Compare commits

..

11 Commits

Author SHA1 Message Date
Niels Hartvig fdcd6b542c Merge branch 'dev-v7' into temp-new-doctype-prototype 2018-03-01 14:03:22 +01:00
Niels Hartvig f5e8675c97 Fix merge conflict 2018-03-01 13:36:55 +01:00
Niels Hartvig 5f83a9f006 Merge 7.9 into the doctype prototype 2018-03-01 13:30:44 +01:00
Niels Hartvig 8b10808d40 Merge branch 'dev-v7.8' into temp-new-doctype-prototype 2018-03-01 13:22:13 +01:00
Niels Hartvig 5cffd43db0 Fixes issue with debouncing alias not getting updated 2018-03-01 13:21:04 +01:00
Niels Hartvig 0589a65281 Make the name field larger, the preview slightly smaller 2018-03-01 10:36:28 +01:00
Niels Hartvig 4f6809173a Fixes issue with properties not being reset if you cancel adding a property 2018-03-01 10:20:10 +01:00
Niels Hartvig 591a4fbf1b Make selecting an editor the default flow when adding property 2018-03-01 10:00:07 +01:00
Niels Hartvig e0c4fc913b Remove the option of giving a data type a name (makes no sense in this step) 2018-03-01 09:59:35 +01:00
Niels Hartvig 15468d3bdc Add support for getting datatype folders and check if one exist 2018-03-01 09:59:10 +01:00
Niels Hartvig 4c1d3b4b27 Work in progress - make picking editor the first option of the doctype editor 2018-02-23 06:35:09 +10:00
141 changed files with 2437 additions and 2864 deletions
-1
View File
@@ -113,7 +113,6 @@ build/ApiDocs/*
build/ApiDocs/Output/*
src/Umbraco.Web.UI.Client/bower_components/*
/src/Umbraco.Web.UI/Umbraco/preview
/src/Umbraco.Web.UI/Umbraco/preview.old
#Ignore Rule for output of generated documentation files from Grunt docserve
src/Umbraco.Web.UI.Client/docs/api
+1 -34
View File
@@ -24,7 +24,6 @@ Remember, we're a friendly bunch and are happy with whatever contribution you mi
* [Working with the source code](#working-with-the-source-code)
* [What branch should I target for my contributions?](#what-branch-should-i-target-for-my-contributions)
* [Building Umbraco from source code](#building-umbraco-from-source-code)
* [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository)
[How do I even begin?](#how-do-i-even-begin)
@@ -115,15 +114,6 @@ Some parts of our source code is over 10 years old now. And when we say "old", w
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 bower
npm install -g gulp
npm install -g gulp-cli
npm install
gulp 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.
To find the general areas of something you're looking to fix or improve, have a look at the following two parts of the API documentation.
@@ -147,29 +137,6 @@ Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 ([the commun
After this build completes, you should be able to hit `F5` in Visual Studio to build and run the project. A IISExpress webserver will start and the Umbraco installer will pop up in your browser, follow the directions there to get a working Umbraco install up and running.
### Keeping your Umbraco fork in sync with the main repository
We recommend you sync with our repository before you submit your pull request. That way, you can fix any potential merge conflicts and make our lives a little bit easier.
Also, if you've submitted a pull request three weeks ago and want to work on something new, you'll want to get the latest code to build against of course.
To sync your fork with this original one, you'll have to add the upstream url, you only have to do this once:
```
git remote add upstream https://github.com/umbraco/Umbraco-CMS.git
```
Then when you want to get the changes from the main repository:
```
git fetch upstream
git rebase upstream/dev-v7
```
In this command we're syncing with the `dev-v7` branch, but you can of course choose another one if needed.
(More info on how this works: [http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated](http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated))
## How do I even begin?
Great question! The short version goes like this:
@@ -204,4 +171,4 @@ Did something not work as expected? Try leaving a note in the ["Contributing to
## Credits
This contribution guide borrows heavily from the excellent work on [the Atom contribution guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md). A big [#h5yr](http://h5yr.com/) to them!
This contribution guide borrows heavily from the excellent work on [the Atom contribution guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md). A big [#h5yr](http://h5yr.com/) to them!
+1 -1
View File
@@ -30,7 +30,7 @@ As an Open Source platform, Umbraco is more than just a CMS. We are transparent
[Umbraco Cloud](https://umbraco.com) is the easiest and fastest way to use Umbraco yet with full support for all your custom .NET code and intergrations. You're up and running in less than a minute and your life will be made easier with automated upgrades and a built-in deployment engine. We offer a free 14 day trial, no credit card needed.
If you want to DIY you can [download Umbraco](https://our.umbraco.org/download) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Cloud, but you'll need to find a place to host yourself and handling deployments and upgrades is all down to you.
If you want to DIY you can [download Umbraco](https://our.umbraco.org/download) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Xloud, but you'll need to find a place to host yourself and handling deployments and upgrades is all down to you.
## Community
@@ -8,7 +8,6 @@ function Get-UmbracoBuildEnv
# store tools in the module's directory
# and cache them for two days
$path = "$PSScriptRoot\temp"
$src = "$PSScriptRoot\..\..\..\src"
$cache = 2
if (-not (test-path $path))
@@ -38,7 +37,7 @@ function Get-UmbracoBuildEnv
if (-not (test-path $sevenZip))
{
Write-Host "Download 7-Zip..."
&$nuget install 7-Zip.CommandLine -configFile "$src\NuGet.config" -OutputDirectory $path -Verbosity quiet
&$nuget install 7-Zip.CommandLine -OutputDirectory $path -Verbosity quiet
$dir = ls "$path\7-Zip.CommandLine.*" | sort -property Name -descending | select -first 1
$file = ls -path "$dir" -name 7za.exe -recurse
$file = ls -path "$dir" -name 7za.exe -recurse | select -first 1 #A select is because there is tools\7za.exe & tools\x64\7za.exe
@@ -55,7 +54,7 @@ function Get-UmbracoBuildEnv
if (-not (test-path $vswhere))
{
Write-Host "Download VsWhere..."
&$nuget install vswhere -configFile "$src\NuGet.config" -OutputDirectory $path -Verbosity quiet
&$nuget install vswhere -OutputDirectory $path -Verbosity quiet
$dir = ls "$path\vswhere.*" | sort -property Name -descending | select -first 1
$file = ls -path "$dir" -name vswhere.exe -recurse
mv "$dir\$file" $vswhere
@@ -71,7 +70,7 @@ function Get-UmbracoBuildEnv
if (-not (test-path $semver))
{
Write-Host "Download Semver..."
&$nuget install semver -configFile "$src\NuGet.config" -OutputDirectory $path -Verbosity quiet
&$nuget install semver -OutputDirectory $path -Verbosity quiet
$dir = ls "$path\semver.*" | sort -property Name -descending | select -first 1
$file = "$dir\lib\net452\Semver.dll"
if (-not (test-path $file))
@@ -376,7 +376,7 @@ function Prepare-Packages
Copy-Files "$src\Umbraco.Web.UI\umbraco\js" "*" "$tmp\WebApp\umbraco\js"
Copy-Files "$src\Umbraco.Web.UI\umbraco\lib" "*" "$tmp\WebApp\umbraco\lib"
Copy-Files "$src\Umbraco.Web.UI\umbraco\views" "*" "$tmp\WebApp\umbraco\views"
Copy-Files "$src\Umbraco.Web.UI\umbraco\preview" "*" "$tmp\WebApp\umbraco\preview"
}
#
@@ -445,7 +445,7 @@ function Restore-NuGet
Write-Host ">> Restore NuGet"
Write-Host "Logging to $tmp\nuget.restore.log"
&$uenv.NuGet restore "$src\Umbraco.sln" -configfile "$src\NuGet.config" > "$tmp\nuget.restore.log"
&$uenv.NuGet restore "$src\Umbraco.sln" > "$tmp\nuget.restore.log"
}
#
-1
View File
@@ -39,7 +39,6 @@
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.7.0, 5.0.0)" />
<dependency id="System.ValueTuple" version="[4.4.0, 5.0.0)" />
</dependencies>
</metadata>
<files>
+1 -1
View File
@@ -8,7 +8,7 @@
----------------------------------------------------
*** IMPORTANT NOTICE FOR UPGRADES FROM VERSIONS BELOW 7.7.0 ***
*** IMPORTANT NOTICE FOR 7.7 UPGRADES ***
Be sure to read the version specific upgrade information before proceeding:
https://our.umbraco.org/documentation/Getting-Started/Setup/Upgrading/version-specific#version-7-7-0
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.10.0")]
[assembly: AssemblyInformationalVersion("7.10.0")]
[assembly: AssemblyFileVersion("7.9.1")]
[assembly: AssemblyInformationalVersion("7.9.1")]
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.10.0");
private static readonly Version Version = new Version("7.9.1");
/// <summary>
/// Gets the current version of Umbraco.
@@ -122,11 +122,6 @@ namespace Umbraco.Core
/// Alias for the Dropdown list, publishing keys datatype.
/// </summary>
public const string DropdownlistPublishingKeysAlias = "Umbraco.DropdownlistPublishingKeys";
/// <summary>
/// Alias for the "new" Dropdown list, that replaces the old four deprecated ones and works as other list based property editors
/// </summary>
public const string DropDownListFlexibleAlias = "Umbraco.DropDown.Flexible";
/// <summary>
/// Guid for the Folder browser datatype.
@@ -457,4 +452,4 @@ namespace Umbraco.Core
}
}
}
}
}
-16
View File
@@ -459,22 +459,6 @@ namespace Umbraco.Core
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
var connectionstrings = xml.Root.DescendantsAndSelf("connectionStrings").Single();
// honour configSource, if its set, change the xml file we are saving the configuration
// to the one set in the configSource attribute
if (connectionstrings.Attribute("configSource") != null)
{
var source = connectionstrings.Attribute("configSource").Value;
var configFile = IOHelper.MapPath(string.Format("{0}/{1}", SystemDirectories.Root, source));
LogHelper.Info<DatabaseContext>("storing ConnectionString in {0}", () => configFile);
if (System.IO.File.Exists(configFile))
{
xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
fileName = configFile;
}
connectionstrings = xml.Root.DescendantsAndSelf("connectionStrings").Single();
}
// Update connectionString if it exists, or else create a new appSetting for the given key and value
var setting = connectionstrings.Descendants("add").FirstOrDefault(s => s.Attribute("name").Value == Constants.System.UmbracoConnectionName);
if (setting == null)
@@ -2,7 +2,6 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Reflection;
using Umbraco.Core.Configuration;
@@ -27,7 +26,6 @@ namespace Umbraco.Core.IO
private ShadowWrapper _xsltFileSystem;
private ShadowWrapper _masterPagesFileSystem;
private ShadowWrapper _mvcViewsFileSystem;
private ShadowWrapper _javaScriptLibraryFileSystem;
#region Singleton & Constructor
@@ -115,7 +113,6 @@ namespace Umbraco.Core.IO
var xsltFileSystem = new PhysicalFileSystem(SystemDirectories.Xslt);
var masterPagesFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages);
var mvcViewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews);
var javaScriptLibraryFileSystem = new PhysicalFileSystem(Path.Combine(SystemDirectories.Umbraco, "lib"));
_macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "Views/MacroPartials", ScopeProvider);
_partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", ScopeProvider);
@@ -126,7 +123,6 @@ namespace Umbraco.Core.IO
_xsltFileSystem = new ShadowWrapper(xsltFileSystem, "xslt", ScopeProvider);
_masterPagesFileSystem = new ShadowWrapper(masterPagesFileSystem, "masterpages", ScopeProvider);
_mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, "Views", ScopeProvider);
_javaScriptLibraryFileSystem = new ShadowWrapper(javaScriptLibraryFileSystem, "Lib", ScopeProvider);
// filesystems obtained from GetFileSystemProvider are already wrapped and do not need to be wrapped again
MediaFileSystem = GetFileSystemProvider<MediaFileSystem>();
@@ -147,7 +143,6 @@ namespace Umbraco.Core.IO
public IFileSystem2 XsltFileSystem { get { return _xsltFileSystem; } }
public IFileSystem2 MasterPagesFileSystem { get { return _mvcViewsFileSystem; } }
public IFileSystem2 MvcViewsFileSystem { get { return _mvcViewsFileSystem; } }
internal IFileSystem2 JavaScriptLibraryFileSystem { get { return _javaScriptLibraryFileSystem; } }
public MediaFileSystem MediaFileSystem { get; private set; }
#endregion
+1
View File
@@ -203,6 +203,7 @@ namespace Umbraco.Core.IO
{
get
{
//by default the packages folder should exist in the data folder
return IOHelper.ReturnPath("umbracoPreviewPath", Data + IOHelper.DirSepChar + "preview");
}
}
+56 -28
View File
@@ -140,7 +140,8 @@ namespace Umbraco.Core
if (underlying != null)
{
// Special case for empty strings for bools/dates which should return null if an empty string.
if (input is string inputString)
var inputString = input as string;
if (inputString != null)
{
//TODO: Why the check against only bool/date when a string is null/empty? In what scenario can we convert to another type when the string is null or empty other than just being null?
if (string.IsNullOrEmpty(inputString) && (underlying == typeof(DateTime) || underlying == typeof(bool)))
@@ -167,7 +168,8 @@ namespace Umbraco.Core
{
// target is not a generic type
if (input is string inputString)
var inputString = input as string;
if (inputString != null)
{
// Try convert from string, returns an Attempt if the string could be
// processed (either succeeded or failed), else null if we need to try
@@ -216,7 +218,8 @@ namespace Umbraco.Core
}
// Re-check convertables since we altered the input through recursion
if (input is IConvertible convertible2)
var convertible2 = input as IConvertible;
if (convertible2 != null)
{
return Attempt.Succeed(Convert.ChangeType(convertible2, target));
}
@@ -274,7 +277,8 @@ namespace Umbraco.Core
{
if (target == typeof(int))
{
if (int.TryParse(input, out var value))
int value;
if (int.TryParse(input, out value))
{
return Attempt<object>.Succeed(value);
}
@@ -282,26 +286,30 @@ namespace Umbraco.Core
// Because decimal 100.01m will happily convert to integer 100, it
// makes sense that string "100.01" *also* converts to integer 100.
var input2 = NormalizeNumberDecimalSeparator(input);
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt32(value2));
decimal value2;
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt32(value2));
}
if (target == typeof(long))
{
if (long.TryParse(input, out var value))
long value;
if (long.TryParse(input, out value))
{
return Attempt<object>.Succeed(value);
}
// Same as int
var input2 = NormalizeNumberDecimalSeparator(input);
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt64(value2));
decimal value2;
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt64(value2));
}
// TODO: Should we do the decimal trick for short, byte, unsigned?
if (target == typeof(bool))
{
if (bool.TryParse(input, out var value))
bool value;
if (bool.TryParse(input, out value))
{
return Attempt<object>.Succeed(value);
}
@@ -314,42 +322,53 @@ namespace Umbraco.Core
switch (Type.GetTypeCode(target))
{
case TypeCode.Int16:
return Attempt<object>.SucceedIf(short.TryParse(input, out var value), value);
short value;
return Attempt<object>.SucceedIf(short.TryParse(input, out value), value);
case TypeCode.Double:
var input2 = NormalizeNumberDecimalSeparator(input);
return Attempt<object>.SucceedIf(double.TryParse(input2, out var valueD), valueD);
double valueD;
return Attempt<object>.SucceedIf(double.TryParse(input2, out valueD), valueD);
case TypeCode.Single:
var input3 = NormalizeNumberDecimalSeparator(input);
return Attempt<object>.SucceedIf(float.TryParse(input3, out var valueF), valueF);
float valueF;
return Attempt<object>.SucceedIf(float.TryParse(input3, out valueF), valueF);
case TypeCode.Char:
return Attempt<object>.SucceedIf(char.TryParse(input, out var valueC), valueC);
char valueC;
return Attempt<object>.SucceedIf(char.TryParse(input, out valueC), valueC);
case TypeCode.Byte:
return Attempt<object>.SucceedIf(byte.TryParse(input, out var valueB), valueB);
byte valueB;
return Attempt<object>.SucceedIf(byte.TryParse(input, out valueB), valueB);
case TypeCode.SByte:
return Attempt<object>.SucceedIf(sbyte.TryParse(input, out var valueSb), valueSb);
sbyte valueSb;
return Attempt<object>.SucceedIf(sbyte.TryParse(input, out valueSb), valueSb);
case TypeCode.UInt32:
return Attempt<object>.SucceedIf(uint.TryParse(input, out var valueU), valueU);
uint valueU;
return Attempt<object>.SucceedIf(uint.TryParse(input, out valueU), valueU);
case TypeCode.UInt16:
return Attempt<object>.SucceedIf(ushort.TryParse(input, out var valueUs), valueUs);
ushort valueUs;
return Attempt<object>.SucceedIf(ushort.TryParse(input, out valueUs), valueUs);
case TypeCode.UInt64:
return Attempt<object>.SucceedIf(ulong.TryParse(input, out var valueUl), valueUl);
ulong valueUl;
return Attempt<object>.SucceedIf(ulong.TryParse(input, out valueUl), valueUl);
}
}
else if (target == typeof(Guid))
{
return Attempt<object>.SucceedIf(Guid.TryParse(input, out var value), value);
Guid value;
return Attempt<object>.SucceedIf(Guid.TryParse(input, out value), value);
}
else if (target == typeof(DateTime))
{
if (DateTime.TryParse(input, out var value))
DateTime value;
if (DateTime.TryParse(input, out value))
{
switch (value.Kind)
{
@@ -369,20 +388,24 @@ namespace Umbraco.Core
}
else if (target == typeof(DateTimeOffset))
{
return Attempt<object>.SucceedIf(DateTimeOffset.TryParse(input, out var value), value);
DateTimeOffset value;
return Attempt<object>.SucceedIf(DateTimeOffset.TryParse(input, out value), value);
}
else if (target == typeof(TimeSpan))
{
return Attempt<object>.SucceedIf(TimeSpan.TryParse(input, out var value), value);
TimeSpan value;
return Attempt<object>.SucceedIf(TimeSpan.TryParse(input, out value), value);
}
else if (target == typeof(decimal))
{
var input2 = NormalizeNumberDecimalSeparator(input);
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out var value), value);
decimal value;
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value), value);
}
else if (input != null && target == typeof(Version))
{
return Attempt<object>.SucceedIf(Version.TryParse(input, out var value), value);
Version value;
return Attempt<object>.SucceedIf(Version.TryParse(input, out value), value);
}
// E_NOTIMPL IPAddress, BigInteger
@@ -667,7 +690,8 @@ namespace Umbraco.Core
{
var key = new CompositeTypeTypeKey(source, target);
if (InputTypeConverterCache.TryGetValue(key, out TypeConverter typeConverter))
TypeConverter typeConverter;
if (InputTypeConverterCache.TryGetValue(key, out typeConverter))
{
return typeConverter;
}
@@ -687,7 +711,8 @@ namespace Umbraco.Core
{
var key = new CompositeTypeTypeKey(source, target);
if (DestinationTypeConverterCache.TryGetValue(key, out TypeConverter typeConverter))
TypeConverter typeConverter;
if (DestinationTypeConverterCache.TryGetValue(key, out typeConverter))
{
return typeConverter;
}
@@ -705,7 +730,8 @@ namespace Umbraco.Core
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static Type GetCachedGenericNullableType(Type type)
{
if (NullableGenericCache.TryGetValue(type, out Type underlyingType))
Type underlyingType;
if (NullableGenericCache.TryGetValue(type, out underlyingType))
{
return underlyingType;
}
@@ -724,7 +750,8 @@ namespace Umbraco.Core
private static bool GetCachedCanAssign(object input, Type source, Type target)
{
var key = new CompositeTypeTypeKey(source, target);
if (AssignableTypeCache.TryGetValue(key, out bool canConvert))
bool canConvert;
if (AssignableTypeCache.TryGetValue(key, out canConvert))
{
return canConvert;
}
@@ -743,7 +770,8 @@ namespace Umbraco.Core
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool GetCachedCanConvertToBoolean(Type type)
{
if (BoolConvertCache.TryGetValue(type, out bool result))
bool result;
if (BoolConvertCache.TryGetValue(type, out result))
{
return result;
}
@@ -6,7 +6,6 @@ using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZero
@@ -344,24 +343,15 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
if (tables.InvariantContains("umbracoUserType") && tables.InvariantContains("umbracoUser"))
{
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_umbracoUser_umbracoUserType_id")))
{
//In MySql, this will drop the FK according to it's special naming rules
Delete.ForeignKey().FromTable("umbracoUser").ForeignColumn("userType").ToTable("umbracoUserType").PrimaryColumn("id");
Delete.ForeignKey("FK_umbracoUser_umbracoUserType_id").OnTable("umbracoUser");
}
else
//This is the super old constraint name of the FK for user type so check this one too
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_user_userType")))
{
//Delete the FK if it exists before dropping the column
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_umbracoUser_umbracoUserType_id")))
{
Delete.ForeignKey("FK_umbracoUser_umbracoUserType_id").OnTable("umbracoUser");
}
//This is the super old constraint name of the FK for user type so check this one too
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_user_userType")))
{
Delete.ForeignKey("FK_user_userType").OnTable("umbracoUser");
}
}
Delete.ForeignKey("FK_user_userType").OnTable("umbracoUser");
}
Delete.Column("userType").FromTable("umbracoUser");
Delete.Table("umbracoUserType");
@@ -371,4 +361,4 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
public override void Down()
{ }
}
}
}
@@ -1,44 +0,0 @@
using System.IO;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.SqlSyntax;
using File = System.IO.File;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenTenZero
{
/// <summary>
/// Renames the preview folder containing static html files to ensure it does not interfere with the MVC route
/// that is now supposed to render these views dynamically. We don't want to delete as people may have made
/// customizations to these files that would need to be migrated to the new .cshtml view files.
/// </summary>
[Migration("7.10.0", 1, Constants.System.UmbracoMigrationName)]
public class RenamePreviewFolder : MigrationBase
{
public RenamePreviewFolder(ISqlSyntaxProvider sqlSyntax, ILogger logger)
: base(sqlSyntax, logger)
{
}
public override void Up()
{
var previewFolderPath = IOHelper.MapPath(SystemDirectories.Umbraco + "/preview");
if (Directory.Exists(previewFolderPath))
{
var newPath = previewFolderPath.Replace("preview", "preview.old");
if (Directory.Exists(newPath) == false)
{
Directory.Move(previewFolderPath, newPath);
var readmeText =
$"Static html files used for preview and canvas editing functionality no longer live in this directory.\r\n" +
$"Instead they have been recreated as MVC views and can now be found in '~/Umbraco/Views/Preview'.\r\n" +
$"See issue: http://issues.umbraco.org/issue/U4-11090";
File.WriteAllText(Path.Combine(newPath, "readme.txt"), readmeText);
}
}
}
public override void Down()
{
}
}
}
@@ -1,5 +1,4 @@
using System;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Models.PublishedContent;
namespace Umbraco.Core.PropertyEditors.ValueConverters
{
@@ -15,7 +14,19 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
{
return source.TryConvertTo<int>().Result;
if (source == null) return 0;
// in XML an integer is a string
var sourceString = source as string;
if (sourceString != null)
{
int i;
return (int.TryParse(sourceString, out i)) ? i : 0;
}
// in the database an integer is an integer
// default value is zero
return (source is int) ? source : 0;
}
}
}
@@ -37,15 +37,15 @@ namespace Umbraco.Core.Security
var username = identity.GetUserName();
var session = identity.FindFirstValue(Constants.Security.SessionIdClaimType);
var securityStamp = identity.FindFirstValue(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType);
var startContentId = identity.FindFirstValue(Constants.Security.StartContentNodeIdClaimType);
var startContentId = identity.FindFirstValue(Constants.Security.StartContentNodeIdClaimType);
var startMediaId = identity.FindFirstValue(Constants.Security.StartMediaNodeIdClaimType);
var culture = identity.FindFirstValue(ClaimTypes.Locality);
var id = identity.FindFirstValue(ClaimTypes.NameIdentifier);
var id = identity.FindFirstValue(ClaimTypes.NameIdentifier);
var realName = identity.FindFirstValue(ClaimTypes.GivenName);
if (username == null || startContentId == null || startMediaId == null
|| culture == null || id == null
if (username == null || startContentId == null || startMediaId == null
|| culture == null || id == null
|| realName == null || session == null)
throw new InvalidOperationException("Cannot create a " + typeof(UmbracoBackOfficeIdentity) + " from " + typeof(ClaimsIdentity) + " since there are missing required claims");
@@ -62,7 +62,7 @@ namespace Umbraco.Core.Security
catch (Exception e)
{
throw new InvalidOperationException("Cannot create a " + typeof(UmbracoBackOfficeIdentity) + " from " + typeof(ClaimsIdentity) + " since the data is not formatted correctly - either content or media start Ids could not be parsed as JSON", e);
}
}
var roles = identity.FindAll(x => x.Type == DefaultRoleClaimType).Select(role => role.Value).ToList();
var allowedApps = identity.FindAll(x => x.Type == Constants.Security.AllowedApplicationsClaimType).Select(app => app.Value).ToList();
@@ -165,7 +165,7 @@ namespace Umbraco.Core.Security
{
foreach (var claim in claimsIdentity.Claims)
{
//In one special case we will replace a claim if it exists already and that is the
//In one special case we will replace a claim if it exists already and that is the
// Forms auth claim for name which automatically gets added
TryRemoveClaim(FindFirst(x => x.Type == claim.Type && x.Issuer == "Forms"));
@@ -187,15 +187,15 @@ namespace Umbraco.Core.Security
{
ClaimTypes.NameIdentifier, //id
ClaimTypes.Name, //username
ClaimTypes.GivenName,
ClaimTypes.GivenName,
Constants.Security.StartContentNodeIdClaimType,
Constants.Security.StartMediaNodeIdClaimType,
ClaimTypes.Locality,
Constants.Security.StartMediaNodeIdClaimType,
ClaimTypes.Locality,
Constants.Security.SessionIdClaimType,
Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType
};
}
}
}
/// <summary>
/// Adds claims based on the UserData data
@@ -222,19 +222,23 @@ namespace Umbraco.Core.Security
AddClaim(new Claim(ClaimTypes.Locality, Culture, ClaimValueTypes.String, Issuer, Issuer, this));
if (HasClaim(x => x.Type == Constants.Security.SessionIdClaimType) == false && SessionId.IsNullOrWhiteSpace() == false)
{
AddClaim(new Claim(Constants.Security.SessionIdClaimType, SessionId, ClaimValueTypes.String, Issuer, Issuer, this));
//The security stamp claim is also required... this is because this claim type is hard coded
// by the SecurityStampValidator, see: https://katanaproject.codeplex.com/workitem/444
if (HasClaim(x => x.Type == Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType) == false)
AddClaim(new Claim(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType, SecurityStamp, ClaimValueTypes.String, Issuer, Issuer, this));
//The security stamp claim is also required... this is because this claim type is hard coded
// by the SecurityStampValidator, see: https://katanaproject.codeplex.com/workitem/444
if (HasClaim(x => x.Type == Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType) == false)
{
AddClaim(new Claim(Microsoft.AspNet.Identity.Constants.DefaultSecurityStampClaimType, SecurityStamp, ClaimValueTypes.String, Issuer, Issuer, this));
}
}
//Add each app as a separate claim
if (HasClaim(x => x.Type == Constants.Security.AllowedApplicationsClaimType) == false)
{
foreach (var application in AllowedApplications)
{
AddClaim(new Claim(Constants.Security.AllowedApplicationsClaimType, application, ClaimValueTypes.String, Issuer, Issuer, this));
AddClaim(new Claim(Constants.Security.AllowedApplicationsClaimType, application, ClaimValueTypes.String, Issuer, Issuer, this));
}
}
@@ -249,8 +253,8 @@ namespace Umbraco.Core.Security
}
}
}
protected internal UserData UserData { get; private set; }
@@ -328,4 +332,4 @@ namespace Umbraco.Core.Security
}
}
}
}
+1 -1
View File
@@ -637,4 +637,4 @@ namespace Umbraco.Core.Services
public static event TypedEventHandler<IDataTypeService, MoveEventArgs<IDataTypeDefinition>> Moved;
#endregion
}
}
}
+45 -218
View File
@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using Umbraco.Core.Models;
@@ -23,139 +22,13 @@ namespace Umbraco.Core.Services
// note - no need for uow, scope would be enough, but a pain to wire
// note - for pure read-only we might want to *not* enforce a transaction?
// notes
//
// - this class assumes that the id/guid map is unique; that is, if an id and a guid map
// to each other, then the id will never map to another guid, and the guid will never map
// to another id
//
// - LeeK's solution in 7.7 was to look for the id/guid in the content cache "on demand" via
// XPath, which is probably fast enough but cannot deal with media ids + it maintains a
// separate, duplicate cache
// see https://github.com/umbraco/Umbraco-CMS/pull/2398
//
// - Andy's solution in a package was to prefetch all by sql; it cannot prefecth reserved ids
// as we don't know the corresponding object type, but that's not a big issue - but then we
// have a full database query on startup
// see https://github.com/AndyButland/UmbracoUdiToIdCache
//
// - the original IdkMap implementation that was used by services, did a database lookup on
// each cache miss, which is fine enough for services, but would be really slow at content
// cache level
//
// - cache is cleared by MediaCacheRefresher, UnpublishedPageCacheRefresher, and other
// refreshers - because id/guid map is unique, we only clear to avoid leaking memory, 'cos
// we don't risk caching obsolete values - and only when actually deleting
//
// so...
//
// - there's a single caching point, and it's idkMap
// - there are no "helper methods" - the published content cache itself knows about Guids
// - when the published content cache is instanciated, it populates the idkMap with what it knows
// and it registers a way for the idkMap to look for id/keys in the published content cache
// - we do NOT prefetch anything from database
// - when a request comes in:
// the published content cache uses the idkMap to map id/key
// if the idkMap already knows about the map, it returns the value
// else it tries the published cache via XPath
// else it hits the database
private readonly ConcurrentDictionary<UmbracoObjectTypes, (Func<int, Guid> id2key, Func<Guid, int> key2id)> _dictionary
= new ConcurrentDictionary<UmbracoObjectTypes, (Func<int, Guid> id2key, Func<Guid, int> key2id)>();
internal void SetMapper(UmbracoObjectTypes umbracoObjectType, Func<int, Guid> id2key, Func<Guid, int> key2id)
{
_dictionary[umbracoObjectType] = (id2key, key2id);
}
internal void Populate(IEnumerable<(int id, Guid key)> pairs, UmbracoObjectTypes umbracoObjectType)
{
try
{
_locker.EnterWriteLock();
foreach (var pair in pairs)
{
_id2Key.Add(pair.id, new TypedId<Guid>(pair.key, umbracoObjectType));
_key2Id.Add(pair.key, new TypedId<int>(pair.id, umbracoObjectType));
}
}
finally
{
if (_locker.IsWriteLockHeld)
_locker.ExitWriteLock();
}
}
#if POPULATE_FROM_DATABASE
private void PopulateLocked()
{
// don't if not empty
if (_key2Id.Count > 0) return;
using (var uow = _uowProvider.GetUnitOfWork())
{
// populate content and media items
var types = new[] { Constants.ObjectTypes.Document, Constants.ObjectTypes.Media };
var values = uow.Database.Query<TypedIdDto>("SELECT id, uniqueId, nodeObjectType FROM umbracoNode WHERE nodeObjectType IN @types", new { types });
foreach (var value in values)
{
var umbracoObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(value.NodeObjectType);
_id2Key.Add(value.Id, new TypedId<Guid>(value.UniqueId, umbracoObjectType));
_key2Id.Add(value.UniqueId, new TypedId<int>(value.Id, umbracoObjectType));
}
}
}
private Attempt<int> PopulateAndGetIdForKey(Guid key, UmbracoObjectTypes umbracoObjectType)
{
try
{
_locker.EnterWriteLock();
PopulateLocked();
return _key2Id.TryGetValue(key, out var id) && id.UmbracoObjectType == umbracoObjectType
? Attempt.Succeed(id.Id)
: Attempt<int>.Fail();
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
}
private Attempt<Guid> PopulateAndGetKeyForId(int id, UmbracoObjectTypes umbracoObjectType)
{
try
{
_locker.EnterWriteLock();
PopulateLocked();
return _id2Key.TryGetValue(id, out var key) && key.UmbracoObjectType == umbracoObjectType
? Attempt.Succeed(key.Id)
: Attempt<Guid>.Fail();
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
}
#endif
public Attempt<int> GetIdForKey(Guid key, UmbracoObjectTypes umbracoObjectType)
{
bool empty;
TypedId<int> id;
try
{
_locker.EnterReadLock();
if (_key2Id.TryGetValue(key, out var id) && id.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(id.Id);
empty = _key2Id.Count == 0;
if (_key2Id.TryGetValue(key, out id) && id.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(id.Id);
}
finally
{
@@ -163,37 +36,20 @@ namespace Umbraco.Core.Services
_locker.ExitReadLock();
}
#if POPULATE_FROM_DATABASE
// if cache is empty and looking for a document or a media,
// populate the cache at once and return what we found
if (empty && (umbracoObjectType == UmbracoObjectTypes.Document || umbracoObjectType == UmbracoObjectTypes.Media))
return PopulateAndGetIdForKey(key, umbracoObjectType);
#endif
// optimize for read speed: reading database outside a lock means that we could read
// multiple times, but we don't lock the cache while accessing the database = better
int? val = null;
if (_dictionary.TryGetValue(umbracoObjectType, out var mappers))
if ((val = mappers.key2id(key)) == default(int)) val = null;
if (val == null)
int? val;
using (var uow = _uowProvider.GetUnitOfWork())
{
using (var uow = _uowProvider.GetUnitOfWork())
//if it's unknown don't include the nodeObjectType in the query
if (umbracoObjectType == UmbracoObjectTypes.Unknown)
{
//if it's unknown don't include the nodeObjectType in the query
if (umbracoObjectType == UmbracoObjectTypes.Unknown)
{
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id", new { id = key});
}
else
{
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)",
new { id = key, type = GetNodeObjectTypeGuid(umbracoObjectType), reservation = Constants.ObjectTypes.IdReservationGuid });
}
uow.Commit();
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id", new { id = key});
}
else
{
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)",
new { id = key, type = GetNodeObjectTypeGuid(umbracoObjectType), reservation = Constants.ObjectTypes.IdReservationGuid });
}
uow.Commit();
}
if (val == null) return Attempt<int>.Fail();
@@ -227,23 +83,13 @@ namespace Umbraco.Core.Services
return GetIdForKey(guidUdi.Guid, umbracoType);
}
public Attempt<Udi> GetUdiForId(int id, UmbracoObjectTypes umbracoObjectType)
{
var keyAttempt = GetKeyForId(id, umbracoObjectType);
return keyAttempt
? Attempt.Succeed<Udi>(new GuidUdi(Constants.UdiEntityType.FromUmbracoObjectType(umbracoObjectType), keyAttempt.Result))
: Attempt<Udi>.Fail();
}
public Attempt<Guid> GetKeyForId(int id, UmbracoObjectTypes umbracoObjectType)
{
bool empty;
TypedId<Guid> key;
try
{
_locker.EnterReadLock();
if (_id2Key.TryGetValue(id, out var key) && key.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(key.Id);
empty = _id2Key.Count == 0;
if (_id2Key.TryGetValue(id, out key) && key.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(key.Id);
}
finally
{
@@ -251,37 +97,20 @@ namespace Umbraco.Core.Services
_locker.ExitReadLock();
}
#if POPULATE_FROM_DATABASE
// if cache is empty and looking for a document or a media,
// populate the cache at once and return what we found
if (empty && (umbracoObjectType == UmbracoObjectTypes.Document || umbracoObjectType == UmbracoObjectTypes.Media))
return PopulateAndGetKeyForId(id, umbracoObjectType);
#endif
// optimize for read speed: reading database outside a lock means that we could read
// multiple times, but we don't lock the cache while accessing the database = better
Guid? val = null;
if (_dictionary.TryGetValue(umbracoObjectType, out var mappers))
if ((val = mappers.id2key(id)) == default(Guid)) val = null;
if (val == null)
Guid? val;
using (var uow = _uowProvider.GetUnitOfWork())
{
using (var uow = _uowProvider.GetUnitOfWork())
//if it's unknown don't include the nodeObjectType in the query
if (umbracoObjectType == UmbracoObjectTypes.Unknown)
{
//if it's unknown don't include the nodeObjectType in the query
if (umbracoObjectType == UmbracoObjectTypes.Unknown)
{
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id", new { id });
}
else
{
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)",
new { id, type = GetNodeObjectTypeGuid(umbracoObjectType), reservation = Constants.ObjectTypes.IdReservationGuid });
}
uow.Commit();
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id", new { id });
}
else
{
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)",
new { id, type = GetNodeObjectTypeGuid(umbracoObjectType), reservation = Constants.ObjectTypes.IdReservationGuid });
}
uow.Commit();
}
if (val == null) return Attempt<Guid>.Fail();
@@ -313,8 +142,6 @@ namespace Umbraco.Core.Services
return guid;
}
// invoked on UnpublishedPageCacheRefresher.RefreshAll
// anything else will use the id-specific overloads
public void ClearCache()
{
try
@@ -335,7 +162,8 @@ namespace Umbraco.Core.Services
try
{
_locker.EnterWriteLock();
if (_id2Key.TryGetValue(id, out var key) == false) return;
TypedId<Guid> key;
if (_id2Key.TryGetValue(id, out key) == false) return;
_id2Key.Remove(id);
_key2Id.Remove(key.Id);
}
@@ -351,7 +179,8 @@ namespace Umbraco.Core.Services
try
{
_locker.EnterWriteLock();
if (_key2Id.TryGetValue(key, out var id) == false) return;
TypedId<int> id;
if (_key2Id.TryGetValue(key, out id) == false) return;
_id2Key.Remove(id.Id);
_key2Id.Remove(key);
}
@@ -362,28 +191,26 @@ namespace Umbraco.Core.Services
}
}
// ReSharper disable ClassNeverInstantiated.Local
// ReSharper disable UnusedAutoPropertyAccessor.Local
private class TypedIdDto
{
public int Id { get; set; }
public Guid UniqueId { get; set; }
public Guid NodeObjectType { get; set; }
}
// ReSharper restore ClassNeverInstantiated.Local
// ReSharper restore UnusedAutoPropertyAccessor.Local
private struct TypedId<T>
{
public TypedId(T id, UmbracoObjectTypes umbracoObjectType)
private readonly T _id;
private readonly UmbracoObjectTypes _umbracoObjectType;
public T Id
{
UmbracoObjectType = umbracoObjectType;
Id = id;
get { return _id; }
}
public UmbracoObjectTypes UmbracoObjectType { get; }
public UmbracoObjectTypes UmbracoObjectType
{
get { return _umbracoObjectType; }
}
public T Id { get; }
public TypedId(T id, UmbracoObjectTypes umbracoObjectType)
{
_umbracoObjectType = umbracoObjectType;
_id = id;
}
}
}
}
}
+24 -18
View File
@@ -207,17 +207,20 @@ namespace Umbraco.Core.Services
{
return repository.GetByUsername(username, includeSecurityData: true);
}
catch (DbException ex)
catch (Exception ex)
{
//we need to handle this one specific case which is when we are upgrading to 7.7 since the user group
//tables don't exist yet. This is the 'easiest' way to deal with this without having to create special
//version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need
//like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of
//that method would not be cached.
if (ApplicationContext.Current.IsUpgrading)
if (ex is SqlException || ex is SqlCeException)
{
//NOTE: this will not be cached
return repository.GetByUsername(username, includeSecurityData: false);
//we need to handle this one specific case which is when we are upgrading to 7.7 since the user group
//tables don't exist yet. This is the 'easiest' way to deal with this without having to create special
//version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need
//like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of
//that method would not be cached.
if (ApplicationContext.Current.IsUpgrading)
{
//NOTE: this will not be cached
return repository.GetByUsername(username, includeSecurityData: false);
}
}
throw;
}
@@ -786,17 +789,20 @@ namespace Umbraco.Core.Services
var result = repository.Get(id);
return result;
}
catch (DbException ex)
catch (Exception ex)
{
//we need to handle this one specific case which is when we are upgrading to 7.7 since the user group
//tables don't exist yet. This is the 'easiest' way to deal with this without having to create special
//version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need
//like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of
//that method would not be cached.
if (ApplicationContext.Current.IsUpgrading)
if (ex is SqlException || ex is SqlCeException)
{
//NOTE: this will not be cached
return repository.Get(id, includeSecurityData: false);
//we need to handle this one specific case which is when we are upgrading to 7.7 since the user group
//tables don't exist yet. This is the 'easiest' way to deal with this without having to create special
//version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need
//like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of
//that method would not be cached.
if (ApplicationContext.Current.IsUpgrading)
{
//NOTE: this will not be cached
return repository.Get(id, includeSecurityData: false);
}
}
throw;
}
-4
View File
@@ -115,9 +115,6 @@
<Reference Include="System.Runtime.Caching" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Transactions" />
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Web" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.Web.Extensions" />
@@ -606,7 +603,6 @@
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\NormalizeTemplateGuids.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\RemovePropertyDataIdIndex.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\RemoveUmbracoDeployTables.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenTenZero\RenamePreviewFolder.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeTwo\EnsureMigrationsTableIdentityIsCorrect.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\EnsureContentTypeUniqueIdsAreConsistent.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\AddExternalLoginsTable.cs" />
@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp50</s:String></wpf:ResourceDictionary>
+5 -14
View File
@@ -3,7 +3,6 @@ using System.IO;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
namespace Umbraco.Core
{
@@ -143,18 +142,10 @@ namespace Umbraco.Core
/// <returns></returns>
internal static bool IsClientSideRequest(this Uri url)
{
try
{
var ext = Path.GetExtension(url.LocalPath);
if (ext.IsNullOrWhiteSpace()) return false;
var toInclude = new[] {".aspx", ".ashx", ".asmx", ".axd", ".svc"};
return toInclude.Any(ext.InvariantEquals) == false;
}
catch (ArgumentException ex)
{
LogHelper.Error(typeof(UriExtensions), "Failed to determine if request was client side", ex);
return false;
}
var ext = Path.GetExtension(url.LocalPath);
if (ext.IsNullOrWhiteSpace()) return false;
var toInclude = new[] { ".aspx", ".ashx", ".asmx", ".axd", ".svc" };
return toInclude.Any(ext.InvariantEquals) == false;
}
/// <summary>
@@ -320,4 +311,4 @@ namespace Umbraco.Core
return new Uri(uri.GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped));
}
}
}
}
-1
View File
@@ -18,5 +18,4 @@
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="semver" version="1.1.2" targetFramework="net45" />
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net45" />
<package id="System.ValueTuple" version="4.4.0" targetFramework="net45" />
</packages>
@@ -139,8 +139,8 @@
<Reference Include="System.Threading.Thread, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Thread.4.3.0\lib\net46\System.Threading.Thread.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
<Reference Include="System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
-4
View File
@@ -62,10 +62,6 @@
<assemblyIdentity name="System.Xml.XPath.XDocument" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
+1 -1
View File
@@ -51,7 +51,7 @@
<package id="System.Threading.Tasks.Extensions" version="4.3.0" targetFramework="net46" />
<package id="System.Threading.Tasks.Parallel" version="4.3.0" targetFramework="net46" />
<package id="System.Threading.Thread" version="4.3.0" targetFramework="net46" />
<package id="System.ValueTuple" version="4.4.0" targetFramework="net46" />
<package id="System.ValueTuple" version="4.3.0" targetFramework="net46" />
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net46" />
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net46" />
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="net46" />
+40 -46
View File
@@ -93,59 +93,53 @@ namespace Umbraco.Tests.Routing
var result = uri.IsClientSideRequest();
Assert.AreEqual(assert, result);
}
[Test]
public void Is_Client_Side_Request_InvalidPath_ReturnFalse()
{
//This url is invalid. Default to false when the extension cannot be determined
var uri = new Uri("http://test.com/installing-modules+foobar+\"yipee\"");
var result = uri.IsClientSideRequest();
Assert.AreEqual(false, result);
}
//NOTE: This test shows how we can test most of the HttpModule, it however is testing a method that no longer exists and is testing too much,
// we need to write unit tests for each of the components: NiceUrlProvider, all of the Lookup classes, etc...
// to ensure that each one is individually tested.
//[TestCase("/", 1046)]
//[TestCase("/home.aspx", 1046)]
//[TestCase("/home/sub1.aspx", 1173)]
//[TestCase("/home.aspx?altTemplate=blah", 1046)]
//public void Process_Front_End_Document_Request_Match_Node(string url, int nodeId)
//{
// var httpContextFactory = new FakeHttpContextFactory(url);
// var httpContext = httpContextFactory.HttpContext;
// var umbracoContext = new UmbracoContext(httpContext, ApplicationContext.Current, new NullRoutesCache());
// var contentStore = new ContentStore(umbracoContext);
// var niceUrls = new NiceUrlProvider(contentStore, umbracoContext);
// umbracoContext.RoutingContext = new RoutingContext(
// new IPublishedContentLookup[] {new LookupByNiceUrl()},
// new DefaultLastChanceLookup(),
// contentStore,
// niceUrls);
// StateHelper.HttpContext = httpContext;
// //because of so much dependency on the db, we need to create som stuff here, i originally abstracted out stuff but
// //was turning out to be quite a deep hole because ultimately we'd have to abstract the old 'Domain' and 'Language' classes
// Domain.MakeNew("Test.com", 1000, Language.GetByCultureCode("en-US").id);
//NOTE: This test shows how we can test most of the HttpModule, it however is testing a method that no longer exists and is testing too much,
// we need to write unit tests for each of the components: NiceUrlProvider, all of the Lookup classes, etc...
// to ensure that each one is individually tested.
// //need to create a template with id 1045
// var template = Template.MakeNew("test", new User(0));
//[TestCase("/", 1046)]
//[TestCase("/home.aspx", 1046)]
//[TestCase("/home/sub1.aspx", 1173)]
//[TestCase("/home.aspx?altTemplate=blah", 1046)]
//public void Process_Front_End_Document_Request_Match_Node(string url, int nodeId)
//{
// var httpContextFactory = new FakeHttpContextFactory(url);
// var httpContext = httpContextFactory.HttpContext;
// var umbracoContext = new UmbracoContext(httpContext, ApplicationContext.Current, new NullRoutesCache());
// var contentStore = new ContentStore(umbracoContext);
// var niceUrls = new NiceUrlProvider(contentStore, umbracoContext);
// umbracoContext.RoutingContext = new RoutingContext(
// new IPublishedContentLookup[] {new LookupByNiceUrl()},
// new DefaultLastChanceLookup(),
// contentStore,
// niceUrls);
// SetupUmbracoContextForTest(umbracoContext, template);
// StateHelper.HttpContext = httpContext;
// _module.AssignDocumentRequest(httpContext, umbracoContext, httpContext.Request.Url);
// //because of so much dependency on the db, we need to create som stuff here, i originally abstracted out stuff but
// //was turning out to be quite a deep hole because ultimately we'd have to abstract the old 'Domain' and 'Language' classes
// Domain.MakeNew("Test.com", 1000, Language.GetByCultureCode("en-US").id);
// Assert.IsNotNull(umbracoContext.PublishedContentRequest);
// Assert.IsNotNull(umbracoContext.PublishedContentRequest.XmlNode);
// Assert.IsFalse(umbracoContext.PublishedContentRequest.IsRedirect);
// Assert.IsFalse(umbracoContext.PublishedContentRequest.Is404);
// Assert.AreEqual(umbracoContext.PublishedContentRequest.Culture, Thread.CurrentThread.CurrentCulture);
// Assert.AreEqual(umbracoContext.PublishedContentRequest.Culture, Thread.CurrentThread.CurrentUICulture);
// Assert.AreEqual(nodeId, umbracoContext.PublishedContentRequest.NodeId);
// //need to create a template with id 1045
// var template = Template.MakeNew("test", new User(0));
//}
// SetupUmbracoContextForTest(umbracoContext, template);
}
}
// _module.AssignDocumentRequest(httpContext, umbracoContext, httpContext.Request.Url);
// Assert.IsNotNull(umbracoContext.PublishedContentRequest);
// Assert.IsNotNull(umbracoContext.PublishedContentRequest.XmlNode);
// Assert.IsFalse(umbracoContext.PublishedContentRequest.IsRedirect);
// Assert.IsFalse(umbracoContext.PublishedContentRequest.Is404);
// Assert.AreEqual(umbracoContext.PublishedContentRequest.Culture, Thread.CurrentThread.CurrentCulture);
// Assert.AreEqual(umbracoContext.PublishedContentRequest.Culture, Thread.CurrentThread.CurrentUICulture);
// Assert.AreEqual(nodeId, umbracoContext.PublishedContentRequest.NodeId);
//}
}
}
+3 -1
View File
@@ -1,11 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Web.UI;
using umbraco;
using umbraco.BusinessLogic;
using umbraco.interfaces;
using Umbraco.Web.umbraco.presentation.umbraco.create;
namespace Umbraco.Tests.UI
{
+1 -6
View File
@@ -42,12 +42,7 @@
"codemirror"
],
"sources": {
"moment": [
"bower_components/moment/min/moment.min.js",
"bower_components/moment/min/moment-with-locales.js",
"bower_components/moment/min/moment-with-locales.min.js",
"bower_components/moment/locale/*.js"
],
"moment": "bower_components/moment/min/moment-with-locales.js",
"underscore": [
"bower_components/underscore/underscore-min.js",
"bower_components/underscore/underscore-min.map"
+605
View File
@@ -0,0 +1,605 @@
module.exports = function (grunt) {
// Default task.
grunt.registerTask('default', ['jshint:dev', 'build', 'karma:unit']);
grunt.registerTask('dev', ['jshint:dev', 'build-dev', 'webserver', 'open:dev', 'watch']);
grunt.registerTask('docserve', ['docs:api', 'connect:docserver', 'open:docs', 'watch:docs']);
grunt.registerTask('vs', ['jshint:dev', 'build-dev', 'watch']);
//TODO: Too much watching, this brings windows to it's knees when in dev mode
//run by the watch task
grunt.registerTask('watch-js', ['jshint:dev', 'concat', 'copy:app', 'copy:mocks', 'copy:canvasdesigner', 'copy:vs', 'karma:unit']);
grunt.registerTask('watch-less', ['recess:build', 'recess:installer', 'recess:nonodes', 'recess:canvasdesigner', 'postcss', 'copy:canvasdesigner', 'copy:assets', 'copy:vs']);
grunt.registerTask('watch-html', ['copy:views', 'copy:vs']);
grunt.registerTask('watch-installer', ['concat:install', 'concat:installJs', 'copy:installer', 'copy:vs']);
grunt.registerTask('watch-canvasdesigner', ['copy:canvasdesigner', 'concat:canvasdesignerJs', 'copy:vs']);
grunt.registerTask('watch-test', ['jshint:dev', 'karma:unit']);
//triggered from grunt
grunt.registerTask('build', ['concat', 'recess:build', 'recess:installer', 'recess:nonodes', 'recess:canvasdesigner', 'postcss', 'bower-install-simple', 'bower', 'copy', 'clean:post']);
//triggered from grunt dev vs or grunt vs
grunt.registerTask('build-dev', ['clean:pre', 'concat', 'recess:build', 'recess:installer', 'recess:nonodes', 'postcss', 'bower-install-simple', 'bower', 'copy']);
//utillity tasks
grunt.registerTask('docs', ['ngdocs']);
grunt.registerTask('webserver', ['connect:devserver']);
// Print a timestamp (useful for when watching)
grunt.registerTask('timestamp', function () {
grunt.log.subhead(Date());
});
// Project configuration.
grunt.initConfig({
buildVersion: grunt.option('buildversion') || '7',
connect: {
devserver: {
options: {
port: 9990,
hostname: '0.0.0.0',
base: './build',
middleware: function(connect, options) {
return [
//uncomment to enable CSP
// util.csp(),
//util.rewrite(),
connect.favicon('images/favicon.ico'),
connect.static(options.base),
connect.directory(options.base)
];
}
}
},
testserver: {},
docserver: {
options: {
port: 8880,
hostname: '0.0.0.0',
base: './docs/api',
middleware: function(connect, options) {
return [
//uncomment to enable CSP
// util.csp(),
//util.rewrite(),
connect.static(options.base),
connect.directory(options.base)
];
}
}
},
},
open: {
dev: {
path: 'http://localhost:9990/belle/'
},
docs: {
path: 'http://localhost:8880/index.html'
}
},
distdir: 'build/belle',
vsdir: '../Umbraco.Web.UI/umbraco',
pkg: grunt.file.readJSON('package.json'),
banner:
'/*! <%= pkg.title || pkg.name %>\n' +
'<%= pkg.homepage ? " * " + pkg.homepage + "\\n" : "" %>' +
' * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>;\n' +
' * Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %>\n */\n',
src: {
js: ['src/**/*.js', 'src/*.js'],
common: ['src/common/**/*.js'],
controllers: ['src/**/*.controller.js'],
specs: ['test/**/*.spec.js'],
scenarios: ['test/**/*.scenario.js'],
samples: ['sample files/*.js'],
html: ['src/index.html', 'src/install.html'],
everything: ['src/**/*.*', 'test/**/*.*', 'docs/**/*.*'],
tpl: {
app: ['src/views/**/*.html'],
common: ['src/common/**/*.tpl.html']
},
less: ['src/less/belle.less'], // recess:build doesn't accept ** in its file patterns
prod: ['<%= distdir %>/js/*.js']
},
clean: {
pre: ['<%= distdir %>/*'],
post: ['<%= distdir %>/js/*.dev.js']
},
copy: {
assets: {
files: [{ dest: '<%= distdir %>/assets', src: '**', expand: true, cwd: 'src/assets/' }]
},
config: {
files: [{ dest: '<%= distdir %>/../config', src: '**', expand: true, cwd: 'src/config/' }]
},
installer: {
files: [{ dest: '<%= distdir %>/views/install', src: '**/*.html', expand: true, cwd: 'src/installer/steps' }]
},
canvasdesigner: {
files: [
{ dest: '<%= distdir %>/preview', src: '**/*.html', expand: true, cwd: 'src/canvasdesigner' },
{ dest: '<%= distdir %>/preview/editors', src: '**/*.html', expand: true, cwd: 'src/canvasdesigner/editors' },
{ dest: '<%= distdir %>/assets/less', src: '**/*.less', expand: true, cwd: 'src/canvasdesigner/editors' },
{ dest: '<%= distdir %>/js', src: 'canvasdesigner.config.js', expand: true, cwd: 'src/canvasdesigner/config' },
{ dest: '<%= distdir %>/js', src: 'canvasdesigner.palettes.js', expand: true, cwd: 'src/canvasdesigner/config' },
{ dest: '<%= distdir %>/js', src: 'canvasdesigner.front.js', expand: true, cwd: 'src/canvasdesigner' }
]
},
vendor: {
files: [{ dest: '<%= distdir %>/lib', src: '**', expand: true, cwd: 'lib/' }]
},
views: {
files: [{ dest: '<%= distdir %>/views', src: ['**/*.*', '!**/*.controller.js'], expand: true, cwd: 'src/views' }]
},
app: {
files: [
{ dest: '<%= distdir %>/js', src: '*.js', expand: true, cwd: 'src/' }
]
},
mocks: {
files: [{ dest: '<%= distdir %>/js', src: '*.js', expand: true, cwd: 'src/common/mocks/' }]
},
vs: {
files: [
//everything except the index.html root file!
//then we need to figure out how to not copy all the test stuff either!?
{ dest: '<%= vsdir %>/assets', src: '**', expand: true, cwd: '<%= distdir %>/assets' },
{ dest: '<%= vsdir %>/js', src: '**', expand: true, cwd: '<%= distdir %>/js' },
{ dest: '<%= vsdir %>/views', src: '**', expand: true, cwd: '<%= distdir %>/views' },
{ dest: '<%= vsdir %>/preview', src: '**', expand: true, cwd: '<%= distdir %>/preview' },
{ dest: '<%= vsdir %>/lib', src: '**', expand: true, cwd: '<%= distdir %>/lib' }
]
}
},
karma: {
unit: { configFile: 'test/config/karma.conf.js', keepalive: true },
e2e: { configFile: 'test/config/e2e.js', keepalive: true },
watch: { configFile: 'test/config/unit.js', singleRun: false, autoWatch: true, keepalive: true }
},
concat: {
index: {
src: ['src/index.html'],
dest: '<%= distdir %>/index.html',
options: {
process: true
}
},
install: {
src: ['src/installer/installer.html'],
dest: '<%= distdir %>/installer.html',
options: {
process: true
}
},
installJs: {
src: ['src/installer/**/*.js'],
dest: '<%= distdir %>/js/umbraco.installer.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
canvasdesignerJs: {
src: ['src/canvasdesigner/canvasdesigner.global.js', 'src/canvasdesigner/canvasdesigner.controller.js', 'src/canvasdesigner/editors/*.js', 'src/canvasdesigner/lib/*.js'],
dest: '<%= distdir %>/js/canvasdesigner.panel.js'
},
controllers: {
src: ['src/controllers/**/*.controller.js', 'src/views/**/*.controller.js'],
dest: '<%= distdir %>/js/umbraco.controllers.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
services: {
src: ['src/common/services/*.js'],
dest: '<%= distdir %>/js/umbraco.services.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
security: {
src: ['src/common/security/*.js'],
dest: '<%= distdir %>/js/umbraco.security.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
resources: {
src: ['src/common/resources/*.js'],
dest: '<%= distdir %>/js/umbraco.resources.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
testing: {
src: ['src/common/mocks/*/*.js'],
dest: '<%= distdir %>/js/umbraco.testing.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
directives: {
src: ['src/common/directives/**/*.js'],
dest: '<%= distdir %>/js/umbraco.directives.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
},
filters: {
src: ['src/common/filters/*.js'],
dest: '<%= distdir %>/js/umbraco.filters.js',
options: {
banner: "<%= banner %>\n(function() { \n\n",
footer: "\n\n})();"
}
}
},
uglify: {
options: {
mangle: true
},
combine: {
files: {
'<%= distdir %>/js/umbraco.min.js': ['<%= distdir %>/js/umbraco.*.js']
}
}
},
recess: {
build: {
files: {
'<%= distdir %>/assets/css/<%= pkg.name %>.css':
['<%= src.less %>']
},
options: {
compile: true,
compress: true
}
},
nonodes: {
files: {
'<%= distdir %>/assets/css/nonodes.style.min.css':
['src/less/pages/nonodes.less']
},
options: {
compile: true,
compress: true
}
},
installer: {
files: {
'<%= distdir %>/assets/css/installer.css':
['src/less/installer.less']
},
options: {
compile: true,
compress: true
}
},
canvasdesigner: {
files: {
'<%= distdir %>/assets/css/canvasdesigner.css':
['src/less/canvas-designer.less', 'src/less/helveticons.less']
},
options: {
compile: true,
compress: true
}
}
},
postcss: {
options: {
processors: [
// add vendor prefixes
require('autoprefixer-core')({
browsers: 'last 2 versions'
})
]
},
dist: {
src: '<%= distdir %>/assets/css/<%= pkg.name %>.css'
}
},
ngTemplateCache: {
views: {
files: {
'<%= distdir %>/js/umbraco.views.js': 'src/views/**/*.html'
},
options: {
trim: 'src/',
module: 'umbraco.views'
}
}
},
watch: {
docs: {
files: ['docs/src/**/*.md'],
tasks: ['watch-docs', 'timestamp']
},
css: {
files: 'src/**/*.less',
tasks: ['watch-less', 'timestamp'],
options: {
livereload: true,
},
},
js: {
files: ['src/**/*.js', 'src/*.js'],
tasks: ['watch-js', 'timestamp'],
},
test: {
files: ['test/**/*.js'],
tasks: ['watch-test', 'timestamp'],
},
installer: {
files: ['src/installer/**/*.*'],
tasks: ['watch-installer', 'timestamp'],
},
canvasdesigner: {
files: ['src/canvasdesigner/**/*.*'],
tasks: ['watch-canvasdesigner', 'timestamp'],
},
html: {
files: ['src/views/**/*.html', 'src/*.html'],
tasks: ['watch-html', 'timestamp']
},
options: {
interval: 500
}
},
ngdocs: {
options: {
dest: 'docs/api',
startPage: '/api',
title: "Umbraco Backoffice UI API Documentation",
html5Mode: false,
styles: [
'docs/umb-docs.css'
],
image: "https://our.umbraco.org/assets/images/logo.svg"
},
api: {
src: ['src/common/**/*.js', 'docs/src/api/**/*.ngdoc'],
title: 'API Documentation'
},
tutorials: {
src: [],
title: ''
}
},
eslint:{
src: ['<%= src.common %>','<%= src.controllers %>'],
options: {quiet: true}
},
jshint: {
dev: {
files: {
src: ['<%= src.common %>']
},
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: "nofunc",
newcap: true,
noarg: true,
sub: true,
boss: true,
//NOTE: This is required so it doesn't barf on reserved words like delete when doing $http.delete
es5: true,
eqnull: true,
//NOTE: we need to use eval sometimes so ignore it
evil: true,
//NOTE: we need to check for strings such as "javascript:" so don't throw errors regarding those
scripturl: true,
//NOTE: we ignore tabs vs spaces because enforcing that causes lots of errors depending on the text editor being used
smarttabs: true,
globals: {}
}
},
build: {
files: {
src: ['<%= src.prod %>']
},
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: "nofunc",
newcap: true,
noarg: true,
sub: true,
boss: true,
//NOTE: This is required so it doesn't barf on reserved words like delete when doing $http.delete
es5: true,
eqnull: true,
//NOTE: we need to use eval sometimes so ignore it
evil: true,
//NOTE: we need to check for strings such as "javascript:" so don't throw errors regarding those
scripturl: true,
//NOTE: we ignore tabs vs spaces because enforcing that causes lots of errors depending on the text editor being used
smarttabs: true,
globalstrict: true,
globals: { $: false, jQuery: false, define: false, require: false, window: false }
}
}
},
bower: {
dev: {
dest: '<%= distdir %>/lib',
options: {
expand: true,
ignorePackages: ['bootstrap'],
packageSpecific: {
'moment': {
keepExpandedHierarchy: false,
files: ['min/moment-with-locales.js']
},
'typeahead.js': {
keepExpandedHierarchy: false,
files: ['dist/typeahead.bundle.min.js']
},
'underscore': {
files: ['underscore-min.js', 'underscore-min.map']
},
'rgrove-lazyload': {
files: ['lazyload.js']
},
'bootstrap-social': {
files: ['bootstrap-social.css']
},
'font-awesome': {
files: ['css/font-awesome.min.css', 'fonts/*']
},
"jquery": {
keepExpandedHierarchy: false,
files: ['dist/jquery.min.js', 'dist/jquery.min.map']
},
'jquery-ui': {
keepExpandedHierarchy: false,
files: ['jquery-ui.min.js']
},
'jquery-migrate': {
keepExpandedHierarchy: false,
files: ['jquery-migrate.min.js']
},
'tinymce': {
files: ['plugins/**', 'themes/**', 'tinymce.min.js']
},
'angular-dynamic-locale': {
files: ['tmhDynamicLocale.min.js', 'tmhDynamicLocale.min.js.map']
},
'ng-file-upload': {
keepExpandedHierarchy: false,
files: ['ng-file-upload.min.js']
},
'angular-local-storage': {
keepExpandedHierarchy: false,
files: ['dist/angular-local-storage.min.js']
},
'codemirror': {
files: [
'lib/codemirror.js',
'lib/codemirror.css',
'mode/css/*',
'mode/javascript/*',
'mode/xml/*',
'mode/htmlmixed/*',
'addon/search/*',
'addon/edit/*',
'addon/selection/*',
'addon/dialog/*'
]
},
'ace-builds': {
files: [
'src-min-noconflict/ace.js',
'src-min-noconflict/ext-language_tools.js',
'src-min-noconflict/ext-searchbox.js',
'src-min-noconflict/ext-settings_menu.js',
'src-min-noconflict/snippets/text.js',
'src-min-noconflict/snippets/javascript.js',
'src-min-noconflict/theme-chrome.js',
'src-min-noconflict/mode-razor.js',
'src-min-noconflict/mode-javascript.js',
'src-min-noconflict/worker-javascript.js',
]
},
'clipboard': {
keepExpandedHierarchy: false,
files: ['dist/clipboard.min.js']
},
'angular-moment': {
keepExpandedHierarchy: false,
files: ['angular-moment.min.js']
}
}
}
},
options: {
expand: true
}
},
"bower-install-simple": {
options: {
color: true
},
"dev": {}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-recess');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-open');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks("grunt-bower-install-simple");
grunt.loadNpmTasks('grunt-bower');
grunt.loadNpmTasks('grunt-ngdocs');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-hustler');
};
+3 -1
View File
@@ -73,6 +73,7 @@ var sources = {
js: {
preview: { files: ["src/canvasdesigner/**/*.js"], out: "umbraco.canvasdesigner.js" },
installer: { files: ["src/installer/**/*.js"], out: "umbraco.installer.js" },
controllers: { files: ["src/{views,controllers}/**/*.controller.js"], out: "umbraco.controllers.js" },
directives: { files: ["src/common/directives/**/*.js"], out: "umbraco.directives.js" },
filters: { files: ["src/common/filters/**/*.js"], out: "umbraco.filters.js" },
@@ -84,7 +85,8 @@ var sources = {
//selectors for copying all views into the build
//processed in the views task
views:{
umbraco: {files: ["src/views/**/*.html"], folder: ""},
umbraco: {files: ["src/views/**/*html"], folder: ""},
preview: { files: ["src/canvasdesigner/**/*.html"], folder: "../preview"},
installer: {files: ["src/installer/steps/*.html"], folder: "install"}
},
+6 -6
View File
@@ -27,9 +27,9 @@
"gulp": "^3.9.1",
"gulp-concat": "^2.6.0",
"gulp-connect": "5.0.0",
"gulp-less": "^3.5.0",
"gulp-less": "^3.1.0",
"gulp-ngdocs": "^0.3.0",
"gulp-open": "^2.1.0",
"gulp-open": "^2.0.0",
"gulp-postcss": "^6.2.0",
"gulp-rename": "^1.2.2",
"gulp-sort": "^2.0.0",
@@ -38,11 +38,11 @@
"gulp-wrap-js": "^0.4.1",
"jasmine-core": "2.5.2",
"karma": "^1.7.0",
"karma-jasmine": "^1.1.1",
"karma-jasmine": "^1.1.0",
"karma-phantomjs-launcher": "^1.0.4",
"less": "^2.7.3",
"lodash": "^4.17.5",
"less": "^2.6.1",
"lodash": "^4.16.3",
"merge-stream": "^1.0.1",
"run-sequence": "^2.2.1"
"run-sequence": "^2.1.0"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 386 KiB

@@ -1,4 +1,4 @@
@inherits System.Web.Mvc.WebViewPage

<div ng-controller="Umbraco.canvasdesigner.background">
<div class="box-slider">
@@ -1,4 +1,4 @@
@inherits System.Web.Mvc.WebViewPage

<div ng-controller="Umbraco.canvasdesigner.border" class="bordereditor">
<div class="box-slider">
@@ -17,4 +17,4 @@
<div ui-slider min="0" max="40" step="1" ng-model="selectedBorder.size"></div>
</div>
</div>
</div>
@@ -0,0 +1,3 @@
<div class="box-slider">
<div colorpicker ng-model="item.values.color"></div>
</div>
@@ -1,4 +1,4 @@
@inherits System.Web.Mvc.WebViewPage

<div ng-controller="Umbraco.canvasdesigner.googlefontpicker">
<div class="box-slider">
@@ -1,8 +1,8 @@
@inherits System.Web.Mvc.WebViewPage

<div ng-controller="Umbraco.canvasdesigner.gridRow">
<div class="box-slider">
<input type="checkbox" ng-model="item.values.fullsize" /><label>Full size</label>
</div>
</div>
</div>
@@ -1,4 +1,4 @@
@inherits System.Web.Mvc.WebViewPage

<div ng-controller="Umbraco.canvasdesigner.layout">
<div class="box-slider">
@@ -7,4 +7,4 @@
<input type="radio" ng-model="item.values.layout" value="full"> Full
</div>
</div>
</div>
@@ -1,7 +1,7 @@
@inherits System.Web.Mvc.WebViewPage

<div ng-controller="Umbraco.canvasdesigner.margin">
<div class="box-slider">
<div class="box-slider">
<ul class="box-preview">
<li ng-repeat="margin in marginList" class="border-{{margin}}" ng-class="{selected: selectedmargin.name == margin}" ng-click="setSelectedmargin(margin)"></li>
</ul>
@@ -11,4 +11,4 @@
<div ui-slider min="0" max="400" step="1" ng-model="selectedmargin.value"></div>
</div>
</div>
</div>
@@ -1,4 +1,4 @@
@inherits System.Web.Mvc.WebViewPage

<div ng-controller="Umbraco.canvasdesigner.padding">
<div class="box-slider">
@@ -11,4 +11,4 @@
<div ui-slider min="0" max="400" step="1" ng-model="selectedpadding.value"></div>
</div>
</div>
</div>
@@ -1,9 +1,9 @@
@inherits System.Web.Mvc.WebViewPage

<div ng-controller="Umbraco.canvasdesigner.radius">
<div class="box-slider">
<ul class="box-preview">
<li ng-repeat="radius in radiusList" ng-class="{selected: selectedradius.name == radius}" ng-click="setSelectedradius(radius)">
<span ng-show="radius == 'topleft' || radius == 'all'" class="radius-top-left"></span>
<span ng-show="radius == 'topright' || radius == 'all'" class="radius-top-right"></span>
@@ -18,4 +18,4 @@
<div ui-slider min="0" max="40" step="1" ng-model="selectedradius.value"></div>
</div>
</div>
</div>
@@ -1,8 +1,8 @@
@inherits System.Web.Mvc.WebViewPage

<div ng-controller="Umbraco.canvasdesigner.shadow">
<div class="box-slider">
<div ui-slider min="0" max="100" step="1" ng-model="item.values.shadow"></div>
</div>
</div>
</div>
@@ -1,8 +1,8 @@
@inherits System.Web.Mvc.WebViewPage

<div ng-controller="Umbraco.canvasdesigner.slider">
<div class="box-slider">
<div ui-slider min="{{item.min}}" max="{{item.max}}" step="1" ng-model="item.values.slider"></div>
</div>
</div>
</div>
@@ -1,9 +1,4 @@
@using System.Web.Mvc.Html
@inherits System.Web.Mvc.WebViewPage<Umbraco.Web.Models.ContentEditing.BackOfficePreview>
@{
var disableDevicePreview = Model.DisableDevicePreview.ToString().ToLowerInvariant();
}
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Umbraco Canvas Designer</title>
@@ -11,25 +6,27 @@
<link href="../lib/spectrum/spectrum.css" type="text/css" rel="stylesheet" />
<link href="../lib/jquery-ui/jquery-ui-1.10.4.custom.min.css" type="text/css" rel="stylesheet" />
</head>
<body id="canvasdesignerPanel" ng-mouseover="outlinePositionHide()" ng-class="{ leftOpen: (showStyleEditor || showPalettePicker) && !showDevicesPreview }" ng-controller="Umbraco.canvasdesignerController">
<div class="wait" ng-show="!frameLoaded"></div>
@if (string.IsNullOrWhiteSpace(Model.PreviewExtendedHeaderView) == false)
{
@Html.Partial(Model.PreviewExtendedHeaderView)
}
<body id="canvasdesignerPanel" ng-mouseover="outlinePositionHide()" ng-class="{ leftOpen: (showStyleEditor || showPalettePicker) && !showDevicesPreview }" ng-controller="Umbraco.canvasdesignerController">
<div class="wait" ng-show="!frameLoaded"></div>
<div id="demo-iframe-wrapper" ng-show="frameLoaded" class="{{previewDevice.css}}">
<iframe id="resultFrame" ng-src="{{pageUrl}}" frameborder="0" iframe-is-loaded></iframe>
</div>
<div class="canvasdesigner" ng-init="showDevicesPreview = true; showDevices = !@(disableDevicePreview); showPalettePicker = true" ng-mouseenter="positionSelectedHide()">
<div class="canvasdesigner" ng-init="showDevicesPreview = true; showPalettePicker = true" ng-mouseenter="positionSelectedHide()">
<div class="fix-left-menu selected">
<div class="avatar">
<img ng-src="../assets/img/application/logo.png"
ng-srcset="../assets/img/application/logo@2x.png 2x,
<img
ng-src="../assets/img/application/logo.png"
ng-srcset="../assets/img/application/logo@2x.png 2x,
../assets/img/application/logo@3x.png 3x" />
</div>
<ul class="sections" ng-class="{selected: showDevicesPreview && showDevices}">
<ul class="sections" ng-class="{selected: showDevicesPreview}">
<li ng-repeat="device in devices" ng-class="{ current:previewDevice==device }" ng-click="updatePreviewDevice(device)">
<a href="#"><i class="icon {{device.icon}}" title="{{device.title}}"></i><span></span></a>
</li>
@@ -42,8 +39,9 @@
</li>
<li ng-click="exitPreview()">
<a href="#" title="Exit Preview"><i class="icon icon-wrong"></i><span> </span></a>
</li>
</li>
</ul>
<ul class="sections" ng-class="{selected: !showDevicesPreview}" ng-if="enableCanvasdesigner > 0">
<li ng-click="openPreviewDevice()">
<a href="#"><i class="icon {{previewDevice.icon}}"></i><span>Preview</span></a>
@@ -55,11 +53,15 @@
<a href="#"><i class="icon icon-paint-roller"></i><span>UI Designer</span></a>
</li>
</ul>
</div>
<div class="main-panel" ng-class="{selected: !showDevicesPreview && ( showPalettePicker || showStyleEditor )}">
<div class="header">
<h3>Palette Style</h3>
</div>
<div class="content">
<ul class="samples">
<li ng-repeat="palette in canvasdesignerPalette">
@@ -76,6 +78,7 @@
</li>
</ul>
</div>
<div class="btn-group">
<a class="btn btn-success" ng-click="saveStyle()">Save Style</a>
<a class="btn btn-success dropdown-toggle" ng-click="opendropdown = !opendropdown">
@@ -87,8 +90,11 @@
<li><a ng-click="makePreset();opendropdown = false">Make preset</a></li>
</ul>
</div>
</div>
<div class="main-panel" ng-class="{selected: !showDevicesPreview && showStyleEditor}">
<div ng-show="!currentSelected">
<div class="header">
<h3>Select</h3>
@@ -106,6 +112,7 @@
</ul>
</div>
</div>
<div ng-repeat="configItem in canvasdesignerModel.configs" ng-show="currentSelected && currentSelected.name.toLowerCase() == configItem.name.toLowerCase()
&& currentSelected.schema.toLowerCase() == configItem.schema.toLowerCase()" on-finish-render-filters>
<div class="header">
@@ -127,6 +134,7 @@
</div>
</div>
</div>
<div class="btn-group">
<a class="btn btn-success" ng-click="saveStyle()">Save Style</a>
<a class="btn btn-success dropdown-toggle" ng-click="opendropdown = !opendropdown">
@@ -138,13 +146,20 @@
<li><a ng-click="makePreset();opendropdown = false">Make preset</a></li>
</ul>
</div>
</div>
<div class="float-panel"></div>
</div>
<div id="speechbubble">
<p>Styles saved and published</p>
</div>
<script src="../lib/rgrove-lazyload/lazyload.js"></script>
<script src="../js/canvasdesigner.loader.js"></script>
</body>
</html>
@@ -255,9 +255,11 @@
else {
$scope.save().then(function (data) {
previewWindow.location.href = redirect;
});
});
}
}
};
$scope.restore = function (content) {
@@ -8,8 +8,6 @@
var evts = [];
var isInfoTab = false;
scope.publishStatus = {};
scope.disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
function onInit() {
@@ -52,7 +50,7 @@
scope.openDocumentType = function (documentType) {
var url = "/settings/documenttypes/edit/" + documentType.id;
$location.url(url);
$location.path(url);
};
scope.updateTemplate = function (templateAlias) {
@@ -145,12 +145,6 @@ angular.module("umbraco.directives")
}
}
}
if (val === "true") {
tinyMceConfig.customConfig[i] = true;
}
if (val === "false") {
tinyMceConfig.customConfig[i] = false;
}
}
angular.extend(baseLineConfigObj, tinyMceConfig.customConfig);
@@ -59,6 +59,7 @@
</pre>
<h1>General Options</h1>
Lorem ipsum dolor sit amet..
<table>
<thead>
<tr>
@@ -73,7 +74,7 @@
<td>Set the title of the overlay.</td>
</tr>
<tr>
<td>model.subtitle</td>
<td>model.subTitle</td>
<td>String</td>
<td>Set the subtitle of the overlay.</td>
</tr>
@@ -495,7 +496,6 @@ Opens an overlay to show a custom YSOD. </br>
var activeElementType = document.activeElement.tagName;
var clickableElements = ["A", "BUTTON"];
var submitOnEnter = document.activeElement.hasAttribute("overlay-submit-on-enter");
var submitOnEnterValue = submitOnEnter ? document.activeElement.getAttribute("overlay-submit-on-enter") : "";
if(clickableElements.indexOf(activeElementType) === 0) {
document.activeElement.click();
@@ -503,9 +503,7 @@ Opens an overlay to show a custom YSOD. </br>
} else if(activeElementType === "TEXTAREA" && !submitOnEnter) {
} else if (submitOnEnter && submitOnEnterValue === "false") {
// don't do anything
}else {
} else {
scope.$apply(function () {
scope.submitForm(scope.model);
});
@@ -10,12 +10,12 @@ function noDirtyCheck() {
require: 'ngModel',
link: function (scope, elm, attrs, ctrl) {
var alwaysFalse = {
get: function () { return false; },
set: function () { }
};
Object.defineProperty(ctrl, '$pristine', alwaysFalse);
Object.defineProperty(ctrl, '$dirty', alwaysFalse);
elm.focus(function () {
scope.$watch(function() {
ctrl.$pristine = false;
});
});
}
};
@@ -357,16 +357,37 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) {
"PostCreateContainer",
{ parentId: parentId, name: name })),
'Failed to create a folder under parent id ' + parentId);
},
},
renameContainer: function (id, name) {
return umbRequestHelper.resourcePromise(
$http.post
(umbRequestHelper.getApiUrl(
$http.post
(umbRequestHelper.getApiUrl(
"dataTypeApiBaseUrl",
"PostRenameContainer",
{ id: id, name: name })),
"Failed to rename the folder with id " + id);
},
getContainer: function (name) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"dataTypeApiBaseUrl",
"GetContainer",
{ name: name })),
'No container found with name: ' + name);
},
containerExist: function (name) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"dataTypeApiBaseUrl",
"ContainerExist",
{ name: name })));
}
};
}
@@ -1,37 +0,0 @@
(function () {
"use strict";
function javascriptLibraryService($q, $http, umbRequestHelper) {
var existingLocales = [];
function getSupportedLocalesForMoment() {
var deferred = $q.defer();
if (existingLocales.length === 0) {
umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"backOfficeAssetsApiBaseUrl",
"GetSupportedMomentLocales")),
"Failed to get cultures").then(function(locales) {
existingLocales = locales;
deferred.resolve(existingLocales);
});
} else {
deferred.resolve(existingLocales);
}
return deferred.promise;
}
var service = {
getSupportedLocalesForMoment: getSupportedLocalesForMoment
};
return service;
}
angular.module("umbraco.services").factory("javascriptLibraryService", javascriptLibraryService);
})();
@@ -1,324 +1,285 @@
angular.module('umbraco.services')
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, assetsService, dialogService, $timeout, angularHelper, $http, javascriptLibraryService) {
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, dialogService, $timeout, angularHelper, $http) {
var currentUser = null;
var lastUserId = null;
var loginDialog = null;
var currentUser = null;
var lastUserId = null;
var loginDialog = null;
//this tracks the last date/time that the user's remainingAuthSeconds was updated from the server
// this is used so that we know when to go and get the user's remaining seconds directly.
var lastServerTimeoutSet = null;
//this tracks the last date/time that the user's remainingAuthSeconds was updated from the server
// this is used so that we know when to go and get the user's remaining seconds directly.
var lastServerTimeoutSet = null;
function openLoginDialog(isTimedOut) {
if (!loginDialog) {
loginDialog = dialogService.open({
function openLoginDialog(isTimedOut) {
if (!loginDialog) {
loginDialog = dialogService.open({
//very special flag which means that global events cannot close this dialog
manualClose: true,
//very special flag which means that global events cannot close this dialog
manualClose: true,
template: 'views/common/dialogs/login.html',
modalClass: "login-overlay",
animation: "slide",
show: true,
callback: onLoginDialogClose,
dialogData: {
isTimedOut: isTimedOut
}
template: 'views/common/dialogs/login.html',
modalClass: "login-overlay",
animation: "slide",
show: true,
callback: onLoginDialogClose,
dialogData: {
isTimedOut: isTimedOut
}
});
}
}
function onLoginDialogClose(success) {
loginDialog = null;
if (success) {
securityRetryQueue.retryAll(currentUser.name);
}
else {
securityRetryQueue.cancelAll();
$location.path('/');
}
}
/**
This methods will set the current user when it is resolved and
will then start the counter to count in-memory how many seconds they have
remaining on the auth session
*/
function setCurrentUser(usr) {
if (!usr.remainingAuthSeconds) {
throw "The user object is invalid, the remainingAuthSeconds is required.";
}
currentUser = usr;
lastServerTimeoutSet = new Date();
//start the timer
countdownUserTimeout();
}
/**
Method to count down the current user's timeout seconds,
this will continually count down their current remaining seconds every 5 seconds until
there are no more seconds remaining.
*/
function countdownUserTimeout() {
$timeout(function () {
if (currentUser) {
//countdown by 5 seconds since that is how long our timer is for.
currentUser.remainingAuthSeconds -= 5;
//if there are more than 30 remaining seconds, recurse!
if (currentUser.remainingAuthSeconds > 30) {
//we need to check when the last time the timeout was set from the server, if
// it has been more than 30 seconds then we'll manually go and retrieve it from the
// server - this helps to keep our local countdown in check with the true timeout.
if (lastServerTimeoutSet != null) {
var now = new Date();
var seconds = (now.getTime() - lastServerTimeoutSet.getTime()) / 1000;
if (seconds > 30) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
}
}
function onLoginDialogClose(success) {
loginDialog = null;
//recurse the countdown!
countdownUserTimeout();
}
else {
if (success) {
securityRetryQueue.retryAll(currentUser.name);
//we are either timed out or very close to timing out so we need to show the login dialog.
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
try {
//NOTE: We are calling this again so that the server can create a log that the timeout has expired, we
// don't actually care about this result.
authResource.getRemainingTimeoutSeconds();
}
finally {
userAuthExpired();
}
});
}
else {
securityRetryQueue.cancelAll();
$location.path('/');
}
}
//we've got less than 30 seconds remaining so let's check the server
/**
This methods will set the current user when it is resolved and
will then start the counter to count in-memory how many seconds they have
remaining on the auth session
*/
function setCurrentUser(usr) {
if (!usr.remainingAuthSeconds) {
throw "The user object is invalid, the remainingAuthSeconds is required.";
}
currentUser = usr;
lastServerTimeoutSet = new Date();
//start the timer
countdownUserTimeout();
}
if (lastServerTimeoutSet != null) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
/**
Method to count down the current user's timeout seconds,
this will continually count down their current remaining seconds every 5 seconds until
there are no more seconds remaining.
*/
function countdownUserTimeout() {
$timeout(function () {
if (currentUser) {
//countdown by 5 seconds since that is how long our timer is for.
currentUser.remainingAuthSeconds -= 5;
//if there are more than 30 remaining seconds, recurse!
if (currentUser.remainingAuthSeconds > 30) {
//we need to check when the last time the timeout was set from the server, if
// it has been more than 30 seconds then we'll manually go and retrieve it from the
// server - this helps to keep our local countdown in check with the true timeout.
if (lastServerTimeoutSet != null) {
var now = new Date();
var seconds = (now.getTime() - lastServerTimeoutSet.getTime()) / 1000;
if (seconds > 30) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
}
//recurse the countdown!
countdownUserTimeout();
}
else {
//we are either timed out or very close to timing out so we need to show the login dialog.
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
try {
//NOTE: We are calling this again so that the server can create a log that the timeout has expired, we
// don't actually care about this result.
authResource.getRemainingTimeoutSeconds();
}
finally {
userAuthExpired();
}
});
}
else {
//we've got less than 30 seconds remaining so let's check the server
if (lastServerTimeoutSet != null) {
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
lastServerTimeoutSet = null;
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
}
//recurse the countdown!
countdownUserTimeout();
}
}
}
}, 5000, //every 5 seconds
false); //false = do NOT execute a digest for every iteration
}
/** Called to update the current user's timeout */
function setUserTimeoutInternal(newTimeout) {
var asNumber = parseFloat(newTimeout);
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
currentUser.remainingAuthSeconds = newTimeout;
lastServerTimeoutSet = new Date();
}
}
/** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */
function userAuthExpired(isLogout) {
//store the last user id and clear the user
if (currentUser && currentUser.id !== undefined) {
lastUserId = currentUser.id;
}
if (currentUser) {
currentUser.remainingAuthSeconds = 0;
}
lastServerTimeoutSet = null;
currentUser = null;
//broadcast a global event that the user is no longer logged in
eventsService.emit("app.notAuthenticated");
openLoginDialog(isLogout === undefined ? true : !isLogout);
}
// Register a handler for when an item is added to the retry queue
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
if (securityRetryQueue.hasMore()) {
userAuthExpired();
}
});
return {
/** Internal method to display the login dialog */
_showLoginDialog: function () {
openLoginDialog();
},
/** Returns a promise, sends a request to the server to check if the current cookie is authorized */
isAuthenticated: function () {
//if we've got a current user then just return true
if (currentUser) {
var deferred = $q.defer();
deferred.resolve(true);
return deferred.promise;
}
return authResource.isAuthenticated();
},
/** Returns a promise, sends a request to the server to validate the credentials */
authenticate: function (login, password) {
return authResource.performLogin(login, password)
.then(this.setAuthenticationSuccessful);
},
setAuthenticationSuccessful: function (data) {
//when it's successful, return the user data
setCurrentUser(data);
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
//broadcast a global event
eventsService.emit("app.authenticated", result);
return result;
},
/** Logs the user out
*/
logout: function () {
return authResource.performLogout()
.then(function (data) {
userAuthExpired();
//done!
return null;
});
},
/** Refreshes the current user data with the data stored for the user on the server and returns it */
refreshCurrentUser: function () {
var deferred = $q.defer();
authResource.getCurrentUser()
.then(function (data) {
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
setCurrentUser(data);
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
return deferred.promise;
},
/** Returns the current user object in a promise */
getCurrentUser: function (args) {
var deferred = $q.defer();
if (!currentUser) {
authResource.getCurrentUser()
.then(function (data) {
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
if (args && args.broadcastEvent) {
//broadcast a global event, will inform listening controllers to load in the user specific data
eventsService.emit("app.authenticated", result);
}
setCurrentUser(data);
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
}
else {
deferred.resolve(currentUser);
}
return deferred.promise;
},
/** Loads the Moment.js Locale for the current user. */
loadMomentLocaleForCurrentUser: function () {
var deferred = $q.defer();
function loadLocales(currentUser, supportedLocales) {
var locale = currentUser.locale.toLowerCase();
if (locale !== 'en-us') {
var localeUrls = [];
if (supportedLocales.indexOf(locale + '.js') > -1) {
localeUrls.push('lib/moment/' + locale + '.js');
}
if (locale.indexOf('-') > -1) {
var majorLocale = locale.split('-')[0] + '.js';
if (supportedLocales.indexOf(majorLocale) > -1) {
localeUrls.push('lib/moment/' + majorLocale);
}
}
assetsService.load(localeUrls).then(function () {
deferred.resolve(localeUrls);
});
} else {
deferred.resolve(['']);
}
}
var promises = {
currentUser: this.getCurrentUser(),
supportedLocales: javascriptLibraryService.getSupportedLocalesForMoment()
}
$q.all(promises).then(function (values) {
loadLocales(values.currentUser, values.supportedLocales);
//now go get it from the server
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
angularHelper.safeApply($rootScope, function () {
authResource.getRemainingTimeoutSeconds().then(function (result) {
setUserTimeoutInternal(result);
});
});
return deferred.promise;
}
},
//recurse the countdown!
countdownUserTimeout();
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
setUserTimeout: function (newTimeout) {
setUserTimeoutInternal(newTimeout);
}
};
}
}
}, 5000, //every 5 seconds
false); //false = do NOT execute a digest for every iteration
}
/** Called to update the current user's timeout */
function setUserTimeoutInternal(newTimeout) {
var asNumber = parseFloat(newTimeout);
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
currentUser.remainingAuthSeconds = newTimeout;
lastServerTimeoutSet = new Date();
}
}
/** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */
function userAuthExpired(isLogout) {
//store the last user id and clear the user
if (currentUser && currentUser.id !== undefined) {
lastUserId = currentUser.id;
}
if (currentUser) {
currentUser.remainingAuthSeconds = 0;
}
lastServerTimeoutSet = null;
currentUser = null;
//broadcast a global event that the user is no longer logged in
eventsService.emit("app.notAuthenticated");
openLoginDialog(isLogout === undefined ? true : !isLogout);
}
// Register a handler for when an item is added to the retry queue
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
if (securityRetryQueue.hasMore()) {
userAuthExpired();
}
});
return {
/** Internal method to display the login dialog */
_showLoginDialog: function () {
openLoginDialog();
},
/** Returns a promise, sends a request to the server to check if the current cookie is authorized */
isAuthenticated: function () {
//if we've got a current user then just return true
if (currentUser) {
var deferred = $q.defer();
deferred.resolve(true);
return deferred.promise;
}
return authResource.isAuthenticated();
},
/** Returns a promise, sends a request to the server to validate the credentials */
authenticate: function (login, password) {
return authResource.performLogin(login, password)
.then(this.setAuthenticationSuccessful);
},
setAuthenticationSuccessful: function (data) {
//when it's successful, return the user data
setCurrentUser(data);
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
//broadcast a global event
eventsService.emit("app.authenticated", result);
return result;
},
/** Logs the user out
*/
logout: function () {
return authResource.performLogout()
.then(function (data) {
userAuthExpired();
//done!
return null;
});
},
/** Refreshes the current user data with the data stored for the user on the server and returns it */
refreshCurrentUser: function() {
var deferred = $q.defer();
authResource.getCurrentUser()
.then(function (data) {
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
setCurrentUser(data);
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
return deferred.promise;
},
/** Returns the current user object in a promise */
getCurrentUser: function (args) {
var deferred = $q.defer();
if (!currentUser) {
authResource.getCurrentUser()
.then(function (data) {
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
if (args && args.broadcastEvent) {
//broadcast a global event, will inform listening controllers to load in the user specific data
eventsService.emit("app.authenticated", result);
}
setCurrentUser(data);
deferred.resolve(currentUser);
}, function () {
//it failed, so they are not logged in
deferred.reject();
});
}
else {
deferred.resolve(currentUser);
}
return deferred.promise;
},
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
setUserTimeout: function (newTimeout) {
setUserTimeoutInternal(newTimeout);
}
};
});
+15 -18
View File
@@ -18,26 +18,23 @@ app.run(['userService', '$log', '$rootScope', '$location', 'queryStrings', 'navi
eventsService.on("app.authenticated", function(evt, data) {
assetsService._loadInitAssets().then(function() {
// Loads the user's locale settings for Moment.
userService.loadMomentLocaleForCurrentUser().then(function() {
//Register all of the tours on the server
tourService.registerAllTours().then(function () {
appReady(data);
// Auto start intro tour
tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) {
// start intro tour if it hasn't been completed or disabled
if (introTour && introTour.disabled !== true && introTour.completed !== true) {
tourService.startTour(introTour);
}
});
}, function(){
appReady(data);
//Register all of the tours on the server
tourService.registerAllTours().then(function () {
appReady(data);
// Auto start intro tour
tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) {
// start intro tour if it hasn't been completed or disabled
if (introTour && introTour.disabled !== true && introTour.completed !== true) {
tourService.startTour(introTour);
}
});
}, function(){
appReady(data);
});
});
});
@@ -168,9 +168,6 @@
//used for property editors
@import "property-editors.less";
//used for prevalue editors
@import "components/prevalues/multivalues.less";
@import "typeahead.less";
@import "hacks.less";
@@ -178,4 +175,4 @@
@import "healthcheck.less";
// cleanup properties.less when it is done
@import "properties.less";
@import "properties.less";
@@ -1,53 +0,0 @@
.umb-prevalues-multivalues {
width: 400px;
}
.umb-prevalues-multivalues__left {
display: flex;
flex: 1 1 auto;
}
.umb-prevalues-multivalues__right {
display: flex;
flex: 0 0 auto;
align-items: center;
}
.umb-prevalues-multivalues__add {
display: flex;
}
.umb-prevalues-multivalues__add input {
width: 320px;
}
.umb-prevalues-multivalues__add input {
display: flex;
}
.umb-prevalues-multivalues__add button {
margin: 0 6px 0 0;
float: right
}
.umb-prevalues-multivalues__listitem {
display: flex;
padding: 6px;
margin: 10px 0px !important;
background: #F3F3F5;
cursor: move;
}
.umb-prevalues-multivalues__listitem i {
display: flex;
align-items: center;
margin-right: 5px
}
.umb-prevalues-multivalues__listitem a {
cursor: pointer;
}
.umb-prevalues-multivalues__listitem input {
width: 295px;
}
@@ -280,6 +280,7 @@
.umb-grid .umb-control {
position: relative;
display: block;
overflow: hidden;
margin-left: 10px;
margin-right: 10px;
margin-bottom: 10px;
@@ -223,16 +223,14 @@ input.umb-group-builder__group-title-input {
flex: 1;
}
.umb-group-builder__property-meta-alias {
font-size: 10px;
color: @gray-3;
word-break: break-word;
line-height: 1.5;
margin-bottom: 5px;
.umb-group-builder__property-meta .umb-locked-field__input {
font-size: 12px !important;
word-break: break-word;
margin-bottom: 2px !important;
}
.umb-group-builder__property-meta-label textarea {
font-size: 14px;
font-size: 16px;
font-weight: bold;
margin-bottom: 0;
color: @gray-1;
@@ -40,16 +40,6 @@
padding: 10px;
}
.umb-contentpicker__min-max-help {
font-size: 13px;
margin-top: 5px;
color: @gray-4;
}
.show-validation .umb-contentpicker__min-max-help {
display: none;
}
.umb-contentpicker small {
&:not(:last-child) {
@@ -119,22 +109,22 @@ ul.color-picker li a {
}
/* pre-value editor */
/*.control-group.color-picker-preval:before {
content: "";
display: inline-block;
vertical-align: middle;
/*.control-group.color-picker-preval:before {
content: "";
display: inline-block;
vertical-align: middle;
height: 100%;
}*/
/*.control-group.color-picker-preval div.thumbnail {
display: inline-block;
}*/
/*.control-group.color-picker-preval div.thumbnail {
display: inline-block;
vertical-align: middle;
}*/
.control-group.color-picker-preval div.color-picker-prediv {
display: inline-block;
}*/
.control-group.color-picker-preval div.color-picker-prediv {
display: inline-block;
width: 60%;
}
}
.control-group.color-picker-preval pre {
display: inline;
margin-right: 20px;
@@ -146,18 +136,18 @@ ul.color-picker li a {
vertical-align: middle;
}
.control-group.color-picker-preval btn {
.control-group.color-picker-preval btn {
//vertical-align: middle;
}
}
.control-group.color-picker-preval input[type="text"] {
min-width: 40%;
width: 40%;
display: inline-block;
margin-right: 20px;
.control-group.color-picker-preval input[type="text"] {
min-width: 40%;
width: 40%;
display: inline-block;
margin-right: 20px;
margin-top: 1px;
}
}
.control-group.color-picker-preval label {
border: solid @white 1px;
padding: 6px;
@@ -213,22 +203,10 @@ ul.color-picker li a {
.umb-thumbnails {
position: relative;
display: flex;
-ms-flex-direction: row;
-webkit-flex-direction: row;
flex-direction: row;
-ms-flex-wrap: wrap;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
justify-content: flex-start;
.umb-thumbnails{
position: relative;
}
.umb-thumbnails > li.icon {
width: 14%;
text-align: center;
}
.umb-thumbnails i{margin: auto;}
.umb-thumbnails a{
@@ -100,27 +100,6 @@
.color-green, .color-green i{color: @green-d1 !important;}
.color-yellow, .color-yellow i{color: @yellow-d1 !important;}
/* Colors based on http://zavoloklom.github.io/material-design-color-palette/colors.html */
.color-black, .color-black i { color: #000 !important; }
.color-blue-grey, .color-blue-grey i { color: #607d8b !important; }
.color-grey, .color-grey i { color: #9e9e9e !important; }
.color-brown, .color-brown i { color: #795548 !important; }
.color-blue, .color-blue i { color: #2196f3 !important; }
.color-light-blue, .color-light-blue i {color: #03a9f4 !important; }
.color-cyan, .color-cyan i { color: #00bcd4 !important; }
.color-green, .color-green i { color: #4caf50 !important; }
.color-light-green, .color-light-green i {color: #8bc34a !important; }
.color-lime, .color-lime i { color: #cddc39 !important; }
.color-yellow, .color-yellow i { color: #ffeb3b !important; }
.color-amber, .color-amber i { color: #ffc107 !important; }
.color-orange, .color-orange i { color: #ff9800 !important; }
.color-deep-orange, .color-deep-orange i { color: #ff5722 !important; }
.color-red, .color-red i { color: #f44336 !important; }
.color-pink, .color-pink i { color: #e91e63 !important; }
.color-purple,.color-purple i { color: #9c27b0 !important; }
.color-deep-purple, .color-deep-purple i { color: #673ab7 !important; }
.color-indigo, .color-indigo i { color: #3f51b5 !important; }
// Scaffolding
// -------------------------
@@ -7,13 +7,14 @@ angular.module("umbraco")
$scope.icons = icons;
});
$scope.submitClass = function(icon){
$scope.submitClass = function (icon) {
if($scope.color) {
$scope.submit(icon + " " + $scope.color);
}
else {
$scope.submit(icon);
else {
$scope.submit(icon);
}
};
}
);
@@ -7,7 +7,7 @@
* The controller for the content type editor property dialog
*/
(function() {
(function () {
"use strict";
function EditorPickerOverlay($scope, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService) {
@@ -24,6 +24,7 @@
vm.typesAndEditors = [];
vm.userConfigured = [];
vm.loading = false;
vm.property = $scope.model.property;
vm.tabs = [{
active: true,
id: 1,
@@ -55,7 +56,7 @@
vm.loading = true;
dataTypeResource.getGroupedPropertyEditors().then(function(data) {
dataTypeResource.getGroupedPropertyEditors().then(function (data) {
vm.tabs[0].typesAndEditors = data;
vm.typesAndEditors = data;
vm.tabsLoaded = vm.tabsLoaded + 1;
@@ -68,7 +69,7 @@
vm.loading = true;
dataTypeResource.getGroupedDataTypes().then(function(data) {
dataTypeResource.getGroupedDataTypes().then(function (data) {
vm.tabs[1].userConfigured = data;
vm.userConfigured = data;
vm.tabsLoaded = vm.tabsLoaded + 1;
@@ -116,7 +117,7 @@
var parentId = -1;
dataTypeResource.getScaffold(parentId).then(function(dataType) {
dataTypeResource.getScaffold(parentId).then(function (dataType) {
// set alias
dataType.selectedEditor = editor.alias;
@@ -124,7 +125,11 @@
// set name
var nameArray = [];
// content type name (if you add properties before filling out a name for the content type, we need a temp name)
var contentTypeName = "Unnamed Content Type";
if ($scope.model.contentTypeName) {
contentTypeName = $scope.model.contentTypeName;
nameArray.push($scope.model.contentTypeName);
}
@@ -138,13 +143,48 @@
// make name
dataType.name = nameArray.join(" - ");
dataType.containerName = contentTypeName;
// get pre values
dataTypeResource.getPreValues(dataType.selectedEditor).then(function(preValues) {
dataTypeResource.getPreValues(dataType.selectedEditor).then(function (preValues) {
// if there's any configuration settings then open the dialog, else just save the editor immediately
if (preValues.length > 0) {
dataType.preValues = preValues;
openEditorSettingsOverlay(dataType, true);
} else {
saveDataTypeWithContainerCheck(dataType.containerName, dataType, preValues, true);
}
});
dataType.preValues = preValues;
});
openEditorSettingsOverlay(dataType, true);
}
function saveDataTypeWithContainerCheck(containerName, dataType, preValues, isNew) {
// check if a container for the doctype already exist
dataTypeResource.containerExist(containerName).then(function (container) {
if (container == "true") {
dataTypeResource.getContainer(containerName).then(
function (container) {
dataType.parentId = container.Id;
saveDataType(dataType, preValues, isNew);
});
} else {
dataTypeResource.createContainer(-1, containerName).then(
function (container) {
dataType.parentId = container.Entity.Id;
saveDataType(dataType, preValues, isNew);
});
}
});
}
function saveDataType(dataType, preValues, isNew) {
dataTypeResource.save(dataType, preValues, isNew).then(function (newDataType) {
contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function (propertyType) {
submitOverlay(newDataType, propertyType, true);
});
@@ -154,8 +194,8 @@
function pickDataType(selectedDataType) {
dataTypeResource.getById(selectedDataType.id).then(function(dataType) {
contentTypeResource.getPropertyTypeScaffold(dataType.id).then(function(propertyType) {
dataTypeResource.getById(selectedDataType.id).then(function (dataType) {
contentTypeResource.getPropertyTypeScaffold(dataType.id).then(function (propertyType) {
submitOverlay(dataType, propertyType, false);
});
});
@@ -168,21 +208,9 @@
dataType: dataType,
view: "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html",
show: true,
submit: function(model) {
submit: function (model) {
var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues);
dataTypeResource.save(model.dataType, preValues, isNew).then(function(newDataType) {
contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function(propertyType) {
submitOverlay(newDataType, propertyType, true);
vm.editorSettingsOverlay.show = false;
vm.editorSettingsOverlay = null;
});
});
saveDataTypeWithContainerCheck(dataType.containerName, dataType, preValues, isNew);
}
};
@@ -4,18 +4,7 @@
<i class="icon-alert red"></i>
<strong class="red"><localize key="contentTypeEditor_allDocumentTypes"></localize></strong> using this editor will get updated with the new settings.
</div>
<div class="control-group umb-control-group">
<div class="umb-el-wrap">
<label class="control-label" for="dataTypeName">
<localize key="name"></localize>
</label>
<div class="controls">
<input type="text" ng-model="model.dataType.name" class="umb-editor" umb-auto-focus focus-on-filled="true" required />
</div>
</div>
</div>
<h5><localize key="contentTypeEditor_configuration"></localize></h5>
<umb-property property="preValue" ng-repeat="preValue in model.dataType.preValues">
@@ -110,7 +110,10 @@
vm.editorSettingsOverlay.title = "Editor settings";
vm.editorSettingsOverlay.view = "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html";
vm.editorSettingsOverlay.dataType = dataType;
vm.editorSettingsOverlay.show = true;
if (dataType.preValues.length > 0) {
vm.editorSettingsOverlay.show = true;
}
vm.editorSettingsOverlay.submit = function (model) {
@@ -150,6 +153,7 @@
});
}
function matchValidationType() {
@@ -1,26 +1,5 @@
<div class="content-type-editor-dialog edit-property-settings" ng-controller="Umbraco.Overlay.PropertySettingsOverlay as vm">
<div class="umb-control-group" ng-if="!model.property.locked">
<div class="control-group">
<textarea class="editor-label"
data-element="property-name"
name="propertyLabel"
ng-model="model.property.label"
localize="placeholder"
placeholder="@placeholders_entername"
umb-auto-focus
focus-on-filled="true"
umb-auto-resize
required
overlay-submit-on-enter>
</textarea>
<div class="umb-validation-label" val-msg-for="propertyLabel" val-toggle-msg="required">Required label</div>
</div>
<div class="control-group -no-margin">
<umb-generate-alias enable-lock="true" alias-from="model.property.label" alias="model.property.alias"></umb-generate-alias>
</div>
</div>
<div class="umb-control-group control-group">
<textarea
data-element="property-description"
@@ -16,25 +16,24 @@
<div class="umb-control-group">
<select ng-model="color" class="input-block-level">
<option value=""><localize key="colors_black">Black</localize></option>
<option value="color-blue-grey"><localize key="colors_bluegrey">Blue Grey</localize></option>
<option value="color-grey"><localize key="colors_grey">Grey</localize></option>
<option value="color-brown"><localize key="colors_brown">Brown</localize></option>
<option value="color-blue"><localize key="colors_blue">Blue</localize></option>
<option value="color-light-blue"><localize key="colors_lightblue">Light Blue</localize></option>
<option value="color-cyan"><localize key="colors_cyan">Cyan</localize></option>
<option value="color-green"><localize key="colors_green">Green</localize></option>
<option value="color-light-green"><localize key="colors_lightgreen">Light Green</localize></option>
<option value="color-yellow"><localize key="colors_yellow">Yellow</localize></option>
<option value="color-lime"><localize key="colors_lime">Lime</localize></option>
<option value="color-amber"><localize key="colors_amber">Amber</localize></option>
<option value="color-orange"><localize key="colors_orange">Orange</localize></option>
<option value="color-deep-orange"><localize key="colors_deeporange">Deep Orange</localize></option>
<option value="color-red"><localize key="colors_red">Red</localize></option>
<option value="color-pink"><localize key="colors_pink">Pink</localize></option>
<option value="color-purple"><localize key="colors_purple">Purple</localize></option>
<option value="color-deep-purple"><localize key="colors_deeppurple">Deep Purple</localize></option>
<option value="color-indigo"><localize key="colors_indigo">Indigo</localize></option>
<option value="">
<localize key="colors_black">Black</localize>
</option>
<option value="color-green">
<localize key="colors_green">Green</localize>
</option>
<option value="color-yellow">
<localize key="colors_yellow">Yellow</localize>
</option>
<option value="color-orange">
<localize key="colors_orange">Orange</localize>
</option>
<option value="color-blue">
<localize key="colors_blue">Blue</localize>
</option>
<option value="color-red">
<localize key="colors_red">Red</localize>
</option>
</select>
</div>
@@ -14,6 +14,7 @@
</li>
<li data-element="section-{{section.alias}}" ng-repeat="section in sections | limitTo: maxSections" ng-class="{current: section.alias == currentSection}">
<a href="#/{{section.alias}}"
hotkey="ctrl+alt+{{$index+1}}"
ng-dblclick="sectionDblClick(section)"
ng-click="sectionClick($event, section)"
prevent-default>
@@ -41,6 +42,8 @@
<ul class="sections sections-tray">
<li ng-repeat="section in sections | limitTo: overflowingSections" ng-class="{current: section.alias == currentSection}">
<a href="#/{{section.alias}}"
hotkey="ctrl+alt+{{$index+1+maxSections}}"
hotkey-when-hidden="true"
ng-dblclick="sectionDblClick(section)"
ng-click="sectionClick($event, section)"
prevent-default>
@@ -171,25 +171,27 @@
{{publishStatus.label}}
</umb-badge>
</umb-control-group>
<umb-control-group data-element="node-info-create-date" label="@template_createdDate">
{{node.createDateFormatted}} <localize key="general_by">by</localize> {{ node.owner.name }}
</umb-control-group>
<umb-control-group data-element="node-info-document-type" label="@content_documentType">
<umb-node-preview style="max-width: 100%; margin-bottom: 0;"
icon="documentType.icon"
name="documentType.name"
allow-open="allowOpen"
on-open="openDocumentType(documentType)">
<umb-node-preview
style="max-width: 100%; margin-bottom: 0;"
icon="documentType.icon"
name="documentType.name"
allow-open="allowOpen"
on-open="openDocumentType(documentType)">
</umb-node-preview>
</umb-control-group>
<umb-control-group ng-if="disableTemplates == false" data-element="node-info-template" label="@template_template">
<select class="input-block-level"
ng-model="node.template"
ng-options="key as value for (key, value) in availableTemplates"
ng-change="updateTemplate(node.template)">
<umb-control-group data-element="node-info-template" label="@template_template">
<select
class="input-block-level"
ng-model="node.template"
ng-options="key as value for (key, value) in availableTemplates"
ng-change="updateTemplate(node.template)">
<option value=""><localize key="general_choose">Choose</localize>...</option>
</select>
</umb-control-group>
@@ -198,7 +200,7 @@
<div>{{ node.id }}</div>
<small>{{ node.key }}</small>
</umb-control-group>
</umb-box-content>
</umb-box>
</div>
@@ -30,6 +30,7 @@
ng-model="name"
ng-class="{'name-is-empty': $parent.name===null || $parent.name===''}"
umb-auto-focus
hotkey="ctrl+,"
val-server-field="Name"
required />
</ng-form>
@@ -4,7 +4,7 @@
class="alert alert-block alert-{{notification.type}} umb-notifications__notification animated -half-second fadeIn"
ng-class="{'-no-border -extra-padding': notification.type === 'form'}">
<a class='close -align-right' ng-click="removeNotification($index)" prevent-default href>&times;</a>
<a class='close -align-right' hotkey="esc" ng-click="removeNotification($index)" prevent-default href>&times;</a>
<div ng-if="notification.view">
<div ng-include="notification.view"></div>
@@ -1,4 +1,5 @@
<div data-element="overlay" class="umb-overlay umb-overlay-{{position}}" on-outside-click="closeOverLay()">
<div data-element="overlay" class="umb-overlay umb-overlay-{{position}}" hotkey="esc" on-outside-click="closeOverLay()">
<ng-form class="umb-overlay__form" name="overlayForm" novalidate val-form-manager>
<div data-element="overlay-header" class="umb-overlay-header">
@@ -1,5 +1,5 @@
<ul class="nav nav-tabs umb-nav-tabs">
<li data-element="tab-{{tab.alias}}" ng-class="{'tab-error': tabHasError}" ng-repeat="tab in model" val-tab>
<a data-toggle="tab" href="#tab{{tab.id}}{{idSuffix}}">{{ tab.label }}</a>
<a data-toggle="tab" hotkey="ctrl+{{$index+1}}" href="#tab{{tab.id}}{{idSuffix}}">{{ tab.label }}</a>
</li>
</ul>
@@ -4,24 +4,22 @@
<umb-editor-sub-header-content-right>
<umb-button
alias="compositions"
ng-if="compositions !== false"
type="button"
button-style="link"
label-key="contentTypeEditor_compositions"
icon="icon-merge"
action="openCompositionsDialog()">
<umb-button alias="compositions"
ng-if="compositions !== false"
type="button"
button-style="link"
label-key="contentTypeEditor_compositions"
icon="icon-merge"
action="openCompositionsDialog()">
</umb-button>
<umb-button
alias="reorder"
ng-if="sorting !== false"
type="button"
button-style="link"
label-key="{{sortingButtonKey}}"
icon="icon-navigation"
action="toggleSortingMode();">
<umb-button alias="reorder"
ng-if="sorting !== false"
type="button"
button-style="link"
label-key="{{sortingButtonKey}}"
icon="icon-navigation"
action="toggleSortingMode();">
</umb-button>
</umb-editor-sub-header-content-right>
@@ -40,7 +38,7 @@
<a href="" class="umb-group-builder__group -placeholder" hotkey="alt+shift+t" ng-click="addGroup(tab)" ng-if="tab.tabState=='init' && !sortingMode" data-element="group-add">
<div class="umb-group-builder__group-title-wrapper -placeholder">
<div class="umb-group-builder__group-title -placeholder"></div>
<div class="umb-group-builder__group-title -placeholder"></div>
</div>
<localize ng-if="model.groups.length === 1" key="contentTypeEditor_addNewTab"></localize>
@@ -53,11 +51,10 @@
<div class="umb-group-builder__group-remove" ng-if="!sortingMode && tab.properties.length <= 1">
<i class="icon-trash" ng-click="togglePrompt(tab)"></i>
<umb-confirm-action
ng-if="tab.deletePrompt"
direction="left"
on-confirm="removeGroup($index)"
on-cancel="hidePrompt(tab)">
<umb-confirm-action ng-if="tab.deletePrompt"
direction="left"
on-confirm="removeGroup($index)"
on-cancel="hidePrompt(tab)">
</umb-confirm-action>
</div>
@@ -114,15 +111,15 @@
<!-- Init property / Property placeholder / add new property -->
<a href=""
data-element="property-add"
class="umb-group-builder__property -placeholder"
ng-if="property.propertyState=='init' && !sortingMode"
ng-class="{'-placeholder': property.propertyState=='init'}"
hotkey="alt+shift+p"
hotkey-when="{{tab.tabState === 'active' && property.propertyState=='init'}}"
ng-click="addProperty(property, tab)"
on-focus="activateGroup(tab)"
focus-when="{{property.focus}}">
data-element="property-add"
class="umb-group-builder__property -placeholder"
ng-if="property.propertyState=='init' && !sortingMode"
ng-class="{'-placeholder': property.propertyState=='init'}"
hotkey="alt+shift+p"
hotkey-when="{{tab.tabState === 'active' && property.propertyState=='init'}}"
ng-click="addProperty(property, tab)"
on-focus="activateGroup(tab)"
focus-when="{{property.focus}}">
<div class="umb-group-builder__property-meta">
<div class="umb-group-builder__placeholder-box -input-small"></div>
@@ -151,24 +148,25 @@
<div class="control-group -no-margin" ng-if="!sortingMode">
<div class="umb-group-builder__property-meta-alias" ng-if="property.inherited || property.locked">{{ property.alias }}</div>
<umb-locked-field
ng-if="!property.inherited && !property.locked"
locked="locked"
ng-model="property.alias"
placeholder-text="'Alias...'"
server-validation-field="{{'Groups[' + $parent.$parent.$parent.$parent.$index + '].Properties[' + $index + '].Alias'}}">
<umb-locked-field ng-if="!property.inherited && !property.locked"
locked="locked"
ng-model="property.alias"
placeholder-text="'Alias...'"
server-validation-field="{{'Groups[' + $parent.$parent.$parent.$parent.$index + '].Properties[' + $index + '].Alias'}}">
</umb-locked-field>
<div class="umb-group-builder__property-meta-label">
<textarea
localize="placeholder"
placeholder="@placeholders_label"
ng-model="property.label"
ng-disabled="property.inherited || property.locked"
name="groupName"
umb-auto-resize
required
val-server-field="{{'Groups[' + $parent.$parent.$parent.$parent.$index + '].Properties[' + $index + '].Label'}}">
<textarea localize="placeholder"
placeholder="@placeholders_label"
ng-model="property.label"
ng-disabled="property.inherited || property.locked"
name="groupName"
ng-change="updateAlias(property)"
umb-auto-resize
select-on-focus
umb-select-when="{{property.tempAlias}}"
required
val-server-field="{{'Groups[' + $parent.$parent.$parent.$parent.$index + '].Properties[' + $index + '].Label'}}">
</textarea>
<div class="umb-validation-label" val-msg-for="groupName" val-toggle-msg="valServerField"></div>
@@ -176,12 +174,11 @@
</div>
<div class="umb-group-builder__property-meta-description">
<textarea
localize="placeholder"
placeholder="@placeholders_enterDescription"
ng-model="property.description"
ng-disabled="property.inherited || property.locked"
umb-auto-resize>
<textarea localize="placeholder"
placeholder="@placeholders_enterDescription"
ng-model="property.description"
ng-disabled="property.inherited || property.locked"
umb-auto-resize>
</textarea>
</div>
</div>
@@ -244,10 +241,9 @@
</div>
<ng-form name="propertyEditorPreviewForm" umb-disable-form-validation>
<umb-property-editor
ng-if="property.view !== undefined"
model="property"
preview="true">
<umb-property-editor ng-if="property.view !== undefined"
model="property"
preview="true">
</umb-property-editor>
</ng-form>
@@ -266,11 +262,10 @@
<!-- delete property -->
<div ng-if="!property.locked" class="umb-group-builder__property-action">
<i class="icon-trash" ng-click="togglePrompt(property)"></i>
<umb-confirm-action
ng-if="property.deletePrompt"
direction="left"
on-confirm="deleteProperty(tab, $index)"
on-cancel="hidePrompt(property)">
<umb-confirm-action ng-if="property.deletePrompt"
direction="left"
on-confirm="deleteProperty(tab, $index)"
on-cancel="hidePrompt(property)">
</umb-confirm-action>
</div>
@@ -292,20 +287,25 @@
</ul>
<umb-overlay
data-element="overlay-compositions"
ng-if="compositionsDialogModel.show"
model="compositionsDialogModel"
position="right"
view="compositionsDialogModel.view">
<umb-overlay data-element="overlay-compositions"
ng-if="compositionsDialogModel.show"
model="compositionsDialogModel"
position="right"
view="compositionsDialogModel.view">
</umb-overlay>
<umb-overlay
data-element="overlay-property-settings"
ng-if="propertySettingsDialogModel.show"
model="propertySettingsDialogModel"
position="right"
view="propertySettingsDialogModel.view">
<umb-overlay data-element="overlay-property-settings"
ng-if="propertySettingsDialogModel.show"
model="propertySettingsDialogModel"
position="right"
view="propertySettingsDialogModel.view">
</umb-overlay>
<umb-overlay data-element="overlay-editor-picker"
ng-if="editorPickerOverlay.show"
model="editorPickerOverlay"
position="right"
view="editorPickerOverlay.view">
</umb-overlay>
</div>
@@ -47,17 +47,7 @@
</a>
</div>
<div class="umb-table-cell" ng-repeat="column in itemProperties">
<span title="{{column.header}}: {{item[column.alias]}}">
<div ng-if="!column.isSensitive">
{{item[column.alias]}}
</div>
<em ng-show="column.isSensitive" class="muted">
<localize key="content_isSensitiveValue_short"></localize>
</em>
</span>
<span title="{{column.header}}: {{item[column.alias]}}">{{item[column.alias]}}</span>
</div>
</div>
</div>
@@ -14,21 +14,18 @@ function DocumentTypesCreateController($scope, $location, navigationService, con
creatingFolder: false,
};
var disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
$scope.model.disableTemplates = disableTemplates;
var node = $scope.dialogOptions.currentNode,
localizeCreateFolder = localizationService.localize("defaultdialog_createFolder");
$scope.showCreateFolder = function () {
$scope.showCreateFolder = function() {
$scope.model.creatingFolder = true;
};
$scope.createContainer = function () {
$scope.createContainer = function() {
if (formHelper.submitForm({ scope: $scope, formCtrl: this.createFolderForm, statusMessage: localizeCreateFolder })) {
if (formHelper.submitForm({scope: $scope, formCtrl: this.createFolderForm, statusMessage: localizeCreateFolder})) {
contentTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) {
contentTypeResource.createContainer(node.id, $scope.model.folderName).then(function(folderId) {
navigationService.hideMenu();
@@ -47,7 +44,7 @@ function DocumentTypesCreateController($scope, $location, navigationService, con
var section = appState.getSectionState("currentSection");
}, function (err) {
}, function(err) {
$scope.error = err;
@@ -61,17 +58,14 @@ function DocumentTypesCreateController($scope, $location, navigationService, con
}
};
// Disabling logic for creating document type with template if disableTemplates is set to true
if (!disableTemplates) {
$scope.createDocType = function () {
$location.search('create', null);
$location.search('notemplate', null);
$location.path("/settings/documenttypes/edit/" + node.id).search("create", "true");
navigationService.hideMenu();
};
}
$scope.createDocType = function() {
$location.search('create', null);
$location.search('notemplate', null);
$location.path("/settings/documenttypes/edit/" + node.id).search("create", "true");
navigationService.hideMenu();
};
$scope.createComponent = function () {
$scope.createComponent = function() {
$location.search('create', null);
$location.search('notemplate', null);
$location.path("/settings/documenttypes/edit/" + node.id).search("create", "true").search("notemplate", "true");
@@ -4,7 +4,7 @@
<h5><localize key="create_createUnder">Create an item under</localize> {{currentNode.name}}</h5>
<ul class="umb-actions umb-actions-child">
<li data-element="action-documentType" ng-hide="model.disableTemplates">
<li data-element="action-documentType">
<a href="" ng-click="createDocType()" umb-auto-focus>
<i class="large icon-item-arrangement"></i>
@@ -15,14 +15,10 @@
</a>
</li>
<li data-element="action-documentTypeWithoutTemplate">
<a href="" ng-click="createComponent()">
<i class="large icon-item-arrangement"></i>
<span class="menu-label">
<localize ng-if="model.disableTemplates == false" key="create_documentTypeWithoutTemplate"></localize>
<localize ng-if="model.disableTemplates == true" key="content_documentType">Document type></localize>
</span>
</a>
<a href="" ng-click="createComponent()">
<i class="large icon-item-arrangement"></i>
<span class="menu-label"><localize key="create_documentTypeWithoutTemplate"></localize></span>
</a>
</li>
<li data-element="action-folder" ng-if="model.allowCreateFolder">
<a href="" ng-click="showCreateFolder()">
@@ -37,7 +33,7 @@
<form novalidate name="createFolderForm"
ng-submit="createContainer()"
val-form-manager>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
@@ -15,36 +15,6 @@
var localizeSaving = localizationService.localize("general_saving");
var evts = [];
var disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
var buttons = [
{
"name": localizationService.localize("general_design"),
"alias": "design",
"icon": "icon-document-dashed-line",
"view": "views/documenttypes/views/design/design.html",
"active": true
},
{
"name": localizationService.localize("general_listView"),
"alias": "listView",
"icon": "icon-list",
"view": "views/documenttypes/views/listview/listview.html"
},
{
"name": localizationService.localize("general_rights"),
"alias": "permissions",
"icon": "icon-keychain",
"view": "views/documenttypes/views/permissions/permissions.html"
},
{
"name": localizationService.localize("treeHeaders_templates"),
"alias": "templates",
"icon": "icon-layout",
"view": "views/documenttypes/views/templates/templates.html"
}
];
vm.save = save;
vm.currentNode = null;
@@ -53,73 +23,97 @@
vm.page = {};
vm.page.loading = false;
vm.page.saveButtonState = "init";
vm.page.navigation = [];
loadButtons();
vm.page.navigation = [
{
"name": localizationService.localize("general_design"),
"alias": "design",
"icon": "icon-document-dashed-line",
"view": "views/documenttypes/views/design/design.html",
"active": true
},
{
"name": localizationService.localize("general_listView"),
"alias": "listView",
"icon": "icon-list",
"view": "views/documenttypes/views/listview/listview.html"
},
{
"name": localizationService.localize("general_rights"),
"alias": "permissions",
"icon": "icon-keychain",
"view": "views/documenttypes/views/permissions/permissions.html"
},
{
"name": localizationService.localize("treeHeaders_templates"),
"alias": "templates",
"icon": "icon-layout",
"view": "views/documenttypes/views/templates/templates.html"
}
];
vm.page.keyboardShortcutsOverview = [
{
"name": localizationService.localize("main_sections"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_navigateSections"),
"keys": [{ "key": "1" }, { "key": "4" }],
"keyRange": true
}
]
},
{
"name": localizationService.localize("general_design"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_addTab"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
},
{
"description": localizationService.localize("shortcuts_addProperty"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }]
},
{
"description": localizationService.localize("shortcuts_addEditor"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }]
},
{
"description": localizationService.localize("shortcuts_editDataType"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
}
]
},
{
"name": localizationService.localize("general_listView"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_toggleListView"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "l" }]
}
]
},
{
"name": localizationService.localize("general_rights"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_toggleAllowAsRoot"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "r" }]
},
{
"description": localizationService.localize("shortcuts_addChildNode"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "c" }]
}
]
},
{
"name": localizationService.localize("treeHeaders_templates"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_addTemplate"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
}
]
}
{
"name": localizationService.localize("main_sections"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_navigateSections"),
"keys": [{ "key": "1" }, { "key": "4" }],
"keyRange": true
}
]
},
{
"name": localizationService.localize("general_design"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_addTab"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
},
{
"description": localizationService.localize("shortcuts_addProperty"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }]
},
{
"description": localizationService.localize("shortcuts_addEditor"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }]
},
{
"description": localizationService.localize("shortcuts_editDataType"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
}
]
},
{
"name": localizationService.localize("general_listView"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_toggleListView"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "l" }]
}
]
},
{
"name": localizationService.localize("general_rights"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_toggleAllowAsRoot"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "r" }]
},
{
"description": localizationService.localize("shortcuts_addChildNode"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "c" }]
}
]
},
{
"name": localizationService.localize("treeHeaders_templates"),
"shortcuts": [
{
"description": localizationService.localize("shortcuts_addTemplate"),
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
}
]
}
];
contentTypeHelper.checkModelsBuilderStatus().then(function (result) {
@@ -150,7 +144,7 @@
vm.page.saveButtonState = "busy";
localizationService.localize("modelsBuilder_buildingModels").then(function (headerValue) {
localizationService.localize("modelsBuilder_waitingMessage").then(function (msgValue) {
localizationService.localize("modelsBuilder_waitingMessage").then(function(msgValue) {
notificationsService.info(headerValue, msgValue);
});
});
@@ -161,26 +155,26 @@
if (!result.lastError) {
//re-check model status
contentTypeHelper.checkModelsBuilderStatus().then(function (statusResult) {
contentTypeHelper.checkModelsBuilderStatus().then(function(statusResult) {
vm.page.modelsBuilder = statusResult;
});
//clear and add success
vm.page.saveButtonState = "init";
localizationService.localize("modelsBuilder_modelsGenerated").then(function (value) {
localizationService.localize("modelsBuilder_modelsGenerated").then(function(value) {
notificationsService.success(value);
});
} else {
vm.page.saveButtonState = "error";
localizationService.localize("modelsBuilder_modelsExceptionInUlog").then(function (value) {
localizationService.localize("modelsBuilder_modelsExceptionInUlog").then(function(value) {
notificationsService.error(value);
});
}
}, function () {
vm.page.saveButtonState = "error";
localizationService.localize("modelsBuilder_modelsGeneratedError").then(function (value) {
localizationService.localize("modelsBuilder_modelsGeneratedError").then(function(value) {
notificationsService.error(value);
});
});
@@ -195,7 +189,7 @@
//we are creating so get an empty data type item
contentTypeResource.getScaffold($routeParams.id)
.then(function (dt) {
.then(function(dt) {
init(dt);
vm.page.loading = false;
});
@@ -213,26 +207,12 @@
});
}
function loadButtons() {
angular.forEach(buttons,
function (val, index) {
if (disableTemplates === true && val.alias === "templates") {
buttons.splice(index, 1);
}
});
vm.page.navigation = buttons;
}
/* ---------- SAVE ---------- */
function save() {
// only save if there is no overlays open
if (overlayHelper.getNumberOfOverlays() === 0) {
if(overlayHelper.getNumberOfOverlays() === 0) {
var deferred = $q.defer();
@@ -253,7 +233,7 @@
// we need to rebind... the IDs that have been created!
rebindCallback: function (origContentType, savedContentType) {
vm.contentType.id = savedContentType.id;
vm.contentType.groups.forEach(function (group) {
vm.contentType.groups.forEach(function(group) {
if (!group.name) return;
var k = 0;
while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name)
@@ -293,7 +273,7 @@
}
else {
localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) {
localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) {
localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) {
notificationsService.error(headerValue, msgValue);
});
});
@@ -351,7 +331,7 @@
function getDataTypeDetails(property) {
if (property.propertyState !== "init") {
dataTypeResource.getById(property.dataTypeId)
.then(function (dataType) {
.then(function(dataType) {
property.dataTypeIcon = dataType.icon;
property.dataTypeName = dataType.name;
});
@@ -365,7 +345,7 @@
});
}
evts.push(eventsService.on("app.refreshEditor", function (name, error) {
evts.push(eventsService.on("app.refreshEditor", function(name, error) {
loadDocumentType();
}));
@@ -1,4 +0,0 @@
angular.module("umbraco").controller("Umbraco.PrevalueEditors.BooleanController",
function ($scope) {
$scope.htmlId = "bool-" + String.CreateGuid();
});
@@ -1,4 +1 @@
<label ng-controller="Umbraco.PrevalueEditors.BooleanController" for="{{htmlId}}" class="checkbox">
<input name="boolean" type="checkbox" ng-model="model.value" ng-true-value="1" ng-false-value="0" id="{{htmlId}}" />
<localize key="general_yes">Yes</localize>
</label>
<input name="boolean" type="checkbox" ng-model="model.value" ng-true-value="1" ng-false-value="0" />
@@ -5,7 +5,6 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiValuesControl
$scope.newItem = "";
$scope.hasError = false;
$scope.focusOnNew = false;
if (!angular.isArray($scope.model.value)) {
@@ -44,7 +43,6 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiValuesControl
$scope.model.value.push({ value: $scope.newItem });
$scope.newItem = "";
$scope.hasError = false;
$scope.focusOnNew = true;
return;
}
}
@@ -75,12 +73,6 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiValuesControl
}
};
$scope.createNew = function (event) {
if (event.keyCode == 13) {
$scope.add(event);
}
}
function getElementIndexByPrevalueText(value) {
for (var i = 0; i < $scope.model.value.length; i++) {
if ($scope.model.value[i].value === value) {
@@ -1,21 +1,13 @@
<div class="umb-editor umb-prevalues-multivalues" ng-controller="Umbraco.PrevalueEditors.MultiValuesController">
<div class="control-group umb-prevalues-multivalues__add">
<div class="umb-prevalues-multivalues__left">
<input overlay-submit-on-enter="false" name="newItem" focus-when="{{focusOnNew}}" ng-keydown="createNew($event)" type="text" ng-model="newItem" val-highlight="{{hasError}}" />
</div>
<div class="umb-prevalues-multivalues__right">
<button class="btn btn-info" ng-click="add($event)">Add</button>
</div>
<div class="umb-editor" ng-controller="Umbraco.PrevalueEditors.MultiValuesController">
<div class="control-group">
<input name="newItem" type="text" ng-model="newItem" val-highlight="{{hasError}}" />
<button class="btn" ng-click="add($event)">Add</button>
</div>
<div ui-sortable="sortableOptions">
<div class="control-group umb-prevalues-multivalues__listitem" ng-repeat="item in model.value">
<div class="control-group" ng-repeat="item in model.value">
<i class="icon icon-navigation handle"></i>
<div class="umb-prevalues-multivalues__left">
<input type="text" ng-model="item.value" val-server="item_{{$index}}" required />
</div>
<div class="umb-prevalues-multivalues__right">
<a class="umb-node-preview__action" ng-click="remove(item, $event)">Remove</a>
</div>
<input type="text" ng-model="item.value" val-server="item_{{$index}}" required />
<button class="btn btn-danger" ng-click="remove(item, $event)">Remove</button>
</div>
</div>
</div>
@@ -15,7 +15,7 @@ function ColorPickerController($scope) {
$scope.toggleItem = function (color) {
var currentColor = ($scope.model.value && $scope.model.value.hasOwnProperty("value"))
var currentColor = $scope.model.value.hasOwnProperty("value")
? $scope.model.value.value
: $scope.model.value;
@@ -20,7 +20,7 @@
</umb-node-preview>
</div>
<a ng-show="model.config.multiPicker === true && renderModel.length < model.config.maxNumber || renderModel.length === 0 || !model.config.maxNumber"
<a ng-show="model.config.multiPicker === true || renderModel.length === 0"
class="umb-node-preview-add"
href=""
ng-click="openContentPicker()"
@@ -28,39 +28,6 @@
<localize key="general_add">Add</localize>
</a>
<div class="umb-contentpicker__min-max-help">
<!-- Both min and max items -->
<span ng-if="model.config.minNumber && model.config.maxNumber && model.config.minNumber !== model.config.maxNumber">
<span ng-if="renderModel.length < model.config.maxNumber">Add between {{model.config.minNumber}} and {{model.config.maxNumber}} items</span>
<span ng-if="renderModel.length > model.config.maxNumber">
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected"> items selected</localize>
</span>
</span>
<!-- Equal min and max -->
<span ng-if="model.config.minNumber && model.config.maxNumber && model.config.minNumber === model.config.maxNumber">
<span ng-if="renderModel.length < model.config.maxNumber">Add {{model.config.minNumber - renderModel.length}} item(s)</span>
<span ng-if="renderModel.length > model.config.maxNumber">
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected"> items selected</localize>
</span>
</span>
<!-- Only max -->
<span ng-if="!model.config.minNumber && model.config.maxNumber">
<span ng-if="renderModel.length < model.config.maxNumber">Add up to {{model.config.maxNumber}} items</span>
<span ng-if="renderModel.length > model.config.maxNumber">
<localize key="validation_maxCount">You can only have</localize> {{model.config.maxNumber}} <localize key="validation_itemsSelected">items selected</localize>
</span>
</span>
<!-- Only min -->
<span ng-if="model.config.minNumber && !model.config.maxNumber && renderModel.length < model.config.minNumber">
Add at least {{model.config.minNumber}} item(s)
</span>
</div>
<!--These are here because we need ng-form fields to validate against-->
<input type="hidden" name="minCount" ng-model="renderModel" />
<input type="hidden" name="maxCount" ng-model="renderModel" />
@@ -1,81 +0,0 @@
angular.module("umbraco").controller("Umbraco.PropertyEditors.DropdownFlexibleController",
function($scope) {
//setup the default config
var config = {
items: [],
multiple: false
};
//map the user config
angular.extend(config, $scope.model.config);
//map back to the model
$scope.model.config = config;
function convertArrayToDictionaryArray(model){
//now we need to format the items in the dictionary because we always want to have an array
var newItems = [];
for (var i = 0; i < model.length; i++) {
newItems.push({ id: model[i], sortOrder: 0, value: model[i] });
}
return newItems;
}
function convertObjectToDictionaryArray(model){
//now we need to format the items in the dictionary because we always want to have an array
var newItems = [];
var vals = _.values($scope.model.config.items);
var keys = _.keys($scope.model.config.items);
for (var i = 0; i < vals.length; i++) {
var label = vals[i].value ? vals[i].value : vals[i];
newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: label });
}
return newItems;
}
$scope.updateSingleDropdownValue = function() {
$scope.model.value = [$scope.model.singleDropdownValue];
}
if (angular.isArray($scope.model.config.items)) {
//PP: I dont think this will happen, but we have tests that expect it to happen..
//if array is simple values, convert to array of objects
if(!angular.isObject($scope.model.config.items[0])){
$scope.model.config.items = convertArrayToDictionaryArray($scope.model.config.items);
}
}
else if (angular.isObject($scope.model.config.items)) {
$scope.model.config.items = convertObjectToDictionaryArray($scope.model.config.items);
}
else {
throw "The items property must be either an array or a dictionary";
}
//sort the values
$scope.model.config.items.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); });
//now we need to check if the value is null/undefined, if it is we need to set it to "" so that any value that is set
// to "" gets selected by default
if ($scope.model.value === null || $scope.model.value === undefined) {
if ($scope.model.config.multiple) {
$scope.model.value = [];
}
else {
$scope.model.value = "";
}
}
// if we run in single mode we'll store the value in a local variable
// so we can pass an array as the model as our PropertyValueEditor expects that
$scope.model.singleDropdownValue = "";
if ($scope.model.config.multiple === "0") {
$scope.model.singleDropdownValue = Array.isArray($scope.model.value) ? $scope.model.value[0] : $scope.model.value;
}
});
@@ -1,19 +0,0 @@
<div ng-controller="Umbraco.PropertyEditors.DropdownFlexibleController" ng-switch="model.config.multiple">
<select name="dropDownList"
class="umb-editor umb-dropdown"
ng-switch-default
ng-change="updateSingleDropdownValue()"
ng-model="model.singleDropdownValue"
ng-options="item.id as item.value for item in model.config.items">
<option></option>
</select>
<!--NOTE: This ng-switch is required because ng-multiple doesn't actually support dynamic bindings with multi-select lists -->
<select name="dropDownList"
class="umb-editor umb-dropdown"
ng-switch-when="1"
multiple
ng-model="model.value"
ng-options="item.id as item.value for item in model.config.items"></select>
</div>
@@ -130,9 +130,6 @@ function fileUploadController($scope, $element, $compile, imageHelper, fileManag
// in the description of this controller, it states that this value isn't actually used for persistence,
// but we need to set it so that the editor and the server can detect that it's been changed, and it is used for validation.
$scope.model.value = { selectedFiles: newVal.trimEnd(",") };
//need to explicity setDirty here as file upload field can't track dirty & we can't use the fileCount (hidden field/model)
$scope.propertyForm.$setDirty();
});
});
@@ -157,24 +154,26 @@ angular.module("umbraco")
.controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController)
.run(function(mediaHelper, umbRequestHelper, assetsService){
if (mediaHelper && mediaHelper.registerFileResolver) {
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
// they contain different data structures so if we need to query against it we need to be aware of this.
mediaHelper.registerFileResolver("Umbraco.UploadField", function(property, entity, thumbnail){
if (thumbnail) {
if (mediaHelper.detectIfImageByExtension(property.value)) {
//get default big thumbnail from image processor
var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first";
return thumbnailUrl;
}
else {
return null;
}
assetsService.load(["lib/moment/moment-with-locales.js"]).then(
function () {
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
// they contain different data structures so if we need to query against it we need to be aware of this.
mediaHelper.registerFileResolver("Umbraco.UploadField", function(property, entity, thumbnail){
if (thumbnail) {
if (mediaHelper.detectIfImageByExtension(property.value)) {
//get default big thumbnail from image processor
var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first";
return thumbnailUrl;
}
else {
return null;
}
}
else {
return property.value;
}
});
}
else {
return property.value;
}
});
);
}
});
@@ -31,6 +31,6 @@
</ul>
</div>
<input type="hidden" name="fileCount" ng-model="files.length" val-property-validator="validateMandatory" no-dirty-check />
<input type="hidden" name="fileCount" ng-model="files.length" val-property-validator="validateMandatory"/>
</div>
@@ -30,14 +30,15 @@
vm.dragLeave = dragLeave;
vm.onFilesQueue = onFilesQueue;
vm.onUploadComplete = onUploadComplete;
markAsSensitive();
function activate() {
if ($scope.entityType === 'media') {
mediaTypeHelper.getAllowedImagetypes(vm.nodeId).then(function (types) {
vm.acceptedMediatypes = types;
});
}
}
function selectAll($event) {
@@ -86,27 +87,6 @@
$scope.getContent($scope.contentId);
}
function markAsSensitive() {
angular.forEach($scope.options.includeProperties, function (option) {
option.isSensitive = false;
angular.forEach($scope.items,
function (item) {
angular.forEach(item.properties,
function (property) {
if (option.alias === property.alias) {
option.isSensitive = property.isSensitive;
}
});
});
});
}
activate();
}
@@ -46,7 +46,6 @@ function MarkdownEditorController($scope, $element, assetsService, dialogService
// init the md editor after this digest because the DOM needs to be ready first
// so run the init on a timeout
$timeout(function () {
$scope.markdownEditorInitComplete = false;
var converter2 = new Markdown.Converter();
var editor2 = new Markdown.Editor(converter2, "-" + $scope.model.alias);
editor2.run();
@@ -60,12 +59,7 @@ function MarkdownEditorController($scope, $element, assetsService, dialogService
editor2.hooks.set("onPreviewRefresh", function () {
// We must manually update the model as there is no way to hook into the markdown editor events without exstensive edits to the library.
if ($scope.model.value !== $("textarea", $element).val()) {
if ($scope.markdownEditorInitComplete) {
//only set dirty after init load to avoid "unsaved" dialogue when we don't want it
angularHelper.getCurrentForm($scope).$setDirty();
} else {
$scope.markdownEditorInitComplete = true;
}
angularHelper.getCurrentForm($scope).$setDirty();
$scope.model.value = $("textarea", $element).val();
}
});
@@ -185,12 +185,6 @@ angular.module("umbraco")
//cannot parse, we'll just leave it
}
}
if (val === "true") {
tinyMceConfig.customConfig[i] = true;
}
if (val === "false") {
tinyMceConfig.customConfig[i] = false;
}
}
}
@@ -365,8 +359,7 @@ angular.module("umbraco")
//this is instead of doing a watch on the model.value = faster
$scope.model.onValueChanged = function (newVal, oldVal) {
//update the display val again if it has changed from the server;
//uses an empty string in the editor when the value is null
tinyMceEditor.setContent(newVal || "", { format: 'raw' });
tinyMceEditor.setContent(newVal, { format: 'raw' });
//we need to manually fire this event since it is only ever fired based on loading from the DOM, this
// is required for our plugins listening to this event to execute
tinyMceEditor.fire('LoadContent', null);
@@ -92,6 +92,7 @@
$scope.model.config.ticksPositions = _.map($scope.model.config.ticksPositions.split(','), function (item) {
return parseInt(item.trim());
});
console.log($scope.model.config.ticksPositions);
}
if (!$scope.model.config.ticksLabels) {
@@ -214,4 +215,4 @@
assetsService.loadCss("lib/slider/bootstrap-slider.css");
assetsService.loadCss("lib/slider/bootstrap-slider-custom.css");
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.SliderController", sliderController);
angular.module("umbraco").controller("Umbraco.PropertyEditors.SliderController", sliderController);
@@ -99,4 +99,4 @@ module.exports = function (config) {
'karma-phantomjs-launcher'
]
});
};
};
+3 -15
View File
@@ -837,19 +837,6 @@
<Content Include="Views\Partials\Grid\Editors\Base.cshtml" />
<Content Include="Views\Partials\Grid\Bootstrap3-Fluid.cshtml" />
<Content Include="Views\Partials\Grid\Bootstrap2-Fluid.cshtml" />
<Content Include="Umbraco\Views\Preview\Background.cshtml" />
<Content Include="Umbraco\Views\Preview\Border.cshtml" />
<Content Include="Umbraco\Views\Preview\Color.cshtml" />
<Content Include="Umbraco\Views\Preview\Googlefontpicker.cshtml" />
<Content Include="Umbraco\Views\Preview\GridRow.cshtml" />
<Content Include="Umbraco\Views\Preview\Index.cshtml" />
<Content Include="Umbraco\Views\Preview\Layout.cshtml" />
<Content Include="Umbraco\Views\Preview\Margin.cshtml" />
<Content Include="Umbraco\Views\Preview\Padding.cshtml" />
<Content Include="Umbraco\Views\Preview\Radius.cshtml" />
<Content Include="Umbraco\Views\Preview\Shadow.cshtml" />
<Content Include="Umbraco\Views\Preview\Slider.cshtml" />
<Content Include="Umbraco\Views\Preview\web.config" />
<None Include="Web.Debug.config.transformed" />
<None Include="web.Template.Debug.config">
<DependentUpon>Web.Template.config</DependentUpon>
@@ -1007,6 +994,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
<Folder Include="Umbraco\preview\" />
<Folder Include="Views\MacroPartials\" />
</ItemGroup>
<ItemGroup>
@@ -1035,9 +1023,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7100</DevelopmentServerPort>
<DevelopmentServerPort>7910</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7100</IISUrl>
<IISUrl>http://localhost:7910</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -1,4 +0,0 @@
@inherits System.Web.Mvc.WebViewPage
<div class="box-slider">
<div colorpicker ng-model="item.values.color"></div>
</div>
@@ -1,41 +0,0 @@
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
</sectionGroup>
</configSections>
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
</system.web.webPages.razor>
<appSettings>
<add key="webpages:Enabled" value="false" />
</appSettings>
<system.webServer>
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>
<system.web>
<compilation>
<assemblies>
<add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
</system.web>
</configuration>
@@ -13,10 +13,8 @@
url += "&mode=crop";
}
}
var altText = Model.value.altText ?? Model.value.caption ?? string.Empty;
<img src="@url" alt="@altText">
<img src="@url" alt="@Model.value.caption">
if (Model.value.caption != null)
{
@@ -661,7 +661,7 @@
<area alias="graphicheadline">
<key alias="backgroundcolor">Baggrundsfarve</key>
<key alias="bold">Fed</key>
<key alias="color">Tekstfarve</key>
<key alias="color">Tekst farve</key>
<key alias="font">Skrifttype</key>
<key alias="text">Tekst</key>
</area>
@@ -1259,9 +1259,9 @@ Mange hilsner fra Umbraco robotten
<key alias="styles">Typografi</key>
<key alias="stylesDetails">Vælg hvilke typografiværdier en redaktør kan ændre</key>
<key alias="stylesDetails">Vælg, hvilke typografiværdier, en redaktør kan ændre</key>
<key alias="settingDialogDetails">Indstillinger gemmes kun, hvis den indtastede json-konfiguration er gyldig</key>
<key alias="settingDialogDetails">Indstillinger gemmes kun, hvis den indtaste json-konfiguration er gyldig</key>
<key alias="allowAllEditors">Tillad alle editorer</key>
<key alias="allowAllRowConfigurations">Tillad alle rækkekonfigurationer</key>
@@ -1272,7 +1272,7 @@ Mange hilsner fra Umbraco robotten
<key alias="areAdded">er tilføjet</key>
<key alias="maxItems">Maksimalt emner</key>
<key alias="maxItemsDescription">Efterlad blank eller sæt til 0 for ubegrænset</key>
<key alias="maxItemsDescription">Efterlad blank eller sat til 0 ubegrænset for</key>
</area>
<area alias="contentTypeEditor">
@@ -1282,7 +1282,7 @@ Mange hilsner fra Umbraco robotten
<key alias="addNewTab">Tilføj ny fane</key>
<key alias="addAnotherTab">Tilføj endnu en fane</key>
<key alias="inheritedFrom">Nedarvet fra</key>
<key alias="addProperty">Tilføj egenskab</key>
<key alias="addProperty">Tilføj property</key>
<key alias="requiredLabel">Påkrævet label</key>
<key alias="enableListViewHeading">Aktiver listevisning</key>
@@ -1473,6 +1473,7 @@ Mange hilsner fra Umbraco robotten
<key alias="noLogin">har endnu ikke logget ind</key>
<key alias="oldPassword">Gammelt kodeord</key>
<key alias="password">Adgangskode</key>
<key alias="removePhoto">Fjern billede</key>
<key alias="resetPassword">Nulstil kodeord</key>
<key alias="passwordChanged">Dit kodeord er blevet ændret!</key>
<key alias="passwordConfirm">Bekræft venligst dit nye kodeord</key>

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