Compare commits

...

61 Commits

Author SHA1 Message Date
Shannon 420746397d fixes macro property saving issue in editor (to ensure the alias/key can be changed) 2014-05-17 15:35:56 +10:00
Shannon 142a9963af Fixes macro property saving issue in repo 2014-05-17 15:35:30 +10:00
Sebastiaan Janssen 1dd103caa3 U4-4930 NuGet update fails to copy new Umbraco binaries 2014-05-16 18:53:23 +02:00
Sebastiaan Janssen 43bfd6e472 U4-4650 Pluralization of document type aliases doesn't always work 2014-05-16 14:33:03 +02:00
Sebastiaan Janssen b07665f4d4 Fixes U4-4650 Pluralization of document type aliases doesn't always work 2014-05-16 14:22:13 +02:00
Sebastiaan Janssen 0ebd66d926 Merge branch 'dampee-oembed_slideshare' into 7.1.3 2014-05-16 12:51:26 +02:00
Sebastiaan Janssen 8d45898274 UmbracoVersion property no longer needed. Added data folder to the targets. 2014-05-16 12:44:02 +02:00
Sebastiaan Janssen a99a33477a Centralized the current Umbraco version number in UmbracoVersion.txt that will be read by
buildbelle.bat, build.bat and the umbraco.web.ui.csproj so it works in VS, command line and
also in TeamCity.
2014-05-16 12:27:54 +02:00
Sebastiaan Janssen b42d3b226d Bump version 2014-05-16 11:29:35 +02:00
Sebastiaan Janssen b6925f9f07 Merge branch 'gambrose-7.1.2' into 7.1.3 2014-05-16 11:28:02 +02:00
Shannon ffbb1a85ab Fixes: U4-4312 Spelling error on "Retreive" should be Retrieve -U7.0.0.4 2014-05-16 12:43:23 +10:00
Shannon ac9c938e92 Fixes: U4-4923 Failed to retrieve data for application tree media after upgrade to 7.1.2 2014-05-16 12:42:21 +10:00
Shannon 762656e964 Fixes: U4-4926 Pre-values need to be cloned in/out of the cache 2014-05-16 12:08:17 +10:00
Shannon 46a4f9242c fixes test 2014-05-16 11:43:20 +10:00
Shannon 8cae2526d5 Fixes: U4-4842 Unable to edit "name" and "alias" to MediaType property 2014-05-16 11:34:10 +10:00
Graham Ambrose 6b313aba48 CopyUmbracoFilesToWebRoot assumes packages loacation
CopyUmbracoFilesToWebRoot assumes packages folder is always one dir up from
project file which is not always the case.

I have changed to use MSBuildThisFileDirectory which makes the path
relative the the UmbracoCms.targets file.
MSBuildThisFileDirectory is supported from MSBuild 4 but as ToolsVersion is
already set to 4.0 that should not be a problem.

UmbracoVersion property is no longer required but removing it may break
external target files depending on it. Have moved it to props file as this
makes it more reliable for external targets files.
2014-05-15 22:43:24 +01:00
Shannon 7b4c58538d wrote test to confirm: U4-2356 2014-05-15 16:11:59 +10:00
Shannon e41c99d5bc Merge branch 'ma1f-7.1.2' into 7.1.3 2014-05-15 15:44:56 +10:00
Shannon a49dd36461 Merge branch '7.1.2' of https://github.com/ma1f/Umbraco-CMS into ma1f-7.1.2 2014-05-15 15:43:53 +10:00
Mark van Schaik 82a1e85431 solves issue U4-2356 when deleting templates 2014-05-15 16:37:32 +12:00
Shannon 1c7d83f589 Undoes the comma encoding for tags and istead adds support to store the tags as json or csv (U4-4741) 2014-05-15 12:49:03 +10:00
Shannon 157428dbc8 ensures multiple commas in tags still work. 2014-05-14 20:54:01 +10:00
Shannon 0bcaa0da7f ensures that when querying for a tag with a comma that we encode it so that it finds it. 2014-05-14 20:31:32 +10:00
Shannon 922942197f Fixes: U4-4741 Tags Property Editor Mishandles Tags with Commas 2014-05-14 19:57:40 +10:00
Shannon b8975de04d Fixes: U4-4666 SQL Error when saving content with Tags property with @ value 2014-05-14 18:55:08 +10:00
Shannon f00950bfe4 adds unit test for image helper 2014-05-14 18:38:05 +10:00
Shannon 80d012f3ad Fixes: U4-4771 u7.1.1 Media section can't view images RangeError: Maximum call stack size exceeded 2014-05-14 18:25:50 +10:00
Shannon 654efd1128 Fixes: U4-3492 Recent history list persist between logged in users 2014-05-14 15:26:52 +10:00
Shannon aa2375b8ba Fixes client side mem leak for dialogs and not correctly removing their scopes. 2014-05-14 15:18:28 +10:00
Shannon f6532d2743 un-does the distinct check, in the past it was never like this, duplicate values were always allowed so by doing this it is a breaking change 2014-05-14 13:09:02 +10:00
Shannon 8c334a8c95 Fixes: U4-4880 SEO Checker fails after upgrading from 7.1.1 to 7.1.2 2014-05-14 12:29:38 +10:00
Shannon 42cd71ef6c Fixes: U4-4865 Umbraco 7.1.2 Date Picker not rendering icon or calendar control 2014-05-14 12:01:38 +10:00
Shannon 5956394bf1 Fixes: U4-4877 Editing macro parameters fails because of new item validation 2014-05-14 10:52:08 +10:00
Shannon d1e9fb6d9f re-fixes Core proj since it would not build 2014-05-14 09:42:34 +10:00
Damiaan 48dcc373dc Oembed for slideshare not working anymore 2014-05-13 22:29:59 +02:00
Stephan c9961b5192 Fix Umbraco.Core project (missing files, would not build) 2014-05-13 15:24:36 +02:00
Stephan 03d3503531 Don't throw when validating doctype/property aliases with dangerous chars 2014-05-13 12:39:35 +02:00
Stephan 9acba1d6d7 U4-4857 - issue with document type new property alias 2014-05-13 09:53:22 +02:00
Shannon 659f47c038 Merge remote-tracking branch 'origin/6.2.1' into 7.1.3 2014-05-13 15:47:38 +10:00
Shannon 78613823ae Fixes: U4-4853 Richtext Editor doesn't render macros after upgrade to Umbraco 6.2.0 2014-05-13 15:22:16 +10:00
Shannon cb7f26c998 Fixes: U4-4197 Error 400 on admin area login 2014-05-13 13:39:18 +10:00
Shannon 16b5c86192 Fixes: U4-4869 Cannot save tags - was due to adding the loading... message and the ng-if creating a new scope and not targeting the $parent in the view, also fixes up clearing the value with typeahead is used 2014-05-13 12:55:31 +10:00
Shannon c83ab00827 Merge remote-tracking branch 'origin/6.2.1' into 7.1.3 2014-05-13 10:46:14 +10:00
Shannon 937e840104 Fixes: U4-4873 RuntimeCacheRefresher does not remove items when media or content requires cache refreshing & U4-4871 Synced media indexes between load balanced servers do not sync all properties 2014-05-13 10:40:56 +10:00
Shannon 8d49c730d5 Merge branch 'meixger-hotfix/imagecropper' into 7.1.3 2014-05-13 09:33:34 +10:00
Shannon fadb503b97 Merge branch 'hotfix/imagecropper' of https://github.com/meixger/Umbraco-CMS into meixger-hotfix/imagecropper 2014-05-13 09:33:22 +10:00
Martin Meixger 289bed8fd2 change negative coordinates to zero 2014-05-12 15:34:50 +02:00
Shannon eec1048631 Merge branch '6.2.1' into 7.1.3 2014-05-12 17:50:33 +10:00
Shannon 313d77eb91 Fixes: U4-4789 PluginCache doesn't update with code defined PropertyEditors in package and publicizes a couple things 2014-05-12 17:19:17 +10:00
Shannon b048341da0 Fixes: U4-4789 PluginCache doesn't update with code defined PropertyEditors in package and publicizes a couple things 2014-05-12 17:07:29 +10:00
Shannon 03cb54e931 fixed merge issue 2014-05-12 14:37:54 +10:00
Shannon 150b67d10f Merge remote-tracking branch 'origin/6.2.1' into 7.1.3
Conflicts:
	src/Umbraco.Tests/Models/ContentTests.cs
	src/Umbraco.Tests/Models/ContentTypeTests.cs
