Merged with dev-v8

This commit is contained in:
Kenn Jacobsen
2019-03-20 11:50:35 +01:00
183 changed files with 1932 additions and 1150 deletions
+2 -4
View File
@@ -103,11 +103,9 @@ There's two big areas that you should know about:
1. The Umbraco backoffice is a extensible AngularJS app and requires you to run a `gulp dev` command while you're working with it, so changes are copied over to the appropriate directories and you can refresh your browser to view the results of your changes.
You may need to run the following commands to set up gulp properly:
```
npm cache clean
npm install -g gulp
npm install -g gulp-cli
npm cache clean --force
npm install
gulp build
npm run build
```
2. "The rest" is a C# based codebase, with some traces of our WebForms past but mostly ASP.NET MVC based these days. You can make changes, build them in Visual Studio, and hit `F5` to see the result.
+1 -1
View File
@@ -2,7 +2,7 @@
- [ ] I have added steps to test this contribution in the description below
If there's an existing issue for this PR then this fixes: <!-- link to the issue here! -->
If there's an existing issue for this PR then this fixes <!-- link to the issue here! -->
### Description
<!-- A description of the changes proposed in the pull-request and how to test these changes -->
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<metadata minClientVersion="4.1.0">
<id>UmbracoCms.Core</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<metadata minClientVersion="4.1.0">
<id>UmbracoCms.Web</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
+2 -2
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<metadata minClientVersion="4.1.0">
<id>UmbracoCms</id>
<version>8.0.0</version>
<title>Umbraco Cms</title>
@@ -25,7 +25,7 @@
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.4.0, 2.999999)" />
<dependency id="Umbraco.ModelsBuilder.Ui" version="[8.0.1]" />
<dependency id="Umbraco.ModelsBuilder.Ui" version="[8.0.4]" />
<dependency id="ImageProcessor.Web" version="[4.10.0.100,4.999999)" />
<dependency id="ImageProcessor.Web.Config" version="[2.5.0.100,2.999999)" />
<dependency id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="[2.0.1,2.999999)" />
+46 -36
View File
@@ -126,45 +126,55 @@
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='Microsoft.CodeAnalysis.CSharp')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='log4net')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Data.SqlServerCe')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.ValueTuple')" xdt:Transform="Remove" />
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" xdt:Transform="Remove" />
</assemblyBinding>
</runtime>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.2.1.0" newVersion="1.2.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.6.0" newVersion="5.2.6.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.6.0" newVersion="5.2.6.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.2.3.0" newVersion="1.2.3.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-5.2.7.0" newVersion="5.2.7.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.web.webPages.razor xdt:Transform="Remove" />
-93
View File
@@ -1,93 +0,0 @@
param($installPath, $toolsPath, $package, $project)
Write-Host "installPath:" "${installPath}"
Write-Host "toolsPath:" "${toolsPath}"
Write-Host " "
if ($project) {
$dateTime = Get-Date -Format yyyyMMdd-HHmmss
# 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\$dateTime"
Write-Host "backupPath:" "${backupPath}"
$copyLogsPath = Join-Path $backupPath "CopyLogs"
Write-Host "copyLogsPath:" "${copyLogsPath}"
$umbracoBinFolder = Join-Path $projectPath "bin"
Write-Host "umbracoBinFolder:" "${umbracoBinFolder}"
# Create backup folder and logs folder if it doesn't exist yet
New-Item -ItemType Directory -Force -Path $backupPath
New-Item -ItemType Directory -Force -Path $copyLogsPath
# After backing up, remove all umbraco dlls from bin folder in case dll files are included in the VS project
# See: http://issues.umbraco.org/issue/U4-4930
if(Test-Path $umbracoBinFolder) {
$umbracoBinBackupPath = Join-Path $backupPath "bin"
New-Item -ItemType Directory -Force -Path $umbracoBinBackupPath
robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log
# Delete files Umbraco ships with
if(Test-Path $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll) { Remove-Item $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.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\Umbraco.Web.dll) { Remove-Item $umbracoBinFolder\Umbraco.Web.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.ModelsBuilder.dll) { Remove-Item $umbracoBinFolder\Umbraco.ModelsBuilder.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Umbraco.ModelsBuilder.AspNet.dll) { Remove-Item $umbracoBinFolder\Umbraco.ModelsBuilder.AspNet.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\Umbraco.Examine.dll) { Remove-Item $umbracoBinFolder\Umbraco.Examine.dll -Force -Confirm:$false }
# Delete files Umbraco depends upon
$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 }
if(Test-Path $umbracoBinFolder\AutoMapper.dll) { Remove-Item $umbracoBinFolder\AutoMapper.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\AutoMapper.Net4.dll) { Remove-Item $umbracoBinFolder\AutoMapper.Net4.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ClientDependency.Core.dll) { Remove-Item $umbracoBinFolder\ClientDependency.Core.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ClientDependency.Core.Mvc.dll) { Remove-Item $umbracoBinFolder\ClientDependency.Core.Mvc.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\CookComputing.XmlRpcV2.dll) { Remove-Item $umbracoBinFolder\CookComputing.XmlRpcV2.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Examine.dll) { Remove-Item $umbracoBinFolder\Examine.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\HtmlAgilityPack.dll) { Remove-Item $umbracoBinFolder\HtmlAgilityPack.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ICSharpCode.SharpZipLib.dll) { Remove-Item $umbracoBinFolder\ICSharpCode.SharpZipLib.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ImageProcessor.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\ImageProcessor.Web.dll) { Remove-Item $umbracoBinFolder\ImageProcessor.Web.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Lucene.Net.dll) { Remove-Item $umbracoBinFolder\Lucene.Net.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.IO.RecyclableMemoryStream.dll) { Remove-Item $umbracoBinFolder\Microsoft.IO.RecyclableMemoryStream.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Core.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.AspNet.Identity.Owin.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll) { Remove-Item $umbracoBinFolder\Microsoft.CodeAnalysis.CSharp.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.CodeAnalysis.dll) { Remove-Item $umbracoBinFolder\Microsoft.CodeAnalysis.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Owin.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Host.SystemWeb.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Host.SystemWeb.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.Cookies.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.Cookies.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Owin.Security.OAuth.dll) { Remove-Item $umbracoBinFolder\Microsoft.Owin.Security.OAuth.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Infrastructure.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Infrastructure.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MiniProfiler.dll) { Remove-Item $umbracoBinFolder\MiniProfiler.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Newtonsoft.Json.dll) { Remove-Item $umbracoBinFolder\Newtonsoft.Json.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Owin.dll) { Remove-Item $umbracoBinFolder\Owin.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Semver.dll) { Remove-Item $umbracoBinFolder\Semver.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Collections.Immutable.dll) { Remove-Item $umbracoBinFolder\System.Collections.Immutable.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Reflection.Metadata.dll) { Remove-Item $umbracoBinFolder\System.Reflection.Metadata.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Net.Http.Extensions.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Extensions.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Net.Http.Formatting.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Formatting.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Net.Http.Primitives.dll) { Remove-Item $umbracoBinFolder\System.Net.Http.Primitives.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\System.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Http.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Http.WebHost.dll) { Remove-Item $umbracoBinFolder\System.Web.Http.WebHost.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Mvc.dll) { Remove-Item $umbracoBinFolder\System.Web.Mvc.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.Razor.dll) { Remove-Item $umbracoBinFolder\System.Web.Razor.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.WebPages.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.WebPages.Deployment.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.Deployment.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Web.WebPages.Razor.dll) { Remove-Item $umbracoBinFolder\System.Web.WebPages.Razor.dll -Force -Confirm:$false }
}
}
-27
View File
@@ -11,37 +11,15 @@ 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\$dateTime"
Write-Host "backupPath:" "${backupPath}"
$copyLogsPath = Join-Path $backupPath "CopyLogs"
Write-Host "copyLogsPath:" "${copyLogsPath}"
$webConfigSource = Join-Path $projectPath "Web.config"
Write-Host "webConfigSource:" "${webConfigSource}"
$configFolder = Join-Path $projectPath "Config"
Write-Host "configFolder:" "${configFolder}"
# Create backup folder and logs folder if it doesn't exist yet
New-Item -ItemType Directory -Force -Path $backupPath
New-Item -ItemType Directory -Force -Path $copyLogsPath
# Create a backup of original web.config
Copy-Item $webConfigSource $backupPath -Force
# Backup config files folder
if(Test-Path $configFolder) {
$umbracoBackupPath = Join-Path $backupPath "Config"
New-Item -ItemType Directory -Force -Path $umbracoBackupPath
robocopy $configFolder $umbracoBackupPath /e /LOG:$copyLogsPath\ConfigBackup.log
}
# Copy umbraco and umbraco_files from package to project folder
$umbracoFolder = Join-Path $projectPath "Umbraco"
New-Item -ItemType Directory -Force -Path $umbracoFolder
$umbracoFolderSource = Join-Path $installPath "UmbracoFiles\Umbraco"
$umbracoBackupPath = Join-Path $backupPath "Umbraco"
New-Item -ItemType Directory -Force -Path $umbracoBackupPath
robocopy $umbracoFolder $umbracoBackupPath /e /LOG:$copyLogsPath\UmbracoBackup.log
robocopy $umbracoFolderSource $umbracoFolder /is /it /e /xf UI.xml /LOG:$copyLogsPath\UmbracoCopy.log
$copyWebconfig = $true
@@ -100,11 +78,6 @@ if ($project) {
}
}
$installFolder = Join-Path $projectPath "Install"
if(Test-Path $installFolder) {
Remove-Item $installFolder -Force -Recurse -Confirm:$false
}
# Open appropriate readme
if($copyWebconfig -eq $true)
{
+11 -2
View File
@@ -15,7 +15,12 @@
[Parameter(Mandatory=$false)]
[Alias("c")]
[Alias("cont")]
[switch] $continue = $false
[switch] $continue = $false,
# execute a command
[Parameter(Mandatory=$false, ValueFromRemainingArguments=$true)]
[String[]]
$command
)
# ################################################################
@@ -475,7 +480,11 @@
# run
if (-not $get)
{
$ubuild.Build()
if ($command.Length -eq 0)
{
$command = @( "Build" )
}
$ubuild.RunMethod($command);
if ($ubuild.OnError()) { return }
}
if ($get) { return $ubuild }
+5 -1
View File
@@ -133,7 +133,11 @@ namespace Umbraco.Core.Composing
Configs.RegisterWith(_register);
return _register.CreateFactory();
IFactory factory = null;
// ReSharper disable once AccessToModifiedClosure -- on purpose
_register.Register(_ => factory, Lifetime.Singleton);
factory = _register.CreateFactory();
return factory;
}
/// <summary>
+1 -2
View File
@@ -8,7 +8,6 @@
/// <summary>
/// Compose.
/// </summary>
/// <param name="composition"></param>
void Compose(Composition composition);
}
}
}
+2 -4
View File
@@ -4,10 +4,8 @@
/// Represents a core <see cref="IComposer"/>.
/// </summary>
/// <remarks>
/// <para>All core composers are required by (compose before) all user composers,
/// and require (compose after) all runtime composers.</para>
/// <para>Core composers compose after the initial composer, and before user composers.</para>
/// </remarks>
[ComposeAfter(typeof(IRuntimeComposer))]
public interface ICoreComposer : IComposer
{ }
}
}
@@ -1,11 +0,0 @@
namespace Umbraco.Core.Composing
{
/// <summary>
/// Represents a runtime <see cref="IComposer"/>.
/// </summary>
/// <remarks>
/// <para>All runtime composers are required by (compose before) all core composers</para>
/// </remarks>
public interface IRuntimeComposer : IComposer
{ }
}
+2 -2
View File
@@ -4,9 +4,9 @@
/// Represents a user <see cref="IComposer"/>.
/// </summary>
/// <remarks>
/// <para>All user composers require (compose after) all core composers.</para>
/// <para>User composers compose after core composers, and before the final composer.</para>
/// </remarks>
[ComposeAfter(typeof(ICoreComposer))]
public interface IUserComposer : IComposer
{ }
}
}
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Composing
{
/// <summary>
/// Provides a base class for collections of types.
/// </summary>
public abstract class TypeCollectionBuilderBase<TCollection, TConstraint> : ICollectionBuilder<TCollection, Type>
where TCollection : class, IBuilderCollection<Type>
{
private readonly HashSet<Type> _types = new HashSet<Type>();
private Type Validate(Type type, string action)
{
if (!typeof(TConstraint).IsAssignableFrom(type))
throw new InvalidOperationException($"Cannot {action} type {type.FullName} as it does not inherit from/implement {typeof(TConstraint).FullName}.");
return type;
}
public void Add(Type type) => _types.Add(Validate(type, "add"));
public void Add<T>() => Add(typeof(T));
public void Add(IEnumerable<Type> types)
{
foreach (var type in types) Add(type);
}
public void Remove(Type type) => _types.Remove(Validate(type, "remove"));
public void Remove<T>() => Remove(typeof(T));
public TCollection CreateCollection(IFactory factory)
{
return factory.CreateInstance<TCollection>(_types);
}
public void RegisterWith(IRegister register)
{
register.Register(CreateCollection, Lifetime.Singleton);
}
}
}
@@ -4,6 +4,7 @@ using System.Linq;
using System.Net.Configuration;
using System.Web;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Xml.Linq;
using Umbraco.Core.IO;
@@ -17,16 +18,15 @@ namespace Umbraco.Core.Configuration
/// </summary>
public class GlobalSettings : IGlobalSettings
{
private string _localTempPath;
#region Private static fields
// TODO these should not be static
private static string _reservedPaths;
private static string _reservedUrls;
//ensure the built on (non-changeable) reserved paths are there at all times
internal const string StaticReservedPaths = "~/app_plugins/,~/install/,~/mini-profiler-resources/,"; //must end with a comma!
internal const string StaticReservedUrls = "~/config/splashes/noNodes.aspx,~/.well-known,"; //must end with a comma!
#endregion
/// <summary>
/// Used in unit testing to reset all config items that were set with property setters (i.e. did not come from config)
@@ -131,7 +131,7 @@ namespace Umbraco.Core.Configuration
: "~/App_Data/umbraco.config";
}
}
/// <summary>
/// Gets the path to umbraco's root directory (/umbraco by default).
/// </summary>
@@ -163,7 +163,7 @@ namespace Umbraco.Core.Configuration
SaveSetting(Constants.AppSettings.ConfigurationStatus, value);
}
}
/// <summary>
/// Saves a setting into the configuration file.
/// </summary>
@@ -206,7 +206,7 @@ namespace Umbraco.Core.Configuration
ConfigurationManager.RefreshSection("appSettings");
}
}
/// <summary>
/// Gets a value indicating whether umbraco is running in [debug mode].
/// </summary>
@@ -250,7 +250,7 @@ namespace Umbraco.Core.Configuration
}
}
}
/// <summary>
/// Returns the number of days that should take place between version checks.
/// </summary>
@@ -269,7 +269,7 @@ namespace Umbraco.Core.Configuration
}
}
}
/// <inheritdoc />
public LocalTempStorage LocalTempStorageLocation
{
@@ -288,25 +288,43 @@ namespace Umbraco.Core.Configuration
{
get
{
if (_localTempPath != null)
return _localTempPath;
switch (LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
return System.IO.Path.Combine(HttpRuntime.CodegenDir, "UmbracoData");
return _localTempPath = System.IO.Path.Combine(HttpRuntime.CodegenDir, "UmbracoData");
case LocalTempStorage.EnvironmentTemp:
// include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path - assuming we cannot have SHA1 collisions on AppDomainAppId
var appDomainHash = HttpRuntime.AppDomainAppId.GenerateHash();
return System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", appDomainHash);
// environment temp is unique, we need a folder per site
// use a hash
// combine site name and application id
// site name is a Guid on Cloud
// application id is eg /LM/W3SVC/123456/ROOT
// the combination is unique on one server
// and, if a site moves from worker A to B and then back to A...
// hopefully it gets a new Guid or new application id?
var siteName = HostingEnvironment.SiteName;
var applicationId = HostingEnvironment.ApplicationID; // ie HttpRuntime.AppDomainAppId
var hashString = siteName + "::" + applicationId;
var hash = hashString.GenerateHash();
var siteTemp = System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", hash);
return _localTempPath = System.IO.Path.Combine(siteTemp, "umbraco.config");
//case LocalTempStorage.Default:
//case LocalTempStorage.Unknown:
default:
return IOHelper.MapPath("~/App_Data/TEMP");
return _localTempPath = IOHelper.MapPath("~/App_Data/TEMP");
}
}
}
/// <summary>
/// Gets the default UI language.
/// </summary>
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Serilog.Events;
using Serilog.Formatting.Compact.Reader;
@@ -10,13 +11,15 @@ namespace Umbraco.Core.Logging.Viewer
internal class JsonLogViewer : LogViewerSourceBase
{
private readonly string _logsPath;
private readonly ILogger _logger;
public JsonLogViewer(string logsPath = "", string searchPath = "") : base(searchPath)
public JsonLogViewer(ILogger logger, string logsPath = "", string searchPath = "") : base(searchPath)
{
if (string.IsNullOrEmpty(logsPath))
logsPath = $@"{AppDomain.CurrentDomain.BaseDirectory}\App_Data\Logs\";
_logsPath = logsPath;
_logger = logger;
}
private const int FileSizeCap = 100;
@@ -77,8 +80,14 @@ namespace Umbraco.Core.Logging.Viewer
using (var stream = new StreamReader(fs))
{
var reader = new LogEventReader(stream);
while (reader.TryRead(out var evt))
while (TryRead(reader, out var evt))
{
//We may get a null if log line is malformed
if (evt == null)
{
continue;
}
if (count > skip + take)
{
break;
@@ -105,5 +114,21 @@ namespace Umbraco.Core.Logging.Viewer
return logs;
}
private bool TryRead(LogEventReader reader, out LogEvent evt)
{
try
{
return reader.TryRead(out evt);
}
catch (JsonReaderException ex)
{
// As we are reading/streaming one line at a time in the JSON file
// Thus we can not report the line number, as it will always be 1
_logger.Error<JsonLogViewer>(ex, "Unable to parse a line in the JSON log file");
evt = null;
return true;
}
}
}
}
@@ -9,7 +9,7 @@ namespace Umbraco.Core.Logging.Viewer
{
public void Compose(Composition composition)
{
composition.SetLogViewer(_ => new JsonLogViewer());
composition.SetLogViewer(_ => new JsonLogViewer(composition.Logger));
}
}
}
@@ -5,6 +5,7 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Migrations.Upgrade.V_7_12_0;
using Umbraco.Core.Migrations.Upgrade.V_7_14_0;
using Umbraco.Core.Migrations.Upgrade.V_8_0_0;
using Umbraco.Core.Migrations.Upgrade.V_8_0_1;
namespace Umbraco.Core.Migrations.Upgrade
{
@@ -137,6 +138,8 @@ namespace Umbraco.Core.Migrations.Upgrade
To<RenameLabelAndRichTextPropertyEditorAliases>("{E0CBE54D-A84F-4A8F-9B13-900945FD7ED9}");
To<MergeDateAndDateTimePropertyEditor>("{78BAF571-90D0-4D28-8175-EF96316DA789}");
To<ChangeNuCacheJsonFormat>("{80C0A0CB-0DD5-4573-B000-C4B7C313C70D}");
//FINAL
@@ -167,7 +170,7 @@ namespace Umbraco.Core.Migrations.Upgrade
From("{init-7.12.4}").To("{init-7.10.0}"); // same as 7.12.0
From("{init-7.13.0}").To("{init-7.10.0}"); // same as 7.12.0
From("{init-7.13.1}").To("{init-7.10.0}"); // same as 7.12.0
// 7.14.0 has migrations, handle it...
// clone going from 7.10 to 1350617A (the last one before we started to merge 7.12 migrations), then
// clone going from CF51B39B (after 7.12 migrations) to 0009109C (the last one before we started to merge 7.12 migrations),
@@ -0,0 +1,16 @@
using Umbraco.Core.Migrations.PostMigrations;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_1
{
public class ChangeNuCacheJsonFormat : MigrationBase
{
public ChangeNuCacheJsonFormat(IMigrationContext context) : base(context)
{ }
public override void Migrate()
{
// nothing - just adding the post-migration
Context.AddPostMigration<RebuildPublishedSnapshot>();
}
}
}
@@ -0,0 +1,16 @@
using Umbraco.Core.Migrations.PostMigrations;
namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
{
public class ChangeNuCacheJsonFormat : MigrationBase
{
public ChangeNuCacheJsonFormat(IMigrationContext context) : base(context)
{ }
public override void Migrate()
{
// nothing - just adding the post-migration
Context.AddPostMigration<RebuildPublishedSnapshot>();
}
}
}
@@ -11,14 +11,14 @@ namespace Umbraco.Core.Models.PublishedContent
/// <summary>
/// Initializes a new instance of the <see cref="PublishedCultureInfo"/> class.
/// </summary>
public PublishedCultureInfo(string culture, string name, DateTime date)
public PublishedCultureInfo(string culture, string name, string urlSegment, DateTime date)
{
if (string.IsNullOrWhiteSpace(culture)) throw new ArgumentNullOrEmptyException(nameof(culture));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
Culture = culture;
Name = name;
UrlSegment = name.ToUrlSegment(culture);
UrlSegment = urlSegment;
Date = date;
}
@@ -292,7 +292,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (psql.Arguments[i] is string s && s == "[[[ISOCODE]]]")
{
psql.Arguments[i] = ordering.Culture;
break;
}
}
}
@@ -982,8 +982,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// invariant: left join will yield NULL and we must use pcv to determine published
// variant: left join may yield NULL or something, and that determines published
var joins = Sql()
.InnerJoin<ContentTypeDto>("ctype").On<ContentDto, ContentTypeDto>((content, contentType) => content.ContentTypeId == contentType.NodeId, aliasRight: "ctype");
.InnerJoin<ContentTypeDto>("ctype").On<ContentDto, ContentTypeDto>((content, contentType) => content.ContentTypeId == contentType.NodeId, aliasRight: "ctype")
// left join on optional culture variation
//the magic "[[[ISOCODE]]]" parameter value will be replaced in ContentRepositoryBase.GetPage() by the actual ISO code
.LeftJoin<ContentVersionCultureVariationDto>(nested =>
nested.InnerJoin<LanguageDto>("langp").On<ContentVersionCultureVariationDto, LanguageDto>((ccv, lang) => ccv.LanguageId == lang.Id && lang.IsoCode == "[[[ISOCODE]]]", "ccvp", "langp"), "ccvp")
.On<ContentVersionDto, ContentVersionCultureVariationDto>((version, ccv) => version.Id == ccv.VersionId, aliasLeft: "pcv", aliasRight: "ccvp");
sql = InsertJoins(sql, joins);
@@ -993,7 +999,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// when invariant, ie 'variations' does not have the culture flag (value 1), use the global 'published' flag on pcv.id,
// otherwise check if there's a version culture variation for the lang, via ccv.id
", (CASE WHEN (ctype.variations & 1) = 0 THEN (CASE WHEN pcv.id IS NULL THEN 0 ELSE 1 END) ELSE (CASE WHEN ccv.id IS NULL THEN 0 ELSE 1 END) END) AS ordering "); // trailing space is important!
", (CASE WHEN (ctype.variations & 1) = 0 THEN (CASE WHEN pcv.id IS NULL THEN 0 ELSE 1 END) ELSE (CASE WHEN ccvp.id IS NULL THEN 0 ELSE 1 END) END) AS ordering "); // trailing space is important!
sql = Sql(sqlText, sql.Arguments);
@@ -36,6 +36,10 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
if (source is int)
return (int)source == 1;
// this is required for correct true/false handling in nested content elements
if (source is long)
return (long)source == 1;
if (source is bool)
return (bool)source;
@@ -5,11 +5,11 @@ using Umbraco.Core.IO;
namespace Umbraco.Core.Runtime
{
public class CoreRuntimeComponent : IComponent
public class CoreInitialComponent : IComponent
{
private readonly IEnumerable<Profile> _mapperProfiles;
public CoreRuntimeComponent(IEnumerable<Profile> mapperProfiles)
public CoreInitialComponent(IEnumerable<Profile> mapperProfiles)
{
_mapperProfiles = mapperProfiles;
}
@@ -24,7 +24,9 @@ using IntegerValidator = Umbraco.Core.PropertyEditors.Validators.IntegerValidato
namespace Umbraco.Core.Runtime
{
public class CoreRuntimeComposer : ComponentComposer<CoreRuntimeComponent>, IRuntimeComposer
// core's initial composer composes before all core composers
[ComposeBefore(typeof(ICoreComposer))]
public class CoreInitialComposer : ComponentComposer<CoreInitialComponent>
{
public override void Compose(Composition composition)
{
+2 -2
View File
@@ -168,7 +168,7 @@ namespace Umbraco.Core.Runtime
_state.BootFailedException = bfe;
}
timer.Fail(exception: bfe); // be sure to log the exception - even if we repeat ourselves
timer?.Fail(exception: bfe); // be sure to log the exception - even if we repeat ourselves
// if something goes wrong above, we may end up with no factory
// meaning nothing can get the runtime state, etc - so let's try
@@ -242,7 +242,7 @@ namespace Umbraco.Core.Runtime
}
catch
{
timer.Fail();
timer?.Fail();
throw;
}
}
@@ -2862,7 +2862,7 @@ namespace Umbraco.Core.Services.Implement
{
if (blueprint == null) throw new ArgumentNullException(nameof(blueprint));
var contentType = _contentTypeRepository.Get(blueprint.ContentType.Id);
var contentType = GetContentType(blueprint.ContentType.Alias);
var content = new Content(name, -1, contentType);
content.Path = string.Concat(content.ParentId.ToString(), ",", content.Id);
@@ -17,7 +17,7 @@ namespace Umbraco.Core.Strings
/// Converts an Utf8 string into an Ascii string.
/// </summary>
/// <param name="text">The text to convert.</param>
/// <param name="fail">The character to used to replace characters that cannot properly be converted.</param>
/// <param name="fail">The character to use to replace characters that cannot properly be converted.</param>
/// <returns>The converted text.</returns>
public static string ToAsciiString(string text, char fail = '?')
{
@@ -39,7 +39,7 @@ namespace Umbraco.Core.Strings
/// Converts an Utf8 string into an array of Ascii characters.
/// </summary>
/// <param name="text">The text to convert.</param>
/// <param name="fail">The character to used to replace characters that cannot properly be converted.</param>
/// <param name="fail">The character to use to replace characters that cannot properly be converted.</param>
/// <returns>The converted text.</returns>
public static char[] ToAsciiCharArray(string text, char fail = '?')
{
@@ -66,7 +66,7 @@ namespace Umbraco.Core.Strings
/// </summary>
/// <param name="input">The input array.</param>
/// <param name="output">The output array.</param>
/// <param name="fail">The character to used to replace characters that cannot properly be converted.</param>
/// <param name="fail">The character to use to replace characters that cannot properly be converted.</param>
/// <returns>The number of characters in the output array.</returns>
/// <remarks>The caller must ensure that the output array is big enough.</remarks>
/// <exception cref="OverflowException">The output array is not big enough.</exception>
@@ -112,7 +112,7 @@ namespace Umbraco.Core.Strings
/// <param name="ipos">The input position.</param>
/// <param name="output">The output array.</param>
/// <param name="opos">The output position.</param>
/// <param name="fail">The character to used to replace characters that cannot properly be converted.</param>
/// <param name="fail">The character to use to replace characters that cannot properly be converted.</param>
/// <remarks>
/// <para>Adapted from various sources on the 'net including <c>Lucene.Net.Analysis.ASCIIFoldingFilter</c>.</para>
/// <para>Input should contain Utf8 characters exclusively and NOT Unicode.</para>
+4 -3
View File
@@ -168,7 +168,6 @@
<Compile Include="Composing\EnableComposerAttribute.cs" />
<Compile Include="Composing\IComposer.cs" />
<Compile Include="Composing\ICoreComposer.cs" />
<Compile Include="Composing\IRuntimeComposer.cs" />
<Compile Include="Composing\IComponent.cs" />
<Compile Include="Composing\IUserComposer.cs" />
<Compile Include="Compose\ManifestWatcherComposer.cs" />
@@ -209,11 +208,13 @@
<Compile Include="Composing\TypeLoader.cs" />
<Compile Include="IO\MediaPathSchemes\UniqueMediaPathScheme.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\MergeDateAndDateTimePropertyEditor.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_1\ChangeNuCacheJsonFormat.cs" />
<Compile Include="Models\PublishedContent\ILivePublishedModelFactory.cs" />
<Compile Include="PropertyEditors\DateTimeConfiguration.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\RenameLabelAndRichTextPropertyEditorAliases.cs" />
<Compile Include="PublishedModelFactoryExtensions.cs" />
<Compile Include="Services\PropertyValidationService.cs" />
<Compile Include="Composing\TypeCollectionBuilderBase.cs" />
<Compile Include="TypeLoaderExtensions.cs" />
<Compile Include="Composing\WeightAttribute.cs" />
<Compile Include="Composing\WeightedCollectionBuilderBase.cs" />
@@ -530,7 +531,7 @@
<Compile Include="RuntimeLevelReason.cs" />
<Compile Include="RuntimeOptions.cs" />
<Compile Include="Runtime\CoreRuntime.cs" />
<Compile Include="Runtime\CoreRuntimeComponent.cs" />
<Compile Include="Runtime\CoreInitialComponent.cs" />
<Compile Include="CustomBooleanTypeConverter.cs" />
<Compile Include="Migrations\Expressions\Common\ExecutableBuilder.cs" />
<Compile Include="Migrations\Expressions\Common\IExecutableBuilder.cs" />
@@ -1311,7 +1312,7 @@
<Compile Include="ReflectionUtilities.cs" />
<Compile Include="RuntimeLevel.cs" />
<Compile Include="RuntimeState.cs" />
<Compile Include="Runtime\CoreRuntimeComposer.cs" />
<Compile Include="Runtime\CoreInitialComposer.cs" />
<Compile Include="SafeCallContext.cs" />
<Compile Include="Scoping\IInstanceIdentifiable.cs" />
<Compile Include="Scoping\IScope.cs" />
+2
View File
@@ -28,6 +28,8 @@ namespace Umbraco.Examine
/// </remarks>
internal static readonly Regex CultureIsoCodeFieldNameMatchExpression = new Regex("^([_\\w]+)_([a-z]{2}-[a-z0-9]{2,4})$", RegexOptions.Compiled);
//TODO: We need a public method here to just match a field name against CultureIsoCodeFieldNameMatchExpression
/// <summary>
/// Returns all index fields that are culture specific (suffixed)
/// </summary>
@@ -158,7 +158,7 @@ namespace Umbraco.Tests.Cache
new TestDefaultCultureAccessor(),
TestObjects.GetUmbracoSettings(),
TestObjects.GetGlobalSettings(),
Enumerable.Empty<IUrlProvider>(),
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
Mock.Of<IUserService>());
// just assert it does not throw
@@ -0,0 +1,53 @@
using System;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
namespace Umbraco.Tests.Composing
{
[TestFixture]
public class CompositionTests
{
[Test]
public void FactoryIsResolvable()
{
Func<IFactory, IFactory> factoryFactory = null;
var mockedRegister = Mock.Of<IRegister>();
var mockedFactory = Mock.Of<IFactory>();
// the mocked register creates the mocked factory
Mock.Get(mockedRegister)
.Setup(x => x.CreateFactory())
.Returns(mockedFactory);
// the mocked register can register a factory factory
Mock.Get(mockedRegister)
.Setup(x => x.Register(It.IsAny<Func<IFactory, IFactory>>(), Lifetime.Singleton))
.Callback<Func<IFactory, IFactory>, Lifetime>((ff, lt) => factoryFactory = ff);
// the mocked factory can invoke the factory factory
Mock.Get(mockedFactory)
.Setup(x => x.GetInstance(typeof(IFactory)))
.Returns(() => factoryFactory?.Invoke(mockedFactory));
var logger = new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>());
var typeLoader = new TypeLoader(Mock.Of<IAppPolicyCache>(), "", logger);
var composition = new Composition(mockedRegister, typeLoader, logger, Mock.Of<IRuntimeState>());
// create the factory, ensure it is the mocked factory
var factory = composition.CreateFactory();
Assert.AreSame(mockedFactory, factory);
// ensure we can get an IFactory instance,
// meaning that it has been properly registered
var resolved = factory.GetInstance<IFactory>();
Assert.IsNotNull(resolved);
Assert.AreSame(factory, resolved);
}
}
}
+4 -2
View File
@@ -1,4 +1,5 @@
using NUnit.Framework;
using Moq;
using NUnit.Framework;
using System;
using System.IO;
using System.Linq;
@@ -47,7 +48,8 @@ namespace Umbraco.Tests.Logging
File.Copy(exampleLogfilePath, _newLogfilePath, true);
File.Copy(exampleSearchfilePath, _newSearchfilePath, true);
_logViewer = new JsonLogViewer(logsPath: _newLogfileDirPath, searchPath: _newSearchfilePath);
var logger = Mock.Of<Core.Logging.ILogger>();
_logViewer = new JsonLogViewer(logger, logsPath: _newLogfileDirPath, searchPath: _newSearchfilePath);
}
[OneTimeTearDown]
@@ -15,6 +15,7 @@ using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Scoping;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Changes;
using Umbraco.Core.Strings;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.Testing.Objects;
using Umbraco.Tests.Testing.Objects.Accessors;
@@ -181,7 +182,8 @@ namespace Umbraco.Tests.PublishedContent
globalSettings,
new SiteDomainHelper(),
Mock.Of<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>());
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
// invariant is the current default
_variationAccesor.VariationContext = new VariationContext();
@@ -40,7 +40,7 @@ namespace Umbraco.Tests.Routing
{
base.SetUp();
WebRuntimeComponent.CreateRoutes(
WebFinalComponent.CreateRoutes(
new TestUmbracoContextAccessor(),
TestObjects.GetGlobalSettings(),
new SurfaceControllerTypeCollection(Enumerable.Empty<Type>()),
@@ -81,7 +81,7 @@ namespace Umbraco.Tests.Runtimes
var composerTypes = typeLoader.GetTypes<IComposer>() // all of them
.Where(x => !x.FullName.StartsWith("Umbraco.Tests.")) // exclude test components
.Where(x => x != typeof(WebRuntimeComposer)); // exclude web runtime
.Where(x => x != typeof(WebInitialComposer)); // exclude web runtime
var composers = new Composers(composition, composerTypes, profilingLogger);
composers.Compose();
@@ -18,7 +18,7 @@ namespace Umbraco.Tests.Runtimes
new PluginViewEngine()
};
WebRuntimeComponent.WrapViewEngines(engines);
WebInitialComponent.WrapViewEngines(engines);
Assert.That(engines.Count, Is.EqualTo(2));
Assert.That(engines[0], Is.InstanceOf<ProfilingViewEngine>());
@@ -34,7 +34,7 @@ namespace Umbraco.Tests.Runtimes
new PluginViewEngine()
};
WebRuntimeComponent.WrapViewEngines(engines);
WebInitialComponent.WrapViewEngines(engines);
Assert.That(engines.Count, Is.EqualTo(2));
Assert.That(((ProfilingViewEngine)engines[0]).Inner, Is.InstanceOf<RenderViewEngine>());
@@ -50,7 +50,7 @@ namespace Umbraco.Tests.Runtimes
profiledEngine
};
WebRuntimeComponent.WrapViewEngines(engines);
WebInitialComponent.WrapViewEngines(engines);
Assert.That(engines[0], Is.SameAs(profiledEngine));
}
@@ -58,7 +58,7 @@ namespace Umbraco.Tests.Runtimes
[Test]
public void WrapViewEngines_CollectionIsNull_DoesNotThrow()
{
Assert.DoesNotThrow(() => WebRuntimeComponent.WrapViewEngines(null));
Assert.DoesNotThrow(() => WebInitialComponent.WrapViewEngines(null));
}
}
}
@@ -17,6 +17,7 @@ using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.Testing;
@@ -99,7 +100,8 @@ namespace Umbraco.Tests.Scoping
new DatabaseDataSource(),
Factory.GetInstance<IGlobalSettings>(), new SiteDomainHelper(),
Factory.GetInstance<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>());
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
}
protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable<IUrlProvider> urlProviders = null)
@@ -15,6 +15,7 @@ using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
using Umbraco.Tests.Testing;
using Umbraco.Web;
@@ -72,7 +73,8 @@ namespace Umbraco.Tests.Services
new DatabaseDataSource(),
Factory.GetInstance<IGlobalSettings>(), new SiteDomainHelper(),
Factory.GetInstance<IEntityXmlSerializer>(),
Mock.Of<IPublishedModelFactory>());
Mock.Of<IPublishedModelFactory>(),
new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }));
}
public class LocalServerMessenger : ServerMessengerBase
@@ -116,7 +116,7 @@ namespace Umbraco.Tests.TestHelpers
var umbracoSettings = GetUmbracoSettings();
var globalSettings = GetGlobalSettings();
var urlProviders = Enumerable.Empty<IUrlProvider>();
var urlProviders = new UrlProviderCollection(Enumerable.Empty<IUrlProvider>());
if (accessor == null) accessor = new TestUmbracoContextAccessor();
+1
View File
@@ -118,6 +118,7 @@
<Compile Include="Cache\SnapDictionaryTests.cs" />
<Compile Include="Clr\ReflectionUtilitiesTests.cs" />
<Compile Include="Collections\OrderedHashSetTests.cs" />
<Compile Include="Composing\CompositionTests.cs" />
<Compile Include="Composing\LightInjectValidation.cs" />
<Compile Include="Composing\ContainerConformingTests.cs" />
<Compile Include="CoreThings\CallContextTests.cs" />
@@ -71,7 +71,7 @@ namespace Umbraco.Tests.Web.Mvc
new TestDefaultCultureAccessor(),
TestObjects.GetUmbracoSettings(),
globalSettings,
Enumerable.Empty<IUrlProvider>(),
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -100,7 +100,7 @@ namespace Umbraco.Tests.Web.Mvc
new TestDefaultCultureAccessor(),
TestObjects.GetUmbracoSettings(),
globalSettings,
Enumerable.Empty<IUrlProvider>(),
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -129,7 +129,7 @@ namespace Umbraco.Tests.Web.Mvc
new TestDefaultCultureAccessor(),
TestObjects.GetUmbracoSettings(),
globalSettings,
Enumerable.Empty<IUrlProvider>(),
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -158,7 +158,7 @@ namespace Umbraco.Tests.Web.Mvc
new TestDefaultCultureAccessor(),
TestObjects.GetUmbracoSettings(),
globalSettings,
Enumerable.Empty<IUrlProvider>(),
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -46,7 +46,7 @@ namespace Umbraco.Tests.Web.Mvc
new TestDefaultCultureAccessor(),
TestObjects.GetUmbracoSettings(),
globalSettings,
Enumerable.Empty<IUrlProvider>(),
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -73,7 +73,7 @@ namespace Umbraco.Tests.Web.Mvc
new TestDefaultCultureAccessor(),
TestObjects.GetUmbracoSettings(),
globalSettings,
Enumerable.Empty<IUrlProvider>(),
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -103,7 +103,7 @@ namespace Umbraco.Tests.Web.Mvc
new TestDefaultCultureAccessor(),
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == Mock.Of<IWebRoutingSection>(routingSection => routingSection.UrlProviderMode == "Auto")),
globalSettings,
Enumerable.Empty<IUrlProvider>(),
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -140,7 +140,7 @@ namespace Umbraco.Tests.Web.Mvc
new TestDefaultCultureAccessor(),
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == webRoutingSettings),
globalSettings,
Enumerable.Empty<IUrlProvider>(),
new UrlProviderCollection(Enumerable.Empty<IUrlProvider>()),
Mock.Of<IUserService>());
var umbracoContextReference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>());
@@ -104,7 +104,7 @@ namespace Umbraco.Tests.Web
new TestDefaultCultureAccessor(),
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == Mock.Of<IWebRoutingSection>(routingSection => routingSection.UrlProviderMode == "Auto")),
globalSettings,
new[] { testUrlProvider.Object },
new UrlProviderCollection(new[] { testUrlProvider.Object }),
Mock.Of<IUserService>());
using (var reference = umbracoContextFactory.EnsureUmbracoContext(Mock.Of<HttpContextBase>()))
-22
View File
@@ -1,22 +0,0 @@
@ECHO OFF
ECHO.
ECHO.
ECHO This will only work when you have NPM available on the command line
ECHO Works great with NodeJS Portable - https://gareth.flowers/nodejs-portable/
ECHO.
ECHO.
set /P c=Are you sure you want to continue [Y/N]?
if /I "%c%" EQU "Y" goto :setupgulp
if /I "%c%" EQU "N" goto :eof
:setupgulp
call npm install
call npm -g install gulp
call npm -g install gulp-cli
ECHO.
ECHO.
ECHO You should now be able to run: gulp build or gulp dev
ECHO.
ECHO.
@@ -331,8 +331,35 @@
// This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish
function performSave(args) {
// Check that all variants for publishing have a name.
if (args.action === "publish" || args.action === "sendToPublish") {
if ($scope.content.variants) {
var iVariant;
for (var i = 0; i < $scope.content.variants.length; i++) {
iVariant = $scope.content.variants[i];
iVariant.notifications = [];// maybe not needed, need to investigate.
if(iVariant.publish === true) {
if (iVariant.name == null) {
var tokens = [iVariant.language.name];
localizationService.localize("publish_contentPublishedFailedByMissingName", tokens).then(function (value) {
iVariant.notifications.push({"message": value, "type": 1});
});
return $q.reject();
}
}
}
}
}
//Used to check validility of nested form - coming from Content Apps mostly
//Set them all to be invalid
var fieldsToRollback = checkValidility();
@@ -344,8 +344,8 @@
loadAuditTrail(true);
loadRedirectUrls();
setNodePublishStatus();
updateCurrentUrls();
}
updateCurrentUrls();
});
//ensure to unregister from all events!
@@ -42,7 +42,7 @@
item: '=',
onOpen: '&',
onOpenAnchor: '&',
index: '@'
hotkey: '<'
}
});
@@ -0,0 +1,72 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbCheckbox
@restrict E
@scope
@description
<b>Added in Umbraco version 7.14.0</b> Use this directive to render an umbraco checkbox.
<h3>Markup example</h3>
<pre>
<div ng-controller="My.Controller as vm">
<umb-checkbox
name="checkboxlist"
value="{{key}}"
model="true"
text="{{text}}">
</umb-checkbox>
</div>
</pre>
@param {boolean} model Set to <code>true</code> or <code>false</code> to set the checkbox to checked or unchecked.
@param {string} input-id Set the <code>id</code> of the checkbox.
@param {string} value Set the value of the checkbox.
@param {string} name Set the name of the checkbox.
@param {string} text Set the text for the checkbox label.
@param {string} server-validation-field Set the <code>val-server-field</code> of the checkbox.
@param {boolean} disabled Set the checkbox to be disabled.
@param {boolean} required Set the checkbox to be required.
@param {string} on-change Callback when the value of the checkbox changed by interaction.
**/
(function () {
'use strict';
function UmbCheckboxController($timeout) {
var vm = this;
vm.callOnChange = function() {
$timeout(function() {
vm.onChange({model:vm.model, value:vm.value});
}, 0);
}
}
var component = {
templateUrl: 'views/components/forms/umb-checkbox.html',
controller: UmbCheckboxController,
controllerAs: 'vm',
bindings: {
model: "=",
inputId: "@",
value: "@",
name: "@",
text: "@",
serverValidationField: "@",
disabled: "<",
required: "<",
onChange: "&"
}
};
angular.module('umbraco.directives').component('umbCheckbox', component);
})();
@@ -0,0 +1,57 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbRadiobutton
@restrict E
@scope
@description
<b>Added in Umbraco version 7.14.0</b> Use this directive to render an umbraco radio button.
<h3>Markup example</h3>
<pre>
<div ng-controller="My.Controller as vm">
<umb-radiobutton
name="radiobuttonlist"
value="{{key}}"
model="true"
text="{{text}}">
</umb-radiobutton>
</div>
</pre>
@param {boolean} model Set to <code>true</code> or <code>false</code> to set the radiobutton to checked or unchecked.
@param {string} value Set the value of the radiobutton.
@param {string} name Set the name of the radiobutton.
@param {string} text Set the text for the radiobutton label.
@param {boolean} disabled Set the radiobutton to be disabled.
@param {boolean} required Set the radiobutton to be required.
**/
(function () {
'use strict';
function RadiobuttonDirective() {
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/forms/umb-radiobutton.html',
scope: {
model: "=",
value: "@",
name: "@",
text: "@",
disabled: "=",
required: "="
}
};
return directive;
}
angular.module('umbraco.directives').directive('umbRadiobutton', RadiobuttonDirective);
})();
@@ -19,9 +19,15 @@ angular.module("umbraco.directives")
promises.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope));
}
var toolbar = ["code", "styleselect", "bold", "italic", "alignleft", "aligncenter", "alignright", "bullist", "numlist", "link", "umbmediapicker", "umbembeddialog"];
if (scope.configuration && scope.configuration.toolbar) {
toolbar = scope.configuration.toolbar;
var editorConfig = scope.configuration ? scope.configuration : null;
if (!editorConfig || angular.isString(editorConfig)) {
editorConfig = tinyMceService.defaultPrevalues();
//for the grid by default, we don't want to include the macro toolbar
editorConfig.toolbar = _.without(editorConfig, "umbmacro");
}
//make sure there's a max image size
if (!scope.configuration.maxImageSize && scope.configuration.maxImageSize !== 0) {
editorConfig.maxImageSize = tinyMceService.defaultPrevalues().maxImageSize;
}
//stores a reference to the editor
@@ -29,9 +35,9 @@ angular.module("umbraco.directives")
promises.push(tinyMceService.getTinyMceEditorConfig({
htmlId: scope.uniqueId,
stylesheets: scope.configuration ? scope.configuration.stylesheets : null,
toolbar: toolbar,
mode: scope.configuration.mode
stylesheets: editorConfig.stylesheets,
toolbar: editorConfig.toolbar,
mode: editorConfig.mode
}));
// pin toolbar to top of screen if we have focus and it scrolls off the screen
@@ -46,9 +52,16 @@ angular.module("umbraco.directives")
$q.all(promises).then(function (result) {
var tinyMceEditorConfig = result[promises.length - 1];
var standardConfig = result[promises.length - 1];
tinyMceEditorConfig.setup = function (editor) {
//create a baseline Config to extend upon
var baseLineConfigObj = {
maxImageSize: editorConfig.maxImageSize
};
angular.extend(baseLineConfigObj, standardConfig);
baseLineConfigObj.setup = function (editor) {
//set the reference
tinyMceEditor = editor;
@@ -111,7 +124,7 @@ angular.module("umbraco.directives")
//the elements needed
$timeout(function () {
tinymce.DOM.events.domLoaded = true;
tinymce.init(tinyMceEditorConfig);
tinymce.init(baseLineConfigObj);
}, 150, false);
}
@@ -117,9 +117,11 @@ Use this directive to generate a list of content items presented as a flexbox gr
};
scope.clickItemName = function(item, $event, $index) {
if(scope.onClickName) {
if(scope.onClickName && !($event.metaKey || $event.ctrlKey)) {
scope.onClickName(item, $event, $index);
$event.preventDefault();
}
$event.stopPropagation();
};
}
@@ -117,10 +117,11 @@
var vm = this;
vm.clickItem = function (item, $event) {
if (vm.onClick) {
if (vm.onClick && !($event.metaKey || $event.ctrlKey)) {
vm.onClick({ item: item});
$event.stopPropagation();
$event.preventDefault();
}
$event.stopPropagation();
};
vm.selectItem = function (item, $index, $event) {
@@ -15,10 +15,12 @@ function umbFileUpload() {
el.on('change', function (event) {
var files = event.target.files;
//emit event upward
scope.$emit("filesSelected", { files: files });
scope.$emit("filesSelected", { files: files });
//clear the element value - this allows us to pick the same file again and again
el.val('');
});
}
};
}
angular.module('umbraco.directives').directive("umbFileUpload", umbFileUpload);
angular.module('umbraco.directives').directive("umbFileUpload", umbFileUpload);
@@ -301,6 +301,19 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
return allProps;
},
/**
* @ngdoc method
* @name umbraco.services.contentEditingHelper#buildCompositeVariantId
* @methodOf umbraco.services.contentEditingHelper
* @function
*
* @description
* Returns a id for the variant that is unique between all variants on the content
*/
buildCompositeVariantId: function (variant) {
return (variant.language ? variant.language.culture : "invariant") + "_" + (variant.segment ? variant.segment : "");
},
/**
* @ngdoc method
@@ -59,11 +59,11 @@
* Method for internal use, based on the collection of layouts passed, the method selects either
* any previous layout from local storage, or picks the first allowed layout
*
* @param {Number} nodeId The id of the current node displayed in the content editor
* @param {Any} id The identifier of the current node or application displayed in the content editor
* @param {Array} availableLayouts Array of all allowed layouts, available from $scope.model.config.layouts
*/
function getLayout(nodeId, availableLayouts) {
function getLayout(id, availableLayouts) {
var storedLayouts = [];
@@ -74,8 +74,8 @@
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);
if (isMatchingLayout(id, layout)) {
return setLayout(id, layout, availableLayouts);
}
}
@@ -93,12 +93,12 @@
* @description
* Changes the current layout used by the listview to the layout passed in. Stores selection in localstorage
*
* @param {Number} nodeID Id of the current node displayed in the content editor
* @param {Any} id The identifier of the current node or application displayed in the content editor
* @param {Object} selectedLayout Layout selected as the layout to set as the current layout
* @param {Array} availableLayouts Array of all allowed layouts, available from $scope.model.config.layouts
*/
function setLayout(nodeId, selectedLayout, availableLayouts) {
function setLayout(id, selectedLayout, availableLayouts) {
var activeLayout = {};
var layoutFound = false;
@@ -118,7 +118,7 @@
activeLayout = getFirstAllowedLayout(availableLayouts);
}
saveLayoutInLocalStorage(nodeId, activeLayout);
saveLayoutInLocalStorage(id, activeLayout);
return activeLayout;
@@ -132,11 +132,11 @@
* @description
* Stores a given layout as the current default selection in local storage
*
* @param {Number} nodeId Id of the current node displayed in the content editor
* @param {Any} id The identifier of the current node or application displayed in the content editor
* @param {Object} selectedLayout Layout selected as the layout to set as the current layout
*/
function saveLayoutInLocalStorage(nodeId, selectedLayout) {
function saveLayoutInLocalStorage(id, selectedLayout) {
var layoutFound = false;
var storedLayouts = [];
@@ -147,7 +147,7 @@
if (storedLayouts.length > 0) {
for (var i = 0; storedLayouts.length > i; i++) {
var layout = storedLayouts[i];
if (layout.nodeId === nodeId) {
if (isMatchingLayout(id, layout)) {
layout.path = selectedLayout.path;
layoutFound = true;
}
@@ -156,7 +156,7 @@
if (!layoutFound) {
var storageObject = {
"nodeId": nodeId,
"id": id,
"path": selectedLayout.path
};
storedLayouts.push(storageObject);
@@ -510,6 +510,12 @@
};
}
function isMatchingLayout(id, layout) {
// legacy format uses "nodeId", be sure to look for both
return layout.id === id || layout.nodeId === id;
}
var service = {
getLayout: getLayout,
@@ -26,6 +26,8 @@ function navigationService($routeParams, $location, $q, $timeout, $injector, eve
navReadyPromise.resolve(mainTreeApi);
});
//A list of query strings defined that when changed will not cause a reload of the route
var nonRoutingQueryStrings = ["mculture", "cculture", "lq"];
var retainedQueryStrings = ["mculture"];
@@ -427,8 +427,8 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
data["rel"] = img.id;
data["data-id"] = img.id;
}
editor.insertContent(editor.dom.createHTML('img', data));
editor.selection.setContent(editor.dom.createHTML('img', data));
$timeout(function () {
var imgElm = editor.dom.get('__mcenew');
@@ -350,6 +350,9 @@ function treeService($q, treeResource, iconHelper, notificationsService, eventsS
parent.children.splice(parent.children.indexOf(treeNode), 1);
parent.hasChildren = parent.children.length !== 0;
//Notify that the node has been removed
eventsService.emit("treeService.removeNode", { node: treeNode });
},
/**
@@ -9,7 +9,7 @@
*
* @param {navigationService} navigationService A reference to the navigationService
*/
function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, $cookies, treeService, appState, navigationService, keyboardService, historyService, eventsService, angularHelper, languageResource, contentResource) {
function NavigationController($scope, $rootScope, $location, $log, $q, $routeParams, $timeout, $cookies, treeService, appState, navigationService, keyboardService, historyService, eventsService, angularHelper, languageResource, contentResource, editorState) {
//this is used to trigger the tree to start loading once everything is ready
var treeInitPromise = $q.defer();
@@ -249,6 +249,20 @@ function NavigationController($scope, $rootScope, $location, $log, $q, $routePar
$scope.infiniteMode = args && args.editors.length > 0 ? true : false;
}));
evts.push(eventsService.on("treeService.removeNode", function (e, args) {
//check to see if the current page has been removed
var currentEditorState = editorState.getCurrent()
if (currentEditorState && currentEditorState.id.toString() === args.node.id.toString()) {
//current page is loaded, so navigate to root
var section = appState.getSectionState("currentSection");
$location.path("/" + section);
}
}));
/**
* Based on the current state of the application, this configures the scope variables that control the main tree and language drop down
*/
@@ -117,6 +117,7 @@
@import "components/umb-confirm-action.less";
@import "components/umb-keyboard-shortcuts-overview.less";
@import "components/umb-checkbox-list.less";
@import "components/umb-form-check.less";
@import "components/umb-locked-field.less";
@import "components/umb-tabs.less";
@import "components/umb-load-indicator.less";
@@ -166,6 +167,7 @@
@import "components/umb-property-file-upload.less";
@import "components/users/umb-user-cards.less";
@import "components/users/umb-user-table.less";
@import "components/users/umb-user-details.less";
@import "components/users/umb-user-group-picker-list.less";
@import "components/users/umb-user-group-preview.less";
@@ -195,6 +195,7 @@ a.umb-variant-switcher__toggle {
justify-content: space-between;
align-items: center;
border-bottom: 1px solid @gray-9;
position: relative;
&:hover .umb-variant-switcher__name-wrapper {
}
@@ -246,10 +247,15 @@ a.umb-variant-switcher__toggle {
}
.umb-variant-switcher__split-view {
font-size: 13px;
display: none;
padding: 16px 20px;
font-size: 13px;
display: none;
padding: 16px 20px;
position: absolute;
right: 0;
top: 0;
bottom: 0;
background-color: @white;
&:hover {
background-color: @ui-option-hover;
color: @ui-option-type-hover;
@@ -250,3 +250,11 @@
.form-horizontal .umb-overlay .controls {
margin-left: 0 !important;
}
.umb-overlay .text-error {
color: @formErrorText;
}
.umb-overlay .text-success {
color: @formSuccessText;
}
@@ -166,7 +166,9 @@ body.touch .umb-tree {
}
.umb-tree .umb-tree-node-checked > .umb-tree-item__inner > i[class^="icon-"],
.umb-tree .umb-tree-node-checked > .umb-tree-item__inner > i[class*=" icon-"] {
.umb-tree .umb-tree-node-checked > .umb-tree-item__inner > i[class*=" icon-"],
.umb-tree .umb-tree-node-checked .umb-search-group-item-name > i[class^="icon-"],
.umb-tree .umb-tree-node-checked .umb-search-group-item-name > i[class*=" icon-"] {
font-family: 'icomoon' !important;
color: @green !important;
@@ -1,3 +1,6 @@
@checkboxWidth: 15px;
@checkboxHeight: 15px;
.umb-checkbox-list {
list-style: none;
margin-left: 0;
@@ -13,7 +13,6 @@
user-select: none;
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16);
border-radius: 3px;
}
.umb-content-grid__item.-selected {
@@ -59,7 +58,8 @@
display: inline-flex;
color: @ui-option-type;
&:hover {
&:hover, &:focus {
text-decoration: none;
color:@ui-option-type-hover;
}
}
@@ -61,6 +61,9 @@
.show-validation .umb-sub-views-nav-item > a.-has-error {
color: @red;
&::after {
background-color: @red;
}
}
.umb-sub-views-nav-item .icon {
@@ -0,0 +1,136 @@
@checkboxWidth: 16px;
@checkboxHeight: 16px;
.umb-form-check {
display: flex;
flex-wrap: wrap;
align-items: center;
position: relative;
padding: 0;
margin: 0;
min-height: 22px;
line-height: 22px;
cursor: pointer !important;
&__text {
margin: 0 0 0 26px;
position: relative;
top: 0;
}
&__input {
position: absolute;
top: 0;
left: 0;
opacity: 0;
&:checked ~ .umb-form-check__state .umb-form-check__check {
border-color: @ui-option-type;
}
&:focus:checked ~ .umb-form-check .umb-form-check__check,
&:focus ~ .umb-form-check__state .umb-form-check__check {
border-color: @inputBorderFocus;
}
&:checked ~ .umb-form-check__state {
.umb-form-check__check {
// This only happens if the state has a radiobutton modifier
.umb-form-check--radiobutton & {
&:before {
opacity: 1;
transform: scale(1);
}
}
// This only happens if state has the checkbox modifier
.umb-form-check--checkbox & {
&:before {
width: @checkboxWidth;
height: @checkboxHeight;
}
}
}
// This only happens if state has the checkbox modifier
.umb-form-check--checkbox & {
.umb-form-check__icon {
opacity: 1;
}
}
}
}
&__state {
display: flex;
height: 18px;
margin-top: 2px;
position: absolute;
top: 0;
left: 0;
}
&__check {
display: flex;
position: relative;
background: @white;
border: 1px solid @inputBorder;
width: @checkboxWidth;
height: @checkboxHeight;
&:before {
content: "";
background: @ui-option-type;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
// This only happens if state has the radiobutton modifier
.umb-form-check--radiobutton & {
border-radius: 100%;
&:before {
width: 10px;
height: 10px;
border-radius: 100%;
opacity: 0;
transform: scale(0);
transition: .15s ease-out;
}
}
// This only happens if state has the checkbox modifier
.umb-form-check--checkbox & {
&:before {
width: 0;
height: 0;
transition: .05s ease-out;
}
}
}
&__icon {
color: @white;
text-align: center;
font-size: 12px;
opacity: 0;
transition: .2s ease-out;
&:before {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
right: 0;
left: 0;
bottom: 0;
margin: auto;
}
}
&.umb-form-check--disabled {
cursor: not-allowed !important;
opacity: 0.5;
}
}
@@ -22,6 +22,10 @@ a.umb-list-item:focus {
opacity: 0.6;
}
.umb-list-item--error {
color: @red;
}
.umb-list-item:hover .umb-list-checkbox,
.umb-list-item--selected .umb-list-checkbox {
opacity: 1;
@@ -34,4 +38,4 @@ a.umb-list-item:focus {
.umb-list-checkbox--visible {
opacity: 1;
}
}
@@ -261,6 +261,7 @@
.umb-package-details {
display: flex;
flex-flow: row wrap;
}
a.umb-package-details__back-link {
@@ -280,6 +281,7 @@ a.umb-package-details__back-link {
flex: 1 1 auto;
margin-right: 20px;
width: calc(~'100%' - ~'@{sidebarwidth}' - ~'20px'); // Make sure that the main content area doesn't gets affected by inline styling
min-width: 500px;
}
.umb-package-details__sidebar {
@@ -151,14 +151,14 @@ input.umb-table__input {
// Show checkmark when checked, hide file icon
.umb-table-row--selected {
/*
.umb-table-body__fileicon {
display: none;
}
.umb-table-body__checkicon {
display: inline-block;
}
*/
&::before {
content: "";
position: absolute;
@@ -17,6 +17,10 @@
outline: none;
text-decoration: none !important;
}
.umb-user-card.-selectable {
cursor: pointer;
}
.umb-user-card.-selected {
&::before {
content: "";
@@ -31,7 +35,6 @@
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
pointer-events: none;
}
}
.umb-user-card__content {
@@ -44,14 +47,18 @@
box-sizing: border-box;
display: flex;
flex-direction: column;
cursor: pointer;
max-width: 100%;
}
.umb-user-card__goToUser {
&:hover {
&:hover, &:focus {
text-decoration: none;
.umb-user-card__name {
text-decoration: underline;
text-decoration: underline;
color: @ui-option-type-hover;
}
.umb-avatar {
border: 1px solid @ui-option-type-hover;
}
}
}
@@ -0,0 +1,81 @@
.umb-user-table {
.umb-user-table-col-avatar {
flex: 0 0 32px;
padding: 15px 0;
}
.umb-table-cell a {
&:hover, &:focus {
.umb-avatar {
border: 1px solid @ui-option-type-hover;
}
}
}
.umb-table-body .umb-table-cell.umb-table__name {
margin: 0;
padding: 0;
a {
display: flex;
padding: 6px 2px;
height: 42px;
span {
margin: auto 14px;
}
}
}
.umb-table-cell.umb-table__name a {
&:hover, &:focus {
text-decoration: underline;
}
}
.umb-user-table-row {
cursor: default;
.umb-checkmark {
visibility: hidden;
}
}
.umb-user-table-row.-selectable {
cursor: pointer;
}
&.-has-selection {
.umb-user-table-row.-selectable {
.umb-checkmark {
visibility: visible;
}
}
}
.umb-user-table-row.-selectable:hover {
.umb-checkmark {
visibility: visible;
}
}
.umb-user-table-row.-selected {
.umb-checkmark {
visibility: visible;
}
&::before {
content: "";
position: absolute;
z-index:1;
top: 1px;
left: 1px;
right: 1px;
bottom: 1px;
border: 2px solid @ui-selected-border;
box-shadow: 0 0 2px 0 fade(@ui-selected-border, 80%);
pointer-events: none;
}
}
}
@@ -30,7 +30,7 @@
position: absolute;
padding: 5px 8px;
pointer-events: none;
top: 0;
top: 2px;
}
input[type="text"] {
@@ -13,23 +13,25 @@
border: none;
border-radius: 0;
overflow-y: auto;
background-color: @purple-d2;
background-color: @blueNight;
}
.login-overlay__background-image {
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
background-image: url('../img/login.jpg');
width: 100%;
height: 100%;
position: absolute;
opacity: 0.05;
}
.login-overlay__logo {
position: absolute;
top: 22px;
left: 25px;
width: 30px;
height: 30px;
z-index: 1;
}
@@ -18,7 +18,16 @@
&-push {
float:right;
}
}
&--list{
float: left;
}
&__item{
line-height: 1;
margin: 0 0 5px;
}
}
.umb-property-editor-tiny {
@@ -69,6 +78,11 @@
}
}
.umb-property .alert {
border-radius: 3px;
}
//
// Content picker
@@ -213,7 +227,7 @@
margin: 24px 0 0;
display: flex;
}
&__input {
width: 100%;
&-wrap{
@@ -37,7 +37,8 @@ ul.sections>li>a::after {
height: 4px;
width: 100%;
background-color: @pinkLight;
position: absolute;
position: absolute;
left: 0;
bottom: -4px;
border-radius: 3px 3px 0 0;
opacity: 0;
@@ -41,8 +41,8 @@
@purple-washed: #F6F3FD;
// UI Colors
@red-d1: #F02E28;// currently used for validation, and is hard coded inline in various html places :/
@red: #D42054;// updated 2019 - should be used as validation! and is already in some cases like the .umb-validation-label
@red-d1: #F02E28;
@red: #D42054;// updated 2019
@red-l1: #e22c60;// updated 2019
@red-l2: #FE8B88;
@red-l3: #FFB2B0;
@@ -508,7 +508,7 @@
@warningBorder: transparent;
@errorText: @white;
@errorBackground: @red-d1;
@errorBackground: @red;
@errorBorder: transparent;
@successText: @white;
@@ -113,7 +113,12 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi
$scope.exitPreview = function () {
var culture = $location.search().culture || getParameterByName("culture");
var relativeUrl = "/" + $scope.pageId +'?culture='+ culture;
var relativeUrl = "/" + $scope.pageId;
if(culture){
relativeUrl +='?culture='+ culture;
}
window.top.location.href = "../preview/end?redir=" + encodeURIComponent(relativeUrl);
};
@@ -78,7 +78,7 @@
// method to select a search result
function selectResult(evt, result) {
result.selected = result.selected === true ? false : true;
nodeSelectHandler(evt, { event: evt, node: result });
nodeSelectHandler({ event: evt, node: result });
}
//callback when there are search results
@@ -96,7 +96,7 @@
// Mini list view
$scope.selectListViewNode = function (node) {
node.selected = node.selected === true ? false : true;
nodeSelectHandler({}, { node: node });
nodeSelectHandler({ node: node });
};
$scope.closeMiniListView = function () {
@@ -10,7 +10,7 @@
(function() {
"use strict";
function DataTypePicker($scope, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService, editorService) {
function DataTypePicker($scope, $filter, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService, editorService) {
var vm = this;
@@ -119,13 +119,28 @@
$scope.model.itemDetails = null;
if (vm.searchTerm) {
vm.showFilterResult = true;
vm.showTabs = false;
var regex = new RegExp(vm.searchTerm, "i");
vm.filterResult = {
userConfigured: filterCollection(vm.userConfigured, regex),
typesAndEditors: filterCollection(vm.typesAndEditors, regex)
};
} else {
vm.showFilterResult = false;
vm.filterResult = null;
vm.showTabs = true;
}
}
function filterCollection(collection, regex) {
return _.map(_.keys(collection), function (key) {
return {
group: key,
dataTypes: $filter('filter')(collection[key], function (dataType) {
return regex.test(dataType.name) || regex.test(dataType.alias);
})
}
});
}
function showDetailsOverlay(property) {
@@ -201,4 +216,4 @@
angular.module("umbraco").controller("Umbraco.Editors.DataTypePickerController", DataTypePicker);
})();
})();
@@ -79,13 +79,13 @@
</umb-tab-content>
</div>
<!-- FILTER RESULTS -->
<div ng-if="vm.showFilterResult">
<div ng-if="vm.filterResult">
<h5 class="-border-bottom -black"><localize key="contentTypeEditor_reuse"></localize></h5>
<div ng-repeat="(key,value) in vm.userConfigured">
<div ng-if="(value | filter:vm.searchTerm).length > 0">
<h5>{{key}}</h5>
<div ng-repeat="result in vm.filterResult.userConfigured">
<div ng-if="result.dataTypes.length > 0">
<h5>{{result.group}}</h5>
<ul class="umb-card-grid" ng-mouseleave="vm.hideDetailsOverlay()">
<li ng-repeat="dataType in value | orderBy:'name' | filter: vm.searchTerm"
<li ng-repeat="dataType in result.dataTypes | orderBy:'name'"
ng-mouseover="vm.showDetailsOverlay(dataType)"
ng-click="vm.pickDataType(dataType)"
class="-four-in-row">
@@ -101,11 +101,11 @@
</div>
</div>
<h5 class="-border-bottom -black"><localize key="contentTypeEditor_availableEditors"></localize></h5>
<div ng-repeat="(key,value) in vm.typesAndEditors">
<div ng-if="(value | filter:vm.searchTerm).length > 0">
<h5>{{key}}</h5>
<div ng-repeat="result in vm.filterResult.typesAndEditors">
<div ng-if="result.dataTypes.length > 0">
<h5>{{result.group}}</h5>
<ul class="umb-card-grid" ng-mouseleave="vm.hideDetailsOverlay()">
<li ng-repeat="systemDataType in value | orderBy:'name' | filter: vm.searchTerm"
<li ng-repeat="systemDataType in result.dataTypes | orderBy:'name'"
ng-mouseover="vm.showDetailsOverlay(systemDataType)"
ng-click="vm.pickEditor(systemDataType)"
class="-four-in-row">
@@ -79,7 +79,7 @@
// method to select a search result
function selectResult(evt, result) {
result.selected = result.selected === true ? false : true;
nodeSelectHandler(evt, { event: evt, node: result });
nodeSelectHandler({ event: evt, node: result });
}
//callback when there are search results
@@ -96,7 +96,7 @@
// Mini list view
$scope.selectListViewNode = function (node) {
node.selected = node.selected === true ? false : true;
nodeSelectHandler({}, { node: node });
nodeSelectHandler({ node: node });
};
$scope.closeMiniListView = function () {
@@ -2,10 +2,10 @@
<div id="login" class="umb-modalcolumn umb-dialog" ng-class="{'show-validation': vm.loginForm.$invalid}" ng-cloak>
<div class="login-overlay__background-image" ng-if="vm.backgroundImage" ng-style="{'background-image':'url(' + vm.backgroundImage + ')'}"></div>
<div class="login-overlay__background-image" ng-style="{'background-image': 'url('+vm.backgroundImage+')'}"></div>
<div class="login-overlay__logo">
<img ng-src="assets/img/application/logo.png" ng-srcset="assets/img/application/logo@2x.png 2x, assets/img/application/logo@3x.png 3x">
<img src="assets/img/application/umbraco_logo_white.svg">
</div>
<div ng-show="vm.invitedUser != null" class="umb-login-container">
@@ -37,11 +37,11 @@
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" ng-message="valCompare"><localize key="user_passwordMismatch">The confirmed password doesn't match the new password!</localize></span>
</span>
</div>
<div class="flex justify-between items-center">
<umb-button
<umb-button
type="submit"
button-style="success"
state="vm.invitedUserPasswordModel.buttonState"
@@ -58,7 +58,7 @@
<ng-form name="vm.avatarForm">
<umb-progress-bar
<umb-progress-bar
style="max-width: 100px; margin-bottom: 5px;"
ng-show="vm.avatarFile.uploadStatus === 'uploading'"
progress="{{ vm.avatarFile.uploadProgress }}"
@@ -77,7 +77,7 @@
ngf-pattern="{{vm.avatarFile.acceptedFileTypes}}"
ngf-max-size="{{ vm.avatarFile.maxFileSize }}">
<umb-avatar
<umb-avatar
color="gray"
size="xl"
unknown-char="+"
@@ -94,7 +94,7 @@
<localize key="user_userinviteAvatarMessage"></localize>
</p>
<div class="flex justify-center items-center">
<umb-button
<umb-button
type="button"
button-style="success"
label="Done"
@@ -102,7 +102,7 @@
</umb-button>
</div>
</div>
</div>
<div ng-show="vm.invitedUser == null && vm.inviteStep === 3" ng-if="vm.inviteStep === 3" class="umb-login-container">
<div class="form">
@@ -169,7 +169,7 @@
</div>
<div class="flex justify-between items-center">
<umb-button
<umb-button
button-style="success"
size="m"
label-key="general_login"
@@ -261,4 +261,4 @@
</div>
</div>
</div>
</div>
</div>
@@ -38,7 +38,7 @@
<span>{{vm.currentVariant.language.name}}</span>
</span>
<umb-dropdown ng-if="vm.dropdownOpen" style="width: 100%; max-height: 250px; overflow-y: scroll; margin-top: 5px;" on-close="vm.dropdownOpen = false" umb-keyboard-list>
<umb-dropdown ng-if="vm.dropdownOpen" style="min-width: 100%; max-height: 250px; overflow-y: auto; margin-top: 5px;" on-close="vm.dropdownOpen = false" umb-keyboard-list>
<umb-dropdown-item class="umb-variant-switcher__item" ng-class="{'umb-variant-switcher_item--current': variant.active, 'umb-variant-switcher_item--not-allowed': variantIsOpen(variant.language.culture)}" ng-repeat="variant in content.variants">
<a href="" class="umb-variant-switcher__name-wrapper" ng-click="selectVariant($event, variant)" prevent-default>
<span class="umb-variant-switcher__name" ng-class="{'bold': variant.language.isDefault}">{{variant.language.name}}</span>
@@ -1,13 +1,13 @@
<a data-element="sub-view-{{vm.item.alias}}"
tabindex="-1"
ng-href=""
ng-click="vm.clicked()"
hotkey="{{vm.index+1}}"
hotkey-when-hidden="true"
ng-class="{'is-active': vm.item.active, '-has-error': vm.item.hasError}">
tabindex="-1"
ng-href=""
ng-click="vm.clicked()"
hotkey="{{::vm.hotkey}}"
hotkey-when-hidden="true"
ng-class="{'is-active': vm.item.active, '-has-error': vm.item.hasError}">
<i class="icon {{ vm.item.icon }}"></i>
<span class="umb-sub-views-nav-item-text">{{ vm.item.name }}</span>
<div ng-show="item.badge" class="badge -type-{{vm.item.badge.type}}">{{vm.item.badge.count}}</div>
<div ng-show="vm.item.badge" class="badge -type-{{vm.item.badge.type}}">{{vm.item.badge.count}}</div>
</a>
<ul class="dropdown-menu umb-sub-views-nav-item__anchor_dropdown" ng-if="vm.item.anchors && vm.item.anchors.length > 1">
@@ -7,7 +7,7 @@
item="navItem"
on-open="openNavigationItem(item)"
on-open-anchor="openAnchorItem(item, anchor)"
index="{{$index}}">
hotkey="$index + 1">
</umb-editor-navigation-item>
</div>
</li>
@@ -0,0 +1,19 @@
<label class="checkbox umb-form-check umb-form-check--checkbox" ng-class="{ 'umb-form-check--disabled': disabled }">
<input type="checkbox"
id="{{vm.inputId}}"
name="{{vm.name}}"
value="{{vm.value}}"
class="umb-form-check__input"
val-server-field="{{vm.serverValidationField}}"
ng-model="vm.model"
ng-disabled="vm.disabled"
ng-required="vm.required"
ng-change="vm.callOnChange()"/>
<span class="umb-form-check__state" aria-hidden="true">
<span class="umb-form-check__check">
<i class="umb-form-check__icon icon-check"></i>
</span>
</span>
<span class="umb-form-check__text">{{vm.text}}</span>
</label>
@@ -0,0 +1,13 @@
<label class="radio umb-form-check umb-form-check--radiobutton" ng-class="{ 'umb-form-check--disabled': disabled }">
<input type="radio" name="{{name}}"
value="{{value}}"
class="umb-form-check__input"
ng-model="model"
ng-disabled="disabled"
ng-required="required" />
<span class="umb-form-check__state" aria-hidden="true">
<span class="umb-form-check__check"></span>
</span>
<span class="umb-form-check__text">{{text}}</span>
</label>
@@ -10,10 +10,13 @@
<div class="umb-content-grid__content">
<div class="umb-content-grid__item-name" ng-click="clickItemName(item, $event, $index)" ng-class="{'-light': !item.published && item.updater != null}">
<a class="umb-content-grid__item-name"
ng-href="{{'#' + item.editPath}}"
ng-click="clickItemName(item, $event, $index)"
ng-class="{'-light': !item.published && item.updater != null}">
<i class="umb-content-grid__icon {{ item.icon }}"></i>
<span>{{ item.name }}</span>
</div>
</a>
<ul class="umb-content-grid__details-list" ng-class="{'-light': !item.published && item.updater != null}">
<li class="umb-content-grid__details-item" ng-if="item.state">
@@ -40,7 +40,8 @@
<i class="umb-table-body__icon umb-table-body__checkicon icon-check"></i>
</div>
<div class="umb-table-cell umb-table__name">
<a title="{{ item.name }}" class="umb-table-body__link" href=""
<a title="{{ item.name }}" class="umb-table-body__link"
ng-href="{{'#' + item.editPath}}"
ng-click="vm.clickItem(item, $event)"
ng-bind="item.name">
</a>
@@ -46,8 +46,7 @@
if (data.language !== "undefined") {
var lang = vm.languages.filter(function (l) {
return matchLanguageById(l, data.language.Id);
return matchLanguageById(l, data.language);
});
if (lang.length > 0) {
vm.language = lang[0];
@@ -116,6 +115,7 @@
if(response.valid) {
vm.submitButtonState = "success";
closeDialog();
// show validation messages for each domain
} else {
@@ -93,7 +93,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController",
// method to select a search result
$scope.selectResult = function (evt, result) {
result.selected = result.selected === true ? false : true;
nodeSelectHandler(evt, { event: evt, node: result });
nodeSelectHandler({ event: evt, node: result });
};
//callback when there are search results
@@ -66,7 +66,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController",
// method to select a search result
$scope.selectResult = function (evt, result) {
result.selected = result.selected === true ? false : true;
nodeSelectHandler(evt, { event: evt, node: result });
nodeSelectHandler({ event: evt, node: result });
};
//callback when there are search results
@@ -124,7 +124,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController",
// Mini list view
$scope.selectListViewNode = function (node) {
node.selected = node.selected === true ? false : true;
nodeSelectHandler({}, { node: node });
nodeSelectHandler({ node: node });
};
$scope.closeMiniListView = function () {
@@ -57,7 +57,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController"
// method to select a search result
$scope.selectResult = function (evt, result) {
result.selected = result.selected === true ? false : true;
nodeSelectHandler(evt, { event: evt, node: result });
nodeSelectHandler({ event: evt, node: result });
};
//callback when there are search results
@@ -74,7 +74,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController"
// Mini list view
$scope.selectListViewNode = function (node) {
node.selected = node.selected === true ? false : true;
nodeSelectHandler({}, { node: node });
nodeSelectHandler({ node: node });
};
$scope.closeMiniListView = function () {
@@ -1,7 +1,7 @@
(function () {
"use strict";
function PublishController($scope, localizationService) {
function PublishController($scope, localizationService, contentEditingHelper) {
var vm = this;
vm.loading = true;
@@ -14,34 +14,51 @@
/** Returns true if publishing is possible based on if there are un-published mandatory languages */
function canPublish() {
var selected = [];
var possible = false;
for (var i = 0; i < vm.variants.length; i++) {
var variant = vm.variants[i];
//if this variant will show up in the publish-able list
var publishable = dirtyVariantFilter(variant);
var published = !(variant.state === "NotCreated" || variant.state === "Draft");
if ((variant.language.isMandatory && !published) && (!publishable || !variant.publish)) {
//if a mandatory variant isn't published
//and it's not publishable or not selected to be published
//then we cannot continue
// TODO: Show a message when this occurs
var state = canVariantPublish(variant);
if (state === true) {
possible = true;
}
if (state === false) {
return false;
}
if (variant.publish) {
selected.push(variant.publish);
}
}
return selected.length > 0;
return possible;
}
/** Returns true if publishing is possible based on if the variant is a un-published mandatory language */
function canVariantPublish(variant) {
//if this variant will show up in the publish-able list
var publishable = dirtyVariantFilter(variant);
var published = !(variant.state === "NotCreated" || variant.state === "Draft");
// is this variant mandatory:
if (variant.language.isMandatory && !published && !variant.publish) {
//if a mandatory variant isn't published or set to be published
//then we cannot continue
return false;
}
// is this variant selected for publish:
if (variant.publish === true) {
return publishable;
}
return null;
}
function changeSelection(variant) {
$scope.model.disableSubmitButton = !canPublish();
//need to set the Save state to true if publish is true
variant.save = variant.publish;
variant.willPublish = canVariantPublish(variant);
}
function dirtyVariantFilter(variant) {
@@ -99,32 +116,49 @@
_.each(vm.variants,
function (variant) {
if(variant.state !== "NotCreated"){
vm.isNew = false;
if(variant.state === "NotCreated") {
vm.isNew = true;
}
});
}
);
_.each(vm.variants,
function (variant) {
variant.compositeId = variant.language.culture + "_" + (variant.segment ? variant.segment : "");
variant.compositeId = contentEditingHelper.buildCompositeVariantId(variant);
variant.htmlId = "_content_variant_" + variant.compositeId;
// reset to not be published
variant.publish = false;
variant.save = false;
//check for pristine variants
if (!vm.hasPristineVariants) {
vm.hasPristineVariants = pristineVariantFilter(variant);
}
if(hasAnyData(variant)){
// If the variant havent been created jet.
if(variant.state === "NotCreated") {
// If the variant is mandatory, then set the variant to be published.
if (variant.language.isMandatory === true) {
variant.publish = true;
variant.save = true;
}
}
variant.canPublish = dirtyVariantFilter(variant);
// if we have data on this variant.
if(variant.canPublish && hasAnyData(variant)) {
// and if some varaints havent been saved before, or they dont have a publishing date set, then we set it for publishing.
if(vm.isNew || variant.publishDate == null){
variant.publish = true;
variant.save = true;
}
}else{
variant.publish = false;
variant.save = false;
variant.canSave = false;
}
});
variant.willPublish = canVariantPublish(variant);
}
);
if (vm.variants.length !== 0) {
//now sort it so that the current one is at the top
@@ -153,7 +187,6 @@
localizationService.localize(labelKey).then(function (value) {
vm.headline = value;
vm.loading = false;
});
@@ -10,7 +10,7 @@
<div class="umb-list umb-list--condensed" ng-if="!vm.loading">
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.dirtyVariantFilter track by variant.compositeId">
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.dirtyVariantFilter track by variant.compositeId" ng-class="{'umb-list-item--error': publishVariantSelectorForm.publishVariantSelector.$invalid || (variant.notifications | filter:{type:1}).length > 0}">
<ng-form name="publishVariantSelectorForm">
<div class="flex">
<input id="{{variant.htmlId}}"
@@ -18,27 +18,28 @@
type="checkbox"
ng-model="variant.publish"
ng-change="vm.changeSelection(variant)"
ng-disabled="(vm.isNew && variant.language.isMandatory) || (variant.canSave === false)"
ng-disabled="(variant.canPublish === false)"
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<div>
<label for="{{variant.htmlId}}" style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
<strong ng-if="variant.language.isMandatory" class="umb-control-required">*</strong>
<span class="db umb-permission__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - </span>
<span ng-if="variant.language.isMandatory" ng-class="{'text-error': (variant.language.isMandatory && variant.willPublish === false) }"><localize key="languages_mandatoryLanguage"></localize></span>
</span>
<span class="db" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
<span class="db umb-permission__description text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
</span>
<span class="db" ng-repeat="notification in variant.notifications">
<span class="db umb-permission__description" ng-class="{'text-success': notification.type === 3, 'text-error': notification.type !== 3}">{{notification.message}}</span>
</span>
</label>
<div class="umb-permission__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</div>
<div ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
<div class="umb-permission__description" style="color: #F02E28;" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</div>
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
</div>
</div>
</div>
@@ -64,7 +65,7 @@
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
<div class="umb-permission__description text-success">{{notification.message}}</div>
</div>
</div>
@@ -22,11 +22,13 @@
<p><localize key="content_publishDescendantsWithVariantsHelp"></localize></p>
</div>
<div style="margin-bottom: 15px;">
<label>
<input type="checkbox"
ng-model="model.includeUnpublished"
style="margin-right: 8px;" />
<div style="margin-bottom: 15px;" class="flex">
<umb-checkbox
input-id="includeUnpublishedSelector"
model="model.includeUnpublished" />
<label for="includeUnpublishedSelector">
<localize key="content_includeUnpublished"></localize>
</label>
</div>
@@ -40,31 +42,31 @@
<div class="umb-list-item umb-list--condensed" ng-repeat="variant in vm.variants">
<ng-form name="publishVariantSelectorForm">
<div class="flex">
<input id="{{variant.htmlId}}"
name="publishVariantSelector"
type="checkbox"
ng-model="variant.publish"
ng-change="vm.changeSelection(variant)"
style="margin-right: 8px;"
val-server-field="{{variant.htmlId}}" />
<umb-checkbox
input-id="{{variant.htmlId}}"
name="publishVariantSelector"
model="variant.publish"
on-change="vm.changeSelection(variant)"
server-validation-field="{{variant.htmlId}}"/>
<div>
<label for="{{variant.htmlId}}" style="margin-bottom: 0;">
<span>{{ variant.language.name }}</span>
<span class="db umb-permission__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</span>
<span class="db" ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
<span class="db umb-permission__description text-error" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</span>
</span>
<span class="db" ng-repeat="notification in variant.notifications">
<span class="db umb-permission__description text-success">{{notification.message}}</span>
</span>
</label>
<div class="umb-permission__description" ng-if="!publishVariantSelectorForm.publishVariantSelector.$invalid && !(variant.notifications && variant.notifications.length > 0)">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-if="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</div>
<div ng-messages="publishVariantSelectorForm.publishVariantSelector.$error" show-validation-on-submit>
<div class="umb-permission__description" style="color: #F02E28;" ng-message="valServerField">{{publishVariantSelectorForm.publishVariantSelector.errorMsg}}</div>
</div>
<div ng-repeat="notification in variant.notifications">
<div class="umb-permission__description" style="color: #1FB572;">{{notification.message}}</div>
</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More