Merge branch 'dev-v7-contenttypeeditor' of https://github.com/umbraco/Umbraco-CMS into dev-v7-contenttypeeditor

Conflicts:
	src/Umbraco.Web.UI/config/trees.config
This commit is contained in:
Shannon
2015-06-08 18:00:29 +02:00
18 changed files with 384 additions and 82 deletions

View File

@@ -1,3 +1,4 @@
LazyLoad.js([
'/Umbraco/lib/jquery/jquery.min.js',
'/Umbraco/lib/jquery-ui/jquery-ui.min.js',
@@ -9,11 +10,11 @@ LazyLoad.js([
'/Umbraco/js/umbraco.security.js',
'/Umbraco/ServerVariables',
'/Umbraco/lib/spectrum/spectrum.js',
'http://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js',
'/umbraco/js/canvasdesigner.panel.js',
'/Umbraco/js/canvasdesigner.panel.js',
], function () {
jQuery(document).ready(function () {
angular.bootstrap(document, ['Umbraco.canvasdesigner']);
});
}
);
});
LazyLoad.js(['https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js']);

View File

@@ -0,0 +1,38 @@
/**
* @ngdoc directive
* @name umbraco.directives.directive:umbContentName
* @restrict E
* @function
* @description
* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
**/
angular.module("umbraco.directives")
.directive('umbLockedField', function ($timeout, localizationService) {
return {
require: "ngModel",
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-locked-field.html',
scope: {
model: '=ngModel'
},
link: function(scope, element, attrs, ngModel) {
scope.locked = true;
scope.toggleLock = function(){
if(scope.locked){
scope.locked = false;
}else{
scope.locked =true;
}
};
}
};
});

View File

@@ -25,7 +25,7 @@ angular.module("umbraco.directives")
$timeout(function() {
update(true);
}, 200);
}, 500);
//I hate bootstrap tabs

View File

