Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.1.2-Usky

This commit is contained in:
antoine
2014-05-21 13:54:25 +02:00
151 changed files with 2824 additions and 1647 deletions
+10 -2
View File
@@ -1,5 +1,9 @@
@ECHO OFF
SET release=7.1.2
IF NOT EXIST UmbracoVersion.txt (
ECHO UmbracoVersion.txt missing!
GOTO :showerror
)
SET /p release=<UmbracoVersion.txt
SET comment=
SET version=%release%
@@ -15,6 +19,11 @@ SET nuGetFolder=%CD%\..\src\packages\
ECHO Removing the belle build folder to make sure everything is clean as a whistle
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
ECHO Removing existing built files to make sure everything is clean as a whistle
DEL /F /Q UmbracoCms.*.zip
DEL /F /Q UmbracoCms.*.nupkg
DEL /F /Q webpihash.txt
ECHO Performing MSBuild and producing Umbraco binaries zip files
%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment%
@@ -37,7 +46,6 @@ ren .\_BuildOutput\WebApp\Xslt\Web.config Web.config.transform
ECHO Packing the NuGet release files
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.nuspec -Version %version%
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.Core.Symbols.nuspec -Symbols -Version %version%
..\src\.nuget\NuGet.exe Pack NuSpecs\UmbracoCms.nuspec -Version %version%
IF ERRORLEVEL 1 GOTO :showerror
+2 -2
View File
@@ -71,7 +71,7 @@
</PropertyGroup>
<PropertyGroup>
<BuildConfiguration>Debug</BuildConfiguration>
<BuildConfiguration>Release</BuildConfiguration>
<BuildFolder>_BuildOutput\</BuildFolder>
<BuildZipFileName>UmbracoCms$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileName>
<BuildZipFileNameBin>UmbracoCms.AllBinaries$(DECIMAL_BUILD_NUMBER).zip</BuildZipFileNameBin>
@@ -325,7 +325,7 @@
Regex="CurrentComment { get { return &quot;([a-zA-Z]+)?&quot;"
ReplacementText="CurrentComment { get { return &quot;$(BUILD_COMMENT)&quot;"/>
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.targets"
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
Namespaces="&lt;Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' /&gt;"
Query="//x:UmbracoVersion"
Value="$(NewVersion)" />
+4 -1
View File
@@ -1,4 +1,5 @@
@ECHO OFF
SET release=%1
ECHO Installing Npm NuGet Package
SET nuGetFolder=%CD%\..\src\packages\
@@ -15,6 +16,8 @@ SET oldPath=%PATH%
path=%npmPath%;%nodePath%;%PATH%
ECHO %path%
SET buildFolder=%CD%
ECHO Change directory to %CD%\..\src\Umbraco.Web.UI.Client\
@@ -24,7 +27,7 @@ ECHO Do npm install and the grunt build of Belle
call npm install
call npm install -g grunt-cli
call npm install -g bower
call grunt build --buildversion=7.1.2
call grunt build --buildversion=%release%
ECHO Reset path to what it was before
path=%oldPath%
+3 -2
View File
@@ -30,8 +30,8 @@
<dependency id="ClientDependency-Mvc" version="[1.7.0.4, 2.0.0)" />
<dependency id="AutoMapper" version="[3.0.0, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[6.0.2, 7.0.0)" />
<dependency id="ImageProcessor" version="[1.8.8, 2.0.0)" />
<dependency id="ImageProcessor.Web" version="[3.2.2, 4.0.0)" />
<dependency id="ImageProcessor" version="[1.9.0, 2.0.0)" />
<dependency id="ImageProcessor.Web" version="[3.2.3, 4.0.0)" />
</dependencies>
</metadata>
<files>
@@ -67,5 +67,6 @@
<file src="..\_BuildOutput\WebApp\bin\umbraco.XmlSerializers.dll" target="lib\umbraco.XmlSerializers.dll" />
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\UmbracoExamine.dll" />
<file src="..\_BuildOutput\WebApp\bin\UrlRewritingNet.UrlRewriter.dll" target="lib\UrlRewritingNet.UrlRewriter.dll" />
<file src="tools\install.core.ps1" target="tools\install.ps1" />
</files>
</package>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<metadata minClientVersion="2.8">
<id>UmbracoCms</id>
<version>7.0.0</version>
<title>Umbraco Cms</title>
+2 -3
View File
@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
AddUmbracoFilesToOutput;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
AddUmbracoFilesToOutput;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
+4 -4
View File
@@ -1,11 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<UmbracoVersion>7.1.2</UmbracoVersion>
</PropertyGroup>
<Target Name="CopyUmbracoFilesToWebRoot" BeforeTargets="AfterBuild">
<PropertyGroup>
<UmbracoFilesFolder>..\packages\UmbracoCms.$(UmbracoVersion)\UmbracoFiles\</UmbracoFilesFolder>
<UmbracoFilesFolder>$(MSBuildThisFileDirectory)..\UmbracoFiles\</UmbracoFilesFolder>
</PropertyGroup>
<ItemGroup>
<UmbracoFiles Include="$(UmbracoFilesFolder)**\*" />
@@ -33,6 +30,9 @@
<CustomFilesToInclude Include=".\Config\Splashes\**\*">
<Dir>Config\Splashes</Dir>
</CustomFilesToInclude>
<CustomFilesToInclude Include=".\data\**\*">
<Dir>data</Dir>
</CustomFilesToInclude>
<CustomFilesToInclude Include=".\umbraco\**\*">
<Dir>umbraco</Dir>
</CustomFilesToInclude>
+24
View File
@@ -0,0 +1,24 @@
param($rootPath, $toolsPath, $package, $project)
if ($project) {
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
$backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime"
$copyLogsPath = Join-Path $backupPath "CopyLogs"
$projectDestinationPath = Split-Path $project.FullName -Parent
# Create backup folder and logs folder if it doesn't exist yet
New-Item -ItemType Directory -Force -Path $backupPath
New-Item -ItemType Directory -Force -Path $copyLogsPath
# After backing up, remove all dlls from bin folder in case dll files are included in the VS project
# See: http://issues.umbraco.org/issue/U4-4930
$umbracoBinFolder = Join-Path $projectDestinationPath "bin"
if(Test-Path $umbracoBinFolder) {
$umbracoBinBackupPath = Join-Path $backupPath "bin"
New-Item -ItemType Directory -Force -Path $umbracoBinBackupPath
robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log
Remove-Item $umbracoBinFolder\*.dll -Force -Confirm:$false
}
}
+16 -6
View File
@@ -3,9 +3,11 @@ param($rootPath, $toolsPath, $package, $project)
if ($project) {
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
$backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime"
$copyLogsPath = Join-Path $backupPath "CopyLogs"
# Create backup folder if it doesn't exist yet
# Create backup folder and logs folder if it doesn't exist yet
New-Item -ItemType Directory -Force -Path $backupPath
New-Item -ItemType Directory -Force -Path $copyLogsPath
# Create a backup of original web.config
$projectDestinationPath = Split-Path $project.FullName -Parent
@@ -19,18 +21,26 @@ if ($project) {
# Copy umbraco and umbraco_files from package to project folder
# This is only done when these folders already exist because we
# only want to do this for upgrades
$umbracoFolder = Join-Path $projectDestinationPath "Umbraco\"
$umbracoFolder = Join-Path $projectDestinationPath "Umbraco"
if(Test-Path $umbracoFolder) {
$umbracoFolderSource = Join-Path $rootPath "UmbracoFiles\Umbraco"
Copy-Item $umbracoFolder $backupPath -Force
robocopy $umbracoFolderSource $umbracoFolder /e /xf UI.xml
$umbracoBackupPath = Join-Path $backupPath "Umbraco"
New-Item -ItemType Directory -Force -Path $umbracoBackupPath
robocopy $umbracoFolder $umbracoBackupPath /e /LOG:$copyLogsPath\UmbracoBackup.log
robocopy $umbracoFolderSource $umbracoFolder /is /it /e /xf UI.xml /LOG:$copyLogsPath\UmbracoCopy.log
}
$umbracoClientFolder = Join-Path $projectDestinationPath "Umbraco_Client"
if(Test-Path $umbracoClientFolder) {
$umbracoClientFolderSource = Join-Path $rootPath "UmbracoFiles\Umbraco_Client"
Copy-Item $umbracoClientFolder $backupPath -Force
robocopy $umbracoFolderSource $umbracoClientFolder /e
$umbracoClientBackupPath = Join-Path $backupPath "Umbraco_Client"
New-Item -ItemType Directory -Force -Path $umbracoClientBackupPath
robocopy $umbracoClientFolder $umbracoClientBackupPath /e /LOG:$copyLogsPath\UmbracoClientBackup.log
robocopy $umbracoClientFolderSource $umbracoClientFolder /is /it /e /LOG:$copyLogsPath\UmbracoClientCopy.log
}
# Open readme.txt file
+1
View File
@@ -0,0 +1 @@
7.1.3
Binary file not shown.
@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.1.2");
private static readonly Version Version = new Version("7.1.3");
/// <summary>
/// Gets the current version of Umbraco.
+6 -36
View File
@@ -15,43 +15,13 @@ namespace Umbraco.Core
public static IEnumerable<IEnumerable<T>> InGroupsOf<T>(this IEnumerable<T> source, int groupSize)
{
if (source == null)
throw new NullReferenceException("source");
throw new ArgumentNullException("source");
if (groupSize <= 0)
throw new ArgumentException("Must be greater than zero.", "groupSize");
// enumerate the source only once!
return new InGroupsEnumerator<T>(source, groupSize).Groups();
}
// this class makes sure that the source is enumerated only ONCE
// which means that when it is enumerated, the actual groups content
// has to be evaluated at the same time, and stored in an array.
private class InGroupsEnumerator<T>
{
private readonly IEnumerator<T> _source;
private readonly int _count;
private bool _mightHaveNext;
public InGroupsEnumerator(IEnumerable<T> source, int count)
{
_source = source.GetEnumerator();
_count = count;
_mightHaveNext = true;
}
public IEnumerable<IEnumerable<T>> Groups()
{
while (_mightHaveNext && _source.MoveNext())
yield return Group().ToArray(); // see note above
}
private IEnumerable<T> Group()
{
var c = 0;
do
{
yield return _source.Current;
} while (++c < _count && _source.MoveNext());
_mightHaveNext = c == _count;
}
return source
.Select((x, i) => Tuple.Create(i / groupSize, x))
.GroupBy(t => t.Item1, t => t.Item2);
}
/// <summary>The distinct by.</summary>
+49 -8
View File
@@ -9,6 +9,8 @@ using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
@@ -539,7 +541,7 @@ namespace Umbraco.Core.Models
return ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id);
}
#region Tag methods
///// <summary>
@@ -567,6 +569,21 @@ namespace Umbraco.Core.Models
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
/// <returns></returns>
public static void SetTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
{
content.SetTags(TagCacheStorageType.Csv, propertyTypeAlias, tags, replaceTags, tagGroup);
}
/// <summary>
/// Sets tags for the property - will add tags to the tags table and set the property value to be the comma delimited value of the tags.
/// </summary>
/// <param name="content">The content item to assign the tags to</param>
/// <param name="storageType">The tag storage type in cache (default is csv)</param>
/// <param name="propertyTypeAlias">The property alias to assign the tags to</param>
/// <param name="tags">The tags to assign</param>
/// <param name="replaceTags">True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones</param>
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
/// <returns></returns>
public static void SetTags(this IContentBase content, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
{
var property = content.Properties[propertyTypeAlias];
if (property == null)
@@ -583,15 +600,39 @@ namespace Umbraco.Core.Models
//ensure the property value is set to the same thing
if (replaceTags)
{
property.Value = string.Join(",", trimmedTags);
switch (storageType)
{
case TagCacheStorageType.Csv:
property.Value = string.Join(",", trimmedTags);
break;
case TagCacheStorageType.Json:
//json array
property.Value = JsonConvert.SerializeObject(trimmedTags);
break;
}
}
else
{
var currTags = property.Value.ToString().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)
switch (storageType)
{
case TagCacheStorageType.Csv:
var currTags = property.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim());
property.Value = string.Join(",", trimmedTags.Union(currTags));
property.Value = string.Join(",", trimmedTags.Union(currTags));
break;
case TagCacheStorageType.Json:
var currJson = JsonConvert.DeserializeObject<JArray>(property.Value.ToString());
//need to append the new ones
foreach (var tag in trimmedTags)
{
currJson.Add(tag);
}
//json array
property.Value = JsonConvert.SerializeObject(currJson);
break;
}
}
}
/// <summary>
@@ -664,7 +705,7 @@ namespace Umbraco.Core.Models
{
return ApplicationContext.Current.Services.PackagingService.Export(media, true, raiseEvents: false);
}
/// <summary>
/// Creates the xml representation for the <see cref="IContent"/> object
/// </summary>
@@ -687,10 +728,10 @@ namespace Umbraco.Core.Models
{
return ((PackagingService)(ApplicationContext.Current.Services.PackagingService)).Export(member);
}
#endregion
}
}
-20
View File
@@ -133,26 +133,6 @@ namespace Umbraco.Core.Models
Key = Guid.NewGuid();
}
/// <summary>
/// Method to call when Entity is being updated
/// </summary>
/// <remarks>Modified Date is set and a new Version guid is set</remarks>
internal override void UpdatingEntity()
{
base.UpdatingEntity();
}
public override object DeepClone()
{
var clone = (ContentType)base.DeepClone();
var propertyGroups = PropertyGroups.Select(x => (PropertyGroup)x.DeepClone()).ToList();
clone.PropertyGroups = new PropertyGroupCollection(propertyGroups);
//set the property types that are not part of a group
clone.PropertyTypes = PropertyTypeCollection
.Where(x => x.PropertyGroupId == null)
.Select(x => (PropertyType)x.DeepClone()).ToList();
return clone;
}
/// <summary>
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
+31 -6
View File
@@ -331,11 +331,6 @@ namespace Umbraco.Core.Models
get { return _additionalData; }
}
/// <summary>
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
/// </summary>
public IDictionary<string, object> AdditionalData { get; private set; }
/// <summary>
/// Gets or sets a list of integer Ids for allowed ContentTypes
/// </summary>
@@ -356,7 +351,9 @@ namespace Umbraco.Core.Models
/// <summary>
/// List of PropertyGroups available on this ContentType
/// </summary>
/// <remarks>A PropertyGroup corresponds to a Tab in the UI</remarks>
/// <remarks>
/// A PropertyGroup corresponds to a Tab in the UI
/// </remarks>
[DataMember]
public virtual PropertyGroupCollection PropertyGroups
{
@@ -372,7 +369,13 @@ namespace Umbraco.Core.Models
/// List of PropertyTypes available on this ContentType.
/// This list aggregates PropertyTypes across the PropertyGroups.
/// </summary>
/// <remarks>
/// Marked as DoNotClone because the result of this property is not the natural result of the data, it is
/// a union of data so when auto-cloning if the setter is used it will be setting the unnatural result of the
/// data. We manually clone this instead.
/// </remarks>
[IgnoreDataMember]
[DoNotClone]
public virtual IEnumerable<PropertyType> PropertyTypes
{
get
@@ -388,6 +391,14 @@ namespace Umbraco.Core.Models
}
/// <summary>
/// Returns the property type collection containing types that are non-groups - used for tests
/// </summary>
internal IEnumerable<PropertyType> NonGroupedPropertyTypes
{
get { return _propertyTypes; }
}
/// <summary>
/// A boolean flag indicating if a property type has been removed from this instance.
/// </summary>
/// <remarks>
@@ -584,5 +595,19 @@ namespace Umbraco.Core.Models
propertyType.ResetDirtyProperties();
}
}
public override object DeepClone()
{
var clone = (ContentTypeBase)base.DeepClone();
//need to manually wire up the event handlers for the property type collections - we've ensured
// its ignored from the auto-clone process because its return values are unions, not raw and
// we end up with duplicates, see: http://issues.umbraco.org/issue/U4-4842
clone._propertyTypes = (PropertyTypeCollection)_propertyTypes.DeepClone();
clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged;
return clone;
}
}
}
+23 -1
View File
@@ -2,9 +2,29 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Umbraco.Core.Models
{
/// <summary>
/// Used to attribute properties that have a setter and are a reference type
/// that should be ignored for cloning when using the DeepCloneHelper
/// </summary>
/// <remarks>
///
/// This attribute must be used:
/// * when the property is backed by a field but the result of the property is the un-natural data stored in the field
///
/// This attribute should not be used:
/// * when the property is virtual
/// * when the setter performs additional required logic other than just setting the underlying field
///
/// </remarks>
internal class DoNotCloneAttribute : Attribute
{
}
public static class DeepCloneHelper
{
/// <summary>
@@ -25,8 +45,10 @@ namespace Umbraco.Core.Models
var refProperties = inputType.GetProperties()
.Where(x =>
//is not attributed with the ignore clone attribute
x.GetCustomAttribute<DoNotCloneAttribute>() == null
//reference type but not string
x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
//settable
&& x.CanWrite
//non-indexed
@@ -240,52 +240,6 @@ namespace Umbraco.Core.Models.EntityBase
DeepCloneHelper.DeepCloneRefProperties(this, clone);
clone.ResetDirtyProperties(false);
return clone;
//Using data contract serializer - has issues
//var s = Serialize(this);
//var d = Deserialize(s, this.GetType());
//return d;
//Using binary serializer - has issues
//using (var memoryStream = new MemoryStream(10))
//{
//IFormatter formatter = new BinaryFormatter();
//formatter.Serialize(memoryStream, this);
//memoryStream.Seek(0, SeekOrigin.Begin);
//return formatter.Deserialize(memoryStream);
//}
}
// serialize/deserialize with data contracts:
//public static string Serialize(object obj)
//{
// using (var memoryStream = new MemoryStream())
// using (var reader = new StreamReader(memoryStream))
// {
// var serializer = new DataContractSerializer(obj.GetType());
// serializer.WriteObject(memoryStream, obj);
// memoryStream.Position = 0;
// return reader.ReadToEnd();
// }
//}
//public static object Deserialize(string xml, Type toType)
//{
// using (Stream stream = new MemoryStream())
// {
// using (var writer = new StreamWriter(stream, Encoding.UTF8))
// {
// writer.Write(xml);
// //byte[] data = Encoding.UTF8.GetBytes(xml);
// //stream.Write(data, 0, data.Length);
// stream.Position = 0;
// var deserializer = new DataContractSerializer(toType);
// return deserializer.ReadObject(stream);
// }
// }
//}
}
}
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Models.EntityBase
/// <summary>
/// Tracks the properties that have changed
/// </summary>
private readonly IDictionary<string, bool> _propertyChangedInfo = new Dictionary<string, bool>();
private IDictionary<string, bool> _propertyChangedInfo = new Dictionary<string, bool>();
/// <summary>
/// Tracks the properties that we're changed before the last commit (or last call to ResetDirtyProperties)
@@ -86,7 +86,9 @@ namespace Umbraco.Core.Models.EntityBase
/// </summary>
public void ForgetPreviouslyDirtyProperties()
{
_lastPropertyChangedInfo.Clear();
//NOTE: We cannot .Clear() because when we memberwise clone this will be the SAME
// instance as the one on the clone, so we need to create a new instance.
_lastPropertyChangedInfo = new Dictionary<string, bool>();
}
/// <summary>
@@ -119,7 +121,9 @@ namespace Umbraco.Core.Models.EntityBase
_lastPropertyChangedInfo = _propertyChangedInfo.ToDictionary(v => v.Key, v => v.Value);
}
_propertyChangedInfo.Clear();
//NOTE: We cannot .Clear() because when we memberwise clone this will be the SAME
// instance as the one on the clone, so we need to create a new instance.
_propertyChangedInfo = new Dictionary<string, bool>();
}
/// <summary>
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Models
/// <summary>
/// Defines a Property for a Macro
/// </summary>
public interface IMacroProperty : IValueObject
public interface IMacroProperty : IValueObject, IDeepCloneable, IRememberBeingDirty
{
[DataMember]
int Id { get; set; }
+27 -17
View File
@@ -109,9 +109,9 @@ namespace Umbraco.Core.Models
private string _scriptAssembly;
private string _scriptPath;
private string _xslt;
private readonly MacroPropertyCollection _properties;
private readonly List<string> _addedProperties;
private readonly List<string> _removedProperties;
private MacroPropertyCollection _properties;
private List<string> _addedProperties;
private List<string> _removedProperties;
private static readonly PropertyInfo AliasSelector = ExpressionHelper.GetPropertyInfo<Macro, string>(x => x.Alias);
private static readonly PropertyInfo NameSelector = ExpressionHelper.GetPropertyInfo<Macro, string>(x => x.Name);
@@ -138,12 +138,11 @@ namespace Umbraco.Core.Models
var alias = prop.Alias;
//remove from the removed/added props (since people could add/remove all they want in one request)
_removedProperties.RemoveAll(s => s == alias);
_addedProperties.RemoveAll(s => s == alias);
//add to the added props
_addedProperties.Add(alias);
if (_addedProperties.Contains(alias) == false)
{
//add to the added props
_addedProperties.Add(alias);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
@@ -153,12 +152,10 @@ namespace Umbraco.Core.Models
var alias = prop.Alias;
//remove from the removed/added props (since people could add/remove all they want in one request)
_removedProperties.RemoveAll(s => s == alias);
_addedProperties.RemoveAll(s => s == alias);
//add to the added props
_removedProperties.Add(alias);
if (_removedProperties.Contains(alias) == false)
{
_removedProperties.Add(alias);
}
}
}
@@ -397,7 +394,20 @@ namespace Umbraco.Core.Models
{
get { return _properties; }
}
public override object DeepClone()
{
var clone = (Macro)base.DeepClone();
clone._addedProperties = new List<string>();
clone._removedProperties = new List<string>();
clone._properties = (MacroPropertyCollection)Properties.DeepClone();
//re-assign the event handler
clone._properties.CollectionChanged += clone.PropertiesChanged;
clone.ResetDirtyProperties(false);
return clone;
}
}
}
+33 -1
View File
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
public class MacroProperty : TracksChangesEntityBase, IMacroProperty, IRememberBeingDirty
public class MacroProperty : TracksChangesEntityBase, IMacroProperty, IRememberBeingDirty, IDeepCloneable
{
public MacroProperty()
{
@@ -176,5 +176,37 @@ namespace Umbraco.Core.Models
}, _editorAlias, PropertyTypeSelector);
}
}
public object DeepClone()
{
//Memberwise clone on MacroProperty will work since it doesn't have any deep elements
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
var clone = (MacroProperty)MemberwiseClone();
//Automatically deep clone ref properties that are IDeepCloneable
DeepCloneHelper.DeepCloneRefProperties(this, clone);
clone.ResetDirtyProperties(false);
return clone;
}
protected bool Equals(MacroProperty other)
{
return string.Equals(_alias, other._alias) && _id == other._id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MacroProperty) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((_alias != null ? _alias.GetHashCode() : 0)*397) ^ _id;
}
}
}
}
@@ -8,13 +8,60 @@ namespace Umbraco.Core.Models
/// <summary>
/// A macro's property collection
/// </summary>
public class MacroPropertyCollection : ObservableDictionary<string, IMacroProperty>
public class MacroPropertyCollection : ObservableDictionary<string, IMacroProperty>, IDeepCloneable
{
public MacroPropertyCollection()
: base(property => property.Alias)
{
}
public object DeepClone()
{
var clone = new MacroPropertyCollection();
foreach (var item in this)
{
clone.Add((IMacroProperty)item.DeepClone());
}
return clone;
}
/// <summary>
/// Used to update an existing macro property
/// </summary>
/// <param name="name"></param>
/// <param name="sortOrder"></param>
/// <param name="editorAlias"></param>
/// <param name="currentAlias">
/// The existing property alias
/// </param>
/// <param name="newAlias"></param>
public void UpdateProperty(string currentAlias, string name = null, int? sortOrder = null, string editorAlias = null, string newAlias = null)
{
var prop = this[currentAlias];
if (prop == null)
{
throw new InvalidOperationException("No property exists with alias " + currentAlias);
}
if (name.IsNullOrWhiteSpace() == false)
{
prop.Name = name;
}
if (sortOrder.HasValue)
{
prop.SortOrder = sortOrder.Value;
}
if (name.IsNullOrWhiteSpace() == false)
{
prop.EditorAlias = editorAlias;
}
if (newAlias.IsNullOrWhiteSpace() == false && currentAlias != newAlias)
{
prop.Alias = newAlias;
ChangeKey(currentAlias, newAlias);
}
}
}
}
+35 -15
View File
@@ -59,9 +59,9 @@ namespace Umbraco.Core.Models.Membership
private IUserType _userType;
private string _name;
private Type _userTypeKey;
private readonly List<string> _addedSections;
private readonly List<string> _removedSections;
private readonly ObservableCollection<string> _sectionCollection;
private List<string> _addedSections;
private List<string> _removedSections;
private ObservableCollection<string> _sectionCollection;
private int _sessionTimeout;
private int _startContentId;
private int _startMediaId;
@@ -244,7 +244,10 @@ namespace Umbraco.Core.Models.Membership
public void RemoveAllowedSection(string sectionAlias)
{
_sectionCollection.Remove(sectionAlias);
if (_sectionCollection.Contains(sectionAlias))
{
_sectionCollection.Remove(sectionAlias);
}
}
public void AddAllowedSection(string sectionAlias)
@@ -423,24 +426,41 @@ namespace Umbraco.Core.Models.Membership
if (e.Action == NotifyCollectionChangedAction.Add)
{
//remove from the removed/added sections (since people could add/remove all they want in one request)
_removedSections.RemoveAll(s => s == e.NewItems.Cast<string>().First());
_addedSections.RemoveAll(s => s == e.NewItems.Cast<string>().First());
var item = e.NewItems.Cast<string>().First();
//add to the added sections
_addedSections.Add(e.NewItems.Cast<string>().First());
if (_addedSections.Contains(item) == false)
{
_addedSections.Add(item);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
//remove from the removed/added sections (since people could add/remove all they want in one request)
_removedSections.RemoveAll(s => s == e.OldItems.Cast<string>().First());
_addedSections.RemoveAll(s => s == e.OldItems.Cast<string>().First());
var item = e.OldItems.Cast<string>().First();
if (_removedSections.Contains(item) == false)
{
_removedSections.Add(item);
}
//add to the added sections
_removedSections.Add(e.OldItems.Cast<string>().First());
}
}
public override object DeepClone()
{
var clone = (User)base.DeepClone();
//need to create new collections otherwise they'll get copied by ref
clone._addedSections = new List<string>();
clone._removedSections = new List<string>();
clone._sectionCollection = new ObservableCollection<string>(_sectionCollection.ToList());
//re-create the event handler
clone._sectionCollection.CollectionChanged += clone.SectionCollectionChanged;
clone.ResetDirtyProperties(false);
return clone;
}
/// <summary>
/// Internal class used to wrap the user in a profile
/// </summary>
+1 -1
View File
@@ -5,7 +5,7 @@ using System.Text;
namespace Umbraco.Core.Models
{
internal class Notification
public class Notification
{
public Notification(int entityId, int userId, string action, Guid entityType)
{
+8 -1
View File
@@ -3,7 +3,7 @@
/// <summary>
/// Represents a stored pre-value field value
/// </summary>
public class PreValue
public class PreValue : IDeepCloneable
{
public PreValue(int id, string value, int sortOrder)
{
@@ -37,5 +37,12 @@
/// The sort order stored for the pre-value field value
/// </summary>
public int SortOrder { get; private set; }
public virtual object DeepClone()
{
//Memberwise clone on PreValue will work since it doesn't have any deep elements
var clone = (PreValue)MemberwiseClone();
return clone;
}
}
}
+17 -1
View File
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Models
/// Most legacy property editors won't support the dictionary format but new property editors should always use the dictionary format.
/// In order to get overrideable pre-values working we need a dictionary since we'll have to reference a pre-value by a key.
/// </remarks>
public class PreValueCollection
public class PreValueCollection : IDeepCloneable
{
private IDictionary<string, PreValue> _preValuesAsDictionary;
private IEnumerable<PreValue> _preValuesAsArray;
@@ -82,5 +82,21 @@ namespace Umbraco.Core.Models
}
return result;
}
public object DeepClone()
{
var clone = (PreValueCollection) MemberwiseClone();
if (_preValuesAsArray != null)
{
clone._preValuesAsArray = _preValuesAsArray.Select(x => (PreValue)x.DeepClone()).ToArray();
}
if (_preValuesAsDictionary != null)
{
clone._preValuesAsDictionary = _preValuesAsDictionary.ToDictionary(x => x.Key, x => (PreValue)x.Value.DeepClone());
}
return clone;
}
}
}
@@ -0,0 +1,9 @@
namespace Umbraco.Core.Models
{
internal enum PropertyTagBehavior
{
Replace,
Remove,
Merge
}
}
-7
View File
@@ -33,11 +33,4 @@ namespace Umbraco.Core.Models
public IEnumerable<Tuple<string, string>> Tags { get; set; }
}
internal enum PropertyTagBehavior
{
Replace,
Remove,
Merge
}
}
@@ -44,6 +44,11 @@ namespace Umbraco.Core.Models.PublishedContent
InitializeIndexes();
}
// create detached content type - ie does not match anything in the DB
internal PublishedContentType(string alias, IEnumerable<PublishedPropertyType> propertyTypes)
: this (0, alias, propertyTypes)
{ }
private void InitializeIndexes()
{
for (var i = 0; i < _propertyTypes.Length; i++)
@@ -0,0 +1,8 @@
namespace Umbraco.Core.Models
{
public enum TagCacheStorageType
{
Csv,
Json
}
}
+24 -10
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using Umbraco.Core.Models.EntityBase;
namespace Umbraco.Core.Models
@@ -47,13 +48,11 @@ namespace Umbraco.Core.Models
public UmbracoEntity()
{
AdditionalData = new Dictionary<string, object>();
UmbracoProperties = new List<UmbracoProperty>();
}
public UmbracoEntity(bool trashed)
{
AdditionalData = new Dictionary<string, object>();
UmbracoProperties = new List<UmbracoProperty>();
Trashed = trashed;
}
@@ -61,7 +60,6 @@ namespace Umbraco.Core.Models
public UmbracoEntity(UInt64 trashed)
{
AdditionalData = new Dictionary<string, object>();
UmbracoProperties = new List<UmbracoProperty>();
Trashed = trashed == 1;
}
@@ -287,15 +285,31 @@ namespace Umbraco.Core.Models
}
}
public override object DeepClone()
{
var clone = (UmbracoEntity) base.DeepClone();
//This ensures that any value in the dictionary that is deep cloneable is cloned too
foreach (var key in clone.AdditionalData.Keys.ToArray())
{
var deepCloneable = clone.AdditionalData[key] as IDeepCloneable;
if (deepCloneable != null)
{
clone.AdditionalData[key] = deepCloneable.DeepClone();
}
}
return clone;
}
/// <summary>
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
/// A struction that can be contained in the additional data of an UmbracoEntity representing
/// a user defined property
/// </summary>
public IList<UmbracoProperty> UmbracoProperties { get; set; }
internal class UmbracoProperty : IDeepCloneable
public class EntityProperty : IDeepCloneable
{
public string PropertyEditorAlias { get; set; }
public string Value { get; set; }
public object Value { get; set; }
public object DeepClone()
{
//Memberwise clone on Entity will work since it doesn't have any deep elements
@@ -304,7 +318,7 @@ namespace Umbraco.Core.Models
return clone;
}
protected bool Equals(UmbracoProperty other)
protected bool Equals(EntityProperty other)
{
return PropertyEditorAlias.Equals(other.PropertyEditorAlias) && string.Equals(Value, other.Value);
}
@@ -314,7 +328,7 @@ namespace Umbraco.Core.Models
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((UmbracoProperty) obj);
return Equals((EntityProperty) obj);
}
public override int GetHashCode()
@@ -49,6 +49,7 @@ namespace Umbraco.Core.ObjectResolution
/// Initializes a new instance of the <see cref="LazyManyObjectsResolverBase{TResolver, TResolved}"/> class with an initial list
/// <remarks>If <paramref name="scope"/> is per HttpRequest then there must be a current HttpContext.</remarks>
/// <exception cref="InvalidOperationException"><paramref name="scope"/> is per HttpRequest but the current HttpContext is null.</exception>
/// </summary>
protected LazyManyObjectsResolverBase(IEnumerable<Lazy<Type>> lazyTypeList, ObjectLifetimeScope scope = ObjectLifetimeScope.Application)
: this(scope)
{
@@ -108,7 +109,7 @@ namespace Umbraco.Core.ObjectResolution
/// Gets a value indicating whether the resolver has resolved types to create instances from.
/// </summary>
/// <remarks>To be used in unit tests.</remarks>
internal bool HasResolvedTypes
public bool HasResolvedTypes
{
get
{
@@ -85,10 +85,9 @@ namespace Umbraco.Core.ObjectResolution
}
/// <summary>
/// Gets a value indicating whether a the singleton nstance has been set.
/// Gets a value indicating whether a the singleton instance has been set.
/// </summary>
/// <remarks>To be used in unit tests.</remarks>
internal static bool HasCurrent
public static bool HasCurrent
{
get
{
@@ -102,9 +101,13 @@ namespace Umbraco.Core.ObjectResolution
/// <summary>
/// Resets the resolver singleton instance to null.
/// </summary>
/// <remarks>To be used in unit tests.</remarks>
/// <param name="resetResolution">By default this is true because we always need to reset resolution before we reset a resolver, however in some insanely rare cases like unit testing you might not want to do this.</param>
internal static void Reset(bool resetResolution = true)
/// <remarks>
/// To be used in unit tests. DO NOT USE THIS DURING PRODUCTION.
/// </remarks>
/// <param name="resetResolution">
/// By default this is true because we always need to reset resolution before we reset a resolver, however in some insanely rare cases like unit testing you might not want to do this.
/// </param>
protected internal static void Reset(bool resetResolution = true)
{
//In order to reset a resolver, we always must reset the resolution
+22
View File
@@ -135,6 +135,28 @@ namespace Umbraco.Core
}
/// <summary>
/// Allows us to change the key of an item
/// </summary>
/// <param name="currentKey"></param>
/// <param name="newKey"></param>
public virtual void ChangeKey(TKey currentKey, TKey newKey)
{
if (!Indecies.ContainsKey(currentKey))
{
throw new InvalidOperationException("No item with the key " + currentKey + "was found in the collection");
}
if (ContainsKey(newKey))
{
throw new DuplicateKeyException(newKey.ToString());
}
var currentIndex = Indecies[currentKey];
Indecies.Remove(currentKey);
Indecies.Add(newKey, currentIndex);
}
internal class DuplicateKeyException : Exception
{
@@ -128,9 +128,9 @@ namespace Umbraco.Core.Persistence.Caching
public void Save(Type type, IEntity entity)
{
//IMPORTANT: we must clone to store, see: http://issues.umbraco.org/issue/U4-4259
entity = (IEntity)entity.DeepClone();
var clone = (IEntity)entity.DeepClone();
var key = GetCompositeId(type, entity.Id);
var key = GetCompositeId(type, clone.Id);
_keyTracker.TryAdd(key);
@@ -139,11 +139,11 @@ namespace Umbraco.Core.Persistence.Caching
if (_memoryCache != null)
{
_memoryCache.Set(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
_memoryCache.Set(key, clone, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
}
else
{
HttpRuntime.Cache.Insert(key, entity, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
HttpRuntime.Cache.Insert(key, clone, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
}
}
@@ -44,7 +44,6 @@ namespace Umbraco.Core.Persistence.Factories
ContentTypeAlias = asDictionary.ContainsKey("alias") ? (d.alias ?? string.Empty) : string.Empty,
ContentTypeIcon = asDictionary.ContainsKey("icon") ? (d.icon ?? string.Empty) : string.Empty,
ContentTypeThumbnail = asDictionary.ContainsKey("thumbnail") ? (d.thumbnail ?? string.Empty) : string.Empty,
UmbracoProperties = new List<UmbracoEntity.UmbracoProperty>()
};
var publishedVersion = default(Guid);
@@ -87,7 +86,6 @@ namespace Umbraco.Core.Persistence.Factories
ContentTypeAlias = dto.Alias ?? string.Empty,
ContentTypeIcon = dto.Icon ?? string.Empty,
ContentTypeThumbnail = dto.Thumbnail ?? string.Empty,
UmbracoProperties = new List<UmbracoEntity.UmbracoProperty>()
};
entity.IsPublished = dto.PublishedVersion != default(Guid) || (dto.NewestVersion != default(Guid) && dto.PublishedVersion == dto.NewestVersion);
@@ -98,12 +96,13 @@ namespace Umbraco.Core.Persistence.Factories
{
foreach (var propertyDto in dto.UmbracoPropertyDtos)
{
entity.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty
{
PropertyEditorAlias =
propertyDto.PropertyEditorAlias,
Value = propertyDto.UmbracoFile
});
entity.AdditionalData[propertyDto.PropertyAlias] = new UmbracoEntity.EntityProperty
{
PropertyEditorAlias = propertyDto.PropertyEditorAlias,
Value = propertyDto.NTextValue.IsNullOrWhiteSpace()
? propertyDto.NVarcharValue
: propertyDto.NTextValue.ConvertToJsonIfPossible()
};
}
}
@@ -72,7 +72,7 @@ namespace Umbraco.Core.Persistence.Factories
{
AppAlias = app
};
if (entity.Id != null)
if (entity.HasIdentity)
{
appDto.UserId = (int) entity.Id;
}
@@ -295,8 +295,8 @@ AND umbracoNode.id <> @id",
var cached = _cacheHelper.RuntimeCache.GetCacheItemsByKeySearch<PreValueCollection>(GetPrefixedCacheKey(dataTypeId));
if (cached != null && cached.Any())
{
//return from the cache
return cached.First();
//return from the cache, ensure it's a cloned result
return (PreValueCollection)cached.First().DeepClone();
}
l.UpgradeToWriteLock();
@@ -66,15 +66,31 @@ namespace Umbraco.Core.Persistence.Repositories
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, key).Append(GetGroupBy(isContent, isMedia));
var nodeDto = _work.Database.FirstOrDefault<dynamic>(sql);
if (nodeDto == null)
return null;
var factory = new UmbracoEntityFactory();
var entity = factory.BuildEntityFromDynamic(nodeDto);
var sql = GetFullSqlForEntityType(key, isContent, isMedia, objectTypeId);
if (isMedia)
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
return entity;
return entities.FirstOrDefault();
}
else
{
var nodeDto = _work.Database.FirstOrDefault<dynamic>(sql);
if (nodeDto == null)
return null;
var factory = new UmbracoEntityFactory();
var entity = factory.BuildEntityFromDynamic(nodeDto);
return entity;
}
}
public virtual IUmbracoEntity Get(int id)
@@ -94,16 +110,31 @@ namespace Umbraco.Core.Persistence.Repositories
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, id).Append(GetGroupBy(isContent, isMedia));
var nodeDto = _work.Database.FirstOrDefault<dynamic>(sql);
if (nodeDto == null)
return null;
var sql = GetFullSqlForEntityType(id, isContent, isMedia, objectTypeId);
if (isMedia)
{
//for now treat media differently
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
var factory = new UmbracoEntityFactory();
var entity = factory.BuildEntityFromDynamic(nodeDto);
return entities.FirstOrDefault();
}
else
{
var nodeDto = _work.Database.FirstOrDefault<dynamic>(sql);
if (nodeDto == null)
return null;
return entity;
var factory = new UmbracoEntityFactory();
var entity = factory.BuildEntityFromDynamic(nodeDto);
return entity;
}
}
public virtual IEnumerable<IUmbracoEntity> GetAll(Guid objectTypeId, params int[] ids)
@@ -119,8 +150,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
bool isContent = objectTypeId == new Guid(Constants.ObjectTypes.Document);
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var sql = GetBaseWhere(GetBase, isContent, isMedia, string.Empty, objectTypeId).Append(GetGroupBy(isContent, isMedia));
var sql = GetFullSqlForEntityType(isContent, isMedia, objectTypeId, string.Empty);
var factory = new UmbracoEntityFactory();
if (isMedia)
@@ -166,24 +197,28 @@ namespace Umbraco.Core.Persistence.Repositories
bool isMedia = objectTypeId == new Guid(Constants.ObjectTypes.Media);
var wheres = string.Concat(" AND ", string.Join(" AND ", ((Query<IUmbracoEntity>)query).WhereClauses()));
var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, wheres, objectTypeId);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var sql = translator.Translate().Append(GetGroupBy(isContent, isMedia));
var entitySql = translator.Translate();
var factory = new UmbracoEntityFactory();
if (isMedia)
{
var mediaSql = GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)), wheres);
//treat media differently for now
//TODO: We should really use this methodology for Content/Members too!! since it includes properties and ALL of the dynamic db fields
var entities = _work.Database.Fetch<dynamic, UmbracoPropertyDto, UmbracoEntity>(
new UmbracoEntityRelator().Map, sql);
new UmbracoEntityRelator().Map, mediaSql);
return entities;
}
else
{
//use dynamic so that we can get ALL properties from the SQL so we can chuck that data into our AdditionalData
var dtos = _work.Database.Fetch<dynamic>(sql);
var dtos = _work.Database.Fetch<dynamic>(entitySql.Append(GetGroupBy(isContent, false)));
return dtos.Select(factory.BuildEntityFromDynamic).Cast<IUmbracoEntity>().ToList();
}
}
@@ -193,6 +228,62 @@ namespace Umbraco.Core.Persistence.Repositories
#region Sql Statements
protected Sql GetFullSqlForEntityType(Guid key, bool isContent, bool isMedia, Guid objectTypeId)
{
var entitySql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, key);
if (isMedia == false) return entitySql.Append(GetGroupBy(isContent, false));
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)));
}
protected Sql GetFullSqlForEntityType(int id, bool isContent, bool isMedia, Guid objectTypeId)
{
var entitySql = GetBaseWhere(GetBase, isContent, isMedia, objectTypeId, id);
if (isMedia == false) return entitySql.Append(GetGroupBy(isContent, false));
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)));
}
protected Sql GetFullSqlForEntityType(bool isContent, bool isMedia, Guid objectTypeId, string additionalWhereClause)
{
var entitySql = GetBaseWhere(GetBase, isContent, isMedia, additionalWhereClause, objectTypeId);
if (isMedia == false) return entitySql.Append(GetGroupBy(isContent, false));
return GetFullSqlForMedia(entitySql.Append(GetGroupBy(isContent, true, false)));
}
private Sql GetFullSqlForMedia(Sql entitySql, string additionWhereStatement = "")
{
//this will add any dataNvarchar property to the output which can be added to the additional properties
var joinSql = new Sql()
.Select("contentNodeId, versionId, dataNvarchar, dataNtext, propertyEditorAlias, alias as propertyTypeAlias")
.From<PropertyDataDto>()
.InnerJoin<NodeDto>()
.On<PropertyDataDto, NodeDto>(dto => dto.NodeId, dto => dto.NodeId)
.InnerJoin<PropertyTypeDto>()
.On<PropertyTypeDto, PropertyDataDto>(dto => dto.Id, dto => dto.PropertyTypeId)
.InnerJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(dto => dto.DataTypeId, dto => dto.DataTypeId)
.Where("umbracoNode.nodeObjectType = @nodeObjectType" + additionWhereStatement, new {nodeObjectType = Constants.ObjectTypes.Media});
//We're going to create a query to query against the entity SQL
// because we cannot group by nText columns and we have a COUNT in the entitySql we cannot simply left join
// the entitySql query, we have to join the wrapped query to get the ntext in the result
var wrappedSql = new Sql("SELECT * FROM (")
.Append(entitySql)
.Append(new Sql(") tmpTbl LEFT JOIN ("))
.Append(joinSql)
.Append(new Sql(") as property ON id = property.contentNodeId"))
.OrderBy("sortOrder");
return wrappedSql;
}
protected virtual Sql GetBase(bool isContent, bool isMedia, string additionWhereStatement = "")
{
var columns = new List<object>
@@ -221,13 +312,9 @@ namespace Umbraco.Core.Persistence.Repositories
columns.Add("contenttype.isContainer");
}
if (isMedia)
{
columns.Add("property.dataNvarchar as umbracoFile");
columns.Add("property.propertyEditorAlias");
}
//Creates an SQL query to return a single row for the entity
var sql = new Sql()
var entitySql = new Sql()
.Select(columns.ToArray())
.From("umbracoNode umbracoNode")
.LeftJoin("umbracoNode parent").On("parent.parentID = umbracoNode.id");
@@ -235,7 +322,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (isContent || isMedia)
{
sql.InnerJoin("cmsContent content").On("content.nodeId = umbracoNode.id")
entitySql.InnerJoin("cmsContent content").On("content.nodeId = umbracoNode.id")
.LeftJoin("cmsContentType contenttype").On("contenttype.nodeId = content.contentType")
.LeftJoin(
"(SELECT nodeId, versionId FROM cmsDocument WHERE published = 1 GROUP BY nodeId, versionId) as published")
@@ -245,18 +332,7 @@ namespace Umbraco.Core.Persistence.Repositories
.On("umbracoNode.id = latest.nodeId");
}
if (isMedia)
{
sql.LeftJoin(
"(SELECT contentNodeId, versionId, dataNvarchar, propertyEditorAlias FROM cmsPropertyData " +
"INNER JOIN umbracoNode ON cmsPropertyData.contentNodeId = umbracoNode.id " +
"INNER JOIN cmsPropertyType ON cmsPropertyType.id = cmsPropertyData.propertytypeid " +
"INNER JOIN cmsDataType ON cmsPropertyType.dataTypeId = cmsDataType.nodeId "+
"WHERE umbracoNode.nodeObjectType = '" + Constants.ObjectTypes.Media + "'" + additionWhereStatement + ") as property")
.On("umbracoNode.id = property.contentNodeId");
}
return sql;
return entitySql;
}
protected virtual Sql GetBaseWhere(Func<bool, bool, string, Sql> baseQuery, bool isContent, bool isMedia, string additionWhereStatement, Guid nodeObjectType)
@@ -298,7 +374,7 @@ namespace Umbraco.Core.Persistence.Repositories
return sql;
}
protected virtual Sql GetGroupBy(bool isContent, bool isMedia)
protected virtual Sql GetGroupBy(bool isContent, bool isMedia, bool includeSort = true)
{
var columns = new List<object>
{
@@ -324,19 +400,18 @@ namespace Umbraco.Core.Persistence.Repositories
columns.Add("contenttype.thumbnail");
columns.Add("contenttype.isContainer");
}
var sql = new Sql()
.GroupBy(columns.ToArray());
if (isMedia)
if (includeSort)
{
columns.Add("property.dataNvarchar");
columns.Add("property.propertyEditorAlias");
sql = sql.OrderBy("umbracoNode.sortOrder");
}
var sql = new Sql()
.GroupBy(columns.ToArray())
.OrderBy("umbracoNode.sortOrder");
return sql;
}
#endregion
/// <summary>
@@ -377,15 +452,21 @@ namespace Umbraco.Core.Persistence.Repositories
[ResultColumn]
public List<UmbracoPropertyDto> UmbracoPropertyDtos { get; set; }
}
[ExplicitColumns]
internal class UmbracoPropertyDto
{
[Column("propertyEditorAlias")]
public string PropertyEditorAlias { get; set; }
[Column("umbracoFile")]
public string UmbracoFile { get; set; }
[Column("propertyTypeAlias")]
public string PropertyAlias { get; set; }
[Column("dataNvarchar")]
public string NVarcharValue { get; set; }
[Column("dataNtext")]
public string NTextValue { get; set; }
}
/// <summary>
@@ -412,16 +493,18 @@ namespace Umbraco.Core.Persistence.Repositories
// Is this the same UmbracoEntity as the current one we're processing
if (Current != null && Current.Key == a.uniqueID)
{
// Yes, just add this UmbracoProperty to the current UmbracoEntity's collection
if (Current.UmbracoProperties == null)
if (p != null && p.PropertyAlias.IsNullOrWhiteSpace() == false)
{
Current.UmbracoProperties = new List<UmbracoEntity.UmbracoProperty>();
}
Current.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty
// Add this UmbracoProperty to the current additional data
Current.AdditionalData[p.PropertyAlias] = new UmbracoEntity.EntityProperty
{
PropertyEditorAlias = p.PropertyEditorAlias,
Value = p.UmbracoFile
});
Value = p.NTextValue.IsNullOrWhiteSpace()
? p.NVarcharValue
: p.NTextValue.ConvertToJsonIfPossible()
};
}
// Return null to indicate we're not done with this UmbracoEntity yet
return null;
}
@@ -436,15 +519,17 @@ namespace Umbraco.Core.Persistence.Repositories
Current = _factory.BuildEntityFromDynamic(a);
//add the property/create the prop list if null
Current.UmbracoProperties = new List<UmbracoEntity.UmbracoProperty>
if (p != null && p.PropertyAlias.IsNullOrWhiteSpace() == false)
{
//add the property/create the prop list if null
Current.AdditionalData[p.PropertyAlias] = new UmbracoEntity.EntityProperty
{
new UmbracoEntity.UmbracoProperty
{
PropertyEditorAlias = p.PropertyEditorAlias,
Value = p.UmbracoFile
}
PropertyEditorAlias = p.PropertyEditorAlias,
Value = p.NTextValue.IsNullOrWhiteSpace()
? p.NVarcharValue
: p.NTextValue.ConvertToJsonIfPossible()
};
}
// Return the now populated previous UmbracoEntity (or null if first time through)
return prev;
@@ -165,10 +165,18 @@ namespace Umbraco.Core.Persistence.Repositories
Database.Update(dto);
//update the sections if they've changed
//update the properties if they've changed
var macro = (Macro)entity;
if (macro.IsPropertyDirty("Properties"))
if (macro.IsPropertyDirty("Properties") || macro.Properties.Any(x => x.IsDirty()))
{
//now we need to delete any props that have been removed
foreach (var propAlias in macro.RemovedProperties)
{
//delete the property
Database.Delete<MacroPropertyDto>("WHERE macro=@macroId AND macroPropertyAlias=@propAlias",
new { macroId = macro.Id, propAlias = propAlias });
}
//for any that exist on the object, we need to determine if we need to update or insert
foreach (var propDto in dto.MacroPropertyDtos)
{
@@ -180,17 +188,15 @@ namespace Umbraco.Core.Persistence.Repositories
}
else
{
Database.Update(propDto);
//only update if it's dirty
if (macro.Properties[propDto.Alias].IsDirty())
{
Database.Update(propDto);
}
}
}
//now we need to delete any props that have been removed
foreach (var propAlias in macro.RemovedProperties)
{
//delete the property
Database.Delete<MacroPropertyDto>("WHERE macro=@macroId AND macroPropertyAlias=@propAlias",
new { macroId = macro.Id, propAlias = propAlias });
}
}
((ICanBeDirty)entity).ResetDirtyProperties();
@@ -285,7 +285,7 @@ namespace Umbraco.Core.Persistence.Repositories
if (group.IsNullOrWhiteSpace() == false)
{
sql = sql.Where<TagDto>(dto => dto.Group == group);
}
}
var factory = new TagFactory();
@@ -329,7 +329,7 @@ namespace Umbraco.Core.Persistence.Repositories
//NOTE: There's some very clever logic in the umbraco.cms.businesslogic.Tags.Tag to insert tags where they don't exist,
// and assign where they don't exist which we've borrowed here. The queries are pretty zany but work, otherwise we'll end up
// with quite a few additional queries.
//do all this in one transaction
using (var trans = Database.GetTransaction())
{
@@ -397,7 +397,7 @@ namespace Umbraco.Core.Persistence.Repositories
public void RemoveTagsFromProperty(int contentId, int propertyTypeId, IEnumerable<ITag> tags)
{
var tagSetSql = GetTagSet(tags);
var deleteSql = string.Concat("DELETE FROM cmsTagRelationship WHERE nodeId = ",
contentId,
" AND propertyTypeId = ",
@@ -439,7 +439,11 @@ namespace Umbraco.Core.Persistence.Repositories
/// <returns></returns>
private static string GetTagSet(IEnumerable<ITag> tagsToInsert)
{
var array = tagsToInsert.Select(tag => string.Format("select '{0}' as Tag, '{1}' as [Group]", tag.Text.Replace("'", "''"), tag.Group)).ToArray();
var array = tagsToInsert
.Select(tag =>
string.Format("select '{0}' as Tag, '{1}' as [Group]",
PetaPocoExtensions.EscapeAtSymbols(tag.Text.Replace("'", "''")), tag.Group))
.ToArray();
return "(" + string.Join(" union ", array).Replace(" ", " ") + ") as TagSet";
}
@@ -158,7 +158,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
"DELETE FROM umbracoUser2NodeNotify WHERE nodeId = @Id",
"DELETE FROM umbracoUser2NodePermission WHERE nodeId = @Id",
"UPDATE cmsDocument SET templateId = NULL WHERE nodeId = @Id",
"UPDATE cmsDocument SET templateId = NULL WHERE templateId = @Id",
"DELETE FROM cmsDocumentType WHERE templateNodeId = @Id",
"DELETE FROM cmsTemplate WHERE nodeId = @Id",
"DELETE FROM umbracoNode WHERE id = @Id"
@@ -210,9 +210,19 @@ namespace Umbraco.Core.Persistence.Repositories
var user = (User)entity;
if (user.IsPropertyDirty("AllowedSections"))
{
//now we need to delete any applications that have been removed
foreach (var section in user.RemovedSections)
{
//we need to manually delete thsi record because it has a composite key
Database.Delete<User2AppDto>("WHERE app=@Section AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@UserId",
new { Section = section, UserId = (int)user.Id });
}
//for any that exist on the object, we need to determine if we need to update or insert
//NOTE: the User2AppDtos collection wil always be equal to the User.AllowedSections
foreach (var sectionDto in userDto.User2AppDtos)
{
//if something has been added then insert it
if (user.AddedSections.Contains(sectionDto.AppAlias))
{
//we need to insert since this was added
@@ -226,13 +236,7 @@ namespace Umbraco.Core.Persistence.Repositories
}
}
//now we need to delete any applications that have been removed
foreach (var section in user.RemovedSections)
{
//we need to manually delete thsi record because it has a composite key
Database.Delete<User2AppDto>("WHERE app=@Section AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("user") + "=@UserId",
new { Section = section, UserId = (int)user.Id });
}
}
((ICanBeDirty)entity).ResetDirtyProperties();
+28 -16
View File
@@ -44,9 +44,9 @@ namespace Umbraco.Core
/// file is cached temporarily until app startup completes.
/// </summary>
/// <param name="appContext"></param>
/// <param name="detectBinChanges"></param>
internal PluginManager(ApplicationContext appContext, bool detectBinChanges = true)
: this(detectBinChanges)
/// <param name="detectChanges"></param>
internal PluginManager(ApplicationContext appContext, bool detectChanges = true)
: this(detectChanges)
{
if (appContext == null) throw new ArgumentNullException("appContext");
_appContext = appContext;
@@ -55,11 +55,11 @@ namespace Umbraco.Core
/// <summary>
/// Creates a new PluginManager
/// </summary>
/// <param name="detectCodeChanges">
/// If true will detect changes in the /bin folder and therefor load plugins from the
/// <param name="detectChanges">
/// If true will detect changes in the /bin folder, app_code, etc... and therefor load plugins from the
/// cached plugins file if one is found. If false will never use the cache file for plugins
/// </param>
internal PluginManager(bool detectCodeChanges = true)
internal PluginManager(bool detectChanges = true)
{
_tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
//create the folder if it doesn't exist
@@ -68,29 +68,41 @@ namespace Umbraco.Core
Directory.CreateDirectory(_tempFolder);
}
var pluginListFile = GetPluginListFilePath();
//this is a check for legacy changes, before we didn't store the TypeResolutionKind in the file which was a mistake,
//so we need to detect if the old file is there without this attribute, if it is then we delete it
if (DetectLegacyPluginListFile())
{
var filePath = GetPluginListFilePath();
File.Delete(filePath);
File.Delete(pluginListFile);
}
if (detectCodeChanges)
if (detectChanges)
{
//first check if the cached hash is 0, if it is then we ne
//do the check if they've changed
HaveAssembliesChanged = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == 0;
RequiresRescanning = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == 0;
//if they have changed, we need to write the new file
if (HaveAssembliesChanged)
if (RequiresRescanning)
{
//if the hash has changed, clear out the persisted list no matter what, this will force
// rescanning of all plugin types including lazy ones.
// http://issues.umbraco.org/issue/U4-4789
File.Delete(pluginListFile);
WriteCachePluginsHash();
}
}
else
{
//if the hash has changed, clear out the persisted list no matter what, this will force
// rescanning of all plugin types including lazy ones.
// http://issues.umbraco.org/issue/U4-4789
File.Delete(pluginListFile);
//always set to true if we're not detecting (generally only for testing)
HaveAssembliesChanged = true;
RequiresRescanning = true;
}
}
@@ -130,9 +142,9 @@ namespace Umbraco.Core
/// <summary>
/// Returns a bool if the assemblies in the /bin have changed since they were last hashed.
/// Returns a bool if the assemblies in the /bin, app_code, global.asax, etc... have changed since they were last hashed.
/// </summary>
internal bool HaveAssembliesChanged { get; private set; }
internal bool RequiresRescanning { get; private set; }
/// <summary>
/// Returns the currently cached hash value of the scanned assemblies in the /bin folder. Returns 0
@@ -328,7 +340,7 @@ namespace Umbraco.Core
/// <remarks>
/// Generally only used for resetting cache, for example during the install process
/// </remarks>
internal void ClearPluginCache()
public void ClearPluginCache()
{
var path = GetPluginListFilePath();
if (File.Exists(path))
@@ -670,7 +682,7 @@ namespace Umbraco.Core
//we first need to look into our cache file (this has nothing to do with the 'cacheResult' parameter which caches in memory).
//if assemblies have not changed and the cache file actually exists, then proceed to try to lookup by the cache file.
if (HaveAssembliesChanged == false && File.Exists(GetPluginListFilePath()))
if (RequiresRescanning == false && File.Exists(GetPluginListFilePath()))
{
var fileCacheResult = TryGetCachedPluginsFromFile<T>(resolutionType);
@@ -1,4 +1,5 @@
using System;
using Umbraco.Core.Models;
namespace Umbraco.Core.PropertyEditors
{
@@ -30,6 +31,7 @@ namespace Umbraco.Core.PropertyEditors
Delimiter = ",";
ReplaceTags = true;
TagGroup = "default";
StorageType = TagCacheStorageType.Csv;
}
/// <summary>
@@ -37,6 +39,11 @@ namespace Umbraco.Core.PropertyEditors
/// </summary>
public TagValueType ValueType { get; set; }
/// <summary>
/// Defines how to store the tags in cache (CSV or Json)
/// </summary>
public TagCacheStorageType StorageType { get; set; }
/// <summary>
/// Defines a custom delimiter, the default is a comma
/// </summary>
@@ -1,4 +1,5 @@
using Umbraco.Core.Models.Editors;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Editors;
namespace Umbraco.Core.PropertyEditors
{
@@ -30,8 +31,14 @@ namespace Umbraco.Core.PropertyEditors
Delimiter = tagsAttribute.Delimiter;
ReplaceTags = tagsAttribute.ReplaceTags;
TagGroup = tagsAttribute.TagGroup;
StorageType = TagCacheStorageType.Csv;
}
/// <summary>
/// Defines how to store the tags in cache (CSV or Json)
/// </summary>
public virtual TagCacheStorageType StorageType { get; private set; }
/// <summary>
/// Defines a custom delimiter, the default is a comma
/// </summary>
@@ -86,6 +86,7 @@ namespace Umbraco.Core.Security
public static UmbracoBackOfficeIdentity GetCurrentIdentity(this HttpContextBase http, bool authenticateRequestIfNotFound)
{
if (http == null) throw new ArgumentNullException("http");
if (http.User == null) return null; //there's no user at all so no identity
var identity = http.User.Identity as UmbracoBackOfficeIdentity;
if (identity != null) return identity;
if (authenticateRequestIfNotFound == false) return null;
+3 -1
View File
@@ -124,7 +124,9 @@ namespace Umbraco.Core.Services
{
var collection = repository.GetPreValuesCollectionByDataTypeId(id);
//now convert the collection to a string list
var list = collection.FormatAsDictionary().Select(x => x.Value.Value).ToList();
var list = collection.FormatAsDictionary()
.Select(x => x.Value.Value)
.ToList();
return list;
}
}
@@ -13,7 +13,7 @@ using umbraco.interfaces;
namespace Umbraco.Core.Services
{
internal interface INotificationService : IService
public interface INotificationService : IService
{
/// <summary>
/// Sends the notifications for the specified user regarding the specified node and action.
@@ -19,7 +19,7 @@ using umbraco.interfaces;
namespace Umbraco.Core.Services
{
internal class NotificationService : INotificationService
public class NotificationService : INotificationService
{
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private readonly IUserService _userService;
+8 -2
View File
@@ -48,6 +48,10 @@ namespace Umbraco.Core.Services
/// <param name="memberTypeService"></param>
/// <param name="memberService"></param>
/// <param name="userService"></param>
/// <param name="sectionService"></param>
/// <param name="treeService"></param>
/// <param name="tagService"></param>
/// <param name="notificationService"></param>
public ServiceContext(
IContentService contentService,
IMediaService mediaService,
@@ -64,7 +68,8 @@ namespace Umbraco.Core.Services
IUserService userService,
ISectionService sectionService,
IApplicationTreeService treeService,
ITagService tagService)
ITagService tagService,
INotificationService notificationService)
{
_tagService = new Lazy<ITagService>(() => tagService);
_contentService = new Lazy<IContentService>(() => contentService);
@@ -82,6 +87,7 @@ namespace Umbraco.Core.Services
_treeService = new Lazy<IApplicationTreeService>(() => treeService);
_memberService = new Lazy<IMemberService>(() => memberService);
_userService = new Lazy<IUserService>(() => userService);
_notificationService = new Lazy<INotificationService>(() => notificationService);
}
@@ -174,7 +180,7 @@ namespace Umbraco.Core.Services
/// <summary>
/// Gets the <see cref="INotificationService"/>
/// </summary>
internal INotificationService NotificationService
public INotificationService NotificationService
{
get { return _notificationService.Value; }
}
+24 -1
View File
@@ -10,6 +10,7 @@ using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml;
using Newtonsoft.Json;
using Umbraco.Core.Configuration;
using System.Web.Security;
using Umbraco.Core.Strings;
@@ -52,6 +53,28 @@ namespace Umbraco.Core
|| (input.StartsWith("[") && input.EndsWith("]"));
}
/// <summary>
/// Returns a JObject/JArray instance if the string can be converted to json, otherwise returns the string
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
internal static object ConvertToJsonIfPossible(this string input)
{
if (input.DetectIsJson() == false)
{
return input;
}
try
{
var obj = JsonConvert.DeserializeObject(input);
return obj;
}
catch (Exception ex)
{
return input;
}
}
internal static string ReplaceNonAlphanumericChars(this string input, char replacement)
{
//any character that is not alphanumeric, convert to a hyphen
@@ -190,7 +213,7 @@ namespace Umbraco.Core
//this is from SqlMetal and just makes it a bit of fun to allow pluralisation
public static string MakePluralName(this string name)
{
if ((name.EndsWith("x", StringComparison.OrdinalIgnoreCase) || name.EndsWith("ch", StringComparison.OrdinalIgnoreCase)) || (name.EndsWith("ss", StringComparison.OrdinalIgnoreCase) || name.EndsWith("sh", StringComparison.OrdinalIgnoreCase)))
if ((name.EndsWith("x", StringComparison.OrdinalIgnoreCase) || name.EndsWith("ch", StringComparison.OrdinalIgnoreCase)) || (name.EndsWith("s", StringComparison.OrdinalIgnoreCase) || name.EndsWith("sh", StringComparison.OrdinalIgnoreCase)))
{
name = name + "es";
return name;
+2
View File
@@ -349,12 +349,14 @@
<Compile Include="Models\MemberGroup.cs" />
<Compile Include="Models\MemberTypePropertyProfileAccess.cs" />
<Compile Include="Models\Notification.cs" />
<Compile Include="Models\PropertyTagBehavior.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentExtended.cs" />
<Compile Include="Models\PublishedContent\PublishedPropertyBase.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelFactoryImpl.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentModelFactory.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModel.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelFactoryResolver.cs" />
<Compile Include="Models\TagCacheStorageType.cs" />
<Compile Include="Models\TaggableObjectTypes.cs" />
<Compile Include="Models\TemplateNode.cs" />
<Compile Include="Models\UmbracoEntityExtensions.cs" />
+4 -2
View File
@@ -55,7 +55,8 @@ namespace Umbraco.Tests
new Mock<IUserService>().Object,
new Mock<ISectionService>().Object,
new Mock<IApplicationTreeService>().Object,
new Mock<ITagService>().Object);
new Mock<ITagService>().Object,
new Mock<INotificationService>().Object);
Assert.Pass();
}
@@ -99,7 +100,8 @@ namespace Umbraco.Tests
new Mock<IUserService>().Object,
new Mock<ISectionService>().Object,
new Mock<IApplicationTreeService>().Object,
new Mock<ITagService>().Object),
new Mock<ITagService>().Object,
new Mock<INotificationService>().Object),
CacheHelper.CreateDisabledCacheHelper());
Assert.Pass();
+8
View File
@@ -272,6 +272,14 @@ namespace Umbraco.Tests.Models
{
Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(content, null));
}
//need to ensure the event handlers are wired
var asDirty = (ICanBeDirty)clone;
Assert.IsFalse(asDirty.IsPropertyDirty("Properties"));
clone.Properties.Add(new Property(1, Guid.NewGuid(), new PropertyType("test", DataTypeDatabaseType.Ntext) {Alias = "blah"}, "blah"));
Assert.IsTrue(asDirty.IsPropertyDirty("Properties"));
}
[Test]
+20 -3
View File
@@ -4,13 +4,16 @@ using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Serialization;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Models
{
[TestFixture]
public class ContentTypeTests
public class ContentTypeTests : BaseUmbracoConfigurationTest
{
[Test]
public void Can_Deep_Clone_Content_Type_Sort()
{
@@ -140,19 +143,22 @@ namespace Umbraco.Tests.Models
Assert.AreNotSame(clone.AllowedTemplates.ElementAt(index), contentType.AllowedTemplates.ElementAt(index));
Assert.AreEqual(clone.AllowedTemplates.ElementAt(index), contentType.AllowedTemplates.ElementAt(index));
}
Assert.AreNotSame(clone.PropertyGroups, contentType.PropertyGroups);
Assert.AreEqual(clone.PropertyGroups.Count, contentType.PropertyGroups.Count);
for (var index = 0; index < contentType.PropertyGroups.Count; index++)
{
Assert.AreNotSame(clone.PropertyGroups[index], contentType.PropertyGroups[index]);
Assert.AreEqual(clone.PropertyGroups[index], contentType.PropertyGroups[index]);
}
Assert.AreNotSame(clone.PropertyTypes, contentType.PropertyTypes);
Assert.AreEqual(clone.PropertyTypes.Count(), contentType.PropertyTypes.Count());
Assert.AreEqual(0, ((ContentTypeBase)clone).NonGroupedPropertyTypes.Count());
for (var index = 0; index < contentType.PropertyTypes.Count(); index++)
{
Assert.AreNotSame(clone.PropertyTypes.ElementAt(index), contentType.PropertyTypes.ElementAt(index));
Assert.AreEqual(clone.PropertyTypes.ElementAt(index), contentType.PropertyTypes.ElementAt(index));
}
Assert.AreEqual(clone.CreateDate, contentType.CreateDate);
Assert.AreEqual(clone.CreatorId, contentType.CreatorId);
Assert.AreEqual(clone.Key, contentType.Key);
@@ -167,13 +173,24 @@ namespace Umbraco.Tests.Models
Assert.AreEqual(clone.Thumbnail, contentType.Thumbnail);
Assert.AreEqual(clone.Icon, contentType.Icon);
Assert.AreEqual(clone.IsContainer, contentType.IsContainer);
//This double verifies by reflection
var allProps = clone.GetType().GetProperties();
foreach (var propertyInfo in allProps)
{
Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(contentType, null));
}
//need to ensure the event handlers are wired
var asDirty = (ICanBeDirty)clone;
Assert.IsFalse(asDirty.IsPropertyDirty("PropertyTypes"));
clone.AddPropertyType(new PropertyType("test", DataTypeDatabaseType.Nvarchar) { Alias = "blah" });
Assert.IsTrue(asDirty.IsPropertyDirty("PropertyTypes"));
Assert.IsFalse(asDirty.IsPropertyDirty("PropertyGroups"));
clone.AddPropertyGroup("hello");
Assert.IsTrue(asDirty.IsPropertyDirty("PropertyGroups"));
}
[Test]
+64
View File
@@ -0,0 +1,64 @@
using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Models
{
[TestFixture]
public class MacroTests
{
[SetUp]
public void Init()
{
var config = SettingsForTests.GetDefault();
SettingsForTests.ConfigureSettings(config);
}
[Test]
public void Can_Deep_Clone()
{
var macro = new Macro(1, true, 3, "test", "Test", "blah", "blah", "xslt", false, true, true, "script");
macro.Properties.Add(new MacroProperty(6, "rewq", "REWQ", 1, "asdfasdf"));
var clone = (Macro)macro.DeepClone();
Assert.AreNotSame(clone, macro);
Assert.AreEqual(clone, macro);
Assert.AreEqual(clone.Id, macro.Id);
Assert.AreEqual(clone.Properties.Count, macro.Properties.Count);
for (int i = 0; i < clone.Properties.Count; i++)
{
Assert.AreEqual(clone.Properties[i], macro.Properties[i]);
Assert.AreNotSame(clone.Properties[i], macro.Properties[i]);
}
Assert.AreNotSame(clone.Properties, macro.Properties);
Assert.AreNotSame(clone.AddedProperties, macro.AddedProperties);
Assert.AreNotSame(clone.RemovedProperties, macro.RemovedProperties);
//This double verifies by reflection
var allProps = clone.GetType().GetProperties();
foreach (var propertyInfo in allProps)
{
Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(macro, null));
}
//need to ensure the event handlers are wired
var asDirty = (ICanBeDirty)clone;
Assert.IsFalse(asDirty.IsPropertyDirty("Properties"));
clone.Properties.Add(new MacroProperty(3, "asdf", "SDF", 3, "asdfasdf"));
Assert.IsTrue(asDirty.IsPropertyDirty("Properties"));
Assert.AreEqual(1, clone.AddedProperties.Count());
clone.Properties.Remove("rewq");
Assert.AreEqual(1, clone.RemovedProperties.Count());
}
}
}
@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Models;
namespace Umbraco.Tests.Models
{
[TestFixture]
public class PreValueCollectionTests
{
[Test]
public void Can_Deep_Clone()
{
var d = new PreValueCollection(new Dictionary<string, PreValue>
{
{"blah1", new PreValue(1, "test1", 1)},
{"blah2", new PreValue(2, "test1", 3)},
{"blah3", new PreValue(3, "test1", 2)}
});
var a = new PreValueCollection(new[]
{
new PreValue(1, "test1", 1),
new PreValue(2, "test1", 3),
new PreValue(3, "test1", 2)
});
var clone1 = (PreValueCollection)d.DeepClone();
var clone2 = (PreValueCollection)a.DeepClone();
Action<PreValueCollection, PreValueCollection> assert = (orig, clone) =>
{
Assert.AreNotSame(orig, clone);
var oDic = orig.FormatAsDictionary();
var cDic = clone.FormatAsDictionary();
Assert.AreEqual(oDic.Keys.Count(), cDic.Keys.Count());
foreach (var k in oDic.Keys)
{
Assert.AreNotSame(oDic[k], cDic[k]);
Assert.AreEqual(oDic[k].Id, cDic[k].Id);
Assert.AreEqual(oDic[k].SortOrder, cDic[k].SortOrder);
Assert.AreEqual(oDic[k].Value, cDic[k].Value);
}
};
assert(d, clone1);
assert(a, clone2);
}
}
}
@@ -2,11 +2,12 @@ using System;
using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Core.Serialization;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Models
{
[TestFixture]
public class PropertyGroupTests
public class PropertyGroupTests : BaseUmbracoConfigurationTest
{
[Test]
public void Can_Deep_Clone()
@@ -20,7 +21,7 @@ namespace Umbraco.Tests.Models
Alias = "test",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 5,
DataTypeId = Guid.NewGuid(),
PropertyEditorAlias = "propTest",
Description = "testing",
Key = Guid.NewGuid(),
Mandatory = true,
@@ -37,7 +38,7 @@ namespace Umbraco.Tests.Models
Alias = "test2",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 6,
DataTypeId = Guid.NewGuid(),
PropertyEditorAlias = "propTest",
Description = "testing2",
Key = Guid.NewGuid(),
Mandatory = true,
@@ -101,7 +102,7 @@ namespace Umbraco.Tests.Models
Alias = "test",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 5,
DataTypeId = Guid.NewGuid(),
PropertyEditorAlias = "propTest",
Description = "testing",
Key = Guid.NewGuid(),
Mandatory = true,
@@ -118,7 +119,7 @@ namespace Umbraco.Tests.Models
Alias = "test2",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 6,
DataTypeId = Guid.NewGuid(),
PropertyEditorAlias = "propTest",
Description = "testing2",
Key = Guid.NewGuid(),
Mandatory = true,
@@ -17,7 +17,7 @@ namespace Umbraco.Tests.Models
Alias = "test",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 5,
DataTypeId = Guid.NewGuid(),
PropertyEditorAlias = "propTest",
Description = "testing",
Key = Guid.NewGuid(),
Mandatory = true,
@@ -67,7 +67,7 @@ namespace Umbraco.Tests.Models
Alias = "test",
CreateDate = DateTime.Now,
DataTypeDefinitionId = 5,
DataTypeId = Guid.NewGuid(),
PropertyEditorAlias = "propTest",
Description = "testing",
Key = Guid.NewGuid(),
Mandatory = true,
+5 -10
View File
@@ -47,12 +47,13 @@ namespace Umbraco.Tests.Models
};
item.AdditionalData.Add("test1", 3);
item.AdditionalData.Add("test2", "valuie");
item.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty()
item.AdditionalData.Add("test3", new UmbracoEntity.EntityProperty()
{
Value = "test",
PropertyEditorAlias = "TestPropertyEditor"
});
item.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty()
item.AdditionalData.Add("test4", new UmbracoEntity.EntityProperty()
{
Value = "test2",
PropertyEditorAlias = "TestPropertyEditor2"
@@ -82,12 +83,6 @@ namespace Umbraco.Tests.Models
Assert.AreEqual(clone.UpdateDate, item.UpdateDate);
Assert.AreEqual(clone.AdditionalData.Count, item.AdditionalData.Count);
Assert.AreEqual(clone.AdditionalData, item.AdditionalData);
Assert.AreEqual(clone.UmbracoProperties.Count, item.UmbracoProperties.Count);
for (var i = 0; i < clone.UmbracoProperties.Count; i++)
{
Assert.AreNotSame(clone.UmbracoProperties[i], item.UmbracoProperties[i]);
Assert.AreEqual(clone.UmbracoProperties[i], item.UmbracoProperties[i]);
}
//This double verifies by reflection
var allProps = clone.GetType().GetProperties();
@@ -125,12 +120,12 @@ namespace Umbraco.Tests.Models
};
item.AdditionalData.Add("test1", 3);
item.AdditionalData.Add("test2", "valuie");
item.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty()
item.AdditionalData.Add("test3", new UmbracoEntity.EntityProperty()
{
Value = "test",
PropertyEditorAlias = "TestPropertyEditor"
});
item.UmbracoProperties.Add(new UmbracoEntity.UmbracoProperty()
item.AdditionalData.Add("test4", new UmbracoEntity.EntityProperty()
{
Value = "test2",
PropertyEditorAlias = "TestPropertyEditor2"
+15
View File
@@ -1,4 +1,5 @@
using System;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Serialization;
@@ -39,6 +40,8 @@ namespace Umbraco.Tests.Models
Username = "username"
};
item.AddAllowedSection("test");
var clone = (User)item.DeepClone();
Assert.AreNotSame(clone, item);
@@ -46,6 +49,7 @@ namespace Umbraco.Tests.Models
Assert.AreNotSame(clone.UserType, item.UserType);
Assert.AreEqual(clone.UserType, item.UserType);
Assert.AreEqual(clone.AllowedSections.Count(), item.AllowedSections.Count());
//Verify normal properties with reflection
var allProps = clone.GetType().GetProperties();
@@ -53,6 +57,17 @@ namespace Umbraco.Tests.Models
{
Assert.AreEqual(propertyInfo.GetValue(clone, null), propertyInfo.GetValue(item, null));
}
//ensure internal collections are differet
Assert.AreNotSame(item.AddedSections, clone.AddedSections);
Assert.AreNotSame(item.RemovedSections, clone.RemovedSections);
//ensure event handlers are still wired on clone
clone.AddAllowedSection("blah");
Assert.AreEqual(1, clone.AddedSections.Count());
clone.RemoveAllowedSection("blah");
Assert.AreEqual(1, clone.RemovedSections.Count());
}
[Test]
@@ -364,7 +364,58 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
[Test]
public void Can_Update_Property_For_Macro()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current))
{
var macro = repository.Get(1);
macro.Properties.Add(new MacroProperty("new1", "New1", 3, "test"));
repository.AddOrUpdate(macro);
unitOfWork.Commit();
//Act
macro = repository.Get(1);
macro.Properties["new1"].Name = "this is a new name";
repository.AddOrUpdate(macro);
unitOfWork.Commit();
// Assert
var result = repository.Get(1);
Assert.AreEqual("new1", result.Properties.First().Alias);
Assert.AreEqual("this is a new name", result.Properties.First().Name);
}
}
[Test]
public void Can_Update_Macro_Property_Alias()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
using (var repository = new MacroRepository(unitOfWork, NullCacheProvider.Current))
{
var macro = repository.Get(1);
macro.Properties.Add(new MacroProperty("new1", "New1", 3, "test"));
repository.AddOrUpdate(macro);
unitOfWork.Commit();
//Act
macro = repository.Get(1);
macro.Properties.UpdateProperty("new1", newAlias: "newAlias");
repository.AddOrUpdate(macro);
unitOfWork.Commit();
// Assert
var result = repository.Get(1);
Assert.AreEqual("newAlias", result.Properties.First().Alias);
}
}
[TearDown]
public override void TearDown()
@@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
@@ -10,6 +11,7 @@ using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Persistence.Repositories
{
@@ -37,10 +39,13 @@ namespace Umbraco.Tests.Persistence.Repositories
var unitOfWork = provider.GetUnitOfWork();
// Act
var repository = RepositoryResolver.Current.ResolveByType<ITemplateRepository>(unitOfWork);
using (var repository = RepositoryResolver.Current.ResolveByType<ITemplateRepository>(unitOfWork))
{
// Assert
Assert.That(repository, Is.Not.Null);
}
// Assert
Assert.That(repository, Is.Not.Null);
}
[Test]
@@ -51,10 +56,13 @@ namespace Umbraco.Tests.Persistence.Repositories
var unitOfWork = provider.GetUnitOfWork();
// Act
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
{
// Assert
Assert.That(repository, Is.Not.Null);
}
// Assert
Assert.That(repository, Is.Not.Null);
}
[Test]
@@ -63,16 +71,18 @@ namespace Umbraco.Tests.Persistence.Repositories
// Arrange
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
{
// Act
var template = new Template("test-add-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
repository.AddOrUpdate(template);
unitOfWork.Commit();
// Act
var template = new Template("test-add-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
repository.AddOrUpdate(template);
unitOfWork.Commit();
//Assert
Assert.That(repository.Get("test"), Is.Not.Null);
Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True);
//Assert
Assert.That(repository.Get("test"), Is.Not.Null);
Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True);
}
}
[Test]
@@ -81,22 +91,25 @@ namespace Umbraco.Tests.Persistence.Repositories
// Arrange
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
{
// Act
var template = new Template("test-updated-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
repository.AddOrUpdate(template);
unitOfWork.Commit();
// Act
var template = new Template("test-updated-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
repository.AddOrUpdate(template);
unitOfWork.Commit();
template.Content = @"<%@ Master Language=""VB"" %>";
repository.AddOrUpdate(template);
unitOfWork.Commit();
template.Content = @"<%@ Master Language=""VB"" %>";
repository.AddOrUpdate(template);
unitOfWork.Commit();
var updated = repository.Get("test");
var updated = repository.Get("test");
// Assert
Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True);
Assert.That(updated.Content, Is.EqualTo(@"<%@ Master Language=""VB"" %>"));
}
// Assert
Assert.That(_masterPageFileSystem.FileExists("test.master"), Is.True);
Assert.That(updated.Content, Is.EqualTo(@"<%@ Master Language=""VB"" %>"));
}
[Test]
@@ -105,19 +118,64 @@ namespace Umbraco.Tests.Persistence.Repositories
// Arrange
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
{
var template = new Template("test-add-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
repository.AddOrUpdate(template);
unitOfWork.Commit();
var template = new Template("test-add-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
repository.AddOrUpdate(template);
unitOfWork.Commit();
// Act
var templates = repository.Get("test");
repository.Delete(templates);
unitOfWork.Commit();
// Act
var templates = repository.Get("test");
repository.Delete(templates);
unitOfWork.Commit();
// Assert
Assert.IsNull(repository.Get("test"));
}
// Assert
Assert.IsNull(repository.Get("test"));
}
[Test]
public void Can_Perform_Delete_When_Assigned_To_Doc()
{
// Arrange
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
var templateRepository = new TemplateRepository(unitOfWork, NullCacheProvider.Current);
var tagRepository = new TagsRepository(unitOfWork, NullCacheProvider.Current);
var contentTypeRepository = new ContentTypeRepository(unitOfWork, NullCacheProvider.Current, templateRepository);
var contentRepo = new ContentRepository(unitOfWork, NullCacheProvider.Current, contentTypeRepository, templateRepository, tagRepository, CacheHelper.CreateDisabledCacheHelper());
using (contentRepo)
{
var contentType = MockedContentTypes.CreateSimpleContentType("umbTextpage2", "Textpage");
var textpage = MockedContent.CreateSimpleContent(contentType);
contentTypeRepository.AddOrUpdate(contentType);
contentRepo.AddOrUpdate(textpage);
unitOfWork.Commit();
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
{
var template = new Template("test-add-masterpage.master", "test", "test") { Content = @"<%@ Master Language=""C#"" %>" };
repository.AddOrUpdate(template);
unitOfWork.Commit();
textpage.Template = template;
contentRepo.AddOrUpdate(textpage);
unitOfWork.Commit();
// Act
var templates = repository.Get("test");
repository.Delete(templates);
unitOfWork.Commit();
// Assert
Assert.IsNull(repository.Get("test"));
}
}
}
[Test]
@@ -126,27 +184,30 @@ namespace Umbraco.Tests.Persistence.Repositories
// Arrange
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
{
var parent = new Template("test-parent-masterpage.master", "parent", "parent") { Content = @"<%@ Master Language=""C#"" %>" };
var child = new Template("test-child-masterpage.master", "child", "child") { Content = @"<%@ Master Language=""C#"" %>" };
var baby = new Template("test-baby-masterpage.master", "baby", "baby") { Content = @"<%@ Master Language=""C#"" %>" };
child.MasterTemplateAlias = parent.Alias;
child.MasterTemplateId = new Lazy<int>(() => parent.Id);
baby.MasterTemplateAlias = child.Alias;
baby.MasterTemplateId = new Lazy<int>(() => child.Id);
repository.AddOrUpdate(parent);
repository.AddOrUpdate(child);
repository.AddOrUpdate(baby);
unitOfWork.Commit();
var parent = new Template("test-parent-masterpage.master", "parent", "parent") { Content = @"<%@ Master Language=""C#"" %>" };
var child = new Template("test-child-masterpage.master", "child", "child") { Content = @"<%@ Master Language=""C#"" %>" };
var baby = new Template("test-baby-masterpage.master", "baby", "baby") { Content = @"<%@ Master Language=""C#"" %>" };
child.MasterTemplateAlias = parent.Alias;
child.MasterTemplateId = new Lazy<int>(() => parent.Id);
baby.MasterTemplateAlias = child.Alias;
baby.MasterTemplateId = new Lazy<int>(() => child.Id);
repository.AddOrUpdate(parent);
repository.AddOrUpdate(child);
repository.AddOrUpdate(baby);
unitOfWork.Commit();
// Act
var templates = repository.Get("parent");
repository.Delete(templates);
unitOfWork.Commit();
// Act
var templates = repository.Get("parent");
repository.Delete(templates);
unitOfWork.Commit();
// Assert
Assert.IsNull(repository.Get("test"));
}
// Assert
Assert.IsNull(repository.Get("test"));
}
[Test]
@@ -155,66 +216,69 @@ namespace Umbraco.Tests.Persistence.Repositories
// Arrange
var provider = new PetaPocoUnitOfWorkProvider();
var unitOfWork = provider.GetUnitOfWork();
var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem);
using (var repository = new TemplateRepository(unitOfWork, NullCacheProvider.Current, _masterPageFileSystem, _viewsFileSystem))
{
var parent = new Template("test-parent-masterpage.master", "parent", "parent") { Content = @"<%@ Master Language=""C#"" %>" };
var child1 = new Template("test-child1-masterpage.master", "child1", "child1") { Content = @"<%@ Master Language=""C#"" %>" };
var toddler1 = new Template("test-toddler1-masterpage.master", "toddler1", "toddler1") { Content = @"<%@ Master Language=""C#"" %>" };
var toddler2 = new Template("test-toddler2-masterpage.master", "toddler2", "toddler2") { Content = @"<%@ Master Language=""C#"" %>" };
var baby1 = new Template("test-baby1-masterpage.master", "baby1", "baby1") { Content = @"<%@ Master Language=""C#"" %>" };
var child2 = new Template("test-child2-masterpage.master", "child2", "child2") { Content = @"<%@ Master Language=""C#"" %>" };
var toddler3 = new Template("test-toddler3-masterpage.master", "toddler3", "toddler3") { Content = @"<%@ Master Language=""C#"" %>" };
var toddler4 = new Template("test-toddler4-masterpage.master", "toddler4", "toddler4") { Content = @"<%@ Master Language=""C#"" %>" };
var baby2 = new Template("test-baby2-masterpage.master", "baby2", "baby2") { Content = @"<%@ Master Language=""C#"" %>" };
child1.MasterTemplateAlias = parent.Alias;
child1.MasterTemplateId = new Lazy<int>(() => parent.Id);
child2.MasterTemplateAlias = parent.Alias;
child2.MasterTemplateId = new Lazy<int>(() => parent.Id);
toddler1.MasterTemplateAlias = child1.Alias;
toddler1.MasterTemplateId = new Lazy<int>(() => child1.Id);
toddler2.MasterTemplateAlias = child1.Alias;
toddler2.MasterTemplateId = new Lazy<int>(() => child1.Id);
toddler3.MasterTemplateAlias = child2.Alias;
toddler3.MasterTemplateId = new Lazy<int>(() => child2.Id);
toddler4.MasterTemplateAlias = child2.Alias;
toddler4.MasterTemplateId = new Lazy<int>(() => child2.Id);
baby1.MasterTemplateAlias = toddler2.Alias;
baby1.MasterTemplateId = new Lazy<int>(() => toddler2.Id);
baby2.MasterTemplateAlias = toddler4.Alias;
baby2.MasterTemplateId = new Lazy<int>(() => toddler4.Id);
repository.AddOrUpdate(parent);
repository.AddOrUpdate(child1);
repository.AddOrUpdate(child2);
repository.AddOrUpdate(toddler1);
repository.AddOrUpdate(toddler2);
repository.AddOrUpdate(toddler3);
repository.AddOrUpdate(toddler4);
repository.AddOrUpdate(baby1);
repository.AddOrUpdate(baby2);
unitOfWork.Commit();
// Act
var rootNode = repository.GetTemplateNode("parent");
// Assert
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "parent"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "child1"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "child2"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler1"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler2"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler3"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler4"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby1"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby2"));
}
var parent = new Template("test-parent-masterpage.master", "parent", "parent") { Content = @"<%@ Master Language=""C#"" %>" };
var child1 = new Template("test-child1-masterpage.master", "child1", "child1") { Content = @"<%@ Master Language=""C#"" %>" };
var toddler1 = new Template("test-toddler1-masterpage.master", "toddler1", "toddler1") { Content = @"<%@ Master Language=""C#"" %>" };
var toddler2 = new Template("test-toddler2-masterpage.master", "toddler2", "toddler2") { Content = @"<%@ Master Language=""C#"" %>" };
var baby1 = new Template("test-baby1-masterpage.master", "baby1", "baby1") { Content = @"<%@ Master Language=""C#"" %>" };
var child2 = new Template("test-child2-masterpage.master", "child2", "child2") { Content = @"<%@ Master Language=""C#"" %>" };
var toddler3 = new Template("test-toddler3-masterpage.master", "toddler3", "toddler3") { Content = @"<%@ Master Language=""C#"" %>" };
var toddler4 = new Template("test-toddler4-masterpage.master", "toddler4", "toddler4") { Content = @"<%@ Master Language=""C#"" %>" };
var baby2 = new Template("test-baby2-masterpage.master", "baby2", "baby2") { Content = @"<%@ Master Language=""C#"" %>" };
child1.MasterTemplateAlias = parent.Alias;
child1.MasterTemplateId = new Lazy<int>(() => parent.Id);
child2.MasterTemplateAlias = parent.Alias;
child2.MasterTemplateId = new Lazy<int>(() => parent.Id);
toddler1.MasterTemplateAlias = child1.Alias;
toddler1.MasterTemplateId = new Lazy<int>(() => child1.Id);
toddler2.MasterTemplateAlias = child1.Alias;
toddler2.MasterTemplateId = new Lazy<int>(() => child1.Id);
toddler3.MasterTemplateAlias = child2.Alias;
toddler3.MasterTemplateId = new Lazy<int>(() => child2.Id);
toddler4.MasterTemplateAlias = child2.Alias;
toddler4.MasterTemplateId = new Lazy<int>(() => child2.Id);
baby1.MasterTemplateAlias = toddler2.Alias;
baby1.MasterTemplateId = new Lazy<int>(() => toddler2.Id);
baby2.MasterTemplateAlias = toddler4.Alias;
baby2.MasterTemplateId = new Lazy<int>(() => toddler4.Id);
repository.AddOrUpdate(parent);
repository.AddOrUpdate(child1);
repository.AddOrUpdate(child2);
repository.AddOrUpdate(toddler1);
repository.AddOrUpdate(toddler2);
repository.AddOrUpdate(toddler3);
repository.AddOrUpdate(toddler4);
repository.AddOrUpdate(baby1);
repository.AddOrUpdate(baby2);
unitOfWork.Commit();
// Act
var rootNode = repository.GetTemplateNode("parent");
// Assert
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "parent"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "child1"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "child2"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler1"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler2"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler3"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "toddler4"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby1"));
Assert.IsNotNull(repository.FindTemplateInTree(rootNode, "baby2"));
}
//[Test]
@@ -32,7 +32,7 @@ namespace Umbraco.Tests.Services
{
var service = ServiceContext.EntityService;
var entities = service.GetAll(UmbracoObjectTypes.Document);
var entities = service.GetAll(UmbracoObjectTypes.Document).ToArray();
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(4));
@@ -45,7 +45,7 @@ namespace Umbraco.Tests.Services
var service = ServiceContext.EntityService;
var objectTypeId = new Guid(Constants.ObjectTypes.Document);
var entities = service.GetAll(objectTypeId);
var entities = service.GetAll(objectTypeId).ToArray();
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(4));
@@ -57,7 +57,7 @@ namespace Umbraco.Tests.Services
{
var service = ServiceContext.EntityService;
var entities = service.GetAll<IContent>();
var entities = service.GetAll<IContent>().ToArray();
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(4));
@@ -69,7 +69,7 @@ namespace Umbraco.Tests.Services
{
var service = ServiceContext.EntityService;
var entities = service.GetChildren(-1, UmbracoObjectTypes.Document);
var entities = service.GetChildren(-1, UmbracoObjectTypes.Document).ToArray();
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(1));
@@ -92,7 +92,7 @@ namespace Umbraco.Tests.Services
{
var service = ServiceContext.EntityService;
var entities = service.GetAll(UmbracoObjectTypes.DocumentType);
var entities = service.GetAll(UmbracoObjectTypes.DocumentType).ToArray();
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(1));
@@ -104,7 +104,7 @@ namespace Umbraco.Tests.Services
var service = ServiceContext.EntityService;
var objectTypeId = new Guid(Constants.ObjectTypes.DocumentType);
var entities = service.GetAll(objectTypeId);
var entities = service.GetAll(objectTypeId).ToArray();
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(1));
@@ -115,7 +115,7 @@ namespace Umbraco.Tests.Services
{
var service = ServiceContext.EntityService;
var entities = service.GetAll<IContentType>();
var entities = service.GetAll<IContentType>().ToArray();
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(1));
@@ -126,16 +126,16 @@ namespace Umbraco.Tests.Services
{
var service = ServiceContext.EntityService;
var entities = service.GetAll(UmbracoObjectTypes.Media);
var entities = service.GetAll(UmbracoObjectTypes.Media).ToArray();
Assert.That(entities.Any(), Is.True);
Assert.That(entities.Count(), Is.EqualTo(3));
//Assert.That(entities.Any(x => ((UmbracoEntity)x).UmbracoFile != string.Empty), Is.True);
Assert.That(
entities.Any(
x =>
((UmbracoEntity) x).UmbracoProperties.Any(
y => y.PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias)), Is.True);
x.AdditionalData.Any(y => y.Value is UmbracoEntity.EntityProperty
&& ((UmbracoEntity.EntityProperty)y.Value).PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias)), Is.True);
}
private static bool _isSetup = false;
@@ -150,6 +150,40 @@ namespace Umbraco.Tests.Services
}
[Test]
public void Can_Add_And_Remove_Properties()
{
var macroService = ServiceContext.MacroService;
var macro = new Macro("test", "Test", scriptPath: "~/Views/MacroPartials/Test.cshtml", cacheDuration: 1234);
//adds some properties
macro.Properties.Add(new MacroProperty("blah1", "Blah1", 0, "blah1"));
macro.Properties.Add(new MacroProperty("blah2", "Blah2", 0, "blah2"));
macro.Properties.Add(new MacroProperty("blah3", "Blah3", 0, "blah3"));
macro.Properties.Add(new MacroProperty("blah4", "Blah4", 0, "blah4"));
macroService.Save(macro);
var result1 = macroService.GetById(macro.Id);
Assert.AreEqual(4, result1.Properties.Count());
//simulate clearing the sections
foreach (var s in result1.Properties.ToArray())
{
result1.Properties.Remove(s.Alias);
}
//now just re-add a couple
result1.Properties.Add(new MacroProperty("blah3", "Blah3", 0, "blah3"));
result1.Properties.Add(new MacroProperty("blah4", "Blah4", 0, "blah4"));
macroService.Save(result1);
//assert
//re-get
result1 = macroService.GetById(result1.Id);
Assert.AreEqual(2, result1.Properties.Count());
}
//[Test]
//public void Can_Get_Many_By_Alias()
//{
@@ -390,6 +390,41 @@ namespace Umbraco.Tests.Services
Assert.That(user.DefaultPermissions, Is.EqualTo(userType.Permissions));
}
[Test]
public void Can_Add_And_Remove_Sections_From_User()
{
var userType = ServiceContext.UserService.GetUserTypeByAlias("admin");
var user1 = ServiceContext.UserService.CreateUserWithIdentity("test1", "test1@test.com", userType);
//adds some allowed sections
user1.AddAllowedSection("test1");
user1.AddAllowedSection("test2");
user1.AddAllowedSection("test3");
user1.AddAllowedSection("test4");
ServiceContext.UserService.Save(user1);
var result1 = ServiceContext.UserService.GetUserById((int)user1.Id);
Assert.AreEqual(4, result1.AllowedSections.Count());
//simulate clearing the sections
foreach (var s in user1.AllowedSections)
{
result1.RemoveAllowedSection(s);
}
//now just re-add a couple
result1.AddAllowedSection("test3");
result1.AddAllowedSection("test4");
ServiceContext.UserService.Save(result1);
//assert
//re-get
result1 = ServiceContext.UserService.GetUserById((int)user1.Id);
Assert.AreEqual(2, result1.AllowedSections.Count());
}
[Test]
public void Can_Remove_Section_From_All_Assigned_Users()
{
+2
View File
@@ -178,6 +178,7 @@
<Compile Include="Migrations\Stubs\SixZeroMigration2.cs" />
<Compile Include="Migrations\Upgrades\ValidateV7TagsUpgradeTest.cs" />
<Compile Include="MockTests.cs" />
<Compile Include="Models\MacroTests.cs" />
<Compile Include="Models\Mapping\AutoMapperTests.cs" />
<Compile Include="Models\Collections\Item.cs" />
<Compile Include="Models\Collections\OrderItem.cs" />
@@ -190,6 +191,7 @@
<Compile Include="Models\LanguageTests.cs" />
<Compile Include="Models\MemberGroupTests.cs" />
<Compile Include="Models\MemberTests.cs" />
<Compile Include="Models\PreValueCollectionTests.cs" />
<Compile Include="Models\PropertyGroupTests.cs" />
<Compile Include="Models\PropertyTypeTests.cs" />
<Compile Include="Models\RelationTests.cs" />
+5 -2
View File
@@ -11,7 +11,7 @@ module.exports = function (grunt) {
grunt.registerTask('watch-html', ['copy:views', 'copy:vs']);
grunt.registerTask('watch-packages', ['copy:packages']);
grunt.registerTask('watch-installer', ['concat:install', 'concat:installJs', 'copy:installer', 'copy:vs']);
grunt.registerTask('watch-tuning', ['copy:tuning', 'concat:tuningJs', 'concat:tuningLess']);
grunt.registerTask('watch-tuning', ['copy:tuning', 'concat:tuningJs', 'concat:tuningLess', 'copy:vs']);
grunt.registerTask('watch-test', ['jshint:dev', 'karma:unit']);
//triggered from grunt dev or grunt
@@ -115,6 +115,7 @@ module.exports = function (grunt) {
},
// Copies over the files downloaded by bower
bower: {
files: [
{
@@ -133,6 +134,7 @@ module.exports = function (grunt) {
}
]
},
*/
installer: {
@@ -170,7 +172,8 @@ module.exports = function (grunt) {
{ dest: '<%= vsdir %>/assets', src: '**', expand: true, cwd: '<%= distdir %>/assets' },
{ dest: '<%= vsdir %>/js', src: '**', expand: true, cwd: '<%= distdir %>/js' },
{ dest: '<%= vsdir %>/lib', src: '**', expand: true, cwd: '<%= distdir %>/lib' },
{ dest: '<%= vsdir %>/views', src: '**', expand: true, cwd: '<%= distdir %>/views' }
{ dest: '<%= vsdir %>/views', src: '**', expand: true, cwd: '<%= distdir %>/views' },
{ dest: '<%= vsdir %>/preview', src: '**', expand: true, cwd: '<%= distdir %>/preview' }
]
},
File diff suppressed because one or more lines are too long
@@ -335,6 +335,10 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
this._modal.push(dialog);
return dialog;
},
rootScope : function(){
return getRootScope();
},
closeModalWindow: function(rVal) {
//get our angular navigation service
@@ -204,7 +204,7 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
}
else {
scope.eventhandler.one("treeLoaded", function(e, args) {
doLoad(args.tree);
doLoad(args.tree.root);
});
}
}
@@ -376,4 +376,4 @@ function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificat
};
}
angular.module("umbraco.directives").directive('umbTree', umbTreeDirective);
angular.module("umbraco.directives").directive('umbTree', umbTreeDirective);
@@ -12,7 +12,7 @@
* Another thing this directive does is to ensure that any .control-group that contains form elements that are invalid will
* be marked with the 'error' css class. This ensures that labels included in that control group are styled correctly.
**/
function valFormManager(serverValidationManager, $rootScope, $log, $timeout, notificationsService) {
function valFormManager(serverValidationManager, $rootScope, $log, $timeout, notificationsService, eventsService) {
return {
require: "form",
restrict: "A",
@@ -57,29 +57,33 @@ function valFormManager(serverValidationManager, $rootScope, $log, $timeout, not
formCtrl.$setPristine();
});
//if we wish to turn of the unsaved changes confirmation msg
//this is the place to do it
var locationEvent = $rootScope.$on('$locationChangeStart', function (event, nextLocation, currentLocation) {
if (!formCtrl.$dirty) {
return;
}
var path = nextLocation.split("#")[1];
if (path) {
if (path.indexOf("%253") || path.indexOf("%252")) {
path = decodeURIComponent(path);
}
//This handles the 'unsaved changes' dialog which is triggered when a route is attempting to be changed but
// the form has pending changes
var locationEvent = $rootScope.$on('$locationChangeStart', function(event, nextLocation, currentLocation) {
if (!formCtrl.$dirty) {
return;
}
if(!notificationsService.hasView()){
var msg = { view: "confirmroutechange", args: { path: path, listener: locationEvent } };
notificationsService.add(msg);
}
event.preventDefault();
var path = nextLocation.split("#")[1];
if (path) {
if (path.indexOf("%253") || path.indexOf("%252")) {
path = decodeURIComponent(path);
}
if (!notificationsService.hasView()) {
var msg = { view: "confirmroutechange", args: { path: path, listener: locationEvent } };
notificationsService.add(msg);
}
//prevent the route!
event.preventDefault();
//raise an event
eventsService.emit("valFormManager.pendingChanges", true);
}
});
//Ensure to remove the event handler when this instance is destroyted
scope.$on('$destroy', function() {
if(locationEvent){
locationEvent();
@@ -55,7 +55,22 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
umbRequestHelper.getApiUrl(
"authenticationApiBaseUrl",
"IsAuthenticated")),
'Server call failed for checking authentication');
{
success: function (data, status, headers, config) {
//if the response is false, they are not logged in so return a rejection
if (data === false || data === "false") {
return $q.reject('User is not logged in');
}
return data;
},
error: function (data, status, headers, config) {
return {
errorMsg: 'Server call failed for checking authentication',
data: data,
status: status
};
}
});
},
/** Gets the user's remaining seconds before their login times out */
@@ -81,7 +81,7 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) {
umbRequestHelper.getApiUrl(
"dataTypeApiBaseUrl",
"GetAll")),
'Failed to retreive data');
'Failed to retrieve data');
},
/**
@@ -118,7 +118,7 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) {
umbRequestHelper.getApiUrl(
"dataTypeApiBaseUrl",
"GetEmpty")),
'Failed to retreive data for empty datatype');
'Failed to retrieve data for empty datatype');
},
/**
* @ngdoc method
@@ -146,7 +146,7 @@ function entityResource($q, $http, umbRequestHelper) {
"entityApiBaseUrl",
"GetByIds",
query)),
'Failed to retreive entity data for ids ' + ids);
'Failed to retrieve entity data for ids ' + ids);
},
/**
@@ -192,7 +192,7 @@ function entityResource($q, $http, umbRequestHelper) {
"entityApiBaseUrl",
"GetAll",
query)),
'Failed to retreive entity data for type ' + type);
'Failed to retrieve entity data for type ' + type);
},
/**
@@ -215,7 +215,7 @@ function entityResource($q, $http, umbRequestHelper) {
"entityApiBaseUrl",
"GetAncestors",
[{id: id}, {type: type}])),
'Failed to retreive ancestor data for id ' + id);
'Failed to retrieve ancestor data for id ' + id);
},
/**
@@ -238,7 +238,7 @@ function entityResource($q, $http, umbRequestHelper) {
"entityApiBaseUrl",
"GetChildren",
[{ id: id }, { type: type }])),
'Failed to retreive child data for id ' + id);
'Failed to retrieve child data for id ' + id);
},
/**
@@ -271,7 +271,7 @@ function entityResource($q, $http, umbRequestHelper) {
"entityApiBaseUrl",
"Search",
[{ query: query }, {type: type}])),
'Failed to retreive entity data for query ' + query);
'Failed to retrieve entity data for query ' + query);
},
@@ -304,7 +304,7 @@ function entityResource($q, $http, umbRequestHelper) {
"entityApiBaseUrl",
"SearchAll",
[{ query: query }])),
'Failed to retreive entity data for query ' + query);
'Failed to retrieve entity data for query ' + query);
}
};
@@ -99,7 +99,7 @@ function logResource($q, $http, umbRequestHelper) {
"logApiBaseUrl",
"GetLog",
[{ logtype: type, sinceDate: since }])),
'Failed to retreive user data for id ' + id);
'Failed to retrieve user data for id ' + id);
}
};
}
@@ -133,7 +133,7 @@ function memberResource($q, $http, umbDataFormatter, umbRequestHelper) {
umbRequestHelper.getApiUrl(
"memberApiBaseUrl",
"GetEmpty")),
'Failed to retreive data for empty member item type ' + alias);
'Failed to retrieve data for empty member item type ' + alias);
}
},
@@ -93,7 +93,7 @@ function stylesheetResource($q, $http, umbRequestHelper) {
"stylesheetApiBaseUrl",
"GetRulesByName",
[{ name: name }])),
'Failed to retreive stylesheets ');
'Failed to retrieve stylesheets ');
}
};
}
@@ -72,7 +72,7 @@ function treeResource($q, $http, umbRequestHelper) {
return umbRequestHelper.resourcePromise(
$http.get(getTreeNodesUrl(options.node)),
'Failed to retreive data for child nodes ' + options.node.nodeId);
'Failed to retrieve data for child nodes ' + options.node.nodeId);
}
};
}
@@ -178,9 +178,8 @@ angular.module('umbraco.services')
* @methodOf umbraco.services.assetsService
*
* @description
* Injects a collection of files, this can be a mixed collection of css and js files, the loader will determine how to load them
* Injects a collection of files, this can be ONLY js files
*
* **Warning:** if the collection of files contains a .css file, you will in some cases not receive a resolved promise, it is therefore prefered to use the individual loadCss and loadJs methods
*
* @param {Array} pathArray string array of paths to the files to load
* @param {Scope} scope optional scope to pass into the loader
@@ -114,11 +114,11 @@ function cropperHelper(umbRequestHelper, $http) {
crop.x2 = x2_px / image.width;
crop.y2 = y2_px / image.height;
_.forEach(crop, function(coord){
if(coord < 0){
coord = 0;
for(var coord in crop){
if(crop[coord] < 0){
crop[coord] = 0;
}
});
}
return crop;
},
@@ -31,476 +31,492 @@
angular.module('umbraco.services')
.factory('dialogService', function ($rootScope, $compile, $http, $timeout, $q, $templateCache, appState, eventsService) {
var dialogs = [];
/** Internal method that removes all dialogs */
function removeAllDialogs(args) {
for (var i = 0; i < dialogs.length; i++) {
var dialog = dialogs[i];
dialog.close(args);
dialogs.splice(i, 1);
}
}
var dialogs = [];
/** Internal method that handles opening all dialogs */
function openDialog(options) {
var defaults = {
container: $("body"),
animation: "fade",
modalClass: "umb-modal",
width: "100%",
inline: false,
iframe: false,
show: true,
template: "views/common/notfound.html",
callback: undefined,
closeCallback: undefined,
element: undefined,
//this allows us to pass in data to the dialog if required which can be used to configure the dialog
//and re-use it for different purposes. It will set on to the $scope.dialogData if it is defined.
dialogData: undefined
};
var dialog = angular.extend(defaults, options);
var scope = options.scope || $rootScope.$new();
//Modal dom obj and unique id
dialog.element = $('<div ng-swipe-right="swipeHide($event)" data-backdrop="false"></div>');
var id = dialog.template.replace('.html', '').replace('.aspx', '').replace(/[\/|\.|:\&\?\=]/g, "-") + '-' + scope.$id;
/** Internal method that removes all dialogs */
function removeAllDialogs(args) {
for (var i = 0; i < dialogs.length; i++) {
var dialog = dialogs[i];
dialog.close(args);
dialogs.splice(i, 1);
}
}
if (options.inline) {
dialog.animation = "";
}
else {
dialog.element.addClass("modal");
dialog.element.addClass("hide");
}
/** Internal method that handles opening all dialogs */
function openDialog(options) {
var defaults = {
container: $("body"),
animation: "fade",
modalClass: "umb-modal",
width: "100%",
inline: false,
iframe: false,
show: true,
template: "views/common/notfound.html",
callback: undefined,
closeCallback: undefined,
element: undefined,
//this allows us to pass in data to the dialog if required which can be used to configure the dialog
//and re-use it for different purposes. It will set on to the $scope.dialogData if it is defined.
dialogData: undefined
};
//set the id and add classes
dialog.element
.attr('id', id)
.addClass(dialog.animation)
.addClass(dialog.modalClass);
var dialog = angular.extend(defaults, options);
var scope = options.scope || $rootScope.$new();
//push the modal into the global modal collection
//we halt the .push because a link click will trigger a closeAll right away
$timeout(function () {
dialogs.push(dialog);
}, 500);
//Modal dom obj and unique id
dialog.element = $('<div ng-swipe-right="swipeHide($event)" data-backdrop="false"></div>');
var id = dialog.template.replace('.html', '').replace('.aspx', '').replace(/[\/|\.|:\&\?\=]/g, "-") + '-' + scope.$id;
dialog.close = function(data) {
if (dialog.closeCallback) {
dialog.closeCallback(data);
}
if (options.inline) {
dialog.animation = "";
}
else {
dialog.element.addClass("modal");
dialog.element.addClass("hide");
}
if(dialog.element){
dialog.element.modal('hide');
//set the id and add classes
dialog.element
.attr('id', id)
.addClass(dialog.animation)
.addClass(dialog.modalClass);
//this is not entirely enough since the damn
//webforms scriploader still complains
if(dialog.iframe){
//push the modal into the global modal collection
//we halt the .push because a link click will trigger a closeAll right away
$timeout(function () {
dialogs.push(dialog);
}, 500);
dialog.close = function (data) {
if (dialog.closeCallback) {
dialog.closeCallback(data);
}
if (dialog.element) {
dialog.element.modal('hide');
//this is not entirely enough since the damn
//webforms scriploader still complains
if (dialog.iframe) {
dialog.element.find("iframe").attr("src", "about:blank");
$timeout(function(){
dialog.element.remove();
}, 1000);
}else{
$timeout(function () {
//we need to do more than just remove the element, this will not destroy the
// scope in angular 1.1x, in angular 1.2x this is taken care of but if we dont
// take care of this ourselves we have memory leaks.
dialog.element.remove();
dialog.scope.$destroy();
}, 1000);
} else {
//we need to do more than just remove the element, this will not destroy the
// scope in angular 1.1x, in angular 1.2x this is taken care of but if we dont
// take care of this ourselves we have memory leaks.
dialog.element.remove();
}
}
};
dialog.scope.$destroy();
}
}
};
//if iframe is enabled, inject that instead of a template
if (dialog.iframe) {
var html = $("<iframe src='" + dialog.template + "' class='auto-expand' style='border: none; width: 100%; height: 100%;'></iframe>");
dialog.element.html(html);
//append to body or whatever element is passed in as options.containerElement
dialog.container.append(dialog.element);
//if iframe is enabled, inject that instead of a template
if (dialog.iframe) {
var html = $("<iframe src='" + dialog.template + "' class='auto-expand' style='border: none; width: 100%; height: 100%;'></iframe>");
dialog.element.html(html);
// Compile modal content
$timeout(function () {
$compile(dialog.element)(dialog.scope);
});
//append to body or whatever element is passed in as options.containerElement
dialog.container.append(dialog.element);
dialog.element.css("width", dialog.width);
// Compile modal content
$timeout(function () {
$compile(dialog.element)(dialog.scope);
});
//Autoshow
if (dialog.show) {
dialog.element.modal('show');
}
dialog.element.css("width", dialog.width);
dialog.scope = scope;
return dialog;
}
else {
//We need to load the template with an httpget and once it's loaded we'll compile and assign the result to the container
// object. However since the result could be a promise or just data we need to use a $q.when. We still need to return the
// $modal object so we'll actually return the modal object synchronously without waiting for the promise. Otherwise this openDialog
// method will always need to return a promise which gets nasty because of promises in promises plus the result just needs a reference
// to the $modal object which will not change (only it's contents will change).
$q.when($templateCache.get(dialog.template) || $http.get(dialog.template, { cache: true }).then(function(res) { return res.data; }))
.then(function onSuccess(template) {
//Autoshow
if (dialog.show) {
dialog.element.modal('show');
}
// Build modal object
dialog.element.html(template);
dialog.scope = scope;
return dialog;
}
else {
//append to body or other container element
dialog.container.append(dialog.element);
// Compile modal content
$timeout(function() {
$compile(dialog.element)(scope);
});
//We need to load the template with an httpget and once it's loaded we'll compile and assign the result to the container
// object. However since the result could be a promise or just data we need to use a $q.when. We still need to return the
// $modal object so we'll actually return the modal object synchronously without waiting for the promise. Otherwise this openDialog
// method will always need to return a promise which gets nasty because of promises in promises plus the result just needs a reference
// to the $modal object which will not change (only it's contents will change).
$q.when($templateCache.get(dialog.template) || $http.get(dialog.template, { cache: true }).then(function (res) { return res.data; }))
.then(function onSuccess(template) {
scope.dialogOptions = dialog;
//Scope to handle data from the modal form
scope.dialogData = dialog.dialogData ? dialog.dialogData : {};
scope.dialogData.selection = [];
// Build modal object
dialog.element.html(template);
// Provide scope display functions
//this passes the modal to the current scope
scope.$modal = function(name) {
dialog.element.modal(name);
};
//append to body or other container element
dialog.container.append(dialog.element);
scope.swipeHide = function(e){
if(appState.getGlobalState("touchDevice")){
// Compile modal content
$timeout(function () {
$compile(dialog.element)(scope);
});
scope.dialogOptions = dialog;
//Scope to handle data from the modal form
scope.dialogData = dialog.dialogData ? dialog.dialogData : {};
scope.dialogData.selection = [];
// Provide scope display functions
//this passes the modal to the current scope
scope.$modal = function (name) {
dialog.element.modal(name);
};
scope.swipeHide = function (e) {
if (appState.getGlobalState("touchDevice")) {
var selection = window.getSelection();
if(selection.type !== "Range"){
scope.hide();
if (selection.type !== "Range") {
scope.hide();
}
}
};
};
scope.hide = function() {
dialog.element.modal('hide');
scope.hide = function () {
dialog.element.modal('hide');
dialog.element.remove();
$("#" + dialog.element.attr("id")).remove();
};
dialog.element.remove();
$("#" + dialog.element.attr("id")).remove();
//we need to do more than just remove the element, this will not destroy the
// scope in angular 1.1x, in angular 1.2x this is taken care of but if we dont
// take care of this ourselves we have memory leaks.
dialog.scope.$destroy();
};
//basic events for submitting and closing
scope.submit = function(data) {
if (dialog.callback) {
dialog.callback(data);
}
//basic events for submitting and closing
scope.submit = function (data) {
if (dialog.callback) {
dialog.callback(data);
}
dialog.element.modal('hide');
dialog.element.remove();
$("#" + dialog.element.attr("id")).remove();
};
dialog.element.modal('hide');
dialog.element.remove();
$("#" + dialog.element.attr("id")).remove();
//we need to do more than just remove the element, this will not destroy the
// scope in angular 1.1x, in angular 1.2x this is taken care of but if we dont
// take care of this ourselves we have memory leaks.
dialog.scope.$destroy();
};
scope.close = function(data) {
scope.close = function (data) {
dialog.close(data);
};
};
scope.show = function() {
dialog.element.modal('show');
};
scope.show = function () {
dialog.element.modal('show');
};
scope.select = function(item) {
scope.select = function (item) {
var i = scope.dialogData.selection.indexOf(item);
if (i < 0) {
scope.dialogData.selection.push(item);
}else{
if (i < 0) {
scope.dialogData.selection.push(item);
} else {
scope.dialogData.selection.splice(i, 1);
}
};
}
};
scope.dismiss = scope.hide;
scope.dismiss = scope.hide;
// Emit modal events
angular.forEach(['show', 'shown', 'hide', 'hidden'], function(name) {
dialog.element.on(name, function(ev) {
scope.$emit('modal-' + name, ev);
});
});
// Emit modal events
angular.forEach(['show', 'shown', 'hide', 'hidden'], function (name) {
dialog.element.on(name, function (ev) {
scope.$emit('modal-' + name, ev);
});
});
// Support autofocus attribute
dialog.element.on('shown', function(event) {
$('input[autofocus]', dialog.element).first().trigger('focus');
});
// Support autofocus attribute
dialog.element.on('shown', function (event) {
$('input[autofocus]', dialog.element).first().trigger('focus');
});
//Autoshow
if (dialog.show) {
dialog.element.modal('show');
}
//Autoshow
if (dialog.show) {
dialog.element.modal('show');
}
dialog.scope = scope;
});
//Return the modal object outside of the promise!
return dialog;
}
}
dialog.scope = scope;
});
/** Handles the closeDialogs event */
eventsService.on("app.closeDialogs", function (evt, args) {
removeAllDialogs(args);
});
//Return the modal object outside of the promise!
return dialog;
}
}
return {
/**
* @ngdoc method
* @name umbraco.services.dialogService#open
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a modal rendering a given template url.
*
* @param {Object} options rendering options
* @param {DomElement} options.container the DOM element to inject the modal into, by default set to body
* @param {Function} options.callback function called when the modal is submitted
* @param {String} options.template the url of the template
* @param {String} options.animation animation csss class, by default set to "fade"
* @param {String} options.modalClass modal css class, by default "umb-modal"
* @param {Bool} options.show show the modal instantly
* @param {Object} options.scope scope to attach the modal to, by default rootScope.new()
* @param {Bool} options.iframe load template in an iframe, only needed for serverside templates
* @param {Int} options.width set a width on the modal, only needed for iframes
* @param {Bool} options.inline strips the modal from any animation and wrappers, used when you want to inject a dialog into an existing container
* @returns {Object} modal object
*/
open: function (options) {
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#close
* @methodOf umbraco.services.dialogService
*
* @description
* Closes a specific dialog
* @param {Object} dialog the dialog object to close
* @param {Object} args if specified this object will be sent to any callbacks registered on the dialogs.
*/
close: function (dialog, args) {
if(dialog){
dialog.close(args);
}
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#closeAll
* @methodOf umbraco.services.dialogService
*
* @description
* Closes all dialogs
* @param {Object} args if specified this object will be sent to any callbacks registered on the dialogs.
*/
closeAll: function(args) {
removeAllDialogs(args);
},
/** Handles the closeDialogs event */
eventsService.on("app.closeDialogs", function (evt, args) {
removeAllDialogs(args);
});
/**
* @ngdoc method
* @name umbraco.services.dialogService#mediaPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a media picker in a modal, the callback returns an array of selected media items
* @param {Object} options mediapicker dialog options object
* @param {$scope} options.scope dialog scope
* @param {Boolean} options.onlyImages Only display files that have an image file-extension
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
mediaPicker: function (options) {
return {
/**
* @ngdoc method
* @name umbraco.services.dialogService#open
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a modal rendering a given template url.
*
* @param {Object} options rendering options
* @param {DomElement} options.container the DOM element to inject the modal into, by default set to body
* @param {Function} options.callback function called when the modal is submitted
* @param {String} options.template the url of the template
* @param {String} options.animation animation csss class, by default set to "fade"
* @param {String} options.modalClass modal css class, by default "umb-modal"
* @param {Bool} options.show show the modal instantly
* @param {Object} options.scope scope to attach the modal to, by default rootScope.new()
* @param {Bool} options.iframe load template in an iframe, only needed for serverside templates
* @param {Int} options.width set a width on the modal, only needed for iframes
* @param {Bool} options.inline strips the modal from any animation and wrappers, used when you want to inject a dialog into an existing container
* @returns {Object} modal object
*/
open: function (options) {
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#close
* @methodOf umbraco.services.dialogService
*
* @description
* Closes a specific dialog
* @param {Object} dialog the dialog object to close
* @param {Object} args if specified this object will be sent to any callbacks registered on the dialogs.
*/
close: function (dialog, args) {
if (dialog) {
dialog.close(args);
}
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#closeAll
* @methodOf umbraco.services.dialogService
*
* @description
* Closes all dialogs
* @param {Object} args if specified this object will be sent to any callbacks registered on the dialogs.
*/
closeAll: function (args) {
removeAllDialogs(args);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#mediaPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a media picker in a modal, the callback returns an array of selected media items
* @param {Object} options mediapicker dialog options object
* @param {$scope} options.scope dialog scope
* @param {Boolean} options.onlyImages Only display files that have an image file-extension
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
mediaPicker: function (options) {
options.template = 'views/common/dialogs/mediaPicker.html';
options.show = true;
return openDialog(options);
},
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#contentPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a content picker tree in a modal, the callback returns an array of selected documents
* @param {Object} options content picker dialog options object
* @param {$scope} options.scope dialog scope
* @param {$scope} options.multipicker should the picker return one or multiple items
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
contentPicker: function (options) {
options.template = 'views/common/dialogs/contentPicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#contentPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a content picker tree in a modal, the callback returns an array of selected documents
* @param {Object} options content picker dialog options object
* @param {$scope} options.scope dialog scope
* @param {$scope} options.multipicker should the picker return one or multiple items
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
contentPicker: function (options) {
options.template = 'views/common/dialogs/contentPicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#linkPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a link picker tree in a modal, the callback returns a single link
* @param {Object} options content picker dialog options object
* @param {$scope} options.scope dialog scope
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
linkPicker: function (options) {
options.template = 'views/common/dialogs/linkPicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#linkPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a link picker tree in a modal, the callback returns a single link
* @param {Object} options content picker dialog options object
* @param {$scope} options.scope dialog scope
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
linkPicker: function (options) {
options.template = 'views/common/dialogs/linkPicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#macroPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a mcaro picker in a modal, the callback returns a object representing the macro and it's parameters
* @param {Object} options macropicker dialog options object
* @param {$scope} options.scope dialog scope
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
macroPicker: function (options) {
options.template = 'views/common/dialogs/insertmacro.html';
options.show = true;
options.modalClass = "span7 umb-modal";
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#macroPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a mcaro picker in a modal, the callback returns a object representing the macro and it's parameters
* @param {Object} options macropicker dialog options object
* @param {$scope} options.scope dialog scope
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
macroPicker: function (options) {
options.template = 'views/common/dialogs/insertmacro.html';
options.show = true;
options.modalClass = "span7 umb-modal";
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#memberPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a member picker in a modal, the callback returns a object representing the selected member
* @param {Object} options member picker dialog options object
* @param {$scope} options.scope dialog scope
* @param {$scope} options.multiPicker should the tree pick one or multiple members before returning
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
memberPicker: function (options) {
options.template = 'views/common/dialogs/memberPicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#memberGroupPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a member group picker in a modal, the callback returns a object representing the selected member
* @param {Object} options member group picker dialog options object
* @param {$scope} options.scope dialog scope
* @param {$scope} options.multiPicker should the tree pick one or multiple members before returning
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
memberGroupPicker: function (options) {
options.template = 'views/common/dialogs/memberGroupPicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#memberPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a member picker in a modal, the callback returns a object representing the selected member
* @param {Object} options member picker dialog options object
* @param {$scope} options.scope dialog scope
* @param {$scope} options.multiPicker should the tree pick one or multiple members before returning
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
memberPicker: function (options) {
options.template = 'views/common/dialogs/memberPicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#iconPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a icon picker in a modal, the callback returns a object representing the selected icon
* @param {Object} options iconpicker dialog options object
* @param {$scope} options.scope dialog scope
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
iconPicker: function (options) {
options.template = 'views/common/dialogs/iconPicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#memberGroupPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a member group picker in a modal, the callback returns a object representing the selected member
* @param {Object} options member group picker dialog options object
* @param {$scope} options.scope dialog scope
* @param {$scope} options.multiPicker should the tree pick one or multiple members before returning
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
memberGroupPicker: function (options) {
options.template = 'views/common/dialogs/memberGroupPicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#treePicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a tree picker in a modal, the callback returns a object representing the selected tree item
* @param {Object} options iconpicker dialog options object
* @param {$scope} options.scope dialog scope
* @param {$scope} options.section tree section to display
* @param {$scope} options.treeAlias specific tree to display
* @param {$scope} options.multiPicker should the tree pick one or multiple items before returning
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
treePicker: function (options) {
options.template = 'views/common/dialogs/treePicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#iconPicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a icon picker in a modal, the callback returns a object representing the selected icon
* @param {Object} options iconpicker dialog options object
* @param {$scope} options.scope dialog scope
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
iconPicker: function (options) {
options.template = 'views/common/dialogs/iconPicker.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#propertyDialog
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a dialog with a chosen property editor in, a value can be passed to the modal, and this value is returned in the callback
* @param {Object} options mediapicker dialog options object
* @param {$scope} options.scope dialog scope
* @param {Function} options.callback callback function
* @param {String} editor editor to use to edit a given value and return on callback
* @param {Object} value value sent to the property editor
* @returns {Object} modal object
*/
propertyDialog: function (options) {
options.template = 'views/common/dialogs/property.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#ysodDialog
* @methodOf umbraco.services.dialogService
* @description
* Opens a dialog to an embed dialog
*/
embedDialog: function (options) {
options.template = 'views/common/dialogs/rteembed.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#ysodDialog
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a dialog to show a custom YSOD
*/
ysodDialog: function (ysodError) {
/**
* @ngdoc method
* @name umbraco.services.dialogService#treePicker
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a tree picker in a modal, the callback returns a object representing the selected tree item
* @param {Object} options iconpicker dialog options object
* @param {$scope} options.scope dialog scope
* @param {$scope} options.section tree section to display
* @param {$scope} options.treeAlias specific tree to display
* @param {$scope} options.multiPicker should the tree pick one or multiple items before returning
* @param {Function} options.callback callback function
* @returns {Object} modal object
*/
treePicker: function (options) {
options.template = 'views/common/dialogs/treePicker.html';
options.show = true;
return openDialog(options);
},
var newScope = $rootScope.$new();
newScope.error = ysodError;
return openDialog({
modalClass: "umb-modal wide",
scope: newScope,
//callback: options.callback,
template: 'views/common/dialogs/ysod.html',
show: true
});
}
};
});
/**
* @ngdoc method
* @name umbraco.services.dialogService#propertyDialog
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a dialog with a chosen property editor in, a value can be passed to the modal, and this value is returned in the callback
* @param {Object} options mediapicker dialog options object
* @param {$scope} options.scope dialog scope
* @param {Function} options.callback callback function
* @param {String} editor editor to use to edit a given value and return on callback
* @param {Object} value value sent to the property editor
* @returns {Object} modal object
*/
propertyDialog: function (options) {
options.template = 'views/common/dialogs/property.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#ysodDialog
* @methodOf umbraco.services.dialogService
* @description
* Opens a dialog to an embed dialog
*/
embedDialog: function (options) {
options.template = 'views/common/dialogs/rteembed.html';
options.show = true;
return openDialog(options);
},
/**
* @ngdoc method
* @name umbraco.services.dialogService#ysodDialog
* @methodOf umbraco.services.dialogService
*
* @description
* Opens a dialog to show a custom YSOD
*/
ysodDialog: function (ysodError) {
var newScope = $rootScope.$new();
newScope.error = ysodError;
return openDialog({
modalClass: "umb-modal wide",
scope: newScope,
//callback: options.callback,
template: 'views/common/dialogs/ysod.html',
show: true
});
}
};
});
@@ -18,7 +18,8 @@ function eventsService($q, $rootScope) {
//there are no listeners
if (!$rootScope.$$listeners[name]) {
return [];
return;
//return [];
}
//send the event
@@ -28,18 +28,25 @@
* </pre>
*/
angular.module('umbraco.services')
.factory('historyService', function ($rootScope, $timeout, angularHelper) {
.factory('historyService', function ($rootScope, $timeout, angularHelper, eventsService) {
var nArray = [];
function add(item) {
var any = _.where(nArray, {link: item.link});
if (!item) {
return null;
}
var listWithoutThisItem = _.reject(nArray, function(i) {
return i.link === item.link;
});
//put it at the top and reassign
listWithoutThisItem.splice(0, 0, item);
nArray = listWithoutThisItem;
return nArray[0];
if(any.length === 0){
nArray.splice(0,0,item);
return nArray[0];
}
}
return {
@@ -60,7 +67,9 @@ angular.module('umbraco.services')
add: function (item) {
var icon = item.icon || "icon-file";
angularHelper.safeApply($rootScope, function () {
return add({name: item.name, icon: icon, link: item.link, time: new Date() });
var result = add({ name: item.name, icon: icon, link: item.link, time: new Date() });
eventsService.emit("historyService.add", {added: result, all: nArray});
return result;
});
},
/**
@@ -75,7 +84,8 @@ angular.module('umbraco.services')
*/
remove: function (index) {
angularHelper.safeApply($rootScope, function() {
nArray.splice(index, 1);
var result = nArray.splice(index, 1);
eventsService.emit("historyService.remove", { removed: result, all: nArray });
});
},
@@ -89,21 +99,11 @@ angular.module('umbraco.services')
*/
removeAll: function () {
angularHelper.safeApply($rootScope, function() {
nArray = [];
nArray = [];
eventsService.emit("historyService.removeAll");
});
},
/**
* @ngdoc property
* @name umbraco.services.historyService#current
* @propertyOf umbraco.services.historyService
*
* @description
*
* @returns {Array} Array of history entries for the current user, newest items first
*/
current: nArray,
/**
* @ngdoc method
* @name umbraco.services.historyService#getCurrent
@@ -129,30 +129,79 @@ function mediaHelper(umbRequestHelper) {
_mediaFileResolvers[propertyEditorAlias] = func;
},
/**
* @ngdoc function
* @name umbraco.services.mediaHelper#resolveFileFromEntity
* @methodOf umbraco.services.mediaHelper
* @function
*
* @description
* Gets the media file url for a media entity returned with the entityResource
*
* @param {object} mediaEntity A media Entity returned from the entityResource
* @param {boolean} thumbnail Whether to return the thumbnail url or normal url
*/
resolveFileFromEntity : function(mediaEntity, thumbnail) {
if (!angular.isObject(mediaEntity.metaData)) {
throw "Cannot resolve the file url from the mediaEntity, it does not contain the required metaData";
}
var values = _.values(mediaEntity.metaData);
for (var i = 0; i < values.length; i++) {
var val = values[i];
if (angular.isObject(val) && val.PropertyEditorAlias) {
for (var resolver in _mediaFileResolvers) {
if (val.PropertyEditorAlias === resolver) {
//we need to format a property variable that coincides with how the property would be structured
// if it came from the mediaResource just to keep things slightly easier for the file resolvers.
var property = { value: val.Value };
return _mediaFileResolvers[resolver](property, mediaEntity, thumbnail);
}
}
}
}
return "";
},
/**
* @ngdoc function
* @name umbraco.services.mediaHelper#resolveFile
* @methodOf umbraco.services.mediaHelper
* @function
*
* @description
* Gets the media file url for a media object returned with the mediaResource
*
* @param {object} mediaEntity A media Entity returned from the entityResource
* @param {boolean} thumbnail Whether to return the thumbnail url or normal url
*/
/*jshint loopfunc: true */
resolveFile : function(mediaItem, thumbnail){
var _props = [];
function _iterateProps(props){
var result = null;
function iterateProps(props){
var res = null;
for(var resolver in _mediaFileResolvers) {
var property = _.find(props, function(property){ return property.editor === resolver; });
var property = _.find(props, function(prop){ return prop.editor === resolver; });
if(property){
result = _mediaFileResolvers[resolver](property, mediaItem, thumbnail);
res = _mediaFileResolvers[resolver](property, mediaItem, thumbnail);
break;
}
}
return result;
return res;
}
//we either have properties raw on the object, or spread out on tabs
var result = "";
if(mediaItem.properties){
result = _iterateProps(mediaItem.properties);
result = iterateProps(mediaItem.properties);
}else if(mediaItem.tabs){
for(var tab in mediaItem.tabs) {
if(mediaItem.tabs[tab].properties){
result = _iterateProps(mediaItem.tabs[tab].properties);
result = iterateProps(mediaItem.tabs[tab].properties);
if(result){
break;
}
@@ -164,26 +213,26 @@ function mediaHelper(umbRequestHelper) {
/*jshint loopfunc: true */
hasFilePropertyType : function(mediaItem){
function _iterateProps(props){
var result = false;
function iterateProps(props){
var res = false;
for(var resolver in _mediaFileResolvers) {
var property = _.find(props, function(property){ return property.editor === resolver; });
var property = _.find(props, function(prop){ return prop.editor === resolver; });
if(property){
result = true;
res = true;
break;
}
}
return result;
return res;
}
//we either have properties raw on the object, or spread out on tabs
var result = false;
if(mediaItem.properties){
result = _iterateProps(mediaItem.properties);
result = iterateProps(mediaItem.properties);
}else if(mediaItem.tabs){
for(var tab in mediaItem.tabs) {
if(mediaItem.tabs[tab].properties){
result = _iterateProps(mediaItem.tabs[tab].properties);
result = iterateProps(mediaItem.tabs[tab].properties);
if(result){
break;
}
@@ -1,256 +1,250 @@
angular.module('umbraco.services')
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, dialogService, $timeout, angularHelper) {
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, dialogService, $timeout, angularHelper) {
var currentUser = null;
var lastUserId = null;
var loginDialog = null;
//this tracks the last date/time that the user's remainingAuthSeconds was updated from the server
// this is used so that we know when to go and get the user's remaining seconds directly.
var lastServerTimeoutSet = null;
var currentUser = null;
var lastUserId = null;
var loginDialog = null;
//this tracks the last date/time that the user's remainingAuthSeconds was updated from the server
// this is used so that we know when to go and get the user's remaining seconds directly.
var lastServerTimeoutSet = null;
// Redirect to the given url (defaults to '/')
function redirect(url) {
url = url || '/';
$location.path(url);
}
function openLoginDialog(isTimedOut) {
if (!loginDialog) {
loginDialog = dialogService.open({
template: 'views/common/dialogs/login.html',
modalClass: "login-overlay",
animation: "slide",
show: true,
callback: onLoginDialogClose,
dialogData: {
isTimedOut: isTimedOut
}
});
}
}
function onLoginDialogClose(success) {
loginDialog = null;
if (success) {
securityRetryQueue.retryAll();
} else {
securityRetryQueue.cancelAll();
redirect();
}
}
/**
This methods will set the current user when it is resolved and
will then start the counter to count in-memory how many seconds they have
remaining on the auth session
*/
function setCurrentUser(usr) {
if (!usr.remainingAuthSeconds) {
throw "The user object is invalid, the remainingAuthSeconds is required.";
}
currentUser = usr;
lastServerTimeoutSet = new Date();
//start the timer
countdownUserTimeout();
}
/**
Method to count down the current user's timeout seconds,
this will continually count down their current remaining seconds every 2 seconds until
there are no more seconds remaining.
*/
function countdownUserTimeout() {
$timeout(function () {
if (currentUser) {
//countdown by 2 seconds since that is how long our timer is for.
currentUser.remainingAuthSeconds -= 2;
//if there are more than 30 remaining seconds, recurse!
if (currentUser.remainingAuthSeconds > 30) {
//we need to check when the last time the timeout was set from the server, if
// it has been more than 30 seconds then we'll manually go and retrieve it from the
// server - this helps to keep our local countdown in check with the true timeout.
if (lastServerTimeoutSet != null) {
var now = new Date();
var seconds = (now.getTime() - lastServerTimeoutSet.getTime()) / 1000;
if (seconds > 30) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function() {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
function openLoginDialog(isTimedOut) {
if (!loginDialog) {
loginDialog = dialogService.open({
template: 'views/common/dialogs/login.html',
modalClass: "login-overlay",
animation: "slide",
show: true,
callback: onLoginDialogClose,
dialogData: {
isTimedOut: isTimedOut
}
});
}
}
//recurse the countdown!
countdownUserTimeout();
}
else {
function onLoginDialogClose(success) {
loginDialog = null;
//we are either timed out or very close to timing out so we need to show the login dialog.
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
userAuthExpired();
});
}
else {
//we've got less than 30 seconds remaining so let's check the server
if (success) {
securityRetryQueue.retryAll();
}
else {
securityRetryQueue.cancelAll();
$location.path('/');
}
}
/**
This methods will set the current user when it is resolved and
will then start the counter to count in-memory how many seconds they have
remaining on the auth session
*/
function setCurrentUser(usr) {
if (!usr.remainingAuthSeconds) {
throw "The user object is invalid, the remainingAuthSeconds is required.";
}
currentUser = usr;
lastServerTimeoutSet = new Date();
//start the timer
countdownUserTimeout();
}
/**
Method to count down the current user's timeout seconds,
this will continually count down their current remaining seconds every 2 seconds until
there are no more seconds remaining.
*/
function countdownUserTimeout() {
$timeout(function () {
if (currentUser) {
//countdown by 2 seconds since that is how long our timer is for.
currentUser.remainingAuthSeconds -= 2;
//if there are more than 30 remaining seconds, recurse!
if (currentUser.remainingAuthSeconds > 30) {
//we need to check when the last time the timeout was set from the server, if
// it has been more than 30 seconds then we'll manually go and retrieve it from the
// server - this helps to keep our local countdown in check with the true timeout.
if (lastServerTimeoutSet != null) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
var now = new Date();
var seconds = (now.getTime() - lastServerTimeoutSet.getTime()) / 1000;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function() {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
if (seconds > 30) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
});
}
}
//recurse the countdown!
countdownUserTimeout();
}
else {
//we are either timed out or very close to timing out so we need to show the login dialog.
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
userAuthExpired();
});
}
else {
//we've got less than 30 seconds remaining so let's check the server
if (lastServerTimeoutSet != null) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
//recurse the countdown!
countdownUserTimeout();
}
}
}
}, 2000, //every 2 seconds
false); //false = do NOT execute a digest for every iteration
}
/** Called to update the current user's timeout */
function setUserTimeoutInternal(newTimeout) {
var asNumber = parseFloat(newTimeout);
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
currentUser.remainingAuthSeconds = newTimeout;
lastServerTimeoutSet = new Date();
}
}, 2000, //every 2 seconds
false); //false = do NOT execute a digest for every iteration
}
/** Called to update the current user's timeout */
function setUserTimeoutInternal(newTimeout) {
var asNumber = parseFloat(newTimeout);
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
currentUser.remainingAuthSeconds = newTimeout;
lastServerTimeoutSet = new Date();
}
}
/** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */
function userAuthExpired(isLogout) {
//store the last user id and clear the user
if (currentUser && currentUser.id !== undefined) {
lastUserId = currentUser.id;
}
if(currentUser){
currentUser.remainingAuthSeconds = 0;
}
lastServerTimeoutSet = null;
currentUser = null;
//broadcast a global event that the user is no longer logged in
eventsService.emit("app.notAuthenticated");
/** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */
function userAuthExpired(isLogout) {
//store the last user id and clear the user
if (currentUser && currentUser.id !== undefined) {
lastUserId = currentUser.id;
}
openLoginDialog(isLogout === undefined ? true : !isLogout);
}
// Register a handler for when an item is added to the retry queue
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
if (securityRetryQueue.hasMore()) {
userAuthExpired();
}
});
return {
/** Internal method to display the login dialog */
_showLoginDialog: function () {
openLoginDialog();
},
/** Returns a promise, sends a request to the server to check if the current cookie is authorized */
isAuthenticated: function () {
//if we've got a current user then just return true
if (currentUser) {
var deferred = $q.defer();
deferred.resolve(true);
return deferred.promise;
currentUser.remainingAuthSeconds = 0;
}
return authResource.isAuthenticated();
},
/** Returns a promise, sends a request to the server to validate the credentials */
authenticate: function (login, password) {
lastServerTimeoutSet = null;
currentUser = null;
return authResource.performLogin(login, password)
.then(function (data) {
//broadcast a global event that the user is no longer logged in
eventsService.emit("app.notAuthenticated");
//when it's successful, return the user data
setCurrentUser(data);
openLoginDialog(isLogout === undefined ? true : !isLogout);
}
var result = { user: data, authenticated: true, lastUserId: lastUserId };
// Register a handler for when an item is added to the retry queue
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
if (securityRetryQueue.hasMore()) {
userAuthExpired();
}
});
//broadcast a global event
eventsService.emit("app.authenticated", result);
return result;
});
},
return {
/** Logs the user out and redirects to the login page */
logout: function () {
return authResource.performLogout()
.then(function (data) {
/** Internal method to display the login dialog */
_showLoginDialog: function () {
openLoginDialog();
},
userAuthExpired();
/** Returns a promise, sends a request to the server to check if the current cookie is authorized */
isAuthenticated: function () {
//if we've got a current user then just return true
if (currentUser) {
var deferred = $q.defer();
deferred.resolve(true);
return deferred.promise;
}
return authResource.isAuthenticated();
},
$location.path("/login").search({check: false});
/** Returns a promise, sends a request to the server to validate the credentials */
authenticate: function (login, password) {
return null;
});
},
return authResource.performLogin(login, password)
.then(function (data) {
/** Returns the current user object in a promise */
getCurrentUser: function (args) {
var deferred = $q.defer();
if (!currentUser) {
authResource.getCurrentUser()
.then(function(data) {
//when it's successful, return the user data
setCurrentUser(data);
var result = { user: data, authenticated: true, lastUserId: lastUserId };
if (args && args.broadcastEvent) {
//broadcast a global event, will inform listening controllers to load in the user specific data
eventsService.emit("app.authenticated", result);
}
setCurrentUser(data);
currentUser.avatar = 'http://www.gravatar.com/avatar/' + data.emailHash + '?s=40&d=404';
deferred.resolve(currentUser);
//broadcast a global event
eventsService.emit("app.authenticated", result);
return result;
});
},
}
else {
deferred.resolve(currentUser);
}
return deferred.promise;
},
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
setUserTimeout: function(newTimeout) {
setUserTimeoutInternal(newTimeout);
}
};
/** Logs the user out
*/
logout: function () {
});
return authResource.performLogout()
.then(function(data) {
userAuthExpired();
//done!
return null;
});
},
/** Returns the current user object in a promise */
getCurrentUser: function (args) {
var deferred = $q.defer();
if (!currentUser) {
authResource.getCurrentUser()
.then(function (data) {
var result = { user: data, authenticated: true, lastUserId: lastUserId };
if (args && args.broadcastEvent) {
//broadcast a global event, will inform listening controllers to load in the user specific data
eventsService.emit("app.authenticated", result);
}
setCurrentUser(data);
currentUser.avatar = 'http://www.gravatar.com/avatar/' + data.emailHash + '?s=40&d=404';
deferred.resolve(currentUser);
});
}
else {
deferred.resolve(currentUser);
}
return deferred.promise;
},
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
setUserTimeout: function (newTimeout) {
setUserTimeoutInternal(newTimeout);
}
};
});
@@ -139,7 +139,8 @@ function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, me
return { height: minDisplayHeight, imgCount: 1 };
}
else if (idealImages.length === idealImgPerRow && targetHeight < maxRowHeight) {
//if we're already dealing with the ideal images per row and it's not quite there, we can scale up a little bit so
//if we're already dealing with the ideal images per row and it's not quite wide enough, we can scale up a little bit so
// long as the targetHeight is currently less than the maxRowHeight. The scale up will be half-way between our current
// target height and the maxRowHeight (we won't loop forever though - if there's a difference of 5 px we'll just quit)
@@ -152,9 +153,8 @@ function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, me
}
}
//Ok, we couldn't actually scale it up with the ideal row count (TBH I'm not sure that this would ever happen but we'll take it into account)
// we'll just recurse with another image count.
return this.getRowHeightForImages(imgs, maxRowHeight, minDisplayHeight, maxRowWidth, idealImgPerRow + 1, margin);
//Ok, we couldn't actually scale it up with the ideal row count we'll just recurse with a lesser image count.
return this.getRowHeightForImages(imgs, maxRowHeight, minDisplayHeight, maxRowWidth, idealImgPerRow - 1, margin);
}
else {
//we have additional images so we'll recurse and add 1 to the idealImgPerRow until it fits
@@ -64,7 +64,7 @@
.alert-form {
background-color: @grayLighter;
border: 1px solid @gray;
border: 1px solid @gray !important;
color: @gray;
}
+1 -1
View File
@@ -156,7 +156,7 @@ body {
#speechbubble {
z-index: 1000;
position: absolute;
bottom: 40px;
bottom: 100px;
left: 0;
right: 0;
border-bottom: none;
+29 -2
View File
@@ -12,7 +12,8 @@ app.config(function ($routeProvider) {
var deferred = $q.defer();
//don't need to check if we've redirected to login and we've already checked auth
if (!$route.current.params.section && $route.current.params.check === false) {
if (!$route.current.params.section
&& ($route.current.params.check === false || $route.current.params.check === "false")) {
deferred.resolve(true);
return deferred.promise;
}
@@ -55,7 +56,7 @@ app.config(function ($routeProvider) {
if (isRequired) {
//the check=false is checked above so that we don't have to make another http call to check
//if they are logged in since we already know they are not.
deferred.reject({ path: "/login", search: { check: false } });
deferred.reject({ path: "/login/false" });
}
else {
//this will resolve successfully so the route will continue
@@ -67,12 +68,38 @@ app.config(function ($routeProvider) {
};
};
/** When this is used to resolve it will attempt to log the current user out */
var doLogout = function() {
return {
isLoggedOut: function ($q, userService) {
var deferred = $q.defer();
userService.logout().then(function () {
//success so continue
deferred.resolve(true);
}, function() {
//logout failed somehow ? we'll reject with the login page i suppose
deferred.reject({ path: "/login/false" });
});
return deferred.promise;
}
}
}
$routeProvider
.when('/login', {
templateUrl: 'views/common/login.html',
//ensure auth is *not* required so it will redirect to /
resolve: canRoute(false)
})
.when('/login/:check', {
templateUrl: 'views/common/login.html',
//ensure auth is *not* required so it will redirect to /
resolve: canRoute(false)
})
.when('/logout', {
redirectTo: '/login/false',
resolve: doLogout()
})
.when('/:section', {
templateUrl: function (rp) {
if (rp.section.toLowerCase() === "default" || rp.section.toLowerCase() === "umbraco" || rp.section === "")
@@ -1,47 +1,69 @@
angular.module("umbraco")
.controller("Umbraco.Dialogs.UserController", function ($scope, $location, $timeout, userService, historyService) {
.controller("Umbraco.Dialogs.UserController", function ($scope, $location, $timeout, userService, historyService, eventsService) {
$scope.user = userService.getCurrentUser();
$scope.history = historyService.current;
$scope.history = historyService.getCurrent();
$scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion;
var evtHandlers = [];
evtHandlers.push(eventsService.on("historyService.add", function (e, args) {
$scope.history = args.all;
}));
evtHandlers.push(eventsService.on("historyService.remove", function (e, args) {
$scope.history = args.all;
}));
evtHandlers.push(eventsService.on("historyService.removeAll", function (e, args) {
$scope.history = [];
}));
$scope.logout = function () {
userService.logout().then(function () {
$scope.remainingAuthSeconds = 0;
$scope.hide();
});
};
$scope.gotoHistory = function (link) {
$location.path(link);
$scope.hide();
};
//Add event listener for when there are pending changes on an editor which means our route was not successful
var pendingChangeEvent = eventsService.on("valFormManager.pendingChanges", function (e, args) {
//one time listener, remove the event
pendingChangeEvent();
$scope.hide();
});
//perform the path change, if it is successful then the promise will resolve otherwise it will fail
$location.path("/logout");
};
$scope.gotoHistory = function (link) {
$location.path(link);
$scope.hide();
};
//Manually update the remaining timeout seconds
function updateTimeout() {
$timeout(function () {
if ($scope.remainingAuthSeconds > 0) {
$scope.remainingAuthSeconds--;
$scope.$digest();
//recurse
updateTimeout();
}
}, 1000, false); // 1 second, do NOT execute a global digest
}
//get the user
userService.getCurrentUser().then(function (user) {
$scope.user = user;
if ($scope.user) {
$scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds;
$scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1;
//set the timer
updateTimeout();
}
});
function updateTimeout() {
$timeout(function () {
if ($scope.remainingAuthSeconds > 0) {
$scope.remainingAuthSeconds--;
$scope.$digest();
//recurse
updateTimeout();
}
}, 1000, false); // 1 second, do NOT execute a global digest
}
//get the user
userService.getCurrentUser().then(function (user) {
$scope.user = user;
if ($scope.user) {
$scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds;
$scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1;
//set the timer
updateTimeout();
}
});
//remove all event handlers
$scope.$on('$destroy', function () {
for (var i = 0; i < evtHandlers.length; i++) {
evtHandlers[i]();
}
});
});
@@ -34,7 +34,7 @@
<div class="umb-pane">
<h5><localize key="user_yourHistory" /></h5>
<ul class="umb-tree">
<li ng-repeat="item in history | orderBy:'time'">
<li ng-repeat="item in history | orderBy:'time':true">
<a ng-href="{{item.link}}" ng-click="gotoHistory(item.link)" prevent-default>
<i class="{{item.icon}}"></i> {{item.name}}</a>
</li>
@@ -6,7 +6,18 @@ angular.module("umbraco").controller("Umbraco.Notifications.ConfirmRouteChangeCo
not.args.listener();
$location.search("");
$location.path(not.args.path);
//we need to break the path up into path and query
var parts = not.args.path.split("?");
var query = {};
if (parts.length > 1) {
_.each(parts[1].split("&"), function(q) {
var keyVal = q.split("=");
query[keyVal[0]] = keyVal[1];
});
}
$location.path(parts[0]).search(query);
notificationsService.remove(not);
};
@@ -1,18 +1,18 @@
<div id='speechbubble' class='umb-notification' ng-cloak>
<ul>
<li ng-repeat="notification in notifications" class="alert alert-block alert-{{notification.type}}">
<a class='close' ng-click="removeNotification($index)" prevent-default href>&times;</a>
<li ng-repeat="notification in notifications" class="alert alert-block alert-{{notification.type}}" >
<a class='close' ng-click="removeNotification($index)" prevent-default href>&times;</a>
<div ng-if="notification.view">
<div ng-include="notification.view"></div>
</div>
<div ng-if="notification.view">
<div ng-include="notification.view"></div>
</div>
<div ng-if="notification.headline">
<a ng-href="{{notification.url}}" target="_blank">
<strong>{{notification.headline}}</strong>
<span ng-bind-html="notification.message"></span>
</a>
</div>
<div ng-if="notification.headline">
<a ng-href="{{notification.url}}" target="_blank">
<strong>{{notification.headline}}</strong>
<span ng-bind-html="notification.message"></span>
</a>
</div>
</li>
</ul>
</div>
@@ -1,4 +1,4 @@
function dateTimePickerController($scope, notificationsService, assetsService, angularHelper, userService) {
function dateTimePickerController($scope, notificationsService, assetsService, angularHelper, userService, $element) {
//lists the custom language files that we currently support
var customLangs = ["pt-BR"];
@@ -31,37 +31,39 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
//get the current user to see if we can localize this picker
userService.getCurrentUser().then(function (user) {
assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css').then(function() {
var filesToLoad = ["lib/datetimepicker/bootstrap-datetimepicker.min.js"];
//if we support this custom culture, set it, then we'll need to load in that lang file
if (_.contains(customLangs, user.locale)) {
$scope.model.config.language = user.locale;
filesToLoad.push("lib/datetimepicker/langs/datetimepicker." + user.locale + ".js");
}
assetsService.load(filesToLoad).then(
function () {
//The Datepicker js and css files are available and all components are ready to use.
// Get the id of the datepicker button that was clicked
var pickerId = $scope.model.alias;
// Open the datepicker and add a changeDate eventlistener
$element.find("div:first")
.datetimepicker($scope.model.config)
.on("changeDate", applyDate);
//now assign the date
$("#datepicker" + pickerId).val($scope.model.value);
//Ensure to remove the event handler when this instance is destroyted
$scope.$on('$destroy', function () {
$element.find("div:first").datetimepicker("destroy");
});
});
});
var filesToLoad = ["lib/datetimepicker/bootstrap-datetimepicker.min.js"];
//if we support this custom culture, set it, then we'll need to load in that lang file
if (_.contains(customLangs, user.locale)) {
$scope.model.config.language = user.locale;
filesToLoad.push("lib/datetimepicker/langs/datetimepicker." + user.locale + ".js");
}
assetsService.load(filesToLoad).then(
function() {
//The Datepicker js and css files are available and all components are ready to use.
// Get the id of the datepicker button that was clicked
var pickerId = $scope.model.alias;
// Open the datepicker and add a changeDate eventlistener
$("#datepicker" + pickerId)
//.datetimepicker(config);
.datetimepicker($scope.model.config)
.on("changeDate", applyDate);
//now assign the date
$("#datepicker" + pickerId).val($scope.model.value);
});
});
assetsService.loadCss(
'lib/datetimepicker/bootstrap-datetimepicker.min.css'
);
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.DatepickerController", dateTimePickerController);
@@ -125,11 +125,15 @@ function fileUploadController($scope, $element, $compile, imageHelper, fileManag
angular.module("umbraco")
.controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController)
.run(function(mediaHelper, umbRequestHelper){
if(mediaHelper && mediaHelper.registerFileResolver){
if (mediaHelper && mediaHelper.registerFileResolver) {
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
// they contain different data structures so if we need to query against it we need to be aware of this.
mediaHelper.registerFileResolver("Umbraco.UploadField", function(property, entity, thumbnail){
if (thumbnail) {
if (mediaHelper.detectIfImageByExtension(property.value)) {
var thumbnailUrl = umbRequestHelper.getApiUrl(
"imagesApiBaseUrl",
"GetBigThumbnail",
@@ -98,11 +98,14 @@ angular.module('umbraco')
})
.run(function (mediaHelper, umbRequestHelper) {
if (mediaHelper && mediaHelper.registerFileResolver) {
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
// they contain different data structures so if we need to query against it we need to be aware of this.
mediaHelper.registerFileResolver("Umbraco.ImageCropper", function (property, entity, thumbnail) {
if (property.value.src) {
if (thumbnail === true) {
return property.value.src + "?width=600&mode=max";
return property.value.src + "?width=500&mode=max";
}
else {
return property.value.src;
@@ -114,6 +117,7 @@ angular.module('umbraco')
if (thumbnail) {
if (mediaHelper.detectIfImageByExtension(property.value)) {
var thumbnailUrl = umbRequestHelper.getApiUrl(
"imagesApiBaseUrl",
"GetBigThumbnail",
@@ -1,10 +1,10 @@
//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerController",
function($rootScope, $scope, dialogService, mediaResource, mediaHelper, $timeout) {
function ($rootScope, $scope, dialogService, entityResource, mediaResource, mediaHelper, $timeout) {
//check the pre-values for multi-picker
var multiPicker = $scope.model.config.multiPicker !== '0' ? true : false;
var multiPicker = $scope.model.config.multiPicker && $scope.model.config.multiPicker !== '0' ? true : false;
if (!$scope.model.config.startNodeId)
$scope.model.config.startNodeId = -1;
@@ -17,18 +17,24 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
if ($scope.model.value) {
var ids = $scope.model.value.split(',');
mediaResource.getByIds(ids).then(function (medias) {
//img.media = media;
//NOTE: We need to use the entityResource NOT the mediaResource here because
// the mediaResource has server side auth configured for which the user must have
// access to the media section, if they don't they'll get auth errors. The entityResource
// acts differently in that it allows access if the user has access to any of the apps that
// might require it's use. Therefore we need to use the metatData property to get at the thumbnail
// value.
entityResource.getByIds(ids, "Media").then(function (medias) {
_.each(medias, function (media, i) {
//only show non-trashed items
if(media.parentId >= -1){
if(!media.thumbnail){
media.thumbnail = mediaHelper.resolveFile(media, true);
if (media.parentId >= -1) {
if (!media.thumbnail) {
media.thumbnail = mediaHelper.resolveFileFromEntity(media, true);
}
//media.src = mediaHelper.getImagePropertyValue({ imageModel: media });
//media.thumbnail = mediaHelper.getThumbnailFromPath(media.src);
$scope.images.push(media);
$scope.ids.push(media.id);
}
@@ -60,10 +66,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
_.each(data, function(media, i) {
if(!media.thumbnail){
media.thumbnail = mediaHelper.resolveFile(media, true);
if (!media.thumbnail) {
media.thumbnail = mediaHelper.resolveFileFromEntity(media, true);
}
$scope.images.push(media);
$scope.ids.push(media.id);
});
@@ -110,4 +116,4 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
setupViewModel();
};
});
});

Some files were not shown because too many files have changed in this diff Show More