Merge remote-tracking branch 'origin/v8/dev' into v8/feature/block-editor-list
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||

|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyCompany("Umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2019")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
protected override void EnterWriteLock()
|
||||
{
|
||||
_locker.EnterWriteLock();;
|
||||
_locker.EnterWriteLock();
|
||||
}
|
||||
|
||||
protected override void ExitReadLock()
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ using Umbraco.Core.Dictionary;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Mapping;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.PackageActions;
|
||||
using Umbraco.Core.Packaging;
|
||||
@@ -208,6 +209,8 @@ namespace Umbraco.Core.Composing
|
||||
public static IVariationContextAccessor VariationContextAccessor
|
||||
=> Factory.GetInstance<IVariationContextAccessor>();
|
||||
|
||||
public static IImageUrlGenerator ImageUrlGenerator
|
||||
=> Factory.GetInstance<IImageUrlGenerator>();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Umbraco.Core
|
||||
|
||||
internal void AddDateTime(DateTime d)
|
||||
{
|
||||
_writer.Write(d.Ticks);;
|
||||
_writer.Write(d.Ticks);
|
||||
}
|
||||
|
||||
internal void AddString(string s)
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
|
||||
if (string.IsNullOrEmpty(dataType.Configuration))
|
||||
{
|
||||
config.Format = "YYYY-MM-DD";
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
public interface IImageUrlGenerator
|
||||
{
|
||||
string GetImageUrl(ImageUrlGenerationOptions options);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
namespace Umbraco.Core.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// These are options that are passed to the IImageUrlGenerator implementation to determine
|
||||
/// the propery URL that is needed
|
||||
/// </summary>
|
||||
public class ImageUrlGenerationOptions
|
||||
{
|
||||
public ImageUrlGenerationOptions (string imageUrl)
|
||||
{
|
||||
ImageUrl = imageUrl;
|
||||
}
|
||||
|
||||
public string ImageUrl { get; }
|
||||
public int? Width { get; set; }
|
||||
public int? Height { get; set; }
|
||||
public decimal? WidthRatio { get; set; }
|
||||
public decimal? HeightRatio { get; set; }
|
||||
public int? Quality { get; set; }
|
||||
public string ImageCropMode { get; set; }
|
||||
public string ImageCropAnchor { get; set; }
|
||||
public bool DefaultCrop { get; set; }
|
||||
public FocalPointPosition FocalPoint { get; set; }
|
||||
public CropCoordinates Crop { get; set; }
|
||||
public string CacheBusterValue { get; set; }
|
||||
public string FurtherOptions { get; set; }
|
||||
public bool UpScale { get; set; } = true;
|
||||
public string AnimationProcessMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The focal point position, in whatever units the registered IImageUrlGenerator uses,
|
||||
/// typically a percentage of the total image from 0.0 to 1.0.
|
||||
/// </summary>
|
||||
public class FocalPointPosition
|
||||
{
|
||||
public FocalPointPosition (decimal top, decimal left)
|
||||
{
|
||||
Left = left;
|
||||
Top = top;
|
||||
}
|
||||
|
||||
public decimal Left { get; }
|
||||
public decimal Top { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The bounds of the crop within the original image, in whatever units the registered
|
||||
/// IImageUrlGenerator uses, typically a percentage between 0 and 100.
|
||||
/// </summary>
|
||||
public class CropCoordinates
|
||||
{
|
||||
public CropCoordinates (decimal x1, decimal y1, decimal x2, decimal y2)
|
||||
{
|
||||
X1 = x1;
|
||||
Y1 = y1;
|
||||
X2 = x2;
|
||||
Y2 = y2;
|
||||
}
|
||||
|
||||
public decimal X1 { get; }
|
||||
public decimal Y1 { get; }
|
||||
public decimal X2 { get; }
|
||||
public decimal Y2 { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <summary>
|
||||
/// Gets the name of the user who created the content item.
|
||||
/// </summary>
|
||||
[Obsolete("Use CreatorName(IUserService) extension instead")]
|
||||
string CreatorName { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -78,6 +79,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <summary>
|
||||
/// Gets the name of the user who last updated the content item.
|
||||
/// </summary>
|
||||
[Obsolete("Use WriterName(IUserService) extension instead")]
|
||||
string WriterName { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -97,6 +99,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
/// <para>The value of this property is contextual. It depends on the 'current' request uri,
|
||||
/// if any.</para>
|
||||
/// </remarks>
|
||||
[Obsolete("Use the Url() extension instead")]
|
||||
string Url { get; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
public virtual DateTime UpdateDate => _content.UpdateDate;
|
||||
|
||||
/// <inheritdoc />
|
||||
[Obsolete("Use the Url() extension instead")]
|
||||
public virtual string Url => _content.Url;
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -106,13 +106,14 @@ namespace Umbraco.Core.Models
|
||||
|
||||
//use the custom avatar
|
||||
var avatarUrl = Current.MediaFileSystem.GetUrl(user.Avatar);
|
||||
var urlGenerator = Current.ImageUrlGenerator;
|
||||
return new[]
|
||||
{
|
||||
avatarUrl + "?width=30&height=30&mode=crop",
|
||||
avatarUrl + "?width=60&height=60&mode=crop",
|
||||
avatarUrl + "?width=90&height=90&mode=crop",
|
||||
avatarUrl + "?width=150&height=150&mode=crop",
|
||||
avatarUrl + "?width=300&height=300&mode=crop"
|
||||
urlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = "crop", Width = 30, Height = 30 }),
|
||||
urlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = "crop", Width = 60, Height = 60 }),
|
||||
urlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = "crop", Width = 90, Height = 90 }),
|
||||
urlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = "crop", Width = 150, Height = 150 }),
|
||||
urlGenerator.GetImageUrl(new ImageUrlGenerationOptions(avatarUrl) { ImageCropMode = "crop", Width = 300, Height = 300 })
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal class ConflictingPackageData
|
||||
internal 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");
|
||||
|
||||
|
||||
@@ -82,7 +82,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);
|
||||
@@ -139,7 +139,7 @@ namespace Umbraco.Core.Packaging
|
||||
var updatedXml = _parser.ToXml(definition);
|
||||
packageXml.ReplaceWith(updatedXml);
|
||||
}
|
||||
|
||||
|
||||
packagesXml.Save(packagesFile);
|
||||
|
||||
return true;
|
||||
@@ -212,7 +212,7 @@ namespace Umbraco.Core.Packaging
|
||||
compiledPackageXml.Save(packageXmlFileName);
|
||||
|
||||
// check if there's a packages directory below media
|
||||
|
||||
|
||||
if (Directory.Exists(IOHelper.MapPath(_mediaFolderPath)) == false)
|
||||
Directory.CreateDirectory(IOHelper.MapPath(_mediaFolderPath));
|
||||
|
||||
@@ -510,7 +510,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");
|
||||
@@ -562,7 +561,7 @@ namespace Umbraco.Core.Packaging
|
||||
package.Add(new XElement("url", definition.Url));
|
||||
|
||||
var requirements = new XElement("requirements");
|
||||
|
||||
|
||||
requirements.Add(new XElement("major", definition.UmbracoVersion == null ? UmbracoVersion.SemanticVersion.Major.ToInvariantString() : definition.UmbracoVersion.Major.ToInvariantString()));
|
||||
requirements.Add(new XElement("minor", definition.UmbracoVersion == null ? UmbracoVersion.SemanticVersion.Minor.ToInvariantString() : definition.UmbracoVersion.Minor.ToInvariantString()));
|
||||
requirements.Add(new XElement("patch", definition.UmbracoVersion == null ? UmbracoVersion.SemanticVersion.Patch.ToInvariantString() : definition.UmbracoVersion.Build.ToInvariantString()));
|
||||
@@ -589,7 +588,7 @@ namespace Umbraco.Core.Packaging
|
||||
contributors.Add(new XElement("contributor", contributor));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
info.Add(contributors);
|
||||
|
||||
info.Add(new XElement("readme", new XCData(definition.Readme)));
|
||||
|
||||
@@ -27,6 +27,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// <returns></returns>
|
||||
bool HasContainerInPath(string contentPath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there is a list view content item in the path.
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <returns></returns>
|
||||
bool HasContainerInPath(params int[] ids);
|
||||
|
||||
/// <summary>
|
||||
/// Returns true or false depending on whether content nodes have been created based on the provided content type id.
|
||||
/// </summary>
|
||||
|
||||
@@ -1309,14 +1309,16 @@ WHERE cmsContentType." + aliasColumn + @" LIKE @pattern",
|
||||
return test;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given the path of a content item, this will return true if the content item exists underneath a list view content item
|
||||
/// </summary>
|
||||
/// <param name="contentPath"></param>
|
||||
/// <returns></returns>
|
||||
/// <inheritdoc />
|
||||
public bool HasContainerInPath(string contentPath)
|
||||
{
|
||||
var ids = contentPath.Split(',').Select(int.Parse);
|
||||
var ids = contentPath.Split(',').Select(int.Parse).ToArray();
|
||||
return HasContainerInPath(ids);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool HasContainerInPath(params int[] ids)
|
||||
{
|
||||
var sql = new Sql($@"SELECT COUNT(*) FROM cmsContentType
|
||||
INNER JOIN {Constants.DatabaseSchema.Tables.Content} ON cmsContentType.nodeId={Constants.DatabaseSchema.Tables.Content}.contentTypeId
|
||||
WHERE {Constants.DatabaseSchema.Tables.Content}.nodeId IN (@ids) AND cmsContentType.isContainer=@isContainer", new { ids, isContainer = true });
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
if (macroDto == null)
|
||||
return null;
|
||||
|
||||
|
||||
var entity = MacroFactory.BuildEntity(macroDto);
|
||||
|
||||
// reset dirty initial properties (U4-1946)
|
||||
@@ -153,7 +153,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
protected override void PersistUpdatedItem(IMacro entity)
|
||||
{
|
||||
entity.UpdatingEntity();
|
||||
;
|
||||
var dto = MacroFactory.BuildDto(entity);
|
||||
|
||||
Database.Update(dto);
|
||||
|
||||
@@ -215,7 +215,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);
|
||||
|
||||
@@ -13,35 +13,46 @@ namespace Umbraco.Core.PropertyEditors
|
||||
|
||||
public IEnumerable<UmbracoEntityReference> GetAllReferences(PropertyCollection properties, PropertyEditorCollection propertyEditors)
|
||||
{
|
||||
var trackedRelations = new List<UmbracoEntityReference>();
|
||||
var trackedRelations = new HashSet<UmbracoEntityReference>();
|
||||
|
||||
foreach (var p in properties)
|
||||
{
|
||||
if (!propertyEditors.TryGet(p.PropertyType.PropertyEditorAlias, out var editor)) continue;
|
||||
|
||||
//TODO: Support variants/segments! This is not required for this initial prototype which is why there is a check here
|
||||
if (!p.PropertyType.VariesByNothing()) continue;
|
||||
var val = p.GetValue(); // get the invariant value
|
||||
//TODO: We will need to change this once we support tracking via variants/segments
|
||||
// for now, we are tracking values from ALL variants
|
||||
|
||||
var valueEditor = editor.GetValueEditor();
|
||||
if (valueEditor is IDataValueReference reference)
|
||||
foreach(var propertyVal in p.Values)
|
||||
{
|
||||
var refs = reference.GetReferences(val);
|
||||
trackedRelations.AddRange(refs);
|
||||
}
|
||||
var val = propertyVal.EditedValue;
|
||||
|
||||
// Loop over collection that may be add to existing property editors
|
||||
// implementation of GetReferences in IDataValueReference.
|
||||
// Allows developers to add support for references by a
|
||||
// package /property editor that did not implement IDataValueReference themselves
|
||||
foreach (var item in this)
|
||||
{
|
||||
// Check if this value reference is for this datatype/editor
|
||||
// Then call it's GetReferences method - to see if the value stored
|
||||
// in the dataeditor/property has referecnes to media/content items
|
||||
if (item.IsForEditor(editor))
|
||||
trackedRelations.AddRange(item.GetDataValueReference().GetReferences(val));
|
||||
var valueEditor = editor.GetValueEditor();
|
||||
if (valueEditor is IDataValueReference reference)
|
||||
{
|
||||
var refs = reference.GetReferences(val);
|
||||
foreach(var r in refs)
|
||||
trackedRelations.Add(r);
|
||||
}
|
||||
|
||||
// Loop over collection that may be add to existing property editors
|
||||
// implementation of GetReferences in IDataValueReference.
|
||||
// Allows developers to add support for references by a
|
||||
// package /property editor that did not implement IDataValueReference themselves
|
||||
foreach (var item in this)
|
||||
{
|
||||
// Check if this value reference is for this datatype/editor
|
||||
// Then call it's GetReferences method - to see if the value stored
|
||||
// in the dataeditor/property has referecnes to media/content items
|
||||
if (item.IsForEditor(editor))
|
||||
{
|
||||
foreach(var r in item.GetDataValueReference().GetReferences(val))
|
||||
trackedRelations.Add(r);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return trackedRelations;
|
||||
|
||||
@@ -7,6 +7,8 @@ using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
@@ -59,38 +61,34 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
: Crops.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
}
|
||||
|
||||
internal void AppendCropBaseUrl(StringBuilder url, ImageCropperCrop crop, bool defaultCrop, bool preferFocalPoint)
|
||||
internal ImageUrlGenerationOptions GetCropBaseOptions(string url, ImageCropperCrop crop, bool defaultCrop, bool preferFocalPoint)
|
||||
{
|
||||
if (preferFocalPoint && HasFocalPoint()
|
||||
|| crop != null && crop.Coordinates == null && HasFocalPoint()
|
||||
|| defaultCrop && HasFocalPoint())
|
||||
{
|
||||
url.Append("?center=");
|
||||
url.Append(FocalPoint.Top.ToString(CultureInfo.InvariantCulture));
|
||||
url.Append(",");
|
||||
url.Append(FocalPoint.Left.ToString(CultureInfo.InvariantCulture));
|
||||
url.Append("&mode=crop");
|
||||
return new ImageUrlGenerationOptions(url) { FocalPoint = new ImageUrlGenerationOptions.FocalPointPosition(FocalPoint.Top, FocalPoint.Left) };
|
||||
}
|
||||
else if (crop != null && crop.Coordinates != null && preferFocalPoint == false)
|
||||
{
|
||||
url.Append("?crop=");
|
||||
url.Append(crop.Coordinates.X1.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
url.Append(crop.Coordinates.Y1.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
url.Append(crop.Coordinates.X2.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
url.Append(crop.Coordinates.Y2.ToString(CultureInfo.InvariantCulture));
|
||||
url.Append("&cropmode=percentage");
|
||||
return new ImageUrlGenerationOptions(url) { Crop = new ImageUrlGenerationOptions.CropCoordinates(crop.Coordinates.X1, crop.Coordinates.Y1, crop.Coordinates.X2, crop.Coordinates.Y2) };
|
||||
}
|
||||
else
|
||||
{
|
||||
url.Append("?anchor=center");
|
||||
url.Append("&mode=crop");
|
||||
return new ImageUrlGenerationOptions(url) { DefaultCrop = true };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value image url for a specified crop.
|
||||
/// </summary>
|
||||
public string GetCropUrl(string alias, bool useCropDimensions = true, bool useFocalPoint = false, string cacheBusterValue = null)
|
||||
[Obsolete("Use the overload that takes an IImageUrlGenerator")]
|
||||
public string GetCropUrl(string alias, bool useCropDimensions = true, bool useFocalPoint = false, string cacheBusterValue = null) => GetCropUrl(alias, Current.ImageUrlGenerator, useCropDimensions, useFocalPoint, cacheBusterValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value image url for a specified crop.
|
||||
/// </summary>
|
||||
public string GetCropUrl(string alias, IImageUrlGenerator imageUrlGenerator, bool useCropDimensions = true, bool useFocalPoint = false, string cacheBusterValue = null)
|
||||
{
|
||||
var crop = GetCrop(alias);
|
||||
|
||||
@@ -98,38 +96,37 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
if (crop == null && !string.IsNullOrWhiteSpace(alias))
|
||||
return null;
|
||||
|
||||
var url = new StringBuilder();
|
||||
|
||||
AppendCropBaseUrl(url, crop, string.IsNullOrWhiteSpace(alias), useFocalPoint);
|
||||
var options = GetCropBaseOptions(string.Empty, crop, string.IsNullOrWhiteSpace(alias), useFocalPoint);
|
||||
|
||||
if (crop != null && useCropDimensions)
|
||||
{
|
||||
url.Append("&width=").Append(crop.Width);
|
||||
url.Append("&height=").Append(crop.Height);
|
||||
options.Width = crop.Width;
|
||||
options.Height = crop.Height;
|
||||
}
|
||||
|
||||
if (cacheBusterValue != null)
|
||||
url.Append("&rnd=").Append(cacheBusterValue);
|
||||
options.CacheBusterValue = cacheBusterValue;
|
||||
|
||||
return url.ToString();
|
||||
return imageUrlGenerator.GetImageUrl(options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value image url for a specific width and height.
|
||||
/// </summary>
|
||||
public string GetCropUrl(int width, int height, bool useFocalPoint = false, string cacheBusterValue = null)
|
||||
[Obsolete("Use the overload that takes an IImageUrlGenerator")]
|
||||
public string GetCropUrl(int width, int height, bool useFocalPoint = false, string cacheBusterValue = null) => GetCropUrl(width, height, Current.ImageUrlGenerator, useFocalPoint, cacheBusterValue);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value image url for a specific width and height.
|
||||
/// </summary>
|
||||
public string GetCropUrl(int width, int height, IImageUrlGenerator imageUrlGenerator, bool useFocalPoint = false, string cacheBusterValue = null)
|
||||
{
|
||||
var url = new StringBuilder();
|
||||
var options = GetCropBaseOptions(string.Empty, null, true, useFocalPoint);
|
||||
|
||||
AppendCropBaseUrl(url, null, true, useFocalPoint);
|
||||
options.Width = width;
|
||||
options.Height = height;
|
||||
options.CacheBusterValue = cacheBusterValue;
|
||||
|
||||
url.Append("&width=").Append(width);
|
||||
url.Append("&height=").Append(height);
|
||||
|
||||
if (cacheBusterValue != null)
|
||||
url.Append("&rnd=").Append(cacheBusterValue);
|
||||
|
||||
return url.ToString();
|
||||
return imageUrlGenerator.GetImageUrl(options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
value = new ImageCropperValue { Src = sourceString };
|
||||
}
|
||||
|
||||
value.ApplyConfiguration(propertyType.DataType.ConfigurationAs<ImageCropperConfiguration>());
|
||||
value?.ApplyConfiguration(propertyType.DataType.ConfigurationAs<ImageCropperConfiguration>());
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,10 @@ namespace Umbraco.Core.Services
|
||||
IEnumerable<TItem> GetComposedOf(int id); // composition axis
|
||||
|
||||
IEnumerable<TItem> GetChildren(int id);
|
||||
IEnumerable<TItem> GetChildren(Guid id);
|
||||
|
||||
bool HasChildren(int id);
|
||||
bool HasChildren(Guid id);
|
||||
|
||||
void Save(TItem item, int userId = Constants.Security.SuperUserId);
|
||||
void Save(IEnumerable<TItem> items, int userId = Constants.Security.SuperUserId);
|
||||
@@ -69,6 +72,13 @@ namespace Umbraco.Core.Services
|
||||
/// <returns></returns>
|
||||
bool HasContainerInPath(string contentPath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether there is a list view content item in the path.
|
||||
/// </summary>
|
||||
/// <param name="ids"></param>
|
||||
/// <returns></returns>
|
||||
bool HasContainerInPath(params int[] ids);
|
||||
|
||||
Attempt<OperationResult<OperationResultType, EntityContainer>> CreateContainer(int parentContainerId, string name, int userId = Constants.Security.SuperUserId);
|
||||
Attempt<OperationResult> SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId);
|
||||
EntityContainer GetContainer(int containerId);
|
||||
|
||||
+9
@@ -321,6 +321,15 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasContainerInPath(params int[] ids)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
// can use same repo for both content and media
|
||||
return Repository.HasContainerInPath(ids);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TItem> GetDescendants(int id, bool andSelf)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
|
||||
@@ -143,6 +143,8 @@
|
||||
<Compile Include="Models\UpgradeResult.cs" />
|
||||
<Compile Include="Persistence\Repositories\Implement\UpgradeCheckRepository.cs" />
|
||||
<Compile Include="Persistence\Repositories\IUpgradeCheckRepository.cs" />
|
||||
<Compile Include="Models\IImageUrlGenerator.cs" />
|
||||
<Compile Include="Models\ImageUrlGenerationOptions.cs" />
|
||||
<Compile Include="Runtime\IMainDomLock.cs" />
|
||||
<Compile Include="Runtime\MainDomSemaphoreLock.cs" />
|
||||
<Compile Include="Runtime\SqlMainDomLock.cs" />
|
||||
|
||||
@@ -214,9 +214,9 @@ namespace Umbraco.Core.Xml
|
||||
var xmlDoc = new XmlDocument();
|
||||
//Load the file into the XmlDocument
|
||||
xmlDoc.Load(reader);
|
||||
|
||||
|
||||
return xmlDoc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -335,7 +335,7 @@ namespace Umbraco.Core.Xml
|
||||
var child = parent.SelectSingleNode(name);
|
||||
if (child != null)
|
||||
{
|
||||
child.InnerXml = "<![CDATA[" + value + "]]>"; ;
|
||||
child.InnerXml = "<![CDATA[" + value + "]]>";
|
||||
return child;
|
||||
}
|
||||
return AddCDataNode(xd, name, value);
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Umbraco.TestData
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
|
||||
|
||||
return Content("Done");
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Umbraco.TestData
|
||||
message = "Count not high enough for specified for number of levels required";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace Umbraco.TestData
|
||||
currChildCount = prev.childCount;
|
||||
// restore the parent
|
||||
parent = prev.parent;
|
||||
|
||||
|
||||
}
|
||||
else if (contentItem.Level < depth)
|
||||
{
|
||||
@@ -149,10 +149,10 @@ namespace Umbraco.TestData
|
||||
|
||||
// not at max depth, create below
|
||||
parent = created.container();
|
||||
|
||||
|
||||
currChildCount = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +219,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);
|
||||
@@ -259,7 +259,7 @@ namespace Umbraco.TestData
|
||||
return docType;
|
||||
}
|
||||
|
||||
private IDataType GetOrCreateRichText() => GetOrCreateDataType(RichTextDataTypeName, Constants.PropertyEditors.Aliases.TinyMce);
|
||||
private IDataType GetOrCreateRichText() => GetOrCreateDataType(RichTextDataTypeName, Constants.PropertyEditors.Aliases.TinyMce);
|
||||
|
||||
private IDataType GetOrCreateMediaPicker() => GetOrCreateDataType(MediaPickerDataTypeName, Constants.PropertyEditors.Aliases.MediaPicker);
|
||||
|
||||
|
||||
@@ -354,7 +354,7 @@ namespace Umbraco.Tests.Composing
|
||||
var col2 = factory.GetInstance<TestCollection>();
|
||||
AssertCollection(col2, typeof(Resolved1), typeof(Resolved2));
|
||||
|
||||
AssertSameCollection(col1, col2);
|
||||
AssertSameCollection(factory, col1, col2);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -413,11 +413,11 @@ namespace Umbraco.Tests.Composing
|
||||
{
|
||||
col1A = factory.GetInstance<TestCollection>();
|
||||
col1B = factory.GetInstance<TestCollection>();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -452,7 +452,7 @@ namespace Umbraco.Tests.Composing
|
||||
Assert.IsInstanceOf(expected[i], colA[i]);
|
||||
}
|
||||
|
||||
private static void AssertSameCollection(IEnumerable<Resolved> col1, IEnumerable<Resolved> col2)
|
||||
private static void AssertSameCollection(IFactory factory, IEnumerable<Resolved> col1, IEnumerable<Resolved> col2)
|
||||
{
|
||||
Assert.AreSame(col1, col2);
|
||||
|
||||
@@ -460,8 +460,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<Resolved> col1, IEnumerable<Resolved> col2)
|
||||
@@ -472,8 +483,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
|
||||
|
||||
@@ -163,7 +163,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Moq;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.PropertyEditors.ValueConverters;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.Components;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Tests.Models
|
||||
{
|
||||
[TestFixture]
|
||||
public class ImageProcessorImageUrlGeneratorTest
|
||||
{
|
||||
private const string MediaPath = "/media/1005/img_0671.jpg";
|
||||
private static readonly ImageUrlGenerationOptions.CropCoordinates Crop = new ImageUrlGenerationOptions.CropCoordinates(0.58729977382575338m, 0.055768992440203169m, 0m, 0.32457553600198386m);
|
||||
private static readonly ImageUrlGenerationOptions.FocalPointPosition Focus1 = new ImageUrlGenerationOptions.FocalPointPosition(0.80827067669172936m, 0.96m);
|
||||
private static readonly ImageUrlGenerationOptions.FocalPointPosition Focus2 = new ImageUrlGenerationOptions.FocalPointPosition(0.41m, 0.4275m);
|
||||
private static readonly ImageProcessorImageUrlGenerator Generator = new ImageProcessorImageUrlGenerator();
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_CropAliasTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = Crop, Width = 100, Height = 100 });
|
||||
Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_WidthHeightTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus1, Width = 200, Height = 300 });
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_FocalPointTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus1, Width = 100, Height = 100 });
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=100&height=100", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrlFurtherOptionsTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus1, Width = 200, Height = 300, FurtherOptions = "&filter=comic&roundedcorners=radius-26|bgcolor-fff" });
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that if a crop alias has been specified that doesn't exist the method returns null
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrlNullTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(null);
|
||||
Assert.AreEqual(null, urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that if a crop alias has been specified that doesn't exist the method returns null
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrlEmptyTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(null));
|
||||
Assert.AreEqual("?mode=crop", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test the GetCropUrl method on the ImageCropDataSet Model
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetBaseCropUrlFromModelTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(null) { Crop = Crop, Width = 100, Height = 100 });
|
||||
Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test the height ratio mode with predefined crop dimensions
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_CropAliasHeightRatioModeTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { Crop = Crop, Width = 100, HeightRatio = 1 });
|
||||
Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&heightratio=1&width=100", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test the height ratio mode with manual width/height dimensions
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_WidthHeightRatioModeTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus1, Width = 300, HeightRatio = 0.5m });
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&heightratio=0.5&width=300", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test the height ratio mode with width/height dimensions
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_HeightWidthRatioModeTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus1, Height = 150, WidthRatio = 2 });
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&widthratio=2&height=150", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that if Crop mode is specified as anything other than Crop the image doesn't use the crop
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_SpecifiedCropModeTest()
|
||||
{
|
||||
var urlStringMin = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Min", Width = 300, Height = 150 });
|
||||
var urlStringBoxPad = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "BoxPad", Width = 300, Height = 150 });
|
||||
var urlStringPad = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Pad", Width = 300, Height = 150 });
|
||||
var urlStringMax = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Max", Width = 300, Height = 150 });
|
||||
var urlStringStretch = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Stretch", Width = 300, Height = 150 });
|
||||
|
||||
Assert.AreEqual(MediaPath + "?mode=min&width=300&height=150", urlStringMin);
|
||||
Assert.AreEqual(MediaPath + "?mode=boxpad&width=300&height=150", urlStringBoxPad);
|
||||
Assert.AreEqual(MediaPath + "?mode=pad&width=300&height=150", urlStringPad);
|
||||
Assert.AreEqual(MediaPath + "?mode=max&width=300&height=150", urlStringMax);
|
||||
Assert.AreEqual(MediaPath + "?mode=stretch&width=300&height=150", urlStringStretch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test for upload property type
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_UploadTypeTest()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Crop", ImageCropAnchor = "Center", Width = 100, Height = 270 });
|
||||
Assert.AreEqual(MediaPath + "?mode=crop&anchor=center&width=100&height=270", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test for preferFocalPoint when focal point is centered
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_PreferFocalPointCenter()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 300, Height = 150 });
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=300&height=150", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to check if height ratio is returned for a predefined crop without coordinates and focal point in centre when a width parameter is passed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidth()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200, HeightRatio = 0.5962962962962962962962962963m });
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to check if height ratio is returned for a predefined crop without coordinates and focal point is custom when a width parameter is passed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPoint()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus2, Width = 200, HeightRatio = 0.5962962962962962962962962963m });
|
||||
Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to check if crop ratio is ignored if useCropDimensions is true
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_PreDefinedCropNoCoordinatesWithWidthAndFocalPointIgnore()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { FocalPoint = Focus2, Width = 270, Height = 161 });
|
||||
Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&width=270&height=161", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to check if width ratio is returned for a predefined crop without coordinates and focal point in centre when a height parameter is passed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_PreDefinedCropNoCoordinatesWithHeight()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200, WidthRatio = 1.6770186335403726708074534161m });
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&widthratio=1.6770186335403726708074534161&height=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to check result when only a width parameter is passed, effectivly a resize only
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_WidthOnlyParameter()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Width = 200 });
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to check result when only a height parameter is passed, effectivly a resize only
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_HeightOnlyParameter()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { DefaultCrop = true, Height = 200 });
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&height=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test to check result when using a background color with padding
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void GetCropUrl_BackgroundColorParameter()
|
||||
{
|
||||
var urlString = Generator.GetImageUrl(new ImageUrlGenerationOptions(MediaPath) { ImageCropMode = "Pad", Width = 400, Height = 400, FurtherOptions = "&bgcolor=fff" });
|
||||
Assert.AreEqual(MediaPath + "?mode=pad&width=400&height=400&bgcolor=fff", urlString);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,6 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
var repository = new MacroRepository((IScopeAccessor) provider, AppCaches.Disabled, Mock.Of<ILogger>());
|
||||
|
||||
var macro = new Macro("test1", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView);
|
||||
;
|
||||
|
||||
Assert.Throws<SqlCeException>(() => repository.Save(macro));
|
||||
}
|
||||
@@ -56,7 +55,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
|
||||
var macro = repository.Get(1);
|
||||
macro.Alias = "test2";
|
||||
|
||||
|
||||
Assert.Throws<SqlCeException>(() => repository.Save(macro));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using static Umbraco.Core.Models.Property;
|
||||
|
||||
namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
[TestFixture]
|
||||
public class DataValueReferenceFactoryCollectionTests
|
||||
{
|
||||
[Test]
|
||||
public void GetAllReferences_All_Variants_With_IDataValueReferenceFactory()
|
||||
{
|
||||
var collection = new DataValueReferenceFactoryCollection(new TestDataValueReferenceFactory().Yield());
|
||||
|
||||
// label does not implement IDataValueReference
|
||||
var labelEditor = new LabelPropertyEditor(Mock.Of<ILogger>());
|
||||
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();
|
||||
var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var property = new Property(new PropertyType(new DataType(labelEditor))
|
||||
{
|
||||
Variations = ContentVariation.CultureAndSegment
|
||||
})
|
||||
{
|
||||
Values = new List<PropertyValue>
|
||||
{
|
||||
// Ignored (no culture)
|
||||
new PropertyValue
|
||||
{
|
||||
EditedValue = trackedUdi1
|
||||
},
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
EditedValue = trackedUdi2
|
||||
},
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi3
|
||||
},
|
||||
// Ignored (no culture)
|
||||
new PropertyValue
|
||||
{
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi4
|
||||
},
|
||||
// duplicate
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
Segment = "B",
|
||||
EditedValue = trackedUdi3
|
||||
}
|
||||
}
|
||||
};
|
||||
var properties = new PropertyCollection
|
||||
{
|
||||
property
|
||||
};
|
||||
var result = collection.GetAllReferences(properties, propertyEditors);
|
||||
|
||||
Assert.AreEqual(2, result.Count());
|
||||
Assert.AreEqual(trackedUdi2, result.ElementAt(0).Udi.ToString());
|
||||
Assert.AreEqual(trackedUdi3, result.ElementAt(1).Udi.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAllReferences_All_Variants_With_IDataValueReference_Editor()
|
||||
{
|
||||
var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
|
||||
// mediaPicker does implement IDataValueReference
|
||||
var mediaPicker = new MediaPickerPropertyEditor(Mock.Of<ILogger>());
|
||||
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();
|
||||
var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var property = new Property(new PropertyType(new DataType(mediaPicker))
|
||||
{
|
||||
Variations = ContentVariation.CultureAndSegment
|
||||
})
|
||||
{
|
||||
Values = new List<PropertyValue>
|
||||
{
|
||||
// Ignored (no culture)
|
||||
new PropertyValue
|
||||
{
|
||||
EditedValue = trackedUdi1
|
||||
},
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
EditedValue = trackedUdi2
|
||||
},
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi3
|
||||
},
|
||||
// Ignored (no culture)
|
||||
new PropertyValue
|
||||
{
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi4
|
||||
},
|
||||
// duplicate
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
Segment = "B",
|
||||
EditedValue = trackedUdi3
|
||||
}
|
||||
}
|
||||
};
|
||||
var properties = new PropertyCollection
|
||||
{
|
||||
property
|
||||
};
|
||||
var result = collection.GetAllReferences(properties, propertyEditors);
|
||||
|
||||
Assert.AreEqual(2, result.Count());
|
||||
Assert.AreEqual(trackedUdi2, result.ElementAt(0).Udi.ToString());
|
||||
Assert.AreEqual(trackedUdi3, result.ElementAt(1).Udi.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetAllReferences_Invariant_With_IDataValueReference_Editor()
|
||||
{
|
||||
var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty<IDataValueReferenceFactory>());
|
||||
|
||||
// mediaPicker does implement IDataValueReference
|
||||
var mediaPicker = new MediaPickerPropertyEditor(Mock.Of<ILogger>());
|
||||
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();
|
||||
var trackedUdi4 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString();
|
||||
var property = new Property(new PropertyType(new DataType(mediaPicker))
|
||||
{
|
||||
Variations = ContentVariation.Nothing | ContentVariation.Segment
|
||||
})
|
||||
{
|
||||
Values = new List<PropertyValue>
|
||||
{
|
||||
new PropertyValue
|
||||
{
|
||||
EditedValue = trackedUdi1
|
||||
},
|
||||
// Ignored (has culture)
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
EditedValue = trackedUdi2
|
||||
},
|
||||
// Ignored (has culture)
|
||||
new PropertyValue
|
||||
{
|
||||
Culture = "en-US",
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi3
|
||||
},
|
||||
new PropertyValue
|
||||
{
|
||||
Segment = "A",
|
||||
EditedValue = trackedUdi4
|
||||
},
|
||||
// duplicate
|
||||
new PropertyValue
|
||||
{
|
||||
Segment = "B",
|
||||
EditedValue = trackedUdi4
|
||||
}
|
||||
}
|
||||
};
|
||||
var properties = new PropertyCollection
|
||||
{
|
||||
property
|
||||
};
|
||||
var result = collection.GetAllReferences(properties, propertyEditors);
|
||||
|
||||
Assert.AreEqual(2, result.Count());
|
||||
Assert.AreEqual(trackedUdi1, result.ElementAt(0).Udi.ToString());
|
||||
Assert.AreEqual(trackedUdi4, result.ElementAt(1).Udi.ToString());
|
||||
}
|
||||
|
||||
private class TestDataValueReferenceFactory : IDataValueReferenceFactory
|
||||
{
|
||||
public IDataValueReference GetDataValueReference() => new TestMediaDataValueReference();
|
||||
|
||||
public bool IsForEditor(IDataEditor dataEditor) => dataEditor.Alias == Constants.PropertyEditors.Aliases.Label;
|
||||
|
||||
private class TestMediaDataValueReference : IDataValueReference
|
||||
{
|
||||
public IEnumerable<UmbracoEntityReference> GetReferences(object value)
|
||||
{
|
||||
// This is the same as the media picker, it will just try to parse the value directly as a UDI
|
||||
|
||||
var asString = value is string str ? str : value?.ToString();
|
||||
|
||||
if (string.IsNullOrEmpty(asString)) yield break;
|
||||
|
||||
if (Udi.TryParse(asString, out var udi))
|
||||
yield return new UmbracoEntityReference(udi);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,7 @@ using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -21,6 +19,7 @@ using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
@@ -110,8 +109,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
[Test]
|
||||
public void GetCropUrl_CropAliasTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true);
|
||||
Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true);
|
||||
Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -120,29 +119,29 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
[Test]
|
||||
public void GetCropUrl_CropAliasIgnoreWidthHeightTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, width: 50, height: 50);
|
||||
Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, width: 50, height: 50);
|
||||
Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_WidthHeightTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 200, height: 300);
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=200&h=300", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrl_FocalPointTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, cropAlias: "thumb", preferFocalPoint: true, useCropDimensions: true);
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=100&height=100", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "thumb", preferFocalPoint: true, useCropDimensions: true);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=100&h=100", urlString);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetCropUrlFurtherOptionsTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "&filter=comic&roundedcorners=radius-26|bgcolor-fff");
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=200&height=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 200, height: 300, furtherOptions: "&filter=comic&roundedcorners=radius-26|bgcolor-fff");
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&w=200&h=300&filter=comic&roundedcorners=radius-26|bgcolor-fff", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -151,7 +150,7 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
[Test]
|
||||
public void GetCropUrlNullTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, cropAlias: "Banner", useCropDimensions: true);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Banner", useCropDimensions: true);
|
||||
Assert.AreEqual(null, urlString);
|
||||
}
|
||||
|
||||
@@ -162,8 +161,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
public void GetBaseCropUrlFromModelTest()
|
||||
{
|
||||
var cropDataSet = CropperJson1.DeserializeImageCropperValue();
|
||||
var urlString = cropDataSet.GetCropUrl("thumb");
|
||||
Assert.AreEqual("?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&height=100", urlString);
|
||||
var urlString = cropDataSet.GetCropUrl("thumb", new TestImageUrlGenerator());
|
||||
Assert.AreEqual("?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&w=100&h=100", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -172,8 +171,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
[Test]
|
||||
public void GetCropUrl_CropAliasHeightRatioModeTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, ratioMode:ImageCropRatioMode.Height);
|
||||
Assert.AreEqual(MediaPath + "?crop=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&cropmode=percentage&width=100&heightratio=1", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, cropAlias: "Thumb", useCropDimensions: true, ratioMode:ImageCropRatioMode.Height);
|
||||
Assert.AreEqual(MediaPath + "?c=0.58729977382575338,0.055768992440203169,0,0.32457553600198386&hr=1&w=100", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -182,8 +181,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
[Test]
|
||||
public void GetCropUrl_WidthHeightRatioModeTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode:ImageCropRatioMode.Height);
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&width=300&heightratio=0.5", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode:ImageCropRatioMode.Height);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&hr=0.5&w=300", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -192,8 +191,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
[Test]
|
||||
public void GetCropUrl_HeightWidthRatioModeTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Width);
|
||||
Assert.AreEqual(MediaPath + "?center=0.80827067669172936,0.96&mode=crop&height=150&widthratio=2", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, ratioMode: ImageCropRatioMode.Width);
|
||||
Assert.AreEqual(MediaPath + "?f=0.80827067669172936x0.96&wr=2&h=150", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -202,17 +201,17 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
[Test]
|
||||
public void GetCropUrl_SpecifiedCropModeTest()
|
||||
{
|
||||
var urlStringMin = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Min);
|
||||
var urlStringBoxPad = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.BoxPad);
|
||||
var urlStringPad = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Pad);
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode:ImageCropMode.Max);
|
||||
var urlStringStretch = MediaPath.GetCropUrl(imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Stretch);
|
||||
var urlStringMin = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Min);
|
||||
var urlStringBoxPad = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.BoxPad);
|
||||
var urlStringPad = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Pad);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode:ImageCropMode.Max);
|
||||
var urlStringStretch = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: CropperJson1, width: 300, height: 150, imageCropMode: ImageCropMode.Stretch);
|
||||
|
||||
Assert.AreEqual(MediaPath + "?mode=min&width=300&height=150", urlStringMin);
|
||||
Assert.AreEqual(MediaPath + "?mode=boxpad&width=300&height=150", urlStringBoxPad);
|
||||
Assert.AreEqual(MediaPath + "?mode=pad&width=300&height=150", urlStringPad);
|
||||
Assert.AreEqual(MediaPath + "?mode=max&width=300&height=150", urlString);
|
||||
Assert.AreEqual(MediaPath + "?mode=stretch&width=300&height=150", urlStringStretch);
|
||||
Assert.AreEqual(MediaPath + "?m=min&w=300&h=150", urlStringMin);
|
||||
Assert.AreEqual(MediaPath + "?m=boxpad&w=300&h=150", urlStringBoxPad);
|
||||
Assert.AreEqual(MediaPath + "?m=pad&w=300&h=150", urlStringPad);
|
||||
Assert.AreEqual(MediaPath + "?m=max&w=300&h=150", urlString);
|
||||
Assert.AreEqual(MediaPath + "?m=stretch&w=300&h=150", urlStringStretch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -221,8 +220,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
[Test]
|
||||
public void GetCropUrl_UploadTypeTest()
|
||||
{
|
||||
var urlString = MediaPath.GetCropUrl(width: 100, height: 270, imageCropMode: ImageCropMode.Crop, imageCropAnchor: ImageCropAnchor.Center);
|
||||
Assert.AreEqual(MediaPath + "?mode=crop&anchor=center&width=100&height=270", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), width: 100, height: 270, imageCropMode: ImageCropMode.Crop, imageCropAnchor: ImageCropAnchor.Center);
|
||||
Assert.AreEqual(MediaPath + "?m=crop&a=center&w=100&h=270", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -233,8 +232,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\":\"thumb\",\"width\": 100,\"height\": 100,\"coordinates\": {\"x1\": 0.58729977382575338,\"y1\": 0.055768992440203169,\"x2\": 0,\"y2\": 0.32457553600198386}}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, width: 300, height: 150, preferFocalPoint:true);
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=300&height=150", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, width: 300, height: 150, preferFocalPoint:true);
|
||||
Assert.AreEqual(MediaPath + "?m=defaultcrop&w=300&h=150", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -245,8 +244,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, cropAlias: "home", width: 200);
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200);
|
||||
Assert.AreEqual(MediaPath + "?m=defaultcrop&hr=0.5962962962962962962962962963&w=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -257,8 +256,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.4275,\"top\": 0.41},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, cropAlias: "home", width: 200);
|
||||
Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&heightratio=0.5962962962962962962962962963&width=200", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200);
|
||||
Assert.AreEqual(MediaPath + "?f=0.41x0.4275&hr=0.5962962962962962962962962963&w=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -269,8 +268,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.4275,\"top\": 0.41},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, cropAlias: "home", width: 200, useCropDimensions: true);
|
||||
Assert.AreEqual(MediaPath + "?center=0.41,0.4275&mode=crop&width=270&height=161", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", width: 200, useCropDimensions: true);
|
||||
Assert.AreEqual(MediaPath + "?f=0.41x0.4275&w=270&h=161", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -281,8 +280,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, cropAlias: "home", height: 200);
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&widthratio=1.6770186335403726708074534161&height=200", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, cropAlias: "home", height: 200);
|
||||
Assert.AreEqual(MediaPath + "?m=defaultcrop&wr=1.6770186335403726708074534161&h=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -293,8 +292,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, width: 200);
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&width=200", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, width: 200);
|
||||
Assert.AreEqual(MediaPath + "?m=defaultcrop&w=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -305,8 +304,8 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
const string cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"/media/1005/img_0671.jpg\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(imageCropperValue: cropperJson, height: 200);
|
||||
Assert.AreEqual(MediaPath + "?anchor=center&mode=crop&height=200", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), imageCropperValue: cropperJson, height: 200);
|
||||
Assert.AreEqual(MediaPath + "?m=defaultcrop&h=200", urlString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -317,8 +316,55 @@ namespace Umbraco.Tests.PropertyEditors
|
||||
{
|
||||
var cropperJson = "{\"focalPoint\": {\"left\": 0.5,\"top\": 0.5},\"src\": \"" + MediaPath + "\",\"crops\": [{\"alias\": \"home\",\"width\": 270,\"height\": 161}]}";
|
||||
|
||||
var urlString = MediaPath.GetCropUrl(400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, furtherOptions: "&bgcolor=fff");
|
||||
Assert.AreEqual(MediaPath + "?mode=pad&width=400&height=400&bgcolor=fff", urlString);
|
||||
var urlString = MediaPath.GetCropUrl(new TestImageUrlGenerator(), 400, 400, cropperJson, imageCropMode: ImageCropMode.Pad, furtherOptions: "&bgcolor=fff");
|
||||
Assert.AreEqual(MediaPath + "?m=pad&w=400&h=400&bgcolor=fff", urlString);
|
||||
}
|
||||
|
||||
internal class TestImageUrlGenerator : IImageUrlGenerator
|
||||
{
|
||||
public string GetImageUrl(ImageUrlGenerationOptions options)
|
||||
{
|
||||
var imageProcessorUrl = new StringBuilder(options.ImageUrl ?? string.Empty);
|
||||
|
||||
if (options.FocalPoint != null)
|
||||
{
|
||||
imageProcessorUrl.Append("?f=");
|
||||
imageProcessorUrl.Append(options.FocalPoint.Top.ToString(CultureInfo.InvariantCulture));
|
||||
imageProcessorUrl.Append("x");
|
||||
imageProcessorUrl.Append(options.FocalPoint.Left.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
else if (options.Crop != null)
|
||||
{
|
||||
imageProcessorUrl.Append("?c=");
|
||||
imageProcessorUrl.Append(options.Crop.X1.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
imageProcessorUrl.Append(options.Crop.Y1.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
imageProcessorUrl.Append(options.Crop.X2.ToString(CultureInfo.InvariantCulture)).Append(",");
|
||||
imageProcessorUrl.Append(options.Crop.Y2.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
else if (options.DefaultCrop)
|
||||
{
|
||||
imageProcessorUrl.Append("?m=defaultcrop");
|
||||
}
|
||||
else
|
||||
{
|
||||
imageProcessorUrl.Append("?m=" + options.ImageCropMode.ToString().ToLower());
|
||||
if (options.ImageCropAnchor != null)imageProcessorUrl.Append("&a=" + options.ImageCropAnchor.ToString().ToLower());
|
||||
}
|
||||
|
||||
var hasFormat = options.FurtherOptions != null && options.FurtherOptions.InvariantContains("&f=");
|
||||
if (options.Quality != null && hasFormat == false) imageProcessorUrl.Append("&q=" + options.Quality);
|
||||
if (options.HeightRatio != null) imageProcessorUrl.Append("&hr=" + options.HeightRatio.Value.ToString(CultureInfo.InvariantCulture));
|
||||
if (options.WidthRatio != null) imageProcessorUrl.Append("&wr=" + options.WidthRatio.Value.ToString(CultureInfo.InvariantCulture));
|
||||
if (options.Width != null) imageProcessorUrl.Append("&w=" + options.Width);
|
||||
if (options.Height != null) imageProcessorUrl.Append("&h=" + options.Height);
|
||||
if (options.UpScale == false) imageProcessorUrl.Append("&u=no");
|
||||
if (options.AnimationProcessMode != null) imageProcessorUrl.Append("&apm=" + options.AnimationProcessMode);
|
||||
if (options.FurtherOptions != null) imageProcessorUrl.Append(options.FurtherOptions);
|
||||
if (options.Quality != null && hasFormat) imageProcessorUrl.Append("&q=" + options.Quality);
|
||||
if (options.CacheBusterValue != null) imageProcessorUrl.Append("&r=").Append(options.CacheBusterValue);
|
||||
|
||||
return imageProcessorUrl.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using Umbraco.Web.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Templates;
|
||||
using Umbraco.Web.Models;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent
|
||||
{
|
||||
@@ -46,7 +47,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
var pastedImages = new RichTextEditorPastedImages(umbracoContextAccessor, logger, Mock.Of<IMediaService>(), Mock.Of<IContentTypeBaseServiceProvider>());
|
||||
var localLinkParser = new HtmlLocalLinkParser(umbracoContextAccessor);
|
||||
var dataTypeService = new TestObjects.TestDataTypeService(
|
||||
new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, imageSourceParser, localLinkParser, pastedImages)) { Id = 1 });
|
||||
new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, imageSourceParser, localLinkParser, pastedImages, Mock.Of<IImageUrlGenerator>())) { Id = 1 });
|
||||
|
||||
var publishedContentTypeFactory = new PublishedContentTypeFactory(Mock.Of<IPublishedModelFactory>(), converters, dataTypeService);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ using Umbraco.Tests.Testing;
|
||||
using Umbraco.Web.Models.PublishedContent;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using Umbraco.Web.Templates;
|
||||
using Umbraco.Web.Models;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent
|
||||
{
|
||||
@@ -53,7 +54,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
var dataTypeService = new TestObjects.TestDataTypeService(
|
||||
new DataType(new VoidEditor(logger)) { Id = 1 },
|
||||
new DataType(new TrueFalsePropertyEditor(logger)) { Id = 1001 },
|
||||
new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, imageSourceParser, linkParser, pastedImages)) { Id = 1002 },
|
||||
new DataType(new RichTextPropertyEditor(logger, umbracoContextAccessor, imageSourceParser, linkParser, pastedImages, Mock.Of<IImageUrlGenerator>())) { Id = 1002 },
|
||||
new DataType(new IntegerPropertyEditor(logger)) { Id = 1003 },
|
||||
new DataType(new TextboxPropertyEditor(logger)) { Id = 1004 },
|
||||
new DataType(new MediaPickerPropertyEditor(logger)) { Id = 1005 });
|
||||
|
||||
@@ -199,6 +199,7 @@ namespace Umbraco.Tests.PublishedContent
|
||||
public DateTime UpdateDate { get; set; }
|
||||
public Guid Version { get; set; }
|
||||
public int Level { get; set; }
|
||||
[Obsolete("Use the Url() extension instead")]
|
||||
public string Url { get; set; }
|
||||
|
||||
public PublishedItemType ItemType => PublishedItemType.Content;
|
||||
|
||||
@@ -97,6 +97,7 @@ namespace Umbraco.Tests.Runtimes
|
||||
composition.Register<IVariationContextAccessor, TestVariationContextAccessor>(Lifetime.Singleton);
|
||||
composition.Register<IDefaultCultureAccessor, TestDefaultCultureAccessor>(Lifetime.Singleton);
|
||||
composition.Register<ISiteDomainHelper>(_ => Mock.Of<ISiteDomainHelper>(), Lifetime.Singleton);
|
||||
composition.Register(_ => Mock.Of<IImageUrlGenerator>(), Lifetime.Singleton);
|
||||
composition.RegisterUnique(f => new DistributedCache());
|
||||
composition.WithCollectionBuilder<UrlProviderCollectionBuilder>().Append<DefaultUrlProvider>();
|
||||
composition.RegisterUnique<IDistributedCacheBinder, DistributedCacheBinder>();
|
||||
|
||||
@@ -43,6 +43,7 @@ using Current = Umbraco.Core.Composing.Current;
|
||||
using FileSystems = Umbraco.Core.IO.FileSystems;
|
||||
using Umbraco.Web.Templates;
|
||||
using Umbraco.Web.PropertyEditors;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Tests.Testing
|
||||
{
|
||||
@@ -248,6 +249,7 @@ namespace Umbraco.Tests.Testing
|
||||
var runtimeStateMock = new Mock<IRuntimeState>();
|
||||
runtimeStateMock.Setup(x => x.Level).Returns(RuntimeLevel.Run);
|
||||
Composition.RegisterUnique(f => runtimeStateMock.Object);
|
||||
Composition.Register(_ => Mock.Of<IImageUrlGenerator>());
|
||||
|
||||
// ah...
|
||||
Composition.WithCollectionBuilder<ActionCollectionBuilder>();
|
||||
|
||||
@@ -140,12 +140,14 @@
|
||||
<Compile Include="ModelsBuilder\UmbracoApplicationTests.cs" />
|
||||
<Compile Include="Models\ContentScheduleTests.cs" />
|
||||
<Compile Include="Models\CultureImpactTests.cs" />
|
||||
<Compile Include="Models\ImageProcessorImageUrlGeneratorTest.cs" />
|
||||
<Compile Include="Models\PathValidationTests.cs" />
|
||||
<Compile Include="Models\VariationTests.cs" />
|
||||
<Compile Include="Persistence\Mappers\MapperTestBase.cs" />
|
||||
<Compile Include="Persistence\Repositories\DocumentRepositoryTest.cs" />
|
||||
<Compile Include="Persistence\Repositories\EntityRepositoryTest.cs" />
|
||||
<Compile Include="PropertyEditors\BlockListPropertyValueConverterTests.cs" />
|
||||
<Compile Include="PropertyEditors\DataValueReferenceFactoryCollectionTests.cs" />
|
||||
<Compile Include="PublishedContent\NuCacheChildrenTests.cs" />
|
||||
<Compile Include="PublishedContent\PublishedContentLanguageVariantTests.cs" />
|
||||
<Compile Include="PublishedContent\PublishedContentSnapshotTestBase.cs" />
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 152 KiB After Width: | Height: | Size: 581 KiB |
+11
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
+3
-3
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+3
@@ -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
|
||||
|
||||
+11
-6
@@ -99,13 +99,18 @@ Use this directive to render a ui component for selecting child items to a paren
|
||||
@param {string} parentName (<code>binding</code>): The parent name.
|
||||
@param {string} parentIcon (<code>binding</code>): The parent icon.
|
||||
@param {number} parentId (<code>binding</code>): The parent id.
|
||||
@param {callback} onRemove (<code>binding</code>): Callback when the remove button is clicked on an item.
|
||||
@param {callback} onRemove (<code>binding</code>): Callback when removing an item.
|
||||
<h3>The callback returns:</h3>
|
||||
<ul>
|
||||
<li><code>child</code>: The selected item.</li>
|
||||
<li><code>$index</code>: The selected item index.</li>
|
||||
</ul>
|
||||
@param {callback} onAdd (<code>binding</code>): Callback when the add button is clicked.
|
||||
@param {callback} onAdd (<code>binding</code>): Callback when adding an item.
|
||||
<h3>The callback returns:</h3>
|
||||
<ul>
|
||||
<li><code>$event</code>: The select event.</li>
|
||||
</ul>
|
||||
@param {callback} onSort (<code>binding</code>): Callback when sorting an item.
|
||||
<h3>The callback returns:</h3>
|
||||
<ul>
|
||||
<li><code>$event</code>: The select event.</li>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+26
-6
@@ -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 === "") {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.resources.imageUrlGeneratorResource
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* Used by the various controllers to get an image URL formatted correctly for the current image URL generator
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function imageUrlGeneratorResource($http, umbRequestHelper) {
|
||||
|
||||
function getCropUrl(mediaPath, width, height, imageCropMode, animationProcessMode) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"imageUrlGeneratorApiBaseUrl",
|
||||
"GetCropUrl",
|
||||
{ mediaPath, width, height, imageCropMode, animationProcessMode })),
|
||||
'Failed to get crop URL');
|
||||
}
|
||||
|
||||
|
||||
var resource = {
|
||||
getCropUrl: getCropUrl
|
||||
};
|
||||
|
||||
return resource;
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.resources').factory('imageUrlGeneratorResource', imageUrlGeneratorResource);
|
||||
|
||||
})();
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -162,5 +162,6 @@
|
||||
&.umb-form-check--disabled {
|
||||
cursor: not-allowed !important;
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.umb-property-file-upload {
|
||||
|
||||
.umb-upload-button-big {
|
||||
max-width: (@propertyEditorLimitedWidth - 40);
|
||||
display: block;
|
||||
padding: 20px;
|
||||
opacity: 1;
|
||||
|
||||
@@ -418,7 +418,7 @@
|
||||
// --------------------------------------------------
|
||||
// Limit width of specific property editors
|
||||
.umb-property-editor--limit-width {
|
||||
max-width: 800px;
|
||||
max-width: @propertyEditorLimitedWidth;
|
||||
}
|
||||
|
||||
// Horizontal dividers
|
||||
|
||||
@@ -722,6 +722,10 @@
|
||||
//
|
||||
// File upload
|
||||
// --------------------------------------------------
|
||||
.umb-fileupload {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.umb-fileupload .preview {
|
||||
border-radius: 5px;
|
||||
border: 1px solid @gray-6;
|
||||
|
||||
@@ -164,4 +164,13 @@
|
||||
|
||||
.mce-fullscreen {
|
||||
position: absolute;
|
||||
|
||||
.mce-in {
|
||||
position: fixed;
|
||||
top: 35px !important;
|
||||
}
|
||||
|
||||
umb-editor__overlay, .umb-editor {
|
||||
position: fixed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+3
-1
@@ -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 = [
|
||||
{
|
||||
|
||||
+6
-5
@@ -84,14 +84,15 @@
|
||||
|
||||
<h5><localize key="validation_validation"></localize></h5>
|
||||
|
||||
<label>
|
||||
<localize key="validation_fieldIsMandatory"></localize>
|
||||
</label>
|
||||
|
||||
<umb-toggle data-element="validation_mandatory"
|
||||
checked="model.property.validation.mandatory"
|
||||
on-click="vm.toggleValidation()">
|
||||
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">
|
||||
</umb-toggle>
|
||||
|
||||
<input type="text"
|
||||
|
||||
+2
-2
@@ -130,8 +130,8 @@
|
||||
}
|
||||
|
||||
// diff requires a string
|
||||
property.value = property.value ? property.value : "";
|
||||
oldProperty.value = oldProperty.value ? oldProperty.value : "";
|
||||
property.value = property.value ? property.value + "" : "";
|
||||
oldProperty.value = oldProperty.value ? oldProperty.value + "" : "";
|
||||
|
||||
var diffProperty = {
|
||||
"alias": property.alias,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li data-element="section-expand" class="expand" ng-class="{ 'open': showTray === true }" ng-show="needTray">
|
||||
<li data-element="section-expand" class="expand" ng-class="{ 'open': showTray === true, current: currentSectionInOverflow() }" ng-show="needTray">
|
||||
<a href="#" ng-click="trayClick()" prevent-default>
|
||||
<span class="section__name"><i></i><i></i><i></i></span>
|
||||
</a>
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
<!-- Dom element not found error -->
|
||||
<div ng-if="elementNotFound && !loadingStep">
|
||||
<umb-tour-step class="tc">
|
||||
<umb-tour-step class="tc" on-close="model.endTour()">
|
||||
<umb-tour-step-header>
|
||||
<h4 class="bold color-red">Oh, we got lost!</h4>
|
||||
</umb-tour-step-header>
|
||||
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
<div class="umb-tour-step umb-tour-step--{{size}}">
|
||||
|
||||
|
||||
<div ng-if="hideClose !== true">
|
||||
<button class="icon-wrong umb-tour-step__close" ng-click="close()">
|
||||
<button type="button" class="icon-wrong umb-tour-step__close" hotkey="esc" ng-click="close()">
|
||||
<span class="sr-only">
|
||||
<localize key="general_close">Close</localize>
|
||||
</span>
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
<ul class="nav nav-stacked" style="margin-bottom: 0;">
|
||||
<li ng-repeat="url in currentUrls">
|
||||
<a href="{{url.text}}" target="_blank" ng-if="url.isUrl">
|
||||
<span ng-if="node.variants.length === 1 && url.culture" style="font-size: 13px; color: #cccccc; width: 50px;display: inline-block">{{url.culture}}</span>
|
||||
<span ng-if="currentUrlsHaveMultipleCultures && url.culture" style="font-size: 13px; color: #cccccc; width: 50px;display: inline-block">{{url.culture}}</span>
|
||||
<i class="icon icon-out"></i>
|
||||
<span>{{url.text}}</span>
|
||||
</a>
|
||||
<div ng-if="!url.isUrl" style="margin-top: 4px;">
|
||||
|
||||
<span ng-if="node.variants.length === 1 && url.culture" style="font-size: 13px; color: #cccccc; width: 50px;display: inline-block">{{url.culture}}</span>
|
||||
<span ng-if="currentUrlsHaveMultipleCultures && url.culture" style="font-size: 13px; color: #cccccc; width: 50px;display: inline-block">{{url.culture}}</span>
|
||||
<em>{{url.text}}</em>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -19,18 +19,20 @@
|
||||
<div class="umb-child-selector__child" ng-repeat="selectedChild in selectedChildren">
|
||||
<div class="umb-child-selector__child-description">
|
||||
<div class="umb-child-selector__child-icon-holder">
|
||||
<i class="umb-child-selector__child-icon {{ selectedChild.icon }}"></i>
|
||||
<i class="umb-child-selector__child-icon {{selectedChild.icon}}" aria-hidden="true"></i>
|
||||
</div>
|
||||
<span class="umb-child-selector__child-name"> {{ selectedChild.name }}</span>
|
||||
<span class="umb-child-selector__child-name">{{selectedChild.name}}</span>
|
||||
</div>
|
||||
<div class="umb-child-selector__child-actions">
|
||||
<i class="umb-child-selector__child-remove icon-trash" ng-click="removeChild(selectedChild, $index)"></i>
|
||||
<button type="button" class="umb-child-selector__child-remove" ng-click="removeChild(selectedChild, $index)">
|
||||
<i class="icon-trash" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="" class="umb-child-selector__child -placeholder" ng-click="addChild($event)" hotkey="alt+shift+c">
|
||||
<button type="button" class="umb-child-selector__child -placeholder unsortable" ng-click="addChild($event)" hotkey="alt+shift+c">
|
||||
<div class="umb-child-selector__child-name -blue"><strong><localize key="shortcuts_addChild">Add Child</localize></strong></div>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
ng-model="vm.model"
|
||||
ng-change="vm.onChange()"
|
||||
ng-keydown="vm.onKeyDown($event)"
|
||||
ng-blur="vm.onBlur($event)"
|
||||
prevent-enter-submit
|
||||
no-dirty-check>
|
||||
</ng-form>
|
||||
|
||||
+2
-1
@@ -10,7 +10,8 @@
|
||||
bindings: {
|
||||
model: "=",
|
||||
onStartTyping: "&?",
|
||||
onSearch: "&?"
|
||||
onSearch: "&?",
|
||||
onBlur: "&?"
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<div class="umb-user-preview">
|
||||
|
||||
<div class="umb-user-preview__avatar">
|
||||
<umb-avatar
|
||||
size="xxs"
|
||||
color="secondary"
|
||||
name="{{name}}"
|
||||
img-src="{{avatars[0]}}"
|
||||
img-srcset="{{avatars[1]}} 2x, {{avatars[2]}} 3x">
|
||||
<umb-avatar size="xxs"
|
||||
color="secondary"
|
||||
name="{{name}}"
|
||||
img-src="{{avatars[0]}}"
|
||||
img-srcset="{{avatars[1]}} 2x, {{avatars[2]}} 3x">
|
||||
</umb-avatar>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +14,6 @@
|
||||
</div>
|
||||
|
||||
<div class="umb-user-preview__actions">
|
||||
<a class="umb-user-preview__action umb-user-preview__action--red" title="Remove" href="" ng-if="allowRemove" ng-click="onRemove()"><localize key="general_remove">Remove</localize></a>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<button type="button" class="umb-user-preview__action umb-user-preview__action--red btn-link" title="Remove" ng-if="allowRemove" ng-click="onRemove()"><localize key="general_remove">Remove</localize></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<umb-box>
|
||||
<umb-box-content>
|
||||
<h3><localize key="settingsDashboardVideos_trainingHeadline">Hours of Umbraco training videos are only a click away</localize></h3>
|
||||
<h3 class="bold"><localize key="settingsDashboardVideos_trainingHeadline">Hours of Umbraco training videos are only a click away</localize></h3>
|
||||
<localize key="settingsDashboardVideos_trainingDescription">
|
||||
<p>Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit <a href="http://umbraco.tv" target="_blank">umbraco.tv</a> for even more Umbraco videos</p>
|
||||
</localize>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+39
-35
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+57
-64
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,16 +36,16 @@
|
||||
</umb-file-icon>
|
||||
|
||||
<div class="umb-sortable-thumbnails__actions" data-element="sortable-thumbnail-actions">
|
||||
<button aria-label="Edit media" ng-if="allowEditMedia" class="umb-sortable-thumbnails__action btn-reset" data-element="action-edit" ng-click="vm.editItem(media)">
|
||||
<button type="button" aria-label="Edit media" ng-if="allowEditMedia" class="umb-sortable-thumbnails__action btn-reset" data-element="action-edit" ng-click="vm.editItem(media)">
|
||||
<i class="icon icon-edit" aria-hidden="true"></i>
|
||||
</button>
|
||||
<button aria-label="Remove" class="umb-sortable-thumbnails__action -red btn-reset" data-element="action-remove" ng-click="vm.remove($index)">
|
||||
<button type="button" aria-label="Remove" class="umb-sortable-thumbnails__action -red btn-reset" data-element="action-remove" ng-click="vm.remove($index)">
|
||||
<i class="icon icon-delete" aria-hidden="true"></i>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<li style="border: none;" class="add-wrapper unsortable" ng-if="vm.showAdd() && allowAddMedia">
|
||||
<button aria-label="Open media picker" data-element="sortable-thumbnails-add" class="add-link btn-reset umb-outline umb-outline--surrounding" ng-click="vm.add()" ng-class="{'add-link-square': (mediaItems.length === 0 || isMultiPicker)}" prevent-default>
|
||||
<button type="button" aria-label="Open media picker" data-element="sortable-thumbnails-add" class="add-link btn-reset umb-outline umb-outline--surrounding" ng-click="vm.add()" ng-class="{'add-link-square': (mediaItems.length === 0 || isMultiPicker)}">
|
||||
<i class="icon icon-add large" aria-hidden="true"></i>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ function multiUrlPickerController($scope, angularHelper, localizationService, en
|
||||
|
||||
_.each($scope.model.value, function (item){
|
||||
// we must reload the "document" link URLs to match the current editor culture
|
||||
if (item.udi.indexOf("/document/") > 0) {
|
||||
if (item.udi && item.udi.indexOf("/document/") > 0) {
|
||||
item.url = null;
|
||||
entityResource.getUrlByUdi(item.udi).then(function (data) {
|
||||
item.url = data;
|
||||
|
||||
+14
@@ -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();
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
return value.toFixed(stepDecimalPlaces);
|
||||
},
|
||||
from: function (value) {
|
||||
return value;
|
||||
return Number(value);
|
||||
}
|
||||
},
|
||||
"range": {
|
||||
|
||||
@@ -40,13 +40,12 @@
|
||||
on-remove="vm.removeSelectedItem($index, vm.userGroup.sections)">
|
||||
</umb-node-preview>
|
||||
|
||||
<a href=""
|
||||
style="max-width: 100%;"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="vm.openSectionPicker()"
|
||||
prevent-default>
|
||||
<button type="button"
|
||||
class="umb-node-preview-add"
|
||||
style="max-width: 100%;"
|
||||
ng-click="vm.openSectionPicker()">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
</button>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group style="margin-bottom: 20px;" label="@user_startnode" description="@user_startnodehelp">
|
||||
@@ -61,14 +60,14 @@
|
||||
on-remove="vm.clearStartNode('content')">
|
||||
</umb-node-preview>
|
||||
|
||||
<a href=""
|
||||
ng-if="!vm.userGroup.contentStartNode"
|
||||
style="max-width: 100%;"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="vm.openContentPicker()"
|
||||
prevent-default>
|
||||
|
||||
<button type="button"
|
||||
ng-if="!vm.userGroup.contentStartNode"
|
||||
class="umb-node-preview-add"
|
||||
style="max-width: 100%;"
|
||||
ng-click="vm.openContentPicker()">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
</umb-control-group>
|
||||
|
||||
@@ -85,14 +84,14 @@
|
||||
on-remove="vm.clearStartNode('media')">
|
||||
</umb-node-preview>
|
||||
|
||||
<a href=""
|
||||
ng-if="!vm.userGroup.mediaStartNode"
|
||||
style="max-width: 100%;"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="vm.openMediaPicker()"
|
||||
prevent-default>
|
||||
|
||||
<button type="button"
|
||||
ng-if="!vm.userGroup.mediaStartNode"
|
||||
class="umb-node-preview-add"
|
||||
style="max-width: 100%;"
|
||||
ng-click="vm.openMediaPicker()">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
</umb-control-group>
|
||||
|
||||
@@ -127,13 +126,12 @@
|
||||
on-edit="vm.setPermissionsForNode(node)">
|
||||
</umb-node-preview>
|
||||
|
||||
<a href=""
|
||||
style="max-width: 100%;"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="vm.openGranularPermissionsPicker()"
|
||||
prevent-default>
|
||||
<button type="button"
|
||||
class="umb-node-preview-add"
|
||||
style="max-width: 100%;"
|
||||
ng-click="vm.openGranularPermissionsPicker()">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
</button>
|
||||
</umb-control-group>
|
||||
|
||||
</umb-box-content>
|
||||
@@ -155,13 +153,11 @@
|
||||
on-remove="vm.removeSelectedItem($index, vm.userGroup.users)">
|
||||
</umb-user-preview>
|
||||
|
||||
<a href=""
|
||||
style="max-width: 100%;"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="vm.openUserPicker()"
|
||||
prevent-default>
|
||||
<button type="button"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="vm.openUserPicker()">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
|
||||
@@ -75,13 +75,11 @@
|
||||
on-remove="model.removeSelectedItem($index, model.user.userGroups)">
|
||||
</umb-user-group-preview>
|
||||
|
||||
<a href=""
|
||||
style="max-width: 100%;"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="model.openUserGroupPicker()"
|
||||
prevent-default>
|
||||
<button type="button"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="model.openUserGroupPicker()">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
</umb-control-group>
|
||||
|
||||
@@ -100,13 +98,12 @@
|
||||
name="model.labels.noStartNodes">
|
||||
</umb-node-preview>
|
||||
|
||||
<a href=""
|
||||
class="umb-node-preview-add"
|
||||
id="content-start-add"
|
||||
ng-click="model.openContentPicker()"
|
||||
prevent-default>
|
||||
<button type="button"
|
||||
class="umb-node-preview-add"
|
||||
id="content-start-add"
|
||||
ng-click="model.openContentPicker()">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
</umb-control-group>
|
||||
|
||||
@@ -125,13 +122,12 @@
|
||||
name="model.labels.noStartNodes">
|
||||
</umb-node-preview>
|
||||
|
||||
<a href=""
|
||||
class="umb-node-preview-add"
|
||||
ng-click="model.openMediaPicker()"
|
||||
id="media-start-add"
|
||||
prevent-default>
|
||||
<button type="button"
|
||||
class="umb-node-preview-add"
|
||||
ng-click="model.openMediaPicker()"
|
||||
id="media-start-add">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
</umb-control-group>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
vm.userStates = [];
|
||||
vm.selection = [];
|
||||
vm.newUser = {};
|
||||
vm.usersOptions = {filter:null};
|
||||
vm.usersOptions = {};
|
||||
vm.userSortData = [
|
||||
{ label: "Name (A-Z)", key: "Name", direction: "Ascending" },
|
||||
{ label: "Name (Z-A)", key: "Name", direction: "Descending" },
|
||||
@@ -112,6 +112,7 @@
|
||||
vm.selectAll = selectAll;
|
||||
vm.areAllSelected = areAllSelected;
|
||||
vm.searchUsers = searchUsers;
|
||||
vm.onBlurSearch = onBlurSearch;
|
||||
vm.getFilterName = getFilterName;
|
||||
vm.setUserStatesFilter = setUserStatesFilter;
|
||||
vm.setUserGroupFilter = setUserGroupFilter;
|
||||
@@ -150,10 +151,12 @@
|
||||
function initViewOptions() {
|
||||
|
||||
// Start with default view options.
|
||||
vm.usersOptions.filter = "";
|
||||
vm.usersOptions.orderBy = "Name";
|
||||
vm.usersOptions.orderDirection = "Ascending";
|
||||
|
||||
// Update from querystring if available.
|
||||
initViewOptionFromQueryString("filter");
|
||||
initViewOptionFromQueryString("orderBy");
|
||||
initViewOptionFromQueryString("orderDirection");
|
||||
initViewOptionFromQueryString("pageNumber");
|
||||
@@ -451,7 +454,8 @@
|
||||
|
||||
var search = _.debounce(function () {
|
||||
$scope.$apply(function () {
|
||||
changePageNumber(1);
|
||||
vm.usersOptions.pageNumber = 1;
|
||||
getUsers();
|
||||
});
|
||||
}, 500);
|
||||
|
||||
@@ -459,6 +463,10 @@
|
||||
search();
|
||||
}
|
||||
|
||||
function onBlurSearch() {
|
||||
updateLocation("filter", vm.usersOptions.filter);
|
||||
}
|
||||
|
||||
function getFilterName(array) {
|
||||
var name = vm.labels.all;
|
||||
var found = false;
|
||||
@@ -547,6 +555,7 @@
|
||||
}
|
||||
|
||||
function updateLocation(key, value) {
|
||||
$location.search("filter", vm.usersOptions.filter);// update filter, but first when something else requests a url update.
|
||||
$location.search(key, value);
|
||||
}
|
||||
|
||||
@@ -657,7 +666,8 @@
|
||||
function usersOptionsAsQueryString() {
|
||||
var qs = "?orderBy=" + vm.usersOptions.orderBy +
|
||||
"&orderDirection=" + vm.usersOptions.orderDirection +
|
||||
"&pageNumber=" + vm.usersOptions.pageNumber;
|
||||
"&pageNumber=" + vm.usersOptions.pageNumber +
|
||||
"&filter=" + vm.usersOptions.filter;
|
||||
|
||||
qs += addUsersOptionsFilterCollectionToQueryString("userStates", vm.usersOptions.userStates);
|
||||
qs += addUsersOptionsFilterCollectionToQueryString("userGroups", vm.usersOptions.userGroups);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</umb-editor-sub-header-section>
|
||||
<umb-editor-sub-header-section>
|
||||
|
||||
<umb-mini-search model="vm.usersOptions.filter" on-search="vm.searchUsers()" on-start-typing="vm.searchUsers()">
|
||||
<umb-mini-search model="vm.usersOptions.filter" on-search="vm.searchUsers()" on-blur="vm.onBlurSearch()">
|
||||
</umb-mini-search>
|
||||
|
||||
</umb-editor-sub-header-section>
|
||||
|
||||
@@ -29,13 +29,13 @@
|
||||
@* a single image *@
|
||||
if (media.IsDocumentType("Image"))
|
||||
{
|
||||
@Render(media);
|
||||
@Render(media)
|
||||
}
|
||||
|
||||
@* a folder with images under it *@
|
||||
foreach (var image in media.Children())
|
||||
{
|
||||
@Render(image);
|
||||
@Render(image)
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
<div class="umb-grid">
|
||||
@if (oneColumn)
|
||||
{
|
||||
foreach (var section in Model.sections) {
|
||||
<div class="grid-section">
|
||||
@foreach (var row in section.rows) {
|
||||
@renderRow(row);
|
||||
@renderRow(row)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}else {
|
||||
}
|
||||
}else {
|
||||
<div class="row clearfix">
|
||||
@foreach (var s in Model.sections) {
|
||||
<div class="grid-section">
|
||||
<div class="col-md-@s.grid column">
|
||||
@foreach (var row in s.rows) {
|
||||
@renderRow(row);
|
||||
@renderRow(row)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -85,4 +85,4 @@
|
||||
|
||||
return new MvcHtmlString(string.Join(" ", attrs));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
foreach (var section in Model.sections) {
|
||||
<div class="grid-section">
|
||||
@foreach (var row in section.rows) {
|
||||
@renderRow(row, true);
|
||||
@renderRow(row, true)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
<div class="grid-section">
|
||||
<div class="col-md-@s.grid column">
|
||||
@foreach (var row in s.rows) {
|
||||
@renderRow(row, false);
|
||||
@renderRow(row, false)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,20 +2,24 @@
|
||||
@using Umbraco.Web.Templates
|
||||
|
||||
@if (Model.value != null)
|
||||
{
|
||||
{
|
||||
var url = Model.value.image;
|
||||
if(Model.editor.config != null && Model.editor.config.size != null){
|
||||
url += "?width=" + Model.editor.config.size.width;
|
||||
url += "&height=" + Model.editor.config.size.height;
|
||||
|
||||
if(Model.value.focalPoint != null){
|
||||
url += "¢er=" + Model.value.focalPoint.top +"," + Model.value.focalPoint.left;
|
||||
url += "&mode=crop";
|
||||
}
|
||||
url = ImageCropperTemplateExtensions.GetCropUrl(url,
|
||||
width: Model.editor.config.size.width,
|
||||
height: Model.editor.config.size.height,
|
||||
cropDataSet: Model.value.focalPoint == null ? null : new Umbraco.Core.PropertyEditors.ValueConverters.ImageCropperValue
|
||||
{
|
||||
FocalPoint = new Umbraco.Core.PropertyEditors.ValueConverters.ImageCropperValue.ImageCropperFocalPoint
|
||||
{
|
||||
Top = Model.value.focalPoint.top,
|
||||
Left = Model.value.focalPoint.left
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var altText = Model.value.altText ?? Model.value.caption ?? string.Empty;
|
||||
|
||||
|
||||
<img src="@url" alt="@altText">
|
||||
|
||||
if (Model.value.caption != null)
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace Umbraco.Web.Cache
|
||||
{
|
||||
macroRepoCache.Result.Clear(RepositoryCacheKeys.GetKey<IMacro>(payload.Id));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
base.Refresh(json);
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Umbraco.Web.Controllers
|
||||
// if it's not a local url we'll redirect to the root of the current site
|
||||
return Redirect(Url.IsLocalUrl(model.RedirectUrl)
|
||||
? model.RedirectUrl
|
||||
: CurrentPage.AncestorOrSelf(1).Url);
|
||||
: CurrentPage.AncestorOrSelf(1).Url());
|
||||
}
|
||||
|
||||
//redirect to current page by default
|
||||
|
||||
@@ -314,6 +314,10 @@ namespace Umbraco.Web.Editors
|
||||
"tinyMceApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<TinyMceController>(
|
||||
controller => controller.UploadImage())
|
||||
},
|
||||
{
|
||||
"imageUrlGeneratorApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ImageUrlGeneratorController>(
|
||||
controller => controller.GetCropUrl(null, null, null, null, null))
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -869,7 +869,7 @@ namespace Umbraco.Web.Editors
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Helper method to perform the saving of the content and add the notifications to the result
|
||||
@@ -1161,14 +1161,14 @@ namespace Umbraco.Web.Editors
|
||||
//validate if we can publish based on the mandatory language requirements
|
||||
var canPublish = ValidatePublishingMandatoryLanguages(
|
||||
cultureErrors,
|
||||
contentItem, cultureVariants, mandatoryCultures,
|
||||
contentItem, cultureVariants, mandatoryCultures,
|
||||
mandatoryVariant => mandatoryVariant.Publish);
|
||||
|
||||
//Now check if there are validation errors on each variant.
|
||||
//If validation errors are detected on a variant and it's state is set to 'publish', then we
|
||||
//need to change it to 'save'.
|
||||
//It is a requirement that this is performed AFTER ValidatePublishingMandatoryLanguages.
|
||||
|
||||
|
||||
foreach (var variant in contentItem.Variants)
|
||||
{
|
||||
if (cultureErrors.Contains(variant.Culture))
|
||||
@@ -1656,14 +1656,14 @@ namespace Umbraco.Web.Editors
|
||||
[HttpPost]
|
||||
public DomainSave PostSaveLanguageAndDomains(DomainSave model)
|
||||
{
|
||||
foreach(var domain in model.Domains)
|
||||
foreach (var domain in model.Domains)
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = DomainUtilities.ParseUriFromDomainName(domain.Name, Request.RequestUri);
|
||||
}
|
||||
catch (UriFormatException)
|
||||
{
|
||||
{
|
||||
var response = Request.CreateValidationErrorResponse(Services.TextService.Localize("assignDomain/invalidDomain"));
|
||||
throw new HttpResponseException(response);
|
||||
}
|
||||
@@ -1829,7 +1829,7 @@ namespace Umbraco.Web.Editors
|
||||
base.HandleInvalidModelState(display);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Maps the dto property values and names to the persisted model
|
||||
/// </summary>
|
||||
@@ -1842,7 +1842,7 @@ namespace Umbraco.Web.Editors
|
||||
var culture = property.PropertyType.VariesByCulture() ? variant.Culture : null;
|
||||
var segment = property.PropertyType.VariesBySegment() ? variant.Segment : null;
|
||||
return (culture, segment);
|
||||
}
|
||||
}
|
||||
|
||||
var variantIndex = 0;
|
||||
|
||||
@@ -1884,15 +1884,15 @@ namespace Umbraco.Web.Editors
|
||||
(save, property) =>
|
||||
{
|
||||
// Get property value
|
||||
(var culture, var segment) = PropertyCultureAndSegment(property, variant);
|
||||
return property.GetValue(culture, segment);
|
||||
(var culture, var segment) = PropertyCultureAndSegment(property, variant);
|
||||
return property.GetValue(culture, segment);
|
||||
},
|
||||
(save, property, v) =>
|
||||
{
|
||||
// Set property value
|
||||
(var culture, var segment) = PropertyCultureAndSegment(property, variant);
|
||||
property.SetValue(v, culture, segment);
|
||||
},
|
||||
property.SetValue(v, culture, segment);
|
||||
},
|
||||
variant.Culture);
|
||||
|
||||
variantIndex++;
|
||||
@@ -2172,7 +2172,10 @@ namespace Umbraco.Web.Editors
|
||||
/// <returns></returns>
|
||||
private ContentItemDisplay MapToDisplay(IContent content)
|
||||
{
|
||||
var display = Mapper.Map<ContentItemDisplay>(content);
|
||||
var display = Mapper.Map<ContentItemDisplay>(content, context =>
|
||||
{
|
||||
context.Items["CurrentUser"] = Security.CurrentUser;
|
||||
});
|
||||
display.AllowPreview = display.AllowPreview && content.Trashed == false && content.ContentType.IsElement == false;
|
||||
return display;
|
||||
}
|
||||
|
||||
@@ -427,7 +427,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
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
/// <summary>
|
||||
/// The API controller used for getting URLs for images with parameters
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This controller allows for retrieving URLs for processed images, such as resized, cropped,
|
||||
/// or otherwise altered. These can be different based on the IImageUrlGenerator
|
||||
/// implementation in use, and so the BackOffice could should not rely on hard-coded string
|
||||
/// building to generate correct URLs
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class ImageUrlGeneratorController : UmbracoAuthorizedJsonController
|
||||
{
|
||||
private readonly IImageUrlGenerator _imageUrlGenerator;
|
||||
|
||||
public ImageUrlGeneratorController(IImageUrlGenerator imageUrlGenerator)
|
||||
{
|
||||
_imageUrlGenerator = imageUrlGenerator;
|
||||
}
|
||||
|
||||
public string GetCropUrl(string mediaPath, int? width = null, int? height = null, ImageCropMode? imageCropMode = null, string animationProcessMode = null)
|
||||
{
|
||||
return mediaPath.GetCropUrl(_imageUrlGenerator, null, width: width, height: height, imageCropMode: imageCropMode, animationProcessMode: animationProcessMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,10 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Media;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi;
|
||||
@@ -19,11 +21,17 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
private readonly IContentSection _contentSection;
|
||||
private readonly IImageUrlGenerator _imageUrlGenerator;
|
||||
|
||||
public ImagesController(IMediaFileSystem mediaFileSystem, IContentSection contentSection)
|
||||
[Obsolete("This constructor will be removed in a future release. Please use the constructor with the IImageUrlGenerator overload")]
|
||||
public ImagesController(IMediaFileSystem mediaFileSystem, IContentSection contentSection) : this (mediaFileSystem, contentSection, Current.ImageUrlGenerator)
|
||||
{
|
||||
}
|
||||
public ImagesController(IMediaFileSystem mediaFileSystem, IContentSection contentSection, IImageUrlGenerator imageUrlGenerator)
|
||||
{
|
||||
_mediaFileSystem = mediaFileSystem;
|
||||
_contentSection = contentSection;
|
||||
_imageUrlGenerator = imageUrlGenerator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -75,12 +83,10 @@ namespace Umbraco.Web.Editors
|
||||
// so ignore and we won't set a last modified date.
|
||||
}
|
||||
|
||||
// TODO: When we abstract imaging for netcore, we are actually just going to be abstracting a URI builder for images, this
|
||||
// is one of those places where this can be used.
|
||||
var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : null;
|
||||
var imageUrl = _imageUrlGenerator.GetImageUrl(new ImageUrlGenerationOptions(imagePath) { UpScale = false, Width = width, AnimationProcessMode = "first", ImageCropMode = "max", CacheBusterValue = rnd });
|
||||
|
||||
var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : string.Empty;
|
||||
|
||||
response.Headers.Location = new Uri($"{imagePath}?upscale=false&width={width}&animationprocessmode=first&mode=max{rnd}", UriKind.RelativeOrAbsolute);
|
||||
response.Headers.Location = new Uri(imageUrl, UriKind.RelativeOrAbsolute);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@@ -102,8 +102,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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user