Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 350f60c056 | |||
| 954b5ac296 | |||
| 12787f0bd2 | |||
| 6cf15f42a8 | |||
| b5b971a189 | |||
| 26a026b17a | |||
| 9054b84c50 | |||
| 5cb5f13d08 | |||
| 89468fce97 | |||
| a1997291ab | |||
| 1f8fae0dd4 | |||
| 3bd2f766ef | |||
| f8ed341eec | |||
| f3b745f5b3 | |||
| 8d23a9b480 | |||
| c2e89ed955 | |||
| 782fc26723 | |||
| 378c2f727c | |||
| 5c983bce5f | |||
| 4eb18855e9 | |||
| 2200839bfa | |||
| 796cb9c35b | |||
| ac0d21cb3d | |||
| 70951317c5 | |||
| 4270a6c440 | |||
| 030688f1a2 | |||
| b11b2c0a71 | |||
| 7680ce4ef1 | |||
| ab872b7ca4 | |||
| 8533210761 | |||
| 673575594d | |||
| 4702bdab33 | |||
| ab08f1fc06 | |||
| 6b29bbc9e7 | |||
| 80f92311dc | |||
| c1723cbda6 | |||
| 5ee0311371 | |||
| d6c7ba03e6 | |||
| 32ecf932a6 | |||
| 26d16e3897 | |||
| ab0074e07c | |||
| 71c8e20a79 | |||
| 58bd7e8453 | |||
| d57c168f82 | |||
| 4a5a7068bc | |||
| d34649ab1a | |||
| 24407ead18 | |||
| ad4f0cf9d1 | |||
| f5efb7609a | |||
| ab82d3b053 | |||
| 36e0673c83 | |||
| 9d27e7386e | |||
| 8502e10900 | |||
| 0a701d0c64 | |||
| ceaa15aa82 | |||
| 2445e0cbcb | |||
| 5a5ad350d6 | |||
| 40e986d5dc | |||
| c81b68e9a8 | |||
| 8e9f1bc949 | |||
| e8b7ffd6f5 | |||
| b12eecfb1d | |||
| 06ca34f434 | |||
| 5880b1b612 | |||
| 12fc88aca9 | |||
| e56db8baa4 | |||
| c7aae533ad | |||
| acaa062b7d | |||
| f9928906fa | |||
| d3719122ce | |||
| 1e3fbf4e2f | |||
| fc8130987c | |||
| f3dc8f592b | |||
| 8fda46f0ad | |||
| 8d3c9caafb | |||
| 23031a64d1 | |||
| 37b27f5e73 |
@@ -13,6 +13,7 @@
|
|||||||
*.suo
|
*.suo
|
||||||
*.vs10x
|
*.vs10x
|
||||||
*.ndproj
|
*.ndproj
|
||||||
|
*.log
|
||||||
|
|
||||||
# common directories
|
# common directories
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -74,6 +74,11 @@ namespace Umbraco.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string Grid = "Umbraco.Grid";
|
public const string Grid = "Umbraco.Grid";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Grid.
|
||||||
|
/// </summary>
|
||||||
|
public const string Grid2 = "Umbraco.Grid2";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Image Cropper.
|
/// Image Cropper.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Umbraco.Core.Models.PublishedContent;
|
||||||
|
|
||||||
|
namespace Umbraco.Core.Models
|
||||||
|
{
|
||||||
|
public class Grid2Cell
|
||||||
|
{
|
||||||
|
public int Colspan { get; set; }
|
||||||
|
|
||||||
|
public IPublishedElement Settings { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<IPublishedElement> Items { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Umbraco.Core.Models.PublishedContent;
|
||||||
|
|
||||||
|
namespace Umbraco.Core.Models
|
||||||
|
{
|
||||||
|
public class Grid2Row
|
||||||
|
{
|
||||||
|
public string Alias { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public IPublishedElement Settings { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<Grid2Cell> Cells { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Umbraco.Core.Models
|
||||||
|
{
|
||||||
|
public class Grid2Value
|
||||||
|
{
|
||||||
|
public int Columns { get; set; }
|
||||||
|
|
||||||
|
public IEnumerable<Grid2Row> Rows { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,16 +23,19 @@ namespace Umbraco.Core.PropertyEditors
|
|||||||
return PropertyCacheLevel.Snapshot;
|
return PropertyCacheLevel.Snapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Rename to ConvertSourceToCachableIntermediate
|
||||||
public virtual object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview)
|
public virtual object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview)
|
||||||
{
|
{
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Rename to ConvertSourceToCachableIntermediate
|
||||||
public virtual object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
|
public virtual object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
|
||||||
{
|
{
|
||||||
return inter;
|
return inter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Rename to ConvertSourceToCachableIntermediate
|
||||||
public virtual object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
|
public virtual object ConvertIntermediateToXPath(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
|
||||||
{
|
{
|
||||||
return inter?.ToString() ?? string.Empty;
|
return inter?.ToString() ?? string.Empty;
|
||||||
|
|||||||
@@ -394,6 +394,9 @@
|
|||||||
<Compile Include="PropertyEditors\PropertyEditorTagsExtensions.cs" />
|
<Compile Include="PropertyEditors\PropertyEditorTagsExtensions.cs" />
|
||||||
<Compile Include="PropertyEditors\SliderPropertyEditorConfiguration.cs" />
|
<Compile Include="PropertyEditors\SliderPropertyEditorConfiguration.cs" />
|
||||||
<Compile Include="PropertyEditors\TagConfiguration.cs" />
|
<Compile Include="PropertyEditors\TagConfiguration.cs" />
|
||||||
|
<Compile Include="Models\Grid2Cell.cs" />
|
||||||
|
<Compile Include="Models\Grid2Row.cs" />
|
||||||
|
<Compile Include="Models\Grid2Value.cs" />
|
||||||
<Compile Include="PropertyEditors\ValueConverters\ImageCropperValue.cs" />
|
<Compile Include="PropertyEditors\ValueConverters\ImageCropperValue.cs" />
|
||||||
<Compile Include="PropertyEditors\ValueConverters\ImageCropperValueTypeConverter.cs" />
|
<Compile Include="PropertyEditors\ValueConverters\ImageCropperValueTypeConverter.cs" />
|
||||||
<Compile Include="PropertyEditors\ValueListConfiguration.cs" />
|
<Compile Include="PropertyEditors\ValueListConfiguration.cs" />
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
angular.module("umbraco")
|
||||||
|
.directive("umbGridEvents",
|
||||||
|
function() {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
replace: false,
|
||||||
|
link: function(scope, el, attrs) {
|
||||||
|
scope.$el = el;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
+1
-7
@@ -22,8 +22,6 @@ angular.module("umbraco.directives")
|
|||||||
//we always fetch the default one, and then override parts with our own
|
//we always fetch the default one, and then override parts with our own
|
||||||
tinyMceService.configuration().then(function (tinyMceConfig) {
|
tinyMceService.configuration().then(function (tinyMceConfig) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//config value from general tinymce.config file
|
//config value from general tinymce.config file
|
||||||
var validElements = tinyMceConfig.validElements;
|
var validElements = tinyMceConfig.validElements;
|
||||||
var fallbackStyles = [{title: "Page header", block: "h2"}, {title: "Section header", block: "h3"}, {title: "Paragraph header", block: "h4"}, {title: "Normal", block: "p"}, {title: "Quote", block: "blockquote"}, {title: "Code", block: "code"}];
|
var fallbackStyles = [{title: "Page header", block: "h2"}, {title: "Section header", block: "h3"}, {title: "Paragraph header", block: "h4"}, {title: "Normal", block: "p"}, {title: "Quote", block: "blockquote"}, {title: "Code", block: "code"}];
|
||||||
@@ -51,12 +49,10 @@ angular.module("umbraco.directives")
|
|||||||
await.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope));
|
await.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(scope.configuration && scope.configuration.toolbar){
|
if(scope.configuration && scope.configuration.toolbar){
|
||||||
toolbar = scope.configuration.toolbar.join(' | ');
|
toolbar = scope.configuration.toolbar.join(' | ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if(scope.configuration && scope.configuration.stylesheets){
|
if(scope.configuration && scope.configuration.stylesheets){
|
||||||
angular.forEach(scope.configuration.stylesheets, function(stylesheet, key){
|
angular.forEach(scope.configuration.stylesheets, function(stylesheet, key){
|
||||||
|
|
||||||
@@ -91,7 +87,7 @@ angular.module("umbraco.directives")
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
stylesheets.push("views/propertyeditors/grid/config/grid.default.rtestyles.css");
|
stylesheets.push("common/directives/components/grid/grid.default.rtestyles.css");
|
||||||
styleFormats = fallbackStyles;
|
styleFormats = fallbackStyles;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +116,6 @@ angular.module("umbraco.directives")
|
|||||||
cache_suffix: "?umb__rnd=" + Umbraco.Sys.ServerVariables.application.cacheBuster
|
cache_suffix: "?umb__rnd=" + Umbraco.Sys.ServerVariables.application.cacheBuster
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if (tinyMceConfig.customConfig) {
|
if (tinyMceConfig.customConfig) {
|
||||||
|
|
||||||
//if there is some custom config, we need to see if the string value of each item might actually be json and if so, we need to
|
//if there is some custom config, we need to see if the string value of each item might actually be json and if so, we need to
|
||||||
@@ -163,7 +158,6 @@ angular.module("umbraco.directives")
|
|||||||
//set the reference
|
//set the reference
|
||||||
tinyMceEditor = editor;
|
tinyMceEditor = editor;
|
||||||
|
|
||||||
|
|
||||||
//enable browser based spell checking
|
//enable browser based spell checking
|
||||||
editor.on('init', function (e) {
|
editor.on('init', function (e) {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* @ngdoc service
|
||||||
|
* @name umbraco.resources.gridResource
|
||||||
|
* @description Handles retrieving grid data
|
||||||
|
**/
|
||||||
|
function gridResource($http, umbRequestHelper) {
|
||||||
|
return {
|
||||||
|
|
||||||
|
getGridContentTypes: function () {
|
||||||
|
return umbRequestHelper.resourcePromise(
|
||||||
|
$http.get(
|
||||||
|
umbRequestHelper.getApiUrl(
|
||||||
|
"gridApiBaseUrl",
|
||||||
|
"GetContentTypes")),
|
||||||
|
"Failed to get grid content types");
|
||||||
|
},
|
||||||
|
|
||||||
|
getScaffold: function (id) {
|
||||||
|
return umbRequestHelper.resourcePromise(
|
||||||
|
$http.get(
|
||||||
|
umbRequestHelper.getApiUrl(
|
||||||
|
"gridApiBaseUrl",
|
||||||
|
"GetScaffold", {id: id})),
|
||||||
|
"Failed to get scaffold");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('umbraco.resources').factory('gridResource', gridResource);
|
||||||
@@ -10,6 +10,38 @@
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
||||||
|
|
||||||
|
mapGridValueToPersistableModel: function (model) {
|
||||||
|
// fixme - we now really only need to remove the properties collections of items
|
||||||
|
var persistableModel = {
|
||||||
|
rows: _.map(model.sections[0].rows,
|
||||||
|
function (row) {
|
||||||
|
return {
|
||||||
|
alias: row.name,
|
||||||
|
settings: {}, // fixme - add the settings
|
||||||
|
cells: _.map(row.cells,
|
||||||
|
function (cell) {
|
||||||
|
return {
|
||||||
|
settings: {},
|
||||||
|
items: _.map(cell.items,
|
||||||
|
function (item) {
|
||||||
|
return {
|
||||||
|
type: item.type,
|
||||||
|
values: _.object(_.map(item.properties,
|
||||||
|
function (prop) {
|
||||||
|
return [prop.alias, prop.value];
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
return persistableModel;
|
||||||
|
},
|
||||||
|
|
||||||
formatChangePasswordModel: function(model) {
|
formatChangePasswordModel: function(model) {
|
||||||
if (!model) {
|
if (!model) {
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -114,13 +114,12 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.umb-card-grid .umb-card-grid-item:hover,
|
.umb-card-grid .umb-card-grid-item:hover,
|
||||||
.umb-card-grid .umb-card-grid-item:focus,
|
.umb-card-grid .umb-card-grid-item:focus {
|
||||||
.umb-card-grid .umb-card-grid-item:hover > *,
|
|
||||||
.umb-card-grid .umb-card-grid-item:focus > * {
|
|
||||||
background: @gray-10;
|
background: @gray-10;
|
||||||
//color: white;
|
//color: white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -140,6 +139,17 @@
|
|||||||
color: @gray-3;
|
color: @gray-3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.umb-card-grid .umb-card-grid__checkmark {
|
||||||
|
font-size: 13px;
|
||||||
|
color: @white;
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
left: 5px;
|
||||||
|
line-height: 1em;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.umb-card-grid .umb-card-grid-item__loading {
|
.umb-card-grid .umb-card-grid-item__loading {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background-color: rgba(255,255,255,0.8);
|
background-color: rgba(255,255,255,0.8);
|
||||||
|
|||||||
-363
@@ -1,363 +0,0 @@
|
|||||||
var uSkyGridConfig = [
|
|
||||||
{
|
|
||||||
|
|
||||||
style:[
|
|
||||||
{
|
|
||||||
label: "Set a background image",
|
|
||||||
description: "Set a row background",
|
|
||||||
key: "background-image",
|
|
||||||
view: "imagepicker",
|
|
||||||
modifier: "url({0})"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
label: "Set a font color",
|
|
||||||
description: "Pick a color",
|
|
||||||
key: "color",
|
|
||||||
view: "colorpicker"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
config:[
|
|
||||||
{
|
|
||||||
label: "Preview",
|
|
||||||
description: "Display a live preview",
|
|
||||||
key: "preview",
|
|
||||||
view: "boolean"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
label: "Class",
|
|
||||||
description: "Set a css class",
|
|
||||||
key: "class",
|
|
||||||
view: "textstring"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
|
|
||||||
layouts: [
|
|
||||||
{
|
|
||||||
grid: 12,
|
|
||||||
percentage: 100,
|
|
||||||
|
|
||||||
|
|
||||||
rows: [
|
|
||||||
{
|
|
||||||
name: "Single column",
|
|
||||||
columns: [{
|
|
||||||
grid: 12,
|
|
||||||
percentage: 100
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Article",
|
|
||||||
models: [{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3,
|
|
||||||
allowed: ["media","quote"]
|
|
||||||
}, {
|
|
||||||
grid: 8,
|
|
||||||
percentage: 66.6,
|
|
||||||
allowed: ["rte"]
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "Article, reverse",
|
|
||||||
models: [
|
|
||||||
{
|
|
||||||
grid: 8,
|
|
||||||
percentage: 66.6,
|
|
||||||
allowed: ["rte","macro"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3,
|
|
||||||
allowed: ["media","quote","embed"]
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Profile page",
|
|
||||||
models: [
|
|
||||||
{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3,
|
|
||||||
allowed: ["media"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
grid: 8,
|
|
||||||
percentage: 66.6,
|
|
||||||
allowed: ["rte"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Headline",
|
|
||||||
models: [
|
|
||||||
{
|
|
||||||
grid: 12,
|
|
||||||
percentage: 100,
|
|
||||||
max: 1,
|
|
||||||
allowed: ["headline"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Three columns",
|
|
||||||
models: [{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3,
|
|
||||||
allowed: ["rte"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3,
|
|
||||||
allowed: ["rte"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3,
|
|
||||||
allowed: ["rte"]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
grid: 9,
|
|
||||||
percentage: 70,
|
|
||||||
|
|
||||||
cellModels: [
|
|
||||||
{
|
|
||||||
models: [{
|
|
||||||
grid: 12,
|
|
||||||
percentage: 100
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 6,
|
|
||||||
percentage: 50
|
|
||||||
}, {
|
|
||||||
grid: 6,
|
|
||||||
percentage: 50
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3
|
|
||||||
}, {
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3
|
|
||||||
}, {
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 3,
|
|
||||||
percentage: 25
|
|
||||||
}, {
|
|
||||||
grid: 3,
|
|
||||||
percentage: 25
|
|
||||||
}, {
|
|
||||||
grid: 3,
|
|
||||||
percentage: 25
|
|
||||||
}, {
|
|
||||||
grid: 3,
|
|
||||||
percentage: 25
|
|
||||||
}, ]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}, {
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}, {
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}, {
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}, {
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}, {
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 8,
|
|
||||||
percentage: 60
|
|
||||||
}, {
|
|
||||||
grid: 4,
|
|
||||||
percentage: 40
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 40
|
|
||||||
}, {
|
|
||||||
grid: 8,
|
|
||||||
percentage: 60
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
grid: 3,
|
|
||||||
percentage: 30,
|
|
||||||
cellModels: [
|
|
||||||
{
|
|
||||||
models: [{
|
|
||||||
grid: 12,
|
|
||||||
percentage: 100
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
grid: 3,
|
|
||||||
percentage: 30,
|
|
||||||
cellModels: [
|
|
||||||
{
|
|
||||||
models: [{
|
|
||||||
grid: 12,
|
|
||||||
percentage: 100
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
grid: 9,
|
|
||||||
percentage: 70,
|
|
||||||
cellModels: [
|
|
||||||
{
|
|
||||||
models: [{
|
|
||||||
grid: 12,
|
|
||||||
percentage: 100
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 6,
|
|
||||||
percentage: 50
|
|
||||||
}, {
|
|
||||||
grid: 6,
|
|
||||||
percentage: 50
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3
|
|
||||||
}, {
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3
|
|
||||||
}, {
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 3,
|
|
||||||
percentage: 25
|
|
||||||
}, {
|
|
||||||
grid: 3,
|
|
||||||
percentage: 25
|
|
||||||
}, {
|
|
||||||
grid: 3,
|
|
||||||
percentage: 25
|
|
||||||
}, {
|
|
||||||
grid: 3,
|
|
||||||
percentage: 25
|
|
||||||
}, ]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}, {
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}, {
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}, {
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}, {
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}, {
|
|
||||||
grid: 2,
|
|
||||||
percentage: 16.6
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 8,
|
|
||||||
percentage: 60
|
|
||||||
}, {
|
|
||||||
grid: 4,
|
|
||||||
percentage: 40
|
|
||||||
}]
|
|
||||||
}, {
|
|
||||||
models: [{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 40
|
|
||||||
}, {
|
|
||||||
grid: 8,
|
|
||||||
percentage: 60
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3,
|
|
||||||
cellModels: [
|
|
||||||
{
|
|
||||||
models: [{
|
|
||||||
grid: 12,
|
|
||||||
percentage: 100
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3,
|
|
||||||
cellModels: [
|
|
||||||
{
|
|
||||||
models: [{
|
|
||||||
grid: 12,
|
|
||||||
percentage: 100
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
grid: 4,
|
|
||||||
percentage: 33.3,
|
|
||||||
cellModels: [
|
|
||||||
{
|
|
||||||
models: [{
|
|
||||||
grid: 12,
|
|
||||||
percentage: 100
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<div class="umb-editor-placeholder">
|
||||||
|
<div ng-if="!preview">
|
||||||
|
<i class="icon {{model.icon}}"></i>
|
||||||
|
<div class="help-text">{{model.title}}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<div ng-if="model.config">
|
||||||
|
<umb-property
|
||||||
|
property="configValue"
|
||||||
|
ng-repeat="configValue in model.config">
|
||||||
|
<umb-property-editor model="configValue" is-pre-value="true"></umb-property-editor>
|
||||||
|
</umb-property>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="model.styles">
|
||||||
|
<h4>Style</h4>
|
||||||
|
<umb-property
|
||||||
|
property="styleValue"
|
||||||
|
ng-repeat="styleValue in model.styles">
|
||||||
|
<umb-property-editor model="styleValue" is-pre-value="true"></umb-property-editor>
|
||||||
|
</umb-property>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<form name="gridConfigEditor">
|
||||||
|
|
||||||
|
<h4>{{model.name}}</h4>
|
||||||
|
<p>Settings will only save if the entered json configuration is valid</p>
|
||||||
|
<textarea name="configSource"
|
||||||
|
validate-on="'blur'"
|
||||||
|
rows="20" class="umb-property-editor umb-textarea"
|
||||||
|
umb-raw-model="model.config"></textarea>
|
||||||
|
|
||||||
|
<div class="alert alert-error" ng-show="gridConfigEditor.$invalid === true">
|
||||||
|
This configuration is not valid json, and will not be saved.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
(function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function GridEditorPicker($scope, editorService) {
|
||||||
|
|
||||||
|
var vm = this;
|
||||||
|
|
||||||
|
vm.gridEditors = $scope.model.gridEditors;
|
||||||
|
|
||||||
|
vm.submit = submit;
|
||||||
|
vm.close = close;
|
||||||
|
vm.selectGridEditor = selectGridEditor;
|
||||||
|
|
||||||
|
function onInit() {
|
||||||
|
|
||||||
|
// set default title
|
||||||
|
if($scope.model && !$scope.model.title) {
|
||||||
|
$scope.model.title = "Select Grid Editors";
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure we have an arrow to push to
|
||||||
|
if($scope.model && !$scope.model.selection) {
|
||||||
|
$scope.model.selection = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectGridEditor(selectedGridEditor) {
|
||||||
|
|
||||||
|
if(!selectedGridEditor) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = $scope.model.selection.indexOf(selectedGridEditor);
|
||||||
|
|
||||||
|
// select grid editor
|
||||||
|
if(index === -1) {
|
||||||
|
selectedGridEditor.selected = true;
|
||||||
|
$scope.model.selection.push(selectedGridEditor);
|
||||||
|
|
||||||
|
// deselect grid editor
|
||||||
|
} else {
|
||||||
|
selectedGridEditor.selected = false;
|
||||||
|
$scope.model.selection.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit(model) {
|
||||||
|
if($scope.model.submit) {
|
||||||
|
$scope.model.submit(model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
if($scope.model.close) {
|
||||||
|
$scope.model.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onInit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module("umbraco").controller("Umbraco.PropertyEditors.GridPrevalueEditor.GridEditorPickerController", GridEditorPicker);
|
||||||
|
|
||||||
|
})();
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
<div ng-controller="Umbraco.PropertyEditors.GridPrevalueEditor.GridEditorPickerController as vm">
|
||||||
|
|
||||||
|
<umb-editor-view>
|
||||||
|
|
||||||
|
<umb-editor-header
|
||||||
|
name="model.title"
|
||||||
|
name-locked="true"
|
||||||
|
hide-alias="true"
|
||||||
|
hide-icon="true"
|
||||||
|
hide-description="true">
|
||||||
|
</umb-editor-header>
|
||||||
|
|
||||||
|
<umb-editor-container>
|
||||||
|
<umb-box>
|
||||||
|
<umb-box-content>
|
||||||
|
<ul class="umb-card-grid">
|
||||||
|
<li ng-repeat="gridEditor in vm.gridEditors | orderBy:'name'"
|
||||||
|
data-element="grid-editor-{{gridEditor.name}}"
|
||||||
|
ng-click="vm.selectGridEditor(gridEditor)"
|
||||||
|
class="-four-in-row">
|
||||||
|
<a class="umb-card-grid-item" href="" title="{{ gridEditor.name }}">
|
||||||
|
<umb-checkmark
|
||||||
|
class="umb-card-grid__checkmark"
|
||||||
|
ng-if="gridEditor.selected"
|
||||||
|
checked="gridEditor.selected"
|
||||||
|
size="xs">
|
||||||
|
</umb-checkmark>
|
||||||
|
<i class="{{ gridEditor.icon }}" ng-class="{'icon-item-arrangement': gridEditor.icon == null}"></i>
|
||||||
|
{{ gridEditor.name }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</umb-box-content>
|
||||||
|
</umb-box>
|
||||||
|
</umb-editor-container>
|
||||||
|
|
||||||
|
<umb-editor-footer>
|
||||||
|
|
||||||
|
<umb-editor-footer-content-right>
|
||||||
|
|
||||||
|
<umb-button
|
||||||
|
type="button"
|
||||||
|
button-style="link"
|
||||||
|
label-key="general_close"
|
||||||
|
action="vm.close()">
|
||||||
|
</umb-button>
|
||||||
|
|
||||||
|
<umb-button
|
||||||
|
type="button"
|
||||||
|
button-style="success"
|
||||||
|
label-key="general_submit"
|
||||||
|
action="vm.submit(model)">
|
||||||
|
</umb-button>
|
||||||
|
|
||||||
|
</umb-editor-footer-content-right>
|
||||||
|
|
||||||
|
</umb-editor-footer>
|
||||||
|
|
||||||
|
</umb-editor-view>
|
||||||
|
|
||||||
|
</div>
|
||||||
+180
@@ -0,0 +1,180 @@
|
|||||||
|
function RowConfigController($scope, gridResource, editorService) {
|
||||||
|
|
||||||
|
var vm = this;
|
||||||
|
|
||||||
|
$scope.currentRow = $scope.model.currentRow;
|
||||||
|
$scope.contentTypes = $scope.model.contentTypes;
|
||||||
|
$scope.columns = $scope.model.columns;
|
||||||
|
|
||||||
|
var availableEditors = [];
|
||||||
|
|
||||||
|
function onInit() {
|
||||||
|
gridResource.getGridContentTypes().then(function(gridEditors){
|
||||||
|
availableEditors = gridEditors;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
vm.selectedGridEditors = [];
|
||||||
|
vm.selectGridEditor = selectGridEditor;
|
||||||
|
vm.removeGridEditor = removeGridEditor;
|
||||||
|
vm.submit = submit;
|
||||||
|
vm.close = close;
|
||||||
|
|
||||||
|
$scope.scaleUp = function(section, max, overflow) {
|
||||||
|
var add = 1;
|
||||||
|
if (overflow !== true) {
|
||||||
|
add = (max > 1) ? 1 : max;
|
||||||
|
}
|
||||||
|
//var add = (max > 1) ? 1 : max;
|
||||||
|
section.grid = section.grid + add;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.scaleDown = function(section) {
|
||||||
|
var remove = (section.grid > 1) ? 1 : 0;
|
||||||
|
section.grid = section.grid - remove;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.percentage = function(spans) {
|
||||||
|
return ((spans / $scope.columns) * 100).toFixed(8);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.toggleCollection = function(collection, toggle) {
|
||||||
|
if (toggle) {
|
||||||
|
collection = [];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete collection;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/****************
|
||||||
|
area
|
||||||
|
*****************/
|
||||||
|
$scope.configureCell = function(cell, row) {
|
||||||
|
if ($scope.currentCell && $scope.currentCell === cell) {
|
||||||
|
delete $scope.currentCell;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (cell === undefined) {
|
||||||
|
var available = $scope.availableRowSpace;
|
||||||
|
var space = 4;
|
||||||
|
|
||||||
|
if (available < 4 && available > 0) {
|
||||||
|
space = available;
|
||||||
|
}
|
||||||
|
|
||||||
|
cell = {
|
||||||
|
grid: space
|
||||||
|
};
|
||||||
|
|
||||||
|
row.areas.push(cell);
|
||||||
|
}
|
||||||
|
$scope.currentCell = cell;
|
||||||
|
|
||||||
|
updateSelectedGridEditors();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.deleteArea = function (cell, row) {
|
||||||
|
if ($scope.currentCell === cell) {
|
||||||
|
$scope.currentCell = undefined;
|
||||||
|
}
|
||||||
|
var index = row.areas.indexOf(cell)
|
||||||
|
row.areas.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.closeArea = function() {
|
||||||
|
$scope.currentCell = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.nameChanged = false;
|
||||||
|
|
||||||
|
var originalName = $scope.currentRow.name;
|
||||||
|
|
||||||
|
$scope.$watch("currentRow", function(row) {
|
||||||
|
if (row) {
|
||||||
|
|
||||||
|
var total = 0;
|
||||||
|
_.forEach(row.areas, function(area) {
|
||||||
|
total = (total + area.grid);
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.availableRowSpace = $scope.columns - total;
|
||||||
|
|
||||||
|
if (originalName) {
|
||||||
|
if (originalName != row.name) {
|
||||||
|
$scope.nameChanged = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$scope.nameChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
function updateSelectedGridEditors() {
|
||||||
|
vm.selectedGridEditors = _.filter(availableEditors, function (editor) {
|
||||||
|
if (_.indexOf($scope.currentCell.allowed, editor.udi) >= 0) {
|
||||||
|
editor.selected = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectGridEditor() {
|
||||||
|
var gridEditorPicker = {
|
||||||
|
view: "views/propertyEditors/grid2/dialogs/grideditorpicker.html",
|
||||||
|
size: "small",
|
||||||
|
gridEditors: availableEditors,
|
||||||
|
selection: vm.selectedGridEditors,
|
||||||
|
submit: function(model) {
|
||||||
|
|
||||||
|
if(model && model.selection.length > 0) {
|
||||||
|
|
||||||
|
if(!$scope.currentCell.allowed) {
|
||||||
|
$scope.currentCell.allowed = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
model.selection.forEach(function(gridEditor){
|
||||||
|
if($scope.currentCell.allowed.indexOf(gridEditor.udi) === -1) {
|
||||||
|
$scope.currentCell.allowed.push(gridEditor.udi);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateSelectedGridEditors();
|
||||||
|
}
|
||||||
|
|
||||||
|
editorService.close();
|
||||||
|
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
editorService.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
editorService.open(gridEditorPicker);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeGridEditor(index, allowedGridEditors) {
|
||||||
|
allowedGridEditors.splice(index, 1);
|
||||||
|
updateSelectedGridEditors();
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit(model) {
|
||||||
|
if($scope.model.submit) {
|
||||||
|
$scope.model.submit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function close() {
|
||||||
|
if($scope.model.close) {
|
||||||
|
$scope.model.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onInit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module("umbraco").controller("Umbraco.PropertyEditors.Grid2PrevalueEditor.RowConfigController", RowConfigController);
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
<div class="usky-grid usky-grid-configuration" ng-controller="Umbraco.PropertyEditors.Grid2PrevalueEditor.RowConfigController as vm">
|
||||||
|
|
||||||
|
<umb-editor-view>
|
||||||
|
|
||||||
|
<umb-editor-header
|
||||||
|
name="currentRow.label"
|
||||||
|
alias="currentRow.name"
|
||||||
|
hide-icon="true"
|
||||||
|
hide-description="true">
|
||||||
|
</umb-editor-header>
|
||||||
|
|
||||||
|
<umb-editor-container>
|
||||||
|
|
||||||
|
<umb-box>
|
||||||
|
|
||||||
|
<umb-box-content>
|
||||||
|
|
||||||
|
<div class="umb-form-settings block-form">
|
||||||
|
|
||||||
|
<h5><localize key="grid_addRowConfiguration" /></h5>
|
||||||
|
<p><localize key="grid_addRowConfigurationDetail" /></p>
|
||||||
|
|
||||||
|
<div class="alert alert-warn ng-scope" ng-show="nameChanged">
|
||||||
|
<p>Modifying a row configuration alias will result in loss of
|
||||||
|
data for any existing content that is based on this configuration.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="uSky-templates-template"
|
||||||
|
style="margin: 0; width: 350px; position: relative;">
|
||||||
|
|
||||||
|
<div class="tb" style="height: auto; border: none !important; background: none">
|
||||||
|
<div class="tr">
|
||||||
|
|
||||||
|
<a class="td uSky-templates-column"
|
||||||
|
ng-class="{last:$last, selected:cell==currentCell}"
|
||||||
|
ng-repeat="cell in currentRow.areas"
|
||||||
|
ng-click="configureCell(cell, currentRow)"
|
||||||
|
ng-style="{width: percentage(cell.grid) + '%'}">
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a class="td uSky-templates-column add"
|
||||||
|
ng-click="configureCell(undefined, currentRow)"
|
||||||
|
ng-style="{width: percentage(availableRowSpace) + '%'}">
|
||||||
|
<i class="icon icon-add"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-if="currentCell" style="padding-bottom: 50px;">
|
||||||
|
|
||||||
|
<umb-control-group label="@general_width">
|
||||||
|
<div class="grid-size-scaler">
|
||||||
|
<a href ng-click="scaleDown(currentCell)">
|
||||||
|
<i class="icon icon-remove"></i>
|
||||||
|
</a>
|
||||||
|
{{currentCell.grid}}
|
||||||
|
<a href ng-click="scaleUp(currentCell, availableRowSpace, true)">
|
||||||
|
<i class="icon icon-add"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</umb-control-group>
|
||||||
|
|
||||||
|
<umb-control-group label="@grid_maxItems" description="@grid_maxItemsDescription">
|
||||||
|
<input type="number" ng-model="currentCell.maxItems" class="umb-property-editor-tiny" placeholder="Max" min="0" />
|
||||||
|
</umb-control-group>
|
||||||
|
|
||||||
|
<umb-control-group hide-label="true">
|
||||||
|
<i class="icon-delete red"></i>
|
||||||
|
<a class="btn btn-small btn-link" href="" ng-click="deleteArea(currentCell, currentRow)">
|
||||||
|
<localize key="general_delete" class="ng-isolate-scope ng-scope">Delete</localize>
|
||||||
|
</a>
|
||||||
|
</umb-control-group>
|
||||||
|
|
||||||
|
<umb-control-group label="Allowed Grid Editors" description="Select the grid editors which should be allowed for this cell">
|
||||||
|
|
||||||
|
<umb-node-preview
|
||||||
|
style="max-width: 100%"
|
||||||
|
ng-repeat="gridEditor in vm.selectedGridEditors"
|
||||||
|
name="gridEditor.name"
|
||||||
|
description="gridEditor.alias"
|
||||||
|
icon="gridEditor.icon"
|
||||||
|
allow-remove="true"
|
||||||
|
on-remove="vm.removeGridEditor($index, currentCell.allowed)">
|
||||||
|
</umb-node-preview>
|
||||||
|
|
||||||
|
<a class="umb-node-preview-add"
|
||||||
|
style="max-width: 100%"
|
||||||
|
href=""
|
||||||
|
ng-click="vm.selectGridEditor()"
|
||||||
|
prevent-default>
|
||||||
|
<localize key="general_add">Add</localize>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</umb-control-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</umb-box-content>
|
||||||
|
|
||||||
|
</umb-box>
|
||||||
|
|
||||||
|
</umb-editor-container>
|
||||||
|
|
||||||
|
<umb-editor-footer>
|
||||||
|
|
||||||
|
<umb-editor-footer-content-right>
|
||||||
|
|
||||||
|
<umb-button
|
||||||
|
type="button"
|
||||||
|
button-style="link"
|
||||||
|
label-key="general_close"
|
||||||
|
action="vm.close()">
|
||||||
|
</umb-button>
|
||||||
|
|
||||||
|
<umb-button
|
||||||
|
type="button"
|
||||||
|
button-style="success"
|
||||||
|
label-key="general_submit"
|
||||||
|
action="vm.submit(model)">
|
||||||
|
</umb-button>
|
||||||
|
|
||||||
|
</umb-editor-footer-content-right>
|
||||||
|
|
||||||
|
</umb-editor-footer>
|
||||||
|
|
||||||
|
</umb-editor-view>
|
||||||
|
|
||||||
|
</div>
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
<div>
|
||||||
|
|
||||||
|
<h3 class="alert alert-warn ng-scope">Warning!</h3>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You are deleting the row configuration '<strong>{{model.dialogData.rowName}}</strong>'
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Modifying a row configuration name will result in loss of
|
||||||
|
data for any existing content that is based on this configuration.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<localize key="general_areyousure">Are you sure?</localize>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
+237
@@ -0,0 +1,237 @@
|
|||||||
|
angular.module("umbraco")
|
||||||
|
.controller("Umbraco.PropertyEditors.Grid2PrevalueEditorController",
|
||||||
|
function ($scope, $http, assetsService, $rootScope, dialogService, mediaResource, gridService, imageHelper, $timeout, editorService, gridResource) {
|
||||||
|
|
||||||
|
var vm = this;
|
||||||
|
|
||||||
|
var emptyModel = {
|
||||||
|
styles:[
|
||||||
|
{
|
||||||
|
label: "Set a background image",
|
||||||
|
description: "Set a row background",
|
||||||
|
key: "background-image",
|
||||||
|
view: "imagepicker",
|
||||||
|
modifier: "url({0})"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
config:[
|
||||||
|
{
|
||||||
|
label: "Class",
|
||||||
|
description: "Set a css class",
|
||||||
|
key: "class",
|
||||||
|
view: "textstring"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
columns: 12,
|
||||||
|
templates:[
|
||||||
|
{
|
||||||
|
name: "1 column layout",
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
grid: 12,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
|
||||||
|
layouts:[
|
||||||
|
{
|
||||||
|
label: "Headline",
|
||||||
|
name: "Headline",
|
||||||
|
areas: [
|
||||||
|
{
|
||||||
|
grid: 12
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Article",
|
||||||
|
name: "Article",
|
||||||
|
areas: [
|
||||||
|
{
|
||||||
|
grid: 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
grid: 8
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.configureRow = configureRow;
|
||||||
|
|
||||||
|
/****************
|
||||||
|
Row
|
||||||
|
*****************/
|
||||||
|
|
||||||
|
function configureRow(row) {
|
||||||
|
|
||||||
|
var rowCopy = angular.copy($scope.model.value.layouts);
|
||||||
|
|
||||||
|
if(row === undefined){
|
||||||
|
row = {
|
||||||
|
name: "",
|
||||||
|
areas: []
|
||||||
|
};
|
||||||
|
$scope.model.value.layouts.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rowConfigurationEditor = {
|
||||||
|
view: "views/propertyEditors/grid2/dialogs/rowconfig.html",
|
||||||
|
size: "small",
|
||||||
|
currentRow: row,
|
||||||
|
columns: $scope.model.value.columns,
|
||||||
|
submit: function(model) {
|
||||||
|
editorService.close();
|
||||||
|
},
|
||||||
|
close: function() {
|
||||||
|
$scope.model.value.layouts = rowCopy;
|
||||||
|
editorService.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
editorService.open(rowConfigurationEditor);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//var rowDeletesPending = false;
|
||||||
|
$scope.deleteLayout = function(index) {
|
||||||
|
|
||||||
|
$scope.rowDeleteOverlay = {};
|
||||||
|
$scope.rowDeleteOverlay.view = "views/propertyEditors/grid2/dialogs/rowdeleteconfirm.html";
|
||||||
|
$scope.rowDeleteOverlay.dialogData = {
|
||||||
|
rowName: $scope.model.value.layouts[index].name
|
||||||
|
};
|
||||||
|
$scope.rowDeleteOverlay.show = true;
|
||||||
|
|
||||||
|
$scope.rowDeleteOverlay.submit = function(model) {
|
||||||
|
|
||||||
|
$scope.model.value.layouts.splice(index, 1);
|
||||||
|
|
||||||
|
$scope.rowDeleteOverlay.show = false;
|
||||||
|
$scope.rowDeleteOverlay = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.rowDeleteOverlay.close = function(oldModel) {
|
||||||
|
$scope.rowDeleteOverlay.show = false;
|
||||||
|
$scope.rowDeleteOverlay = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/****************
|
||||||
|
utillities
|
||||||
|
*****************/
|
||||||
|
$scope.toggleCollection = function(collection, toggle){
|
||||||
|
if(toggle){
|
||||||
|
collection = [];
|
||||||
|
}else{
|
||||||
|
delete collection;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.percentage = function(spans){
|
||||||
|
return ((spans / $scope.model.value.columns) * 100).toFixed(8);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.zeroWidthFilter = function (cell) {
|
||||||
|
return cell.grid > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/****************
|
||||||
|
Config
|
||||||
|
*****************/
|
||||||
|
|
||||||
|
$scope.removeConfigValue = function(collection, index){
|
||||||
|
collection.splice(index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
var editConfigCollection = function(configValues, title, callback) {
|
||||||
|
|
||||||
|
$scope.editConfigCollectionOverlay = {};
|
||||||
|
$scope.editConfigCollectionOverlay.view = "views/propertyeditors/grid2/dialogs/editconfig.html";
|
||||||
|
$scope.editConfigCollectionOverlay.config = configValues;
|
||||||
|
$scope.editConfigCollectionOverlay.title = title;
|
||||||
|
$scope.editConfigCollectionOverlay.show = true;
|
||||||
|
|
||||||
|
$scope.editConfigCollectionOverlay.submit = function(model) {
|
||||||
|
|
||||||
|
callback(model.config)
|
||||||
|
|
||||||
|
$scope.editConfigCollectionOverlay.show = false;
|
||||||
|
$scope.editConfigCollectionOverlay = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.editConfigCollectionOverlay.close = function(oldModel) {
|
||||||
|
$scope.editConfigCollectionOverlay.show = false;
|
||||||
|
$scope.editConfigCollectionOverlay = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.editConfig = function() {
|
||||||
|
editConfigCollection($scope.model.value.config, "Settings", function(data) {
|
||||||
|
$scope.model.value.config = data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.editStyles = function() {
|
||||||
|
editConfigCollection($scope.model.value.styles, "Styling", function(data){
|
||||||
|
$scope.model.value.styles = data;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/* init grid data */
|
||||||
|
if (!$scope.model.value || $scope.model.value === "" || !$scope.model.value.templates) {
|
||||||
|
$scope.model.value = emptyModel;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if (!$scope.model.value.columns) {
|
||||||
|
$scope.model.value.columns = emptyModel.columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!$scope.model.value.config) {
|
||||||
|
$scope.model.value.config = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$scope.model.value.styles) {
|
||||||
|
$scope.model.value.styles = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/****************
|
||||||
|
Clean up
|
||||||
|
*****************/
|
||||||
|
var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
|
||||||
|
var ts = $scope.model.value.templates;
|
||||||
|
var ls = $scope.model.value.layouts;
|
||||||
|
|
||||||
|
_.each(ts, function(t){
|
||||||
|
_.each(t.sections, function(section, index){
|
||||||
|
if(section.grid === 0){
|
||||||
|
t.sections.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
_.each(ls, function(l){
|
||||||
|
_.each(l.areas, function(area, index){
|
||||||
|
if(area.grid === 0){
|
||||||
|
l.areas.splice(index, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//when the scope is destroyed we need to unsubscribe
|
||||||
|
$scope.$on('$destroy', function () {
|
||||||
|
unsubscribe();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
<div ng-controller="Umbraco.PropertyEditors.Grid2PrevalueEditorController as vm" class="usky-grid usky-grid-configuration">
|
||||||
|
|
||||||
|
<div style="max-width: 600px">
|
||||||
|
|
||||||
|
<div class="control-group uSky-templates">
|
||||||
|
|
||||||
|
<umb-control-group
|
||||||
|
label="@grid_columns"
|
||||||
|
description="@grid_columnsDetails">
|
||||||
|
<input type="number" min="1" ng-model="model.value.columns" />
|
||||||
|
</umb-control-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="max-width: 600px">
|
||||||
|
|
||||||
|
<div class="control-group uSky-templates">
|
||||||
|
|
||||||
|
<umb-control-group
|
||||||
|
label="@grid_rowConfigurations"
|
||||||
|
description="@grid_rowConfigurationsDetail">
|
||||||
|
|
||||||
|
<div class="control-group uSky-templates-rows">
|
||||||
|
<ul class="unstyled"
|
||||||
|
ui-sortable
|
||||||
|
ng-model="model.value.layouts">
|
||||||
|
|
||||||
|
<li ng-repeat="row in model.value.layouts" class="clearfix">
|
||||||
|
|
||||||
|
<div ng-click="vm.configureRow(row)"
|
||||||
|
class="preview-rows columns" style="margin-top: 5px; margin-bottom: 25px; float:left">
|
||||||
|
|
||||||
|
<div class="preview-row">
|
||||||
|
<div class="preview-col"
|
||||||
|
ng-class="{last:$last}"
|
||||||
|
ng-repeat="area in row.areas | filter:zeroWidthFilter"
|
||||||
|
ng-style="{width: percentage(area.grid) + '%', 'max-width': '100%'}">
|
||||||
|
|
||||||
|
<div class="preview-cell">
|
||||||
|
<p style="font-size: 6px; line-height: 8px; text-align: center" ng-show="area.maxItems > 0">{{area.maxItems}}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{{row.label || row.name}}<br />
|
||||||
|
<i class="icon-delete red"></i>
|
||||||
|
<a class="btn btn-small btn-link" href ng-click="deleteLayout($index)">
|
||||||
|
<localize key="general_delete" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button class="btn btn-small" prevent-default ng-click="vm.configureRow()">
|
||||||
|
<i class="icon-add"></i> <localize key="grid_addRowConfiguration" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</umb-control-group>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="max-width: 600px">
|
||||||
|
|
||||||
|
<umb-control-group
|
||||||
|
label="@grid_settings"
|
||||||
|
description="@grid_settingsDetails">
|
||||||
|
|
||||||
|
<ul class="unstyled list-icons umb-contentpicker"
|
||||||
|
ui-sortable
|
||||||
|
ng-model="model.value.config">
|
||||||
|
|
||||||
|
<li ng-repeat="configValue in model.value.config">
|
||||||
|
<i class="icon icon-navigation handle"></i>
|
||||||
|
|
||||||
|
<a href="#" prevent-default ng-click="removeConfigValue(model.value.config, $index)">
|
||||||
|
<i class="icon icon-delete red hover-show"></i>
|
||||||
|
<i class="icon-settings-alt-2 hover-hide"></i>
|
||||||
|
{{configValue.label}}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="unstyled list-icons">
|
||||||
|
<li>
|
||||||
|
<i class="icon icon-add turquoise"></i>
|
||||||
|
|
||||||
|
<a href="#" ng-click="editConfig()" prevent-default>
|
||||||
|
<localize key="general_edit" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</umb-control-group>
|
||||||
|
|
||||||
|
<umb-control-group
|
||||||
|
label="@grid_styles"
|
||||||
|
description="@grid_stylesDetails">
|
||||||
|
|
||||||
|
<ul class="unstyled list-icons umb-contentpicker"
|
||||||
|
ui-sortable
|
||||||
|
ng-model="model.value.styles">
|
||||||
|
|
||||||
|
<li ng-repeat="style in model.value.styles">
|
||||||
|
<i class="icon icon-navigation handle"></i>
|
||||||
|
|
||||||
|
<a href="#" prevent-default ng-click="removeConfigValue(model.value.styles, $index)">
|
||||||
|
<i class="icon icon-delete red hover-show"></i>
|
||||||
|
<i class="icon-brush hover-hide"></i>
|
||||||
|
{{style.label}}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ul class="unstyled list-icons">
|
||||||
|
<li>
|
||||||
|
<i class="icon icon-add turquoise"></i>
|
||||||
|
<a href="#" ng-click="editStyles()" prevent-default>
|
||||||
|
<localize key="general_edit" />
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</umb-control-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<umb-overlay
|
||||||
|
ng-if="layoutConfigOverlay.show"
|
||||||
|
model="layoutConfigOverlay"
|
||||||
|
view="layoutConfigOverlay.view"
|
||||||
|
position="right">
|
||||||
|
</umb-overlay>
|
||||||
|
|
||||||
|
<umb-overlay
|
||||||
|
ng-if="rowConfigOverlay.show"
|
||||||
|
model="rowConfigOverlay"
|
||||||
|
view="rowConfigOverlay.view"
|
||||||
|
position="right">
|
||||||
|
</umb-overlay>
|
||||||
|
|
||||||
|
<umb-overlay
|
||||||
|
ng-if="editConfigCollectionOverlay.show"
|
||||||
|
model="editConfigCollectionOverlay"
|
||||||
|
view="editConfigCollectionOverlay.view"
|
||||||
|
position="right">
|
||||||
|
</umb-overlay>
|
||||||
|
|
||||||
|
<umb-overlay
|
||||||
|
ng-if="rowDeleteOverlay.show"
|
||||||
|
model="rowDeleteOverlay"
|
||||||
|
view="rowDeleteOverlay.view"
|
||||||
|
position="right">
|
||||||
|
</umb-overlay>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,900 @@
|
|||||||
|
/*
|
||||||
|
Events:
|
||||||
|
grid.initializing
|
||||||
|
grid.initialized
|
||||||
|
grid.rowAdded
|
||||||
|
grid.itemAdded
|
||||||
|
*/
|
||||||
|
angular.module("umbraco")
|
||||||
|
.controller("Umbraco.PropertyEditors.Grid2Controller",
|
||||||
|
function (
|
||||||
|
$scope,
|
||||||
|
$http,
|
||||||
|
assetsService,
|
||||||
|
localizationService,
|
||||||
|
$rootScope,
|
||||||
|
dialogService,
|
||||||
|
gridResource,
|
||||||
|
mediaResource,
|
||||||
|
imageHelper,
|
||||||
|
$timeout,
|
||||||
|
umbRequestHelper,
|
||||||
|
angularHelper,
|
||||||
|
umbDataFormatter,
|
||||||
|
eventsService
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Grid status variables
|
||||||
|
var placeHolder = "";
|
||||||
|
var currentForm = angularHelper.getCurrentForm($scope);
|
||||||
|
|
||||||
|
$scope.currentRow = null;
|
||||||
|
$scope.currentCell = null;
|
||||||
|
$scope.currentToolsControl = null;
|
||||||
|
$scope.currentControl = null;
|
||||||
|
$scope.openRTEToolbarId = null;
|
||||||
|
$scope.hasSettings = false;
|
||||||
|
$scope.showRowConfigurations = true;
|
||||||
|
$scope.sortMode = false;
|
||||||
|
$scope.reorderKey = "general_reorder";
|
||||||
|
|
||||||
|
// *********************************************
|
||||||
|
// Sortable options
|
||||||
|
// *********************************************
|
||||||
|
|
||||||
|
var draggedRteSettings;
|
||||||
|
|
||||||
|
$scope.sortableOptionsRow = {
|
||||||
|
distance: 10,
|
||||||
|
cursor: "move",
|
||||||
|
placeholder: "ui-sortable-placeholder",
|
||||||
|
handle: ".umb-row-title-bar",
|
||||||
|
helper: "clone",
|
||||||
|
forcePlaceholderSize: true,
|
||||||
|
tolerance: "pointer",
|
||||||
|
zIndex: 999999999999999999,
|
||||||
|
scrollSensitivity: 100,
|
||||||
|
cursorAt: {
|
||||||
|
top: 40,
|
||||||
|
left: 60
|
||||||
|
},
|
||||||
|
|
||||||
|
sort: function (event, ui) {
|
||||||
|
/* prevent vertical scroll out of the screen */
|
||||||
|
var max = $(".umb-grid").width() - 150;
|
||||||
|
if (parseInt(ui.helper.css("left")) > max) {
|
||||||
|
ui.helper.css({ "left": max + "px" });
|
||||||
|
}
|
||||||
|
if (parseInt(ui.helper.css("left")) < 20) {
|
||||||
|
ui.helper.css({ "left": 20 });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
start: function (e, ui) {
|
||||||
|
|
||||||
|
// Fade out row when sorting
|
||||||
|
ui.item.context.style.display = "block";
|
||||||
|
ui.item.context.style.opacity = "0.5";
|
||||||
|
|
||||||
|
draggedRteSettings = {};
|
||||||
|
ui.item.find(".mceNoEditor").each(function () {
|
||||||
|
// remove all RTEs in the dragged row and save their settings
|
||||||
|
var id = $(this).attr("id");
|
||||||
|
draggedRteSettings[id] = _.findWhere(tinyMCE.editors, { id: id }).settings;
|
||||||
|
// tinyMCE.execCommand("mceRemoveEditor", false, id);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
stop: function (e, ui) {
|
||||||
|
|
||||||
|
// Fade in row when sorting stops
|
||||||
|
ui.item.context.style.opacity = "1";
|
||||||
|
|
||||||
|
// reset all RTEs affected by the dragging
|
||||||
|
ui.item.parents(".umb-column").find(".mceNoEditor").each(function () {
|
||||||
|
var id = $(this).attr("id");
|
||||||
|
draggedRteSettings[id] = draggedRteSettings[id] || _.findWhere(tinyMCE.editors, { id: id }).settings;
|
||||||
|
tinyMCE.execCommand("mceRemoveEditor", false, id);
|
||||||
|
tinyMCE.init(draggedRteSettings[id]);
|
||||||
|
});
|
||||||
|
currentForm.$setDirty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var notIncludedRte = [];
|
||||||
|
var cancelMove = false;
|
||||||
|
var startingCell;
|
||||||
|
|
||||||
|
$scope.sortableOptionsCell = {
|
||||||
|
distance: 10,
|
||||||
|
cursor: "move",
|
||||||
|
placeholder: "ui-sortable-placeholder",
|
||||||
|
handle: ".umb-control-handle",
|
||||||
|
helper: "clone",
|
||||||
|
connectWith: ".umb-cell-inner",
|
||||||
|
forcePlaceholderSize: true,
|
||||||
|
tolerance: "pointer",
|
||||||
|
zIndex: 999999999999999999,
|
||||||
|
scrollSensitivity: 100,
|
||||||
|
cursorAt: {
|
||||||
|
top: 45,
|
||||||
|
left: 90
|
||||||
|
},
|
||||||
|
|
||||||
|
sort: function (event, ui) {
|
||||||
|
|
||||||
|
/* prevent vertical scroll out of the screen */
|
||||||
|
var position = parseInt(ui.item.parent().offset().left) + parseInt(ui.helper.css("left")) - parseInt($(".umb-grid").offset().left);
|
||||||
|
var max = $(".umb-grid").width() - 220;
|
||||||
|
if (position > max) {
|
||||||
|
ui.helper.css({ "left": max - parseInt(ui.item.parent().offset().left) + parseInt($(".umb-grid").offset().left) + "px" });
|
||||||
|
}
|
||||||
|
if (position < 0) {
|
||||||
|
ui.helper.css({ "left": 0 - parseInt(ui.item.parent().offset().left) + parseInt($(".umb-grid").offset().left) + "px" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
over: function (event, ui) {
|
||||||
|
var cell = $(event.target).scope().cell;
|
||||||
|
var allowedEditors = cell.allowed;
|
||||||
|
|
||||||
|
if (($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) ||
|
||||||
|
(startingCell != cell && cell.maxItems != '' && cell.maxItems > 0 && cell.maxItems < cell.items.length + 1)) {
|
||||||
|
|
||||||
|
$scope.$apply(function () {
|
||||||
|
$(event.target).scope().cell.dropNotAllowed = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.placeholder.hide();
|
||||||
|
cancelMove = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ($(event.target).scope().cell.items.length == 0){
|
||||||
|
|
||||||
|
$scope.$apply(function () {
|
||||||
|
$(event.target).scope().cell.dropOnEmpty = true;
|
||||||
|
});
|
||||||
|
ui.placeholder.hide();
|
||||||
|
} else {
|
||||||
|
ui.placeholder.show();
|
||||||
|
}
|
||||||
|
cancelMove = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
out: function(event, ui) {
|
||||||
|
$scope.$apply(function () {
|
||||||
|
$(event.target).scope().cell.dropNotAllowed = false;
|
||||||
|
$(event.target).scope().cell.dropOnEmpty = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function (event, ui) {
|
||||||
|
/* add all RTEs which are affected by the dragging */
|
||||||
|
if (!ui.sender) {
|
||||||
|
if (cancelMove) {
|
||||||
|
ui.item.sortable.cancel();
|
||||||
|
}
|
||||||
|
ui.item.parents(".umb-cell.content").find(".mceNoEditor").each(function () {
|
||||||
|
if ($.inArray($(this).attr("id"), notIncludedRte) < 0) {
|
||||||
|
notIncludedRte.splice(0, 0, $(this).attr("id"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$(event.target).find(".mceNoEditor").each(function () {
|
||||||
|
if ($.inArray($(this).attr("id"), notIncludedRte) < 0) {
|
||||||
|
notIncludedRte.splice(0, 0, $(this).attr("id"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
currentForm.$setDirty();
|
||||||
|
},
|
||||||
|
|
||||||
|
start: function (e, ui) {
|
||||||
|
|
||||||
|
//Get the starting cell for reference
|
||||||
|
var cell = $(e.target).scope().cell;
|
||||||
|
startingCell = cell;
|
||||||
|
|
||||||
|
// fade out control when sorting
|
||||||
|
ui.item.context.style.display = "block";
|
||||||
|
ui.item.context.style.opacity = "0.5";
|
||||||
|
|
||||||
|
// reset dragged RTE settings in case a RTE isn't dragged
|
||||||
|
draggedRteSettings = undefined;
|
||||||
|
ui.item.context.style.display = "block";
|
||||||
|
ui.item.find(".mceNoEditor").each(function () {
|
||||||
|
notIncludedRte = [];
|
||||||
|
var editors = _.findWhere(tinyMCE.editors, { id: $(this).attr("id") });
|
||||||
|
|
||||||
|
// save the dragged RTE settings
|
||||||
|
if(editors) {
|
||||||
|
draggedRteSettings = editors.settings;
|
||||||
|
|
||||||
|
// remove the dragged RTE
|
||||||
|
tinyMCE.execCommand("mceRemoveEditor", false, $(this).attr("id"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
stop: function (e, ui) {
|
||||||
|
|
||||||
|
// Fade in control when sorting stops
|
||||||
|
ui.item.context.style.opacity = "1";
|
||||||
|
|
||||||
|
ui.item.parents(".umb-cell-content").find(".mceNoEditor").each(function () {
|
||||||
|
if ($.inArray($(this).attr("id"), notIncludedRte) < 0) {
|
||||||
|
// add all dragged's neighbouring RTEs in the new cell
|
||||||
|
notIncludedRte.splice(0, 0, $(this).attr("id"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$timeout(function () {
|
||||||
|
// reconstruct the dragged RTE (could be undefined when dragging something else than RTE)
|
||||||
|
if (draggedRteSettings !== undefined) {
|
||||||
|
tinyMCE.init(draggedRteSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
_.forEach(notIncludedRte, function (id) {
|
||||||
|
// reset all the other RTEs
|
||||||
|
if (draggedRteSettings === undefined || id !== draggedRteSettings.id) {
|
||||||
|
var rteSettings = _.findWhere(tinyMCE.editors, { id: id }).settings;
|
||||||
|
tinyMCE.execCommand("mceRemoveEditor", false, id);
|
||||||
|
tinyMCE.init(rteSettings);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 500, false);
|
||||||
|
|
||||||
|
$scope.$apply(function () {
|
||||||
|
|
||||||
|
var cell = $(e.target).scope().cell;
|
||||||
|
cell.hasActiveChild = hasActiveChild(cell, cell.items);
|
||||||
|
cell.active = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.toggleSortMode = function() {
|
||||||
|
$scope.sortMode = !$scope.sortMode;
|
||||||
|
if($scope.sortMode) {
|
||||||
|
$scope.reorderKey = "general_reorderDone";
|
||||||
|
} else {
|
||||||
|
$scope.reorderKey = "general_reorder";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.showReorderButton = function() {
|
||||||
|
if($scope.model.value && $scope.model.value.sections) {
|
||||||
|
for(var i = 0; $scope.model.value.sections.length > i; i++) {
|
||||||
|
var section = $scope.model.value.sections[i];
|
||||||
|
if(section.rows && section.rows.length > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// *********************************************
|
||||||
|
// Add items overlay menu
|
||||||
|
// *********************************************
|
||||||
|
$scope.openEditorOverlay = function (event, cell, index, key) {
|
||||||
|
$scope.editorOverlay = {
|
||||||
|
view: "itempicker",
|
||||||
|
filter: cell.$allowedEditors.length > 15,
|
||||||
|
title: localizationService.localize("grid_insertControl"),
|
||||||
|
availableItems: cell.$allowedEditors,
|
||||||
|
event: event,
|
||||||
|
show: true,
|
||||||
|
submit: function(model) {
|
||||||
|
$scope.addControl(model.selectedItem, cell, index);
|
||||||
|
$scope.editorOverlay.show = false;
|
||||||
|
$scope.editorOverlay = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// *********************************************
|
||||||
|
// Row management function
|
||||||
|
// *********************************************
|
||||||
|
|
||||||
|
$scope.clickRow = function(index, rows) {
|
||||||
|
rows[index].active = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.clickOutsideRow = function(index, rows) {
|
||||||
|
rows[index].active = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getAllowedRowLayouts(section) {
|
||||||
|
|
||||||
|
var rowLayouts = $scope.model.config.items.layouts;
|
||||||
|
|
||||||
|
// fixme - remove when refactored datatype
|
||||||
|
_.forEach(rowLayouts, function(rowLayout) {
|
||||||
|
if (!rowLayout.cells) {
|
||||||
|
rowLayout.cells = rowLayout.areas;
|
||||||
|
delete rowLayout.areas;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return rowLayouts;
|
||||||
|
|
||||||
|
//fixme - this is probably not necessary any more
|
||||||
|
|
||||||
|
//This will occur if it is a new section which has been
|
||||||
|
// created from a 'template'
|
||||||
|
if (section.allowed && section.allowed.length > 0) {
|
||||||
|
return _.filter(rowLayouts, function (layout) {
|
||||||
|
return _.indexOf(section.allowed, layout.name) >= 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
|
||||||
|
return rowLayouts;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.addRow = function (section, rowLayout) {
|
||||||
|
|
||||||
|
//copy the selected layout into the rows collection
|
||||||
|
var row = angular.copy(rowLayout);
|
||||||
|
|
||||||
|
//fixme - we've got discrepancies between alias/name on row layouts and rows.
|
||||||
|
row.alias = row.name;
|
||||||
|
|
||||||
|
// Init row value
|
||||||
|
row = initRow(row);
|
||||||
|
|
||||||
|
// Push the new row
|
||||||
|
if (row) {
|
||||||
|
section.rows.push(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentForm.$setDirty();
|
||||||
|
|
||||||
|
$scope.showRowConfigurations = false;
|
||||||
|
|
||||||
|
eventsService.emit("grid.rowAdded", { el: $scope.$el, scope: $scope, row: row });
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.removeRow = function (section, $index) {
|
||||||
|
if (section.rows.length > 0) {
|
||||||
|
section.rows.splice($index, 1);
|
||||||
|
$scope.currentRow = null;
|
||||||
|
$scope.openRTEToolbarId = null;
|
||||||
|
currentForm.$setDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(section.rows.length === 0) {
|
||||||
|
$scope.showRowConfigurations = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var shouldApply = function(item, itemType, gridItem) {
|
||||||
|
if (item.applyTo === undefined || item.applyTo === null || item.applyTo === "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof (item.applyTo) === "string") {
|
||||||
|
return item.applyTo === itemType;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemType === "row") {
|
||||||
|
if (item.applyTo.row === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (item.applyTo.row === null || item.applyTo.row === "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var rows = item.applyTo.row.split(',');
|
||||||
|
return _.indexOf(rows, gridItem.name) !== -1;
|
||||||
|
} else if (itemType === "cell") {
|
||||||
|
if (item.applyTo.cell === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (item.applyTo.cell === null || item.applyTo.cell === "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var cells = item.applyTo.cell.split(',');
|
||||||
|
var cellSize = gridItem.grid.toString();
|
||||||
|
return _.indexOf(cells, cellSize) !== -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.editGridItemSettings = function (gridItem, itemType) {
|
||||||
|
|
||||||
|
placeHolder = "{0}";
|
||||||
|
|
||||||
|
var styles, config;
|
||||||
|
if (itemType === 'control') {
|
||||||
|
styles = null;
|
||||||
|
config = angular.copy(gridItem.editor.config.settings);
|
||||||
|
} else {
|
||||||
|
styles = _.filter(angular.copy($scope.model.config.items.styles), function (item) { return shouldApply(item, itemType, gridItem); });
|
||||||
|
config = _.filter(angular.copy($scope.model.config.items.config), function (item) { return shouldApply(item, itemType, gridItem); });
|
||||||
|
}
|
||||||
|
|
||||||
|
if(angular.isObject(gridItem.config)){
|
||||||
|
_.each(config, function(cfg){
|
||||||
|
var val = gridItem.config[cfg.key];
|
||||||
|
if(val){
|
||||||
|
cfg.value = stripModifier(val, cfg.modifier);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(angular.isObject(gridItem.styles)){
|
||||||
|
_.each(styles, function(style){
|
||||||
|
var val = gridItem.styles[style.key];
|
||||||
|
if(val){
|
||||||
|
style.value = stripModifier(val, style.modifier);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.gridItemSettingsDialog = {};
|
||||||
|
$scope.gridItemSettingsDialog.view = "views/propertyeditors/grid/dialogs/config.html";
|
||||||
|
$scope.gridItemSettingsDialog.title = "Settings";
|
||||||
|
$scope.gridItemSettingsDialog.styles = styles;
|
||||||
|
$scope.gridItemSettingsDialog.config = config;
|
||||||
|
|
||||||
|
$scope.gridItemSettingsDialog.show = true;
|
||||||
|
|
||||||
|
$scope.gridItemSettingsDialog.submit = function(model) {
|
||||||
|
|
||||||
|
var styleObject = {};
|
||||||
|
var configObject = {};
|
||||||
|
|
||||||
|
_.each(model.styles, function(style){
|
||||||
|
if(style.value){
|
||||||
|
styleObject[style.key] = addModifier(style.value, style.modifier);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_.each(model.config, function (cfg) {
|
||||||
|
if (cfg.value) {
|
||||||
|
configObject[cfg.key] = addModifier(cfg.value, cfg.modifier);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gridItem.styles = styleObject;
|
||||||
|
gridItem.config = configObject;
|
||||||
|
gridItem.hasConfig = gridItemHasConfig(styleObject, configObject);
|
||||||
|
|
||||||
|
currentForm.$setDirty();
|
||||||
|
|
||||||
|
$scope.gridItemSettingsDialog.show = false;
|
||||||
|
$scope.gridItemSettingsDialog = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.gridItemSettingsDialog.close = function(oldModel) {
|
||||||
|
$scope.gridItemSettingsDialog.show = false;
|
||||||
|
$scope.gridItemSettingsDialog = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function stripModifier(val, modifier) {
|
||||||
|
if (!val || !modifier || modifier.indexOf(placeHolder) < 0) {
|
||||||
|
return val;
|
||||||
|
} else {
|
||||||
|
var paddArray = modifier.split(placeHolder);
|
||||||
|
if(paddArray.length == 1){
|
||||||
|
if (modifier.indexOf(placeHolder) === 0) {
|
||||||
|
return val.slice(0, -paddArray[0].length);
|
||||||
|
} else {
|
||||||
|
return val.slice(paddArray[0].length, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (paddArray[1].length === 0) {
|
||||||
|
return val.slice(paddArray[0].length);
|
||||||
|
}
|
||||||
|
return val.slice(paddArray[0].length, -paddArray[1].length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var addModifier = function(val, modifier){
|
||||||
|
if (!modifier || modifier.indexOf(placeHolder) < 0) {
|
||||||
|
return val;
|
||||||
|
} else {
|
||||||
|
return modifier.replace(placeHolder, val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function gridItemHasConfig(styles, config) {
|
||||||
|
|
||||||
|
if(_.isEmpty(styles) && _.isEmpty(config)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// *********************************************
|
||||||
|
// cell management functions
|
||||||
|
// *********************************************
|
||||||
|
|
||||||
|
$scope.clickCell = function(index, cells, row) {
|
||||||
|
cells[index].active = true;
|
||||||
|
row.hasActiveChild = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.clickOutsideCell = function(index, cells, row) {
|
||||||
|
cells[index].active = false;
|
||||||
|
row.hasActiveChild = hasActiveChild(row, cells);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.cellPreview = function (cell) {
|
||||||
|
if (cell && cell.$allowedEditors) {
|
||||||
|
var editor = cell.$allowedEditors[0];
|
||||||
|
return editor.icon;
|
||||||
|
} else {
|
||||||
|
return "icon-layout";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// *********************************************
|
||||||
|
// Control management functions
|
||||||
|
// *********************************************
|
||||||
|
$scope.clickControl = function (index, items, cell) {
|
||||||
|
items[index].active = true;
|
||||||
|
cell.hasActiveChild = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.clickOutsideControl = function (index, items, cell) {
|
||||||
|
items[index].active = false;
|
||||||
|
cell.hasActiveChild = hasActiveChild(cell, items);
|
||||||
|
};
|
||||||
|
|
||||||
|
function hasActiveChild(item, children) {
|
||||||
|
|
||||||
|
var activeChild = false;
|
||||||
|
|
||||||
|
for(var i = 0; children.length > i; i++) {
|
||||||
|
var child = children[i];
|
||||||
|
|
||||||
|
if(child.active) {
|
||||||
|
activeChild = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(activeChild) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var guid = (function () {
|
||||||
|
function s4() {
|
||||||
|
return Math.floor((1 + Math.random()) * 0x10000)
|
||||||
|
.toString(16)
|
||||||
|
.substring(1);
|
||||||
|
}
|
||||||
|
return function () {
|
||||||
|
return s4() + s4() + "-" + s4() + "-" + s4() + "-" +
|
||||||
|
s4() + "-" + s4() + s4() + s4();
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
$scope.addControl = function (editor, cell, index, initialize) {
|
||||||
|
|
||||||
|
initialize = (initialize !== false);
|
||||||
|
|
||||||
|
var newItem = {
|
||||||
|
type: editor.udi,
|
||||||
|
values: {},
|
||||||
|
//editor: editor,
|
||||||
|
$initializing: initialize
|
||||||
|
};
|
||||||
|
|
||||||
|
if (index === undefined) {
|
||||||
|
index = cell.items.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
newItem.active = true;
|
||||||
|
|
||||||
|
//populate control
|
||||||
|
initItem(newItem, index + 1);
|
||||||
|
|
||||||
|
cell.items.push(newItem);
|
||||||
|
|
||||||
|
eventsService.emit("grid.itemAdded", { el: $scope.$el, scope: $scope, cell: cell, item: newItem });
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.addTinyMce = function (cell) {
|
||||||
|
var rte = getEditor("rte");
|
||||||
|
$scope.addControl(rte, cell);
|
||||||
|
};
|
||||||
|
|
||||||
|
function getEditor(alias) {
|
||||||
|
return _.find($scope.availableEditors, function (editor) { return editor.alias === alias; });
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEditorByUdi(udi) {
|
||||||
|
return _.find($scope.availableEditors, function (editor) { return editor.udi === udi; });
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.removeControl = function (cell, $index) {
|
||||||
|
$scope.currentControl = null;
|
||||||
|
cell.items.splice($index, 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.percentage = function (spans) {
|
||||||
|
return ((spans / $scope.model.config.items.columns) * 100).toFixed(8);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
$scope.clearPrompt = function (scopedObject, e) {
|
||||||
|
scopedObject.deletePrompt = false;
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.togglePrompt = function (scopedObject) {
|
||||||
|
scopedObject.deletePrompt = !scopedObject.deletePrompt;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.hidePrompt = function (scopedObject) {
|
||||||
|
scopedObject.deletePrompt = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.toggleAddRow = function() {
|
||||||
|
$scope.showRowConfigurations = !$scope.showRowConfigurations;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// *********************************************
|
||||||
|
// Initialization - this runs ONE time
|
||||||
|
// these methods are called from ng-init on the template
|
||||||
|
// so we can controll their first load data
|
||||||
|
//
|
||||||
|
// intialization sets non-saved data like percentage sizing, allowed editors and
|
||||||
|
// other data that should all be pre-fixed with $ to strip it out on save
|
||||||
|
// *********************************************
|
||||||
|
|
||||||
|
// *********************************************
|
||||||
|
// Init template + sections
|
||||||
|
// *********************************************
|
||||||
|
function initContent () {
|
||||||
|
//fixme - config will be different we need to wait for that
|
||||||
|
////settings indicator shortcut
|
||||||
|
//if (($scope.model.config.items.config && $scope.model.config.items.config.length > 0) || ($scope.model.config.items.styles && $scope.model.config.items.styles.length > 0)) {
|
||||||
|
// $scope.hasSettings = true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//ensure the grid has a column value set, if nothing is found, set it to 12
|
||||||
|
if (!$scope.model.config.items.columns){
|
||||||
|
$scope.model.config.items.columns = 12;
|
||||||
|
} else if (angular.isString($scope.model.config.items.columns)) {
|
||||||
|
$scope.model.config.items.columns = parseInt($scope.model.config.items.columns);
|
||||||
|
}
|
||||||
|
|
||||||
|
//fixme - we are currently still using "layouts" but we will eventually get rid of them. So we need to wrap
|
||||||
|
//our current value with the layout JS since we only persist with rows.
|
||||||
|
var section = {
|
||||||
|
grid: $scope.model.config.items.columns,
|
||||||
|
rows: []
|
||||||
|
};
|
||||||
|
var sectionWrapper = {
|
||||||
|
name: "1 column layout",
|
||||||
|
sections: [
|
||||||
|
section
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
angular.extend(sectionWrapper.sections[0], $scope.model.value);
|
||||||
|
$scope.model.value = sectionWrapper;
|
||||||
|
|
||||||
|
initSection(section);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
function initSection(section) {
|
||||||
|
section.$percentage = $scope.percentage(section.grid);
|
||||||
|
|
||||||
|
section.allowedRowLayouts = getAllowedRowLayouts(section);
|
||||||
|
|
||||||
|
if (!section.rows || section.rows.length === 0) {
|
||||||
|
section.rows = [];
|
||||||
|
if (section.allowedRowLayouts.length === 1){
|
||||||
|
$scope.addRow(section, section.allowedRowLayouts[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_.forEach(section.rows, function (row, index) {
|
||||||
|
if (!row.$initialized) {
|
||||||
|
var initd = initRow(row);
|
||||||
|
|
||||||
|
//if init fails, remove
|
||||||
|
if (!initd) {
|
||||||
|
section.rows.splice(index, 1);
|
||||||
|
} else {
|
||||||
|
section.rows[index] = initd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// if there is more than one row added - hide row add tools
|
||||||
|
$scope.showRowConfigurations = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// *********************************************
|
||||||
|
// Init layout / row
|
||||||
|
// *********************************************
|
||||||
|
function initRow (row) {
|
||||||
|
|
||||||
|
//merge the layout data with the original config data
|
||||||
|
//if there are no config info on this, splice it out
|
||||||
|
var original = _.find($scope.model.config.items.layouts, function (o) { return o.name === row.alias; });
|
||||||
|
|
||||||
|
if (!original) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
//make a copy to not touch the original config
|
||||||
|
original = angular.copy(original);
|
||||||
|
original.styles = row.styles;
|
||||||
|
original.config = row.config;
|
||||||
|
original.hasConfig = gridItemHasConfig(row.styles, row.config);
|
||||||
|
|
||||||
|
|
||||||
|
//sync cell configuration
|
||||||
|
_.each(original.cells, function (cell, cellIndex) {
|
||||||
|
|
||||||
|
|
||||||
|
if (cell.grid > 0) {
|
||||||
|
var currentCell = row.cells[cellIndex];
|
||||||
|
|
||||||
|
if (currentCell) {
|
||||||
|
cell.config = currentCell.config;
|
||||||
|
cell.styles = currentCell.styles;
|
||||||
|
cell.hasConfig = gridItemHasConfig(currentCell.styles, currentCell.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
//set editor permissions
|
||||||
|
if (!cell.allowed || cell.allowAll === true) {
|
||||||
|
cell.$allowedEditors = $scope.availableEditors;
|
||||||
|
cell.$allowsRTE = true;
|
||||||
|
} else {
|
||||||
|
cell.$allowedEditors = _.filter($scope.availableEditors, function (editor) {
|
||||||
|
return _.indexOf(cell.allowed, editor.udi) >= 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (_.indexOf(cell.allowed, "rte") >= 0) {
|
||||||
|
cell.$allowsRTE = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//copy over existing items into the new cells
|
||||||
|
if (row.cells.length > cellIndex && row.cells[cellIndex].items) {
|
||||||
|
cell.items = currentCell.items;
|
||||||
|
|
||||||
|
_.forEach(cell.items, function (item, itemIndex) {
|
||||||
|
initItem(item, itemIndex);
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
//if empty
|
||||||
|
cell.items = [];
|
||||||
|
|
||||||
|
//if only one allowed editor
|
||||||
|
if (cell.$allowedEditors.length === 1){
|
||||||
|
$scope.addControl(cell.$allowedEditors[0], cell, 0, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//set width
|
||||||
|
cell.$percentage = $scope.percentage(cell.grid);
|
||||||
|
cell.$uniqueId = guid();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
original.cells.splice(cellIndex, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//replace the old row
|
||||||
|
original.$initialized = true;
|
||||||
|
|
||||||
|
//set a disposable unique ID
|
||||||
|
original.$uniqueId = guid();
|
||||||
|
|
||||||
|
//set a no disposable unique ID (util for row styling)
|
||||||
|
original.id = !row.id ? guid() : row.id;
|
||||||
|
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// *********************************************
|
||||||
|
// Init control
|
||||||
|
// *********************************************
|
||||||
|
|
||||||
|
function initItem(item, index) {
|
||||||
|
item.$index = index;
|
||||||
|
item.$uniqueId = guid();
|
||||||
|
|
||||||
|
//create the properties collection which will be bound here
|
||||||
|
item.properties = [];
|
||||||
|
|
||||||
|
//get a scaffold for the current doc type
|
||||||
|
gridResource.getScaffold(item.type).then(function (c) {
|
||||||
|
if (c.tabs && c.tabs.length) {
|
||||||
|
item.properties = c.tabs[0].properties;
|
||||||
|
_.each(item.properties, function (p) {
|
||||||
|
p.$uniqueId = guid();
|
||||||
|
p.hideLabel = true;
|
||||||
|
p.value = item.values[p.alias];
|
||||||
|
|
||||||
|
var editor = getEditorByUdi(item.type);
|
||||||
|
|
||||||
|
//now we need to re-assign the view and set the boolean if it's a preview or not
|
||||||
|
if (editor.views && editor.views[p.alias]) {
|
||||||
|
p.view = editor.views[p.alias].view;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//show the icon
|
||||||
|
p.view = "views/propertyeditors/grid2/cellplaceholder.html";
|
||||||
|
p.icon = editor.icon;
|
||||||
|
p.title = editor.name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Called once to initialize the editor */
|
||||||
|
function init() {
|
||||||
|
gridResource.getGridContentTypes().then(function (response) {
|
||||||
|
$scope.availableEditors = response;
|
||||||
|
|
||||||
|
//Localize the grid editor names
|
||||||
|
angular.forEach($scope.availableEditors, function (value, key) {
|
||||||
|
//If no translation is provided, keep using the editor name from the manifest
|
||||||
|
if (localizationService.dictionary.hasOwnProperty("grid_" + value.alias)) {
|
||||||
|
value.name = localizationService.localize("grid_" + value.alias);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.contentReady = true;
|
||||||
|
|
||||||
|
eventsService.emit("grid.initializing", { el: $scope.$el, scope: $scope });
|
||||||
|
|
||||||
|
//Init grid
|
||||||
|
initContent();
|
||||||
|
|
||||||
|
eventsService.emit("grid.initialized", { el: $scope.$el, scope: $scope });
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
//Clean the grid value before submitting to the server, we don't need
|
||||||
|
// all of that grid configuration in the value to be stored!! All of that
|
||||||
|
// needs to be merged in at runtime to ensure that the real config values are used
|
||||||
|
// if they are ever updated.
|
||||||
|
|
||||||
|
var unsubscribe = $scope.$on("formSubmitting", function () {
|
||||||
|
$scope.model.value = umbDataFormatter.mapGridValueToPersistableModel($scope.model.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
//when the scope is destroyed we need to unsubscribe
|
||||||
|
$scope.$on("$destroy", function () {
|
||||||
|
unsubscribe();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,319 @@
|
|||||||
|
<div ng-controller="Umbraco.PropertyEditors.Grid2Controller" class="umb-grid umb-property-editor clearfix" umb-grid-events id="umb-grid">
|
||||||
|
|
||||||
|
<umb-editor-sub-header style="background-color: white;">
|
||||||
|
|
||||||
|
<umb-editor-sub-header-content-right>
|
||||||
|
<umb-button ng-if="showReorderButton()"
|
||||||
|
type="button"
|
||||||
|
icon="icon-navigation"
|
||||||
|
button-style="link"
|
||||||
|
label-key="{{reorderKey}}"
|
||||||
|
action="toggleSortMode()">
|
||||||
|
</umb-button>
|
||||||
|
</umb-editor-sub-header-content-right>
|
||||||
|
|
||||||
|
</umb-editor-sub-header>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<div ng-if="contentReady">
|
||||||
|
|
||||||
|
<!-- Template picker -->
|
||||||
|
|
||||||
|
<div class="templates-preview"
|
||||||
|
ng-show="!model.value || model.value == ''">
|
||||||
|
|
||||||
|
<p><strong><localize key="grid_chooseLayout" /></strong></p>
|
||||||
|
|
||||||
|
<div class="preview-rows layout"
|
||||||
|
ng-repeat="template in model.config.items.templates"
|
||||||
|
ng-click="addTemplate(template)">
|
||||||
|
|
||||||
|
<div class="preview-row">
|
||||||
|
|
||||||
|
<div class="preview-col"
|
||||||
|
ng-repeat="section in template.sections"
|
||||||
|
ng-style="{width: percentage(section.grid) + '%'}">
|
||||||
|
|
||||||
|
<div class="preview-cell">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="preview-overlay">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<small>{{template.name}}</small>
|
||||||
|
|
||||||
|
</div> <!-- .templates-preview-rows -->
|
||||||
|
|
||||||
|
</div> <!-- .templates-preview -->
|
||||||
|
<!-- template picker end -->
|
||||||
|
<!-- Grids -->
|
||||||
|
<div class="umb-grid-width">
|
||||||
|
<div class="tb">
|
||||||
|
|
||||||
|
<!-- for each column in model -->
|
||||||
|
<div class="umb-column td"
|
||||||
|
ng-repeat="section in model.value.sections"
|
||||||
|
ng-init="initSection(section)"
|
||||||
|
ng-style="{width: section.$percentage + '%'}">
|
||||||
|
|
||||||
|
<div ui-sortable="sortableOptionsRow" ng-model="section.rows">
|
||||||
|
|
||||||
|
<!-- for each row in template section -->
|
||||||
|
<!-- ng-mouseenter="setCurrentRow(row)" -->
|
||||||
|
<!-- ng-mouseleave="disableCurrentRow()" -->
|
||||||
|
<div class="umb-row"
|
||||||
|
ng-repeat="row in section.rows"
|
||||||
|
ng-click="clickRow($index, section.rows)"
|
||||||
|
ng-class="{
|
||||||
|
'-has-config': row.hasConfig,
|
||||||
|
'-active': row.active,
|
||||||
|
'-active-child': row.hasActiveChild}"
|
||||||
|
on-outside-click="clickOutsideRow($index, section.rows)"
|
||||||
|
bind-click-on="{{row.active}}"
|
||||||
|
data-rowid="{{row.$uniqueId}}">
|
||||||
|
|
||||||
|
<div class="umb-row-title-bar">
|
||||||
|
|
||||||
|
<div class=".umb-grid-right">
|
||||||
|
<div class="umb-row-title">{{row.label || row.name}}</div>
|
||||||
|
|
||||||
|
<div class="umb-grid-has-config" ng-if="row.hasConfig && !sortMode">
|
||||||
|
<localize key="grid_settingsApplied" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Row tool -->
|
||||||
|
<div class="umb-tools row-tools" ng-show="row.active && !sortMode">
|
||||||
|
|
||||||
|
<div class="cell-tools-edit row-tool" ng-if="hasSettings">
|
||||||
|
<i class="icon icon-settings" title="@grid_settings" localize="title" ng-click="editGridItemSettings(row, 'row')"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell-tools-remove row-tool">
|
||||||
|
<i class="icon-trash" ng-click="togglePrompt(row)"></i>
|
||||||
|
<umb-confirm-action ng-if="row.deletePrompt"
|
||||||
|
direction="left"
|
||||||
|
on-confirm="removeRow(section, $index)"
|
||||||
|
on-cancel="hidePrompt(row)">
|
||||||
|
</umb-confirm-action>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- row container -->
|
||||||
|
<div class="{{row.cssClass}} umb-row-inner">
|
||||||
|
|
||||||
|
<div class="mainTb">
|
||||||
|
<div class="tb">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<!-- Cells in row -->
|
||||||
|
<div class="umb-cell td mainTd"
|
||||||
|
ng-repeat="cell in row.cells"
|
||||||
|
ng-style="{width: cell.$percentage + '%'}"
|
||||||
|
ng-class="{
|
||||||
|
'-has-config': cell.hasConfig,
|
||||||
|
'-active': cell.active,
|
||||||
|
'-active-child': cell.hasActiveChild}"
|
||||||
|
ng-model="cell.items"
|
||||||
|
ng-click="clickCell($index, row.cells, row)"
|
||||||
|
on-outside-click="clickOutsideCell($index, row.cells, row)"
|
||||||
|
bind-click-on="{{cell.active}}">
|
||||||
|
|
||||||
|
<!-- Cell -->
|
||||||
|
<div class="umb-cell-content"
|
||||||
|
ng-class="
|
||||||
|
{'-active': cell.active,
|
||||||
|
'-has-editors' : cell.items.length>
|
||||||
|
0,
|
||||||
|
'-collapsed': sortMode}">
|
||||||
|
|
||||||
|
<!-- disable drop overlay -->
|
||||||
|
<div class="drop-overlay -disable" ng-if="cell.dropNotAllowed">
|
||||||
|
<i class="icon-delete drop-icon"></i>
|
||||||
|
<localize key="grid_contentNotAllowed" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- allow drop overlay -->
|
||||||
|
<div class="drop-overlay -allow" ng-if="cell.dropOnEmpty">
|
||||||
|
<i class="icon-download drop-icon"></i>
|
||||||
|
<localize key="grid_contentAllowed" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="umb-grid-has-config" ng-if="cell.hasConfig && !sortMode">
|
||||||
|
<localize key="grid_settingsApplied" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell-tools" ng-if="cell.active && !sortMode">
|
||||||
|
<div class="cell-tool" ng-click="editGridItemSettings(cell, 'cell')">
|
||||||
|
<i class="icon-settings"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="umb-cell-inner" ui-sortable="sortableOptionsCell" ng-model="cell.items">
|
||||||
|
|
||||||
|
<!-- Item placeholder -->
|
||||||
|
<div class="umb-cell-placeholder" ng-if="cell.items.length === 0" ng-click="openEditorOverlay($event, cell, 0, cell.$uniqueId);">
|
||||||
|
<div class="cell-tools-add -center">
|
||||||
|
<localize ng-if="!sortMode" key="grid_addElement" />
|
||||||
|
<localize ng-if="sortMode" key="grid_dropElement" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- for each control in cells -->
|
||||||
|
<div class="umb-control"
|
||||||
|
ng-repeat="item in cell.items"
|
||||||
|
ng-click="clickControl($index, cell.items, cell)"
|
||||||
|
ng-class="{'-active': item.active}"
|
||||||
|
on-outside-click="clickOutsideControl($index, cell.items, cell)"
|
||||||
|
bind-click-on="{{item.active}}"
|
||||||
|
umb-set-dirty-on-change="{{item.value}}"
|
||||||
|
data-itemid="{{item.$uniqueId}}">
|
||||||
|
|
||||||
|
<div class="umb-control-click-overlay" ng-show="!item.active && !sortMode"></div>
|
||||||
|
|
||||||
|
<div class="umb-control-collapsed umb-control-handle" ng-show="sortMode">
|
||||||
|
{{item.editor.name}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="umb-control-inner" ng-hide="sortMode">
|
||||||
|
|
||||||
|
<div class="umb-control-bar umb-control-handle">
|
||||||
|
|
||||||
|
<div class="umb-control-title" ng-if="item.active">
|
||||||
|
{{item.editor.name}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="umb-tools" ng-if="item.active">
|
||||||
|
|
||||||
|
<div class="umb-control-tool" ng-if="item.editor.config.settings">
|
||||||
|
<i class="umb-control-tool-icon icon-settings" ng-click="editGridItemSettings(item, 'control')"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="umb-control-tool">
|
||||||
|
<i class="umb-control-tool-icon icon-trash" ng-click="togglePrompt(item)"></i>
|
||||||
|
<umb-confirm-action ng-if="item.deletePrompt"
|
||||||
|
direction="left"
|
||||||
|
on-confirm="removeControl(cell, $index)"
|
||||||
|
on-cancel="hidePrompt(control)">
|
||||||
|
</umb-confirm-action>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Rendering the editor for specific control -->
|
||||||
|
<div ng-if="item">
|
||||||
|
<div ng-repeat="property in item.properties">
|
||||||
|
<umb-property property="property">
|
||||||
|
<umb-property-editor model="property"></umb-property-editor>
|
||||||
|
</umb-property>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- items repeat end -->
|
||||||
|
<!-- if cell is empty tools -->
|
||||||
|
<div class="umb-grid-add-more-content" ng-if="cell.items.length > 0 && !sortMode && (cell.maxItems == undefined || cell.maxItems == '' || cell.maxItems == 0 || cell.maxItems > cell.items.length)">
|
||||||
|
<div class="cell-tools-add -bar newbtn" ng-click="openEditorOverlay($event, cell, 0, cell.$uniqueId);"><localize key="grid_addElement" /></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- cells repeat end -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- row container end -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- row repeat -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- row sortable end -->
|
||||||
|
<!-- column tools -->
|
||||||
|
|
||||||
|
<div class="umb-add-row" ng-if="!sortMode">
|
||||||
|
|
||||||
|
<a href=""
|
||||||
|
class="iconBox"
|
||||||
|
ng-click="toggleAddRow()"
|
||||||
|
ng-if="!showRowConfigurations">
|
||||||
|
|
||||||
|
<i class=" icon icon-add" title="@general_add" localize="title"></i>
|
||||||
|
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="templates-preview" ng-if="showRowConfigurations">
|
||||||
|
|
||||||
|
<p ng-hide="section.rows.length > 0"><strong><localize key="grid_addRows" /></strong></p>
|
||||||
|
|
||||||
|
<div class="preview-rows columns"
|
||||||
|
ng-repeat="rowLayout in section.allowedRowLayouts"
|
||||||
|
ng-show="rowLayout.cells.length > 0"
|
||||||
|
ng-click="addRow(section, rowLayout)">
|
||||||
|
|
||||||
|
<div class="preview-row">
|
||||||
|
|
||||||
|
<div class="preview-col" ng-style="{width: percentage(cell.grid) + '%'}" ng-repeat="cell in rowLayout.cells">
|
||||||
|
|
||||||
|
<div class="preview-cell">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="preview-overlay">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<small>{{rowLayout.label || rowLayout.name}}</small>
|
||||||
|
|
||||||
|
</div> <!-- .templates-preview-rows -->
|
||||||
|
|
||||||
|
</div> <!-- .templates-preview -->
|
||||||
|
<!-- column tools end -->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- column repeat end -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
<code>{{model.value | json}}</code>
|
||||||
|
</pre>
|
||||||
|
<pre>
|
||||||
|
<code>{{availableEditors | json}}</code>
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<umb-overlay ng-if="editorOverlay.show"
|
||||||
|
model="editorOverlay"
|
||||||
|
view="editorOverlay.view"
|
||||||
|
position="target">
|
||||||
|
</umb-overlay>
|
||||||
|
|
||||||
|
<umb-overlay ng-if="gridItemSettingsDialog.show"
|
||||||
|
model="gridItemSettingsDialog"
|
||||||
|
view="gridItemSettingsDialog.view"
|
||||||
|
position="right">
|
||||||
|
</umb-overlay>
|
||||||
|
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<div ng-controller="Umbraco.PropertyEditors.textboxController">
|
||||||
|
<textarea rows="1"
|
||||||
|
umb-auto-resize
|
||||||
|
umb-auto-focus
|
||||||
|
ng-required="model.validation.mandatory"
|
||||||
|
ng-keyup="model.change()"
|
||||||
|
ng-trim="false"
|
||||||
|
class="textstring input-block-level" id="{{model.$uniqueId}}_text"
|
||||||
|
ng-model="model.value"
|
||||||
|
localize="placeholder" placeholder="@grid_placeholderWriteHere"
|
||||||
|
style="font-size: 36px; line-height: 45px; font-weight: bold;"></textarea>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,361 @@
|
|||||||
|
/*globals jasmine*/
|
||||||
|
|
||||||
|
describe("grid 2", function () {
|
||||||
|
|
||||||
|
var controller,
|
||||||
|
rootScope,
|
||||||
|
scope,
|
||||||
|
gridResource,
|
||||||
|
angularHelper,
|
||||||
|
q,
|
||||||
|
// These are populated at the bottom of this file
|
||||||
|
gridEditors,
|
||||||
|
rteScaffold,
|
||||||
|
headerScaffold,
|
||||||
|
scaffolds,
|
||||||
|
fullModel;
|
||||||
|
|
||||||
|
beforeEach(module('umbraco'));
|
||||||
|
|
||||||
|
beforeEach(inject(function (
|
||||||
|
$rootScope,
|
||||||
|
$controller,
|
||||||
|
$q
|
||||||
|
) {
|
||||||
|
q = $q;
|
||||||
|
rootScope = $rootScope;
|
||||||
|
scope = $rootScope.$new();
|
||||||
|
|
||||||
|
scope.model = JSON.parse(JSON.stringify(fullModel));
|
||||||
|
|
||||||
|
gridResource = {
|
||||||
|
getGridContentTypes: function () {
|
||||||
|
var def = q.defer();
|
||||||
|
def.resolve(gridEditors);
|
||||||
|
return def.promise;
|
||||||
|
},
|
||||||
|
getScaffold: function (uuid) {
|
||||||
|
var def = q.defer();
|
||||||
|
def.resolve(scaffolds[uuid]);
|
||||||
|
return def.promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angularHelper = {
|
||||||
|
getCurrentForm: function () {
|
||||||
|
return {
|
||||||
|
$setDirty: function () {
|
||||||
|
var iRememberBeingDirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
controller = $controller("Umbraco.PropertyEditors.Grid2Controller", {
|
||||||
|
"$scope": scope,
|
||||||
|
"gridResource": gridResource,
|
||||||
|
"angularHelper": angularHelper
|
||||||
|
});
|
||||||
|
|
||||||
|
//fixme - Disable digest while we don't have the right logic
|
||||||
|
scope.$digest();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("defaults to 12 columns", function () {
|
||||||
|
expect(scope.model.config.items.columns).toBe(12);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when only one layout and row config, adds layout and row", function () {
|
||||||
|
expect(scope.model.value.sections[0].rows[0]).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows add editor dialog", function () {
|
||||||
|
expect(scope.editorOverlay).toBeUndefined();
|
||||||
|
scope.openEditorOverlay(
|
||||||
|
{},
|
||||||
|
scope.model.value.sections[0].rows[0].cells[0],
|
||||||
|
0,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
expect(scope.editorOverlay).toEqual(jasmine.objectContaining({
|
||||||
|
view: "itempicker"
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("adds editor to cell",
|
||||||
|
function () {
|
||||||
|
scope.addControl(
|
||||||
|
gridEditors[0],
|
||||||
|
scope.model.value.sections[0].rows[0].cells[0],
|
||||||
|
0
|
||||||
|
);
|
||||||
|
expect(scope.model.value.sections[0].rows[0].cells[0].items[2]).toEqual(jasmine.objectContaining({
|
||||||
|
"type": "umb://document-type/9c620216c4f14f67b16468b103a09144",
|
||||||
|
"values": {
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("maps the model to persistable model",
|
||||||
|
function () {
|
||||||
|
//var persistable = scope.mapToPersistableModel(fullModel.value);
|
||||||
|
|
||||||
|
scope.$emit("formSubmitting");
|
||||||
|
var persisted = scope.model.value;
|
||||||
|
|
||||||
|
expect(persisted).toEqual(jasmine.objectContaining({
|
||||||
|
rows: [
|
||||||
|
{
|
||||||
|
alias: "Headline",
|
||||||
|
settings: {},
|
||||||
|
cells: [
|
||||||
|
{
|
||||||
|
"settings": {},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "umb://document-type/9c620216c4f14f67b16468b103a09144",
|
||||||
|
"values": {
|
||||||
|
"value": "Hello world!"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"type": "umb://document-type/437c65f147054681911d8a884459ab47",
|
||||||
|
"values": {
|
||||||
|
"content": "<p>More hello world!</p>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
gridEditors = [
|
||||||
|
{
|
||||||
|
"id": 1061,
|
||||||
|
"udi": "umb://document-type/9c620216c4f14f67b16468b103a09144",
|
||||||
|
"name": "Header",
|
||||||
|
"alias": "header",
|
||||||
|
"icon": "icon-document",
|
||||||
|
"views": {
|
||||||
|
"value": {
|
||||||
|
"view": "views/propertyeditors/textbox/textbox.inline.html",
|
||||||
|
"isPreview": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"id": 1062,
|
||||||
|
"udi": "umb://document-type/437c65f147054681911d8a884459ab47",
|
||||||
|
"name": "RTE",
|
||||||
|
"alias": "richText",
|
||||||
|
"icon": "icon-document",
|
||||||
|
"views": {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
rteScaffold = {
|
||||||
|
"tabs": [
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"active": true,
|
||||||
|
"label": "Inline",
|
||||||
|
"alias": "Inline",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"label": "Content",
|
||||||
|
"description": null,
|
||||||
|
"view": "rte",
|
||||||
|
"config": {
|
||||||
|
"editor": null,
|
||||||
|
"hideLabel": false
|
||||||
|
},
|
||||||
|
"hideLabel": false,
|
||||||
|
"validation": {
|
||||||
|
"mandatory": false,
|
||||||
|
"pattern": null
|
||||||
|
},
|
||||||
|
"readonly": false,
|
||||||
|
"id": 0,
|
||||||
|
"value": null,
|
||||||
|
"alias": "content",
|
||||||
|
"editor": "Umbraco.TinyMCEv3",
|
||||||
|
"isSensitive": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"updateDate": "0001-01-01T00:00:00",
|
||||||
|
"createDate": "0001-01-01T00:00:00",
|
||||||
|
"published": false,
|
||||||
|
"edited": false,
|
||||||
|
"owner": null,
|
||||||
|
"updater": null,
|
||||||
|
"contentTypeAlias": "richText",
|
||||||
|
"sortOrder": 0,
|
||||||
|
"name": null,
|
||||||
|
"id": 0,
|
||||||
|
"udi": "umb://document/437c65f147054681911d8a884459ab47",
|
||||||
|
"icon": "icon-document",
|
||||||
|
"trashed": false,
|
||||||
|
"key": "437c65f1-4705-4681-911d-8a884459ab47",
|
||||||
|
"parentId": -1,
|
||||||
|
"alias": null,
|
||||||
|
"path": null,
|
||||||
|
"metaData": {}
|
||||||
|
};
|
||||||
|
|
||||||
|
headerScaffold = {
|
||||||
|
"tabs": [
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"active": true,
|
||||||
|
"label": "Inline",
|
||||||
|
"alias": "Inline",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"label": "Value",
|
||||||
|
"description": null,
|
||||||
|
"view": "textbox",
|
||||||
|
"config": {
|
||||||
|
"maxChars": null
|
||||||
|
},
|
||||||
|
"hideLabel": false,
|
||||||
|
"validation": {
|
||||||
|
"mandatory": false,
|
||||||
|
"pattern": null
|
||||||
|
},
|
||||||
|
"readonly": false,
|
||||||
|
"id": 0,
|
||||||
|
"value": "",
|
||||||
|
"alias": "value",
|
||||||
|
"editor": "Umbraco.TextBox",
|
||||||
|
"isSensitive": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"updateDate": "0001-01-01T00:00:00",
|
||||||
|
"createDate": "0001-01-01T00:00:00",
|
||||||
|
"published": false,
|
||||||
|
"edited": false,
|
||||||
|
"owner": null,
|
||||||
|
"updater": null,
|
||||||
|
"contentTypeAlias": "header",
|
||||||
|
"sortOrder": 0,
|
||||||
|
"name": null,
|
||||||
|
"id": 0,
|
||||||
|
"udi": "umb://document/9c620216c4f14f67b16468b103a09144",
|
||||||
|
"icon": "icon-document",
|
||||||
|
"trashed": false,
|
||||||
|
"key": "1e03aad06a124e9e8092540709d3c1cf",
|
||||||
|
"parentId": -1,
|
||||||
|
"alias": null,
|
||||||
|
"path": null,
|
||||||
|
"metaData": {}
|
||||||
|
};
|
||||||
|
|
||||||
|
scaffolds = {
|
||||||
|
"umb://document-type/437c65f147054681911d8a884459ab47": rteScaffold,
|
||||||
|
"umb://document-type/9c620216c4f14f67b16468b103a09144": headerScaffold
|
||||||
|
};
|
||||||
|
|
||||||
|
fullModel = {
|
||||||
|
"label": "Layout",
|
||||||
|
"description": null,
|
||||||
|
"view": "grid2",
|
||||||
|
"config": {
|
||||||
|
"items": {
|
||||||
|
"styles": [
|
||||||
|
{
|
||||||
|
"label": "Set a background image",
|
||||||
|
"description": "Set a row background",
|
||||||
|
"key": "background-image",
|
||||||
|
"view": "imagepicker",
|
||||||
|
"modifier": "url({0})"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config": [
|
||||||
|
{
|
||||||
|
"label": "Class",
|
||||||
|
"description": "Set a css class",
|
||||||
|
"key": "class",
|
||||||
|
"view": "textstring"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"columns": 12,
|
||||||
|
"templates": [
|
||||||
|
{
|
||||||
|
"name": "1 column layout",
|
||||||
|
"sections": [
|
||||||
|
{
|
||||||
|
"grid": 12
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"layouts": [
|
||||||
|
{
|
||||||
|
"label": "Headline",
|
||||||
|
"name": "Headline",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"grid": 12,
|
||||||
|
"allowed": [
|
||||||
|
"umb://document-type/437c65f147054681911d8a884459ab47",
|
||||||
|
"umb://document-type/9c620216c4f14f67b16468b103a09144"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"label": "FiftyFifty",
|
||||||
|
"name": "Article",
|
||||||
|
"areas": [
|
||||||
|
{
|
||||||
|
"grid": 6
|
||||||
|
}, {
|
||||||
|
"grid": 6
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hideLabel": true,
|
||||||
|
"validation": {
|
||||||
|
"mandatory": false,
|
||||||
|
"pattern": null
|
||||||
|
},
|
||||||
|
"readonly": false,
|
||||||
|
"id": 0,
|
||||||
|
"value": {
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"alias": "Headline",
|
||||||
|
"settings": {},
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"settings": {},
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"type": "umb://document-type/9c620216c4f14f67b16468b103a09144",
|
||||||
|
"values": {
|
||||||
|
"value": "Hello world!"
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
"type": "umb://document-type/437c65f147054681911d8a884459ab47",
|
||||||
|
"values": {
|
||||||
|
"content": "<p>More hello world!</p>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"alias": "layout",
|
||||||
|
"editor": "Umbraco.Grid2",
|
||||||
|
"isSensitive": false
|
||||||
|
};
|
||||||
|
});
|
||||||
@@ -486,6 +486,13 @@
|
|||||||
<Content Include="Umbraco\Views\Preview\Shadow.cshtml" />
|
<Content Include="Umbraco\Views\Preview\Shadow.cshtml" />
|
||||||
<Content Include="Umbraco\Views\Preview\Slider.cshtml" />
|
<Content Include="Umbraco\Views\Preview\Slider.cshtml" />
|
||||||
<Content Include="Umbraco\Views\Preview\web.config" />
|
<Content Include="Umbraco\Views\Preview\web.config" />
|
||||||
|
<Content Include="Views\Partials\Grid2\Bootstrap4.cshtml" />
|
||||||
|
<Content Include="Views\Partials\Grid2\Editors\Embed.cshtml" />
|
||||||
|
<Content Include="Views\Partials\Grid2\Editors\Macro.cshtml" />
|
||||||
|
<Content Include="Views\Partials\Grid2\Editors\Media.cshtml" />
|
||||||
|
<Content Include="Views\Partials\Grid2\Editors\Rte.cshtml" />
|
||||||
|
<Content Include="Views\Partials\Grid2\Editors\TextString.cshtml" />
|
||||||
|
<Content Include="Views\Home.cshtml" />
|
||||||
<None Include="Web.Debug.config.transformed" />
|
<None Include="Web.Debug.config.transformed" />
|
||||||
<None Include="web.Template.Debug.config">
|
<None Include="web.Template.Debug.config">
|
||||||
<DependentUpon>Web.Template.config</DependentUpon>
|
<DependentUpon>Web.Template.config</DependentUpon>
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
@inherits UmbracoViewPage<Grid2Value>
|
||||||
|
@if (Model != null && Model.Rows != null)
|
||||||
|
{
|
||||||
|
<div class="umb-grid2">
|
||||||
|
<div class="container">
|
||||||
|
@foreach (var row in Model.Rows)
|
||||||
|
{
|
||||||
|
<div @RenderElementAttributes(row.Settings, "row")>
|
||||||
|
@foreach (var cell in row.Cells)
|
||||||
|
{
|
||||||
|
<div @RenderElementAttributes(cell.Settings, $"col-{cell.Colspan}")>
|
||||||
|
@foreach (var item in cell.Items)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
@Html.Partial($"Grid2/Editors/{item.ContentType.Alias}", item)
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
<pre>@ex.ToString()</pre>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@functions {
|
||||||
|
public static MvcHtmlString RenderElementAttributes(IPublishedElement settings, string className)
|
||||||
|
{
|
||||||
|
/* NOTE: Developers will need to implement their own attributes based on their Settings document type */
|
||||||
|
|
||||||
|
var items = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
// loop over each of the properties
|
||||||
|
if (settings != null)
|
||||||
|
{
|
||||||
|
foreach (var property in settings.Properties)
|
||||||
|
{
|
||||||
|
var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value<string>());
|
||||||
|
items.Add(property.Alias, propertyValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we need to add the `className` (for row/cell),
|
||||||
|
// so we check if the "class" attribute has already been defined.
|
||||||
|
// if it does, then we'll add to it, otherwise we'll create it.
|
||||||
|
if (items.ContainsKey("class"))
|
||||||
|
{
|
||||||
|
items["class"] = $"{className} {items["class"]}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
items.Add("class", className);
|
||||||
|
}
|
||||||
|
|
||||||
|
var attributes = new List<string>();
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
attributes.Add($"{item.Key}=\"{item.Value}\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MvcHtmlString(string.Join(" ", attributes));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
@inherits UmbracoViewPage<GridValue.GridControl>
|
||||||
|
|
||||||
|
<div class="video-wrapper">
|
||||||
|
@Html.Raw(Model.Value)
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
@inherits UmbracoViewPage<GridValue.GridControl>
|
||||||
|
@if (Model.Value != null)
|
||||||
|
{
|
||||||
|
// TODO: Get the "macroAlias" items
|
||||||
|
var macroAlias = Model.Value.macroAlias.ToString();
|
||||||
|
ViewDataDictionary parameters = new ViewDataDictionary();
|
||||||
|
|
||||||
|
// TODO: Get the "macroParamsDictionary" items
|
||||||
|
foreach (dynamic mpd in Model.value.macroParamsDictionary)
|
||||||
|
{
|
||||||
|
parameters.Add(mpd.Name, mpd.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
<text>
|
||||||
|
@Umbraco.RenderMacro(macroAlias, parameters)
|
||||||
|
</text>
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
@inherits UmbracoViewPage<GridValue.GridControl>
|
||||||
|
@if (Model.Value != null)
|
||||||
|
{
|
||||||
|
// TODO: Get the "image" value
|
||||||
|
var url = Model.Value.image;
|
||||||
|
if (Model.Editor.Config != null && Model.Editor.Config.ContainsKey("size"))
|
||||||
|
{
|
||||||
|
url += "?width=" + Model.Editor.Config.size.width;
|
||||||
|
url += "&height=" + Model.Editor.Config.size.height;
|
||||||
|
|
||||||
|
// TODO: Get the "focalPoint" value
|
||||||
|
if (Model.Value.focalPoint != null)
|
||||||
|
{
|
||||||
|
url += "¢er=" + Model.Value.focalPoint.top + "," + Model.Value.focalPoint.left;
|
||||||
|
url += "&mode=crop";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Get the "altText" value
|
||||||
|
var altText = Model.Value.altText ?? Model.Value.caption ?? string.Empty;
|
||||||
|
|
||||||
|
<img src="@url" alt="@altText">
|
||||||
|
|
||||||
|
// TODO: Get the "caption" value
|
||||||
|
if (Model.Value.caption != null)
|
||||||
|
{
|
||||||
|
<p class="caption">@Model.Value.caption</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@using Umbraco.Web.Templates
|
||||||
|
@inherits UmbracoViewPage<GridValue.GridControl>
|
||||||
|
|
||||||
|
@Html.Raw(TemplateUtilities.ParseInternalLinks(Model.Value.ToString()))
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
@inherits UmbracoViewPage<GridValue.GridControl>
|
||||||
|
@if (Model.Editor.Config != null && Model.Editor.Config.ContainsKey("markup"))
|
||||||
|
{
|
||||||
|
var markup = Model.Editor.Config["markup"].ToString();
|
||||||
|
markup = markup.Replace("#value#", Umbraco.ReplaceLineBreaksForHtml(HttpUtility.HtmlEncode(Model.Value)));
|
||||||
|
|
||||||
|
if (markup.Contains("#style#") && Model.Editor.Config.ContainsKey("style"))
|
||||||
|
{
|
||||||
|
var style = Model.Editor.Config["style"].ToString();
|
||||||
|
markup = markup.Replace("#style#", style);
|
||||||
|
}
|
||||||
|
|
||||||
|
<text>
|
||||||
|
@Html.Raw(markup)
|
||||||
|
</text>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var style = Model.Editor.Config.ContainsKey("style") ? Model.Editor.Config["style"].ToString() : null;
|
||||||
|
<text>
|
||||||
|
<div style="@style">@Model.Value</div>
|
||||||
|
</text>
|
||||||
|
}
|
||||||
@@ -292,6 +292,10 @@ namespace Umbraco.Web.Editors
|
|||||||
{
|
{
|
||||||
"languageApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<LanguageController>(
|
"languageApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<LanguageController>(
|
||||||
controller => controller.GetAllLanguages())
|
controller => controller.GetAllLanguages())
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gridApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<GridController>(
|
||||||
|
controller => controller.GetContentTypes())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,135 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Web.Http;
|
||||||
|
using Umbraco.Core;
|
||||||
|
using Umbraco.Core.IO;
|
||||||
|
using Umbraco.Core.Models;
|
||||||
|
using Umbraco.Core.PropertyEditors;
|
||||||
|
using Umbraco.Web.Models.ContentEditing;
|
||||||
|
|
||||||
|
namespace Umbraco.Web.Editors
|
||||||
|
{
|
||||||
|
public class GridController : UmbracoAuthorizedJsonController
|
||||||
|
{
|
||||||
|
private readonly PropertyEditorCollection _propertyEditors;
|
||||||
|
|
||||||
|
public GridController(PropertyEditorCollection propertyEditors)
|
||||||
|
{
|
||||||
|
_propertyEditors = propertyEditors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<GridContentType> GetContentTypes()
|
||||||
|
{
|
||||||
|
var contentTypes = GetGridContentTypes();
|
||||||
|
|
||||||
|
return contentTypes.Select(x => new GridContentType
|
||||||
|
{
|
||||||
|
Id = x.Id,
|
||||||
|
Udi = x.GetUdi(),
|
||||||
|
Name = x.Name,
|
||||||
|
Alias = x.Alias,
|
||||||
|
Icon = x.Icon,
|
||||||
|
Views = GetPaths(x)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public GridContentCell GetScaffold(Udi id)
|
||||||
|
{
|
||||||
|
var guidUdi = id as GuidUdi;
|
||||||
|
if (guidUdi == null)
|
||||||
|
{
|
||||||
|
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var contentType = Services.ContentTypeService.Get(guidUdi.Guid);
|
||||||
|
if (contentType == null) throw new HttpResponseException(System.Net.HttpStatusCode.NotFound);
|
||||||
|
|
||||||
|
var emptyContent = Services.ContentService.Create("", -1, contentType.Alias, Security.GetUserId().ResultOr(0));
|
||||||
|
var mapped = Mapper.Map<GridContentCell>(emptyContent);
|
||||||
|
|
||||||
|
//remove this tab if it exists: umbContainerView
|
||||||
|
var containerTab = mapped.Tabs.FirstOrDefault(x => x.Alias == Constants.Conventions.PropertyGroups.ListViewGroupName);
|
||||||
|
mapped.Tabs = mapped.Tabs.Except(new[] { containerTab });
|
||||||
|
|
||||||
|
return mapped;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDictionary<string, GridEditorPath> GetPaths(IContentType contentType)
|
||||||
|
{
|
||||||
|
if (contentType.PropertyGroups.Count == 0)
|
||||||
|
throw new InvalidOperationException($"The content type {contentType.Alias} does not contain any tabs/properties");
|
||||||
|
|
||||||
|
var props = contentType.PropertyGroups[0].PropertyTypes;
|
||||||
|
|
||||||
|
var result = new Dictionary<string, GridEditorPath>();
|
||||||
|
|
||||||
|
var editors = props.Select(x => _propertyEditors.FirstOrDefault(p => p.Alias == x.PropertyEditorAlias))
|
||||||
|
.WhereNotNull()
|
||||||
|
.ToDictionary(x => x.Alias, x => x);
|
||||||
|
|
||||||
|
foreach (var x in props)
|
||||||
|
{
|
||||||
|
if (editors.TryGetValue(x.PropertyEditorAlias, out var editor))
|
||||||
|
{
|
||||||
|
var valueEditor = editor.GetValueEditor();
|
||||||
|
if (valueEditor == null) continue;
|
||||||
|
|
||||||
|
if (valueEditor.View.IsNullOrWhiteSpace()) continue;
|
||||||
|
|
||||||
|
var inlineResult = GetPath(valueEditor, "inline");
|
||||||
|
if (inlineResult)
|
||||||
|
result[x.Alias] = new GridEditorPath(inlineResult.Result, false);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var previewResult = GetPath(valueEditor, "preview");
|
||||||
|
if (previewResult)
|
||||||
|
result[x.Alias] = new GridEditorPath(previewResult.Result, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<IContentType> GetGridContentTypes()
|
||||||
|
{
|
||||||
|
const string GridContentTypeFolderName = "GridEditors";
|
||||||
|
|
||||||
|
var folder = Services.ContentTypeService
|
||||||
|
.GetContainers(GridContentTypeFolderName, 1)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (folder == null)
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<IContentType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var contentTypes = Services.ContentTypeService
|
||||||
|
.GetDescendants(folder.Id, false)
|
||||||
|
.Where(x => !x.IsContainer);
|
||||||
|
|
||||||
|
return contentTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Attempt<string> GetPath(IDataValueEditor valueEditor, string suffix)
|
||||||
|
{
|
||||||
|
var inlinePath = valueEditor.View.InvariantEndsWith(".html")
|
||||||
|
? valueEditor.View.TrimEnd(".html") + $".{suffix}.html"
|
||||||
|
: valueEditor.View + $".{suffix}.html";
|
||||||
|
|
||||||
|
var relativePath = !inlinePath.Contains("/") ? $"views/propertyeditors/{valueEditor.View}/{inlinePath}" : inlinePath;
|
||||||
|
var fullPath = !inlinePath.Contains("/") ? $"~{GlobalSettings.Path}/{relativePath}" : relativePath;
|
||||||
|
|
||||||
|
var file = IOHelper.MapPath(fullPath);
|
||||||
|
|
||||||
|
if (!System.IO.File.Exists(file))
|
||||||
|
return Attempt<string>.Fail();
|
||||||
|
|
||||||
|
return Attempt.Succeed(relativePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,151 +1,74 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Web.Mvc.Html;
|
|
||||||
using Umbraco.Core;
|
|
||||||
using Umbraco.Core.Models;
|
|
||||||
using System.Web.Mvc;
|
using System.Web.Mvc;
|
||||||
using Umbraco.Web.Templates;
|
using System.Web.Mvc.Html;
|
||||||
using System.IO;
|
|
||||||
using System.Web.Routing;
|
|
||||||
using Umbraco.Core.Exceptions;
|
using Umbraco.Core.Exceptions;
|
||||||
|
using Umbraco.Core.Models;
|
||||||
using Umbraco.Core.Models.PublishedContent;
|
using Umbraco.Core.Models.PublishedContent;
|
||||||
using Umbraco.Web.Mvc;
|
|
||||||
|
|
||||||
namespace Umbraco.Web
|
namespace Umbraco.Web
|
||||||
{
|
{
|
||||||
|
|
||||||
public static class GridTemplateExtensions
|
public static class GridTemplateExtensions
|
||||||
{
|
{
|
||||||
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedProperty property, string framework = "bootstrap3")
|
private const string _defaultPropertyAlias = "bodyText";
|
||||||
{
|
private const string _defaultFramework = "bootstrap4";
|
||||||
var asString = property.GetValue() as string;
|
|
||||||
if (asString != null && string.IsNullOrEmpty(asString)) return new MvcHtmlString(string.Empty);
|
|
||||||
|
|
||||||
var view = "Grid/" + framework;
|
|
||||||
return html.Partial(view, property.GetValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedContent contentItem)
|
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedContent contentItem)
|
||||||
{
|
{
|
||||||
return html.GetGridHtml(contentItem, "bodyText", "bootstrap3");
|
return html.GetGridHtml(contentItem, _defaultPropertyAlias, _defaultFramework);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias)
|
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
return html.GetGridHtml(contentItem, propertyAlias, _defaultFramework);
|
||||||
|
|
||||||
return html.GetGridHtml(contentItem, propertyAlias, "bootstrap3");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias, string framework)
|
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias, string framework)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
if (string.IsNullOrWhiteSpace(propertyAlias))
|
||||||
|
throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
||||||
|
|
||||||
var view = "Grid/" + framework;
|
var property = contentItem.GetProperty(propertyAlias);
|
||||||
var prop = contentItem.GetProperty(propertyAlias);
|
if (property == null)
|
||||||
if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias);
|
throw new NullReferenceException($"No property type found with alias '{propertyAlias}'");
|
||||||
var model = prop.GetValue();
|
|
||||||
|
|
||||||
var asString = model as string;
|
return html.GetGridHtml(property, framework);
|
||||||
if (asString != null && string.IsNullOrEmpty(asString)) return new MvcHtmlString(string.Empty);
|
|
||||||
|
|
||||||
return html.Partial(view, model);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MvcHtmlString GetGridHtml(this IPublishedProperty property, HtmlHelper html, string framework = "bootstrap3")
|
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedProperty property, string framework = _defaultFramework)
|
||||||
{
|
{
|
||||||
var asString = property.GetValue() as string;
|
if (string.IsNullOrWhiteSpace(framework))
|
||||||
if (asString != null && string.IsNullOrEmpty(asString)) return new MvcHtmlString(string.Empty);
|
framework = _defaultFramework;
|
||||||
|
|
||||||
var view = "Grid/" + framework;
|
var model = property.GetValue();
|
||||||
return html.Partial(view, property.GetValue());
|
|
||||||
|
// NOTE: The Grid v2 uses a strongly-typed model, v1 is dynamic
|
||||||
|
if (model is Grid2Value grid)
|
||||||
|
return html.Partial($"Grid2/{framework}", model);
|
||||||
|
|
||||||
|
if (model is string s && string.IsNullOrEmpty(s))
|
||||||
|
return new MvcHtmlString(string.Empty);
|
||||||
|
|
||||||
|
return html.Partial($"Grid/{framework}", model);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html)
|
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html)
|
||||||
{
|
{
|
||||||
return GetGridHtml(contentItem, html, "bodyText", "bootstrap3");
|
return GetGridHtml(contentItem, html, _defaultPropertyAlias, _defaultFramework);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias)
|
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
return GetGridHtml(contentItem, html, propertyAlias, _defaultFramework);
|
||||||
|
|
||||||
return GetGridHtml(contentItem, html, propertyAlias, "bootstrap3");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias, string framework)
|
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias, string framework)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
return html.GetGridHtml(contentItem, propertyAlias, framework);
|
||||||
|
|
||||||
var view = "Grid/" + framework;
|
|
||||||
var prop = contentItem.GetProperty(propertyAlias);
|
|
||||||
if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias);
|
|
||||||
var model = prop.GetValue();
|
|
||||||
|
|
||||||
var asString = model as string;
|
|
||||||
if (asString != null && string.IsNullOrEmpty(asString)) return new MvcHtmlString(string.Empty);
|
|
||||||
|
|
||||||
return html.Partial(view, model);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MvcHtmlString GetGridHtml(this IPublishedProperty property, HtmlHelper html, string framework = _defaultFramework)
|
||||||
[Obsolete("This should not be used, GetGridHtml methods accepting HtmlHelper as a parameter or GetGridHtml extensions on HtmlHelper should be used instead")]
|
|
||||||
public static MvcHtmlString GetGridHtml(this IPublishedProperty property, string framework = "bootstrap3")
|
|
||||||
{
|
{
|
||||||
var asString = property.GetValue() as string;
|
return html.GetGridHtml(property, framework);
|
||||||
if (asString != null && string.IsNullOrEmpty(asString)) return new MvcHtmlString(string.Empty);
|
|
||||||
|
|
||||||
var htmlHelper = CreateHtmlHelper(property.GetValue());
|
|
||||||
return htmlHelper.GetGridHtml(property, framework);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("This should not be used, GetGridHtml methods accepting HtmlHelper as a parameter or GetGridHtml extensions on HtmlHelper should be used instead")]
|
|
||||||
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem)
|
|
||||||
{
|
|
||||||
return GetGridHtml(contentItem, "bodyText", "bootstrap3");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("This should not be used, GetGridHtml methods accepting HtmlHelper as a parameter or GetGridHtml extensions on HtmlHelper should be used instead")]
|
|
||||||
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, string propertyAlias)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
|
||||||
|
|
||||||
return GetGridHtml(contentItem, propertyAlias, "bootstrap3");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("This should not be used, GetGridHtml methods accepting HtmlHelper as a parameter or GetGridHtml extensions on HtmlHelper should be used instead")]
|
|
||||||
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, string propertyAlias, string framework)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
|
||||||
|
|
||||||
var prop = contentItem.GetProperty(propertyAlias);
|
|
||||||
if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias);
|
|
||||||
var model = prop.GetValue();
|
|
||||||
|
|
||||||
var asString = model as string;
|
|
||||||
if (asString != null && string.IsNullOrEmpty(asString)) return new MvcHtmlString(string.Empty);
|
|
||||||
|
|
||||||
var htmlHelper = CreateHtmlHelper(model);
|
|
||||||
return htmlHelper.GetGridHtml(contentItem, propertyAlias, framework);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Obsolete("This shouldn't need to be used but because the obsolete extension methods above don't have access to the current HtmlHelper, we need to create a fake one, unfortunately however this will not pertain the current views viewdata, tempdata or model state so should not be used")]
|
|
||||||
private static HtmlHelper CreateHtmlHelper(object model)
|
|
||||||
{
|
|
||||||
var cc = new ControllerContext
|
|
||||||
{
|
|
||||||
RequestContext = UmbracoContext.Current.HttpContext.Request.RequestContext
|
|
||||||
};
|
|
||||||
var viewContext = new ViewContext(cc, new FakeView(), new ViewDataDictionary(model), new TempDataDictionary(), new StringWriter());
|
|
||||||
var htmlHelper = new HtmlHelper(viewContext, new ViewPage());
|
|
||||||
return htmlHelper;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FakeView : IView
|
|
||||||
{
|
|
||||||
public void Render(ViewContext viewContext, TextWriter writer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ using Umbraco.Core;
|
|||||||
|
|
||||||
namespace Umbraco.Web.Models.ContentEditing
|
namespace Umbraco.Web.Models.ContentEditing
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
[DataContract(Name = "scriptFile", Namespace = "")]
|
[DataContract(Name = "scriptFile", Namespace = "")]
|
||||||
public class CodeFileDisplay : INotificationModel, IValidatableObject
|
public class CodeFileDisplay : INotificationModel, IValidatableObject
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Runtime.Serialization;
|
||||||
|
using Umbraco.Core.Models;
|
||||||
|
|
||||||
|
namespace Umbraco.Web.Models.ContentEditing
|
||||||
|
{
|
||||||
|
[DataContract(Name = "gridCell", Namespace = "")]
|
||||||
|
public class GridContentCell : TabbedContentItem<ContentPropertyDisplay, IContentBase>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.Serialization;
|
||||||
|
using Umbraco.Core;
|
||||||
|
|
||||||
|
namespace Umbraco.Web.Models.ContentEditing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A simplified content type model used to render grid cells
|
||||||
|
/// </summary>
|
||||||
|
[DataContract(Name = "contentType", Namespace = "")]
|
||||||
|
public class GridContentType
|
||||||
|
{
|
||||||
|
[DataMember(Name = "id")]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "udi")]
|
||||||
|
public Udi Udi { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "name")]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "alias")]
|
||||||
|
public string Alias { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "icon")]
|
||||||
|
public string Icon { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "views")]
|
||||||
|
public IDictionary<string, GridEditorPath> Views { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataContract(Name = "editorPath", Namespace = "")]
|
||||||
|
public class GridEditorPath
|
||||||
|
{
|
||||||
|
public GridEditorPath()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public GridEditorPath(string view, bool isPreview)
|
||||||
|
{
|
||||||
|
View = view;
|
||||||
|
IsPreview = isPreview;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataMember(Name = "view")]
|
||||||
|
public string View { get; set; }
|
||||||
|
|
||||||
|
[DataMember(Name = "isPreview")]
|
||||||
|
public bool IsPreview { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
var creatorResolver = new CreatorResolver(userService);
|
var creatorResolver = new CreatorResolver(userService);
|
||||||
var actionButtonsResolver = new ActionButtonsResolver(userService, contentService);
|
var actionButtonsResolver = new ActionButtonsResolver(userService, contentService);
|
||||||
var tabsAndPropertiesResolver = new TabsAndPropertiesResolver<IContent, ContentItemDisplay>(textService);
|
var tabsAndPropertiesResolver = new TabsAndPropertiesResolver<IContent, ContentItemDisplay>(textService);
|
||||||
|
var gridCellTabsAndPropertiesResolver = new TabsAndPropertiesResolver<IContent, GridContentCell>(textService);
|
||||||
var childOfListViewResolver = new ContentChildOfListViewResolver(contentService, contentTypeService);
|
var childOfListViewResolver = new ContentChildOfListViewResolver(contentService, contentTypeService);
|
||||||
var contentTypeBasicResolver = new ContentTypeBasicResolver<IContent, ContentItemDisplay>();
|
var contentTypeBasicResolver = new ContentTypeBasicResolver<IContent, ContentItemDisplay>();
|
||||||
var contentTreeNodeUrlResolver = new ContentTreeNodeUrlResolver<IContent, ContentTreeController>();
|
var contentTreeNodeUrlResolver = new ContentTreeNodeUrlResolver<IContent, ContentTreeController>();
|
||||||
@@ -28,6 +29,15 @@ namespace Umbraco.Web.Models.Mapping
|
|||||||
var contentUrlResolver = new ContentUrlResolver(textService, contentService, logger);
|
var contentUrlResolver = new ContentUrlResolver(textService, contentService, logger);
|
||||||
var variantResolver = new ContentItemDisplayVariationResolver(localizationService);
|
var variantResolver = new ContentItemDisplayVariationResolver(localizationService);
|
||||||
|
|
||||||
|
CreateMap<IContent, GridContentCell>()
|
||||||
|
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(Constants.UdiEntityType.Document, src.Key)))
|
||||||
|
.ForMember(dest => dest.Icon, opt => opt.MapFrom(src => src.ContentType.Icon))
|
||||||
|
.ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias))
|
||||||
|
.ForMember(dest => dest.Properties, opt => opt.Ignore())
|
||||||
|
.ForMember(dest => dest.Alias, opt => opt.Ignore())
|
||||||
|
.ForMember(dest => dest.Tabs, opt => opt.ResolveUsing(gridCellTabsAndPropertiesResolver))
|
||||||
|
.ForMember(dest => dest.AdditionalData, opt => opt.Ignore());
|
||||||
|
|
||||||
//FROM IContent TO ContentItemDisplay
|
//FROM IContent TO ContentItemDisplay
|
||||||
CreateMap<IContent, ContentItemDisplay>()
|
CreateMap<IContent, ContentItemDisplay>()
|
||||||
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, src.Key)))
|
.ForMember(dest => dest.Udi, opt => opt.MapFrom(src => Udi.Create(src.Blueprint ? Constants.UdiEntityType.DocumentBlueprint : Constants.UdiEntityType.Document, src.Key)))
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Umbraco.Web.PropertyEditors
|
|||||||
/// Due to maintaining backwards compatibility this data type stores the value as a string which is a comma separated value of the
|
/// Due to maintaining backwards compatibility this data type stores the value as a string which is a comma separated value of the
|
||||||
/// ids of the individual items so we have logic in here to deal with that.
|
/// ids of the individual items so we have logic in here to deal with that.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DataEditor(Constants.PropertyEditors.Aliases.DropDownListMultiple, "Dropdown list multiple", "dropdown", Group = "lists", Icon="icon-bulleted-list", IsDeprecated = true)]
|
[DataEditor(Constants.PropertyEditors.Aliases.DropDownListMultiple, "(Obsolete) Dropdown list multiple", "dropdown", Group = "lists", Icon="icon-bulleted-list", IsDeprecated = true)]
|
||||||
public class DropDownMultiplePropertyEditor : DropDownMultipleWithKeysPropertyEditor
|
public class DropDownMultiplePropertyEditor : DropDownMultipleWithKeysPropertyEditor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Umbraco.Web.PropertyEditors
|
|||||||
/// Due to backwards compatibility, this editor stores the value as a CSV string listing
|
/// Due to backwards compatibility, this editor stores the value as a CSV string listing
|
||||||
/// the ids of individual items.
|
/// the ids of individual items.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DataEditor(Constants.PropertyEditors.Aliases.DropdownlistMultiplePublishKeys, "Dropdown list multiple, publish keys", "dropdown", Group = "lists", Icon = "icon-bulleted-list", IsDeprecated = true)]
|
[DataEditor(Constants.PropertyEditors.Aliases.DropdownlistMultiplePublishKeys, "(Obsolete) Dropdown list multiple, publish keys", "dropdown", Group = "lists", Icon = "icon-bulleted-list", IsDeprecated = true)]
|
||||||
public class DropDownMultipleWithKeysPropertyEditor : DropDownPropertyEditor
|
public class DropDownMultipleWithKeysPropertyEditor : DropDownPropertyEditor
|
||||||
{
|
{
|
||||||
private readonly ILocalizedTextService _textService;
|
private readonly ILocalizedTextService _textService;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Umbraco.Web.PropertyEditors
|
|||||||
/// as INT and we have logic in here to ensure it is formatted correctly including ensuring that the string value is published
|
/// as INT and we have logic in here to ensure it is formatted correctly including ensuring that the string value is published
|
||||||
/// in cache and not the int ID.
|
/// in cache and not the int ID.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DataEditor(Constants.PropertyEditors.Aliases.DropDownList, "Dropdown list", "dropdown", ValueType = ValueTypes.String, Group = "lists", Icon = "icon-indent", IsDeprecated = true)]
|
[DataEditor(Constants.PropertyEditors.Aliases.DropDownList, "(Obsolete) Dropdown list", "dropdown", ValueType = ValueTypes.String, Group = "lists", Icon = "icon-indent", IsDeprecated = true)]
|
||||||
public class DropDownPropertyEditor : DropDownWithKeysPropertyEditor
|
public class DropDownPropertyEditor : DropDownWithKeysPropertyEditor
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace Umbraco.Web.PropertyEditors
|
|||||||
/// as INT and we have logic in here to ensure it is formatted correctly including ensuring that the INT ID value is published
|
/// as INT and we have logic in here to ensure it is formatted correctly including ensuring that the INT ID value is published
|
||||||
/// in cache and not the string value.
|
/// in cache and not the string value.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[DataEditor(Constants.PropertyEditors.Aliases.DropdownlistPublishKeys, "Dropdown list, publishing keys", "dropdown", ValueType = ValueTypes.Integer, Group = "lists", Icon = "icon-indent", IsDeprecated = true)]
|
[DataEditor(Constants.PropertyEditors.Aliases.DropdownlistPublishKeys, "(Obsolete) Dropdown list, publishing keys", "dropdown", ValueType = ValueTypes.Integer, Group = "lists", Icon = "icon-indent", IsDeprecated = true)]
|
||||||
public class DropDownWithKeysPropertyEditor : DataEditor
|
public class DropDownWithKeysPropertyEditor : DataEditor
|
||||||
{
|
{
|
||||||
private readonly ILocalizedTextService _textService;
|
private readonly ILocalizedTextService _textService;
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Umbraco.Core.PropertyEditors;
|
||||||
|
|
||||||
|
namespace Umbraco.Web.PropertyEditors
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the configuration for the grid value editor
|
||||||
|
/// </summary>
|
||||||
|
public class Grid2Configuration
|
||||||
|
{
|
||||||
|
[ConfigurationField("items", "Grid", "views/propertyeditors/grid2/grid.prevalues.html", Description = "Grid configuration")]
|
||||||
|
public JObject Items { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Umbraco.Core.PropertyEditors;
|
||||||
|
|
||||||
|
namespace Umbraco.Web.PropertyEditors
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the configuration for the gid value editor.
|
||||||
|
/// </summary>
|
||||||
|
public class Grid2ConfigurationEditor : ConfigurationEditor<Grid2Configuration>
|
||||||
|
{ }
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Umbraco.Core.Logging;
|
||||||
|
using Examine;
|
||||||
|
using Lucene.Net.Documents;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Umbraco.Core;
|
||||||
|
using Umbraco.Core.PropertyEditors;
|
||||||
|
using Umbraco.Core.Xml;
|
||||||
|
using Umbraco.Examine;
|
||||||
|
|
||||||
|
namespace Umbraco.Web.PropertyEditors
|
||||||
|
{
|
||||||
|
using Examine = global::Examine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a grid property and parameter editor.
|
||||||
|
/// </summary>
|
||||||
|
[DataEditor(Constants.PropertyEditors.Aliases.Grid2, "Grid", "grid2", HideLabel = true, ValueType = ValueTypes.Json, Group="rich content", Icon="icon-layout")]
|
||||||
|
public class Grid2PropertyEditor : DataEditor
|
||||||
|
{
|
||||||
|
public Grid2PropertyEditor(ILogger logger)
|
||||||
|
: base(logger)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
//internal void DocumentWriting(object sender, Examine.LuceneEngine.DocumentWritingEventArgs e)
|
||||||
|
//{
|
||||||
|
// foreach (var value in e.ValueSet.Values)
|
||||||
|
// {
|
||||||
|
// //if there is a value, it's a string and it's detected as json
|
||||||
|
// if (value.Value.Count > 0 && value.Value[0] != null && (value.Value[0] is string firstVal) && firstVal.DetectIsJson())
|
||||||
|
// {
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// //TODO: We should deserialize this to Umbraco.Core.Models.GridValue instead of doing the below
|
||||||
|
// var json = JsonConvert.DeserializeObject<JObject>(firstVal);
|
||||||
|
|
||||||
|
// //check if this is formatted for grid json
|
||||||
|
// if (json.HasValues && json.TryGetValue("name", out _) && json.TryGetValue("sections", out _))
|
||||||
|
// {
|
||||||
|
// //get all values and put them into a single field (using JsonPath)
|
||||||
|
// var sb = new StringBuilder();
|
||||||
|
// foreach (var row in json.SelectTokens("$.sections[*].rows[*]"))
|
||||||
|
// {
|
||||||
|
// var rowName = row["name"].Value<string>();
|
||||||
|
// var areaVals = row.SelectTokens("$.areas[*].controls[*].value");
|
||||||
|
|
||||||
|
// foreach (var areaVal in areaVals)
|
||||||
|
// {
|
||||||
|
// //TODO: If it's not a string, then it's a json formatted value -
|
||||||
|
// // we cannot really index this in a smart way since it could be 'anything'
|
||||||
|
// if (areaVal.Type == JTokenType.String)
|
||||||
|
// {
|
||||||
|
// var str = areaVal.Value<string>();
|
||||||
|
// str = XmlHelper.CouldItBeXml(str) ? str.StripHtml() : str;
|
||||||
|
// sb.Append(str);
|
||||||
|
// sb.Append(" ");
|
||||||
|
|
||||||
|
// //add the row name as an individual field
|
||||||
|
// e.Document.Add(
|
||||||
|
// new Field(
|
||||||
|
// $"{value.Key}.{rowName}", str, Field.Store.YES, Field.Index.ANALYZED));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (sb.Length > 0)
|
||||||
|
// {
|
||||||
|
// //First save the raw value to a raw field
|
||||||
|
// e.Document.Add(
|
||||||
|
// new Field(
|
||||||
|
// $"{UmbracoExamineIndexer.RawFieldPrefix}{value.Key}",
|
||||||
|
// firstVal, Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO));
|
||||||
|
|
||||||
|
// //now replace the original value with the combined/cleaned value
|
||||||
|
// e.Document.RemoveField(value.Key);
|
||||||
|
// e.Document.Add(
|
||||||
|
// new Field(
|
||||||
|
// value.Key,
|
||||||
|
// sb.ToString(), Field.Store.YES, Field.Index.ANALYZED));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// catch (InvalidCastException)
|
||||||
|
// {
|
||||||
|
// //swallow...on purpose, there's a chance that this isn't the json format we are looking for
|
||||||
|
// // and we don't want that to affect the website.
|
||||||
|
// }
|
||||||
|
// catch (JsonException)
|
||||||
|
// {
|
||||||
|
// //swallow...on purpose, there's a chance that this isn't json and we don't want that to affect
|
||||||
|
// // the website.
|
||||||
|
// }
|
||||||
|
// catch (ArgumentException)
|
||||||
|
// {
|
||||||
|
// //swallow on purpose to prevent this error:
|
||||||
|
// // Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overridden to ensure that the value is validated
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected override IDataValueEditor CreateValueEditor() => new GridPropertyValueEditor(Attribute);
|
||||||
|
|
||||||
|
protected override IConfigurationEditor CreateConfigurationEditor() => new Grid2ConfigurationEditor();
|
||||||
|
|
||||||
|
internal class GridPropertyValueEditor : DataValueEditor
|
||||||
|
{
|
||||||
|
public GridPropertyValueEditor(DataEditorAttribute attribute)
|
||||||
|
: base(attribute)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Umbraco.Core.PropertyEditors;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Umbraco.Core.PropertyEditors;
|
||||||
|
|
||||||
namespace Umbraco.Web.PropertyEditors
|
namespace Umbraco.Web.PropertyEditors
|
||||||
{
|
{
|
||||||
@@ -8,9 +9,9 @@ namespace Umbraco.Web.PropertyEditors
|
|||||||
public class GridConfiguration
|
public class GridConfiguration
|
||||||
{
|
{
|
||||||
[ConfigurationField("items", "Grid", "views/propertyeditors/grid/grid.prevalues.html", Description = "Grid configuration")]
|
[ConfigurationField("items", "Grid", "views/propertyeditors/grid/grid.prevalues.html", Description = "Grid configuration")]
|
||||||
public string Items { get; set; }
|
public JObject Items { get; set; }
|
||||||
|
|
||||||
[ConfigurationField("rte", "Rich text editor", "views/propertyeditors/rte/rte.prevalues.html", Description = "Rich text editor configuration")]
|
[ConfigurationField("rte", "Rich text editor", "views/propertyeditors/rte/rte.prevalues.html", Description = "Rich text editor configuration")]
|
||||||
public string Rte { get; set; }
|
public JObject Rte { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ namespace Umbraco.Web.PropertyEditors
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a grid property and parameter editor.
|
/// Represents a grid property and parameter editor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataEditor(Constants.PropertyEditors.Aliases.Grid, "Grid layout", "grid", HideLabel = true, ValueType = ValueTypes.Json, Group="rich content", Icon="icon-layout")]
|
[DataEditor(Constants.PropertyEditors.Aliases.Grid, "(Obsolete) Grid layout", "grid", HideLabel = true, ValueType = ValueTypes.Json, Group="rich content", Icon="icon-layout", IsDeprecated = true)]
|
||||||
public class GridPropertyEditor : DataEditor
|
public class GridPropertyEditor : DataEditor
|
||||||
{
|
{
|
||||||
public GridPropertyEditor(ILogger logger)
|
public GridPropertyEditor(ILogger logger)
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Umbraco.Core;
|
||||||
|
using Umbraco.Core.Composing;
|
||||||
|
using Umbraco.Core.Logging;
|
||||||
|
using Umbraco.Core.Models;
|
||||||
|
using Umbraco.Core.Models.PublishedContent;
|
||||||
|
using Umbraco.Core.PropertyEditors;
|
||||||
|
using Umbraco.Core.PropertyEditors.ValueConverters;
|
||||||
|
using Umbraco.Core.Services;
|
||||||
|
using Umbraco.Web.PublishedCache;
|
||||||
|
|
||||||
|
namespace Umbraco.Web.PropertyEditors.ValueConverters
|
||||||
|
{
|
||||||
|
public class Grid2ValueConverter : PropertyValueConverterBase
|
||||||
|
{
|
||||||
|
private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor;
|
||||||
|
private readonly IEntityService _entityService;
|
||||||
|
private readonly IDataTypeService _dataTypeService;
|
||||||
|
private readonly IPublishedModelFactory _publishedModelFactory;
|
||||||
|
|
||||||
|
public Grid2ValueConverter(
|
||||||
|
IPublishedSnapshotAccessor publishedSnapshotAccessor,
|
||||||
|
IPublishedModelFactory publishedModelFactory,
|
||||||
|
IEntityService entityService,
|
||||||
|
IDataTypeService dataTypeService)
|
||||||
|
{
|
||||||
|
_publishedSnapshotAccessor = publishedSnapshotAccessor;
|
||||||
|
_publishedModelFactory = publishedModelFactory;
|
||||||
|
_entityService = entityService;
|
||||||
|
_dataTypeService = dataTypeService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsConverter(PublishedPropertyType propertyType)
|
||||||
|
=> propertyType.EditorAlias.InvariantEquals(Constants.PropertyEditors.Aliases.Grid2);
|
||||||
|
|
||||||
|
public override Type GetPropertyValueType(PublishedPropertyType propertyType)
|
||||||
|
=> typeof(Grid2Value);
|
||||||
|
|
||||||
|
public override PropertyCacheLevel GetPropertyCacheLevel(PublishedPropertyType propertyType)
|
||||||
|
=> PropertyCacheLevel.Element;
|
||||||
|
|
||||||
|
public override object ConvertIntermediateToObject(IPublishedElement owner, PublishedPropertyType propertyType, PropertyCacheLevel referenceCacheLevel, object inter, bool preview)
|
||||||
|
{
|
||||||
|
if (inter == null)
|
||||||
|
return default(Grid2Value);
|
||||||
|
|
||||||
|
var source = inter.ToString();
|
||||||
|
if (source.DetectIsJson() == false)
|
||||||
|
return default(Grid2Value);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var obj = JsonConvert.DeserializeObject<JObject>(source);
|
||||||
|
|
||||||
|
var dataType = _dataTypeService.GetDataType(propertyType.DataType.Id);
|
||||||
|
var config = dataType.ConfigurationAs<Grid2Configuration>().Items;
|
||||||
|
|
||||||
|
var value = new Grid2Value();
|
||||||
|
value.Columns = config["columns"].ToObject<int>();
|
||||||
|
|
||||||
|
var layoutConfigs = GetArray(config, "layouts")
|
||||||
|
.ToDictionary(x => x["name"].ToString(), x => x.ToObject<JObject>());
|
||||||
|
|
||||||
|
var rows = new List<Grid2Row>();
|
||||||
|
var rawRows = GetArray(obj, "rows");
|
||||||
|
for (var r = 0; r < rawRows.Count; r++)
|
||||||
|
{
|
||||||
|
var rawRow = rawRows[r].ToObject<JObject>();
|
||||||
|
var rowConfig = layoutConfigs[rawRow["alias"].ToString()];
|
||||||
|
|
||||||
|
var row = new Grid2Row();
|
||||||
|
row.Alias = rawRow["alias"].ToString();
|
||||||
|
row.Name = rowConfig["name"].ToString();
|
||||||
|
|
||||||
|
var rowSettingsValue = rawRow["settings"]?.ToObject<JObject>();
|
||||||
|
if (rowSettingsValue != null)
|
||||||
|
{
|
||||||
|
row.Settings = ConvertToElement(rowSettingsValue, referenceCacheLevel, preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cells = new List<Grid2Cell>();
|
||||||
|
var areaConfigs = GetArray(rowConfig, "areas");
|
||||||
|
var rawCells = GetArray(rawRow, "cells");
|
||||||
|
for (var c = 0; c < rawCells.Count; c++)
|
||||||
|
{
|
||||||
|
var rawCell = rawCells[c].ToObject<JObject>();
|
||||||
|
var cellConfig = areaConfigs[c].ToObject<JObject>();
|
||||||
|
|
||||||
|
var cell = new Grid2Cell();
|
||||||
|
cell.Colspan = cellConfig["grid"].ToObject<int>();
|
||||||
|
|
||||||
|
var cellSettingsValue = rawCell["settings"]?.ToObject<JObject>();
|
||||||
|
if (cellSettingsValue != null)
|
||||||
|
{
|
||||||
|
cell.Settings = ConvertToElement(cellSettingsValue, referenceCacheLevel, preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = new List<IPublishedElement>();
|
||||||
|
var rawItems = GetArray(rawCell, "items");
|
||||||
|
foreach (var rawItem in rawItems.Cast<JObject>())
|
||||||
|
{
|
||||||
|
items.Add(ConvertToElement(rawItem, referenceCacheLevel, preview));
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.Items = items;
|
||||||
|
cells.Add(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
row.Cells = cells;
|
||||||
|
rows.Add(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
value.Rows = rows;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Current.Logger.Error<GridValueConverter>($"Could not parse the string '{source}' to a JSON object.", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we get here, then the value wasn't able to be processed,
|
||||||
|
// there's nothing we can do with it, so we return a default Grid value object.
|
||||||
|
return default(Grid2Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JArray GetArray(JObject obj, string propertyName)
|
||||||
|
{
|
||||||
|
if (obj.TryGetValue(propertyName, out JToken token))
|
||||||
|
{
|
||||||
|
var asArray = token as JArray;
|
||||||
|
return asArray ?? new JArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IPublishedElement ConvertToElement(JObject sourceObject, PropertyCacheLevel referenceCacheLevel, bool preview)
|
||||||
|
{
|
||||||
|
if (sourceObject.TryGetValue("type", out var elementType) == false || Udi.TryParse(elementType.ToString(), out var elementTypeUdi) == false)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var elementTypeId = _entityService.GetId(elementTypeUdi);
|
||||||
|
if (elementTypeId.Success == false)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var publishedContentType = _publishedSnapshotAccessor.PublishedSnapshot.Content.GetContentType(elementTypeId.Result);
|
||||||
|
if (publishedContentType == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var propertyValues = sourceObject["values"].ToObject<Dictionary<string, object>>();
|
||||||
|
|
||||||
|
// TODO: Review how we can give each editor a unique GUID key
|
||||||
|
if (propertyValues.TryGetValue("key", out var keyo) == false || Guid.TryParse(keyo.ToString(), out var key) == false)
|
||||||
|
key = Guid.NewGuid();
|
||||||
|
|
||||||
|
IPublishedElement element = new PublishedElement(publishedContentType, key, propertyValues, preview, referenceCacheLevel, _publishedSnapshotAccessor);
|
||||||
|
return _publishedModelFactory.CreateModel(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -123,6 +123,7 @@
|
|||||||
<Compile Include="Editors\EditorValidatorCollectionBuilder.cs" />
|
<Compile Include="Editors\EditorValidatorCollectionBuilder.cs" />
|
||||||
<Compile Include="Editors\EditorValidatorOfT.cs" />
|
<Compile Include="Editors\EditorValidatorOfT.cs" />
|
||||||
<Compile Include="Editors\FromJsonPathAttribute.cs" />
|
<Compile Include="Editors\FromJsonPathAttribute.cs" />
|
||||||
|
<Compile Include="Editors\GridController.cs" />
|
||||||
<Compile Include="Editors\HelpController.cs" />
|
<Compile Include="Editors\HelpController.cs" />
|
||||||
<Compile Include="Editors\IsCurrentUserModelFilterAttribute.cs" />
|
<Compile Include="Editors\IsCurrentUserModelFilterAttribute.cs" />
|
||||||
<Compile Include="Editors\LanguageController.cs" />
|
<Compile Include="Editors\LanguageController.cs" />
|
||||||
@@ -212,6 +213,8 @@
|
|||||||
<Compile Include="Editors\IEditorValidator.cs" />
|
<Compile Include="Editors\IEditorValidator.cs" />
|
||||||
<Compile Include="Editors\EditorModelEventManager.cs" />
|
<Compile Include="Editors\EditorModelEventManager.cs" />
|
||||||
<Compile Include="HtmlHelperBackOfficeExtensions.cs" />
|
<Compile Include="HtmlHelperBackOfficeExtensions.cs" />
|
||||||
|
<Compile Include="Models\ContentEditing\GridContentCell.cs" />
|
||||||
|
<Compile Include="Models\ContentEditing\GridContentType.cs" />
|
||||||
<Compile Include="Models\ContentEditing\Permission.cs" />
|
<Compile Include="Models\ContentEditing\Permission.cs" />
|
||||||
<Compile Include="Models\ContentEditing\PostedFolder.cs" />
|
<Compile Include="Models\ContentEditing\PostedFolder.cs" />
|
||||||
<Compile Include="Models\ContentEditing\SnippetDisplay.cs" />
|
<Compile Include="Models\ContentEditing\SnippetDisplay.cs" />
|
||||||
@@ -277,8 +280,11 @@
|
|||||||
<Compile Include="PropertyEditors\DropDownMultipleConfigurationEditor.cs" />
|
<Compile Include="PropertyEditors\DropDownMultipleConfigurationEditor.cs" />
|
||||||
<Compile Include="PropertyEditors\EmailAddressConfigurationEditor.cs" />
|
<Compile Include="PropertyEditors\EmailAddressConfigurationEditor.cs" />
|
||||||
<Compile Include="PropertyEditors\EmailAddressConfiguration.cs" />
|
<Compile Include="PropertyEditors\EmailAddressConfiguration.cs" />
|
||||||
|
<Compile Include="PropertyEditors\Grid2Configuration.cs" />
|
||||||
|
<Compile Include="PropertyEditors\Grid2ConfigurationEditor.cs" />
|
||||||
<Compile Include="PropertyEditors\GridConfiguration.cs" />
|
<Compile Include="PropertyEditors\GridConfiguration.cs" />
|
||||||
<Compile Include="PropertyEditors\GridConfigurationEditor.cs" />
|
<Compile Include="PropertyEditors\GridConfigurationEditor.cs" />
|
||||||
|
<Compile Include="PropertyEditors\Grid2PropertyEditor.cs" />
|
||||||
<Compile Include="PropertyEditors\ImageCropperConfigurationEditor.cs" />
|
<Compile Include="PropertyEditors\ImageCropperConfigurationEditor.cs" />
|
||||||
<Compile Include="PropertyEditors\IntegerConfigurationEditor.cs" />
|
<Compile Include="PropertyEditors\IntegerConfigurationEditor.cs" />
|
||||||
<Compile Include="PropertyEditors\LabelConfiguration.cs" />
|
<Compile Include="PropertyEditors\LabelConfiguration.cs" />
|
||||||
@@ -321,6 +327,7 @@
|
|||||||
<Compile Include="PropertyEditors\TrueFalseConfiguration.cs" />
|
<Compile Include="PropertyEditors\TrueFalseConfiguration.cs" />
|
||||||
<Compile Include="PropertyEditors\TrueFalseConfigurationEditor.cs" />
|
<Compile Include="PropertyEditors\TrueFalseConfigurationEditor.cs" />
|
||||||
<Compile Include="PropertyEditors\UserPickerConfiguration.cs" />
|
<Compile Include="PropertyEditors\UserPickerConfiguration.cs" />
|
||||||
|
<Compile Include="PropertyEditors\ValueConverters\Grid2ValueConverter.cs" />
|
||||||
<Compile Include="PropertyEditors\ValueConverters\NestedContentManyValueConverter.cs" />
|
<Compile Include="PropertyEditors\ValueConverters\NestedContentManyValueConverter.cs" />
|
||||||
<Compile Include="PropertyEditors\ValueConverters\NestedContentValueConverterBase.cs" />
|
<Compile Include="PropertyEditors\ValueConverters\NestedContentValueConverterBase.cs" />
|
||||||
<Compile Include="PropertyEditors\ValueConverters\NestedContentSingleValueConverter.cs" />
|
<Compile Include="PropertyEditors\ValueConverters\NestedContentSingleValueConverter.cs" />
|
||||||
|
|||||||
Reference in New Issue
Block a user