Compare commits
136 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31767a4152 | |||
| d79134c710 | |||
| 2ed4948619 | |||
| 0e67c787a8 | |||
| ebfb2b536f | |||
| 81010444f3 | |||
| bbbc1949bc | |||
| 7b34e59222 | |||
| 3706405bff | |||
| 8a0efd7790 | |||
| bd2ac3d128 | |||
| c25b710b80 | |||
| 09e060ff92 | |||
| 2248a85876 | |||
| a3f4b2db5d | |||
| 5947f01590 | |||
| 75a006ca87 | |||
| cee5880501 | |||
| f65002ea16 | |||
| bd1bfa416c | |||
| 317f96a1bd | |||
| a42d6b954f | |||
| 71b8674664 | |||
| 9e3ca51286 | |||
| 0e6f2b542e | |||
| 218f523022 | |||
| 50c6fb036a | |||
| bc2115ff0a | |||
| 9eee5c2bc0 | |||
| 038163421b | |||
| 7ce58f6e5f | |||
| c6470decb2 | |||
| 8a28639331 | |||
| 65b2b7cb82 | |||
| 2daf71aee4 | |||
| 339edb2295 | |||
| e117b764fb | |||
| ed3096642d | |||
| d51082f651 | |||
| f2fb275b34 | |||
| 95508c4fb7 | |||
| 4677faff38 | |||
| c5f388f6bc | |||
| 4419af427b | |||
| dbdee5fc2c | |||
| 725b3c5eca | |||
| 25f5179b29 | |||
| bf6d084f82 | |||
| 1a03bd9624 | |||
| 6a1d60c5a7 | |||
| 4fef17190f | |||
| 08ca75bb35 | |||
| 143b9c605b | |||
| d76a0271e9 | |||
| 5deb483b94 | |||
| db809f30fe | |||
| dcb50d10c4 | |||
| a409b8b0f7 | |||
| 0512eaa7fd | |||
| a91a67ed8d | |||
| e8df5f9eb4 | |||
| af1257d4ae | |||
| 1f3e568a0d | |||
| c46678c766 | |||
| cb539e82d0 | |||
| 1432d602ba | |||
| 841d2a7010 | |||
| 7cdb48b6c9 | |||
| a667058422 | |||
| 4ffa6bb41b | |||
| 77af16db55 | |||
| 2a44b73056 | |||
| 85d85fa587 | |||
| bd27ed739d | |||
| 3938868115 | |||
| 140792430f | |||
| c04cf1fe2e | |||
| d1d83c84a0 | |||
| 98acc50e13 | |||
| 1a0a02463e | |||
| 96c499a47e | |||
| f7e8aefeab | |||
| dfa803293f | |||
| 7ade64f855 | |||
| 346e8dcaf0 | |||
| ddfd07bf4a | |||
| a9fe46da0c | |||
| aca1f0d186 | |||
| 9eb415d398 | |||
| 8f6b728534 | |||
| 2e0c09ab39 | |||
| f211344786 | |||
| f8764cdd20 | |||
| 1d17bfd59a | |||
| 98ad6ad366 | |||
| 68702b98f5 | |||
| f93caf277b | |||
| 32db674397 | |||
| e0a7698780 | |||
| 783c74d3ba | |||
| 96d7bc3898 | |||
| 21963c03a2 | |||
| a2a9794fdb | |||
| 879a132077 | |||
| 9650d314c6 | |||
| fcc0c452bb | |||
| 606ebcae92 | |||
| af10ae9e34 | |||
| a23c150738 | |||
| 085a678266 | |||
| c73fb6144b | |||
| 5a2b4f031b | |||
| cf7cafeb4c | |||
| 870657affa | |||
| fe11d4770d | |||
| ac75d8148d | |||
| 4a32cddf08 | |||
| 7461e7c55b | |||
| c8fe9030c7 | |||
| b90cccbfff | |||
| 22f0641c54 | |||
| e465a937c5 | |||
| d2164bc94d | |||
| df9d09725b | |||
| 0a56f46b12 | |||
| 972f9b3546 | |||
| 28ab176466 | |||
| 4093fd9b79 | |||
| 5027757760 | |||
| d4d040d9ec | |||
| 4f1b76f5ff | |||
| 3b3eda5bfc | |||
| becc07c2bd | |||
| 5841377431 | |||
| 5d69ec2391 | |||
| 9f9b4d2e84 |
@@ -33,7 +33,7 @@
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
|
||||
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
|
||||
<dependency id="Newtonsoft.Json" version="[9.0.1, 10.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.81, 1.0.0)" />
|
||||
<dependency id="Examine" version="[0.1.82, 1.0.0)" />
|
||||
<dependency id="ImageProcessor" version="[2.5.2, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web" version="[4.8.2, 5.0.0)" />
|
||||
<dependency id="semver" version="[2.0.4, 3.0.0)" />
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<dependencies>
|
||||
<dependency id="UmbracoCms.Core" version="[$version$]" />
|
||||
<dependency id="Newtonsoft.Json" version="[9.0.1, 10.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.5, 4.0.0)" />
|
||||
<dependency id="Umbraco.ModelsBuilder" version="[3.0.6, 4.0.0)" />
|
||||
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
|
||||
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
|
||||
</dependencies>
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<httpRuntime xdt:Transform="InsertIfMissing" />
|
||||
<httpRuntime maxRequestLength="51200" fcnMode="Single" xdt:Transform="SetAttributes(fcnMode,maxRequestLength)" />
|
||||
<httpRuntime targetFramework="4.5" xdt:Locator="Condition(count(@targetFramework) != 1)" xdt:Transform="SetAttributes(targetFramework)" />
|
||||
|
||||
|
||||
<membership defaultProvider="DefaultMembershipProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove" />
|
||||
<roleManager defaultProvider="DefaultRoleProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>
|
||||
<profile defaultProvider="DefaultProfileProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>>
|
||||
@@ -337,7 +337,7 @@
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="HtmlAgilityPack" publicKeyToken="bd319b19eaf3b43a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.4.9.0" newVersion="1.4.9.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.4.9.5" newVersion="1.4.9.5" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly xdt:Transform="Insert">
|
||||
<assemblyIdentity name="AutoMapper" publicKeyToken="be96cd2c38ef1005" culture="neutral" />
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
|
||||
7.6.0
|
||||
beta05
|
||||
RC
|
||||
+1
-1
@@ -12,4 +12,4 @@ using System.Resources;
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.6.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.0-beta05")]
|
||||
[assembly: AssemblyInformationalVersion("7.6.0-RC")]
|
||||
@@ -17,9 +17,12 @@ namespace Umbraco.Core
|
||||
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
|
||||
}
|
||||
|
||||
private static readonly Regex Log4NetAssemblyPattern = new Regex("log4net, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=null", RegexOptions.Compiled);
|
||||
private static readonly Regex Log4NetAssemblyPattern = new Regex("log4net, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=null");
|
||||
private const string Log4NetReplacement = "log4net, Version=1.2.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a";
|
||||
|
||||
|
||||
private static readonly Regex HtmlAgilityAssemblyPattern = new Regex("HtmlAgilityPack, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=bd319b19eaf3b43a");
|
||||
private const string HtmlAgilityReplacement = "HtmlAgilityPack, Version=1.4.9.5, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a";
|
||||
|
||||
/// <summary>
|
||||
/// This is used to do an assembly binding redirect via code - normally required due to signature changes in assemblies
|
||||
/// </summary>
|
||||
@@ -28,13 +31,18 @@ namespace Umbraco.Core
|
||||
/// <returns></returns>
|
||||
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
|
||||
{
|
||||
//log4net:
|
||||
// Use regex to match and replace
|
||||
//log4net:
|
||||
if (Log4NetAssemblyPattern.IsMatch(args.Name) && args.Name != Log4NetReplacement)
|
||||
{
|
||||
return Assembly.Load(Log4NetAssemblyPattern.Replace(args.Name, Log4NetReplacement));
|
||||
}
|
||||
|
||||
//HtmlAgility:
|
||||
if (HtmlAgilityAssemblyPattern.IsMatch(args.Name) && args.Name != HtmlAgilityReplacement)
|
||||
{
|
||||
return Assembly.Load(HtmlAgilityAssemblyPattern.Replace(args.Name, HtmlAgilityReplacement));
|
||||
}
|
||||
|
||||
//AutoMapper:
|
||||
// ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again
|
||||
// do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider)
|
||||
{
|
||||
if (innerProvider.GetType() == typeof(DeepCloneRuntimeCacheProvider))
|
||||
throw new InvalidOperationException("A " + typeof(DeepCloneRuntimeCacheProvider) + " cannot wrap another instance of " + typeof(DeepCloneRuntimeCacheProvider));
|
||||
|
||||
InnerProvider = innerProvider;
|
||||
}
|
||||
|
||||
@@ -105,9 +108,11 @@ namespace Umbraco.Core.Cache
|
||||
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
|
||||
if (value == null) return null; // do not store null values (backward compat)
|
||||
|
||||
//Clone/reset to go into the cache
|
||||
return CheckCloneableAndTracksChanges(value);
|
||||
}, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
|
||||
//Clone/reset to go out of the cache
|
||||
return CheckCloneableAndTracksChanges(cached);
|
||||
}
|
||||
|
||||
|
||||
@@ -183,6 +183,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
get { return GetOptionalTextElement("EnablePropertyValueConverters", false); }
|
||||
}
|
||||
|
||||
[ConfigurationProperty("loginBackgroundImage")]
|
||||
internal InnerTextConfigurationElement<string> LoginBackgroundImage
|
||||
{
|
||||
get { return GetOptionalTextElement("loginBackgroundImage", string.Empty); }
|
||||
}
|
||||
|
||||
string IContentSection.NotificationEmailAddress
|
||||
{
|
||||
get { return Notifications.NotificationEmailAddress; }
|
||||
@@ -336,5 +342,10 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
get { return EnablePropertyValueConverters; }
|
||||
}
|
||||
|
||||
string IContentSection.LoginBackgroundImage
|
||||
{
|
||||
get { return LoginBackgroundImage; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,5 +72,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
|
||||
bool EnablePropertyValueConverters { get; }
|
||||
|
||||
string LoginBackgroundImage { get; }
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
|
||||
/// Gets the version comment (like beta or RC).
|
||||
/// </summary>
|
||||
/// <value>The version comment.</value>
|
||||
public static string CurrentComment { get { return "beta05"; } }
|
||||
public static string CurrentComment { get { return "RC"; } }
|
||||
|
||||
// Get the version of the umbraco.dll by looking at a class in that dll
|
||||
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Core.Deploy
|
||||
@@ -8,7 +9,7 @@ namespace Umbraco.Core.Deploy
|
||||
/// <summary>
|
||||
/// Connects to an Umbraco service.
|
||||
/// </summary>
|
||||
public interface IServiceConnector
|
||||
public interface IServiceConnector : IDiscoverable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an artifact.
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace Umbraco.Core.IO
|
||||
private long _folderCounter;
|
||||
private bool _folderCounterInitialized;
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
private static readonly Dictionary<int, string> DefaultSizes = new Dictionary<int, string>
|
||||
{
|
||||
{ 100, "thumb" },
|
||||
@@ -263,7 +264,7 @@ namespace Umbraco.Core.IO
|
||||
var filename = Path.GetFileName(sourcepath);
|
||||
var filepath = GetMediaPath(filename, content.Key, propertyType.Key);
|
||||
this.CopyFile(sourcepath, filepath);
|
||||
CopyThumbnails(sourcepath, filepath);
|
||||
|
||||
return filepath;
|
||||
}
|
||||
|
||||
@@ -312,29 +313,17 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
}
|
||||
|
||||
// sets a file for the FileUpload property editor
|
||||
// ie generates thumbnails and populates autofill properties
|
||||
/// <summary>
|
||||
/// Sets a file for the FileUpload property editor and populates autofill properties
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <param name="property"></param>
|
||||
/// <param name="filepath"></param>
|
||||
/// <param name="filestream"></param>
|
||||
private void SetUploadFile(IContentBase content, Property property, string filepath, Stream filestream)
|
||||
{
|
||||
// check if file is an image (and supports resizing and thumbnails etc)
|
||||
var extension = Path.GetExtension(filepath);
|
||||
var isImage = IsImageFile(extension);
|
||||
|
||||
// specific stuff for images (thumbnails etc)
|
||||
if (isImage)
|
||||
{
|
||||
using (var image = Image.FromStream(filestream))
|
||||
{
|
||||
// use one image for all
|
||||
GenerateThumbnails(image, filepath, property.PropertyType);
|
||||
_uploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream, image);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// will use filepath for extension, and filestream for length
|
||||
_uploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream);
|
||||
}
|
||||
{
|
||||
// will use filepath for extension, and filestream for length
|
||||
_uploadAutoFillProperties.Populate(content, property.Alias, filepath, filestream);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -401,6 +390,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
// note: this does not find 'custom' thumbnails?
|
||||
// will find _thumb and _big-thumb but NOT _custom?
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public IEnumerable<string> GetThumbnails(string path)
|
||||
{
|
||||
var parentDirectory = Path.GetDirectoryName(path);
|
||||
@@ -421,12 +411,14 @@ namespace Umbraco.Core.IO
|
||||
DeleteThumbnails(path);
|
||||
}
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public void DeleteThumbnails(string path)
|
||||
{
|
||||
GetThumbnails(path)
|
||||
.ForEach(x => base.DeleteFile(x));
|
||||
}
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public void CopyThumbnails(string sourcePath, string targetPath)
|
||||
{
|
||||
var targetPathBase = Path.GetDirectoryName(targetPath) ?? "";
|
||||
@@ -479,6 +471,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
#region GenerateThumbnails
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public IEnumerable<ResizedImage> GenerateThumbnails(
|
||||
Image image,
|
||||
string filepath,
|
||||
@@ -500,6 +493,7 @@ namespace Umbraco.Core.IO
|
||||
return GenerateThumbnails(image, filepath, additionalSizes);
|
||||
}
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public IEnumerable<ResizedImage> GenerateThumbnails(
|
||||
Image image,
|
||||
string filepath,
|
||||
@@ -522,6 +516,7 @@ namespace Umbraco.Core.IO
|
||||
.ToList(); // now
|
||||
}
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public IEnumerable<ResizedImage> GenerateThumbnails(
|
||||
Stream filestream,
|
||||
string filepath,
|
||||
@@ -535,6 +530,7 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public IEnumerable<ResizedImage> GenerateThumbnails(
|
||||
Image image,
|
||||
string filepath,
|
||||
@@ -556,16 +552,19 @@ namespace Umbraco.Core.IO
|
||||
|
||||
#region GenerateResized - Generate at resized filepath derived from origin filepath
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public ResizedImage GenerateResized(Image originImage, string originFilepath, string sizeName, int maxWidthHeight)
|
||||
{
|
||||
return GenerateResized(originImage, originFilepath, sizeName, maxWidthHeight, -1, -1);
|
||||
}
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public ResizedImage GenerateResized(Image originImage, string originFilepath, string sizeName, int fixedWidth, int fixedHeight)
|
||||
{
|
||||
return GenerateResized(originImage, originFilepath, sizeName, -1, fixedWidth, fixedHeight);
|
||||
}
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public ResizedImage GenerateResized(Image originImage, string originFilepath, string sizeName, int maxWidthHeight, int fixedWidth, int fixedHeight)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sizeName))
|
||||
@@ -581,16 +580,19 @@ namespace Umbraco.Core.IO
|
||||
|
||||
#region GenerateResizedAt - Generate at specified resized filepath
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public ResizedImage GenerateResizedAt(Image originImage, string resizedFilepath, int maxWidthHeight)
|
||||
{
|
||||
return GenerateResizedAt(originImage, resizedFilepath, maxWidthHeight, -1, -1);
|
||||
}
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public ResizedImage GenerateResizedAt(Image originImage, int fixedWidth, int fixedHeight, string resizedFilepath)
|
||||
{
|
||||
return GenerateResizedAt(originImage, resizedFilepath, -1, fixedWidth, fixedHeight);
|
||||
}
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public ResizedImage GenerateResizedAt(Image originImage, string resizedFilepath, int maxWidthHeight, int fixedWidth, int fixedHeight)
|
||||
{
|
||||
// target dimensions
|
||||
@@ -699,6 +701,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
#region Inner classes
|
||||
|
||||
[Obsolete("This should no longer be used, image manipulation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public class ResizedImage
|
||||
{
|
||||
public ResizedImage()
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Media
|
||||
{
|
||||
public interface IImageUrlProvider
|
||||
// note: because this interface is obsolete is is *not* IDiscoverable, and in case the
|
||||
// PluginManager is asked to find types implementing this interface it will fall back
|
||||
// to a complete scan.
|
||||
|
||||
[Obsolete("IImageUrlProvider is no longer used and will be removed in future versions")]
|
||||
public interface IImageUrlProvider // IDiscoverable
|
||||
{
|
||||
string Name { get; }
|
||||
string GetImageUrlFromMedia(int mediaId, IDictionary<string, string> parameters);
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
namespace Umbraco.Core.Media
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Media
|
||||
{
|
||||
public interface IThumbnailProvider
|
||||
// note: because this interface is obsolete is is *not* IDiscoverable, and in case the
|
||||
// PluginManager is asked to find types implementing this interface it will fall back
|
||||
// to a complete scan.
|
||||
|
||||
[Obsolete("Thumbnails are generated by ImageProcessor, use that instead")]
|
||||
public interface IThumbnailProvider // : IDiscoverable
|
||||
{
|
||||
bool CanProvideThumbnail(string fileUrl);
|
||||
string GetThumbnailUrl(string fileUrl);
|
||||
|
||||
@@ -96,8 +96,7 @@ namespace Umbraco.Core.Media
|
||||
/// <param name="propertyTypeAlias">The property type alias.</param>
|
||||
/// <param name="filepath">The filesystem-relative filepath, or null to clear properties.</param>
|
||||
/// <param name="filestream">The stream containing the file data.</param>
|
||||
/// <param name="image">The file data as an image object.</param>
|
||||
public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream, Image image = null)
|
||||
public void Populate(IContentBase content, string propertyTypeAlias, string filepath, Stream filestream)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
if (propertyTypeAlias == null) throw new ArgumentNullException("propertyTypeAlias");
|
||||
@@ -110,7 +109,7 @@ namespace Umbraco.Core.Media
|
||||
if (autoFillConfig == null) return; // nothing
|
||||
|
||||
// populate
|
||||
Populate(content, autoFillConfig, filepath, filestream, image);
|
||||
Populate(content, autoFillConfig, filepath, filestream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -158,8 +157,7 @@ namespace Umbraco.Core.Media
|
||||
/// <param name="autoFillConfig"></param>
|
||||
/// <param name="filepath">The filesystem-relative filepath, or null to clear properties.</param>
|
||||
/// <param name="filestream">The stream containing the file data.</param>
|
||||
/// <param name="image">The file data as an image object.</param>
|
||||
public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream, Image image = null)
|
||||
public void Populate(IContentBase content, IImagingAutoFillUploadField autoFillConfig, string filepath, Stream filestream)
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException("content");
|
||||
if (autoFillConfig == null) throw new ArgumentNullException("autoFillConfig");
|
||||
@@ -172,11 +170,7 @@ namespace Umbraco.Core.Media
|
||||
else
|
||||
{
|
||||
var extension = (Path.GetExtension(filepath) ?? "").TrimStart('.');
|
||||
Size? size;
|
||||
if (image == null)
|
||||
size = _mediaFileSystem.IsImageFile(extension) ? (Size?) _mediaFileSystem.GetDimensions(filestream) : null;
|
||||
else
|
||||
size = new Size(image.Width, image.Height);
|
||||
var size = _mediaFileSystem.IsImageFile(extension) ? (Size?)_mediaFileSystem.GetDimensions(filestream) : null;
|
||||
SetProperties(content, autoFillConfig, size, filestream.Length, extension);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,6 +274,9 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
public bool HasPublishedVersion { get { return PublishedVersionGuid != default(Guid); } }
|
||||
|
||||
[IgnoreDataMember]
|
||||
internal DateTime PublishedDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Changes the Trashed state of the content object
|
||||
/// </summary>
|
||||
|
||||
@@ -19,5 +19,8 @@ namespace Umbraco.Core.Models.Rdbms
|
||||
|
||||
[Column("newest")]
|
||||
public bool Newest { get; set; }
|
||||
|
||||
[Column("updateDate")]
|
||||
public DateTime VersionDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
private readonly string _httpContextKey;
|
||||
private readonly List<Type> _instanceTypes = new List<Type>();
|
||||
private IEnumerable<TResolved> _sortedValues;
|
||||
private readonly Func<HttpContextBase> _httpContextGetter;
|
||||
|
||||
private int _defaultPluginWeight = 100;
|
||||
|
||||
@@ -42,12 +43,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
if (logger == null) throw new ArgumentNullException("logger");
|
||||
CanResolveBeforeFrozen = false;
|
||||
if (scope == ObjectLifetimeScope.HttpRequest)
|
||||
{
|
||||
if (HttpContext.Current == null)
|
||||
throw new InvalidOperationException("Use alternative constructor accepting a HttpContextBase object in order to set the lifetime scope to HttpRequest when HttpContext.Current is null");
|
||||
|
||||
CurrentHttpContext = new HttpContextWrapper(HttpContext.Current);
|
||||
}
|
||||
_httpContextGetter = () => new HttpContextWrapper(HttpContext.Current);
|
||||
|
||||
ServiceProvider = serviceProvider;
|
||||
Logger = logger;
|
||||
@@ -84,7 +80,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
LifetimeScope = ObjectLifetimeScope.HttpRequest;
|
||||
_httpContextKey = GetType().FullName;
|
||||
ServiceProvider = serviceProvider;
|
||||
CurrentHttpContext = httpContext;
|
||||
_httpContextGetter = () => httpContext;
|
||||
_instanceTypes = new List<Type>();
|
||||
|
||||
InitializeAppInstances();
|
||||
@@ -160,7 +156,16 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// Gets or sets the <see cref="HttpContextBase"/> used to initialize this object, if any.
|
||||
/// </summary>
|
||||
/// <remarks>If not null, then <c>LifetimeScope</c> will be <c>ObjectLifetimeScope.HttpRequest</c>.</remarks>
|
||||
protected HttpContextBase CurrentHttpContext { get; private set; }
|
||||
protected HttpContextBase CurrentHttpContext
|
||||
{
|
||||
get
|
||||
{
|
||||
var context = _httpContextGetter == null ? null : _httpContextGetter();
|
||||
if (context == null)
|
||||
throw new InvalidOperationException("Cannot use this resolver with lifetime 'HttpRequest' when there is no current HttpContext. Either use the ctor accepting an HttpContextBase, or use the resolver from within a request exclusively.");
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the service provider used to instantiate objects
|
||||
@@ -196,7 +201,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// <summary>
|
||||
/// Gets or sets the default type weight.
|
||||
/// </summary>
|
||||
/// <remarks>Determines the weight of types that do not have a <c>WeightAttribute</c> set on
|
||||
/// <remarks>Determines the weight of types that do not have a <c>WeightAttribute</c> set on
|
||||
/// them, when calling <c>GetSortedValues</c>.</remarks>
|
||||
protected virtual int DefaultPluginWeight
|
||||
{
|
||||
@@ -276,7 +281,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// Removes a type.
|
||||
/// </summary>
|
||||
/// <param name="value">The type to remove.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
|
||||
/// the type is not a valid type for the resolver.</exception>
|
||||
public virtual void RemoveType(Type value)
|
||||
{
|
||||
@@ -296,7 +301,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// Removes a type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to remove.</typeparam>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support removing types, or
|
||||
/// the type is not a valid type for the resolver.</exception>
|
||||
public void RemoveType<T>()
|
||||
where T : TResolved
|
||||
@@ -309,7 +314,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <param name="types">The types to add.</param>
|
||||
/// <remarks>The types are appended at the end of the list.</remarks>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// a type is not a valid type for the resolver, or a type is already in the collection of types.</exception>
|
||||
protected void AddTypes(IEnumerable<Type> types)
|
||||
{
|
||||
@@ -336,7 +341,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <param name="value">The type to add.</param>
|
||||
/// <remarks>The type is appended at the end of the list.</remarks>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
public virtual void AddType(Type value)
|
||||
{
|
||||
@@ -362,7 +367,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type to add.</typeparam>
|
||||
/// <remarks>The type is appended at the end of the list.</remarks>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support adding types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
public void AddType<T>()
|
||||
where T : TResolved
|
||||
@@ -404,7 +409,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <param name="index">The zero-based index at which the type should be inserted.</param>
|
||||
/// <param name="value">The type to insert.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
/// <exception cref="ArgumentOutOfRangeException"><paramref name="index"/> is out of range.</exception>
|
||||
public virtual void InsertType(int index, Type value)
|
||||
@@ -430,7 +435,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// Inserts a type at the beginning of the list.
|
||||
/// </summary>
|
||||
/// <param name="value">The type to insert.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// the type is not a valid type for the resolver, or the type is already in the collection of types.</exception>
|
||||
public virtual void InsertType(Type value)
|
||||
{
|
||||
@@ -464,7 +469,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <param name="existingType">The existing type before which to insert.</param>
|
||||
/// <param name="value">The type to insert.</param>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// one of the types is not a valid type for the resolver, or the existing type is not in the collection,
|
||||
/// or the new type is already in the collection of types.</exception>
|
||||
public virtual void InsertTypeBefore(Type existingType, Type value)
|
||||
@@ -498,7 +503,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// </summary>
|
||||
/// <typeparam name="TExisting">The existing type before which to insert.</typeparam>
|
||||
/// <typeparam name="T">The type to insert.</typeparam>
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// <exception cref="InvalidOperationException">the resolver does not support inserting types, or
|
||||
/// one of the types is not a valid type for the resolver, or the existing type is not in the collection,
|
||||
/// or the new type is already in the collection of types.</exception>
|
||||
public void InsertTypeBefore<TExisting, T>()
|
||||
|
||||
@@ -63,49 +63,6 @@ namespace Umbraco.Core.ObjectResolution
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE - the ugly code below exists only because of umbraco.BusinessLogic.Actions.Action.ReRegisterActionsAndHandlers
|
||||
// which wants to re-register actions and handlers instead of properly restarting the application. Don't even think
|
||||
// about using it for anything else. Also, while the backdoor is open, the resolution system is locked so nothing
|
||||
// can work properly => deadlocks. Therefore, open the backdoor, do resolution changes EXCLUSIVELY, and close the door!
|
||||
|
||||
/// <summary>
|
||||
/// Returns a disposable object that reprents dirty access to temporarily unfrozen resolution configuration.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Should not be used.</para>
|
||||
/// <para>Should be used in a <c>using(Resolution.DirtyBackdoorToConfiguration) { ... }</c> mode.</para>
|
||||
/// <para>Because we just lift the frozen state, and we don't actually re-freeze, the <c>Frozen</c> event does not trigger.</para>
|
||||
/// </remarks>
|
||||
internal static IDisposable DirtyBackdoorToConfiguration
|
||||
{
|
||||
get { return new DirtyBackdoor(); }
|
||||
}
|
||||
|
||||
// keep the class here because it needs write-access to Resolution.IsFrozen
|
||||
private class DirtyBackdoor : IDisposable
|
||||
{
|
||||
|
||||
private readonly IDisposable _lock;
|
||||
private readonly bool _frozen;
|
||||
|
||||
public DirtyBackdoor()
|
||||
{
|
||||
LogHelper.Debug(typeof(DirtyBackdoor), "Creating back door for resolution");
|
||||
|
||||
_lock = new WriteLock(ConfigurationLock);
|
||||
_frozen = _isFrozen;
|
||||
_isFrozen = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
LogHelper.Debug(typeof(DirtyBackdoor), "Disposing back door for resolution");
|
||||
|
||||
_isFrozen = _frozen;
|
||||
_lock.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Freezes resolution.
|
||||
/// </summary>
|
||||
|
||||
@@ -72,6 +72,9 @@ namespace Umbraco.Core.Persistence.Factories
|
||||
content.PublishedVersionGuid = publishedDto == null
|
||||
? (dto.DocumentPublishedReadOnlyDto == null ? default(Guid) : dto.DocumentPublishedReadOnlyDto.VersionId)
|
||||
: publishedDto.VersionId;
|
||||
content.PublishedDate = publishedDto == null
|
||||
? (dto.DocumentPublishedReadOnlyDto == null ? default(DateTime) : dto.DocumentPublishedReadOnlyDto.VersionDate)
|
||||
: publishedDto.VersionDate;
|
||||
|
||||
//on initial construction we don't want to have dirty properties tracked
|
||||
// http://issues.umbraco.org/issue/U4-1946
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Mappers
|
||||
{
|
||||
public abstract class BaseMapper
|
||||
public abstract class BaseMapper : IDiscoverable
|
||||
{
|
||||
private readonly ISqlSyntaxProvider _sqlSyntax;
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
namespace Umbraco.Core.Persistence.Migrations
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Core.Persistence.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface for database migrations
|
||||
/// </summary>
|
||||
public interface IMigration
|
||||
public interface IMigration : IDiscoverable
|
||||
{
|
||||
void Up();
|
||||
void Down();
|
||||
|
||||
@@ -513,11 +513,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto
|
||||
{
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.UpdateDate,
|
||||
Newest = true,
|
||||
NodeId = dto.NodeId,
|
||||
Published = true
|
||||
Published = true
|
||||
};
|
||||
((Content)entity).PublishedVersionGuid = dto.VersionId;
|
||||
((Content) entity).PublishedVersionGuid = dto.VersionId;
|
||||
((Content) entity).PublishedDate = dto.UpdateDate;
|
||||
}
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
@@ -688,22 +690,26 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto
|
||||
{
|
||||
VersionId = dto.VersionId,
|
||||
VersionDate = dto.UpdateDate,
|
||||
Newest = true,
|
||||
NodeId = dto.NodeId,
|
||||
Published = true
|
||||
};
|
||||
((Content)entity).PublishedVersionGuid = dto.VersionId;
|
||||
((Content) entity).PublishedVersionGuid = dto.VersionId;
|
||||
((Content) entity).PublishedDate = dto.UpdateDate;
|
||||
}
|
||||
else if (publishedStateChanged)
|
||||
{
|
||||
dto.DocumentPublishedReadOnlyDto = new DocumentPublishedReadOnlyDto
|
||||
{
|
||||
VersionId = default(Guid),
|
||||
VersionId = default (Guid),
|
||||
VersionDate = default (DateTime),
|
||||
Newest = false,
|
||||
NodeId = dto.NodeId,
|
||||
Published = false
|
||||
};
|
||||
((Content)entity).PublishedVersionGuid = default(Guid);
|
||||
((Content) entity).PublishedVersionGuid = default(Guid);
|
||||
((Content) entity).PublishedDate = default (DateTime);
|
||||
}
|
||||
|
||||
entity.ResetDirtyProperties();
|
||||
@@ -975,7 +981,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
|
||||
}
|
||||
|
||||
//order by update date DESC, if there is corrupted published flags we only want the latest!
|
||||
var publishedSql = new Sql(@"SELECT cmsDocument.nodeId, cmsDocument.published, cmsDocument.versionId, cmsDocument.newest
|
||||
var publishedSql = new Sql(@"SELECT cmsDocument.nodeId, cmsDocument.published, cmsDocument.versionId, cmsDocument.updateDate, cmsDocument.newest
|
||||
FROM cmsDocument INNER JOIN cmsContentVersion ON cmsContentVersion.VersionId = cmsDocument.versionId
|
||||
WHERE cmsDocument.published = 1 AND cmsDocument.nodeId IN
|
||||
(" + parsedOriginalSql + @")
|
||||
|
||||
@@ -1252,5 +1252,19 @@ WHERE cmsContentType." + aliasColumn + @" LIKE @pattern",
|
||||
while (aliases.Contains(test = alias + i)) i++;
|
||||
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>
|
||||
public bool HasContainerInPath(string contentPath)
|
||||
{
|
||||
var ids = contentPath.Split(',').Select(int.Parse);
|
||||
var sql = new Sql(@"SELECT COUNT(*) FROM cmsContentType
|
||||
INNER JOIN cmsContent ON cmsContentType.nodeId=cmsContent.contentType
|
||||
WHERE cmsContent.nodeId IN (@ids) AND cmsContentType.isContainer=@isContainer", new { ids, isContainer = true });
|
||||
return Database.ExecuteScalar<int>(sql) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,47 +276,33 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
public PreValueCollection GetPreValuesCollectionByDataTypeId(int dataTypeId)
|
||||
{
|
||||
var cached = IsolatedCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
|
||||
if (cached != null && cached.Any())
|
||||
{
|
||||
//return from the cache, ensure it's a cloned result
|
||||
return (PreValueCollection)cached.First().DeepClone();
|
||||
}
|
||||
|
||||
return GetAndCachePreValueCollection(dataTypeId);
|
||||
}
|
||||
|
||||
internal static string GetCacheKeyRegex(int preValueId)
|
||||
{
|
||||
return CacheKeys.DataTypePreValuesCacheKey + @"[-\d]+-([\d]*,)*" + preValueId + @"(?!\d)[,\d$]*";
|
||||
var collection = GetCachedPreValueCollection(dataTypeId);
|
||||
return collection;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a specific PreValue by its Id
|
||||
/// </summary>
|
||||
/// <param name="preValueId">Id of the PreValue to retrieve the value from</param>
|
||||
/// <returns>PreValue as a string</returns>
|
||||
public string GetPreValueAsString(int preValueId)
|
||||
{
|
||||
//We need to see if we can find the cached PreValueCollection based on the cache key above
|
||||
var collections = IsolatedCache.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + "_");
|
||||
|
||||
var cached = IsolatedCache.GetCacheItemsByKeyExpression<PreValueCollection>(GetCacheKeyRegex(preValueId));
|
||||
if (cached != null && cached.Any())
|
||||
{
|
||||
//return from the cache
|
||||
var collection = cached.First();
|
||||
var preVal = collection.FormatAsDictionary().Single(x => x.Value.Id == preValueId);
|
||||
return preVal.Value.Value;
|
||||
}
|
||||
var preValue = collections.SelectMany(x => x.FormatAsDictionary().Values).FirstOrDefault(x => x.Id == preValueId);
|
||||
if (preValue != null)
|
||||
return preValue.Value;
|
||||
|
||||
//go and find the data type id for the pre val id passed in
|
||||
|
||||
var dto = Database.FirstOrDefault<DataTypePreValueDto>("WHERE id = @preValueId", new { preValueId = preValueId });
|
||||
var dto = Database.FirstOrDefault<DataTypePreValueDto>("WHERE id = @preValueId", new { preValueId });
|
||||
if (dto == null)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
// go cache the collection
|
||||
var preVals = GetAndCachePreValueCollection(dto.DataTypeNodeId);
|
||||
|
||||
//return the single value for this id
|
||||
var pv = preVals.FormatAsDictionary().Single(x => x.Value.Id == preValueId);
|
||||
return pv.Value.Value;
|
||||
var collection = GetCachedPreValueCollection(dto.DataTypeNodeId);
|
||||
if (collection == null)
|
||||
return string.Empty;
|
||||
|
||||
preValue = collection.FormatAsDictionary().Values.FirstOrDefault(x => x.Id == preValueId);
|
||||
return preValue == null ? string.Empty : preValue.Value;
|
||||
}
|
||||
|
||||
public void AddOrUpdatePreValues(int dataTypeId, IDictionary<string, PreValue> values)
|
||||
@@ -441,40 +427,28 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
sortOrder++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private string GetPrefixedCacheKey(int dataTypeId)
|
||||
private static string GetPrefixedCacheKey(int dataTypeId)
|
||||
{
|
||||
return CacheKeys.DataTypePreValuesCacheKey + dataTypeId + "-";
|
||||
return CacheKeys.DataTypePreValuesCacheKey + "_" + dataTypeId;
|
||||
}
|
||||
|
||||
private PreValueCollection GetAndCachePreValueCollection(int dataTypeId)
|
||||
private PreValueCollection GetCachedPreValueCollection(int datetypeId)
|
||||
{
|
||||
//go get the data
|
||||
var dtos = Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = dataTypeId });
|
||||
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList();
|
||||
var collection = PreValueConverter.ConvertToPreValuesCollection(list);
|
||||
|
||||
//now create the cache key, this needs to include all pre-value ids so that we can use this cached item in the GetPreValuesAsString method
|
||||
//the key will be: "UmbracoPreValDATATYPEID-CSVOFPREVALIDS
|
||||
|
||||
var key = GetPrefixedCacheKey(dataTypeId)
|
||||
+ string.Join(",", collection.FormatAsDictionary().Select(x => x.Value.Id).ToArray());
|
||||
|
||||
//store into cache
|
||||
IsolatedCache.InsertCacheItem(key, () => collection,
|
||||
//30 mins
|
||||
new TimeSpan(0, 0, 30),
|
||||
//sliding is true
|
||||
true);
|
||||
|
||||
return collection;
|
||||
var key = GetPrefixedCacheKey(datetypeId);
|
||||
return IsolatedCache.GetCacheItem<PreValueCollection>(key, () =>
|
||||
{
|
||||
var dtos = Database.Fetch<DataTypePreValueDto>("WHERE datatypeNodeId = @Id", new { Id = datetypeId });
|
||||
var list = dtos.Select(x => new Tuple<PreValue, string, int>(new PreValue(x.Id, x.Value, x.SortOrder), x.Alias, x.SortOrder)).ToList();
|
||||
var collection = PreValueConverter.ConvertToPreValuesCollection(list);
|
||||
return collection;
|
||||
}, TimeSpan.FromMinutes(20), isSliding: true);
|
||||
}
|
||||
|
||||
private string EnsureUniqueNodeName(string nodeName, int id = 0)
|
||||
{
|
||||
|
||||
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
@@ -575,7 +549,7 @@ AND umbracoNode.id <> @id",
|
||||
}
|
||||
|
||||
//NOTE: We used to check that the Alias was unique for the given DataTypeNodeId prevalues list, BUT
|
||||
// in reality there is no need to check the uniqueness of this alias because the only way that this code executes is
|
||||
// in reality there is no need to check the uniqueness of this alias because the only way that this code executes is
|
||||
// based on an IDictionary<string, PreValue> dictionary being passed to this repository and a dictionary
|
||||
// must have unique aliases by definition, so there is no need for this additional check
|
||||
|
||||
@@ -595,10 +569,10 @@ AND umbracoNode.id <> @id",
|
||||
{
|
||||
throw new InvalidOperationException("Cannot update a pre value for a data type that has no identity");
|
||||
}
|
||||
|
||||
|
||||
//NOTE: We used to check that the Alias was unique for the given DataTypeNodeId prevalues list, BUT
|
||||
// this causes issues when sorting the pre-values (http://issues.umbraco.org/issue/U4-5670) but in reality
|
||||
// there is no need to check the uniqueness of this alias because the only way that this code executes is
|
||||
// there is no need to check the uniqueness of this alias because the only way that this code executes is
|
||||
// based on an IDictionary<string, PreValue> dictionary being passed to this repository and a dictionary
|
||||
// must have unique aliases by definition, so there is no need for this additional check
|
||||
|
||||
@@ -613,7 +587,7 @@ AND umbracoNode.id <> @id",
|
||||
Database.Update(dto);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
internal static class PreValueConverter
|
||||
|
||||
@@ -8,6 +8,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
{
|
||||
public interface IContentTypeRepository : IContentTypeCompositionRepository<IContentType>
|
||||
{
|
||||
/// <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>
|
||||
bool HasContainerInPath(string contentPath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all entities of the specified <see cref="PropertyType"/> query
|
||||
/// </summary>
|
||||
|
||||
@@ -49,7 +49,12 @@ namespace Umbraco.Core.Persistence
|
||||
_cacheHelper.IsolatedRuntimeCache.CacheFactory = type =>
|
||||
{
|
||||
var cache = origFactory(type);
|
||||
return new DeepCloneRuntimeCacheProvider(cache);
|
||||
|
||||
//if the result is already a DeepCloneRuntimeCacheProvider then return it, otherwise
|
||||
//wrap the result with a DeepCloneRuntimeCacheProvider
|
||||
return cache is DeepCloneRuntimeCacheProvider
|
||||
? cache
|
||||
: new DeepCloneRuntimeCacheProvider(cache);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+706
-631
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
public interface IParameterEditor
|
||||
public interface IParameterEditor : IDiscoverable
|
||||
{
|
||||
/// <summary>
|
||||
/// The id of the property editor
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using umbraco.interfaces;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
@@ -7,8 +8,8 @@ namespace Umbraco.Core.PropertyEditors
|
||||
/// </summary>
|
||||
// todo: drop IPropertyEditorValueConverter support (when?).
|
||||
[Obsolete("Use IPropertyValueConverter.")]
|
||||
public interface IPropertyEditorValueConverter
|
||||
{
|
||||
public interface IPropertyEditorValueConverter : IDiscoverable
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this provider applies to the specified property.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides published content properties conversion service.
|
||||
/// </summary>
|
||||
public interface IPropertyValueConverter
|
||||
public interface IPropertyValueConverter : IDiscoverable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the converter supports a property type.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
@@ -17,11 +19,13 @@ namespace Umbraco.Core.PropertyEditors
|
||||
internal class ParameterEditorResolver : LazyManyObjectsResolverBase<ParameterEditorResolver, IParameterEditor>
|
||||
{
|
||||
private readonly ManifestBuilder _builder;
|
||||
private readonly IContentSection _contentSection;
|
||||
|
||||
public ParameterEditorResolver(IServiceProvider serviceProvider, ILogger logger, Func<IEnumerable<Type>> typeListProducerList, ManifestBuilder builder)
|
||||
: base(serviceProvider, logger, typeListProducerList, ObjectLifetimeScope.Application)
|
||||
{
|
||||
_builder = builder;
|
||||
_contentSection = UmbracoConfig.For.UmbracoSettings().Content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -31,20 +35,27 @@ namespace Umbraco.Core.PropertyEditors
|
||||
{
|
||||
get
|
||||
{
|
||||
//This will by default include all property editors and parameter editors but we need to filter this
|
||||
//list to ensure that none of the property editors that do not have the IsParameterEditor flag set to true
|
||||
//are filtered.
|
||||
var filtered = Values.Select(x => x as PropertyEditor)
|
||||
.WhereNotNull()
|
||||
.Where(x => x.IsParameterEditor == false);
|
||||
|
||||
return Values
|
||||
//exclude the non parameter editor c# property editors
|
||||
.Except(filtered)
|
||||
//include the manifest parameter editors
|
||||
.Union(_builder.ParameterEditors)
|
||||
//include the manifest prop editors that are parameter editors
|
||||
// all property editors and parameter editors
|
||||
// except property editors where !IsParameterEditor
|
||||
var values = Values
|
||||
.Where(x => x is PropertyEditor == false || ((PropertyEditor) x).IsParameterEditor);
|
||||
|
||||
// union all manifest parameter editors
|
||||
values = values
|
||||
.Union(_builder.ParameterEditors);
|
||||
|
||||
// union all manifest property editors where IsParameterEditor
|
||||
values = values
|
||||
.Union(_builder.PropertyEditors.Where(x => x.IsParameterEditor));
|
||||
|
||||
if (_contentSection.ShowDeprecatedPropertyEditors == false)
|
||||
{
|
||||
// except deprecated property editors
|
||||
values = values
|
||||
.Where(x => x is PropertyEditor == false || ((PropertyEditor) x).IsDeprecated == false);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,14 +19,15 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public class ContentTypeService : ContentTypeServiceBase, IContentTypeService
|
||||
{
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IMediaService _mediaService;
|
||||
|
||||
//Support recursive locks because some of the methods that require locking call other methods that require locking.
|
||||
//for example, the Move method needs to be locked but this calls the Save method which also needs to be locked.
|
||||
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
|
||||
|
||||
public ContentTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IContentService contentService, IMediaService mediaService)
|
||||
public ContentTypeService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger, IEventMessagesFactory eventMessagesFactory, IContentService contentService,
|
||||
IMediaService mediaService)
|
||||
: base(provider, repositoryFactory, logger, eventMessagesFactory)
|
||||
{
|
||||
if (contentService == null) throw new ArgumentNullException("contentService");
|
||||
@@ -210,7 +211,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
public IEnumerable<EntityContainer> GetMediaTypeContainers(IMediaType mediaType)
|
||||
{
|
||||
var ancestorIds = mediaType.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
var ancestorIds = mediaType.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x =>
|
||||
{
|
||||
var asInt = x.TryConvertTo<int>();
|
||||
@@ -297,7 +298,7 @@ namespace Umbraco.Core.Services
|
||||
repo.Delete(container);
|
||||
uow.Commit();
|
||||
|
||||
uow.Events.Dispatch(DeletedContentTypeContainer, this, new DeleteEventArgs<EntityContainer>(container, evtMsgs));
|
||||
uow.Events.Dispatch(DeletedContentTypeContainer, this, new DeleteEventArgs<EntityContainer>(container, evtMsgs), "DeletedContentTypeContainer");
|
||||
|
||||
return OperationStatus.Success(evtMsgs);
|
||||
//TODO: Audit trail ?
|
||||
@@ -425,7 +426,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
clone.Name = name;
|
||||
|
||||
var compositionAliases = clone.CompositionAliases().Except(new[] { alias }).ToList();
|
||||
var compositionAliases = clone.CompositionAliases().Except(new[] {alias}).ToList();
|
||||
//remove all composition that is not it's current alias
|
||||
foreach (var a in compositionAliases)
|
||||
{
|
||||
@@ -712,7 +713,7 @@ namespace Umbraco.Core.Services
|
||||
var comparer = new DelegateEqualityComparer<IContentTypeComposition>((x, y) => x.Id == y.Id, x => x.Id);
|
||||
var dependencies = new HashSet<IContentTypeComposition>(compositions, comparer);
|
||||
var stack = new Stack<IContentTypeComposition>();
|
||||
indirectReferences.ForEach(stack.Push);//Push indirect references to a stack, so we can add recursively
|
||||
indirectReferences.ForEach(stack.Push); //Push indirect references to a stack, so we can add recursively
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var indirectReference = stack.Pop();
|
||||
@@ -770,6 +771,8 @@ namespace Umbraco.Core.Services
|
||||
|
||||
ValidateLocked(contentType); // throws if invalid
|
||||
contentType.CreatorId = userId;
|
||||
if (contentType.Description == string.Empty)
|
||||
contentType.Description = null;
|
||||
repository.AddOrUpdate(contentType);
|
||||
|
||||
uow.Commit();
|
||||
@@ -812,6 +815,8 @@ namespace Umbraco.Core.Services
|
||||
foreach (var contentType in asArray)
|
||||
{
|
||||
contentType.CreatorId = userId;
|
||||
if (contentType.Description == string.Empty)
|
||||
contentType.Description = null;
|
||||
repository.AddOrUpdate(contentType);
|
||||
}
|
||||
|
||||
@@ -1233,6 +1238,8 @@ namespace Umbraco.Core.Services
|
||||
|
||||
ValidateLocked(mediaType); // throws if invalid
|
||||
mediaType.CreatorId = userId;
|
||||
if (mediaType.Description == string.Empty)
|
||||
mediaType.Description = null;
|
||||
repository.AddOrUpdate(mediaType);
|
||||
uow.Commit();
|
||||
|
||||
@@ -1274,6 +1281,8 @@ namespace Umbraco.Core.Services
|
||||
foreach (var mediaType in asArray)
|
||||
{
|
||||
mediaType.CreatorId = userId;
|
||||
if (mediaType.Description == string.Empty)
|
||||
mediaType.Description = null;
|
||||
repository.AddOrUpdate(mediaType);
|
||||
}
|
||||
|
||||
@@ -1452,40 +1461,40 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IContentType>> DeletingContentType;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IContentType>> DeletedContentType;
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IContentType>> DeletedContentType;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IMediaType>> DeletingMediaType;
|
||||
/// <summary>
|
||||
/// Occurs before Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IMediaType>> DeletingMediaType;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IMediaType>> DeletedMediaType;
|
||||
/// <summary>
|
||||
/// Occurs after Delete
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, DeleteEventArgs<IMediaType>> DeletedMediaType;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IContentType>> SavingContentType;
|
||||
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IContentType>> SavingContentType;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IContentType>> SavedContentType;
|
||||
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IContentType>> SavedContentType;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IMediaType>> SavingMediaType;
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IMediaType>> SavingMediaType;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IMediaType>> SavedMediaType;
|
||||
/// <summary>
|
||||
/// Occurs after Save
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentTypeService, SaveEventArgs<IMediaType>> SavedMediaType;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Move
|
||||
|
||||
@@ -5,6 +5,7 @@ using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.EntityBase;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
|
||||
namespace Umbraco.Core.Services
|
||||
@@ -74,5 +75,20 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
return Enumerable.Empty<IContentTypeBase>();
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public bool HasContainerInPath(string contentPath)
|
||||
{
|
||||
using (var uow = UowProvider.GetUnitOfWork())
|
||||
{
|
||||
// can use same repo for both content and media
|
||||
var repository = RepositoryFactory.CreateContentTypeRepository(uow);
|
||||
return repository.HasContainerInPath(contentPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,13 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public interface IContentTypeService : IService
|
||||
{
|
||||
/// <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>
|
||||
bool HasContainerInPath(string contentPath);
|
||||
|
||||
int CountContentTypes();
|
||||
int CountMediaTypes();
|
||||
|
||||
|
||||
@@ -468,12 +468,7 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="filepath">The filesystem path to the media.</param>
|
||||
void DeleteMediaFile(string filepath);
|
||||
|
||||
/// <summary>
|
||||
/// Generates thumbnails.
|
||||
/// </summary>
|
||||
/// <param name="filepath">The filesystem-relative path to the original image.</param>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <remarks>This should be obsoleted, we should not generate thumbnails.</remarks>
|
||||
[Obsolete("This should no longer be used, thumbnail generation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
void GenerateThumbnails(string filepath, PropertyType propertyType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1431,6 +1431,7 @@ namespace Umbraco.Core.Services
|
||||
_mediaFileSystem.DeleteFile(filepath, true);
|
||||
}
|
||||
|
||||
[Obsolete("This should no longer be used, thumbnail generation should be done via ImageProcessor, Umbraco no longer generates '_thumb' files for media")]
|
||||
public void GenerateThumbnails(string filepath, PropertyType propertyType)
|
||||
{
|
||||
using (var filestream = _mediaFileSystem.OpenFile(filepath))
|
||||
|
||||
@@ -93,6 +93,8 @@ namespace Umbraco.Core.Services
|
||||
|
||||
var repository = RepositoryFactory.CreateMemberTypeRepository(uow);
|
||||
memberType.CreatorId = userId;
|
||||
if (memberType.Description == string.Empty)
|
||||
memberType.Description = null;
|
||||
repository.AddOrUpdate(memberType);
|
||||
uow.Commit(); // flush, so that the db contains the saved value
|
||||
|
||||
@@ -121,6 +123,8 @@ namespace Umbraco.Core.Services
|
||||
foreach (var memberType in asArray)
|
||||
{
|
||||
memberType.CreatorId = userId;
|
||||
if (memberType.Description == string.Empty)
|
||||
memberType.Description = null;
|
||||
repository.AddOrUpdate(memberType);
|
||||
}
|
||||
uow.Commit(); // flush, so that the db contains the saved values
|
||||
|
||||
+118
-225
@@ -1,32 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Security;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Compilation;
|
||||
using System.Web.Hosting;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// A utility class to find all classes of a certain type by reflection in the current bin folder
|
||||
/// of the web application.
|
||||
/// </summary>
|
||||
public static class TypeFinder
|
||||
{
|
||||
private static volatile HashSet<Assembly> _localFilteredAssemblyCache = null;
|
||||
private static volatile HashSet<Assembly> _localFilteredAssemblyCache;
|
||||
private static readonly object LocalFilteredAssemblyCacheLocker = new object();
|
||||
|
||||
/// <summary>
|
||||
@@ -63,7 +55,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
if (!(e.InnerException is SecurityException))
|
||||
if ((e.InnerException is SecurityException) == false)
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -99,7 +91,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
//if for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies.
|
||||
if (!assemblies.Any())
|
||||
if (assemblies.Any() == false)
|
||||
{
|
||||
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
@@ -111,12 +103,12 @@ namespace Umbraco.Core
|
||||
var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported
|
||||
var appCodeFolder = new DirectoryInfo(IOHelper.MapPath(IOHelper.ResolveUrl("~/App_code")));
|
||||
//check if the folder exists and if there are any files in it with the supported file extensions
|
||||
if (appCodeFolder.Exists && (fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())))
|
||||
if (appCodeFolder.Exists && fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any()))
|
||||
{
|
||||
try
|
||||
{
|
||||
var appCodeAssembly = Assembly.Load("App_Code");
|
||||
if (!assemblies.Contains(appCodeAssembly)) // BuildManager will find App_Code already
|
||||
if (assemblies.Contains(appCodeAssembly) == false) // BuildManager will find App_Code already
|
||||
assemblies.Add(appCodeAssembly);
|
||||
}
|
||||
catch (FileNotFoundException ex)
|
||||
@@ -128,7 +120,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
if (!(e.InnerException is SecurityException))
|
||||
if (e.InnerException is SecurityException == false)
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -145,23 +137,15 @@ namespace Umbraco.Core
|
||||
internal static HashSet<Assembly> GetAssembliesWithKnownExclusions(
|
||||
IEnumerable<Assembly> excludeFromResults = null)
|
||||
{
|
||||
if (_localFilteredAssemblyCache == null)
|
||||
lock (LocalFilteredAssemblyCacheLocker)
|
||||
{
|
||||
lock (LocalFilteredAssemblyCacheLocker)
|
||||
{
|
||||
//double check
|
||||
if (_localFilteredAssemblyCache == null)
|
||||
{
|
||||
_localFilteredAssemblyCache = new HashSet<Assembly>();
|
||||
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
|
||||
foreach (var a in assemblies)
|
||||
{
|
||||
_localFilteredAssemblyCache.Add(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_localFilteredAssemblyCache != null)
|
||||
return _localFilteredAssemblyCache;
|
||||
|
||||
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
|
||||
_localFilteredAssemblyCache = new HashSet<Assembly>(assemblies);
|
||||
return _localFilteredAssemblyCache;
|
||||
}
|
||||
return _localFilteredAssemblyCache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -180,9 +164,9 @@ namespace Umbraco.Core
|
||||
exclusionFilter = new string[] { };
|
||||
|
||||
return GetAllAssemblies()
|
||||
.Where(x => !excludeFromResults.Contains(x)
|
||||
&& !x.GlobalAssemblyCache
|
||||
&& !exclusionFilter.Any(f => x.FullName.StartsWith(f)));
|
||||
.Where(x => excludeFromResults.Contains(x) == false
|
||||
&& x.GlobalAssemblyCache == false
|
||||
&& exclusionFilter.Any(f => x.FullName.StartsWith(f)) == false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -298,7 +282,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
if (assemblies == null) throw new ArgumentNullException("assemblies");
|
||||
|
||||
return GetClasses(assignTypeFrom, assemblies, onlyConcreteClasses,
|
||||
return GetClassesWithBaseType(assignTypeFrom, assemblies, onlyConcreteClasses,
|
||||
//the additional filter will ensure that any found types also have the attribute applied.
|
||||
t => t.GetCustomAttributes<TAttribute>(false).Any());
|
||||
}
|
||||
@@ -324,7 +308,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
if (assemblies == null) throw new ArgumentNullException("assemblies");
|
||||
|
||||
return GetClasses(typeof(T), assemblies, onlyConcreteClasses);
|
||||
return GetClassesWithBaseType(typeof(T), assemblies, onlyConcreteClasses);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -363,105 +347,9 @@ namespace Umbraco.Core
|
||||
IEnumerable<Assembly> assemblies,
|
||||
bool onlyConcreteClasses)
|
||||
{
|
||||
if (assemblies == null) throw new ArgumentNullException("assemblies");
|
||||
|
||||
if (TypeHelper.IsTypeAssignableFrom<Attribute>(attributeType) == false)
|
||||
throw new ArgumentException("The type specified: " + attributeType + " is not an Attribute type");
|
||||
|
||||
var foundAttributedTypes = new HashSet<Type>();
|
||||
|
||||
var assemblyList = assemblies.ToArray();
|
||||
|
||||
//find all assembly references that are referencing the attribute type's assembly since we
|
||||
//should only be scanning those assemblies because any other assembly will definitely not
|
||||
//contain a class that has this attribute.
|
||||
var referencedAssemblies = TypeHelper.GetReferencedAssemblies(attributeType, assemblyList);
|
||||
|
||||
//get a list of non-referenced assemblies (we'll use this when we recurse below)
|
||||
var otherAssemblies = assemblyList.Where(x => referencedAssemblies.Contains(x) == false).ToArray();
|
||||
|
||||
//loop through the referenced assemblies
|
||||
foreach (var a in referencedAssemblies)
|
||||
{
|
||||
//get all types in this assembly
|
||||
var allTypes = GetTypesWithFormattedException(a)
|
||||
.ToArray();
|
||||
|
||||
var attributedTypes = new Type[] { };
|
||||
try
|
||||
{
|
||||
//now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes but have
|
||||
//the specified attribute
|
||||
attributedTypes = allTypes
|
||||
.Where(t => (TypeHelper.IsNonStaticClass(t)
|
||||
&& (onlyConcreteClasses == false || t.IsAbstract == false))
|
||||
//the type must have this attribute
|
||||
&& t.GetCustomAttributes(attributeType, false).Any())
|
||||
.ToArray();
|
||||
}
|
||||
catch (TypeLoadException ex)
|
||||
{
|
||||
LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", a), ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
//add the types to our list to return
|
||||
foreach (var t in attributedTypes)
|
||||
{
|
||||
foundAttributedTypes.Add(t);
|
||||
}
|
||||
|
||||
//get all attributes of the type being searched for
|
||||
var allAttributeTypes = allTypes.Where(attributeType.IsAssignableFrom);
|
||||
|
||||
//now we need to include types that may be inheriting from sub classes of the attribute type being searched for
|
||||
//so we will search in assemblies that reference those types too.
|
||||
foreach (var subTypesInAssembly in allAttributeTypes.GroupBy(x => x.Assembly))
|
||||
{
|
||||
|
||||
//So that we are not scanning too much, we need to group the sub types:
|
||||
// * if there is more than 1 sub type in the same assembly then we should only search on the 'lowest base' type.
|
||||
// * We should also not search for sub types if the type is sealed since you cannot inherit from a sealed class
|
||||
// * We should not search for sub types if the type is static since you cannot inherit from them.
|
||||
var subTypeList = subTypesInAssembly
|
||||
.Where(t => t.IsSealed == false && TypeHelper.IsStaticClass(t) == false)
|
||||
.ToArray();
|
||||
|
||||
var baseClassAttempt = TypeHelper.GetLowestBaseType(subTypeList);
|
||||
|
||||
//if there's a base class amongst the types then we'll only search for that type.
|
||||
//otherwise we'll have to search for all of them.
|
||||
var subTypesToSearch = new HashSet<Type>();
|
||||
if (baseClassAttempt.Success)
|
||||
{
|
||||
subTypesToSearch.Add(baseClassAttempt.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var t in subTypeList)
|
||||
{
|
||||
subTypesToSearch.Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var typeToSearch in subTypesToSearch)
|
||||
{
|
||||
//recursively find the types inheriting from this sub type in the other non-scanned assemblies.
|
||||
var foundTypes = FindClassesWithAttribute(typeToSearch, otherAssemblies, onlyConcreteClasses);
|
||||
|
||||
foreach (var f in foundTypes)
|
||||
{
|
||||
foundAttributedTypes.Add(f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return foundAttributedTypes;
|
||||
return GetClassesWithAttribute(attributeType, assemblies, onlyConcreteClasses);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Finds the classes with attribute.
|
||||
/// </summary>
|
||||
@@ -485,122 +373,129 @@ namespace Umbraco.Core
|
||||
return FindClassesWithAttribute<T>(GetAssembliesWithKnownExclusions());
|
||||
}
|
||||
|
||||
|
||||
#region Private methods
|
||||
|
||||
private static IEnumerable<Type> GetClassesWithAttribute(
|
||||
Type attributeType,
|
||||
IEnumerable<Assembly> assemblies,
|
||||
bool onlyConcreteClasses)
|
||||
{
|
||||
if (typeof(Attribute).IsAssignableFrom(attributeType) == false)
|
||||
throw new ArgumentException("Type " + attributeType + " is not an Attribute type.");
|
||||
|
||||
var candidateAssemblies = new HashSet<Assembly>(assemblies);
|
||||
var attributeAssemblyIsCandidate = candidateAssemblies.Contains(attributeType.Assembly);
|
||||
candidateAssemblies.Remove(attributeType.Assembly);
|
||||
var types = new List<Type>();
|
||||
|
||||
var stack = new Stack<Assembly>();
|
||||
stack.Push(attributeType.Assembly);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var assembly = stack.Pop();
|
||||
|
||||
Type[] assemblyTypes = null;
|
||||
if (assembly != attributeType.Assembly || attributeAssemblyIsCandidate)
|
||||
{
|
||||
// get all assembly types that can be assigned to baseType
|
||||
try
|
||||
{
|
||||
assemblyTypes = GetTypesWithFormattedException(assembly)
|
||||
.ToArray(); // in try block
|
||||
}
|
||||
catch (TypeLoadException ex)
|
||||
{
|
||||
LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly), ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
types.AddRange(assemblyTypes.Where(x =>
|
||||
x.IsClass // only classes
|
||||
&& (x.IsAbstract == false || x.IsSealed == false) // ie non-static, static is abstract and sealed
|
||||
&& x.IsNestedPrivate == false // exclude nested private
|
||||
&& (onlyConcreteClasses == false || x.IsAbstract == false) // exclude abstract
|
||||
&& x.GetCustomAttribute<HideFromTypeFinderAttribute>() == null // exclude hidden
|
||||
&& x.GetCustomAttributes(attributeType, false).Any())); // marked with the attribute
|
||||
}
|
||||
|
||||
if (assembly != attributeType.Assembly && assemblyTypes.Where(attributeType.IsAssignableFrom).Any() == false)
|
||||
continue;
|
||||
|
||||
foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies))
|
||||
{
|
||||
candidateAssemblies.Remove(referencing);
|
||||
stack.Push(referencing);
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds types that are assignable from the assignTypeFrom parameter and will scan for these types in the assembly
|
||||
/// list passed in, however we will only scan assemblies that have a reference to the assignTypeFrom Type or any type
|
||||
/// deriving from the base type.
|
||||
/// </summary>
|
||||
/// <param name="assignTypeFrom"></param>
|
||||
/// <param name="baseType"></param>
|
||||
/// <param name="assemblies"></param>
|
||||
/// <param name="onlyConcreteClasses"></param>
|
||||
/// <param name="additionalFilter">An additional filter to apply for what types will actually be included in the return value</param>
|
||||
/// <returns></returns>
|
||||
private static IEnumerable<Type> GetClasses(
|
||||
Type assignTypeFrom,
|
||||
private static IEnumerable<Type> GetClassesWithBaseType(
|
||||
Type baseType,
|
||||
IEnumerable<Assembly> assemblies,
|
||||
bool onlyConcreteClasses,
|
||||
Func<Type, bool> additionalFilter = null)
|
||||
{
|
||||
//the default filter will always return true.
|
||||
if (additionalFilter == null)
|
||||
var candidateAssemblies = new HashSet<Assembly>(assemblies);
|
||||
var baseTypeAssemblyIsCandidate = candidateAssemblies.Contains(baseType.Assembly);
|
||||
candidateAssemblies.Remove(baseType.Assembly);
|
||||
var types = new List<Type>();
|
||||
|
||||
var stack = new Stack<Assembly>();
|
||||
stack.Push(baseType.Assembly);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
additionalFilter = type => true;
|
||||
}
|
||||
var assembly = stack.Pop();
|
||||
|
||||
var foundAssignableTypes = new HashSet<Type>();
|
||||
|
||||
var assemblyList = assemblies.ToArray();
|
||||
|
||||
//find all assembly references that are referencing the current type's assembly since we
|
||||
//should only be scanning those assemblies because any other assembly will definitely not
|
||||
//contain sub type's of the one we're currently looking for
|
||||
var referencedAssemblies = TypeHelper.GetReferencedAssemblies(assignTypeFrom, assemblyList);
|
||||
|
||||
//get a list of non-referenced assemblies (we'll use this when we recurse below)
|
||||
var otherAssemblies = assemblyList.Where(x => referencedAssemblies.Contains(x) == false).ToArray();
|
||||
|
||||
//loop through the referenced assemblies
|
||||
foreach (var a in referencedAssemblies)
|
||||
{
|
||||
//get all types in the assembly that are sub types of the current type
|
||||
var allSubTypes = GetTypesWithFormattedException(a)
|
||||
.Where(assignTypeFrom.IsAssignableFrom)
|
||||
.ToArray();
|
||||
|
||||
var filteredTypes = new Type[] { };
|
||||
try
|
||||
// get all assembly types that can be assigned to baseType
|
||||
Type[] assemblyTypes = null;
|
||||
if (assembly != baseType.Assembly || baseTypeAssemblyIsCandidate)
|
||||
{
|
||||
//now filter the types based on the onlyConcreteClasses flag, not interfaces, not static classes
|
||||
filteredTypes = allSubTypes
|
||||
.Where(t => (TypeHelper.IsNonStaticClass(t)
|
||||
//Do not include nested private classes - since we are in full trust now this will find those too!
|
||||
&& t.IsNestedPrivate == false
|
||||
&& (onlyConcreteClasses == false || t.IsAbstract == false)
|
||||
//Do not include classes that are flagged to hide from the type finder
|
||||
&& t.GetCustomAttribute<HideFromTypeFinderAttribute>() == null
|
||||
&& additionalFilter(t)))
|
||||
.ToArray();
|
||||
try
|
||||
{
|
||||
assemblyTypes = GetTypesWithFormattedException(assembly)
|
||||
.Where(baseType.IsAssignableFrom)
|
||||
.ToArray(); // in try block
|
||||
}
|
||||
catch (TypeLoadException ex)
|
||||
{
|
||||
LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", assembly), ex);
|
||||
continue;
|
||||
}
|
||||
|
||||
types.AddRange(assemblyTypes.Where(x =>
|
||||
x.IsClass // only classes
|
||||
&& (x.IsAbstract == false || x.IsSealed == false) // ie non-static, static is abstract and sealed
|
||||
&& x.IsNestedPrivate == false // exclude nested private
|
||||
&& (onlyConcreteClasses == false || x.IsAbstract == false) // exclude abstract
|
||||
&& x.GetCustomAttribute<HideFromTypeFinderAttribute>() == null // exclude hidden
|
||||
&& (additionalFilter == null || additionalFilter(x)))); // filter
|
||||
}
|
||||
catch (TypeLoadException ex)
|
||||
{
|
||||
LogHelper.Error(typeof(TypeFinder), string.Format("Could not query types on {0} assembly, this is most likely due to this assembly not being compatible with the current Umbraco version", a), ex);
|
||||
|
||||
if (assembly != baseType.Assembly && assemblyTypes.All(x => x.IsSealed))
|
||||
continue;
|
||||
}
|
||||
|
||||
//add the types to our list to return
|
||||
foreach (var t in filteredTypes)
|
||||
foreach (var referencing in TypeHelper.GetReferencingAssemblies(assembly, candidateAssemblies))
|
||||
{
|
||||
foundAssignableTypes.Add(t);
|
||||
candidateAssemblies.Remove(referencing);
|
||||
stack.Push(referencing);
|
||||
}
|
||||
|
||||
//now we need to include types that may be inheriting from sub classes of the type being searched for
|
||||
//so we will search in assemblies that reference those types too.
|
||||
foreach (var subTypesInAssembly in allSubTypes.GroupBy(x => x.Assembly))
|
||||
{
|
||||
|
||||
//So that we are not scanning too much, we need to group the sub types:
|
||||
// * if there is more than 1 sub type in the same assembly then we should only search on the 'lowest base' type.
|
||||
// * We should also not search for sub types if the type is sealed since you cannot inherit from a sealed class
|
||||
// * We should not search for sub types if the type is static since you cannot inherit from them.
|
||||
var subTypeList = subTypesInAssembly
|
||||
.Where(t => t.IsSealed == false && TypeHelper.IsStaticClass(t) == false)
|
||||
.ToArray();
|
||||
|
||||
var baseClassAttempt = TypeHelper.GetLowestBaseType(subTypeList);
|
||||
|
||||
//if there's a base class amongst the types then we'll only search for that type.
|
||||
//otherwise we'll have to search for all of them.
|
||||
var subTypesToSearch = new HashSet<Type>();
|
||||
if (baseClassAttempt.Success)
|
||||
{
|
||||
subTypesToSearch.Add(baseClassAttempt.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var t in subTypeList)
|
||||
{
|
||||
subTypesToSearch.Add(t);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var typeToSearch in subTypesToSearch)
|
||||
{
|
||||
//recursively find the types inheriting from this sub type in the other non-scanned assemblies.
|
||||
var foundTypes = GetClasses(typeToSearch, otherAssemblies, onlyConcreteClasses, additionalFilter);
|
||||
|
||||
foreach (var f in foundTypes)
|
||||
{
|
||||
foundAssignableTypes.Add(f);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
return foundAssignableTypes;
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
internal static IEnumerable<Type> GetTypesWithFormattedException(Assembly a)
|
||||
@@ -666,7 +561,6 @@ namespace Umbraco.Core
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public static Type GetTypeByName(string typeName)
|
||||
{
|
||||
var type = BuildManager.GetType(typeName, false);
|
||||
@@ -684,6 +578,5 @@ namespace Umbraco.Core
|
||||
.Select(x => x.GetType(typeName))
|
||||
.FirstOrDefault(x => x != null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,12 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
internal static class TypeHelper
|
||||
{
|
||||
|
||||
private static readonly ConcurrentDictionary<Type, FieldInfo[]> GetFieldsCache = new ConcurrentDictionary<Type, FieldInfo[]>();
|
||||
private static readonly ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]> GetPropertiesCache = new ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>();
|
||||
private static readonly ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]> GetPropertiesCache
|
||||
= new ConcurrentDictionary<Tuple<Type, bool, bool, bool>, PropertyInfo[]>();
|
||||
private static readonly ConcurrentDictionary<Type, FieldInfo[]> GetFieldsCache
|
||||
= new ConcurrentDictionary<Type, FieldInfo[]>();
|
||||
|
||||
private static readonly Assembly[] EmptyAssemblies = new Assembly[0];
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the method is actually overriding a base method
|
||||
@@ -30,44 +33,40 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Find all assembly references that are referencing the assignTypeFrom Type's assembly found in the assemblyList
|
||||
/// </summary>
|
||||
/// <param name="assignTypeFrom"></param>
|
||||
/// <param name="assemblies"></param>
|
||||
/// <param name="assembly">The referenced assembly.</param>
|
||||
/// <param name="assemblies">A list of assemblies.</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// If the assembly of the assignTypeFrom Type is in the App_Code assembly, then we return nothing since things cannot
|
||||
/// reference that assembly, same with the global.asax assembly.
|
||||
/// </remarks>
|
||||
public static Assembly[] GetReferencedAssemblies(Type assignTypeFrom, IEnumerable<Assembly> assemblies)
|
||||
public static Assembly[] GetReferencingAssemblies(Assembly assembly, IEnumerable<Assembly> assemblies)
|
||||
{
|
||||
//check if it is the app_code assembly.
|
||||
//check if it is App_global.asax assembly
|
||||
if (assignTypeFrom.Assembly.IsAppCodeAssembly() || assignTypeFrom.Assembly.IsGlobalAsaxAssembly())
|
||||
{
|
||||
return Enumerable.Empty<Assembly>().ToArray();
|
||||
}
|
||||
if (assembly.IsAppCodeAssembly() || assembly.IsGlobalAsaxAssembly())
|
||||
return EmptyAssemblies;
|
||||
|
||||
|
||||
//find all assembly references that are referencing the current type's assembly since we
|
||||
//should only be scanning those assemblies because any other assembly will definitely not
|
||||
//contain sub type's of the one we're currently looking for
|
||||
return assemblies
|
||||
.Where(assembly =>
|
||||
assembly == assignTypeFrom.Assembly
|
||||
|| HasReferenceToAssemblyWithName(assembly, assignTypeFrom.Assembly.GetName().Name))
|
||||
.ToArray();
|
||||
// find all assembly references that are referencing the current type's assembly since we
|
||||
// should only be scanning those assemblies because any other assembly will definitely not
|
||||
// contain sub type's of the one we're currently looking for
|
||||
var name = assembly.GetName().Name;
|
||||
return assemblies.Where(x => x == assembly || HasReference(x, name)).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// checks if the assembly has a reference with the same name as the expected assembly name.
|
||||
/// Determines if an assembly references another assembly.
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
/// <param name="expectedAssemblyName"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
private static bool HasReferenceToAssemblyWithName(Assembly assembly, string expectedAssemblyName)
|
||||
{
|
||||
return assembly
|
||||
.GetReferencedAssemblies()
|
||||
.Select(a => a.Name)
|
||||
.Contains(expectedAssemblyName, StringComparer.Ordinal);
|
||||
public static bool HasReference(Assembly assembly, string name)
|
||||
{
|
||||
// ReSharper disable once LoopCanBeConvertedToQuery - no!
|
||||
foreach (var a in assembly.GetReferencedAssemblies())
|
||||
{
|
||||
if (string.Equals(a.Name, name, StringComparison.Ordinal)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -106,13 +105,10 @@ namespace Umbraco.Core
|
||||
public static Attempt<Type> GetLowestBaseType(params Type[] types)
|
||||
{
|
||||
if (types.Length == 0)
|
||||
{
|
||||
return Attempt<Type>.Fail();
|
||||
}
|
||||
if (types.Length == 1)
|
||||
{
|
||||
|
||||
if (types.Length == 1)
|
||||
return Attempt.Succeed(types[0]);
|
||||
}
|
||||
|
||||
foreach (var curr in types)
|
||||
{
|
||||
@@ -196,20 +192,15 @@ namespace Umbraco.Core
|
||||
/// <param name="includeIndexed"></param>
|
||||
/// <param name="caseSensitive"> </param>
|
||||
/// <returns></returns>
|
||||
public static PropertyInfo GetProperty(Type type, string name,
|
||||
bool mustRead = true,
|
||||
bool mustWrite = true,
|
||||
public static PropertyInfo GetProperty(Type type, string name,
|
||||
bool mustRead = true,
|
||||
bool mustWrite = true,
|
||||
bool includeIndexed = false,
|
||||
bool caseSensitive = true)
|
||||
{
|
||||
return CachedDiscoverableProperties(type, mustRead, mustWrite, includeIndexed)
|
||||
.FirstOrDefault(x =>
|
||||
{
|
||||
if (caseSensitive)
|
||||
return x.Name == name;
|
||||
return x.Name.InvariantEquals(name);
|
||||
});
|
||||
}
|
||||
return CachedDiscoverableProperties(type, mustRead, mustWrite, includeIndexed)
|
||||
.FirstOrDefault(x => caseSensitive ? (x.Name == name) : x.Name.InvariantEquals(name));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets (and caches) <see cref="FieldInfo"/> discoverable in the current <see cref="AppDomain"/> for a given <paramref name="type"/>.
|
||||
@@ -222,7 +213,7 @@ namespace Umbraco.Core
|
||||
type,
|
||||
x => type
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(y => !y.IsInitOnly)
|
||||
.Where(y => y.IsInitOnly == false)
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
@@ -240,13 +231,12 @@ namespace Umbraco.Core
|
||||
new Tuple<Type, bool, bool, bool>(type, mustRead, mustWrite, includeIndexed),
|
||||
x => type
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(y => (!mustRead || y.CanRead)
|
||||
&& (!mustWrite || y.CanWrite)
|
||||
&& (includeIndexed || !y.GetIndexParameters().Any()))
|
||||
.Where(y => (mustRead == false || y.CanRead)
|
||||
&& (mustWrite == false || y.CanWrite)
|
||||
&& (includeIndexed || y.GetIndexParameters().Any() == false))
|
||||
.ToArray());
|
||||
}
|
||||
|
||||
|
||||
#region Match Type
|
||||
|
||||
//TODO: Need to determine if these methods should replace/combine/merge etc with IsTypeAssignableFrom, IsAssignableFromGeneric
|
||||
@@ -337,9 +327,9 @@ namespace Umbraco.Core
|
||||
|
||||
// not a generic type, not a generic parameter
|
||||
// so normal class or interface
|
||||
// fixme structs? enums? array types?
|
||||
// about primitive types, value types, etc:
|
||||
// http://stackoverflow.com/questions/1827425/how-to-check-programatically-if-a-type-is-a-struct-or-a-class
|
||||
// if it's a primitive type... it needs to be ==
|
||||
|
||||
if (implementation == contract) return true;
|
||||
if (contract.IsClass && implementation.IsClass && implementation.IsSubclassOf(contract)) return true;
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Umbraco.Tests.CodeFirst
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -84,10 +84,7 @@ namespace Umbraco.Tests.CodeFirst
|
||||
var foundTypes = _pluginManager.ResolveContentTypeBaseTypes();
|
||||
|
||||
Assert.That(foundTypes.Count(), Is.EqualTo(15));
|
||||
Assert.AreEqual(1,
|
||||
_pluginManager.GetTypeLists()
|
||||
.Count(x => x.IsTypeList<ContentTypeBase>(PluginManager.TypeResolutionKind.FindAllTypes)));
|
||||
Assert.AreEqual(1, _pluginManager.TypeLists.Count(x => x.BaseType == typeof (ContentTypeBase) && x.AttributeType == null));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -46,23 +46,6 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
return new EntityContainerRepository(unitOfWork, CacheHelper.CreateDisabledCacheHelper(), Mock.Of<ILogger>(), SqlSyntax, Constants.ObjectTypes.DataTypeContainerGuid);
|
||||
}
|
||||
|
||||
[TestCase("UmbracoPreVal87-21,3,48", 3, true)]
|
||||
[TestCase("UmbracoPreVal87-21,33,48", 3, false)]
|
||||
[TestCase("UmbracoPreVal87-21,33,48", 33, true)]
|
||||
[TestCase("UmbracoPreVal87-21,3,48", 33, false)]
|
||||
[TestCase("UmbracoPreVal87-21,3,48", 21, true)]
|
||||
[TestCase("UmbracoPreVal87-21,3,48", 48, true)]
|
||||
[TestCase("UmbracoPreVal87-22,33,48", 2, false)]
|
||||
[TestCase("UmbracoPreVal87-22,33,48", 22, true)]
|
||||
[TestCase("UmbracoPreVal87-22,33,44", 4, false)]
|
||||
[TestCase("UmbracoPreVal87-22,33,44", 44, true)]
|
||||
[TestCase("UmbracoPreVal87-22,333,44", 33, false)]
|
||||
[TestCase("UmbracoPreVal87-22,333,44", 333, true)]
|
||||
public void Pre_Value_Cache_Key_Tests(string cacheKey, int preValueId, bool outcome)
|
||||
{
|
||||
Assert.AreEqual(outcome, Regex.IsMatch(cacheKey, DataTypeDefinitionRepository.GetCacheKeyRegex(preValueId)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Move()
|
||||
{
|
||||
@@ -537,7 +520,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
}
|
||||
|
||||
var cached = cache.IsolatedRuntimeCache.GetCache<IDataTypeDefinition>().Result
|
||||
.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-");
|
||||
.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + "_" + dtd.Id);
|
||||
|
||||
Assert.IsNotNull(cached);
|
||||
Assert.AreEqual(1, cached.Count());
|
||||
@@ -581,7 +564,7 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
}
|
||||
|
||||
var cached = cache.IsolatedRuntimeCache.GetCache<IDataTypeDefinition>().Result
|
||||
.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + dtd.Id + "-");
|
||||
.GetCacheItemsByKeySearch<PreValueCollection>(CacheKeys.DataTypePreValuesCacheKey + "_" + dtd.Id);
|
||||
|
||||
Assert.IsNotNull(cached);
|
||||
Assert.AreEqual(1, cached.Count());
|
||||
|
||||
@@ -18,6 +18,7 @@ using Umbraco.Core.Profiling;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using umbraco.DataLayer;
|
||||
using umbraco.editorControls;
|
||||
using umbraco.interfaces;
|
||||
using umbraco.MacroEngines;
|
||||
using umbraco.uicontrols;
|
||||
using Umbraco.Web;
|
||||
@@ -34,14 +35,14 @@ namespace Umbraco.Tests.Plugins
|
||||
public void Initialize()
|
||||
{
|
||||
//this ensures its reset
|
||||
_manager = new PluginManager(new ActivatorServiceProvider(), new NullCacheProvider(),
|
||||
_manager = new PluginManager(new ActivatorServiceProvider(), new NullCacheProvider(),
|
||||
new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
|
||||
|
||||
//for testing, we'll specify which assemblies are scanned for the PluginTypeResolver
|
||||
//TODO: Should probably update this so it only searches this assembly and add custom types to be found
|
||||
_manager.AssembliesToScan = new[]
|
||||
{
|
||||
this.GetType().Assembly,
|
||||
this.GetType().Assembly,
|
||||
typeof(ApplicationStartupHandler).Assembly,
|
||||
typeof(SqlCEHelper).Assembly,
|
||||
typeof(CMSNode).Assembly,
|
||||
@@ -160,7 +161,7 @@ namespace Umbraco.Tests.Plugins
|
||||
public void Detect_Legacy_Plugin_File_List()
|
||||
{
|
||||
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
|
||||
|
||||
|
||||
var filePath= Path.Combine(tempFolder, string.Format("umbraco-plugins.{0}.list", NetworkHelper.FileSafeMachineName));
|
||||
|
||||
File.WriteAllText(filePath, @"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
@@ -169,12 +170,11 @@ namespace Umbraco.Tests.Plugins
|
||||
<add type=""umbraco.macroCacheRefresh, umbraco, Version=6.0.0.0, Culture=neutral, PublicKeyToken=null"" />
|
||||
</baseType>
|
||||
</plugins>");
|
||||
|
||||
Assert.IsTrue(_manager.DetectLegacyPluginListFile());
|
||||
|
||||
Assert.IsEmpty(_manager.ReadCache()); // uber-legacy cannot be read
|
||||
|
||||
File.Delete(filePath);
|
||||
|
||||
//now create a valid one
|
||||
File.WriteAllText(filePath, @"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<plugins>
|
||||
<baseType type=""umbraco.interfaces.ICacheRefresher"" resolutionType=""FindAllTypes"">
|
||||
@@ -182,19 +182,32 @@ namespace Umbraco.Tests.Plugins
|
||||
</baseType>
|
||||
</plugins>");
|
||||
|
||||
Assert.IsFalse(_manager.DetectLegacyPluginListFile());
|
||||
Assert.IsEmpty(_manager.ReadCache()); // legacy cannot be read
|
||||
|
||||
File.Delete(filePath);
|
||||
|
||||
File.WriteAllText(filePath, @"IContentFinder
|
||||
|
||||
MyContentFinder
|
||||
AnotherContentFinder
|
||||
|
||||
");
|
||||
|
||||
Assert.IsNotNull(_manager.ReadCache()); // works
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Create_Cached_Plugin_File()
|
||||
{
|
||||
var types = new[] { typeof(PluginManager), typeof(PluginManagerTests), typeof(UmbracoContext) };
|
||||
var types = new[] { typeof (PluginManager), typeof (PluginManagerTests), typeof (UmbracoContext) };
|
||||
|
||||
//yes this is silly, none of these types inherit from string, but this is just to test the xml file format
|
||||
_manager.UpdateCachedPluginsFile<string>(types, PluginManager.TypeResolutionKind.FindAllTypes);
|
||||
var typeList1 = new PluginManager.TypeList(typeof (object), null);
|
||||
foreach (var type in types) typeList1.Add(type);
|
||||
_manager.AddTypeList(typeList1);
|
||||
_manager.WriteCache();
|
||||
|
||||
var plugins = _manager.TryGetCachedPluginsFromFile<string>(PluginManager.TypeResolutionKind.FindAllTypes);
|
||||
var diffType = _manager.TryGetCachedPluginsFromFile<string>(PluginManager.TypeResolutionKind.FindAttributedTypes);
|
||||
var plugins = _manager.TryGetCached(typeof (object), null);
|
||||
var diffType = _manager.TryGetCached(typeof (object), typeof (ObsoleteAttribute));
|
||||
|
||||
Assert.IsTrue(plugins.Success);
|
||||
//this will be false since there is no cache of that type resolution kind
|
||||
@@ -210,7 +223,7 @@ namespace Umbraco.Tests.Plugins
|
||||
public void PluginHash_From_String()
|
||||
{
|
||||
var s = "hello my name is someone".GetHashCode().ToString("x", CultureInfo.InvariantCulture);
|
||||
var output = PluginManager.ConvertPluginsHashFromHex(s);
|
||||
var output = PluginManager.ConvertHashToInt64(s);
|
||||
Assert.AreNotEqual(0, output);
|
||||
}
|
||||
|
||||
@@ -240,7 +253,7 @@ namespace Umbraco.Tests.Plugins
|
||||
var list1 = new[] { f1, f2, f3, f4, f5, f6 };
|
||||
var list2 = new[] { f1, f3, f5 };
|
||||
var list3 = new[] { f1, f3, f5, f7 };
|
||||
|
||||
|
||||
//Act
|
||||
var hash1 = PluginManager.GetFileHash(list1, new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
|
||||
var hash2 = PluginManager.GetFileHash(list2, new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
|
||||
@@ -259,9 +272,7 @@ namespace Umbraco.Tests.Plugins
|
||||
{
|
||||
var foundTypes1 = _manager.ResolveFindMeTypes();
|
||||
var foundTypes2 = _manager.ResolveFindMeTypes();
|
||||
Assert.AreEqual(1,
|
||||
_manager.GetTypeLists()
|
||||
.Count(x => x.IsTypeList<IFindMe>(PluginManager.TypeResolutionKind.FindAllTypes)));
|
||||
Assert.AreEqual(1, _manager.TypeLists.Count(x => x.BaseType == typeof(IFindMe) && x.AttributeType == null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -344,20 +355,20 @@ namespace Umbraco.Tests.Plugins
|
||||
{
|
||||
var types = new HashSet<PluginManager.TypeList>();
|
||||
|
||||
var propEditors = new PluginManager.TypeList<PropertyEditor>(PluginManager.TypeResolutionKind.FindAllTypes);
|
||||
propEditors.AddType(typeof(LabelPropertyEditor));
|
||||
var propEditors = new PluginManager.TypeList(typeof (PropertyEditor), null);
|
||||
propEditors.Add(typeof(LabelPropertyEditor));
|
||||
types.Add(propEditors);
|
||||
|
||||
var found = types.SingleOrDefault(x => x.IsTypeList<PropertyEditor>(PluginManager.TypeResolutionKind.FindAllTypes));
|
||||
var found = types.SingleOrDefault(x => x.BaseType == typeof (PropertyEditor) && x.AttributeType == null);
|
||||
|
||||
Assert.IsNotNull(found);
|
||||
|
||||
//This should not find a type list of this type
|
||||
var shouldNotFind = types.SingleOrDefault(x => x.IsTypeList<IParameterEditor>(PluginManager.TypeResolutionKind.FindAllTypes));
|
||||
var shouldNotFind = types.SingleOrDefault(x => x.BaseType == typeof (IParameterEditor) && x.AttributeType == null);
|
||||
|
||||
Assert.IsNull(shouldNotFind);
|
||||
}
|
||||
|
||||
|
||||
[XsltExtension("Blah.Blah")]
|
||||
public class MyXsltExtension
|
||||
{
|
||||
@@ -371,7 +382,7 @@ namespace Umbraco.Tests.Plugins
|
||||
|
||||
}
|
||||
|
||||
public interface IFindMe
|
||||
public interface IFindMe : IDiscoverable
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -26,11 +26,11 @@ using Umbraco.Web.BaseRest;
|
||||
|
||||
namespace Umbraco.Tests.Plugins
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Tests for typefinder
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
[TestFixture]
|
||||
public class TypeFinderTests
|
||||
{
|
||||
/// <summary>
|
||||
@@ -43,7 +43,7 @@ namespace Umbraco.Tests.Plugins
|
||||
{
|
||||
_assemblies = new[]
|
||||
{
|
||||
this.GetType().Assembly,
|
||||
this.GetType().Assembly,
|
||||
typeof(ApplicationStartupHandler).Assembly,
|
||||
typeof(SqlCEHelper).Assembly,
|
||||
typeof(CMSNode).Assembly,
|
||||
@@ -75,7 +75,7 @@ namespace Umbraco.Tests.Plugins
|
||||
[Test]
|
||||
public void Find_Classes_Of_Type()
|
||||
{
|
||||
var typesFound = TypeFinder.FindClassesOfType<IApplicationStartupHandler>(_assemblies);
|
||||
var typesFound = TypeFinder.FindClassesOfType<IApplicationStartupHandler>(_assemblies);
|
||||
var originalTypesFound = TypeFinderOriginal.FindClassesOfType<IApplicationStartupHandler>(_assemblies);
|
||||
|
||||
Assert.AreEqual(originalTypesFound.Count(), typesFound.Count());
|
||||
@@ -118,7 +118,7 @@ namespace Umbraco.Tests.Plugins
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
[Ignore]
|
||||
@@ -149,7 +149,7 @@ namespace Umbraco.Tests.Plugins
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class MyTag : ITag
|
||||
@@ -161,7 +161,7 @@ namespace Umbraco.Tests.Plugins
|
||||
|
||||
public class MySuperTag : MyTag
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
@@ -203,7 +203,7 @@ namespace Umbraco.Tests.Plugins
|
||||
/// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been
|
||||
/// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been
|
||||
/// loaded in the CLR, not all assemblies.
|
||||
/// See these threads:
|
||||
/// http://issues.umbraco.org/issue/U5-198
|
||||
@@ -329,8 +329,8 @@ namespace Umbraco.Tests.Plugins
|
||||
}
|
||||
catch (SecurityException)
|
||||
{
|
||||
//we will just ignore this because this will fail
|
||||
//in medium trust for system assemblies, we get an exception but we just want to continue until we get to
|
||||
//we will just ignore this because this will fail
|
||||
//in medium trust for system assemblies, we get an exception but we just want to continue until we get to
|
||||
//an assembly that is ok.
|
||||
}
|
||||
}
|
||||
@@ -347,9 +347,9 @@ namespace Umbraco.Tests.Plugins
|
||||
}
|
||||
catch (SecurityException)
|
||||
{
|
||||
//we will just ignore this because if we are trying to do a call to:
|
||||
//we will just ignore this because if we are trying to do a call to:
|
||||
// AssemblyName.ReferenceMatchesDefinition(a.GetName(), assemblyName)))
|
||||
//in medium trust for system assemblies, we get an exception but we just want to continue until we get to
|
||||
//in medium trust for system assemblies, we get an exception but we just want to continue until we get to
|
||||
//an assembly that is ok.
|
||||
}
|
||||
}
|
||||
@@ -361,7 +361,7 @@ namespace Umbraco.Tests.Plugins
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a list of found local Assemblies excluding the known assemblies we don't want to scan
|
||||
/// Return a list of found local Assemblies excluding the known assemblies we don't want to scan
|
||||
/// and exluding the ones passed in and excluding the exclusion list filter, the results of this are
|
||||
/// cached for perforance reasons.
|
||||
/// </summary>
|
||||
@@ -429,7 +429,7 @@ namespace Umbraco.Tests.Plugins
|
||||
"RouteDebugger,",
|
||||
"SqlCE4Umbraco,",
|
||||
"umbraco.datalayer,",
|
||||
"umbraco.interfaces,",
|
||||
"umbraco.interfaces,",
|
||||
"umbraco.providers,",
|
||||
"Umbraco.Web.UI,",
|
||||
"umbraco.webservices",
|
||||
@@ -631,5 +631,5 @@ namespace Umbraco.Tests.Plugins
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Runtime.Remoting;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -9,7 +8,6 @@ using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.CodeFirst.TestModels.Composition;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.Entities;
|
||||
|
||||
@@ -1644,6 +1642,38 @@ namespace Umbraco.Tests.Services
|
||||
Assert.That(descriptionPropertyTypeReloaded.PropertyGroupId.IsValueCreated, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Empty_Description_Is_Always_Null_After_Saving_Content_Type()
|
||||
{
|
||||
var service = ServiceContext.ContentTypeService;
|
||||
var contentType = MockedContentTypes.CreateBasicContentType();
|
||||
contentType.Description = null;
|
||||
service.Save(contentType);
|
||||
|
||||
var contentType2 = MockedContentTypes.CreateBasicContentType("basePage2", "Base Page 2");
|
||||
contentType2.Description = string.Empty;
|
||||
service.Save(contentType2);
|
||||
|
||||
Assert.IsNull(contentType.Description);
|
||||
Assert.IsNull(contentType2.Description);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Empty_Description_Is_Always_Null_After_Saving_Media_Type()
|
||||
{
|
||||
var service = ServiceContext.ContentTypeService;
|
||||
var mediaType = MockedContentTypes.CreateSimpleMediaType("mediaType", "Media Type");
|
||||
mediaType.Description = null;
|
||||
service.Save(mediaType);
|
||||
|
||||
var mediaType2 = MockedContentTypes.CreateSimpleMediaType("mediaType2", "Media Type 2");
|
||||
mediaType2.Description = string.Empty;
|
||||
service.Save(mediaType2);
|
||||
|
||||
Assert.IsNull(mediaType.Description);
|
||||
Assert.IsNull(mediaType2.Description);
|
||||
}
|
||||
|
||||
private ContentType CreateComponent()
|
||||
{
|
||||
var component = new ContentType(-1)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using umbraco.cms.presentation.create.controls;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Rdbms;
|
||||
@@ -183,6 +181,22 @@ namespace Umbraco.Tests.Services
|
||||
Assert.Throws<ArgumentException>(() => ServiceContext.MemberTypeService.Save(memberType));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Empty_Description_Is_Always_Null_After_Saving_Member_Type()
|
||||
{
|
||||
var service = ServiceContext.MemberTypeService;
|
||||
var memberType = MockedContentTypes.CreateSimpleMemberType();
|
||||
memberType.Description = null;
|
||||
service.Save(memberType);
|
||||
|
||||
var memberType2 = MockedContentTypes.CreateSimpleMemberType("memberType2", "Member Type 2");
|
||||
memberType2.Description = string.Empty;
|
||||
service.Save(memberType2);
|
||||
|
||||
Assert.IsNull(memberType.Description);
|
||||
Assert.IsNull(memberType2.Description);
|
||||
}
|
||||
|
||||
//[Test]
|
||||
//public void Can_Save_MemberType_Structure_And_Create_A_Member_Based_On_It()
|
||||
//{
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
public override void TearDown()
|
||||
{
|
||||
base.TearDown();
|
||||
|
||||
|
||||
// reset settings
|
||||
SettingsForTests.Reset();
|
||||
UmbracoContext.Current = null;
|
||||
@@ -119,7 +119,7 @@ namespace Umbraco.Tests.TestHelpers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// By default this returns false which means the plugin manager will not be reset so it doesn't need to re-scan
|
||||
/// By default this returns false which means the plugin manager will not be reset so it doesn't need to re-scan
|
||||
/// all of the assemblies. Inheritors can override this if plugin manager resetting is required, generally needs
|
||||
/// to be set to true if the SetupPluginManager has been overridden.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@
|
||||
<Reference Include="AutoMapper.Net4, Version=3.3.1.0, Culture=neutral, PublicKeyToken=be96cd2c38ef1005, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Examine, Version=0.1.81.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Examine.0.1.81\lib\net45\Examine.dll</HintPath>
|
||||
<Reference Include="Examine, Version=0.1.82.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Examine.0.1.82\lib\net45\Examine.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<packages>
|
||||
<package id="AspNetWebApi.SelfHost" version="4.0.20710.0" targetFramework="net45" />
|
||||
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.81" targetFramework="net45" />
|
||||
<package id="Examine" version="0.1.82" targetFramework="net45" />
|
||||
<package id="log4net" version="2.0.5" targetFramework="net45" />
|
||||
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
|
||||
<package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net45" />
|
||||
|
||||
+3
-2
@@ -17,6 +17,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
|
||||
hideheader: '@',
|
||||
cachekey: '@',
|
||||
isdialog: '@',
|
||||
onlyinitialized: '@',
|
||||
//Custom query string arguments to pass in to the tree as a string, example: "startnodeid=123&something=value"
|
||||
customtreeparams: '@',
|
||||
eventhandler: '=',
|
||||
@@ -252,7 +253,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
|
||||
deleteAnimations = false;
|
||||
|
||||
//default args
|
||||
var args = { section: scope.section, tree: scope.treealias, cacheKey: scope.cachekey, isDialog: scope.isdialog ? scope.isdialog : false };
|
||||
var args = { section: scope.section, tree: scope.treealias, cacheKey: scope.cachekey, isDialog: scope.isdialog ? scope.isdialog : false, onlyinitialized: scope.onlyinitialized };
|
||||
|
||||
//add the extra query string params if specified
|
||||
if (scope.customtreeparams) {
|
||||
@@ -305,7 +306,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
|
||||
scope.selectEnabledNodeClass = function (node) {
|
||||
return node ?
|
||||
node.selected ?
|
||||
'icon umb-tree-icon sprTree icon-check blue temporary' :
|
||||
'icon umb-tree-icon sprTree icon-check green temporary' :
|
||||
'' :
|
||||
'';
|
||||
};
|
||||
|
||||
+11
-1
@@ -1,7 +1,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function MiniListViewDirective(entityResource) {
|
||||
function MiniListViewDirective(entityResource, iconHelper) {
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
|
||||
function open(node) {
|
||||
|
||||
// convert legacy icon for node
|
||||
if(node && node.icon) {
|
||||
node.icon = iconHelper.convertFromLegacyIcon(node.icon);
|
||||
}
|
||||
|
||||
goingForward = true;
|
||||
|
||||
var miniListView = {
|
||||
@@ -58,6 +63,11 @@
|
||||
// update children
|
||||
miniListView.children = data.items;
|
||||
_.each(miniListView.children, function(c) {
|
||||
// convert legacy icon for node
|
||||
if(c.icon) {
|
||||
c.icon = iconHelper.convertFromLegacyIcon(c.icon);
|
||||
}
|
||||
// set published state for content
|
||||
if (c.metaData) {
|
||||
c.hasChildren = c.metaData.HasChildren;
|
||||
if(scope.entityType === "Document") {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function TableDirective() {
|
||||
function TableDirective(iconHelper) {
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
@@ -43,6 +43,10 @@
|
||||
}
|
||||
};
|
||||
|
||||
scope.getIcon = function (entry) {
|
||||
return iconHelper.convertFromLegacyIcon(entry.icon);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
var directive = {
|
||||
|
||||
@@ -343,8 +343,13 @@ angular.module('umbraco.mocks').
|
||||
{
|
||||
results.push(decodeURIComponent(match[1].replace(/\+/g, " ")));
|
||||
}
|
||||
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
getObjectPropertyFromJsonString: function(data, name) {
|
||||
var obj = JSON.parse(data);
|
||||
return obj[name];
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
@@ -21,8 +21,27 @@ angular.module('umbraco.mocks').
|
||||
if (!mocksUtils.checkAuth()) {
|
||||
return [401, null, null];
|
||||
}
|
||||
|
||||
|
||||
var ids = mocksUtils.getParametersByName(data, "ids") || [1234, 23324, 2323, 23424];
|
||||
|
||||
var nodes = [];
|
||||
|
||||
$(ids).each(function (i, id) {
|
||||
var _id = parseInt(id, 10);
|
||||
nodes.push(mocksUtils.getMockEntity(_id));
|
||||
});
|
||||
|
||||
return [200, nodes, null];
|
||||
}
|
||||
|
||||
function returnEntitybyIdsPost(method, url, data, headers) {
|
||||
|
||||
if (!mocksUtils.checkAuth()) {
|
||||
return [401, null, null];
|
||||
}
|
||||
|
||||
var ids = mocksUtils.getObjectPropertyFromJsonString(data, "ids") || [1234, 23324, 2323, 23424];
|
||||
|
||||
var nodes = [];
|
||||
|
||||
$(ids).each(function (i, id) {
|
||||
@@ -52,7 +71,7 @@ angular.module('umbraco.mocks').
|
||||
|
||||
$httpBackend
|
||||
.whenPOST(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetByIds'))
|
||||
.respond(returnEntitybyIds);
|
||||
.respond(returnEntitybyIdsPost);
|
||||
|
||||
$httpBackend
|
||||
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetAncestors'))
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
* @ngdoc service
|
||||
* @name umbraco.resources.macroResource
|
||||
* @description Deals with data for macros
|
||||
*
|
||||
*
|
||||
**/
|
||||
function macroResource($q, $http, umbRequestHelper) {
|
||||
|
||||
//the factory object returned
|
||||
return {
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.macroResource#getMacroParameters
|
||||
@@ -20,7 +20,7 @@ function macroResource($q, $http, umbRequestHelper) {
|
||||
* @param {int} macroId The macro id to get parameters for
|
||||
*
|
||||
*/
|
||||
getMacroParameters: function (macroId) {
|
||||
getMacroParameters: function (macroId) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
@@ -29,7 +29,7 @@ function macroResource($q, $http, umbRequestHelper) {
|
||||
[{ macroId: macroId }])),
|
||||
'Failed to retrieve macro parameters for macro with id ' + macroId);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.macroResource#getMacroResult
|
||||
@@ -55,6 +55,27 @@ function macroResource($q, $http, umbRequestHelper) {
|
||||
macroParams: macroParamDictionary
|
||||
}),
|
||||
'Failed to retrieve macro result for macro with alias ' + macroAlias);
|
||||
},
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {} filename
|
||||
* @returns {}
|
||||
*/
|
||||
createPartialViewMacroWithFile: function(virtualPath, filename) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"macroApiBaseUrl",
|
||||
"CreatePartialViewMacroWithFile"), {
|
||||
virtualPath: virtualPath,
|
||||
filename: filename
|
||||
}
|
||||
),
|
||||
'Failed to create macro "' + filename + '"'
|
||||
);
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,10 +51,15 @@ function treeResource($q, $http, umbRequestHelper) {
|
||||
if (!options.isDialog) {
|
||||
options.isDialog = false;
|
||||
}
|
||||
|
||||
|
||||
//create the query string for the tree request, these are the mandatory options:
|
||||
var query = "application=" + options.section + "&tree=" + options.tree + "&isDialog=" + options.isDialog;
|
||||
|
||||
//if you need to load a not initialized tree set this value to false - default is true
|
||||
if (options.onlyinitialized) {
|
||||
query += "&onlyInitialized=" + options.onlyinitialized;
|
||||
}
|
||||
|
||||
//the options can contain extra query string parameters
|
||||
if (options.queryString) {
|
||||
query += "&" + options.queryString;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function entityHelper() {
|
||||
|
||||
function getEntityTypeFromSection(section) {
|
||||
if (section === "member") {
|
||||
return "Member";
|
||||
}
|
||||
else if (section === "media") {
|
||||
return "Media";
|
||||
} else {
|
||||
return "Document";
|
||||
}
|
||||
}
|
||||
|
||||
////////////
|
||||
|
||||
var service = {
|
||||
getEntityTypeFromSection: getEntityTypeFromSection
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco.services').factory('entityHelper', entityHelper);
|
||||
|
||||
})();
|
||||
@@ -8,8 +8,15 @@
|
||||
return "@Umbraco.GetDictionaryValue(\"" + nodeName + "\")";
|
||||
}
|
||||
|
||||
function getInsertPartialSnippet(nodeName) {
|
||||
return "@Html.Partial(\"" + nodeName + "\")";
|
||||
function getInsertPartialSnippet(parentId, nodeName) {
|
||||
|
||||
var partialViewName = nodeName.replace(".cshtml", "");
|
||||
|
||||
if(parentId) {
|
||||
partialViewName = parentId + "/" + partialViewName;
|
||||
}
|
||||
|
||||
return "@Html.Partial(\"" + partialViewName + "\")";
|
||||
}
|
||||
|
||||
function getQuerySnippet(queryExpression) {
|
||||
|
||||
@@ -139,6 +139,7 @@
|
||||
//page specific styles
|
||||
@import "pages/document-type-editor.less";
|
||||
@import "pages/login.less";
|
||||
@import "pages/welcome-dashboard.less";
|
||||
|
||||
|
||||
//used for property editors
|
||||
|
||||
@@ -65,12 +65,6 @@
|
||||
-webkit-box-shadow:none;
|
||||
}
|
||||
|
||||
.btn-group > .btn:first-child,
|
||||
.btn-group > .btn:last-child,
|
||||
.btn-group > .dropdown-toggle {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Button Sizes
|
||||
|
||||
@@ -138,16 +138,6 @@ input.umb-group-builder__group-title-input {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
|
||||
input.umb-group-builder__group-sort-value {
|
||||
font-size: 12px;
|
||||
padding: 0px 0 0px 5px;
|
||||
width: 40px;
|
||||
margin-bottom: 0;
|
||||
border-color: @gray-8;
|
||||
}
|
||||
|
||||
/* ---------- PROPERTIES ---------- */
|
||||
|
||||
@@ -47,8 +47,8 @@
|
||||
border-radius: 3px;
|
||||
padding: 10px;
|
||||
img {
|
||||
max-width: 50vw;
|
||||
max-height: 70vh;
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.forms-install-button {
|
||||
.btn {
|
||||
padding: 14px 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.installer-top {
|
||||
overflow: hidden;
|
||||
margin: 0 auto 30px;
|
||||
|
||||
@@ -4,9 +4,6 @@
|
||||
.login-overlay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: @gray-1 url(../img/application/logo.png) no-repeat 25px 30px fixed !important;
|
||||
background-size: 30px 30px !important;
|
||||
color: @white;
|
||||
position: absolute;
|
||||
z-index: 10000;
|
||||
top: 0;
|
||||
@@ -16,34 +13,24 @@
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
overflow-y: auto;
|
||||
background-color: @purple-d2;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
|
||||
only screen and (min-resolution: 144dpi)
|
||||
{
|
||||
.login-overlay {
|
||||
background-image: url(../img/application/logo@2x.png) !important;
|
||||
}
|
||||
.login-overlay__background-image {
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
opacity: 0.05;
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2),
|
||||
only screen and (min-resolution: 192dpi)
|
||||
{
|
||||
.login-overlay {
|
||||
background-image: url(../img/application/logo@2x.png) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 3),
|
||||
only screen and (min-resolution: 3dppx),
|
||||
only screen and (min-resolution: 350dpi)
|
||||
{
|
||||
.login-overlay {
|
||||
background-image: url(../img/application/logo@3x.png) !important;
|
||||
}
|
||||
.login-overlay__logo {
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
left: 25px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.login-overlay .umb-modalcolumn {
|
||||
@@ -51,21 +38,56 @@
|
||||
border: none;
|
||||
}
|
||||
|
||||
.login-overlay .umb-login-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 565px) {
|
||||
// making sure we don't crash into the logo
|
||||
.login-overlay .umb-login-container {
|
||||
padding-top: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-overlay .form {
|
||||
position:relative;
|
||||
display: block;
|
||||
top: 100px;
|
||||
left: 165px;
|
||||
width: 370px;
|
||||
text-align: right;
|
||||
background: @white;
|
||||
padding: 25px;
|
||||
width: 500px;
|
||||
margin-left: 25px;
|
||||
margin-right: 25px;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
|
||||
.login-overlay .form input[type="text"],
|
||||
.login-overlay .form input[type="password"] {
|
||||
height: 36px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.login-overlay .btn-success {
|
||||
padding: 12px 24px;
|
||||
}
|
||||
|
||||
.login-overlay .form label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.login-overlay h1 {
|
||||
display: block;
|
||||
text-align: right;
|
||||
color: @white;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
color: @black;
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.login-overlay .alert {
|
||||
@@ -76,62 +98,19 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-overlay .switch-view {
|
||||
margin-top: 10px;
|
||||
.login-overlay .external-logins form {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) and (max-height: 420px) and (orientation: landscape) {
|
||||
// Move form closer to top on narrow screen sizes
|
||||
.login-overlay .form {
|
||||
top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 565px) {
|
||||
// Remove padding on login-form on smaller devices
|
||||
.login-overlay .form {
|
||||
top: 60px;
|
||||
right: 25px;
|
||||
left: inherit;
|
||||
padding-left: 25px;
|
||||
padding-right:25px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 339px) {
|
||||
.login-overlay .form {
|
||||
input[type="text"], input[type="password"] {
|
||||
width: 250px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#hrOr {
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
#hrOr hr {
|
||||
.login-overlay .btn-social {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
margin: 0;
|
||||
border: none;
|
||||
background-color: @gray-3;
|
||||
height: 1px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#hrOr div {
|
||||
background-color: @black;
|
||||
position: relative;
|
||||
top: -16px;
|
||||
border: 1px solid @gray-3;
|
||||
padding: 4px;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: auto;
|
||||
color: @gray-8;
|
||||
.login-overlay .btn-social>:first-child {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.login-overlay .text-error,
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
// Title
|
||||
.welcome-dashboard__intro {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.welcome-dashboard__title {
|
||||
font-size: 30px;
|
||||
color: @gray-2;
|
||||
line-height: 1.3em;
|
||||
text-align: center;
|
||||
max-width: 750px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 15px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.welcome-dashboard__intro-text {
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
max-width: 750px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
|
||||
// Info box
|
||||
.welcome-dashboard__info-box-boxes {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.welcome-dashboard__info-box {
|
||||
background-color: @turquoise-washed;
|
||||
border-radius: 3px;
|
||||
border: 2px solid transparent;
|
||||
padding: 25px;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.welcome-dashboard__info-box:hover {
|
||||
border: 2px solid @turquoise;
|
||||
cursor: pointer;
|
||||
transition: border-color 150ms ease-in-out;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.welcome-dashboard__info-box:active,
|
||||
.welcome-dashboard__info-box:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
.welcome-dashboard__info-box-title {
|
||||
color: @turquoise-d1;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.welcome-dashboard__info-box-description {
|
||||
text-align: center;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
// Articles
|
||||
.welcome-dashboard__cards {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.welcome-dashboard__card {
|
||||
background-color: @gray-10;
|
||||
border-radius: 3px;
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.welcome-dashboard__card-image-wrapper {
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
margin-bottom: 30px;
|
||||
max-height: 225px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.welcome-dashboard__card-image {
|
||||
cursor: pointer;
|
||||
border-radius: 3px 3px 0 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.welcome-dashboard__card-image-wrapper + .welcome-dashboard__card-content {
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
.welcome-dashboard__card-content {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.welcome-dashboard__card-title {
|
||||
color: @gray-2;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
|
||||
.welcome-dashboard__card-teaser {
|
||||
font-size: 14px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
.umb-editor-tiny {
|
||||
width: 50px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.umb-editor-small {
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
@turquoise-l3: #C0F0ED;
|
||||
@turquoise-washed: #F3FDFC;
|
||||
|
||||
@purple-d2: #1D1333;
|
||||
@purple-d1: #2E2246;
|
||||
@purple: #413659;
|
||||
@purple-l1: #675E7A;
|
||||
@@ -141,9 +142,9 @@
|
||||
|
||||
|
||||
// Disabled this to keep consistency throughout the backoffice UI. Untill a better solution is thought up, this will do.
|
||||
@baseBorderRadius: 0px; // 2px;
|
||||
@borderRadiusLarge: 0px; // 6px;
|
||||
@borderRadiusSmall: 0px; // 3px;
|
||||
@baseBorderRadius: 3px; // 2px;
|
||||
@borderRadiusLarge: 3px; // 6px;
|
||||
@borderRadiusSmall: 3px; // 3px;
|
||||
|
||||
|
||||
// Tables
|
||||
@@ -185,7 +186,7 @@
|
||||
// -------------------------
|
||||
@inputBackground: @white;
|
||||
@inputBorder: @gray-7;
|
||||
@inputBorderRadius: @baseBorderRadius;
|
||||
@inputBorderRadius: 0;
|
||||
@inputDisabledBackground: @gray-10;
|
||||
@formActionsBackground: @gray-9;
|
||||
@inputHeight: @baseLineHeight + 10px; // base line-height + 8px vertical padding + 2px top/bottom border
|
||||
@@ -195,7 +196,7 @@
|
||||
// Tabs
|
||||
// -------------------------
|
||||
|
||||
@tabsBorderRadius: 0px;
|
||||
@tabsBorderRadius: @baseBorderRadius;
|
||||
|
||||
// Dropdowns
|
||||
// -------------------------
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
$scope.externalLoginProviders = externalLoginInfo.providers;
|
||||
$scope.externalLoginInfo = externalLoginInfo;
|
||||
$scope.resetPasswordCodeInfo = resetPasswordCodeInfo;
|
||||
$scope.backgroundImage = Umbraco.Sys.ServerVariables.umbracoSettings.loginBackgroundImage;
|
||||
|
||||
$scope.activateKonamiMode = function () {
|
||||
if ($cookies.konamiLogin == "1") {
|
||||
|
||||
@@ -1,133 +1,146 @@
|
||||
<div ng-controller="Umbraco.Dialogs.LoginController">
|
||||
|
||||
<div id="login" class="umb-modalcolumn umb-dialog" ng-class="{'show-validation': loginForm.$invalid}" ng-cloak konami-code="activateKonamiMode()">
|
||||
<div class="form">
|
||||
<h1>{{greeting}}</h1>
|
||||
|
||||
<div class="login-overlay__background-image" ng-if="backgroundImage" ng-style="{'background-image':'url(' + backgroundImage + ')'}"></div>
|
||||
|
||||
<div ng-show="view == 'login'">
|
||||
<div class="login-overlay__logo">
|
||||
<img ng-src="assets/img/application/logo.png" ng-srcset="assets/img/application/logo@2x.png 2x, assets/img/application/logo@3x.png 3x">
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<span ng-show="dialogData.isTimedOut"><localize key="login_timeout">Log in below</localize>.</span>
|
||||
<localize key="login_instruction">Log in below</localize>
|
||||
</p>
|
||||
<div class="umb-login-container">
|
||||
|
||||
<div class="external-logins" ng-if="externalLoginProviders.length > 0">
|
||||
<div class="form">
|
||||
<h1>{{greeting}}</h1>
|
||||
|
||||
<div class="text-error" ng-repeat="error in externalLoginInfo.errors">
|
||||
<div ng-show="view == 'login'">
|
||||
|
||||
<p>
|
||||
<span ng-show="dialogData.isTimedOut"><localize key="login_timeout">Log in below</localize>.</span>
|
||||
</p>
|
||||
|
||||
<div class="external-logins" ng-if="externalLoginProviders.length > 0">
|
||||
|
||||
<div class="text-error" ng-repeat="error in externalLoginInfo.errors">
|
||||
<span>{{error}}</span>
|
||||
</div>
|
||||
|
||||
<form method="POST" name="externalLoginForm" action="{{externalLoginFormAction}}">
|
||||
|
||||
<div ng-repeat="login in externalLoginProviders">
|
||||
|
||||
<button type="submit" class="btn btn-block btn-social"
|
||||
ng-class="login.properties.SocialStyle"
|
||||
id="{{login.authType}}" name="provider" value="{{login.authType}}"
|
||||
title="Log in using your {{login.caption}} account">
|
||||
<i class="fa" ng-class="login.properties.SocialIcon"></i>
|
||||
<localize key="login_signInWith">Sign in with</localize> {{login.caption}}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<form method="POST" name="loginForm" ng-submit="loginSubmit(login, password)">
|
||||
|
||||
<div class="control-group" ng-show="loginForm.$invalid">
|
||||
<div class="text-error">{{errorMsg}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-class="{error: loginForm.username.$invalid}">
|
||||
<label><localize key="general_username">Username</localize></label>
|
||||
<input type="text" ng-model="login" name="username" class="-full-width-input" localize="placeholder" placeholder="@placeholders_username" />
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-class="{error: loginForm.password.$invalid}">
|
||||
<label><localize key="general_password">Password</localize></label>
|
||||
<input type="password" ng-model="password" name="password" class="-full-width-input" localize="placeholder" placeholder="@placeholders_password" autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<button type="submit" class="btn btn-success" val-trigger-change="#login .form input"><localize key="general_login">Login</localize></button>
|
||||
<div ng-show="allowPasswordReset">
|
||||
<a class="muted" style="text-decoration: underline;" href="#" prevent-default ng-click="showRequestPasswordReset()"><localize key="login_forgottenPassword">Forgotten password?</localize></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-show="view == 'request-password-reset'">
|
||||
<p>
|
||||
<localize key="login_forgottenPasswordInstruction">An email will be sent to the address specified with a link to reset your password</localize>
|
||||
</p>
|
||||
|
||||
<form method="POST" name="requestPasswordResetForm" ng-submit="requestPasswordResetSubmit(email)">
|
||||
<div class="control-group" ng-class="{error: requestPasswordResetForm.email.$invalid}">
|
||||
<label><localize key="general_email">Email</localize></label>
|
||||
<input type="text" ng-model="email" name="email" class="-full-width-input" localize="placeholder" placeholder="@placeholders_email" />
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-show="requestPasswordResetForm.$invalid">
|
||||
<div class="text-error">{{errorMsg}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-show="showEmailResetConfirmation">
|
||||
<div class="text-info">
|
||||
<localize key="login_requestPasswordResetConfirmation">An email with password reset instructions will be sent to the specified address if it matched our records</localize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<button type="submit" class="btn btn-success" val-trigger-change="#login .form input"><localize key="general_submit">Submit</localize></button>
|
||||
<a class="muted" href="#" prevent-default ng-click="showLogin()" style="text-decoration: underline;"><localize key="login_returnToLogin">Return to login form</localize></a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-show="view == 'set-password'">
|
||||
|
||||
<p ng-hide="resetComplete">
|
||||
<localize key="login_setPasswordInstruction">Please provide a new password.</localize>
|
||||
</p>
|
||||
|
||||
<form method="POST" name="setPasswordForm" ng-submit="setPasswordSubmit(password, confirmPassword)">
|
||||
|
||||
<div ng-hide="resetComplete" class="control-group" ng-class="{error: setPasswordForm.password.$invalid}">
|
||||
<label><localize key="user_newPassword">New password</localize></label>
|
||||
<input type="password" ng-model="password" name="password" class="-full-width-input" localize="placeholder" placeholder="@placeholders_password" />
|
||||
</div>
|
||||
|
||||
<div ng-hide="resetComplete" class="control-group" ng-class="{error: setPasswordForm.confirmPassword.$invalid}">
|
||||
<label><localize key="user_confirmNewPassword">Confirm new password</localize></label>
|
||||
<input type="password" ng-model="confirmPassword" name="confirmPassword" class="-full-width-input" localize="placeholder" placeholder="@placeholders_confirmPassword" />
|
||||
</div>
|
||||
|
||||
<div ng-hide="resetComplete" class="control-group" ng-show="setPasswordForm.$invalid">
|
||||
<div class="text-error">{{errorMsg}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-show="showSetPasswordConfirmation">
|
||||
<div class="text-info">
|
||||
<localize key="login_setPasswordConfirmation">Your new password has been set and you may now use it to log in.</localize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-between items-center">
|
||||
<button ng-hide="resetComplete" type="submit" class="btn btn-success" val-trigger-change="#login .form input"><localize key="general_submit">Submit</localize></button>
|
||||
<a class="muted" href="#" prevent-default ng-click="showLogin()"><localize key="login_returnToLogin">Return to login form</localize></a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-show="view == 'password-reset-code-expired'">
|
||||
<div class="text-error" ng-repeat="error in resetPasswordCodeInfo.errors">
|
||||
<span>{{error}}</span>
|
||||
</div>
|
||||
|
||||
<form method="POST" name="externalLoginForm" action="{{externalLoginFormAction}}">
|
||||
|
||||
<div ng-repeat="login in externalLoginProviders">
|
||||
|
||||
<button type="submit" class="btn btn-block btn-social"
|
||||
ng-class="login.properties.SocialStyle"
|
||||
id="{{login.authType}}" name="provider" value="{{login.authType}}"
|
||||
title="Log in using your {{login.caption}} account">
|
||||
<i class="fa" ng-class="login.properties.SocialIcon"></i>
|
||||
<localize key="login_signInWith">Sign in with</localize> {{login.caption}}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div id="hrOr">
|
||||
<hr />
|
||||
<div><localize key="general_or">or</localize></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<form method="POST" name="loginForm" ng-submit="loginSubmit(login, password)">
|
||||
<div class="control-group" ng-class="{error: loginForm.username.$invalid}">
|
||||
<input type="text" ng-model="login" name="username" class="input-xlarge input--no-border" localize="placeholder" placeholder="@placeholders_username" />
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-class="{error: loginForm.password.$invalid}">
|
||||
<input type="password" ng-model="password" name="password" class="input-xlarge input--no-border" localize="placeholder" placeholder="@placeholders_password" autocomplete="off" />
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-show="loginForm.$invalid">
|
||||
<div class="text-error">{{errorMsg}}</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn" val-trigger-change="#login .form input"><localize key="general_login">Login</localize></button>
|
||||
|
||||
<div class="switch-view" ng-show="allowPasswordReset">
|
||||
<a class="muted" href="#" prevent-default ng-click="showRequestPasswordReset()"><localize key="login_forgottenPassword">Forgotten password?</localize></a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-show="view == 'request-password-reset'">
|
||||
<p>
|
||||
<localize key="login_forgottenPasswordInstruction">An email will be sent to the address specified with a link to reset your password</localize>
|
||||
</p>
|
||||
|
||||
<form method="POST" name="requestPasswordResetForm" ng-submit="requestPasswordResetSubmit(email)">
|
||||
<div class="control-group" ng-class="{error: requestPasswordResetForm.email.$invalid}">
|
||||
<input type="text" ng-model="email" name="email" class="input-xlarge input--no-border" localize="placeholder" placeholder="@placeholders_email" />
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-show="requestPasswordResetForm.$invalid">
|
||||
<div class="text-error">{{errorMsg}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-show="showEmailResetConfirmation">
|
||||
<div class="text-info">
|
||||
<localize key="login_requestPasswordResetConfirmation">An email with password reset instructions will be sent to the specified address if it matched our records</localize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn" val-trigger-change="#login .form input"><localize key="general_submit">Submit</localize></button>
|
||||
|
||||
<div class="switch-view">
|
||||
<a class="muted" href="#" prevent-default ng-click="showLogin()"><localize key="login_returnToLogin">Return to login form</localize></a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-show="view == 'set-password'">
|
||||
|
||||
<p ng-hide="resetComplete">
|
||||
<localize key="login_setPasswordInstruction">Please provide a new password.</localize>
|
||||
</p>
|
||||
|
||||
<form method="POST" name="setPasswordForm" ng-submit="setPasswordSubmit(password, confirmPassword)">
|
||||
|
||||
<div ng-hide="resetComplete" class="control-group" ng-class="{error: setPasswordForm.password.$invalid}">
|
||||
<input type="password" ng-model="password" name="password" class="input-xlarge" localize="placeholder" placeholder="@placeholders_password" />
|
||||
</div>
|
||||
|
||||
<div ng-hide="resetComplete" class="control-group" ng-class="{error: setPasswordForm.confirmPassword.$invalid}">
|
||||
<input type="password" ng-model="confirmPassword" name="confirmPassword" class="input-xlarge" localize="placeholder" placeholder="@placeholders_confirmPassword" />
|
||||
</div>
|
||||
|
||||
<div ng-hide="resetComplete" class="control-group" ng-show="setPasswordForm.$invalid">
|
||||
<div class="text-error">{{errorMsg}}</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group" ng-show="showSetPasswordConfirmation">
|
||||
<div class="text-info">
|
||||
<localize key="login_setPasswordConfirmation">Your new password has been set and you may now use it to log in.</localize>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button ng-hide="resetComplete" type="submit" class="btn" val-trigger-change="#login .form input"><localize key="general_submit">Submit</localize></button>
|
||||
|
||||
<div class="switch-view">
|
||||
<a class="muted" href="#" prevent-default ng-click="showLogin()"><localize key="login_returnToLogin">Return to login form</localize></a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div ng-show="view == 'password-reset-code-expired'">
|
||||
<div class="text-error" ng-repeat="error in resetPasswordCodeInfo.errors">
|
||||
<span>{{error}}</span>
|
||||
</div>
|
||||
|
||||
<div class="switch-view">
|
||||
<a class="muted" href="#" prevent-default ng-click="showLogin()"><localize key="login_returnToLogin">Return to login form</localize></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
function CopyOverlay($scope, localizationService, eventsService) {
|
||||
function CopyOverlay($scope, localizationService, eventsService, entityHelper) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
@@ -32,54 +32,30 @@
|
||||
selectedSearchResults: []
|
||||
};
|
||||
|
||||
// get entity type based on the section
|
||||
$scope.entityType = entityHelper.getEntityTypeFromSection(dialogOptions.section);
|
||||
|
||||
function nodeSelectHandler(ev, args) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
|
||||
if (args.node.metaData.listViewNode) {
|
||||
//check if list view 'search' node was selected
|
||||
|
||||
vm.searchInfo.showSearch = true;
|
||||
vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
|
||||
vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
|
||||
}
|
||||
else {
|
||||
//eventsService.emit("editors.content.copyController.select", args);
|
||||
|
||||
if ($scope.model.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.model.target.selected = false;
|
||||
}
|
||||
|
||||
$scope.model.target = args.node;
|
||||
$scope.model.target.selected = true;
|
||||
if(args && args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
|
||||
//eventsService.emit("editors.content.copyController.select", args);
|
||||
|
||||
if ($scope.model.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.model.target.selected = false;
|
||||
}
|
||||
|
||||
$scope.model.target = args.node;
|
||||
$scope.model.target.selected = true;
|
||||
}
|
||||
|
||||
function nodeExpandedHandler(ev, args) {
|
||||
if (angular.isArray(args.children)) {
|
||||
|
||||
//iterate children
|
||||
_.each(args.children, function (child) {
|
||||
//check if any of the items are list views, if so we need to add a custom
|
||||
// child: A node to activate the search
|
||||
if (child.metaData.isContainer) {
|
||||
child.hasChildren = true;
|
||||
child.children = [
|
||||
{
|
||||
level: child.level + 1,
|
||||
hasChildren: false,
|
||||
name: searchText,
|
||||
metaData: {
|
||||
listViewNode: child,
|
||||
},
|
||||
cssClass: "icon umb-tree-icon sprTree icon-search",
|
||||
cssClasses: ["not-published"]
|
||||
}
|
||||
];
|
||||
}
|
||||
});
|
||||
// open mini list view for list views
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +86,20 @@
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
|
||||
});
|
||||
|
||||
// Mini list view
|
||||
$scope.selectListViewNode = function (node) {
|
||||
node.selected = node.selected === true ? false : true;
|
||||
nodeSelectHandler({}, { node: node });
|
||||
};
|
||||
|
||||
$scope.closeMiniListView = function () {
|
||||
$scope.miniListView = undefined;
|
||||
};
|
||||
|
||||
function openMiniListView(node) {
|
||||
$scope.miniListView = node;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +1,47 @@
|
||||
<div ng-controller="Umbraco.Overlays.CopyOverlay as vm">
|
||||
|
||||
<div class="umb-control-group">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="vm.hideSearch"
|
||||
search-callback="vm.onSearchResults"
|
||||
search-from-id="{{vm.searchInfo.searchFromId}}"
|
||||
search-from-name="{{vm.searchInfo.searchFromName}}"
|
||||
show-search="{{vm.searchInfo.showSearch}}"
|
||||
section="{{model.section}}">
|
||||
</umb-tree-search-box>
|
||||
</div>
|
||||
<div ng-hide="miniListView">
|
||||
<div class="umb-control-group">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="vm.hideSearch"
|
||||
search-callback="vm.onSearchResults"
|
||||
search-from-id="{{vm.searchInfo.searchFromId}}"
|
||||
search-from-name="{{vm.searchInfo.searchFromName}}"
|
||||
show-search="{{vm.searchInfo.showSearch}}"
|
||||
section="{{model.section}}">
|
||||
</umb-tree-search-box>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group" ng-show="vm.searchInfo.showSearch">
|
||||
<umb-tree-search-results
|
||||
ng-if="vm.searchInfo.showSearch"
|
||||
results="vm.searchInfo.results"
|
||||
select-result-callback="vm.selectResult">
|
||||
</umb-tree-search-results>
|
||||
</div>
|
||||
<div class="umb-control-group" ng-show="vm.searchInfo.showSearch">
|
||||
<umb-tree-search-results
|
||||
ng-if="vm.searchInfo.showSearch"
|
||||
results="vm.searchInfo.results"
|
||||
select-result-callback="vm.selectResult">
|
||||
</umb-tree-search-results>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group -no-border" ng-hide="vm.searchInfo.showSearch">
|
||||
<umb-tree
|
||||
section="{{model.section}}"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewexpand="true"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group -no-border" ng-hide="vm.searchInfo.showSearch">
|
||||
<umb-tree
|
||||
section="{{model.section}}"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
|
||||
<div ng-if="miniListView" class="umb-control-group">
|
||||
<umb-mini-list-view
|
||||
node="miniListView"
|
||||
entity-type="{{entityType}}"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group -no-border">
|
||||
<label>
|
||||
|
||||
@@ -104,13 +104,18 @@
|
||||
function openPartialOverlay() {
|
||||
vm.partialItemOverlay = {
|
||||
view: "treepicker",
|
||||
section: "settings",
|
||||
section: "settings",
|
||||
treeAlias: "partialViews",
|
||||
entityType: "partialView",
|
||||
multiPicker: false,
|
||||
show: true,
|
||||
filter: function(i) {
|
||||
if(i.name.indexOf(".cshtml") === -1 && i.name.indexOf(".vbhtml") === -1) {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
filterCssClass: "not-allowed",
|
||||
title: localizationService.localize("template_insertPartialView"),
|
||||
|
||||
show: true,
|
||||
select: function(node){
|
||||
|
||||
$scope.model.insert = {
|
||||
|
||||
+44
-54
@@ -47,69 +47,44 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
|
||||
}
|
||||
|
||||
function nodeSelectHandler(ev, args) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
|
||||
if (args.node.metaData.listViewNode) {
|
||||
//check if list view 'search' node was selected
|
||||
if(args && args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
|
||||
$scope.searchInfo.showSearch = true;
|
||||
$scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
|
||||
$scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
|
||||
}
|
||||
else {
|
||||
eventsService.emit("dialogs.linkPicker.select", args);
|
||||
eventsService.emit("dialogs.linkPicker.select", args);
|
||||
|
||||
if ($scope.currentNode) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.currentNode.selected = false;
|
||||
}
|
||||
if ($scope.currentNode) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.currentNode.selected = false;
|
||||
}
|
||||
|
||||
$scope.currentNode = args.node;
|
||||
$scope.currentNode.selected = true;
|
||||
$scope.model.target.id = args.node.id;
|
||||
$scope.model.target.udi = args.node.udi;
|
||||
$scope.model.target.name = args.node.name;
|
||||
$scope.currentNode = args.node;
|
||||
$scope.currentNode.selected = true;
|
||||
$scope.model.target.id = args.node.id;
|
||||
$scope.model.target.udi = args.node.udi;
|
||||
$scope.model.target.name = args.node.name;
|
||||
|
||||
if (args.node.id < 0) {
|
||||
$scope.model.target.url = "/";
|
||||
}
|
||||
else {
|
||||
contentResource.getNiceUrl(args.node.id).then(function (url) {
|
||||
$scope.model.target.url = url;
|
||||
});
|
||||
}
|
||||
if (args.node.id < 0) {
|
||||
$scope.model.target.url = "/";
|
||||
}
|
||||
else {
|
||||
contentResource.getNiceUrl(args.node.id).then(function (url) {
|
||||
$scope.model.target.url = url;
|
||||
});
|
||||
}
|
||||
|
||||
if (!angular.isUndefined($scope.model.target.isMedia)) {
|
||||
delete $scope.model.target.isMedia;
|
||||
}
|
||||
}
|
||||
if (!angular.isUndefined($scope.model.target.isMedia)) {
|
||||
delete $scope.model.target.isMedia;
|
||||
}
|
||||
}
|
||||
|
||||
function nodeExpandedHandler(ev, args) {
|
||||
if (angular.isArray(args.children)) {
|
||||
|
||||
//iterate children
|
||||
_.each(args.children, function (child) {
|
||||
//check if any of the items are list views, if so we need to add a custom
|
||||
// child: A node to activate the search
|
||||
if (child.metaData.isContainer) {
|
||||
child.hasChildren = true;
|
||||
child.children = [
|
||||
{
|
||||
level: child.level + 1,
|
||||
hasChildren: false,
|
||||
name: searchText,
|
||||
metaData: {
|
||||
listViewNode: child,
|
||||
},
|
||||
cssClass: "icon umb-tree-icon sprTree icon-search",
|
||||
cssClasses: ["not-published"]
|
||||
}
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
// open mini list view for list views
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.switchToMediaPicker = function () {
|
||||
@@ -160,4 +135,19 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
|
||||
});
|
||||
|
||||
// Mini list view
|
||||
$scope.selectListViewNode = function (node) {
|
||||
node.selected = node.selected === true ? false : true;
|
||||
nodeSelectHandler({}, { node: node });
|
||||
};
|
||||
|
||||
$scope.closeMiniListView = function () {
|
||||
$scope.miniListView = undefined;
|
||||
};
|
||||
|
||||
function openMiniListView(node) {
|
||||
$scope.miniListView = node;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -28,34 +28,45 @@
|
||||
<localize key="defaultdialogs_linkToPage">Link to page</localize>
|
||||
</h5>
|
||||
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="{{section}}">
|
||||
</umb-tree-search-box>
|
||||
<div ng-hide="miniListView">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="{{section}}">
|
||||
</umb-tree-search-box>
|
||||
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
|
||||
<div ng-hide="searchInfo.showSearch">
|
||||
<umb-tree
|
||||
section="content"
|
||||
hideheader="true"
|
||||
hideoptions="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
isdialog="true"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
<div ng-hide="searchInfo.showSearch">
|
||||
<umb-tree
|
||||
section="content"
|
||||
hideheader="true"
|
||||
hideoptions="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewexpand="true"
|
||||
isdialog="true"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-mini-list-view
|
||||
ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="Document"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
function MoveOverlay($scope, localizationService, eventsService) {
|
||||
function MoveOverlay($scope, localizationService, eventsService, entityHelper) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
@@ -32,54 +32,32 @@
|
||||
selectedSearchResults: []
|
||||
};
|
||||
|
||||
// get entity type based on the section
|
||||
$scope.entityType = entityHelper.getEntityTypeFromSection(dialogOptions.section);
|
||||
|
||||
function nodeSelectHandler(ev, args) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
|
||||
if (args.node.metaData.listViewNode) {
|
||||
//check if list view 'search' node was selected
|
||||
|
||||
vm.searchInfo.showSearch = true;
|
||||
vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
|
||||
vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
|
||||
if(args && args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
else {
|
||||
//eventsService.emit("editors.content.copyController.select", args);
|
||||
|
||||
if ($scope.model.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.model.target.selected = false;
|
||||
}
|
||||
//eventsService.emit("editors.content.copyController.select", args);
|
||||
|
||||
$scope.model.target = args.node;
|
||||
$scope.model.target.selected = true;
|
||||
if ($scope.model.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.model.target.selected = false;
|
||||
}
|
||||
|
||||
$scope.model.target = args.node;
|
||||
$scope.model.target.selected = true;
|
||||
|
||||
}
|
||||
|
||||
function nodeExpandedHandler(ev, args) {
|
||||
if (angular.isArray(args.children)) {
|
||||
|
||||
//iterate children
|
||||
_.each(args.children, function (child) {
|
||||
//check if any of the items are list views, if so we need to add a custom
|
||||
// child: A node to activate the search
|
||||
if (child.metaData.isContainer) {
|
||||
child.hasChildren = true;
|
||||
child.children = [
|
||||
{
|
||||
level: child.level + 1,
|
||||
hasChildren: false,
|
||||
name: searchText,
|
||||
metaData: {
|
||||
listViewNode: child,
|
||||
},
|
||||
cssClass: "icon umb-tree-icon sprTree icon-search",
|
||||
cssClasses: ["not-published"]
|
||||
}
|
||||
];
|
||||
}
|
||||
});
|
||||
// open mini list view for list views
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +88,19 @@
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
|
||||
});
|
||||
|
||||
// Mini list view
|
||||
$scope.selectListViewNode = function (node) {
|
||||
node.selected = node.selected === true ? false : true;
|
||||
nodeSelectHandler({}, { node: node });
|
||||
};
|
||||
|
||||
$scope.closeMiniListView = function () {
|
||||
$scope.miniListView = undefined;
|
||||
};
|
||||
|
||||
function openMiniListView(node) {
|
||||
$scope.miniListView = node;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,33 +1,43 @@
|
||||
<div ng-controller="Umbraco.Overlays.MoveOverlay as vm">
|
||||
<div ng-hide="miniListView">
|
||||
<div class="umb-control-group">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="vm.hideSearch"
|
||||
search-callback="vm.onSearchResults"
|
||||
search-from-id="{{vm.searchInfo.searchFromId}}"
|
||||
search-from-name="{{vm.searchInfo.searchFromName}}"
|
||||
show-search="{{vm.searchInfo.showSearch}}"
|
||||
section="{{model.section}}">
|
||||
</umb-tree-search-box>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="vm.hideSearch"
|
||||
search-callback="vm.onSearchResults"
|
||||
search-from-id="{{vm.searchInfo.searchFromId}}"
|
||||
search-from-name="{{vm.searchInfo.searchFromName}}"
|
||||
show-search="{{vm.searchInfo.showSearch}}"
|
||||
section="{{model.section}}">
|
||||
</umb-tree-search-box>
|
||||
<div class="umb-control-group" ng-show="vm.searchInfo.showSearch">
|
||||
<umb-tree-search-results
|
||||
ng-if="vm.searchInfo.showSearch"
|
||||
results="vm.searchInfo.results"
|
||||
select-result-callback="vm.selectResult">
|
||||
</umb-tree-search-results>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group -no-border" ng-hide="vm.searchInfo.showSearch">
|
||||
<umb-tree
|
||||
section="{{model.section}}"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewexpand="true"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group" ng-show="vm.searchInfo.showSearch">
|
||||
<umb-tree-search-results
|
||||
ng-if="vm.searchInfo.showSearch"
|
||||
results="vm.searchInfo.results"
|
||||
select-result-callback="vm.selectResult">
|
||||
</umb-tree-search-results>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group -no-border" ng-hide="vm.searchInfo.showSearch">
|
||||
<umb-tree
|
||||
section="{{model.section}}"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
<umb-mini-list-view
|
||||
ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="{{entityType}}"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
|
||||
</div>
|
||||
|
||||
+2
@@ -10,6 +10,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
|
||||
$scope.treeAlias = dialogOptions.treeAlias;
|
||||
$scope.multiPicker = dialogOptions.multiPicker;
|
||||
$scope.hideHeader = true;
|
||||
// if you need to load a not initialized tree set this value to false - default is true
|
||||
$scope.onlyInitialized = dialogOptions.onlyInitialized;
|
||||
$scope.searchInfo = {
|
||||
searchFromId: dialogOptions.startNodeId,
|
||||
searchFromName: null,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
hideheader="{{hideHeader}}"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
onlyinitialized="{{onlyInitialized}}"
|
||||
customtreeparams="{{customTreeParams}}"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewsearch="true"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<a class="icon icon-arrow-left" ng-if="showSearch == 'true'" title="Back" ng-click="hideSearch()"></a>
|
||||
<input type="text"
|
||||
ng-model="term"
|
||||
class="umb-search-field search-query"
|
||||
class="umb-search-field search-query -full-width-input"
|
||||
placeholder="{{searchPlaceholderText}}"
|
||||
focus-when="{{showSearch}}">
|
||||
<h4 ng-if="showSearch && searchFromName">
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
|
||||
<ng-form name="groupSortOrderForm">
|
||||
<div class="umb-group-builder__group-sort-order" ng-if="sortingMode">
|
||||
<input name="groupSortOrder" type="number" class="umb-group-builder__group-sort-value" ng-model="tab.sortOrder" ng-disabled="tab.inherited" on-blur="changeSortOrderValue(tab)" required />
|
||||
<input name="groupSortOrder" type="number" class="umb-editor-tiny" style="margin-bottom: 0;" ng-model="tab.sortOrder" ng-disabled="tab.inherited" on-blur="changeSortOrderValue(tab)" required />
|
||||
<div class="umb-validation-label -arrow-left" ng-if="groupSortOrderForm.groupSortOrder.$error.required && tab.showSortOrderMissing"><localize key="required"></localize></div>
|
||||
<div class="umb-validation-label -arrow-left" ng-if="!tab.showSortOrderMissing" val-msg-for="groupSortOrder" val-toggle-msg="required"><localize key="required"></localize></div>
|
||||
</div>
|
||||
|
||||
@@ -22,54 +22,29 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController",
|
||||
var node = dialogOptions.currentNode;
|
||||
|
||||
function nodeSelectHandler(ev, args) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
|
||||
if (args.node.metaData.listViewNode) {
|
||||
//check if list view 'search' node was selected
|
||||
if(args && args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
|
||||
$scope.searchInfo.showSearch = true;
|
||||
$scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
|
||||
$scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
|
||||
}
|
||||
else {
|
||||
eventsService.emit("editors.content.copyController.select", args);
|
||||
eventsService.emit("editors.content.copyController.select", args);
|
||||
|
||||
if ($scope.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.target.selected = false;
|
||||
}
|
||||
if ($scope.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.target.selected = false;
|
||||
}
|
||||
|
||||
$scope.target = args.node;
|
||||
$scope.target.selected = true;
|
||||
}
|
||||
$scope.target = args.node;
|
||||
$scope.target.selected = true;
|
||||
|
||||
}
|
||||
|
||||
function nodeExpandedHandler(ev, args) {
|
||||
if (angular.isArray(args.children)) {
|
||||
|
||||
//iterate children
|
||||
_.each(args.children, function (child) {
|
||||
//check if any of the items are list views, if so we need to add a custom
|
||||
// child: A node to activate the search
|
||||
if (child.metaData.isContainer) {
|
||||
child.hasChildren = true;
|
||||
child.children = [
|
||||
{
|
||||
level: child.level + 1,
|
||||
hasChildren: false,
|
||||
name: searchText,
|
||||
metaData: {
|
||||
listViewNode: child,
|
||||
},
|
||||
cssClass: "icon umb-tree-icon sprTree icon-search",
|
||||
cssClasses: ["not-published"]
|
||||
}
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
// open mini list view for list views
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.hideSearch = function () {
|
||||
@@ -136,4 +111,19 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController",
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
|
||||
});
|
||||
|
||||
// Mini list view
|
||||
$scope.selectListViewNode = function (node) {
|
||||
node.selected = node.selected === true ? false : true;
|
||||
nodeSelectHandler({}, { node: node });
|
||||
};
|
||||
|
||||
$scope.closeMiniListView = function () {
|
||||
$scope.miniListView = undefined;
|
||||
};
|
||||
|
||||
function openMiniListView(node) {
|
||||
$scope.miniListView = node;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -20,53 +20,29 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController",
|
||||
var node = dialogOptions.currentNode;
|
||||
|
||||
function nodeSelectHandler(ev, args) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
|
||||
if (args.node.metaData.listViewNode) {
|
||||
//check if list view 'search' node was selected
|
||||
if(args && args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
|
||||
$scope.searchInfo.showSearch = true;
|
||||
$scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
|
||||
$scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
|
||||
}
|
||||
else {
|
||||
eventsService.emit("editors.content.moveController.select", args);
|
||||
eventsService.emit("editors.content.moveController.select", args);
|
||||
|
||||
if ($scope.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.target.selected = false;
|
||||
}
|
||||
if ($scope.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.target.selected = false;
|
||||
}
|
||||
|
||||
$scope.target = args.node;
|
||||
$scope.target.selected = true;
|
||||
|
||||
$scope.target = args.node;
|
||||
$scope.target.selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
function nodeExpandedHandler(ev, args) {
|
||||
if (angular.isArray(args.children)) {
|
||||
|
||||
//iterate children
|
||||
_.each(args.children, function (child) {
|
||||
//check if any of the items are list views, if so we need to add a custom
|
||||
// child: A node to activate the search
|
||||
if (child.metaData.isContainer) {
|
||||
child.hasChildren = true;
|
||||
child.children = [
|
||||
{
|
||||
level: child.level + 1,
|
||||
hasChildren: false,
|
||||
name: searchText,
|
||||
metaData: {
|
||||
listViewNode: child,
|
||||
},
|
||||
cssClass: "icon umb-tree-icon sprTree icon-search",
|
||||
cssClasses: ["not-published"]
|
||||
}
|
||||
];
|
||||
}
|
||||
});
|
||||
}
|
||||
// open mini list view for list views
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.hideSearch = function () {
|
||||
@@ -136,4 +112,19 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController",
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
|
||||
});
|
||||
|
||||
// Mini list view
|
||||
$scope.selectListViewNode = function (node) {
|
||||
node.selected = node.selected === true ? false : true;
|
||||
nodeSelectHandler({}, { node: node });
|
||||
};
|
||||
|
||||
$scope.closeMiniListView = function () {
|
||||
$scope.miniListView = undefined;
|
||||
};
|
||||
|
||||
function openMiniListView(node) {
|
||||
$scope.miniListView = node;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,6 +1,22 @@
|
||||
<div ng-controller="Umbraco.Editors.Content.CopyController">
|
||||
<div class="umb-dialog-body form-horizontal">
|
||||
<div class="umb-dialog-body form-horizontal" ng-cloak>
|
||||
<div class="umb-pane">
|
||||
|
||||
<div ng-show="error">
|
||||
<div class="alert alert-error">
|
||||
<div><strong>{{error.errorMsg}}</strong></div>
|
||||
<div>{{error.data.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="success">
|
||||
<div class="alert alert-success">
|
||||
<strong>{{currentNode.name}}</strong> was copied to
|
||||
<strong>{{target.name}}</strong>
|
||||
</div>
|
||||
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
|
||||
</div>
|
||||
|
||||
<p class="abstract" ng-hide="success">
|
||||
Choose where to copy <strong>{{currentNode.name}}</strong> to in the tree structure below
|
||||
</p>
|
||||
@@ -9,52 +25,51 @@
|
||||
<div class="umb-loader"></div>
|
||||
</div>
|
||||
|
||||
<div ng-show="error">
|
||||
<h5 class="text-error">{{error.errorMsg}}</h5>
|
||||
<p class="text-error">{{error.data.message}}</p>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success" ng-show="success">
|
||||
<p>
|
||||
<strong>{{currentNode.name}}</strong> was copied to
|
||||
<strong>{{target.name}}</strong>
|
||||
</p>
|
||||
|
||||
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
|
||||
</div>
|
||||
|
||||
<div ng-hide="success">
|
||||
|
||||
<umb-tree-search-box hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="{{section}}">
|
||||
</umb-tree-search-box>
|
||||
<div ng-hide="miniListView">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="{{section}}">
|
||||
</umb-tree-search-box>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<umb-tree-search-results ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
|
||||
<div ng-hide="searchInfo.showSearch">
|
||||
<umb-tree section="content"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
<div ng-hide="searchInfo.showSearch">
|
||||
<umb-tree
|
||||
section="content"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewexpand="true"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-mini-list-view
|
||||
ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="Document"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
|
||||
<umb-pane>
|
||||
<umb-control-group localize="label" label="@defaultdialogs_relateToOriginalLabel">
|
||||
<input type="checkbox" ng-model="$parent.$parent.relateToOriginal"/>
|
||||
</umb-control-group>
|
||||
|
||||
</umb-pane>
|
||||
|
||||
<umb-pane>
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<div ng-controller="Umbraco.Editors.Content.MoveController">
|
||||
<div class="umb-dialog-body">
|
||||
<div class="umb-dialog-body" ng-cloak>
|
||||
<div class="umb-pane">
|
||||
|
||||
<div ng-show="error">
|
||||
<div class="alert alert-error">
|
||||
<div><strong>{{error.errorMsg}}</strong></div>
|
||||
<div>{{error.data.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="success">
|
||||
<div class="alert alert-success">
|
||||
<strong>{{currentNode.name}}</strong> was moved underneath <strong>{{target.name}}</strong>
|
||||
</div>
|
||||
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
|
||||
</div>
|
||||
|
||||
<p class="abstract" ng-hide="success">
|
||||
<localize key="actions_chooseWhereToMove">Choose where to move</localize>
|
||||
<strong>{{currentNode.name}}</strong>
|
||||
@@ -12,43 +26,47 @@
|
||||
<div class="umb-loader"></div>
|
||||
</div>
|
||||
|
||||
<div ng-show="error">
|
||||
<h5 class="text-error">{{error.errorMsg}}</h5>
|
||||
<p class="text-error">{{error.data.message}}</p>
|
||||
</div>
|
||||
|
||||
<div ng-show="success">
|
||||
<h5 class="text-success"><strong>{{currentNode.name}}</strong> was moved underneath <strong>{{target.name}}</strong></h5>
|
||||
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
|
||||
</div>
|
||||
|
||||
<div ng-hide="success">
|
||||
|
||||
<umb-tree-search-box hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="{{section}}">
|
||||
</umb-tree-search-box>
|
||||
<div ng-hide="miniListView">
|
||||
<umb-tree-search-box
|
||||
hide-search-callback="hideSearch"
|
||||
search-callback="onSearchResults"
|
||||
search-from-id="{{searchInfo.searchFromId}}"
|
||||
search-from-name="{{searchInfo.searchFromName}}"
|
||||
show-search="{{searchInfo.showSearch}}"
|
||||
section="{{section}}">
|
||||
</umb-tree-search-box>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
||||
<umb-tree-search-results ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
<umb-tree-search-results
|
||||
ng-if="searchInfo.showSearch"
|
||||
results="searchInfo.results"
|
||||
select-result-callback="selectResult">
|
||||
</umb-tree-search-results>
|
||||
|
||||
<div ng-hide="searchInfo.showSearch">
|
||||
<umb-tree section="content"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
<div ng-hide="searchInfo.showSearch">
|
||||
<umb-tree
|
||||
section="content"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewexpand="true"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<umb-mini-list-view
|
||||
ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="Document"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,6 +22,51 @@ function startUpDynamicContentController(dashboardResource, assetsService) {
|
||||
var vm = this;
|
||||
vm.loading = true;
|
||||
vm.showDefault = false;
|
||||
|
||||
// default dashboard content
|
||||
vm.defaultDashboard = {
|
||||
infoBoxes: [
|
||||
{
|
||||
title: "Documentation",
|
||||
description: "Find the answers to your Umbraco questions",
|
||||
url: "https://our.umbraco.org/documentation/?utm_source=core&utm_medium=dashboard&utm_content=text&utm_campaign=documentation/"
|
||||
},
|
||||
{
|
||||
title: "Community",
|
||||
description: "Find the answers or ask your Umbraco questions",
|
||||
url: "https://our.umbraco.org/?utm_source=core&utm_medium=dashboard&utm_content=text&utm_campaign=our_forum"
|
||||
},
|
||||
{
|
||||
title: "Umbraco.tv",
|
||||
description: "Tutorial videos (some are free, some are on subscription)",
|
||||
url: "https://umbraco.tv/?utm_source=core&utm_medium=dashboard&utm_content=text&utm_campaign=tutorial_videos"
|
||||
},
|
||||
{
|
||||
title: "Training",
|
||||
description: "Real-life training and official Umbraco certifications",
|
||||
url: "https://umbraco.com/training/?utm_source=core&utm_medium=dashboard&utm_content=text&utm_campaign=training"
|
||||
}
|
||||
],
|
||||
articles: [
|
||||
{
|
||||
title: "Umbraco.TV - Learn from the source!",
|
||||
description: "Umbraco.TV will help you go from zero to Umbraco hero at a pace that suits you. Our easy to follow online training videos will give you the fundamental knowledge to start building awesome Umbraco websites.",
|
||||
img: "views/dashboard/default/umbracotv.jpg",
|
||||
url: "https://umbraco.tv/?utm_source=core&utm_medium=dashboard&utm_content=image&utm_campaign=tv",
|
||||
altText: "Umbraco.TV - Hours of Umbraco Video Tutorials",
|
||||
buttonText: "Visit Umbraco.TV"
|
||||
},
|
||||
{
|
||||
title: "Our Umbraco - The Friendliest Community",
|
||||
description: "Our Umbraco - the official community site is your one stop for everything Umbraco. Whether you need a question answered or looking for cool plugins, the world's best and friendliest community is just a click away.",
|
||||
img: "views/dashboard/default/ourumbraco.jpg",
|
||||
url: "https://our.umbraco.org/?utm_source=core&utm_medium=dashboard&utm_content=image&utm_campaign=our",
|
||||
altText: "Our Umbraco",
|
||||
buttonText: "Visit Our Umbraco"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
//proxy remote css through the local server
|
||||
assetsService.loadCss( dashboardResource.getRemoteDashboardCssUrl("content") );
|
||||
|
||||
@@ -38,58 +38,34 @@
|
||||
|
||||
<!-- Default content in case we cannot fetch content from the outside -->
|
||||
<div style="max-width: 1200px" ng-if="vm.showDefault">
|
||||
<h3><strong>Welcome to The Friendly CMS</strong></h3>
|
||||
<p style="font-size: 16px; line-height: 1.5; margin-bottom: 30px; max-width: 760px;">
|
||||
<span>Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible.</span>
|
||||
</p>
|
||||
<h4>Find out more:</h4>
|
||||
<ul>
|
||||
<li>Find the answers to your Umbraco questions in <a href="https://our.umbraco.org/documentation/" target="_blank" class="btn-link-underline">our Documentation</a></li>
|
||||
<li>Ask a question in the <a href="https://our.umbraco.org" target="_blank" class="btn-link-underline">Community Forum</a></li>
|
||||
<li>Find <a href="https://our.umbraco.org/projects" target="_blank" class="btn-link-underline">an add-on package</a> to help you get going quickly</li>
|
||||
<li>Watch <a href="https://umbraco.tv" class="btn-link-underline">our tutorial videos</a> (some are free, some require a subscription)</li>
|
||||
<li>Find out about <a href="https://umbraco.com/products-and-support" target="_blank" class="btn-link-underline">our productivity boosting tools and commercial support</a></li>
|
||||
<li>Find out about <a href="https://umbraco.com/products/training" target="_blank" class="btn-link-underline">real-life training and certification opportunities</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="row">
|
||||
<div class="span4">
|
||||
<a href="https://umbraco.tv/?utm_source=core&utm_medium=dashboard&utm_content=image&utm_campaign=tv" target="_blank" class="nounderline">
|
||||
<img src="views/dashboard/default/umbracotv.png" alt="Umbraco.TV - Hours of Umbraco Video Tutorials" style="margin: 10px 0 10px 0" />
|
||||
</a>
|
||||
<div class="welcome-dashboard__intro">
|
||||
<h1 class="welcome-dashboard__title">Welcome to The Friendly CMS</h1>
|
||||
<p class="welcome-dashboard__intro-text">Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible.</p>
|
||||
</div>
|
||||
|
||||
<div class="welcome-dashboard__info-box-boxes">
|
||||
<a ng-href="{{infoBox.url}}" target="_blank" class="welcome-dashboard__info-box" ng-repeat="infoBox in vm.defaultDashboard.infoBoxes">
|
||||
<div class="welcome-dashboard__info-box-title">{{ infoBox.title }}</div>
|
||||
<div class="welcome-dashboard__info-box-description">{{ infoBox.description }}</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a href="https://umbraco.tv/?utm_source=core&utm_medium=dashboard&utm_content=header&utm_campaign=tv" target="_blank" class="btn-link -underline">
|
||||
<h4> <strong>Umbraco.TV - Learn from the source!</strong> </h4>
|
||||
</a>
|
||||
|
||||
<p style="line-height: 1.5;">
|
||||
Umbraco.TV will help you go from zero to Umbraco
|
||||
hero at a pace that suits you. Our easy to follow
|
||||
online training videos will give you the fundamental
|
||||
knowledge to start building awesome Umbraco websites.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="span4 offset1">
|
||||
|
||||
<a href="https://our.umbraco.org/?utm_source=core&utm_medium=dashboard&utm_content=image&utm_campaign=our" target="_blank" class="nounderline">
|
||||
<img src="views/dashboard/default/ourumbraco.png" alt="Our Umbraco" style="margin: 10px 0 10px 0" />
|
||||
</a>
|
||||
|
||||
<a href="https://our.umbraco.org/?utm_source=core&utm_medium=dashboard&utm_content=header&utm_campaign=our" target="_blank" class="btn-link -underline">
|
||||
<h4><strong>Our Umbraco - The Friendliest Community</strong></h4>
|
||||
</a>
|
||||
|
||||
<p style="line-height: 1.5;">
|
||||
Our Umbraco - the official community site is your one
|
||||
stop for everything Umbraco. Whether you need a
|
||||
question answered or looking for cool plugins, the
|
||||
worlds best community is just a click away.
|
||||
</p>
|
||||
|
||||
<div class="welcome-dashboard__cards">
|
||||
<div class="welcome-dashboard__card" ng-repeat="article in vm.defaultDashboard.articles">
|
||||
<div class="welcome-dashboard__card-image-wrapper">
|
||||
<a ng-href="{{article.url}}" target="_blank">
|
||||
<img class="welcome-dashboard__card-image" ng-src="{{article.img}}" alt="{{ article.altText }}" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="welcome-dashboard__card-content">
|
||||
<h2 class="welcome-dashboard__card-title">{{ article.title }}</h2>
|
||||
<div class="welcome-dashboard__card-teaser">{{ article.description }}</div>
|
||||
<a class="btn btn-success" ng-href="{{article.url}}" target="_blank">{{article.buttonText}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB |
@@ -3,232 +3,285 @@
|
||||
<h3 class="bold">Examine Management</h3>
|
||||
|
||||
<div ng-show="loading">
|
||||
Loading...
|
||||
<div class="umb-healthcheck-group__details-status-overlay"></div>
|
||||
<umb-load-indicator></umb-load-indicator>
|
||||
</div>
|
||||
|
||||
<h4>Indexers</h4>
|
||||
<div ng-hide="loading" class="umb-healthcheck-group__details">
|
||||
|
||||
<ul ng-hide="loading">
|
||||
<li class="provider" ng-repeat="indexer in indexerDetails">
|
||||
<div class="umb-healthcheck-group__details-group-title">
|
||||
<div class="umb-healthcheck-group__details-group-name">Indexers</div>
|
||||
</div>
|
||||
|
||||
<a class="btn-link -underline" href="" ng-click="toggle(indexer, 'showProperties')">
|
||||
{{indexer.name}}
|
||||
</a>
|
||||
<div class="umb-healthcheck-group__details-checks">
|
||||
<div class="umb-healthcheck-group__details-check">
|
||||
<div class="umb-healthcheck-group__details-check-title">
|
||||
<div class="umb-healthcheck-group__details-check-name">Manage Examine's indexes</div>
|
||||
<div class="umb-healthcheck-group__details-check-description">Allows you to view the details of each index and provides some tools for managing the indexes</div>
|
||||
</div>
|
||||
|
||||
<ul ng-show="indexer.showProperties">
|
||||
<div class="umb-healthcheck-group__details-status" ng-repeat="indexer in indexerDetails">
|
||||
|
||||
<li>
|
||||
<div class="umb-healthcheck-group__details-status-icon-container">
|
||||
<i class="umb-healthcheck-status-icon" ng-class="{'icon-check color-green' : indexer.isHealthy, 'icon-delete color-red' : !indexer.isHealthy}"></i>
|
||||
</div>
|
||||
|
||||
<a href="" ng-click="toggle(indexer, 'showTools')">Index info & tools</a>
|
||||
|
||||
<div ng-show="indexer.showTools && indexer.isLuceneIndex">
|
||||
<div>
|
||||
<br />
|
||||
<div ng-show="!indexer.isProcessing && (!indexer.processingAttempts || indexer.processingAttempts < 100)">
|
||||
|
||||
<button type="button" class="btn btn-warning" ng-click="rebuildIndex(indexer)">Rebuild index</button>
|
||||
<button type="button" class="btn btn-warning" ng-click="optimizeIndex(indexer)" ng-show="indexer.documentCount > 0">Optimize index</button>
|
||||
<div class="umb-healthcheck-group__details-status-content">
|
||||
<div class="umb-healthcheck-group__details-status-text">
|
||||
<div ng-show="!indexer.isHealthy">
|
||||
{{indexer.name}}
|
||||
</div>
|
||||
|
||||
<div ng-show="indexer.isProcessing" class="umb-loader-wrapper" ng-show="actionInProgress">
|
||||
<div class="umb-loader"></div>
|
||||
</div>
|
||||
|
||||
<div class="error" ng-show="indexer.processingAttempts >= 100">
|
||||
The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation
|
||||
<a class="btn-link -underline" href="" ng-click="toggle(indexer, 'showProperties')" ng-show="indexer.isHealthy">
|
||||
{{indexer.name}}
|
||||
</a>
|
||||
<div ng-if="!indexer.isHealthy" class="text-error">
|
||||
The index cannot be read and will need to be rebuilt
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-bordered table-condensed">
|
||||
<caption> </caption>
|
||||
<tr>
|
||||
<th>Documents in index</th>
|
||||
<td>{{indexer.documentCount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Fields in index</th>
|
||||
<td>{{indexer.fieldCount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Has deletions?</th>
|
||||
<td>
|
||||
<span>{{indexer.deletionCount > 0}}</span>
|
||||
(<span>{{indexer.deletionCount}}</span>)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Optimized?</th>
|
||||
<td>
|
||||
<span>{{indexer.isOptimized}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li ng-show="indexer.indexCriteria.IncludeNodeTypes.length > 0 || indexer.indexCriteria.ExcludeNodeTypes.length > 0 || indexer.indexCriteria.ParentNodeId">
|
||||
<a href="" ng-click="toggle(indexer, 'showNodeTypes')">Node types</a>
|
||||
<table ng-show="indexer.showNodeTypes" class="table table-bordered table-condensed">
|
||||
<tr ng-show="indexer.indexCriteria.IncludeNodeTypes.length > 0">
|
||||
<th>Include node types</th>
|
||||
<td>{{indexer.indexCriteria.IncludeNodeTypes | json}}</td>
|
||||
</tr>
|
||||
<tr ng-show="indexer.indexCriteria.ExcludeNodeTypes.length > 0">
|
||||
<th>Exclude node types</th>
|
||||
<td>{{indexer.indexCriteria.ExcludeNodeTypes | json}}</td>
|
||||
</tr>
|
||||
<tr ng-show="indexer.indexCriteria.ParentNodeId">
|
||||
<th>Parent node id</th>
|
||||
<td>{{indexer.indexCriteria.ParentNodeId}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
|
||||
<li ng-show="indexer.indexCriteria.StandardFields.length > 0">
|
||||
<a href="" ng-click="toggle(indexer, 'showSystemFields')">System fields</a>
|
||||
<table ng-show="indexer.showSystemFields" class="table table-bordered table-condensed">
|
||||
<caption> </caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Enable sorting</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="field in indexer.indexCriteria.StandardFields">
|
||||
<th>{{field.Name}}</th>
|
||||
<td>{{field.EnableSorting}}</td>
|
||||
<td>{{field.Type}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
|
||||
<li ng-show="indexer.indexCriteria.UserFields.length > 0">
|
||||
<a href="" ng-click="toggle(indexer, 'showUserFields')">User fields</a>
|
||||
<table ng-show="indexer.showUserFields" class="table table-bordered table-condensed">
|
||||
<caption> </caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Enable sorting</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="field in indexer.indexCriteria.UserFields">
|
||||
<th>{{field.Name}}</th>
|
||||
<td>{{field.EnableSorting}}</td>
|
||||
<td>{{field.Type}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="" ng-click="toggle(indexer, 'showProviderProperties')">Provider properties</a>
|
||||
<table ng-show="indexer.showProviderProperties" class="table table-bordered table-condensed">
|
||||
<caption> </caption>
|
||||
<tr ng-repeat="(key, val) in indexer.providerProperties track by $index">
|
||||
<th>{{key}}</th>
|
||||
<td>{{val}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
</ul>
|
||||
|
||||
<h4>Searchers</h4>
|
||||
|
||||
<ul ng-hide="loading">
|
||||
<li class="provider" ng-repeat="searcher in searcherDetails">
|
||||
<a class="btn-link -underline" href="" ng-click="toggle(searcher, 'showProperties')">
|
||||
{{searcher.name}}
|
||||
</a>
|
||||
|
||||
<ul ng-show="searcher.showProperties">
|
||||
|
||||
<li class="search-tools">
|
||||
|
||||
<a href="" ng-click="toggle(searcher, 'showTools')">Search tools</a>
|
||||
|
||||
<div ng-show="searcher.showTools">
|
||||
<a class="hide" href="" ng-click="closeSearch(searcher)" ng-show="searcher.isSearching">Hide search results</a>
|
||||
|
||||
<br />
|
||||
|
||||
<form>
|
||||
|
||||
<div class="row form-search">
|
||||
<div class="span8 input-append">
|
||||
<input type="text" class="search-query" ng-model="searcher.searchText" no-dirty-check />
|
||||
<button type="button" class="btn btn-info" ng-click="search(searcher)" ng-disabled="searcher.isProcessing">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="{{searcher.name}}-textSearch" class="radio inline">
|
||||
<input type="radio" name="searchType" id="{{searcher.name}}-textSearch" value="text" ng-model="searcher.searchType" no-dirty-check />
|
||||
Text Search
|
||||
</label>
|
||||
<label for="{{searcher.name}}-luceneSearch" class="radio inline">
|
||||
<input type="radio" name="searchType" id="{{searcher.name}}-luceneSearch" value="lucene" ng-model="searcher.searchType" no-dirty-check />
|
||||
Lucene Search
|
||||
</label>
|
||||
<div class="umb-healthcheck-group__details-status-actions" ng-if="!indexer.isHealthy">
|
||||
<div class="umb-healthcheck-group__details-status-action">
|
||||
<button type="button" class="umb-era-button -blue"
|
||||
ng-show="!indexer.isProcessing && (!indexer.processingAttempts || indexer.processingAttempts < 100)"
|
||||
ng-click="rebuildIndex(indexer)">
|
||||
Rebuild index
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-healthcheck-group__details-status-actions" ng-show="indexer.isHealthy && indexer.showProperties">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="" ng-click="toggle(indexer, 'showTools')">Index info & tools</a>
|
||||
|
||||
</form>
|
||||
<div ng-show="indexer.showTools && indexer.isLuceneIndex">
|
||||
<div>
|
||||
<br />
|
||||
|
||||
<div class="search-results" ng-show="searcher.isSearching">
|
||||
<div ng-show="!indexer.isProcessing && (!indexer.processingAttempts || indexer.processingAttempts < 100)"
|
||||
class="umb-healthcheck-group__details-status-action">
|
||||
<button type="button" class="umb-era-button -blue" ng-click="rebuildIndex(indexer)">Rebuild index</button>
|
||||
</div>
|
||||
|
||||
<div ng-show="indexer.isProcessing" class="umb-loader-wrapper" ng-show="indexer.isProcessing">
|
||||
<div class="umb-loader"></div>
|
||||
</div>
|
||||
<div ng-show="indexer.processingAttempts >= 100">
|
||||
The process is taking longer than expected, check the umbraco log to see if there have been any errors during this operation
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-bordered table-condensed">
|
||||
<caption> </caption>
|
||||
<tr>
|
||||
<th>Documents in index</th>
|
||||
<td>{{indexer.documentCount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Fields in index</th>
|
||||
<td>{{indexer.fieldCount}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Has deletions?</th>
|
||||
<td>
|
||||
<span>{{indexer.deletionCount > 0}}</span>
|
||||
(<span>{{indexer.deletionCount}}</span>)
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Optimized?</th>
|
||||
<td>
|
||||
<span>{{indexer.isOptimized}}</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<table ng-hide="searcher.isProcessing" class="table table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="score">Score</th>
|
||||
<th class="id">Id</th>
|
||||
<th>Values</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="result in searcher.searchResults">
|
||||
<td>{{result.Score}}</td>
|
||||
<td>{{result.Id}}</td>
|
||||
<td>
|
||||
<span ng-repeat="(key,val) in result.Fields track by $index">
|
||||
<span class=""><em>{{key}}</em>:</span>
|
||||
<span class="text-info">{{val}}</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</table>
|
||||
</div>
|
||||
</li>
|
||||
<li ng-show="indexer.indexCriteria.IncludeNodeTypes.length > 0 || indexer.indexCriteria.ExcludeNodeTypes.length > 0 || indexer.indexCriteria.ParentNodeId">
|
||||
<a href="" ng-click="toggle(indexer, 'showNodeTypes')">Node types</a>
|
||||
<table ng-show="indexer.showNodeTypes" class="table table-bordered table-condensed">
|
||||
<tr ng-show="indexer.indexCriteria.IncludeNodeTypes.length > 0">
|
||||
<th>Include node types</th>
|
||||
<td>{{indexer.indexCriteria.IncludeNodeTypes | json}}</td>
|
||||
</tr>
|
||||
<tr ng-show="indexer.indexCriteria.ExcludeNodeTypes.length > 0">
|
||||
<th>Exclude node types</th>
|
||||
<td>{{indexer.indexCriteria.ExcludeNodeTypes | json}}</td>
|
||||
</tr>
|
||||
<tr ng-show="indexer.indexCriteria.ParentNodeId">
|
||||
<th>Parent node id</th>
|
||||
<td>{{indexer.indexCriteria.ParentNodeId}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
<li ng-show="indexer.indexCriteria.StandardFields.length > 0">
|
||||
<a href="" ng-click="toggle(indexer, 'showSystemFields')">System fields</a>
|
||||
<table ng-show="indexer.showSystemFields" class="table table-bordered table-condensed">
|
||||
<caption> </caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Enable sorting</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="field in indexer.indexCriteria.StandardFields">
|
||||
<th>{{field.Name}}</th>
|
||||
<td>{{field.EnableSorting}}</td>
|
||||
<td>{{field.Type}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
<li ng-show="indexer.indexCriteria.UserFields.length > 0">
|
||||
<a href="" ng-click="toggle(indexer, 'showUserFields')">User fields</a>
|
||||
<table ng-show="indexer.showUserFields" class="table table-bordered table-condensed">
|
||||
<caption> </caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Enable sorting</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="field in indexer.indexCriteria.UserFields">
|
||||
<th>{{field.Name}}</th>
|
||||
<td>{{field.EnableSorting}}</td>
|
||||
<td>{{field.Type}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" ng-click="toggle(indexer, 'showProviderProperties')">Provider properties</a>
|
||||
<table ng-show="indexer.showProviderProperties" class="table table-bordered table-condensed">
|
||||
<caption> </caption>
|
||||
<tr ng-repeat="(key, val) in indexer.providerProperties track by $index">
|
||||
<th>{{key}}</th>
|
||||
<td>{{val}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="" ng-click="toggle(searcher, 'showProviderProperties')">Provider properties</a>
|
||||
<table ng-show="searcher.showProviderProperties" class="table table-bordered table-condensed">
|
||||
<caption> </caption>
|
||||
<tr ng-repeat="(key, val) in searcher.providerProperties track by $index">
|
||||
<th>{{key}}</th>
|
||||
<td>{{val}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
<div ng-show="indexer.isProcessing">
|
||||
<div class="umb-healthcheck-group__details-status-overlay"></div>
|
||||
<umb-load-indicator></umb-load-indicator>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="umb-healthcheck-group__details-group-title">
|
||||
<div class="umb-healthcheck-group__details-group-name">Searchers</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-healthcheck-group__details-checks">
|
||||
<div class="umb-healthcheck-group__details-check">
|
||||
<div class="umb-healthcheck-group__details-check-title">
|
||||
<div class="umb-healthcheck-group__details-check-name">Search indexes</div>
|
||||
<div class="umb-healthcheck-group__details-check-description">Allows you to search the indexes and view the searcher properties</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-healthcheck-group__details-status" ng-repeat="searcher in searcherDetails">
|
||||
|
||||
<div class="umb-healthcheck-group__details-status-icon-container">
|
||||
<i class="umb-healthcheck-status-icon icon-info"></i>
|
||||
</div>
|
||||
|
||||
<div class="umb-healthcheck-group__details-status-content">
|
||||
<div class="umb-healthcheck-group__details-status-text">
|
||||
<a class="btn-link -underline" href="" ng-click="toggle(searcher, 'showProperties')">
|
||||
{{searcher.name}}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="umb-healthcheck-group__details-status-actions" ng-show="searcher.showProperties">
|
||||
<ul>
|
||||
<li class="search-tools">
|
||||
|
||||
<a href="" ng-click="toggle(searcher, 'showTools')">Search tools</a>
|
||||
|
||||
<div ng-show="searcher.showTools">
|
||||
<a class="hide" href="" ng-click="closeSearch(searcher)" ng-show="searcher.isSearching">Hide search results</a>
|
||||
|
||||
<br />
|
||||
|
||||
<form>
|
||||
|
||||
<div class="row form-search">
|
||||
<div class="span8 input-append">
|
||||
<input type="text" class="search-query" ng-model="searcher.searchText" no-dirty-check />
|
||||
<button type="button" class="btn btn-info" ng-click="search(searcher)" ng-disabled="searcher.isProcessing">Search</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label for="{{searcher.name}}-textSearch" class="radio inline">
|
||||
<input type="radio" name="searchType" id="{{searcher.name}}-textSearch" value="text" ng-model="searcher.searchType" no-dirty-check />
|
||||
Text Search
|
||||
</label>
|
||||
<label for="{{searcher.name}}-luceneSearch" class="radio inline">
|
||||
<input type="radio" name="searchType" id="{{searcher.name}}-luceneSearch" value="lucene" ng-model="searcher.searchType" no-dirty-check />
|
||||
Lucene Search
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
<div class="search-results" ng-show="searcher.isSearching">
|
||||
|
||||
<div ng-show="indexer.isProcessing" class="umb-loader-wrapper" ng-show="indexer.isProcessing">
|
||||
<div class="umb-loader"></div>
|
||||
</div>
|
||||
|
||||
<table ng-hide="searcher.isProcessing" class="table table-bordered table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="score">Score</th>
|
||||
<th class="id">Id</th>
|
||||
<th>Values</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="result in searcher.searchResults">
|
||||
<td>{{result.Score}}</td>
|
||||
<td>{{result.Id}}</td>
|
||||
<td>
|
||||
<span ng-repeat="(key,val) in result.Fields track by $index">
|
||||
<span class=""><em>{{key}}</em>:</span>
|
||||
<span class="text-info">{{val}}</span>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" ng-click="toggle(searcher, 'showProviderProperties')">Provider properties</a>
|
||||
<table ng-show="searcher.showProviderProperties" class="table table-bordered table-condensed">
|
||||
<caption> </caption>
|
||||
<tr ng-repeat="(key, val) in searcher.providerProperties track by $index">
|
||||
<th>{{key}}</th>
|
||||
<td>{{val}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</ul>
|
||||
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -16,9 +16,16 @@
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<p style="font-size: 16px; line-height: 1.5; color: #4c4c4c;">Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it!</p>
|
||||
<p style="font-size: 16px; line-height: 1.5; color: #4c4c4c; margin-bottom: 20px;">Create forms using an intuitive drag and drop interface. From simple contact forms that sends e-mails to advanced questionaires that integrate with CRM systems. Your clients will love it!</p>
|
||||
|
||||
<umb-button
|
||||
class="forms-install-button"
|
||||
type="button"
|
||||
button-style="success"
|
||||
label="Install"
|
||||
action="installForms()">
|
||||
</umb-button>
|
||||
|
||||
<button class="btn publish btn-install" ng-click="installForms()">Install</button>
|
||||
</div>
|
||||
|
||||
<div class="step-two" ng-if="state"> <!-- STEP two -->
|
||||
|
||||
@@ -7,8 +7,11 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController",
|
||||
var node = dialogOptions.currentNode;
|
||||
|
||||
function nodeSelectHandler(ev, args) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
|
||||
if(args && args.event) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
}
|
||||
|
||||
eventsService.emit("editors.media.moveController.select", args);
|
||||
|
||||
@@ -21,8 +24,15 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController",
|
||||
$scope.target.selected = true;
|
||||
}
|
||||
|
||||
$scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
|
||||
function nodeExpandedHandler(ev, args) {
|
||||
// open mini list view for list views
|
||||
if (args.node.metaData.isContainer) {
|
||||
openMiniListView(args.node);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
|
||||
$scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
|
||||
|
||||
$scope.move = function () {
|
||||
mediaResource.move({ parentId: $scope.target.id, id: node.id })
|
||||
@@ -55,5 +65,21 @@ angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController",
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
|
||||
});
|
||||
|
||||
// Mini list view
|
||||
$scope.selectListViewNode = function (node) {
|
||||
node.selected = node.selected === true ? false : true;
|
||||
nodeSelectHandler({}, { node: node });
|
||||
};
|
||||
|
||||
$scope.closeMiniListView = function () {
|
||||
$scope.miniListView = undefined;
|
||||
};
|
||||
|
||||
function openMiniListView(node) {
|
||||
$scope.miniListView = node;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,33 +1,49 @@
|
||||
<div ng-controller="Umbraco.Editors.Media.MoveController">
|
||||
<div class="umb-dialog-body">
|
||||
<div class="umb-dialog-body" ng-cloak>
|
||||
<div class="umb-pane">
|
||||
|
||||
<div ng-show="error">
|
||||
<div class="alert alert-error">
|
||||
<div><strong>{{error.errorMsg}}</strong></div>
|
||||
<div>{{error.data.message}}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="success">
|
||||
<div class="alert alert-success">
|
||||
<strong>{{currentNode.name}}</strong> was moved underneath <strong>{{target.name}}</strong>
|
||||
</div>
|
||||
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
|
||||
</div>
|
||||
|
||||
<p class="abstract" ng-hide="success">
|
||||
<localize key="actions_chooseWhereToMove">Choose where to move</localize>
|
||||
<strong>{{currentNode.name}}</strong>
|
||||
<localize key="actions_toInTheTreeStructureBelow">to in the tree structure below</localize>
|
||||
</p>
|
||||
|
||||
<div class="alert alert-error" ng-show="error" ng-cloak>
|
||||
<h4>{{error.errorMsg}}</h4>
|
||||
<p>{{error.data.Message}}</p>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success" ng-show="success" ng-cloak>
|
||||
<p><strong>{{currentNode.name}}</strong> was moved underneath
|
||||
<strong>{{target.name}}</strong></p>
|
||||
|
||||
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
|
||||
</div>
|
||||
|
||||
<div ng-hide="success">
|
||||
<umb-tree section="media"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
|
||||
<div ng-hide="miniListView">
|
||||
<umb-tree
|
||||
section="media"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablelistviewexpand="true"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
|
||||
<umb-mini-list-view
|
||||
ng-if="miniListView"
|
||||
node="miniListView"
|
||||
entity-type="Media"
|
||||
on-select="selectListViewNode(node)"
|
||||
on-close="closeMiniListView()">
|
||||
</umb-mini-list-view>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,49 +1,39 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function PartialViewMacrosCreateController($scope, codefileResource, $location, navigationService, formHelper, localizationService, appState) {
|
||||
function PartialViewMacrosCreateController($scope, codefileResource, macroResource, $location, navigationService, formHelper, localizationService, appState) {
|
||||
|
||||
var vm = this;
|
||||
var node = $scope.dialogOptions.currentNode;
|
||||
var localizeCreateFolder = localizationService.localize("defaultdialog_createFolder");
|
||||
|
||||
vm.snippets = [];
|
||||
vm.showSnippets = false;
|
||||
vm.creatingFolder = false;
|
||||
vm.createFolderError = "";
|
||||
vm.folderName = "";
|
||||
vm.fileName = "";
|
||||
vm.showSnippets = false;
|
||||
vm.creatingFolder = false;
|
||||
|
||||
vm.createPartialViewMacro = createPartialViewMacro;
|
||||
vm.showCreateFolder = showCreateFolder;
|
||||
vm.createFolder = createFolder;
|
||||
vm.createFile = createFile;
|
||||
vm.createFileWithoutMacro = createFileWithoutMacro;
|
||||
vm.showCreateFromSnippet = showCreateFromSnippet;
|
||||
vm.createFileFromSnippet = createFileFromSnippet;
|
||||
|
||||
function onInit() {
|
||||
codefileResource.getSnippets('partialViewMacros')
|
||||
.then(function(snippets) {
|
||||
.then(function (snippets) {
|
||||
vm.snippets = snippets;
|
||||
});
|
||||
}
|
||||
|
||||
function createPartialViewMacro(selectedSnippet) {
|
||||
|
||||
var snippet = null;
|
||||
|
||||
if(selectedSnippet && selectedSnippet.fileName) {
|
||||
snippet = selectedSnippet.fileName;
|
||||
}
|
||||
|
||||
$location.path("/developer/partialviewmacros/edit/" + node.id).search("create", "true").search("snippet", snippet);
|
||||
navigationService.hideMenu();
|
||||
|
||||
}
|
||||
|
||||
function showCreateFolder() {
|
||||
vm.creatingFolder = true;
|
||||
}
|
||||
|
||||
function createFolder(form) {
|
||||
if (formHelper.submitForm({scope: $scope, formCtrl: form, statusMessage: localizeCreateFolder})) {
|
||||
if (formHelper.submitForm({ scope: $scope, formCtrl: form, statusMessage: localizeCreateFolder })) {
|
||||
|
||||
codefileResource.createContainer("partialViewMacros", node.id, vm.folderName).then(function (saved) {
|
||||
|
||||
@@ -62,7 +52,7 @@
|
||||
|
||||
var section = appState.getSectionState("currentSection");
|
||||
|
||||
}, function(err) {
|
||||
}, function (err) {
|
||||
|
||||
vm.createFolderError = err;
|
||||
|
||||
@@ -75,11 +65,26 @@
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createFile() {
|
||||
$location.path("/developer/partialviewmacros/edit/" + node.id).search("create", "true");
|
||||
navigationService.hideMenu();
|
||||
}
|
||||
|
||||
function createFileWithoutMacro() {
|
||||
$location.path("/developer/partialviewmacros/edit/" + node.id).search("create", "true").search("nomacro", "true");
|
||||
navigationService.hideMenu();
|
||||
}
|
||||
|
||||
function createFileFromSnippet(snippet) {
|
||||
$location.path("/developer/partialviewmacros/edit/" + node.id).search("create", "true").search("snippet", snippet.fileName);
|
||||
navigationService.hideMenu();
|
||||
}
|
||||
|
||||
function showCreateFromSnippet() {
|
||||
vm.showSnippets = true;
|
||||
}
|
||||
|
||||
|
||||
onInit();
|
||||
|
||||
}
|
||||
|
||||
@@ -3,22 +3,29 @@
|
||||
<div class="umbracoDialog umb-dialog-body with-footer">
|
||||
|
||||
<div class="umb-pane" ng-if="!vm.creatingFolder">
|
||||
|
||||
<h5><localize key="create_createUnder">Create an item under</localize> {{currentNode.name}}</h5>
|
||||
|
||||
<!-- Main options -->
|
||||
<div ng-if="!vm.showSnippets">
|
||||
|
||||
<h5><localize key="create_createUnder">Create an item under</localize> {{currentNode.name}}</h5>
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li>
|
||||
<a href="" ng-click="vm.createPartialViewMacro()" umb-auto-focus>
|
||||
<a href="" ng-click="vm.createFile()" umb-auto-focus>
|
||||
<i class="large icon-article"></i>
|
||||
<span class="menu-label"><localize key="create_newEmptyPartialViewMacro">New empty partial view macro</localize></span>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacro">New partial view macro</localize></span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" ng-click="vm.createFileWithoutMacro()">
|
||||
<i class="large icon-article"></i>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacroNoMacro">New partial view macro (without macro)</localize></span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" ng-click="vm.showCreateFromSnippet()">
|
||||
<i class="large icon-article"></i>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacroFromSnippet">New partial view macro from snippet</localize></span>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacroFromSnippet">>New partial view macro from snippet</localize></span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
@@ -32,9 +39,12 @@
|
||||
|
||||
<!-- Snippets list -->
|
||||
<div ng-if="vm.showSnippets">
|
||||
|
||||
<h5>Select snippet</h5>
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li ng-repeat="snippet in vm.snippets">
|
||||
<a href="" ng-click="vm.createPartialViewMacro(snippet)" style="padding-top: 6px; padding-bottom: 6px;">
|
||||
<a href="" ng-click="vm.createFileFromSnippet(snippet)" style="padding-top: 6px; padding-bottom: 6px;">
|
||||
<i class="icon-article" style="font-size: 20px;"></i>
|
||||
<span class="menu-label" style="margin-left: 0; padding-left: 5px;">{{ snippet.name }}</span>
|
||||
</a>
|
||||
@@ -49,14 +59,14 @@
|
||||
<form novalidate name="createFolderForm"
|
||||
ng-submit="vm.createFolder(createFolderForm)"
|
||||
val-form-manager>
|
||||
|
||||
|
||||
<div ng-show="vm.createFolderError">
|
||||
<h5 class="text-error">{{vm.createFolderError.errorMsg}}</h5>
|
||||
<p class="text-error">{{vm.createFolderError.data.message}}</p>
|
||||
</div>
|
||||
|
||||
<umb-control-group label="Enter a folder name" hide-label="false">
|
||||
<input type="text" name="folderName" ng-model="vm.folderName" class="umb-textstring textstring input-block-level" umb-auto-focus required />
|
||||
<input type="text" name="folderName" ng-model="vm.folderName" class="umb-textstring textstring input-block-level" umb-auto-focus required no-dirty-check />
|
||||
</umb-control-group>
|
||||
|
||||
<button type="submit" class="btn btn-primary"><localize key="general_create">Create</localize></button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function partialViewMacrosEditController($scope, $routeParams, codefileResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper) {
|
||||
function partialViewMacrosEditController($scope, $routeParams, codefileResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper, macroResource) {
|
||||
|
||||
var vm = this;
|
||||
var localizeSaving = localizationService.localize("general_saving");
|
||||
@@ -26,7 +26,7 @@
|
||||
/* Functions bound to view model */
|
||||
|
||||
function save() {
|
||||
|
||||
|
||||
vm.page.saveButtonState = "busy";
|
||||
vm.partialViewMacro.content = vm.editor.getValue();
|
||||
|
||||
@@ -41,36 +41,26 @@
|
||||
redirectOnFailure: false,
|
||||
rebindCallback: function (orignal, saved) {}
|
||||
}).then(function (saved) {
|
||||
|
||||
localizationService.localize("speechBubbles_partialViewSavedHeader").then(function (headerValue) {
|
||||
localizationService.localize("speechBubbles_partialViewSavedText").then(function(msgValue) {
|
||||
notificationsService.success(headerValue, msgValue);
|
||||
// create macro if needed
|
||||
if($routeParams.create && $routeParams.nomacro !== "true") {
|
||||
macroResource.createPartialViewMacroWithFile(saved.virtualPath, saved.name).then(function(created) {
|
||||
completeSave(saved);
|
||||
}, function(err) {
|
||||
//show any notifications
|
||||
if (angular.isArray(err.data.notifications)) {
|
||||
for (var i = 0; i < err.data.notifications.length; i++) {
|
||||
notificationsService.showNotification(err.data.notifications[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//check if the name changed, if so we need to redirect
|
||||
if (vm.partialViewMacro.id !== saved.id) {
|
||||
contentEditingHelper.redirectToRenamedContent(saved.id);
|
||||
} else {
|
||||
completeSave(saved);
|
||||
}
|
||||
else {
|
||||
vm.page.saveButtonState = "success";
|
||||
vm.partialViewMacro = saved;
|
||||
|
||||
//sync state
|
||||
editorState.set(vm.partialViewMacro);
|
||||
|
||||
// normal tree sync
|
||||
navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) {
|
||||
vm.page.menu.currentNode = syncArgs.node;
|
||||
});
|
||||
|
||||
// clear $dirty state on form
|
||||
setFormState("pristine");
|
||||
}
|
||||
}, function (err) {
|
||||
|
||||
vm.page.saveButtonState = "error";
|
||||
|
||||
|
||||
localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) {
|
||||
localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) {
|
||||
notificationsService.error(headerValue, msgValue);
|
||||
@@ -81,6 +71,36 @@
|
||||
|
||||
}
|
||||
|
||||
function completeSave(saved) {
|
||||
|
||||
localizationService.localize("speechBubbles_partialViewSavedHeader").then(function (headerValue) {
|
||||
localizationService.localize("speechBubbles_partialViewSavedText").then(function (msgValue) {
|
||||
notificationsService.success(headerValue, msgValue);
|
||||
});
|
||||
});
|
||||
|
||||
//check if the name changed, if so we need to redirect
|
||||
if (vm.partialViewMacro.id !== saved.id) {
|
||||
contentEditingHelper.redirectToRenamedContent(saved.id);
|
||||
}
|
||||
else {
|
||||
vm.page.saveButtonState = "success";
|
||||
vm.partialViewMacro = saved;
|
||||
|
||||
//sync state
|
||||
editorState.set(vm.partialViewMacro);
|
||||
|
||||
// normal tree sync
|
||||
navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) {
|
||||
vm.page.menu.currentNode = syncArgs.node;
|
||||
});
|
||||
|
||||
// clear $dirty state on form
|
||||
setFormState("pristine");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function openInsertOverlay() {
|
||||
|
||||
vm.insertOverlay = {
|
||||
@@ -104,7 +124,7 @@
|
||||
var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name);
|
||||
insert(code);
|
||||
break;
|
||||
|
||||
|
||||
case "umbracoField":
|
||||
insert(model.insert.umbracoField);
|
||||
break;
|
||||
@@ -169,7 +189,7 @@
|
||||
vm.pageFieldOverlay.show = false;
|
||||
vm.pageFieldOverlay = null;
|
||||
// focus editor
|
||||
vm.editor.focus();
|
||||
vm.editor.focus();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -212,7 +232,7 @@
|
||||
|
||||
var code = templateHelper.getQuerySnippet(model.result.queryExpression);
|
||||
insert(code);
|
||||
|
||||
|
||||
vm.queryBuilderOverlay.show = false;
|
||||
vm.queryBuilderOverlay = null;
|
||||
},
|
||||
@@ -222,7 +242,7 @@
|
||||
vm.queryBuilderOverlay.show = false;
|
||||
vm.queryBuilderOverlay = null;
|
||||
// focus editor
|
||||
vm.editor.focus();
|
||||
vm.editor.focus();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -232,9 +252,9 @@
|
||||
function init() {
|
||||
//we need to load this somewhere, for now its here.
|
||||
assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css");
|
||||
|
||||
|
||||
if ($routeParams.create) {
|
||||
|
||||
|
||||
var snippet = "Empty";
|
||||
|
||||
if($routeParams.snippet) {
|
||||
@@ -242,6 +262,9 @@
|
||||
}
|
||||
|
||||
codefileResource.getScaffold("partialViewMacros", $routeParams.id, snippet).then(function (partialViewMacro) {
|
||||
if ($routeParams.name) {
|
||||
partialViewMacro.name = $routeParams.name;
|
||||
}
|
||||
ready(partialViewMacro, false);
|
||||
});
|
||||
|
||||
@@ -276,7 +299,7 @@
|
||||
},
|
||||
onLoad: function(_editor) {
|
||||
vm.editor = _editor;
|
||||
|
||||
|
||||
// initial cursor placement
|
||||
// Keep cursor in name field if we are create a new template
|
||||
// else set the cursor at the bottom of the code editor
|
||||
@@ -291,6 +314,8 @@
|
||||
//change on blur, focus
|
||||
vm.editor.on("blur", persistCurrentLocation);
|
||||
vm.editor.on("focus", persistCurrentLocation);
|
||||
vm.editor.on("change", changeAceEditor);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,8 +334,12 @@
|
||||
vm.currentPosition = vm.editor.getCursorPosition();
|
||||
}
|
||||
|
||||
function changeAceEditor() {
|
||||
setFormState("dirty");
|
||||
}
|
||||
|
||||
function setFormState(state) {
|
||||
|
||||
|
||||
// get the current form
|
||||
var currentForm = angularHelper.getCurrentForm($scope);
|
||||
|
||||
@@ -322,7 +351,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
init();
|
||||
|
||||
}
|
||||
|
||||
@@ -367,6 +367,8 @@
|
||||
//change on blur, focus
|
||||
vm.editor.on("blur", persistCurrentLocation);
|
||||
vm.editor.on("focus", persistCurrentLocation);
|
||||
vm.editor.on("change", changeAceEditor);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,6 +387,10 @@
|
||||
vm.currentPosition = vm.editor.getCursorPosition();
|
||||
}
|
||||
|
||||
function changeAceEditor() {
|
||||
setFormState("dirty");
|
||||
}
|
||||
|
||||
function setFormState(state) {
|
||||
|
||||
// get the current form
|
||||
|
||||
+2
-2
@@ -30,10 +30,10 @@
|
||||
<div class="control-group">
|
||||
<label>Size</label>
|
||||
<input name="newItem.width" type="number" placeholder="Width"
|
||||
ng-model="newItem.width" class="umb-editor-tiny" val-highlight="{{hasError}}" />
|
||||
ng-model="newItem.width" class="umb-editor-small" val-highlight="{{hasError}}" />
|
||||
×
|
||||
<input name="newItem.height" type="number" placeholder="Height"
|
||||
ng-model="newItem.height" class="umb-editor-tiny" val-highlight="{{hasError}}" />
|
||||
ng-model="newItem.height" class="umb-editor-small" val-highlight="{{hasError}}" />
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user