2014-05-12 14:37:19 +10:00
Shannon ac88da4188 Fixes: U4-4856 ApplicationContext.Current.Services.ContentService.Save throw exception 2014-05-12 14:32:34 +10:00
Shannon cca27e064a Fixes: U4-4855 Can't use macro parameters at all 2014-05-12 13:48:56 +10:00
Stephan 4e5019d5af U4-4851 - Umbraco.Field legacy names 2014-05-11 21:33:56 +02:00
Stephan f1a312b14a U4-4851 - Umbraco.Field legacy names 2014-05-11 21:06:33 +02:00
Shannon da0b935604 Fixes merge 2014-05-09 16:08:05 +10:00
Shannon b91a06ab98 Fixes: U4-4849 Cannot change a users language/sections in the back office 2014-05-09 16:00:30 +10:00
Shannon d61fb5d94d Fixes user cloning issues with allowed sections 2014-05-09 15:59:30 +10:00
Shannon 3b424274ee ensure properties reset on deep clone of ContentType (this is just a fail safe check) 2014-05-09 15:57:17 +10:00
Shannon b763c2ab2f Fixes deep clone of User object 2014-05-09 15:56:45 +10:00
84 changed files with 1696 additions and 978 deletions
+10 -1
View File
@@ -1,5 +1,9 @@
@ECHO OFF
SET release=7.1.2
IF NOT EXIST UmbracoVersion.txt (
ECHO UmbracoVersion.txt missing!
GOTO :showerror
)
SET /p release=<UmbracoVersion.txt
SET comment=
SET version=%release%
@@ -15,6 +19,11 @@ SET nuGetFolder=%CD%\..\src\packages\
ECHO Removing the belle build folder to make sure everything is clean as a whistle
RD ..\src\Umbraco.Web.UI.Client\build /Q /S
ECHO Removing existing built files to make sure everything is clean as a whistle
DEL /F /Q UmbracoCms.*.zip
DEL /F /Q UmbracoCms.*.nupkg
DEL /F /Q webpihash.txt
ECHO Performing MSBuild and producing Umbraco binaries zip files
%windir%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe "Build.proj" /p:BUILD_RELEASE=%release% /p:BUILD_COMMENT=%comment%
+1 -1
View File
@@ -325,7 +325,7 @@
Regex="CurrentComment { get { return &quot;([a-zA-Z]+)?&quot;"
ReplacementText="CurrentComment { get { return &quot;$(BUILD_COMMENT)&quot;"/>
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.targets"
<XmlPoke XmlInputPath=".\NuSpecs\build\UmbracoCms.props"
Namespaces="&lt;Namespace Prefix='x' Uri='http://schemas.microsoft.com/developer/msbuild/2003' /&gt;"
Query="//x:UmbracoVersion"
Value="$(NewVersion)" />
+2 -1
View File
@@ -1,4 +1,5 @@
@ECHO OFF
SET release=%1
ECHO Installing Npm NuGet Package
SET nuGetFolder=%CD%\..\src\packages\
@@ -26,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%
+1
View File
@@ -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>
+2 -3
View File
@@ -1,12 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CopyAllFilesToSingleFolderForPackageDependsOn>
AddUmbracoFilesToOutput;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForPackageDependsOn>
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
<CopyAllFilesToSingleFolderForMsdeployDependsOn>
AddUmbracoFilesToOutput;
$(CopyAllFilesToSingleFolderForPackageDependsOn);
</CopyAllFilesToSingleFolderForMsdeployDependsOn>
+4 -4
View File
@@ -1,11 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<UmbracoVersion>7.1.2</UmbracoVersion>
</PropertyGroup>
<Target Name="CopyUmbracoFilesToWebRoot" BeforeTargets="AfterBuild">
<PropertyGroup>
<UmbracoFilesFolder>..\packages\UmbracoCms.$(UmbracoVersion)\UmbracoFiles\</UmbracoFilesFolder>
<UmbracoFilesFolder>$(MSBuildThisFileDirectory)..\UmbracoFiles\</UmbracoFilesFolder>
</PropertyGroup>
<ItemGroup>
<UmbracoFiles Include="$(UmbracoFilesFolder)**\*" />
@@ -33,6 +30,9 @@
<CustomFilesToInclude Include=".\Config\Splashes\**\*">
<Dir>Config\Splashes</Dir>
</CustomFilesToInclude>
<CustomFilesToInclude Include=".\data\**\*">
<Dir>data</Dir>
</CustomFilesToInclude>
<CustomFilesToInclude Include=".\umbraco\**\*">
<Dir>umbraco</Dir>
</CustomFilesToInclude>
+24
View File
@@ -0,0 +1,24 @@
param($rootPath, $toolsPath, $package, $project)
if ($project) {
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
$backupPath = Join-Path (Split-Path $project.FullName -Parent) "\App_Data\NuGetBackup\$dateTime"
$copyLogsPath = Join-Path $backupPath "CopyLogs"
$projectDestinationPath = Split-Path $project.FullName -Parent
# Create backup folder and logs folder if it doesn't exist yet
New-Item -ItemType Directory -Force -Path $backupPath
New-Item -ItemType Directory -Force -Path $copyLogsPath
# After backing up, remove all dlls from bin folder in case dll files are included in the VS project
# See: http://issues.umbraco.org/issue/U4-4930
$umbracoBinFolder = Join-Path $projectDestinationPath "bin"
if(Test-Path $umbracoBinFolder) {
$umbracoBinBackupPath = Join-Path $backupPath "bin"
New-Item -ItemType Directory -Force -Path $umbracoBinBackupPath
robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log
Remove-Item $umbracoBinFolder\*.dll -Force -Confirm:$false
}
}
+1
View File
@@ -0,0 +1 @@
7.1.3
@@ -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.
+49 -8
View File
@@ -9,6 +9,8 @@ using System.Linq;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
@@ -539,7 +541,7 @@ namespace Umbraco.Core.Models
return ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id);
}
#region Tag methods
///// <summary>
@@ -567,6 +569,21 @@ namespace Umbraco.Core.Models
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
/// <returns></returns>
public static void SetTags(this IContentBase content, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
{
content.SetTags(TagCacheStorageType.Csv, propertyTypeAlias, tags, replaceTags, tagGroup);
}
/// <summary>
/// Sets tags for the property - will add tags to the tags table and set the property value to be the comma delimited value of the tags.
/// </summary>
/// <param name="content">The content item to assign the tags to</param>
/// <param name="storageType">The tag storage type in cache (default is csv)</param>
/// <param name="propertyTypeAlias">The property alias to assign the tags to</param>
/// <param name="tags">The tags to assign</param>
/// <param name="replaceTags">True to replace the tags on the current property with the tags specified or false to merge them with the currently assigned ones</param>
/// <param name="tagGroup">The group/category to assign the tags, the default value is "default"</param>
/// <returns></returns>
public static void SetTags(this IContentBase content, TagCacheStorageType storageType, string propertyTypeAlias, IEnumerable<string> tags, bool replaceTags, string tagGroup = "default")
{
var property = content.Properties[propertyTypeAlias];
if (property == null)
@@ -583,15 +600,39 @@ namespace Umbraco.Core.Models
//ensure the property value is set to the same thing
if (replaceTags)
{
property.Value = string.Join(",", trimmedTags);
switch (storageType)
{
case TagCacheStorageType.Csv:
property.Value = string.Join(",", trimmedTags);
break;
case TagCacheStorageType.Json:
//json array
property.Value = JsonConvert.SerializeObject(trimmedTags);
break;
}
}
else
{
var currTags = property.Value.ToString().Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries)
switch (storageType)
{
case TagCacheStorageType.Csv:
var currTags = property.Value.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim());
property.Value = string.Join(",", trimmedTags.Union(currTags));
property.Value = string.Join(",", trimmedTags.Union(currTags));
break;
case TagCacheStorageType.Json:
var currJson = JsonConvert.DeserializeObject<JArray>(property.Value.ToString());
//need to append the new ones
foreach (var tag in trimmedTags)
{
currJson.Add(tag);
}
//json array
property.Value = JsonConvert.SerializeObject(currJson);
break;
}
}
}
/// <summary>
@@ -664,7 +705,7 @@ namespace Umbraco.Core.Models
{
return ApplicationContext.Current.Services.PackagingService.Export(media, true, raiseEvents: false);
}
/// <summary>
/// Creates the xml representation for the <see cref="IContent"/> object
/// </summary>
@@ -687,10 +728,10 @@ namespace Umbraco.Core.Models
{
return ((PackagingService)(ApplicationContext.Current.Services.PackagingService)).Export(member);
}
#endregion
}
}
-23
View File
@@ -133,29 +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();
clone.ResetDirtyProperties(false);
return clone;
}
/// <summary>
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
+31 -6
View File
@@ -331,11 +331,6 @@ namespace Umbraco.Core.Models
get { return _additionalData; }
}
/// <summary>
/// Some entities may expose additional data that other's might not, this custom data will be available in this collection
/// </summary>
public IDictionary<string, object> AdditionalData { get; private set; }
/// <summary>
/// Gets or sets a list of integer Ids for allowed ContentTypes
/// </summary>
@@ -356,7 +351,9 @@ namespace Umbraco.Core.Models
/// <summary>
/// List of PropertyGroups available on this ContentType
/// </summary>
/// <remarks>A PropertyGroup corresponds to a Tab in the UI</remarks>
/// <remarks>
/// A PropertyGroup corresponds to a Tab in the UI
/// </remarks>
[DataMember]
public virtual PropertyGroupCollection PropertyGroups
{
@@ -372,7 +369,13 @@ namespace Umbraco.Core.Models
/// List of PropertyTypes available on this ContentType.
/// This list aggregates PropertyTypes across the PropertyGroups.
/// </summary>
/// <remarks>
/// Marked as DoNotClone because the result of this property is not the natural result of the data, it is
/// a union of data so when auto-cloning if the setter is used it will be setting the unnatural result of the
/// data. We manually clone this instead.
/// </remarks>
[IgnoreDataMember]
[DoNotClone]
public virtual IEnumerable<PropertyType> PropertyTypes
{
get
@@ -388,6 +391,14 @@ namespace Umbraco.Core.Models
}
/// <summary>
/// Returns the property type collection containing types that are non-groups - used for tests
/// </summary>
internal IEnumerable<PropertyType> NonGroupedPropertyTypes
{
get { return _propertyTypes; }
}
/// <summary>
/// A boolean flag indicating if a property type has been removed from this instance.
/// </summary>
/// <remarks>
@@ -584,5 +595,19 @@ namespace Umbraco.Core.Models
propertyType.ResetDirtyProperties();
}
}
public override object DeepClone()
{
var clone = (ContentTypeBase)base.DeepClone();
//need to manually wire up the event handlers for the property type collections - we've ensured
// its ignored from the auto-clone process because its return values are unions, not raw and
// we end up with duplicates, see: http://issues.umbraco.org/issue/U4-4842
clone._propertyTypes = (PropertyTypeCollection)_propertyTypes.DeepClone();
clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged;
return clone;
}
}
}
+23 -1
View File
@@ -2,9 +2,29 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Umbraco.Core.Models
{
/// <summary>
/// Used to attribute properties that have a setter and are a reference type
/// that should be ignored for cloning when using the DeepCloneHelper
/// </summary>
/// <remarks>
///
/// This attribute must be used:
/// * when the property is backed by a field but the result of the property is the un-natural data stored in the field
///
/// This attribute should not be used:
/// * when the property is virtual
/// * when the setter performs additional required logic other than just setting the underlying field
///
/// </remarks>
internal class DoNotCloneAttribute : Attribute
{
}
public static class DeepCloneHelper
{
/// <summary>
@@ -25,8 +45,10 @@ namespace Umbraco.Core.Models
var refProperties = inputType.GetProperties()
.Where(x =>
//is not attributed with the ignore clone attribute
x.GetCustomAttribute<DoNotCloneAttribute>() == null
//reference type but not string
x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
&& x.PropertyType.IsValueType == false && x.PropertyType != typeof (string)
//settable
&& x.CanWrite
//non-indexed
@@ -240,52 +240,6 @@ namespace Umbraco.Core.Models.EntityBase
DeepCloneHelper.DeepCloneRefProperties(this, clone);
clone.ResetDirtyProperties(false);
return clone;
//Using data contract serializer - has issues
//var s = Serialize(this);
//var d = Deserialize(s, this.GetType());
//return d;
//Using binary serializer - has issues
//using (var memoryStream = new MemoryStream(10))
//{
//IFormatter formatter = new BinaryFormatter();
//formatter.Serialize(memoryStream, this);
//memoryStream.Seek(0, SeekOrigin.Begin);
//return formatter.Deserialize(memoryStream);
//}
}
// serialize/deserialize with data contracts:
//public static string Serialize(object obj)
//{
// using (var memoryStream = new MemoryStream())
// using (var reader = new StreamReader(memoryStream))
// {
// var serializer = new DataContractSerializer(obj.GetType());
// serializer.WriteObject(memoryStream, obj);
// memoryStream.Position = 0;
// return reader.ReadToEnd();
// }
//}
//public static object Deserialize(string xml, Type toType)
//{
// using (Stream stream = new MemoryStream())
// {
// using (var writer = new StreamWriter(stream, Encoding.UTF8))
// {
// writer.Write(xml);
// //byte[] data = Encoding.UTF8.GetBytes(xml);
// //stream.Write(data, 0, data.Length);
// stream.Position = 0;
// var deserializer = new DataContractSerializer(toType);
// return deserializer.ReadObject(stream);
// }
// }
//}
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Models
/// <summary>
/// Defines a Property for a Macro
/// </summary>
public interface IMacroProperty : IValueObject
public interface IMacroProperty : IValueObject, IDeepCloneable, IRememberBeingDirty
{
[DataMember]
int Id { get; set; }
+11 -13
View File
@@ -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);
}
}
}
@@ -404,7 +401,8 @@ namespace Umbraco.Core.Models
clone._addedProperties = new List<string>();
clone._removedProperties = new List<string>();
clone._properties = new MacroPropertyCollection();
clone._properties = (MacroPropertyCollection)Properties.DeepClone();
//re-assign the event handler
clone._properties.CollectionChanged += clone.PropertiesChanged;
clone.ResetDirtyProperties(false);
+33 -1
View File
@@ -11,7 +11,7 @@ namespace Umbraco.Core.Models
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
public class MacroProperty : TracksChangesEntityBase, IMacroProperty, IRememberBeingDirty
public class MacroProperty : TracksChangesEntityBase, IMacroProperty, IRememberBeingDirty, IDeepCloneable
{
public MacroProperty()
{
@@ -176,5 +176,37 @@ namespace Umbraco.Core.Models
}, _editorAlias, PropertyTypeSelector);
}
}
public object DeepClone()
{
//Memberwise clone on MacroProperty will work since it doesn't have any deep elements
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
var clone = (MacroProperty)MemberwiseClone();
//Automatically deep clone ref properties that are IDeepCloneable
DeepCloneHelper.DeepCloneRefProperties(this, clone);
clone.ResetDirtyProperties(false);
return clone;
}
protected bool Equals(MacroProperty other)
{
return string.Equals(_alias, other._alias) && _id == other._id;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((MacroProperty) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((_alias != null ? _alias.GetHashCode() : 0)*397) ^ _id;
}
}
}
}
@@ -8,13 +8,60 @@ namespace Umbraco.Core.Models
/// <summary>
/// A macro's property collection
/// </summary>
public class MacroPropertyCollection : ObservableDictionary<string, IMacroProperty>
public class MacroPropertyCollection : ObservableDictionary<string, IMacroProperty>, IDeepCloneable
{
public MacroPropertyCollection()
: base(property => property.Alias)
{
}
public object DeepClone()
{
var clone = new MacroPropertyCollection();
foreach (var item in this)
{
clone.Add((IMacroProperty)item.DeepClone());
}
return clone;
}
/// <summary>
/// Used to update an existing macro property
/// </summary>
/// <param name="name"></param>
/// <param name="sortOrder"></param>
/// <param name="editorAlias"></param>
/// <param name="currentAlias">
/// The existing property alias
/// </param>
/// <param name="newAlias"></param>
public void UpdateProperty(string currentAlias, string name = null, int? sortOrder = null, string editorAlias = null, string newAlias = null)
{
var prop = this[currentAlias];
if (prop == null)
{
throw new InvalidOperationException("No property exists with alias " + currentAlias);
}
if (name.IsNullOrWhiteSpace() == false)
{
prop.Name = name;
}
if (sortOrder.HasValue)
{
prop.SortOrder = sortOrder.Value;
}
if (name.IsNullOrWhiteSpace() == false)
{
prop.EditorAlias = editorAlias;
}
if (newAlias.IsNullOrWhiteSpace() == false && currentAlias != newAlias)
{
prop.Alias = newAlias;
ChangeKey(currentAlias, newAlias);
}
}
}
}
+8 -1
View File
@@ -3,7 +3,7 @@
/// <summary>
/// Represents a stored pre-value field value
/// </summary>
public class PreValue
public class PreValue : IDeepCloneable
{
public PreValue(int id, string value, int sortOrder)
{
@@ -37,5 +37,12 @@
/// The sort order stored for the pre-value field value
/// </summary>
public int SortOrder { get; private set; }
public virtual object DeepClone()
{
//Memberwise clone on PreValue will work since it doesn't have any deep elements
var clone = (PreValue)MemberwiseClone();
return clone;
}
}
}
+17 -1
View File
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Models
/// Most legacy property editors won't support the dictionary format but new property editors should always use the dictionary format.
/// In order to get overrideable pre-values working we need a dictionary since we'll have to reference a pre-value by a key.
/// </remarks>
public class PreValueCollection
public class PreValueCollection : IDeepCloneable
{
private IDictionary<string, PreValue> _preValuesAsDictionary;
private IEnumerable<PreValue> _preValuesAsArray;
@@ -82,5 +82,21 @@ namespace Umbraco.Core.Models
}
return result;
}
public object DeepClone()
{
var clone = (PreValueCollection) MemberwiseClone();
if (_preValuesAsArray != null)
{
clone._preValuesAsArray = _preValuesAsArray.Select(x => (PreValue)x.DeepClone()).ToArray();
}
if (_preValuesAsDictionary != null)
{
clone._preValuesAsDictionary = _preValuesAsDictionary.ToDictionary(x => x.Key, x => (PreValue)x.Value.DeepClone());
}
return clone;
}
}
}
@@ -0,0 +1,9 @@
namespace Umbraco.Core.Models
{
internal enum PropertyTagBehavior
{
Replace,
Remove,
Merge
}
}
-7
View File
@@ -33,11 +33,4 @@ namespace Umbraco.Core.Models
public IEnumerable<Tuple<string, string>> Tags { get; set; }
}
internal enum PropertyTagBehavior
{
Replace,
Remove,
Merge
}
}
@@ -0,0 +1,8 @@
namespace Umbraco.Core.Models
{
public enum TagCacheStorageType
{
Csv,
Json
}
}
@@ -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
{
+22
View File
@@ -135,6 +135,28 @@ namespace Umbraco.Core
}
/// <summary>
/// Allows us to change the key of an item
/// </summary>
/// <param name="currentKey"></param>
/// <param name="newKey"></param>
public virtual void ChangeKey(TKey currentKey, TKey newKey)
{
if (!Indecies.ContainsKey(currentKey))
{
throw new InvalidOperationException("No item with the key " + currentKey + "was found in the collection");
}
if (ContainsKey(newKey))
{
throw new DuplicateKeyException(newKey.ToString());
}
var currentIndex = Indecies[currentKey];
Indecies.Remove(currentKey);
Indecies.Add(newKey, currentIndex);
}
internal class DuplicateKeyException : Exception
{
@@ -128,9 +128,9 @@ namespace Umbraco.Core.Persistence.Caching
public void Save(Type type, IEntity entity)
{
//IMPORTANT: we must clone to store, see: http://issues.umbraco.org/issue/U4-4259
entity = (IEntity)entity.DeepClone();
var clone = (IEntity)entity.DeepClone();
var key = GetCompositeId(type, entity.Id);
var key = GetCompositeId(type, clone.Id);
_keyTracker.TryAdd(key);
@@ -139,11 +139,11 @@ namespace Umbraco.Core.Persistence.Caching
if (_memoryCache != null)
{
_memoryCache.Set(key, entity, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
_memoryCache.Set(key, clone, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(5) });
}
else
{
HttpRuntime.Cache.Insert(key, entity, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
HttpRuntime.Cache.Insert(key, clone, null, System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(5));
}
}
@@ -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();
@@ -493,14 +493,17 @@ namespace Umbraco.Core.Persistence.Repositories
// Is this the same UmbracoEntity as the current one we're processing
if (Current != null && Current.Key == a.uniqueID)
{
// Add this UmbracoProperty to the current additional data
Current.AdditionalData[p.PropertyAlias] = new UmbracoEntity.EntityProperty
if (p != null && p.PropertyAlias.IsNullOrWhiteSpace() == false)
{
PropertyEditorAlias = p.PropertyEditorAlias,
Value = p.NTextValue.IsNullOrWhiteSpace()
? p.NVarcharValue
: p.NTextValue.ConvertToJsonIfPossible()
};
// Add this UmbracoProperty to the current additional data
Current.AdditionalData[p.PropertyAlias] = new UmbracoEntity.EntityProperty
{
PropertyEditorAlias = p.PropertyEditorAlias,
Value = p.NTextValue.IsNullOrWhiteSpace()
? p.NVarcharValue
: p.NTextValue.ConvertToJsonIfPossible()
};
}
// Return null to indicate we're not done with this UmbracoEntity yet
return null;
@@ -516,14 +519,17 @@ namespace Umbraco.Core.Persistence.Repositories
Current = _factory.BuildEntityFromDynamic(a);
//add the property/create the prop list if null
Current.AdditionalData[p.PropertyAlias] = new UmbracoEntity.EntityProperty
if (p != null && p.PropertyAlias.IsNullOrWhiteSpace() == false)
{
PropertyEditorAlias = p.PropertyEditorAlias,
Value = p.NTextValue.IsNullOrWhiteSpace()
? p.NVarcharValue
: p.NTextValue.ConvertToJsonIfPossible()
};
//add the property/create the prop list if null
Current.AdditionalData[p.PropertyAlias] = new UmbracoEntity.EntityProperty
{
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"
+28 -16
View File
@@ -44,9 +44,9 @@ namespace Umbraco.Core
/// file is cached temporarily until app startup completes.
/// </summary>
/// <param name="appContext"></param>
/// <param name="detectBinChanges"></param>
internal PluginManager(ApplicationContext appContext, bool detectBinChanges = true)
: this(detectBinChanges)
/// <param name="detectChanges"></param>
internal PluginManager(ApplicationContext appContext, bool detectChanges = true)
: this(detectChanges)
{
if (appContext == null) throw new ArgumentNullException("appContext");
_appContext = appContext;
@@ -55,11 +55,11 @@ namespace Umbraco.Core
/// <summary>
/// Creates a new PluginManager
/// </summary>
/// <param name="detectCodeChanges">
/// If true will detect changes in the /bin folder and therefor load plugins from the
/// <param name="detectChanges">
/// If true will detect changes in the /bin folder, app_code, etc... and therefor load plugins from the
/// cached plugins file if one is found. If false will never use the cache file for plugins
/// </param>
internal PluginManager(bool detectCodeChanges = true)
internal PluginManager(bool detectChanges = true)
{
_tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
//create the folder if it doesn't exist
@@ -68,29 +68,41 @@ namespace Umbraco.Core
Directory.CreateDirectory(_tempFolder);
}
var pluginListFile = GetPluginListFilePath();
//this is a check for legacy changes, before we didn't store the TypeResolutionKind in the file which was a mistake,
//so we need to detect if the old file is there without this attribute, if it is then we delete it
if (DetectLegacyPluginListFile())
{
var filePath = GetPluginListFilePath();
File.Delete(filePath);
File.Delete(pluginListFile);
}
if (detectCodeChanges)
if (detectChanges)
{
//first check if the cached hash is 0, if it is then we ne
//do the check if they've changed
HaveAssembliesChanged = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == 0;
RequiresRescanning = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == 0;
//if they have changed, we need to write the new file
if (HaveAssembliesChanged)
if (RequiresRescanning)
{
//if the hash has changed, clear out the persisted list no matter what, this will force
// rescanning of all plugin types including lazy ones.
// http://issues.umbraco.org/issue/U4-4789
File.Delete(pluginListFile);
WriteCachePluginsHash();
}
}
else
{
//if the hash has changed, clear out the persisted list no matter what, this will force
// rescanning of all plugin types including lazy ones.
// http://issues.umbraco.org/issue/U4-4789
File.Delete(pluginListFile);
//always set to true if we're not detecting (generally only for testing)
HaveAssembliesChanged = true;
RequiresRescanning = true;
}
}
@@ -130,9 +142,9 @@ namespace Umbraco.Core
/// <summary>
/// Returns a bool if the assemblies in the /bin have changed since they were last hashed.
/// Returns a bool if the assemblies in the /bin, app_code, global.asax, etc... have changed since they were last hashed.
/// </summary>
internal bool HaveAssembliesChanged { get; private set; }
internal bool RequiresRescanning { get; private set; }
/// <summary>
/// Returns the currently cached hash value of the scanned assemblies in the /bin folder. Returns 0
@@ -328,7 +340,7 @@ namespace Umbraco.Core
/// <remarks>
/// Generally only used for resetting cache, for example during the install process
/// </remarks>
internal void ClearPluginCache()
public void ClearPluginCache()
{
var path = GetPluginListFilePath();
if (File.Exists(path))
@@ -670,7 +682,7 @@ namespace Umbraco.Core
//we first need to look into our cache file (this has nothing to do with the 'cacheResult' parameter which caches in memory).
//if assemblies have not changed and the cache file actually exists, then proceed to try to lookup by the cache file.
if (HaveAssembliesChanged == false && File.Exists(GetPluginListFilePath()))
if (RequiresRescanning == false && File.Exists(GetPluginListFilePath()))
{
var fileCacheResult = TryGetCachedPluginsFromFile<T>(resolutionType);
@@ -1,4 +1,5 @@
using System;
using Umbraco.Core.Models;
namespace Umbraco.Core.PropertyEditors
{
@@ -30,6 +31,7 @@ namespace Umbraco.Core.PropertyEditors
Delimiter = ",";
ReplaceTags = true;
TagGroup = "default";
StorageType = TagCacheStorageType.Csv;
}
/// <summary>
@@ -37,6 +39,11 @@ namespace Umbraco.Core.PropertyEditors
/// </summary>
public TagValueType ValueType { get; set; }
/// <summary>
/// Defines how to store the tags in cache (CSV or Json)
/// </summary>
public TagCacheStorageType StorageType { get; set; }
/// <summary>
/// Defines a custom delimiter, the default is a comma
/// </summary>
@@ -1,4 +1,5 @@
using Umbraco.Core.Models.Editors;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Editors;
namespace Umbraco.Core.PropertyEditors
{
@@ -30,8 +31,14 @@ namespace Umbraco.Core.PropertyEditors
Delimiter = tagsAttribute.Delimiter;
ReplaceTags = tagsAttribute.ReplaceTags;
TagGroup = tagsAttribute.TagGroup;
StorageType = TagCacheStorageType.Csv;
}
/// <summary>
/// Defines how to store the tags in cache (CSV or Json)
/// </summary>
public virtual TagCacheStorageType StorageType { get; private set; }
/// <summary>
/// Defines a custom delimiter, the default is a comma
/// </summary>
@@ -86,6 +86,7 @@ namespace Umbraco.Core.Security
public static UmbracoBackOfficeIdentity GetCurrentIdentity(this HttpContextBase http, bool authenticateRequestIfNotFound)
{
if (http == null) throw new ArgumentNullException("http");
if (http.User == null) return null; //there's no user at all so no identity
var identity = http.User.Identity as UmbracoBackOfficeIdentity;
if (identity != null) return identity;
if (authenticateRequestIfNotFound == false) return null;
+3 -1
View File
@@ -124,7 +124,9 @@ namespace Umbraco.Core.Services
{
var collection = repository.GetPreValuesCollectionByDataTypeId(id);
//now convert the collection to a string list
var list = collection.FormatAsDictionary().Select(x => x.Value.Value).ToList();
var list = collection.FormatAsDictionary()
.Select(x => x.Value.Value)
.ToList();
return list;
}
}
+1 -1
View File
@@ -213,7 +213,7 @@ namespace Umbraco.Core
//this is from SqlMetal and just makes it a bit of fun to allow pluralisation
public static string MakePluralName(this string name)
{
if ((name.EndsWith("x", StringComparison.OrdinalIgnoreCase) || name.EndsWith("ch", StringComparison.OrdinalIgnoreCase)) || (name.EndsWith("ss", StringComparison.OrdinalIgnoreCase) || name.EndsWith("sh", StringComparison.OrdinalIgnoreCase)))
if ((name.EndsWith("x", StringComparison.OrdinalIgnoreCase) || name.EndsWith("ch", StringComparison.OrdinalIgnoreCase)) || (name.EndsWith("s", StringComparison.OrdinalIgnoreCase) || name.EndsWith("sh", StringComparison.OrdinalIgnoreCase)))
{
name = name + "es";
return name;
+2
View File
@@ -349,12 +349,14 @@
<Compile Include="Models\MemberGroup.cs" />
<Compile Include="Models\MemberTypePropertyProfileAccess.cs" />
<Compile Include="Models\Notification.cs" />
<Compile Include="Models\PropertyTagBehavior.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentExtended.cs" />
<Compile Include="Models\PublishedContent\PublishedPropertyBase.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelFactoryImpl.cs" />
<Compile Include="Models\PublishedContent\IPublishedContentModelFactory.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModel.cs" />
<Compile Include="Models\PublishedContent\PublishedContentModelFactoryResolver.cs" />
<Compile Include="Models\TagCacheStorageType.cs" />
<Compile Include="Models\TaggableObjectTypes.cs" />
<Compile Include="Models\TemplateNode.cs" />
<Compile Include="Models\UmbracoEntityExtensions.cs" />
@@ -12,6 +12,8 @@ namespace Umbraco.Tests.Models
[TestFixture]
public class ContentTypeTests : BaseUmbracoConfigurationTest
{
[Test]
public void Can_Deep_Clone_Content_Type_Sort()
{
@@ -150,6 +152,7 @@ namespace Umbraco.Tests.Models
}
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));
+13
View File
@@ -1,3 +1,4 @@
using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
@@ -19,6 +20,7 @@ namespace Umbraco.Tests.Models
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();
@@ -26,6 +28,14 @@ namespace Umbraco.Tests.Models
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);
@@ -44,6 +54,9 @@ namespace Umbraco.Tests.Models
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()
@@ -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,
@@ -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,
@@ -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]
@@ -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()
//{
+1
View File
@@ -191,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" />
@@ -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
});
}
};
});
@@ -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
@@ -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
@@ -1,10 +1,20 @@
angular.module("umbraco")
.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 () {
@@ -16,38 +26,44 @@ angular.module("umbraco")
});
//perform the path change, if it is successful then the promise will resolve otherwise it will fail
$location.path("/logout");
};
$location.path("/logout");
};
$scope.gotoHistory = function (link) {
$location.path(link);
$scope.hide();
};
$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>
@@ -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);
@@ -1,4 +1,5 @@
<div ng-controller="Umbraco.PropertyEditors.RTEController" class="umb-editor umb-rte">
<div ng-if="isLoading">Loading...</div>
<textarea ng-style="{ visibility : isLoading ? 'hidden' : 'visible'}" ng-model="model.value" rows="10" id="{{model.alias}}_rte"></textarea>
<textarea ng-style="{ visibility : isLoading ? 'hidden' : 'visible'}"
ng-model="model.value" rows="10" id="{{model.alias}}_rte"></textarea>
</div>
@@ -12,13 +12,21 @@ angular.module("umbraco")
//load current value
$scope.currentTags = [];
if ($scope.model.value) {
$scope.currentTags = $scope.model.value.split(",");
if ($scope.model.config.storageType && $scope.model.config.storageType === "Json") {
//it's a json array already
$scope.currentTags = $scope.model.value;
}
else {
//it is csv
$scope.currentTags = $scope.model.value.split(",");
}
}
//Helper method to add a tag on enter or on typeahead select
function addTag(tagToAdd) {
if (tagToAdd.length > 0) {
if ($scope.currentTags.indexOf(tagToAdd) < 0) {
if ($scope.currentTags.indexOf(tagToAdd) < 0) {
$scope.currentTags.push(tagToAdd);
}
}
@@ -34,6 +42,7 @@ angular.module("umbraco")
e.preventDefault();
//we need to use jquery because typeahead duplicates the text box
addTag($scope.tagToAdd);
$scope.tagToAdd = "";
}
}
@@ -46,16 +55,24 @@ angular.module("umbraco")
}
};
//sync model on submit (needed since we convert an array to string)
//sync model on submit, always push up a json array
$scope.$on("formSubmitting", function (ev, args) {
$scope.model.value = $scope.currentTags.join();
$scope.model.value = $scope.currentTags;
});
//vice versa
$scope.model.onValueChanged = function (newVal, oldVal) {
//update the display val again if it has changed from the server
$scope.model.val = newVal;
$scope.currentTags = $scope.model.value.split(",");
$scope.model.value = newVal;
if ($scope.model.config.storageType && $scope.model.config.storageType === "Json") {
//it's a json array already
$scope.currentTags = $scope.model.value;
}
else {
//it is csv
$scope.currentTags = $scope.model.value.split(",");
}
};
//configure the tags data source
@@ -111,15 +128,17 @@ angular.module("umbraco")
}).bind("typeahead:selected", function (obj, datum, name) {
angularHelper.safeApply($scope, function () {
addTag(datum["value"]);
$scope.tagToAdd = "";
});
}).bind("typeahead:autocompleted", function (obj, datum, name) {
angularHelper.safeApply($scope, function () {
addTag(datum["value"]);
$scope.tagToAdd = "";
});
}).bind("typeahead:opened", function (obj) {
console.log("opened ");
//console.log("opened ");
});
});
@@ -1,19 +1,19 @@
<div ng-controller="Umbraco.PropertyEditors.TagsController" class="umb-editor umb-tags">
<div ng-if="isLoading">
Loading...
</div>
<ng-form>
<div ng-if="!isLoading">
<span ng-repeat="tag in currentTags" ng-click="removeTag(tag)" class="label label-primary tag">{{tag}} <i class="icon icon-delete"></i></span>
<div ng-if="!isLoading">
<span ng-repeat="tag in currentTags" ng-click="removeTag(tag)" class="label label-primary tag">
<span ng-bind-html="tag"></span>
<i class="icon icon-delete"></i>
</span>
<input type="text"
class="typeahead tags-{{model.alias}}"
ng-keydown="addTag($event)"
placeholder="Type to add tags.."
ng-model="tagToAdd" />
</div>
</ng-form>
<input type="text"
class="typeahead tags-{{model.alias}}"
ng-model="$parent.tagToAdd"
ng-keydown="$parent.addTag($event)"
placeholder="Type to add tags.." />
</div>
</div>
@@ -0,0 +1,10 @@
<div >
<select name="dropDownList"
class="umb-editor umb-dropdown"
ng-model="model.value">
<option>Csv</option>
<option>Json</option>
</select>
</div>
@@ -31,7 +31,7 @@ describe('umbPhotoFolderHelper tests', function () {
});
it('Build a row by removing an item to scale up to fit', function () {
it('Builds a row by removing an item to scale up to fit', function () {
var images = [
{ "properties": [{ "id": 8737, "value": "/media/2173/Save-The-Date.jpg", "alias": "umbracoFile" }, { "id": 8738, "value": "443", "alias": "umbracoWidth" }, { "id": 8739, "value": "500", "alias": "umbracoHeight" }, { "id": 8740, "value": "30830", "alias": "umbracoBytes" }, { "id": 8741, "value": "jpg", "alias": "umbracoExtension" }], "updateDate": "2013-12-10 16:57:56", "createDate": "2013-12-10 14:21:26", "published": false, "owner": { "id": 0, "name": "admin" }, "updator": null, "contentTypeAlias": "Image", "sortOrder": 5, "name": "Save-The-Date.jpg", "id": 1349, "icon": "mediaPhoto.gif", "key": "8eb67ae3-49da-4a25-ab39-185667a9b412", "parentId": 1160, "alias": null, "path": "-1,1142,1160,1349", "metaData": {}, "thumbnail": "/umbraco/UmbracoApi/Images/GetBigThumbnail?mediaId=1349", "originalWidth": 443, "originalHeight": 500 },
@@ -52,6 +52,26 @@ describe('umbPhotoFolderHelper tests', function () {
expect(result.images.length).toBe(4);
});
it('Builds a row by removing an item to scale up to fit, then attempts to upscale remaining 2 images, but that doesnt fit so drops another and we end up with one', function () {
var images = [
{ "properties": [{ "id": 8737, "value": "/media/2173/Save-The-Date.jpg", "alias": "umbracoFile" }, { "id": 8738, "value": "198", "alias": "umbracoWidth" }, { "id": 8739, "value": "220", "alias": "umbracoHeight" }, { "id": 8740, "value": "30830", "alias": "umbracoBytes" }, { "id": 8741, "value": "jpg", "alias": "umbracoExtension" }], "updateDate": "2013-12-10 16:57:56", "createDate": "2013-12-10 14:21:26", "published": false, "owner": { "id": 0, "name": "admin" }, "updator": null, "contentTypeAlias": "Image", "sortOrder": 5, "name": "Save-The-Date.jpg", "id": 1349, "icon": "mediaPhoto.gif", "key": "8eb67ae3-49da-4a25-ab39-185667a9b412", "parentId": 1160, "alias": null, "path": "-1,1142,1160,1349", "metaData": {}, "thumbnail": "/umbraco/UmbracoApi/Images/GetBigThumbnail?mediaId=1349", "originalWidth": 198, "originalHeight": 220 },
{ "properties": [{ "id": 8742, "value": "/media/2174/IMG_2980.JPG", "alias": "umbracoFile" }, { "id": 8743, "value": "211", "alias": "umbracoWidth" }, { "id": 8744, "value": "500", "alias": "umbracoHeight" }, { "id": 8745, "value": "113311", "alias": "umbracoBytes" }, { "id": 8746, "value": "jpg", "alias": "umbracoExtension" }], "updateDate": "2013-12-10 16:57:51", "createDate": "2013-12-10 14:22:33", "published": false, "owner": { "id": 0, "name": "admin" }, "updator": null, "contentTypeAlias": "Image", "sortOrder": 6, "name": "IMG_2980.JPG", "id": 1350, "icon": "mediaPhoto.gif", "key": "0a9618ea-9b4a-4d34-bf53-e76a0d252048", "parentId": 1160, "alias": null, "path": "-1,1142,1160,1350", "metaData": {}, "thumbnail": "/umbraco/UmbracoApi/Images/GetBigThumbnail?mediaId=1350", "originalWidth": 211, "originalHeight": 500 },
{ "properties": [{ "id": 8747, "value": "/media/2175/IMG_3023.JPG", "alias": "umbracoFile" }, { "id": 8748, "value": "940", "alias": "umbracoWidth" }, { "id": 8749, "value": "317", "alias": "umbracoHeight" }, { "id": 8750, "value": "106365", "alias": "umbracoBytes" }, { "id": 8751, "value": "jpg", "alias": "umbracoExtension" }], "updateDate": "2013-12-10 16:57:46", "createDate": "2013-12-10 14:39:28", "published": false, "owner": { "id": 0, "name": "admin" }, "updator": null, "contentTypeAlias": "Image", "sortOrder": 7, "name": "IMG_3023.JPG", "id": 1351, "icon": "mediaPhoto.gif", "key": "44cb1ee0-e3d7-40f7-b27c-ae05fb1a8e0c", "parentId": 1160, "alias": null, "path": "-1,1142,1160,1351", "metaData": {}, "thumbnail": "/umbraco/UmbracoApi/Images/GetBigThumbnail?mediaId=1351", "originalWidth": 940, "originalHeight": 317 }
];
var maxRowHeight = 250;
var minDisplayHeight = 105;
var maxRowWidth = 400;
var idealImgPerRow = 3;
var margin = 5;
var result = umbPhotoFolderHelper.buildRow(images, maxRowHeight, minDisplayHeight, maxRowWidth, idealImgPerRow, margin);
expect(result.images.length).toBe(1);
});
});
+6 -3
View File
@@ -2530,9 +2530,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7120</DevelopmentServerPort>
<DevelopmentServerPort>7130</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7120</IISUrl>
<IISUrl>http://localhost:7130</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -2564,7 +2564,10 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
</Target>
<Target Name="BelleBuild" BeforeTargets="Rebuild">
<!-- Only runs when a Rebuild is requested -->
<Exec WorkingDirectory="$(ProjectDir)\..\..\build\" Command="BuildBelle.bat" ConsoleToMSBuild="true" IgnoreExitCode="true" ContinueOnError="WarnAndContinue" />
<ReadLinesFromFile File="$(ProjectDir)\..\..\build\UmbracoVersion.txt">
<Output TaskParameter="Lines" PropertyName="VersionNumber" />
</ReadLinesFromFile>
<Exec WorkingDirectory="$(ProjectDir)\..\..\build\" Command="BuildBelle.bat $(VersionNumber)" ConsoleToMSBuild="true" IgnoreExitCode="true" ContinueOnError="WarnAndContinue" />
</Target>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
@@ -15,7 +15,7 @@
<!-- SlideShare Settings -->
<provider name="SlideShare" type="Umbraco.Web.Media.EmbedProviders.OEmbedRich, umbraco">
<urlShemeRegex><![CDATA[slideshare\.net/]]></urlShemeRegex>
<apiEndpoint><![CDATA[http://www.slideshare.net/api/oembed/1]]></apiEndpoint>
<apiEndpoint><![CDATA[http://www.slideshare.net/api/oembed/2]]></apiEndpoint>
<requestParams type="Umbraco.Web.Media.EmbedProviders.Settings.Dictionary, umbraco">
<param name="format">xml</param>
</requestParams>
@@ -15,7 +15,7 @@
<!-- SlideShare Settings -->
<provider name="SlideShare" type="Umbraco.Web.Media.EmbedProviders.OEmbedRich, umbraco">
<urlShemeRegex><![CDATA[slideshare\.net/]]></urlShemeRegex>
<apiEndpoint><![CDATA[http://www.slideshare.net/api/oembed/1]]></apiEndpoint>
<apiEndpoint><![CDATA[http://www.slideshare.net/api/oembed/2]]></apiEndpoint>
<requestParams type="Umbraco.Web.Media.EmbedProviders.Settings.Dictionary, umbraco">
<param name="format">xml</param>
</requestParams>
@@ -23,20 +23,7 @@
$(".fileChooser input[type='text']").not(txt).val("");
//reset other drop downs
$(".fileChooser select").not($(this)).val("");
});
//disable 'add macro property validators' when save or delete is clicked
$("#body_save,.delete-button").click(function (evt) {
$(".add-validator").each(function() {
ValidatorEnable($(this).get(0), false);
});
});
//enable the addmacro property validators when the add button is clicked
$(".add-button").click(function(evt) {
$(".add-validator").each(function () {
ValidatorEnable($(this).get(0), true);
});
});
});
UmbClientMgr.appActions().bindSaveShortCut();
@@ -182,15 +169,15 @@
<FooterTemplate>
<tr>
<td>
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" CssClass="add-validator" runat="server" ControlToValidate="macroPropertyAliasNew" Display="Dynamic" ForeColor="#b94a48">Required<br/></asp:RequiredFieldValidator>
<asp:TextBox runat="server" ID="macroPropertyAliasNew" PlaceHolder='New Alias' OnTextChanged="macroPropertyCreate" />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" EnableViewState="false" Enabled="false" EnableClientScript="false" runat="server" ControlToValidate="macroPropertyAliasNew" Display="Dynamic" ForeColor="#b94a48">Required<br/></asp:RequiredFieldValidator>
<asp:TextBox runat="server" ID="macroPropertyAliasNew" PlaceHolder='New Alias' />
</td>
<td>
<asp:RequiredFieldValidator ID="RequiredFieldValidator4" CssClass="add-validator" runat="server" ControlToValidate="macroPropertyNameNew" Display="Dynamic" ForeColor="#b94a48">Required<br/></asp:RequiredFieldValidator>
<asp:RequiredFieldValidator ID="RequiredFieldValidator4" EnableViewState="false" Enabled="false" EnableClientScript="false" runat="server" ControlToValidate="macroPropertyNameNew" Display="Dynamic" ForeColor="#b94a48">Required<br/></asp:RequiredFieldValidator>
<asp:TextBox runat="server" ID="macroPropertyNameNew" PlaceHolder='New Name' />
</td>
<td>
<asp:RequiredFieldValidator ID="RequiredFieldValidator5" CssClass="add-validator" runat="server" ControlToValidate="macroPropertyTypeNew" Display="Dynamic" ForeColor="#b94a48">Required<br/></asp:RequiredFieldValidator>
<asp:RequiredFieldValidator ID="RequiredFieldValidator5" EnableViewState="false" Enabled="false" EnableClientScript="false" runat="server" ControlToValidate="macroPropertyTypeNew" Display="Dynamic" ForeColor="#b94a48">Required<br/></asp:RequiredFieldValidator>
<asp:DropDownList OnPreRender="AddChooseList" runat="server" ID="macroPropertyTypeNew"
DataTextField="Name"
DataValueField="Alias"
@@ -198,7 +185,7 @@
</asp:DropDownList>
</td>
<td>
<asp:Button ID="createNew" Text="Add" runat="server" CssClass="btn btn-default add-button" />
<asp:Button ID="createNew" Text="Add" runat="server" CssClass="btn btn-default add-button" OnClick="macroPropertyCreate" />
</td>
</tr>
</tbody>
@@ -17,25 +17,30 @@ function duplicatePropertyNameAsSafeAlias(propertySelector) {
var prop = $(this);
var inputName = prop.find('.prop-name');
var inputAlias = prop.find('.prop-alias');
inputName.on('input', function (event) {
inputName.on('input blur', function (event) {
getSafeAlias(inputAlias, inputName.val(), false, function (alias) {
inputAlias.val(alias);
if (!inputAlias.data('dirty'))
inputAlias.val(alias);
});
}).on('blur', function (event) {
$(this).off('input');
});
inputAlias.on('input', function(event) {
inputName.off('input blur');
});
});
}
function checkAlias(aliasSelector) {
$(aliasSelector).keyup(function (event) {
$(aliasSelector).on('input', function (event) {
var input = $(this);
input.data('dirty', true);
var value = input.val();
validateSafeAlias(input, value, false, function (isSafe) {
input.toggleClass('highlight-error', !isSafe);
});
}).blur(function(event) {
}).on('blur', function(event) {
var input = $(this);
if (!input.data('dirty')) return;
input.removeData('dirty');
var value = input.val();
getSafeAlias(input, value, true, function (alias) {
if (value.toLowerCase() != alias.toLowerCase())
@@ -7,6 +7,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.Caching;
using umbraco.interfaces;
using System.Linq;
@@ -155,6 +156,7 @@ namespace Umbraco.Web.Cache
payloads.ForEach(payload =>
{
//if there's no path, then just use id (this will occur on permanent deletion like emptying recycle bin)
if (payload.Path.IsNullOrWhiteSpace())
{
@@ -165,6 +167,12 @@ namespace Umbraco.Web.Cache
{
foreach (var idPart in payload.Path.Split(','))
{
int idPartAsInt;
if (int.TryParse(idPart, out idPartAsInt))
{
RuntimeCacheProvider.Current.Delete(typeof(IMedia), idPartAsInt);
}
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(
string.Format("{0}_{1}_True", CacheKeys.MediaCacheKey, idPart));
@@ -3,22 +3,16 @@ using System.Web.Script.Serialization;
using Umbraco.Core.Cache;
using Umbraco.Core.Models;
using System.Linq;
using Umbraco.Core.Persistence.Caching;
using Umbraco.Core.Sync;
namespace Umbraco.Web.Cache
{
/// <summary>
/// A cache refresher used for non-published content, this is primarily to notify Examine indexes to update
/// A cache refresher used for non-published content, this is primarily to notify Examine indexes to update and to refresh the RuntimeCacheRefresher
/// </summary>
public sealed class UnpublishedPageCacheRefresher : TypedCacheRefresherBase<UnpublishedPageCacheRefresher, IContent>, IJsonCacheRefresher
{
//NOTE: There is no functionality for this cache refresher, it is here simply to emit events on each server for which examine
// binds to. We could put the Examine index functionality in here but we've kept it all in the ExamineEvents class so that all of
// the logic is in one place. In the future we may put the examine logic in a cache refresher instead (that would make sense) but we'd
// want to get this done before making more cache refreshers:
// http://issues.umbraco.org/issue/U4-2633
protected override UnpublishedPageCacheRefresher Instance
{
get { return this; }
@@ -78,12 +72,48 @@ namespace Umbraco.Web.Cache
#endregion
public override void RefreshAll()
{
RuntimeCacheProvider.Current.Clear(typeof(IContent));
base.RefreshAll();
}
public override void Refresh(int id)
{
RuntimeCacheProvider.Current.Delete(typeof(IContent), id);
base.Refresh(id);
}
public override void Remove(int id)
{
RuntimeCacheProvider.Current.Delete(typeof(IContent), id);
base.Remove(id);
}
public override void Refresh(IContent instance)
{
RuntimeCacheProvider.Current.Delete(typeof(IContent), instance.Id);
base.Refresh(instance);
}
public override void Remove(IContent instance)
{
RuntimeCacheProvider.Current.Delete(typeof(IContent), instance.Id);
base.Remove(instance);
}
/// <summary>
/// Implement the IJsonCacheRefresher so that we can bulk delete the cache based on multiple IDs for when the recycle bin is emptied
/// </summary>
/// <param name="jsonPayload"></param>
public void Refresh(string jsonPayload)
{
foreach (var payload in DeserializeFromJsonPayload(jsonPayload))
{
RuntimeCacheProvider.Current.Delete(typeof(IContent), payload.Id);
}
OnCacheUpdated(Instance, new CacheRefresherEventArgs(jsonPayload, MessageType.RefreshByJson));
}
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Helpers;
using System.Web.Http;
@@ -59,18 +60,14 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <returns></returns>
[HttpGet]
public HttpResponseMessage IsAuthenticated()
public bool IsAuthenticated()
{
var attempt = UmbracoContext.Security.AuthorizeRequest();
if (attempt == ValidateRequestAttempt.Success)
{
return Request.CreateResponse(HttpStatusCode.OK);
}
//return BadRequest (400), we don't want to return a 401 because that get's intercepted
// by our angular helper because it thinks that we need to re-perform the request once we are
// authorized and we don't want to return a 403 because angular will show a warning msg indicating
// that the user doesn't have access to perform this function, we just want to return a normal invalid msg.
return Request.CreateResponse(HttpStatusCode.BadRequest);
return true;
}
return false;
}
+5 -5
View File
@@ -43,15 +43,15 @@ namespace Umbraco.Web.Editors
LogHelper.Error<TagExtractor>("Could not create custom " + attribute.TagPropertyDefinitionType + " tag definition", ex);
throw;
}
SetPropertyTags(content, property, convertedPropertyValue, def.Delimiter, def.ReplaceTags, def.TagGroup, attribute.ValueType);
SetPropertyTags(content, property, convertedPropertyValue, def.Delimiter, def.ReplaceTags, def.TagGroup, attribute.ValueType, def.StorageType);
}
else
{
SetPropertyTags(content, property, convertedPropertyValue, attribute.Delimiter, attribute.ReplaceTags, attribute.TagGroup, attribute.ValueType);
SetPropertyTags(content, property, convertedPropertyValue, attribute.Delimiter, attribute.ReplaceTags, attribute.TagGroup, attribute.ValueType, attribute.StorageType);
}
}
public static void SetPropertyTags(IContentBase content, Property property, object convertedPropertyValue, string delimiter, bool replaceTags, string tagGroup, TagValueType valueType)
public static void SetPropertyTags(IContentBase content, Property property, object convertedPropertyValue, string delimiter, bool replaceTags, string tagGroup, TagValueType valueType, TagCacheStorageType storageType)
{
if (convertedPropertyValue == null)
{
@@ -62,14 +62,14 @@ namespace Umbraco.Web.Editors
{
case TagValueType.FromDelimitedValue:
var tags = convertedPropertyValue.ToString().Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries);
content.SetTags(property.Alias, tags, replaceTags, tagGroup);
content.SetTags(storageType, property.Alias, tags, replaceTags, tagGroup);
break;
case TagValueType.CustomTagList:
//for this to work the object value must be IENumerable<string>
var stringList = convertedPropertyValue as IEnumerable<string>;
if (stringList != null)
{
content.SetTags(property.Alias, stringList, replaceTags, tagGroup);
content.SetTags(storageType, property.Alias, stringList, replaceTags, tagGroup);
}
break;
}
@@ -2,7 +2,9 @@ using System;
using System.Globalization;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
using umbraco;
using umbraco.BusinessLogic.Actions;
@@ -10,28 +12,64 @@ using umbraco.interfaces;
namespace Umbraco.Web
{
//NOTE: all of these require an UmbracoContext because currently to send the notifications we need an HttpContext, this is based on legacy code
// for which probably requires updating so that these can be sent outside of the http context.
internal static class NotificationServiceExtensions
{
internal static void SendNotification(this INotificationService service, IUmbracoEntity entity, IAction action, ApplicationContext applicationContext)
{
if (global::Umbraco.Web.UmbracoContext.Current == null) return;
service.SendNotification(entity, action, global::Umbraco.Web.UmbracoContext.Current);
if (UmbracoContext.Current == null)
{
LogHelper.Warn(typeof(NotificationServiceExtensions), "Cannot send notifications, there is no current UmbracoContext");
return;
}
service.SendNotification(entity, action, UmbracoContext.Current);
}
internal static void SendNotification(this INotificationService service, IUmbracoEntity entity, IAction action, UmbracoContext umbracoContext)
{
if (umbracoContext == null) return;
if (umbracoContext == null)
{
LogHelper.Warn(typeof(NotificationServiceExtensions), "Cannot send notifications, there is no current UmbracoContext");
return;
}
service.SendNotification(entity, action, umbracoContext, umbracoContext.Application);
}
internal static void SendNotification(this INotificationService service, IUmbracoEntity entity, IAction action, UmbracoContext umbracoContext, ApplicationContext applicationContext)
{
if (umbracoContext == null)
{
LogHelper.Warn(typeof(NotificationServiceExtensions), "Cannot send notifications, there is no current UmbracoContext");
return;
}
var user = umbracoContext.Security.CurrentUser;
//if there is no current user, then use the admin
if (user == null)
{
LogHelper.Warn(typeof(NotificationServiceExtensions), "There is no current Umbraco user logged in, the notifications will be sent from the administrator");
user = applicationContext.Services.UserService.GetUserById(0);
if (user == null)
{
LogHelper.Warn(typeof(NotificationServiceExtensions), "Noticiations can not be sent, no admin user with id 0 could be resolved");
return;
}
}
service.SendNotification(user, entity, action, umbracoContext, applicationContext);
}
internal static void SendNotification(this INotificationService service, IUser sender, IUmbracoEntity entity, IAction action, UmbracoContext umbracoContext, ApplicationContext applicationContext)
{
if (sender == null) throw new ArgumentNullException("sender");
if (umbracoContext == null) throw new ArgumentNullException("umbracoContext");
if (applicationContext == null) throw new ArgumentNullException("applicationContext");
var user = umbracoContext.Security.CurrentUser;
applicationContext.Services.NotificationService.SendNotifications(
user,
sender,
entity,
action.Letter.ToString(CultureInfo.InvariantCulture),
ui.Text("actions", action.Alias),
@@ -1,10 +1,13 @@
using Umbraco.Core.Models.Editors;
using System;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Editors;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PropertyEditors
{
/// <summary>
/// Used to dynamically change the tag group based on the pre-values
/// Used to dynamically change the tag group and storage type based on the pre-values
/// </summary>
internal class TagPropertyEditorTagDefinition : TagPropertyDefinition
{
@@ -21,5 +24,16 @@ namespace Umbraco.Web.PropertyEditors
return preVals.ContainsKey("group") ? preVals["group"].Value : "default";
}
}
public override TagCacheStorageType StorageType
{
get
{
var preVals = PropertySaving.PreValues.FormatAsDictionary();
return preVals.ContainsKey("storageType")
? Enum<TagCacheStorageType>.Parse(preVals["storageType"].Value)
: TagCacheStorageType.Csv;
}
}
}
}
@@ -1,10 +1,15 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using Newtonsoft.Json.Linq;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Editors;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PropertyEditors
{
[SupportTags(typeof(TagPropertyEditorTagDefinition))]
[SupportTags(typeof(TagPropertyEditorTagDefinition), ValueType = TagValueType.CustomTagList)]
[PropertyEditor(Constants.PropertyEditors.TagsAlias, "Tags", "tags")]
public class TagsPropertyEditor : PropertyEditor
{
@@ -12,7 +17,8 @@ namespace Umbraco.Web.PropertyEditors
{
_defaultPreVals = new Dictionary<string, object>
{
{"group", "default"}
{"group", "default"},
{"storageType", TagCacheStorageType.Csv.ToString()}
};
}
@@ -27,11 +33,36 @@ namespace Umbraco.Web.PropertyEditors
set { _defaultPreVals = value; }
}
protected override PropertyValueEditor CreateValueEditor()
{
return new TagPropertyValueEditor(base.CreateValueEditor());
}
protected override PreValueEditor CreatePreValueEditor()
{
return new TagPreValueEditor();
}
internal class TagPropertyValueEditor : PropertyValueEditorWrapper
{
public TagPropertyValueEditor(PropertyValueEditor wrapped)
: base(wrapped)
{
}
/// <summary>
/// This needs to return IEnumerable{string}
/// </summary>
/// <param name="editorValue"></param>
/// <param name="currentValue"></param>
/// <returns></returns>
public override object ConvertEditorToDb(ContentPropertyData editorValue, object currentValue)
{
var json = editorValue.Value as JArray;
return json == null ? null : json.Select(x => x.Value<string>());
}
}
internal class TagPreValueEditor : PreValueEditor
{
public TagPreValueEditor()
@@ -43,11 +74,26 @@ namespace Umbraco.Web.PropertyEditors
Name = "Tag group",
View = "requiredfield"
});
Fields.Add(new PreValueField(new ManifestPropertyValidator {Type = "Required"})
{
Description = "Select whether to store the tags in cache as CSV (default) or as JSON. The only benefits of storage as JSON is that you are able to have commas in a tag value but this will require parsing the json in your views or using a property value converter",
Key = "storageType",
Name = "Storage Type",
View = "views/propertyeditors/tags/tags.prevalues.html"
});
}
public override IDictionary<string, object> ConvertDbToEditor(IDictionary<string, object> defaultPreVals, Core.Models.PreValueCollection persistedPreVals)
public override IDictionary<string, object> ConvertDbToEditor(IDictionary<string, object> defaultPreVals, PreValueCollection persistedPreVals)
{
var result = base.ConvertDbToEditor(defaultPreVals, persistedPreVals);
//This is required because we've added this pre-value so old installs that don't have it will need to have a default.
if (result.ContainsKey("storageType") == false || result["storageType"] == null || result["storageType"].ToString().IsNullOrWhiteSpace())
{
result["storageType"] = TagCacheStorageType.Csv.ToString();
}
return result;
}
}
@@ -18,6 +18,7 @@ namespace Umbraco.Web.WebServices
public class CoreStringsController : UmbracoAuthorizedController
{
[HttpGet]
[ValidateInput(false)]
public JsonResult ToSafeAlias(string value, bool camelCase = true)
{
// always return a proper camel-cased alias
+11 -10
View File
@@ -85,31 +85,32 @@ namespace umbraco
var rval = pval == null ? string.Empty : pval.ToString();
_fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval;
}
else if (elements[_fieldName] != null && string.IsNullOrEmpty(elements[_fieldName].ToString()) == false)
{
else
{
//get the vaue the legacy way (this will not parse locallinks, etc... since that is handled with ipublishedcontent)
_fieldContent = elements[_fieldName].ToString().Trim();
var elt = elements[_fieldName];
if (elt != null && string.IsNullOrEmpty(elt.ToString()) == false)
_fieldContent = elt.ToString().Trim();
}
//now we check if the value is still empty and if so we'll check useIfEmpty
if (string.IsNullOrEmpty(_fieldContent))
{
//if useIfEmpty is true
if (string.IsNullOrEmpty(helper.FindAttribute(attributes, "useIfEmpty")) == false)
var altFieldName = helper.FindAttribute(attributes, "useIfEmpty");
if (string.IsNullOrEmpty(altFieldName) == false)
{
var altFieldName = helper.FindAttribute(attributes, "useIfEmpty");
//check for published content and get its value using that
if (publishedContent != null && publishedContent.HasProperty(altFieldName))
{
var pval = publishedContent.GetPropertyValue(altFieldName);
var rval = pval == null ? string.Empty : pval.ToString();
_fieldContent = rval.IsNullOrWhiteSpace() ? _fieldContent : rval;
}
else if (elements[altFieldName] != null && string.IsNullOrEmpty(elements[altFieldName].ToString()) == false)
else
{
//get the vaue the legacy way (this will not parse locallinks, etc... since that is handled with ipublishedcontent)
_fieldContent = elements[altFieldName].ToString().Trim();
var elt = elements[altFieldName];
if (elt != null && string.IsNullOrEmpty(elt.ToString()) == false)
_fieldContent = elt.ToString().Trim();
}
}
}
@@ -342,6 +342,9 @@ namespace umbraco
}
catch (Exception e)
{
LogHelper.WarnWithException<macro>(
"Error loading partial view macro (View: " + Model.ScriptName + ")", true, e);
renderFailed = true;
Exceptions.Add(e);
macroControl = handleError(e);
@@ -1666,6 +1669,12 @@ namespace umbraco
public static string MacroContentByHttp(int PageID, Guid PageVersion, Hashtable attributes)
{
if (SystemUtilities.GetCurrentTrustLevel() != AspNetHostingPermissionLevel.Unrestricted)
{
return "<span style='color: red'>Cannot render macro content in the rich text editor when the application is running in a Partial Trust environment</span>";
}
string tempAlias = (attributes["macroalias"] != null)
? attributes["macroalias"].ToString()
: attributes["macroAlias"].ToString();
@@ -39,9 +39,8 @@ namespace umbraco.cms.presentation.developer
{
_macro = Services.MacroService.GetById(Convert.ToInt32(Request.QueryString["macroID"]));
if (!IsPostBack)
if (IsPostBack == false)
{
ClientTools
.SetActiveTreeType(TreeDefinitionCollection.Instance.FindTree<loadMacros>().Tree.Alias)
.SyncTree("-1,init," + _macro.Id.ToString(), false);
@@ -77,57 +76,6 @@ namespace umbraco.cms.presentation.developer
userControlList.Items.Insert(0, new ListItem("Browse usercontrols on server...", string.Empty));
}
else
{
Page.Validate();
ClientTools
.SetActiveTreeType(TreeDefinitionCollection.Instance.FindTree<loadMacros>().Tree.Alias)
.SyncTree("-1,init," + _macro.Id.ToInvariantString(), true); //true forces the reload
var tempMacroAssembly = macroAssembly.Text;
var tempMacroType = macroType.Text;
var tempCachePeriod = cachePeriod.Text;
if (tempCachePeriod == string.Empty)
tempCachePeriod = "0";
if (tempMacroAssembly == string.Empty && macroUserControl.Text != string.Empty)
tempMacroType = macroUserControl.Text;
SetMacroValuesFromPostBack(_macro, Convert.ToInt32(tempCachePeriod), tempMacroAssembly, tempMacroType);
// Save elements
var sort = 0;
foreach (RepeaterItem item in macroProperties.Items)
{
var macroPropertyId = (HtmlInputHidden)item.FindControl("macroPropertyID");
var macroElementName = (TextBox)item.FindControl("macroPropertyName");
var macroElementAlias = (TextBox)item.FindControl("macroPropertyAlias");
var macroElementType = (DropDownList)item.FindControl("macroPropertyType");
var prop = _macro.Properties.Single(x => x.Id == int.Parse(macroPropertyId.Value));
prop.Alias = macroElementAlias.Text.Trim();
prop.Name = macroElementName.Text.Trim();
prop.EditorAlias = macroElementType.SelectedValue;
prop.SortOrder = sort;
sort++;
}
Services.MacroService.Save(_macro);
ClientTools.ShowSpeechBubble(speechBubbleIcon.save, "Macro saved", "");
// Check for assemblyBrowser
if (tempMacroType.IndexOf(".ascx", StringComparison.Ordinal) > 0)
assemblyBrowserUserControl.Controls.Add(
new LiteralControl("<br/><button onClick=\"UmbClientMgr.openModalWindow('developer/macros/assemblyBrowser.aspx?fileName=" + macroUserControl.Text +
"&macroID=" + Request.QueryString["macroID"] +
"', 'Browse Properties', true, 500, 475); return false\" class=\"guiInputButton\"><img src=\"../../images/editor/propertiesNew.gif\" align=\"absmiddle\" style=\"width: 18px; height: 17px; padding-right: 5px;\"/> Browse properties</button>"));
else if (tempMacroType != string.Empty && tempMacroAssembly != string.Empty)
assemblyBrowser.Controls.Add(
new LiteralControl("<br/><button onClick=\"UmbClientMgr.openModalWindow('developer/macros/assemblyBrowser.aspx?fileName=" + macroAssembly.Text +
"&macroID=" + Request.QueryString["macroID"] + "&type=" + macroType.Text +
"', 'Browse Properties', true, 500, 475); return false\" class=\"guiInputButton\"><img src=\"../../images/editor/propertiesNew.gif\" align=\"absmiddle\" style=\"width: 18px; height: 17px; padding-right: 5px;\"/> Browse properties</button>"));
}
}
/// <summary>
@@ -267,7 +215,17 @@ namespace umbraco.cms.presentation.developer
}
public void macroPropertyCreate(object sender, EventArgs e)
{
{
//enable add validators
var val1 = (RequiredFieldValidator)((Control)sender).Parent.FindControl("RequiredFieldValidator1");
var val2 = (RequiredFieldValidator)((Control)sender).Parent.FindControl("RequiredFieldValidator4");
var val3 = (RequiredFieldValidator)((Control)sender).Parent.FindControl("RequiredFieldValidator5");
val1.Enabled = true;
val2.Enabled = true;
val3.Enabled = true;
Page.Validate();
if (Page.IsValid == false)
{
return;
@@ -336,25 +294,90 @@ namespace umbraco.cms.presentation.developer
}
protected override void OnInit(EventArgs e)
{
// Tab setup
InfoTabPage = TabView1.NewTabPage("Macro Properties");
InfoTabPage.Controls.Add(Pane1);
InfoTabPage.Controls.Add(Pane1_2);
InfoTabPage.Controls.Add(Pane1_3);
InfoTabPage.Controls.Add(Pane1_4);
{
base.OnInit(e);
EnsureChildControls();
}
Parameters = TabView1.NewTabPage("Parameters");
Parameters.Controls.Add(Panel2);
protected override void CreateChildControls()
{
base.CreateChildControls();
// Tab setup
InfoTabPage = TabView1.NewTabPage("Macro Properties");
InfoTabPage.Controls.Add(Pane1);
InfoTabPage.Controls.Add(Pane1_2);
InfoTabPage.Controls.Add(Pane1_3);
InfoTabPage.Controls.Add(Pane1_4);
Parameters = TabView1.NewTabPage("Parameters");
Parameters.Controls.Add(Panel2);
MenuButton save = TabView1.Menu.NewButton();
save.ButtonType = MenuButtonType.Primary;
save.Text = ui.Text("save");
save.ID = "save";
base.OnInit(e);
}
save.Click += Save_Click;
}
/// <summary>
void Save_Click(object sender, EventArgs e)
{
Page.Validate();
ClientTools
.SetActiveTreeType(TreeDefinitionCollection.Instance.FindTree<loadMacros>().Tree.Alias)
.SyncTree("-1,init," + _macro.Id.ToInvariantString(), true); //true forces the reload
var tempMacroAssembly = macroAssembly.Text;
var tempMacroType = macroType.Text;
var tempCachePeriod = cachePeriod.Text;
if (tempCachePeriod == string.Empty)
tempCachePeriod = "0";
if (tempMacroAssembly == string.Empty && macroUserControl.Text != string.Empty)
tempMacroType = macroUserControl.Text;
SetMacroValuesFromPostBack(_macro, Convert.ToInt32(tempCachePeriod), tempMacroAssembly, tempMacroType);
// Save elements
var sort = 0;
foreach (RepeaterItem item in macroProperties.Items)
{
var macroPropertyId = (HtmlInputHidden)item.FindControl("macroPropertyID");
var macroElementName = (TextBox)item.FindControl("macroPropertyName");
var macroElementAlias = (TextBox)item.FindControl("macroPropertyAlias");
var macroElementType = (DropDownList)item.FindControl("macroPropertyType");
var prop = _macro.Properties.Single(x => x.Id == int.Parse(macroPropertyId.Value));
_macro.Properties.UpdateProperty(
prop.Alias,
macroElementName.Text.Trim(),
sort,
macroElementType.SelectedValue,
macroElementAlias.Text.Trim());
sort++;
}
Services.MacroService.Save(_macro);
ClientTools.ShowSpeechBubble(speechBubbleIcon.save, "Macro saved", "");
// Check for assemblyBrowser
if (tempMacroType.IndexOf(".ascx", StringComparison.Ordinal) > 0)
assemblyBrowserUserControl.Controls.Add(
new LiteralControl("<br/><button onClick=\"UmbClientMgr.openModalWindow('developer/macros/assemblyBrowser.aspx?fileName=" + macroUserControl.Text +
"&macroID=" + Request.QueryString["macroID"] +
"', 'Browse Properties', true, 500, 475); return false\" class=\"guiInputButton\"><img src=\"../../images/editor/propertiesNew.gif\" align=\"absmiddle\" style=\"width: 18px; height: 17px; padding-right: 5px;\"/> Browse properties</button>"));
else if (tempMacroType != string.Empty && tempMacroAssembly != string.Empty)
assemblyBrowser.Controls.Add(
new LiteralControl("<br/><button onClick=\"UmbClientMgr.openModalWindow('developer/macros/assemblyBrowser.aspx?fileName=" + macroAssembly.Text +
"&macroID=" + Request.QueryString["macroID"] + "&type=" + macroType.Text +
"', 'Browse Properties', true, 500, 475); return false\" class=\"guiInputButton\"><img src=\"../../images/editor/propertiesNew.gif\" align=\"absmiddle\" style=\"width: 18px; height: 17px; padding-right: 5px;\"/> Browse properties</button>"));
}
/// <summary>
/// TabView1 control.
/// </summary>
/// <remarks>
@@ -551,7 +551,13 @@ namespace umbraco.NodeFactory
{
if (HttpContext.Current.Items["pageID"] == null)
throw new InvalidOperationException("There is no current node.");
return (int)HttpContext.Current.Items["pageID"];
var intAttempt = HttpContext.Current.Items["pageID"].TryConvertTo<int>();
if (intAttempt == false)
{
throw new InvalidOperationException("The pageID value in the HttpContext.Items cannot be converted to an integer");
}
return intAttempt.Result;
}
}
}
@@ -887,7 +887,7 @@ namespace umbraco.MacroEngines
//this is from SqlMetal and just makes it a bit of fun to allow pluralisation
private static string MakePluralName(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;