Merge branch temp8 into temp8-di2690

This commit is contained in:
Stephan
2018-11-29 12:38:44 +01:00
46 changed files with 827 additions and 4208 deletions
+1 -1
View File
@@ -33,5 +33,5 @@ We recommend running the site with the Visual Studio since you'll be able to rem
We are keeping track of [known issues and limitations here](http://issues.umbraco.org/issue/U4-11279). These line items will eventually be turned into actual tasks to be worked on. Feel free to help us keep this list updated if you find issues and even help fix some of these items. If there is a particular item you'd like to help fix please mention this on the task and we'll create a sub task for the item to continue discussion there.
There's [a list of tasks for v8 that haven't been completed](https://issues.umbraco.org/issues?q=&project=U4&tagValue=&release=8.0.0&issueType=&resolvedState=open&search=search). If you are interested in helping out with any of these please mention this on the task. This list will be constantly updated as we begin to document and design some of the other tasks that still need to get done.
There's [a list of tasks for v8 that haven't been completed](hhttps://github.com/umbraco/Umbraco-CMS/labels/release%2F8.0.0). If you are interested in helping out with any of these please mention this on the task. This list will be constantly updated as we begin to document and design some of the other tasks that still need to get done.
+1 -1
View File
@@ -36,7 +36,7 @@
<dependency id="Serilog.Enrichers.Thread" version="[3.0.0,3.999999)" />
<dependency id="Serilog.Filters.Expressions" version="[2.0.0,2.999999)" />
<dependency id="Serilog.Formatting.Compact" version="[1.0.0,1.999999)" />
<dependency id="Serilog.Settings.AppSettings" version="[2.1.2,2.999999)" />
<dependency id="Serilog.Settings.AppSettings" version="[2.2.2,2.999999)" />
<dependency id="Serilog.Sinks.File" version="[4.0.0,4.999999)" />
<dependency id="Umbraco.SqlServerCE" version="[4.0.0.1,4.999999)" />
<dependency id="NPoco" version="[3.9.4,3.999999)" />
+49 -47
View File
@@ -210,53 +210,55 @@ namespace Umbraco.Core.Composing
/// NOTE the comma vs period... comma delimits the name in an Assembly FullName property so if it ends with comma then its an exact name match
/// NOTE this means that "foo." will NOT exclude "foo.dll" but only "foo.*.dll"
/// </remarks>
internal static readonly string[] KnownAssemblyExclusionFilter = new[]
{
"mscorlib,",
"System.",
"Antlr3.",
"Autofac.",
"Autofac,",
"Castle.",
"ClientDependency.",
"DataAnnotationsExtensions.",
"DataAnnotationsExtensions,",
"Dynamic,",
"HtmlDiff,",
"Iesi.Collections,",
"Microsoft.",
"Newtonsoft.",
"NHibernate.",
"NHibernate,",
"NuGet.",
"RouteDebugger,",
"SqlCE4Umbraco,",
"umbraco.datalayer,",
"umbraco.interfaces,",
//"umbraco.providers,",
//"Umbraco.Web.UI,",
"umbraco.webservices",
"Lucene.",
"Examine,",
"Examine.",
"ServiceStack.",
"MySql.",
"HtmlAgilityPack.",
"TidyNet.",
"ICSharpCode.",
"CookComputing.",
"AutoMapper,",
"AutoMapper.",
"AzureDirectory,",
"itextsharp,",
"UrlRewritingNet.",
"HtmlAgilityPack,",
"MiniProfiler,",
"Moq,",
"nunit.framework,",
"TidyNet,",
"WebDriver,"
};
internal static readonly string[] KnownAssemblyExclusionFilter = {
"Antlr3.",
"AutoMapper,",
"AutoMapper.",
"Autofac,", // DI
"Autofac.",
"AzureDirectory,",
"Castle.", // DI, tests
"ClientDependency.",
"CookComputing.",
"CSharpTest.", // BTree for NuCache
"DataAnnotationsExtensions,",
"DataAnnotationsExtensions.",
"Dynamic,",
"Examine,",
"Examine.",
"HtmlAgilityPack,",
"HtmlAgilityPack.",
"HtmlDiff,",
"ICSharpCode.",
"Iesi.Collections,", // used by NHibernate
"LightInject.", // DI
"LightInject,",
"Lucene.",
"Markdown,",
"Microsoft.",
"MiniProfiler,",
"Moq,",
"MySql.",
"NHibernate,",
"NHibernate.",
"Newtonsoft.",
"NPoco,",
"NuGet.",
"RouteDebugger,",
"Semver.",
"Serilog.",
"Serilog,",
"ServiceStack.",
"SqlCE4Umbraco,",
"Superpower,", // used by Serilog
"System.",
"TidyNet,",
"TidyNet.",
"WebDriver,",
"itextsharp,",
"mscorlib,",
"nunit.framework,",
};
/// <summary>
/// Finds any classes derived from the type T that contain the attribute TAttribute
+9
View File
@@ -530,6 +530,8 @@ namespace Umbraco.Core.Composing
// if not caching, or not IDiscoverable, directly get types
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
{
_logger.Logger.Debug<TypeLoader>("Running a full, non-cached, scan for type {TypeName} (slow).", typeof(T).FullName);
return GetTypesInternal(
typeof (T), null,
() => TypeFinder.FindClassesOfType<T>(specificAssemblies ?? AssembliesToScan),
@@ -572,6 +574,8 @@ namespace Umbraco.Core.Composing
// if not caching, or not IDiscoverable, directly get types
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
{
_logger.Logger.Debug<TypeLoader>("Running a full, non-cached, scan for type {TypeName} / attribute {AttributeName} (slow).", typeof(T).FullName, typeof(TAttribute).FullName);
return GetTypesInternal(
typeof (T), typeof (TAttribute),
() => TypeFinder.FindClassesOfTypeWithAttribute<T, TAttribute>(specificAssemblies ?? AssembliesToScan),
@@ -611,6 +615,11 @@ namespace Umbraco.Core.Composing
// do not cache anything from specific assemblies
cache &= specificAssemblies == null;
if (cache == false)
{
_logger.Logger.Debug<TypeLoader>("Running a full, non-cached, scan for types / attribute {AttributeName} (slow).", typeof(TAttribute).FullName);
}
return GetTypesInternal(
typeof (object), typeof (TAttribute),
() => TypeFinder.FindClassesWithAttribute<TAttribute>(specificAssemblies ?? AssembliesToScan),
@@ -1,5 +1,5 @@
namespace Umbraco.Core.Persistence.Repositories
{
interface IDocumentBlueprintRepository : IDocumentRepository
public interface IDocumentBlueprintRepository : IDocumentRepository
{ }
}
@@ -8,9 +8,6 @@
[ConfigurationField("enableRange", "Enable range", "boolean")]
public bool EnableRange { get; set; }
[ConfigurationField("orientation", "Orientation", "views/propertyeditors/slider/orientation.prevalues.html")]
public string Orientation { get; set; }
[ConfigurationField("initVal1", "Initial value", "number")]
public int InitialValue { get; set; }
@@ -25,38 +22,5 @@
[ConfigurationField("step", "Step increments", "number")]
public int StepIncrements { get; set; }
[ConfigurationField("precision", "Precision", "number", Description = "The number of digits shown after the decimal. Defaults to the number of digits after the decimal of step value.")]
public int Precision { get; set; }
[ConfigurationField("handle", "Handle", "views/propertyeditors/slider/handle.prevalues.html", Description = "Handle shape. Default is 'round\'")]
public string Handle { get; set; }
[ConfigurationField("tooltip", "Tooltip", "views/propertyeditors/slider/tooltip.prevalues.html", Description = "Whether to show the tooltip on drag, hide the tooltip, or always show the tooltip. Accepts: 'show', 'hide', or 'always'")]
public string Tooltip { get; set; }
[ConfigurationField("tooltipSplit", "Tooltip split", "boolean", Description = "If false show one tootip if true show two tooltips one for each handler")]
public bool TooltipSplit { get; set; } // fixme bool?
[ConfigurationField("tooltipFormat", "Tooltip format", "textstring", Description = "The value wanted to be displayed in the tooltip. Use {0} and {1} for current values - {1} is only for range slider and if not using tooltip split.")]
public string TooltipFormat { get; set; }
[ConfigurationField("tooltipPosition", "Tooltip position", "textstring", Description = "Position of tooltip, relative to slider. Accepts 'top'/'bottom' for horizontal sliders and 'left'/'right' for vertically orientated sliders. Default positions are 'top' for horizontal and 'right' for vertical slider.")]
public string TooltipPosition { get; set; }
[ConfigurationField("reversed", "Reversed", "boolean", Description = "Whether or not the slider should be reversed")]
public bool Reversed { get; set; } // fixme bool?
[ConfigurationField("ticks", "Ticks", "textstring", Description = "Comma-separated values. Used to define the values of ticks. Tick marks are indicators to denote special values in the range. This option overwrites min and max options.")]
public string Ticks { get; set; }
[ConfigurationField("ticksPositions", "Ticks positions", "textstring", Description = "Comma-separated values. Defines the positions of the tick values in percentages. The first value should always be 0, the last value should always be 100 percent.")]
public string TicksPositions { get; set; }
[ConfigurationField("ticksLabels", "Ticks labels", "textstring", Description = "Comma-separated values. Defines the labels below the tick marks. Accepts HTML input.")]
public string TicksLabels { get; set; }
[ConfigurationField("ticksSnapBounds", "Ticks snap bounds", "number", Description = "Used to define the snap bounds of a tick. Snaps to the tick if value is within these bounds.")]
public int TicksSnapBounds { get; set; }
}
}
@@ -19,7 +19,7 @@ namespace Umbraco.Core.Services.Implement
/// <summary>
/// Implements the content service.
/// </summary>
internal class ContentService : RepositoryService, IContentService
public class ContentService : RepositoryService, IContentService
{
private readonly IDocumentRepository _documentRepository;
private readonly IEntityRepository _entityRepository;
@@ -7,7 +7,7 @@
/// <remarks>
/// Anything less than 10 = Success!
/// </remarks>
public enum MoveOperationStatusType
public enum MoveOperationStatusType : byte
{
/// <summary>
/// The move was successful.
+1 -1
View File
@@ -89,7 +89,7 @@
<Version>1.0.0</Version>
</PackageReference>
<PackageReference Include="Serilog.Settings.AppSettings">
<Version>2.1.2</Version>
<Version>2.2.2</Version>
</PackageReference>
<PackageReference Include="Serilog.Sinks.File">
<Version>4.0.0</Version>
+8
View File
@@ -331,6 +331,14 @@ gulp.task('dependencies', function () {
"src": ["./node_modules/ng-file-upload/dist/ng-file-upload.min.js"],
"base": "./node_modules/ng-file-upload/dist"
},
{
"name": "nouislider",
"src": [
"./node_modules/nouislider/distribute/nouislider.min.js",
"./node_modules/nouislider/distribute/nouislider.min.css"
],
"base": "./node_modules/nouislider/distribute"
},
{
"name": "signalr",
"src": ["./node_modules/signalr/jquery.signalR.js"],
File diff suppressed because it is too large Load Diff
@@ -1,5 +0,0 @@
/*!
* Datetimepicker for Bootstrap v3
//! version : 3.1.3
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/.bootstrap-datetimepicker-widget{top:0;left:0;width:250px;padding:4px;margin-top:1px;z-index:99999!important;border-radius:4px}.bootstrap-datetimepicker-widget.timepicker-sbs{width:600px}.bootstrap-datetimepicker-widget.bottom:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,.2);position:absolute;top:-7px;left:7px}.bootstrap-datetimepicker-widget.bottom:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;top:-6px;left:8px}.bootstrap-datetimepicker-widget.top:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #ccc;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:-7px;left:6px}.bootstrap-datetimepicker-widget.top:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #fff;position:absolute;bottom:-6px;left:7px}.bootstrap-datetimepicker-widget .dow{width:14.2857%}.bootstrap-datetimepicker-widget.pull-right:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.pull-right:after{left:auto;right:7px}.bootstrap-datetimepicker-widget>ul{list-style-type:none;margin:0}.bootstrap-datetimepicker-widget a[data-action]{padding:6px 0}.bootstrap-datetimepicker-widget a[data-action]:active{box-shadow:none}.bootstrap-datetimepicker-widget .timepicker-hour,.bootstrap-datetimepicker-widget .timepicker-minute,.bootstrap-datetimepicker-widget .timepicker-second{width:54px;font-weight:700;font-size:1.2em;margin:0}.bootstrap-datetimepicker-widget button[data-action]{padding:6px}.bootstrap-datetimepicker-widget table[data-hour-format="12"] .separator{width:4px;padding:0;margin:0}.bootstrap-datetimepicker-widget .datepicker>div{display:none}.bootstrap-datetimepicker-widget .picker-switch{text-align:center}.bootstrap-datetimepicker-widget table{width:100%;margin:0}.bootstrap-datetimepicker-widget td,.bootstrap-datetimepicker-widget th{text-align:center;border-radius:4px}.bootstrap-datetimepicker-widget td{height:54px;line-height:54px;width:54px}.bootstrap-datetimepicker-widget td.cw{font-size:10px;height:20px;line-height:20px;color:#777}.bootstrap-datetimepicker-widget td.day{height:20px;line-height:20px;width:20px}.bootstrap-datetimepicker-widget td.day:hover,.bootstrap-datetimepicker-widget td.hour:hover,.bootstrap-datetimepicker-widget td.minute:hover,.bootstrap-datetimepicker-widget td.second:hover{background:#eee;cursor:pointer}.bootstrap-datetimepicker-widget td.old,.bootstrap-datetimepicker-widget td.new{color:#777}.bootstrap-datetimepicker-widget td.today{position:relative}.bootstrap-datetimepicker-widget td.today:before{content:'';display:inline-block;border-left:7px solid transparent;border-bottom:7px solid #428bca;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:4px;right:4px}.bootstrap-datetimepicker-widget td.active,.bootstrap-datetimepicker-widget td.active:hover{background-color:#428bca;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget td.active.today:before{border-bottom-color:#fff}.bootstrap-datetimepicker-widget td.disabled,.bootstrap-datetimepicker-widget td.disabled:hover{background:0 0;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget td span{display:inline-block;width:54px;height:54px;line-height:54px;margin:2px 1.5px;cursor:pointer;border-radius:4px}.bootstrap-datetimepicker-widget td span:hover{background:#eee}.bootstrap-datetimepicker-widget td span.active{background-color:#428bca;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget td span.old{color:#777}.bootstrap-datetimepicker-widget td span.disabled,.bootstrap-datetimepicker-widget td span.disabled:hover{background:0 0;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget th{height:20px;line-height:20px;width:20px}.bootstrap-datetimepicker-widget th.picker-switch{width:145px}.bootstrap-datetimepicker-widget th.next,.bootstrap-datetimepicker-widget th.prev{font-size:21px}.bootstrap-datetimepicker-widget th.disabled,.bootstrap-datetimepicker-widget th.disabled:hover{background:0 0;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget thead tr:first-child th{cursor:pointer}.bootstrap-datetimepicker-widget thead tr:first-child th:hover{background:#eee}.input-group.date .input-group-addon span{display:block;cursor:pointer;width:16px;height:16px}.bootstrap-datetimepicker-widget.left-oriented:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.left-oriented:after{left:auto;right:7px}.bootstrap-datetimepicker-widget ul.list-unstyled li div.timepicker div.timepicker-picker table.table-condensed tbody>tr>td{padding:0!important}@media screen and (max-width:767px){.bootstrap-datetimepicker-widget.timepicker-sbs{width:283px}}
@@ -1,79 +0,0 @@
/* set default selection color */
.slider-track .slider-selection {
background: #89cdef;
}
/* custom styling for triangle */
.slider.slider-horizontal .slider-tick.triangle,
.slider.slider-horizontal .slider-handle.triangle {
box-shadow: none;
border-width: 0 12px 15px 12px !important;
margin-left: -12px;
margin-top: -5px !important;
}
.slider.slider-vertical .slider-tick.triangle,
.slider.slider-vertical .slider-handle.triangle {
box-shadow: none;
border-width: 12px 0 12px 15px !important;
margin-top: -12px;
}
/* for custom handle remove box-shadow */
.slider-track .slider-handle.custom {
box-shadow: none;
margin-left: -8px;
}
/* for custom handle add text-shadow */
.slider-track .slider-handle.custom::before {
text-shadow: 0 1px 2px rgba(0,0,0,.05);
color: #337ab7;
font-size: 26px;
}
/* we make each tick a bit more clear */
.slider .slider-tick {
background-image: -webkit-linear-gradient(top, #e6e6e6 0, #dedede 100%);
background-image: -o-linear-gradient(top, #e6e6e6 0, #dedede 100%);
background-image: linear-gradient(to bottom, #e6e6e6 0, #dedede 100%);
}
.slider .slider-tick.in-selection {
background-image: -webkit-linear-gradient(top, #89cdef 0, #81bfde 100%);
background-image: -o-linear-gradient(top, #89cdef 0, #81bfde 100%);
background-image: linear-gradient(to bottom, #89cdef 0, #81bfde 100%);
}
/* horizontal - triangle border-bottom color */
.slider.slider-horizontal .slider-tick.triangle {
border-bottom-color: #dedede;
}
.slider.slider-horizontal .slider-handle.triangle {
border-bottom-color: #0480be;
}
.slider.slider-horizontal .slider-tick.triangle.in-selection {
border-bottom-color: #81bfde;
}
/* vertical - triangle border-left color */
.slider.slider-vertical .slider-tick.triangle {
border-left-color: #dedede;
}
.slider.slider-vertical .slider-handle.triangle {
border-left-color: #0480be;
}
.slider.slider-vertical .slider-tick.triangle.in-selection {
border-left-color: #81bfde;
}
.slider.slider-horizontal {
width: 400px;
max-width: 100%;
}
@media only screen and (max-width: 767px) {
.slider.slider-horizontal {
width: 100%;
}
}
@@ -1,255 +0,0 @@
/*! =======================================================
VERSION 5.2.6
========================================================= */
/*! =========================================================
* bootstrap-slider.js
*
* Maintainers:
* Kyle Kemp
* - Twitter: @seiyria
* - Github: seiyria
* Rohit Kalkur
* - Twitter: @Rovolutionary
* - Github: rovolution
*
* =========================================================
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ========================================================= */
.slider {
display: inline-block;
vertical-align: middle;
position: relative;
}
.slider.slider-horizontal {
width: 210px;
height: 20px;
}
.slider.slider-horizontal .slider-track {
height: 10px;
width: 100%;
margin-top: -5px;
top: 50%;
left: 0;
}
.slider.slider-horizontal .slider-selection,
.slider.slider-horizontal .slider-track-low,
.slider.slider-horizontal .slider-track-high {
height: 100%;
top: 0;
bottom: 0;
}
.slider.slider-horizontal .slider-tick,
.slider.slider-horizontal .slider-handle {
margin-left: -10px;
margin-top: -5px;
}
.slider.slider-horizontal .slider-tick.triangle,
.slider.slider-horizontal .slider-handle.triangle {
border-width: 0 10px 10px 10px;
width: 0;
height: 0;
border-bottom-color: #0480be;
margin-top: 0;
}
.slider.slider-horizontal .slider-tick-label-container {
white-space: nowrap;
margin-top: 20px;
}
.slider.slider-horizontal .slider-tick-label-container .slider-tick-label {
padding-top: 4px;
display: inline-block;
text-align: center;
}
.slider.slider-vertical {
height: 210px;
width: 20px;
}
.slider.slider-vertical .slider-track {
width: 10px;
height: 100%;
margin-left: -5px;
left: 50%;
top: 0;
}
.slider.slider-vertical .slider-selection {
width: 100%;
left: 0;
top: 0;
bottom: 0;
}
.slider.slider-vertical .slider-track-low,
.slider.slider-vertical .slider-track-high {
width: 100%;
left: 0;
right: 0;
}
.slider.slider-vertical .slider-tick,
.slider.slider-vertical .slider-handle {
margin-left: -5px;
margin-top: -10px;
}
.slider.slider-vertical .slider-tick.triangle,
.slider.slider-vertical .slider-handle.triangle {
border-width: 10px 0 10px 10px;
width: 1px;
height: 1px;
border-left-color: #0480be;
margin-left: 0;
}
.slider.slider-vertical .slider-tick-label-container {
white-space: nowrap;
}
.slider.slider-vertical .slider-tick-label-container .slider-tick-label {
padding-left: 4px;
}
.slider.slider-disabled .slider-handle {
background-image: -webkit-linear-gradient(top, #dfdfdf 0%, #bebebe 100%);
background-image: -o-linear-gradient(top, #dfdfdf 0%, #bebebe 100%);
background-image: linear-gradient(to bottom, #dfdfdf 0%, #bebebe 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdfdfdf', endColorstr='#ffbebebe', GradientType=0);
}
.slider.slider-disabled .slider-track {
background-image: -webkit-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%);
background-image: -o-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%);
background-image: linear-gradient(to bottom, #e5e5e5 0%, #e9e9e9 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5', endColorstr='#ffe9e9e9', GradientType=0);
cursor: not-allowed;
}
.slider input {
display: none;
}
.slider .tooltip.top {
margin-top: -36px;
}
.slider .tooltip-inner {
white-space: nowrap;
}
.slider .hide {
display: none;
}
.slider-track {
position: absolute;
cursor: pointer;
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%);
background-image: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.slider-selection {
position: absolute;
background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0);
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
border-radius: 4px;
}
.slider-selection.tick-slider-selection {
background-image: -webkit-linear-gradient(top, #89cdef 0%, #81bfde 100%);
background-image: -o-linear-gradient(top, #89cdef 0%, #81bfde 100%);
background-image: linear-gradient(to bottom, #89cdef 0%, #81bfde 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff89cdef', endColorstr='#ff81bfde', GradientType=0);
}
.slider-track-low,
.slider-track-high {
position: absolute;
background: transparent;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
border-radius: 4px;
}
.slider-handle {
position: absolute;
width: 20px;
height: 20px;
background-color: #337ab7;
background-image: -webkit-linear-gradient(top, #149bdf 0%, #0480be 100%);
background-image: -o-linear-gradient(top, #149bdf 0%, #0480be 100%);
background-image: linear-gradient(to bottom, #149bdf 0%, #0480be 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
filter: none;
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
border: 0px solid transparent;
}
.slider-handle.round {
border-radius: 50%;
}
.slider-handle.triangle {
background: transparent none;
}
.slider-handle.custom {
background: transparent none;
}
.slider-handle.custom::before {
line-height: 20px;
font-size: 20px;
content: '\2605';
color: #726204;
}
.slider-tick {
position: absolute;
width: 20px;
height: 20px;
background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%);
background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0);
-webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
filter: none;
opacity: 0.8;
border: 0px solid transparent;
}
.slider-tick.round {
border-radius: 50%;
}
.slider-tick.triangle {
background: transparent none;
}
.slider-tick.custom {
background: transparent none;
}
.slider-tick.custom::before {
line-height: 20px;
font-size: 20px;
content: '\2605';
color: #726204;
}
.slider-tick.in-selection {
background-image: -webkit-linear-gradient(top, #89cdef 0%, #81bfde 100%);
background-image: -o-linear-gradient(top, #89cdef 0%, #81bfde 100%);
background-image: linear-gradient(to bottom, #89cdef 0%, #81bfde 100%);
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff89cdef', endColorstr='#ff81bfde', GradientType=0);
opacity: 1;
}
File diff suppressed because it is too large Load Diff
+2
View File
@@ -32,6 +32,8 @@
"lazyload-js": "1.0.0",
"moment": "2.10.6",
"ng-file-upload": "12.2.13",
"nouislider": "12.1.0",
"npm": "^6.4.1",
"signalr": "2.3.0",
"tinymce": "4.8.3",
"typeahead.js": "0.10.5",
@@ -66,6 +66,7 @@ Use this directive to render an umbraco button. The directive can be used to gen
@param {string=} size Set a button icon ("xs", "m", "l", "xl").
@param {boolean=} disabled Set to <code>true</code> to disable the button.
@param {string=} addEllipsis Adds an ellipsis character (…) to the button label which means the button will open a dialog or prompt the user for more information.
@param {string=} showCaret Shows a caret on the right side of the button label
**/
@@ -93,7 +94,8 @@ Use this directive to render an umbraco button. The directive can be used to gen
disabled: "<?",
size: "@?",
alias: "@?",
addEllipsis: "@?"
addEllipsis: "@?",
showCaret: "@?"
}
});
@@ -89,30 +89,52 @@ Use this directive to render a button with a dropdown of alternative actions.
@param {string=} float Set the float of the dropdown. ("left", "right").
**/
(function() {
'use strict';
(function () {
'use strict';
function ButtonGroupDirective() {
function ButtonGroupDirective() {
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/buttons/umb-button-group.html',
scope: {
defaultButton: "=",
subButtons: "=",
state: "=?",
direction: "@?",
float: "@?",
buttonStyle: "@?",
size: "@?",
icon: "@?"
}
};
function link(scope) {
return directive;
}
scope.dropdown = {
isOpen: false
};
angular.module('umbraco.directives').directive('umbButtonGroup', ButtonGroupDirective);
scope.toggleDropdown = function() {
scope.dropdown.isOpen = !scope.dropdown.isOpen;
};
scope.closeDropdown = function() {
scope.dropdown.isOpen = false;
};
scope.executeMenuItem = function(subButton) {
subButton.handler();
scope.closeDropdown();
};
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/buttons/umb-button-group.html',
scope: {
defaultButton: "=",
subButtons: "=",
state: "=?",
direction: "@?",
float: "@?",
buttonStyle: "@?",
size: "@?",
icon: "@?"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbButtonGroup', ButtonGroupDirective);
})();
@@ -1,7 +1,7 @@
(function () {
'use strict';
function ContentNodeInfoDirective($timeout, $routeParams, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource) {
function ContentNodeInfoDirective($timeout, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource) {
function link(scope, element, attrs, umbVariantContentCtrl) {
@@ -17,10 +17,13 @@
function onInit() {
// if there are any infinite editors open we are in infinite editing
scope.isInfiniteMode = editorService.getNumberOfEditors() > 0 ? true : false;
userService.getCurrentUser().then(function(user){
// only allow change of media type if user has access to the settings sections
angular.forEach(user.sections, function(section){
if(section.alias === "settings") {
if(section.alias === "settings" && !scope.isInfiniteMode) {
scope.allowChangeDocumentType = true;
scope.allowChangeTemplate = true;
}
@@ -1,50 +1,63 @@
(function() {
'use strict';
(function () {
'use strict';
function EditorMenuDirective($injector, treeService, navigationService, umbModelMapper, appState) {
function EditorMenuDirective($injector, treeService, navigationService, umbModelMapper, appState) {
function link(scope, el, attr, ctrl) {
function link(scope, el, attr, ctrl) {
//adds a handler to the context menu item click, we need to handle this differently
//depending on what the menu item is supposed to do.
scope.executeMenuItem = function (action) {
navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection);
};
scope.dropdown = {
isOpen: false
};
//callback method to go and get the options async
scope.getOptions = function () {
function onInit() {
if (!scope.currentNode) {
return;
}
getOptions();
//when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu)
appState.setMenuState("currentNode", scope.currentNode);
}
if (!scope.actions) {
treeService.getMenu({ treeNode: scope.currentNode })
.then(function (data) {
scope.actions = data.menuItems;
});
}
};
//adds a handler to the context menu item click, we need to handle this differently
//depending on what the menu item is supposed to do.
scope.executeMenuItem = function (action) {
navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection);
scope.dropdown.isOpen = false;
};
}
//callback method to go and get the options async
function getOptions() {
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/umb-editor-menu.html',
link: link,
scope: {
currentNode: "=",
currentSection: "@"
}
};
if (!scope.currentNode) {
return;
}
return directive;
}
//when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu)
appState.setMenuState("currentNode", scope.currentNode);
angular.module('umbraco.directives').directive('umbEditorMenu', EditorMenuDirective);
if (!scope.actions) {
treeService.getMenu({ treeNode: scope.currentNode })
.then(function (data) {
scope.actions = data.menuItems;
});
}
};
onInit();
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/editor/umb-editor-menu.html',
link: link,
scope: {
currentNode: "=",
currentSection: "@"
}
};
return directive;
}
angular.module('umbraco.directives').directive('umbEditorMenu', EditorMenuDirective);
})();
@@ -1,186 +0,0 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbDateTimePicker
@restrict E
@scope
@description
<b>Added in Umbraco version 7.6</b>
This directive is a wrapper of the bootstrap datetime picker version 3.1.3. Use it to render a date time picker.
For extra details about options and events take a look here: https://eonasdan.github.io/bootstrap-datetimepicker/
Use this directive to render a date time picker
<h3>Markup example</h3>
<pre>
<div ng-controller="My.Controller as vm">
<umb-date-time-picker
options="vm.config"
on-change="vm.datePickerChange(event)"
on-error="vm.datePickerError(event)">
</umb-date-time-picker>
</div>
</pre>
<h3>Controller example</h3>
<pre>
(function () {
"use strict";
function Controller() {
var vm = this;
vm.date = "";
vm.config = {
pickDate: true,
pickTime: true,
useSeconds: true,
format: "YYYY-MM-DD HH:mm:ss",
icons: {
time: "icon-time",
date: "icon-calendar",
up: "icon-chevron-up",
down: "icon-chevron-down"
}
};
vm.datePickerChange = datePickerChange;
vm.datePickerError = datePickerError;
function datePickerChange(event) {
// handle change
if(event.date && event.date.isValid()) {
var date = event.date.format(vm.datePickerConfig.format);
}
}
function datePickerError(event) {
// handle error
}
}
angular.module("umbraco").controller("My.Controller", Controller);
})();
</pre>
@param {object} options (<code>binding</code>): Config object for the date picker.
@param {callback} onHide (<code>callback</code>): Hide callback.
@param {callback} onShow (<code>callback</code>): Show callback.
@param {callback} onChange (<code>callback</code>): Change callback.
@param {callback} onError (<code>callback</code>): Error callback.
@param {callback} onUpdate (<code>callback</code>): Update callback.
**/
(function () {
'use strict';
function DateTimePickerDirective(assetsService) {
function link(scope, element, attrs, ctrl) {
scope.hasTranscludedContent = false;
function onInit() {
// check for transcluded content so we can hide the defualt markup
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', scope);
// load the js file for the date picker
assetsService.loadJs('lib/datetimepicker/bootstrap-datetimepicker.js', scope).then(function () {
// init date picker
initDatePicker();
});
}
function onHide(event) {
if (scope.onHide) {
scope.$apply(function(){
// callback
scope.onHide({event: event});
});
}
}
function onShow() {
if (scope.onShow) {
scope.$apply(function(){
// callback
scope.onShow();
});
}
}
function onChange(event) {
if (scope.onChange && event.date && event.date.isValid()) {
scope.$apply(function(){
// callback
scope.onChange({event: event});
});
}
}
function onError(event) {
if (scope.onError) {
scope.$apply(function(){
// callback
scope.onError({event:event});
});
}
}
function onUpdate(event) {
if (scope.onUpdate) {
scope.$apply(function(){
// callback
scope.onUpdate({event: event});
});
}
}
function initDatePicker() {
// Open the datepicker and add a changeDate eventlistener
element
.datetimepicker(scope.options)
.on("dp.hide", onHide)
.on("dp.show", onShow)
.on("dp.change", onChange)
.on("dp.error", onError)
.on("dp.update", onUpdate);
}
onInit();
}
var directive = {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: 'views/components/umb-date-time-picker.html',
scope: {
options: "=",
onHide: "&",
onShow: "&",
onChange: "&",
onError: "&",
onUpdate: "&"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbDateTimePicker', DateTimePickerDirective);
})();
@@ -0,0 +1,207 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbRangeSlider
@restrict E
@scope
@description
<b>Added in Umbraco version 8.0</b>
This directive is a wrapper of the noUiSlider library. Use it to render a slider.
For extra details about options and events take a look here: https://refreshless.com/nouislider/
<h3>Markup example</h3>
<pre>
<div ng-controller="My.Controller as vm">
<umb-range-slider
ng-model="vm.value"
on-end="vm.slideEnd(values)">
</umb-range-slider>
</div>
</pre>
<h3>Controller example</h3>
<pre>
(function () {
"use strict";
function Controller() {
var vm = this;
vm.value = [10];
vm.slideEnd = slideEnd;
function slideEnd(values) {
// handle change
}
}
angular.module("umbraco").controller("My.Controller", Controller);
})();
</pre>
@param {object} ngModel (<code>binding</code>): Value for the slider.
@param {object} options (<code>binding</code>): Config object for the date picker.
@param {callback} onSetup (<code>callback</code>): onSetup gets triggered when the slider is initialized
@param {callback} onUpdate (<code>callback</code>): onUpdate fires every time the slider values are changed.
@param {callback} onSlide (<code>callback</code>): onSlide gets triggered when the handle is being dragged.
@param {callback} onSet (<code>callback</code>): onSet will trigger every time a slider stops changing.
@param {callback} onChange (<code>callback</code>): onChange fires when a user stops sliding, or when a slider value is changed by 'tap'.
@param {callback} onStart (<code>callback</code>): onStart fires when a handle is clicked (mousedown, or the equivalent touch events).
@param {callback} onEnd (<code>callback</code>): onEnd fires when a handle is released (mouseup etc), or when a slide is canceled due to other reasons.
**/
(function() {
'use strict';
var umbRangeSlider = {
template: '<div class="umb-range-slider"></div>',
controller: UmbRangeSliderController,
bindings: {
ngModel: '<',
options: '<',
onSetup: '&?',
onUpdate: '&?',
onSlide: '&?',
onSet: '&?',
onChange: '&?',
onStart: '&?',
onEnd: '&?'
}
};
function UmbRangeSliderController($element, $timeout, $scope, assetsService) {
const ctrl = this;
let sliderInstance = null;
ctrl.$onInit = function() {
// load css file for the date picker
assetsService.loadCss('lib/nouislider/nouislider.min.css', $scope);
// load the js file for the date picker
assetsService.loadJs('lib/nouislider/nouislider.min.js', $scope).then(function () {
// init date picker
grabElementAndRun();
});
};
function grabElementAndRun() {
$timeout(function() {
const element = $element.find('.umb-range-slider')[0];
setSlider(element);
}, 0, true);
}
function setSlider(element) {
sliderInstance = element;
const defaultOptions = {
"start": [0],
"step": 1,
"range": {
"min": [0],
"max": [100]
}
};
const options = ctrl.options ? ctrl.options : defaultOptions;
// create new slider
noUiSlider.create(sliderInstance, options);
if (ctrl.onSetup) {
ctrl.onSetup({
slider: sliderInstance
});
}
// If has ngModel set the date
if (ctrl.ngModel) {
sliderInstance.noUiSlider.set(ctrl.ngModel);
}
// destroy the slider instance when the dom element is removed
angular.element(element).on('$destroy', function() {
sliderInstance.noUiSlider.off();
});
setUpCallbacks();
// Refresh the scope
$scope.$applyAsync();
}
function setUpCallbacks() {
if(sliderInstance) {
// bind hook for update
if(ctrl.onUpdate) {
sliderInstance.noUiSlider.on('update', function (values, handle, unencoded, tap, positions) {
$timeout(function() {
ctrl.onUpdate({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions});
});
});
}
// bind hook for slide
if(ctrl.onSlide) {
sliderInstance.noUiSlider.on('slide', function (values, handle, unencoded, tap, positions) {
$timeout(function() {
ctrl.onSlide({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions});
});
});
}
// bind hook for set
if(ctrl.onSet) {
sliderInstance.noUiSlider.on('set', function (values, handle, unencoded, tap, positions) {
$timeout(function() {
ctrl.onSet({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions});
});
});
}
// bind hook for change
if(ctrl.onChange) {
sliderInstance.noUiSlider.on('change', function (values, handle, unencoded, tap, positions) {
$timeout(function() {
ctrl.onChange({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions});
});
});
}
// bind hook for start
if(ctrl.onStart) {
sliderInstance.noUiSlider.on('start', function (values, handle, unencoded, tap, positions) {
$timeout(function() {
ctrl.onStart({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions});
});
});
}
// bind hook for end
if(ctrl.onEnd) {
sliderInstance.noUiSlider.on('end', function (values, handle, unencoded, tap, positions) {
$timeout(function() {
ctrl.onEnd({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions});
});
});
}
}
}
}
angular.module('umbraco.directives').component('umbRangeSlider', umbRangeSlider);
})();
@@ -152,6 +152,7 @@
@import "components/umb-number-badge.less";
@import "components/umb-progress-circle.less";
@import "components/umb-stylesheet.less";
@import "components/umb-range-slider.less";
@import "components/buttons/umb-button.less";
@import "components/buttons/umb-button-group.less";
@@ -29,6 +29,11 @@
opacity: 0;
}
.umb-button .umb-button__caret {
margin-top: 0;
margin-left: 5px;
}
.umb-button__progress {
position: absolute;
left: 50%;
@@ -39,4 +39,8 @@
.umb-querybuilder .query-items > * {
flex: 0 1 auto;
margin: 5px;
}
}
.umb-querybuilder .query-items .btn {
min-height: 2rem;
}
@@ -0,0 +1,39 @@
.umb-range-slider.noUi-target {
background: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
border-radius: 20px;
height: 10px;
border: none;
}
.umb-range-slider .noUi-handle {
border-radius: 100px;
border: none;
box-shadow: none;
width: 20px !important;
height: 20px !important;
background-color: @turquoise;
}
.umb-range-slider .noUi-handle::before {
display: none;
}
.umb-range-slider .noUi-handle::after {
display: none;
}
.umb-range-slider .noUi-handle {
right: -10px !important; // half the handle width
}
.umb-range-slider .noUi-marker-large.noUi-marker-horizontal {
height: 10px;
}
.umb-range-slider .noUi-marker.noUi-marker-horizontal {
width: 1px;
}
@@ -15,9 +15,7 @@
vm.conditions = [];
vm.datePickerConfig = {
pickDate: true,
pickTime: false,
format: "YYYY-MM-DD"
dateFormat: "Y-m-d"
};
vm.chooseSource = chooseSource;
@@ -180,9 +178,10 @@
throttledFunc();
}
function datePickerChange(event, filter) {
if (event.date && event.date.isValid()) {
filter.constraintValue = event.date.format(vm.datePickerConfig.format);
function datePickerChange(date, filter) {
const momentDate = moment(date);
if (momentDate && momentDate.isValid()) {
filter.constraintValue = momentDate.format(vm.datePickerConfig.format);
throttledFunc();
}
}
@@ -23,26 +23,36 @@
<span><localize key="template_iWant">I want</localize></span>
<div class="btn-group">
<a class="btn btn-link dropdown-toggle" data-toggle="dropdown" href="#">
{{vm.query.contentType.name}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li ng-repeat="contentType in vm.contentTypes">
<a href ng-click="vm.setContentType(contentType)">{{contentType.name}}</a>
</li>
</ul>
<umb-button
type="button"
size="xs"
button-style="outline"
action="vm.contentTypeSelectOpen = !vm.contentTypeSelectOpen"
label="{{vm.query.contentType.name}}"
show-caret="true">
</umb-button>
<umb-dropdown ng-if="vm.contentTypeSelectOpen" on-close="vm.contentTypeSelectOpen = false">
<umb-dropdown-item ng-repeat="contentType in vm.contentTypes">
<a href ng-click="vm.setContentType(contentType); vm.contentTypeSelectOpen = false;">
{{contentType.name}}
</a>
</umb-dropdown-item>
</umb-dropdown>
</div>
<span><localize key="template_from">from</localize></span>
<a href class="btn btn-link" ng-click="vm.chooseSource(vm.query)">
{{vm.query.source.name}}
<span class="caret"></span>
</a>
<umb-button
type="button"
button-style="outline"
size="xs"
action="vm.chooseSource(vm.query)"
label="{{vm.query.source.name}}"
add-ellipsis="true">
</umb-button>
</div>
@@ -56,52 +66,62 @@
</span>
<div class="btn-group">
<a class="btn btn-link dropdown-toggle" data-toggle="dropdown" href="#">
{{filter.property.name}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li ng-repeat="property in vm.properties">
<a href ng-click="vm.setFilterProperty(filter, property)">
<umb-button
type="button"
button-style="outline"
size="xs"
action="vm.propertyFilterOpen = !vm.propertyFilterOpen"
label="{{filter.property.name}}"
show-caret="true">
</umb-button>
<umb-dropdown ng-if="vm.propertyFilterOpen" on-close="vm.propertyFilterOpen = false">
<umb-dropdown-item ng-repeat="property in vm.properties">
<a href ng-click="vm.setFilterProperty(filter, property); vm.propertyFilterOpen = false;">
{{property.name}}
</a>
</li>
</ul>
</umb-dropdown-item>
</umb-dropdown>
</div>
<div class="btn-group" ng-if="filter.property">
<a class="btn btn-link dropdown-toggle" data-toggle="dropdown" href="#">
{{filter.term.name}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li ng-repeat="term in vm.getPropertyOperators(filter.property)">
<a href ng-click="vm.setFilterTerm(filter, term)">
<umb-button
type="button"
size="xs"
button-style="outline"
action="vm.termFilterOpen = !vm.termFilterOpen"
label="{{filter.term.name}}"
show-caret="true">
</umb-button>
<umb-dropdown ng-if="vm.termFilterOpen" on-close="vm.termFilterOpen = false">
<umb-dropdown-item ng-repeat="term in vm.getPropertyOperators(filter.property)">
<a href ng-click="vm.setFilterTerm(filter, term); vm.termFilterOpen = false;">
{{term.name}}
</a>
</li>
</ul>
</umb-dropdown-item>
</umb-dropdown>
</div>
<span ng-switch="filter.term.appliesTo[0]">
<!-- Filter term types (string, int, date) -->
<input type="text" ng-switch-when="string" style="width:90px;" ng-model="filter.constraintValue" ng-change="vm.changeConstraintValue()" />
<input type="number" ng-switch-when="int" style="width:90px;" ng-model="filter.constraintValue" ng-change="vm.changeConstraintValue()" />
<input type="text" ng-switch-when="string" style="width:90px; margin-bottom: 0;" ng-model="filter.constraintValue" ng-change="vm.changeConstraintValue()" />
<input type="number" ng-switch-when="int" style="width:90px; margin-bottom: 0;" ng-model="filter.constraintValue" ng-change="vm.changeConstraintValue()" />
<span ng-switch-when="datetime">
<umb-date-time-picker options="vm.datePickerConfig"
on-change="vm.datePickerChange(event, filter)">
</umb-date-time-picker>
<umb-flatpickr
options="vm.datePickerConfig"
on-change="vm.datePickerChange(dateStr, filter)">
</umb-flatpickr>
</span>
</span>
<a href ng-click="vm.addFilter(vm.query)">
<i class="icon-add"></i>
</a>
@@ -112,30 +132,38 @@
</div>
<div class="query-items">
<span><localize key="template_orderBy">order by</localize></span>
<div class="btn-group">
<a class="btn btn-link dropdown-toggle" data-toggle="dropdown" href="#">
{{vm.query.sort.property.name}}
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li ng-repeat="property in vm.properties">
<a href ng-click="vm.setSortProperty(vm.query, property)">
{{property.name}}
</a>
</li>
</ul>
<umb-button
type="button"
size="xs"
button-style="outline"
action="vm.sortPropertyOpen = !vm.sortPropertyOpen"
label="{{vm.query.sort.property.name}}"
show-caret="true">
</umb-button>
<umb-dropdown ng-if="vm.sortPropertyOpen" on-close="vm.sortPropertyOpen = false">
<umb-dropdown-item ng-repeat="property in vm.properties">
<a href ng-click="vm.setSortProperty(vm.query, property); vm.sortPropertyOpen = false;">
{{property.name}}
</a>
</umb-dropdown-item>
</umb-dropdown>
</div>
<a href class="btn" ng-click="vm.changeSortOrder(vm.query)">
{{vm.query.sort.translation.currentLabel}}
</a>
<umb-button
ng-show="vm.query.sort.property.name"
type="button"
button-style="outline"
action="vm.changeSortOrder(vm.query)"
label="{{vm.query.sort.translation.currentLabel}}">
</umb-button>
</div>
</div>
@@ -148,9 +176,7 @@
</li>
</ul>
<pre>
{{model.result.queryExpression}}
</pre>
<pre>{{model.result.queryExpression}}</pre>
</div>
@@ -16,17 +16,20 @@
add-ellipsis={{defaultButton.addEllipsis}}>
</umb-button>
<a data-element="button-group-toggle" class="btn btn-{{buttonStyle}} dropdown-toggle umb-button-group__toggle umb-button--{{size}}" data-toggle="dropdown" ng-if="subButtons.length > 0">
<a data-element="button-group-toggle"
class="btn btn-{{buttonStyle}} dropdown-toggle umb-button-group__toggle umb-button--{{size}}"
ng-if="subButtons.length > 0"
ng-click="toggleDropdown()">
<span class="caret"></span>
</a>
<ul aria-labelledby="dLabel" class="dropdown-menu bottom-up umb-button-group__sub-buttons" ng-if="subButtons.length > 0" role="menu" ng-class="{'-align-right': float === 'right'}">
<li ng-repeat="subButton in subButtons">
<a data-element="{{subButton.alias ? 'button-' + subButton.alias : 'button-group-secondary-' + $index }}" href="#" ng-click="subButton.handler()" hotkey="{{subButton.hotKey}}" hotkey-when-hidden="{{subButton.hotKeyWhenHidden}}" prevent-default>
<umb-dropdown ng-if="subButtons.length > 0 && dropdown.isOpen" class="umb-button-group__sub-buttons" on-close="closeDropdown()" ng-class="{'-align-right': float === 'right'}">
<umb-dropdown-item ng-repeat="subButton in subButtons">
<a data-element="{{subButton.alias ? 'button-' + subButton.alias : 'button-group-secondary-' + $index }}" href="#" ng-click="executeMenuItem(subButton)" hotkey="{{subButton.hotKey}}" hotkey-when-hidden="{{subButton.hotKeyWhenHidden}}" prevent-default>
<localize key="{{subButton.labelKey}}">{{subButton.labelKey}}</localize>
<span ng-if="subButton.addEllipsis === 'true'">...</span>
</a>
</li>
</ul>
</umb-dropdown-item>
</umb-dropdown>
</div>
@@ -11,6 +11,7 @@
<span class="umb-button__content" ng-class="{'-hidden': vm.innerState !== 'init'}">
<i ng-if="vm.icon" class="{{vm.icon}} umb-button__icon"></i>
{{vm.buttonLabel}}
<span ng-if="vm.showCaret" class="umb-button__caret caret"></span>
</span>
</a>
@@ -18,6 +19,7 @@
<span class="umb-button__content" ng-class="{'-hidden': vm.innerState !== 'init'}">
<i ng-if="vm.icon" class="{{vm.icon}} umb-button__icon"></i>
{{vm.buttonLabel}}
<span ng-if="vm.showCaret" class="umb-button__caret caret"></span>
</span>
</button>
@@ -25,6 +27,7 @@
<span class="umb-button__content" ng-class="{'-hidden': vm.innerState !== 'init'}">
<i ng-if="vm.icon" class="{{vm.icon}} umb-button__icon"></i>
{{vm.buttonLabel}}
<span ng-if="vm.showCaret" class="umb-button__caret caret"></span>
</span>
</button>
@@ -1,22 +1,20 @@
<div class="pull-right" style="position: relative;">
<!-- options button -->
<a class="btn btn-info" href="#" ng-click="getOptions()" prevent-default data-toggle="dropdown">
<localize key="general_actions">Actions</localize>
<span class="caret"></span>
</a>
<umb-button
type="button"
button-style="info"
action="dropdown.isOpen = !dropdown.isOpen"
label-key="general_actions"
show-caret="true">
</umb-button>
<!-- actions -->
<ul class="dropdown-menu umb-actions" role="menu" aria-labelledby="dLabel">
<li class="umb-action" ng-class="{'sep':action.seperatorm, '-opens-dialog': action.opensDialog}" ng-repeat="action in actions">
<!-- How does this reference executeMenuItem() i really don't think that this is very clear -->
<a prevent-default
class="umb-action-link"
ng-click="executeMenuItem(action)">
<umb-dropdown ng-if="dropdown.isOpen" class="umb-actions" on-close="dropdown.isOpen = false">
<umb-dropdown-item class="umb-action" ng-class="{'sep':action.seperatorm, '-opens-dialog': action.opensDialog}" ng-repeat="action in actions">
<a href="" ng-click="executeMenuItem(action)" prevent-default>
<i class="icon icon-{{action.cssclass}}"></i>
<span class="menu-label">{{action.name}}</span>
</a>
</li>
</ul>
</umb-dropdown-item>
</umb-dropdown>
</div>
@@ -63,6 +63,8 @@
vm.labels.permissionsSetForGroup = value;
});
setViewSate("managePermissions");
// hide dropdown
vm.groupsDropdownOpen = false;
}
function assignGroupPermissions(group) {
@@ -27,20 +27,24 @@
<p class="abstract" style="margin-bottom: 20px;"><localize key="defaultdialogs_permissionsHelp"></localize></p>
<div style="position: relative; display: inline-block; margin-bottom: 20px;">
<umb-button
type="button"
button-style="info"
label-key="defaultdialogs_permissionsSet"
action="vm.groupsDropdownOpen = !vm.groupsDropdownOpen"
add-ellipsis="true">
</umb-button>
<a class="btn btn-info dropdown-toggle" data-toggle="dropdown" href="#">
<localize key="defaultdialogs_permissionsSet">Set permissions for</localize>...
<span class="caret" style="margin-left: 10px;"></span>
</a>
<ul class="dropdown-menu">
<li ng-repeat="group in vm.availableUserGroups | filter:{selected: '!true'}">
<umb-dropdown ng-if="vm.groupsDropdownOpen" on-close="vm.groupsDropdownOpen = false">
<umb-dropdown-item ng-repeat="group in vm.availableUserGroups | filter:{selected: '!true'}">
<a href="" ng-click="vm.editPermissions(group)" prevent-default>
<i class="{{group.icon}}"></i>
{{group.name}}
</a>
</li>
</ul>
</umb-dropdown-item>
</umb-dropdown>
</div>
<div umb-set-dirty-on-change="{{vm.selectedUserGroups}}">
@@ -1,38 +1,81 @@
function dateTimePickerController($scope, notificationsService, assetsService, angularHelper, userService, $element, dateHelper) {
//setup the default config
var config = {
pickDate: true,
pickTime: true,
useSeconds: true,
format: "YYYY-MM-DD HH:mm:ss",
icons: {
time: "icon-time",
date: "icon-calendar",
up: "icon-chevron-up",
down: "icon-chevron-down"
}
let flatPickr = null;
};
function onInit() {
$scope.hasDatetimePickerValue = $scope.model.value ? true : false;
$scope.model.datetimePickerValue = null;
$scope.serverTime = null;
$scope.serverTimeNeedsOffsetting = false;
// setup the default config
var config = {
pickDate: true,
pickTime: true,
useSeconds: true,
format: "YYYY-MM-DD HH:mm:ss",
icons: {
time: "icon-time",
date: "icon-calendar",
up: "icon-chevron-up",
down: "icon-chevron-down"
}
};
// map the user config
$scope.model.config = angular.extend(config, $scope.model.config);
// ensure the format doesn't get overwritten with an empty string
if ($scope.model.config.format === "" || $scope.model.config.format === undefined || $scope.model.config.format === null) {
$scope.model.config.format = $scope.model.config.pickTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD";
}
// check whether a server time offset is needed
if (Umbraco.Sys.ServerVariables.application.serverTimeOffset !== undefined) {
// Will return something like 120
var serverOffset = Umbraco.Sys.ServerVariables.application.serverTimeOffset;
// Will return something like -120
var localOffset = new Date().getTimezoneOffset();
// If these aren't equal then offsetting is needed
// note the minus in front of serverOffset needed
// because C# and javascript return the inverse offset
$scope.serverTimeNeedsOffsetting = (-serverOffset !== localOffset);
}
const dateFormat = $scope.model.config.pickTime ? "Y-m-d H:i:S" : "Y-m-d";
// date picker config
$scope.datePickerConfig = {
enableTime: $scope.model.config.pickTime,
dateFormat: dateFormat,
time_24hr: true
};
setDatePickerVal();
//map the user config
$scope.model.config = angular.extend(config, $scope.model.config);
//ensure the format doesn't get overwritten with an empty string
if ($scope.model.config.format === "" || $scope.model.config.format === undefined || $scope.model.config.format === null) {
$scope.model.config.format = $scope.model.config.pickTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD";
}
$scope.hasDatetimePickerValue = $scope.model.value ? true : false;
$scope.datetimePickerValue = null;
//hide picker if clicking on the document
$scope.hidePicker = function () {
//$element.find("div:first").datetimepicker("hide");
// Sometimes the statement above fails and generates errors in the browser console. The following statements fix that.
var dtp = $element.find("div:first");
if (dtp && dtp.datetimepicker) {
dtp.datetimepicker("hide");
$scope.clearDate = function() {
$scope.hasDatetimePickerValue = false;
if($scope.model) {
$scope.model.datetimePickerValue = null;
$scope.model.value = null;
}
if($scope.datePickerForm && $scope.datePickerForm.datepicker) {
$scope.datePickerForm.datepicker.$setValidity("pickerError", true);
}
}
$scope.datePickerSetup = function(instance) {
flatPickr = instance;
};
$scope.datePickerChange = function(date) {
setDate(date);
setDatePickerVal();
};
//here we declare a special method which will be called whenever the value has changed from the server
@@ -44,53 +87,45 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
var newDate = moment(newVal);
if (newDate.isAfter(minDate)) {
applyDate({ date: moment(newVal) });
setDate(newVal);
} else {
$scope.clearDate();
}
}
};
//handles the date changing via the date picker
function applyDate(e) {
function setDate(date) {
const momentDate = moment(date);
angularHelper.safeApply($scope, function() {
// when a date is changed, update the model
if (e.date && e.date.isValid()) {
if (momentDate && momentDate.isValid()) {
$scope.datePickerForm.datepicker.$setValidity("pickerError", true);
$scope.hasDatetimePickerValue = true;
$scope.datetimePickerValue = e.date.format($scope.model.config.format);
$scope.model.datetimePickerValue = momentDate.format($scope.model.config.format);
}
else {
$scope.hasDatetimePickerValue = false;
$scope.datetimePickerValue = null;
}
setModelValue();
if (!$scope.model.config.pickTime) {
$element.find("div:first").datetimepicker("hide", 0);
$scope.model.datetimePickerValue = null;
}
updateModelValue(date);
});
}
//sets the scope model value accordingly - this is the value to be sent up to the server and depends on
// if the picker is configured to offset time. We always format the date/time in a specific format for sending
// to the server, this is different from the format used to display the date/time.
function setModelValue() {
function updateModelValue(date) {
const momentDate = moment(date);
if ($scope.hasDatetimePickerValue) {
var elementData = $element.find("div:first").data().DateTimePicker;
if ($scope.model.config.pickTime) {
//check if we are supposed to offset the time
if ($scope.model.value && Object.toBoolean($scope.model.config.offsetTime) && Umbraco.Sys.ServerVariables.application.serverTimeOffset !== undefined) {
$scope.model.value = dateHelper.convertToServerStringTime(elementData.getDate(), Umbraco.Sys.ServerVariables.application.serverTimeOffset);
$scope.serverTime = dateHelper.convertToServerStringTime(elementData.getDate(), Umbraco.Sys.ServerVariables.application.serverTimeOffset, "YYYY-MM-DD HH:mm:ss Z");
$scope.model.value = dateHelper.convertToServerStringTime(momentDate, Umbraco.Sys.ServerVariables.application.serverTimeOffset);
$scope.serverTime = dateHelper.convertToServerStringTime(momentDate, Umbraco.Sys.ServerVariables.application.serverTimeOffset, "YYYY-MM-DD HH:mm:ss Z");
}
else {
$scope.model.value = elementData.getDate().format("YYYY-MM-DD HH:mm:ss");
$scope.model.value = momentDate.format("YYYY-MM-DD HH:mm:ss");
}
}
else {
$scope.model.value = elementData.getDate().format("YYYY-MM-DD");
$scope.model.value = momentDate.format("YYYY-MM-DD");
}
}
else {
@@ -99,7 +134,7 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
}
/** Sets the value of the date picker control adn associated viewModel objects based on the model value */
function setDatePickerVal(element) {
function setDatePickerVal() {
if ($scope.model.value) {
var dateVal;
//check if we are supposed to offset the time
@@ -112,98 +147,21 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
//create a normal moment , no offset required
var dateVal = $scope.model.value ? moment($scope.model.value, "YYYY-MM-DD HH:mm:ss") : moment();
}
element.datetimepicker("setValue", dateVal);
$scope.datetimePickerValue = dateVal.format($scope.model.config.format);
$scope.model.datetimePickerValue = dateVal.format($scope.model.config.format);
}
else {
$scope.clearDate();
}
}
$scope.clearDate = function() {
$scope.hasDatetimePickerValue = false;
$scope.datetimePickerValue = null;
$scope.model.value = null;
$scope.datePickerForm.datepicker.$setValidity("pickerError", true);
}
$scope.serverTime = null;
$scope.serverTimeNeedsOffsetting = false;
if (Umbraco.Sys.ServerVariables.application.serverTimeOffset !== undefined) {
// Will return something like 120
var serverOffset = Umbraco.Sys.ServerVariables.application.serverTimeOffset;
// Will return something like -120
var localOffset = new Date().getTimezoneOffset();
// If these aren't equal then offsetting is needed
// note the minus in front of serverOffset needed
// because C# and javascript return the inverse offset
$scope.serverTimeNeedsOffsetting = (-serverOffset !== localOffset);
}
//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', $scope).then(function() {
var filesToLoad = ["lib/datetimepicker/bootstrap-datetimepicker.js"];
$scope.model.config.language = user.locale;
assetsService.load(filesToLoad, $scope).then(
function () {
//The Datepicker js and css files are available and all components are ready to use.
// Get the id of the datepicker button that was clicked
var pickerId = $scope.model.alias;
var element = $element.find("div:first");
// Create the datepicker and add a changeDate eventlistener
element
.datetimepicker(angular.extend({ useCurrent: true }, $scope.model.config))
.on("dp.change", applyDate)
.on("dp.error", function(a, b, c) {
$scope.hasDatetimePickerValue = false;
$scope.datePickerForm.datepicker.$setValidity("pickerError", false);
});
$(document).bind("click", $scope.hidePicker);
setDatePickerVal(element);
element.find("input").bind("blur", function() {
//we need to force an apply here
$scope.$apply();
});
$scope.$watch("model.value", function(newVal, oldVal) {
if (newVal !== oldVal) {
$scope.hasDatetimePickerValue = newVal ? true : false;
setDatePickerVal(element);
}
});
var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
setModelValue();
});
//Ensure to remove the event handler when this instance is destroyted
$scope.$on('$destroy', function () {
element.find("input").unbind("blur");
element.datetimepicker("destroy");
unsubscribe();
$(document).unbind("click", $scope.hidePicker);
});
});
});
$scope.$watch("model.value", function(newVal, oldVal) {
if (newVal !== oldVal) {
$scope.hasDatetimePickerValue = newVal ? true : false;
setDatePickerVal();
}
});
onInit();
}
@@ -1,17 +1,30 @@
<div class="umb-property-editor umb-datepicker" ng-controller="Umbraco.PropertyEditors.DatepickerController">
<ng-form name="datePickerForm">
<div class="input-append date datepicker" style="position: relative;" id="datepicker{{model.alias}}">
<input name="datepicker" data-format="{{model.config.format}}" id="{{model.alias}}" type="text"
ng-model="datetimePickerValue"
ng-required="model.validation.mandatory"
val-server="value"
class="datepickerinput" />
<div id="datepicker{{model.alias}}">
<span class="add-on">
<i class="icon-calendar"></i>
</span>
<umb-flatpickr
ng-model="model.value"
options="datePickerConfig"
on-setup="datePickerSetup(fpItem)"
on-change="datePickerChange(dateStr)">
<div class="input-append">
<input
name="datepicker"
id="{{model.alias}}"
type="text"
ng-model="model.datetimePickerValue"
ng-required="model.validation.mandatory"
val-server="value"
class="datepickerinput">
<span class="add-on">
<i class="icon-calendar"></i>
</span>
</div>
</umb-flatpickr>
</div>
@@ -1,13 +0,0 @@
<div>
<ng-form name="sliderHandleForm">
<select ng-model="model.value" name="handle">
<option value="round">Round</option>
<option value="square">Square</option>
<option value="triangle">Triangle</option>
</select>
<span ng-messages="sliderHandleForm.handle.$error" show-validation-on-submit >
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
</span>
</ng-form>
</div>
@@ -1,12 +0,0 @@
<div>
<ng-form name="sliderOrientationForm">
<select ng-model="model.value" required name="orientation">
<option value="horizontal">Horizontal</option>
<option value="vertical">Vertical</option>
</select>
<span ng-messages="sliderOrientationForm.orientation.$error" show-validation-on-submit >
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
</span>
</ng-form>
</div>
@@ -1,248 +1,80 @@
function sliderController($scope, $log, $element, assetsService, angularHelper) {
function sliderController($scope) {
var sliderRef = null;
let sliderRef = null;
/** configure some defaults on init */
function configureDefaults() {
if (!$scope.model.config.orientation) {
$scope.model.config.orientation = "horizontal";
}
if (!$scope.model.config.enableRange) {
$scope.model.config.enableRange = false;
}
else {
$scope.model.config.enableRange = Object.toBoolean($scope.model.config.enableRange);
}
if (!$scope.model.config.initVal1) {
$scope.model.config.initVal1 = 0;
}
else {
$scope.model.config.initVal1 = parseFloat($scope.model.config.initVal1);
}
if (!$scope.model.config.initVal2) {
$scope.model.config.initVal2 = 0;
}
else {
$scope.model.config.initVal2 = parseFloat($scope.model.config.initVal2);
}
if (!$scope.model.config.minVal) {
$scope.model.config.minVal = 0;
}
else {
$scope.model.config.minVal = parseFloat($scope.model.config.minVal);
}
if (!$scope.model.config.maxVal) {
$scope.model.config.maxVal = 100;
}
else {
$scope.model.config.maxVal = parseFloat($scope.model.config.maxVal);
}
if (!$scope.model.config.step) {
$scope.model.config.step = 1;
}
else {
$scope.model.config.step = parseFloat($scope.model.config.step);
}
if (!$scope.model.config.handle) {
$scope.model.config.handle = "round";
}
if (!$scope.model.config.reversed) {
$scope.model.config.reversed = false;
}
else {
$scope.model.config.reversed = Object.toBoolean($scope.model.config.reversed);
}
if (!$scope.model.config.tooltip) {
$scope.model.config.tooltip = "show";
}
if (!$scope.model.config.tooltipSplit) {
$scope.model.config.tooltipSplit = false;
}
else {
$scope.model.config.tooltipSplit = Object.toBoolean($scope.model.config.tooltipSplit);
}
if ($scope.model.config.tooltipFormat) {
$scope.model.config.formatter = function (value) {
if (angular.isArray(value) && $scope.model.config.enableRange) {
return $scope.model.config.tooltipFormat.replace("{0}", value[0]).replace("{1}", value[1]);
} else {
return $scope.model.config.tooltipFormat.replace("{0}", value);
}
}
}
if (!$scope.model.config.ticks) {
$scope.model.config.ticks = [];
}
else if (angular.isString($scope.model.config.ticks)) {
// returns comma-separated string to an array, e.g. [0, 100, 200, 300, 400]
$scope.model.config.ticks = _.map($scope.model.config.ticks.split(','), function (item) {
return parseInt(item.trim());
});
}
if (!$scope.model.config.ticksPositions) {
$scope.model.config.ticksPositions = [];
}
else if (angular.isString($scope.model.config.ticksPositions)) {
// returns comma-separated string to an array, e.g. [0, 30, 60, 70, 90, 100]
$scope.model.config.ticksPositions = _.map($scope.model.config.ticksPositions.split(','), function (item) {
return parseInt(item.trim());
});
}
if (!$scope.model.config.ticksLabels) {
$scope.model.config.ticksLabels = [];
}
else if (angular.isString($scope.model.config.ticksLabels)) {
// returns comma-separated string to an array, e.g. ['$0', '$100', '$200', '$300', '$400']
$scope.model.config.ticksLabels = _.map($scope.model.config.ticksLabels.split(','), function (item) {
return item.trim();
});
}
if (!$scope.model.config.ticksSnapBounds) {
$scope.model.config.ticksSnapBounds = 0;
}
else {
$scope.model.config.ticksSnapBounds = parseFloat($scope.model.config.ticksSnapBounds);
}
$scope.model.config.enableRange = $scope.model.config.enableRange ? Object.toBoolean($scope.model.config.enableRange) : false;
$scope.model.config.initVal1 = $scope.model.config.initVal1 ? parseFloat($scope.model.config.initVal1) : 0;
$scope.model.config.initVal2 = $scope.model.config.initVal2 ? parseFloat($scope.model.config.initVal2) : 0;
$scope.model.config.minVal = $scope.model.config.minVal ? parseFloat($scope.model.config.minVal) : 0;
$scope.model.config.maxVal = $scope.model.config.maxVal ? parseFloat($scope.model.config.maxVal) : 100;
$scope.model.config.step = $scope.model.config.step ? parseFloat($scope.model.config.step) : 1;
}
function getValueForSlider(val) {
if (!angular.isArray(val)) {
val = val.toString().split(",");
}
var val1 = val[0];
var val2 = val.length > 1 ? val[1] : null;
//configure the model value based on if range is enabled or not
if ($scope.model.config.enableRange == true) {
var i1 = parseFloat(val1);
var i2 = parseFloat(val2);
return [
isNaN(i1) ? $scope.model.config.minVal : (i1 >= $scope.model.config.minVal ? i1 : $scope.model.config.minVal),
isNaN(i2) ? $scope.model.config.maxVal : (i2 >= i1 ? (i2 <= $scope.model.config.maxVal ? i2 : $scope.model.config.maxVal) : $scope.model.config.maxVal)
];
}
else {
return parseFloat(val1);
}
function setModelValue(values) {
$scope.model.value = values ? values.toString() : null;
}
/** This creates the slider with the model values - it's called on startup and returns a reference to the slider object */
function createSlider() {
$scope.setup = function(slider) {
sliderRef = slider;
};
//the value that we'll give the slider - if it's a range, we store our value as a comma separated val but this slider expects an array
var sliderVal = null;
//configure the model value based on if range is enabled or not
if ($scope.model.config.enableRange == true) {
//If no value saved yet - then use default value
//If it contains a single value - then also create a new array value
if (!$scope.model.value || $scope.model.value.indexOf(",") == -1) {
sliderVal = getValueForSlider([$scope.model.config.initVal1, $scope.model.config.initVal2]);
}
else {
//this will mean it's a delimited value stored in the db, convert it to an array
sliderVal = getValueForSlider($scope.model.value.split(','));
}
}
else {
//If no value saved yet - then use default value
if ($scope.model.value) {
sliderVal = getValueForSlider($scope.model.value);
}
else {
sliderVal = getValueForSlider($scope.model.config.initVal1);
}
}
//initiate slider, add event handler and get the instance reference (stored in data)
var slider = $element.find('.slider-item').bootstrapSlider({
max: $scope.model.config.maxVal,
min: $scope.model.config.minVal,
orientation: $scope.model.config.orientation,
selection: $scope.model.config.reversed ? "after" : "before",
step: $scope.model.config.step,
precision: $scope.model.config.precision,
tooltip: $scope.model.config.tooltip,
tooltip_split: $scope.model.config.tooltipSplit,
tooltip_position: $scope.model.config.tooltipPosition,
handle: $scope.model.config.handle,
reversed: $scope.model.config.reversed,
ticks: $scope.model.config.ticks,
ticks_positions: $scope.model.config.ticksPositions,
ticks_labels: $scope.model.config.ticksLabels,
ticks_snap_bounds: $scope.model.config.ticksSnapBounds,
formatter: $scope.model.config.formatter,
range: $scope.model.config.enableRange,
//set the slider val - we cannot do this with data- attributes when using ranges
value: sliderVal
});
slider.on('slideStop', function (e) {
var value = e.value;
angularHelper.safeApply($scope, function () {
$scope.model.value = getModelValueFromSlider(value);
});
}).data('slider');
return slider;
}
function getModelValueFromSlider(sliderVal) {
//Get the value from the slider and format it correctly, if it is a range we want a comma delimited value
if ($scope.model.config.enableRange == true) {
return sliderVal.join(",");
}
else {
return sliderVal.toString();
}
}
$scope.end = function(values) {
setModelValue(values);
};
function init() {
// convert to array
$scope.sliderValue = $scope.model.value ? $scope.model.value.split(',') : null;
configureDefaults();
//tell the assetsService to load the bootstrap slider
//libs from the plugin folder
assetsService
.loadJs("lib/slider/js/bootstrap-slider.js")
.then(function () {
// format config to fit slider plugin
const start = $scope.model.config.enableRange ? [$scope.model.config.initVal1, $scope.model.config.initVal2] : [$scope.model.config.initVal1];
const step = $scope.model.config.step;
const tooltips = $scope.model.config.enableRange ? [true, true] : [true];
const min = $scope.model.config.minVal ? [$scope.model.config.minVal] : [$scope.model.config.minVal];
const max = $scope.model.config.maxVal ? [$scope.model.config.maxVal] : [$scope.model.config.maxVal];
var slider = createSlider();
// Initialize model value if not set
if (!$scope.model.value) {
var sliderVal = slider.bootstrapSlider('getValue');
$scope.model.value = getModelValueFromSlider(sliderVal);
// setup default
$scope.sliderOptions = {
"start": start,
"step": step,
"tooltips": tooltips,
"format": {
to: function (value) {
return Math.round(value);
},
from: function (value) {
return Math.round(value);
}
},
"range": {
"min": min,
"max": max
},
"pips": {
mode: 'steps',
density: 100,
filter: filterPips
}
};
//watch for the model value being changed and update the slider value when it does
$scope.$watch("model.value", function (newVal, oldVal) {
if (newVal != oldVal) {
var sliderVal = getModelValueFromSlider(slider.bootstrapSlider('getValue'));
if (newVal !== sliderVal) {
slider.bootstrapSlider('setValue', getValueForSlider(newVal));
}
}
});
function filterPips(value) {
// show a pip for min and maximum value
return value === $scope.model.config.minVal || value === $scope.model.config.maxVal ? 1 : -1;
}
});
//load the separate css for the editor to avoid it blocking our js loading
assetsService.loadCss("lib/slider/bootstrap-slider.css", $scope);
assetsService.loadCss("lib/slider/bootstrap-slider-custom.css", $scope);
}
$scope.$watch('model.value', function(newValue, oldValue){
if(newValue && newValue !== oldValue) {
$scope.sliderValue = newValue.split(',');
sliderRef.noUiSlider.set($scope.sliderValue);
}
})
init();
@@ -1,5 +1,12 @@
<div ng-controller="Umbraco.PropertyEditors.SliderController">
<input type="text" name="slider" class="slider-item" />
<div style="padding-top: 50px; padding-bottom: 40px; width: 66.6%">
<umb-range-slider
ng-model="sliderValue"
options="sliderOptions"
on-setup="setup(slider)"
on-end="end(values)">
</umb-range-slider>
</div>
</div>
@@ -1,13 +0,0 @@
<div>
<ng-form name="sliderTooltipForm">
<select ng-model="model.value" required name="tooltip">
<option value="show">Show</option>
<option value="hide">Hide</option>
<option value="always">Always</option>
</select>
<span ng-messages="sliderTooltipForm.tooltip.$error" show-validation-on-submit >
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
</span>
</ng-form>
</div>
+4 -2
View File
@@ -21,18 +21,20 @@ namespace Umbraco.Web.Actions
internal IEnumerable<IAction> GetByLetters(IEnumerable<string> letters)
{
var actions = this.ToArray(); // no worry: internally, it's already an array
return letters
.Where(x => x.Length == 1)
.Select(x => this.FirstOrDefault(y => y.Letter == x[0]))
.Select(x => actions.FirstOrDefault(y => y.Letter == x[0]))
.WhereNotNull()
.ToList();
}
internal IReadOnlyList<IAction> FromEntityPermission(EntityPermission entityPermission)
{
var actions = this.ToArray(); // no worry: internally, it's already an array
return entityPermission.AssignedPermissions
.Where(x => x.Length == 1)
.SelectMany(x => this.Where(y => y.Letter == x[0]))
.SelectMany(x => actions.Where(y => y.Letter == x[0]))
.WhereNotNull()
.ToList();
}
@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Composing;
namespace Umbraco.Web.Actions
{
internal class ActionCollectionBuilder : LazyCollectionBuilderBase<ActionCollectionBuilder, ActionCollection, IAction>
@@ -1,44 +1,84 @@
using System.Reflection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Http.Controllers;
using System.Web.Mvc;
using Umbraco.Core.Components;
using Umbraco.Core.Composing;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Composing.Composers
{
internal static class ControllersComposer
{
/// <summary>
/// Registers all IControllers using the TypeLoader for scanning and caching found instances for the calling assembly
/// Registers Umbraco controllers.
/// </summary>
public static Composition ComposeMvcControllers(this Composition composition, Assembly assembly)
public static Composition ComposeUmbracoControllers(this Composition composition, Assembly umbracoWebAssembly)
{
//TODO: We've already scanned for UmbracoApiControllers and SurfaceControllers - should we scan again
// for all controllers? Seems like we should just do this once and then filter. That said here we are
// only scanning our own single assembly. Hrm.
// notes
//
// We scan and auto-registers:
// - every IController and IHttpController that *we* have in Umbraco.Web
// - PluginController and UmbracoApiController in every assembly
//
// We do NOT scan:
// - any IController or IHttpController (anything not PluginController nor UmbracoApiController), outside of Umbraco.Web
// which means that users HAVE to explicitly register their own non-Umbraco controllers
//
// This is because we try to achieve a balance between "simple" and "fast. Scanning for PluginController or
// UmbracoApiController is fast-ish because they both are IDiscoverable. Scanning for IController or IHttpController
// is a full, non-cached scan = expensive, we do it only for 1 assembly.
//
// TODO
// find a way to scan for IController *and* IHttpController in one single pass
// or, actually register them manually so don't require a full scan for these
// 5 are IController but not PluginController
// Umbraco.Web.Mvc.RenderMvcController
// Umbraco.Web.Install.Controllers.InstallController
// Umbraco.Web.Macros.PartialViewMacroController
// Umbraco.Web.Editors.PreviewController
// Umbraco.Web.Editors.BackOfficeController
// 9 are IHttpController but not UmbracoApiController
// Umbraco.Web.Controllers.UmbProfileController
// Umbraco.Web.Controllers.UmbLoginStatusController
// Umbraco.Web.Controllers.UmbRegisterController
// Umbraco.Web.Controllers.UmbLoginController
// Umbraco.Web.Mvc.RenderMvcController
// Umbraco.Web.Install.Controllers.InstallController
// Umbraco.Web.Macros.PartialViewMacroController
// Umbraco.Web.Editors.PreviewController
// Umbraco.Web.Editors.BackOfficeController
composition.RegisterControllers<IController>(assembly);
// scan and register every IController in Umbraco.Web
var umbracoWebControllers = composition.TypeLoader.GetTypes<IController>(specificAssemblies: new[] { umbracoWebAssembly });
//foreach (var controller in umbracoWebControllers.Where(x => !typeof(PluginController).IsAssignableFrom(x)))
// Current.Logger.Debug(typeof(LightInjectExtensions), "IController NOT PluginController: " + controller.FullName);
composition.RegisterControllers(umbracoWebControllers);
// scan and register every PluginController in everything (PluginController is IDiscoverable and IController)
var nonUmbracoWebPluginController = composition.TypeLoader.GetTypes<PluginController>().Where(x => x.Assembly != umbracoWebAssembly);
composition.RegisterControllers(nonUmbracoWebPluginController);
// scan and register every IHttpController in Umbraco.Web
var umbracoWebHttpControllers = composition.TypeLoader.GetTypes<IHttpController>(specificAssemblies: new[] { umbracoWebAssembly });
//foreach (var controller in umbracoWebControllers.Where(x => !typeof(UmbracoApiController).IsAssignableFrom(x)))
// Current.Logger.Debug(typeof(LightInjectExtensions), "IHttpController NOT UmbracoApiController: " + controller.FullName);
composition.RegisterControllers(umbracoWebHttpControllers);
// scan and register every UmbracoApiController in everything (UmbracoApiController is IDiscoverable and IHttpController)
var nonUmbracoWebApiControllers = composition.TypeLoader.GetTypes<UmbracoApiController>().Where(x => x.Assembly != umbracoWebAssembly);
composition.RegisterControllers(nonUmbracoWebApiControllers);
return composition;
}
/// <summary>
/// Registers all IHttpController using the TypeLoader for scanning and caching found instances for the calling assembly
/// </summary>
public static Composition ComposeApiControllers(this Composition composition, Assembly assembly)
private static void RegisterControllers<TController>(this Composition composition, IEnumerable<Type> controllerTypes)
{
//TODO: We've already scanned for UmbracoApiControllers and SurfaceControllers - should we scan again
// for all controllers? Seems like we should just do this once and then filter. That said here we are
// only scanning our own single assembly. Hrm.
composition.RegisterControllers<IHttpController>(assembly);
return composition;
}
private static void RegisterControllers<TController>(this Composition composition, Assembly assembly)
{
var types = composition.TypeLoader.GetTypes<TController>(specificAssemblies: new[] { assembly });
foreach (var type in types)
composition.Register(type, Lifetime.Request);
foreach (var controllerType in controllerTypes)
composition.Register(controllerType, Lifetime.Request);
}
}
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
@@ -194,7 +195,7 @@ namespace Umbraco.Web.Editors
const int level = 0;
foreach (var dictionaryItem in Services.LocalizationService.GetRootDictionaryItems())
foreach (var dictionaryItem in Services.LocalizationService.GetRootDictionaryItems().OrderBy(ItemSort()))
{
var item = Mapper.Map<IDictionaryItem, DictionaryOverviewDisplay>(dictionaryItem);
item.Level = 0;
@@ -220,8 +221,7 @@ namespace Umbraco.Web.Editors
/// </param>
private void GetChildItemsForList(IDictionaryItem dictionaryItem, int level, List<DictionaryOverviewDisplay> list)
{
foreach (var childItem in Services.LocalizationService.GetDictionaryItemChildren(
dictionaryItem.Key))
foreach (var childItem in Services.LocalizationService.GetDictionaryItemChildren(dictionaryItem.Key).OrderBy(ItemSort()))
{
var item = Mapper.Map<IDictionaryItem, DictionaryOverviewDisplay>(childItem);
item.Level = level;
@@ -230,5 +230,7 @@ namespace Umbraco.Web.Editors
GetChildItemsForList(childItem, level + 1, list);
}
}
private Func<IDictionaryItem, string> ItemSort() => item => item.ItemKey;
}
}
@@ -121,8 +121,7 @@ namespace Umbraco.Web.Runtime
composition.ConfigureForWeb();
composition
.ComposeMvcControllers(GetType().Assembly)
.ComposeApiControllers(GetType().Assembly);
.ComposeUmbracoControllers(GetType().Assembly);
composition.WithCollectionBuilder<SearchableTreeCollectionBuilder>()
.Add(() => composition.TypeLoader.GetTypes<ISearchableTree>()); // fixme which searchable trees?!
@@ -2,6 +2,7 @@
using System.Linq;
using System.Net.Http.Formatting;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Web.Actions;
@@ -52,10 +53,12 @@ namespace Umbraco.Web.Trees
var nodes = new TreeNodeCollection();
Func<IDictionaryItem, string> ItemSort() => item => item.ItemKey;
if (id == Constants.System.Root.ToInvariantString())
{
nodes.AddRange(
Services.LocalizationService.GetRootDictionaryItems().Select(
Services.LocalizationService.GetRootDictionaryItems().OrderBy(ItemSort()).Select(
x => CreateTreeNode(
x.Id.ToInvariantString(),
id,
@@ -71,7 +74,7 @@ namespace Umbraco.Web.Trees
if (parentDictionary == null)
return nodes;
nodes.AddRange(Services.LocalizationService.GetDictionaryItemChildren(parentDictionary.Key).ToList().OrderByDescending(item => item.Key).Select(
nodes.AddRange(Services.LocalizationService.GetDictionaryItemChildren(parentDictionary.Key).ToList().OrderBy(ItemSort()).Select(
x => CreateTreeNode(
x.Id.ToInvariantString(),
id,