Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bfd201efcc | |||
| 2ae236e98d | |||
| 802fb95784 | |||
| 0297fdb32f | |||
| 5bede4bf9f | |||
| d37106576d | |||
| ed0f0f7942 | |||
| 3d81c73495 | |||
| e7a540e717 | |||
| bb886c237e | |||
| 6c1e271fa8 | |||
| 4c1b807e4f | |||
| 3b22c0cdd7 | |||
| 31d8f49d3b | |||
| 631404743f | |||
| 1e154e8bc9 | |||
| d5c9639170 | |||
| 94b7c36d62 | |||
| 1ffdd1abd1 | |||
| a06de1b165 | |||
| 0fd3af6828 | |||
| fafb207799 | |||
| b1aea424e8 | |||
| 3598acaddc | |||
| 8e03a1ffca | |||
| ded8afa8fc | |||
| dcd2b1eb6b | |||
| c04ef47be3 | |||
| 62f3c87e8a | |||
| 1b9776ecfb | |||
| 7b1efa718e | |||
| f60ad15f70 | |||
| 3f3db006e7 | |||
| 7801cdf9ff | |||
| 836e1bba54 | |||
| de9c5b88ab | |||
| 77981f889f | |||
| 0a132ed5ed | |||
| 5a953761b0 | |||
| 27ee192c8f | |||
| dfa58183c6 | |||
| e8668f3ed4 | |||
| c84e3dac38 | |||
| 72ab51ea60 | |||
| 1bfb63fe86 | |||
| 2cf68ab263 | |||
| 215bc64a83 | |||
| 1b17f4e9df | |||
| 73f0b1992d | |||
| 21f54dd4ff | |||
| a5903ec419 | |||
| ac496b0cf6 | |||
| 6036136938 | |||
| a9c7a7a405 | |||
| ce8286dadd | |||
| 532b177d2e | |||
| 504aac0350 | |||
| 454aad2170 | |||
| fd0cb7da23 | |||
| 3d6f91cbef | |||
| fffd508bb2 | |||
| f4eb37d44d | |||
| c79aaa9c48 | |||
| 6aa00c52dc | |||
| 333b35d8b9 | |||
| 890673a197 | |||
| 6344ad28aa | |||
| eadc3c8c5a | |||
| 27539d4aad | |||
| f7f23015d0 | |||
| e432b7061d | |||
| 30f63eefb3 | |||
| 6114193fd5 | |||
| 82420605ef | |||
| fc92d40b3a |
@@ -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,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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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;
|
||||
}
|
||||
|
||||
+4
-2
@@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+2
-1
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+10
-8
@@ -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) {
|
||||
|
||||
+49
-23
@@ -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;
|
||||
}
|
||||
|
||||
+8
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")]
|
||||
|
||||
|
||||
+6
-5
@@ -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
|
||||
|
||||
+20
-6
@@ -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)));
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user