@@ -598,23 +598,59 @@
}
/* ---------- SUB VIEWS COLUMNS ----------- */
/* ---------- TEMPLATES GRID ------------ */
.sub-view-columns {
.templates-grid {
display: flex;
margin-bottom: 40px;
h5 {
margin-top: 0;
}
flex-direction: row;
flex-wrap: wrap;
}
.sub-view-column-left {
flex: 0 0 250px;
margin-right: 70px;
.template {
width: 125px;
height: 150px;
padding: 20px;
background: #f8f8f8;
border: 1px solid #CCCCCC;
text-align: center;
margin: 0 20px 20px 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 5px;
animation: fadeIn 0.5s;
position: relative;
}
.sub-view-column-right {
flex: 1;
.template .template-content .icon {
font-size: 50px;
color: #d9d9d9;
}
.template .remove-template {
position: absolute;
top: 5px;
right: 5px;
cursor: pointer;
}
.template .template-default-label {
font-size: 11px;
color: #d9d9d9;
}
.template .link-blue {
color: @blue;
}
.template.template-default {
border-width: 2px;
}
.template.template-placeholder {
border: 1px dashed #d9d9d9;
background: none;
cursor: pointer;
}
/* ---------- CHILD NODE TREE ---------- */

View File

@@ -353,6 +353,10 @@ input.umb-panel-header-name {
}
}
input.umb-panel-header-name.name-is-empty {
min-width: 110px;
}
.umb-panel-header-alias {
font-size: 13px;
color: #cccccc;

View File

@@ -28,6 +28,17 @@
width: 99%;
}
.umb-locked-field{
font-size: 13px;
color: #ccc;
a{text-decoration: none;}
a:hover {color: #000;}
i.icon{color: #ccc; margin-right: 5px;}
input{ background: transparent; border: none; line-height: 13px; font-size: 13px; padding: 0; margin-bottom: 1px}
}
//
// Content picker
// --------------------------------------------------

View File

@@ -20,8 +20,13 @@
</umb-content-name>
-->
<div class="umb-panel-header-name-wrapper">
<input type="text" class="umb-panel-header-name" ng-model="name" placeholder="Enter name..." umb-auto-resize />
<div class="umb-panel-header-alias"><i class="icon icon-lock"></i>{{ alias }}</div>
<input type="text" class="umb-panel-header-name" ng-model="name" placeholder="Enter name..." ng-class="{'name-is-empty': name===null || name===''}" umb-auto-resize />
<umb-locked-field ng-model="alias"></umb-locked-field>
<!--
<div class="umb-panel-header-alias"><i class="icon icon-lock"></i>{{ alias }}</div>-->
</div>
<input type="text" class="umb-panel-header-description" ng-model="description" placeholder="Enter description..." />

View File

@@ -0,0 +1,14 @@
<div class="umb-locked-field">
<a href ng-click="toggleLock()">
<i class="icon icon-lock"
ng-class="{'icon-lock': locked, 'icon-unlocked': locked != true}"></i>
</a>
<span ng-show="locked">{{model}}</span>
<input type="text"
ng-if="locked === false"
ng-model="$parent.model"
placeholder="Enter value..." umb-auto-resize />
</div>

View File

@@ -6,11 +6,11 @@
<div class="umb-control-group clearfix">
<div class="title-wrapper">
<input class="input-style-underline" ng-model="model.property.label" placeholder="Enter a name..." umb-auto-focus />
<a href="" class="alias">
<i class="icon icon-lock"></i>
<span>{{ model.property.alias }}</span>
</a>
<input class="input-style-underline"
ng-model="model.property.label"
placeholder="Enter a name..." umb-auto-focus />
<umb-locked-field ng-model="model.property.alias"></umb-locked-field>
</div>
</div>
@@ -34,9 +34,12 @@
<div class="editor-editor">{{ model.property.editor }}</div>
</div>
</div>
<a href class="pull-right" style="padding-top: 15px"
ng-click="model.editDataType(model.property)" ng-if="model.property.editor">
<i class="icon icon-settings" style="font-size: 15px; color: #555"></i>
</a>
<button class="btn" ng-click="model.editDataType(model.property)" ng-if="model.property.editor">Change editor settings</button>
</div>
</div>
@@ -47,10 +50,12 @@
</label>
<select class="umb-editor umb-dropdown" placeholder="Validation">
<option>Email</option>
<option>Number</option>
<option>Tekst</option>
<option>Etc...</option>
<option>Validation</option>
<option>Validate as email</option>
<option>Validate as a number</option>
<option>Validate as a Url</option>
<option>...or enter a custom validation</option>
</select>
</div>

View File

@@ -0,0 +1,14 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.DocumentType.PropertyController
* @function
*
* @description
* The controller for the content type editor property dialog
*/
function AvailableTemplatesController($scope) {
}
angular.module("umbraco").controller("Umbraco.Editors.DocumentType.AvailableTemplatesController", AvailableTemplatesController);

View File

@@ -0,0 +1,10 @@
<div ng-controller="Umbraco.Editors.DocumentType.AvailableTemplatesController">
<div class="dialog-grid">
<div class="dialog-grid-element" ng-repeat="availableTemplate in model.templates | filter:{show:true} | orderBy:'name'">
<a href="" ng-click="model.chooseTemplate(availableTemplate)">
<i class="icon icon-newspaper-alt"></i>
{{ availableTemplate.name }}
</a>
</div>
</div>
</div>

View File

@@ -6,7 +6,7 @@
* @description
* The controller for the content type editor
*/
function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, contentTypeResource, dataTypeResource, editorState) {
function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, contentTypeResource, entityResource, dataTypeResource, editorState, contentEditingHelper, formHelper) {
$scope.page = {actions: [], menu: [], subViews: [] };
$scope.sortingMode = false;
@@ -46,47 +46,24 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont
},
{
"name": "Templates",
"icon": "article",
"icon": "newspaper-alt",
"view": "views/documentType/views/templates/templates.html"
}
];
if ($routeParams.create) {
//we are creating so get an empty data type item
contentTypeResource.getScaffold()
contentTypeResource.getEmpty()
.then(function(dt) {
$scope.contentType = dt;
//set a shared state
editorState.set($scope.contentType);
// add init tab
addInitTab();
init(dt);
});
}
else {
contentTypeResource.getById($routeParams.id).then(function(dt){
$scope.contentType = dt;
// set all tab to inactive
if( $scope.contentType.groups.length !== 0 ) {
angular.forEach($scope.contentType.groups, function(group){
// set state
group.tabState = "inActive";
// push init/placeholder property
addInitProperty(group);
});
}
//set a shared state
editorState.set($scope.contentType);
// add init tab
addInitTab();
init(dt);
});
}
/* ---------- SAVE ---------- */
@@ -96,25 +73,17 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont
contentTypeResource.save($scope.contentType).then(function(dt){
formHelper.resetForm({ scope: $scope, notifications: dt.notifications });
contentEditingHelper.handleSuccessfulSave({
scope: $scope,
savedContent: dt,
rebindCallback: function() {
}
});
//post save logic here -the saved doctype returns as a new object
// set all tab to inactive
if( $scope.contentType.groups.length !== 0 ) {
angular.forEach($scope.contentType.groups, function(group){
// set state
group.tabState = "inActive";
// push init/placeholder property
addInitProperty(group);
});
}
//set a shared state
editorState.set($scope.contentType);
// add init tab
addInitTab();
init(dt);
});
};
@@ -273,6 +242,28 @@ function DocumentTypeEditController($scope, $rootScope, $routeParams, $log, cont
}
};
function init(contentType){
$scope.contentType = contentType;
// set all tab to inactive
if( $scope.contentType.groups.length !== 0 ) {
angular.forEach($scope.contentType.groups, function(group){
// set state
group.tabState = "inActive";
// push init/placeholder property
addInitProperty(group);
});
}
//set a shared state
editorState.set($scope.contentType);
// add init tab
addInitTab();
}
function addInitTab() {
// check i init tab already exists

View File

@@ -3,11 +3,11 @@
<div class="sub-view-column-left">
<h5>Allow as root</h5>
<small>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vulputate velit a velit consectetur luctus.</small>
<small>Allow editors to create content if this type in the root of the content tree</small>
</div>
<div class="sub-view-column-right">
<label class="checkbox no-indent">
<input type="checkbox" ng-model="contentType.allowAsRoot" /> Yes - lorem ipsum dolor sit
<input type="checkbox" ng-model="contentType.allowAsRoot" /> Yes - allow content of this type in the root
</label>
</div>
@@ -17,7 +17,7 @@
<div class="sub-view-column-left">
<h5>Allowed child node types</h5>
<small>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vulputate velit a velit consectetur luctus.</small>
<small>Allow content of these types to be created underneath content this type</small>
</div>
<div class="sub-view-column-right">

View File

@@ -0,0 +1,108 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.DocumentType.TemplatesController
* @function
*
* @description
* The controller for the content type editor templates sub view
*/
function TemplatesController($scope, entityResource) {
/* ----------- SCOPE VARIABLES ----------- */
$scope.templates = {};
$scope.templates.availableTemplates = [];
/* ---------- INIT ---------- */
init();
function init() {
entityResource.getAll("Template").then(function(templates){
$scope.templates.availableTemplates = templates;
// we should be able to move this to a custom filter
angular.forEach($scope.templates.availableTemplates, function(template){
var exists = false;
angular.forEach($scope.contentType.allowedTemplates, function(allowedTemplate){
if( template.alias === allowedTemplate.alias ) {
exists = true;
}
});
if(exists) {
template.show = false;
} else {
template.show = true;
}
});
});
}
$scope.removeTemplate = function(selectedTemplate) {
// splice from array
var selectedTemplateIndex = $scope.contentType.allowedTemplates.indexOf(selectedTemplate);
$scope.contentType.allowedTemplates.splice(selectedTemplateIndex, 1);
// show content type in content types array
for (var templateIndex = 0; templateIndex < $scope.templates.availableTemplates.length; templateIndex++) {
var template = $scope.templates.availableTemplates[templateIndex];
if( selectedTemplate.alias === template.alias ) {
template.show = true;
}
}
};
$scope.openTemplatesPicker = function($event){
$scope.showDialog = false;
$scope.dialogModel = {};
$scope.dialogModel.title = "Choose template";
$scope.dialogModel.templates = $scope.templates.availableTemplates;
$scope.dialogModel.event = $event;
$scope.dialogModel.view = "views/documentType/dialogs/templates/templates.html";
$scope.showDialog = true;
$scope.dialogModel.chooseTemplate = function(selectedTemplate) {
$scope.contentType.allowedTemplates.push(selectedTemplate);
for (var templateIndex = 0; templateIndex < $scope.templates.availableTemplates.length; templateIndex++) {
var template = $scope.templates.availableTemplates[templateIndex];
if( selectedTemplate.alias === template.alias ) {
template.show = false;
}
}
$scope.showDialog = false;
$scope.dialogModel = null;
};
$scope.dialogModel.close = function(){
$scope.showDialog = false;
$scope.dialogModel = null;
};
};
$scope.setAsDefaultTemplate = function(template) {
$scope.contentType.defaultTemplate = {};
$scope.contentType.defaultTemplate = template;
};
}
angular.module("umbraco").controller("Umbraco.Editors.DocumentType.TemplatesController", TemplatesController);

View File

@@ -1 +1,49 @@
templates
<div class="sub-view-columns" ng-controller="Umbraco.Editors.DocumentType.TemplatesController">
<div class="sub-view-column-left">
<h5>Allowed Templates</h5>
<small>Choose which templates editors are allowed to use on content of this type</small>
</div>
<div class="sub-view-column-right">
<div class="templates-grid">
<div class="template template-default">
<div class="template-content">
<i class="icon icon-newspaper-alt"></i>
<div class="template-name">{{ contentType.defaultTemplate.name }}</div>
<span class="template-default-label">(Default)</span>
</div>
</div>
<div class="template" ng-repeat="template in contentType.allowedTemplates | filter: { id:'!'+contentType.defaultTemplate.id }">
<div class="template-content">
<i class="icon icon-newspaper-alt"></i>
<div class="template-name">{{ template.name }}</div>
<a href="" class="template-default-label link-blue" ng-click="setAsDefaultTemplate(template)">Set as default</a>
</div>
<i class="icon icon-trash remove-template" ng-click="removeTemplate(template)"></i>
</div>
<div class="template template-placeholder">
<div class="template-content">
<div class="template-name" ng-click="openTemplatesPicker($event)">Choose template</div>
</div>
</div>
</div>
</div>
<umb-overlay
ng-style=""
ng-if="showDialog"
model="dialogModel"
animation="slide-in-right"
position="target"
view="dialogModel.view">
</umb-overlay>
</div>

View File

@@ -181,6 +181,13 @@ namespace Umbraco.Web.Editors
var ctId = Convert.ToInt32(contentType.Id);
//filter out empty properties
contentType.Groups = contentType.Groups.Where(x => x.Name.IsNullOrWhiteSpace() == false).ToList();
foreach (var group in contentType.Groups)
{
group.Properties = group.Properties.Where(x => x.Alias.IsNullOrWhiteSpace() == false).ToList();
}
if (ctId > 0)
{
//its an update to an existing

View File

@@ -535,6 +535,11 @@ namespace Umbraco.Web.Editors
//now we need to convert the unknown ones
switch (entityType)
{
case UmbracoEntityTypes.Template:
var templates = Services.FileService.GetTemplates();
var filteredTemplates = ExecutePostFilter(templates, postFilter, postFilterParams);
return filteredTemplates.Select(Mapper.Map<EntityBasic>);
case UmbracoEntityTypes.Macro:
//Get all macros from the macro service
var macros = Services.MacroService.GetAll().WhereNotNull().OrderBy(x => x.Name);
@@ -717,8 +722,6 @@ namespace Umbraco.Web.Editors
return UmbracoObjectTypes.Media;
case UmbracoEntityTypes.MemberType:
return UmbracoObjectTypes.MediaType;
case UmbracoEntityTypes.Template:
return UmbracoObjectTypes.Template;
case UmbracoEntityTypes.MemberGroup:
return UmbracoObjectTypes.MemberGroup;
case UmbracoEntityTypes.ContentItem:

View File

@@ -49,6 +49,8 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dto => dto.SortOrder, expression => expression.Ignore())
//mapped in aftermap
.ForMember(dto => dto.AllowedContentTypes, expression => expression.Ignore())
.ForMember(dto => dto.AllowedTemplates, expression => expression.Ignore())
//ignore, we'll do this in after map
.ForMember(dto => dto.PropertyGroups, expression => expression.Ignore())
@@ -92,6 +94,9 @@ namespace Umbraco.Web.Models.Mapping
if(addCt != null)
dest.AddContentType(addCt);
}
//sync templates
dest.AllowedTemplates = source.AllowedTemplates.Select(x => Mapper.Map<ITemplate>(x));
});
@@ -132,12 +137,14 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(g => g.PropertyTypes, expression => expression.Ignore());
config.CreateMap<PropertyTypeDisplay, PropertyType>()
.ConstructUsing((PropertyTypeDisplay propertyTypeDisplay) =>
{
var dataType = applicationContext.Services.DataTypeService.GetDataTypeDefinitionById(propertyTypeDisplay.DataTypeId);
if (dataType == null) throw new NullReferenceException("No data type found with id " + propertyTypeDisplay.DataTypeId);
return new PropertyType(dataType, propertyTypeDisplay.Alias);
})
//only map if it is actually set
.ForMember(dest => dest.Id, expression => expression.Condition(source => source.Id > 0))
.ForMember(dto => dto.CreateDate, expression => expression.Ignore())