Compare commits

...

75 Commits

Author SHA1 Message Date
mikkelhm bfd201efcc Bump version to 7.6-Beta5 2017-03-23 15:46:18 +01:00
Shannon 2ae236e98d Adds xml transform for partial view tree types 2017-03-23 18:48:41 +11:00
Shannon 802fb95784 Fixes issue with renaming PartialViewMacro and PartialViewTree controller names - this also fixes an issue that we were looking up these types over and over again and now we just do it once. 2017-03-23 18:44:28 +11:00
Shannon 0297fdb32f Fixes a bunch of the JS unit tests 2017-03-23 13:19:37 +11:00
Shannon Deminick 5bede4bf9f Merge pull request #1816 from umbraco/temp-u4-9664
u4-9664 - Enables HasAccess on the MediaTreeController to use a udi for checkin…
2017-03-23 12:54:11 +11:00
Shannon d37106576d Adds null checks to the ContentTreeController too 2017-03-23 12:53:48 +11:00
Shannon ed0f0f7942 Merge remote-tracking branch 'origin/dev-v7' into dev-v7.6
# Conflicts:
#	src/Umbraco.Core/Configuration/CoreDebug.cs
#	src/Umbraco.Core/Publishing/PublishingStrategy.cs
#	src/Umbraco.Core/Umbraco.Core.csproj
2017-03-23 12:24:16 +11:00
Shannon Deminick 3d81c73495 Merge pull request #1814 from umbraco/temp-u4-9658
U4-9658 - detect umbraco.config rogue content types, and fix
2017-03-23 12:16:10 +11:00
Shannon e7a540e717 Proper Exception handling 2017-03-23 12:15:14 +11:00
Shannon Deminick bb886c237e Merge pull request #1815 from umbraco/temp-u4-9610
U4-9610 - fix nullref in BulkPublishController
2017-03-23 11:28:03 +11:00
Shannon 6c1e271fa8 Fixes styling and fixing error message quotes to make it more clear 2017-03-23 11:27:46 +11:00
Shannon Deminick 4c1b807e4f Merge pull request #1812 from umbraco/temp-u4-6811
U4-6811 - fix preview set deletion
2017-03-23 11:06:33 +11:00
Shannon 3b22c0cdd7 Catches correct expected exception type, we'll log for other non expected errors. 2017-03-23 11:06:06 +11:00
mikkelhm 31d8f49d3b Enables HasAccess on the MediaTreeController to use a udi for checking access 2017-03-22 20:25:15 +01:00
Stephan 631404743f U4-9610 - fix nullref in BulkPublishController 2017-03-22 14:44:39 +01:00
Emil Wangaa 1e154e8bc9 Internal Beta04 version bump - needed for Forms 2017-03-22 14:32:35 +01:00
Stephan d5c9639170 U4-9658 - detect umbraco.config rogue content types, and fix 2017-03-22 13:58:40 +01:00
Stephan 94b7c36d62 U4-6811 - fix preview set deletion 2017-03-22 12:16:33 +01:00
Stephan 1ffdd1abd1 Merge pull request #1811 from umbraco/temp-U4-9660
U4-9660 EntityService is not using readonly uow where it could be
2017-03-22 11:20:29 +01:00
Claus a06de1b165 U4-9660 EntityService is not using readonly uow where it could be 2017-03-22 11:14:16 +01:00
Shannon 0fd3af6828 Fixes release version of tinymceconfig 2017-03-22 18:32:28 +11:00
Shannon fafb207799 U4-8658 RTE Editor: Edit an existing image by clicking 'media picker' in toolbar, show a blank picker 2017-03-22 18:27:18 +11:00
Shannon b1aea424e8 U4-9659 Javascript error when inserting an image into the rich text editor from the media picker 2017-03-22 18:26:11 +11:00
Shannon Deminick 3598acaddc Merge pull request #1805 from umbraco/temp-u4-9644
u4-9644 fix local link selection in the rich text editor
2017-03-22 18:25:04 +11:00
Shannon 8e03a1ffca Updates tha a and img tags in the rte to use a data-udi attribute when udi's are used 2017-03-22 18:17:59 +11:00
Shannon Deminick ded8afa8fc Merge pull request #1808 from umbraco/temp-u4-9582
U4-9582
2017-03-22 13:56:54 +11:00
Mads Rasmussen dcd2b1eb6b Merge pull request #1807 from crgrieve/U4-9648
U4-9648 Remove the relevant query rather than the first one in list.
2017-03-21 19:59:57 +01:00
Stephan c04ef47be3 Merge pull request #1806 from umbraco/temp-u4-9640
U4-9640
2017-03-21 17:48:07 +01:00
Stephan 62f3c87e8a Merge remote-tracking branch 'origin/dev-v7.6' into temp-u4-9640 2017-03-21 17:12:29 +01:00
Stephan 1b9776ecfb Merge pull request #1800 from umbraco/temp-U4-9591
U4-9591 All PetaPoco Relators for 1:M mappings need to be reviewed to…
2017-03-21 16:52:12 +01:00
Warren Buckley 7b1efa718e Internal Beta03 version bump - needed for Forms for access to [FileUploadCleanupFilter] 2017-03-21 15:50:13 +00:00
Warren Buckley f60ad15f70 Marks Forms access to internals in Umbraco.Web assembly - needed for [FileUploadCleanupFilter] in Forms 2017-03-21 15:42:30 +00:00
mikkelhm 3f3db006e7 When selecting a local link, also update the data-id to be the udi 2017-03-21 14:43:54 +01:00
Stephan 7801cdf9ff Merge remote-tracking branch 'origin/dev-v7.6' into temp-u4-9640 2017-03-21 13:14:18 +01:00
mikkelhm 836e1bba54 Revert "moves binding redirects to app pre init"
This reverts commit 5a953761b0.
2017-03-21 13:14:07 +01:00
mikkelhm de9c5b88ab Updates the legacy linkpicker to show udi links properly, i.e. translate them to nice internal urls 2017-03-21 11:48:50 +01:00
Stephan 77981f889f U4-9582 - perfs, cache converter prevalues 2017-03-21 11:10:52 +01:00
Stephan 0a132ed5ed U4-9582 - perfs, remove Yield method 2017-03-21 11:10:37 +01:00
Shannon 5a953761b0 moves binding redirects to app pre init 2017-03-21 20:41:07 +11:00
Shannon 27ee192c8f Fixes all the logic in the CodeFileController, propery null checking, proper handling of persisting everything including renames, removes code duplication 2017-03-21 18:39:45 +11:00
Shannon dfa58183c6 Fixes deleting partial view and script files 2017-03-21 18:38:54 +11:00
Shannon e8668f3ed4 Don't pass in a value for snippets if it's not there, it will show up as a "undefined" string otherwise, adds the redirect method to the contentEditingHelper 2017-03-21 18:38:32 +11:00
Shannon c84e3dac38 Adds notes, return false if the file is not actually deleted, adds test assertions 2017-03-21 18:37:46 +11:00
Shannon 72ab51ea60 Fixes angular file controllers to not sync the tree if an item is being created, redirects to the correct path if the file is renamed 2017-03-21 18:36:14 +11:00
Shannon 1bfb63fe86 Adds validation for file names 2017-03-21 18:35:13 +11:00
Shannon 2cf68ab263 dont' render files that are invalid (no name) 2017-03-21 18:30:39 +11:00
Shannon 215bc64a83 Fixes val-server directive since it wasn't picking up on ngModel changes 2017-03-21 18:20:22 +11:00
Shannon Deminick 1b17f4e9df Merge pull request #1804 from umbraco/temp-u4-9543
U4-9543 - fix shadow filesystem filter
2017-03-21 12:59:43 +11:00
Shannon 73f0b1992d Fixes up sorting when using relators 2017-03-21 12:37:35 +11:00
carole_grieve_eqtr 21f54dd4ff Remove the relevant query rather than the first one! 2017-03-20 17:55:48 +00:00
Stephan a5903ec419 U4-9640 - fix CodeFileController for scripts 2017-03-20 16:53:08 +01:00
Warren Buckley ac496b0cf6 Updates version to beta02 - after Warren forgot to merge & push AccessToInternals for Forms before public beta 2017-03-20 15:40:15 +00:00
Warren Buckley 6036136938 Merge branch 'dev-v7.6' of https://github.com/umbraco/Umbraco-CMS into dev-v7.6
# Conflicts:
#	build/UmbracoVersion.txt
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/UmbracoVersion.cs
2017-03-20 14:55:29 +00:00
Stephan a9c7a7a405 U4-9640 - use IFileSystem 2017-03-20 15:53:34 +01:00
mikkelhm ce8286dadd Sets the udi as well as the id when selecting a local link in the rich text editor 2017-03-20 15:53:32 +01:00
Claus 532b177d2e Merge pull request #1793 from Phosworks/U4-8418
Fixes jquery selector bug, http://issues.umbraco.org/issue/U4-8418
2017-03-20 14:43:43 +01:00
mikkelhm 504aac0350 Fixes variable names when selecting a local link in the rich text editor 2017-03-20 14:32:43 +01:00
Stephan 454aad2170 U4-9543 - fix shadow filesystem filter 2017-03-20 13:01:03 +01:00
Shannon fd0cb7da23 Merge remote-tracking branch 'refs/remotes/origin/dev-v7.6' into temp-U4-9591 2017-03-20 18:28:38 +11:00
Shannon 3d6f91cbef Merge branch 'dev-v7' of https://github.com/umbraco/Umbraco-CMS into dev-v7 2017-03-18 15:47:56 +11:00
Shannon fffd508bb2 missing null check 2017-03-18 15:44:57 +11:00
Sebastiaan Janssen f4eb37d44d Missing null check (for unit test) 2017-03-17 12:11:28 +01:00
Sebastiaan Janssen c79aaa9c48 Bumps version 2017-03-17 11:49:42 +01:00
Warren Buckley 6aa00c52dc Merge branch 'dev-v7.6' of https://github.com/umbraco/Umbraco-CMS into dev-v7.6 2017-03-16 15:08:33 +00:00
Stephan 333b35d8b9 U4-9606 - hard cap of 8 minidump files 2017-03-16 15:51:19 +01:00
Claus 890673a197 Merge pull request #1797 from umbraco/temp-u4-9606
U4-9606 - New DumpOnTimeoutThreadAbort debug option
2017-03-16 15:38:01 +01:00
Claus 6344ad28aa adding sqlsyntax. 2017-03-16 14:52:50 +01:00
Claus eadc3c8c5a U4-9591 All PetaPoco Relators for 1:M mappings need to be reviewed to ensure there is a correct sort being used 2017-03-16 14:45:59 +01:00
Stephan 27539d4aad U4-9606 - always minidump on Monitor.ReliableEnter exceptions 2017-03-16 14:04:28 +01:00
Stephan f7f23015d0 U4-9606 - missing file 2017-03-15 09:39:35 +01:00
Stephan e432b7061d U4-9606 - New DumpOnTimeoutThreadAbort debug option 2017-03-14 19:02:33 +01:00
Warren Buckley 30f63eefb3 Merge branch 'dev-v7.6' of https://github.com/umbraco/Umbraco-CMS into dev-v7.6 2017-03-14 10:18:00 +00:00
Warren Buckley 6114193fd5 Bump version to alpha 75 2017-03-14 10:17:43 +00:00
Warren Buckley 82420605ef Allow Forms Assemblies to access internals in Umbraco.Core - Required to use AppContext.ScopeProvider for Scope Events for Deploy 2017-03-14 10:01:23 +00:00
Wincent fc92d40b3a Fixes jquery selector bug, http://issues.umbraco.org/issue/U4-8418 2017-03-13 11:25:02 +01:00
74 changed files with 1199 additions and 735 deletions
+17 -7
View File
@@ -34,10 +34,15 @@
xdt:Transform="SetAttributes()" />
<add application="settings" alias="templates" title="Templates" type="Umbraco.Web.Trees.TemplatesTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="1"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="partialViews" title="Partial Views" silent="false" initialize="true" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.PartialViewsTree, umbraco" sortOrder="2"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
xdt:Transform="SetAttributes()" />
<add application="settings" alias="partialViews" type="Umbraco.Web.Trees.PartialViewsTree, umbraco"
xdt:Locator="Match(application,alias,type)"
xdt:Transform="Remove()" />
<add application="settings" alias="partialViews" title="Partial Views" silent="false" initialize="true" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.PartialViewsTreeController, umbraco" sortOrder="2"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="settings" alias="scripts" title="Scripts" type="umbraco.loadScripts, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
@@ -84,9 +89,14 @@
<add application="developer" alias="xslt" title="XSLT Files" type="umbraco.loadXslt, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="5"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, umbraco" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, umbraco"
xdt:Locator="Match(application,alias,type)"
xdt:Transform="Remove()" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTreeController, umbraco" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="developer" alias="python"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
+1 -1
View File
@@ -1,3 +1,3 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.6.0
alpha074
beta05
+1 -1
View File
@@ -12,4 +12,4 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.0")]
[assembly: AssemblyInformationalVersion("7.6.0-alpha074")]
[assembly: AssemblyInformationalVersion("7.6.0-beta05")]
@@ -18,10 +18,14 @@ namespace Umbraco.Core.Configuration
{
var appSettings = System.Configuration.ConfigurationManager.AppSettings;
LogUncompletedScopes = string.Equals("true", appSettings["Umbraco.CoreDebug.LogUncompletedScopes"], StringComparison.OrdinalIgnoreCase);
DumpOnTimeoutThreadAbort = string.Equals("true", appSettings["Umbraco.CoreDebug.DumpOnTimeoutThreadAbort"], StringComparison.OrdinalIgnoreCase);
}
// when true, Scope logs the stack trace for any scope that gets disposed without being completed.
// this helps troubleshooting rogue scopes that we forget to complete
public bool LogUncompletedScopes { get; private set; }
// when true, the Logger creates a minidump of w3wp in ~/App_Data/MiniDump whenever it logs
// an error due to a ThreadAbortException that is due to a timeout.
public bool DumpOnTimeoutThreadAbort { get; private set; }
}
}
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration
/// Gets the version comment (like beta or RC).
/// </summary>
/// <value>The version comment.</value>
public static string CurrentComment { get { return "alpha074"; } }
public static string CurrentComment { get { return "beta05"; } }
// Get the version of the umbraco.dll by looking at a class in that dll
// Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx
+135
View File
@@ -0,0 +1,135 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Umbraco.Core.IO;
namespace Umbraco.Core.Diagnostics
{
// taken from https://blogs.msdn.microsoft.com/dondu/2010/10/24/writing-minidumps-in-c/
// and https://blogs.msdn.microsoft.com/dondu/2010/10/31/writing-minidumps-from-exceptions-in-c/
// which itself got it from http://blog.kalmbach-software.de/2008/12/13/writing-minidumps-in-c/
internal static class MiniDump
{
private static readonly object LockO = new object();
[Flags]
public enum Option : uint
{
// From dbghelp.h:
Normal = 0x00000000,
WithDataSegs = 0x00000001,
WithFullMemory = 0x00000002,
WithHandleData = 0x00000004,
FilterMemory = 0x00000008,
ScanMemory = 0x00000010,
WithUnloadedModules = 0x00000020,
WithIndirectlyReferencedMemory = 0x00000040,
FilterModulePaths = 0x00000080,
WithProcessThreadData = 0x00000100,
WithPrivateReadWriteMemory = 0x00000200,
WithoutOptionalData = 0x00000400,
WithFullMemoryInfo = 0x00000800,
WithThreadInfo = 0x00001000,
WithCodeSegs = 0x00002000,
WithoutAuxiliaryState = 0x00004000,
WithFullAuxiliaryState = 0x00008000,
WithPrivateWriteCopyMemory = 0x00010000,
IgnoreInaccessibleMemory = 0x00020000,
ValidTypeFlags = 0x0003ffff,
}
//typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
// DWORD ThreadId;
// PEXCEPTION_POINTERS ExceptionPointers;
// BOOL ClientPointers;
//} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
[StructLayout(LayoutKind.Sequential, Pack = 4)] // Pack=4 is important! So it works also for x64!
public struct MiniDumpExceptionInformation
{
public uint ThreadId;
public IntPtr ExceptionPointers;
[MarshalAs(UnmanagedType.Bool)]
public bool ClientPointers;
}
//BOOL
//WINAPI
//MiniDumpWriteDump(
// __in HANDLE hProcess,
// __in DWORD ProcessId,
// __in HANDLE hFile,
// __in MINIDUMP_TYPE DumpType,
// __in_opt PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
// __in_opt PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
// __in_opt PMINIDUMP_CALLBACK_INFORMATION CallbackParam
// );
// Overload requiring MiniDumpExceptionInformation
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
private static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, ref MiniDumpExceptionInformation expParam, IntPtr userStreamParam, IntPtr callbackParam);
// Overload supporting MiniDumpExceptionInformation == NULL
[DllImport("dbghelp.dll", EntryPoint = "MiniDumpWriteDump", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
private static extern bool MiniDumpWriteDump(IntPtr hProcess, uint processId, SafeHandle hFile, uint dumpType, IntPtr expParam, IntPtr userStreamParam, IntPtr callbackParam);
[DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
private static extern uint GetCurrentThreadId();
private static bool Write(SafeHandle fileHandle, Option options, bool withException = false)
{
var currentProcess = Process.GetCurrentProcess();
var currentProcessHandle = currentProcess.Handle;
var currentProcessId = (uint)currentProcess.Id;
MiniDumpExceptionInformation exp;
exp.ThreadId = GetCurrentThreadId();
exp.ClientPointers = false;
exp.ExceptionPointers = IntPtr.Zero;
if (withException)
exp.ExceptionPointers = Marshal.GetExceptionPointers();
var bRet = exp.ExceptionPointers == IntPtr.Zero
? MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)
: MiniDumpWriteDump(currentProcessHandle, currentProcessId, fileHandle, (uint) options, ref exp, IntPtr.Zero, IntPtr.Zero);
return bRet;
}
public static bool Dump(Option options = Option.WithFullMemory, bool withException = false)
{
lock (LockO)
{
// work around "stack trace is not available while minidump debugging",
// by making sure a local var (that we can inspect) contains the stack trace.
// getting the call stack before it is unwound would require a special exception
// filter everywhere in our code = not!
var stacktrace = withException ? Environment.StackTrace : string.Empty;
var filepath = IOHelper.MapPath("~/App_Data/MiniDump");
if (Directory.Exists(filepath) == false)
Directory.CreateDirectory(filepath);
var filename = Path.Combine(filepath, string.Format("{0:yyyyMMddTHHmmss}.{1}.dmp", DateTime.UtcNow, Guid.NewGuid().ToString("N").Substring(0, 4)));
using (var stream = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite, FileShare.Write))
{
return Write(stream.SafeFileHandle, options, withException);
}
}
}
public static bool OkToDump()
{
lock (LockO)
{
var filepath = IOHelper.MapPath("~/App_Data/MiniDump");
if (Directory.Exists(filepath) == false) return true;
var count = Directory.GetFiles(filepath, "*.dmp").Length;
return count < 8;
}
}
}
}
-17
View File
@@ -295,22 +295,5 @@ namespace Umbraco.Core
return list1Groups.Count == list2Groups.Count
&& list1Groups.All(g => g.Count() == list2Groups[g.Key].Count());
}
///<summary>
/// Returns the items of the given enumerable as a pure enumerable.
/// <remarks>
/// When quering lists using methods such as <see cref="M:List.Where"/>, the result, despite appearing to look like and quack like an
/// <see cref="T:Enumerable{T}"/> the type is actually an instance of <see cref="T:System.Linq.Enumerable.WhereEnumerableIterator"/>
/// </remarks>
/// </summary>
///<param name="source">The item to find.</param>
///<returns>The index of the first matching item, or -1 if the item was not found.</returns>
internal static IEnumerable<T> Yield<T>(this IEnumerable<T> source)
{
foreach (var element in source)
{
yield return element;
}
}
}
}
+59 -26
View File
@@ -218,11 +218,11 @@ namespace Umbraco.Core.IO
var normPath = NormPath(path);
var shadows = Nodes.Where(kvp => IsChild(normPath, kvp.Key)).ToArray();
var files = filter != null ? _fs.GetFiles(path, filter) : _fs.GetFiles(path);
var regexFilter = FilterToRegex(filter);
var wildcard = filter == null ? null : new WildcardExpression(filter);
return files
.Except(shadows.Where(kvp => (kvp.Value.IsFile && kvp.Value.IsDelete) || kvp.Value.IsDir)
.Select(kvp => kvp.Key))
.Union(shadows.Where(kvp => kvp.Value.IsFile && kvp.Value.IsExist && FilterByRegex(kvp.Key, regexFilter)).Select(kvp => kvp.Key))
.Union(shadows.Where(kvp => kvp.Value.IsFile && kvp.Value.IsExist && (wildcard == null || wildcard.IsMatch(kvp.Key))).Select(kvp => kvp.Key))
.Distinct();
}
@@ -326,32 +326,65 @@ namespace Umbraco.Core.IO
_sfs.AddFile(path, physicalPath, overrideIfExists, copy);
Nodes[normPath] = new ShadowNode(false, false);
}
/// <summary>
/// Helper function for filtering keys by Regex if a filter is specified.
/// </summary>
/// <param name="input"></param>
/// <param name="regexFilter"></param>
/// <returns></returns>
internal static bool FilterByRegex(string input, string regexFilter)
{
if (regexFilter == null) return true;
return regexFilter != string.Empty && Regex.IsMatch(input, regexFilter);
}
/// <summary>
/// Transforms a filter pattern into a Regex pattern
/// </summary>
/// <param name="pattern"></param>
/// <returns></returns>
/// <remarks>
/// Appending '$' only if not containing wildcard is stupid and broken.
/// It is however what seems to be what they're doing in .NET so we need to match the functionality.
/// </remarks>
internal static string FilterToRegex(string pattern)
// copied from System.Web.Util.Wildcard internal
internal class WildcardExpression
{
if (pattern == null) return null;
return "^" + Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + (pattern.Contains("*") ? string.Empty : "$");
private readonly string _pattern;
private readonly bool _caseInsensitive;
private Regex _regex;
private static Regex metaRegex = new Regex("[\\+\\{\\\\\\[\\|\\(\\)\\.\\^\\$]");
private static Regex questRegex = new Regex("\\?");
private static Regex starRegex = new Regex("\\*");
private static Regex commaRegex = new Regex(",");
private static Regex slashRegex = new Regex("(?=/)");
private static Regex backslashRegex = new Regex("(?=[\\\\:])");
public WildcardExpression(string pattern, bool caseInsensitive = true)
{
_pattern = pattern;
_caseInsensitive = caseInsensitive;
}
private void EnsureRegex(string pattern)
{
if (_regex != null) return;
var options = RegexOptions.None;
// match right-to-left (for speed) if the pattern starts with a *
if (pattern.Length > 0 && pattern[0] == '*')
options = RegexOptions.RightToLeft | RegexOptions.Singleline;
else
options = RegexOptions.Singleline;
// case insensitivity
if (_caseInsensitive)
options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
// Remove regex metacharacters
pattern = metaRegex.Replace(pattern, "\\$0");
// Replace wildcard metacharacters with regex codes
pattern = questRegex.Replace(pattern, ".");
pattern = starRegex.Replace(pattern, ".*");
pattern = commaRegex.Replace(pattern, "\\z|\\A");
// anchor the pattern at beginning and end, and return the regex
_regex = new Regex("\\A" + pattern + "\\z", options);
}
public bool IsMatch(string input)
{
EnsureRegex(_pattern);
return _regex.IsMatch(input);
}
}
}
}
+39 -5
View File
@@ -7,6 +7,8 @@ using System.Threading;
using System.Web;
using log4net;
using log4net.Config;
using Umbraco.Core.Configuration;
using Umbraco.Core.Diagnostics;
namespace Umbraco.Core.Logging
{
@@ -57,23 +59,55 @@ namespace Umbraco.Core.Logging
internal ILog LoggerFor(object getTypeFromInstance)
{
if (getTypeFromInstance == null) throw new ArgumentNullException("getTypeFromInstance");
return LogManager.GetLogger(getTypeFromInstance.GetType());
}
public void Error(Type callingType, string message, Exception exception)
{
var logger = LogManager.GetLogger(callingType);
if (logger == null) return;
var dump = false;
if (IsTimeoutThreadAbortException(exception))
{
message += "\r\nThe thread has been aborted, because the request has timed out.";
// dump if configured, or if stacktrace contains Monitor.ReliableEnter
dump = UmbracoConfig.For.CoreDebug().DumpOnTimeoutThreadAbort || IsMonitorEnterThreadAbortException(exception);
// dump if it is ok to dump (might have a cap on number of dump...)
dump &= MiniDump.OkToDump();
}
logger.Error(message, exception);
if (dump)
{
try
{
var dumped = MiniDump.Dump(withException: true);
message += dumped
? "\r\nA minidump was created in App_Data/MiniDump"
: "\r\nFailed to create a minidump";
}
catch (Exception e)
{
message += string.Format("\r\nFailed to create a minidump ({0}: {1})", e.GetType().FullName, e.Message);
}
}
logger.Error(message, exception);
}
private static bool IsMonitorEnterThreadAbortException(Exception exception)
{
var abort = exception as ThreadAbortException;
if (abort == null) return false;
var stacktrace = abort.StackTrace;
return stacktrace.Contains("System.Threading.Monitor.ReliableEnter");
}
private static bool IsTimeoutThreadAbortException(Exception exception)
{
var abort = exception as ThreadAbortException;
@@ -105,7 +139,7 @@ namespace Umbraco.Core.Logging
if (showHttpTrace && HttpContext.Current != null)
{
HttpContext.Current.Trace.Warn(callingType.Name, string.Format(message, formatItems.Select(x => x.Invoke()).ToArray()));
}
}
var logger = LogManager.GetLogger(callingType);
if (logger == null || logger.IsWarnEnabled == false) return;
@@ -122,7 +156,7 @@ namespace Umbraco.Core.Logging
var logger = LogManager.GetLogger(callingType);
if (logger == null || logger.IsWarnEnabled == false) return;
var executedParams = formatItems.Select(x => x.Invoke()).ToArray();
logger.WarnFormat((message) + ". Exception: " + e, executedParams);
logger.WarnFormat((message) + ". Exception: " + e, executedParams);
}
/// <summary>
+54 -4
View File
@@ -1,5 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Models
{
@@ -84,16 +86,64 @@ namespace Umbraco.Core.Models
public string Type { get; set; }
private Type _runtimeType;
/// <summary>
/// Returns the CLR type based on it's assembly name stored in the config
/// </summary>
/// <returns></returns>
public Type GetRuntimeType()
{
return _runtimeType ?? (_runtimeType = System.Type.GetType(Type));
}
if (_runtimeType != null)
return _runtimeType;
_runtimeType = TryGetType(Type);
return _runtimeType;
}
/// <summary>
/// Used to try to get and cache the tree type
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
internal static Type TryGetType(string type)
{
try
{
return ResolvedTypes.GetOrAdd(type, s =>
{
var result = System.Type.GetType(type);
if (result != null)
{
return result;
}
//we need to implement a bit of a hack here due to some trees being renamed and backwards compat
var parts = type.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 2)
{
if (parts[1].Trim() == "umbraco" && parts[0].StartsWith("Umbraco.Web.Trees") && parts[0].EndsWith("Controller") == false)
{
//if it's one of our controllers but it's not suffixed with "Controller" then add it and try again
var tempType = parts[0] + "Controller, umbraco";
result = System.Type.GetType(tempType);
if (result != null)
{
return result;
}
}
}
throw new InvalidOperationException("Could not resolve type");
});
}
catch (InvalidOperationException)
{
//swallow, this is our own exception, couldn't find the type
return null;
}
}
private static readonly ConcurrentDictionary<string, Type> ResolvedTypes = new ConcurrentDictionary<string, Type>();
}
}
+4 -1
View File
@@ -49,7 +49,10 @@ namespace Umbraco.Core.Models
return path
.Replace('\\', System.IO.Path.DirectorySeparatorChar)
.Replace('/', System.IO.Path.DirectorySeparatorChar);
//.TrimStart(System.IO.Path.DirectorySeparatorChar);
//Don't strip the start - this was a bug fixed in 7.3, see ScriptRepositoryTests.PathTests
//.TrimStart(System.IO.Path.DirectorySeparatorChar)
//.TrimStart('/');
}
/// <summary>
@@ -179,7 +179,7 @@ namespace Umbraco.Core.Models.PublishedContent
}
if (contentType == null)
throw new Exception(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".",
throw new InvalidOperationException(string.Format("ContentTypeService failed to find a {0} type with alias \"{1}\".",
itemType.ToString().ToLower(), alias));
return new PublishedContentType(contentType);
@@ -101,7 +101,8 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<PropertyType>(sqlClause, query);
var sql = translator.Translate()
.OrderBy<PropertyTypeDto>(x => x.PropertyTypeGroupId, SqlSyntax);
//must be sorted this way for the relator to work
.OrderBy<PropertyTypeGroupDto>(x => x.Id, SqlSyntax);
var dtos = Database.Fetch<PropertyTypeGroupDto, PropertyTypeDto, DataTypeDto, PropertyTypeGroupDto>(new GroupPropertyTypeRelator().Map, sql);
@@ -468,7 +469,8 @@ AND umbracoNode.id <> @id",
.LeftJoin<DataTypeDto>()
.On<PropertyTypeDto, DataTypeDto>(left => left.DataTypeId, right => right.DataTypeId)
.Where<PropertyTypeGroupDto>(x => x.ContentTypeNodeId == id)
.OrderBy<PropertyTypeGroupDto>(x => x.Id);
//must be sorted this way for the relator to work
.OrderBy<PropertyTypeGroupDto>(x => x.Id, SqlSyntax);
var dtos = Database.Fetch<PropertyTypeGroupDto, PropertyTypeDto, DataTypeDto, PropertyTypeGroupDto>(new GroupPropertyTypeRelator().Map, sql);
@@ -41,6 +41,7 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false)
.Where(GetBaseWhereClause(), new { Id = id })
//must be sorted this way for the relator to work
.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
var dto = Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql).FirstOrDefault();
@@ -64,6 +65,9 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Where("cmsDictionary.pk in (@ids)", new { ids = ids });
}
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
.Select(dto => ConvertFromDto(dto));
}
@@ -73,6 +77,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sqlClause = GetBaseQuery(false);
var translator = new SqlTranslator<IDictionaryItem>(sqlClause, query);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
@@ -94,9 +99,9 @@ namespace Umbraco.Core.Persistence.Repositories
else
{
sql.Select("*")
.From<DictionaryDto>(SqlSyntax)
.LeftJoin<LanguageTextDto>(SqlSyntax)
.On<DictionaryDto, LanguageTextDto>(SqlSyntax, left => left.UniqueId, right => right.UniqueId);
.From<DictionaryDto>(SqlSyntax)
.LeftJoin<LanguageTextDto>(SqlSyntax)
.On<DictionaryDto, LanguageTextDto>(SqlSyntax, left => left.UniqueId, right => right.UniqueId);
}
return sql;
}
@@ -270,6 +275,8 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IDictionaryItem>(sqlClause, Query<IDictionaryItem>.Builder);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql)
@@ -297,6 +304,9 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<DictionaryDto> PerformFetch(Sql sql)
{
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql);
}
@@ -349,6 +359,9 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<DictionaryDto> PerformFetch(Sql sql)
{
//must be sorted this way for the relator to work
sql.OrderBy<DictionaryDto>(x => x.UniqueId, SqlSyntax);
return Database.Fetch<DictionaryDto, LanguageTextDto, DictionaryDto>(new DictionaryLanguageTextRelator().Map, sql);
}
@@ -37,6 +37,9 @@ namespace Umbraco.Core.Persistence.Repositories
private IMacro GetBySql(Sql sql)
{
//must be sorted this way for the relator to work
sql.OrderBy<MacroDto>(x => x.Id, SqlSyntax);
var macroDto = Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql).FirstOrDefault();
if (macroDto == null)
return null;
@@ -68,7 +71,10 @@ namespace Umbraco.Core.Persistence.Repositories
private IEnumerable<IMacro> GetAllNoIds()
{
var sql = GetBaseQuery(false);
var sql = GetBaseQuery(false)
//must be sorted this way for the relator to work
.OrderBy<MacroDto>(x => x.Id, SqlSyntax);
return ConvertFromDtos(Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql))
.ToArray();// we don't want to re-iterate again!
}
@@ -92,6 +98,9 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IMacro>(sqlClause, query);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<MacroDto>(x => x.Id, SqlSyntax);
var dtos = Database.Fetch<MacroDto, MacroPropertyDto, MacroDto>(new MacroPropertyRelator().Map, sql);
foreach (var dto in dtos)
@@ -114,13 +123,13 @@ namespace Umbraco.Core.Persistence.Repositories
return sql;
}
private static Sql GetBaseQuery()
private Sql GetBaseQuery()
{
var sql = new Sql();
sql.Select("*")
.From<MacroDto>()
.LeftJoin<MacroPropertyDto>()
.On<MacroDto, MacroPropertyDto>(left => left.Id, right => right.Macro);
.From<MacroDto>()
.LeftJoin<MacroPropertyDto>()
.On<MacroDto, MacroPropertyDto>(left => left.Id, right => right.Macro);
return sql;
}
@@ -46,7 +46,9 @@ namespace Umbraco.Core.Persistence.Repositories
var statement = string.Join(" OR ", ids.Select(x => string.Format("umbracoNode.id='{0}'", x)));
sql.Where(statement);
}
sql.OrderByDescending<NodeDto>(x => x.NodeId, SqlSyntax);
//must be sorted this way for the relator to work
sql.OrderBy<MemberTypeReadOnlyDto>(x => x.UniqueId, SqlSyntax);
var dtos =
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
@@ -62,7 +64,8 @@ namespace Umbraco.Core.Persistence.Repositories
var subquery = translator.Translate();
var sql = GetBaseQuery(false)
.Append(new Sql("WHERE umbracoNode.id IN (" + subquery.SQL + ")", subquery.Arguments))
.OrderBy<NodeDto>(x => x.SortOrder, SqlSyntax);
//must be sorted this way for the relator to work
.OrderBy<MemberTypeReadOnlyDto>(x => x.UniqueId, SqlSyntax);
var dtos =
Database.Fetch<MemberTypeReadOnlyDto, PropertyTypeReadOnlyDto, PropertyTypeGroupReadOnlyDto, MemberTypeReadOnlyDto>(
@@ -40,7 +40,10 @@ namespace Umbraco.Core.Persistence.Repositories
}
var factory = new PublicAccessEntryFactory();
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
sql.OrderBy<AccessDto>(dto => dto.Id, SqlSyntax);
var dtos = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql);
return dtos.Select(factory.BuildEntity);
}
@@ -52,7 +55,10 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = translator.Translate();
var factory = new PublicAccessEntryFactory();
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
sql.OrderBy<AccessDto>(dto => dto.Id, SqlSyntax);
var dtos = Database.Fetch<AccessDto, AccessRuleDto, AccessDto>(new AccessRulesRelator().Map, sql);
return dtos.Select(factory.BuildEntity);
}
@@ -63,9 +69,8 @@ namespace Umbraco.Core.Persistence.Repositories
sql.Select("*")
.From<AccessDto>(SqlSyntax)
.LeftJoin<AccessRuleDto>(SqlSyntax)
.On<AccessDto, AccessRuleDto>(SqlSyntax, left => left.Id, right => right.AccessId)
//MUST be ordered by this GUID ID for the AccessRulesRelator to work
.OrderBy<AccessDto>(dto => dto.Id, SqlSyntax);
.On<AccessDto, AccessRuleDto>(SqlSyntax, left => left.Id, right => right.AccessId);
return sql;
}
@@ -39,6 +39,8 @@ namespace Umbraco.Core.Persistence.Repositories
{
var sql = GetBaseQuery(false);
sql.Where(GetBaseWhereClause(), new { Id = id });
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
var dto = Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql).FirstOrDefault();
@@ -60,7 +62,10 @@ namespace Umbraco.Core.Persistence.Repositories
{
sql.Where("umbracoUser.id in (@ids)", new {ids = ids});
}
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
return ConvertFromDtos(Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql))
.ToArray(); // important so we don't iterate twice, if we don't do this we can end up with null values in cache if we were caching.
}
@@ -71,6 +76,9 @@ namespace Umbraco.Core.Persistence.Repositories
var translator = new SqlTranslator<IUser>(sqlClause, query);
var sql = translator.Translate();
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
var dtos = Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql)
.DistinctBy(x => x.Id);
@@ -96,13 +104,13 @@ namespace Umbraco.Core.Persistence.Repositories
return sql;
}
private static Sql GetBaseQuery(string columns)
private Sql GetBaseQuery(string columns)
{
var sql = new Sql();
sql.Select(columns)
.From<UserDto>()
.LeftJoin<User2AppDto>()
.On<UserDto, User2AppDto>(left => left.Id, right => right.UserId);
.From<UserDto>()
.LeftJoin<User2AppDto>()
.On<UserDto, User2AppDto>(left => left.Id, right => right.UserId);
return sql;
}
@@ -300,6 +308,8 @@ namespace Umbraco.Core.Persistence.Repositories
var innerSql = GetBaseQuery("umbracoUser.id");
innerSql.Where("umbracoUser2app.app = " + SqlSyntax.GetQuotedValue(sectionAlias));
sql.Where(string.Format("umbracoUser.id IN ({0})", innerSql.SQL));
//must be sorted this way for the relator to work
sql.OrderBy<UserDto>(x => x.Id, SqlSyntax);
return ConvertFromDtos(Database.Fetch<UserDto, User2AppDto, UserDto>(new UserSectionRelator().Map, sql));
}
@@ -47,5 +47,9 @@ using System.Security.Permissions;
[assembly: InternalsVisibleTo("Umbraco.Deploy.UI")]
[assembly: InternalsVisibleTo("Umbraco.Deploy.Cloud")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Core")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Core.Providers")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Web")]
//allow this to be mocked in our unit tests
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
@@ -88,13 +89,26 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
/// </returns>
private bool IsRangeDataType(int dataTypeId)
{
// ** This must be cached (U4-8862) **
var enableRange =
_dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId)
.PreValuesAsDictionary.FirstOrDefault(
x => string.Equals(x.Key, "enableRange", StringComparison.InvariantCultureIgnoreCase)).Value;
// GetPreValuesCollectionByDataTypeId is cached at repository level;
// still, the collection is deep-cloned so this is kinda expensive,
// better to cache here + trigger refresh in DataTypeCacheRefresher
return enableRange != null && enableRange.Value.TryConvertTo<bool>().Result;
return Storages.GetOrAdd(dataTypeId, id =>
{
var preValue = _dataTypeService.GetPreValuesCollectionByDataTypeId(id)
.PreValuesAsDictionary
.FirstOrDefault(x => string.Equals(x.Key, "enableRange", StringComparison.InvariantCultureIgnoreCase))
.Value;
return preValue != null && preValue.Value.TryConvertTo<bool>().Result;
});
}
private static readonly ConcurrentDictionary<int, bool> Storages = new ConcurrentDictionary<int, bool>();
internal static void ClearCaches()
{
Storages.Clear();
}
}
}
@@ -1,9 +1,12 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
@@ -74,18 +77,26 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
/// </returns>
private bool JsonStorageType(int dataTypeId)
{
// ** This must be cached (U4-8862) **
var storageType =
_dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId)
.PreValuesAsDictionary.FirstOrDefault(
x => string.Equals(x.Key, "storageType", StringComparison.InvariantCultureIgnoreCase)).Value;
// GetPreValuesCollectionByDataTypeId is cached at repository level;
// still, the collection is deep-cloned so this is kinda expensive,
// better to cache here + trigger refresh in DataTypeCacheRefresher
if (storageType != null && storageType.Value.InvariantEquals("Json"))
return Storages.GetOrAdd(dataTypeId, id =>
{
return true;
}
var preValue = _dataTypeService.GetPreValuesCollectionByDataTypeId(id)
.PreValuesAsDictionary
.FirstOrDefault(x => string.Equals(x.Key, "storageType", StringComparison.InvariantCultureIgnoreCase))
.Value;
return false;
return preValue != null && preValue.Value.InvariantEquals("json");
});
}
private static readonly ConcurrentDictionary<int, bool> Storages = new ConcurrentDictionary<int, bool>();
internal static void ClearCaches()
{
Storages.Clear();
}
}
}
+4 -3
View File
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
@@ -12,7 +13,8 @@ namespace Umbraco.Core.Publishing
{
public PublishStatus(IContent content, PublishStatusType statusType, EventMessages eventMessages)
: base(content, statusType, eventMessages)
{
{
InvalidProperties = Enumerable.Empty<Property>();
}
/// <summary>
@@ -20,8 +22,7 @@ namespace Umbraco.Core.Publishing
/// </summary>
public PublishStatus(IContent content, EventMessages eventMessages)
: this(content, PublishStatusType.Success, eventMessages)
{
}
{ }
public IContent ContentItem
{
@@ -51,7 +51,7 @@ namespace Umbraco.Core.Publishing
/// </summary>
/// <param name="uow"></param>
/// <param name="content"><see cref="IContent"/> to publish</param>
/// <param name="userId">Id of the User issueing the publish operation</param>
/// <param name="userId">Id of the User issueing the publish operation</param>
Attempt<PublishStatus> IPublishingStrategy2.Publish(IScopeUnitOfWork uow, IContent content, int userId)
{
var evtMsgs = _eventMessagesFactory.Get();
@@ -124,26 +124,26 @@ namespace Umbraco.Core.Publishing
/// By default this is set to true which means that it will publish any content item in the list that is completely unpublished and
/// not visible on the front-end. If set to false, this will only publish content that is live on the front-end but has new versions
/// that have yet to be published.
/// </param>
/// </param>
/// <returns></returns>
/// <remarks>
///
///
/// This method becomes complex once we start to be able to cancel events or stop publishing a content item in any way because if a
/// content item is not published then it's children shouldn't be published either. This rule will apply for the following conditions:
/// * If a document fails to be published, do not proceed to publish it's children if:
/// ** The document does not have a publish version
/// ** The document does have a published version but the includeUnpublishedDocuments = false
///
///
/// In order to do this, we will order the content by level and begin by publishing each item at that level, then proceed to the next
/// level and so on. If we detect that the above rule applies when the document publishing is cancelled we'll add it to the list of
/// level and so on. If we detect that the above rule applies when the document publishing is cancelled we'll add it to the list of
/// parentsIdsCancelled so that it's children don't get published.
///
///
/// Its important to note that all 'root' documents included in the list *will* be published regardless of the rules mentioned
/// above (unless it is invalid)!! By 'root' documents we are referring to documents in the list with the minimum value for their 'level'.
/// In most cases the 'root' documents will only be one document since under normal circumstance we only publish one document and
/// above (unless it is invalid)!! By 'root' documents we are referring to documents in the list with the minimum value for their 'level'.
/// In most cases the 'root' documents will only be one document since under normal circumstance we only publish one document and
/// its children. The reason we have to do this is because if a user is publishing a document and it's children, it is implied that
/// the user definitely wants to publish it even if it has never been published before.
///
///
/// </remarks>
IEnumerable<Attempt<PublishStatus>> IPublishingStrategy2.PublishWithChildren(IScopeUnitOfWork uow,
IEnumerable<IContent> content, int userId, bool includeUnpublishedDocuments)
@@ -157,7 +157,7 @@ namespace Umbraco.Core.Publishing
//group by levels and iterate over the sorted ascending level.
//TODO: This will cause all queries to execute, they will not be lazy but I'm not really sure being lazy actually made
// much difference because we iterate over them all anyways?? Morten?
// much difference because we iterate over them all anyways?? Morten?
// Because we're grouping I think this will execute all the queries anyways so need to fetch it all first.
var fetchedContent = content.ToArray();
@@ -174,7 +174,7 @@ namespace Umbraco.Core.Publishing
var levelGroups = fetchedContent.GroupBy(x => x.Level);
foreach (var level in levelGroups.OrderBy(x => x.Key))
{
//set the first level flag, used to ensure that all documents at the first level will
//set the first level flag, used to ensure that all documents at the first level will
//be published regardless of the rules mentioned in the remarks.
if (!firstLevel.HasValue)
{
@@ -224,7 +224,10 @@ namespace Umbraco.Core.Publishing
_logger.Info<PublishingStrategy>(
string.Format("Content '{0}' with Id '{1}' will not be published because some of it's content is not passing validation rules.",
item.Name, item.Id));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedContentInvalid, evtMsgs)));
statuses.Add(Attempt.Fail(new PublishStatus(item, PublishStatusType.FailedContentInvalid, evtMsgs)
{
InvalidProperties = ((ContentBase)item).LastInvalidProperties
}));
//Does this document apply to our rule to cancel it's children being published?
CheckCancellingOfChildPublishing(item, parentsIdsCancelled, includeUnpublishedDocuments);
@@ -296,11 +299,11 @@ namespace Umbraco.Core.Publishing
/// <param name="includeUnpublishedDocuments"></param>
/// <remarks>
/// See remarks on method: PublishWithChildrenInternal
/// </remarks>
/// </remarks>
private void CheckCancellingOfChildPublishing(IContent content, List<int> parentsIdsCancelled, bool includeUnpublishedDocuments)
{
//Does this document apply to our rule to cancel it's children being published?
//TODO: We're going back to the service layer here... not sure how to avoid this? And this will add extra overhead to
//TODO: We're going back to the service layer here... not sure how to avoid this? And this will add extra overhead to
// any document that fails to publish...
var hasPublishedVersion = ApplicationContext.Current.Services.ContentService.HasPublishedVersion(content.Id);
@@ -326,7 +326,8 @@ namespace Umbraco.Core.Security
//Special case to allow changing password without validating existing credentials
//This is used during installation only
if (AllowManuallyChangingPassword == false && ApplicationContext.Current.IsConfigured == false && oldPassword == "default")
if (AllowManuallyChangingPassword == false && ApplicationContext.Current != null
&& ApplicationContext.Current.IsConfigured == false && oldPassword == "default")
{
return PerformChangePassword(username, oldPassword, newPassword);
}
@@ -358,9 +358,8 @@ namespace Umbraco.Core.Services
{
var applicationAlias = (string)addElement.Attribute("application");
var type = (string)addElement.Attribute("type");
var assembly = (string)addElement.Attribute("assembly");
var clrType = Type.GetType(type);
var clrType = ApplicationTree.TryGetType(type);
if (clrType == null)
{
_logger.Warn<ApplicationTreeService>("The tree definition: " + addElement.ToString() + " could not be resolved to a .Net object type");
+21 -42
View File
@@ -79,7 +79,7 @@ namespace Umbraco.Core.Services
{
var result = _runtimeCache.GetCacheItem<int?>(CacheKeys.IdToKeyCacheKey + key, () =>
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
switch (umbracoObjectType)
{
@@ -92,7 +92,6 @@ namespace Umbraco.Core.Services
case UmbracoObjectTypes.Member:
case UmbracoObjectTypes.DataType:
case UmbracoObjectTypes.DocumentTypeContainer:
uow.Commit();
return uow.Database.ExecuteScalar<int?>(new Sql().Select("id").From<NodeDto>().Where<NodeDto>(dto => dto.UniqueId == key));
case UmbracoObjectTypes.RecycleBin:
case UmbracoObjectTypes.Stylesheet:
@@ -119,7 +118,7 @@ namespace Umbraco.Core.Services
{
var result = _runtimeCache.GetCacheItem<Guid?>(CacheKeys.KeyToIdCacheKey + id, () =>
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
switch (umbracoObjectType)
{
@@ -131,7 +130,6 @@ namespace Umbraco.Core.Services
case UmbracoObjectTypes.DocumentType:
case UmbracoObjectTypes.Member:
case UmbracoObjectTypes.DataType:
uow.Commit();
return uow.Database.ExecuteScalar<Guid?>(new Sql().Select("uniqueID").From<NodeDto>().Where<NodeDto>(dto => dto.NodeId == id));
case UmbracoObjectTypes.RecycleBin:
case UmbracoObjectTypes.Stylesheet:
@@ -185,11 +183,10 @@ namespace Umbraco.Core.Services
{
if (loadBaseType)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.Get(id);
uow.Commit();
return ret;
}
}
@@ -207,11 +204,10 @@ namespace Umbraco.Core.Services
if (loadBaseType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetByKey(key, objectTypeId);
uow.Commit();
return ret;
}
}
@@ -241,11 +237,10 @@ namespace Umbraco.Core.Services
if (loadBaseType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.Get(id, objectTypeId);
uow.Commit();
return ret;
}
}
@@ -276,11 +271,10 @@ namespace Umbraco.Core.Services
{
if (loadBaseType)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.Get(id);
uow.Commit();
return ret;
}
}
@@ -303,7 +297,7 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
public virtual IUmbracoEntity GetParent(int id)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
@@ -311,7 +305,6 @@ namespace Umbraco.Core.Services
if (entity.ParentId == -1 || entity.ParentId == -20 || entity.ParentId == -21)
return null;
var ret = repository.Get(entity.ParentId);
uow.Commit();
return ret;
}
}
@@ -324,7 +317,7 @@ namespace Umbraco.Core.Services
/// <returns>An <see cref="IUmbracoEntity"/></returns>
public virtual IUmbracoEntity GetParent(int id, UmbracoObjectTypes umbracoObjectType)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
@@ -334,7 +327,6 @@ namespace Umbraco.Core.Services
var objectTypeId = umbracoObjectType.GetGuid();
var ret = repository.Get(entity.ParentId, objectTypeId);
uow.Commit();
return ret;
}
}
@@ -346,13 +338,12 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
public virtual IEnumerable<IUmbracoEntity> GetChildren(int parentId)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId);
var contents = repository.GetByQuery(query);
uow.Commit();
return contents;
}
}
@@ -366,13 +357,12 @@ namespace Umbraco.Core.Services
public virtual IEnumerable<IUmbracoEntity> GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId);
var contents = repository.GetByQuery(query, objectTypeId);
uow.Commit();
return contents;
}
}
@@ -393,7 +383,7 @@ namespace Umbraco.Core.Services
string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, string filter = "")
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId && x.Trashed == false);
@@ -405,7 +395,6 @@ namespace Umbraco.Core.Services
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
@@ -426,7 +415,7 @@ namespace Umbraco.Core.Services
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "")
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
@@ -442,7 +431,6 @@ namespace Umbraco.Core.Services
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
@@ -463,7 +451,7 @@ namespace Umbraco.Core.Services
string orderBy = "path", Direction orderDirection = Direction.Ascending, string filter = "", bool includeTrashed = true)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
@@ -481,7 +469,6 @@ namespace Umbraco.Core.Services
}
var contents = repository.GetPagedResultsByQuery(query, objectTypeId, pageIndex, pageSize, out totalRecords, orderBy, orderDirection, filterQuery);
uow.Commit();
return contents;
}
}
@@ -493,7 +480,7 @@ namespace Umbraco.Core.Services
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
public virtual IEnumerable<IUmbracoEntity> GetDescendents(int id)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
@@ -501,7 +488,6 @@ namespace Umbraco.Core.Services
var query = Query<IUmbracoEntity>.Builder.Where(x => x.Path.StartsWith(pathMatch) && x.Id != id);
var entities = repository.GetByQuery(query);
uow.Commit();
return entities;
}
}
@@ -515,14 +501,13 @@ namespace Umbraco.Core.Services
public virtual IEnumerable<IUmbracoEntity> GetDescendents(int id, UmbracoObjectTypes umbracoObjectType)
{
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entity = repository.Get(id);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.Path.StartsWith(entity.Path) && x.Id != id);
var entities = repository.GetByQuery(query, objectTypeId);
uow.Commit();
return entities;
}
}
@@ -541,11 +526,10 @@ namespace Umbraco.Core.Services
}
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var entities = repository.GetByQuery(_rootEntityQuery, objectTypeId);
uow.Commit();
return entities;
}
}
@@ -585,11 +569,10 @@ namespace Umbraco.Core.Services
});
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetAll(objectTypeId, ids);
uow.Commit();
return ret;
}
}
@@ -605,11 +588,10 @@ namespace Umbraco.Core.Services
});
var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetAll(objectTypeId, keys);
uow.Commit();
return ret;
}
}
@@ -631,11 +613,10 @@ namespace Umbraco.Core.Services
("The passed in type is not supported");
});
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var ret = repository.GetAll(objectTypeId, ids);
uow.Commit();
return ret;
}
}
@@ -721,22 +702,20 @@ namespace Umbraco.Core.Services
public bool Exists(Guid key)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var exists = repository.Exists(key);
uow.Commit();
return exists;
}
}
public bool Exists(int id)
{
using (var uow = UowProvider.GetUnitOfWork())
using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var exists = repository.Exists(id);
uow.Commit();
return exists;
}
}
+1 -1
View File
@@ -868,7 +868,7 @@ namespace Umbraco.Core.Services
if (partialView == null)
{
uow.Commit();
return true;
return false;
}
if (uow.Events.DispatchCancelable(DeletingPartialView, this, new DeleteEventArgs<IPartialView>(partialView)))
+2 -1
View File
@@ -199,6 +199,7 @@
<Compile Include="Configuration\BaseRest\ExtensionElementCollection.cs" />
<Compile Include="Configuration\BaseRest\MethodElement.cs" />
<Compile Include="Configuration\ContentXmlStorage.cs" />
<Compile Include="Configuration\CoreDebug.cs" />
<Compile Include="Configuration\Dashboard\AccessElement.cs" />
<Compile Include="Configuration\Dashboard\AccessItem.cs" />
<Compile Include="Configuration\Dashboard\AccessType.cs" />
@@ -219,7 +220,6 @@
<Compile Include="Configuration\Dashboard\SectionElement.cs" />
<Compile Include="Configuration\Dashboard\TabCollection.cs" />
<Compile Include="Configuration\Dashboard\TabElement.cs" />
<Compile Include="Configuration\CoreDebug.cs" />
<Compile Include="Configuration\FileSystemProviderElement.cs" />
<Compile Include="Configuration\FileSystemProviderElementCollection.cs" />
<Compile Include="Configuration\FileSystemProvidersSection.cs" />
@@ -323,6 +323,7 @@
<Compile Include="DateTimeExtensions.cs" />
<Compile Include="DecimalExtensions.cs" />
<Compile Include="DelegateExtensions.cs" />
<Compile Include="Diagnostics\MiniDump.cs" />
<Compile Include="Deploy\ArtifactBase.cs" />
<Compile Include="Deploy\ArtifactDependency.cs" />
<Compile Include="Deploy\ArtifactDependencyCollection.cs" />
@@ -805,101 +805,6 @@ namespace Umbraco.Tests.IO
Assert.AreEqual(3, sfsFiles.Length);
}
[Test]
[Ignore("Does not work on all environments, Directory.GetFiles is broken.")]
public void ShadowGetFilesUsingWildcardAndSingleCharacterFilter()
{
// Arrange
var path = IOHelper.MapPath("FileSysTests");
Directory.CreateDirectory(path);
Directory.CreateDirectory(path + "/ShadowTests");
Directory.CreateDirectory(path + "/ShadowSystem");
var fs = new PhysicalFileSystem(path + "/ShadowTests/", "ignore");
var sfs = new PhysicalFileSystem(path + "/ShadowSystem/", "ignore");
var ss = new ShadowFileSystem(fs, sfs);
// Act
File.WriteAllText(path + "/ShadowTests/f2.txt", "foo");
File.WriteAllText(path + "/ShadowTests/f2.doc", "foo");
File.WriteAllText(path + "/ShadowTests/f2.docx", "foo");
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
ss.AddFile("f1.txt", ms);
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
ss.AddFile("f1.doc", ms);
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes("foo")))
ss.AddFile("f1.docx", ms);
// Assert
// ensure we get 6 files from the shadow
var getFiles = ss.GetFiles(string.Empty);
Assert.AreEqual(6, getFiles.Count());
var getFilesWithWildcardSinglecharFilter = ss.GetFiles(string.Empty, "*.d?c");
Assert.AreEqual(4, getFilesWithWildcardSinglecharFilter.Count());
var getFilesWithWildcardSinglecharFilter2 = ss.GetFiles(string.Empty, "*.d?cx");
Assert.AreEqual(2, getFilesWithWildcardSinglecharFilter2.Count());
var fsFiles = fs.GetFiles(string.Empty).ToArray();
Assert.AreEqual(3, fsFiles.Length);
var sfsFiles = sfs.GetFiles(string.Empty).ToArray();
Assert.AreEqual(3, sfsFiles.Length);
}
[Test]
[Ignore("Does not work on all environments, Directory.GetFiles is broken.")]
public void ShadowFileSystemFilterIsAsBrokenAsRealFileSystemFilter()
{
// Arrange
var path = IOHelper.MapPath("FileSysTests");
Directory.CreateDirectory(path);
Directory.CreateDirectory(path + "/filter");
// create files on disk and create a "fake" list of files to verify filters against
File.WriteAllText(path + "/filter/f1.txt", "foo");
File.WriteAllText(path + "/filter/f1.doc", "foo");
File.WriteAllText(path + "/filter/f1.docx", "foo");
var files = new string[]
{
"f1.txt",
"f1.doc",
"f1.docx",
};
var filter1 = "";
var filter2 = "*";
var filter3 = "*.doc";
var filter4 = "*.d?c";
var filter5 = "f1.doc";
var filter6 = "f1.d?c";
var filter7 = "**.d?c";
var filter8 = "f*.doc";
// Act & Assert
var result1Disk = Directory.GetFiles(path + "/filter/", filter1).Select(Path.GetFileName).OrderBy(x => x).ToArray();
var result1Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter1))).OrderBy(x => x).ToArray();
Assert.AreEqual(result1Disk, result1Fake);
var result2Disk = Directory.GetFiles(path + "/filter/", filter2).Select(Path.GetFileName).OrderBy(x => x).ToArray();
var result2Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter2))).OrderBy(x => x).ToArray();
Assert.AreEqual(result2Disk, result2Fake);
var result3Disk = Directory.GetFiles(path + "/filter/", filter3).Select(Path.GetFileName).OrderBy(x => x).ToArray();
var result3Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter3))).OrderBy(x => x).ToArray();
Assert.AreEqual(result3Disk, result3Fake);
var result4Disk = Directory.GetFiles(path + "/filter/", filter4).Select(Path.GetFileName).OrderBy(x => x).ToArray();
var result4Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter4))).OrderBy(x => x).ToArray();
Assert.AreEqual(result4Disk, result4Fake);
var result5Disk = Directory.GetFiles(path + "/filter/", filter5).Select(Path.GetFileName).OrderBy(x => x).ToArray();
var result5Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter5))).OrderBy(x => x).ToArray();
Assert.AreEqual(result5Disk, result5Fake);
var result6Disk = Directory.GetFiles(path + "/filter/", filter6).Select(Path.GetFileName).OrderBy(x => x).ToArray();
var result6Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter6))).OrderBy(x => x).ToArray();
Assert.AreEqual(result6Disk, result6Fake);
var result7Disk = Directory.GetFiles(path + "/filter/", filter7).Select(Path.GetFileName).OrderBy(x => x).ToArray();
var result7Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter7))).OrderBy(x => x).ToArray();
Assert.AreEqual(result7Disk, result7Fake);
var result8Disk = Directory.GetFiles(path + "/filter/", filter8).Select(Path.GetFileName).OrderBy(x => x).ToArray();
var result8Fake = files.Where(x => ShadowFileSystem.FilterByRegex(x, ShadowFileSystem.FilterToRegex(filter8))).OrderBy(x => x).ToArray();
Assert.AreEqual(result8Disk, result8Fake);
}
/// <summary>
/// Returns the full paths of the files on the disk.
/// Note that this will be the *actual* path of the file, meaning a file existing on the initialized FS
@@ -237,6 +237,14 @@ namespace Umbraco.Tests.Persistence.Repositories
Assert.AreEqual("test-path-1.js", script.Path);
Assert.AreEqual("/scripts/test-path-1.js", script.VirtualPath);
//ensure you can prefix the same path as the root path name
script = new Script("scripts/path-2/test-path-2.js") { Content = "// script" };
repository.AddOrUpdate(script);
unitOfWork.Commit();
Assert.IsTrue(_fileSystem.FileExists("scripts/path-2/test-path-2.js"));
Assert.AreEqual("scripts\\path-2\\test-path-2.js", script.Path);
Assert.AreEqual("/scripts/scripts/path-2/test-path-2.js", script.VirtualPath);
script = new Script("path-2/test-path-2.js") { Content = "// script" };
repository.AddOrUpdate(script);
unitOfWork.Commit();
@@ -191,27 +191,34 @@ tinymce.PluginManager.add('umbracolink', function(editor) {
callback: function (data) {
if (data) {
var href = data.url;
// We want to use the Udi. If it is set, we use it, else fallback to id, and finally to null
var hasUdi = data.udi ? true : false;
var id = hasUdi ? data.udi : (data.id ? data.id : null);
//Create a json obj used to create the attributes for the tag
function createElemAttributes() {
var a = {
href: href,
title: data.name,
target: data.target ? data.target : null,
rel: data.rel ? data.rel : null
};
if (hasUdi) {
a["data-udi"] = data.udi;
}
else if (data.id) {
a["data-id"] = data.id;
}
}
function insertLink() {
if (anchorElm) {
dom.setAttribs(anchorElm, {
href: href,
title: data.name,
target: data.target ? data.target : null,
rel: data.rel ? data.rel : null,
'data-id': data.id ? data.id : null
});
dom.setAttribs(anchorElm, createElemAttributes());
selection.select(anchorElm);
editor.execCommand('mceEndTyping');
} else {
editor.execCommand('mceInsertLink', false, {
href: href,
title: data.name,
target: data.target ? data.target : null,
rel: data.rel ? data.rel : null,
'data-id': data.id ? data.id : null
});
editor.execCommand('mceInsertLink', false, createElemAttributes());
}
}
@@ -221,15 +228,11 @@ tinymce.PluginManager.add('umbracolink', function(editor) {
}
//if we have an id, it must be a locallink:id, aslong as the isMedia flag is not set
if(data.id && (angular.isUndefined(data.isMedia) || !data.isMedia)){
if (target.udi) {
href = "/{localLink:" + target.udi + "}";
}
else {
//This shouldn't happen! but just in case we'll leave this here
href = "/{localLink:" + target.id + "}";
}
insertLink();
if (id && (angular.isUndefined(data.isMedia) || !data.isMedia)){
href = "/{localLink:" + id + "}";
insertLink();
return;
}
@@ -163,13 +163,13 @@ angular.module('umbraco.directives')
}
// ignore clicks on dialog from old dialog service
var oldDialog = $(el).parents("#old-dialog-service");
var oldDialog = $(event.target).parents("#old-dialog-service");
if (oldDialog.length === 1) {
return;
}
// ignore clicks in tinyMCE dropdown(floatpanel)
var floatpanel = $(el).parents(".mce-floatpanel");
var floatpanel = $(event.target).closest(".mce-floatpanel");
if (floatpanel.length === 1) {
return;
}
@@ -108,8 +108,10 @@ angular.module("umbraco.directives")
$image.load(function(){
$timeout(function(){
setDimensions();
scope.loaded = true;
scope.onImageLoaded();
scope.loaded = true;
if (scope.onImageLoaded) {
scope.onImageLoaded();
}
});
});
@@ -87,7 +87,8 @@ Use this directive to generate a list of folders presented as a flexbox grid.
scope.clickFolder = function(folder, $event, $index) {
if(scope.onClick) {
scope.onClick(folder, $event, $index);
scope.onClick(folder, $event, $index);
$event.stopPropagation();
}
};
@@ -247,6 +247,7 @@ Use this directive to generate a thumbnail grid of media items.
scope.clickItem = function(item, $event, $index) {
if (scope.onClick) {
scope.onClick(item, $event, $index);
$event.stopPropagation();
}
};
@@ -9,7 +9,7 @@ function valServerField(serverValidationManager) {
return {
require: 'ngModel',
restrict: "A",
link: function (scope, element, attr, ctrl) {
link: function (scope, element, attr, ngModel) {
var fieldName = null;
var eventBindings = [];
@@ -23,23 +23,25 @@ function valServerField(serverValidationManager) {
// resubmitted. So once a field is changed that has a server error assigned to it
// we need to re-validate it for the server side validator so the user can resubmit
// the form. Of course normal client-side validators will continue to execute.
eventBindings.push(scope.$watch('ngModel', function(newValue){
if (ctrl.$invalid) {
ctrl.$setValidity('valServerField', true);
eventBindings.push(scope.$watch(function() {
return ngModel.$modelValue;
}, function(newValue){
if (ngModel.$invalid) {
ngModel.$setValidity('valServerField', true);
}
}));
//subscribe to the server validation changes
serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) {
if (!isValid) {
ctrl.$setValidity('valServerField', false);
ngModel.$setValidity('valServerField', false);
//assign an error msg property to the current validator
ctrl.errorMsg = fieldErrors[0].errorMsg;
ngModel.errorMsg = fieldErrors[0].errorMsg;
}
else {
ctrl.$setValidity('valServerField', true);
ngModel.$setValidity('valServerField', true);
//reset the error message
ctrl.errorMsg = "";
ngModel.errorMsg = "";
}
});
@@ -50,6 +50,10 @@ angular.module('umbraco.mocks').
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetByIds'))
.respond(returnEntitybyIds);
$httpBackend
.whenPOST(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetByIds'))
.respond(returnEntitybyIds);
$httpBackend
.whenGET(mocksUtils.urlRegex('/umbraco/UmbracoApi/Entity/GetAncestors'))
.respond(returnEntitybyIds);
@@ -199,11 +199,17 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) {
*/
getScaffold: function (type, id, snippetName) {
var queryString = "?type=" + type + "&id=" + id;
if (snippetName) {
queryString += "&snippetName=" + snippetName;
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"codeFileApiBaseUrl",
"GetScaffold?type=" + type + "&id=" + id + "&snippetName=" + snippetName)),
"codeFileApiBaseUrl",
"GetScaffold" + queryString)),
"Failed to get scaffold for" + type);
},
@@ -522,6 +522,26 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
return true;
}
return false;
},
/**
* @ngdoc function
* @name umbraco.services.contentEditingHelper#redirectToRenamedContent
* @methodOf umbraco.services.contentEditingHelper
* @function
*
* @description
* For some editors like scripts or entites that have names as ids, these names can change and we need to redirect
* to their new paths, this is helper method to do that.
*/
redirectToRenamedContent: function (id) {
//clear the query strings
$location.search("");
//change to new path
$location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id);
//don't add a browser history for this
$location.replace();
return true;
}
};
}
@@ -21,7 +21,9 @@ function mediaTypeHelper(mediaTypeResource, $q) {
},
getAllowedImagetypes: function (mediaId){
//TODO: This is horribly inneficient - why make one request per type!?
// Get All allowedTypes
return mediaTypeResource.getAllowedTypes(mediaId)
.then(function(types){
@@ -95,11 +95,20 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
if(selectedElm.nodeName === 'IMG'){
var img = $(selectedElm);
var hasUdi = img.attr("data-udi") ? true : false;
currentTarget = {
altText: img.attr("alt"),
url: img.attr("src"),
id: img.attr("rel")
url: img.attr("src")
};
if (hasUdi) {
currentTarget["udi"] = img.attr("data-udi");
}
else {
currentTarget["id"] = img.attr("rel");
}
}
userService.getCurrentUser().then(function(userData) {
@@ -115,13 +124,23 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
insertMediaInEditor: function(editor, img) {
if(img) {
var hasUdi = img.udi ? true : false;
var data = {
alt: img.altText || "",
src: (img.url) ? img.url : "nothing.jpg",
rel: img.id,
'data-id': img.id,
src: (img.url) ? img.url : "nothing.jpg",
id: '__mcenew'
};
};
if (hasUdi) {
data["data-udi"] = img.udi;
}
else {
//Considering these fixed because UDI will now be used and thus
// we have no need for rel http://issues.umbraco.org/issue/U4-6228, http://issues.umbraco.org/issue/U4-6595
data["rel"] = img.id;
data["data-id"] = img.id;
}
editor.insertContent(editor.dom.createHTML('img', data));
@@ -725,27 +744,36 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
insertLinkInEditor: function(editor, target, anchorElm) {
var href = target.url;
// We want to use the Udi. If it is set, we use it, else fallback to id, and finally to null
var hasUdi = target.udi ? true : false;
var id = hasUdi ? target.udi : (target.id ? target.id : null);
//Create a json obj used to create the attributes for the tag
function createElemAttributes() {
var a = {
href: href,
title: target.name,
target: target.target ? target.target : null,
rel: target.rel ? target.rel : null
};
if (hasUdi) {
a["data-udi"] = target.udi;
}
else if (target.id) {
a["data-id"] = target.id;
}
return a;
}
function insertLink() {
if (anchorElm) {
editor.dom.setAttribs(anchorElm, {
href: href,
title: target.name,
target: target.target ? target.target : null,
rel: target.rel ? target.rel : null,
'data-id': target.id ? target.id : null
});
editor.dom.setAttribs(anchorElm, createElemAttributes());
editor.selection.select(anchorElm);
editor.execCommand('mceEndTyping');
} else {
editor.execCommand('mceInsertLink', false, {
href: href,
title: target.name,
target: target.target ? target.target : null,
rel: target.rel ? target.rel : null,
'data-id': target.id ? target.id : null
});
}
else {
editor.execCommand('mceInsertLink', false, createElemAttributes());
}
}
@@ -755,14 +783,9 @@ function tinyMceService(dialogService, $log, imageHelper, $http, $timeout, macro
}
//if we have an id, it must be a locallink:id, aslong as the isMedia flag is not set
if(target.id && (angular.isUndefined(target.isMedia) || !target.isMedia)){
if (target.udi) {
href = "/{localLink:" + target.udi + "}";
}
else {
//This shouldn't happen! but just in case we'll leave this here
href = "/{localLink:" + target.id + "}";
}
if(id && (angular.isUndefined(target.isMedia) || !target.isMedia)){
href = "/{localLink:" + id + "}";
insertLink();
return;
@@ -22,17 +22,19 @@ angular.module("umbraco").controller("Umbraco.Dialogs.LinkPickerController",
$scope.target = dialogOptions.currentTarget;
//if we have a node ID, we fetch the current node to build the form data
if ($scope.target.id) {
if ($scope.target.id || $scope.target.udi) {
var id = $scope.target.udi ? $scope.target.udi : $scope.target.id;
if (!$scope.target.path) {
entityResource.getPath($scope.target.id, "Document").then(function (path) {
entityResource.getPath(id, "Document").then(function (path) {
$scope.target.path = path;
//now sync the tree to this path
$scope.dialogTreeEventHandler.syncTree({ path: $scope.target.path, tree: "content" });
});
}
contentResource.getNiceUrl($scope.target.id).then(function (url) {
contentResource.getNiceUrl(id).then(function (url) {
$scope.target.url = url;
});
}
@@ -59,7 +61,8 @@ angular.module("umbraco").controller("Umbraco.Dialogs.LinkPickerController",
$scope.currentNode = args.node;
$scope.currentNode.selected = true;
$scope.target.id = args.node.id;
$scope.target.id = args.node.id;
$scope.target.udi = args.node.udi;
$scope.target.name = args.node.name;
if (args.node.id < 0) {
@@ -119,7 +119,7 @@ angular.module("umbraco")
localStorageService.set("umbLastOpenedMediaNodeId", folder.id);
};
$scope.clickHandler = function(image, event, index) {
$scope.clickHandler = function (image, event, index) {
if (image.isFolder) {
if ($scope.disableFolderSelect) {
$scope.gotoFolder(image);
@@ -179,27 +179,49 @@ angular.module("umbraco")
$scope.activeDrag = false;
};
function ensureWithinStartNode(node) {
// make sure that last opened node is on the same path as start node
var nodePath = node.path.split(",");
if (nodePath.indexOf($scope.startNodeId.toString()) !== -1) {
$scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder" });
return true;
}
else {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
return false;
}
}
function gotoStartNode(err) {
$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(function(node) {
// make sure that las opened node is on the same path as start node
var nodePath = node.path.split(",");
if (nodePath.indexOf($scope.startNodeId.toString()) !== -1) {
$scope
.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder" });
} else {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
}
},
function(err) {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
});
} else {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
.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() {
@@ -308,15 +330,19 @@ angular.module("umbraco")
var folderImage = $scope.images[folderImageIndex];
var imageIsSelected = false;
for (var selectedImageIndex = 0;
selectedImageIndex < $scope.model.selectedImages.length;
selectedImageIndex++) {
var selectedImage = $scope.model.selectedImages[selectedImageIndex];
if ($scope.model && angular.isArray($scope.model.selectedImages)) {
for (var selectedImageIndex = 0;
selectedImageIndex < $scope.model.selectedImages.length;
selectedImageIndex++) {
var selectedImage = $scope.model.selectedImages[selectedImageIndex];
if (folderImage.key === selectedImage.key) {
imageIsSelected = true;
if (folderImage.key === selectedImage.key) {
imageIsSelected = true;
}
}
}
if (imageIsSelected) {
folderImage.selected = true;
}
@@ -122,9 +122,14 @@
query.filters.push({});
}
function trashFilter(query) {
query.filters.splice(query, 1);
function trashFilter(query, filter) {
for (var i = 0; i < query.filters.length; i++)
{
if (query.filters[i] == filter)
{
query.filters.splice(i, 1);
}
}
//if we remove the last one, add a new one to generate ui for it.
if (query.filters.length == 0) {
query.filters.push({});
@@ -91,7 +91,7 @@
<i class="icon-add"></i>
</a>
<a href ng-click="vm.trashFilter(vm.query)">
<a href ng-click="vm.trashFilter(vm.query, filter)">
<i class="icon-trash"></i>
</a>
@@ -12,10 +12,8 @@ function PartialViewMacrosDeleteController($scope, codefileResource, treeService
//mark it for deletion (used in the UI)
$scope.currentNode.loading = true;
var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name;
codefileResource.deleteByPath('partialViewMacros', virtualPath)
codefileResource.deleteByPath('partialViewMacros', $scope.currentNode.id)
.then(function() {
$scope.currentNode.loading = false;
//get the root node before we remove it
@@ -48,20 +48,25 @@
});
});
vm.page.saveButtonState = "success";
vm.partialViewMacro = saved;
//check if the name changed, if so we need to redirect
if (vm.partialViewMacro.id !== saved.id) {
contentEditingHelper.redirectToRenamedContent(saved.id);
}
else {
vm.page.saveButtonState = "success";
vm.partialViewMacro = saved;
//sync state
editorState.set(vm.partialViewMacro);
//sync state
editorState.set(vm.partialViewMacro);
// normal tree sync
navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
// clear $dirty state on form
setFormState("pristine");
// normal tree sync
navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
// clear $dirty state on form
setFormState("pristine");
}
}, function (err) {
vm.page.saveButtonState = "error";
@@ -237,26 +242,29 @@
}
codefileResource.getScaffold("partialViewMacros", $routeParams.id, snippet).then(function (partialViewMacro) {
ready(partialViewMacro);
ready(partialViewMacro, false);
});
} else {
codefileResource.getByPath('partialViewMacros', $routeParams.id).then(function (partialViewMacro) {
ready(partialViewMacro);
ready(partialViewMacro, true);
});
}
}
function ready(partialViewMacro) {
function ready(partialViewMacro, syncTree) {
vm.page.loading = false;
vm.partialViewMacro = partialViewMacro;
//sync state
editorState.set(vm.partialViewMacro);
navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.virtualPath, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
if (syncTree) {
navigationService.syncTree({ tree: "partialViewMacros", path: vm.partialViewMacro.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
}
// ace configuration
vm.aceOption = {
@@ -12,10 +12,8 @@ function PartialViewsDeleteController($scope, codefileResource, treeService, nav
//mark it for deletion (used in the UI)
$scope.currentNode.loading = true;
var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name;
codefileResource.deleteByPath('partialViews', virtualPath)
codefileResource.deleteByPath('partialViews', $scope.currentNode.id)
.then(function() {
$scope.currentNode.loading = false;
//get the root node before we remove it
@@ -58,20 +58,25 @@
});
});
vm.page.saveButtonState = "success";
vm.partialView = saved;
//check if the name changed, if so we need to redirect
if (vm.partialView.id !== saved.id) {
contentEditingHelper.redirectToRenamedContent(saved.id);
}
else {
vm.page.saveButtonState = "success";
vm.partialView = saved;
//sync state
editorState.set(vm.partialView);
//sync state
editorState.set(vm.partialView);
// normal tree sync
navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
// clear $dirty state on form
setFormState("pristine");
// normal tree sync
navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
// clear $dirty state on form
setFormState("pristine");
}
}, function (err) {
vm.page.saveButtonState = "error";
@@ -247,18 +252,18 @@
}
codefileResource.getScaffold("partialViews", $routeParams.id, snippet).then(function (partialView) {
ready(partialView);
ready(partialView, false);
});
} else {
codefileResource.getByPath('partialViews', $routeParams.id).then(function (partialView) {
ready(partialView);
ready(partialView, true);
});
}
}
function ready(partialView) {
function ready(partialView, syncTree) {
vm.page.loading = false;
vm.partialView = partialView;
@@ -266,9 +271,11 @@
//sync state
editorState.set(vm.partialView);
navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
if (syncTree) {
navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
}
// ace configuration
vm.aceOption = {
@@ -12,10 +12,8 @@ function ScriptsDeleteController($scope, codefileResource, treeService, navigati
//mark it for deletion (used in the UI)
$scope.currentNode.loading = true;
var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name;
codefileResource.deleteByPath('scripts', virtualPath)
codefileResource.deleteByPath('scripts', $scope.currentNode.id)
.then(function() {
$scope.currentNode.loading = false;
//get the root node before we remove it
@@ -55,16 +55,22 @@
notificationsService.success(header, message);
});
vm.page.saveButtonState = "success";
vm.script = saved;
//check if the name changed, if so we need to redirect
if (vm.script.id !== saved.id) {
contentEditingHelper.redirectToRenamedContent(saved.id);
}
else {
vm.page.saveButtonState = "success";
vm.script = saved;
//sync state
editorState.set(vm.script);
// sync tree
navigationService.syncTree({ tree: "scripts", path: vm.script.virtualPath, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
//sync state
editorState.set(vm.script);
// sync tree
navigationService.syncTree({ tree: "scripts", path: vm.script.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
}
}, function (err) {
@@ -90,17 +96,17 @@
if ($routeParams.create) {
codefileResource.getScaffold("scripts", $routeParams.id).then(function (script) {
ready(script);
ready(script, false);
});
} else {
codefileResource.getByPath('scripts', $routeParams.id).then(function (script) {
ready(script);
ready(script, true);
});
}
}
function ready(script) {
function ready(script, syncTree) {
vm.page.loading = false;
@@ -109,9 +115,11 @@
//sync state
editorState.set(vm.script);
navigationService.syncTree({ tree: "scripts", path: vm.script.virtualPath, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
if (syncTree) {
navigationService.syncTree({ tree: "scripts", path: vm.script.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
}
vm.aceOption = {
mode: "javascript",
@@ -27,7 +27,6 @@
<command>
<umbracoAlias>undo</umbracoAlias>
<name>Undo</name>
<name>Remove Format</name>
<icon>images/editor/undo.gif</icon>
<tinyMceCommand value="" userInterface="false" frontendCommand="undo">undo</tinyMceCommand>
<priority>11</priority>
@@ -233,7 +232,6 @@
<plugin loadOnFrontend="true">code</plugin>
<plugin loadOnFrontend="true">codemirror</plugin>
<plugin loadOnFrontend="true">paste</plugin>
<plugin loadOnFrontend="true">umbracolink</plugin>
<plugin loadOnFrontend="true">anchor</plugin>
<plugin loadOnFrontend="true">charmap</plugin>
<plugin loadOnFrontend="true">table</plugin>
@@ -241,7 +239,7 @@
<plugin loadOnFrontend="true">hr</plugin>
</plugins>
<validElements>
<![CDATA[+a[id|style|rel|data-id|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|
<![CDATA[+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|
ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],
-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],
img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],
@@ -239,7 +239,7 @@
<plugin loadOnFrontend="true">hr</plugin>
</plugins>
<validElements>
<![CDATA[+a[id|style|rel|data-id|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|
<![CDATA[+a[id|style|rel|data-id|data-udi|rev|charset|hreflang|dir|lang|tabindex|accesskey|type|name|href|target|title|class|onfocus|onblur|onclick|
ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup],-strong/-b[class|style],-em/-i[class|style],
-strike[class|style],-u[class|style],#p[id|style|dir|class|align],-ol[class|reversed|start|style|type],-ul[class|style],-li[class|style],br[class],
img[id|dir|lang|longdesc|usemap|style|class|src|onmouseover|onmouseout|border|alt=|title|hspace|vspace|width|height|align|umbracoorgwidth|umbracoorgheight|onresize|onresizestart|onresizeend|rel|data-id],
@@ -80,7 +80,7 @@
<div id="feedbackMsg" data-bind="visible: processStatus() == 'complete'">
<div data-bind="css: { success: isSuccessful(), error: !isSuccessful() }">
<div data-bind="css: { 'text-success': isSuccessful(), 'text-error': !isSuccessful() }">
<span data-bind="text: resultMessage, visible: resultMessages().length == 0"></span>
<ul data-bind="foreach: resultMessages, visible: resultMessages().length > 1">
<li data-bind="text: message"></li>
@@ -5,6 +5,8 @@ using Umbraco.Core.Cache;
using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PropertyEditors.ValueConverters;
using Umbraco.Web.PropertyEditors.ValueConverters;
namespace Umbraco.Web.Cache
@@ -111,6 +113,10 @@ namespace Umbraco.Web.Cache
PublishedContentType.ClearDataType(payload.Id);
});
TagsValueConverter.ClearCaches();
MultipleMediaPickerPropertyConverter.ClearCaches();
SliderValueConverter.ClearCaches();
base.Refresh(jsonPayload);
}
}
+163 -125
View File
@@ -1,4 +1,5 @@
using AutoMapper;
using System;
using AutoMapper;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -18,9 +19,11 @@ using Umbraco.Web.Trees;
namespace Umbraco.Web.Editors
{
//TODO: Put some exception filters in our webapi to return 404 instead of 500 when we throw ArgumentNullException
// ref: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/
[PluginController("UmbracoApi")]
[PrefixlessBodyModelValidator]
[UmbracoApplicationAuthorizeAttribute(Core.Constants.Applications.Settings)]
[UmbracoApplicationAuthorize(Core.Constants.Applications.Settings)]
public class CodeFileController : BackOfficeNotificationsController
{
@@ -33,6 +36,9 @@ namespace Umbraco.Web.Editors
[ValidationFilter]
public HttpResponseMessage PostCreate(string type, CodeFileDisplay display)
{
if (display == null) throw new ArgumentNullException("display");
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
switch (type)
{
case Core.Constants.Trees.PartialViews:
@@ -67,12 +73,11 @@ namespace Umbraco.Web.Editors
[HttpPost]
public CodeFileDisplay PostCreateContainer(string type, string parentId, string name)
{
if (string.IsNullOrWhiteSpace(type) || string.IsNullOrWhiteSpace(name))
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(parentId)) throw new ArgumentException("Value cannot be null or whitespace.", "parentId");
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name");
// if the parentId is root (-1) then we just need an empty string as we are
// if the parentId is root (-1) then we just need an empty string as we are
// creating the path below and we don't wan't -1 in the path
if (parentId == Core.Constants.System.Root.ToInvariantString())
{
@@ -120,10 +125,8 @@ namespace Umbraco.Web.Editors
/// <returns>The file and its contents from the virtualPath</returns>
public CodeFileDisplay GetByPath(string type, string virtualPath)
{
if (string.IsNullOrWhiteSpace(type) || string.IsNullOrWhiteSpace(virtualPath))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(virtualPath)) throw new ArgumentException("Value cannot be null or whitespace.", "virtualPath");
virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath);
@@ -139,7 +142,7 @@ namespace Umbraco.Web.Editors
display.Id = System.Web.HttpUtility.UrlEncode(view.Path);
return display;
}
return null;
throw new HttpResponseException(HttpStatusCode.NotFound);
case Core.Constants.Trees.PartialViewMacros:
var viewMacro = Services.FileService.GetPartialViewMacro(virtualPath);
@@ -151,7 +154,7 @@ namespace Umbraco.Web.Editors
display.Id = System.Web.HttpUtility.UrlEncode(viewMacro.Path);
return display;
}
return null;
throw new HttpResponseException(HttpStatusCode.NotFound);
case Core.Constants.Trees.Scripts:
var script = Services.FileService.GetScriptByName(virtualPath);
@@ -163,7 +166,7 @@ namespace Umbraco.Web.Editors
display.Id = System.Web.HttpUtility.UrlEncode(script.Path);
return display;
}
return null;
throw new HttpResponseException(HttpStatusCode.NotFound);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
@@ -176,10 +179,7 @@ namespace Umbraco.Web.Editors
/// <returns>Returns a list of <see cref="SnippetDisplay"/> if a correct type is sent</returns>
public IEnumerable<SnippetDisplay> GetSnippets(string type)
{
if (string.IsNullOrWhiteSpace(type))
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
IEnumerable<string> snippets;
switch (type)
@@ -209,15 +209,10 @@ namespace Umbraco.Web.Editors
/// <param name="id"></param>
/// <param name="snippetName"></param>
/// <returns></returns>
public CodeFileDisplay GetScaffold(string type, string id = null, string snippetName = null)
public CodeFileDisplay GetScaffold(string type, string id, string snippetName = null)
{
if (string.IsNullOrWhiteSpace(type))
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
if (id.IsNullOrWhiteSpace())
id = string.Empty;
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(id)) throw new ArgumentException("Value cannot be null or whitespace.", "id");
CodeFileDisplay codeFileDisplay;
@@ -246,15 +241,15 @@ namespace Umbraco.Web.Editors
// Make sure that the root virtual path ends with '/'
codeFileDisplay.VirtualPath = codeFileDisplay.VirtualPath.EnsureEndsWith("/");
if (id.IsNullOrWhiteSpace() == false && id != Core.Constants.System.Root.ToInvariantString())
if (id != Core.Constants.System.Root.ToInvariantString())
{
codeFileDisplay.VirtualPath += id.TrimStart("/").EnsureEndsWith("/");
//if it's not new then it will have a path, otherwise it won't
codeFileDisplay.Path = Url.GetTreePathFromFilePath(id);
}
codeFileDisplay.VirtualPath = codeFileDisplay.VirtualPath.TrimStart("~");
codeFileDisplay.Path = Url.GetTreePathFromFilePath(id);
codeFileDisplay.VirtualPath = codeFileDisplay.VirtualPath.TrimStart("~");
codeFileDisplay.FileType = type;
return codeFileDisplay;
}
@@ -268,52 +263,52 @@ namespace Umbraco.Web.Editors
[HttpPost]
public HttpResponseMessage Delete(string type, string virtualPath)
{
if (string.IsNullOrWhiteSpace(type) == false && string.IsNullOrWhiteSpace(virtualPath) == false)
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(virtualPath)) throw new ArgumentException("Value cannot be null or whitespace.", "virtualPath");
virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath);
switch (type)
{
virtualPath = System.Web.HttpUtility.UrlDecode(virtualPath);
case Core.Constants.Trees.PartialViews:
if (IsDirectory(virtualPath, SystemDirectories.PartialViews))
{
Services.FileService.DeletePartialViewFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.DeletePartialView(virtualPath, Security.CurrentUser.Id))
{
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View or folder found with the specified path");
switch (type)
{
case Core.Constants.Trees.PartialViews:
if (IsDirectory(virtualPath, SystemDirectories.PartialViews))
{
Services.FileService.DeletePartialViewFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.DeletePartialView(virtualPath, Security.CurrentUser.Id))
{
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View or folder found with the specified path");
case Core.Constants.Trees.PartialViewMacros:
if (IsDirectory(virtualPath, SystemDirectories.MacroPartials))
{
Services.FileService.DeletePartialViewMacroFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.DeletePartialViewMacro(virtualPath, Security.CurrentUser.Id))
{
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro or folder found with the specified path");
case Core.Constants.Trees.PartialViewMacros:
if (IsDirectory(virtualPath, SystemDirectories.MacroPartials))
{
Services.FileService.DeletePartialViewMacroFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.DeletePartialViewMacro(virtualPath, Security.CurrentUser.Id))
{
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Partial View Macro or folder found with the specified path");
case Core.Constants.Trees.Scripts:
if (IsDirectory(virtualPath, SystemDirectories.Scripts))
{
Services.FileService.DeleteScriptFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.GetScriptByName(virtualPath) != null)
{
Services.FileService.DeleteScript(virtualPath, Security.CurrentUser.Id);
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script or folder found with the specified path");
case Core.Constants.Trees.Scripts:
if (IsDirectory(virtualPath, SystemDirectories.Scripts))
{
Services.FileService.DeleteScriptFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.GetScriptByName(virtualPath) != null)
{
Services.FileService.DeleteScript(virtualPath, Security.CurrentUser.Id);
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script or folder found with the specified path");
default:
return Request.CreateResponse(HttpStatusCode.NotFound);
}
default:
return Request.CreateResponse(HttpStatusCode.NotFound);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
@@ -326,16 +321,13 @@ namespace Umbraco.Web.Editors
/// <returns>The updated CodeFileDisplay model</returns>
public CodeFileDisplay PostSave(CodeFileDisplay display)
{
if (display == null) throw new ArgumentNullException("display");
if (ModelState.IsValid == false)
{
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
}
if (display == null || string.IsNullOrWhiteSpace(display.FileType))
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
switch (display.FileType)
{
case Core.Constants.Trees.PartialViews:
@@ -362,35 +354,29 @@ namespace Umbraco.Web.Editors
display.Id = System.Web.HttpUtility.UrlEncode(partialViewMacroResult.Result.Path);
return display;
}
display.AddErrorNotification(
Services.TextService.Localize("speechBubbles/partialViewErrorHeader"),
Services.TextService.Localize("speechBubbles/partialViewErrorText"));
break;
case Core.Constants.Trees.Scripts:
var virtualPath = display.VirtualPath;
var script = Services.FileService.GetScriptByName(display.VirtualPath);
if (script != null)
{
script.Path = display.Name;
display = Mapper.Map(script, display);
display.Path = Url.GetTreePathFromFilePath(script.Path);
display.Id = System.Web.HttpUtility.UrlEncode(script.Path);
return display;
}
else
{
var fileName = EnsurePartialViewExtension(display.Name, ".js");
script = new Script(virtualPath + fileName);
}
script.Content = display.Content;
var scriptResult = CreateOrUpdateScript(display);
display = Mapper.Map(scriptResult, display);
display.Path = Url.GetTreePathFromFilePath(scriptResult.Path);
display.Id = System.Web.HttpUtility.UrlEncode(scriptResult.Path);
return display;
//display.AddErrorNotification(
// Services.TextService.Localize("speechBubbles/partialViewErrorHeader"),
// Services.TextService.Localize("speechBubbles/partialViewErrorText"));
Services.FileService.SaveScript(script, Security.CurrentUser.Id);
break;
default:
throw new HttpResponseException(HttpStatusCode.NotFound);
}
@@ -398,11 +384,86 @@ namespace Umbraco.Web.Editors
return display;
}
/// <summary>
/// Create or Update a Script
/// </summary>
/// <param name="display"></param>
/// <returns></returns>
/// <remarks>
/// It's important to note that Scripts are DIFFERENT from cshtml files since scripts use IFileSystem and cshtml files
/// use a normal file system because they must exist on a real file system for ASP.NET to work.
/// </remarks>
private Script CreateOrUpdateScript(CodeFileDisplay display)
{
//must always end with the correct extension
display.Name = EnsureCorrectFileExtension(display.Name, ".js");
var virtualPath = display.VirtualPath ?? string.Empty;
// this is all weird, should be using relative paths everywhere!
var relPath = FileSystemProviderManager.Current.ScriptsFileSystem.GetRelativePath(virtualPath);
if (relPath.EndsWith(".js") == false)
{
//this would typically mean it's new
relPath = relPath.IsNullOrWhiteSpace()
? relPath + display.Name
: relPath.EnsureEndsWith('/') + display.Name;
}
var script = Services.FileService.GetScriptByName(relPath);
if (script != null)
{
// might need to find the path
var orgPath = script.OriginalPath.Substring(0, script.OriginalPath.IndexOf(script.Name));
script.Path = orgPath + display.Name;
script.Content = display.Content;
//try/catch? since this doesn't return an Attempt?
Services.FileService.SaveScript(script, Security.CurrentUser.Id);
}
else
{
script = new Script(relPath);
script.Content = display.Content;
Services.FileService.SaveScript(script, Security.CurrentUser.Id);
}
return script;
}
private Attempt<IPartialView> CreateOrUpdatePartialView(CodeFileDisplay display)
{
return CreateOrUpdatePartialView(display, SystemDirectories.MacroPartials,
Services.FileService.GetPartialView, Services.FileService.SavePartialView, Services.FileService.CreatePartialView);
}
private Attempt<IPartialView> CreateOrUpdatePartialViewMacro(CodeFileDisplay display)
{
return CreateOrUpdatePartialView(display, SystemDirectories.MacroPartials,
Services.FileService.GetPartialViewMacro, Services.FileService.SavePartialViewMacro, Services.FileService.CreatePartialViewMacro);
}
/// <summary>
/// Helper method to take care of persisting partial views or partial view macros - so we're not duplicating the same logic
/// </summary>
/// <param name="display"></param>
/// <param name="systemDirectory"></param>
/// <param name="getView"></param>
/// <param name="saveView"></param>
/// <param name="createView"></param>
/// <returns></returns>
private Attempt<IPartialView> CreateOrUpdatePartialView(
CodeFileDisplay display, string systemDirectory,
Func<string, IPartialView> getView,
Func<IPartialView, int, Attempt<IPartialView>> saveView,
Func<IPartialView, string, int, Attempt<IPartialView>> createView)
{
//must always end with the correct extension
display.Name = EnsureCorrectFileExtension(display.Name, ".cshtml");
Attempt<IPartialView> partialViewResult;
string virtualPath = NormalizeVirtualPath(display.VirtualPath, SystemDirectories.PartialViews);
var view = Services.FileService.GetPartialView(virtualPath);
var virtualPath = NormalizeVirtualPath(display.VirtualPath, systemDirectory);
var view = getView(virtualPath);
if (view != null)
{
// might need to find the path
@@ -410,14 +471,13 @@ namespace Umbraco.Web.Editors
view.Path = orgPath + display.Name;
view.Content = display.Content;
partialViewResult = Services.FileService.SavePartialView(view, Security.CurrentUser.Id);
partialViewResult = saveView(view, Security.CurrentUser.Id);
}
else
{
var fileName = EnsurePartialViewExtension(display.Name, ".cshtml");
view = new PartialView(virtualPath + fileName);
view = new PartialView(virtualPath + display.Name);
view.Content = display.Content;
partialViewResult = Services.FileService.CreatePartialView(view, display.Snippet, Security.CurrentUser.Id);
partialViewResult = createView(view, display.Snippet, Security.CurrentUser.Id);
}
return partialViewResult;
@@ -437,29 +497,7 @@ namespace Umbraco.Web.Editors
return virtualPath;
}
private Attempt<IPartialView> CreateOrUpdatePartialViewMacro(CodeFileDisplay display)
{
Attempt<IPartialView> partialViewMacroResult;
var virtualPath = display.VirtualPath ?? string.Empty;
var viewMacro = Services.FileService.GetPartialViewMacro(virtualPath);
if (viewMacro != null)
{
viewMacro.Content = display.Content;
viewMacro.Path = display.Name;
partialViewMacroResult = Services.FileService.SavePartialViewMacro(viewMacro, Security.CurrentUser.Id);
}
else
{
var fileName = EnsurePartialViewExtension(display.Name, ".cshtml");
viewMacro = new PartialView(virtualPath + fileName);
viewMacro.Content = display.Content;
partialViewMacroResult = Services.FileService.CreatePartialViewMacro(viewMacro, display.Snippet, Security.CurrentUser.Id);
}
return partialViewMacroResult;
}
private string EnsurePartialViewExtension(string value, string extension)
private string EnsureCorrectFileExtension(string value, string extension)
{
if (value.EndsWith(extension) == false)
value += extension;
+39 -1
View File
@@ -53,6 +53,7 @@ namespace Umbraco.Web.Editors
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
{
controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector(
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)),
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetChildren", "id", typeof(int), typeof(Guid), typeof(Udi), typeof(string))));
}
}
@@ -122,7 +123,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Gets the content json for the content id
/// Gets the media item by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
@@ -141,6 +142,43 @@ namespace Umbraco.Web.Editors
return Mapper.Map<IMedia, MediaItemDisplay>(foundContent);
}
/// <summary>
/// Gets the media item by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
[EnsureUserPermissionForMedia("id")]
public MediaItemDisplay GetById(Guid id)
{
var foundContent = GetObjectFromRequest(() => Services.MediaService.GetById(id));
if (foundContent == null)
{
HandleContentNotFound(id);
//HandleContentNotFound will throw an exception
return null;
}
return Mapper.Map<IMedia, MediaItemDisplay>(foundContent);
}
/// <summary>
/// Gets the media item by id
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[OutgoingEditorModelEvent]
[EnsureUserPermissionForMedia("id")]
public MediaItemDisplay GetById(Udi id)
{
var guidUdi = id as GuidUdi;
if (guidUdi != null)
{
return GetById(guidUdi.Guid);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
/// <summary>
/// Return media for the specified ids
/// </summary>
@@ -1,16 +1,23 @@
using System;
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 Umbraco.Core;
namespace Umbraco.Web.Models.ContentEditing
{
[DataContract(Name = "scriptFile", Namespace = "")]
public class CodeFileDisplay : INotificationModel
public class CodeFileDisplay : INotificationModel, IValidatableObject
{
public CodeFileDisplay()
{
Notifications = new List<Notification>();
}
/// <summary>
/// VirtualPath is the path to the file on disk
/// /views/partials/file.cshtml
@@ -47,5 +54,27 @@ namespace Umbraco.Web.Models.ContentEditing
public string Id { get; set; }
public List<Notification> Notifications { get; private set; }
/// <summary>
/// Some custom validation is required for valid file names
/// </summary>
/// <param name="validationContext"></param>
/// <returns></returns>
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var illegalChars = System.IO.Path.GetInvalidFileNameChars();
if (Name.ContainsAny(illegalChars))
{
yield return new ValidationResult(
"The file name cannot contain illegal characters",
new[] { "Name" });
}
else if (System.IO.Path.GetFileNameWithoutExtension(Name).IsNullOrWhiteSpace())
{
yield return new ValidationResult(
"The file name cannot be empty",
new[] { "Name" });
}
}
}
}
+6 -1
View File
@@ -41,4 +41,9 @@ using System.Security;
[assembly: InternalsVisibleTo("Umbraco.VisualStudio")]
[assembly: InternalsVisibleTo("Umbraco.ModelsBuilder")]
[assembly: InternalsVisibleTo("Umbraco.ModelsBuilder.AspNet")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Core")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Core.Providers")]
[assembly: InternalsVisibleTo("Umbraco.Forms.Web")]
@@ -132,7 +132,6 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
if (nodeIds.Length > 0)
{
var umbHelper = new UmbracoHelper(UmbracoContext.Current);
var objectType = UmbracoObjectTypes.Unknown;
foreach (var nodeId in nodeIds)
@@ -148,8 +147,8 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
}
}
}
//TODO: Get rid of this Yield thing
return multiNodeTreePicker.Yield().Where(x => x != null);
return multiNodeTreePicker;
}
// return the first nodeId as this is one of the excluded properties that expects a single id
@@ -170,11 +169,13 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
{
var item = udi.ToPublishedContent();
if (item != null)
{
multiNodeTreePicker.Add(item);
}
}
}
//TODO: Get rid of this Yield thing
return multiNodeTreePicker.Yield().Where(x => x != null);
return multiNodeTreePicker;
}
// return the first nodeId as this is one of the excluded properties that expects a single id
@@ -8,6 +8,7 @@
// --------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
@@ -223,13 +224,26 @@ namespace Umbraco.Web.PropertyEditors.ValueConverters
/// </returns>
public bool IsMultipleDataType(int dataTypeId)
{
// ** This must be cached (U4-8862) **
var multiPickerPreValue =
_dataTypeService.GetPreValuesCollectionByDataTypeId(dataTypeId)
.PreValuesAsDictionary.FirstOrDefault(
x => string.Equals(x.Key, "multiPicker", StringComparison.InvariantCultureIgnoreCase)).Value;
// GetPreValuesCollectionByDataTypeId is cached at repository level;
// still, the collection is deep-cloned so this is kinda expensive,
// better to cache here + trigger refresh in DataTypeCacheRefresher
return multiPickerPreValue != null && multiPickerPreValue.Value.TryConvertTo<bool>().Result;
return Storages.GetOrAdd(dataTypeId, id =>
{
var preValue = _dataTypeService.GetPreValuesCollectionByDataTypeId(id)
.PreValuesAsDictionary
.FirstOrDefault(x => string.Equals(x.Key, "multiPicker", StringComparison.InvariantCultureIgnoreCase))
.Value;
return preValue != null && preValue.Value.TryConvertTo<bool>().Result;
});
}
private static readonly ConcurrentDictionary<int, bool> Storages = new ConcurrentDictionary<int, bool>();
internal static void ClearCaches()
{
Storages.Clear();
}
}
}
@@ -4,6 +4,7 @@ using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using umbraco;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
@@ -315,9 +316,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
private void InitializeNode()
{
InitializeNode(_xmlNode, UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema, _isPreviewing,
out _id, out _key, out _template, out _sortOrder, out _name, out _writerName,
out _urlName, out _creatorName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path,
InitializeNode(_xmlNode, UmbracoConfig.For.UmbracoSettings().Content.UseLegacyXmlSchema, _isPreviewing,
out _id, out _key, out _template, out _sortOrder, out _name, out _writerName,
out _urlName, out _creatorName, out _creatorId, out _writerId, out _docTypeAlias, out _docTypeId, out _path,
out _version, out _createDate, out _updateDate, out _level, out _isDraft, out _contentType, out _properties,
PublishedContentType.Get);
@@ -345,7 +346,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
//return if this is null
if (xmlNode == null)
{
{
return;
}
@@ -407,8 +408,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
//dictionary to store the property node data
var propertyNodes = new Dictionary<string, XmlNode>();
var propertyNodes = new Dictionary<string, XmlNode>();
foreach (XmlNode n in xmlNode.ChildNodes)
{
var e = n as XmlElement;
@@ -432,7 +433,22 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
//lookup the content type and create the properties collection
contentType = getPublishedContentType(PublishedItemType.Content, docTypeAlias);
try
{
contentType = getPublishedContentType(PublishedItemType.Content, docTypeAlias);
}
catch (InvalidOperationException e)
{
content.Instance.RefreshContentFromDatabase();
throw new InvalidOperationException(
string.Format("{0}. This usually indicates that the content cache is corrupt; the content cache has been rebuilt in an attempt to self-fix the issue.",
//keep the original message but don't use this as an inner exception because we want the above message to be displayed, if we use the inner exception
//we can keep the stack trace but the header message will be the original message and the one we really want to display will be hidden below the fold.
e.Message));
}
properties = new Dictionary<string, IPublishedProperty>(StringComparer.OrdinalIgnoreCase);
//fill in the property collection
@@ -207,7 +207,11 @@ namespace Umbraco.Web.Trees
return false;
}
IContent content = Services.ContentService.GetById(entity.Id);
var content = Services.ContentService.GetById(entity.Id);
if (content == null)
{
return false;
}
return Security.CurrentUser.HasPathAccess(content);
}
@@ -308,7 +308,7 @@ namespace Umbraco.Web.Trees
{
IUmbracoEntity entity;
Guid idGuid = Guid.Empty;
Guid idGuid;
int idInt;
Udi idUdi;
@@ -1,90 +1,71 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http.Formatting;
using umbraco.BusinessLogic.Actions;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Services;
using Umbraco.Web.Models.Trees;
using System.Web;
namespace Umbraco.Web.Trees
{
public abstract class FileSystemTreeController : TreeController
{
protected abstract string FilePath { get; }
protected abstract string FileSearchPattern { get; }
protected abstract IFileSystem2 FileSystem { get; }
protected abstract string[] Extensions { get; }
protected abstract string FileIcon { get; }
/// <summary>
/// Inheritors can override this method to modify the file node that is created.
/// </summary>
/// <param name="xNode"></param>
/// <param name="treeNode"></param>
protected virtual void OnRenderFileNode(ref TreeNode treeNode) { }
/// <summary>
/// Inheritors can override this method to modify the folder node that is created.
/// </summary>
/// <param name="xNode"></param>
/// <param name="treeNode"></param>
protected virtual void OnRenderFolderNode(ref TreeNode treeNode) { }
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
string orgPath = "";
string path = "";
if (!string.IsNullOrEmpty(id) && id != "-1")
{
orgPath = System.Web.HttpUtility.UrlDecode(id);
path = IOHelper.MapPath(FilePath + "/" + orgPath);
orgPath += "/";
}
else
{
path = IOHelper.MapPath(FilePath);
}
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.Root.ToInvariantString()
? HttpUtility.UrlDecode(id).TrimStart("/")
: "";
DirectoryInfo dirInfo = new DirectoryInfo(path);
DirectoryInfo[] dirInfos = dirInfo.GetDirectories();
var directories = FileSystem.GetDirectories(path);
var nodes = new TreeNodeCollection();
foreach (DirectoryInfo dir in dirInfos)
foreach (var directory in directories)
{
if ((dir.Attributes & FileAttributes.Hidden) == 0)
{
var HasChildren = dir.GetFiles().Length > 0 || dir.GetDirectories().Length > 0;
var node = CreateTreeNode(System.Web.HttpUtility.UrlEncode(orgPath + dir.Name), orgPath, queryStrings, dir.Name, "icon-folder", HasChildren);
var hasChildren = FileSystem.GetFiles(directory).Any() || FileSystem.GetDirectories(directory).Any();
OnRenderFolderNode(ref node);
if(node != null)
nodes.Add(node);
}
var name = Path.GetFileName(directory);
var node = CreateTreeNode(HttpUtility.UrlEncode(directory), path, queryStrings, name, "icon-folder", hasChildren);
OnRenderFolderNode(ref node);
if(node != null)
nodes.Add(node);
}
//this is a hack to enable file system tree to support multiple file extension look-up
//so the pattern both support *.* *.xml and xml,js,vb for lookups
string[] allowedExtensions = new string[0];
bool filterByMultipleExtensions = FileSearchPattern.Contains(",");
FileInfo[] fileInfo;
if (filterByMultipleExtensions)
var files = FileSystem.GetFiles(path).Where(x =>
{
fileInfo = dirInfo.GetFiles();
allowedExtensions = FileSearchPattern.ToLower().Split(',');
}
else
fileInfo = dirInfo.GetFiles(FileSearchPattern);
var extension = Path.GetExtension(x);
return extension != null && Extensions.Contains(extension.Trim('.'), StringComparer.InvariantCultureIgnoreCase);
});
foreach (FileInfo file in fileInfo)
{
if ((file.Attributes & FileAttributes.Hidden) == 0)
foreach (var file in files)
{
var withoutExt = Path.GetFileNameWithoutExtension(file);
if (withoutExt.IsNullOrWhiteSpace() == false)
{
if (filterByMultipleExtensions && Array.IndexOf<string>(allowedExtensions, file.Extension.ToLower().Trim('.')) < 0)
continue;
var node = CreateTreeNode(System.Web.HttpUtility.UrlEncode(orgPath + file.Name), orgPath, queryStrings, file.Name, FileIcon, false);
var name = Path.GetFileName(file);
var node = CreateTreeNode(HttpUtility.UrlEncode(file), path, queryStrings, name, FileIcon, false);
OnRenderFileNode(ref node);
if(node != null)
if (node != null)
nodes.Add(node);
}
}
@@ -109,27 +90,22 @@ namespace Umbraco.Web.Trees
return menu;
}
string path;
if (string.IsNullOrEmpty(id) == false)
{
var orgPath = System.Web.HttpUtility.UrlDecode(id);
path = IOHelper.MapPath(FilePath + "/" + orgPath);
}
else
{
path = IOHelper.MapPath(FilePath);
}
var path = string.IsNullOrEmpty(id) == false && id != Constants.System.Root.ToInvariantString()
? System.Web.HttpUtility.UrlDecode(id).TrimStart("/")
: "";
var dirInfo = new DirectoryInfo(path);
//check if it's a directory
if (dirInfo.Attributes == FileAttributes.Directory)
var isFile = FileSystem.FileExists(path);
var isDirectory = FileSystem.DirectoryExists(path);
if (isDirectory)
{
//set the default to create
menu.DefaultMenuAlias = ActionNew.Instance.Alias;
//create action
menu.Items.Add<ActionNew>(Services.TextService.Localize(string.Format("actions/{0}", ActionNew.Instance.Alias)));
var hasChildren = dirInfo.GetFiles().Length > 0 || dirInfo.GetDirectories().Length > 0;
var hasChildren = FileSystem.GetFiles(path).Any() || FileSystem.GetDirectories(path).Any();
//We can only delete folders if it doesn't have any children (folders or files)
if (hasChildren == false)
{
@@ -140,10 +116,9 @@ namespace Umbraco.Web.Trees
//refresh action
menu.Items.Add<RefreshNode, ActionRefresh>(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true);
}
//if it's not a directory then we only allow to delete the item
else
else if (isFile)
{
//delete action
//if it's not a directory then we only allow to delete the item
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)));
}
+9 -3
View File
@@ -76,7 +76,7 @@ namespace Umbraco.Web.Trees
entity,
Constants.ObjectTypes.MediaGuid,
parentId,
queryStrings,
queryStrings,
entity.HasChildren && (isContainer == false));
node.AdditionalData.Add("contentType", entity.ContentTypeAlias);
@@ -130,7 +130,7 @@ namespace Umbraco.Web.Trees
menu.Items.Add<ActionRefresh>(ui.Text("actions", ActionRefresh.Instance.Alias), true);
//if the media item is in the recycle bin, don't have a default menu, just show the regular menu
if (item.Path.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries).Contains(RecycleBinId.ToInvariantString()))
if (item.Path.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Contains(RecycleBinId.ToInvariantString()))
{
menu.DefaultMenuAlias = null;
}
@@ -151,7 +151,13 @@ namespace Umbraco.Web.Trees
/// <returns></returns>
protected override bool HasPathAccess(string id, FormDataCollection queryStrings)
{
var media = Services.MediaService.GetById(int.Parse(id));
var entity = GetEntityFromId(id);
if (entity == null)
{
return false;
}
var media = Services.MediaService.GetById(entity.Id);
if (media == null)
{
return false;
@@ -10,14 +10,16 @@ namespace Umbraco.Web.Trees
[Tree(Constants.Applications.Developer, "partialViewMacros", "Partial View Macro Files", sortOrder: 6)]
public class PartialViewMacrosTreeController : FileSystemTreeController
{
protected override string FilePath
protected override IFileSystem2 FileSystem
{
get { return SystemDirectories.MacroPartials; }
get { return FileSystemProviderManager.Current.MacroPartialsFileSystem; }
}
protected override string FileSearchPattern
private static readonly string[] ExtensionsStatic = { "cshtml" };
protected override string[] Extensions
{
get { return "*.cshtml"; }
get { return ExtensionsStatic; }
}
protected override string FileIcon
@@ -25,11 +27,6 @@ namespace Umbraco.Web.Trees
get { return "icon-article"; }
}
protected override void OnRenderFileNode(ref TreeNode treeNode)
{
base.OnRenderFileNode(ref treeNode);
}
protected override void OnRenderFolderNode(ref TreeNode treeNode)
{
//TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
@@ -10,25 +10,23 @@ namespace Umbraco.Web.Trees
[Tree(Constants.Applications.Settings, "partialViews", "Partial Views", sortOrder: 2)]
public class PartialViewsTreeController : FileSystemTreeController
{
protected override string FilePath
protected override IFileSystem2 FileSystem
{
get { return SystemDirectories.PartialViews; }
get { return FileSystemProviderManager.Current.PartialViewsFileSystem; }
}
protected override string FileSearchPattern
{
get { return "*.cshtml"; }
}
private static readonly string[] ExtensionsStatic = { "cshtml" };
protected override string[] Extensions
{
get { return ExtensionsStatic; }
}
protected override string FileIcon
{
get { return "icon-article"; }
}
protected override void OnRenderFileNode(ref TreeNode treeNode)
{
base.OnRenderFileNode(ref treeNode);
}
protected override void OnRenderFolderNode(ref TreeNode treeNode)
{
//TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
@@ -7,14 +7,16 @@ namespace Umbraco.Web.Trees
[Tree(Constants.Applications.Settings, "scripts", "Scripts", sortOrder: 4)]
public class ScriptTreeController : FileSystemTreeController
{
protected override string FilePath
protected override IFileSystem2 FileSystem
{
get { return SystemDirectories.Scripts; }
get { return FileSystemProviderManager.Current.ScriptsFileSystem; }
}
protected override string FileSearchPattern
private static readonly string[] ExtensionsStatic = { "js" };
protected override string[] Extensions
{
get { return "*.js"; }
get { return ExtensionsStatic; }
}
protected override string FileIcon
{
@@ -26,10 +28,5 @@ namespace Umbraco.Web.Trees
//TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now.
treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);";
}
protected override void OnRenderFileNode(ref TreeNode treeNode)
{
base.OnRenderFileNode(ref treeNode);
}
}
}
@@ -22,7 +22,6 @@ namespace Umbraco.Web.WebApi.Filters
{
private readonly int? _nodeId;
private readonly string _paramName;
private DictionarySource _source;
public enum DictionarySource
{
@@ -43,14 +42,12 @@ namespace Umbraco.Web.WebApi.Filters
{
Mandate.ParameterNotNullOrEmpty(paramName, "paramName");
_paramName = paramName;
_source = DictionarySource.ActionArguments;
}
public EnsureUserPermissionForMediaAttribute(string paramName, DictionarySource source)
{
Mandate.ParameterNotNullOrEmpty(paramName, "paramName");
_paramName = paramName;
_source = source;
}
public override bool AllowMultiple
@@ -58,6 +55,33 @@ namespace Umbraco.Web.WebApi.Filters
get { return true; }
}
private int GetNodeIdFromParameter(object parameterValue)
{
if (parameterValue is int)
{
return (int) parameterValue;
}
var guidId = Guid.Empty;
if (parameterValue is Guid)
{
guidId = (Guid)parameterValue;
}
else if (parameterValue is GuidUdi)
{
guidId = ((GuidUdi) parameterValue).Guid;
}
if (guidId != Guid.Empty)
{
var found = ApplicationContext.Current.Services.EntityService.GetIdForKey(guidId, UmbracoObjectTypes.Media);
if (found)
return found.Result;
}
throw new InvalidOperationException("The id type: " + parameterValue.GetType() + " is not a supported id");
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (UmbracoContext.Current.Security.CurrentUser == null)
@@ -68,7 +92,7 @@ namespace Umbraco.Web.WebApi.Filters
int nodeId;
if (_nodeId.HasValue == false)
{
var parts = _paramName.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
var parts = _paramName.Split(new [] { '.' }, StringSplitOptions.RemoveEmptyEntries);
if (actionContext.ActionArguments[parts[0]] == null)
{
@@ -77,7 +101,7 @@ namespace Umbraco.Web.WebApi.Filters
if (parts.Length == 1)
{
nodeId = (int)actionContext.ActionArguments[parts[0]];
nodeId = GetNodeIdFromParameter(actionContext.ActionArguments[parts[0]]);
}
else
{
@@ -88,7 +112,7 @@ namespace Umbraco.Web.WebApi.Filters
{
throw new InvalidOperationException("No argument found for the current action with the name: " + _paramName);
}
nodeId = (int)prop.GetValue(actionContext.ActionArguments[parts[0]]);
nodeId = GetNodeIdFromParameter(prop.GetValue(actionContext.ActionArguments[parts[0]]));
}
}
else
@@ -109,22 +133,7 @@ namespace Umbraco.Web.WebApi.Filters
}
}
//private object GetValueFromSource(HttpActionContext actionContext, string key)
//{
// switch (_source)
// {
// case DictionarySource.ActionArguments:
// return actionContext.ActionArguments[key];
// case DictionarySource.RequestForm:
// return actionContext.Request.Properties
// case DictionarySource.RequestQueryString:
// break;
// default:
// throw new ArgumentOutOfRangeException();
// }
//}
}
@@ -46,16 +46,16 @@ namespace Umbraco.Web.WebServices
/*var result = ((ContentService) Services.ContentService)
.PublishWithChildrenInternal(content, UmbracoUser.Id, includeUnpublished)
.ToArray();*/
var result = doc.PublishWithSubs(UmbracoUser.Id, includeUnpublished);
var result = doc.PublishWithSubs(UmbracoUser.Id, includeUnpublished).ToArray();
return Json(new
{
success = result.All(x => x.Success),
message = GetMessageForStatuses(result.Select(x => x.Result), content)
message = GetMessageForStatuses(result.Select(x => x.Result).ToArray(), content)
});
}
}
private string GetMessageForStatuses(IEnumerable<PublishStatus> statuses, IContent doc)
private string GetMessageForStatuses(PublishStatus[] statuses, IContent doc)
{
//if all are successful then just say it was successful
if (statuses.All(x => ((int) x.StatusType) < 10))
@@ -91,12 +91,12 @@ namespace Umbraco.Web.WebServices
return "Cannot publish document with a status of " + status.StatusType;
case PublishStatusType.FailedCancelledByEvent:
return ui.Text("publish", "contentPublishedFailedByEvent",
string.Format("{0} ({1})", status.ContentItem.Name, status.ContentItem.Id), UmbracoUser);
string.Format("'{0}' ({1})", status.ContentItem.Name, status.ContentItem.Id), UmbracoUser);
case PublishStatusType.FailedContentInvalid:
return ui.Text("publish", "contentPublishedFailedInvalid",
new []{
string.Format("{0} ({1})", status.ContentItem.Name, status.ContentItem.Id),
string.Join(",", status.InvalidProperties.Select(x => x.Alias))
string.Format("'{0}' ({1})", status.ContentItem.Name, status.ContentItem.Id),
string.Format("'{0}'", string.Join(", ", status.InvalidProperties.Select(x => x.Alias)))
},
UmbracoUser);
default:
@@ -99,7 +99,7 @@ namespace umbraco.presentation.preview
//Inject preview xml
parentId = document.Level == 1 ? -1 : document.ParentId;
var previewXml = document.ToPreviewXml(XmlContent);
if (document.ContentEntity.Published == false
if (document.ContentEntity.Published == false
&& ApplicationContext.Current.Services.ContentService.HasPublishedVersion(document.Id))
previewXml.Attributes.Append(XmlContent.CreateAttribute("isDraft"));
XmlContent = content.GetAddOrUpdateXmlNode(XmlContent, document.Id, document.Level, parentId, previewXml);
@@ -187,13 +187,9 @@ namespace umbraco.presentation.preview
private static void CleanPreviewDirectory(int userId, DirectoryInfo dir)
{
foreach (FileInfo file in dir.GetFiles(userId + "_*.config"))
{
DeletePreviewFile(userId, file);
}
// also delete any files accessed more than 10 minutes ago
var now = DateTime.Now;
foreach (FileInfo file in dir.GetFiles("*.config"))
foreach (var file in dir.GetFiles("*.config"))
{
if ((now - file.LastAccessTime).TotalMinutes > 10)
DeletePreviewFile(userId, file);
@@ -206,6 +202,12 @@ namespace umbraco.presentation.preview
{
file.Delete();
}
catch (IOException)
{
// for *some* reason deleting the file can fail,
// and it will work later on (long-lasting locks, etc),
// so just ignore the exception
}
catch (Exception ex)
{
LogHelper.Error<PreviewContent>(string.Format("Couldn't delete preview set: {0} - User {1}", file.Name, userId), ex);
@@ -302,7 +302,11 @@ namespace umbraco.cms.businesslogic.packager
XmlHelper.SetAttribute(Source, xmlDef, "enableSkins", package.EnableSkins.ToString());
XmlHelper.SetAttribute(Source, xmlDef, "skinRepoGuid", package.SkinRepoGuid.ToString());
XmlHelper.SetAttribute(Source, xmlDef, "iconUrl", package.IconUrl);
XmlHelper.SetAttribute(Source, xmlDef, "umbVersion", package.UmbracoVersion.ToString(3));
if (package.UmbracoVersion != null)
{
XmlHelper.SetAttribute(Source, xmlDef, "umbVersion", package.UmbracoVersion.ToString(3));
}
var licenseNode = xmlDef.SelectSingleNode("license");
if (licenseNode == null)
+22 -30
View File
@@ -874,18 +874,15 @@ namespace umbraco.cms.businesslogic.web
[Obsolete("Don't use! Only used internally to support the legacy events", false)]
internal IEnumerable<Attempt<PublishStatus>> PublishWithSubs(int userId, bool includeUnpublished)
{
PublishEventArgs e = new PublishEventArgs();
var e = new PublishEventArgs();
FireBeforePublish(e);
IEnumerable<Attempt<PublishStatus>> publishedResults = Enumerable.Empty<Attempt<PublishStatus>>();
if (e.Cancel) return Enumerable.Empty<Attempt<PublishStatus>>();
if (!e.Cancel)
{
publishedResults = ApplicationContext.Current.Services.ContentService
.PublishWithChildrenWithStatus(ContentEntity, userId, includeUnpublished);
var publishedResults = ApplicationContext.Current.Services.ContentService
.PublishWithChildrenWithStatus(ContentEntity, userId, includeUnpublished);
FireAfterPublish(e);
}
FireAfterPublish(e);
return publishedResults;
}
@@ -893,7 +890,6 @@ namespace umbraco.cms.businesslogic.web
[Obsolete("Don't use! Only used internally to support the legacy events", false)]
internal Attempt<PublishStatus> SaveAndPublish(int userId)
{
var result = Attempt.Fail(new PublishStatus(ContentEntity, PublishStatusType.FailedCancelledByEvent, new EventMessages()));
foreach (var property in GenericProperties)
{
ContentEntity.SetValue(property.PropertyType.Alias, property.Value);
@@ -901,32 +897,28 @@ namespace umbraco.cms.businesslogic.web
var saveArgs = new SaveEventArgs();
FireBeforeSave(saveArgs);
if (saveArgs.Cancel) return Attempt.Fail(new PublishStatus(ContentEntity, PublishStatusType.FailedCancelledByEvent, new EventMessages()));
if (!saveArgs.Cancel)
{
var publishArgs = new PublishEventArgs();
FireBeforePublish(publishArgs);
var publishArgs = new PublishEventArgs();
FireBeforePublish(publishArgs);
if (publishArgs.Cancel) return Attempt.Fail(new PublishStatus(ContentEntity, PublishStatusType.FailedCancelledByEvent, new EventMessages()));
if (!publishArgs.Cancel)
{
//NOTE: The 'false' parameter will cause the PublishingStrategy events to fire which will ensure that the cache is refreshed.
result = ApplicationContext.Current.Services.ContentService
.SaveAndPublishWithStatus(ContentEntity, userId);
base.VersionDate = ContentEntity.UpdateDate;
this.UpdateDate = ContentEntity.UpdateDate;
//NOTE: The 'false' parameter will cause the PublishingStrategy events to fire which will ensure that the cache is refreshed.
var result = ApplicationContext.Current.Services.ContentService
.SaveAndPublishWithStatus(ContentEntity, userId);
VersionDate = ContentEntity.UpdateDate;
UpdateDate = ContentEntity.UpdateDate;
//NOTE: This is just going to call the CMSNode Save which will launch into the CMSNode.BeforeSave and CMSNode.AfterSave evenths
// which actually do dick all and there's no point in even having them there but just in case for some insane reason someone
// has bound to those events, I suppose we'll need to keep this here.
base.Save();
//NOTE: This is just going to call the CMSNode Save which will launch into the CMSNode.BeforeSave and CMSNode.AfterSave evenths
// which actually do dick all and there's no point in even having them there but just in case for some insane reason someone
// has bound to those events, I suppose we'll need to keep this here.
base.Save();
//Launch the After Save event since we're doing 2 things in one operation: Saving and publishing.
FireAfterSave(saveArgs);
//Launch the After Save event since we're doing 2 things in one operation: Saving and publishing.
FireAfterSave(saveArgs);
//Now we need to fire the After publish event
FireAfterPublish(publishArgs);
}
}
//Now we need to fire the After publish event
FireAfterPublish(publishArgs);
return result;
}