Merge branch '7.1.2-Usky' of https://github.com/umbraco/UmbracoCms-Internal into 7.1.2-Usky
This commit is contained in:
+10
-2
@@ -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
@@ -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 "([a-zA-Z]+)?""
|
||||
ReplacementText="CurrentComment { get { return "$(BUILD_COMMENT)""/>
|
||||
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.targets"
|
||||
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
|
||||
Namespaces="<Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' />"
|
||||
Query="//x:UmbracoVersion"
|
||||
Value="$(NewVersion)" />
|
||||
|
||||
@@ -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%
|
||||
|
||||
@@ -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,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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
+25
-21
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ body {
|
||||
#speechbubble {
|
||||
z-index: 1000;
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
bottom: 100px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-bottom: none;
|
||||
|
||||
@@ -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>
|
||||
|
||||
+12
-1
@@ -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>×</a>
|
||||
<li ng-repeat="notification in notifications" class="alert alert-block alert-{{notification.type}}" >
|
||||
<a class='close' ng-click="removeNotification($index)" prevent-default href>×</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>
|
||||
|
||||
+32
-30
@@ -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);
|
||||
|
||||
+5
-1
@@ -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",
|
||||
|
||||
+5
-1
@@ -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",
|
||||
|
||||
+19
-13
@@ -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
Reference in New Issue
Block a user