diff --git a/.github/BUILD.md b/.github/BUILD.md index c6e870f396..ad33872423 100644 --- a/.github/BUILD.md +++ b/.github/BUILD.md @@ -43,6 +43,8 @@ If you only see a build.bat-file, you're probably on the wrong branch. If you sw You might run into [Powershell quirks](#powershell-quirks). +If it runs without errors; Hooray! Now you can continue with [the next step](CONTRIBUTING.md#how-do-i-begin) and open the solution and build it. + ### Build Infrastructure The Umbraco Build infrastructure relies on a PowerShell object. The object can be retrieved with: diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index cea5859486..0101ac9d16 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -28,7 +28,7 @@ This project and everyone participating in it, is governed by the [our Code of C [Working with the code](#working-with-the-code) * [Building Umbraco from source code](#building-umbraco-from-source-code) * [Working with the source code](#working-with-the-source-code) - * [Making changes after the PR was opened](#making-changes-after-the-pr-was-opened) + * [Making changes after the PR is open](#making-changes-after-the-pr-is-open) * [Which branch should I target for my contributions?](#which-branch-should-i-target-for-my-contributions) * [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository) @@ -65,7 +65,7 @@ Great question! The short version goes like this: * **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions) * **Commit** - done? Yay! 🎉 **Important:** create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`. When you have a branch, commit your changes. Don't commit to `v8/contrib`, create a new branch first. * **Push** - great, now you can push the changes up to your fork on GitHub - * **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress - you can now make use of GitHub's draft pull request status, detailed [here] (https://github.blog/2019-02-14-introducing-draft-pull-requests/)). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go. + * **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress - you can now make use of GitHub's draft pull request status, detailed [here](https://github.blog/2019-02-14-introducing-draft-pull-requests/)). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go. ![Create a pull request](img/createpullrequest.png) diff --git a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs index 0863577487..009e9f38d0 100644 --- a/src/Umbraco.Core/Cache/MacroCacheRefresher.cs +++ b/src/Umbraco.Core/Cache/MacroCacheRefresher.cs @@ -53,7 +53,7 @@ namespace Umbraco.Web.Cache { macroRepoCache.Result.Clear(RepositoryCacheKeys.GetKey(payload.Id)); } - }; + } base.Refresh(json); } diff --git a/src/Umbraco.Core/Collections/TopoGraph.cs b/src/Umbraco.Core/Collections/TopoGraph.cs index b8ded4a458..955a210465 100644 --- a/src/Umbraco.Core/Collections/TopoGraph.cs +++ b/src/Umbraco.Core/Collections/TopoGraph.cs @@ -126,7 +126,7 @@ namespace Umbraco.Core.Collections if (_items.TryGetValue(key, out value)) yield return value; else if (throwOnMissing) - throw new Exception(MissingDependencyError); + throw new Exception($"{MissingDependencyError} Error in type {typeof(TItem).Name}, with key {key}"); } } } diff --git a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs index 41038ea4e9..0d398be83b 100644 --- a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs @@ -79,9 +79,12 @@ namespace Umbraco.Core.Composing foreach (var type in types) EnsureType(type, "register"); - // register them + // register them - ensuring that each item is registered with the same lifetime as the collection. + // NOTE: Previously each one was not registered with the same lifetime which would mean that if there + // was a dependency on an individual item, it would resolve a brand new transient instance which isn't what + // we would expect to happen. The same item should be resolved from the container as the collection. foreach (var type in types) - register.Register(type); + register.Register(type, CollectionLifetime); _registeredTypes = types; } diff --git a/src/Umbraco.Core/HashGenerator.cs b/src/Umbraco.Core/HashGenerator.cs index 62be0dacdd..c7fad08089 100644 --- a/src/Umbraco.Core/HashGenerator.cs +++ b/src/Umbraco.Core/HashGenerator.cs @@ -41,7 +41,7 @@ namespace Umbraco.Core public void AddDateTime(DateTime d) { - _writer.Write(d.Ticks);; + _writer.Write(d.Ticks); } public void AddString(string s) diff --git a/src/Umbraco.Core/Models/PartialViewMacroModel.cs b/src/Umbraco.Core/Models/PartialViewMacroModel.cs index 9964877cba..f3272644f4 100644 --- a/src/Umbraco.Core/Models/PartialViewMacroModel.cs +++ b/src/Umbraco.Core/Models/PartialViewMacroModel.cs @@ -6,7 +6,7 @@ namespace Umbraco.Web.Models /// /// The model used when rendering Partial View Macros /// - public class PartialViewMacroModel + public class PartialViewMacroModel : IContentModel { public PartialViewMacroModel(IPublishedContent page, diff --git a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs index 7425f64727..9a976bea41 100644 --- a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs +++ b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs @@ -7,7 +7,7 @@ using Umbraco.Core.Services; namespace Umbraco.Core.Packaging { - public class ConflictingPackageData + public class ConflictingPackageData { private readonly IMacroService _macroService; private readonly IFileService _fileService; @@ -23,7 +23,7 @@ namespace Umbraco.Core.Packaging return stylesheetNodes .Select(n => { - var xElement = n.Element("Name") ?? n.Element("name"); ; + var xElement = n.Element("Name") ?? n.Element("name"); if (xElement == null) throw new FormatException("Missing \"Name\" element"); diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index a2a30f3a06..2c5b964518 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -90,7 +90,7 @@ namespace Umbraco.Core.Packaging { var packagesXml = EnsureStorage(out _); if (packagesXml?.Root == null) - yield break;; + yield break; foreach (var packageXml in packagesXml.Root.Elements("package")) yield return _parser.ToPackageDefinition(packageXml); @@ -518,7 +518,6 @@ namespace Umbraco.Core.Packaging private XElement GetStylesheetXml(string name, 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"); diff --git a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs index 82e5c6f171..4c818aa87c 100644 --- a/src/Umbraco.Core/Services/IContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/IContentTypeServiceBase.cs @@ -51,7 +51,10 @@ namespace Umbraco.Core.Services IEnumerable GetComposedOf(int id); // composition axis IEnumerable GetChildren(int id); + IEnumerable GetChildren(Guid id); + bool HasChildren(int id); + bool HasChildren(Guid id); void Save(TItem item, int userId = Constants.Security.SuperUserId); void Save(IEnumerable items, int userId = Constants.Security.SuperUserId); diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 7a15e7fbed..3df0eeaf16 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -25,3 +25,1609 @@ + + + + + v4.7.2 + false + {31785BC3-256C-4613-B2F5-A1B0BDDED8C1} + Library + Umbraco.Core + Umbraco.Core + ..\ + + $(AdditionalFileItemNames);Content + + + true + portable + false + bin\Debug\ + TRACE;DEBUG + prompt + 4 + false + latest + + + portable + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\Umbraco.Core.xml + false + latest + + + + + + + + + + + + + + + + + + + + + + + + + 1.0.0-beta2-19324-01 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + 3.3.0 + runtime; build; native; contentfiles; analyzers + all + + + 1.3.0 + + + 1.0.0 + + + 1.0.5 + runtime; build; native; contentfiles; analyzers + all + + + + + + 2.2.2 + + + 5.2.7 + + + 4.0.1 + + + + + + + 2.8.0 + + + 2.0.1 + + + 3.0.0 + + + 2.0.0 + + + 1.0.0 + + + 1.0.3 + + + 2.2.2 + + + 4.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Component + + + Properties\SolutionInfo.cs + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Umbraco.Core/Xml/XmlHelper.cs b/src/Umbraco.Core/Xml/XmlHelper.cs index 099b73ef36..4b76dc3799 100644 --- a/src/Umbraco.Core/Xml/XmlHelper.cs +++ b/src/Umbraco.Core/Xml/XmlHelper.cs @@ -336,7 +336,7 @@ namespace Umbraco.Core.Xml var child = parent.SelectSingleNode(name); if (child != null) { - child.InnerXml = ""; ; + child.InnerXml = ""; return child; } return AddCDataNode(xd, name, value); diff --git a/src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs b/src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs index ab648a261e..17043f18a5 100644 --- a/src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs +++ b/src/Umbraco.Infrastructure/Intall/InstallSteps/SetUmbracoVersionStep.cs @@ -6,7 +6,7 @@ using Umbraco.Web.Install.Models; namespace Umbraco.Web.Install.InstallSteps { [InstallSetupStep(InstallationType.NewInstall | InstallationType.Upgrade, - "UmbracoVersion", 50, "Installation is complete!, get ready to be redirected to your new CMS.", + "UmbracoVersion", 50, "Installation is complete! Get ready to be redirected to your new CMS.", PerformsAppRestart = true)] public class SetUmbracoVersionStep : InstallSetupStep { diff --git a/src/Umbraco.Infrastructure/Media/Exif/ExifBitConverter.cs b/src/Umbraco.Infrastructure/Media/Exif/ExifBitConverter.cs index 86bd12c15e..1dcae62acd 100644 --- a/src/Umbraco.Infrastructure/Media/Exif/ExifBitConverter.cs +++ b/src/Umbraco.Infrastructure/Media/Exif/ExifBitConverter.cs @@ -12,7 +12,7 @@ namespace Umbraco.Web.Media.Exif public ExifBitConverter(ByteOrder from, ByteOrder to) : base(from, to) { - ; + } #endregion diff --git a/src/Umbraco.Infrastructure/Media/Exif/ExifExtendedProperty.cs b/src/Umbraco.Infrastructure/Media/Exif/ExifExtendedProperty.cs index 63c1ce3365..8889a13e1d 100644 --- a/src/Umbraco.Infrastructure/Media/Exif/ExifExtendedProperty.cs +++ b/src/Umbraco.Infrastructure/Media/Exif/ExifExtendedProperty.cs @@ -28,7 +28,7 @@ namespace Umbraco.Web.Media.Exif public ExifEnumProperty(ExifTag tag, T value) : this(tag, value, false) { - ; + } public override ExifInterOperability Interoperability @@ -210,13 +210,13 @@ namespace Umbraco.Web.Media.Exif public ExifPointSubjectArea(ExifTag tag, ushort[] value) : base(tag, value) { - ; + } public ExifPointSubjectArea(ExifTag tag, ushort x, ushort y) - : base(tag, new ushort[] { x, y }) + : base(tag, new ushort[] {x, y}) { - ; + } } @@ -239,13 +239,13 @@ namespace Umbraco.Web.Media.Exif public ExifCircularSubjectArea(ExifTag tag, ushort[] value) : base(tag, value) { - ; + } public ExifCircularSubjectArea(ExifTag tag, ushort x, ushort y, ushort d) : base(tag, new ushort[] { x, y, d }) { - ; + } } @@ -269,13 +269,13 @@ namespace Umbraco.Web.Media.Exif public ExifRectangularSubjectArea(ExifTag tag, ushort[] value) : base(tag, value) { - ; + } public ExifRectangularSubjectArea(ExifTag tag, ushort x, ushort y, ushort w, ushort h) : base(tag, new ushort[] { x, y, w, h }) { - ; + } } @@ -303,13 +303,13 @@ namespace Umbraco.Web.Media.Exif public GPSLatitudeLongitude(ExifTag tag, MathEx.UFraction32[] value) : base(tag, value) { - ; + } public GPSLatitudeLongitude(ExifTag tag, float d, float m, float s) : base(tag, new MathEx.UFraction32[] { new MathEx.UFraction32(d), new MathEx.UFraction32(m), new MathEx.UFraction32(s) }) { - ; + } } @@ -331,13 +331,13 @@ namespace Umbraco.Web.Media.Exif public GPSTimeStamp(ExifTag tag, MathEx.UFraction32[] value) : base(tag, value) { - ; + } public GPSTimeStamp(ExifTag tag, float h, float m, float s) : base(tag, new MathEx.UFraction32[] { new MathEx.UFraction32(h), new MathEx.UFraction32(m), new MathEx.UFraction32(s) }) { - ; + } } diff --git a/src/Umbraco.Infrastructure/Media/Exif/JFIFExtendedProperty.cs b/src/Umbraco.Infrastructure/Media/Exif/JFIFExtendedProperty.cs index 94b255f4d1..24b3ac74be 100644 --- a/src/Umbraco.Infrastructure/Media/Exif/JFIFExtendedProperty.cs +++ b/src/Umbraco.Infrastructure/Media/Exif/JFIFExtendedProperty.cs @@ -19,7 +19,7 @@ namespace Umbraco.Web.Media.Exif public JFIFVersion(ExifTag tag, ushort value) : base(tag, value) { - ; + } public override string ToString() diff --git a/src/Umbraco.Infrastructure/Media/Exif/JPEGSection.cs b/src/Umbraco.Infrastructure/Media/Exif/JPEGSection.cs index a1bc420fe4..78565d2bfa 100644 --- a/src/Umbraco.Infrastructure/Media/Exif/JPEGSection.cs +++ b/src/Umbraco.Infrastructure/Media/Exif/JPEGSection.cs @@ -45,7 +45,7 @@ public JPEGSection(JPEGMarker marker) : this(marker, new byte[0], new byte[0]) { - ; + } #endregion diff --git a/src/Umbraco.Infrastructure/Media/Exif/MathEx.cs b/src/Umbraco.Infrastructure/Media/Exif/MathEx.cs index 735358c40a..94cbccfbda 100644 --- a/src/Umbraco.Infrastructure/Media/Exif/MathEx.cs +++ b/src/Umbraco.Infrastructure/Media/Exif/MathEx.cs @@ -403,37 +403,37 @@ namespace Umbraco.Web.Media.Exif public Fraction32(int numerator, int denominator) : this(numerator, denominator, 0) { - ; + } public Fraction32(int numerator) : this(numerator, (int)1) { - ; + } public Fraction32(Fraction32 f) : this(f.Numerator, f.Denominator, f.Error) { - ; + } public Fraction32(float value) : this((double)value) { - ; + } public Fraction32(double value) : this(FromDouble(value)) { - ; + } public Fraction32(string s) : this(FromString(s)) { - ; + } #endregion @@ -1033,37 +1033,37 @@ namespace Umbraco.Web.Media.Exif public UFraction32(uint numerator, uint denominator) : this(numerator, denominator, 0) { - ; + } public UFraction32(uint numerator) : this(numerator, (uint)1) { - ; + } public UFraction32(UFraction32 f) : this(f.Numerator, f.Denominator, f.Error) { - ; + } public UFraction32(float value) : this((double)value) { - ; + } public UFraction32(double value) : this(FromDouble(value)) { - ; + } public UFraction32(string s) : this(FromString(s)) { - ; + } #endregion diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs index 06a7f9aeb6..4ab5d386b0 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/MergeDateAndDateTimePropertyEditor.cs @@ -36,7 +36,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 if (string.IsNullOrEmpty(dataType.Configuration)) { config.Format = "YYYY-MM-DD"; - }; + } } catch (Exception ex) { diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs index 6e5dcc6b76..ef5a41b21d 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MacroRepository.cs @@ -158,7 +158,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement protected override void PersistUpdatedItem(IMacro entity) { entity.UpdatingEntity(); -; var dto = MacroFactory.BuildDto(entity); Database.Update(dto); diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs index 3db94b026e..c02329aac4 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/TemplateRepository.cs @@ -219,7 +219,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement //Save updated entity to db template.UpdateDate = DateTime.Now; - ; var dto = TemplateFactory.BuildDto(template, NodeObjectTypeId, templateDto.PrimaryKey); Database.Update(dto.NodeDto); diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs index 64e4312b3f..0043eeed72 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ValueConverters/ImageCropperValueConverter.cs @@ -48,7 +48,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters value = new ImageCropperValue { Src = sourceString }; } - value.ApplyConfiguration(propertyType.DataType.ConfigurationAs()); + value?.ApplyConfiguration(propertyType.DataType.ConfigurationAs()); return value; } diff --git a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs index 68b7ee596a..3ce60aa2b5 100644 --- a/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.PublishedCache.NuCache/PublishedSnapshotService.cs @@ -1309,7 +1309,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var member = args.Entity; // refresh the edited data - OnRepositoryRefreshed(db, member, true); + OnRepositoryRefreshed(db, member, false); } private void OnRepositoryRefreshed(IUmbracoDatabase db, IContentBase content, bool published) diff --git a/src/Umbraco.TestData/UmbracoTestDataController.cs b/src/Umbraco.TestData/UmbracoTestDataController.cs index 522d4f6a40..dad9e72c73 100644 --- a/src/Umbraco.TestData/UmbracoTestDataController.cs +++ b/src/Umbraco.TestData/UmbracoTestDataController.cs @@ -223,7 +223,7 @@ namespace Umbraco.TestData { var content = Services.ContentService.Create(faker.Commerce.ProductName(), currParent, docType.Alias); content.SetValue("review", faker.Rant.Review()); - content.SetValue("desc", string.Join(", ", Enumerable.Range(0, 5).Select(x => faker.Commerce.ProductAdjective()))); ; + content.SetValue("desc", string.Join(", ", Enumerable.Range(0, 5).Select(x => faker.Commerce.ProductAdjective()))); content.SetValue("media", imageIds[random.Next(0, imageIds.Count - 1)]); Services.ContentService.Save(content); diff --git a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs index 575bd869ba..2d977e89c7 100644 --- a/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs +++ b/src/Umbraco.Tests/Composing/CollectionBuildersTests.cs @@ -357,7 +357,7 @@ namespace Umbraco.Tests.Composing var col2 = factory.GetInstance(); AssertCollection(col2, typeof(Resolved1), typeof(Resolved2)); - AssertSameCollection(col1, col2); + AssertSameCollection(factory, col1, col2); } } @@ -416,11 +416,11 @@ namespace Umbraco.Tests.Composing { col1A = factory.GetInstance(); col1B = factory.GetInstance(); - } - AssertCollection(col1A, typeof(Resolved1), typeof(Resolved2)); - AssertCollection(col1B, typeof(Resolved1), typeof(Resolved2)); - AssertSameCollection(col1A, col1B); + AssertCollection(col1A, typeof(Resolved1), typeof(Resolved2)); + AssertCollection(col1B, typeof(Resolved1), typeof(Resolved2)); + AssertSameCollection(factory, col1A, col1B); + } TestCollection col2; @@ -455,7 +455,7 @@ namespace Umbraco.Tests.Composing Assert.IsInstanceOf(expected[i], colA[i]); } - private static void AssertSameCollection(IEnumerable col1, IEnumerable col2) + private static void AssertSameCollection(IFactory factory, IEnumerable col1, IEnumerable col2) { Assert.AreSame(col1, col2); @@ -463,8 +463,19 @@ namespace Umbraco.Tests.Composing var col2A = col2.ToArray(); Assert.AreEqual(col1A.Length, col2A.Length); + + // Ensure each item in each collection is the same but also + // resolve each item from the factory to ensure it's also the same since + // it should have the same lifespan. for (var i = 0; i < col1A.Length; i++) + { Assert.AreSame(col1A[i], col2A[i]); + + var itemA = factory.GetInstance(col1A[i].GetType()); + var itemB = factory.GetInstance(col2A[i].GetType()); + + Assert.AreSame(itemA, itemB); + } } private static void AssertNotSameCollection(IEnumerable col1, IEnumerable col2) @@ -475,8 +486,11 @@ namespace Umbraco.Tests.Composing var col2A = col2.ToArray(); Assert.AreEqual(col1A.Length, col2A.Length); + for (var i = 0; i < col1A.Length; i++) + { Assert.AreNotSame(col1A[i], col2A[i]); + } } #endregion diff --git a/src/Umbraco.Tests/Logging/LogviewerTests.cs b/src/Umbraco.Tests/Logging/LogviewerTests.cs index 6e0e82827c..87cc19a2c6 100644 --- a/src/Umbraco.Tests/Logging/LogviewerTests.cs +++ b/src/Umbraco.Tests/Logging/LogviewerTests.cs @@ -164,7 +164,7 @@ namespace Umbraco.Tests.Logging //Query @Level='Warning' BUT we pass in array of LogLevels for Debug & Info (Expect to get 0 results) string[] logLevelMismatch = { "Debug", "Information" }; - var filterLevelQuery = _logViewer.GetLogs(_logTimePeriod, pageNumber: 1, filterExpression: "@Level='Warning'", logLevels: logLevelMismatch); ; + var filterLevelQuery = _logViewer.GetLogs(_logTimePeriod, pageNumber: 1, filterExpression: "@Level='Warning'", logLevels: logLevelMismatch); Assert.AreEqual(0, filterLevelQuery.TotalItems); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js index b8ee797c82..a33796ab6d 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbsections.directive.js @@ -133,6 +133,17 @@ function sectionsDirective($timeout, $window, navigationService, treeService, se } }; + scope.currentSectionInOverflow = function () { + if (scope.overflowingSections === 0) { + return false; + } + + var currentSection = scope.sections.filter(s => s.alias === scope.currentSection); + + return (scope.sections.indexOf(currentSection[0]) >= scope.maxSections); + + }; + loadSections(); } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js index 2dc0ebdf93..381ecc76c3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbtour/umbtourstep.directive.js @@ -18,9 +18,9 @@ function link(scope, element, attrs, ctrl) { - scope.close = function() { - if(scope.onClose) { - scope.onClose(); + scope.close = function () { + if (scope.onClose) { + scope.onClose(); } } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 7429f60e0d..91b0ba8754 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -42,7 +42,7 @@ $scope.page.isNew = Object.toBoolean(newVal); //We fetch all ancestors of the node to generate the footer breadcrumb navigation - if (content.parentId && content.parentId !== -1) { + if (content.parentId && content.parentId !== -1 && content.parentId !== -20) { loadBreadcrumb(); if (!watchingCulture) { $scope.$watch('culture', @@ -287,6 +287,8 @@ $scope.page.showSaveButton = true; // add ellipsis to the save button if it opens the variant overlay $scope.page.saveButtonEllipsis = content.variants && content.variants.length > 1 ? "true" : "false"; + } else { + $scope.page.showSaveButton = false; } // create the pubish combo button diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js index 0ed1b12000..95b2a520d1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/umbcontentnodeinfo.directive.js @@ -328,6 +328,9 @@ // invariant nodes scope.currentUrls = scope.node.urls; } + + // figure out if multiple cultures apply across the content urls + scope.currentUrlsHaveMultipleCultures = _.keys(_.groupBy(scope.currentUrls, url => url.culture)).length > 1; } // load audit trail and redirects when on the info tab diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbchildselector.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbchildselector.directive.js index a33fd4be53..96ce8735eb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbchildselector.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbchildselector.directive.js @@ -99,13 +99,18 @@ Use this directive to render a ui component for selecting child items to a paren @param {string} parentName (binding): The parent name. @param {string} parentIcon (binding): The parent icon. @param {number} parentId (binding): The parent id. -@param {callback} onRemove (binding): Callback when the remove button is clicked on an item. +@param {callback} onRemove (binding): Callback when removing an item.

The callback returns:

  • child: The selected item.
  • $index: The selected item index.
-@param {callback} onAdd (binding): Callback when the add button is clicked. +@param {callback} onAdd (binding): Callback when adding an item. +

The callback returns:

+
    +
  • $event: The select event.
  • +
+@param {callback} onSort (binding): Callback when sorting an item.

The callback returns:

  • $event: The select event.
  • @@ -174,16 +179,15 @@ Use this directive to render a ui component for selecting child items to a paren eventBindings.push(scope.$watch('parentName', function(newValue, oldValue){ if (newValue === oldValue) { return; } - if ( oldValue === undefined || newValue === undefined) { return; } + if (oldValue === undefined || newValue === undefined) { return; } syncParentName(); - })); eventBindings.push(scope.$watch('parentIcon', function(newValue, oldValue){ if (newValue === oldValue) { return; } - if ( oldValue === undefined || newValue === undefined) { return; } + if (oldValue === undefined || newValue === undefined) { return; } syncParentIcon(); })); @@ -191,6 +195,7 @@ Use this directive to render a ui component for selecting child items to a paren // sortable options for allowed child content types scope.sortableOptions = { axis: "y", + cancel: ".unsortable", containment: "parent", distance: 10, opacity: 0.7, @@ -199,7 +204,7 @@ Use this directive to render a ui component for selecting child items to a paren zIndex: 6000, update: function (e, ui) { if(scope.onSort) { - scope.onSort(); + scope.onSort(); } } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js index 0369b4bd2e..149c2b5087 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valpropertymsg.directive.js @@ -24,8 +24,7 @@ function valPropertyMsg(serverValidationManager, localizationService) { var hasError = false; //create properties on our custom scope so we can use it in our template - scope.errorMsg = ""; - + scope.errorMsg = ""; //the property form controller api var formCtrl = ctrl[0]; @@ -34,11 +33,15 @@ function valPropertyMsg(serverValidationManager, localizationService) { //the property controller api var umbPropCtrl = ctrl[2]; //the variants controller api - var umbVariantCtrl = ctrl[3]; + var umbVariantCtrl = ctrl[3]; var currentProperty = umbPropCtrl.property; scope.currentProperty = currentProperty; + var currentCulture = currentProperty.culture; + + // validation object won't exist when editor loads outside the content form (ie in settings section when modifying a content type) + var isMandatory = currentProperty.validation ? currentProperty.validation.mandatory : undefined; var labels = {}; localizationService.localize("errors_propertyHasErrors").then(function (data) { @@ -91,23 +94,25 @@ function valPropertyMsg(serverValidationManager, localizationService) { if (!watcher) { watcher = scope.$watch("currentProperty.value", function (newValue, oldValue) { - if (angular.equals(newValue, oldValue)) { return; } var errCount = 0; + for (var e in formCtrl.$error) { if (angular.isArray(formCtrl.$error[e])) { errCount++; } - } + } //we are explicitly checking for valServer errors here, since we shouldn't auto clear // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg // is the only one, then we'll clear. - if (errCount === 0 || (errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) { + if (errCount === 0 + || (errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) + || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) { scope.errorMsg = ""; formCtrl.$setValidity('valPropertyMsg', true); } else if (showValidation && scope.errorMsg === "") { @@ -136,6 +141,21 @@ function valPropertyMsg(serverValidationManager, localizationService) { } //if there are any errors in the current property form that are not valPropertyMsg else if (_.without(_.keys(formCtrl.$error), "valPropertyMsg").length > 0) { + + // errors exist, but if the property is NOT mandatory and has no value, the errors should be cleared + if (isMandatory !== undefined && isMandatory === false && !currentProperty.value) { + hasError = false; + showValidation = false; + scope.errorMsg = ""; + + // if there's no value, the controls can be reset, which clears the error state on formCtrl + for (let control of formCtrl.$getControls()) { + control.$setValidity(); + } + + return; + } + hasError = true; //update the validation message if we don't already have one assigned. if (showValidation && scope.errorMsg === "") { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js index d481205657..5bfe66b25f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js @@ -525,7 +525,7 @@ When building a custom infinite editor view you can use the same components as a function rollback(editor) { editor.view = "views/common/infiniteeditors/rollback/rollback.html"; - if (!editor.size) editor.size = "small"; + if (!editor.size) editor.size = "medium"; open(editor); } @@ -784,7 +784,7 @@ When building a custom infinite editor view you can use the same components as a * @methodOf umbraco.services.editorService * * @description - * Opens the user group picker in infinite editing, the submit callback returns the saved template + * Opens the template editor in infinite editing, the submit callback returns the saved template * @param {Object} editor rendering options * @param {String} editor.id The template id * @param {Callback} editor.submit Submits the editor diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less index 456601a7bd..ff4122b258 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-toggle.less @@ -103,14 +103,21 @@ - +.umb-toggle.umb-toggle--disabled.umb-toggle--checked, .umb-toggle.umb-toggle--disabled { .umb-toggle__toggle { cursor: not-allowed; - border-color: @gray-5; + background-color: @gray-9; + border-color: @gray-9; + } + .umb-toggle__icon--left { + color: @gray-6; + } + .umb-toggle__icon--right { + color: @gray-6; } .umb-toggle__handler { - background-color: @gray-5; + background-color: @gray-10; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-child-selector.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-child-selector.less index da690663d0..937f746c56 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-child-selector.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-child-selector.less @@ -16,23 +16,24 @@ .umb-child-selector__child.-placeholder { border: 1px dashed @gray-8; background: none; - cursor: pointer; text-align: center; justify-content: center; - - color:@ui-action-type; + width: 100%; + color: @ui-action-type; + &:hover { - color:@ui-action-type-hover; - border-color:@ui-action-type-hover; - text-decoration:none; + color: @ui-action-type-hover; + border-color: @ui-action-type-hover; + text-decoration: none; } } .umb-child-selector__children-container { - margin-left: 30px; - .umb-child-selector__child { - cursor: move; - } + margin-left: 30px; + + .umb-child-selector__child.ui-sortable-handle { + cursor: move; + } } .umb-child-selector__child-description { @@ -65,5 +66,6 @@ } .umb-child-selector__child-remove { - cursor: pointer; + background: none; + border: none; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less index b9efd8f7d8..1c5c275642 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-form-check.less @@ -162,5 +162,6 @@ &.umb-form-check--disabled { cursor: not-allowed !important; opacity: 0.5; + pointer-events: none; } } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-search.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-search.less index 68cb9b2e58..ec598c17eb 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-search.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-mini-search.less @@ -23,6 +23,7 @@ background-color: transparent; border-color: @ui-action-discreet-border; transition: background-color .1s linear, border-color .1s linear, color .1s linear, width .1s ease-in-out, padding-left .1s ease-in-out; + cursor: pointer; } &:focus-within, &:hover { @@ -39,6 +40,7 @@ background-color: white; color: @ui-action-discreet-border-hover; border-color: @ui-action-discreet-border-hover; + cursor: unset; } input:focus, &:focus-within input, &.--has-value input { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less index 4168ab3c39..716693c778 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-nested-content.less @@ -135,6 +135,8 @@ .umb-nested-content__header-bar:hover .umb-nested-content__icons, +.umb-nested-content__header-bar:focus .umb-nested-content__icons, +.umb-nested-content__header-bar:focus-within .umb-nested-content__icons, .umb-nested-content__item--active > .umb-nested-content__header-bar .umb-nested-content__icons { opacity: 1; } diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less index f754a09368..939fd79826 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-node-preview.less @@ -29,7 +29,8 @@ .umb-node-preview__icon { display: flex; width: 25px; - height: 25px; + min-height: 25px; + height: 100%; justify-content: center; align-items: center; font-size: 20px; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less index 3f0b981ac6..17c4bf1a55 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-actions.less @@ -61,6 +61,7 @@ opacity: 0; transition: opacity 120ms; } +.umb-property:focus-within .umb-property-actions__toggle, .umb-property:hover .umb-property-actions__toggle, .umb-property .umb-property-actions__toggle:focus { opacity: 1; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less index 08b1a1b5e1..9550acfb1b 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-property-file-upload.less @@ -1,6 +1,7 @@ .umb-property-file-upload { .umb-upload-button-big { + max-width: (@propertyEditorLimitedWidth - 40); display: block; padding: 20px; opacity: 1; diff --git a/src/Umbraco.Web.UI.Client/src/less/mixins.less b/src/Umbraco.Web.UI.Client/src/less/mixins.less index efc0178ca2..f5c499cbfc 100644 --- a/src/Umbraco.Web.UI.Client/src/less/mixins.less +++ b/src/Umbraco.Web.UI.Client/src/less/mixins.less @@ -418,7 +418,7 @@ // -------------------------------------------------- // Limit width of specific property editors .umb-property-editor--limit-width { - max-width: 800px; + max-width: @propertyEditorLimitedWidth; } // Horizontal dividers diff --git a/src/Umbraco.Web.UI.Client/src/less/property-editors.less b/src/Umbraco.Web.UI.Client/src/less/property-editors.less index c25941514e..764b73c593 100644 --- a/src/Umbraco.Web.UI.Client/src/less/property-editors.less +++ b/src/Umbraco.Web.UI.Client/src/less/property-editors.less @@ -722,6 +722,10 @@ // // File upload // -------------------------------------------------- +.umb-fileupload { + display: flex; +} + .umb-fileupload .preview { border-radius: 5px; border: 1px solid @gray-6; diff --git a/src/Umbraco.Web.UI.Client/src/less/rte.less b/src/Umbraco.Web.UI.Client/src/less/rte.less index d6d38f540a..0e43f428ae 100644 --- a/src/Umbraco.Web.UI.Client/src/less/rte.less +++ b/src/Umbraco.Web.UI.Client/src/less/rte.less @@ -164,4 +164,13 @@ .mce-fullscreen { position: absolute; + + .mce-in { + position: fixed; + top: 35px !important; + } + + umb-editor__overlay, .umb-editor { + position: fixed; + } } diff --git a/src/Umbraco.Web.UI.Client/src/less/sections.less b/src/Umbraco.Web.UI.Client/src/less/sections.less index 27b11b1c3b..1f19786b3c 100644 --- a/src/Umbraco.Web.UI.Client/src/less/sections.less +++ b/src/Umbraco.Web.UI.Client/src/less/sections.less @@ -55,7 +55,7 @@ ul.sections { transition: opacity .1s linear, box-shadow .1s; } - &.current a { + &.current > a { color: @ui-active; &::after { @@ -76,6 +76,13 @@ ul.sections { transition: opacity .1s linear; } + &.current { + i { + opacity: 1; + background: @ui-active; + } + } + &:hover i { opacity: 1; } diff --git a/src/Umbraco.Web.UI.Client/src/less/variables.less b/src/Umbraco.Web.UI.Client/src/less/variables.less index a906bc0eed..5c0e6f7729 100644 --- a/src/Umbraco.Web.UI.Client/src/less/variables.less +++ b/src/Umbraco.Web.UI.Client/src/less/variables.less @@ -244,6 +244,7 @@ @paddingSmall: 2px 10px; // 26px @paddingMini: 0 6px; // 22px +@propertyEditorLimitedWidth: 800px; // Disabled this to keep consistency throughout the backoffice UI. Untill a better solution is thought up, this will do. @baseBorderRadius: 3px; // 2px; diff --git a/src/Umbraco.Web.UI.Client/src/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/navigation.controller.js index 194c45afe6..281be2d331 100644 --- a/src/Umbraco.Web.UI.Client/src/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/navigation.controller.js @@ -257,6 +257,7 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar evts.push(eventsService.on("app.ready", function (evt, data) { $scope.authenticated = true; ensureInit(); + ensureMainCulture(); })); // event for infinite editors @@ -279,8 +280,22 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar } })); - - + /** + * For multi language sites, this ensures that mculture is set to either the last selected language or the default one + */ + function ensureMainCulture() { + if ($location.search().mculture) { + return; + } + var language = lastLanguageOrDefault(); + if (!language) { + return; + } + // trigger a language selection in the next digest cycle + $timeout(function () { + $scope.selectLanguage(language); + }); + } /** * Based on the current state of the application, this configures the scope variables that control the main tree and language drop down @@ -385,28 +400,19 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar if ($scope.languages.length > 1) { //if there's already one set, check if it exists - var currCulture = null; + var language = null; var mainCulture = $location.search().mculture; if (mainCulture) { - currCulture = _.find($scope.languages, function (l) { + language = _.find($scope.languages, function (l) { return l.culture.toLowerCase() === mainCulture.toLowerCase(); }); } - if (!currCulture) { - // no culture in the request, let's look for one in the cookie that's set when changing language - var defaultCulture = $cookies.get("UMB_MCULTURE"); - if (!defaultCulture || !_.find($scope.languages, function (l) { - return l.culture.toLowerCase() === defaultCulture.toLowerCase(); - })) { - // no luck either, look for the default language - var defaultLang = _.find($scope.languages, function (l) { - return l.isDefault; - }); - if (defaultLang) { - defaultCulture = defaultLang.culture; - } + if (!language) { + language = lastLanguageOrDefault(); + + if (language) { + $location.search("mculture", language.culture); } - $location.search("mculture", defaultCulture ? defaultCulture : null); } } @@ -431,6 +437,25 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar }); }); } + + function lastLanguageOrDefault() { + if (!$scope.languages || $scope.languages.length <= 1) { + return null; + } + // see if we can find a culture in the cookie set when changing language + var lastCulture = $cookies.get("UMB_MCULTURE"); + var language = lastCulture ? _.find($scope.languages, function (l) { + return l.culture.toLowerCase() === lastCulture.toLowerCase(); + }) : null; + if (!language) { + // no luck, look for the default language + language = _.find($scope.languages, function (l) { + return l.isDefault; + }); + } + return language; + } + function nodeExpandedHandler(args) { //store the reference to the expanded node path if (args.node) { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js index b8581d28d0..9640f2eba2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.controller.js @@ -56,7 +56,8 @@ "validation_validateAsEmail", "validation_validateAsNumber", "validation_validateAsUrl", - "validation_enterCustomValidation" + "validation_enterCustomValidation", + "validation_fieldIsMandatory" ]; localizationService.localizeMany(labels) @@ -66,6 +67,7 @@ vm.labels.validateAsNumber = data[1]; vm.labels.validateAsUrl = data[2]; vm.labels.customValidation = data[3]; + vm.labels.fieldIsMandatory = data[4]; vm.validationTypes = [ { diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html index 4474390199..77ee276e3e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html @@ -84,14 +84,15 @@
    - - + on-click="vm.toggleValidation()" + label-on="{{vm.labels.fieldIsMandatory}}" + label-off="{{vm.labels.fieldIsMandatory}}" + show-labels="true" + label-position="right" focus-when="{{vm.focusOnMandatoryField}}" + class="mb1"> -
  • +
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-tour.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-tour.html index e358d75b9e..064dcf6945 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-tour.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-tour.html @@ -70,7 +70,7 @@
    - +

    Oh, we got lost!

    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umbtour/umb-tour-step.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umbtour/umb-tour-step.html index a2caff4ff2..9e161d6b4e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umbtour/umb-tour-step.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umbtour/umb-tour-step.html @@ -1,8 +1,7 @@
    -
    -
    - +
    diff --git a/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-preview.html b/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-preview.html index 32aa5ed1a2..014297ae51 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-preview.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/users/umb-user-preview.html @@ -1,12 +1,11 @@
    - +
    @@ -15,7 +14,6 @@
    - Remove -
    - -
    \ No newline at end of file + +
    + diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js index 64f601f8b7..1a1254fcf1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.create.controller.js @@ -22,10 +22,14 @@ function contentCreateController($scope, function initialize() { $scope.loading = true; $scope.allowedTypes = null; - $scope.countTypes = contentTypeResource.getCount; var getAllowedTypes = contentTypeResource.getAllowedTypes($scope.currentNode.id).then(function (data) { $scope.allowedTypes = iconHelper.formatContentTypeIcons(data); + if ($scope.allowedTypes.length === 0) { + contentTypeResource.getCount().then(function(count) { + $scope.countTypes = count; + }); + } }); var getCurrentUser = authResource.getCurrentUser().then(function (currentUser) { if (currentUser.allowedSections.indexOf("settings") > -1) { diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js index a35a404c24..cdabb05fd4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js @@ -220,7 +220,7 @@ function startupLatestEditsController($scope) { } angular.module("umbraco").controller("Umbraco.Dashboard.StartupLatestEditsController", startupLatestEditsController); -function MediaFolderBrowserDashboardController($rootScope, $scope, $location, contentTypeResource, userService) { +function MediaFolderBrowserDashboardController($scope, $routeParams, $location, contentTypeResource, userService) { var currentUser = {}; @@ -251,6 +251,8 @@ function MediaFolderBrowserDashboardController($rootScope, $scope, $location, co view: dt.view }; + // tell the list view to list content at root + $routeParams.id = -1; }); } else if (currentUser.startMediaIds.length > 0){ diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html b/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html index a7f7d45087..f1388753a0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html @@ -4,7 +4,7 @@ -

    Hours of Umbraco training videos are only a click away

    +

    Hours of Umbraco training videos are only a click away

    Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit umbraco.tv for even more Umbraco videos

    diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js index a5884c2355..a7ce67fc0b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.edit.controller.js @@ -73,14 +73,14 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource, var content = $scope.content; - // we need to check wether an app is present in the current data, if not we will present the default app. + // we need to check whether an app is present in the current data, if not we will present the default app. var isAppPresent = false; // on first init, we dont have any apps. but if we are re-initializing, we do, but ... if ($scope.app) { // lets check if it still exists as part of our apps array. (if not we have made a change to our docType, even just a re-save of the docType it will turn into new Apps.) - _.forEach(content.apps, function(app) { + content.apps.forEach(app => { if (app === $scope.app) { isAppPresent = true; } @@ -88,7 +88,7 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource, // if we did reload our DocType, but still have the same app we will try to find it by the alias. if (isAppPresent === false) { - _.forEach(content.apps, function(app) { + content.apps.forEach(app => { if (app.alias === $scope.app.alias) { isAppPresent = true; app.active = true; @@ -182,24 +182,26 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource, formHelper.resetForm({ scope: $scope }); - contentEditingHelper.handleSuccessfulSave({ - scope: $scope, - savedContent: data, - rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) - }); - - editorState.set($scope.content); - - syncTreeNode($scope.content, data.path); - - init(); - - $scope.page.saveButtonState = "success"; - // close the editor if it's infinite mode + // submit function manages rebinding changes if(infiniteMode && $scope.model.submit) { $scope.model.mediaNode = $scope.content; $scope.model.submit($scope.model); + } else { + // if not infinite mode, rebind changed props etc + contentEditingHelper.handleSuccessfulSave({ + scope: $scope, + savedContent: data, + rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data) + }); + + editorState.set($scope.content); + + syncTreeNode($scope.content, data.path); + + $scope.page.saveButtonState = "success"; + + init(); } }, function(err) { @@ -245,7 +247,7 @@ function mediaEditController($scope, $routeParams, $q, appState, mediaResource, syncTreeNode($scope.content, data.path, true); } - if ($scope.content.parentId && $scope.content.parentId != -1) { + if ($scope.content.parentId && $scope.content.parentId !== -1 && $scope.content.parentId !== -21) { //We fetch all ancestors of the node to generate the footer breadcrump navigation entityResource.getAncestors(nodeId, "media") .then(function (anc) { diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js index 3c954b9316..139a2515b5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js @@ -276,6 +276,9 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time } $scope.reloadView = function (id, reloadActiveNode) { + if (!id) { + return; + } $scope.viewLoaded = false; $scope.folders = []; @@ -713,45 +716,12 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time } function initView() { - //default to root id if the id is undefined var id = $routeParams.id; if (id === undefined) { - id = -1; + // no ID found in route params - don't list anything as we don't know for sure where we are + return; } - getContentTypesCallback(id).then(function (listViewAllowedTypes) { - $scope.listViewAllowedTypes = listViewAllowedTypes; - - var blueprints = false; - _.each(listViewAllowedTypes, function (allowedType) { - if (_.isEmpty(allowedType.blueprints)) { - // this helps the view understand that there are no blueprints available - allowedType.blueprints = null; - } - else { - blueprints = true; - // turn the content type blueprints object into an array of sortable objects for the view - allowedType.blueprints = _.map(_.pairs(allowedType.blueprints || {}), function (pair) { - return { - id: pair[0], - name: pair[1] - }; - }); - } - }); - - if (listViewAllowedTypes.length === 1 && blueprints === false) { - $scope.createAllowedButtonSingle = true; - } - if (listViewAllowedTypes.length === 1 && blueprints === true) { - $scope.createAllowedButtonSingleWithBlueprints = true; - } - if (listViewAllowedTypes.length > 1) { - $scope.createAllowedButtonMultiWithBlueprints = true; - } - }); - - $scope.contentId = id; $scope.isTrashed = editorState.current ? editorState.current.trashed : id === "-20" || id === "-21"; @@ -765,6 +735,40 @@ function listViewController($scope, $interpolate, $routeParams, $injector, $time $scope.options.allowBulkMove || $scope.options.allowBulkDelete; + if ($scope.isTrashed === false) { + getContentTypesCallback(id).then(function (listViewAllowedTypes) { + $scope.listViewAllowedTypes = listViewAllowedTypes; + + var blueprints = false; + _.each(listViewAllowedTypes, function (allowedType) { + if (_.isEmpty(allowedType.blueprints)) { + // this helps the view understand that there are no blueprints available + allowedType.blueprints = null; + } + else { + blueprints = true; + // turn the content type blueprints object into an array of sortable objects for the view + allowedType.blueprints = _.map(_.pairs(allowedType.blueprints || {}), function (pair) { + return { + id: pair[0], + name: pair[1] + }; + }); + } + }); + + if (listViewAllowedTypes.length === 1 && blueprints === false) { + $scope.createAllowedButtonSingle = true; + } + if (listViewAllowedTypes.length === 1 && blueprints === true) { + $scope.createAllowedButtonSingleWithBlueprints = true; + } + if (listViewAllowedTypes.length > 1) { + $scope.createAllowedButtonMultiWithBlueprints = true; + } + }); + } + $scope.reloadView($scope.contentId); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js index 8c6194d638..cde99d7b9e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js @@ -48,49 +48,45 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // This is done by remapping the int/guid ids into a new array of items, where we create "Deleted item" placeholders // when there is no match for a selected id. This will ensure that the values being set on save, are the same as before. - medias = _.map(ids, - function (id) { - var found = _.find(medias, - function (m) { - // We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and - // it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString() - // compares and be completely sure it works. - return m.udi.toString() === id.toString() || m.id.toString() === id.toString(); - }); - if (found) { - return found; - } else { - return { - name: vm.labels.deletedItem, - id: $scope.model.config.idType !== "udi" ? id : null, - udi: $scope.model.config.idType === "udi" ? id : null, - icon: "icon-picture", - thumbnail: null, - trashed: true - }; - } - }); + medias = ids.map(id => { + var found = medias.find(m => + // We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and + // it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString() + // compares and be completely sure it works. + m.udi.toString() === id.toString() || m.id.toString() === id.toString()); + + if (found) { + return found; + } else { + return { + name: vm.labels.deletedItem, + id: $scope.model.config.idType !== "udi" ? id : null, + udi: $scope.model.config.idType === "udi" ? id : null, + icon: "icon-picture", + thumbnail: null, + trashed: true + }; + } + }); - _.each(medias, - function (media, i) { + medias.forEach(media => { + if (!media.extension && media.id && media.metaData) { + media.extension = mediaHelper.getFileExtension(media.metaData.MediaPath); + } - if (!media.extension && media.id && media.metaData) { - media.extension = mediaHelper.getFileExtension(media.metaData.MediaPath); - } + // if there is no thumbnail, try getting one if the media is not a placeholder item + if (!media.thumbnail && media.id && media.metaData) { + media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); + } - // if there is no thumbnail, try getting one if the media is not a placeholder item - if (!media.thumbnail && media.id && media.metaData) { - media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); - } + $scope.mediaItems.push(media); - $scope.mediaItems.push(media); - - if ($scope.model.config.idType === "udi") { - $scope.ids.push(media.udi); - } else { - $scope.ids.push(media.id); - } - }); + if ($scope.model.config.idType === "udi") { + $scope.ids.push(media.udi); + } else { + $scope.ids.push(media.id); + } + }); sync(); }); @@ -100,7 +96,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl function sync() { $scope.model.value = $scope.ids.join(); removeAllEntriesAction.isDisabled = $scope.ids.length === 0; - }; + } function setDirty() { angularHelper.getCurrentForm($scope).$setDirty(); @@ -111,18 +107,17 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // reload. We only reload the images that is already picked but has been updated. // We have to get the entities from the server because the media // can be edited without being selected - _.each($scope.images, - function (image, i) { - if (updatedMediaNodes.indexOf(image.udi) !== -1) { - image.loading = true; - entityResource.getById(image.udi, "media") - .then(function (mediaEntity) { - angular.extend(image, mediaEntity); - image.thumbnail = mediaHelper.resolveFileFromEntity(image, true); - image.loading = false; - }); - } - }); + $scope.mediaItems.forEach(media => { + if (updatedMediaNodes.indexOf(media.udi) !== -1) { + media.loading = true; + entityResource.getById(media.udi, "Media") + .then(function (mediaEntity) { + angular.extend(media, mediaEntity); + media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); + media.loading = false; + }); + } + }); } function init() { @@ -177,20 +172,20 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl // the media picker is using media entities so we get the // entity so we easily can format it for use in the media grid if (model && model.mediaNode) { - entityResource.getById(model.mediaNode.id, "media") + entityResource.getById(model.mediaNode.id, "Media") .then(function (mediaEntity) { // if an image is selecting more than once // we need to update all the media items - angular.forEach($scope.images, function (image) { - if (image.id === model.mediaNode.id) { - angular.extend(image, mediaEntity); - image.thumbnail = mediaHelper.resolveFileFromEntity(image, true); + $scope.mediaItems.forEach(media => { + if (media.id === model.mediaNode.id) { + angular.extend(media, mediaEntity); + media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); } }); }); } }, - close: function (model) { + close: function () { editorService.close(); } }; @@ -210,7 +205,7 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl editorService.close(); - _.each(model.selection, function (media, i) { + model.selection.forEach(media => { // if there is no thumbnail, try getting one if the media is not a placeholder item if (!media.thumbnail && media.id && media.metaData) { media.thumbnail = mediaHelper.resolveFileFromEntity(media, true); @@ -280,16 +275,14 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl disabled: !multiPicker, items: "li:not(.add-wrapper)", cancel: ".unsortable", - update: function (e, ui) { + update: function () { setDirty(); $timeout(function() { // TODO: Instead of doing this with a timeout would be better to use a watch like we do in the // content picker. Then we don't have to worry about setting ids, render models, models, we just set one and let the // watch do all the rest. - $scope.ids = _.map($scope.mediaItems, - function (item) { - return $scope.model.config.idType === "udi" ? item.udi : item.id; - }); + $scope.ids = $scope.mediaItems.map(media => $scope.model.config.idType === "udi" ? media.udi : media.id); + sync(); }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html index b17906272d..c4dba4d373 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.html @@ -36,16 +36,16 @@
    - -
  • -
  • diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js index 7de3a5b567..f2538c66c1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js @@ -221,6 +221,7 @@ if (vm.overlayMenu.availableItems.length === 1 && vm.overlayMenu.pasteItems.length === 0) { // only one scaffold type - no need to display the picker addNode(vm.scaffolds[0].contentTypeAlias); + vm.overlayMenu = null; return; } @@ -276,6 +277,9 @@ }; vm.getName = function (idx) { + if (!model.value || !model.value.length) { + return ""; + } var name = ""; @@ -325,6 +329,10 @@ }; vm.getIcon = function (idx) { + if (!model.value || !model.value.length) { + return ""; + } + var scaffold = getScaffold(model.value[idx].ncContentTypeAlias); return scaffold && scaffold.icon ? iconHelper.convertFromLegacyIcon(scaffold.icon) : "icon-folder"; } @@ -480,10 +488,12 @@ } // Enforce min items if we only have one scaffold type + var modelWasChanged = false; if (vm.nodes.length < vm.minItems && vm.scaffolds.length === 1) { for (var i = vm.nodes.length; i < model.config.minItems; i++) { addNode(vm.scaffolds[0].contentTypeAlias); } + modelWasChanged = true; } // If there is only one item, set it as current node @@ -495,6 +505,10 @@ vm.inited = true; + if (modelWasChanged) { + updateModel(); + } + updatePropertyActionStates(); checkAbilityToPasteContent(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js index a20289d076..f5f0f7f2a2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js @@ -56,7 +56,7 @@ return value.toFixed(stepDecimalPlaces); }, from: function (value) { - return value; + return Number(value); } }, "range": { diff --git a/src/Umbraco.Web.UI.Client/src/views/users/group.html b/src/Umbraco.Web.UI.Client/src/views/users/group.html index 0244819655..eae6dbd75c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/group.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/group.html @@ -40,13 +40,12 @@ on-remove="vm.removeSelectedItem($index, vm.userGroup.sections)"> - + @@ -61,14 +60,14 @@ on-remove="vm.clearStartNode('content')"> - + + @@ -85,14 +84,14 @@ on-remove="vm.clearStartNode('media')"> - + + @@ -127,13 +126,12 @@ on-edit="vm.setPermissionsForNode(node)"> - + @@ -155,13 +153,11 @@ on-remove="vm.removeSelectedItem($index, vm.userGroup.users)"> - + diff --git a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html index e0cf09da50..bb3efaede2 100644 --- a/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html +++ b/src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html @@ -75,13 +75,11 @@ on-remove="model.removeSelectedItem($index, model.user.userGroups)"> - + @@ -100,13 +98,12 @@ name="model.labels.noStartNodes"> - + @@ -125,13 +122,12 @@ name="model.labels.noStartNodes"> - + diff --git a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml index 7962d17898..8bcb535bd6 100755 --- a/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/PartialViewMacros/Templates/Gallery.cshtml @@ -31,13 +31,13 @@ @* a single image *@ if (media.IsDocumentType("Image")) { - @Render(media); + @Render(media) } @* a folder with images under it *@ foreach (var image in media.Children(Current.VariationContextAccessor)) { - @Render(image); + @Render(image) } } diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml index defe59d808..131b0515ae 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3-Fluid.cshtml @@ -2,31 +2,31 @@ @using Umbraco.Web.Templates @using Newtonsoft.Json.Linq -@* +@* Razor helpers located at the bottom of this file *@ @if (Model != null && Model.sections != null) { var oneColumn = ((System.Collections.ICollection)Model.sections).Count == 1; - +
    @if (oneColumn) { foreach (var section in Model.sections) {
    @foreach (var row in section.rows) { - @renderRow(row); + @renderRow(row) }
    - } - }else { + } + }else {
    @foreach (var s in Model.sections) {
    @foreach (var row in s.rows) { - @renderRow(row); + @renderRow(row) }
    @@ -85,4 +85,4 @@ return new MvcHtmlString(string.Join(" ", attrs)); } -} \ No newline at end of file +} diff --git a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml index 9333628ed6..23fee33043 100644 --- a/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml +++ b/src/Umbraco.Web.UI/Views/Partials/Grid/Bootstrap3.cshtml @@ -12,7 +12,7 @@ foreach (var section in Model.sections) {
    @foreach (var row in section.rows) { - @renderRow(row, true); + @renderRow(row, true) }
    } @@ -23,7 +23,7 @@
    @foreach (var row in s.rows) { - @renderRow(row, false); + @renderRow(row, false) }
    diff --git a/src/Umbraco.Web/Cache/WebCachingAppCache.cs b/src/Umbraco.Web/Cache/WebCachingAppCache.cs index 1879e8b69b..087d393d3c 100644 --- a/src/Umbraco.Web/Cache/WebCachingAppCache.cs +++ b/src/Umbraco.Web/Cache/WebCachingAppCache.cs @@ -89,7 +89,7 @@ namespace Umbraco.Web.Cache protected override void EnterWriteLock() { - _locker.EnterWriteLock();; + _locker.EnterWriteLock(); } protected override void ExitReadLock() diff --git a/src/Umbraco.Web/Editors/DataTypeController.cs b/src/Umbraco.Web/Editors/DataTypeController.cs index 3d205dcaed..835bda19b6 100644 --- a/src/Umbraco.Web/Editors/DataTypeController.cs +++ b/src/Umbraco.Web/Editors/DataTypeController.cs @@ -446,7 +446,7 @@ namespace Umbraco.Web.Editors { var propertyEditor = propertyEditors.SingleOrDefault(x => x.Alias == dataType.Alias); if (propertyEditor != null) - dataType.HasPrevalues = propertyEditor.GetConfigurationEditor().Fields.Any(); ; + dataType.HasPrevalues = propertyEditor.GetConfigurationEditor().Fields.Any(); } var grouped = dataTypes diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index a0ab2de68a..ed37bc9fbd 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -125,8 +125,8 @@ namespace Umbraco.Web.Editors model.LicenseUrl = ins.LicenseUrl; model.Readme = ins.Readme; model.ConflictingMacroAliases = ins.Warnings.ConflictingMacros.ToDictionary(x => x.Name, x => x.Alias); - model.ConflictingStyleSheetNames = ins.Warnings.ConflictingStylesheets.ToDictionary(x => x.Name, x => x.Alias); ; - model.ConflictingTemplateAliases = ins.Warnings.ConflictingTemplates.ToDictionary(x => x.Name, x => x.Alias); ; + model.ConflictingStyleSheetNames = ins.Warnings.ConflictingStylesheets.ToDictionary(x => x.Name, x => x.Alias); + model.ConflictingTemplateAliases = ins.Warnings.ConflictingTemplates.ToDictionary(x => x.Name, x => x.Alias); model.ContainsUnsecureFiles = ins.Warnings.UnsecureFiles.Any(); model.Url = ins.Url; model.Version = ins.Version; diff --git a/src/Umbraco.Web/JavaScript/AssetInitialization.cs b/src/Umbraco.Web/JavaScript/AssetInitialization.cs index 00605ece1f..42c750ffd3 100644 --- a/src/Umbraco.Web/JavaScript/AssetInitialization.cs +++ b/src/Umbraco.Web/JavaScript/AssetInitialization.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Web; using ClientDependency.Core; using ClientDependency.Core.Config; +using Umbraco.Core; using Umbraco.Web.Composing; using Umbraco.Web.PropertyEditors; @@ -35,7 +36,7 @@ namespace Umbraco.Web.JavaScript var requestUrl = httpContext.Request.Url; if (requestUrl == null) throw new ArgumentException("HttpContext.Request.Url is null.", nameof(httpContext)); - var dependencies = assets.Select(x => + var dependencies = assets.Where(x => x.IsNullOrWhiteSpace() == false).Select(x => { // most declarations with be made relative to the /umbraco folder, so things // like lib/blah/blah.js so we need to turn them into absolutes here diff --git a/src/Umbraco.Web/PublishedContentExtensions.cs b/src/Umbraco.Web/PublishedContentExtensions.cs index 0c8f663e38..0e31d46d3c 100644 --- a/src/Umbraco.Web/PublishedContentExtensions.cs +++ b/src/Umbraco.Web/PublishedContentExtensions.cs @@ -32,6 +32,36 @@ namespace Umbraco.Web private static IExamineManager ExamineManager => Current.Factory.GetInstance(); private static IUserService UserService => Current.Services.UserService; + + #region Creator/Writer Names + + public static string CreatorName(this IPublishedContent content, IUserService userService) + { + return userService.GetProfileById(content.CreatorId)?.Name; + } + + public static string WriterName(this IPublishedContent content, IUserService userService) + { + return userService.GetProfileById(content.WriterId)?.Name; + } + + #endregion + + #region IsComposedOf + + /// + /// Gets a value indicating whether the content is of a content type composed of the given alias + /// + /// The content. + /// The content type alias. + /// A value indicating whether the content is of a content type composed of a content type identified by the alias. + public static bool IsComposedOf(this IPublishedContent content, string alias) + { + return content.ContentType.CompositionAliases.InvariantContains(alias); + } + + #endregion + #region Template /// diff --git a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs index afd89f967a..4fac87e226 100644 --- a/src/Umbraco.Web/Trees/ContentTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTypeTreeController.cs @@ -86,15 +86,18 @@ namespace Umbraco.Web.Trees .OrderBy(entity => entity.Name) .Select(dt => { + // get the content type here so we can get the icon from it to use when we create the tree node + // and we can enrich the result with content type data that's not available in the entity service output + var contentType = contentTypes[dt.Id]; + // since 7.4+ child type creation is enabled by a config option. It defaults to on, but can be disabled if we decide to. // need this check to keep supporting sites where children have already been created. var hasChildren = dt.HasChildren; - var node = CreateTreeNode(dt, Constants.ObjectTypes.DocumentType, id, queryStrings, Constants.Icons.ContentType, hasChildren); + var node = CreateTreeNode(dt, Constants.ObjectTypes.DocumentType, id, queryStrings, contentType?.Icon ?? Constants.Icons.ContentType, hasChildren); node.Path = dt.Path; - // enrich the result with content type data that's not available in the entity service output - var contentType = contentTypes[dt.Id]; + // now we can enrich the result with content type data that's not available in the entity service output node.Alias = contentType.Alias; node.AdditionalData["isElement"] = contentType.IsElement; diff --git a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs index d89e93bb49..c366791d2b 100644 --- a/src/Umbraco.Web/Trees/MediaTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTypeTreeController.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; using Umbraco.Core.Models; -using Umbraco.Core.Models.Entities; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi.Filters; using Umbraco.Core.Services; @@ -28,6 +27,7 @@ namespace Umbraco.Web.Trees { private readonly UmbracoTreeSearcher _treeSearcher; private readonly IMenuItemCollectionFactory _menuItemCollectionFactory; + private readonly IMediaTypeService _mediaTypeService; public MediaTypeTreeController( UmbracoTreeSearcher treeSearcher, @@ -41,11 +41,13 @@ namespace Umbraco.Web.Trees UmbracoHelper umbracoHelper, UmbracoMapper umbracoMapper, IPublishedUrlProvider publishedUrlProvider, - IMenuItemCollectionFactory menuItemCollectionFactory) + IMenuItemCollectionFactory menuItemCollectionFactory, + IMediaTypeService mediaTypeService) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, umbracoMapper, publishedUrlProvider) { _treeSearcher = treeSearcher; _menuItemCollectionFactory = menuItemCollectionFactory; + _mediaTypeService = mediaTypeService; } protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) @@ -71,6 +73,8 @@ namespace Umbraco.Web.Trees // if the request is for folders only then just return if (queryStrings["foldersonly"].IsNullOrWhiteSpace() == false && queryStrings["foldersonly"] == "1") return nodes; + var mediaTypes = _mediaTypeService.GetAll(); + nodes.AddRange( Services.EntityService.GetChildren(intId.Result, UmbracoObjectTypes.MediaType) .OrderBy(entity => entity.Name) @@ -79,7 +83,8 @@ namespace Umbraco.Web.Trees // since 7.4+ child type creation is enabled by a config option. It defaults to on, but can be disabled if we decide to. // need this check to keep supporting sites where children have already been created. var hasChildren = dt.HasChildren; - var node = CreateTreeNode(dt, Constants.ObjectTypes.MediaType, id, queryStrings, Constants.Icons.MediaType, hasChildren); + var mt = mediaTypes.FirstOrDefault(x => x.Id == dt.Id); + var node = CreateTreeNode(dt, Constants.ObjectTypes.MediaType, id, queryStrings, mt?.Icon ?? Constants.Icons.MediaType, hasChildren); node.Path = dt.Path; return node; diff --git a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs index 2046baf2d3..5db9088f20 100644 --- a/src/Umbraco.Web/Trees/MemberTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTypeTreeController.cs @@ -33,7 +33,7 @@ namespace Umbraco.Web.Trees { return Services.MemberTypeService.GetAll() .OrderBy(x => x.Name) - .Select(dt => CreateTreeNode(dt, Constants.ObjectTypes.MemberType, id, queryStrings, Constants.Icons.MemberType, false)); + .Select(dt => CreateTreeNode(dt, Constants.ObjectTypes.MemberType, id, queryStrings, dt?.Icon ?? Constants.Icons.MemberType, false)); } public IEnumerable Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null)