Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1dacbbdbf6 | |||
| f0b374c30b | |||
| ec5feee1f3 | |||
| 1d1d99ddad | |||
| c306f94a6a | |||
| 11b2ec89fd | |||
| 89596f00f1 | |||
| 4fa6e70e72 | |||
| a5a0a84d46 | |||
| 839431e6fa | |||
| 5369a6fc56 | |||
| 8a078e258f | |||
| fb42574c34 | |||
| 9ad3b6a337 | |||
| 65b16fbfe5 | |||
| 6ad2853bce | |||
| b2407eab98 | |||
| 778c7a836b | |||
| 11f9cdead0 | |||
| a1076d5443 | |||
| 4a11bf68f3 | |||
| fa64e73a43 | |||
| adf3e2dfe0 | |||
| c4c86cf5ce | |||
| 479aba8e1d | |||
| 0c474e2aba | |||
| 4f5b34cdfe | |||
| 0cb7f220ea | |||
| 0995b2cb5a | |||
| 1b37475144 | |||
| be0bacd895 | |||
| a2564bd0c3 | |||
| 402a5c9d56 | |||
| 2a2f22c7d9 | |||
| 372a768dca | |||
| f6e48243a6 | |||
| 94ea677ec0 | |||
| 55e7159f7d | |||
| 390a94e323 | |||
| 19fb920aa1 | |||
| f35e1604ca | |||
| b9765c3bec | |||
| d5a57c505c | |||
| 5f365241a8 | |||
| 9c121f0044 | |||
| f13bc07ddc | |||
| a707d23692 | |||
| 0929b7d42c | |||
| 35a9272b68 | |||
| 112453a032 | |||
| bba7367ba6 | |||
| 83e679ba5e | |||
| 4d950fb4d4 | |||
| 0d191fb8bd | |||
| c7a3efadb1 | |||
| 956425dfbf | |||
| c616354522 | |||
| 36246b96d4 | |||
| 7d8791ec86 | |||
| f349a9ae3e | |||
| 2719e2c231 | |||
| dafde545b0 | |||
| 610538261e | |||
| 9f7b82e20f | |||
| 04b3532e9d | |||
| 01bcd3b623 | |||
| 64fd95bd22 | |||
| 5176b68935 | |||
| 0bfc5f1272 | |||
| 39c18d4201 | |||
| 66ea8ae604 | |||
| 6fc794ca2e | |||
| df958223b0 | |||
| 1217d9f0b3 | |||
| 1301897f39 | |||
| 6d34fe416d | |||
| a884ece8b1 | |||
| 0ffcc9b889 | |||
| ee3131b63d | |||
| 4fd7182388 |
@@ -72,7 +72,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.dll" target="lib\UmbracoExamine.dll" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\UmbracoExamine.xml" target="lib\UmbracoExamine.xml" />
|
||||
<file src="tools\install.core.ps1" target="tools\install.ps1" />
|
||||
<file src="tools\uninstall.core.ps1" target="tools\uninstall.ps1" />
|
||||
|
||||
<!-- Added to be able to produce a symbols package -->
|
||||
<file src="..\_BuildOutput\bin\SQLCE4Umbraco.pdb" target="lib" />
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
|
||||
<file src="..\_BuildOutput\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
|
||||
<file src="tools\install.ps1" target="tools\install.ps1" />
|
||||
<file src="tools\uninstall.ps1" target="tools\uninstall.ps1" />
|
||||
<file src="tools\Readme.txt" target="tools\Readme.txt" />
|
||||
<file src="tools\ReadmeUpgrade.txt" target="tools\ReadmeUpgrade.txt" />
|
||||
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
<appSettings xdt:Transform="InsertIfMissing" />
|
||||
<appSettings>
|
||||
<add key="owin:appStartup" value="UmbracoDefaultOwinStartup" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
|
||||
<add key="Umbraco.ModelsBuilder.Enable" value="false" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
|
||||
<add key="Umbraco.ModelsBuilder.ModelsMode" value="Nothing" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
|
||||
</appSettings>
|
||||
|
||||
<umbracoConfiguration xdt:Transform="InsertIfMissing">
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
param($installPath, $toolsPath, $package, $project)
|
||||
|
||||
Write-Host "installPath:" "${installPath}"
|
||||
Write-Host "toolsPath:" "${toolsPath}"
|
||||
|
||||
Write-Host " "
|
||||
|
||||
if ($project) {
|
||||
|
||||
# Create paths and list them
|
||||
$projectPath = (Get-Item $project.Properties.Item("FullPath").Value).FullName
|
||||
Write-Host "projectPath:" "${projectPath}"
|
||||
$backupPath = Join-Path $projectPath "App_Data\NuGetBackup"
|
||||
Write-Host "backupPath:" "${backupPath}"
|
||||
$umbracoBinFolder = Join-Path $projectPath "bin"
|
||||
Write-Host "umbracoBinFolder:" "${umbracoBinFolder}"
|
||||
|
||||
# Remove backups
|
||||
Write-Host "removing backups:" "${backupPath}"
|
||||
if(Test-Path $backupPath) { Remove-Item -Recurse -Force $backupPath -Confirm:$false }
|
||||
|
||||
# Delete files Umbraco ships with
|
||||
|
||||
Write-Host "removing dlls:" "${umbracoBinFolder}"
|
||||
if(Test-Path $umbracoBinFolder\businesslogic.dll) { Remove-Item $umbracoBinFolder\businesslogic.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\cms.dll) { Remove-Item $umbracoBinFolder\cms.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\controls.dll) { Remove-Item $umbracoBinFolder\controls.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\interfaces.dll) { Remove-Item $umbracoBinFolder\interfaces.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\log4net.dll) { Remove-Item $umbracoBinFolder\log4net.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll) { Remove-Item $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\SQLCE4Umbraco.dll) { Remove-Item $umbracoBinFolder\SQLCE4Umbraco.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\TidyNet.dll) { Remove-Item $umbracoBinFolder\TidyNet.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\umbraco.dll) { Remove-Item $umbracoBinFolder\umbraco.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Umbraco.Core.dll) { Remove-Item $umbracoBinFolder\Umbraco.Core.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\umbraco.DataLayer.dll) { Remove-Item $umbracoBinFolder\umbraco.DataLayer.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\umbraco.editorControls.dll) { Remove-Item $umbracoBinFolder\umbraco.editorControls.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\umbraco.MacroEngines.dll) { Remove-Item $umbracoBinFolder\umbraco.MacroEngines.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\umbraco.providers.dll) { Remove-Item $umbracoBinFolder\umbraco.providers.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\Umbraco.Web.UI.dll) { Remove-Item $umbracoBinFolder\Umbraco.Web.UI.dll -Force -Confirm:$false }
|
||||
if(Test-Path $umbracoBinFolder\UmbracoExamine.dll) { Remove-Item $umbracoBinFolder\UmbracoExamine.dll -Force -Confirm:$false }
|
||||
|
||||
$amd64Folder = Join-Path $umbracoBinFolder "amd64"
|
||||
if(Test-Path $amd64Folder) { Remove-Item $amd64Folder -Force -Recurse -Confirm:$false }
|
||||
$x86Folder = Join-Path $umbracoBinFolder "x86"
|
||||
if(Test-Path $x86Folder) { Remove-Item $x86Folder -Force -Recurse -Confirm:$false }
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
param($installPath, $toolsPath, $package, $project)
|
||||
|
||||
Write-Host "installPath:" "${installPath}"
|
||||
Write-Host "toolsPath:" "${toolsPath}"
|
||||
|
||||
Write-Host " "
|
||||
|
||||
if ($project) {
|
||||
|
||||
# Create paths and list them
|
||||
$projectPath = (Get-Item $project.Properties.Item("FullPath").Value).FullName
|
||||
Write-Host "projectPath:" "${projectPath}"
|
||||
$backupPath = Join-Path $projectPath "App_Data\NuGetBackup"
|
||||
Write-Host "backupPath:" "${backupPath}"
|
||||
$appBrowsers = Join-Path $projectPath "App_Browsers"
|
||||
Write-Host "appBrowsers:" "${appBrowsers}"
|
||||
$appData = Join-Path $projectPath "App_Data"
|
||||
Write-Host "appData:" "${appData}"
|
||||
|
||||
# Remove backups
|
||||
Write-Host "removing backups:" "${backupPath}"
|
||||
if(Test-Path $backupPath) { Remove-Item -Recurse -Force $backupPath -Confirm:$false }
|
||||
|
||||
# Remove app_data files
|
||||
Write-Host "removing app_data files:" "${appData}"
|
||||
if(Test-Path $appData\packages) { Remove-Item $appData\packages -Recurse -Force -Confirm:$false }
|
||||
|
||||
Write-Host "removing app_browsers:" "${appBrowsers}"
|
||||
if(Test-Path $appBrowsers\Form.browser) { Remove-Item $appBrowsers\Form.browser -Force -Confirm:$false }
|
||||
if(Test-Path $appBrowsers\w3cvalidator.browser) { Remove-Item $appBrowsers\w3cvalidator.browser -Force -Confirm:$false }
|
||||
|
||||
# Remove umbraco and umbraco_files
|
||||
$umbracoFolder = Join-Path $projectPath "Umbraco"
|
||||
Write-Host "removing umbraco folder:" "${umbracoFolder}"
|
||||
if(Test-Path $umbracoFolder) { Remove-Item $umbracoFolder -Recurse -Force -Confirm:$false }
|
||||
$umbracoClientFolder = Join-Path $projectPath "Umbraco_Client"
|
||||
Write-Host "removing umbraco client folder:" "${umbracoClientFolder}"
|
||||
if(Test-Path $umbracoClientFolder) { Remove-Item $umbracoClientFolder -Recurse -Force -Confirm:$false }
|
||||
}
|
||||
@@ -5,8 +5,10 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Web.Services.Description;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
@@ -20,6 +22,20 @@ namespace Umbraco.Core.Dynamics
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Tuple<Type, string, int>, MethodInfo[]> MethodCache = new ConcurrentDictionary<Tuple<Type, string, int>, MethodInfo[]>();
|
||||
|
||||
private static IEnumerable<Type> GetTypes(Assembly a)
|
||||
{
|
||||
try
|
||||
{
|
||||
return TypeFinder.GetTypesWithFormattedException(a);
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
{
|
||||
// is this going to flood the log?
|
||||
LogHelper.Error(typeof (ExtensionMethodFinder), "Failed to get types.", ex);
|
||||
return Enumerable.Empty<Type>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the enumerable of all extension method info's in the app domain = USE SPARINGLY!!!
|
||||
/// </summary>
|
||||
@@ -36,7 +52,7 @@ namespace Umbraco.Core.Dynamics
|
||||
// assemblies that contain extension methods
|
||||
.Where(a => a.IsDefined(typeof (ExtensionAttribute), false))
|
||||
// types that contain extension methods
|
||||
.SelectMany(a => a.GetTypes()
|
||||
.SelectMany(a => GetTypes(a)
|
||||
.Where(t => t.IsDefined(typeof (ExtensionAttribute), false) && t.IsSealed && t.IsGenericType == false && t.IsNested == false))
|
||||
// actual extension methods
|
||||
.SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public)
|
||||
@@ -45,9 +61,9 @@ namespace Umbraco.Core.Dynamics
|
||||
.Concat(typeof (Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public))
|
||||
//If we don't do this then we'll be scanning all assemblies each time!
|
||||
.ToArray(),
|
||||
|
||||
|
||||
//only cache for 5 minutes
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
timeout: TimeSpan.FromMinutes(5),
|
||||
|
||||
//each time this is accessed it will be for 5 minutes longer
|
||||
isSliding:true);
|
||||
@@ -57,7 +73,7 @@ namespace Umbraco.Core.Dynamics
|
||||
/// Returns all extension methods found matching the definition
|
||||
/// </summary>
|
||||
/// <param name="runtimeCache">
|
||||
/// The runtime cache is used to temporarily cache all extension methods found in the app domain so that
|
||||
/// The runtime cache is used to temporarily cache all extension methods found in the app domain so that
|
||||
/// while we search for individual extension methods, the process will be reasonably 'quick'. We then statically
|
||||
/// cache the MethodInfo's that we are looking for and then the runtime cache will expire and give back all that memory.
|
||||
/// </param>
|
||||
@@ -78,7 +94,7 @@ namespace Umbraco.Core.Dynamics
|
||||
{
|
||||
var candidates = GetAllExtensionMethodsInAppDomain(runtimeCache);
|
||||
|
||||
// filter by name
|
||||
// filter by name
|
||||
var filtr1 = candidates.Where(m => m.Name == name);
|
||||
|
||||
// filter by args count
|
||||
@@ -102,7 +118,7 @@ namespace Umbraco.Core.Dynamics
|
||||
|
||||
return filtr3.ToArray();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static MethodInfo DetermineMethodFromParams(IEnumerable<MethodInfo> methods, Type genericType, IEnumerable<object> args)
|
||||
@@ -123,12 +139,12 @@ namespace Umbraco.Core.Dynamics
|
||||
types = method.GetParameters().Select(pi => pi.ParameterType).Skip(1)
|
||||
});
|
||||
|
||||
//This type comparer will check
|
||||
//This type comparer will check
|
||||
var typeComparer = new DelegateEqualityComparer<Type>(
|
||||
//Checks if the argument type passed in can be assigned from the parameter type in the method. For
|
||||
//Checks if the argument type passed in can be assigned from the parameter type in the method. For
|
||||
// example, if the argument type is HtmlHelper<MyModel> but the method parameter type is HtmlHelper then
|
||||
// it will match because the argument is assignable to that parameter type and will be able to execute
|
||||
TypeHelper.IsTypeAssignableFrom,
|
||||
TypeHelper.IsTypeAssignableFrom,
|
||||
//This will not ever execute but if it does we need to get the hash code of the string because the hash
|
||||
// code of a type is random
|
||||
type => type.FullName.GetHashCode());
|
||||
@@ -159,7 +175,7 @@ namespace Umbraco.Core.Dynamics
|
||||
.ToArray();
|
||||
|
||||
var methods = GetAllExtensionMethods(runtimeCache, thisType, name, args.Length).ToArray();
|
||||
|
||||
|
||||
return DetermineMethodFromParams(methods, genericType, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using Semver;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
@@ -13,23 +14,49 @@ namespace Umbraco.Core.Events
|
||||
/// </summary>
|
||||
/// <param name="eventObject"></param>
|
||||
/// <param name="targetVersion"></param>
|
||||
/// <param name="productName"></param>
|
||||
/// <param name="canCancel"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
ConfiguredSemVersion = configuredVersion;
|
||||
TargetSemVersion = targetVersion;
|
||||
}
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, SemVersion configuredVersion, SemVersion targetVersion, string productName, bool canCancel)
|
||||
: this(eventObject, null, configuredVersion, targetVersion, productName, canCancel)
|
||||
{ }
|
||||
|
||||
[Obsolete("Use constructor accepting UmbracoVersion instances instead")]
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple migrations that are used in the migration runner
|
||||
/// </summary>
|
||||
/// <param name="eventObject"></param>
|
||||
/// <param name="targetVersion"></param>
|
||||
/// <param name="canCancel"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
[Obsolete("Use constructor accepting a product name instead.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel)
|
||||
: this(eventObject, null, configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName, canCancel)
|
||||
{ }
|
||||
|
||||
[Obsolete("Use constructor accepting SemVersion instances and a product name instead.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, Version configuredVersion, Version targetVersion, bool canCancel)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
ConfiguredSemVersion = new SemVersion(configuredVersion);
|
||||
TargetSemVersion = new SemVersion(targetVersion);
|
||||
}
|
||||
: this(eventObject, null, new SemVersion(configuredVersion), new SemVersion(targetVersion), GlobalSettings.UmbracoMigrationName, canCancel)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple migrations that are used in the migration runner
|
||||
/// </summary>
|
||||
/// <param name="eventObject"></param>
|
||||
/// <param name="migrationContext"></param>
|
||||
/// <param name="targetVersion"></param>
|
||||
/// <param name="productName"></param>
|
||||
/// <param name="canCancel"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
internal MigrationEventArgs(IList<IMigration> eventObject, MigrationContext migrationContext, SemVersion configuredVersion, SemVersion targetVersion, string productName, bool canCancel)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
MigrationContext = migrationContext;
|
||||
ConfiguredSemVersion = configuredVersion;
|
||||
TargetSemVersion = targetVersion;
|
||||
ProductName = productName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple migrations that are used in the migration runner
|
||||
@@ -39,12 +66,15 @@ namespace Umbraco.Core.Events
|
||||
/// <param name="targetVersion"></param>
|
||||
/// <param name="canCancel"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
[Obsolete("Use constructor accepting a product name instead.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
internal MigrationEventArgs(IList<IMigration> eventObject, MigrationContext migrationContext, SemVersion configuredVersion, SemVersion targetVersion, bool canCancel)
|
||||
: base(eventObject, canCancel)
|
||||
{
|
||||
MigrationContext = migrationContext;
|
||||
ConfiguredSemVersion = configuredVersion;
|
||||
TargetSemVersion = targetVersion;
|
||||
ProductName = GlobalSettings.UmbracoMigrationName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -53,21 +83,28 @@ namespace Umbraco.Core.Events
|
||||
/// <param name="eventObject"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
/// <param name="targetVersion"></param>
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, SemVersion configuredVersion, SemVersion targetVersion)
|
||||
: base(eventObject)
|
||||
{
|
||||
ConfiguredSemVersion = configuredVersion;
|
||||
TargetSemVersion = targetVersion;
|
||||
}
|
||||
/// <param name="productName"></param>
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, SemVersion configuredVersion, SemVersion targetVersion, string productName)
|
||||
: this(eventObject, null, configuredVersion, targetVersion, productName, false)
|
||||
{ }
|
||||
|
||||
[Obsolete("Use constructor accepting UmbracoVersion instances instead")]
|
||||
/// <summary>
|
||||
/// Constructor accepting multiple migrations that are used in the migration runner
|
||||
/// </summary>
|
||||
/// <param name="eventObject"></param>
|
||||
/// <param name="configuredVersion"></param>
|
||||
/// <param name="targetVersion"></param>
|
||||
[Obsolete("Use constructor accepting a product name instead.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, SemVersion configuredVersion, SemVersion targetVersion)
|
||||
: this(eventObject, null, configuredVersion, targetVersion, GlobalSettings.UmbracoMigrationName, false)
|
||||
{ }
|
||||
|
||||
[Obsolete("Use constructor accepting SemVersion instances and a product name instead.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public MigrationEventArgs(IList<IMigration> eventObject, Version configuredVersion, Version targetVersion)
|
||||
: base(eventObject)
|
||||
{
|
||||
ConfiguredSemVersion = new SemVersion(configuredVersion);
|
||||
TargetSemVersion = new SemVersion(targetVersion);
|
||||
}
|
||||
: this(eventObject, null, new SemVersion(configuredVersion), new SemVersion(targetVersion), GlobalSettings.UmbracoMigrationName, false)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Returns all migrations that were used in the migration runner
|
||||
@@ -95,6 +132,8 @@ namespace Umbraco.Core.Events
|
||||
|
||||
public SemVersion TargetSemVersion { get; private set; }
|
||||
|
||||
public string ProductName { get; private set; }
|
||||
|
||||
internal MigrationContext MigrationContext { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,12 @@ namespace Umbraco.Core.Models
|
||||
/// </summary
|
||||
public interface IMediaType : IContentTypeComposition
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <param name="newAlias"></param>
|
||||
/// <returns></returns>
|
||||
IMediaType DeepCloneWithResetIdentities(string newAlias);
|
||||
}
|
||||
}
|
||||
@@ -38,5 +38,30 @@ namespace Umbraco.Core.Models
|
||||
: base(parent, alias)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deep clone of the current entity with its identity/alias and it's property identities reset
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IMediaType DeepCloneWithResetIdentities(string alias)
|
||||
{
|
||||
var clone = (MediaType)DeepClone();
|
||||
clone.Alias = alias;
|
||||
clone.Key = Guid.Empty;
|
||||
foreach (var propertyGroup in clone.PropertyGroups)
|
||||
{
|
||||
propertyGroup.ResetIdentity();
|
||||
propertyGroup.ResetDirtyProperties(false);
|
||||
}
|
||||
foreach (var propertyType in clone.PropertyTypes)
|
||||
{
|
||||
propertyType.ResetIdentity();
|
||||
propertyType.ResetDirtyProperties(false);
|
||||
}
|
||||
|
||||
clone.ResetIdentity();
|
||||
clone.ResetDirtyProperties(false);
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -114,10 +114,25 @@ namespace Umbraco.Core.Models.PublishedContent
|
||||
internal static void ClearContentType(int id)
|
||||
{
|
||||
Logging.LogHelper.Debug<PublishedContentType>("Clear content type w/id {0}.", () => id);
|
||||
// requires a predicate because the key does not contain the ID
|
||||
// faster than key strings comparisons anyway
|
||||
|
||||
// we don't support "get all" at the moment - so, cheating
|
||||
var all = ApplicationContext.Current.ApplicationCache.StaticCache.GetCacheItemsByKeySearch<PublishedContentType>("PublishedContentType_").ToArray();
|
||||
|
||||
// the one we want to clear
|
||||
var clr = all.FirstOrDefault(x => x.Id == id);
|
||||
if (clr == null) return;
|
||||
|
||||
// those that have that one in their composition aliases
|
||||
// note: CompositionAliases contains all recursive aliases
|
||||
var oth = all.Where(x => x.CompositionAliases.InvariantContains(clr.Alias)).Select(x => x.Id);
|
||||
|
||||
// merge ids
|
||||
var ids = oth.Concat(new[] { clr.Id }).ToArray();
|
||||
|
||||
// clear them all at once
|
||||
// we don't support "clear many at once" at the moment - so, cheating
|
||||
ApplicationContext.Current.ApplicationCache.StaticCache.ClearCacheObjectTypes<PublishedContentType>(
|
||||
(key, value) => value.Id == id);
|
||||
(key, value) => ids.Contains(value.Id));
|
||||
}
|
||||
|
||||
internal static void ClearDataType(int id)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -27,18 +28,21 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
private readonly IMigration[] _migrations;
|
||||
|
||||
[Obsolete("Use the ctor that specifies all dependencies instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public MigrationRunner(Version currentVersion, Version targetVersion, string productName)
|
||||
: this(LoggerResolver.Current.Logger, currentVersion, targetVersion, productName)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Use the ctor that specifies all dependencies instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public MigrationRunner(ILogger logger, Version currentVersion, Version targetVersion, string productName)
|
||||
: this(logger, currentVersion, targetVersion, productName, null)
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Use the ctor that specifies all dependencies instead")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public MigrationRunner(ILogger logger, Version currentVersion, Version targetVersion, string productName, params IMigration[] migrations)
|
||||
: this(ApplicationContext.Current.Services.MigrationEntryService, logger, new SemVersion(currentVersion), new SemVersion(targetVersion), productName, migrations)
|
||||
{
|
||||
@@ -92,7 +96,7 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
: OrderedDowngradeMigrations(foundMigrations).ToList();
|
||||
|
||||
|
||||
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this))
|
||||
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, _productName, true), this))
|
||||
{
|
||||
_logger.Warn<MigrationRunner>("Migration was cancelled by an event");
|
||||
return false;
|
||||
@@ -121,7 +125,7 @@ namespace Umbraco.Core.Persistence.Migrations
|
||||
throw;
|
||||
}
|
||||
|
||||
Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _currentVersion, _targetVersion, false), this);
|
||||
Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _currentVersion, _targetVersion, _productName, false), this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -128,6 +128,34 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
right = Visit(b.Right);
|
||||
}
|
||||
}
|
||||
else if (operand == "=")
|
||||
{
|
||||
// deal with (x == true|false) - most common
|
||||
var constRight = b.Right as ConstantExpression;
|
||||
if (constRight != null && constRight.Type == typeof (bool))
|
||||
return ((bool) constRight.Value) ? VisitNotNot(b.Left) : VisitNot(b.Left);
|
||||
right = Visit(b.Right);
|
||||
|
||||
// deal with (true|false == x) - why not
|
||||
var constLeft = b.Left as ConstantExpression;
|
||||
if (constLeft != null && constLeft.Type == typeof (bool))
|
||||
return ((bool) constLeft.Value) ? VisitNotNot(b.Right) : VisitNot(b.Right);
|
||||
left = Visit(b.Left);
|
||||
}
|
||||
else if (operand == "<>")
|
||||
{
|
||||
// deal with (x != true|false) - most common
|
||||
var constRight = b.Right as ConstantExpression;
|
||||
if (constRight != null && constRight.Type == typeof(bool))
|
||||
return ((bool) constRight.Value) ? VisitNot(b.Left) : VisitNotNot(b.Left);
|
||||
right = Visit(b.Right);
|
||||
|
||||
// deal with (true|false != x) - why not
|
||||
var constLeft = b.Left as ConstantExpression;
|
||||
if (constLeft != null && constLeft.Type == typeof(bool))
|
||||
return ((bool) constLeft.Value) ? VisitNot(b.Right) : VisitNotNot(b.Right);
|
||||
left = Visit(b.Left);
|
||||
}
|
||||
else
|
||||
{
|
||||
left = Visit(b.Left);
|
||||
@@ -231,25 +259,47 @@ namespace Umbraco.Core.Persistence.Querying
|
||||
switch (u.NodeType)
|
||||
{
|
||||
case ExpressionType.Not:
|
||||
var o = Visit(u.Operand);
|
||||
|
||||
//use a Not equal operator instead of <> since we don't know that <> works in all sql servers
|
||||
|
||||
switch (u.Operand.NodeType)
|
||||
{
|
||||
case ExpressionType.MemberAccess:
|
||||
//In this case it wil be a false property , i.e. x => !Trashed
|
||||
SqlParameters.Add(true);
|
||||
return string.Format("NOT ({0} = @0)", o);
|
||||
default:
|
||||
//In this case it could be anything else, such as: x => !x.Path.StartsWith("-20")
|
||||
return string.Format("NOT ({0})", o);
|
||||
}
|
||||
return VisitNot(u.Operand);
|
||||
default:
|
||||
return Visit(u.Operand);
|
||||
}
|
||||
}
|
||||
|
||||
private string VisitNot(Expression exp)
|
||||
{
|
||||
var o = Visit(exp);
|
||||
|
||||
// use a "NOT (...)" syntax instead of "<>" since we don't know whether "<>" works in all sql servers
|
||||
// also, x.StartsWith(...) translates to "x LIKE '...%'" which we cannot "<>" and have to "NOT (...")
|
||||
|
||||
switch (exp.NodeType)
|
||||
{
|
||||
case ExpressionType.MemberAccess:
|
||||
// false property , i.e. x => !Trashed
|
||||
SqlParameters.Add(true);
|
||||
return "NOT (" + o + " = @0)";
|
||||
default:
|
||||
// could be anything else, such as: x => !x.Path.StartsWith("-20")
|
||||
return "NOT (" + o + ")";
|
||||
}
|
||||
}
|
||||
|
||||
private string VisitNotNot(Expression exp)
|
||||
{
|
||||
var o = Visit(exp);
|
||||
|
||||
switch (exp.NodeType)
|
||||
{
|
||||
case ExpressionType.MemberAccess:
|
||||
// true property, i.e. x => Trashed
|
||||
SqlParameters.Add(true);
|
||||
return o + " = @0";
|
||||
default:
|
||||
// could be anything else, such as: x => x.Path.StartsWith("-20")
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual string VisitNewArray(NewArrayExpression na)
|
||||
{
|
||||
|
||||
|
||||
@@ -1212,5 +1212,19 @@ AND umbracoNode.id <> @id",
|
||||
{
|
||||
return PerformExists(id);
|
||||
}
|
||||
|
||||
public string GetUniqueAlias(string alias)
|
||||
{
|
||||
// alias is unique accross ALL content types!
|
||||
var aliasColumn = SqlSyntax.GetQuotedColumnName("alias");
|
||||
var aliases = Database.Fetch<string>(@"SELECT cmsContentType." + aliasColumn + @" FROM cmsContentType
|
||||
INNER JOIN umbracoNode ON cmsContentType.nodeId = umbracoNode.id
|
||||
WHERE cmsContentType." + aliasColumn + @" LIKE @pattern",
|
||||
new { pattern = alias + "%", objectType = NodeObjectTypeId });
|
||||
var i = 1;
|
||||
string test;
|
||||
while (aliases.Contains(test = alias + i)) i++;
|
||||
return test;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,5 +32,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<string> GetAllContentTypeAliases(params Guid[] objectTypes);
|
||||
|
||||
/// <summary>
|
||||
/// Derives a unique alias from an existing alias.
|
||||
/// </summary>
|
||||
/// <param name="alias">The original alias.</param>
|
||||
/// <returns>The original alias with a number appended to it, so that it is unique.</returns>
|
||||
/// /// <remarks>Unique accross all content, media and member types.</remarks>
|
||||
string GetUniqueAlias(string alias);
|
||||
}
|
||||
}
|
||||
@@ -15,5 +15,13 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
IEnumerable<IMediaType> GetByQuery(IQuery<PropertyType> query);
|
||||
|
||||
IEnumerable<MoveEventInfo<IMediaType>> Move(IMediaType toMove, EntityContainer container);
|
||||
|
||||
/// <summary>
|
||||
/// Derives a unique alias from an existing alias.
|
||||
/// </summary>
|
||||
/// <param name="alias">The original alias.</param>
|
||||
/// <returns>The original alias with a number appended to it, so that it is unique.</returns>
|
||||
/// <remarks>Unique accross all content, media and member types.</remarks>
|
||||
string GetUniqueAlias(string alias);
|
||||
}
|
||||
}
|
||||
@@ -794,7 +794,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
//TODO: This needs to change, if we are deleting a content type, we should just delete the data,
|
||||
// this method will recursively go lookup every content item, check if any of it's descendants are
|
||||
// of a different type, move them to the recycle bin, then permanently delete the content items.
|
||||
// of a different type, move them to the recycle bin, then permanently delete the content items.
|
||||
// The main problem with this is that for every content item being deleted, events are raised...
|
||||
// which we need for many things like keeping caches in sync, but we can surely do this MUCH better.
|
||||
|
||||
@@ -1067,6 +1067,92 @@ namespace Umbraco.Core.Services
|
||||
new OperationStatus<MoveOperationStatusType>(MoveOperationStatusType.Success, evtMsgs));
|
||||
}
|
||||
|
||||
public Attempt<OperationStatus<IMediaType, MoveOperationStatusType>> CopyMediaType(IMediaType toCopy, int containerId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
IMediaType copy;
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.MediaTypeContainerGuid))
|
||||
using (var repository = RepositoryFactory.CreateMediaTypeRepository(uow))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (containerId > 0)
|
||||
{
|
||||
var container = containerRepository.Get(containerId);
|
||||
if (container == null)
|
||||
throw new DataOperationException<MoveOperationStatusType>(MoveOperationStatusType.FailedParentNotFound);
|
||||
}
|
||||
var alias = repository.GetUniqueAlias(toCopy.Alias);
|
||||
copy = toCopy.DeepCloneWithResetIdentities(alias);
|
||||
copy.Name = copy.Name + " (copy)"; // might not be unique
|
||||
|
||||
// if it has a parent, and the parent is a content type, unplug composition
|
||||
// all other compositions remain in place in the copied content type
|
||||
if (copy.ParentId > 0)
|
||||
{
|
||||
var parent = repository.Get(copy.ParentId);
|
||||
if (parent != null)
|
||||
copy.RemoveContentType(parent.Alias);
|
||||
}
|
||||
|
||||
copy.ParentId = containerId;
|
||||
repository.AddOrUpdate(copy);
|
||||
}
|
||||
catch (DataOperationException<MoveOperationStatusType> ex)
|
||||
{
|
||||
return Attempt.Fail(new OperationStatus<IMediaType, MoveOperationStatusType>(null, ex.Operation, evtMsgs));
|
||||
}
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
return Attempt.Succeed(new OperationStatus<IMediaType, MoveOperationStatusType>(copy, MoveOperationStatusType.Success, evtMsgs));
|
||||
}
|
||||
|
||||
public Attempt<OperationStatus<IContentType, MoveOperationStatusType>> CopyContentType(IContentType toCopy, int containerId)
|
||||
{
|
||||
var evtMsgs = EventMessagesFactory.Get();
|
||||
|
||||
IContentType copy;
|
||||
var uow = UowProvider.GetUnitOfWork();
|
||||
using (var containerRepository = RepositoryFactory.CreateEntityContainerRepository(uow, Constants.ObjectTypes.DocumentTypeContainerGuid))
|
||||
using (var repository = RepositoryFactory.CreateContentTypeRepository(uow))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (containerId > 0)
|
||||
{
|
||||
var container = containerRepository.Get(containerId);
|
||||
if (container == null)
|
||||
throw new DataOperationException<MoveOperationStatusType>(MoveOperationStatusType.FailedParentNotFound);
|
||||
}
|
||||
var alias = repository.GetUniqueAlias(toCopy.Alias);
|
||||
copy = toCopy.DeepCloneWithResetIdentities(alias);
|
||||
copy.Name = copy.Name + " (copy)"; // might not be unique
|
||||
|
||||
// if it has a parent, and the parent is a content type, unplug composition
|
||||
// all other compositions remain in place in the copied content type
|
||||
if (copy.ParentId > 0)
|
||||
{
|
||||
var parent = repository.Get(copy.ParentId);
|
||||
if (parent != null)
|
||||
copy.RemoveContentType(parent.Alias);
|
||||
}
|
||||
|
||||
copy.ParentId = containerId;
|
||||
repository.AddOrUpdate(copy);
|
||||
}
|
||||
catch (DataOperationException<MoveOperationStatusType> ex)
|
||||
{
|
||||
return Attempt.Fail(new OperationStatus<IContentType, MoveOperationStatusType>(null, ex.Operation, evtMsgs));
|
||||
}
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
return Attempt.Succeed(new OperationStatus<IContentType, MoveOperationStatusType>(copy, MoveOperationStatusType.Success, evtMsgs));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a single <see cref="IMediaType"/> object
|
||||
/// </summary>
|
||||
|
||||
@@ -291,5 +291,7 @@ namespace Umbraco.Core.Services
|
||||
|
||||
Attempt<OperationStatus<MoveOperationStatusType>> MoveMediaType(IMediaType toMove, int containerId);
|
||||
Attempt<OperationStatus<MoveOperationStatusType>> MoveContentType(IContentType toMove, int containerId);
|
||||
Attempt<OperationStatus<IMediaType, MoveOperationStatusType>> CopyMediaType(IMediaType toCopy, int containerId);
|
||||
Attempt<OperationStatus<IContentType, MoveOperationStatusType>> CopyContentType(IContentType toCopy, int containerId);
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Umbraco.Core
|
||||
|
||||
/// <summary>
|
||||
/// A utility class to find all classes of a certain type by reflection in the current bin folder
|
||||
/// of the web application.
|
||||
/// of the web application.
|
||||
/// </summary>
|
||||
public static class TypeFinder
|
||||
{
|
||||
@@ -34,7 +34,7 @@ namespace Umbraco.Core
|
||||
/// This is a modified version of: http://www.dominicpettifer.co.uk/Blog/44/how-to-get-a-reference-to-all-assemblies-in-the--bin-folder
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been
|
||||
/// We do this because we cannot use AppDomain.Current.GetAssemblies() as this will return only assemblies that have been
|
||||
/// loaded in the CLR, not all assemblies.
|
||||
/// See these threads:
|
||||
/// http://issues.umbraco.org/issue/U5-198
|
||||
@@ -136,7 +136,7 @@ namespace Umbraco.Core
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Return a list of found local Assemblies excluding the known assemblies we don't want to scan
|
||||
/// Return a list of found local Assemblies excluding the known assemblies we don't want to scan
|
||||
/// and exluding the ones passed in and excluding the exclusion list filter, the results of this are
|
||||
/// cached for perforance reasons.
|
||||
/// </summary>
|
||||
@@ -190,6 +190,7 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// NOTE the comma vs period... comma delimits the name in an Assembly FullName property so if it ends with comma then its an exact name match
|
||||
/// NOTE this means that "foo." will NOT exclude "foo.dll" but only "foo.*.dll"
|
||||
/// </remarks>
|
||||
internal static readonly string[] KnownAssemblyExclusionFilter = new[]
|
||||
{
|
||||
@@ -214,7 +215,7 @@ namespace Umbraco.Core
|
||||
"RouteDebugger,",
|
||||
"SqlCE4Umbraco,",
|
||||
"umbraco.datalayer,",
|
||||
"umbraco.interfaces,",
|
||||
"umbraco.interfaces,",
|
||||
//"umbraco.providers,",
|
||||
//"Umbraco.Web.UI,",
|
||||
"umbraco.webservices",
|
||||
@@ -230,9 +231,9 @@ namespace Umbraco.Core
|
||||
"AutoMapper,",
|
||||
"AutoMapper.",
|
||||
"AzureDirectory,",
|
||||
"itextsharp,",
|
||||
"itextsharp,",
|
||||
"UrlRewritingNet.",
|
||||
"HtmlAgilityPack,",
|
||||
"HtmlAgilityPack,",
|
||||
"MiniProfiler,",
|
||||
"Moq,",
|
||||
"nunit.framework,",
|
||||
@@ -371,7 +372,7 @@ namespace Umbraco.Core
|
||||
|
||||
var assemblyList = assemblies.ToArray();
|
||||
|
||||
//find all assembly references that are referencing the attribute type's assembly since we
|
||||
//find all assembly references that are referencing the attribute type's assembly since we
|
||||
//should only be scanning those assemblies because any other assembly will definitely not
|
||||
//contain a class that has this attribute.
|
||||
var referencedAssemblies = TypeHelper.GetReferencedAssemblies(attributeType, assemblyList);
|
||||
@@ -418,7 +419,7 @@ namespace Umbraco.Core
|
||||
foreach (var subTypesInAssembly in allAttributeTypes.GroupBy(x => x.Assembly))
|
||||
{
|
||||
|
||||
//So that we are not scanning too much, we need to group the sub types:
|
||||
//So that we are not scanning too much, we need to group the sub types:
|
||||
// * if there is more than 1 sub type in the same assembly then we should only search on the 'lowest base' type.
|
||||
// * We should also not search for sub types if the type is sealed since you cannot inherit from a sealed class
|
||||
// * We should not search for sub types if the type is static since you cannot inherit from them.
|
||||
@@ -489,7 +490,7 @@ namespace Umbraco.Core
|
||||
|
||||
/// <summary>
|
||||
/// Finds types that are assignable from the assignTypeFrom parameter and will scan for these types in the assembly
|
||||
/// list passed in, however we will only scan assemblies that have a reference to the assignTypeFrom Type or any type
|
||||
/// list passed in, however we will only scan assemblies that have a reference to the assignTypeFrom Type or any type
|
||||
/// deriving from the base type.
|
||||
/// </summary>
|
||||
/// <param name="assignTypeFrom"></param>
|
||||
@@ -513,7 +514,7 @@ namespace Umbraco.Core
|
||||
|
||||
var assemblyList = assemblies.ToArray();
|
||||
|
||||
//find all assembly references that are referencing the current type's assembly since we
|
||||
//find all assembly references that are referencing the current type's assembly since we
|
||||
//should only be scanning those assemblies because any other assembly will definitely not
|
||||
//contain sub type's of the one we're currently looking for
|
||||
var referencedAssemblies = TypeHelper.GetReferencedAssemblies(assignTypeFrom, assemblyList);
|
||||
@@ -560,7 +561,7 @@ namespace Umbraco.Core
|
||||
foreach (var subTypesInAssembly in allSubTypes.GroupBy(x => x.Assembly))
|
||||
{
|
||||
|
||||
//So that we are not scanning too much, we need to group the sub types:
|
||||
//So that we are not scanning too much, we need to group the sub types:
|
||||
// * if there is more than 1 sub type in the same assembly then we should only search on the 'lowest base' type.
|
||||
// * We should also not search for sub types if the type is sealed since you cannot inherit from a sealed class
|
||||
// * We should not search for sub types if the type is static since you cannot inherit from them.
|
||||
@@ -602,39 +603,69 @@ namespace Umbraco.Core
|
||||
return foundAssignableTypes;
|
||||
}
|
||||
|
||||
private static IEnumerable<Type> GetTypesWithFormattedException(Assembly a)
|
||||
internal static IEnumerable<Type> GetTypesWithFormattedException(Assembly a)
|
||||
{
|
||||
//if the assembly is dynamic, do not try to scan it
|
||||
if (a.IsDynamic)
|
||||
return Enumerable.Empty<Type>();
|
||||
|
||||
var getAll = a.GetCustomAttribute<AllowPartiallyTrustedCallersAttribute>() == null;
|
||||
|
||||
try
|
||||
{
|
||||
//we need to detect if an assembly is partially trusted, if so we cannot go interrogating all of it's types
|
||||
//only its exported types, otherwise we'll get exceptions.
|
||||
if (a.GetCustomAttribute<AllowPartiallyTrustedCallersAttribute>() == null)
|
||||
{
|
||||
return a.GetTypes();
|
||||
}
|
||||
else
|
||||
{
|
||||
return a.GetExportedTypes();
|
||||
}
|
||||
return getAll ? a.GetTypes() : a.GetExportedTypes();
|
||||
}
|
||||
catch (ReflectionTypeLoadException ex)
|
||||
catch (TypeLoadException ex) // GetExportedTypes *can* throw TypeLoadException!
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine("Could not load types from assembly " + a.FullName + ", errors:");
|
||||
foreach (var loaderException in ex.LoaderExceptions.WhereNotNull())
|
||||
{
|
||||
sb.AppendLine("Exception: " + loaderException);
|
||||
}
|
||||
throw new ReflectionTypeLoadException(ex.Types, ex.LoaderExceptions, sb.ToString());
|
||||
AppendCouldNotLoad(sb, a, getAll);
|
||||
AppendLoaderException(sb, ex);
|
||||
|
||||
// rethrow as ReflectionTypeLoadException (for consistency) with new message
|
||||
throw new ReflectionTypeLoadException(new Type[0], new Exception[] { ex }, sb.ToString());
|
||||
}
|
||||
catch (ReflectionTypeLoadException rex) // GetTypes throws ReflectionTypeLoadException
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
AppendCouldNotLoad(sb, a, getAll);
|
||||
foreach (var loaderException in rex.LoaderExceptions.WhereNotNull())
|
||||
AppendLoaderException(sb, loaderException);
|
||||
|
||||
// rethrow with new message
|
||||
throw new ReflectionTypeLoadException(rex.Types, rex.LoaderExceptions, sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private static void AppendCouldNotLoad(StringBuilder sb, Assembly a, bool getAll)
|
||||
{
|
||||
sb.Append("Could not load ");
|
||||
sb.Append(getAll ? "all" : "exported");
|
||||
sb.Append(" types from \"");
|
||||
sb.Append(a.FullName);
|
||||
sb.AppendLine("\" due to LoaderExceptions, skipping:");
|
||||
}
|
||||
|
||||
private static void AppendLoaderException(StringBuilder sb, Exception loaderException)
|
||||
{
|
||||
sb.Append(". ");
|
||||
sb.Append(loaderException.GetType().FullName);
|
||||
|
||||
var tloadex = loaderException as TypeLoadException;
|
||||
if (tloadex != null)
|
||||
{
|
||||
sb.Append(" on ");
|
||||
sb.Append(tloadex.TypeName);
|
||||
}
|
||||
|
||||
sb.Append(": ");
|
||||
sb.Append(loaderException.Message);
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
public static Type GetTypeByName(string typeName)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Semver;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
using Umbraco.Core.Profiling;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Strategies.Migrations;
|
||||
|
||||
namespace Umbraco.Tests.Persistence.Migrations
|
||||
{
|
||||
[TestFixture]
|
||||
public class MigrationStartupHandlerTests
|
||||
{
|
||||
[Test]
|
||||
public void Executes_For_Any_Product_Name_When_Not_Specified()
|
||||
{
|
||||
var changed1 = new Args { CountExecuted = 0 };
|
||||
var testHandler1 = new TestMigrationHandler(changed1);
|
||||
testHandler1.OnApplicationStarting(Mock.Of<UmbracoApplicationBase>(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>())));
|
||||
|
||||
var conn = new Mock<IDbConnection>();
|
||||
conn.Setup(x => x.BeginTransaction(It.IsAny<IsolationLevel>())).Returns(Mock.Of<IDbTransaction>());
|
||||
var db = new Mock<Database>(conn.Object);
|
||||
|
||||
var runner1 = new MigrationRunner(Mock.Of<IMigrationEntryService>(), Mock.Of<ILogger>(), new SemVersion(1), new SemVersion(2), "Test1",
|
||||
new IMigration[] { Mock.Of<IMigration>() });
|
||||
var result1 = runner1.Execute(db.Object, DatabaseProviders.SqlServerCE, false);
|
||||
Assert.AreEqual(1, changed1.CountExecuted);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Executes_Only_For_Specified_Product_Name()
|
||||
{
|
||||
var changed1 = new Args { CountExecuted = 0};
|
||||
var testHandler1 = new TestMigrationHandler("Test1", changed1);
|
||||
testHandler1.OnApplicationStarting(Mock.Of<UmbracoApplicationBase>(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>())));
|
||||
var changed2 = new Args { CountExecuted = 0 };
|
||||
var testHandler2 = new TestMigrationHandler("Test2", changed2);
|
||||
testHandler2.OnApplicationStarting(Mock.Of<UmbracoApplicationBase>(), new ApplicationContext(CacheHelper.CreateDisabledCacheHelper(), new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>())));
|
||||
|
||||
var conn = new Mock<IDbConnection>();
|
||||
conn.Setup(x => x.BeginTransaction(It.IsAny<IsolationLevel>())).Returns(Mock.Of<IDbTransaction>());
|
||||
var db = new Mock<Database>(conn.Object);
|
||||
|
||||
var runner1 = new MigrationRunner(Mock.Of<IMigrationEntryService>(), Mock.Of<ILogger>(), new SemVersion(1), new SemVersion(2), "Test1",
|
||||
new IMigration[] { Mock.Of<IMigration>()});
|
||||
var result1 = runner1.Execute(db.Object, DatabaseProviders.SqlServerCE, false);
|
||||
Assert.AreEqual(1, changed1.CountExecuted);
|
||||
Assert.AreEqual(0, changed2.CountExecuted);
|
||||
|
||||
var runner2 = new MigrationRunner(Mock.Of<IMigrationEntryService>(), Mock.Of<ILogger>(), new SemVersion(1), new SemVersion(2), "Test2",
|
||||
new IMigration[] { Mock.Of<IMigration>() });
|
||||
var result2 = runner2.Execute(db.Object, DatabaseProviders.SqlServerCE, false);
|
||||
Assert.AreEqual(1, changed1.CountExecuted);
|
||||
Assert.AreEqual(1, changed2.CountExecuted);
|
||||
}
|
||||
|
||||
public class Args
|
||||
{
|
||||
public int CountExecuted { get; set; }
|
||||
}
|
||||
|
||||
public class TestMigrationHandler : MigrationStartupHander
|
||||
{
|
||||
private readonly string _prodName;
|
||||
private readonly Args _changed;
|
||||
|
||||
public TestMigrationHandler(Args changed)
|
||||
{
|
||||
_changed = changed;
|
||||
}
|
||||
|
||||
public TestMigrationHandler(string prodName, Args changed)
|
||||
{
|
||||
_prodName = prodName;
|
||||
_changed = changed;
|
||||
}
|
||||
|
||||
protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e)
|
||||
{
|
||||
_changed.CountExecuted++;
|
||||
}
|
||||
|
||||
public override string[] TargetProductNames
|
||||
{
|
||||
get { return _prodName.IsNullOrWhiteSpace() ? new string[] {} : new[] {_prodName}; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,18 @@ namespace Umbraco.Tests.Persistence.Querying
|
||||
Assert.AreEqual("-20%", sql.Arguments[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Where_Clause_With_EqualsFalse_Starts_With()
|
||||
{
|
||||
var level = 1;
|
||||
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.Level == level && x.Path.StartsWith("-20") == false);
|
||||
|
||||
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE ([umbracoNode].[level] = @0 AND NOT (upper([umbracoNode].[path]) LIKE upper(@1)))", sql.SQL.Replace("\n", " "));
|
||||
Assert.AreEqual(2, sql.Arguments.Length);
|
||||
Assert.AreEqual(level, sql.Arguments[0]);
|
||||
Assert.AreEqual("-20%", sql.Arguments[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Where_Clause_With_Equals_Clause()
|
||||
{
|
||||
@@ -71,6 +83,16 @@ namespace Umbraco.Tests.Persistence.Querying
|
||||
Assert.AreEqual(true, sql.Arguments[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Where_Clause_With_EqualsFalse_Boolean()
|
||||
{
|
||||
var sql = new Sql("SELECT *").From<NodeDto>().Where<NodeDto>(x => x.Trashed == false);
|
||||
|
||||
Assert.AreEqual("SELECT * FROM [umbracoNode] WHERE (NOT ([umbracoNode].[trashed] = @0))", sql.SQL.Replace("\n", " "));
|
||||
Assert.AreEqual(1, sql.Arguments.Length);
|
||||
Assert.AreEqual(true, sql.Arguments[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Where_Clause_With_Boolean()
|
||||
{
|
||||
|
||||
@@ -79,8 +79,8 @@ namespace Umbraco.Tests.Plugins
|
||||
var originalTypesFound = TypeFinderOriginal.FindClassesOfType<IApplicationStartupHandler>(_assemblies);
|
||||
|
||||
Assert.AreEqual(originalTypesFound.Count(), typesFound.Count());
|
||||
Assert.AreEqual(8, typesFound.Count());
|
||||
Assert.AreEqual(8, originalTypesFound.Count());
|
||||
Assert.AreEqual(9, typesFound.Count());
|
||||
Assert.AreEqual(9, originalTypesFound.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -175,6 +175,7 @@
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Persistence\Migrations\MigrationStartupHandlerTests.cs" />
|
||||
<Compile Include="Web\AngularIntegration\AngularAntiForgeryTests.cs" />
|
||||
<Compile Include="Web\AngularIntegration\ContentModelSerializationTests.cs" />
|
||||
<Compile Include="Web\AngularIntegration\JsInitializationTests.cs" />
|
||||
@@ -244,6 +245,7 @@
|
||||
<Compile Include="Models\UmbracoEntityTests.cs" />
|
||||
<Compile Include="Models\UserTests.cs" />
|
||||
<Compile Include="Models\UserTypeTests.cs" />
|
||||
<Compile Include="Web\Mvc\RenderModelBinderTests.cs" />
|
||||
<Compile Include="Web\Mvc\SurfaceControllerTests.cs" />
|
||||
<Compile Include="Web\Mvc\UmbracoViewPageTests.cs" />
|
||||
<Compile Include="BootManagers\CoreBootManagerTests.cs" />
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Tests.Web.Mvc
|
||||
{
|
||||
[TestFixture]
|
||||
public class RenderModelBinderTests
|
||||
{
|
||||
[Test]
|
||||
public void Returns_Binder_For_IPublishedContent_And_IRenderModel()
|
||||
{
|
||||
var binder = new RenderModelBinder();
|
||||
var found = binder.GetBinder(typeof (IPublishedContent));
|
||||
Assert.IsNotNull(found);
|
||||
found = binder.GetBinder(typeof(IRenderModel));
|
||||
Assert.IsNotNull(found);
|
||||
found = binder.GetBinder(typeof(RenderModel));
|
||||
Assert.IsNotNull(found);
|
||||
found = binder.GetBinder(typeof(DynamicPublishedContent));
|
||||
Assert.IsNotNull(found);
|
||||
found = binder.GetBinder(typeof(MyContent));
|
||||
Assert.IsNotNull(found);
|
||||
|
||||
found = binder.GetBinder(typeof(MyOtherContent));
|
||||
Assert.IsNull(found);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BindModel_Null_Source_Returns_Null()
|
||||
{
|
||||
Assert.IsNull(RenderModelBinder.BindModel(null, typeof(MyContent), CultureInfo.CurrentCulture));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BindModel_Returns_If_Same_Type()
|
||||
{
|
||||
var content = new MyContent(Mock.Of<IPublishedContent>());
|
||||
var bound = RenderModelBinder.BindModel(content, typeof (IPublishedContent), CultureInfo.CurrentCulture);
|
||||
Assert.AreSame(content, bound);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BindModel_RenderModel_To_IPublishedContent()
|
||||
{
|
||||
var content = new MyContent(Mock.Of<IPublishedContent>());
|
||||
var renderModel = new RenderModel(content, CultureInfo.CurrentCulture);
|
||||
var bound = RenderModelBinder.BindModel(renderModel, typeof(IPublishedContent), CultureInfo.CurrentCulture);
|
||||
Assert.AreSame(content, bound);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BindModel_IPublishedContent_To_RenderModel()
|
||||
{
|
||||
var content = new MyContent(Mock.Of<IPublishedContent>());
|
||||
var bound = (IRenderModel)RenderModelBinder.BindModel(content, typeof(RenderModel), CultureInfo.CurrentCulture);
|
||||
Assert.AreSame(content, bound.Content);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BindModel_IPublishedContent_To_Generic_RenderModel()
|
||||
{
|
||||
var content = new MyContent(Mock.Of<IPublishedContent>());
|
||||
var bound = (IRenderModel)RenderModelBinder.BindModel(content, typeof(RenderModel<MyContent>), CultureInfo.CurrentCulture);
|
||||
Assert.AreSame(content, bound.Content);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void No_DataToken_Returns_Null()
|
||||
{
|
||||
var binder = new RenderModelBinder();
|
||||
var routeData = new RouteData();
|
||||
var result = binder.BindModel(new ControllerContext(Mock.Of<HttpContextBase>(), routeData, Mock.Of<ControllerBase>()),
|
||||
new ModelBindingContext());
|
||||
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Invalid_DataToken_Model_Type_Returns_Null()
|
||||
{
|
||||
var binder = new RenderModelBinder();
|
||||
var routeData = new RouteData();
|
||||
routeData.DataTokens[Core.Constants.Web.UmbracoDataToken] = "hello";
|
||||
|
||||
//the value provider is the default implementation
|
||||
var valueProvider = new Mock<IValueProvider>();
|
||||
//also IUnvalidatedValueProvider
|
||||
var invalidatedValueProvider = valueProvider.As<IUnvalidatedValueProvider>();
|
||||
invalidatedValueProvider.Setup(x => x.GetValue(It.IsAny<string>(), It.IsAny<bool>())).Returns(() =>
|
||||
new ValueProviderResult(null, "", CultureInfo.CurrentCulture));
|
||||
|
||||
var controllerCtx = new ControllerContext(
|
||||
Mock.Of<HttpContextBase>(http => http.Items == new Dictionary<object, object>()),
|
||||
routeData,
|
||||
Mock.Of<ControllerBase>());
|
||||
|
||||
var result = binder.BindModel(controllerCtx,
|
||||
new ModelBindingContext
|
||||
{
|
||||
ValueProvider = valueProvider.Object,
|
||||
ModelMetadata = new ModelMetadata(new EmptyModelMetadataProvider(), null, () => null, typeof(IPublishedContent), "content")
|
||||
});
|
||||
|
||||
Assert.IsNull(result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void IPublishedContent_DataToken_Model_Type_Uses_DefaultImplementation()
|
||||
{
|
||||
var content = new MyContent(Mock.Of<IPublishedContent>());
|
||||
var binder = new RenderModelBinder();
|
||||
var routeData = new RouteData();
|
||||
routeData.DataTokens[Core.Constants.Web.UmbracoDataToken] = content;
|
||||
|
||||
//the value provider is the default implementation
|
||||
var valueProvider = new Mock<IValueProvider>();
|
||||
//also IUnvalidatedValueProvider
|
||||
var invalidatedValueProvider = valueProvider.As<IUnvalidatedValueProvider>();
|
||||
invalidatedValueProvider.Setup(x => x.GetValue(It.IsAny<string>(), It.IsAny<bool>())).Returns(() =>
|
||||
new ValueProviderResult(content, "content", CultureInfo.CurrentCulture));
|
||||
|
||||
var controllerCtx = new ControllerContext(
|
||||
Mock.Of<HttpContextBase>(http => http.Items == new Dictionary<object, object>()),
|
||||
routeData,
|
||||
Mock.Of<ControllerBase>());
|
||||
var result = binder.BindModel(controllerCtx,
|
||||
new ModelBindingContext
|
||||
{
|
||||
ValueProvider = valueProvider.Object,
|
||||
ModelMetadata = new ModelMetadata(new EmptyModelMetadataProvider(), null, () => null, typeof(IPublishedContent), "content")
|
||||
});
|
||||
|
||||
Assert.AreEqual(content, result);
|
||||
}
|
||||
|
||||
public class MyOtherContent
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class MyContent : PublishedContentWrapped
|
||||
{
|
||||
public MyContent(IPublishedContent content) : base(content)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,7 +210,7 @@ function submit()
|
||||
if (findDepth(curLineHTML, cc) !== 0) {
|
||||
// Cursor is inside a <tag>, don't set cursor:
|
||||
curLineHTML = curLineHTML.replace(cc, '');
|
||||
doc.setLine(pos.line, curLineHTML);
|
||||
doc.getLineHandle(pos.line).text = curLineHTML;
|
||||
}
|
||||
|
||||
// Submit HTML to TinyMCE:
|
||||
|
||||
@@ -22,15 +22,35 @@ var packages = angular.module("umbraco.packages", []);
|
||||
//module is initilized.
|
||||
angular.module("umbraco.views", ["umbraco.viewcache"]);
|
||||
angular.module("umbraco.viewcache", [])
|
||||
.run(function($rootScope, $templateCache){
|
||||
/** For debug mode, always clear template cache to cut down on
|
||||
.run(function($rootScope, $templateCache) {
|
||||
/** For debug mode, always clear template cache to cut down on
|
||||
dev frustration and chrome cache on templates */
|
||||
if(Umbraco.Sys.ServerVariables.isDebuggingEnabled){
|
||||
//$rootScope.$on('$viewContentLoaded', function() {
|
||||
$templateCache.removeAll();
|
||||
//});
|
||||
if (Umbraco.Sys.ServerVariables.isDebuggingEnabled) {
|
||||
$templateCache.removeAll();
|
||||
}
|
||||
})
|
||||
})
|
||||
.config([
|
||||
//This ensures that all of our angular views are cache busted, if the path starts with views/ and ends with .html, then
|
||||
// we will append the cache busting value to it. This way all upgraded sites will not have to worry about browser cache.
|
||||
"$provide", function($provide) {
|
||||
return $provide.decorator("$http", [
|
||||
"$delegate", function($delegate) {
|
||||
var get = $delegate.get;
|
||||
$delegate.get = function (url, config) {
|
||||
|
||||
if (Umbraco.Sys.ServerVariables.application && url.startsWith("views/") && url.endsWith(".html")) {
|
||||
var rnd = Umbraco.Sys.ServerVariables.application.version + "." + Umbraco.Sys.ServerVariables.application.cdf;
|
||||
var _op = (url.indexOf("?") > 0) ? "&" : "?";
|
||||
url += _op + "umb__rnd=" + rnd;
|
||||
}
|
||||
|
||||
return get(url, config);
|
||||
};
|
||||
return $delegate;
|
||||
}
|
||||
]);
|
||||
}
|
||||
]);
|
||||
|
||||
//Call a document callback if defined, this is sort of a dodgy hack to
|
||||
// be able to configure angular values in the Default.cshtml
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
if(convert){
|
||||
element.html("<i class='icon-section " + convert + "'></i>");
|
||||
}else{
|
||||
element.html("<img src='images/tray/" + icon + "'>");
|
||||
element.html("<img class='icon-section' src='images/tray/" + icon + "'>");
|
||||
}
|
||||
//it's a file, normally legacy so look in the icon tray images
|
||||
}
|
||||
|
||||
+8
-1
@@ -1,10 +1,13 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function EditorHeaderDirective(iconHelper) {
|
||||
function EditorHeaderDirective(iconHelper, variationsHelper) {
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
scope.showVariationsQuickSwitch = false;
|
||||
scope.variations = variationsHelper.getVariations();
|
||||
|
||||
scope.openIconPicker = function() {
|
||||
scope.dialogModel = {
|
||||
view: "iconpicker",
|
||||
@@ -24,6 +27,10 @@
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
scope.toggleVariationsQuickSwitch = function() {
|
||||
scope.showVariationsQuickSwitch = !scope.showVariationsQuickSwitch;
|
||||
};
|
||||
}
|
||||
|
||||
var directive = {
|
||||
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function WebsiteSelector() {
|
||||
|
||||
function link(scope, el, attr, ctrl) {
|
||||
|
||||
scope.showWebsiteDropdown = false;
|
||||
scope.selectedWebsite = {};
|
||||
|
||||
function activate() {
|
||||
for(var i = 0; i < scope.websites.length; i++) {
|
||||
var website = scope.websites[i];
|
||||
if(website.selected) {
|
||||
scope.selectedWebsite = website;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scope.toggleWebsiteDropDown = function() {
|
||||
scope.showWebsiteDropdown = !scope.showWebsiteDropdown;
|
||||
};
|
||||
|
||||
scope.closeWebsiteDropDown = function() {
|
||||
scope.showWebsiteDropdown = false;
|
||||
};
|
||||
|
||||
scope.createWebsite = function() {
|
||||
if(scope.onCreateWebsite) {
|
||||
scope.onCreateWebsite();
|
||||
scope.closeWebsiteDropDown();
|
||||
}
|
||||
};
|
||||
|
||||
scope.clickWebsite = function(website) {
|
||||
if(scope.onClickWebsite) {
|
||||
scope.onClickWebsite(website);
|
||||
setSelectedWebsite(website, scope.websites);
|
||||
scope.closeWebsiteDropDown();
|
||||
}
|
||||
};
|
||||
|
||||
function setSelectedWebsite(selectedWebsite, websites) {
|
||||
for(var i = 0; i < websites.length; i++) {
|
||||
var website = websites[i];
|
||||
website.selected = false;
|
||||
}
|
||||
selectedWebsite.selected = true;
|
||||
scope.selectedWebsite = selectedWebsite;
|
||||
}
|
||||
|
||||
scope.$watch('websites', function(newValue, oldValue){
|
||||
activate();
|
||||
}, true);
|
||||
|
||||
|
||||
}
|
||||
|
||||
var directive = {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
templateUrl: 'views/components/umb-website-selector.html',
|
||||
link: link,
|
||||
scope: {
|
||||
websites: "=",
|
||||
onCreateWebsite: "=",
|
||||
onClickWebsite: "="
|
||||
}
|
||||
};
|
||||
|
||||
return directive;
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').directive('umbWebsiteSelector', WebsiteSelector);
|
||||
|
||||
})();
|
||||
@@ -509,6 +509,16 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
|
||||
'Failed to check permission for item ' + id);
|
||||
},
|
||||
|
||||
getPermissions: function (nodeIds) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"contentApiBaseUrl",
|
||||
"GetPermissions"),
|
||||
nodeIds),
|
||||
'Failed to get permissions');
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.contentResource#save
|
||||
|
||||
@@ -29,7 +29,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
filterContentTypes: filterContentTypes,
|
||||
filterPropertyTypes: filterPropertyTypes
|
||||
};
|
||||
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(
|
||||
umbRequestHelper.getApiUrl(
|
||||
@@ -201,9 +201,9 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
* .then(function() {
|
||||
* alert("node was moved");
|
||||
* }, function(err){
|
||||
* alert("node didnt move:" + err.data.Message);
|
||||
* alert("node didnt move:" + err.data.Message);
|
||||
* });
|
||||
* </pre>
|
||||
* </pre>
|
||||
* @param {Object} args arguments object
|
||||
* @param {Int} args.idd the ID of the node to move
|
||||
* @param {Int} args.parentId the ID of the parent node to move to
|
||||
@@ -230,6 +230,26 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
'Failed to move content');
|
||||
},
|
||||
|
||||
copy: function(args) {
|
||||
if (!args) {
|
||||
throw "args cannot be null";
|
||||
}
|
||||
if (!args.parentId) {
|
||||
throw "args.parentId cannot be null";
|
||||
}
|
||||
if (!args.id) {
|
||||
throw "args.id cannot be null";
|
||||
}
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCopy"),
|
||||
{
|
||||
parentId: args.parentId,
|
||||
id: args.id
|
||||
}),
|
||||
'Failed to copy content');
|
||||
},
|
||||
|
||||
createContainer: function(parentId, name) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
@@ -237,7 +257,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
'Failed to create a folder under parent id ' + parentId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.resources').factory('contentTypeResource', contentTypeResource);
|
||||
|
||||
@@ -53,7 +53,7 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
* .then(function(array) {
|
||||
* $scope.type = type;
|
||||
* });
|
||||
* </pre>
|
||||
* </pre>
|
||||
* @param {Int} mediaId id of the media item to retrive allowed child types for
|
||||
* @returns {Promise} resourcePromise object.
|
||||
*
|
||||
@@ -145,9 +145,9 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
* .then(function() {
|
||||
* alert("node was moved");
|
||||
* }, function(err){
|
||||
* alert("node didnt move:" + err.data.Message);
|
||||
* alert("node didnt move:" + err.data.Message);
|
||||
* });
|
||||
* </pre>
|
||||
* </pre>
|
||||
* @param {Object} args arguments object
|
||||
* @param {Int} args.idd the ID of the node to move
|
||||
* @param {Int} args.parentId the ID of the parent node to move to
|
||||
@@ -174,6 +174,26 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
|
||||
'Failed to move content');
|
||||
},
|
||||
|
||||
copy: function (args) {
|
||||
if (!args) {
|
||||
throw "args cannot be null";
|
||||
}
|
||||
if (!args.parentId) {
|
||||
throw "args.parentId cannot be null";
|
||||
}
|
||||
if (!args.id) {
|
||||
throw "args.id cannot be null";
|
||||
}
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostCopy"),
|
||||
{
|
||||
parentId: args.parentId,
|
||||
id: args.id
|
||||
}),
|
||||
'Failed to copy content');
|
||||
},
|
||||
|
||||
createContainer: function(parentId, name) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
|
||||
@@ -16,7 +16,7 @@ function treeResource($q, $http, umbRequestHelper) {
|
||||
/** internal method to get the tree menu url */
|
||||
function getTreeMenuUrl(node) {
|
||||
if (!node.menuUrl) {
|
||||
throw "No menuUrl property found on the tree node, cannot load menu";
|
||||
return null;
|
||||
}
|
||||
return node.menuUrl;
|
||||
}
|
||||
@@ -26,10 +26,16 @@ function treeResource($q, $http, umbRequestHelper) {
|
||||
|
||||
/** Loads in the data to display the nodes menu */
|
||||
loadMenu: function (node) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(getTreeMenuUrl(node)),
|
||||
"Failed to retrieve data for a node's menu " + node.id);
|
||||
var treeMenuUrl = getTreeMenuUrl(node);
|
||||
if (treeMenuUrl !== undefined && treeMenuUrl !== null && treeMenuUrl.length > 0) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(getTreeMenuUrl(node)),
|
||||
"Failed to retrieve data for a node's menu " + node.id);
|
||||
} else {
|
||||
return $q.reject({
|
||||
errorMsg: "No tree menu url defined for node " + node.id
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/** Loads in the data to display the nodes for an application */
|
||||
|
||||
@@ -1,270 +1,323 @@
|
||||
// This service was based on OpenJS library available in BSD License
|
||||
// http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php
|
||||
angular.module('umbraco.services')
|
||||
.factory('keyboardService', ['$window', '$timeout', function ($window, $timeout) {
|
||||
var keyboardManagerService = {};
|
||||
var defaultOpt = {
|
||||
'type': 'keydown',
|
||||
'propagate': false,
|
||||
'inputDisabled': false,
|
||||
'target': $window.document,
|
||||
'keyCode': false
|
||||
};
|
||||
|
||||
var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0;
|
||||
function keyboardService($window, $timeout) {
|
||||
|
||||
var keyboardManagerService = {};
|
||||
|
||||
var defaultOpt = {
|
||||
'type': 'keydown',
|
||||
'propagate': false,
|
||||
'inputDisabled': false,
|
||||
'target': $window.document,
|
||||
'keyCode': false
|
||||
};
|
||||
|
||||
// Store all keyboard combination shortcuts
|
||||
keyboardManagerService.keyboardEvent = {};
|
||||
// Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
|
||||
var shift_nums = {
|
||||
"`": "~",
|
||||
"1": "!",
|
||||
"2": "@",
|
||||
"3": "#",
|
||||
"4": "$",
|
||||
"5": "%",
|
||||
"6": "^",
|
||||
"7": "&",
|
||||
"8": "*",
|
||||
"9": "(",
|
||||
"0": ")",
|
||||
"-": "_",
|
||||
"=": "+",
|
||||
";": ":",
|
||||
"'": "\"",
|
||||
",": "<",
|
||||
".": ">",
|
||||
"/": "?",
|
||||
"\\": "|"
|
||||
};
|
||||
|
||||
// Special Keys - and their codes
|
||||
var special_keys = {
|
||||
'esc': 27,
|
||||
'escape': 27,
|
||||
'tab': 9,
|
||||
'space': 32,
|
||||
'return': 13,
|
||||
'enter': 13,
|
||||
'backspace': 8,
|
||||
|
||||
// Add a new keyboard combination shortcut
|
||||
keyboardManagerService.bind = function (label, callback, opt) {
|
||||
'scrolllock': 145,
|
||||
'scroll_lock': 145,
|
||||
'scroll': 145,
|
||||
'capslock': 20,
|
||||
'caps_lock': 20,
|
||||
'caps': 20,
|
||||
'numlock': 144,
|
||||
'num_lock': 144,
|
||||
'num': 144,
|
||||
|
||||
//replace ctrl key with meta key
|
||||
if(isMac && label !== "ctrl+space"){
|
||||
label = label.replace("ctrl","meta");
|
||||
}
|
||||
'pause': 19,
|
||||
'break': 19,
|
||||
|
||||
var fct, elt, code, k;
|
||||
// Initialize opt object
|
||||
opt = angular.extend({}, defaultOpt, opt);
|
||||
label = label.toLowerCase();
|
||||
elt = opt.target;
|
||||
if(typeof opt.target === 'string'){
|
||||
elt = document.getElementById(opt.target);
|
||||
}
|
||||
'insert': 45,
|
||||
'home': 36,
|
||||
'delete': 46,
|
||||
'end': 35,
|
||||
|
||||
'pageup': 33,
|
||||
'page_up': 33,
|
||||
'pu': 33,
|
||||
|
||||
fct = function (e) {
|
||||
e = e || $window.event;
|
||||
'pagedown': 34,
|
||||
'page_down': 34,
|
||||
'pd': 34,
|
||||
|
||||
// Disable event handler when focus input and textarea
|
||||
if (opt['inputDisabled']) {
|
||||
var elt;
|
||||
if (e.target){
|
||||
elt = e.target;
|
||||
}else if (e.srcElement){
|
||||
elt = e.srcElement;
|
||||
}
|
||||
'left': 37,
|
||||
'up': 38,
|
||||
'right': 39,
|
||||
'down': 40,
|
||||
|
||||
if (elt.nodeType === 3){elt = elt.parentNode;}
|
||||
if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA'){return;}
|
||||
}
|
||||
'f1': 112,
|
||||
'f2': 113,
|
||||
'f3': 114,
|
||||
'f4': 115,
|
||||
'f5': 116,
|
||||
'f6': 117,
|
||||
'f7': 118,
|
||||
'f8': 119,
|
||||
'f9': 120,
|
||||
'f10': 121,
|
||||
'f11': 122,
|
||||
'f12': 123
|
||||
};
|
||||
|
||||
// Find out which key is pressed
|
||||
if (e.keyCode){
|
||||
code = e.keyCode;
|
||||
}else if (e.which){
|
||||
code = e.which;
|
||||
}
|
||||
var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0;
|
||||
|
||||
var character = String.fromCharCode(code).toLowerCase();
|
||||
// The event handler for bound element events
|
||||
function eventHandler(e) {
|
||||
e = e || $window.event;
|
||||
|
||||
if (code === 188){character = ",";} // If the user presses , when the type is onkeydown
|
||||
if (code === 190){character = ".";} // If the user presses , when the type is onkeydown
|
||||
var code, k;
|
||||
|
||||
var keys = label.split("+");
|
||||
// Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
|
||||
var kp = 0;
|
||||
// Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
|
||||
var shift_nums = {
|
||||
"`":"~",
|
||||
"1":"!",
|
||||
"2":"@",
|
||||
"3":"#",
|
||||
"4":"$",
|
||||
"5":"%",
|
||||
"6":"^",
|
||||
"7":"&",
|
||||
"8":"*",
|
||||
"9":"(",
|
||||
"0":")",
|
||||
"-":"_",
|
||||
"=":"+",
|
||||
";":":",
|
||||
"'":"\"",
|
||||
",":"<",
|
||||
".":">",
|
||||
"/":"?",
|
||||
"\\":"|"
|
||||
};
|
||||
// Special Keys - and their codes
|
||||
var special_keys = {
|
||||
'esc':27,
|
||||
'escape':27,
|
||||
'tab':9,
|
||||
'space':32,
|
||||
'return':13,
|
||||
'enter':13,
|
||||
'backspace':8,
|
||||
// Find out which key is pressed
|
||||
if (e.keyCode)
|
||||
{
|
||||
code = e.keyCode;
|
||||
}
|
||||
else if (e.which) {
|
||||
code = e.which;
|
||||
}
|
||||
|
||||
'scrolllock':145,
|
||||
'scroll_lock':145,
|
||||
'scroll':145,
|
||||
'capslock':20,
|
||||
'caps_lock':20,
|
||||
'caps':20,
|
||||
'numlock':144,
|
||||
'num_lock':144,
|
||||
'num':144,
|
||||
var character = String.fromCharCode(code).toLowerCase();
|
||||
|
||||
'pause':19,
|
||||
'break':19,
|
||||
if (code === 188){character = ",";} // If the user presses , when the type is onkeydown
|
||||
if (code === 190){character = ".";} // If the user presses , when the type is onkeydown
|
||||
|
||||
'insert':45,
|
||||
'home':36,
|
||||
'delete':46,
|
||||
'end':35,
|
||||
var propagate = true;
|
||||
|
||||
'pageup':33,
|
||||
'page_up':33,
|
||||
'pu':33,
|
||||
//Now we need to determine which shortcut this event is for, we'll do this by iterating over each
|
||||
//registered shortcut to find the match. We use Find here so that the loop exits as soon
|
||||
//as we've found the one we're looking for
|
||||
_.find(_.keys(keyboardManagerService.keyboardEvent), function(key) {
|
||||
|
||||
'pagedown':34,
|
||||
'page_down':34,
|
||||
'pd':34,
|
||||
var shortcutLabel = key;
|
||||
var shortcutVal = keyboardManagerService.keyboardEvent[key];
|
||||
|
||||
'left':37,
|
||||
'up':38,
|
||||
'right':39,
|
||||
'down':40,
|
||||
// Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
|
||||
var kp = 0;
|
||||
|
||||
'f1':112,
|
||||
'f2':113,
|
||||
'f3':114,
|
||||
'f4':115,
|
||||
'f5':116,
|
||||
'f6':117,
|
||||
'f7':118,
|
||||
'f8':119,
|
||||
'f9':120,
|
||||
'f10':121,
|
||||
'f11':122,
|
||||
'f12':123
|
||||
};
|
||||
// Some modifiers key
|
||||
var modifiers = {
|
||||
shift: {
|
||||
wanted: false,
|
||||
pressed: e.shiftKey ? true : false
|
||||
},
|
||||
ctrl : {
|
||||
wanted: false,
|
||||
pressed: e.ctrlKey ? true : false
|
||||
},
|
||||
alt : {
|
||||
wanted: false,
|
||||
pressed: e.altKey ? true : false
|
||||
},
|
||||
meta : { //Meta is Mac specific
|
||||
wanted: false,
|
||||
pressed: e.metaKey ? true : false
|
||||
}
|
||||
};
|
||||
// Foreach keys in label (split on +)
|
||||
var l = keys.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
// Some modifiers key
|
||||
var modifiers = {
|
||||
shift: {
|
||||
wanted: false,
|
||||
pressed: e.shiftKey ? true : false
|
||||
},
|
||||
ctrl: {
|
||||
wanted: false,
|
||||
pressed: e.ctrlKey ? true : false
|
||||
},
|
||||
alt: {
|
||||
wanted: false,
|
||||
pressed: e.altKey ? true : false
|
||||
},
|
||||
meta: { //Meta is Mac specific
|
||||
wanted: false,
|
||||
pressed: e.metaKey ? true : false
|
||||
}
|
||||
};
|
||||
|
||||
var k=keys[i];
|
||||
switch (k) {
|
||||
case 'ctrl':
|
||||
case 'control':
|
||||
kp++;
|
||||
modifiers.ctrl.wanted = true;
|
||||
break;
|
||||
case 'shift':
|
||||
case 'alt':
|
||||
case 'meta':
|
||||
kp++;
|
||||
modifiers[k].wanted = true;
|
||||
break;
|
||||
}
|
||||
var keys = shortcutLabel.split("+");
|
||||
var opt = shortcutVal.opt;
|
||||
var callback = shortcutVal.callback;
|
||||
|
||||
if (k.length > 1) { // If it is a special key
|
||||
if(special_keys[k] === code){
|
||||
kp++;
|
||||
}
|
||||
// Foreach keys in label (split on +)
|
||||
var l = keys.length;
|
||||
for (var i = 0; i < l; i++) {
|
||||
|
||||
} else if (opt['keyCode']) { // If a specific key is set into the config
|
||||
if (opt['keyCode'] === code) {
|
||||
kp++;
|
||||
}
|
||||
var k = keys[i];
|
||||
switch (k) {
|
||||
case 'ctrl':
|
||||
case 'control':
|
||||
kp++;
|
||||
modifiers.ctrl.wanted = true;
|
||||
break;
|
||||
case 'shift':
|
||||
case 'alt':
|
||||
case 'meta':
|
||||
kp++;
|
||||
modifiers[k].wanted = true;
|
||||
break;
|
||||
}
|
||||
|
||||
} else { // The special keys did not match
|
||||
if(character === k) {
|
||||
kp++;
|
||||
}else {
|
||||
if(shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase
|
||||
character = shift_nums[character];
|
||||
if(character === k){
|
||||
kp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (k.length > 1) { // If it is a special key
|
||||
if (special_keys[k] === code) {
|
||||
kp++;
|
||||
}
|
||||
}
|
||||
else if (opt['keyCode']) { // If a specific key is set into the config
|
||||
if (opt['keyCode'] === code) {
|
||||
kp++;
|
||||
}
|
||||
}
|
||||
else { // The special keys did not match
|
||||
if (character === k) {
|
||||
kp++;
|
||||
}
|
||||
else {
|
||||
if (shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase
|
||||
character = shift_nums[character];
|
||||
if (character === k) {
|
||||
kp++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} //for end
|
||||
} //for end
|
||||
|
||||
if(kp === keys.length &&
|
||||
modifiers.ctrl.pressed === modifiers.ctrl.wanted &&
|
||||
modifiers.shift.pressed === modifiers.shift.wanted &&
|
||||
modifiers.alt.pressed === modifiers.alt.wanted &&
|
||||
modifiers.meta.pressed === modifiers.meta.wanted) {
|
||||
$timeout(function() {
|
||||
callback(e);
|
||||
}, 1);
|
||||
if (kp === keys.length &&
|
||||
modifiers.ctrl.pressed === modifiers.ctrl.wanted &&
|
||||
modifiers.shift.pressed === modifiers.shift.wanted &&
|
||||
modifiers.alt.pressed === modifiers.alt.wanted &&
|
||||
modifiers.meta.pressed === modifiers.meta.wanted) {
|
||||
|
||||
if(!opt['propagate']) { // Stop the event
|
||||
// e.cancelBubble is supported by IE - this will kill the bubbling process.
|
||||
e.cancelBubble = true;
|
||||
e.returnValue = false;
|
||||
//found the right callback!
|
||||
|
||||
// e.stopPropagation works in Firefox.
|
||||
if (e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
// Store shortcut
|
||||
keyboardManagerService.keyboardEvent[label] = {
|
||||
'callback': fct,
|
||||
'target': elt,
|
||||
'event': opt['type']
|
||||
};
|
||||
// Disable event handler when focus input and textarea
|
||||
if (opt['inputDisabled']) {
|
||||
var elt;
|
||||
if (e.target) {
|
||||
elt = e.target;
|
||||
} else if (e.srcElement) {
|
||||
elt = e.srcElement;
|
||||
}
|
||||
|
||||
//Attach the function with the event
|
||||
if(elt.addEventListener){
|
||||
elt.addEventListener(opt['type'], fct, false);
|
||||
}else if(elt.attachEvent){
|
||||
elt.attachEvent('on' + opt['type'], fct);
|
||||
}else{
|
||||
elt['on' + opt['type']] = fct;
|
||||
}
|
||||
};
|
||||
// Remove the shortcut - just specify the shortcut and I will remove the binding
|
||||
keyboardManagerService.unbind = function (label) {
|
||||
label = label.toLowerCase();
|
||||
var binding = keyboardManagerService.keyboardEvent[label];
|
||||
delete(keyboardManagerService.keyboardEvent[label]);
|
||||
if (elt.nodeType === 3) { elt = elt.parentNode; }
|
||||
if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA') {
|
||||
//This exits the Find loop
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!binding){return;}
|
||||
$timeout(function () {
|
||||
callback(e);
|
||||
}, 1);
|
||||
|
||||
var type = binding['event'],
|
||||
if (!opt['propagate']) { // Stop the event
|
||||
propagate = false;
|
||||
}
|
||||
|
||||
//This exits the Find loop
|
||||
return true;
|
||||
}
|
||||
|
||||
//we haven't found one so continue looking
|
||||
return false;
|
||||
|
||||
});
|
||||
|
||||
// Stop the event if required
|
||||
if (!propagate) {
|
||||
// e.cancelBubble is supported by IE - this will kill the bubbling process.
|
||||
e.cancelBubble = true;
|
||||
e.returnValue = false;
|
||||
|
||||
// e.stopPropagation works in Firefox.
|
||||
if (e.stopPropagation) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Store all keyboard combination shortcuts
|
||||
keyboardManagerService.keyboardEvent = {};
|
||||
|
||||
// Add a new keyboard combination shortcut
|
||||
keyboardManagerService.bind = function (label, callback, opt) {
|
||||
|
||||
//replace ctrl key with meta key
|
||||
if(isMac && label !== "ctrl+space"){
|
||||
label = label.replace("ctrl","meta");
|
||||
}
|
||||
|
||||
var elt;
|
||||
// Initialize opt object
|
||||
opt = angular.extend({}, defaultOpt, opt);
|
||||
label = label.toLowerCase();
|
||||
elt = opt.target;
|
||||
if(typeof opt.target === 'string'){
|
||||
elt = document.getElementById(opt.target);
|
||||
}
|
||||
|
||||
//Ensure we aren't double binding to the same element + type otherwise we'll end up multi-binding
|
||||
// and raising events for now reason. So here we'll check if the event is already registered for the element
|
||||
var boundValues = _.values(keyboardManagerService.keyboardEvent);
|
||||
var found = _.find(boundValues, function (i) {
|
||||
return i.target === elt && i.event === opt['type'];
|
||||
});
|
||||
|
||||
// Store shortcut
|
||||
keyboardManagerService.keyboardEvent[label] = {
|
||||
'callback': callback,
|
||||
'target': elt,
|
||||
'opt': opt['type']
|
||||
};
|
||||
|
||||
if (!found) {
|
||||
//Attach the function with the event
|
||||
if (elt.addEventListener) {
|
||||
elt.addEventListener(opt['type'], eventHandler, false);
|
||||
} else if (elt.attachEvent) {
|
||||
elt.attachEvent('on' + opt['type'], eventHandler);
|
||||
} else {
|
||||
elt['on' + opt['type']] = eventHandler;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
// Remove the shortcut - just specify the shortcut and I will remove the binding
|
||||
keyboardManagerService.unbind = function (label) {
|
||||
label = label.toLowerCase();
|
||||
var binding = keyboardManagerService.keyboardEvent[label];
|
||||
delete(keyboardManagerService.keyboardEvent[label]);
|
||||
|
||||
if(!binding){return;}
|
||||
|
||||
var type = binding['event'],
|
||||
elt = binding['target'],
|
||||
callback = binding['callback'];
|
||||
|
||||
if(elt.detachEvent){
|
||||
elt.detachEvent('on' + type, callback);
|
||||
}else if(elt.removeEventListener){
|
||||
elt.removeEventListener(type, callback, false);
|
||||
}else{
|
||||
elt['on'+type] = false;
|
||||
}
|
||||
};
|
||||
//
|
||||
if(elt.detachEvent){
|
||||
elt.detachEvent('on' + type, callback);
|
||||
}else if(elt.removeEventListener){
|
||||
elt.removeEventListener(type, callback, false);
|
||||
}else{
|
||||
elt['on'+type] = false;
|
||||
}
|
||||
};
|
||||
//
|
||||
|
||||
return keyboardManagerService;
|
||||
}]);
|
||||
return keyboardManagerService;
|
||||
}
|
||||
@@ -1,288 +1,319 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function listViewHelper(localStorageService) {
|
||||
function listViewHelper(localStorageService) {
|
||||
|
||||
var firstSelectedIndex = 0;
|
||||
var localStorageKey = "umblistViewLayout";
|
||||
var firstSelectedIndex = 0;
|
||||
var localStorageKey = "umblistViewLayout";
|
||||
|
||||
function getLayout(nodeId, availableLayouts) {
|
||||
function getLayout(nodeId, availableLayouts) {
|
||||
|
||||
var storedLayouts = [];
|
||||
var storedLayouts = [];
|
||||
|
||||
if(localStorageService.get(localStorageKey)) {
|
||||
storedLayouts = localStorageService.get(localStorageKey);
|
||||
}
|
||||
|
||||
if (storedLayouts && storedLayouts.length > 0) {
|
||||
for (var i = 0; storedLayouts.length > i; i++) {
|
||||
var layout = storedLayouts[i];
|
||||
if (layout.nodeId === nodeId) {
|
||||
return setLayout(nodeId, layout, availableLayouts);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return getFirstAllowedLayout(availableLayouts);
|
||||
|
||||
}
|
||||
|
||||
function setLayout(nodeId, selectedLayout, availableLayouts) {
|
||||
|
||||
var activeLayout = {};
|
||||
var layoutFound = false;
|
||||
|
||||
for (var i = 0; availableLayouts.length > i; i++) {
|
||||
var layout = availableLayouts[i];
|
||||
if (layout.path === selectedLayout.path) {
|
||||
activeLayout = layout;
|
||||
layout.active = true;
|
||||
layoutFound = true;
|
||||
} else {
|
||||
layout.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!layoutFound) {
|
||||
activeLayout = getFirstAllowedLayout(availableLayouts);
|
||||
}
|
||||
|
||||
saveLayoutInLocalStorage(nodeId, activeLayout);
|
||||
|
||||
return activeLayout;
|
||||
|
||||
}
|
||||
|
||||
function saveLayoutInLocalStorage(nodeId, selectedLayout) {
|
||||
var layoutFound = false;
|
||||
var storedLayouts = [];
|
||||
|
||||
if(localStorageService.get(localStorageKey)) {
|
||||
storedLayouts = localStorageService.get(localStorageKey);
|
||||
}
|
||||
|
||||
if(storedLayouts.length > 0) {
|
||||
for(var i = 0; storedLayouts.length > i; i++) {
|
||||
var layout = storedLayouts[i];
|
||||
if(layout.nodeId === nodeId) {
|
||||
layout.path = selectedLayout.path;
|
||||
layoutFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!layoutFound) {
|
||||
var storageObject = {
|
||||
"nodeId": nodeId,
|
||||
"path": selectedLayout.path
|
||||
};
|
||||
storedLayouts.push(storageObject);
|
||||
}
|
||||
|
||||
localStorageService.set(localStorageKey, storedLayouts);
|
||||
|
||||
}
|
||||
|
||||
function getFirstAllowedLayout(layouts) {
|
||||
|
||||
var firstAllowedLayout = {};
|
||||
|
||||
for (var i = 0; layouts.length > i; i++) {
|
||||
var layout = layouts[i];
|
||||
if (layout.selected === true) {
|
||||
firstAllowedLayout = layout;
|
||||
break;
|
||||
if (localStorageService.get(localStorageKey)) {
|
||||
storedLayouts = localStorageService.get(localStorageKey);
|
||||
}
|
||||
}
|
||||
|
||||
return firstAllowedLayout;
|
||||
}
|
||||
if (storedLayouts && storedLayouts.length > 0) {
|
||||
for (var i = 0; storedLayouts.length > i; i++) {
|
||||
var layout = storedLayouts[i];
|
||||
if (layout.nodeId === nodeId) {
|
||||
return setLayout(nodeId, layout, availableLayouts);
|
||||
}
|
||||
}
|
||||
|
||||
function selectHandler(selectedItem, selectedIndex, items, selection, $event) {
|
||||
}
|
||||
|
||||
var start = 0;
|
||||
var end = 0;
|
||||
var item = null;
|
||||
return getFirstAllowedLayout(availableLayouts);
|
||||
|
||||
if ($event.shiftKey === true) {
|
||||
}
|
||||
|
||||
if(selectedIndex > firstSelectedIndex) {
|
||||
function setLayout(nodeId, selectedLayout, availableLayouts) {
|
||||
|
||||
start = firstSelectedIndex;
|
||||
end = selectedIndex;
|
||||
var activeLayout = {};
|
||||
var layoutFound = false;
|
||||
|
||||
for (; end >= start; start++) {
|
||||
item = items[start];
|
||||
selectItem(item, selection);
|
||||
}
|
||||
for (var i = 0; availableLayouts.length > i; i++) {
|
||||
var layout = availableLayouts[i];
|
||||
if (layout.path === selectedLayout.path) {
|
||||
activeLayout = layout;
|
||||
layout.active = true;
|
||||
layoutFound = true;
|
||||
} else {
|
||||
layout.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!layoutFound) {
|
||||
activeLayout = getFirstAllowedLayout(availableLayouts);
|
||||
}
|
||||
|
||||
saveLayoutInLocalStorage(nodeId, activeLayout);
|
||||
|
||||
return activeLayout;
|
||||
|
||||
}
|
||||
|
||||
function saveLayoutInLocalStorage(nodeId, selectedLayout) {
|
||||
var layoutFound = false;
|
||||
var storedLayouts = [];
|
||||
|
||||
if (localStorageService.get(localStorageKey)) {
|
||||
storedLayouts = localStorageService.get(localStorageKey);
|
||||
}
|
||||
|
||||
if (storedLayouts.length > 0) {
|
||||
for (var i = 0; storedLayouts.length > i; i++) {
|
||||
var layout = storedLayouts[i];
|
||||
if (layout.nodeId === nodeId) {
|
||||
layout.path = selectedLayout.path;
|
||||
layoutFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!layoutFound) {
|
||||
var storageObject = {
|
||||
"nodeId": nodeId,
|
||||
"path": selectedLayout.path
|
||||
};
|
||||
storedLayouts.push(storageObject);
|
||||
}
|
||||
|
||||
localStorageService.set(localStorageKey, storedLayouts);
|
||||
|
||||
}
|
||||
|
||||
function getFirstAllowedLayout(layouts) {
|
||||
|
||||
var firstAllowedLayout = {};
|
||||
|
||||
for (var i = 0; layouts.length > i; i++) {
|
||||
var layout = layouts[i];
|
||||
if (layout.selected === true) {
|
||||
firstAllowedLayout = layout;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return firstAllowedLayout;
|
||||
}
|
||||
|
||||
function selectHandler(selectedItem, selectedIndex, items, selection, $event) {
|
||||
|
||||
var start = 0;
|
||||
var end = 0;
|
||||
var item = null;
|
||||
|
||||
if ($event.shiftKey === true) {
|
||||
|
||||
if (selectedIndex > firstSelectedIndex) {
|
||||
|
||||
start = firstSelectedIndex;
|
||||
end = selectedIndex;
|
||||
|
||||
for (; end >= start; start++) {
|
||||
item = items[start];
|
||||
selectItem(item, selection);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
start = firstSelectedIndex;
|
||||
end = selectedIndex;
|
||||
|
||||
for (; end <= start; start--) {
|
||||
item = items[start];
|
||||
selectItem(item, selection);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
start = firstSelectedIndex;
|
||||
end = selectedIndex;
|
||||
if (selectedItem.selected) {
|
||||
deselectItem(selectedItem, selection);
|
||||
} else {
|
||||
selectItem(selectedItem, selection);
|
||||
}
|
||||
|
||||
for (; end <= start; start--) {
|
||||
item = items[start];
|
||||
selectItem(item, selection);
|
||||
}
|
||||
firstSelectedIndex = selectedIndex;
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
}
|
||||
|
||||
if(selectedItem.selected) {
|
||||
deselectItem(selectedItem, selection);
|
||||
} else {
|
||||
selectItem(selectedItem, selection);
|
||||
function selectItem(item, selection) {
|
||||
var isSelected = false;
|
||||
for (var i = 0; selection.length > i; i++) {
|
||||
var selectedItem = selection[i];
|
||||
if (item.id === selectedItem.id) {
|
||||
isSelected = true;
|
||||
}
|
||||
}
|
||||
if (!isSelected) {
|
||||
selection.push({ id: item.id });
|
||||
item.selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
function deselectItem(item, selection) {
|
||||
for (var i = 0; selection.length > i; i++) {
|
||||
var selectedItem = selection[i];
|
||||
if (item.id === selectedItem.id) {
|
||||
selection.splice(i, 1);
|
||||
item.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearSelection(items, folders, selection) {
|
||||
|
||||
var i = 0;
|
||||
|
||||
selection.length = 0;
|
||||
|
||||
if (angular.isArray(items)) {
|
||||
for (i = 0; items.length > i; i++) {
|
||||
var item = items[i];
|
||||
item.selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
firstSelectedIndex = selectedIndex;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function selectItem(item, selection) {
|
||||
var isSelected = false;
|
||||
for (var i = 0; selection.length > i; i++) {
|
||||
var selectedItem = selection[i];
|
||||
if (item.id === selectedItem.id) {
|
||||
isSelected = true;
|
||||
if (angular.isArray(items)) {
|
||||
for (i = 0; folders.length > i; i++) {
|
||||
var folder = folders[i];
|
||||
folder.selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!isSelected) {
|
||||
selection.push({id: item.id});
|
||||
item.selected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function deselectItem(item, selection) {
|
||||
for (var i = 0; selection.length > i; i++) {
|
||||
var selectedItem = selection[i];
|
||||
if (item.id === selectedItem.id) {
|
||||
selection.splice(i, 1);
|
||||
item.selected = false;
|
||||
function selectAllItems(items, selection, $event) {
|
||||
|
||||
var checkbox = $event.target;
|
||||
var clearSelection = false;
|
||||
|
||||
if (!angular.isArray(items)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function clearSelection(items, folders, selection) {
|
||||
selection.length = 0;
|
||||
|
||||
var i = 0;
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
|
||||
selection.length = 0;
|
||||
var item = items[i];
|
||||
|
||||
if (checkbox.checked) {
|
||||
selection.push({ id: item.id });
|
||||
} else {
|
||||
clearSelection = true;
|
||||
}
|
||||
|
||||
item.selected = checkbox.checked;
|
||||
|
||||
if(angular.isArray(items)) {
|
||||
for(i = 0; items.length > i; i++) {
|
||||
var item = items[i];
|
||||
item.selected = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(angular.isArray(items)) {
|
||||
for(i = 0; folders.length > i; i++) {
|
||||
var folder = folders[i];
|
||||
folder.selected = false;
|
||||
if (clearSelection) {
|
||||
selection.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function selectAllItems(items, selection, $event) {
|
||||
}
|
||||
|
||||
var checkbox = $event.target;
|
||||
var clearSelection = false;
|
||||
function isSelectedAll(items, selection) {
|
||||
|
||||
if (!angular.isArray(items)) {
|
||||
return;
|
||||
}
|
||||
var numberOfSelectedItem = 0;
|
||||
|
||||
selection.length = 0;
|
||||
for (var itemIndex = 0; items.length > itemIndex; itemIndex++) {
|
||||
var item = items[itemIndex];
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
for (var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) {
|
||||
var selectedItem = selection[selectedIndex];
|
||||
|
||||
var item = items[i];
|
||||
if (item.id === selectedItem.id) {
|
||||
numberOfSelectedItem++;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkbox.checked) {
|
||||
selection.push({id: item.id});
|
||||
} else {
|
||||
clearSelection = true;
|
||||
}
|
||||
}
|
||||
|
||||
item.selected = checkbox.checked;
|
||||
if (numberOfSelectedItem === items.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (clearSelection) {
|
||||
selection.length = 0;
|
||||
}
|
||||
function setSortingDirection(col, direction, options) {
|
||||
return options.orderBy.toUpperCase() === col.toUpperCase() && options.orderDirection === direction;
|
||||
}
|
||||
|
||||
}
|
||||
function setSorting(field, allow, options) {
|
||||
if (allow) {
|
||||
options.orderBy = field;
|
||||
|
||||
function isSelectedAll(items, selection) {
|
||||
if (options.orderDirection === "desc") {
|
||||
options.orderDirection = "asc";
|
||||
} else {
|
||||
options.orderDirection = "desc";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//This takes in a dictionary of Ids with Permissions and determines
|
||||
// the intersect of all permissions to return an object representing the
|
||||
// listview button permissions
|
||||
function getButtonPermissions(unmergedPermissions, currentIdsWithPermissions) {
|
||||
|
||||
if (currentIdsWithPermissions == null) {
|
||||
currentIdsWithPermissions = {};
|
||||
}
|
||||
|
||||
var numberOfSelectedItem = 0;
|
||||
//merge the newly retrieved permissions to the main dictionary
|
||||
_.each(unmergedPermissions, function (value, key, list) {
|
||||
currentIdsWithPermissions[key] = value;
|
||||
});
|
||||
|
||||
for(var itemIndex = 0; items.length > itemIndex; itemIndex++) {
|
||||
var item = items[itemIndex];
|
||||
//get the intersect permissions
|
||||
var arr = [];
|
||||
_.each(currentIdsWithPermissions, function (value, key, list) {
|
||||
arr.push(value);
|
||||
});
|
||||
|
||||
for(var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) {
|
||||
var selectedItem = selection[selectedIndex];
|
||||
//we need to use 'apply' to call intersection with an array of arrays,
|
||||
//see: http://stackoverflow.com/a/16229480/694494
|
||||
var intersectPermissions = _.intersection.apply(_, arr);
|
||||
|
||||
if(item.id === selectedItem.id) {
|
||||
numberOfSelectedItem++;
|
||||
}
|
||||
}
|
||||
return {
|
||||
canCopy: _.contains(intersectPermissions, 'O'), //Magic Char = O
|
||||
canCreate: _.contains(intersectPermissions, 'C'), //Magic Char = C
|
||||
canDelete: _.contains(intersectPermissions, 'D'), //Magic Char = D
|
||||
canMove: _.contains(intersectPermissions, 'M'), //Magic Char = M
|
||||
canPublish: _.contains(intersectPermissions, 'U'), //Magic Char = U
|
||||
canUnpublish: _.contains(intersectPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish)
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
var service = {
|
||||
getLayout: getLayout,
|
||||
getFirstAllowedLayout: getFirstAllowedLayout,
|
||||
setLayout: setLayout,
|
||||
saveLayoutInLocalStorage: saveLayoutInLocalStorage,
|
||||
selectHandler: selectHandler,
|
||||
selectItem: selectItem,
|
||||
deselectItem: deselectItem,
|
||||
clearSelection: clearSelection,
|
||||
selectAllItems: selectAllItems,
|
||||
isSelectedAll: isSelectedAll,
|
||||
setSortingDirection: setSortingDirection,
|
||||
setSorting: setSorting,
|
||||
getButtonPermissions: getButtonPermissions
|
||||
};
|
||||
|
||||
if(numberOfSelectedItem === items.length) {
|
||||
return true;
|
||||
}
|
||||
return service;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setSortingDirection(col, direction, options) {
|
||||
return options.orderBy.toUpperCase() === col.toUpperCase() && options.orderDirection === direction;
|
||||
}
|
||||
|
||||
|
||||
function setSorting(field, allow, options) {
|
||||
if (allow) {
|
||||
options.orderBy = field;
|
||||
|
||||
if (options.orderDirection === "desc") {
|
||||
options.orderDirection = "asc";
|
||||
} else {
|
||||
options.orderDirection = "desc";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
var service = {
|
||||
getLayout: getLayout,
|
||||
getFirstAllowedLayout: getFirstAllowedLayout,
|
||||
setLayout: setLayout,
|
||||
saveLayoutInLocalStorage: saveLayoutInLocalStorage,
|
||||
selectHandler: selectHandler,
|
||||
selectItem: selectItem,
|
||||
deselectItem: deselectItem,
|
||||
clearSelection: clearSelection,
|
||||
selectAllItems: selectAllItems,
|
||||
isSelectedAll: isSelectedAll,
|
||||
setSortingDirection: setSortingDirection,
|
||||
setSorting: setSorting
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
}
|
||||
|
||||
|
||||
angular.module('umbraco.services').factory('listViewHelper', listViewHelper);
|
||||
angular.module('umbraco.services').factory('listViewHelper', listViewHelper);
|
||||
|
||||
|
||||
})();
|
||||
|
||||
@@ -21,6 +21,7 @@ function packageHelper(assetsService, treeService, eventsService, $templateCache
|
||||
}
|
||||
angular.module('umbraco.services').factory('packageHelper', packageHelper);
|
||||
|
||||
//TODO: I believe this is obsolete
|
||||
function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, mediaHelper, umbRequestHelper) {
|
||||
return {
|
||||
/** sets the image's url, thumbnail and if its a folder */
|
||||
@@ -319,7 +320,6 @@ function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, me
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular.module("umbraco.services").factory("umbPhotoFolderHelper", umbPhotoFolderHelper);
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
function variationsHelper() {
|
||||
|
||||
var variations = [];
|
||||
|
||||
function setMaster(master) {
|
||||
master.master = true;
|
||||
variations.push(master);
|
||||
}
|
||||
|
||||
function getVariations() {
|
||||
return variations;
|
||||
}
|
||||
|
||||
function createVariation(variation) {
|
||||
var newVariation = angular.copy(variation);
|
||||
newVariation.master = false;
|
||||
variations.push(newVariation);
|
||||
}
|
||||
|
||||
function saveVariation(updatedVariation) {
|
||||
|
||||
for(var i = 0; i < variations.length; i++) {
|
||||
|
||||
var variation = variations[i];
|
||||
|
||||
if(variation.id === updatedVariation) {
|
||||
variation.name = updatedVariation.name;
|
||||
variation.description = updatedVariation.description;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function cloneVariation(variation) {
|
||||
var variationClone = angular.copy(variation);
|
||||
variationClone.master = false;
|
||||
variationClone.published = false;
|
||||
variations.push(variationClone);
|
||||
console.log(variations);
|
||||
}
|
||||
|
||||
function deleteVariation(variation, event, index) {
|
||||
variations.splice(index, 1);
|
||||
}
|
||||
|
||||
var service = {
|
||||
setMaster: setMaster,
|
||||
getVariations: getVariations,
|
||||
createVariation: createVariation,
|
||||
saveVariation: saveVariation,
|
||||
cloneVariation: cloneVariation,
|
||||
deleteVariation: deleteVariation
|
||||
};
|
||||
|
||||
return service;
|
||||
|
||||
}
|
||||
|
||||
|
||||
angular.module('umbraco.services').factory('variationsHelper', variationsHelper);
|
||||
|
||||
})();
|
||||
@@ -155,6 +155,53 @@ function NavigationController($scope, $rootScope, $location, $log, $routeParams,
|
||||
}
|
||||
};
|
||||
|
||||
$scope.websites = [
|
||||
{
|
||||
name: "English",
|
||||
language: "en-UK",
|
||||
selected: true
|
||||
},
|
||||
{
|
||||
name: "Dansk",
|
||||
language: "da-DK",
|
||||
selected: false
|
||||
}
|
||||
];
|
||||
|
||||
$scope.createWebsite = function() {
|
||||
|
||||
$scope.createWebsiteOverlay = {
|
||||
view: "createwebsite",
|
||||
title: "Create website",
|
||||
submitButtonLabel: "Create website",
|
||||
show: true,
|
||||
submit: function(model) {
|
||||
|
||||
// deselct all current sites
|
||||
for(var i = 0; i < $scope.websites.length; i++) {
|
||||
var website = $scope.websites[i];
|
||||
website.selected = false;
|
||||
}
|
||||
|
||||
// set new site to selected and push to scope
|
||||
model.website.selected = true;
|
||||
$scope.websites.push(model.website);
|
||||
|
||||
// clsoe overlay
|
||||
$scope.createWebsiteOverlay.show = false;
|
||||
$scope.createWebsiteOverlay = null;
|
||||
},
|
||||
close: function() {
|
||||
$scope.createWebsiteOverlay.show = false;
|
||||
$scope.createWebsiteOverlay = null;
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
$scope.clickWebsite = function(website) {
|
||||
};
|
||||
|
||||
//ensure to unregister from all events!
|
||||
$scope.$on('$destroy', function () {
|
||||
for (var e in evts) {
|
||||
|
||||
@@ -108,7 +108,8 @@
|
||||
@import "components/umb-empty-state.less";
|
||||
@import "components/umb-property-editor.less";
|
||||
@import "components/umb-iconpicker.less";
|
||||
|
||||
@import "components/umb-website-selector.less";
|
||||
@import "components/umb-dots.less";
|
||||
@import "components/buttons/umb-button.less";
|
||||
@import "components/buttons/umb-button-group.less";
|
||||
|
||||
|
||||
@@ -152,6 +152,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- OVERLAY TREE ---------- */
|
||||
.umb-overlay.umb-overlay-tree {
|
||||
position: absolute;
|
||||
width: 500px;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
bottom: 0;
|
||||
border: none;
|
||||
//box-shadow: 0 0 20px rgba(0,0,0,0.19), 0 0 6px rgba(0,0,0,0.23);
|
||||
box-shadow: 3px 0 7px #dbdbdb;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.umb-overlay.umb-overlay-tree .umb-overlay-header {
|
||||
flex-basis: 100px;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.umb-overlay.umb-overlay-tree .umb-overlay-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
|
||||
/* ---------- OVERLAY ITEM DETAILS ---------- */
|
||||
.umb-overlay__item-details {
|
||||
position: absolute;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
.umb-dots {
|
||||
position: relative;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background-color: @gray;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.umb-dots:before,
|
||||
.umb-dots:after {
|
||||
display: block;
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
background-color: @gray;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.umb-dots:before {
|
||||
transform: translateX(-8px);
|
||||
}
|
||||
|
||||
.umb-dots:after {
|
||||
transform: translateX(8px);
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
.umb-website-selector {
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
height: 50px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
||||
.umb-website-selector__create-website {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: @blue;
|
||||
}
|
||||
|
||||
.umb-website-selector__dropdown-toggle {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.umb-website-selector__dropdown-toggle:hover,
|
||||
.umb-website-selector__dropdown-toggle:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.umb-website-selector__dropdown-toggle-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.umb-website-selector__dropdown-toggle-name {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.umb-website-selector__dropdown {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.umb-website-selector__websites {
|
||||
margin-left: 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.umb-website-selector__website-link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 10px;
|
||||
padding-right: 20px;
|
||||
padding-bottom: 10px;
|
||||
padding-left: 38px;
|
||||
}
|
||||
|
||||
.umb-website-selector__website-link:hover,
|
||||
.umb-website-selector__website-link.-selected {
|
||||
background-color: @grayLighter;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.umb-website-selector__dropdown-action {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
color: @blue;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.umb-website-selector__dropdown-arrow {
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.umb-website-selector__dropdown-arrow.-down {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-top: 4px solid @gray;
|
||||
}
|
||||
|
||||
.umb-website-selector__dropdown-arrow.-up {
|
||||
border-left: 4px solid transparent;
|
||||
border-right: 4px solid transparent;
|
||||
border-bottom: 4px solid @gray;
|
||||
}
|
||||
|
||||
.umb-website-selector__dropdown-action:hover {
|
||||
text-decoration: none;
|
||||
background: @blue;
|
||||
color: #ffffff;
|
||||
}
|
||||
@@ -16,12 +16,12 @@ label small, .guiDialogTiny {
|
||||
font-size: 11px
|
||||
}
|
||||
|
||||
label.control-label {
|
||||
label.control-label, .control-label {
|
||||
padding: 0 10px 0 0 !important;
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.umb-status-label{
|
||||
color: @gray !important;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ textarea {
|
||||
|
||||
// Identify controls by their labels
|
||||
label {
|
||||
display: block;
|
||||
display: inline-block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ h5.-black {
|
||||
}
|
||||
|
||||
/* LABELS*/
|
||||
.umb-control-group label.control-label {
|
||||
.umb-control-group label.control-label, .umb-control-group .control-label {
|
||||
text-align: left;
|
||||
}
|
||||
.umb-control-group label.control-label > div > label {
|
||||
|
||||
@@ -493,3 +493,66 @@ input.umb-panel-header-description {
|
||||
.umb-editor-drawer-content__left-side {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* --- VARIATIONS --- */
|
||||
.umb-variations-quick-switcher__toggle {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.umb-variations-quick-switcher_variations {
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 20;
|
||||
background-color: #ffffff;
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.umb-variations-quick-switcher__variation {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.umb-variations-quick-switcher__variation:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.umb-variations-quick-switcher__variation:hover,
|
||||
.umb-variations-quick-switcher__variation:focus {
|
||||
background-color: @grayLighter;
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.umb-variations-quick-switcher__variation-published-state {
|
||||
display: block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: @grayLight;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.umb-variations-quick-switcher__variation-published-state.-published {
|
||||
background-color: @green;
|
||||
}
|
||||
|
||||
.umb-variations-quick-switcher__variation-name {
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.umb-variations-quick-switcher__variation-description {
|
||||
font-size: 12px;
|
||||
color: @gray;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,9 @@ div.umb-codeeditor .umb-btn-toolbar {
|
||||
font-size:16px !important;
|
||||
}
|
||||
|
||||
/* pre-value editor */
|
||||
.rte-editor-preval .control-group .controls > div > label .mce-ico { line-height: 20px; }
|
||||
|
||||
|
||||
//
|
||||
// Color picker
|
||||
|
||||
@@ -19,7 +19,8 @@ ul.sections li {
|
||||
}
|
||||
|
||||
ul.sections li [class^="icon-"]:before,
|
||||
ul.sections li [class*=" icon-"]:before{
|
||||
ul.sections li [class*=" icon-"]:before,
|
||||
ul.sections li img.icon-section {
|
||||
font-size: 30px;
|
||||
margin: 1px 0 0 0;
|
||||
opacity: 0.4;
|
||||
@@ -29,7 +30,8 @@ ul.sections li [class*=" icon-"]:before{
|
||||
}
|
||||
|
||||
ul.sections:hover li [class^="icon-"]:before,
|
||||
ul.sections:hover li [class*=" icon-"]:before {
|
||||
ul.sections:hover li [class*=" icon-"]:before,
|
||||
ul.sections:hover li img.icon-section {
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
function CreateWebsiteOverlay($scope) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
$scope.model.website = {};
|
||||
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Overlay.CreateWebsiteOverlay", CreateWebsiteOverlay);
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,26 @@
|
||||
<div ng-controller="Umbraco.Overlay.CreateWebsiteOverlay as vm">
|
||||
|
||||
<div class="umb-control-group control-group">
|
||||
<label>Website name</label>
|
||||
<input type="text" ng-model="model.website.name" />
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group control-group">
|
||||
<label>Website language</label>
|
||||
<select ng-model="model.website.language">
|
||||
<option value="aa-DJ">Afar (Djinouti) [aa-DJ]</option>
|
||||
<option value="aa-ER">Afar (Eritrea) [aa-ER]</option>
|
||||
<option value="aa-ET">Afar (Ethiopia) [aa-ET]</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="umb-control-group control-group">
|
||||
<label>Create blank</label>
|
||||
<input type="checkbox" ng-model="model.website.blank" />
|
||||
<label>Copy all</label>
|
||||
<input type="checkbox" ng-model="model.website.copyAll" />
|
||||
<label>Copy selected</label>
|
||||
<input type="checkbox" ng-model="model.website.copySelected" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -65,8 +65,14 @@
|
||||
|
||||
</div>
|
||||
|
||||
<umb-website-selector
|
||||
websites="websites"
|
||||
on-create-website="createWebsite"
|
||||
on-click-website="clickWebsite">
|
||||
</umb-website-selector>
|
||||
|
||||
<!-- the tree -->
|
||||
<div id="tree" class="umb-modalcolumn-body" ng-if="authenticated">
|
||||
<div id="tree" ng-if="authenticated && websites.length > 0">
|
||||
<umb-tree
|
||||
cachekey="_"
|
||||
eventhandler="treeEventHandler"
|
||||
@@ -87,6 +93,12 @@
|
||||
</umb-context-menu>
|
||||
</div>
|
||||
|
||||
<umb-overlay
|
||||
ng-if="createWebsiteOverlay.show"
|
||||
model="createWebsiteOverlay"
|
||||
view="createWebsiteOverlay.view"
|
||||
position="tree">
|
||||
</umb-overlay>
|
||||
|
||||
<!-- Tree dialogs -->
|
||||
<div id="dialog" class='umb-modalcolumn fill shadow'
|
||||
|
||||
@@ -31,10 +31,14 @@
|
||||
|
||||
<div class="umb-panel-header-name" ng-if="nameLocked">{{ name }}</div>
|
||||
|
||||
<umb-generate-alias class="umb-panel-header-alias"
|
||||
ng-if="!hideAlias"
|
||||
alias="$parent.alias"
|
||||
alias-from="$parent.name"
|
||||
<a class="umb-variations-quick-switcher__toggle" href="" ng-click="toggleVariationsQuickSwitch()">
|
||||
<i class="icon-documents"></i>
|
||||
</a>
|
||||
|
||||
<umb-generate-alias class="umb-panel-header-alias"
|
||||
ng-if="!hideAlias"
|
||||
alias="$parent.alias"
|
||||
alias-from="$parent.name"
|
||||
enable-lock="true"
|
||||
server-validation-field="Alias"></umb-generate-alias>
|
||||
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
<div>
|
||||
<ul class="umb-tree">
|
||||
<li class="root">
|
||||
<ul class="umb-search-group">
|
||||
<li ng-repeat="result in results">
|
||||
<div style="padding-left: 20px">
|
||||
<a class="umb-search-group-item-link" ng-class="{first:$first}" ng-click="selectResultCallback($event, result)">
|
||||
<div class="umb-search-group-item-name">
|
||||
<i class="icon umb-tree-icon sprTree {{result.icon}}"></i>
|
||||
{{result.name}}
|
||||
</div>
|
||||
<small class="search-subtitle" ng-if="result.subTitle">{{result.subTitle}}</small>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul class="umb-tree">
|
||||
|
||||
<li class="root">
|
||||
<ul class="umb-search-group">
|
||||
|
||||
<li ng-repeat="result in results">
|
||||
|
||||
<div style="padding-left: 20px" ng-class="{'umb-tree-node-checked' : result.selected}">
|
||||
<a class="umb-search-group-item-link" ng-class="{first:$first}" ng-click="selectResultCallback($event, result)">
|
||||
<div class="umb-search-group-item-name">
|
||||
<i class="icon umb-tree-icon sprTree {{result.icon}}"></i>
|
||||
{{result.name}}
|
||||
</div>
|
||||
<small class="search-subtitle" ng-if="result.subTitle">{{result.subTitle}}</small>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<div class="umb-website-selector" on-outside-click="closeWebsiteDropDown()">
|
||||
|
||||
<a class="umb-website-selector__create-website" ng-if="websites.length === 0" href="" ng-click="createWebsite()">
|
||||
Create website
|
||||
</span>
|
||||
|
||||
<a class="umb-website-selector__dropdown-toggle" ng-if="websites.length > 0" href="" ng-click="toggleWebsiteDropDown()">
|
||||
<div class="umb-website-selector__dropdown-toggle-content">
|
||||
<span ng-if="showWebsiteDropdown" class="umb-website-selector__dropdown-arrow -up"></span>
|
||||
<span ng-if="!showWebsiteDropdown" class="umb-website-selector__dropdown-arrow -down"></span>
|
||||
<span class="umb-website-selector__dropdown-toggle-name">{{ selectedWebsite.name }}</span>
|
||||
<small>({{ selectedWebsite.language }})</small>
|
||||
</div>
|
||||
<div class="umb-dots"></div>
|
||||
</a>
|
||||
|
||||
<div class="umb-website-selector__dropdown shadow-depth-3" ng-show="showWebsiteDropdown">
|
||||
<ul class="umb-website-selector__websites">
|
||||
<li class="umb-website-selector__website" ng-repeat="website in websites | filter:{ selected: '!'+true }">
|
||||
<a class="umb-website-selector__website-link" ng-click="clickWebsite(website)" href="" ng-class="{'-selecetd': website.selected}">
|
||||
<div>
|
||||
<span class="umb-website-selector__website-name">{{ website.name}}</span>
|
||||
<small>({{ website.language }})</small>
|
||||
</div>
|
||||
<div class="umb-dots"></div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<a class="umb-website-selector__dropdown-action" ng-click="createWebsite()" href="">Create website</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,63 @@
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.Editors.DocumentTypes.CopyController",
|
||||
function ($scope, contentTypeResource, treeService, navigationService, notificationsService, appState, eventsService) {
|
||||
var dialogOptions = $scope.dialogOptions;
|
||||
$scope.dialogTreeEventHandler = $({});
|
||||
|
||||
function nodeSelectHandler(ev, args) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
|
||||
if ($scope.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.target.selected = false;
|
||||
}
|
||||
|
||||
$scope.target = args.node;
|
||||
$scope.target.selected = true;
|
||||
}
|
||||
|
||||
$scope.copy = function () {
|
||||
|
||||
$scope.busy = true;
|
||||
$scope.error = false;
|
||||
|
||||
contentTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id })
|
||||
.then(function (path) {
|
||||
$scope.error = false;
|
||||
$scope.success = true;
|
||||
$scope.busy = false;
|
||||
|
||||
//get the currently edited node (if any)
|
||||
var activeNode = appState.getTreeState("selectedNode");
|
||||
|
||||
//we need to do a double sync here: first sync to the copied content - but don't activate the node,
|
||||
//then sync to the currenlty edited content (note: this might not be the content that was copied!!)
|
||||
|
||||
navigationService.syncTree({ tree: "documentTypes", path: path, forceReload: true, activate: false }).then(function (args) {
|
||||
if (activeNode) {
|
||||
var activeNodePath = treeService.getPath(activeNode).join();
|
||||
//sync to this node now - depending on what was copied this might already be synced but might not be
|
||||
navigationService.syncTree({ tree: "documentTypes", path: activeNodePath, forceReload: false, activate: true });
|
||||
}
|
||||
});
|
||||
|
||||
}, function (err) {
|
||||
$scope.success = false;
|
||||
$scope.error = err;
|
||||
$scope.busy = false;
|
||||
//show any notifications
|
||||
if (angular.isArray(err.data.notifications)) {
|
||||
for (var i = 0; i < err.data.notifications.length; i++) {
|
||||
notificationsService.showNotification(err.data.notifications[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
<div class="umb-dialog umb-pane" ng-controller="Umbraco.Editors.DocumentTypes.CopyController">
|
||||
|
||||
<div class="umb-dialog-body">
|
||||
<div class="umb-pane">
|
||||
|
||||
<p class="abstract" ng-hide="success">
|
||||
<localize key="contentTypeEditor_folderToCopy">Select the folder to copy</localize> <strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_structureBelow">to in the tree structure below</localize>
|
||||
</p>
|
||||
|
||||
<div class="umb-loader-wrapper" ng-show="busy">
|
||||
<div class="umb-loader"></div>
|
||||
</div>
|
||||
|
||||
<div ng-show="error">
|
||||
<h5 class="text-error">{{error.errorMsg}}</h5>
|
||||
<p class="text-error">{{error.data.message}}</p>
|
||||
</div>
|
||||
|
||||
<div ng-show="success">
|
||||
<h5 class="text-success">
|
||||
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_copiedUnderneath">was copied underneath</localize> <strong>{{target.name}}</strong></h5>
|
||||
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
|
||||
</div>
|
||||
|
||||
<div ng-hide="success">
|
||||
|
||||
<div>
|
||||
<umb-tree section="settings"
|
||||
treealias="documentTypes"
|
||||
customtreeparams="foldersonly=1"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success">
|
||||
<a class="btn btn-link" ng-click="nav.hideDialog()" ng-if="!busy">
|
||||
<localize key="general_cancel">Cancel</localize>
|
||||
</a>
|
||||
<button class="btn btn-primary" ng-click="copy()" ng-disabled="busy">
|
||||
<localize key="actions_copy">Copy</localize>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,63 @@
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.Editors.MediaTypes.CopyController",
|
||||
function ($scope, mediaTypeResource, treeService, navigationService, notificationsService, appState, eventsService) {
|
||||
var dialogOptions = $scope.dialogOptions;
|
||||
$scope.dialogTreeEventHandler = $({});
|
||||
|
||||
function nodeSelectHandler(ev, args) {
|
||||
args.event.preventDefault();
|
||||
args.event.stopPropagation();
|
||||
|
||||
if ($scope.target) {
|
||||
//un-select if there's a current one selected
|
||||
$scope.target.selected = false;
|
||||
}
|
||||
|
||||
$scope.target = args.node;
|
||||
$scope.target.selected = true;
|
||||
}
|
||||
|
||||
$scope.copy = function () {
|
||||
|
||||
$scope.busy = true;
|
||||
$scope.error = false;
|
||||
|
||||
mediaTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id })
|
||||
.then(function (path) {
|
||||
$scope.error = false;
|
||||
$scope.success = true;
|
||||
$scope.busy = false;
|
||||
|
||||
//get the currently edited node (if any)
|
||||
var activeNode = appState.getTreeState("selectedNode");
|
||||
|
||||
//we need to do a double sync here: first sync to the copied content - but don't activate the node,
|
||||
//then sync to the currenlty edited content (note: this might not be the content that was copied!!)
|
||||
|
||||
navigationService.syncTree({ tree: "mediaTypes", path: path, forceReload: true, activate: false }).then(function (args) {
|
||||
if (activeNode) {
|
||||
var activeNodePath = treeService.getPath(activeNode).join();
|
||||
//sync to this node now - depending on what was copied this might already be synced but might not be
|
||||
navigationService.syncTree({ tree: "mediaTypes", path: activeNodePath, forceReload: false, activate: true });
|
||||
}
|
||||
});
|
||||
|
||||
}, function (err) {
|
||||
$scope.success = false;
|
||||
$scope.error = err;
|
||||
$scope.busy = false;
|
||||
//show any notifications
|
||||
if (angular.isArray(err.data.notifications)) {
|
||||
for (var i = 0; i < err.data.notifications.length; i++) {
|
||||
notificationsService.showNotification(err.data.notifications[i]);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
<div class="umb-dialog umb-pane" ng-controller="Umbraco.Editors.MediaTypes.CopyController">
|
||||
|
||||
<div class="umb-dialog-body">
|
||||
<div class="umb-pane">
|
||||
|
||||
<p class="abstract" ng-hide="success">
|
||||
<localize key="contentTypeEditor_folderToCopy">Select the folder to copy</localize> <strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_structureBelow">to in the tree structure below</localize>
|
||||
</p>
|
||||
|
||||
<div class="umb-loader-wrapper" ng-show="busy">
|
||||
<div class="umb-loader"></div>
|
||||
</div>
|
||||
|
||||
<div ng-show="error">
|
||||
<h5 class="text-error">{{error.errorMsg}}</h5>
|
||||
<p class="text-error">{{error.data.message}}</p>
|
||||
</div>
|
||||
|
||||
<div ng-show="success">
|
||||
<h5 class="text-success">
|
||||
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_copiedUnderneath">was copied underneath</localize> <strong>{{target.name}}</strong></h5>
|
||||
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
|
||||
</div>
|
||||
|
||||
<div ng-hide="success">
|
||||
|
||||
<div>
|
||||
<umb-tree section="settings"
|
||||
treealias="mediaTypes"
|
||||
customtreeparams="foldersonly=1"
|
||||
hideheader="false"
|
||||
hideoptions="true"
|
||||
isdialog="true"
|
||||
eventhandler="dialogTreeEventHandler"
|
||||
enablecheckboxes="true">
|
||||
</umb-tree>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success">
|
||||
<a class="btn btn-link" ng-click="nav.hideDialog()" ng-if="!busy">
|
||||
<localize key="general_cancel">Cancel</localize>
|
||||
</a>
|
||||
<button class="btn btn-primary" ng-click="copy()" ng-disabled="busy">
|
||||
<localize key="actions_copy">Copy</localize>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4,8 +4,8 @@ function booleanEditorController($scope, $rootScope, assetsService) {
|
||||
$scope.renderModel = {
|
||||
value: false
|
||||
};
|
||||
|
||||
if($scope.model.config.default.toString() === "1" && $scope.model && !$scope.model.value) {
|
||||
|
||||
if ($scope.model.config && $scope.model.config.default && $scope.model.config.default.toString() === "1" && $scope.model && !$scope.model.value) {
|
||||
$scope.renderModel.value = true;
|
||||
}
|
||||
|
||||
|
||||
+59
-3
@@ -1,4 +1,4 @@
|
||||
function listViewController($rootScope, $scope, $routeParams, $injector, $cookieStore, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper) {
|
||||
function listViewController($rootScope, $scope, $routeParams, $injector, $cookieStore, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper, userService) {
|
||||
|
||||
//this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content
|
||||
// that isn't created yet, if we continue this will use the parent id in the route params which isn't what
|
||||
@@ -58,6 +58,63 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie
|
||||
totalPages: 0,
|
||||
items: []
|
||||
};
|
||||
|
||||
//when this is null, we don't check permissions
|
||||
$scope.buttonPermissions = null;
|
||||
|
||||
//When we are dealing with 'content', we need to deal with permissions on child nodes.
|
||||
// Currently there is no real good way to
|
||||
if ($scope.entityType === "content") {
|
||||
|
||||
var idsWithPermissions = null;
|
||||
|
||||
$scope.buttonPermissions = {
|
||||
canCopy: true,
|
||||
canCreate: true,
|
||||
canDelete: true,
|
||||
canMove: true,
|
||||
canPublish: true,
|
||||
canUnpublish: true
|
||||
};
|
||||
|
||||
$scope.$watch(function() {
|
||||
return $scope.selection.length;
|
||||
}, function(newVal, oldVal) {
|
||||
|
||||
if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) {
|
||||
|
||||
//get all of the selected ids
|
||||
var ids = _.map($scope.selection, function(i) {
|
||||
return i.id.toString();
|
||||
});
|
||||
|
||||
//remove the dictionary items that don't have matching ids
|
||||
var filtered = {};
|
||||
_.each(idsWithPermissions, function (value, key, list) {
|
||||
if (_.contains(ids, key)) {
|
||||
filtered[key] = value;
|
||||
}
|
||||
});
|
||||
idsWithPermissions = filtered;
|
||||
|
||||
//find all ids that we haven't looked up permissions for
|
||||
var existingIds = _.keys(idsWithPermissions);
|
||||
var missingLookup = _.map(_.difference(ids, existingIds), function (i) {
|
||||
return Number(i);
|
||||
});
|
||||
|
||||
if (missingLookup.length > 0) {
|
||||
contentResource.getPermissions(missingLookup).then(function(p) {
|
||||
$scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$scope.buttonPermissions = listViewHelper.getButtonPermissions({}, idsWithPermissions);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
$scope.options = {
|
||||
displayAtTabNumber: $scope.model.config.displayAtTabNumber ? $scope.model.config.displayAtTabNumber : 1,
|
||||
@@ -166,9 +223,8 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie
|
||||
getListResultsCallback(id, $scope.options).then(function(data) {
|
||||
|
||||
$scope.actionInProgress = false;
|
||||
|
||||
$scope.listViewResultSet = data;
|
||||
|
||||
|
||||
//update all values for display
|
||||
if ($scope.listViewResultSet.items) {
|
||||
_.each($scope.listViewResultSet.items, function(e, index) {
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<umb-editor-sub-header-content-left>
|
||||
|
||||
<umb-editor-sub-header-section ng-if="listViewAllowedTypes && listViewAllowedTypes.length > 0 && !isAnythingSelected()">
|
||||
<umb-editor-sub-header-section ng-if="(listViewAllowedTypes && listViewAllowedTypes.length > 0 && !isAnythingSelected()) && currentNodePermissions.canCreate">
|
||||
<div class="btn-group">
|
||||
<a class="btn dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<localize key="actions_create">Create</localize>
|
||||
@@ -82,7 +82,7 @@
|
||||
<umb-editor-sub-header-section ng-if="isAnythingSelected()">
|
||||
|
||||
<umb-button
|
||||
ng-if="options.allowBulkPublish"
|
||||
ng-if="options.allowBulkPublish && (buttonPermissions == null || buttonPermissions.canPublish)"
|
||||
type="button"
|
||||
button-style="link"
|
||||
label="Publish"
|
||||
@@ -93,7 +93,7 @@
|
||||
</umb-button>
|
||||
|
||||
<umb-button
|
||||
ng-if="options.allowBulkUnpublish"
|
||||
ng-if="options.allowBulkUnpublish && (buttonPermissions == null || buttonPermissions.canUnpublish)"
|
||||
type="button"
|
||||
button-style="link"
|
||||
label="Unpublish"
|
||||
@@ -104,7 +104,7 @@
|
||||
</umb-button>
|
||||
|
||||
<umb-button
|
||||
ng-if="options.allowBulkCopy"
|
||||
ng-if="options.allowBulkCopy && (buttonPermissions == null || buttonPermissions.canCopy)"
|
||||
type="button"
|
||||
button-style="link"
|
||||
label="Copy"
|
||||
@@ -115,7 +115,7 @@
|
||||
</umb-button>
|
||||
|
||||
<umb-button
|
||||
ng-if="options.allowBulkMove"
|
||||
ng-if="options.allowBulkMove && (buttonPermissions == null || buttonPermissions.canMove)"
|
||||
type="button"
|
||||
button-style="link"
|
||||
label="Move"
|
||||
@@ -126,7 +126,7 @@
|
||||
</umb-button>
|
||||
|
||||
<umb-button
|
||||
ng-if="options.allowBulkDelete"
|
||||
ng-if="options.allowBulkDelete && (buttonPermissions == null || buttonPermissions.canDelete)"
|
||||
type="button"
|
||||
button-style="link"
|
||||
label="Delete"
|
||||
|
||||
@@ -261,7 +261,7 @@ angular.module("umbraco")
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
//Create the embedded plugin
|
||||
tinyMceService.createInsertEmbeddedMedia(editor, $scope, function() {
|
||||
|
||||
|
||||
+50
-3
@@ -1,5 +1,5 @@
|
||||
angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
|
||||
function ($scope, $timeout, $log, tinyMceService, stylesheetResource) {
|
||||
function ($scope, $timeout, $log, tinyMceService, stylesheetResource, assetsService) {
|
||||
var cfg = tinyMceService.defaultPrevalues();
|
||||
|
||||
if($scope.model.value){
|
||||
@@ -23,6 +23,14 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
|
||||
tinyMceService.configuration().then(function(config){
|
||||
$scope.tinyMceConfig = config;
|
||||
|
||||
// extend commands with properties for font-icon and if it is a custom command
|
||||
$scope.tinyMceConfig.commands = _.map($scope.tinyMceConfig.commands, function (obj) {
|
||||
var icon = getFontIcon(obj.frontEndCommand);
|
||||
return angular.extend(obj, {
|
||||
fontIcon: icon.name,
|
||||
isCustom: icon.isCustom
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
stylesheetResource.getAll().then(function(stylesheets){
|
||||
@@ -57,6 +65,43 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
|
||||
$scope.model.value.stylesheets.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// map properties for specific commands
|
||||
function getFontIcon(alias) {
|
||||
var icon = { name: alias, isCustom: false };
|
||||
|
||||
switch (alias) {
|
||||
case "codemirror":
|
||||
icon.name = "code";
|
||||
icon.isCustom = false;
|
||||
break;
|
||||
case "styleselect":
|
||||
icon.name = "icon-list";
|
||||
icon.isCustom = true;
|
||||
break;
|
||||
case "umbembeddialog":
|
||||
icon.name = "icon-tv";
|
||||
icon.isCustom = true;
|
||||
break;
|
||||
case "umbmediapicker":
|
||||
icon.name = "icon-picture";
|
||||
icon.isCustom = true;
|
||||
break;
|
||||
case "umbmacro":
|
||||
icon.name = "icon-settings-alt";
|
||||
icon.isCustom = true;
|
||||
break;
|
||||
case "umbmacro":
|
||||
icon.name = "icon-settings-alt";
|
||||
icon.isCustom = true;
|
||||
break;
|
||||
default:
|
||||
icon.name = alias;
|
||||
icon.isCustom = false;
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
|
||||
|
||||
@@ -65,9 +110,11 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
|
||||
|
||||
});
|
||||
|
||||
//when the scope is destroyed we need to unsubscribe
|
||||
// when the scope is destroyed we need to unsubscribe
|
||||
$scope.$on('$destroy', function () {
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
});
|
||||
// load TinyMCE skin which contains css for font-icons
|
||||
assetsService.loadCss("lib/tinymce/skins/umbraco/skin.min.css");
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
<div ng-controller="Umbraco.PrevalueEditors.RteController">
|
||||
<div ng-controller="Umbraco.PrevalueEditors.RteController" class="rte-editor-preval">
|
||||
|
||||
<umb-control-group label="Toolbar" hide-label="false">
|
||||
<div ng-repeat="cmd in tinyMceConfig.commands">
|
||||
@@ -8,9 +8,10 @@
|
||||
ng-model="cmd.selected"
|
||||
ng-change="selectCommand(cmd)" />
|
||||
|
||||
<img ng-src="{{cmd.icon}}" />
|
||||
<!--<img ng-src="{{cmd.icon}}" />-->
|
||||
<i class="mce-ico" ng-class="(cmd.isCustom ? ' mce-i-custom ' : ' mce-i-') + cmd.fontIcon"></i>
|
||||
|
||||
{{cmd.frontEndCommand}}
|
||||
{{cmd.name}}
|
||||
</label>
|
||||
</div>
|
||||
</umb-control-group>
|
||||
@@ -27,12 +28,12 @@
|
||||
</div>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="Dimensions" description="Width x Height">
|
||||
<umb-control-group label="Dimensions" description="Width x Height">
|
||||
<div class="vertical-align-items">
|
||||
<input type="number" min="0" ng-model="model.value.dimensions.width" class="umb-editor-tiny" placeholder="Width" /> ×
|
||||
<input type="number" min="0" ng-model="model.value.dimensions.width" class="umb-editor-tiny" placeholder="Width" /> ×
|
||||
<input type="number" min="0" ng-model="model.value.dimensions.height" class="umb-editor-tiny" placeholder="Height" /> Pixels
|
||||
</div>
|
||||
</umb-control-group>
|
||||
</div>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group label="Maximum size for inserted images" description="0 to disable resizing">
|
||||
<div class="vertical-align-items">
|
||||
|
||||
@@ -5,7 +5,8 @@ var app = angular.module('umbraco', [
|
||||
'umbraco.services',
|
||||
'umbraco.mocks',
|
||||
'umbraco.security',
|
||||
'ngCookies'
|
||||
'ngCookies',
|
||||
'LocalStorageModule'
|
||||
]);
|
||||
|
||||
/* For Angular 1.2: we need to load in Routing separately
|
||||
|
||||
@@ -26,7 +26,7 @@ module.exports = function(karma) {
|
||||
'lib/../build/belle/lib/underscore/underscore-min.js',
|
||||
'lib/umbraco/Extensions.js',
|
||||
'lib/../build/belle/lib/rgrove-lazyload/lazyload.js',
|
||||
|
||||
'lib/../build/belle/lib/angular-local-storage/angular-local-storage.min.js',
|
||||
|
||||
'test/config/app.unit.js',
|
||||
'src/common/mocks/umbraco.servervariables.js',
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
describe('list view helper tests', function () {
|
||||
var $scope, listViewHelper;
|
||||
|
||||
beforeEach(module('LocalStorageModule'));
|
||||
beforeEach(module('umbraco.services'));
|
||||
|
||||
beforeEach(inject(function ($injector) {
|
||||
$scope = $injector.get('$rootScope');
|
||||
listViewHelper = $injector.get('listViewHelper');
|
||||
}));
|
||||
|
||||
describe('getButtonPermissions', function () {
|
||||
|
||||
it('should update the currentIdsWithPermissions dictionary', function () {
|
||||
|
||||
var currentIdsWithPermissions = {};
|
||||
var result = listViewHelper.getButtonPermissions({ "1234": ["A", "B", "C"] }, currentIdsWithPermissions);
|
||||
|
||||
expect(_.has(currentIdsWithPermissions, "1234")).toBe(true);
|
||||
expect(_.keys(currentIdsWithPermissions).length).toEqual(1);
|
||||
|
||||
});
|
||||
|
||||
it('returns button permissions', function () {
|
||||
|
||||
var currentIdsWithPermissions = {};
|
||||
var result1 = listViewHelper.getButtonPermissions({ "1234": ["O", "C", "D", "M", "U"] }, currentIdsWithPermissions);
|
||||
|
||||
expect(result1["canCopy"]).toBe(true);
|
||||
expect(result1["canCreate"]).toBe(true);
|
||||
expect(result1["canDelete"]).toBe(true);
|
||||
expect(result1["canMove"]).toBe(true);
|
||||
expect(result1["canPublish"]).toBe(true);
|
||||
expect(result1["canUnpublish"]).toBe(true);
|
||||
|
||||
var result2 = listViewHelper.getButtonPermissions({ "1234": ["A", "B"] }, currentIdsWithPermissions);
|
||||
|
||||
expect(result2["canCopy"]).toBe(false);
|
||||
expect(result2["canCreate"]).toBe(false);
|
||||
expect(result2["canDelete"]).toBe(false);
|
||||
expect(result2["canMove"]).toBe(false);
|
||||
expect(result2["canPublish"]).toBe(false);
|
||||
expect(result2["canUnpublish"]).toBe(false);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -7,9 +7,18 @@
|
||||
'Failed to generate.')
|
||||
.then(function (result) {
|
||||
$scope.generating = false;
|
||||
$scope.dashboard = result;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.reload = function () {
|
||||
$scope.ready = false;
|
||||
modelsBuilderResource.getDashboard().then(function (result) {
|
||||
$scope.dashboard = result;
|
||||
$scope.ready = true;
|
||||
});
|
||||
};
|
||||
|
||||
function init() {
|
||||
modelsBuilderResource.getDashboard().then(function(result) {
|
||||
$scope.dashboard = result;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
<div id="modelsBuilder" ng-controller="Umbraco.Dashboard.ModelsBuilderController">
|
||||
|
||||
<div ng-show="ready" style="float: right;">
|
||||
<button type="button" class="btn" ng-click="reload()"><span>Reload</span></button>
|
||||
</div>
|
||||
|
||||
<h3>Models Builder</h3>
|
||||
|
||||
<div ng-show="!ready">
|
||||
@@ -10,7 +14,8 @@
|
||||
<div ng-bind-html-unsafe="dashboard.text"></div>
|
||||
|
||||
<div ng-if="dashboard.outOfDateModels">
|
||||
<p>Models are <strong>out-of-date</strong>.</p>
|
||||
<p>Models are <strong>out-of-date</strong>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div ng-if="dashboard.canGenerate">
|
||||
@@ -26,6 +31,11 @@
|
||||
<div class="umb-loader"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="dashboard.lastError" style="margin-top: 32px;" ng-show="!generating">
|
||||
<span style="color: red; font-weight: bold;">Last generation failed with the following error:</span>
|
||||
<pre style="width: 80%; white-space: pre-line; background: #f8f8f8; padding: 4px; font-size: small;">{{dashboard.lastError}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -348,8 +348,8 @@
|
||||
<Name>umbraco.providers</Name>
|
||||
</ProjectReference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Umbraco.ModelsBuilder, Version=3.0.0.90, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Umbraco.ModelsBuilder.3.0.0\lib\Umbraco.ModelsBuilder.dll</HintPath>
|
||||
<Reference Include="Umbraco.ModelsBuilder, Version=3.0.1.92, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Umbraco.ModelsBuilder.3.0.1\lib\Umbraco.ModelsBuilder.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="UrlRewritingNet.UrlRewriter, Version=2.0.7.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
|
||||
@@ -2,221 +2,246 @@
|
||||
<!-- Any changes to this file requires the application (umbraco) to restart -->
|
||||
<!-- This can be done by touching the web.config or recycling the application pool -->
|
||||
<tinymceConfig>
|
||||
<commands>
|
||||
<command>
|
||||
<umbracoAlias>code</umbracoAlias>
|
||||
<icon>images/editor/code.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="code">code</tinyMceCommand>
|
||||
<priority>1</priority>
|
||||
</command>
|
||||
<command>
|
||||
<commands>
|
||||
<command>
|
||||
<umbracoAlias>code</umbracoAlias>
|
||||
<name>Code</name>
|
||||
<icon>images/editor/code.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="code">code</tinyMceCommand>
|
||||
<priority>1</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>codemirror</umbracoAlias>
|
||||
<name>Code mirror</name>
|
||||
<icon>images/editor/code.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="codemirror">codemirror</tinyMceCommand>
|
||||
<priority>1</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>removeformat</umbracoAlias>
|
||||
<icon>images/editor/removeformat.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="removeformat">removeformat</tinyMceCommand>
|
||||
<priority>2</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>Undo</umbracoAlias>
|
||||
<icon>images/editor/undo.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="undo">undo</tinyMceCommand>
|
||||
<priority>11</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Redo</umbracoAlias>
|
||||
<icon>images/editor/redo.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="redo">redo</tinyMceCommand>
|
||||
<priority>12</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Cut</umbracoAlias>
|
||||
<icon>images/editor/cut.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="cut">cut</tinyMceCommand>
|
||||
<priority>13</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Copy</umbracoAlias>
|
||||
<icon>images/editor/copy.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="copy">copy</tinyMceCommand>
|
||||
<priority>14</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>styleselect</umbracoAlias>
|
||||
<icon>images/editor/showStyles.png</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="styleselect">styleselect</tinyMceCommand>
|
||||
<priority>20</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>bold</umbracoAlias>
|
||||
<icon>images/editor/bold.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="bold">bold</tinyMceCommand>
|
||||
<priority>21</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>italic</umbracoAlias>
|
||||
<icon>images/editor/italic.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="italic">italic</tinyMceCommand>
|
||||
<priority>22</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Underline</umbracoAlias>
|
||||
<icon>images/editor/underline.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="underline">underline</tinyMceCommand>
|
||||
<priority>23</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Strikethrough</umbracoAlias>
|
||||
<icon>images/editor/strikethrough.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="strikethrough">strikethrough</tinyMceCommand>
|
||||
<priority>24</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>JustifyLeft</umbracoAlias>
|
||||
<icon>images/editor/justifyleft.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="alignleft">justifyleft</tinyMceCommand>
|
||||
<priority>31</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>JustifyCenter</umbracoAlias>
|
||||
<icon>images/editor/justifycenter.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="aligncenter">justifycenter</tinyMceCommand>
|
||||
<priority>32</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>JustifyRight</umbracoAlias>
|
||||
<icon>images/editor/justifyright.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="alignright">justifyright</tinyMceCommand>
|
||||
<priority>33</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>JustifyFull</umbracoAlias>
|
||||
<icon>images/editor/justifyfull.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="alignjustify">alignjustify</tinyMceCommand>
|
||||
<priority>34</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>bullist</umbracoAlias>
|
||||
<icon>images/editor/bullist.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="bullist">bullist</tinyMceCommand>
|
||||
<priority>41</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>numlist</umbracoAlias>
|
||||
<icon>images/editor/numlist.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="numlist">numlist</tinyMceCommand>
|
||||
<priority>42</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Outdent</umbracoAlias>
|
||||
<icon>images/editor/outdent.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="outdent">outdent</tinyMceCommand>
|
||||
<priority>43</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Indent</umbracoAlias>
|
||||
<icon>images/editor/indent.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="indent">indent</tinyMceCommand>
|
||||
<priority>44</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>mceLink</umbracoAlias>
|
||||
<icon>images/editor/link.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="link">link</tinyMceCommand>
|
||||
<priority>51</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>unlink</umbracoAlias>
|
||||
<icon>images/editor/unLink.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="unlink">unlink</tinyMceCommand>
|
||||
<priority>52</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>mceInsertAnchor</umbracoAlias>
|
||||
<icon>images/editor/anchor.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="anchor">anchor</tinyMceCommand>
|
||||
<priority>53</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>mceImage</umbracoAlias>
|
||||
<icon>images/editor/image.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="umbmediapicker">image</tinyMceCommand>
|
||||
<priority>61</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>umbracomacro</umbracoAlias>
|
||||
<icon>images/editor/insMacro.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="umbmacro">umbracomacro</tinyMceCommand>
|
||||
<priority>62</priority>
|
||||
</command>
|
||||
|
||||
|
||||
|
||||
<command>
|
||||
<umbracoAlias>mceInsertTable</umbracoAlias>
|
||||
<icon>images/editor/table.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="table">table</tinyMceCommand>
|
||||
<priority>63</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>umbracoembed</umbracoAlias>
|
||||
<icon>images/editor/media.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="umbembeddialog">umbracoembed</tinyMceCommand>
|
||||
<priority>66</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>inserthorizontalrule</umbracoAlias>
|
||||
<icon>images/editor/hr.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="hr">hr</tinyMceCommand>
|
||||
<priority>71</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>subscript</umbracoAlias>
|
||||
<icon>images/editor/sub.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="subscript">subscript</tinyMceCommand>
|
||||
<priority>72</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>superscript</umbracoAlias>
|
||||
<icon>images/editor/sup.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="superscript">superscript</tinyMceCommand>
|
||||
<priority>73</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>mceCharMap</umbracoAlias>
|
||||
<icon>images/editor/charmap.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="charmap">charmap</tinyMceCommand>
|
||||
<priority>74</priority>
|
||||
</command>
|
||||
|
||||
</commands>
|
||||
<plugins>
|
||||
<umbracoAlias>removeformat</umbracoAlias>
|
||||
<name>Remove format</name>
|
||||
<icon>images/editor/removeformat.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="removeformat">removeformat</tinyMceCommand>
|
||||
<priority>2</priority>
|
||||
</command>
|
||||
<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>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>redo</umbracoAlias>
|
||||
<name>Redo</name>
|
||||
<icon>images/editor/redo.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="redo">redo</tinyMceCommand>
|
||||
<priority>12</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>cut</umbracoAlias>
|
||||
<name>Cut</name>
|
||||
<icon>images/editor/cut.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="cut">cut</tinyMceCommand>
|
||||
<priority>13</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>copy</umbracoAlias>
|
||||
<name>Copy</name>
|
||||
<icon>images/editor/copy.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="copy">copy</tinyMceCommand>
|
||||
<priority>14</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>paste</umbracoAlias>
|
||||
<name>Paste</name>
|
||||
<icon>images/editor/paste.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="paste">paste</tinyMceCommand>
|
||||
<priority>15</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>styleselect</umbracoAlias>
|
||||
<name>Style select</name>
|
||||
<icon>images/editor/showStyles.png</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="styleselect">styleselect</tinyMceCommand>
|
||||
<priority>20</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>bold</umbracoAlias>
|
||||
<name>Bold</name>
|
||||
<icon>images/editor/bold.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="bold">bold</tinyMceCommand>
|
||||
<priority>21</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>italic</umbracoAlias>
|
||||
<name>Italic</name>
|
||||
<icon>images/editor/italic.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="italic">italic</tinyMceCommand>
|
||||
<priority>22</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>underline</umbracoAlias>
|
||||
<name>Underline</name>
|
||||
<icon>images/editor/underline.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="underline">underline</tinyMceCommand>
|
||||
<priority>23</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>strikethrough</umbracoAlias>
|
||||
<name>Strikethrough</name>
|
||||
<icon>images/editor/strikethrough.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="strikethrough">strikethrough</tinyMceCommand>
|
||||
<priority>24</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>justifyleft</umbracoAlias>
|
||||
<name>Justify left</name>
|
||||
<icon>images/editor/justifyleft.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="alignleft">justifyleft</tinyMceCommand>
|
||||
<priority>31</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>justifycenter</umbracoAlias>
|
||||
<name>Justify center</name>
|
||||
<icon>images/editor/justifycenter.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="aligncenter">justifycenter</tinyMceCommand>
|
||||
<priority>32</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>justifyright</umbracoAlias>
|
||||
<name>Justify right</name>
|
||||
<icon>images/editor/justifyright.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="alignright">justifyright</tinyMceCommand>
|
||||
<priority>33</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>justifyfull</umbracoAlias>
|
||||
<name>Justify full</name>
|
||||
<icon>images/editor/justifyfull.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="alignjustify">alignjustify</tinyMceCommand>
|
||||
<priority>34</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>bullist</umbracoAlias>
|
||||
<name>Bullet list</name>
|
||||
<icon>images/editor/bullist.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="bullist">bullist</tinyMceCommand>
|
||||
<priority>41</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>numlist</umbracoAlias>
|
||||
<name>Numbered list</name>
|
||||
<icon>images/editor/numlist.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="numlist">numlist</tinyMceCommand>
|
||||
<priority>42</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>outdent</umbracoAlias>
|
||||
<name>Decrease indent</name>
|
||||
<icon>images/editor/outdent.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="outdent">outdent</tinyMceCommand>
|
||||
<priority>43</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>indent</umbracoAlias>
|
||||
<name>Increase indent</name>
|
||||
<icon>images/editor/indent.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="indent">indent</tinyMceCommand>
|
||||
<priority>44</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>mceLink</umbracoAlias>
|
||||
<name>Insert/edit link</name>
|
||||
<icon>images/editor/link.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="link">link</tinyMceCommand>
|
||||
<priority>51</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>unlink</umbracoAlias>
|
||||
<name>Remove link</name>
|
||||
<icon>images/editor/unLink.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="unlink">unlink</tinyMceCommand>
|
||||
<priority>52</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>mceInsertAnchor</umbracoAlias>
|
||||
<name>Anchor</name>
|
||||
<icon>images/editor/anchor.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="anchor">anchor</tinyMceCommand>
|
||||
<priority>53</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>mceImage</umbracoAlias>
|
||||
<name>Image</name>
|
||||
<icon>images/editor/image.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="umbmediapicker">image</tinyMceCommand>
|
||||
<priority>61</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>umbracomacro</umbracoAlias>
|
||||
<name>Macro</name>
|
||||
<icon>images/editor/insMacro.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="umbmacro">umbracomacro</tinyMceCommand>
|
||||
<priority>62</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>mceInsertTable</umbracoAlias>
|
||||
<name>Table</name>
|
||||
<icon>images/editor/table.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="table">table</tinyMceCommand>
|
||||
<priority>63</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>umbracoembed</umbracoAlias>
|
||||
<name>Embed</name>
|
||||
<icon>images/editor/media.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="umbembeddialog">umbracoembed</tinyMceCommand>
|
||||
<priority>66</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>inserthorizontalrule</umbracoAlias>
|
||||
<name>Horizontal rule</name>
|
||||
<icon>images/editor/hr.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="hr">hr</tinyMceCommand>
|
||||
<priority>71</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>subscript</umbracoAlias>
|
||||
<name>Subscript</name>
|
||||
<icon>images/editor/sub.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="subscript">subscript</tinyMceCommand>
|
||||
<priority>72</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>superscript</umbracoAlias>
|
||||
<name>Superscript</name>
|
||||
<icon>images/editor/sup.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="superscript">superscript</tinyMceCommand>
|
||||
<priority>73</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>mceCharMap</umbracoAlias>
|
||||
<name>Character map</name>
|
||||
<icon>images/editor/charmap.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="charmap">charmap</tinyMceCommand>
|
||||
<priority>74</priority>
|
||||
</command>
|
||||
</commands>
|
||||
<plugins>
|
||||
<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>
|
||||
<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>
|
||||
<plugin loadOnFrontend="true">lists</plugin>
|
||||
<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|
|
||||
<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|
|
||||
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],
|
||||
@@ -228,24 +253,24 @@ thead[id|class],tfoot[id|class],#td[id|lang|dir|class|colspan|rowspan|width|heig
|
||||
-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align|style],hr[class|style],
|
||||
dd[id|class|title|style|dir|lang],dl[id|class|title|style|dir|lang],dt[id|class|title|style|dir|lang],object[class|id|width|height|codebase|*],
|
||||
param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|class],area[shape|coords|href|alt|target|class],bdo[class],button[class],iframe[*]]]>
|
||||
</validElements>
|
||||
<invalidElements>font</invalidElements>
|
||||
</validElements>
|
||||
<invalidElements>font</invalidElements>
|
||||
|
||||
<!-- this area is for custom config settings that should be added during TinyMCE initialization -->
|
||||
<customConfig>
|
||||
<!-- <config key="myKey">mySetting</config>-->
|
||||
<config key="entity_encoding">raw</config>
|
||||
<config key="codemirror">
|
||||
{
|
||||
"indentOnInit": false,
|
||||
"path": "../../../../lib/codemirror",
|
||||
"config": {
|
||||
},
|
||||
"jsFiles": [
|
||||
],
|
||||
"cssFiles": [
|
||||
]
|
||||
}
|
||||
</config>
|
||||
</customConfig>
|
||||
<!-- this area is for custom config settings that should be added during TinyMCE initialization -->
|
||||
<customConfig>
|
||||
<!-- <config key="myKey">mySetting</config>-->
|
||||
<config key="entity_encoding">raw</config>
|
||||
<config key="codemirror">
|
||||
{
|
||||
"indentOnInit": false,
|
||||
"path": "../../../../lib/codemirror",
|
||||
"config": {
|
||||
},
|
||||
"jsFiles": [
|
||||
],
|
||||
"cssFiles": [
|
||||
]
|
||||
}
|
||||
</config>
|
||||
</customConfig>
|
||||
</tinymceConfig>
|
||||
@@ -5,204 +5,228 @@
|
||||
<commands>
|
||||
<command>
|
||||
<umbracoAlias>code</umbracoAlias>
|
||||
<name>Code</name>
|
||||
<icon>images/editor/code.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="code">code</tinyMceCommand>
|
||||
<priority>1</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>codemirror</umbracoAlias>
|
||||
<name>Code mirror</name>
|
||||
<icon>images/editor/code.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="codemirror">codemirror</tinyMceCommand>
|
||||
<priority>1</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>removeformat</umbracoAlias>
|
||||
<name>Remove format</name>
|
||||
<icon>images/editor/removeformat.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="removeformat">removeformat</tinyMceCommand>
|
||||
<priority>2</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>Undo</umbracoAlias>
|
||||
<umbracoAlias>undo</umbracoAlias>
|
||||
<name>Undo</name>
|
||||
<icon>images/editor/undo.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="undo">undo</tinyMceCommand>
|
||||
<priority>11</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Redo</umbracoAlias>
|
||||
<umbracoAlias>redo</umbracoAlias>
|
||||
<name>Redo</name>
|
||||
<icon>images/editor/redo.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="redo">redo</tinyMceCommand>
|
||||
<priority>12</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Cut</umbracoAlias>
|
||||
<umbracoAlias>cut</umbracoAlias>
|
||||
<name>Cut</name>
|
||||
<icon>images/editor/cut.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="cut">cut</tinyMceCommand>
|
||||
<priority>13</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Copy</umbracoAlias>
|
||||
<umbracoAlias>copy</umbracoAlias>
|
||||
<name>Copy</name>
|
||||
<icon>images/editor/copy.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="copy">copy</tinyMceCommand>
|
||||
<priority>14</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>paste</umbracoAlias>
|
||||
<name>Paste</name>
|
||||
<icon>images/editor/paste.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="paste">paste</tinyMceCommand>
|
||||
<priority>15</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>styleselect</umbracoAlias>
|
||||
<name>Style select</name>
|
||||
<icon>images/editor/showStyles.png</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="styleselect">styleselect</tinyMceCommand>
|
||||
<priority>20</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>bold</umbracoAlias>
|
||||
<name>Bold</name>
|
||||
<icon>images/editor/bold.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="bold">bold</tinyMceCommand>
|
||||
<priority>21</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>italic</umbracoAlias>
|
||||
<name>Italic</name>
|
||||
<icon>images/editor/italic.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="italic">italic</tinyMceCommand>
|
||||
<priority>22</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Underline</umbracoAlias>
|
||||
<umbracoAlias>underline</umbracoAlias>
|
||||
<name>Underline</name>
|
||||
<icon>images/editor/underline.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="underline">underline</tinyMceCommand>
|
||||
<priority>23</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Strikethrough</umbracoAlias>
|
||||
<umbracoAlias>strikethrough</umbracoAlias>
|
||||
<name>Strikethrough</name>
|
||||
<icon>images/editor/strikethrough.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="strikethrough">strikethrough</tinyMceCommand>
|
||||
<priority>24</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>JustifyLeft</umbracoAlias>
|
||||
<umbracoAlias>justifyleft</umbracoAlias>
|
||||
<name>Justify left</name>
|
||||
<icon>images/editor/justifyleft.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="alignleft">justifyleft</tinyMceCommand>
|
||||
<priority>31</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>JustifyCenter</umbracoAlias>
|
||||
<umbracoAlias>justifycenter</umbracoAlias>
|
||||
<name>Justify center</name>
|
||||
<icon>images/editor/justifycenter.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="aligncenter">justifycenter</tinyMceCommand>
|
||||
<priority>32</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>JustifyRight</umbracoAlias>
|
||||
<umbracoAlias>justifyright</umbracoAlias>
|
||||
<name>Justify right</name>
|
||||
<icon>images/editor/justifyright.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="alignright">justifyright</tinyMceCommand>
|
||||
<priority>33</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>JustifyFull</umbracoAlias>
|
||||
<umbracoAlias>justifyfull</umbracoAlias>
|
||||
<name>Justify full</name>
|
||||
<icon>images/editor/justifyfull.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="alignjustify">alignjustify</tinyMceCommand>
|
||||
<priority>34</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>bullist</umbracoAlias>
|
||||
<name>Bullet list</name>
|
||||
<icon>images/editor/bullist.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="bullist">bullist</tinyMceCommand>
|
||||
<priority>41</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>numlist</umbracoAlias>
|
||||
<name>Numbered list</name>
|
||||
<icon>images/editor/numlist.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="numlist">numlist</tinyMceCommand>
|
||||
<priority>42</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Outdent</umbracoAlias>
|
||||
<umbracoAlias>outdent</umbracoAlias>
|
||||
<name>Decrease indent</name>
|
||||
<icon>images/editor/outdent.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="outdent">outdent</tinyMceCommand>
|
||||
<priority>43</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>Indent</umbracoAlias>
|
||||
<umbracoAlias>indent</umbracoAlias>
|
||||
<name>Increase indent</name>
|
||||
<icon>images/editor/indent.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="indent">indent</tinyMceCommand>
|
||||
<priority>44</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>mceLink</umbracoAlias>
|
||||
<name>Insert/edit link</name>
|
||||
<icon>images/editor/link.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="link">link</tinyMceCommand>
|
||||
<priority>51</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>unlink</umbracoAlias>
|
||||
<name>Remove link</name>
|
||||
<icon>images/editor/unLink.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="unlink">unlink</tinyMceCommand>
|
||||
<priority>52</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>mceInsertAnchor</umbracoAlias>
|
||||
<name>Anchor</name>
|
||||
<icon>images/editor/anchor.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="anchor">anchor</tinyMceCommand>
|
||||
<priority>53</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>mceImage</umbracoAlias>
|
||||
<name>Image</name>
|
||||
<icon>images/editor/image.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="umbmediapicker">image</tinyMceCommand>
|
||||
<priority>61</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>umbracomacro</umbracoAlias>
|
||||
<name>Macro</name>
|
||||
<icon>images/editor/insMacro.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="umbmacro">umbracomacro</tinyMceCommand>
|
||||
<priority>62</priority>
|
||||
</command>
|
||||
|
||||
|
||||
|
||||
<command>
|
||||
<umbracoAlias>mceInsertTable</umbracoAlias>
|
||||
<name>Table</name>
|
||||
<icon>images/editor/table.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="table">table</tinyMceCommand>
|
||||
<priority>63</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>umbracoembed</umbracoAlias>
|
||||
<name>Embed</name>
|
||||
<icon>images/editor/media.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="true" frontendCommand="umbembeddialog">umbracoembed</tinyMceCommand>
|
||||
<priority>66</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>inserthorizontalrule</umbracoAlias>
|
||||
<name>Horizontal rule</name>
|
||||
<icon>images/editor/hr.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="hr">hr</tinyMceCommand>
|
||||
<priority>71</priority>
|
||||
</command>
|
||||
<command>
|
||||
<umbracoAlias>subscript</umbracoAlias>
|
||||
<name>Subscript</name>
|
||||
<icon>images/editor/sub.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="subscript">subscript</tinyMceCommand>
|
||||
<priority>72</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>superscript</umbracoAlias>
|
||||
<name>Superscript</name>
|
||||
<icon>images/editor/sup.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="superscript">superscript</tinyMceCommand>
|
||||
<priority>73</priority>
|
||||
</command>
|
||||
|
||||
<command>
|
||||
<umbracoAlias>mceCharMap</umbracoAlias>
|
||||
<name>Character map</name>
|
||||
<icon>images/editor/charmap.gif</icon>
|
||||
<tinyMceCommand value="" userInterface="false" frontendCommand="charmap">charmap</tinyMceCommand>
|
||||
<priority>74</priority>
|
||||
</command>
|
||||
|
||||
</commands>
|
||||
<plugins>
|
||||
<plugin loadOnFrontend="true">code</plugin>
|
||||
@@ -247,4 +271,4 @@ param[name|value|_value|class],embed[type|width|height|src|class|*],map[name|cla
|
||||
}
|
||||
</config>
|
||||
</customConfig>
|
||||
</tinymceConfig>
|
||||
</tinymceConfig>
|
||||
@@ -35,6 +35,6 @@
|
||||
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net45" />
|
||||
<package id="System.Collections.Immutable" version="1.1.36" targetFramework="net45" />
|
||||
<package id="System.Reflection.Metadata" version="1.0.21" targetFramework="net45" />
|
||||
<package id="Umbraco.ModelsBuilder" version="3.0.0" targetFramework="net45" />
|
||||
<package id="Umbraco.ModelsBuilder" version="3.0.1" targetFramework="net45" />
|
||||
<package id="UrlRewritingNet.UrlRewriter" version="2.0.7" targetFramework="net45" />
|
||||
</packages>
|
||||
@@ -1055,7 +1055,9 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="yesDelete">Yes, delete</key>
|
||||
|
||||
<key alias="movedUnderneath">was moved underneath</key>
|
||||
<key alias="copiedUnderneath">was copied underneath</key>
|
||||
<key alias="folderToMove">Select the folder to move</key>
|
||||
<key alias="folderToCopy">Select the folder to copy</key>
|
||||
<key alias="structureBelow">to in the tree structure below</key>
|
||||
|
||||
<key alias="allDocumentTypes">All Document types</key>
|
||||
|
||||
@@ -1051,7 +1051,9 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="yesDelete">Yes, delete</key>
|
||||
|
||||
<key alias="movedUnderneath">was moved underneath</key>
|
||||
<key alias="copiedUnderneath">was copied underneath</key>
|
||||
<key alias="folderToMove">Select the folder to move</key>
|
||||
<key alias="folderToCopy">Select the folder to copy</key>
|
||||
<key alias="structureBelow">to in the tree structure below</key>
|
||||
|
||||
<key alias="allDocumentTypes">All Document types</key>
|
||||
|
||||
@@ -8,6 +8,7 @@ using System.Net.Http.Formatting;
|
||||
using System.Text;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using System.Web.Http.ModelBinding.Binders;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -215,6 +216,19 @@ namespace Umbraco.Web.Editors
|
||||
return HasPermission(permissionToCheck, nodeId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns permissions for all nodes passed in for the current user
|
||||
/// </summary>
|
||||
/// <param name="nodeIds"></param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
public Dictionary<int, string[]> GetPermissions(int[] nodeIds)
|
||||
{
|
||||
return Services.UserService
|
||||
.GetPermissions(Security.CurrentUser, nodeIds)
|
||||
.ToDictionary(x => x.EntityId, x => x.AssignedPermissions);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public bool HasPermission(string permissionToCheck, int nodeId)
|
||||
{
|
||||
@@ -352,9 +366,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Publishes a document with a given ID
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
@@ -22,9 +23,9 @@ using Umbraco.Web.Models;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
//TODO: We'll need to be careful about the security on this controller, when we start implementing
|
||||
//TODO: We'll need to be careful about the security on this controller, when we start implementing
|
||||
// methods to modify content types we'll need to enforce security on the individual methods, we
|
||||
// cannot put security on the whole controller because things like
|
||||
// cannot put security on the whole controller because things like
|
||||
// GetAllowedChildren, GetPropertyTypeScaffold, GetAllPropertyTypeAliases are required for content editing.
|
||||
|
||||
/// <summary>
|
||||
@@ -129,7 +130,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
[UmbracoTreeAuthorize(
|
||||
Constants.Trees.DocumentTypes, Constants.Trees.Content,
|
||||
Constants.Trees.DocumentTypes, Constants.Trees.Content,
|
||||
Constants.Trees.MediaTypes, Constants.Trees.Media,
|
||||
Constants.Trees.MemberTypes, Constants.Trees.Members)]
|
||||
public ContentPropertyDisplay GetPropertyTypeScaffold(int id)
|
||||
@@ -166,13 +167,13 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
|
||||
public HttpResponseMessage PostCreateContainer(int parentId, string name)
|
||||
{
|
||||
var result = Services.ContentTypeService.CreateContentTypeContainer(parentId, name, Security.CurrentUser.Id);
|
||||
|
||||
return result
|
||||
? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id
|
||||
? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id
|
||||
: Request.CreateNotificationValidationErrorResponse(result.Exception.Message);
|
||||
}
|
||||
|
||||
@@ -203,7 +204,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
//make sure the template alias is set on the default and allowed template so we can map it back
|
||||
ctSave.DefaultTemplate = template.Alias;
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -231,7 +232,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
else
|
||||
ct = new ContentType(parentId);
|
||||
|
||||
|
||||
ct.Icon = "icon-document";
|
||||
|
||||
var dto = Mapper.Map<IContentType, DocumentTypeDisplay>(ct);
|
||||
@@ -302,7 +303,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move the media type
|
||||
/// Move the content type
|
||||
/// </summary>
|
||||
/// <param name="move"></param>
|
||||
/// <returns></returns>
|
||||
@@ -314,6 +315,17 @@ namespace Umbraco.Web.Editors
|
||||
doMove: (type, i) => Services.ContentTypeService.MoveContentType(type, i));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Copy the content type
|
||||
/// </summary>
|
||||
/// <param name="copy"></param>
|
||||
/// <returns></returns>
|
||||
public HttpResponseMessage PostCopy(MoveOrCopy copy)
|
||||
{
|
||||
return PerformCopy(
|
||||
copy,
|
||||
getContentType: i => Services.ContentTypeService.GetContentType(i),
|
||||
doCopy: (type, i) => Services.ContentTypeService.CopyContentType(type, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,7 @@ namespace Umbraco.Web.Editors
|
||||
/// <summary>
|
||||
/// Am abstract API controller providing functionality used for dealing with content and media types
|
||||
/// </summary>
|
||||
[PluginController("UmbracoApi")]
|
||||
[PluginController("UmbracoApi")]
|
||||
[PrefixlessBodyModelValidator]
|
||||
public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController
|
||||
{
|
||||
@@ -36,7 +36,7 @@ namespace Umbraco.Web.Editors
|
||||
/// </summary>
|
||||
protected ContentTypeControllerBase()
|
||||
: this(UmbracoContext.Current)
|
||||
{
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -61,17 +61,17 @@ namespace Umbraco.Web.Editors
|
||||
/// This is required because in the case of creating/modifying a content type because new property types being added to it are not yet persisted so cannot
|
||||
/// be looked up via the db, they need to be passed in.
|
||||
/// </param>
|
||||
/// <param name="contentTypeId"></param>
|
||||
/// <param name="contentTypeId"></param>
|
||||
/// <returns></returns>
|
||||
protected IEnumerable<Tuple<EntityBasic, bool>> PerformGetAvailableCompositeContentTypes(int contentTypeId,
|
||||
UmbracoObjectTypes type,
|
||||
protected IEnumerable<Tuple<EntityBasic, bool>> PerformGetAvailableCompositeContentTypes(int contentTypeId,
|
||||
UmbracoObjectTypes type,
|
||||
string[] filterContentTypes,
|
||||
string[] filterPropertyTypes)
|
||||
{
|
||||
IContentTypeComposition source = null;
|
||||
|
||||
//below is all ported from the old doc type editor and comes with the same weaknesses /insanity / magic
|
||||
|
||||
|
||||
IContentTypeComposition[] allContentTypes;
|
||||
|
||||
switch (type)
|
||||
@@ -132,7 +132,7 @@ namespace Umbraco.Web.Editors
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected string TranslateItem(string text)
|
||||
{
|
||||
@@ -155,7 +155,7 @@ namespace Umbraco.Web.Editors
|
||||
Action<TContentTypeSave> beforeCreateNew = null)
|
||||
where TContentType : class, IContentTypeComposition
|
||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
where TPropertyType : PropertyTypeBasic
|
||||
{
|
||||
var ctId = Convert.ToInt32(contentTypeSave.Id);
|
||||
@@ -187,10 +187,10 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList();
|
||||
}
|
||||
|
||||
|
||||
if (ctId > 0)
|
||||
{
|
||||
//its an update to an existing content type
|
||||
//its an update to an existing content type
|
||||
|
||||
//This mapping will cause a lot of content type validation to occur which we need to deal with
|
||||
try
|
||||
@@ -216,7 +216,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
beforeCreateNew(contentTypeSave);
|
||||
}
|
||||
|
||||
|
||||
//check if the type is trying to allow type 0 below itself - id zero refers to the currently unsaved type
|
||||
//always filter these 0 types out
|
||||
var allowItselfAsChild = false;
|
||||
@@ -227,12 +227,12 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
//save as new
|
||||
|
||||
|
||||
TContentType newCt = null;
|
||||
try
|
||||
{
|
||||
//This mapping will cause a lot of content type validation to occur which we need to deal with
|
||||
newCt = Mapper.Map<TContentType>(contentTypeSave);
|
||||
newCt = Mapper.Map<TContentType>(contentTypeSave);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -260,9 +260,9 @@ namespace Umbraco.Web.Editors
|
||||
return newCt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Change the sort order for media
|
||||
/// Move
|
||||
/// </summary>
|
||||
/// <param name="move"></param>
|
||||
/// <param name="getContentType"></param>
|
||||
@@ -293,7 +293,52 @@ namespace Umbraco.Web.Editors
|
||||
case MoveOperationStatusType.FailedParentNotFound:
|
||||
return Request.CreateResponse(HttpStatusCode.NotFound);
|
||||
case MoveOperationStatusType.FailedCancelledByEvent:
|
||||
//returning an object of INotificationModel will ensure that any pending
|
||||
//returning an object of INotificationModel will ensure that any pending
|
||||
// notification messages are added to the response.
|
||||
return Request.CreateValidationErrorResponse(new SimpleNotificationModel());
|
||||
case MoveOperationStatusType.FailedNotAllowedByPath:
|
||||
var notificationModel = new SimpleNotificationModel();
|
||||
notificationModel.AddErrorNotification(Services.TextService.Localize("moveOrCopy/notAllowedByPath"), "");
|
||||
return Request.CreateValidationErrorResponse(notificationModel);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move
|
||||
/// </summary>
|
||||
/// <param name="move"></param>
|
||||
/// <param name="getContentType"></param>
|
||||
/// <param name="doCopy"></param>
|
||||
/// <returns></returns>
|
||||
protected HttpResponseMessage PerformCopy<TContentType>(
|
||||
MoveOrCopy move,
|
||||
Func<int, TContentType> getContentType,
|
||||
Func<TContentType, int, Attempt<OperationStatus<TContentType, MoveOperationStatusType>>> doCopy)
|
||||
where TContentType : IContentTypeComposition
|
||||
{
|
||||
var toMove = getContentType(move.Id);
|
||||
if (toMove == null)
|
||||
{
|
||||
return Request.CreateResponse(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
var result = doCopy(toMove, move.ParentId);
|
||||
if (result.Success)
|
||||
{
|
||||
var copy = result.Result.Entity;
|
||||
var response = Request.CreateResponse(HttpStatusCode.OK);
|
||||
response.Content = new StringContent(copy.Path, Encoding.UTF8, "application/json");
|
||||
return response;
|
||||
}
|
||||
|
||||
switch (result.Result.StatusType)
|
||||
{
|
||||
case MoveOperationStatusType.FailedParentNotFound:
|
||||
return Request.CreateResponse(HttpStatusCode.NotFound);
|
||||
case MoveOperationStatusType.FailedCancelledByEvent:
|
||||
//returning an object of INotificationModel will ensure that any pending
|
||||
// notification messages are added to the response.
|
||||
return Request.CreateValidationErrorResponse(new SimpleNotificationModel());
|
||||
case MoveOperationStatusType.FailedNotAllowedByPath:
|
||||
@@ -312,14 +357,14 @@ namespace Umbraco.Web.Editors
|
||||
/// <param name="composition"></param>
|
||||
/// <returns></returns>
|
||||
private HttpResponseException CreateCompositionValidationExceptionIfInvalid<TContentTypeSave, TPropertyType, TContentTypeDisplay>(TContentTypeSave contentTypeSave, IContentTypeComposition composition)
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
where TPropertyType : PropertyTypeBasic
|
||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||
{
|
||||
var validateAttempt = Services.ContentTypeService.ValidateComposition(composition);
|
||||
if (validateAttempt == false)
|
||||
{
|
||||
//if it's not successful then we need to return some model state for the property aliases that
|
||||
//if it's not successful then we need to return some model state for the property aliases that
|
||||
// are duplicated
|
||||
var invalidPropertyAliases = validateAttempt.Result.Distinct();
|
||||
AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(contentTypeSave, invalidPropertyAliases);
|
||||
@@ -340,7 +385,7 @@ namespace Umbraco.Web.Editors
|
||||
/// <param name="invalidPropertyAliases"></param>
|
||||
/// <returns></returns>
|
||||
private void AddCompositionValidationErrors<TContentTypeSave, TPropertyType>(TContentTypeSave contentTypeSave, IEnumerable<string> invalidPropertyAliases)
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
where TPropertyType : PropertyTypeBasic
|
||||
{
|
||||
foreach (var propertyAlias in invalidPropertyAliases)
|
||||
@@ -369,8 +414,8 @@ namespace Umbraco.Web.Editors
|
||||
private HttpResponseException CreateInvalidCompositionResponseException<TContentTypeDisplay, TContentType, TContentTypeSave, TPropertyType>(
|
||||
Exception ex, TContentTypeSave contentTypeSave, TContentType ct, int ctId)
|
||||
where TContentType : class, IContentTypeComposition
|
||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
where TContentTypeDisplay : ContentTypeCompositionDisplay
|
||||
where TContentTypeSave : ContentTypeSave<TPropertyType>
|
||||
where TPropertyType : PropertyTypeBasic
|
||||
{
|
||||
InvalidCompositionException invalidCompositionException = null;
|
||||
@@ -431,7 +476,7 @@ namespace Umbraco.Web.Editors
|
||||
(_cultureDictionary = CultureDictionaryFactoryResolver.Current.Factory.CreateDictionary());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ using Umbraco.Web.Models;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
//TODO: We'll need to be careful about the security on this controller, when we start implementing
|
||||
//TODO: We'll need to be careful about the security on this controller, when we start implementing
|
||||
// methods to modify content types we'll need to enforce security on the individual methods, we
|
||||
// cannot put security on the whole controller because things like GetAllowedChildren are required for content editing.
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Umbraco.Web.Editors
|
||||
public MediaTypeController(UmbracoContext umbracoContext)
|
||||
: base(umbracoContext)
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
public int GetCount()
|
||||
@@ -112,7 +112,7 @@ namespace Umbraco.Web.Editors
|
||||
contentType = x.Item1,
|
||||
allowed = x.Item2
|
||||
});
|
||||
return Request.CreateResponse(result);
|
||||
return Request.CreateResponse(result);
|
||||
}
|
||||
|
||||
public MediaTypeDisplay GetEmpty(int parentId)
|
||||
@@ -129,8 +129,8 @@ namespace Umbraco.Web.Editors
|
||||
/// Returns all member types
|
||||
/// </summary>
|
||||
public IEnumerable<ContentTypeBasic> GetAll()
|
||||
{
|
||||
|
||||
{
|
||||
|
||||
return Services.ContentTypeService.GetAllMediaTypes()
|
||||
.Select(Mapper.Map<IMediaType, ContentTypeBasic>);
|
||||
}
|
||||
@@ -154,7 +154,7 @@ namespace Umbraco.Web.Editors
|
||||
var result = Services.ContentTypeService.CreateMediaTypeContainer(parentId, name, Security.CurrentUser.Id);
|
||||
|
||||
return result
|
||||
? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id
|
||||
? Request.CreateResponse(HttpStatusCode.OK, result.Result) //return the id
|
||||
: Request.CreateNotificationValidationErrorResponse(result.Exception.Message);
|
||||
}
|
||||
|
||||
@@ -228,10 +228,22 @@ namespace Umbraco.Web.Editors
|
||||
public HttpResponseMessage PostMove(MoveOrCopy move)
|
||||
{
|
||||
return PerformMove(
|
||||
move,
|
||||
getContentType: i => Services.ContentTypeService.GetMediaType(i),
|
||||
doMove: (type, i) => Services.ContentTypeService.MoveMediaType(type, i));
|
||||
move,
|
||||
getContentType: i => Services.ContentTypeService.GetMediaType(i),
|
||||
doMove: (type, i) => Services.ContentTypeService.MoveMediaType(type, i));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy the media type
|
||||
/// </summary>
|
||||
/// <param name="copy"></param>
|
||||
/// <returns></returns>
|
||||
public HttpResponseMessage PostCopy(MoveOrCopy copy)
|
||||
{
|
||||
return PerformCopy(
|
||||
copy,
|
||||
getContentType: i => Services.ContentTypeService.GetMediaType(i),
|
||||
doCopy: (type, i) => Services.ContentTypeService.CopyMediaType(type, i));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -275,6 +275,14 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
throw new NotSupportedException("Currently the member editor does not support providers that have RequiresQuestionAndAnswer specified");
|
||||
}
|
||||
|
||||
//We're gonna look up the current roles now because the below code can cause
|
||||
// events to be raised and developers could be manually adding roles to members in
|
||||
// their handlers. If we don't look this up now there's a chance we'll just end up
|
||||
// removing the roles they've assigned.
|
||||
var currRoles = Roles.GetRolesForUser(contentItem.PersistedContent.Username);
|
||||
//find the ones to remove and remove them
|
||||
var rolesToRemove = currRoles.Except(contentItem.Groups).ToArray();
|
||||
|
||||
string generatedPassword = null;
|
||||
//Depending on the action we need to first do a create or update using the membership provider
|
||||
@@ -316,18 +324,15 @@ namespace Umbraco.Web.Editors
|
||||
//create/save the IMember
|
||||
Services.MemberService.Save(contentItem.PersistedContent);
|
||||
}
|
||||
|
||||
|
||||
//Now let's do the role provider stuff - now that we've saved the content item (that is important since
|
||||
// if we are changing the username, it must be persisted before looking up the member roles).
|
||||
var currGroups = Roles.GetRolesForUser(contentItem.PersistedContent.Username);
|
||||
//find the ones to remove and remove them
|
||||
var toRemove = currGroups.Except(contentItem.Groups).ToArray();
|
||||
if (toRemove.Any())
|
||||
if (rolesToRemove.Any())
|
||||
{
|
||||
Roles.RemoveUserFromRoles(contentItem.PersistedContent.Username, toRemove);
|
||||
Roles.RemoveUserFromRoles(contentItem.PersistedContent.Username, rolesToRemove);
|
||||
}
|
||||
//find the ones to add and add them
|
||||
var toAdd = contentItem.Groups.Except(currGroups).ToArray();
|
||||
var toAdd = contentItem.Groups.Except(currRoles).ToArray();
|
||||
if (toAdd.Any())
|
||||
{
|
||||
//add the ones submitted
|
||||
|
||||
@@ -3,8 +3,11 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using ClientDependency.Core.Config;
|
||||
using Microsoft.Owin.Security;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Web.Editors;
|
||||
|
||||
namespace Umbraco.Web
|
||||
@@ -30,6 +33,7 @@ namespace Umbraco.Web
|
||||
/// </remarks>
|
||||
public static IHtmlString BareMinimumServerVariablesScript(this HtmlHelper html, UrlHelper uri, string externalLoginsUrl)
|
||||
{
|
||||
var version = UmbracoVersion.GetSemanticVersion().ToSemanticString();
|
||||
var str = @"<script type=""text/javascript"">
|
||||
var Umbraco = {};
|
||||
Umbraco.Sys = {};
|
||||
@@ -40,7 +44,9 @@ namespace Umbraco.Web
|
||||
""externalLoginsUrl"": """ + externalLoginsUrl + @"""
|
||||
},
|
||||
""application"": {
|
||||
""applicationPath"": """ + html.ViewContext.HttpContext.Request.ApplicationPath + @"""
|
||||
""applicationPath"": """ + html.ViewContext.HttpContext.Request.ApplicationPath + @""",
|
||||
""version"": """ + version + @""",
|
||||
""cdf"": """ + ClientDependencySettings.Instance.Version + @"""
|
||||
},
|
||||
""isDebuggingEnabled"" : " + html.ViewContext.HttpContext.IsDebuggingEnabled.ToString().ToLowerInvariant() + @"
|
||||
};
|
||||
|
||||
@@ -10,6 +10,9 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataContract(Name = "richtexteditorcommand", Namespace = "")]
|
||||
public class RichTextEditorCommand
|
||||
{
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "icon")]
|
||||
public string Icon { get; set; }
|
||||
|
||||
|
||||
@@ -292,35 +292,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
source.HasIdentity ? source.Id : source.ParentId)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (permissions == null)
|
||||
{
|
||||
return Enumerable.Empty<char>();
|
||||
}
|
||||
|
||||
var result = new List<char>();
|
||||
|
||||
//can they publish ?
|
||||
if (permissions.AssignedPermissions.Contains(ActionPublish.Instance.Letter.ToString(CultureInfo.InvariantCulture)))
|
||||
{
|
||||
result.Add(ActionPublish.Instance.Letter);
|
||||
}
|
||||
//can they send to publish ?
|
||||
if (permissions.AssignedPermissions.Contains(ActionToPublish.Instance.Letter.ToString(CultureInfo.InvariantCulture)))
|
||||
{
|
||||
result.Add(ActionToPublish.Instance.Letter);
|
||||
}
|
||||
//can they save ?
|
||||
if (permissions.AssignedPermissions.Contains(ActionUpdate.Instance.Letter.ToString(CultureInfo.InvariantCulture)))
|
||||
{
|
||||
result.Add(ActionUpdate.Instance.Letter);
|
||||
}
|
||||
//can they create ?
|
||||
if (permissions.AssignedPermissions.Contains(ActionNew.Instance.Letter.ToString(CultureInfo.InvariantCulture)))
|
||||
{
|
||||
result.Add(ActionNew.Instance.Letter);
|
||||
}
|
||||
|
||||
return result;
|
||||
return permissions == null
|
||||
? Enumerable.Empty<char>()
|
||||
: permissions.AssignedPermissions.Where(x => x.Length == 1).Select(x => x.ToUpperInvariant()[0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,10 @@ using Umbraco.Web.Models;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
public class RenderModelBinder : IModelBinder, IModelBinderProvider
|
||||
/// <summary>
|
||||
/// Allows for Model Binding any IPublishedContent or IRenderModel
|
||||
/// </summary>
|
||||
public class RenderModelBinder : DefaultModelBinder, IModelBinder, IModelBinderProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Binds the model to a value by using the specified controller context and binding context.
|
||||
@@ -17,14 +20,29 @@ namespace Umbraco.Web.Mvc
|
||||
/// The bound value.
|
||||
/// </returns>
|
||||
/// <param name="controllerContext">The controller context.</param><param name="bindingContext">The binding context.</param>
|
||||
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
||||
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
|
||||
{
|
||||
object model;
|
||||
if (controllerContext.RouteData.DataTokens.TryGetValue(Core.Constants.Web.UmbracoDataToken, out model) == false)
|
||||
return null;
|
||||
|
||||
//default culture
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
//This model binder deals with IRenderModel and IPublishedContent by extracting the model from the route's
|
||||
// datatokens. This data token is set in 2 places: RenderRouteHandler, UmbracoVirtualNodeRouteHandler
|
||||
// and both always set the model to an instance of `RenderModel`. So if this isn't an instance of IRenderModel then
|
||||
// we need to let the DefaultModelBinder deal with the logic.
|
||||
var renderModel = model as IRenderModel;
|
||||
if (renderModel == null)
|
||||
{
|
||||
model = base.BindModel(controllerContext, bindingContext);
|
||||
if (model == null) return null;
|
||||
}
|
||||
|
||||
//if for any reason the model is not either IRenderModel or IPublishedContent, then we return since those are the only
|
||||
// types this binder is dealing with.
|
||||
if ((model is IRenderModel) == false && (model is IPublishedContent) == false) return null;
|
||||
|
||||
//default culture
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
|
||||
var umbracoContext = controllerContext.GetUmbracoContext()
|
||||
?? UmbracoContext.Current;
|
||||
@@ -34,8 +52,8 @@ namespace Umbraco.Web.Mvc
|
||||
culture = umbracoContext.PublishedContentRequest.Culture;
|
||||
}
|
||||
|
||||
return BindModel(model, bindingContext.ModelType, culture);
|
||||
}
|
||||
return BindModel(model, bindingContext.ModelType, culture);
|
||||
}
|
||||
|
||||
// source is the model that we have
|
||||
// modelType is the type of the model that we need to bind to
|
||||
@@ -140,15 +158,9 @@ namespace Umbraco.Web.Mvc
|
||||
|
||||
public IModelBinder GetBinder(Type modelType)
|
||||
{
|
||||
// can bind to RenderModel
|
||||
if (modelType == typeof(RenderModel)) return this;
|
||||
|
||||
// can bind to RenderModel<TContent>
|
||||
if (modelType.IsGenericType && modelType.GetGenericTypeDefinition() == typeof(RenderModel<>)) return this;
|
||||
|
||||
// can bind to TContent where TContent : IPublishedContent
|
||||
if (typeof(IPublishedContent).IsAssignableFrom(modelType)) return this;
|
||||
return null;
|
||||
return TypeHelper.IsTypeAssignableFrom<IRenderModel>(modelType) || TypeHelper.IsTypeAssignableFrom<IPublishedContent>(modelType)
|
||||
? this
|
||||
: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,8 +42,6 @@ namespace Umbraco.Web.PropertyEditors
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void EnsureInit()
|
||||
{
|
||||
|
||||
@@ -71,9 +69,10 @@ namespace Umbraco.Web.PropertyEditors
|
||||
new RichTextEditorCommand()
|
||||
{
|
||||
IsStylePicker = isStyle,
|
||||
Name = n.SelectSingleNode("./name") != null ? n.SelectSingleNode("./name").FirstChild.Value : alias,
|
||||
Icon = n.SelectSingleNode("./icon").FirstChild.Value,
|
||||
Command = n.SelectSingleNode("./tinyMceCommand").FirstChild.Value,
|
||||
Alias = n.SelectSingleNode("./umbracoAlias").FirstChild.Value.ToLower(),
|
||||
Alias = alias,
|
||||
UserInterface = n.SelectSingleNode("./tinyMceCommand").Attributes.GetNamedItem("userInterface").Value,
|
||||
FrontEndCommand = n.SelectSingleNode("./tinyMceCommand").Attributes.GetNamedItem("frontendCommand").Value,
|
||||
Value = n.SelectSingleNode("./tinyMceCommand").Attributes.GetNamedItem("value").Value,
|
||||
@@ -136,4 +135,4 @@ namespace Umbraco.Web.PropertyEditors
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
{
|
||||
throw new NotImplementedException("PublishedMediaCache does not support XPath.");
|
||||
}
|
||||
|
||||
|
||||
public virtual IEnumerable<IPublishedContent> GetByXPath(UmbracoContext umbracoContext, bool preview, string xpath, XPathVariable[] vars)
|
||||
{
|
||||
throw new NotImplementedException("PublishedMediaCache does not support XPath.");
|
||||
@@ -105,7 +105,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
|
||||
public bool XPathNavigatorIsNavigable { get { return false; } }
|
||||
|
||||
public virtual bool HasContent(UmbracoContext context, bool preview) { throw new NotImplementedException(); }
|
||||
public virtual bool HasContent(UmbracoContext context, bool preview) { throw new NotImplementedException(); }
|
||||
|
||||
private ExamineManager GetExamineManagerSafe()
|
||||
{
|
||||
@@ -138,7 +138,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
return indexer;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
{
|
||||
LogHelper.Error<PublishedMediaCache>("Could not retrieve the InternalIndexer", ex);
|
||||
//something didn't work, continue returning null.
|
||||
}
|
||||
@@ -182,6 +182,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
// it is called, but at least it should NOT hit the database
|
||||
// nor Lucene each time, relying on the memory cache instead
|
||||
|
||||
if (id <= 0) return null; // fail fast
|
||||
|
||||
var cacheValues = GetCacheValues(id, GetUmbracoMediaCacheValues);
|
||||
|
||||
return cacheValues == null ? null : CreateFromCacheValues(cacheValues);
|
||||
@@ -197,11 +199,11 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
{
|
||||
//first check in Examine as this is WAY faster
|
||||
var criteria = searchProvider.CreateSearchCriteria("media");
|
||||
|
||||
|
||||
var filter = criteria.Id(id).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard());
|
||||
//the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene.
|
||||
//+(+__NodeId:3113 -__Path:-1,-21,*) +__IndexType:media
|
||||
|
||||
|
||||
var results = searchProvider.Search(filter.Compile());
|
||||
if (results.Any())
|
||||
{
|
||||
@@ -215,7 +217,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
//Catch the exception here for the time being, and just fallback to GetMedia
|
||||
//TODO: Need to fix examine in LB scenarios!
|
||||
LogHelper.Error<PublishedMediaCache>("Could not load data from Examine index for media", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogHelper.Warn<PublishedMediaCache>(
|
||||
@@ -231,8 +233,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
{
|
||||
if (media != null && media.Current != null)
|
||||
{
|
||||
return media.Current.Name.InvariantEquals("error")
|
||||
? null
|
||||
return media.Current.Name.InvariantEquals("error")
|
||||
? null
|
||||
: ConvertFromXPathNavigator(media.Current);
|
||||
}
|
||||
|
||||
@@ -245,14 +247,14 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
|
||||
internal CacheValues ConvertFromSearchResult(SearchResult searchResult)
|
||||
{
|
||||
//NOTE: Some fields will not be included if the config section for the internal index has been
|
||||
//NOTE: Some fields will not be included if the config section for the internal index has been
|
||||
//mucked around with. It should index everything and so the index definition should simply be:
|
||||
// <IndexSet SetName="InternalIndexSet" IndexPath="~/App_Data/TEMP/ExamineIndexes/Internal/" />
|
||||
|
||||
|
||||
|
||||
var values = new Dictionary<string, string>(searchResult.Fields);
|
||||
//we need to ensure some fields exist, because of the above issue
|
||||
if (!new []{"template", "templateId"}.Any(values.ContainsKey))
|
||||
if (!new []{"template", "templateId"}.Any(values.ContainsKey))
|
||||
values.Add("template", 0.ToString());
|
||||
if (!new[] { "sortOrder" }.Any(values.ContainsKey))
|
||||
values.Add("sortOrder", 0.ToString());
|
||||
@@ -269,7 +271,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
if (!new[] { "createDate" }.Any(values.ContainsKey))
|
||||
values.Add("createDate", default(DateTime).ToString("yyyy-MM-dd HH:mm:ss"));
|
||||
if (!new[] { "level" }.Any(values.ContainsKey))
|
||||
{
|
||||
{
|
||||
values.Add("level", values["__Path"].Split(',').Length.ToString());
|
||||
}
|
||||
|
||||
@@ -303,7 +305,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
{
|
||||
values["nodeTypeAlias"] = xpath.Name;
|
||||
}
|
||||
|
||||
|
||||
var result = xpath.SelectChildren(XPathNodeType.Element);
|
||||
//add the attributes e.g. id, parentId etc
|
||||
if (result.Current != null && result.Current.HasAttributes)
|
||||
@@ -320,7 +322,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
if (!values.ContainsKey(result.Current.Name))
|
||||
{
|
||||
values[result.Current.Name] = result.Current.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
result.Current.MoveToParent();
|
||||
}
|
||||
@@ -351,9 +353,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
XPath = forceNav ? xpath : null // outside of tests we do NOT want to cache the navigator!
|
||||
};
|
||||
|
||||
//var content = new DictionaryPublishedContent(values,
|
||||
//var content = new DictionaryPublishedContent(values,
|
||||
// d => d.ParentId != -1 //parent should be null if -1
|
||||
// ? GetUmbracoMedia(d.ParentId)
|
||||
// ? GetUmbracoMedia(d.ParentId)
|
||||
// : null,
|
||||
// //callback to return the children of the current node based on the xml structure already found
|
||||
// d => GetChildrenMedia(d.Id, xpath),
|
||||
@@ -363,8 +365,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists
|
||||
/// in the results, if it does not, then we'll have to revert to looking up in the db.
|
||||
/// We will need to first check if the document was loaded by Examine, if so we'll need to check if this property exists
|
||||
/// in the results, if it does not, then we'll have to revert to looking up in the db.
|
||||
/// </summary>
|
||||
/// <param name="dd"> </param>
|
||||
/// <param name="alias"></param>
|
||||
@@ -399,7 +401,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
/// <param name="xpath"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<IPublishedContent> GetChildrenMedia(int parentId, XPathNavigator xpath = null)
|
||||
{
|
||||
{
|
||||
|
||||
//if there is no navigator, try examine first, then re-look it up
|
||||
if (xpath == null)
|
||||
@@ -412,7 +414,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
{
|
||||
//first check in Examine as this is WAY faster
|
||||
var criteria = searchProvider.CreateSearchCriteria("media");
|
||||
|
||||
|
||||
var filter = criteria.ParentId(parentId).Not().Field(UmbracoContentIndexer.IndexPathFieldName, "-1,-21,".MultipleCharacterWildcard());
|
||||
//the above filter will create a query like this, NOTE: That since the use of the wildcard, it automatically escapes it in Lucene.
|
||||
//+(+parentId:3113 -__Path:-1,-21,*) +__IndexType:media
|
||||
@@ -433,7 +435,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
{
|
||||
results = searchProvider.Search(filter.Compile());
|
||||
}
|
||||
|
||||
|
||||
if (results.Any())
|
||||
{
|
||||
// var medias = results.Select(ConvertFromSearchResult);
|
||||
@@ -451,7 +453,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
else
|
||||
{
|
||||
//if there's no result then return null. Previously we defaulted back to library.GetMedia below
|
||||
//but this will always get called for when we are getting descendents since many items won't have
|
||||
//but this will always get called for when we are getting descendents since many items won't have
|
||||
//children and then we are hitting the database again!
|
||||
//So instead we're going to rely on Examine to have the correct results like it should.
|
||||
return Enumerable.Empty<IPublishedContent>();
|
||||
@@ -462,7 +464,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
//Currently examine is throwing FileNotFound exceptions when we have a loadbalanced filestore and a node is published in umbraco
|
||||
//See this thread: http://examine.cdodeplex.com/discussions/264341
|
||||
//Catch the exception here for the time being, and just fallback to GetMedia
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//falling back to get media
|
||||
@@ -514,13 +516,13 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
// // will leave it here as it must have done something!
|
||||
// if (x.Name != "contents")
|
||||
// {
|
||||
// //make sure it's actually a node, not a property
|
||||
// //make sure it's actually a node, not a property
|
||||
// if (!string.IsNullOrEmpty(x.GetAttribute("path", "")) &&
|
||||
// !string.IsNullOrEmpty(x.GetAttribute("id", "")))
|
||||
// {
|
||||
// mediaList.Add(ConvertFromXPathNavigator(x));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
return mediaList;
|
||||
@@ -530,7 +532,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
/// An IPublishedContent that is represented all by a dictionary.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a helper class and definitely not intended for public use, it expects that all of the values required
|
||||
/// This is a helper class and definitely not intended for public use, it expects that all of the values required
|
||||
/// to create an IPublishedContent exist in the dictionary by specific aliases.
|
||||
/// </remarks>
|
||||
internal class DictionaryPublishedContent : PublishedContentWithKeyBase
|
||||
@@ -544,7 +546,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
private static readonly string[] IgnoredKeys = { "version", "isDoc" };
|
||||
|
||||
public DictionaryPublishedContent(
|
||||
IDictionary<string, string> valueDictionary,
|
||||
IDictionary<string, string> valueDictionary,
|
||||
Func<int, IPublishedContent> getParent,
|
||||
Func<int, XPathNavigator, IEnumerable<IPublishedContent>> getChildren,
|
||||
Func<DictionaryPublishedContent, string, IPublishedProperty> getProperty,
|
||||
@@ -585,7 +587,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
if (int.TryParse(val, out pId))
|
||||
{
|
||||
ParentId = pId;
|
||||
}
|
||||
}
|
||||
}, "parentID");
|
||||
|
||||
_contentType = PublishedContentType.Get(PublishedItemType.Media, _documentTypeAlias);
|
||||
@@ -600,7 +602,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
string value;
|
||||
const bool isPreviewing = false; // false :: never preview a media
|
||||
var property = valueDictionary.TryGetValue(alias, out value) == false
|
||||
? new XmlPublishedProperty(propertyType, isPreviewing)
|
||||
? new XmlPublishedProperty(propertyType, isPreviewing)
|
||||
: new XmlPublishedProperty(propertyType, isPreviewing, value);
|
||||
_properties.Add(property);
|
||||
}
|
||||
@@ -648,7 +650,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
internal bool LoadedFromExamine { get; private set; }
|
||||
|
||||
//private readonly Func<DictionaryPublishedContent, IPublishedContent> _getParent;
|
||||
private readonly Lazy<IPublishedContent> _getParent;
|
||||
private readonly Lazy<IPublishedContent> _getParent;
|
||||
//private readonly Func<DictionaryPublishedContent, IEnumerable<IPublishedContent>> _getChildren;
|
||||
private readonly Lazy<IEnumerable<IPublishedContent>> _getChildren;
|
||||
private readonly Func<DictionaryPublishedContent, string, IPublishedProperty> _getProperty;
|
||||
@@ -788,7 +790,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
IPublishedProperty property;
|
||||
string key = null;
|
||||
var cache = UmbracoContextCache.Current;
|
||||
|
||||
|
||||
if (cache != null)
|
||||
{
|
||||
key = string.Format("RECURSIVE_PROPERTY::{0}::{1}", Id, alias.ToLowerInvariant());
|
||||
@@ -911,15 +913,15 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
|
||||
|
||||
// we do clear a lot of things... but the cache refresher is somewhat
|
||||
// convoluted and it's hard to tell what to clear exactly ;-(
|
||||
|
||||
|
||||
// clear the parent - NOT (why?)
|
||||
//var exist = (CacheValues) cache.GetCacheItem(key);
|
||||
//if (exist != null)
|
||||
// cache.ClearCacheItem(PublishedMediaCacheKey + GetValuesValue(exist.Values, "parentID"));
|
||||
|
||||
|
||||
// clear the item
|
||||
cache.ClearCacheItem(key);
|
||||
|
||||
|
||||
// clear all children - in case we moved and their path has changed
|
||||
var fid = "/" + sid + "/";
|
||||
cache.ClearCacheObjectTypes<CacheValues>((k, v) =>
|
||||
|
||||
@@ -217,7 +217,11 @@ namespace Umbraco.Web.Scheduling
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
if (_isCompleted) return false;
|
||||
if (_isCompleted)
|
||||
{
|
||||
_logger.Debug<BackgroundTaskRunner>(_logPrefix + "Task cannot be added {0}, the task runner is already shutdown", task.GetType);
|
||||
return false;
|
||||
}
|
||||
|
||||
// add task
|
||||
_logger.Debug<BackgroundTaskRunner>(_logPrefix + "Task added {0}", task.GetType);
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System.Web;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Web.Routing;
|
||||
|
||||
namespace Umbraco.Web.Scheduling
|
||||
{
|
||||
@@ -14,60 +17,74 @@ namespace Umbraco.Web.Scheduling
|
||||
/// </remarks>
|
||||
internal sealed class Scheduler : ApplicationEventHandler
|
||||
{
|
||||
private static BackgroundTaskRunner<IBackgroundTask> _keepAliveRunner;
|
||||
private static BackgroundTaskRunner<IBackgroundTask> _publishingRunner;
|
||||
private static BackgroundTaskRunner<IBackgroundTask> _tasksRunner;
|
||||
private static BackgroundTaskRunner<IBackgroundTask> _scrubberRunner;
|
||||
private static volatile bool _started;
|
||||
private static readonly object Locker = new object();
|
||||
private BackgroundTaskRunner<IBackgroundTask> _keepAliveRunner;
|
||||
private BackgroundTaskRunner<IBackgroundTask> _publishingRunner;
|
||||
private BackgroundTaskRunner<IBackgroundTask> _tasksRunner;
|
||||
private BackgroundTaskRunner<IBackgroundTask> _scrubberRunner;
|
||||
private bool _started = false;
|
||||
private object _locker = new object();
|
||||
private IBackgroundTask[] _tasks;
|
||||
|
||||
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
||||
{
|
||||
if (umbracoApplication.Context == null)
|
||||
return;
|
||||
|
||||
//subscribe to app init so we can subsribe to the application events
|
||||
UmbracoApplicationBase.ApplicationInit += (sender, args) =>
|
||||
// backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly
|
||||
_keepAliveRunner = new BackgroundTaskRunner<IBackgroundTask>("KeepAlive", applicationContext.ProfilingLogger.Logger);
|
||||
_publishingRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledPublishing", applicationContext.ProfilingLogger.Logger);
|
||||
_tasksRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledTasks", applicationContext.ProfilingLogger.Logger);
|
||||
_scrubberRunner = new BackgroundTaskRunner<IBackgroundTask>("LogScrubber", applicationContext.ProfilingLogger.Logger);
|
||||
|
||||
//We will start the whole process when a successful request is made
|
||||
UmbracoModule.RouteAttempt += UmbracoModuleRouteAttempt;
|
||||
}
|
||||
|
||||
private void UmbracoModuleRouteAttempt(object sender, RoutableAttemptEventArgs e)
|
||||
{
|
||||
switch (e.Outcome)
|
||||
{
|
||||
var app = (HttpApplication)sender;
|
||||
case EnsureRoutableOutcome.IsRoutable:
|
||||
case EnsureRoutableOutcome.NotDocumentRequest:
|
||||
RegisterBackgroundTasks(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//subscribe to the end of a successful request (a handler actually executed)
|
||||
app.PostRequestHandlerExecute += (o, eventArgs) =>
|
||||
private void RegisterBackgroundTasks(UmbracoRequestEventArgs e)
|
||||
{
|
||||
//remove handler, we're done
|
||||
UmbracoModule.RouteAttempt -= UmbracoModuleRouteAttempt;
|
||||
|
||||
LazyInitializer.EnsureInitialized(ref _tasks, ref _started, ref _locker, () =>
|
||||
{
|
||||
LogHelper.Debug<Scheduler>(() => "Initializing the scheduler");
|
||||
var settings = UmbracoConfig.For.UmbracoSettings();
|
||||
|
||||
var tasks = new List<IBackgroundTask>
|
||||
{
|
||||
if (_started == false)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (_started == false)
|
||||
{
|
||||
_started = true;
|
||||
LogHelper.Debug<Scheduler>(() => "Initializing the scheduler");
|
||||
|
||||
// backgrounds runners are web aware, if the app domain dies, these tasks will wind down correctly
|
||||
_keepAliveRunner = new BackgroundTaskRunner<IBackgroundTask>("KeepAlive", applicationContext.ProfilingLogger.Logger);
|
||||
_publishingRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledPublishing", applicationContext.ProfilingLogger.Logger);
|
||||
_tasksRunner = new BackgroundTaskRunner<IBackgroundTask>("ScheduledTasks", applicationContext.ProfilingLogger.Logger);
|
||||
_scrubberRunner = new BackgroundTaskRunner<IBackgroundTask>("LogScrubber", applicationContext.ProfilingLogger.Logger);
|
||||
|
||||
var settings = UmbracoConfig.For.UmbracoSettings();
|
||||
|
||||
// ping/keepalive
|
||||
// on all servers
|
||||
_keepAliveRunner.Add(new KeepAlive(_keepAliveRunner, 60000, 300000, applicationContext));
|
||||
|
||||
// scheduled publishing/unpublishing
|
||||
// install on all, will only run on non-slaves servers
|
||||
_publishingRunner.Add(new ScheduledPublishing(_publishingRunner, 60000, 60000, applicationContext, settings));
|
||||
_tasksRunner.Add(new ScheduledTasks(_tasksRunner, 60000, 60000, applicationContext, settings));
|
||||
|
||||
// log scrubbing
|
||||
// install on all, will only run on non-slaves servers
|
||||
_scrubberRunner.Add(new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings), applicationContext, settings));
|
||||
}
|
||||
}
|
||||
}
|
||||
new KeepAlive(_keepAliveRunner, 60000, 300000, e.UmbracoContext.Application),
|
||||
new ScheduledPublishing(_publishingRunner, 60000, 60000, e.UmbracoContext.Application, settings),
|
||||
new ScheduledTasks(_tasksRunner, 60000, 60000, e.UmbracoContext.Application, settings),
|
||||
new LogScrubber(_scrubberRunner, 60000, LogScrubber.GetLogScrubbingInterval(settings), e.UmbracoContext.Application, settings)
|
||||
};
|
||||
};
|
||||
|
||||
// ping/keepalive
|
||||
// on all servers
|
||||
_keepAliveRunner.TryAdd(tasks[0]);
|
||||
|
||||
// scheduled publishing/unpublishing
|
||||
// install on all, will only run on non-slaves servers
|
||||
_publishingRunner.TryAdd(tasks[1]);
|
||||
|
||||
_tasksRunner.TryAdd(tasks[2]);
|
||||
|
||||
// log scrubbing
|
||||
// install on all, will only run on non-slaves servers
|
||||
_scrubberRunner.TryAdd(tasks[3]);
|
||||
|
||||
return tasks.ToArray();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Web;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
@@ -13,6 +15,8 @@ namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e)
|
||||
{
|
||||
if (e.ProductName != GlobalSettings.UmbracoMigrationName) return;
|
||||
|
||||
if (HttpContext.Current == null) return;
|
||||
|
||||
var http = new HttpContextWrapper(HttpContext.Current);
|
||||
|
||||
+8
-5
@@ -5,21 +5,24 @@ using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// This will execute after upgrading to remove any xml cache for media that are currently in the bin
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will execute for specific versions -
|
||||
///
|
||||
/// <remarks>
|
||||
/// This will execute for specific versions -
|
||||
///
|
||||
/// * If current is less than or equal to 7.0.0
|
||||
/// </remarks>
|
||||
public class ClearMediaXmlCacheForDeletedItemsAfterUpgrade : MigrationStartupHander
|
||||
{
|
||||
{
|
||||
protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e)
|
||||
{
|
||||
if (e.ProductName != GlobalSettings.UmbracoMigrationName) return;
|
||||
|
||||
var target70 = new Version(7, 0, 0);
|
||||
|
||||
if (e.ConfiguredVersion <= target70)
|
||||
@@ -28,7 +31,7 @@ namespace Umbraco.Web.Strategies.Migrations
|
||||
// http://issues.umbraco.org/issue/U4-3876
|
||||
|
||||
var sql = @"DELETE FROM cmsContentXml WHERE nodeId IN
|
||||
(SELECT nodeId FROM (SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
|
||||
(SELECT nodeId FROM (SELECT DISTINCT cmsContentXml.nodeId FROM cmsContentXml
|
||||
INNER JOIN umbracoNode ON cmsContentXml.nodeId = umbracoNode.id
|
||||
WHERE nodeObjectType = '" + Constants.ObjectTypes.Media + "' AND " + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("path") + " LIKE '%-21%') x)";
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using Umbraco.Core.Persistence.Migrations;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
@@ -20,6 +21,8 @@ namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e)
|
||||
{
|
||||
if (e.ProductName != GlobalSettings.UmbracoMigrationName) return;
|
||||
|
||||
var target720 = new Version(7, 2, 0);
|
||||
|
||||
if (e.ConfiguredVersion <= target720)
|
||||
@@ -95,7 +98,7 @@ namespace Umbraco.Web.Strategies.Migrations
|
||||
e.MigrationContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF;", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsDataTypePreValues"))));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
transaction.Complete();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Umbraco.Core;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
|
||||
namespace Umbraco.Web.Strategies.Migrations
|
||||
@@ -41,9 +42,20 @@ namespace Umbraco.Web.Strategies.Migrations
|
||||
|
||||
private void MigrationRunner_Migrated(MigrationRunner sender, Core.Events.MigrationEventArgs e)
|
||||
{
|
||||
AfterMigration(sender, e);
|
||||
if (TargetProductNames.Length == 0 || TargetProductNames.Contains(e.ProductName))
|
||||
{
|
||||
AfterMigration(sender, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void AfterMigration(MigrationRunner sender, Core.Events.MigrationEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// Override to specify explicit target product names
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Leaving empty will run for all migration products
|
||||
/// </remarks>
|
||||
public virtual string[] TargetProductNames { get { return new string[] {}; } }
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Events;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
@@ -22,6 +23,8 @@ namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e)
|
||||
{
|
||||
if (e.ProductName != GlobalSettings.UmbracoMigrationName) return;
|
||||
|
||||
var target73 = new Version(7, 3, 0);
|
||||
|
||||
if (e.ConfiguredVersion <= target73)
|
||||
|
||||
@@ -8,6 +8,7 @@ using Umbraco.Core.Persistence.Migrations;
|
||||
using Umbraco.Core.Persistence.UnitOfWork;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
@@ -19,6 +20,8 @@ namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e)
|
||||
{
|
||||
if (e.ProductName != GlobalSettings.UmbracoMigrationName) return;
|
||||
|
||||
var target = new Version(6, 0, 0);
|
||||
if (e.ConfiguredVersion < target)
|
||||
{
|
||||
@@ -69,7 +72,7 @@ namespace Umbraco.Web.Strategies.Migrations
|
||||
transaction.Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using Umbraco.Core.Events;
|
||||
using Umbraco.Core.Persistence.Migrations;
|
||||
using Umbraco.Core.Services;
|
||||
using umbraco.interfaces;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.Strategies.Migrations
|
||||
{
|
||||
@@ -12,15 +13,17 @@ namespace Umbraco.Web.Strategies.Migrations
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This cannot execute as part of a db migration since we need access to the services/repos.
|
||||
///
|
||||
/// This will execute for specific versions -
|
||||
///
|
||||
///
|
||||
/// This will execute for specific versions -
|
||||
///
|
||||
/// * If current is less than or equal to 7.0.0
|
||||
/// </remarks>
|
||||
public class RebuildMediaXmlCacheAfterUpgrade : MigrationStartupHander
|
||||
{
|
||||
protected override void AfterMigration(MigrationRunner sender, MigrationEventArgs e)
|
||||
{
|
||||
if (e.ProductName != GlobalSettings.UmbracoMigrationName) return;
|
||||
|
||||
var target70 = new Version(7, 0, 0);
|
||||
|
||||
if (e.ConfiguredVersion <= target70)
|
||||
|
||||
@@ -28,19 +28,21 @@ namespace Umbraco.Web.Strategies
|
||||
private DatabaseServerRegistrar _registrar;
|
||||
private BackgroundTaskRunner<IBackgroundTask> _backgroundTaskRunner;
|
||||
private bool _started = false;
|
||||
private TouchServerTask _task;
|
||||
private object _lock = new object();
|
||||
|
||||
// bind to events
|
||||
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
|
||||
{
|
||||
_registrar = ServerRegistrarResolver.Current.Registrar as DatabaseServerRegistrar;
|
||||
|
||||
// only for the DatabaseServerRegistrar
|
||||
if (_registrar == null) return;
|
||||
|
||||
_backgroundTaskRunner = new BackgroundTaskRunner<IBackgroundTask>(
|
||||
new BackgroundTaskRunnerOptions { AutoStart = true },
|
||||
applicationContext.ProfilingLogger.Logger);
|
||||
|
||||
// only for the DatabaseServerRegistrar
|
||||
if (_registrar == null) return;
|
||||
|
||||
//We will start the whole process when a successful request is made
|
||||
UmbracoModule.RouteAttempt += UmbracoModuleRouteAttempt;
|
||||
}
|
||||
@@ -61,40 +63,34 @@ namespace Umbraco.Web.Strategies
|
||||
switch (e.Outcome)
|
||||
{
|
||||
case EnsureRoutableOutcome.IsRoutable:
|
||||
// front-end request
|
||||
RegisterServer(e);
|
||||
//remove handler, we're done
|
||||
UmbracoModule.RouteAttempt -= UmbracoModuleRouteAttempt;
|
||||
break;
|
||||
case EnsureRoutableOutcome.NotDocumentRequest:
|
||||
// anything else (back-end request, service...)
|
||||
//so it's not a document request, we'll check if it's a back office request
|
||||
if (e.HttpContext.Request.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath))
|
||||
{
|
||||
RegisterServer(e);
|
||||
//remove handler, we're done
|
||||
UmbracoModule.RouteAttempt -= UmbracoModuleRouteAttempt;
|
||||
}
|
||||
break;
|
||||
RegisterBackgroundTasks(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterServer(UmbracoRequestEventArgs e)
|
||||
private void RegisterBackgroundTasks(UmbracoRequestEventArgs e)
|
||||
{
|
||||
//only process once
|
||||
if (_started) return;
|
||||
//remove handler, we're done
|
||||
UmbracoModule.RouteAttempt -= UmbracoModuleRouteAttempt;
|
||||
|
||||
_started = true;
|
||||
//only perform this one time ever
|
||||
LazyInitializer.EnsureInitialized(ref _task, ref _started, ref _lock, () =>
|
||||
{
|
||||
var serverAddress = e.UmbracoContext.Application.UmbracoApplicationUrl;
|
||||
var svc = e.UmbracoContext.Application.Services.ServerRegistrationService;
|
||||
|
||||
var serverAddress = e.UmbracoContext.Application.UmbracoApplicationUrl;
|
||||
var svc = e.UmbracoContext.Application.Services.ServerRegistrationService;
|
||||
var task = new TouchServerTask(_backgroundTaskRunner,
|
||||
15000, //delay before first execution
|
||||
_registrar.Options.RecurringSeconds*1000, //amount of ms between executions
|
||||
svc, _registrar, serverAddress);
|
||||
|
||||
//Perform the rest async, we don't want to block the startup sequence
|
||||
// this will just reoccur on a background thread
|
||||
_backgroundTaskRunner.TryAdd(task);
|
||||
|
||||
//Perform the rest async, we don't want to block the startup sequence
|
||||
// this will just reoccur on a background thread
|
||||
_backgroundTaskRunner.Add(new TouchServerTask(_backgroundTaskRunner,
|
||||
15000, //delay before first execution
|
||||
_registrar.Options.RecurringSeconds * 1000, //amount of ms between executions
|
||||
svc, _registrar, serverAddress));
|
||||
return task;
|
||||
});
|
||||
}
|
||||
|
||||
private class TouchServerTask : RecurringTaskBase
|
||||
|
||||
@@ -123,6 +123,7 @@ namespace Umbraco.Web.Trees
|
||||
menu.Items.Add<ActionMove>(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), true);
|
||||
}
|
||||
}
|
||||
menu.Items.Add<ActionCopy>(Services.TextService.Localize(string.Format("actions/{0}", ActionCopy.Instance.Alias)));
|
||||
menu.Items.Add<ActionExport>(Services.TextService.Localize(string.Format("actions/{0}", ActionExport.Instance.Alias)), true).ConvertLegacyMenuItem(new UmbracoEntity
|
||||
{
|
||||
Id = int.Parse(id),
|
||||
|
||||
@@ -92,6 +92,7 @@ namespace Umbraco.Web.Trees
|
||||
{
|
||||
menu.Items.Add<ActionDelete>(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias)));
|
||||
menu.Items.Add<ActionMove>(Services.TextService.Localize(string.Format("actions/{0}", ActionMove.Instance.Alias)), hasSeparator: true);
|
||||
menu.Items.Add<ActionCopy>(Services.TextService.Localize(string.Format("actions/{0}", ActionCopy.Instance.Alias)));
|
||||
}
|
||||
|
||||
return menu;
|
||||
|
||||
@@ -77,8 +77,8 @@ namespace Umbraco.Web.Trees
|
||||
protected override void OnRenderFileNode(ref XmlTreeNode xNode)
|
||||
{
|
||||
ChangeNodeAction(xNode);
|
||||
xNode.Icon = "settingView.gif";
|
||||
xNode.OpenIcon = "settingView.gif";
|
||||
xNode.Icon = "icon-article";
|
||||
xNode.OpenIcon = "icon-article";
|
||||
|
||||
xNode.Text = xNode.Text.StripFileExtension();
|
||||
}
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ namespace umbraco.cms.presentation.settings.stylesheet
|
||||
|
||||
lttPath.Text = "<a id=\"" + lttPath.ClientID + "\" target=\"_blank\" href=\"" + stylesheet.VirtualPath + "\">" + stylesheet.VirtualPath + "</a>";
|
||||
editorSource.Text = stylesheet.Content;
|
||||
TreeSyncPath = DeepLink.GetTreePathFromFilePath(filename);
|
||||
TreeSyncPath = DeepLink.GetTreePathFromFilePath(filename).TrimEnd(".css");
|
||||
|
||||
// name derives from path, without the .css extension, clean for xss
|
||||
NameTxt.Text = stylesheet.Path.TrimEnd(".css").CleanForXss('\\', '/');
|
||||
|
||||
Reference in New Issue
Block a user