Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 354505fde3 | |||
| 95d40049f5 | |||
| f003c3602c | |||
| e25d648f44 | |||
| 43e32e66b6 | |||
| 859cb6ce1a | |||
| bdaa70592f | |||
| 27b19a0a33 | |||
| 047a05cfb3 | |||
| e77ebc0c1c | |||
| a27ea994b7 | |||
| e019c94207 | |||
| 5b7193aa64 | |||
| f6263aad5b | |||
| 28a0e30315 | |||
| d1eb6b1aa8 | |||
| 458d023ff4 | |||
| 89c376f77a | |||
| 7c264b4da0 | |||
| 716020602d | |||
| 8c0ec489c2 | |||
| 81d380a47e | |||
| 19e5715a84 | |||
| 2e2dce170a | |||
| 59f9a64e81 | |||
| 98b7467306 | |||
| 4bb75d73dd | |||
| dd8e43d80a | |||
| c9c9e8352a | |||
| c4e5ab54d2 | |||
| 53bfec8a04 | |||
| b4a0dadee9 | |||
| 014ac98d0b | |||
| 35a79a8598 | |||
| 3c4f052a27 | |||
| 97d7e3db84 | |||
| 0d2df4de81 | |||
| b361cfebb7 | |||
| ebea5e8170 | |||
| 995f3f38a3 | |||
| 2ad79956d9 | |||
| 96c6a17d03 | |||
| e2e0dd0c73 | |||
| 1a590664e7 | |||
| 64fd6ee7c5 | |||
| 2ed5b6c1ee | |||
| 41c4e024de | |||
| b46add81f0 | |||
| ff31cd9f72 | |||
| 084af43dab | |||
| edf7038848 | |||
| bcac02861d | |||
| 79ecaecc9e | |||
| f604117c6d | |||
| dcf7f35078 | |||
| 2aa0dfb2c5 | |||
| e4b77416a6 | |||
| 3aed889e2a | |||
| 2c6e7e46c2 | |||
| 0a71a09d21 | |||
| 0c868fb72d | |||
| a96cbce9b1 | |||
| e3e425695f | |||
| b539c6ec27 | |||
| 69f7623fb7 | |||
| 82820284f3 | |||
| 5f3090c81e | |||
| f66e239584 | |||
| a465ed415d | |||
| 5161fd0cef | |||
| eac9785c45 | |||
| 054a8b48fa | |||
| af0a03a3e0 | |||
| fb5e6c22ce | |||
| 6bc68005f4 | |||
| dfbc3c477a | |||
| 57426ae895 | |||
| 05c38db54c | |||
| 00fdace570 | |||
| 4e531976e9 | |||
| 0feb1b6cc0 | |||
| 830f460b7e | |||
| 3e2fa64820 | |||
| 7db4f841e3 | |||
| 509d27f93b | |||
| f42b122e73 | |||
| b4aad19573 | |||
| 1b6c070752 | |||
| bedce100d2 | |||
| 0e08a56ada | |||
| 03012cf54d | |||
| 7ff339e18a | |||
| 0048b9c4c8 | |||
| 47f5e6bf17 | |||
| a8908c90bc | |||
| 9e5e8b539a | |||
| 45d292e196 | |||
| 6bafd3a254 | |||
| 564074a2bd | |||
| 4161d244c1 | |||
| 171e0d87db | |||
| eea87e411f | |||
| 89edfe5782 | |||
| cbd5071eed | |||
| 3dbc2944aa | |||
| 709dab6afd | |||
| 8749b030cf | |||
| 62fd15d54b | |||
| 66e71db85c | |||
| 469527da0a | |||
| 304e574bbc | |||
| 723644ace5 | |||
| 2b936a6d31 | |||
| 3121635e07 | |||
| f2a3a269ba | |||
| 557fc1bcdd | |||
| 617bdb2bd1 | |||
| 2c91f9bf14 | |||
| 1a5fe63638 | |||
| dfb0efbc98 | |||
| d399ad6e51 | |||
| eb06e62937 | |||
| 728bf8b52d | |||
| ca74100e12 | |||
| cd63806895 | |||
| 3061dc4e6f | |||
| 5166dc08d6 | |||
| 05a9c44e9e | |||
| 8e0565e030 | |||
| ceb3ae361a | |||
| 4cee7b831a | |||
| f146262031 | |||
| ddae0d01e3 | |||
| b9ee85dabc | |||
| 29fa6061a9 | |||
| 8ee61312b6 | |||
| cfd4b8c7bf | |||
| 7ce3b6046c | |||
| 43d98c9682 | |||
| 62e4825a5e | |||
| 81d35c6079 | |||
| 7e15077f96 | |||
| 6543e3c058 | |||
| 7c73c379cf | |||
| 4bc9d96bce | |||
| 45fd54d046 | |||
| 4f062306a6 | |||
| 4a8730b925 | |||
| 0beda4b790 | |||
| d94e7cd906 | |||
| 213ea6470f | |||
| dde8ba5617 | |||
| e783308cf0 | |||
| 01f49bb07e | |||
| d06b7edd84 | |||
| 466de5ee0b | |||
| 976e1e8a35 | |||
| a3d08c19c5 | |||
| 23638702c8 | |||
| a633e45b44 | |||
| 63b58dcd09 | |||
| 52684b6189 | |||
| 76a0c6c39f | |||
| 13c675d718 | |||
| 0ee3ebd180 | |||
| da0a1c808a | |||
| 9c8c9d8a39 | |||
| 37d2203de1 | |||
| 08965982fd | |||
| 732d3ef326 | |||
| 858156bab2 | |||
| 2ae96f3810 | |||
| 7019c9433b | |||
| b37197f66a | |||
| a5f689385a | |||
| 1607e51270 | |||
| 044b43f6e1 | |||
| b2004057a5 | |||
| c59a226d56 | |||
| 0c25c36af0 | |||
| ae110c1cae | |||
| 3f1f67e323 | |||
| f1d37a5072 | |||
| 2dc6e395a8 | |||
| eedeb90673 | |||
| 977c390a95 |
@@ -113,6 +113,7 @@ 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
|
||||
|
||||
+34
-1
@@ -24,6 +24,7 @@ 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)
|
||||
|
||||
@@ -114,6 +115,15 @@ 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.
|
||||
@@ -137,6 +147,29 @@ 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:
|
||||
@@ -171,4 +204,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!
|
||||
|
||||
@@ -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 Xloud, 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 Cloud, but you'll need to find a place to host yourself and handling deployments and upgrades is all down to you.
|
||||
|
||||
## Community
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ 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))
|
||||
@@ -37,7 +38,7 @@ function Get-UmbracoBuildEnv
|
||||
if (-not (test-path $sevenZip))
|
||||
{
|
||||
Write-Host "Download 7-Zip..."
|
||||
&$nuget install 7-Zip.CommandLine -OutputDirectory $path -Verbosity quiet
|
||||
&$nuget install 7-Zip.CommandLine -configFile "$src\NuGet.config" -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
|
||||
@@ -54,7 +55,7 @@ function Get-UmbracoBuildEnv
|
||||
if (-not (test-path $vswhere))
|
||||
{
|
||||
Write-Host "Download VsWhere..."
|
||||
&$nuget install vswhere -OutputDirectory $path -Verbosity quiet
|
||||
&$nuget install vswhere -configFile "$src\NuGet.config" -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
|
||||
@@ -70,7 +71,7 @@ function Get-UmbracoBuildEnv
|
||||
if (-not (test-path $semver))
|
||||
{
|
||||
Write-Host "Download Semver..."
|
||||
&$nuget install semver -OutputDirectory $path -Verbosity quiet
|
||||
&$nuget install semver -configFile "$src\NuGet.config" -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" > "$tmp\nuget.restore.log"
|
||||
&$uenv.NuGet restore "$src\Umbraco.sln" -configfile "$src\NuGet.config" > "$tmp\nuget.restore.log"
|
||||
}
|
||||
|
||||
#
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
<!-- 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>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
----------------------------------------------------
|
||||
|
||||
*** IMPORTANT NOTICE FOR 7.7 UPGRADES ***
|
||||
*** IMPORTANT NOTICE FOR UPGRADES FROM VERSIONS BELOW 7.7.0 ***
|
||||
|
||||
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
@@ -11,5 +11,5 @@ using System.Resources;
|
||||
|
||||
[assembly: AssemblyVersion("1.0.*")]
|
||||
|
||||
[assembly: AssemblyFileVersion("7.9.1")]
|
||||
[assembly: AssemblyInformationalVersion("7.9.1")]
|
||||
[assembly: AssemblyFileVersion("7.10.2")]
|
||||
[assembly: AssemblyInformationalVersion("7.10.2")]
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public class UmbracoVersion
|
||||
{
|
||||
private static readonly Version Version = new Version("7.9.1");
|
||||
private static readonly Version Version = new Version("7.10.2");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of Umbraco.
|
||||
|
||||
@@ -122,6 +122,11 @@ 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.
|
||||
@@ -452,4 +457,4 @@ namespace Umbraco.Core
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -459,6 +459,22 @@ 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,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Umbraco.Core.Configuration;
|
||||
@@ -26,6 +27,7 @@ namespace Umbraco.Core.IO
|
||||
private ShadowWrapper _xsltFileSystem;
|
||||
private ShadowWrapper _masterPagesFileSystem;
|
||||
private ShadowWrapper _mvcViewsFileSystem;
|
||||
private ShadowWrapper _javaScriptLibraryFileSystem;
|
||||
|
||||
#region Singleton & Constructor
|
||||
|
||||
@@ -113,6 +115,7 @@ 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);
|
||||
@@ -123,6 +126,7 @@ 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>();
|
||||
@@ -143,6 +147,7 @@ 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
|
||||
|
||||
@@ -203,7 +203,6 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
get
|
||||
{
|
||||
//by default the packages folder should exist in the data folder
|
||||
return IOHelper.ReturnPath("umbracoPreviewPath", Data + IOHelper.DirSepChar + "preview");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,8 +140,7 @@ namespace Umbraco.Core
|
||||
if (underlying != null)
|
||||
{
|
||||
// Special case for empty strings for bools/dates which should return null if an empty string.
|
||||
var inputString = input as string;
|
||||
if (inputString != null)
|
||||
if (input is string inputString)
|
||||
{
|
||||
//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)))
|
||||
@@ -168,8 +167,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
// target is not a generic type
|
||||
|
||||
var inputString = input as string;
|
||||
if (inputString != null)
|
||||
if (input is string inputString)
|
||||
{
|
||||
// Try convert from string, returns an Attempt if the string could be
|
||||
// processed (either succeeded or failed), else null if we need to try
|
||||
@@ -218,8 +216,7 @@ namespace Umbraco.Core
|
||||
}
|
||||
|
||||
// Re-check convertables since we altered the input through recursion
|
||||
var convertible2 = input as IConvertible;
|
||||
if (convertible2 != null)
|
||||
if (input is IConvertible convertible2)
|
||||
{
|
||||
return Attempt.Succeed(Convert.ChangeType(convertible2, target));
|
||||
}
|
||||
@@ -277,8 +274,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
if (target == typeof(int))
|
||||
{
|
||||
int value;
|
||||
if (int.TryParse(input, out value))
|
||||
if (int.TryParse(input, out var value))
|
||||
{
|
||||
return Attempt<object>.Succeed(value);
|
||||
}
|
||||
@@ -286,30 +282,26 @@ 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);
|
||||
decimal value2;
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt32(value2));
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt32(value2));
|
||||
}
|
||||
|
||||
if (target == typeof(long))
|
||||
{
|
||||
long value;
|
||||
if (long.TryParse(input, out value))
|
||||
if (long.TryParse(input, out var value))
|
||||
{
|
||||
return Attempt<object>.Succeed(value);
|
||||
}
|
||||
|
||||
// Same as int
|
||||
var input2 = NormalizeNumberDecimalSeparator(input);
|
||||
decimal value2;
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value2), Convert.ToInt64(value2));
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out var value2), Convert.ToInt64(value2));
|
||||
}
|
||||
|
||||
// TODO: Should we do the decimal trick for short, byte, unsigned?
|
||||
|
||||
if (target == typeof(bool))
|
||||
{
|
||||
bool value;
|
||||
if (bool.TryParse(input, out value))
|
||||
if (bool.TryParse(input, out var value))
|
||||
{
|
||||
return Attempt<object>.Succeed(value);
|
||||
}
|
||||
@@ -322,53 +314,42 @@ namespace Umbraco.Core
|
||||
switch (Type.GetTypeCode(target))
|
||||
{
|
||||
case TypeCode.Int16:
|
||||
short value;
|
||||
return Attempt<object>.SucceedIf(short.TryParse(input, out value), value);
|
||||
return Attempt<object>.SucceedIf(short.TryParse(input, out var value), value);
|
||||
|
||||
case TypeCode.Double:
|
||||
var input2 = NormalizeNumberDecimalSeparator(input);
|
||||
double valueD;
|
||||
return Attempt<object>.SucceedIf(double.TryParse(input2, out valueD), valueD);
|
||||
return Attempt<object>.SucceedIf(double.TryParse(input2, out var valueD), valueD);
|
||||
|
||||
case TypeCode.Single:
|
||||
var input3 = NormalizeNumberDecimalSeparator(input);
|
||||
float valueF;
|
||||
return Attempt<object>.SucceedIf(float.TryParse(input3, out valueF), valueF);
|
||||
return Attempt<object>.SucceedIf(float.TryParse(input3, out var valueF), valueF);
|
||||
|
||||
case TypeCode.Char:
|
||||
char valueC;
|
||||
return Attempt<object>.SucceedIf(char.TryParse(input, out valueC), valueC);
|
||||
return Attempt<object>.SucceedIf(char.TryParse(input, out var valueC), valueC);
|
||||
|
||||
case TypeCode.Byte:
|
||||
byte valueB;
|
||||
return Attempt<object>.SucceedIf(byte.TryParse(input, out valueB), valueB);
|
||||
return Attempt<object>.SucceedIf(byte.TryParse(input, out var valueB), valueB);
|
||||
|
||||
case TypeCode.SByte:
|
||||
sbyte valueSb;
|
||||
return Attempt<object>.SucceedIf(sbyte.TryParse(input, out valueSb), valueSb);
|
||||
return Attempt<object>.SucceedIf(sbyte.TryParse(input, out var valueSb), valueSb);
|
||||
|
||||
case TypeCode.UInt32:
|
||||
uint valueU;
|
||||
return Attempt<object>.SucceedIf(uint.TryParse(input, out valueU), valueU);
|
||||
return Attempt<object>.SucceedIf(uint.TryParse(input, out var valueU), valueU);
|
||||
|
||||
case TypeCode.UInt16:
|
||||
ushort valueUs;
|
||||
return Attempt<object>.SucceedIf(ushort.TryParse(input, out valueUs), valueUs);
|
||||
return Attempt<object>.SucceedIf(ushort.TryParse(input, out var valueUs), valueUs);
|
||||
|
||||
case TypeCode.UInt64:
|
||||
ulong valueUl;
|
||||
return Attempt<object>.SucceedIf(ulong.TryParse(input, out valueUl), valueUl);
|
||||
return Attempt<object>.SucceedIf(ulong.TryParse(input, out var valueUl), valueUl);
|
||||
}
|
||||
}
|
||||
else if (target == typeof(Guid))
|
||||
{
|
||||
Guid value;
|
||||
return Attempt<object>.SucceedIf(Guid.TryParse(input, out value), value);
|
||||
return Attempt<object>.SucceedIf(Guid.TryParse(input, out var value), value);
|
||||
}
|
||||
else if (target == typeof(DateTime))
|
||||
{
|
||||
DateTime value;
|
||||
if (DateTime.TryParse(input, out value))
|
||||
if (DateTime.TryParse(input, out var value))
|
||||
{
|
||||
switch (value.Kind)
|
||||
{
|
||||
@@ -388,24 +369,20 @@ namespace Umbraco.Core
|
||||
}
|
||||
else if (target == typeof(DateTimeOffset))
|
||||
{
|
||||
DateTimeOffset value;
|
||||
return Attempt<object>.SucceedIf(DateTimeOffset.TryParse(input, out value), value);
|
||||
return Attempt<object>.SucceedIf(DateTimeOffset.TryParse(input, out var value), value);
|
||||
}
|
||||
else if (target == typeof(TimeSpan))
|
||||
{
|
||||
TimeSpan value;
|
||||
return Attempt<object>.SucceedIf(TimeSpan.TryParse(input, out value), value);
|
||||
return Attempt<object>.SucceedIf(TimeSpan.TryParse(input, out var value), value);
|
||||
}
|
||||
else if (target == typeof(decimal))
|
||||
{
|
||||
var input2 = NormalizeNumberDecimalSeparator(input);
|
||||
decimal value;
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out value), value);
|
||||
return Attempt<object>.SucceedIf(decimal.TryParse(input2, out var value), value);
|
||||
}
|
||||
else if (input != null && target == typeof(Version))
|
||||
{
|
||||
Version value;
|
||||
return Attempt<object>.SucceedIf(Version.TryParse(input, out value), value);
|
||||
return Attempt<object>.SucceedIf(Version.TryParse(input, out var value), value);
|
||||
}
|
||||
|
||||
// E_NOTIMPL IPAddress, BigInteger
|
||||
@@ -690,8 +667,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
|
||||
TypeConverter typeConverter;
|
||||
if (InputTypeConverterCache.TryGetValue(key, out typeConverter))
|
||||
if (InputTypeConverterCache.TryGetValue(key, out TypeConverter typeConverter))
|
||||
{
|
||||
return typeConverter;
|
||||
}
|
||||
@@ -711,8 +687,7 @@ namespace Umbraco.Core
|
||||
{
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
|
||||
TypeConverter typeConverter;
|
||||
if (DestinationTypeConverterCache.TryGetValue(key, out typeConverter))
|
||||
if (DestinationTypeConverterCache.TryGetValue(key, out TypeConverter typeConverter))
|
||||
{
|
||||
return typeConverter;
|
||||
}
|
||||
@@ -730,8 +705,7 @@ namespace Umbraco.Core
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static Type GetCachedGenericNullableType(Type type)
|
||||
{
|
||||
Type underlyingType;
|
||||
if (NullableGenericCache.TryGetValue(type, out underlyingType))
|
||||
if (NullableGenericCache.TryGetValue(type, out Type underlyingType))
|
||||
{
|
||||
return underlyingType;
|
||||
}
|
||||
@@ -750,8 +724,7 @@ namespace Umbraco.Core
|
||||
private static bool GetCachedCanAssign(object input, Type source, Type target)
|
||||
{
|
||||
var key = new CompositeTypeTypeKey(source, target);
|
||||
bool canConvert;
|
||||
if (AssignableTypeCache.TryGetValue(key, out canConvert))
|
||||
if (AssignableTypeCache.TryGetValue(key, out bool canConvert))
|
||||
{
|
||||
return canConvert;
|
||||
}
|
||||
@@ -770,8 +743,7 @@ namespace Umbraco.Core
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static bool GetCachedCanConvertToBoolean(Type type)
|
||||
{
|
||||
bool result;
|
||||
if (BoolConvertCache.TryGetValue(type, out result))
|
||||
if (BoolConvertCache.TryGetValue(type, out bool result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
+17
-7
@@ -6,6 +6,7 @@ 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
|
||||
@@ -343,15 +344,24 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
|
||||
if (tables.InvariantContains("umbracoUserType") && tables.InvariantContains("umbracoUser"))
|
||||
{
|
||||
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_umbracoUser_umbracoUserType_id")))
|
||||
if (Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
|
||||
{
|
||||
Delete.ForeignKey("FK_umbracoUser_umbracoUserType_id").OnTable("umbracoUser");
|
||||
//In MySql, this will drop the FK according to it's special naming rules
|
||||
Delete.ForeignKey().FromTable("umbracoUser").ForeignColumn("userType").ToTable("umbracoUserType").PrimaryColumn("id");
|
||||
}
|
||||
//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")))
|
||||
else
|
||||
{
|
||||
Delete.ForeignKey("FK_user_userType").OnTable("umbracoUser");
|
||||
}
|
||||
//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.Column("userType").FromTable("umbracoUser");
|
||||
Delete.Table("umbracoUserType");
|
||||
@@ -361,4 +371,4 @@ namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenSevenZe
|
||||
public override void Down()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
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,4 +1,5 @@
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using System;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
{
|
||||
@@ -14,19 +15,7 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
|
||||
public override object ConvertDataToSource(PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
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;
|
||||
return source.TryConvertTo<int>().Result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,23 +222,19 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,8 +249,8 @@ namespace Umbraco.Core.Security
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected internal UserData UserData { get; private set; }
|
||||
@@ -332,4 +328,4 @@ namespace Umbraco.Core.Security
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -22,13 +23,139 @@ 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)
|
||||
{
|
||||
TypedId<int> id;
|
||||
bool empty;
|
||||
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
if (_key2Id.TryGetValue(key, out id) && id.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(id.Id);
|
||||
if (_key2Id.TryGetValue(key, out var id) && id.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(id.Id);
|
||||
empty = _key2Id.Count == 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -36,20 +163,37 @@ namespace Umbraco.Core.Services
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
|
||||
int? val;
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
#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)
|
||||
{
|
||||
//if it's unknown don't include the nodeObjectType in the query
|
||||
if (umbracoObjectType == UmbracoObjectTypes.Unknown)
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id", new { id = key});
|
||||
//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();
|
||||
}
|
||||
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();
|
||||
@@ -83,13 +227,23 @@ 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)
|
||||
{
|
||||
TypedId<Guid> key;
|
||||
bool empty;
|
||||
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
if (_id2Key.TryGetValue(id, out key) && key.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(key.Id);
|
||||
if (_id2Key.TryGetValue(id, out var key) && key.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(key.Id);
|
||||
empty = _id2Key.Count == 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -97,20 +251,37 @@ namespace Umbraco.Core.Services
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
|
||||
Guid? val;
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
#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)
|
||||
{
|
||||
//if it's unknown don't include the nodeObjectType in the query
|
||||
if (umbracoObjectType == UmbracoObjectTypes.Unknown)
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id", new { id });
|
||||
//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();
|
||||
}
|
||||
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();
|
||||
@@ -142,6 +313,8 @@ namespace Umbraco.Core.Services
|
||||
return guid;
|
||||
}
|
||||
|
||||
// invoked on UnpublishedPageCacheRefresher.RefreshAll
|
||||
// anything else will use the id-specific overloads
|
||||
public void ClearCache()
|
||||
{
|
||||
try
|
||||
@@ -162,8 +335,7 @@ namespace Umbraco.Core.Services
|
||||
try
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
TypedId<Guid> key;
|
||||
if (_id2Key.TryGetValue(id, out key) == false) return;
|
||||
if (_id2Key.TryGetValue(id, out var key) == false) return;
|
||||
_id2Key.Remove(id);
|
||||
_key2Id.Remove(key.Id);
|
||||
}
|
||||
@@ -179,8 +351,7 @@ namespace Umbraco.Core.Services
|
||||
try
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
TypedId<int> id;
|
||||
if (_key2Id.TryGetValue(key, out id) == false) return;
|
||||
if (_key2Id.TryGetValue(key, out var id) == false) return;
|
||||
_id2Key.Remove(id.Id);
|
||||
_key2Id.Remove(key);
|
||||
}
|
||||
@@ -191,26 +362,28 @@ 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>
|
||||
{
|
||||
private readonly T _id;
|
||||
private readonly UmbracoObjectTypes _umbracoObjectType;
|
||||
|
||||
public T Id
|
||||
{
|
||||
get { return _id; }
|
||||
}
|
||||
|
||||
public UmbracoObjectTypes UmbracoObjectType
|
||||
{
|
||||
get { return _umbracoObjectType; }
|
||||
}
|
||||
|
||||
public TypedId(T id, UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
_umbracoObjectType = umbracoObjectType;
|
||||
_id = id;
|
||||
UmbracoObjectType = umbracoObjectType;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public UmbracoObjectTypes UmbracoObjectType { get; }
|
||||
|
||||
public T Id { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,20 +207,17 @@ namespace Umbraco.Core.Services
|
||||
{
|
||||
return repository.GetByUsername(username, includeSecurityData: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (DbException ex)
|
||||
{
|
||||
if (ex is SqlException || ex is SqlCeException)
|
||||
//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)
|
||||
{
|
||||
//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);
|
||||
}
|
||||
//NOTE: this will not be cached
|
||||
return repository.GetByUsername(username, includeSecurityData: false);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
@@ -789,20 +786,17 @@ namespace Umbraco.Core.Services
|
||||
var result = repository.Get(id);
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (DbException ex)
|
||||
{
|
||||
if (ex is SqlException || ex is SqlCeException)
|
||||
//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)
|
||||
{
|
||||
//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);
|
||||
}
|
||||
//NOTE: this will not be cached
|
||||
return repository.Get(id, includeSecurityData: false);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
@@ -115,6 +115,9 @@
|
||||
<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" />
|
||||
@@ -603,6 +606,7 @@
|
||||
<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" />
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
@@ -142,10 +143,18 @@ namespace Umbraco.Core
|
||||
/// <returns></returns>
|
||||
internal static bool IsClientSideRequest(this Uri url)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -311,4 +320,4 @@ namespace Umbraco.Core
|
||||
return new Uri(uri.GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,4 +18,5 @@
|
||||
<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.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
|
||||
<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.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
|
||||
@@ -62,6 +62,10 @@
|
||||
<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>
|
||||
@@ -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.3.0" targetFramework="net46" />
|
||||
<package id="System.ValueTuple" version="4.4.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" />
|
||||
|
||||
@@ -93,53 +93,59 @@ 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;
|
||||
|
||||
//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.
|
||||
// //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);
|
||||
|
||||
//[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);
|
||||
// //need to create a template with id 1045
|
||||
// var template = Template.MakeNew("test", new User(0));
|
||||
|
||||
// StateHelper.HttpContext = httpContext;
|
||||
// SetupUmbracoContextForTest(umbracoContext, template);
|
||||
|
||||
// //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);
|
||||
// _module.AssignDocumentRequest(httpContext, umbracoContext, httpContext.Request.Url);
|
||||
|
||||
// //need to create a template with id 1045
|
||||
// var template = Template.MakeNew("test", new User(0));
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.UI;
|
||||
|
||||
@@ -42,7 +42,12 @@
|
||||
"codemirror"
|
||||
],
|
||||
"sources": {
|
||||
"moment": "bower_components/moment/min/moment-with-locales.js",
|
||||
"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"
|
||||
],
|
||||
"underscore": [
|
||||
"bower_components/underscore/underscore-min.js",
|
||||
"bower_components/underscore/underscore-min.map"
|
||||
|
||||
@@ -1,605 +0,0 @@
|
||||
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');
|
||||
};
|
||||
@@ -73,7 +73,6 @@ 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" },
|
||||
@@ -85,8 +84,7 @@ var sources = {
|
||||
//selectors for copying all views into the build
|
||||
//processed in the views task
|
||||
views:{
|
||||
umbraco: {files: ["src/views/**/*html"], folder: ""},
|
||||
preview: { files: ["src/canvasdesigner/**/*.html"], folder: "../preview"},
|
||||
umbraco: {files: ["src/views/**/*.html"], folder: ""},
|
||||
installer: {files: ["src/installer/steps/*.html"], folder: "install"}
|
||||
},
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-connect": "5.0.0",
|
||||
"gulp-less": "^3.1.0",
|
||||
"gulp-less": "^3.5.0",
|
||||
"gulp-ngdocs": "^0.3.0",
|
||||
"gulp-open": "^2.0.0",
|
||||
"gulp-open": "^2.1.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.0",
|
||||
"karma-jasmine": "^1.1.1",
|
||||
"karma-phantomjs-launcher": "^1.0.4",
|
||||
"less": "^2.6.1",
|
||||
"lodash": "^4.16.3",
|
||||
"less": "^2.7.3",
|
||||
"lodash": "^4.17.5",
|
||||
"merge-stream": "^1.0.1",
|
||||
"run-sequence": "^2.1.0"
|
||||
"run-sequence": "^2.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 386 KiB After Width: | Height: | Size: 180 KiB |
@@ -1,3 +0,0 @@
|
||||
<div class="box-slider">
|
||||
<div colorpicker ng-model="item.values.color"></div>
|
||||
</div>
|
||||
+1
-3
@@ -255,11 +255,9 @@
|
||||
else {
|
||||
$scope.save().then(function (data) {
|
||||
previewWindow.location.href = redirect;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.restore = function (content) {
|
||||
|
||||
+3
-1
@@ -8,6 +8,8 @@
|
||||
var evts = [];
|
||||
var isInfoTab = false;
|
||||
scope.publishStatus = {};
|
||||
|
||||
scope.disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
|
||||
|
||||
function onInit() {
|
||||
|
||||
@@ -50,7 +52,7 @@
|
||||
|
||||
scope.openDocumentType = function (documentType) {
|
||||
var url = "/settings/documenttypes/edit/" + documentType.id;
|
||||
$location.path(url);
|
||||
$location.url(url);
|
||||
};
|
||||
|
||||
scope.updateTemplate = function (templateAlias) {
|
||||
|
||||
@@ -145,6 +145,12 @@ angular.module("umbraco.directives")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (val === "true") {
|
||||
tinyMceConfig.customConfig[i] = true;
|
||||
}
|
||||
if (val === "false") {
|
||||
tinyMceConfig.customConfig[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
angular.extend(baseLineConfigObj, tinyMceConfig.customConfig);
|
||||
|
||||
+5
-3
@@ -59,7 +59,6 @@
|
||||
</pre>
|
||||
|
||||
<h1>General Options</h1>
|
||||
Lorem ipsum dolor sit amet..
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -74,7 +73,7 @@ Lorem ipsum dolor sit amet..
|
||||
<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>
|
||||
@@ -496,6 +495,7 @@ 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,7 +503,9 @@ Opens an overlay to show a custom YSOD. </br>
|
||||
} else if(activeElementType === "TEXTAREA" && !submitOnEnter) {
|
||||
|
||||
|
||||
} else {
|
||||
} else if (submitOnEnter && submitOnEnterValue === "false") {
|
||||
// don't do anything
|
||||
}else {
|
||||
scope.$apply(function () {
|
||||
scope.submitForm(scope.model);
|
||||
});
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@
|
||||
function link(scope, el, attr, ngModel) {
|
||||
|
||||
// Load in ace library
|
||||
assetsService.load(['lib/ace-builds/src-min-noconflict/ace.js', 'lib/ace-builds/src-min-noconflict/ext-language_tools.js']).then(function () {
|
||||
assetsService.load(['lib/ace-builds/src-min-noconflict/ace.js', 'lib/ace-builds/src-min-noconflict/ext-language_tools.js'], scope).then(function () {
|
||||
if (angular.isUndefined(window.ace)) {
|
||||
throw new Error('ui-ace need ace to work... (o rly?)');
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@
|
||||
var clipboard;
|
||||
var target = element[0];
|
||||
|
||||
assetsService.loadJs("lib/clipboard/clipboard.min.js")
|
||||
assetsService.loadJs("lib/clipboard/clipboard.min.js", scope)
|
||||
.then(function () {
|
||||
|
||||
if(scope.umbClipboardTarget) {
|
||||
|
||||
+2
-2
@@ -92,10 +92,10 @@ Use this directive to render a date time picker
|
||||
scope.hasTranscludedContent = element.find('.js-datePicker__transcluded-content')[0].children.length > 0;
|
||||
|
||||
// load css file for the date picker
|
||||
assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css');
|
||||
assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css', scope);
|
||||
|
||||
// load the js file for the date picker
|
||||
assetsService.loadJs('lib/datetimepicker/bootstrap-datetimepicker.js').then(function () {
|
||||
assetsService.loadJs('lib/datetimepicker/bootstrap-datetimepicker.js', scope).then(function () {
|
||||
// init date picker
|
||||
initDatePicker();
|
||||
});
|
||||
|
||||
+6
-6
@@ -10,12 +10,12 @@ function noDirtyCheck() {
|
||||
require: 'ngModel',
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
|
||||
elm.focus(function () {
|
||||
scope.$watch(function() {
|
||||
ctrl.$pristine = false;
|
||||
});
|
||||
});
|
||||
|
||||
var alwaysFalse = {
|
||||
get: function () { return false; },
|
||||
set: function () { }
|
||||
};
|
||||
Object.defineProperty(ctrl, '$pristine', alwaysFalse);
|
||||
Object.defineProperty(ctrl, '$dirty', alwaysFalse);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@@ -129,14 +129,12 @@ angular.module('umbraco.services')
|
||||
asset.state = "loading";
|
||||
LazyLoad.css(appendRnd(path), function () {
|
||||
if (!scope) {
|
||||
asset.state = "loaded";
|
||||
asset.deferred.resolve(true);
|
||||
} else {
|
||||
asset.state = "loaded";
|
||||
angularHelper.safeApply(scope, function () {
|
||||
asset.deferred.resolve(true);
|
||||
});
|
||||
scope = $rootScope;
|
||||
}
|
||||
asset.state = "loaded";
|
||||
angularHelper.safeApply(scope, function () {
|
||||
asset.deferred.resolve(true);
|
||||
});
|
||||
});
|
||||
} else if (asset.state === "loaded") {
|
||||
asset.deferred.resolve(true);
|
||||
@@ -171,14 +169,12 @@ angular.module('umbraco.services')
|
||||
|
||||
LazyLoad.js(appendRnd(path), function () {
|
||||
if (!scope) {
|
||||
asset.state = "loaded";
|
||||
asset.deferred.resolve(true);
|
||||
} else {
|
||||
asset.state = "loaded";
|
||||
angularHelper.safeApply(scope, function () {
|
||||
asset.deferred.resolve(true);
|
||||
});
|
||||
scope = $rootScope;
|
||||
}
|
||||
asset.state = "loaded";
|
||||
angularHelper.safeApply(scope, function () {
|
||||
asset.deferred.resolve(true);
|
||||
});
|
||||
});
|
||||
|
||||
} else if (asset.state === "loaded") {
|
||||
@@ -234,8 +230,7 @@ angular.module('umbraco.services')
|
||||
assets.push(asset);
|
||||
}
|
||||
|
||||
//we need to always push to the promises collection to monitor correct
|
||||
//execution
|
||||
//we need to always push to the promises collection to monitor correct execution
|
||||
promises.push(asset.deferred.promise);
|
||||
}
|
||||
});
|
||||
@@ -256,8 +251,7 @@ angular.module('umbraco.services')
|
||||
function assetLoaded(asset) {
|
||||
asset.state = "loaded";
|
||||
if (!scope) {
|
||||
asset.deferred.resolve(true);
|
||||
return;
|
||||
scope = $rootScope;
|
||||
}
|
||||
angularHelper.safeApply(scope,
|
||||
function () {
|
||||
@@ -280,4 +274,4 @@ angular.module('umbraco.services')
|
||||
};
|
||||
|
||||
return service;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
(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);
|
||||
|
||||
})();
|
||||
@@ -288,9 +288,12 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
|
||||
throw "args.tree cannot be null";
|
||||
}
|
||||
|
||||
if (mainTreeEventHandler) {
|
||||
//returns a promise
|
||||
return mainTreeEventHandler.syncTree(args);
|
||||
if (mainTreeEventHandler) {
|
||||
|
||||
if (mainTreeEventHandler.syncTree) {
|
||||
//returns a promise,
|
||||
return mainTreeEventHandler.syncTree(args);
|
||||
}
|
||||
}
|
||||
|
||||
//couldn't sync
|
||||
|
||||
@@ -1,285 +1,325 @@
|
||||
angular.module('umbraco.services')
|
||||
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, dialogService, $timeout, angularHelper, $http) {
|
||||
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, assetsService, dialogService, $timeout, angularHelper, $http, javascriptLibraryService) {
|
||||
|
||||
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
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
template: 'views/common/dialogs/login.html',
|
||||
modalClass: "login-overlay",
|
||||
animation: "slide",
|
||||
show: true,
|
||||
callback: onLoginDialogClose,
|
||||
dialogData: {
|
||||
isTimedOut: isTimedOut
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//recurse the countdown!
|
||||
countdownUserTimeout();
|
||||
}
|
||||
else {
|
||||
function onLoginDialogClose(success) {
|
||||
loginDialog = null;
|
||||
|
||||
//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();
|
||||
}
|
||||
});
|
||||
if (success) {
|
||||
securityRetryQueue.retryAll(currentUser.name);
|
||||
}
|
||||
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();
|
||||
|
||||
securityRetryQueue.cancelAll();
|
||||
$location.path('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 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;
|
||||
/**
|
||||
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();
|
||||
}
|
||||
return authResource.isAuthenticated();
|
||||
},
|
||||
|
||||
/** Returns a promise, sends a request to the server to validate the credentials */
|
||||
authenticate: function (login, password) {
|
||||
/**
|
||||
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() {
|
||||
|
||||
return authResource.performLogin(login, password)
|
||||
.then(this.setAuthenticationSuccessful);
|
||||
},
|
||||
setAuthenticationSuccessful: function (data) {
|
||||
$timeout(function () {
|
||||
|
||||
//when it's successful, return the user data
|
||||
setCurrentUser(data);
|
||||
if (currentUser) {
|
||||
//countdown by 5 seconds since that is how long our timer is for.
|
||||
currentUser.remainingAuthSeconds -= 5;
|
||||
|
||||
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
|
||||
//if there are more than 30 remaining seconds, recurse!
|
||||
if (currentUser.remainingAuthSeconds > 30) {
|
||||
|
||||
//broadcast a global event
|
||||
eventsService.emit("app.authenticated", result);
|
||||
return result;
|
||||
},
|
||||
//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;
|
||||
|
||||
/** Logs the user out
|
||||
*/
|
||||
logout: function () {
|
||||
if (seconds > 30) {
|
||||
|
||||
return authResource.performLogout()
|
||||
.then(function (data) {
|
||||
userAuthExpired();
|
||||
//done!
|
||||
return 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;
|
||||
|
||||
/** Refreshes the current user data with the data stored for the user on the server and returns it */
|
||||
refreshCurrentUser: function() {
|
||||
var deferred = $q.defer();
|
||||
//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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
authResource.getCurrentUser()
|
||||
.then(function (data) {
|
||||
//recurse the countdown!
|
||||
countdownUserTimeout();
|
||||
}
|
||||
else {
|
||||
|
||||
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
|
||||
|
||||
setCurrentUser(data);
|
||||
//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
|
||||
|
||||
deferred.resolve(currentUser);
|
||||
}, function () {
|
||||
//it failed, so they are not logged in
|
||||
deferred.reject();
|
||||
});
|
||||
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;
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
//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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns the current user object in a promise */
|
||||
getCurrentUser: function (args) {
|
||||
var deferred = $q.defer();
|
||||
//recurse the countdown!
|
||||
countdownUserTimeout();
|
||||
|
||||
if (!currentUser) {
|
||||
authResource.getCurrentUser()
|
||||
.then(function (data) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 5000, //every 5 seconds
|
||||
false); //false = do NOT execute a digest for every iteration
|
||||
}
|
||||
|
||||
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
|
||||
/** Called to update the current user's timeout */
|
||||
function setUserTimeoutInternal(newTimeout) {
|
||||
|
||||
if (args && args.broadcastEvent) {
|
||||
//broadcast a global event, will inform listening controllers to load in the user specific data
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
setCurrentUser(data);
|
||||
/** Logs the user out
|
||||
*/
|
||||
logout: function () {
|
||||
|
||||
deferred.resolve(currentUser);
|
||||
}, function () {
|
||||
//it failed, so they are not logged in
|
||||
deferred.reject();
|
||||
});
|
||||
return authResource.performLogout()
|
||||
.then(function (data) {
|
||||
userAuthExpired();
|
||||
//done!
|
||||
return null;
|
||||
});
|
||||
},
|
||||
|
||||
}
|
||||
else {
|
||||
deferred.resolve(currentUser);
|
||||
}
|
||||
/** Refreshes the current user data with the data stored for the user on the server and returns it */
|
||||
refreshCurrentUser: function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
authResource.getCurrentUser()
|
||||
.then(function (data) {
|
||||
|
||||
/** 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);
|
||||
}
|
||||
};
|
||||
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 () {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return assetsService.load(localeUrls, $rootScope);
|
||||
}
|
||||
else {
|
||||
//return a noop promise
|
||||
var deferred = $q.defer();
|
||||
var promise = deferred.promise;
|
||||
deferred.resolve(true);
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
var promises = {
|
||||
currentUser: this.getCurrentUser(),
|
||||
supportedLocales: javascriptLibraryService.getSupportedLocalesForMoment()
|
||||
}
|
||||
|
||||
return $q.all(promises).then(function (values) {
|
||||
return loadLocales(values.currentUser, values.supportedLocales);
|
||||
});
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
/** 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);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -18,23 +18,26 @@ app.run(['userService', '$log', '$rootScope', '$location', 'queryStrings', 'navi
|
||||
eventsService.on("app.authenticated", function(evt, data) {
|
||||
|
||||
assetsService._loadInitAssets().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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
}, function(){
|
||||
appReady(data);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -168,6 +168,9 @@
|
||||
//used for property editors
|
||||
@import "property-editors.less";
|
||||
|
||||
//used for prevalue editors
|
||||
@import "components/prevalues/multivalues.less";
|
||||
|
||||
|
||||
@import "typeahead.less";
|
||||
@import "hacks.less";
|
||||
@@ -175,4 +178,4 @@
|
||||
@import "healthcheck.less";
|
||||
|
||||
// cleanup properties.less when it is done
|
||||
@import "properties.less";
|
||||
@import "properties.less";
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
.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,7 +280,6 @@
|
||||
.umb-grid .umb-control {
|
||||
position: relative;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
|
||||
@@ -40,6 +40,16 @@
|
||||
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) {
|
||||
@@ -109,22 +119,22 @@ ul.color-picker li a {
|
||||
}
|
||||
|
||||
/* pre-value editor */
|
||||
/*.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:before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}*/
|
||||
.control-group.color-picker-preval div.color-picker-prediv {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
}*/
|
||||
|
||||
/*.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;
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.control-group.color-picker-preval pre {
|
||||
display: inline;
|
||||
margin-right: 20px;
|
||||
@@ -136,18 +146,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;
|
||||
@@ -203,10 +213,22 @@ ul.color-picker li a {
|
||||
|
||||
|
||||
|
||||
.umb-thumbnails{
|
||||
position: relative;
|
||||
.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 > li.icon {
|
||||
width: 14%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.umb-thumbnails i{margin: auto;}
|
||||
.umb-thumbnails a{
|
||||
|
||||
@@ -100,6 +100,27 @@
|
||||
.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
|
||||
// -------------------------
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.Dialogs.ApprovedColorPickerController", function ($scope, $http, umbPropEditorHelper, assetsService) {
|
||||
assetsService.loadJs("lib/cssparser/cssparser.js")
|
||||
assetsService.loadJs("lib/cssparser/cssparser.js", $scope)
|
||||
.then(function () {
|
||||
|
||||
var cssPath = $scope.dialogData.cssPath;
|
||||
@@ -19,7 +19,7 @@ angular.module("umbraco")
|
||||
$scope.classes.splice(0, 0, "noclass");
|
||||
})
|
||||
|
||||
assetsService.loadCss("/App_Plugins/Lecoati.uSky.Grid/lib/uSky.Grid.ApprovedColorPicker.css");
|
||||
assetsService.loadCss(cssPath);
|
||||
assetsService.loadCss("/App_Plugins/Lecoati.uSky.Grid/lib/uSky.Grid.ApprovedColorPicker.css", $scope);
|
||||
assetsService.loadCss(cssPath, $scope);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,14 +7,13 @@ 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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
);
|
||||
@@ -16,24 +16,25 @@
|
||||
|
||||
<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-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>
|
||||
<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>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
</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>
|
||||
@@ -42,8 +41,6 @@
|
||||
<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,27 +171,25 @@
|
||||
{{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 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 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)">
|
||||
<option value=""><localize key="general_choose">Choose</localize>...</option>
|
||||
</select>
|
||||
</umb-control-group>
|
||||
@@ -200,7 +198,7 @@
|
||||
<div>{{ node.id }}</div>
|
||||
<small>{{ node.key }}</small>
|
||||
</umb-control-group>
|
||||
|
||||
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
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' hotkey="esc" ng-click="removeNotification($index)" prevent-default href>×</a>
|
||||
<a class='close -align-right' ng-click="removeNotification($index)" prevent-default href>×</a>
|
||||
|
||||
<div ng-if="notification.view">
|
||||
<div ng-include="notification.view"></div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<div data-element="overlay" class="umb-overlay umb-overlay-{{position}}" hotkey="esc" on-outside-click="closeOverLay()">
|
||||
|
||||
<div data-element="overlay" class="umb-overlay umb-overlay-{{position}}" 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" hotkey="ctrl+{{$index+1}}" href="#tab{{tab.id}}{{idSuffix}}">{{ tab.label }}</a>
|
||||
<a data-toggle="tab" href="#tab{{tab.id}}{{idSuffix}}">{{ tab.label }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -47,7 +47,17 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="umb-table-cell" ng-repeat="column in itemProperties">
|
||||
<span title="{{column.header}}: {{item[column.alias]}}">{{item[column.alias]}}</span>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@ function startUpVideosDashboardController($scope, xmlhelper, $log, $http) {
|
||||
angular.module("umbraco").controller("Umbraco.Dashboard.StartupVideosController", startUpVideosDashboardController);
|
||||
|
||||
|
||||
function startUpDynamicContentController($timeout, dashboardResource, assetsService, tourService, eventsService) {
|
||||
function startUpDynamicContentController($timeout, $scope, dashboardResource, assetsService, tourService, eventsService) {
|
||||
var vm = this;
|
||||
var evts = [];
|
||||
|
||||
@@ -95,7 +95,7 @@ function startUpDynamicContentController($timeout, dashboardResource, assetsServ
|
||||
}));
|
||||
|
||||
//proxy remote css through the local server
|
||||
assetsService.loadCss( dashboardResource.getRemoteDashboardCssUrl("content") );
|
||||
assetsService.loadCss(dashboardResource.getRemoteDashboardCssUrl("content"), $scope);
|
||||
dashboardResource.getRemoteDashboardContent("content").then(
|
||||
function (data) {
|
||||
|
||||
|
||||
@@ -14,18 +14,21 @@ 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();
|
||||
|
||||
@@ -44,7 +47,7 @@ function DocumentTypesCreateController($scope, $location, navigationService, con
|
||||
|
||||
var section = appState.getSectionState("currentSection");
|
||||
|
||||
}, function(err) {
|
||||
}, function (err) {
|
||||
|
||||
$scope.error = err;
|
||||
|
||||
@@ -58,14 +61,17 @@ function DocumentTypesCreateController($scope, $location, navigationService, con
|
||||
}
|
||||
};
|
||||
|
||||
$scope.createDocType = function() {
|
||||
$location.search('create', null);
|
||||
$location.search('notemplate', null);
|
||||
$location.path("/settings/documenttypes/edit/" + node.id).search("create", "true");
|
||||
navigationService.hideMenu();
|
||||
};
|
||||
// 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.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">
|
||||
<li data-element="action-documentType" ng-hide="model.disableTemplates">
|
||||
<a href="" ng-click="createDocType()" umb-auto-focus>
|
||||
|
||||
<i class="large icon-item-arrangement"></i>
|
||||
@@ -15,10 +15,14 @@
|
||||
</a>
|
||||
</li>
|
||||
<li data-element="action-documentTypeWithoutTemplate">
|
||||
<a href="" ng-click="createComponent()">
|
||||
<i class="large icon-item-arrangement"></i>
|
||||
<span class="menu-label"><localize key="create_documentTypeWithoutTemplate"></localize></span>
|
||||
</a>
|
||||
<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>
|
||||
</li>
|
||||
<li data-element="action-folder" ng-if="model.allowCreateFolder">
|
||||
<a href="" ng-click="showCreateFolder()">
|
||||
@@ -33,7 +37,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,6 +15,36 @@
|
||||
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;
|
||||
@@ -23,97 +53,73 @@
|
||||
vm.page = {};
|
||||
vm.page.loading = false;
|
||||
vm.page.saveButtonState = "init";
|
||||
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.navigation = [];
|
||||
|
||||
loadButtons();
|
||||
|
||||
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) {
|
||||
@@ -144,7 +150,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);
|
||||
});
|
||||
});
|
||||
@@ -155,26 +161,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);
|
||||
});
|
||||
});
|
||||
@@ -189,7 +195,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;
|
||||
});
|
||||
@@ -207,12 +213,26 @@
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -233,7 +253,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)
|
||||
@@ -273,7 +293,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);
|
||||
});
|
||||
});
|
||||
@@ -331,7 +351,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;
|
||||
});
|
||||
@@ -345,7 +365,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
evts.push(eventsService.on("app.refreshEditor", function(name, error) {
|
||||
evts.push(eventsService.on("app.refreshEditor", function (name, error) {
|
||||
loadDocumentType();
|
||||
}));
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@
|
||||
|
||||
function init() {
|
||||
//we need to load this somewhere, for now its here.
|
||||
assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css");
|
||||
assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope);
|
||||
|
||||
if ($routeParams.create) {
|
||||
|
||||
@@ -355,4 +355,4 @@
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.PartialViewMacros.EditController", partialViewMacrosEditController);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -242,7 +242,7 @@
|
||||
|
||||
function init() {
|
||||
//we need to load this somewhere, for now its here.
|
||||
assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css");
|
||||
assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope);
|
||||
|
||||
if ($routeParams.create) {
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
angular.module("umbraco").controller("Umbraco.PrevalueEditors.BooleanController",
|
||||
function ($scope) {
|
||||
$scope.htmlId = "bool-" + String.CreateGuid();
|
||||
});
|
||||
@@ -1 +1,4 @@
|
||||
<input name="boolean" type="checkbox" ng-model="model.value" ng-true-value="1" ng-false-value="0" />
|
||||
<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>
|
||||
|
||||
@@ -5,6 +5,7 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiValuesControl
|
||||
|
||||
$scope.newItem = "";
|
||||
$scope.hasError = false;
|
||||
$scope.focusOnNew = false;
|
||||
|
||||
if (!angular.isArray($scope.model.value)) {
|
||||
|
||||
@@ -43,6 +44,7 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiValuesControl
|
||||
$scope.model.value.push({ value: $scope.newItem });
|
||||
$scope.newItem = "";
|
||||
$scope.hasError = false;
|
||||
$scope.focusOnNew = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -73,6 +75,12 @@ 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,13 +1,21 @@
|
||||
<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 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>
|
||||
<div ui-sortable="sortableOptions">
|
||||
<div class="control-group" ng-repeat="item in model.value">
|
||||
<div class="control-group umb-prevalues-multivalues__listitem" ng-repeat="item in model.value">
|
||||
<i class="icon icon-navigation handle"></i>
|
||||
<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 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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ function ColorPickerController($scope) {
|
||||
|
||||
$scope.toggleItem = function (color) {
|
||||
|
||||
var currentColor = $scope.model.value.hasOwnProperty("value")
|
||||
var currentColor = ($scope.model.value && $scope.model.value.hasOwnProperty("value"))
|
||||
? $scope.model.value.value
|
||||
: $scope.model.value;
|
||||
|
||||
|
||||
+1
-1
@@ -105,5 +105,5 @@
|
||||
};
|
||||
|
||||
//load the separate css for the editor to avoid it blocking our js loading
|
||||
assetsService.loadCss("lib/spectrum/spectrum.css");
|
||||
assetsService.loadCss("lib/spectrum/spectrum.css", $scope);
|
||||
});
|
||||
|
||||
+2
@@ -66,6 +66,8 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
|
||||
showOpenButton: false,
|
||||
showEditButton: false,
|
||||
showPathOnHover: false,
|
||||
maxNumber: 1,
|
||||
minNumber : 0,
|
||||
startNode: {
|
||||
query: "",
|
||||
type: "content",
|
||||
|
||||
+34
-1
@@ -20,7 +20,7 @@
|
||||
</umb-node-preview>
|
||||
</div>
|
||||
|
||||
<a ng-show="model.config.multiPicker === true || renderModel.length === 0"
|
||||
<a ng-show="model.config.multiPicker === true && renderModel.length < model.config.maxNumber || renderModel.length === 0 || !model.config.maxNumber"
|
||||
class="umb-node-preview-add"
|
||||
href=""
|
||||
ng-click="openContentPicker()"
|
||||
@@ -28,6 +28,39 @@
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
|
||||
<div class="umb-contentpicker__min-max-help" ng-if="model.config.multiPicker === true">
|
||||
|
||||
<!-- 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
-1
@@ -112,7 +112,7 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
|
||||
//get the current user to see if we can localize this picker
|
||||
userService.getCurrentUser().then(function (user) {
|
||||
|
||||
assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css').then(function() {
|
||||
assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css', $scope).then(function() {
|
||||
|
||||
var filesToLoad = ["lib/datetimepicker/bootstrap-datetimepicker.js"];
|
||||
|
||||
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
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;
|
||||
}
|
||||
|
||||
});
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
<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>
|
||||
+21
-20
@@ -130,6 +130,9 @@ 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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -154,26 +157,24 @@ angular.module("umbraco")
|
||||
.controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController)
|
||||
.run(function(mediaHelper, umbRequestHelper, assetsService){
|
||||
if (mediaHelper && mediaHelper.registerFileResolver) {
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
//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;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -31,6 +31,6 @@
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="fileCount" ng-model="files.length" val-property-validator="validateMandatory"/>
|
||||
<input type="hidden" name="fileCount" ng-model="files.length" val-property-validator="validateMandatory" no-dirty-check />
|
||||
|
||||
</div>
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ angular.module("umbraco")
|
||||
.controller("Umbraco.PropertyEditors.GoogleMapsController",
|
||||
function ($element, $rootScope, $scope, notificationsService, dialogService, assetsService, $log, $timeout) {
|
||||
|
||||
assetsService.loadJs('https://www.google.com/jsapi')
|
||||
assetsService.loadJs('https://www.google.com/jsapi', $scope)
|
||||
.then(function () {
|
||||
google.load("maps", "3",
|
||||
{
|
||||
@@ -94,4 +94,4 @@ angular.module("umbraco")
|
||||
//update the display val again if it has changed from the server
|
||||
initMap();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
+22
-2
@@ -30,15 +30,14 @@
|
||||
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) {
|
||||
@@ -87,6 +86,27 @@
|
||||
$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();
|
||||
|
||||
}
|
||||
|
||||
+8
-2
@@ -46,6 +46,7 @@ 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();
|
||||
@@ -59,7 +60,12 @@ 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()) {
|
||||
angularHelper.getCurrentForm($scope).$setDirty();
|
||||
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;
|
||||
}
|
||||
$scope.model.value = $("textarea", $element).val();
|
||||
}
|
||||
});
|
||||
@@ -68,7 +74,7 @@ function MarkdownEditorController($scope, $element, assetsService, dialogService
|
||||
});
|
||||
|
||||
//load the seperat css for the editor to avoid it blocking our js loading TEMP HACK
|
||||
assetsService.loadCss("lib/markdown/markdown.css");
|
||||
assetsService.loadCss("lib/markdown/markdown.css", $scope);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -185,6 +185,12 @@ angular.module("umbraco")
|
||||
//cannot parse, we'll just leave it
|
||||
}
|
||||
}
|
||||
if (val === "true") {
|
||||
tinyMceConfig.customConfig[i] = true;
|
||||
}
|
||||
if (val === "false") {
|
||||
tinyMceConfig.customConfig[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +365,8 @@ 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;
|
||||
tinyMceEditor.setContent(newVal, { format: 'raw' });
|
||||
//uses an empty string in the editor when the value is null
|
||||
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);
|
||||
|
||||
@@ -117,5 +117,5 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
|
||||
});
|
||||
|
||||
// load TinyMCE skin which contains css for font-icons
|
||||
assetsService.loadCss("lib/tinymce/skins/umbraco/skin.min.css");
|
||||
});
|
||||
assetsService.loadCss("lib/tinymce/skins/umbraco/skin.min.css", $scope);
|
||||
});
|
||||
|
||||
@@ -92,7 +92,6 @@
|
||||
$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) {
|
||||
@@ -212,7 +211,7 @@
|
||||
});
|
||||
|
||||
//load the separate css for the editor to avoid it blocking our js loading
|
||||
assetsService.loadCss("lib/slider/bootstrap-slider.css");
|
||||
assetsService.loadCss("lib/slider/bootstrap-slider-custom.css");
|
||||
assetsService.loadCss("lib/slider/bootstrap-slider.css", $scope);
|
||||
assetsService.loadCss("lib/slider/bootstrap-slider-custom.css", $scope);
|
||||
}
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.SliderController", sliderController);
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.SliderController", sliderController);
|
||||
|
||||
@@ -7,7 +7,7 @@ angular.module("umbraco")
|
||||
$scope.isLoading = true;
|
||||
$scope.tagToAdd = "";
|
||||
|
||||
assetsService.loadJs("lib/typeahead.js/typeahead.bundle.min.js").then(function () {
|
||||
assetsService.loadJs("lib/typeahead.js/typeahead.bundle.min.js", $scope).then(function () {
|
||||
|
||||
$scope.isLoading = false;
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
function init() {
|
||||
|
||||
//we need to load this somewhere, for now its here.
|
||||
assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css");
|
||||
assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope);
|
||||
|
||||
if ($routeParams.create) {
|
||||
codefileResource.getScaffold("scripts", $routeParams.id).then(function (script) {
|
||||
@@ -199,4 +199,4 @@
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Scripts.EditController", ScriptsEditController);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
vm.init = function () {
|
||||
|
||||
//we need to load this somewhere, for now its here.
|
||||
assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css");
|
||||
assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css", $scope);
|
||||
|
||||
//load templates - used in the master template picker
|
||||
templateResource.getAll()
|
||||
|
||||
@@ -99,4 +99,4 @@ module.exports = function (config) {
|
||||
'karma-phantomjs-launcher'
|
||||
]
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -837,6 +837,19 @@
|
||||
<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>
|
||||
@@ -994,7 +1007,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="App_Data\" />
|
||||
<Folder Include="Umbraco\preview\" />
|
||||
<Folder Include="Views\MacroPartials\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -1023,9 +1035,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
|
||||
<WebProjectProperties>
|
||||
<UseIIS>True</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>7910</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>7102</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:7910</IISUrl>
|
||||
<IISUrl>http://localhost:7102</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.background">
|
||||
|
||||
<div class="box-slider">
|
||||
+2
-2
@@ -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,4 @@
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div class="box-slider">
|
||||
<div colorpicker ng-model="item.values.color"></div>
|
||||
</div>
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.googlefontpicker">
|
||||
|
||||
<div class="box-slider">
|
||||
+2
-2
@@ -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>
|
||||
+16
-31
@@ -1,4 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
@using System.Web.Mvc.Html
|
||||
@inherits System.Web.Mvc.WebViewPage<Umbraco.Web.Models.ContentEditing.BackOfficePreview>
|
||||
@{
|
||||
var disableDevicePreview = Model.DisableDevicePreview.ToString().ToLowerInvariant();
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Umbraco Canvas Designer</title>
|
||||
@@ -6,27 +11,25 @@
|
||||
<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)
|
||||
}
|
||||
|
||||
<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; showPalettePicker = true" ng-mouseenter="positionSelectedHide()">
|
||||
|
||||
<div class="canvasdesigner" ng-init="showDevicesPreview = true; showDevices = !@(disableDevicePreview); 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}">
|
||||
<ul class="sections" ng-class="{selected: showDevicesPreview && showDevices}">
|
||||
<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>
|
||||
@@ -39,9 +42,8 @@
|
||||
</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>
|
||||
@@ -53,15 +55,11 @@
|
||||
<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">
|
||||
@@ -78,7 +76,6 @@
|
||||
</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">
|
||||
@@ -90,11 +87,8 @@
|
||||
<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>
|
||||
@@ -112,7 +106,6 @@
|
||||
</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">
|
||||
@@ -134,7 +127,6 @@
|
||||
</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">
|
||||
@@ -146,20 +138,13 @@
|
||||
<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>
|
||||
+2
-2
@@ -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>
|
||||
+3
-3
@@ -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>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user