Compare commits

...

106 Commits

Author SHA1 Message Date
Sebastiaan Janssen 7e7f9902f6 Bumps version to 7.7.4 2017-10-23 14:18:00 +02:00
Sebastiaan Janssen dcd2630d6b Merge branch 'dev-v7.6' into dev-v7 2017-10-23 14:10:04 +02:00
Sebastiaan Janssen b70f543790 Reverts wrongly committed files 2017-10-23 14:02:00 +02:00
Sebastiaan Janssen 24c1c15b66 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2017-10-23 13:59:30 +02:00
Sebastiaan Janssen 4a0cd7228b Revert "U4-10554 - warn if closing page with changes"
This reverts commit 24b147e2a6.
2017-10-23 13:59:06 +02:00
Stephan edfe212227 Fix login issue 2017-10-20 15:35:30 +02:00
Sebastiaan Janssen e3bc98a155 Merge pull request #2257 from umbraco/temp-U4-10566
U4-10566 The legacy API Member.PopulateGroups throws an exception
2017-10-19 06:59:18 -07:00
Shannon f85532f15e U4-10566 The legacy API Member.PopulateGroups throws an exception 2017-10-20 00:56:34 +11:00
Sebastiaan Janssen b04d71ca5f Merge pull request #2256 from richarth/patch-1
Removed duplicate translations
2017-10-19 00:41:33 -07:00
Sebastiaan Janssen ab4de382f3 Merge pull request #2252 from umbraco/temp-u4-7317
U4-7317 - select default template
2017-10-18 12:38:53 -07:00
Sebastiaan Janssen a293c633fc Merge pull request #2249 from umbraco/temp-u4-8652
U4-8652 - accept exceptions when loading some assemblies
2017-10-18 11:00:45 -07:00
Richard Thompson 507e3ece98 Removed duplicate translations
Removed duplicate definition of English translations
2017-10-18 15:36:17 +01:00
Stephan b77826fd3d Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2017-10-18 12:18:59 +02:00
Stephan 4c7358d63b U4-8960 - minor fixes 2017-10-18 12:17:58 +02:00
Stephan 4814612102 Merge branch 'U4-8960' of https://github.com/lars-erik/Umbraco-CMS into temp-u4-8960 2017-10-18 11:56:00 +02:00
Robert 6dae4396bb Merge pull request #2250 from umbraco/temp-u4-10554
U4-10554 - warn if closing page with changes
2017-10-18 11:53:54 +02:00
Stephan dd5560f00b U4-8616 - minor cleanup 2017-10-18 10:11:23 +02:00
Stephan b56575fc50 Merge branch 'dev-v7' of https://github.com/jhauge/Umbraco-CMS into temp-u4-8616 2017-10-18 09:49:05 +02:00
Stephan b06d30c0f9 U4-10554 - cross-browser 2017-10-18 09:29:50 +02:00
Claus 9c50e1c48d fixing bug in U4-10524 Show warning in UI for the media/content picker that is referencing a trashed or deleted item 2017-10-17 23:52:34 +02:00
Claus 0688835ac0 fixing bug in U4-10524 Show warning in UI for the media/content picker that is referencing a trashed or deleted item 2017-10-17 23:44:03 +02:00
Stephan 353b8f21cd U4-7317 - select default template 2017-10-17 16:04:31 +02:00
Stephan 30a0d553d5 Merge pull request #1230 from bjarnef/dev-v7-U4-8319
U4-8319 - Sort data types array by name A-Z
2017-10-17 15:06:54 +02:00
Stephan d0fbc362b2 Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2017-10-17 15:00:13 +02:00
Stephan 39c534ef54 Merge branch 'temp-u4-7115' into dev-v7 2017-10-17 14:59:56 +02:00
Stephan 7f86166abf Merge branch 'dev-v7' of https://github.com/hfloyd/Umbraco-CMS into temp-u4-7115 2017-10-17 14:59:07 +02:00
Robert 838e86469a Merge pull request #2245 from umbraco/temp-u4-5322
U4-5322 - add labels to color picker
2017-10-17 14:47:36 +02:00
Stephan 24b147e2a6 U4-10554 - warn if closing page with changes 2017-10-17 14:39:47 +02:00
Stephan 11342ff1df U4-8652 - accept exceptions when loading some assemblies 2017-10-17 14:04:24 +02:00
Robert bf20af9345 Merge pull request #2247 from umbraco/temp-u4-5722b
Temp u4 5722b
2017-10-17 13:19:34 +02:00
Robert c69774e3f2 Merge pull request #2246 from umbraco/temp-u4-7360
U4-7360 - toggle password visibility
2017-10-17 12:30:37 +02:00
Robert 7de8f5a59a Added missing width and height to the "show password" icon
Not having the height and width on the icon, caused IE 11 to shrink the icon which looked a bit off.
2017-10-17 12:09:55 +02:00
Sebastiaan Janssen 6795afd5c2 Merge branch 'dev-v7.6' into dev-v7
# Conflicts:
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/UmbracoVersion.cs
#	src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
#	src/Umbraco.Web/UmbracoDefaultOwinStartup.cs
2017-10-17 11:06:52 +02:00
Sebastiaan Janssen 9411a22a0d Don't forget to call the ConfigureUmbracoAuthentication method 2017-10-17 11:04:01 +02:00
Sebastiaan Janssen 461ce64feb Bump version to 7.6.10 2017-10-17 10:57:18 +02:00
Robert 53c251feb5 Merge pull request #2237 from umbraco/temp-U4-10504
U4-10504 Previewing content results in the same PrepareDocument (expe…
2017-10-17 10:20:21 +02:00
Sebastiaan Janssen c5a55b17e6 Manually applying U4-10369: Split out ConfigureUmbracoAuthentication method 2017-10-17 10:08:37 +02:00
Sebastiaan Janssen fd577afe2c Html encode nodenames to prevent XSS attacks. Fixes U4-10497 XSS Vulnerability in page name.
(cherry picked from commit fe2b86b681)
2017-10-16 20:32:56 +02:00
Sebastiaan Janssen f46ef0b006 U4-10506 Importing a specially crafted document type file can cause XXE attack
(cherry picked from commit 5dde2efe0d)
2017-10-16 20:32:04 +02:00
Stephan 909e3788ea Merge branch 'U4-5722' of https://github.com/NikRimington/Umbraco-CMS into temp-u4-5722b 2017-10-16 17:20:19 +02:00
Stephan 09a92ef6ce U4-7360 - toggle password visibility 2017-10-16 16:58:15 +02:00
Stephan 7d9c2bcce9 Merge branch 'dev-v7' into temp-u4-5322 2017-10-16 16:32:43 +02:00
Stephan 529ae961ff U4-5322 - add labels to color picker 2017-10-16 16:16:43 +02:00
Sebastiaan Janssen 04f64cd804 Merge branch 'dev-v7.6' into dev-v7
# Conflicts:
#	src/Umbraco.Web/Editors/MediaController.cs
2017-10-16 16:14:54 +02:00
Sebastiaan Janssen dc937ec942 Fix the "Add media" box no longer being square 2017-10-16 16:13:47 +02:00
Shannon cefd9e7323 Fixes PostAddFolder
(cherry picked from commit c5d874464a)

# Conflicts:
#	src/Umbraco.Web/Editors/MediaController.cs
2017-10-16 15:53:54 +02:00
Mads Rasmussen 315690e758 fixes breadcrumbs and search when start node is set
(cherry picked from commit 1ec07aa205)
2017-10-16 15:14:24 +02:00
Mads Rasmussen fb8c519f71 fixes: U4-10521 Creating new folders from the media picker no longer works
(cherry picked from commit 1cc3b39601)

# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html
2017-10-16 15:13:56 +02:00
Sebastiaan Janssen 80f172e3fb Bump version to 7.7.3 2017-10-16 14:56:43 +02:00
Sebastiaan Janssen 5eced19816 Merge branch 'dev-v7.6' into dev-v7
# Conflicts:
#	src/Umbraco.Web.UI.Client/src/less/property-editors.less
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js
2017-10-16 14:24:18 +02:00
Warren Buckley 5df4d3509d Fix in PowerShell build - The 7Zip tool download was giving two paths & thus break the build script
(cherry picked from commit a2e6da2491)
2017-10-16 14:22:09 +02:00
Claus 4bd263e7bc U4-10524 Show warning in UI for the media/content picker that is referencing a trashed or deleted item
U4-10533 Show warning in UI for the link picker that is referencing a trashed or deleted item
cherrypicked from dev-v7.
2017-10-16 13:52:17 +02:00
Claus b094bfad29 U4-10533 Show warning in UI for the link picker that is referencing a trashed or deleted item 2017-10-16 13:05:20 +02:00
Sebastiaan Janssen e333f6c628 Merge pull request #2236 from umbraco/temp-U4-10521
fixes: U4-10521 Creating new folders from the media picker no longer …
2017-10-16 12:44:32 +02:00
Shannon 916bc6badb U4-10539 - reverts merge issue that reverted the fix for U4-9262
(cherry picked from commit 2a0cc71d06)
2017-10-16 08:38:17 +02:00
Sebastiaan Janssen 76ee23b933 Merge pull request #2243 from umbraco/temp-U4-10539
U4-10539 - reverts merge issue that reverted the fix for U4-9262
2017-10-16 08:36:56 +02:00
Shannon 2a0cc71d06 U4-10539 - reverts merge issue that reverted the fix for U4-9262 2017-10-16 17:05:33 +11:00
Warren Buckley a2e6da2491 Fix in PowerShell build - The 7Zip tool download was giving two paths & thus break the build script 2017-10-13 11:19:39 +01:00
Mads Rasmussen ba7d8b0955 Merge pull request #2240 from bjarnef/dev-v7-U4-10528
U4-10528 - Add better function to get initials from name
2017-10-13 10:13:00 +02:00
Shannon Deminick 7288bcf679 Merge pull request #2239 from umbraco/temp-U4-10524
U4-10524 Show warning in UI for any picker that is referencing a tras…
2017-10-13 17:08:16 +11:00
Shannon ca57b9c5e6 fixes issue with js exceptions 2017-10-13 16:50:39 +11:00
Shannon 62ce7f6132 U4-10519 Legacy create is broken 2017-10-13 16:33:49 +11:00
Bjarne Fyrstenborg b1f2c5d93a Add better function to get initials from name 2017-10-12 23:46:02 +02:00
Mads Rasmussen 1ec07aa205 fixes breadcrumbs and search when start node is set 2017-10-12 16:13:14 +02:00
Claus 13055ad1f2 U4-10524 Show warning in UI for any picker that is referencing a trashed or deleted item
fixing the trash flag not getting mapped via automapper.
fixing up the ui for mediapicker.
fixing a small ui issue with the contentpicker and trashed content.
2017-10-12 11:35:08 +02:00
Shannon d5447106a6 U4-10504 Previewing content results in the same PrepareDocument (expensive) procedure to be executed 3 times 2017-10-12 18:23:19 +11:00
Shannon c5d874464a Fixes PostAddFolder 2017-10-12 17:11:45 +11:00
Mads Rasmussen 1cc3b39601 fixes: U4-10521 Creating new folders from the media picker no longer works 2017-10-11 10:18:40 +02:00
Warren Buckley c5f4793d11 Merge pull request #2233 from umbraco/temp-U4-10503-2
U4-10503 Umbraco plugins cache files should be stored in the same local temp location as the umbraco xml cache file
2017-10-09 14:04:16 +01:00
Shannon 4175c0530f Merge branch 'dev-v7' into temp-U4-10503-2 2017-10-09 23:05:17 +11:00
Shannon cf97926f26 trying another test fix 2017-10-09 19:00:00 +11:00
Shannon 4b08c3a5c3 Reset PluginManager on each tests, no reason not to, this should resolve the issue 2017-10-09 18:20:07 +11:00
Shannon fbda4fc8d1 Fixes part of the failing tests 2017-10-09 17:49:01 +11:00
Shannon 312b994537 Merge branch 'dev-v7' into temp-U4-10503-2 2017-10-09 16:42:21 +11:00
Shannon dd1bfe9cbf Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2017-10-09 16:08:14 +11:00
Shannon de17b69564 U4-10452 Unable to create new User Groups programmatically - publicizes UserGroup 2017-10-09 16:07:55 +11:00
Shannon 7f44f4a030 readonly field 2017-10-09 16:01:49 +11:00
Shannon 422219ab21 Updates the cache file paths to be resolved behind a lazy so that there's proper locking in place for accessing the value, this also makes the code a little nicer. 2017-10-09 16:01:22 +11:00
Shannon Deminick 9986a3c2c2 Merge pull request #2162 from Bitmapped/U4-9081-external
U4-10369: Split out ConfigureUmbracoAuthentication method
2017-10-09 15:50:45 +11:00
Shannon ce81416368 Ensures the DistCache file is stored in the same configured temp location as the plugin cache files 2017-10-09 14:50:34 +11:00
Shannon 04e04ae47a Ensures that the local folder does not also get created when using the env temp storage location, this is also a tiny bit better for perfs 2017-10-09 14:40:23 +11:00
Warren Buckley 0f6e7d26c9 Merge pull request #1596 from StefanoChiodino/U4-7721
U4-7721 install button miss aligned
2017-10-06 15:00:09 +01:00
Warren Buckley 51ceba6fea Merge pull request #1780 from bjarnef/dev-v7-U4-9599
U4-9599 - Fix caret in font size select
2017-10-06 14:25:55 +01:00
Sebastiaan Janssen 5dde2efe0d U4-10506 Importing a specially crafted document type file can cause XXE attack 2017-10-06 15:14:41 +02:00
Sebastiaan Janssen fe2b86b681 Html encode nodenames to prevent XSS attacks. Fixes U4-10497 XSS Vulnerability in page name. 2017-10-06 14:38:21 +02:00
Warren Buckley 368fec4ac1 Merge pull request #2231 from umbraco/temp-U4-10503
U4-10503 Umbraco plugins cache files should be stored in the same local temp location as the umbraco xml cache file
2017-10-06 13:27:22 +01:00
Shannon 9016147118 Streamlines temp folder name so it's consistent, ensure the folder exists before writing to it 2017-10-06 12:11:20 +11:00
Shannon bee75803f7 Adds file exist checks 2017-10-06 11:51:27 +11:00
Shannon 7b3f7f4ad4 U4-10503 Umbraco plugins cache files should be stored in the same local temp location as the umbraco xml cache file 2017-10-06 11:47:25 +11:00
Shannon Deminick f6c25f29c0 Merge pull request #2229 from keroberts/dev-v7-U4-10496
Fix Typo in Email Property Editor
2017-10-06 01:01:48 +11:00
keroberts 6de3b920eb Fix Typo in Email Property Editor 2017-10-05 14:04:02 +01:00
Bitmapped 606abd8ac7 Restructure per discussion in U4-10369 2017-09-06 00:43:27 -04:00
Bitmapped 65db5396cf Spelling fix 2017-08-30 20:06:20 -04:00
Bitmapped 32d5a0a57c Add method for configuring external logins so things can be granularly overriden. 2017-08-30 20:04:30 -04:00
Nik 7367c8ef67 Re-do min max number of items in the grid after rebasing 2017-07-30 02:09:38 +01:00
Bjarne Fyrstenborg be1172d604 Add icon for font select 2017-03-04 18:20:03 +01:00
Bjarne Fyrstenborg 7cf8cd3518 Fix font size caret 2017-03-04 18:19:44 +01:00
Stefano Chiodino 4cab1c89b4 U4-7721 Specify margin 0 for buttons
On Safari input elemnts have 1px margin which use to make customise and
install buttons not aligned
2016-11-03 16:05:38 +00:00
Stefano Chiodino 88fd5a9623 Remove background-color on preview that was interfering with the website
style
2016-11-03 11:40:20 +00:00
Lars-Erik Aabech 361ef2a475 Opening up GetHttpContextFactory for extending (actually in prev commit, but messed up) 2016-09-13 23:57:31 +02:00
Lars-Erik Aabech d382fcc919 Issue with isHosted in TypeFinder when unit testing. 2016-09-13 23:56:15 +02:00
Lars-Erik Aabech aac8451627 Removing HttpContext dependency from GridValueConverter. Adding configuration for IOHelper when testing to remove usage of HttpContext. 2016-09-11 21:36:10 +02:00
Jesper Hauge c96c09f42f Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2016-06-20 21:55:25 +02:00
Jesper Hauge 50f8f18503 Fixes U4-8616 2016-06-20 21:53:40 +02:00
bjarnef 9001cd8a6f Sort data types array by name A-Z 2016-04-14 23:50:43 +02:00
Heather Floyd 1279fd234f Checking for possible Null Ref Exception in Textstring.cshtml grid renderer 2015-09-11 19:43:41 -04:00
71 changed files with 1311 additions and 606 deletions
@@ -40,6 +40,7 @@ function Get-UmbracoBuildEnv
&$nuget install 7-Zip.CommandLine -OutputDirectory $path -Verbosity quiet
$dir = ls "$path\7-Zip.CommandLine.*" | sort -property Name -descending | select -first 1
$file = ls -path "$dir" -name 7za.exe -recurse
$file = ls -path "$dir" -name 7za.exe -recurse | select -first 1 #A select is because there is tools\7za.exe & tools\x64\7za.exe
mv "$dir\$file" $sevenZip
Remove-Directory $dir
}
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.7.2")]
[assembly: AssemblyInformationalVersion("7.7.2")]
[assembly: AssemblyFileVersion("7.7.4")]
[assembly: AssemblyInformationalVersion("7.7.4")]
@@ -538,24 +538,41 @@ namespace Umbraco.Core.Configuration
internal static bool ContentCacheXmlStoredInCodeGen
{
get { return ContentCacheXmlStorageLocation == ContentXmlStorage.AspNetTemp; }
get { return LocalTempStorageLocation == LocalTempStorage.AspNetTemp; }
}
internal static ContentXmlStorage ContentCacheXmlStorageLocation
/// <summary>
/// This is the location type to store temporary files such as cache files or other localized files for a given machine
/// </summary>
/// <remarks>
/// Currently used for the xml cache file and the plugin cache files
/// </remarks>
internal static LocalTempStorage LocalTempStorageLocation
{
get
{
{
//there's a bunch of backwards compat config checks here....
//This is the current one
if (ConfigurationManager.AppSettings.ContainsKey("umbracoLocalTempStorage"))
{
return Enum<LocalTempStorage>.Parse(ConfigurationManager.AppSettings["umbracoLocalTempStorage"]);
}
//This one is old
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLStorage"))
{
return Enum<ContentXmlStorage>.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]);
}
return Enum<LocalTempStorage>.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]);
}
//This one is older
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLUseLocalTemp"))
{
return bool.Parse(ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"])
? ContentXmlStorage.AspNetTemp
: ContentXmlStorage.Default;
? LocalTempStorage.AspNetTemp
: LocalTempStorage.Default;
}
return ContentXmlStorage.Default;
return LocalTempStorage.Default;
}
}
@@ -1,6 +1,6 @@
namespace Umbraco.Core.Configuration
{
internal enum ContentXmlStorage
internal enum LocalTempStorage
{
Default,
AspNetTemp,
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.7.2");
private static readonly Version Version = new Version("7.7.4");
/// <summary>
/// Gets the current version of Umbraco.
+24 -6
View File
@@ -13,11 +13,28 @@ using Umbraco.Core.Configuration;
namespace Umbraco.Core.IO
{
public static class IOHelper
{
{
/// <summary>
/// Gets or sets a value forcing Umbraco to consider it is non-hosted.
/// </summary>
/// <remarks>This should always be false, unless unit testing.</remarks>
public static bool ForceNotHosted { get; set; }
private static string _rootDir = "";
// static compiled regex for faster performance
private readonly static Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
/// <summary>
/// Gets a value indicating whether Umbraco is hosted.
/// </summary>
public static bool IsHosted
{
get
{
return ForceNotHosted == false && (HttpContext.Current != null || HostingEnvironment.IsHosted);
}
}
public static char DirSepChar
{
@@ -72,14 +89,14 @@ namespace Umbraco.Core.IO
internal static string ResolveUrlsFromTextString(string text)
{
if (UmbracoConfig.For.UmbracoSettings().Content.ResolveUrlsFromTextString)
{
{
using (DisposableTimer.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete"))
{
// find all relative urls (ie. urls that contain ~)
var tags = ResolveUrlPattern.Matches(text);
foreach (Match tag in tags)
{
{
string url = "";
if (tag.Groups[1].Success)
url = tag.Groups[1].Value;
@@ -97,7 +114,8 @@ namespace Umbraco.Core.IO
public static string MapPath(string path, bool useHttpContext)
{
if (path == null) throw new ArgumentNullException("path");
if (path == null) throw new ArgumentNullException("path");
useHttpContext = useHttpContext && IsHosted;
// Check if the path is already mapped
if ((path.Length >= 2 && path[1] == Path.VolumeSeparatorChar)
@@ -303,7 +321,7 @@ namespace Umbraco.Core.IO
var debugFolder = Path.Combine(binFolder, "debug");
if (Directory.Exists(debugFolder))
return debugFolder;
#endif
#endif
var releaseFolder = Path.Combine(binFolder, "release");
if (Directory.Exists(releaseFolder))
return releaseFolder;
+8 -8
View File
@@ -73,19 +73,19 @@ namespace Umbraco.Core.IO
{
get
{
switch (GlobalSettings.ContentCacheXmlStorageLocation)
switch (GlobalSettings.LocalTempStorageLocation)
{
case ContentXmlStorage.AspNetTemp:
case LocalTempStorage.AspNetTemp:
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
case ContentXmlStorage.EnvironmentTemp:
case LocalTempStorage.EnvironmentTemp:
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoXml",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path
appDomainHash);
return Path.Combine(cachePath, "umbraco.config");
case ContentXmlStorage.Default:
case LocalTempStorage.Default:
return IOHelper.ReturnPath("umbracoContentXML", "~/App_Data/umbraco.config");
default:
throw new ArgumentOutOfRangeException();
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Models.Membership
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
internal class UserGroup : Entity, IUserGroup, IReadOnlyUserGroup
public class UserGroup : Entity, IUserGroup, IReadOnlyUserGroup
{
private int? _startContentId;
private int? _startMediaId;
+94 -53
View File
@@ -6,6 +6,7 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Compilation;
using Umbraco.Core.Cache;
using Umbraco.Core.IO;
@@ -15,6 +16,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Profiling;
using Umbraco.Core.PropertyEditors;
using umbraco.interfaces;
using Umbraco.Core.Configuration;
using File = System.IO.File;
namespace Umbraco.Core
@@ -39,7 +41,8 @@ namespace Umbraco.Core
private readonly IServiceProvider _serviceProvider;
private readonly IRuntimeCacheProvider _runtimeCache;
private readonly ProfilingLogger _logger;
private readonly string _tempFolder;
private readonly Lazy<string> _pluginListFilePath = new Lazy<string>(GetPluginListFilePath);
private readonly Lazy<string> _pluginHashFilePath = new Lazy<string>(GetPluginHashFilePath);
private readonly object _typesLock = new object();
private readonly Dictionary<TypeListKey, TypeList> _types = new Dictionary<TypeListKey, TypeList>();
@@ -65,14 +68,7 @@ namespace Umbraco.Core
_serviceProvider = serviceProvider;
_runtimeCache = runtimeCache;
_logger = logger;
// the temp folder where the cache file lives
_tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
if (Directory.Exists(_tempFolder) == false)
Directory.CreateDirectory(_tempFolder);
var pluginListFile = GetPluginListFilePath();
if (detectChanges)
{
//first check if the cached hash is string.Empty, if it is then we need
@@ -84,7 +80,8 @@ namespace Umbraco.Core
// 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);
if(File.Exists(_pluginListFilePath.Value))
File.Delete(_pluginListFilePath.Value);
WriteCachePluginsHash();
}
@@ -94,7 +91,8 @@ namespace Umbraco.Core
// 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);
if (File.Exists(_pluginListFilePath.Value))
File.Delete(_pluginListFilePath.Value);
// always set to true if we're not detecting (generally only for testing)
RequiresRescanning = true;
@@ -187,11 +185,10 @@ namespace Umbraco.Core
{
if (_cachedAssembliesHash != null)
return _cachedAssembliesHash;
if (File.Exists(_pluginHashFilePath.Value) == false) return string.Empty;
var filePath = GetPluginHashFilePath();
if (File.Exists(filePath) == false) return string.Empty;
var hash = File.ReadAllText(filePath, Encoding.UTF8);
var hash = File.ReadAllText(_pluginHashFilePath.Value, Encoding.UTF8);
_cachedAssembliesHash = hash;
return _cachedAssembliesHash;
@@ -229,9 +226,8 @@ namespace Umbraco.Core
/// Writes the assembly hash file.
/// </summary>
private void WriteCachePluginsHash()
{
var filePath = GetPluginHashFilePath();
File.WriteAllText(filePath, CurrentAssembliesHash.ToString(), Encoding.UTF8);
{
File.WriteAllText(_pluginHashFilePath.Value, CurrentAssembliesHash, Encoding.UTF8);
}
/// <summary>
@@ -274,7 +270,7 @@ namespace Umbraco.Core
}
}
}
return generator.GenerateHash();
return generator.GenerateHash();
}
}
}
@@ -314,7 +310,7 @@ namespace Umbraco.Core
uniqInfos.Add(fileOrFolder.FullName);
generator.AddFileSystemItem(fileOrFolder);
}
return generator.GenerateHash();
return generator.GenerateHash();
}
}
}
@@ -347,12 +343,11 @@ namespace Umbraco.Core
{
return ReadCache();
}
catch
catch (Exception ex)
{
try
{
var filePath = GetPluginListFilePath();
File.Delete(filePath);
File.Delete(_pluginListFilePath.Value);
}
catch
{
@@ -366,12 +361,11 @@ namespace Umbraco.Core
internal Dictionary<Tuple<string, string>, IEnumerable<string>> ReadCache()
{
var cache = new Dictionary<Tuple<string, string>, IEnumerable<string>>();
var filePath = GetPluginListFilePath();
if (File.Exists(filePath) == false)
if (File.Exists(_pluginListFilePath.Value) == false)
return cache;
using (var stream = GetFileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout))
using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout))
using (var reader = new StreamReader(stream))
{
while (true)
@@ -414,38 +408,86 @@ namespace Umbraco.Core
/// <remarks>Generally only used for resetting cache, for example during the install process.</remarks>
public void ClearPluginCache()
{
var path = GetPluginListFilePath();
if (File.Exists(path))
File.Delete(path);
path = GetPluginHashFilePath();
if (File.Exists(path))
File.Delete(path);
if (File.Exists(_pluginListFilePath.Value))
File.Delete(_pluginListFilePath.Value);
if (File.Exists(_pluginHashFilePath.Value))
File.Delete(_pluginHashFilePath.Value);
_runtimeCache.ClearCacheItem(CacheKey);
}
private string GetPluginListFilePath()
private static string GetPluginListFilePath()
{
var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list";
return Path.Combine(_tempFolder, filename);
string pluginListFilePath;
switch (GlobalSettings.LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
pluginListFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.list");
break;
case LocalTempStorage.EnvironmentTemp:
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path
appDomainHash);
pluginListFilePath = Path.Combine(cachePath, "umbraco-plugins.list");
break;
case LocalTempStorage.Default:
default:
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
pluginListFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list");
break;
}
//ensure the folder exists
var folder = Path.GetDirectoryName(pluginListFilePath);
if (folder == null)
throw new InvalidOperationException("The folder could not be determined for the file " + pluginListFilePath);
if (Directory.Exists(folder) == false)
Directory.CreateDirectory(folder);
return pluginListFilePath;
}
private string GetPluginHashFilePath()
private static string GetPluginHashFilePath()
{
var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash";
return Path.Combine(_tempFolder, filename);
string pluginHashFilePath;
switch (GlobalSettings.LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
pluginHashFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.hash");
break;
case LocalTempStorage.EnvironmentTemp:
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path
appDomainHash);
pluginHashFilePath = Path.Combine(cachePath, "umbraco-plugins.hash");
break;
case LocalTempStorage.Default:
default:
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
pluginHashFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash");
break;
}
//ensure the folder exists
var folder = Path.GetDirectoryName(pluginHashFilePath);
if (folder == null)
throw new InvalidOperationException("The folder could not be determined for the file " + pluginHashFilePath);
if (Directory.Exists(folder) == false)
Directory.CreateDirectory(folder);
return pluginHashFilePath;
}
internal void WriteCache()
{
// be absolutely sure
if (Directory.Exists(_tempFolder) == false)
Directory.CreateDirectory(_tempFolder);
var filePath = GetPluginListFilePath();
using (var stream = GetFileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout))
using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout))
using (var writer = new StreamWriter(stream))
{
foreach (var typeList in _types.Values)
@@ -461,8 +503,7 @@ namespace Umbraco.Core
internal void UpdateCache()
{
// note
// at the moment we write the cache to disk every time we update it. ideally we defer the writing
// TODO: at the moment we write the cache to disk every time we update it. ideally we defer the writing
// since all the updates are going to happen in a row when Umbraco starts. that being said, the
// file is small enough, so it is not a priority.
WriteCache();
@@ -475,13 +516,13 @@ namespace Umbraco.Core
while (true)
{
try
{
{
return new FileStream(path, fileMode, fileAccess, fileShare);
}
catch
catch (Exception ex)
{
if (--attempts == 0)
throw;
throw;
LogHelper.Debug<PluginManager>(string.Format("Attempted to get filestream for file {0} failed, {1} attempts left, pausing for {2} milliseconds", path, attempts, pauseMilliseconds));
Thread.Sleep(pauseMilliseconds);
@@ -691,7 +732,7 @@ namespace Umbraco.Core
// else proceed,
typeList = new TypeList(baseType, attributeType);
var scan = RequiresRescanning || File.Exists(GetPluginListFilePath()) == false;
var scan = RequiresRescanning || File.Exists(_pluginListFilePath.Value) == false;
if (scan)
{
@@ -178,29 +178,25 @@ namespace Umbraco.Core.PropertyEditors
return result;
}
private void ConvertItemsToJsonIfDetected(IDictionary<string, object> result)
protected void ConvertItemsToJsonIfDetected(IDictionary<string, object> result)
{
//now we're going to try to see if any of the values are JSON, if they are we'll convert them to real JSON objects
// so they can be consumed as real json in angular!
// convert values that are Json to true Json objects that can be consumed by Angular
var keys = result.Keys.ToArray();
for (var i = 0; i < keys.Length; i++)
{
if (result[keys[i]] is string)
if ((result[keys[i]] is string) == false) continue;
var asString = result[keys[i]].ToString();
if (asString.DetectIsJson() == false) continue;
try
{
var asString = result[keys[i]].ToString();
if (asString.DetectIsJson())
{
try
{
var json = JsonConvert.DeserializeObject(asString);
result[keys[i]] = json;
}
catch
{
//swallow this exception, we thought it was json but it really isn't so continue returning a string
}
}
result[keys[i]] = JsonConvert.DeserializeObject(asString);
}
catch
{
// swallow this exception, we thought it was Json but it really isn't so continue returning a string
}
}
}
@@ -1,13 +1,14 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.PropertyEditors.ValueConverters
{
[DefaultPropertyValueConverter]
[PropertyValueType(typeof(string))]
[PropertyValueCache(PropertyCacheValue.All, PropertyCacheLevel.Content)]
public class ColorPickerValueConverter : PropertyValueConverterBase
public class ColorPickerValueConverter : PropertyValueConverterBase, IPropertyValueConverterMeta
{
public override bool IsConverter(PublishedPropertyType propertyType)
{
@@ -18,11 +19,60 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
return false;
}
public Type GetPropertyValueType(PublishedPropertyType propertyType)
{
return UseLabel(propertyType) ? typeof(PickedColor) : typeof(string);
}
public PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType, PropertyCacheValue cacheValue)
{
return PropertyCacheLevel.Content;
}
private bool UseLabel(PublishedPropertyType propertyType)
{
var preValues = ApplicationContext.Current.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(propertyType.DataTypeId);
PreValue preValue;
return preValues.PreValuesAsDictionary.TryGetValue("useLabel", out preValue) && preValue.Value == "1";
}
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
{
// make sure it's a string
return source == null ? string.Empty : source.ToString();
var useLabel = UseLabel(propertyType);
if (source == null) return useLabel ? null : string.Empty;
var ssource = source.ToString();
if (ssource.DetectIsJson())
{
try
{
var jo = JsonConvert.DeserializeObject<JObject>(ssource);
if (useLabel) return new PickedColor(jo["value"].ToString(), jo["label"].ToString());
return jo["value"].ToString();
}
catch { /* not json finally */ }
}
if (useLabel) return new PickedColor(ssource, ssource);
return ssource;
}
public class PickedColor
{
public PickedColor(string color, string label)
{
Color = color;
Label = label;
}
public string Color { get; private set; }
public string Label { get; private set; }
public override string ToString()
{
return Color;
}
}
}
}
@@ -43,8 +43,8 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
var gridConfig = UmbracoConfig.For.GridConfig(
ApplicationContext.Current.ProfilingLogger.Logger,
ApplicationContext.Current.ApplicationCache.RuntimeCache,
new DirectoryInfo(HttpContext.Current.Server.MapPath(SystemDirectories.AppPlugins)),
new DirectoryInfo(HttpContext.Current.Server.MapPath(SystemDirectories.Config)),
new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins)),
new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Config)),
HttpContext.Current.IsDebuggingEnabled);
var sections = GetArray(obj, "sections");
@@ -14,6 +14,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using umbraco.interfaces;
using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Sync
@@ -39,6 +40,7 @@ namespace Umbraco.Core.Sync
private bool _syncing;
private bool _released;
private readonly ProfilingLogger _profilingLogger;
private readonly Lazy<string> _distCacheFilePath = new Lazy<string>(GetDistCacheFilePath);
protected DatabaseServerMessengerOptions Options { get; private set; }
protected ApplicationContext ApplicationContext { get { return _appContext; } }
@@ -460,10 +462,9 @@ namespace Umbraco.Core.Sync
/// </remarks>
private void ReadLastSynced()
{
var path = SyncFilePath;
if (File.Exists(path) == false) return;
if (File.Exists(_distCacheFilePath.Value) == false) return;
var content = File.ReadAllText(path);
var content = File.ReadAllText(_distCacheFilePath.Value);
int last;
if (int.TryParse(content, out last))
_lastId = last;
@@ -478,7 +479,7 @@ namespace Umbraco.Core.Sync
/// </remarks>
private void SaveLastSynced(int id)
{
File.WriteAllText(SyncFilePath, id.ToString(CultureInfo.InvariantCulture));
File.WriteAllText(_distCacheFilePath.Value, id.ToString(CultureInfo.InvariantCulture));
_lastId = id;
}
@@ -498,20 +499,40 @@ namespace Umbraco.Core.Sync
+ "/D" + AppDomain.CurrentDomain.Id // eg 22
+ "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique
/// <summary>
/// Gets the sync file path for the local server.
/// </summary>
/// <returns>The sync file path for the local server.</returns>
private static string SyncFilePath
private static string GetDistCacheFilePath()
{
get
{
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/DistCache/" + NetworkHelper.FileSafeMachineName);
if (Directory.Exists(tempFolder) == false)
Directory.CreateDirectory(tempFolder);
var fileName = HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt";
return Path.Combine(tempFolder, HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt");
string distCacheFilePath;
switch (GlobalSettings.LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
distCacheFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData", fileName);
break;
case LocalTempStorage.EnvironmentTemp:
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path
appDomainHash);
distCacheFilePath = Path.Combine(cachePath, fileName);
break;
case LocalTempStorage.Default:
default:
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/DistCache");
distCacheFilePath = Path.Combine(tempFolder, fileName);
break;
}
//ensure the folder exists
var folder = Path.GetDirectoryName(distCacheFilePath);
if (folder == null)
throw new InvalidOperationException("The folder could not be determined for the file " + distCacheFilePath);
if (Directory.Exists(folder) == false)
Directory.CreateDirectory(folder);
return distCacheFilePath;
}
#endregion
+49 -5
View File
@@ -1,13 +1,12 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Text;
using System.Web;
using System.Web.Compilation;
using System.Web.Hosting;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
@@ -21,6 +20,38 @@ namespace Umbraco.Core
{
private static volatile HashSet<Assembly> _localFilteredAssemblyCache;
private static readonly object LocalFilteredAssemblyCacheLocker = new object();
private static readonly List<string> NotifiedLoadExceptionAssemblies = new List<string>();
private static string[] _assembliesAcceptingLoadExceptions;
private static string[] AssembliesAcceptingLoadExceptions
{
get
{
if (_assembliesAcceptingLoadExceptions != null)
return _assembliesAcceptingLoadExceptions;
var s = ConfigurationManager.AppSettings["Umbraco.AssembliesAcceptingLoadExceptions"];
return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s)
? new string[0]
: s.Split(',').Select(x => x.Trim()).ToArray();
}
}
private static bool AcceptsLoadExceptions(Assembly a)
{
if (AssembliesAcceptingLoadExceptions.Length == 0)
return false;
if (AssembliesAcceptingLoadExceptions.Length == 1 && AssembliesAcceptingLoadExceptions[0] == "*")
return true;
var name = a.GetName().Name; // simple name of the assembly
return AssembliesAcceptingLoadExceptions.Any(pattern =>
{
if (pattern.Length > name.Length) return false; // pattern longer than name
if (pattern.Length == name.Length) return pattern.InvariantEquals(name); // same length, must be identical
if (pattern[pattern.Length] != '.') return false; // pattern is shorter than name, must end with dot
return name.StartsWith(pattern); // and name must start with pattern
});
}
/// <summary>
/// lazily load a reference to all assemblies and only local assemblies.
@@ -45,7 +76,7 @@ namespace Umbraco.Core
HashSet<Assembly> assemblies = null;
try
{
var isHosted = HttpContext.Current != null || HostingEnvironment.IsHosted;
var isHosted = IOHelper.IsHosted;
try
{
@@ -528,9 +559,22 @@ namespace Umbraco.Core
AppendCouldNotLoad(sb, a, getAll);
foreach (var loaderException in rex.LoaderExceptions.WhereNotNull())
AppendLoaderException(sb, loaderException);
var ex = new ReflectionTypeLoadException(rex.Types, rex.LoaderExceptions, sb.ToString());
// rethrow with new message
throw new ReflectionTypeLoadException(rex.Types, rex.LoaderExceptions, sb.ToString());
// rethrow with new message, unless accepted
if (AcceptsLoadExceptions(a) == false) throw ex;
// log a warning, and return what we can
lock (NotifiedLoadExceptionAssemblies)
{
if (NotifiedLoadExceptionAssemblies.Contains(a.FullName) == false)
{
NotifiedLoadExceptionAssemblies.Add(a.FullName);
LogHelper.WarnWithException(typeof(TypeFinder), "Could not load all types from " + a.GetName().Name + ".", ex);
}
}
return rex.Types.WhereNotNull().ToArray();
}
}
+1 -1
View File
@@ -198,7 +198,7 @@
<Compile Include="Configuration\BaseRest\ExtensionElement.cs" />
<Compile Include="Configuration\BaseRest\ExtensionElementCollection.cs" />
<Compile Include="Configuration\BaseRest\MethodElement.cs" />
<Compile Include="Configuration\ContentXmlStorage.cs" />
<Compile Include="Configuration\LocalTempStorage.cs" />
<Compile Include="Configuration\CoreDebug.cs" />
<Compile Include="Configuration\Dashboard\AccessElement.cs" />
<Compile Include="Configuration\Dashboard\AccessItem.cs" />
+1 -1
View File
@@ -20,7 +20,7 @@ using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Models
{
[TestFixture]
public class ContentTests : BaseUmbracoConfigurationTest
public class ContentTests : BaseUmbracoApplicationTest
{
[SetUp]
public void Init()
@@ -388,13 +388,12 @@ namespace Umbraco.Tests.TestHelpers
return ctx;
}
protected FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null)
protected virtual FakeHttpContextFactory GetHttpContextFactory(string url, RouteData routeData = null)
{
var factory = routeData != null
? new FakeHttpContextFactory(url, routeData)
: new FakeHttpContextFactory(url);
//set the state helper
StateHelper.HttpContext = factory.HttpContext;
@@ -118,25 +118,12 @@ namespace Umbraco.Tests.TestHelpers
});
}
/// <summary>
/// By default this returns false which means the plugin manager will not be reset so it doesn't need to re-scan
/// all of the assemblies. Inheritors can override this if plugin manager resetting is required, generally needs
/// to be set to true if the SetupPluginManager has been overridden.
/// </summary>
protected virtual bool PluginManagerResetRequired
{
get { return false; }
}
/// <summary>
/// Inheritors can resset the plugin manager if they choose to on teardown
/// </summary>
protected virtual void ResetPluginManager()
{
if (PluginManagerResetRequired)
{
PluginManager.Current = null;
}
PluginManager.Current = null;
}
protected virtual CacheHelper CreateCacheHelper()
@@ -185,26 +172,23 @@ namespace Umbraco.Tests.TestHelpers
/// </summary>
protected virtual void SetupPluginManager()
{
if (PluginManager.Current == null || PluginManagerResetRequired)
PluginManager.Current = new PluginManager(
new ActivatorServiceProvider(),
CacheHelper.RuntimeCache, ProfilingLogger, false)
{
PluginManager.Current = new PluginManager(
new ActivatorServiceProvider(),
CacheHelper.RuntimeCache, ProfilingLogger, false)
AssembliesToScan = new[]
{
AssembliesToScan = new[]
{
Assembly.Load("Umbraco.Core"),
Assembly.Load("umbraco"),
Assembly.Load("Umbraco.Tests"),
Assembly.Load("businesslogic"),
Assembly.Load("cms"),
Assembly.Load("controls"),
Assembly.Load("umbraco.editorControls"),
Assembly.Load("umbraco.MacroEngines"),
Assembly.Load("umbraco.providers"),
}
};
}
Assembly.Load("Umbraco.Core"),
Assembly.Load("umbraco"),
Assembly.Load("Umbraco.Tests"),
Assembly.Load("businesslogic"),
Assembly.Load("cms"),
Assembly.Load("controls"),
Assembly.Load("umbraco.editorControls"),
Assembly.Load("umbraco.MacroEngines"),
Assembly.Load("umbraco.providers"),
}
};
}
/// <summary>
+2 -1
View File
@@ -7,11 +7,12 @@ using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Deploy;
using Umbraco.Core.Serialization;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests
{
[TestFixture]
public class UdiTests
public class UdiTests : BaseUmbracoApplicationTest
{
[Test]
public void StringUdiCtorTest()
@@ -7,12 +7,22 @@ var app = angular.module("Umbraco.canvasdesigner", ['colorpicker', 'ui.slider',
.controller("Umbraco.canvasdesignerController", function ($scope, $http, $window, $timeout, $location, dialogService) {
var isInit = $location.search().init;
if (isInit === "true") {
//do not continue, this is the first load of this new window, if this is passed in it means it's been
//initialized by the content editor and then the content editor will actually re-load this window without
//this flag. This is a required trick to get around chrome popup mgr. We don't want to double load preview.aspx
//since that will double prepare the preview documents
return;
}
$scope.isOpen = false;
$scope.frameLoaded = false;
$scope.enableCanvasdesigner = 0;
$scope.googleFontFamilies = {};
$scope.pageId = $location.search().id;
$scope.pageUrl = "../dialogs/Preview.aspx?id=" + $location.search().id;
var pageId = $location.search().id;
$scope.pageId = pageId;
$scope.pageUrl = "../dialogs/Preview.aspx?id=" + pageId;
$scope.valueAreLoaded = false;
$scope.devices = [
{ name: "desktop", css: "desktop", icon: "icon-display", title: "Desktop" },
@@ -208,9 +208,9 @@
if (!$scope.busy) {
// Chromes popup blocker will kick in if a window is opened
// outwith the initial scoped request. This trick will fix that.
// without the initial scoped request. This trick will fix that.
//
var previewWindow = $window.open('preview/?id=' + content.id, 'umbpreview');
var previewWindow = $window.open('preview/?init=true&id=' + content.id, 'umbpreview');
// Build the correct path so both /#/ and #/ work.
var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?id=' + content.id;
@@ -66,9 +66,13 @@ Use this directive to render an avatar.
function getNameInitials(name) {
if(name) {
var initials = name.match(/\b\w/g) || [];
initials = ((initials.shift() || '') + (initials.pop() || '')).toUpperCase();
return initials;
var names = name.split(' '),
initials = names[0].substring(0, 1);
if (names.length > 1) {
initials += names[names.length - 1].substring(0, 1);
}
return initials.toUpperCase();
}
return null;
}
@@ -0,0 +1,26 @@
(function () {
'use strict';
// comes from https://codepen.io/jakob-e/pen/eNBQaP
// works fine with Angular 1.6.5 - alas not with 1.1.5 - binding issue
function PasswordToggleDirective($compile) {
var directive = {
restrict: 'A',
scope: {},
link: function(scope, elem, attrs) {
scope.tgl = function () { elem.attr("type", (elem.attr("type") === "text" ? "password" : "text")); }
var lnk = angular.element("<a data-ng-click=\"tgl()\">Toggle</a>");
$compile(lnk)(scope);
elem.wrap("<div class=\"password-toggle\"/>").after(lnk);
}
};
return directive;
}
angular.module('umbraco.directives').directive('umbPasswordToggle', PasswordToggleDirective);
})();
@@ -2,12 +2,12 @@
* @ngdoc service
* @name umbraco.services.assetsService
*
* @requires $q
* @requires $q
* @requires angularHelper
*
*
* @description
* Promise-based utillity service to lazy-load client-side dependencies inside angular controllers.
*
*
* ##usage
* To use, simply inject the assetsService into any controller that needs it, and make
* sure the umbraco.services module is accesible - which it should be by default.
@@ -18,7 +18,7 @@
* //this code executes when the dependencies are done loading
* });
* });
* </pre>
* </pre>
*
* You can also load individual files, which gives you greater control over what attibutes are passed to the file, as well as timeout
*
@@ -38,13 +38,14 @@
* //loadcss cannot determine when the css is done loading, so this will trigger instantly
* });
* });
* </pre>
* </pre>
*/
angular.module('umbraco.services')
.factory('assetsService', function ($q, $log, angularHelper, umbRequestHelper, $rootScope, $http) {
var initAssetsLoaded = false;
var appendRnd = function (url) {
function appendRnd (url) {
//if we don't have a global umbraco obj yet, the app is bootstrapping
if (!Umbraco.Sys.ServerVariables.application) {
return url;
@@ -77,7 +78,8 @@ angular.module('umbraco.services')
return this.loadedAssets[path];
}
},
/**
/**
Internal method. This is called when the application is loading and the user is already authenticated, or once the user is authenticated.
There's a few assets the need to be loaded for the application to function but these assets require authentication to load.
*/
@@ -108,10 +110,10 @@ angular.module('umbraco.services')
*
* @description
* Injects a file as a stylesheet into the document head
*
*
* @param {String} path path to the css file to load
* @param {Scope} scope optional scope to pass into the loader
* @param {Object} keyvalue collection of attributes to pass to the stylesheet element
* @param {Object} keyvalue collection of attributes to pass to the stylesheet element
* @param {Number} timeout in milliseconds
* @returns {Promise} Promise object which resolves when the file has loaded
*/
@@ -149,10 +151,10 @@ angular.module('umbraco.services')
*
* @description
* Injects a file as a javascript into the document
*
*
* @param {String} path path to the js file to load
* @param {Scope} scope optional scope to pass into the loader
* @param {Object} keyvalue collection of attributes to pass to the script element
* @param {Object} keyvalue collection of attributes to pass to the script element
* @param {Number} timeout in milliseconds
* @returns {Promise} Promise object which resolves when the file has loaded
*/
@@ -192,8 +194,8 @@ angular.module('umbraco.services')
* @methodOf umbraco.services.assetsService
*
* @description
* Injects a collection of files, this can be ONLY js files
*
* Injects a collection of css and js files
*
*
* @param {Array} pathArray string array of paths to the files to load
* @param {Scope} scope optional scope to pass into the loader
@@ -206,61 +208,72 @@ angular.module('umbraco.services')
throw "pathArray must be an array";
}
// Check to see if there's anything to load, resolve promise if not
var nonEmpty = _.reject(pathArray, function (item) {
return item === undefined || item === "";
});
});
//don't load anything if there's nothing to load
if (nonEmpty.length > 0) {
var promises = [];
var assets = [];
//compile a list of promises
//blocking
_.each(nonEmpty, function (path) {
path = convertVirtualPath(path);
var asset = service._getAssetPromise(path);
//if not previously loaded, add to list of promises
if (asset.state !== "loaded") {
if (asset.state === "new") {
asset.state = "loading";
assets.push(asset);
}
//we need to always push to the promises collection to monitor correct
//execution
promises.push(asset.deferred.promise);
}
});
//gives a central monitoring of all assets to load
promise = $q.all(promises);
_.each(assets, function (asset) {
LazyLoad.js(appendRnd(asset.path), function () {
asset.state = "loaded";
if (!scope) {
asset.deferred.resolve(true);
}
else {
angularHelper.safeApply(scope, function () {
asset.deferred.resolve(true);
});
}
});
});
}
else {
//return and resolve
if (nonEmpty.length === 0) {
var deferred = $q.defer();
promise = deferred.promise;
deferred.resolve(true);
return promise;
}
//compile a list of promises
//blocking
var promises = [];
var assets = [];
_.each(nonEmpty, function (path) {
path = convertVirtualPath(path);
var asset = service._getAssetPromise(path);
//if not previously loaded, add to list of promises
if (asset.state !== "loaded") {
if (asset.state === "new") {
asset.state = "loading";
assets.push(asset);
}
//we need to always push to the promises collection to monitor correct
//execution
promises.push(asset.deferred.promise);
}
});
//gives a central monitoring of all assets to load
promise = $q.all(promises);
// Split into css and js asset arrays, and use LazyLoad on each array
var cssAssets = _.filter(assets,
function (asset) {
return asset.path.match(/(\.css$|\.css\?)/ig);
});
var jsAssets = _.filter(assets,
function (asset) {
return asset.path.match(/(\.js$|\.js\?)/ig);
});
function assetLoaded(asset) {
asset.state = "loaded";
if (!scope) {
asset.deferred.resolve(true);
return;
}
angularHelper.safeApply(scope,
function () {
asset.deferred.resolve(true);
});
}
if (cssAssets.length > 0) {
var cssPaths = _.map(cssAssets, function (asset) { return appendRnd(asset.path) });
LazyLoad.css(cssPaths, function() { _.each(cssAssets, assetLoaded); });
}
if (jsAssets.length > 0) {
var jsPaths = _.map(jsAssets, function (asset) { return appendRnd(asset.path) });
LazyLoad.js(jsPaths, function () { _.each(jsAssets, assetLoaded); });
}
return promise;
}
@@ -261,6 +261,9 @@ input[type="submit"].btn {
*padding-top: 1px;
*padding-bottom: 1px;
}
// Safari defaults to 1px for input. Ref U4-7721.
margin: 0px;
}
@@ -35,3 +35,10 @@
margin-right: 5px;
color: @gray-7;
}
input.umb-breadcrumbs__add-ancestor {
height: 25px;
margin-top: -2px;
margin-left: 3px;
width: 100px;
}
@@ -636,9 +636,19 @@
clear: both;
}
.umb-grid .mce-btn button {
padding: 8px 6px;
line-height: inherit;
.umb-grid .mce-btn {
button {
padding-top: 8px;
padding-bottom: 8px;
padding-left: 6px;
line-height: inherit;
}
&:not(.mce-menubtn) {
button {
padding-right: 6px;
}
}
}
.umb-grid .mce-toolbar {
@@ -66,7 +66,7 @@
margin-bottom: auto;
}
.login-overlay .form input[type="text"],
.login-overlay .form input[type="text"],
.login-overlay .form input[type="password"],
.login-overlay .form input[type="email"] {
height: 36px;
@@ -114,8 +114,44 @@
line-height: 36px;
}
.login-overlay .text-error,
.login-overlay .text-info
.login-overlay .text-error,
.login-overlay .text-info
{
font-weight:bold;
}
}
.password-toggle {
position: relative;
display: block;
user-select: none;
input::-ms-clear, input::-ms-reveal {
display: none;
}
a {
opacity: .5;
cursor: pointer;
display: inline-block;
position: absolute;
height: 1px;
width: 45px;
height: 75%;
font-size: 0;
background-repeat: no-repeat;
background-size: 50%;
background-position: center;
top: 0;
margin-left: -45px;
z-index: 1;
-webkit-tap-highlight-color: transparent;
}
[type="text"] + a {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath fill='%23444' d='M29.6.4C29 0 28 0 27.4.4L21 6.8c-1.4-.5-3-.8-5-.8C9 6 3 10 0 16c1.3 2.6 3 4.8 5.4 6.5l-5 5c-.5.5-.5 1.5 0 2 .3.4.7.5 1 .5s1 0 1.2-.4l27-27C30 2 30 1 29.6.4zM13 10c1.3 0 2.4 1 2.8 2L12 15.8c-1-.4-2-1.5-2-2.8 0-1.7 1.3-3 3-3zm-9.6 6c1.2-2 2.8-3.5 4.7-4.7l.7-.2c-.4 1-.6 2-.6 3 0 1.8.6 3.4 1.6 4.7l-2 2c-1.6-1.2-3-2.7-4-4.4zM24 13.8c0-.8 0-1.7-.4-2.4l-10 10c.7.3 1.6.4 2.4.4 4.4 0 8-3.6 8-8z'/%3E%3Cpath fill='%23444' d='M26 9l-2.2 2.2c2 1.3 3.6 3 4.8 4.8-1.2 2-2.8 3.5-4.7 4.7-2.7 1.5-5.4 2.3-8 2.3-1.4 0-2.6 0-3.8-.4L10 25c2 .6 4 1 6 1 7 0 13-4 16-10-1.4-2.8-3.5-5.2-6-7z'/%3E%3C/svg%3E");
}
[type="password"] + a {
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath fill='%23444' d='M16 6C9 6 3 10 0 16c3 6 9 10 16 10s13-4 16-10c-3-6-9-10-16-10zm8 5.3c1.8 1.2 3.4 2.8 4.6 4.7-1.2 2-2.8 3.5-4.7 4.7-3 1.5-6 2.3-8 2.3s-6-.8-8-2.3C6 19.5 4 18 3 16c1.5-2 3-3.5 5-4.7l.6-.2C8 12 8 13 8 14c0 4.5 3.5 8 8 8s8-3.5 8-8c0-1-.3-2-.6-2.6l.4.3zM16 13c0 1.7-1.3 3-3 3s-3-1.3-3-3 1.3-3 3-3 3 1.3 3 3z'/%3E%3C/svg%3E");
}
}
@@ -109,16 +109,48 @@ ul.color-picker li a {
}
/* pre-value editor */
/*.control-group.color-picker-preval:before {
content: "";
display: inline-block;
vertical-align: middle;
height: 100%;
}*/
/*.control-group.color-picker-preval div.thumbnail {
display: inline-block;
vertical-align: middle;
}*/
.control-group.color-picker-preval div.color-picker-prediv {
display: inline-block;
width: 60%;
}
.control-group.color-picker-preval pre {
display: inline;
margin-right: 20px;
margin-left: 10px;
width: 50%;
white-space: nowrap;
overflow: hidden;
margin-bottom: 0;
vertical-align: middle;
}
.control-group.color-picker-preval btn {
//vertical-align: middle;
}
.control-group.color-picker-preval input[type="text"] {
min-width: 40%;
width: 40%;
display: inline-block;
margin-right: 20px;
margin-top: 1px;
}
.control-group.color-picker-preval label {
border:solid @white 1px;
padding:6px;
border: solid @white 1px;
padding: 6px;
}
@@ -126,21 +158,21 @@ ul.color-picker li a {
// Media picker
// --------------------------------------------------
.umb-mediapicker .add-link {
display: inline-block;
height: 120px;
width: 120px;
text-align: center;
color: @gray-8;
border: 2px @gray-8 dashed;
line-height: 120px;
text-decoration: none;
display: flex;
justify-content:center;
align-items:center;
width: 120px;
text-align: center;
color: @gray-8;
border: 2px @gray-8 dashed;
text-decoration: none;
transition: all 150ms ease-in-out;
transition: all 150ms ease-in-out;
&:hover {
color: @turquoise-d1;
border-color: @turquoise;
}
&:hover {
color: @turquoise-d1;
border-color: @turquoise;
}
}
.umb-mediapicker .picked-image {
@@ -165,6 +197,10 @@ ul.color-picker li a {
text-decoration: none;
}
.umb-mediapicker .add-link-square {
height: 120px;
}
.umb-thumbnails{
@@ -207,11 +243,10 @@ ul.color-picker li a {
.umb-mediapicker .umb-sortable-thumbnails li {
flex-direction: column;
margin: 0;
margin: 0 5px 0 0;
padding: 5px;
}
.umb-sortable-thumbnails li:hover a {
display: flex;
justify-content: center;
@@ -219,16 +254,20 @@ ul.color-picker li a {
}
.umb-sortable-thumbnails li img {
max-width:100%;
max-height:100%;
margin:auto;
display:block;
background-image: url(../img/checkered-background.png);
max-width:100%;
max-height:100%;
margin:auto;
display:block;
background-image: url(../img/checkered-background.png);
}
.umb-sortable-thumbnails li img.noScale{
max-width: none !important;
max-height: none !important;
.umb-sortable-thumbnails li img.trashed {
opacity:0.3;
}
.umb-sortable-thumbnails li img.noScale {
max-width: none !important;
max-height: none !important;
}
.umb-sortable-thumbnails .umb-icon-holder {
@@ -254,8 +293,8 @@ ul.color-picker li a {
}
.umb-sortable-thumbnails li:hover .umb-sortable-thumbnails__actions {
opacity: 1;
visibility: visible;
opacity: 1;
visibility: visible;
}
.umb-sortable-thumbnails .umb-sortable-thumbnails__action {
@@ -285,27 +324,27 @@ ul.color-picker li a {
// -------------------------------------------------
.umb-cropper{
position: relative;
position: relative;
}
.umb-cropper img, .umb-cropper-gravity img{
position: relative;
max-width: 100%;
height: auto;
top: 0;
left: 0;
position: relative;
max-width: 100%;
height: auto;
top: 0;
left: 0;
}
.umb-cropper img {
max-width: none;
max-width: none;
}
.umb-cropper .overlay, .umb-cropper-gravity .overlay {
top: 0;
left: 0;
cursor: move;
z-index: @zindexCropperOverlay;
position: absolute;
top: 0;
left: 0;
cursor: move;
z-index: @zindexCropperOverlay;
position: absolute;
}
.umb-cropper .viewport{
@@ -317,43 +356,43 @@ ul.color-picker li a {
}
.umb-cropper-gravity .viewport{
overflow: hidden;
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
width: 100%;
height: 100%;
}
.umb-cropper .viewport:after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: @zindexCropperOverlay - 1;
-moz-opacity: .75;
opacity: .75;
filter: alpha(opacity=7);
-webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: @zindexCropperOverlay - 1;
-moz-opacity: .75;
opacity: .75;
filter: alpha(opacity=7);
-webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
}
.umb-cropper-gravity .overlay{
width: 14px;
height: 14px;
text-align: center;
border-radius: 20px;
background: @turquoise;
border: 3px solid @white;
opacity: 0.8;
width: 14px;
height: 14px;
text-align: center;
border-radius: 20px;
background: @turquoise;
border: 3px solid @white;
opacity: 0.8;
}
.umb-cropper-gravity .overlay i {
font-size: 26px;
line-height: 26px;
opacity: 0.8 !important;
font-size: 26px;
line-height: 26px;
opacity: 0.8 !important;
}
.umb-cropper .crop-container {
@@ -361,16 +400,16 @@ ul.color-picker li a {
}
.umb-cropper .crop-slider {
padding: 10px;
border-top: 1px solid @gray-10;
margin-top: 10px;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
@media (min-width: 769px) {
padding: 10px;
border-top: 1px solid @gray-10;
margin-top: 10px;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
@media (min-width: 769px) {
padding: 10px 50px 10px 50px;
}
}
}
.umb-cropper .crop-slider i {
@@ -16,7 +16,11 @@
maxFileSize: Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB",
acceptedFileTypes: mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes),
uploaded: false
}
}
$scope.togglePassword = function () {
var elem = $("form[name='loginForm'] input[name='password']");
elem.attr("type", (elem.attr("type") === "text" ? "password" : "text"));
}
function init() {
// Check if it is a new user
@@ -48,7 +52,7 @@
]).then(function () {
$scope.inviteStep = Number(inviteVal);
});
}
}
@@ -82,7 +86,7 @@
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
// set percentage property on file
$scope.avatarFile.uploadProgress = progressPercentage;
$scope.avatarFile.uploadProgress = progressPercentage;
}
}).success(function (data, status, headers, config) {
@@ -149,11 +153,11 @@
//error
formHelper.handleError(err);
$scope.invitedUserPasswordModel.buttonState = "error";
});
}
}
};
var setFieldFocus = function (form, field) {
@@ -180,7 +184,7 @@
}
function resetInputValidation() {
$scope.confirmPassword = "";
$scope.confirmPassword = "";
$scope.password = "";
$scope.login = "";
if ($scope.loginForm) {
@@ -255,7 +259,7 @@
//TODO: Do validation properly like in the invite password update
//if the login and password are not empty we need to automatically
//if the login and password are not empty we need to automatically
// validate them - this is because if there are validation errors on the server
// then the user has to change both username & password to resubmit which isn't ideal,
// so if they're not empty, we'll just make sure to set them to valid.
@@ -289,7 +293,7 @@
});
//setup a watch for both of the model values changing, if they change
// while the form is invalid, then revalidate them so that the form can
// while the form is invalid, then revalidate them so that the form can
// be submitted again.
$scope.loginForm.username.$viewChangeListeners.push(function () {
if ($scope.loginForm.username.$invalid) {
@@ -24,8 +24,8 @@
</label>
<input type="password" ng-model="invitedUserPasswordModel.password" name="password" class="-full-width-input" umb-auto-focus required val-server-field="value" autocomplete="off" ng-minlength="{{invitedUserPasswordModel.passwordPolicies.minPasswordLength}}" />
<span class="help-inline" val-msg-for="password" val-toggle-msg="required"><localize key="user_passwordIsBlank">Your new password cannot be blank!</localize></span>
<span class="help-inline" val-msg-for="password" val-toggle-msg="minlength">Minimum {{invitedUserPasswordModel.passwordPolicies.minPasswordLength}} characters</span>
<span class="help-inline" val-msg-for="password" val-toggle-msg="valServerField"></span>
<span class="help-inline" val-msg-for="password" val-toggle-msg="minlength">Minimum {{invitedUserPasswordModel.passwordPolicies.minPasswordLength}} characters</span>
<span class="help-inline" val-msg-for="password" val-toggle-msg="valServerField"></span>
</div>
<div class="control-group" ng-class="{error: setPasswordForm.confirmPassword.$invalid}">
@@ -49,15 +49,15 @@
<div class="form" ng-if="inviteStep === 2">
<div class="flex justify-center items-center">
<ng-form name="avatarForm">
<umb-progress-bar style="max-width: 100px; margin-bottom: 5px;"
ng-show="avatarFile.uploadStatus === 'uploading'"
progress="{{ avatarFile.uploadProgress }}"
size="s">
</umb-progress-bar>
<div class="umb-info-local-item text-error mt3" ng-if="avatarFile.uploadStatus === 'error'">
{{ avatarFile.serverErrorMessage }}
</div>
@@ -69,7 +69,7 @@
ngf-multiple="false"
ngf-pattern="{{avatarFile.acceptedFileTypes}}"
ngf-max-size="{{ avatarFile.maxFileSize }}">
<umb-avatar color="gray"
size="xl"
unknown-char="+"
@@ -77,7 +77,7 @@
img-srcset="{{invitedUser.avatars[4]}} 2x, {{invitedUser.avatars[4]}} 3x">
</umb-avatar>
</a>
</ng-form>
</div>
@@ -149,8 +149,9 @@
<div class="control-group" ng-class="{error: loginForm.password.$invalid}">
<label><localize key="general_password">Password</localize></label>
<input type="password" ng-model="password" name="password" class="-full-width-input" localize="placeholder" placeholder="@placeholders_password" autocomplete="off" />
</div>
<div class="password-toggle">
<input type="password" ng-model="password" name="password" class="-full-width-input" localize="placeholder" placeholder="@placeholders_password" autocomplete="off" /><a ng-click="togglePassword()">Toggle</a>
</div>
<div class="flex justify-between items-center">
<button type="submit" class="btn btn-success" val-trigger-change="#login .form input"><localize key="general_login">Login</localize></button>
@@ -56,6 +56,46 @@ angular.module("umbraco")
$scope.target = dialogOptions.currentTarget;
}
function onInit() {
if ($scope.startNodeId !== -1) {
entityResource.getById($scope.startNodeId, "media")
.then(function (ent) {
$scope.startNodeId = ent.id;
run();
});
} else {
run();
}
}
function run() {
//default root item
if (!$scope.target) {
if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) {
entityResource.getById($scope.lastOpenedNode, "media")
.then(ensureWithinStartNode, gotoStartNode);
} else {
gotoStartNode();
}
} else {
//if a target is specified, go look it up - generally this target will just contain ids not the actual full
//media object so we need to look it up
var id = $scope.target.udi ? $scope.target.udi : $scope.target.id
var altText = $scope.target.altText;
mediaResource.getById(id)
.then(function (node) {
$scope.target = node;
if (ensureWithinStartNode(node)) {
selectImage(node);
$scope.target.url = mediaHelper.resolveFile(node);
$scope.target.altText = altText;
$scope.openDetailsDialog();
}
},
gotoStartNode);
}
}
$scope.upload = function(v) {
angular.element(".umb-file-dropzone-directive .file-select").click();
};
@@ -107,7 +147,7 @@ angular.module("umbraco")
if (folder.id > 0) {
entityResource.getAncestors(folder.id, "media")
.then(function(anc) {
.then(function(anc) {
$scope.path = _.filter(anc,
function(f) {
return f.path.indexOf($scope.startNodeId) !== -1;
@@ -218,32 +258,6 @@ angular.module("umbraco")
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
}
//default root item
if (!$scope.target) {
if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) {
entityResource.getById($scope.lastOpenedNode, "media")
.then(ensureWithinStartNode, gotoStartNode);
} else {
gotoStartNode();
}
} else {
//if a target is specified, go look it up - generally this target will just contain ids not the actual full
//media object so we need to look it up
var id = $scope.target.udi ? $scope.target.udi : $scope.target.id
var altText = $scope.target.altText;
mediaResource.getById(id)
.then(function(node) {
$scope.target = node;
if (ensureWithinStartNode(node)) {
selectImage(node);
$scope.target.url = mediaHelper.resolveFile(node);
$scope.target.altText = altText;
$scope.openDetailsDialog();
}
},
gotoStartNode);
}
$scope.openDetailsDialog = function() {
$scope.mediaPickerDetailsOverlay = {};
@@ -368,4 +382,7 @@ angular.module("umbraco")
}
}
}
onInit();
});
@@ -48,14 +48,14 @@
<span class="umb-breadcrumbs__seperator">&#47;</span>
</li>
<li class="umb-breadcrumbs__ancestor" ng-if="!lockedFolder">
<li class="umb-breadcrumbs__ancestor" ng-show="!lockedFolder">
<a href ng-hide="showFolderInput" ng-click="showFolderInput = true">
<i class="icon icon-add small"></i>
</a>
<input
type="text"
class="input-foldername input-mini inline"
class="umb-breadcrumbs__add-ancestor"
ng-show="showFolderInput"
ng-model="newFolderName"
ng-keydown="enterSubmitFolder($event)"
@@ -1,25 +1,131 @@
function ColorPickerController($scope) {
$scope.toggleItem = function (color) {
if ($scope.model.value == color) {
$scope.model.value = "";
//this is required to re-validate
$scope.propertyForm.modelValue.$setViewValue($scope.model.value);
}
else {
$scope.model.value = color;
//this is required to re-validate
$scope.propertyForm.modelValue.$setViewValue($scope.model.value);
}
};
function ColorPickerController($scope) {
$scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0;
if ($scope.isConfigured) {
for (var key in $scope.model.config.items) {
if (!$scope.model.config.items[key].hasOwnProperty("value"))
$scope.model.config.items[key] = { value: $scope.model.config.items[key], label: $scope.model.config.items[key] };
}
$scope.model.useLabel = isTrue($scope.model.config.useLabel);
initActiveColor();
}
$scope.toggleItem = function (color) {
var currentColor = $scope.model.value.hasOwnProperty("value")
? $scope.model.value.value
: $scope.model.value;
var newColor;
if (currentColor === color.value) {
// deselect
$scope.model.value = $scope.model.useLabel ? { value: "", label: "" } : "";
newColor = "";
}
else {
// select
$scope.model.value = $scope.model.useLabel ? { value: color.value, label: color.label } : color.value;
newColor = color.value;
}
// this is required to re-validate
$scope.propertyForm.modelValue.$setViewValue(newColor);
};
// Method required by the valPropertyValidator directive (returns true if the property editor has at least one color selected)
$scope.validateMandatory = function () {
$scope.validateMandatory = function () {
var isValid = !$scope.model.validation.mandatory || (
$scope.model.value != null
&& $scope.model.value != ""
&& (!$scope.model.value.hasOwnProperty("value") || $scope.model.value.value !== "")
);
return {
isValid: !$scope.model.validation.mandatory || ($scope.model.value != null && $scope.model.value != ""),
isValid: isValid,
errorMsg: "Value cannot be empty",
errorKey: "required"
};
}
$scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0;
$scope.isConfigured = $scope.model.config && $scope.model.config.items && _.keys($scope.model.config.items).length > 0;
// A color is active if it matches the value and label of the model.
// If the model doesn't store the label, ignore the label during the comparison.
$scope.isActiveColor = function (color) {
// no value
if (!$scope.model.value)
return false;
// Complex color (value and label)?
if (!$scope.model.value.hasOwnProperty("value"))
return $scope.model.value === color.value;
return $scope.model.value.value === color.value && $scope.model.value.label === color.label;
};
// Finds the color best matching the model's color,
// and sets the model color to that one. This is useful when
// either the value or label was changed on the data type.
function initActiveColor() {
// no value
if (!$scope.model.value)
return;
// Complex color (value and label)?
if (!$scope.model.value.hasOwnProperty("value"))
return;
var modelColor = $scope.model.value.value;
var modelLabel = $scope.model.value.label;
// Check for a full match or partial match.
var foundItem = null;
// Look for a fully matching color.
for (var key in $scope.model.config.items) {
var item = $scope.model.config.items[key];
if (item.value == modelColor && item.label == modelLabel) {
foundItem = item;
break;
}
}
// Look for a color with a matching value.
if (!foundItem) {
for (var key in $scope.model.config.items) {
var item = $scope.model.config.items[key];
if (item.value == modelColor) {
foundItem = item;
break;
}
}
}
// Look for a color with a matching label.
if (!foundItem) {
for (var key in $scope.model.config.items) {
var item = $scope.model.config.items[key];
if (item.label == modelLabel) {
foundItem = item;
break;
}
}
}
// If a match was found, set it as the active color.
if (foundItem) {
$scope.model.value.value = foundItem.value;
$scope.model.value.label = foundItem.label;
}
}
// figures out if a value is trueish enough
function isTrue(bool) {
return !!bool && bool !== "0" && angular.lowercase(bool) !== "false";
}
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.ColorPickerController", ColorPickerController);
@@ -5,10 +5,10 @@
</div>
<ul class="thumbnails color-picker">
<li ng-repeat="(key, val) in model.config.items" ng-class="{active: model.value === val}">
<a ng-click="toggleItem(val)" class="thumbnail" hex-bg-color="{{val}}">
</a>
<li ng-repeat="(key, val) in model.config.items" ng-class="{active: isActiveColor(val)}">
<a ng-click="toggleItem(val)" class="thumbnail" hex-bg-color="{{val.value}}">
</a>
<span class="color-label" ng-if="model.useLabel" ng-bind="val.label"></span>
</li>
</ul>
@@ -1,12 +1,13 @@
<div ng-controller="Umbraco.PrevalueEditors.MultiColorPickerController">
<div class="control-group color-picker-preval">
<input name="newColor" type="hidden" />
<label for="newColor" val-highlight="hasError">#{{newColor}}</label>
<input name="newLabel" type="text" ng-model="newLabel" class="umb-editor color-label" placeholder="Label" />
<button class="btn add" ng-click="add($event)"><localize key="general_add">Add</localize></button>
<label for="newColor" val-highlight="hasError">{{newColor}}</label>
</div>
<div class="control-group color-picker-preval" ng-repeat="item in model.value">
<div class="thumbnail span1" hex-bg-color="{{item.value}}" bg-orig="transparent"></div>
<pre>{{item.value}}</pre>
<div class="color-picker-prediv"><pre>#{{item.value}} - {{item.label}}</pre></div>
<button class="btn btn-danger" ng-click="remove(item, $event)"><localize key="general_remove">Remove</localize></button>
</div>
</div>
@@ -2,15 +2,17 @@
function ($scope, $timeout, assetsService, angularHelper, $element) {
//NOTE: We need to make each color an object, not just a string because you cannot 2-way bind to a primitive.
var defaultColor = "000000";
var defaultLabel = null;
$scope.newColor = defaultColor;
$scope.newLavel = defaultLabel;
$scope.hasError = false;
assetsService.load([
//"lib/spectrum/tinycolor.js",
"lib/spectrum/spectrum.js"
"lib/spectrum/spectrum.js"
], $scope).then(function () {
var elem = $element.find("input");
var elem = $element.find("input[name='newColor']");
elem.spectrum({
color: null,
showInitial: false,
@@ -21,7 +23,7 @@
clickoutFiresChange: true,
hide: function (color) {
//show the add butotn
$element.find(".btn.add").show();
$element.find(".btn.add").show();
},
change: function (color) {
angularHelper.safeApply($scope, function () {
@@ -39,21 +41,41 @@
//make an array from the dictionary
var items = [];
for (var i in $scope.model.value) {
items.push({
value: $scope.model.value[i],
id: i
});
var oldValue = $scope.model.value[i];
if (oldValue.hasOwnProperty("value")) {
items.push({
value: oldValue.value,
label: oldValue.label,
id: i
});
} else {
items.push({
value: oldValue,
label: oldValue,
id: i
});
}
}
//now make the editor model the array
$scope.model.value = items;
}
// ensure labels
for (var i = 0; i < $scope.model.value.length; i++) {
var item = $scope.model.value[i];
item.label = item.hasOwnProperty("label") ? item.label : item.value;
}
function validLabel(label) {
return label !== null && typeof label !== "undefined" && label !== "" && label.length && label.length > 0;
}
$scope.remove = function (item, evt) {
evt.preventDefault();
$scope.model.value = _.reject($scope.model.value, function (x) {
return x.value === item.value;
return x.value === item.value && x.label === item.label;
});
};
@@ -63,15 +85,15 @@
evt.preventDefault();
if ($scope.newColor) {
var newLabel = validLabel($scope.newLabel) ? $scope.newLabel : $scope.newColor;
var exists = _.find($scope.model.value, function(item) {
return item.value.toUpperCase() == $scope.newColor.toUpperCase();
return item.value.toUpperCase() === $scope.newColor.toUpperCase() || item.label.toUpperCase() === newLabel.toUpperCase();
});
if (!exists) {
$scope.model.value.push({ value: $scope.newColor });
//$scope.newColor = defaultColor;
// set colorpicker to default color
//var elem = $element.find("input");
//elem.spectrum("set", $scope.newColor);
$scope.model.value.push({
value: $scope.newColor,
label: newLabel
});
$scope.hasError = false;
return;
}
@@ -1,7 +1,7 @@
//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper) {
function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper, localizationService) {
var unsubscribe;
@@ -154,7 +154,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
}
}
if ($routeParams.section === "settings" && $routeParams.tree === "documentTypes") {
//if the content-picker is being rendered inside the document-type editor, we don't need to process the startnode query
dialogOptions.startNodeId = -1;
@@ -287,8 +286,12 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
entityResource.getUrl(entity.id, entityType).then(function(data){
// update url
angular.forEach($scope.renderModel, function(item){
if(item.id === entity.id) {
item.url = data;
if (item.id === entity.id) {
if (entity.trashed) {
item.url = localizationService.dictionary.general_recycleBin;
} else {
item.url = data;
}
}
});
});
@@ -330,6 +333,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
"icon": item.icon,
"path": item.path,
"url": item.url,
"trashed": item.trashed,
"published": (item.metaData && item.metaData.IsPublished === false && entityType === "Document") ? false : true
// only content supports published/unpublished content so we set everything else to published so the UI looks correct
});
@@ -1,5 +1,8 @@
<div ng-controller="Umbraco.PropertyEditors.ContentPickerController" class="umb-editor umb-contentpicker">
<p ng-if="(renderModel|filter:{trashed:true}).length == 1"><localize key="contentPicker_pickedTrashedItem"></localize></p>
<p ng-if="(renderModel|filter:{trashed:true}).length > 1"><localize key="contentPicker_pickedTrashedItems"></localize></p>
<ng-form name="contentPickerForm">
<div ui-sortable="sortableOptions" ng-model="renderModel">
@@ -8,6 +8,6 @@
val-server="value" />
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="valEmail"><localize key="valiation_invalidEmail">Invalid email</localize></span>
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="valEmail"><localize key="validation_invalidEmail">Invalid email</localize></span>
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="valServer"></span>
</div>
@@ -48,14 +48,18 @@
<a href ng-click="scaleDown(currentCell)">
<i class="icon icon-remove"></i>
</a>
{{currentCell.grid}}
{{currentCell.grid}}
<a href ng-click="scaleUp(currentCell, availableRowSpace, true)">
<i class="icon icon-add"></i>
</a>
</div>
</umb-control-group>
<umb-control-group label="@grid_maxItems" description="@grid_maxItemsDescription">
<input type="number" ng-model="currentCell.maxItems" class="umb-editor-tiny" placeholder="Max" min="0" />
</umb-control-group>
<umb-control-group hide-label="true">
<umb-control-group hide-label="true">
<i class="icon-delete red"></i>
<a class="btn btn-small btn-link" href="" ng-click="deleteArea(currentCell, currentRow)">
<localize key="general_delete" class="ng-isolate-scope ng-scope">Delete</localize>
@@ -81,6 +81,7 @@ angular.module("umbraco")
var notIncludedRte = [];
var cancelMove = false;
var startingArea;
$scope.sortableOptionsCell = {
distance: 10,
@@ -112,9 +113,11 @@ angular.module("umbraco")
},
over: function (event, ui) {
var allowedEditors = $(event.target).scope().area.allowed;
var area = $(event.target).scope().area;
var allowedEditors = area.allowed;
if ($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) {
if (($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) ||
(startingArea != area && area.maxItems != '' && area.maxItems > 0 && area.maxItems < area.controls.length + 1)) {
$scope.$apply(function () {
$(event.target).scope().area.dropNotAllowed = true;
@@ -168,6 +171,10 @@ angular.module("umbraco")
start: function (e, ui) {
//Get the starting area for reference
var area = $(e.target).scope().area;
startingArea = area;
// fade out control when sorting
ui.item.context.style.display = "block";
ui.item.context.style.opacity = "0.5";
@@ -224,7 +224,7 @@
<!-- Controls repeat end -->
<!-- if area is empty tools -->
<div class="umb-grid-add-more-content" ng-if="area.controls.length > 0 && !sortMode">
<div class="umb-grid-add-more-content" ng-if="area.controls.length > 0 && !sortMode && (area.maxItems == undefined || area.maxItems == '' || area.maxItems == 0 || area.maxItems > area.controls.length)">
<div class="cell-tools-add -bar newbtn" ng-click="openEditorOverlay($event, area, 0, area.$uniqueId);"><localize key="grid_addElement" /></div>
</div>
@@ -65,7 +65,9 @@
ng-repeat="area in layout.areas | filter:zeroWidthFilter"
ng-style="{width: percentage(area.grid) + '%', 'max-width': '100%'}">
<div class="preview-cell"></div>
<div class="preview-cell">
<p style="font-size: 6px; line-height: 8px; text-align: center" ng-show="area.maxItems > 0">{{area.maxItems}}</p>
</div>
</div>
</div>
</div>
@@ -1,7 +1,7 @@
//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerController",
function ($rootScope, $scope, dialogService, entityResource, mediaResource, mediaHelper, $timeout, userService, $location) {
function ($rootScope, $scope, dialogService, entityResource, mediaResource, mediaHelper, $timeout, userService, $location, localizationService) {
//check the pre-values for multi-picker
var multiPicker = $scope.model.config.multiPicker && $scope.model.config.multiPicker !== '0' ? true : false;
@@ -26,17 +26,47 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
// the mediaResource has server side auth configured for which the user must have
// access to the media section, if they don't they'll get auth errors. The entityResource
// acts differently in that it allows access if the user has access to any of the apps that
// might require it's use. Therefore we need to use the metatData property to get at the thumbnail
// might require it's use. Therefore we need to use the metaData property to get at the thumbnail
// value.
entityResource.getByIds(ids, "Media").then(function (medias) {
entityResource.getByIds(ids, "Media").then(function(medias) {
_.each(medias, function (media, i) {
// The service only returns item results for ids that exist (deleted items are silently ignored).
// This results in the picked items value to be set to contain only ids of picked items that could actually be found.
// Since a referenced item could potentially be restored later on, instead of changing the selected values here based
// on whether the items exist during a save event - we should keep "placeholder" items for picked items that currently
// could not be fetched. This will preserve references and ensure that the state of an item does not differ depending
// on whether it is simply resaved or not.
// This is done by remapping the int/guid ids into a new array of items, where we create "Deleted item" placeholders
// when there is no match for a selected id. This will ensure that the values being set on save, are the same as before.
medias = _.map(ids,
function(id) {
var found = _.find(medias,
function(m) {
// We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and
// it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString()
// compares and be completely sure it works.
return m.udi.toString() === id.toString() || m.id.toString() === id.toString();
});
if (found) {
return found;
} else {
return {
name: localizationService.dictionary.mediaPicker_deletedItem,
id: $scope.model.config.idType !== "udi" ? id : null,
udi: $scope.model.config.idType === "udi" ? id : null,
icon: "icon-picture",
thumbnail: null,
trashed: true
};
}
});
//only show non-trashed items
if (media.parentId >= -1) {
if (!media.thumbnail) {
_.each(medias,
function(media, i) {
// if there is no thumbnail, try getting one if the media is not a placeholder item
if (!media.thumbnail && media.id && media.metaData) {
media.thumbnail = mediaHelper.resolveFileFromEntity(media, true);
}
@@ -44,12 +74,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
if ($scope.model.config.idType === "udi") {
$scope.ids.push(media.udi);
}
else {
} else {
$scope.ids.push(media.id);
}
}
});
});
$scope.sync();
});
@@ -82,8 +110,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
submit: function(model) {
_.each(model.selectedImages, function(media, i) {
if (!media.thumbnail) {
// if there is no thumbnail, try getting one if the media is not a placeholder item
if (!media.thumbnail && media.id && media.metaData) {
media.thumbnail = mediaHelper.resolveFileFromEntity(media, true);
}
@@ -101,10 +129,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
$scope.mediaPickerOverlay.show = false;
$scope.mediaPickerOverlay = null;
}
};
};
$scope.sortableOptions = {
@@ -142,5 +168,4 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
//update the display val again if it has changed from the server
setupViewModel();
};
});
@@ -1,47 +1,47 @@
<div class="umb-editor umb-mediapicker" ng-controller="Umbraco.PropertyEditors.MediaPickerController">
<ul ui-sortable="sortableOptions" ng-model="images" class="umb-sortable-thumbnails">
<li style="width: 120px; height: 100px; overflow: hidden;" ng-repeat="image in images">
<p ng-if="(images|filter:{trashed:true}).length == 1"><localize key="mediaPicker_pickedTrashedItem"></localize></p>
<p ng-if="(images|filter:{trashed:true}).length > 1"><localize key="mediaPicker_pickedTrashedItems"></localize></p>
<!-- IMAGE -->
<img ng-src="{{image.thumbnail}}" alt="" ng-show="image.thumbnail" title="{{image.name}}" />
<div class="flex error">
<ul ui-sortable="sortableOptions" ng-model="images" class="umb-sortable-thumbnails">
<li style="width: 120px; height: 100px; overflow: hidden;" ng-repeat="image in images">
<!-- SVG -->
<img ng-if="image.metaData.umbracoExtension.Value === 'svg'" ng-src="{{image.metaData.umbracoFile.Value}}" alt="" title="{{image.name}}" />
<img ng-if="image.extension === 'svg'" ng-src="{{image.file}}" alt="" />
<!-- IMAGE -->
<img ng-class="{'trashed': image.trashed}" ng-src="{{image.thumbnail}}" alt="" ng-show="image.thumbnail" title="{{image.trashed ? 'Trashed: ' + image.name : image.name}}" />
<!-- FILE -->
<span class="umb-icon-holder" ng-hide="image.thumbnail || image.metaData.umbracoExtension.Value === 'svg' || image.extension === 'svg'">
<i class="icon {{image.icon}} large"></i>
<small>{{image.name}}</small>
</span>
<!-- SVG -->
<img ng-class="{'trashed': image.trashed}" ng-if="image.metaData.umbracoExtension.Value === 'svg'" ng-src="{{image.metaData.umbracoFile.Value}}" alt="" title="{{image.trashed ? 'Trashed: ' + image.name : image.name}}" />
<img ng-class="{'trashed': image.trashed}" ng-if="image.extension === 'svg'" ng-src="{{image.file}}" alt="" />
<div class="umb-sortable-thumbnails__actions">
<a class="umb-sortable-thumbnails__action" href="" ng-click="goToItem(image)">
<i class="icon icon-edit"></i>
</a>
<a class="umb-sortable-thumbnails__action -red" href="" ng-click="remove($index)">
<i class="icon icon-delete"></i>
</a>
</div>
<!-- FILE -->
<span class="umb-icon-holder" ng-hide="image.thumbnail || image.metaData.umbracoExtension.Value === 'svg' || image.extension === 'svg'">
<i class="icon {{image.icon}} large"></i>
<small>{{image.name}}</small>
</span>
</li>
</ul>
<div class="umb-sortable-thumbnails__actions">
<a class="umb-sortable-thumbnails__action" href="" ng-click="goToItem(image)">
<i class="icon icon-edit"></i>
</a>
<a class="umb-sortable-thumbnails__action -red" href="" ng-click="remove($index)">
<i class="icon icon-delete"></i>
</a>
</div>
</li>
<ul class="umb-sortable-thumbnails" ng-if="showAdd()">
<li style="border: none">
<a href="#" class="add-link" ng-click="add()" prevent-default>
<i class="icon icon-add large"></i>
</a>
</li>
</ul>
</ul>
<a href="#" class="add-link" ng-click="add()" ng-class="{'add-link-square': images.length === 0}" ng-if="showAdd()" prevent-default>
<i class="icon icon-add large"></i>
</a>
</div>
<umb-overlay
ng-if="mediaPickerOverlay.show"
model="mediaPickerOverlay"
position="right"
view="mediaPickerOverlay.view">
</umb-overlay>
<umb-overlay
ng-if="mediaPickerOverlay.show"
model="mediaPickerOverlay"
position="right"
view="mediaPickerOverlay.view">
</umb-overlay>
</div>
@@ -76,6 +76,7 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
icon.isCustom = false;
break;
case "styleselect":
case "fontsizeselect":
icon.name = "icon-list";
icon.isCustom = true;
break;
+2 -2
View File
@@ -1027,9 +1027,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7720</DevelopmentServerPort>
<DevelopmentServerPort>7740</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7720</IISUrl>
<IISUrl>http://localhost:7740</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -60,21 +60,29 @@
JObject cfg = contentItem.config;
if(cfg != null)
foreach (JProperty property in cfg.Properties()) {
attrs.Add(property.Name + "='" + property.Value.ToString() + "'");
foreach (JProperty property in cfg.Properties())
{
var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString());
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
}
JObject style = contentItem.styles;
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
{
var propertyValue = property.Value.ToString();
if (string.IsNullOrWhiteSpace(propertyValue) == false)
{
cssVals.Add(property.Name + ":" + propertyValue + ";");
}
}
if (cssVals.Any())
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
if (cssVals.Any())
attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\"");
}
return new MvcHtmlString(string.Join(" ", attrs));
}
}
@@ -60,21 +60,29 @@
JObject cfg = contentItem.config;
if(cfg != null)
foreach (JProperty property in cfg.Properties()) {
attrs.Add(property.Name + "='" + property.Value.ToString() + "'");
foreach (JProperty property in cfg.Properties())
{
var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString());
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
}
JObject style = contentItem.styles;
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
{
var propertyValue = property.Value.ToString();
if (string.IsNullOrWhiteSpace(propertyValue) == false)
{
cssVals.Add(property.Name + ":" + propertyValue + ";");
}
}
if (cssVals.Any())
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
if (cssVals.Any())
attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\"");
}
return new MvcHtmlString(string.Join(" ", attrs));
}
}
@@ -4,9 +4,13 @@
@if (Model.editor.config.markup != null)
{
string markup = Model.editor.config.markup.ToString();
markup = markup.Replace("#value#", Model.value.ToString());
markup = markup.Replace("#style#", Model.editor.config.style.ToString());
var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
markup = markup.Replace("#value#", umbracoHelper.ReplaceLineBreaksForHtml(HttpUtility.HtmlEncode(Model.value.ToString())));
if (Model.editor.config.style != null)
{
markup = markup.Replace("#style#", Model.editor.config.style.ToString());
}
<text>
@Html.Raw(markup)
@@ -935,6 +935,15 @@ Mange hilsner fra Umbraco robotten
<area alias="colorpicker">
<key alias="noColors">Du har ikke konfigureret nogen godkendte farver</key>
</area>
<area alias="contentPicker">
<key alias="pickedTrashedItem">Du har valgt et dokument som er slettet eller lagt i papirkurven</key>
<key alias="pickedTrashedItems">Du har valgt dokumenter som er slettede eller lagt i papirkurven</key>
</area>
<area alias="mediaPicker">
<key alias="pickedTrashedItem">Du har valgt et medie som er slettet eller lagt i papirkurven</key>
<key alias="pickedTrashedItems">Du har valgt medier som er slettede eller lagt i papirkurven</key>
<key alias="deletedItem">Slettet medie</key>
</area>
<area alias="relatedlinks">
<key alias="enterExternal">indtast eksternt link</key>
<key alias="chooseInternal">vælg en intern side</key>
@@ -1251,6 +1260,9 @@ Mange hilsner fra Umbraco robotten
<key alias="chooseDefault">Vælg standard</key>
<key alias="areAdded">er tilføjet</key>
<key alias="maxItems">Maksimalt emner</key>
<key alias="maxItemsDescription">Efterlad blank eller sat til 0 ubegrænset for</key>
</area>
<area alias="contentTypeEditor">
+21 -10
View File
@@ -87,14 +87,14 @@
<key alias="domainUpdated">Domain '%0%' has been updated</key>
<key alias="orEdit">Edit Current Domains</key>
<key alias="domainHelp">
<![CDATA[Valid domain names are: "example.com", "www.example.com", "example.com:8080" or
"https://www.example.com/". One-level paths in domains are supported, eg. "example.com/en". However, they
<![CDATA[Valid domain names are: "example.com", "www.example.com", "example.com:8080" or
"https://www.example.com/". One-level paths in domains are supported, eg. "example.com/en". However, they
should be avoided. Better use the culture setting above.]]>
</key>
<key alias="inherit">Inherit</key>
<key alias="setLanguage">Culture</key>
<key alias="setLanguageHelp">
<![CDATA[Set the culture for nodes below the current node,<br /> or inherit culture from parent nodes. Will also apply<br />
<![CDATA[Set the culture for nodes below the current node,<br /> or inherit culture from parent nodes. Will also apply<br />
to the current node, unless a domain below applies too.]]>
</key>
<key alias="setDomains">Domains</key>
@@ -351,11 +351,11 @@
<key alias="tableColumns">Number of columns</key>
<key alias="tableRows">Number of rows</key>
<key alias="templateContentAreaHelp">
<![CDATA[<strong>Set a placeholder id</strong> by setting an ID on your placeholder you can inject content into this template from child templates,
<![CDATA[<strong>Set a placeholder id</strong> by setting an ID on your placeholder you can inject content into this template from child templates,
by referring this ID using a <code>&lt;asp:content /&gt;</code> element.]]>
</key>
<key alias="templateContentPlaceHolderHelp">
<![CDATA[<strong>Select a placeholder id</strong> from the list below. You can only
<![CDATA[<strong>Select a placeholder id</strong> from the list below. You can only
choose Id's from the current template's master.]]>
</key>
<key alias="thumbnailimageclickfororiginal">Click on the image to see full size</key>
@@ -397,15 +397,15 @@
</area>
<area alias="dictionaryItem">
<key alias="description">
<![CDATA[
Edit the different language versions for the dictionary item '<em>%0%</em>' below<br/>You can add additional languages under the 'languages' in the menu on the left
<![CDATA[
Edit the different language versions for the dictionary item '<em>%0%</em>' below<br/>You can add additional languages under the 'languages' in the menu on the left
]]>
</key>
<key alias="displayName">Culture Name</key>
<key alias="changeKey">Edit the key of the dictionary item.</key>
<key alias="changeKeyError">
<![CDATA[
The key '%0%' already exists.
<![CDATA[
The key '%0%' already exists.
]]>
</key>
</area>
@@ -1081,6 +1081,15 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="colorpicker">
<key alias="noColors">You have not configured any approved colours</key>
</area>
<area alias="contentPicker">
<key alias="pickedTrashedItem">You have picked a content item currently deleted or in the recycle bin</key>
<key alias="pickedTrashedItems">You have picked content items currently deleted or in the recycle bin</key>
</area>
<area alias="mediaPicker">
<key alias="pickedTrashedItem">You have picked a media item currently deleted or in the recycle bin</key>
<key alias="pickedTrashedItems">You have picked media items currently deleted or in the recycle bin</key>
<key alias="deletedItem">Deleted item</key>
</area>
<area alias="relatedlinks">
<key alias="enterExternal">enter external link</key>
<key alias="chooseInternal">choose internal page</key>
@@ -1401,6 +1410,8 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="allowAllEditors">Allow all editors</key>
<key alias="allowAllRowConfigurations">Allow all row configurations</key>
<key alias="maxItemsDescription">Leave blank or set to 0 for unlimited</key>
<key alias="maxItems">Maximum items</key>
<key alias="setAsDefault">Set as default</key>
<key alias="chooseExtra">Choose extra</key>
<key alias="chooseDefault">Choose default</key>
@@ -1869,4 +1880,4 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="textbox">
<key alias="characters_left">characters left</key>
</area>
</language>
</language>
@@ -1079,6 +1079,15 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="colorpicker">
<key alias="noColors">You have not configured any approved colors</key>
</area>
<area alias="contentPicker">
<key alias="pickedTrashedItem">You have picked a content item currently deleted or in the recycle bin</key>
<key alias="pickedTrashedItems">You have picked content items currently deleted or in the recycle bin</key>
</area>
<area alias="mediaPicker">
<key alias="pickedTrashedItem">You have picked a media item currently deleted or in the recycle bin</key>
<key alias="pickedTrashedItems">You have picked media items currently deleted or in the recycle bin</key>
<key alias="deletedItem">Deleted item</key>
</area>
<area alias="relatedlinks">
<key alias="enterExternal">enter external link</key>
<key alias="chooseInternal">choose internal page</key>
@@ -1397,6 +1406,8 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="allowAllEditors">Allow all editors</key>
<key alias="allowAllRowConfigurations">Allow all row configurations</key>
<key alias="maxItems">Maximum items</key>
<key alias="maxItemsDescription">Leave blank or set to 0 for unlimited</key>
<key alias="setAsDefault">Set as default</key>
<key alias="chooseExtra">Choose extra</key>
<key alias="chooseDefault">Choose default</key>
@@ -789,6 +789,9 @@
<key alias="allowAllEditors">Permitir todos los controles de edición</key>
<key alias="allowAllRowConfigurations">Permitir todas las configuraciones de fila</key>
<key alias="maxItems">Artículos máximos</key>
<key alias="maxItemsDescription">Laat dit leeg of is ingesteld op -1 voor onbeperkt</key>
<key alias="maxItemsDescription">Dejar en blanco o se establece en 0 para ilimitada</key>
</area>
<area alias="templateEditor">
<key alias="alternativeField">Campo opcional</key>
@@ -1077,7 +1077,9 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="allowAllEditors">Alle editors toelaten</key>
<key alias="allowAllRowConfigurations">Alle rijconfiguraties toelaten</key>
<key alias="setAsDefault">Instellen als standaard</key>
<key alias="maxItems">Maximale artikelen</key>
<key alias="maxItemsDescription">Laat dit leeg of is ingesteld op -1 voor onbeperkt</key>
<key alias="setAsDefault">Instellen als standaard</key>
<key alias="chooseExtra">Kies extra</key>
<key alias="chooseDefault">Kies standaard</key>
<key alias="areAdded">zijn toegevoegd</key>
@@ -30,7 +30,7 @@ namespace Umbraco.Web.UI.Umbraco.Dialogs
}
DocumentId = doc.Id;
PageName = doc.Name;
PageName = Server.HtmlEncode(doc.Name);
DocumentPath = doc.Path;
}
+1 -21
View File
@@ -643,9 +643,7 @@ namespace Umbraco.Web.Editors
ShowMessageForPublishStatus(publishStatus.Result, display);
break;
}
UpdatePreviewContext(contentItem.PersistedContent.Id);
//If the item is new and the operation was cancelled, we need to return a different
// status code so the UI can handle it since it won't be able to redirect since there
// is no Id to redirect to!
@@ -875,24 +873,6 @@ namespace Umbraco.Web.Editors
}
}
/// <summary>
/// Checks if the user is currently in preview mode and if so will update the preview content for this item
/// </summary>
/// <param name="contentId"></param>
private void UpdatePreviewContext(int contentId)
{
var previewId = Request.GetPreviewCookieValue();
if (previewId.IsNullOrWhiteSpace()) return;
Guid id;
if (Guid.TryParse(previewId, out id))
{
var d = new Document(contentId);
var pc = new PreviewContent(UmbracoUser, id, false);
pc.PrepareDocument(UmbracoUser, d, true);
pc.SavePreviewSet();
}
}
/// <summary>
/// Maps the dto property values to the persisted model
/// </summary>
+75 -61
View File
@@ -599,12 +599,14 @@ namespace Umbraco.Web.Editors
throw;
}
}
[EnsureUserPermissionForMedia("folder.ParentId")]
public MediaItemDisplay PostAddFolder(EntityBasic folder)
public MediaItemDisplay PostAddFolder(PostedFolder folder)
{
var intParentId = GetParentIdAsInt(folder.ParentId, validatePermissions:true);
var mediaService = ApplicationContext.Services.MediaService;
var f = mediaService.CreateMedia(folder.Name, folder.ParentId, Constants.Conventions.MediaTypes.Folder);
var f = mediaService.CreateMedia(folder.Name, intParentId, Constants.Conventions.MediaTypes.Folder);
mediaService.Save(f, Security.CurrentUser.Id);
return Mapper.Map<IMedia, MediaItemDisplay>(f);
@@ -636,66 +638,15 @@ namespace Umbraco.Web.Editors
if (result.FileData.Count == 0)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
}
}
//get the string json from the request
int parentId; bool entityFound; GuidUdi parentUdi;
string currentFolderId = result.FormData["currentFolder"];
// test for udi
if (GuidUdi.TryParse(currentFolderId, out parentUdi))
{
currentFolderId = parentUdi.Guid.ToString();
}
if (int.TryParse(currentFolderId, out parentId) == false)
{
// if a guid then try to look up the entity
Guid idGuid;
if (Guid.TryParse(currentFolderId, out idGuid))
{
var entity = Services.EntityService.GetByKey(idGuid);
if (entity != null)
{
entityFound = true;
parentId = entity.Id;
}
else
{
throw new EntityNotFoundException(currentFolderId, "The passed id doesn't exist");
}
}
else
{
return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer or Guid");
}
if (entityFound == false)
{
return Request.CreateValidationErrorResponse("The request was not formatted correctly, the currentFolder is not an integer or Guid");
}
}
//ensure the user has access to this folder by parent id!
if (CheckPermissions(
new Dictionary<string, object>(),
Security.CurrentUser,
Services.MediaService,
Services.EntityService,
parentId) == false)
{
return Request.CreateResponse(
HttpStatusCode.Forbidden,
new SimpleNotificationModel(new Notification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"),
SpeechBubbleIcon.Warning)));
}
string currentFolderId = result.FormData["currentFolder"];
int parentId = GetParentIdAsInt(currentFolderId, validatePermissions: true);
var tempFiles = new PostedFiles();
var mediaService = ApplicationContext.Services.MediaService;
//in case we pass a path with a folder in it, we will create it and upload media to it.
if (result.FormData.ContainsKey("path"))
{
@@ -830,6 +781,69 @@ namespace Umbraco.Web.Editors
return Request.CreateResponse(HttpStatusCode.OK, tempFiles);
}
/// <summary>
/// Given a parent id which could be a GUID, UDI or an INT, this will resolve the INT
/// </summary>
/// <param name="parentId"></param>
/// <param name="validatePermissions">
/// If true, this will check if the current user has access to the resolved integer parent id
/// and if that check fails an unauthorized exception will occur
/// </param>
/// <returns></returns>
private int GetParentIdAsInt(string parentId, bool validatePermissions)
{
int intParentId;
GuidUdi parentUdi;
// test for udi
if (GuidUdi.TryParse(parentId, out parentUdi))
{
parentId = parentUdi.Guid.ToString();
}
//if it's not an INT then we'll check for GUID
if (int.TryParse(parentId, out intParentId) == false)
{
// if a guid then try to look up the entity
Guid idGuid;
if (Guid.TryParse(parentId, out idGuid))
{
var entity = Services.EntityService.GetByKey(idGuid);
if (entity != null)
{
intParentId = entity.Id;
}
else
{
throw new EntityNotFoundException(parentId, "The passed id doesn't exist");
}
}
else
{
throw new HttpResponseException(
Request.CreateValidationErrorResponse("The request was not formatted correctly, the parentId is not an integer, Guid or UDI"));
}
}
//ensure the user has access to this folder by parent id!
if (validatePermissions && CheckPermissions(
new Dictionary<string, object>(),
Security.CurrentUser,
Services.MediaService,
Services.EntityService,
intParentId) == false)
{
throw new HttpResponseException(Request.CreateResponse(
HttpStatusCode.Forbidden,
new SimpleNotificationModel(new Notification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"),
SpeechBubbleIcon.Warning))));
}
return intParentId;
}
/// <summary>
/// Ensures the item can be moved/copied to the new location
/// </summary>
@@ -2,10 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Umbraco.Core;
using Umbraco.Core.Models.Validation;
@@ -0,0 +1,17 @@
using System.Runtime.Serialization;
namespace Umbraco.Web.Models.ContentEditing
{
/// <summary>
/// Used to create a folder with the MediaController
/// </summary>
[DataContract]
public class PostedFolder
{
[DataMember(Name = "parentId")]
public string ParentId { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
}
}
@@ -107,7 +107,7 @@ namespace Umbraco.Web.Models.Mapping
}
//fill in the template config to be passed to the template drop down.
var templateItemConfig = new Dictionary<string, string> {{"", localizedText.Localize("general/choose")}};
var templateItemConfig = new Dictionary<string, string>();
foreach (var t in content.ContentType.AllowedTemplates
.Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false))
{
@@ -158,7 +158,9 @@ namespace Umbraco.Web.Models.Mapping
{
Alias = string.Format("{0}template", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
Label = localizedText.Localize("template/template"),
Value = display.TemplateAlias,
Value = string.IsNullOrEmpty(display.TemplateAlias)
? (content.ContentType.DefaultTemplate == null ? "" : content.ContentType.DefaultTemplate.Alias)
: display.TemplateAlias,
View = "dropdown", //TODO: Hard coding until we make a real dropdown property editor to lookup
Config = new Dictionary<string, object>
{
@@ -6,7 +6,6 @@ using Examine;
using Examine.LuceneEngine.Providers;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Models.Membership;
using Umbraco.Web.Models.ContentEditing;
@@ -21,7 +20,7 @@ namespace Umbraco.Web.Models.Mapping
config.CreateMap<UmbracoEntity, EntityBasic>()
.ForMember(x => x.Udi, expression => expression.MapFrom(x => Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(x.NodeObjectTypeId), x.Key)))
.ForMember(basic => basic.Icon, expression => expression.MapFrom(entity => entity.ContentTypeIcon))
.ForMember(dto => dto.Trashed, expression => expression.Ignore())
.ForMember(dto => dto.Trashed, expression => expression.MapFrom(x => x.Trashed))
.ForMember(x => x.Alias, expression => expression.Ignore())
.AfterMap((entity, basic) =>
{
@@ -98,8 +97,8 @@ namespace Umbraco.Web.Models.Mapping
else if (entity.NodeObjectTypeId == Constants.ObjectTypes.TemplateTypeGuid)
basic.Icon = "icon-newspaper-alt";
}
});
});
config.CreateMap<SearchResult, SearchResultItem>()
//default to document icon
.ForMember(x => x.Score, expression => expression.MapFrom(result => result.Score))
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
@@ -11,7 +14,7 @@ namespace Umbraco.Web.PropertyEditors
{
internal class ColorListPreValueEditor : ValueListPreValueEditor
{
public ColorListPreValueEditor()
{
var field = Fields.First();
@@ -23,14 +26,98 @@ namespace Umbraco.Web.PropertyEditors
//change the label
field.Name = "Add color";
//need to have some custom validation happening here
field.Validators.Add(new ColorListValidator());
field.Validators.Add(new ColorListValidator());
Fields.Insert(0, new PreValueField
{
Name = "Include labels?",
View = "boolean",
Key = "useLabel",
Description = "Stores colors as a Json object containing both the color hex string and label, rather than just the hex string."
});
}
public override IDictionary<string, object> ConvertDbToEditor(IDictionary<string, object> defaultPreVals, PreValueCollection persistedPreVals)
{
var dictionary = persistedPreVals.FormatAsDictionary();
var arrayOfVals = dictionary.Select(item => item.Value).ToList();
return new Dictionary<string, object> { { "items", arrayOfVals.ToDictionary(x => x.Id, x => x.Value) } };
var items = dictionary
.Where(x => x.Key != "useLabel")
.ToDictionary(x => x.Value.Id, x => x.Value.Value);
var items2 = new Dictionary<int, object>();
foreach (var item in items)
{
if (item.Value.DetectIsJson() == false)
{
items2[item.Key] = item.Value;
continue;
}
try
{
items2[item.Key] = JsonConvert.DeserializeObject(item.Value);
}
catch
{
// let's say parsing Json failed, so what we have is the string - build json
items2[item.Key] = new JObject { { "color", item.Value }, { "label", item.Value } };
}
}
var result = new Dictionary<string, object> { { "items", items2 } };
var useLabel = dictionary.ContainsKey("useLabel") && dictionary["useLabel"].Value == "1";
if (useLabel)
result["useLabel"] = dictionary["useLabel"].Value;
return result;
}
public override IDictionary<string, PreValue> ConvertEditorToDb(IDictionary<string, object> editorValue, PreValueCollection currentValue)
{
var val = editorValue["items"] as JArray;
var result = new Dictionary<string, PreValue>();
if (val == null) return result;
try
{
object useLabelObj;
var useLabel = false;
if (editorValue.TryGetValue("useLabel", out useLabelObj))
{
useLabel = useLabelObj is string && (string) useLabelObj == "1";
result["useLabel"] = new PreValue(useLabel ? "1" : "0");
}
// get all non-empty values
var index = 0;
foreach (var preValue in val.OfType<JObject>()
.Where(x => x["value"] != null)
.Select(x =>
{
var idString = x["id"] == null ? "0" : x["id"].ToString();
int id;
if (int.TryParse(idString, out id) == false) id = 0;
var color = x["value"].ToString();
if (string.IsNullOrWhiteSpace(color)) return null;
var label = x["label"].ToString();
return new PreValue(id, useLabel
? JsonConvert.SerializeObject(new { value = color, label = label })
: color);
})
.WhereNotNull())
{
result.Add(index.ToInvariantString(), preValue);
index++;
}
}
catch (Exception ex)
{
LogHelper.Error<ValueListPreValueEditor>("Could not deserialize the posted value: " + val, ex);
}
return result;
}
internal class ColorListValidator : IPropertyValidator
@@ -39,7 +126,7 @@ namespace Umbraco.Web.PropertyEditors
{
var json = value as JArray;
if (json == null) yield break;
//validate each item which is a json object
for (var index = 0; index < json.Count; index++)
{
+8 -4
View File
@@ -126,8 +126,10 @@ namespace Umbraco.Web.UI
{
var task = GetTaskForOperation(httpContext, umbracoUser, Operation.Create, nodeType);
if (task == null)
throw new InvalidOperationException(
string.Format("Could not task for operation {0} for node type {1}", Operation.Create, nodeType));
{
//if no task was found it will use the default task and we cannot validate the application assigned so return true
return true;
}
var dialogTask = task as LegacyDialogTask;
if (dialogTask != null)
@@ -154,8 +156,10 @@ namespace Umbraco.Web.UI
{
var task = GetTaskForOperation(httpContext, umbracoUser, Operation.Delete, nodeType);
if (task == null)
throw new InvalidOperationException(
string.Format("Could not task for operation {0} for node type {1}", Operation.Delete, nodeType));
{
//if no task was found it will use the default task and we cannot validate the application assigned so return true
return true;
}
var dialogTask = task as LegacyDialogTask;
if (dialogTask != null)
+1
View File
@@ -392,6 +392,7 @@
<Compile Include="Models\ContentEditing\MemberTypeSave.cs" />
<Compile Include="Models\ContentEditing\Permission.cs" />
<Compile Include="Models\ContentEditing\PostedFiles.cs" />
<Compile Include="Models\ContentEditing\PostedFolder.cs" />
<Compile Include="Models\ContentEditing\PropertyGroupBasic.cs" />
<Compile Include="Models\ContentEditing\PropertyTypeBasic.cs" />
<Compile Include="Models\ContentEditing\SimpleNotificationModel.cs" />
+18 -5
View File
@@ -46,12 +46,11 @@ namespace Umbraco.Web
/// <param name="app"></param>
protected virtual void ConfigureMiddleware(IAppBuilder app)
{
//Ensure owin is configured for Umbraco back office authentication. If you have any front-end OWIN
// cookie configuration, this must be declared after it.
// Configure OWIN for authentication.
ConfigureUmbracoAuthentication(app);
app
.UseUmbracoBackOfficeCookieAuthentication(ApplicationContext, PipelineStage.Authenticate)
.UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext, PipelineStage.Authenticate)
.UseUmbracoPreviewAuthentication(ApplicationContext, PipelineStage.Authorize)
.UseSignalR()
.FinalizeMiddlewareConfiguration();
}
@@ -68,6 +67,20 @@ namespace Umbraco.Web
Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider());
}
/// <summary>
/// Configure external/OAuth login providers
/// </summary>
/// <param name="app"></param>
protected virtual void ConfigureUmbracoAuthentication(IAppBuilder app)
{
// Ensure owin is configured for Umbraco back office authentication.
// Front-end OWIN cookie configuration must be declared after this code.
app
.UseUmbracoBackOfficeCookieAuthentication(ApplicationContext, PipelineStage.Authenticate)
.UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext, PipelineStage.Authenticate)
.UseUmbracoPreviewAuthentication(ApplicationContext, PipelineStage.Authorize);
}
/// <summary>
/// Raised when the middleware has been configured
/// </summary>
@@ -162,6 +162,13 @@ namespace umbraco.presentation.developer.packages
/*Data types */
cms.businesslogic.datatype.DataTypeDefinition[] umbDataType = cms.businesslogic.datatype.DataTypeDefinition.GetAll();
// sort array by name
Array.Sort(umbDataType, delegate(cms.businesslogic.datatype.DataTypeDefinition umbDataType1, cms.businesslogic.datatype.DataTypeDefinition umbDataType2)
{
return umbDataType1.Text.CompareTo(umbDataType2.Text);
});
foreach (cms.businesslogic.datatype.DataTypeDefinition umbDtd in umbDataType)
{
@@ -70,10 +70,11 @@ namespace umbraco.presentation.umbraco.dialogs
private void import_Click(object sender, EventArgs e)
{
var xd = new XmlDocument();
xd.XmlResolver = null;
xd.Load(tempFile.Value);
var userId = base.getUser().Id;
var element = XElement.Parse(xd.InnerXml);
var importContentTypes = ApplicationContext.Current.Services.PackagingService.ImportContentTypes(element, userId);
var contentType = importContentTypes.FirstOrDefault();
@@ -104,7 +105,8 @@ namespace umbraco.presentation.umbraco.dialogs
documentTypeFile.PostedFile.SaveAs(fileName);
var xd = new XmlDocument();
xd.Load(fileName);
xd.XmlResolver = null;
xd.Load(fileName);
dtName.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Name").FirstChild.Value;
dtAlias.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Alias").FirstChild.Value;
@@ -27,7 +27,7 @@ namespace umbraco.dialogs
protected void Page_Load(object sender, EventArgs e)
{
Button1.Text = ui.Text("update");
pane_form.Text = ui.Text("notifications", "editNotifications", node.Text, base.getUser());
pane_form.Text = ui.Text("notifications", "editNotifications", Server.HtmlEncode(node.Text), base.getUser());
}
#region Web Form Designer generated code
@@ -773,14 +773,20 @@ namespace umbraco.cms.businesslogic.member
{
var temp = new Hashtable();
var groupIds = new List<int>();
using (var sqlHelper = Application.SqlHelper)
using (var dr = sqlHelper.ExecuteReader(
"select memberGroup from cmsMember2MemberGroup where member = @id",
sqlHelper.CreateParameter("@id", Id)))
{
while (dr.Read())
temp.Add(dr.GetInt("memberGroup"),
new MemberGroup(dr.GetInt("memberGroup")));
groupIds.Add(dr.GetInt("memberGroup"));
}
foreach (var groupId in groupIds)
{
temp.Add(groupId, new MemberGroup(groupId));
}
_groups = temp;
}