Merge pull request #6625 from umbraco/v8/feature/AB2913-DataTypeTracking
Data type usage reporting and deletion warning
This commit is contained in:
@@ -7,5 +7,12 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
public interface IDataTypeRepository : IReadWriteQueryRepository<int, IDataType>
|
||||
{
|
||||
IEnumerable<MoveEventInfo<IDataType>> Move(IDataType toMove, EntityContainer container);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a dictionary of content type <see cref="Udi"/>s and the property type aliases that use a <see cref="IDataType"/>
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
IReadOnlyDictionary<Udi, IEnumerable<string>> FindUsages(int id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -279,6 +279,28 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
return moveInfo;
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<Udi, IEnumerable<string>> FindUsages(int id)
|
||||
{
|
||||
if (id == default)
|
||||
return new Dictionary<Udi, IEnumerable<string>>();
|
||||
|
||||
var sql = Sql()
|
||||
.Select<ContentTypeDto>(ct => ct.Select(node => node.NodeDto))
|
||||
.AndSelect<PropertyTypeDto>(pt => Alias(pt.Alias, "ptAlias"), pt => Alias(pt.Name, "ptName"))
|
||||
.From<PropertyTypeDto>()
|
||||
.InnerJoin<ContentTypeDto>().On<ContentTypeDto, PropertyTypeDto>(ct => ct.NodeId, pt => pt.ContentTypeId)
|
||||
.InnerJoin<NodeDto>().On<NodeDto, ContentTypeDto>(n => n.NodeId, ct => ct.NodeId)
|
||||
.Where<PropertyTypeDto>(pt => pt.DataTypeId == id)
|
||||
.OrderBy<NodeDto>(node => node.NodeId)
|
||||
.AndBy<PropertyTypeDto>(pt => pt.Alias);
|
||||
|
||||
var dtos = Database.FetchOneToMany<ContentTypeReferenceDto>(ct => ct.PropertyTypes, sql);
|
||||
|
||||
return dtos.ToDictionary(
|
||||
x => (Udi)new GuidUdi(ObjectTypes.GetUdiType(x.NodeDto.NodeObjectType.Value), x.NodeDto.UniqueId).EnsureClosed(),
|
||||
x => (IEnumerable<string>)x.PropertyTypes.Select(p => p.Alias).ToList());
|
||||
}
|
||||
|
||||
private string EnsureUniqueNodeName(string nodeName, int id = 0)
|
||||
{
|
||||
var template = SqlContext.Templates.Get("Umbraco.Core.DataTypeDefinitionRepository.EnsureUniqueNodeName", tsql => tsql
|
||||
@@ -291,5 +313,24 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
|
||||
|
||||
return SimilarNodeName.GetUniqueName(names, id, nodeName);
|
||||
}
|
||||
|
||||
|
||||
[TableName(Constants.DatabaseSchema.Tables.ContentType)]
|
||||
private class ContentTypeReferenceDto : ContentTypeDto
|
||||
{
|
||||
[ResultColumn]
|
||||
[Reference(ReferenceType.Many)]
|
||||
public List<PropertyTypeReferenceDto> PropertyTypes { get; set; }
|
||||
}
|
||||
|
||||
[TableName(Constants.DatabaseSchema.Tables.PropertyType)]
|
||||
private class PropertyTypeReferenceDto
|
||||
{
|
||||
[Column("ptAlias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[Column("ptName")]
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// Gets a content type.
|
||||
/// </summary>
|
||||
TItem Get(int id);
|
||||
new TItem Get(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a content type.
|
||||
@@ -40,6 +40,7 @@ namespace Umbraco.Core.Services
|
||||
int Count();
|
||||
|
||||
IEnumerable<TItem> GetAll(params int[] ids);
|
||||
IEnumerable<TItem> GetAll(IEnumerable<Guid> ids);
|
||||
|
||||
IEnumerable<TItem> GetDescendants(int id, bool andSelf); // parent-child axis
|
||||
IEnumerable<TItem> GetComposedOf(int id); // composition axis
|
||||
|
||||
@@ -10,6 +10,13 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
public interface IDataTypeService : IService
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a dictionary of content type <see cref="Udi"/>s and the property type aliases that use a <see cref="IDataType"/>
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id);
|
||||
|
||||
Attempt<OperationResult<OperationResultType, EntityContainer>> CreateContainer(int parentId, string name, int userId = Constants.Security.SuperUserId);
|
||||
Attempt<OperationResult> SaveContainer(EntityContainer container, int userId = Constants.Security.SuperUserId);
|
||||
EntityContainer GetContainer(int containerId);
|
||||
|
||||
+2
-2
@@ -252,12 +252,12 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<TItem> GetAll(params Guid[] ids)
|
||||
public IEnumerable<TItem> GetAll(IEnumerable<Guid> ids)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete: true))
|
||||
{
|
||||
scope.ReadLock(ReadLockIds);
|
||||
return Repository.GetMany(ids);
|
||||
return Repository.GetMany(ids.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -466,6 +466,14 @@ namespace Umbraco.Core.Services.Implement
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id)
|
||||
{
|
||||
using (var scope = ScopeProvider.CreateScope(autoComplete:true))
|
||||
{
|
||||
return _dataTypeRepository.FindUsages(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void Audit(AuditType type, int userId, int objectId)
|
||||
{
|
||||
_auditRepository.Save(new AuditItem(objectId, type, userId, ObjectTypes.GetName(UmbracoObjectTypes.DataType)));
|
||||
|
||||
@@ -28,6 +28,68 @@ namespace Umbraco.Tests.Persistence.Repositories
|
||||
return new EntityContainerRepository(scopeAccessor, AppCaches.Disabled, Logger, Constants.ObjectTypes.DataTypeContainer);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Find_Usages()
|
||||
{
|
||||
var provider = TestObjects.GetScopeProvider(Logger);
|
||||
|
||||
using (provider.CreateScope())
|
||||
{
|
||||
var dtRepo = CreateRepository();
|
||||
IDataType dataType1 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService)) { Name = "dt1" };
|
||||
dtRepo.Save(dataType1);
|
||||
IDataType dataType2 = new DataType(new RadioButtonsPropertyEditor(Logger, ServiceContext.TextService)) { Name = "dt2" };
|
||||
dtRepo.Save(dataType2);
|
||||
|
||||
var ctRepo = Factory.GetInstance<IContentTypeRepository>();
|
||||
IContentType ct = new ContentType(-1)
|
||||
{
|
||||
Alias = "ct1",
|
||||
Name = "CT1",
|
||||
AllowedAsRoot = true,
|
||||
Icon = "icon-home",
|
||||
PropertyGroups = new PropertyGroupCollection
|
||||
{
|
||||
new PropertyGroup(true)
|
||||
{
|
||||
Name = "PG1",
|
||||
PropertyTypes = new PropertyTypeCollection(true)
|
||||
{
|
||||
new PropertyType(dataType1, "pt1")
|
||||
{
|
||||
Name = "PT1"
|
||||
},
|
||||
new PropertyType(dataType1, "pt2")
|
||||
{
|
||||
Name = "PT2"
|
||||
},
|
||||
new PropertyType(dataType2, "pt3")
|
||||
{
|
||||
Name = "PT3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
ctRepo.Save(ct);
|
||||
|
||||
var usages = dtRepo.FindUsages(dataType1.Id);
|
||||
|
||||
var key = usages.First().Key;
|
||||
Assert.AreEqual(ct.Key, ((GuidUdi)key).Guid);
|
||||
Assert.AreEqual(2, usages[key].Count());
|
||||
Assert.AreEqual("pt1", usages[key].ElementAt(0));
|
||||
Assert.AreEqual("pt2", usages[key].ElementAt(1));
|
||||
|
||||
usages = dtRepo.FindUsages(dataType2.Id);
|
||||
|
||||
key = usages.First().Key;
|
||||
Assert.AreEqual(ct.Key, ((GuidUdi)key).Guid);
|
||||
Assert.AreEqual(1, usages[key].Count());
|
||||
Assert.AreEqual("pt3", usages[key].ElementAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Move()
|
||||
{
|
||||
|
||||
@@ -329,6 +329,11 @@ namespace Umbraco.Tests.TestHelpers
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<Udi, IEnumerable<string>> GetReferences(int id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -241,3 +241,7 @@ table th[class*="span"],
|
||||
background-color: darken(@infoBackground, 5%);
|
||||
}
|
||||
}
|
||||
|
||||
.table .icon {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
@@ -55,7 +55,8 @@ function confirmDirective() {
|
||||
onConfirm: '=',
|
||||
onCancel: '=',
|
||||
caption: '@',
|
||||
confirmButtonStyle: '@'
|
||||
confirmButtonStyle: '@',
|
||||
confirmLabelKey: '@'
|
||||
},
|
||||
link: function (scope, element, attr, ctrl) {
|
||||
scope.showCancel = false;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name umbraco.filters.filter:CMS_joinArray
|
||||
* @namespace umbCmsJoinArray
|
||||
*
|
||||
* param {array} array of string or objects, if an object use the third argument to specify which prop to list.
|
||||
* param {seperator} string containing the seperator to add between joined values.
|
||||
* param {prop} string used if joining an array of objects, set the name of properties to join.
|
||||
*
|
||||
* @description
|
||||
* Join an array of string or an array of objects, with a costum seperator.
|
||||
*
|
||||
*/
|
||||
angular.module("umbraco.filters").filter('umbCmsJoinArray', function () {
|
||||
return function join(array, separator, prop) {
|
||||
return (!angular.isUndefined(prop) ? array.map(function (item) {
|
||||
return item[prop];
|
||||
}) : array).join(separator || '');
|
||||
};
|
||||
});
|
||||
@@ -43,6 +43,30 @@ function dataTypeResource($q, $http, umbDataFormatter, umbRequestHelper) {
|
||||
"Failed to retrieve pre values for editor alias " + editorAlias);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.dataTypeResource#getReferences
|
||||
* @methodOf umbraco.resources.dataTypeResource
|
||||
*
|
||||
* @description
|
||||
* Retrieves references of a given data type.
|
||||
*
|
||||
* @param {Int} id id of datatype to retrieve references for
|
||||
* @returns {Promise} resourcePromise object.
|
||||
*
|
||||
*/
|
||||
getReferences: function (id) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"dataTypeApiBaseUrl",
|
||||
"GetReferences",
|
||||
{ id: id })),
|
||||
"Failed to retrieve usages for data type of id " + id);
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.dataTypeResource#getById
|
||||
|
||||
@@ -138,14 +138,14 @@ app.run(['$rootScope', '$route', '$location', 'urlHelper', 'navigationService',
|
||||
|
||||
var toRetain = navigationService.retainQueryStrings(currentRouteParams, next.params);
|
||||
|
||||
//if toRetain is not null it means that there are missing query strings and we need to update the current params
|
||||
//if toRetain is not null it means that there are missing query strings and we need to update the current params.
|
||||
if (toRetain) {
|
||||
$route.updateParams(toRetain);
|
||||
}
|
||||
|
||||
//check if the location being changed is only due to global/state query strings which means the location change
|
||||
//isn't actually going to cause a route change.
|
||||
if (!toRetain && navigationService.isRouteChangingNavigation(currentRouteParams, next.params)) {
|
||||
if (navigationService.isRouteChangingNavigation(currentRouteParams, next.params)) {
|
||||
|
||||
//The location change will cause a route change, continue the route if the query strings haven't been updated.
|
||||
$route.reload();
|
||||
|
||||
@@ -160,6 +160,7 @@ input.umb-table__input {
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
color: @ui-option-type;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.umb-table-body__checkicon,
|
||||
@@ -245,7 +246,7 @@ input.umb-table__input {
|
||||
.umb-table-cell {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
flex: 1 1 1%; //NOTE 1% is a Internet Explore hack, so that cells don't collapse
|
||||
flex: 1 1 5%;
|
||||
position: relative;
|
||||
margin: auto 14px;
|
||||
padding: 6px 2px;
|
||||
@@ -258,6 +259,11 @@ input.umb-table__input {
|
||||
white-space: nowrap; //NOTE Disable/Enable this to keep textstring on one line
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.umb-table-cell.--noOverflow > * {
|
||||
overflow: visible;
|
||||
white-space: normal;
|
||||
text-overflow: unset;
|
||||
}
|
||||
|
||||
.umb-table-cell:first-of-type:not(.not-fixed) {
|
||||
flex: 0 0 25px;
|
||||
@@ -269,6 +275,9 @@ input.umb-table__input {
|
||||
flex: 0 0 auto !important;
|
||||
}
|
||||
|
||||
.umb-table-cell--nano {
|
||||
flex: 0 0 50px;
|
||||
}
|
||||
.umb-table-cell--small {
|
||||
flex: .5 .5 1%;
|
||||
max-width: 12.5%;
|
||||
@@ -285,8 +294,8 @@ input.umb-table__input {
|
||||
|
||||
// Increases the space for the name cell
|
||||
.umb-table__name {
|
||||
flex: 1 1 25%;
|
||||
max-width: 25%;
|
||||
flex: 1 1 20%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.umb-table__loading-overlay {
|
||||
|
||||
@@ -62,7 +62,7 @@ table {
|
||||
}
|
||||
|
||||
|
||||
.table tr > td:first-child {
|
||||
.table:not(.table-bordered) tr > td:first-child {
|
||||
border-left: 4px solid transparent;
|
||||
}
|
||||
.table tr.--selected > td:first-child {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
action="confirm()"
|
||||
button-style="{{confirmButtonStyle || 'primary'}}"
|
||||
state="confirmButtonState"
|
||||
label-key="general_ok">
|
||||
label-key="{{confirmLabelKey || 'general_ok'}}">
|
||||
</umb-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
*/
|
||||
function DataTypeDeleteController($scope, dataTypeResource, treeService, navigationService, localizationService) {
|
||||
|
||||
$scope.performDelete = function() {
|
||||
var vm = this;
|
||||
|
||||
vm.propertyJoinSeparator = ', <i class="icon-alert red"></i>';
|
||||
vm.hasReferences = false;
|
||||
vm.references = [];
|
||||
|
||||
vm.performDelete = function() {
|
||||
|
||||
//mark it for deletion (used in the UI)
|
||||
$scope.currentNode.loading = true;
|
||||
@@ -24,7 +30,7 @@ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigat
|
||||
});
|
||||
};
|
||||
|
||||
$scope.performContainerDelete = function () {
|
||||
vm.performContainerDelete = function () {
|
||||
|
||||
//mark it for deletion (used in the UI)
|
||||
$scope.currentNode.loading = true;
|
||||
@@ -41,16 +47,36 @@ function DataTypeDeleteController($scope, dataTypeResource, treeService, navigat
|
||||
|
||||
};
|
||||
|
||||
$scope.cancel = function() {
|
||||
vm.cancel = function() {
|
||||
navigationService.hideDialog();
|
||||
};
|
||||
|
||||
$scope.labels = {};
|
||||
vm.labels = {};
|
||||
localizationService
|
||||
.format(["editdatatype_yesDelete", "editdatatype_andAllRelated"], "%0% " + $scope.currentNode.name + " %1%")
|
||||
.localize("editdatatype_acceptDeleteConsequence", [$scope.currentNode.name])
|
||||
.then(function (data) {
|
||||
$scope.labels.deleteConfirm = data;
|
||||
vm.labels.deleteConfirm = data;
|
||||
});
|
||||
|
||||
var init = function() {
|
||||
|
||||
if($scope.currentNode.nodeType === "dataTypes") {
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
dataTypeResource.getReferences($scope.currentNode.id)
|
||||
.then(function (data) {
|
||||
vm.loading = false;
|
||||
vm.references = data;
|
||||
|
||||
vm.hasReferences = data.documentTypes.length > 0 || data.mediaTypes.length > 0 || data.memberTypes.length > 0;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.DataType.DeleteController", DataTypeDeleteController);
|
||||
|
||||
@@ -6,35 +6,20 @@
|
||||
* @description
|
||||
* The controller for the content editor
|
||||
*/
|
||||
function DataTypeEditController($scope, $routeParams, appState, navigationService, dataTypeResource, serverValidationManager, contentEditingHelper, formHelper, editorState, dataTypeHelper, eventsService) {
|
||||
|
||||
//setup scope vars
|
||||
$scope.page = {};
|
||||
$scope.page.loading = false;
|
||||
$scope.page.nameLocked = false;
|
||||
$scope.page.menu = {};
|
||||
$scope.page.menu.currentSection = appState.getSectionState("currentSection");
|
||||
$scope.page.menu.currentNode = null;
|
||||
function DataTypeEditController($scope, $routeParams, appState, navigationService, dataTypeResource, serverValidationManager, contentEditingHelper, formHelper, editorState, dataTypeHelper, eventsService, localizationService) {
|
||||
|
||||
var evts = [];
|
||||
|
||||
//method used to configure the pre-values when we retrieve them from the server
|
||||
function createPreValueProps(preVals) {
|
||||
$scope.preValues = [];
|
||||
for (var i = 0; i < preVals.length; i++) {
|
||||
$scope.preValues.push({
|
||||
hideLabel: preVals[i].hideLabel,
|
||||
alias: preVals[i].key,
|
||||
description: preVals[i].description,
|
||||
label: preVals[i].label,
|
||||
view: preVals[i].view,
|
||||
value: preVals[i].value,
|
||||
config: preVals[i].config
|
||||
});
|
||||
}
|
||||
}
|
||||
var vm = this;
|
||||
|
||||
//setup scope vars
|
||||
vm.page = {};
|
||||
vm.page.loading = false;
|
||||
vm.page.menu = {};
|
||||
vm.page.menu.currentSection = appState.getSectionState("currentSection");
|
||||
vm.page.menu.currentNode = null;
|
||||
|
||||
//set up the standard data type props
|
||||
$scope.properties = {
|
||||
vm.properties = {
|
||||
selectedEditor: {
|
||||
alias: "selectedEditor",
|
||||
description: "Select a property editor",
|
||||
@@ -47,52 +32,51 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic
|
||||
};
|
||||
|
||||
//setup the pre-values as props
|
||||
$scope.preValues = [];
|
||||
|
||||
if ($routeParams.create) {
|
||||
|
||||
$scope.page.loading = true;
|
||||
$scope.showIdentifier = false;
|
||||
|
||||
//we are creating so get an empty data type item
|
||||
dataTypeResource.getScaffold($routeParams.id)
|
||||
.then(function(data) {
|
||||
|
||||
$scope.preValuesLoaded = true;
|
||||
$scope.content = data;
|
||||
|
||||
setHeaderNameState($scope.content);
|
||||
|
||||
//set a shared state
|
||||
editorState.set($scope.content);
|
||||
|
||||
$scope.page.loading = false;
|
||||
|
||||
vm.preValues = [];
|
||||
|
||||
|
||||
//method used to configure the pre-values when we retrieve them from the server
|
||||
function createPreValueProps(preVals) {
|
||||
vm.preValues = [];
|
||||
for (var i = 0; i < preVals.length; i++) {
|
||||
vm.preValues.push({
|
||||
hideLabel: preVals[i].hideLabel,
|
||||
alias: preVals[i].key,
|
||||
description: preVals[i].description,
|
||||
label: preVals[i].label,
|
||||
view: preVals[i].view,
|
||||
value: preVals[i].value,
|
||||
config: preVals[i].config
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
loadDataType();
|
||||
|
||||
|
||||
function setHeaderNameState(content) {
|
||||
if(content.isSystem == 1) {
|
||||
vm.page.nameLocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function loadDataType() {
|
||||
|
||||
$scope.page.loading = true;
|
||||
|
||||
$scope.showIdentifier = true;
|
||||
vm.page.loading = true;
|
||||
vm.showIdentifier = true;
|
||||
|
||||
//we are editing so get the content item from the server
|
||||
dataTypeResource.getById($routeParams.id)
|
||||
.then(function(data) {
|
||||
|
||||
$scope.preValuesLoaded = true;
|
||||
$scope.content = data;
|
||||
vm.preValuesLoaded = true;
|
||||
vm.content = data;
|
||||
|
||||
createPreValueProps($scope.content.preValues);
|
||||
createPreValueProps(vm.content.preValues);
|
||||
|
||||
setHeaderNameState($scope.content);
|
||||
setHeaderNameState(vm.content);
|
||||
|
||||
//share state
|
||||
editorState.set($scope.content);
|
||||
editorState.set(vm.content);
|
||||
|
||||
//in one particular special case, after we've created a new item we redirect back to the edit
|
||||
// route but there might be server validation errors in the collection which we need to display
|
||||
@@ -101,49 +85,22 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic
|
||||
serverValidationManager.notifyAndClearAllSubscriptions();
|
||||
|
||||
navigationService.syncTree({ tree: "datatypes", path: data.path }).then(function (syncArgs) {
|
||||
$scope.page.menu.currentNode = syncArgs.node;
|
||||
vm.page.menu.currentNode = syncArgs.node;
|
||||
});
|
||||
|
||||
$scope.page.loading = false;
|
||||
vm.page.loading = false;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
$scope.$watch("content.selectedEditor", function (newVal, oldVal) {
|
||||
|
||||
//when the value changes, we need to dynamically load in the new editor
|
||||
if (newVal && (newVal != oldVal && (oldVal || $routeParams.create))) {
|
||||
//we are editing so get the content item from the server
|
||||
var currDataTypeId = $routeParams.create ? undefined : $routeParams.id;
|
||||
dataTypeResource.getPreValues(newVal, currDataTypeId)
|
||||
.then(function (data) {
|
||||
$scope.preValuesLoaded = true;
|
||||
$scope.content.preValues = data;
|
||||
createPreValueProps($scope.content.preValues);
|
||||
|
||||
setHeaderNameState($scope.content);
|
||||
|
||||
//share state
|
||||
editorState.set($scope.content);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function setHeaderNameState(content) {
|
||||
|
||||
if(content.isSystem == 1) {
|
||||
$scope.page.nameLocked = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$scope.save = function() {
|
||||
function saveDataType() {
|
||||
|
||||
if (formHelper.submitForm({ scope: $scope })) {
|
||||
|
||||
$scope.page.saveButtonState = "busy";
|
||||
vm.page.saveButtonState = "busy";
|
||||
|
||||
dataTypeResource.save($scope.content, $scope.preValues, $routeParams.create)
|
||||
dataTypeResource.save(vm.content, vm.preValues, $routeParams.create)
|
||||
.then(function(data) {
|
||||
|
||||
formHelper.resetForm({ scope: $scope });
|
||||
@@ -156,18 +113,18 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic
|
||||
}
|
||||
});
|
||||
|
||||
setHeaderNameState($scope.content);
|
||||
setHeaderNameState(vm.content);
|
||||
|
||||
//share state
|
||||
editorState.set($scope.content);
|
||||
editorState.set(vm.content);
|
||||
|
||||
navigationService.syncTree({ tree: "datatypes", path: data.path, forceReload: true }).then(function (syncArgs) {
|
||||
$scope.page.menu.currentNode = syncArgs.node;
|
||||
vm.page.menu.currentNode = syncArgs.node;
|
||||
});
|
||||
|
||||
$scope.page.saveButtonState = "success";
|
||||
vm.page.saveButtonState = "success";
|
||||
|
||||
dataTypeHelper.rebindChangedProperties($scope.content, data);
|
||||
dataTypeHelper.rebindChangedProperties(vm.content, data);
|
||||
|
||||
}, function(err) {
|
||||
|
||||
@@ -177,19 +134,21 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic
|
||||
err: err
|
||||
});
|
||||
|
||||
$scope.page.saveButtonState = "error";
|
||||
vm.page.saveButtonState = "error";
|
||||
|
||||
//share state
|
||||
editorState.set($scope.content);
|
||||
editorState.set(vm.content);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
vm.save = saveDataType;
|
||||
|
||||
evts.push(eventsService.on("app.refreshEditor", function(name, error) {
|
||||
loadDataType();
|
||||
}));
|
||||
|
||||
|
||||
//ensure to unregister from all events!
|
||||
$scope.$on('$destroy', function () {
|
||||
for (var e in evts) {
|
||||
@@ -197,6 +156,80 @@ function DataTypeEditController($scope, $routeParams, appState, navigationServic
|
||||
}
|
||||
});
|
||||
|
||||
function init() {
|
||||
|
||||
$scope.$watch("vm.content.selectedEditor", function (newVal, oldVal) {
|
||||
|
||||
//when the value changes, we need to dynamically load in the new editor
|
||||
if (newVal && (newVal != oldVal && (oldVal || $routeParams.create))) {
|
||||
//we are editing so get the content item from the server
|
||||
var currDataTypeId = $routeParams.create ? undefined : $routeParams.id;
|
||||
dataTypeResource.getPreValues(newVal, currDataTypeId)
|
||||
.then(function (data) {
|
||||
vm.preValuesLoaded = true;
|
||||
vm.content.preValues = data;
|
||||
createPreValueProps(vm.content.preValues);
|
||||
|
||||
setHeaderNameState(vm.content);
|
||||
|
||||
//share state
|
||||
editorState.set(vm.content);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if ($routeParams.create) {
|
||||
|
||||
vm.page.loading = true;
|
||||
vm.showIdentifier = false;
|
||||
|
||||
//we are creating so get an empty data type item
|
||||
dataTypeResource.getScaffold($routeParams.id)
|
||||
.then(function(data) {
|
||||
|
||||
vm.preValuesLoaded = true;
|
||||
vm.content = data;
|
||||
|
||||
setHeaderNameState(vm.content);
|
||||
|
||||
//set a shared state
|
||||
editorState.set(vm.content);
|
||||
|
||||
vm.page.loading = false;
|
||||
|
||||
});
|
||||
}
|
||||
else {
|
||||
loadDataType();
|
||||
}
|
||||
|
||||
var labelKeys = [
|
||||
"general_settings",
|
||||
"references_tabName"
|
||||
];
|
||||
|
||||
localizationService.localizeMany(labelKeys).then(function (values) {
|
||||
|
||||
vm.page.navigation = [
|
||||
{
|
||||
"name": values[0],
|
||||
"alias": "settings",
|
||||
"icon": "icon-settings",
|
||||
"view": "views/datatypes/views/datatype.settings.html",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"name": values[1],
|
||||
"alias": "references",
|
||||
"icon": "icon-molecular-network",
|
||||
"view": "views/datatypes/views/datatype.references.html"
|
||||
}
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.DataType.EditController", DataTypeEditController);
|
||||
|
||||
@@ -1,29 +1,122 @@
|
||||
<div class="umb-dialog umb-pane" ng-controller="Umbraco.Editors.DataType.DeleteController">
|
||||
<div class="umb-dialog umb-pane" ng-controller="Umbraco.Editors.DataType.DeleteController as vm">
|
||||
<div class="umb-dialog-body">
|
||||
|
||||
<p class="abstract">
|
||||
<localize key="defaultdialogs_confirmdelete">Are you sure you want to delete</localize> <strong>{{currentNode.name}}</strong>?
|
||||
</p>
|
||||
|
||||
<ng-switch on="currentNode.nodeType">
|
||||
<div ng-switch-when="container">
|
||||
<umb-confirm on-confirm="performContainerDelete"
|
||||
on-cancel="cancel">
|
||||
|
||||
<p class="abstract">
|
||||
<localize key="defaultdialogs_confirmdelete">Are you sure you want to delete</localize> <strong>{{currentNode.name}}</strong>?
|
||||
</p>
|
||||
<umb-confirm on-confirm="vm.performContainerDelete"
|
||||
on-cancel="vm.cancel"
|
||||
confirm-button-style="danger"
|
||||
confirm-label-key="general_delete">
|
||||
</umb-confirm>
|
||||
</div>
|
||||
|
||||
<div ng-switch-default>
|
||||
<p>
|
||||
<i class="icon-alert red"></i> <strong class="red"><localize key="editdatatype_allPropTypes">All property types & property data</localize></strong>
|
||||
<localize key="editdatatype_willBeDeleted">using this data type will be deleted permanently, please confirm you want to delete these as well</localize>.
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
||||
<umb-checkbox model="confirmed" text="{{labels.deleteConfirm}}">
|
||||
</umb-checkbox>
|
||||
<umb-load-indicator ng-if="vm.loading === true"></umb-load-indicator>
|
||||
|
||||
<umb-confirm ng-if="confirmed" on-confirm="performDelete" on-cancel="cancel">
|
||||
<div ng-if="vm.loading === false && vm.hasReferences === false">
|
||||
<p class="abstract">
|
||||
<localize key="defaultdialogs_confirmdelete">Are you sure you want to delete</localize> <strong>{{currentNode.name}}</strong>?
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.loading === false && vm.hasReferences === true">
|
||||
|
||||
<p class="abstract">
|
||||
<localize key="editdatatype_hasReferencesDeleteConsequence" tokens="[currentNode.name]">Deleting <strong>{{currentNode.name}}</strong> will have the following consequence</localize>
|
||||
</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
<!-- Document Types -->
|
||||
|
||||
<div ng-if="vm.references.documentTypes.length > 0">
|
||||
|
||||
<h5 class="mt4" style="margin-bottom: 20px;">
|
||||
<localize key="treeHeaders_documentTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<table class="table table-condensed table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><localize key="general_name">Name</localize></th>
|
||||
<th><localize key="general_properties">Properties</localize></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="relation in vm.references.documentTypes">
|
||||
<td><span title="{{::relation.name}}({{::relation.alias}})"><i class="umb-table-body__icon {{relation.icon}}"></i>{{::relation.name}}</span></td>
|
||||
<td><span><span class="red" ng-repeat="property in relation.properties"><i class="icon icon-alert red"></i>{{::property.name}}{{$last ? '' : ', '}}</span></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Media Types -->
|
||||
<div ng-if="vm.references.mediaTypes.length > 0">
|
||||
|
||||
<h5 class="mt4" style="margin-bottom: 20px;">
|
||||
<localize key="treeHeaders_mediaTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<table class="table table-condensed table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><localize key="general_name">Name</localize></th>
|
||||
<th><localize key="general_properties">Properties</localize></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="relation in vm.references.mediaTypes">
|
||||
<td><span title="{{::relation.name}}({{::relation.alias}})"><i class="umb-table-body__icon {{relation.icon}}"></i>{{::relation.name}}</span></td>
|
||||
<td><span><span class="red" ng-repeat="property in relation.properties"><i class="icon icon-alert red"></i>{{::property.name}}{{$last ? '' : ', '}}</span></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Member Types -->
|
||||
<div ng-if="vm.references.memberTypes.length > 0">
|
||||
|
||||
<h5 class="mt4" style="margin-bottom: 20px;">
|
||||
<localize key="treeHeaders_memberTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<table class="table table-condensed table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><localize key="general_name">Name</localize></th>
|
||||
<th><localize key="general_properties">Properties</localize></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="relation in vm.references.memberTypes">
|
||||
<td><span title="{{::relation.name}}({{::relation.alias}})"><i class="umb-table-body__icon {{relation.icon}}"></i>{{::relation.name}}</span></td>
|
||||
<td><span><span class="red" ng-repeat="property in relation.properties"><i class="icon icon-alert red"></i>{{::property.name}}{{$last ? '' : ', '}}</span></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<umb-checkbox model="vm.confirmed" text="{{vm.labels.deleteConfirm}}">
|
||||
</umb-checkbox>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<umb-confirm ng-if="vm.confirmed || vm.hasReferences === false"
|
||||
on-confirm="vm.performDelete"
|
||||
on-cancel="vm.cancel"
|
||||
confirm-button-style="danger"
|
||||
confirm-label-key="general_delete">
|
||||
</umb-confirm>
|
||||
</div>
|
||||
</ng-switch>
|
||||
|
||||
@@ -1,81 +1,33 @@
|
||||
<div data-element="editor-data-types" ng-controller="Umbraco.Editors.DataType.EditController">
|
||||
<div data-element="editor-data-types" ng-controller="Umbraco.Editors.DataType.EditController as vm">
|
||||
|
||||
<umb-load-indicator ng-if="page.loading"></umb-load-indicator>
|
||||
<form name="contentForm" novalidate val-form-manager ng-submit="vm.save()">
|
||||
<umb-load-indicator ng-if="vm.page.loading"></umb-load-indicator>
|
||||
<umb-editor-view ng-if="!vm.page.loading">
|
||||
<umb-editor-header name="vm.content.name"
|
||||
name-locked="vm.page.nameLocked"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true"
|
||||
navigation="vm.page.navigation">
|
||||
</umb-editor-header>
|
||||
|
||||
<form name="contentForm"
|
||||
ng-submit="save()"
|
||||
novalidate
|
||||
val-form-manager>
|
||||
<umb-editor-container class="form-horizontal">
|
||||
<umb-editor-sub-views sub-views="vm.page.navigation" model="vm">
|
||||
</umb-editor-sub-views>
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-editor-view ng-if="!page.loading">
|
||||
<umb-editor-footer>
|
||||
<umb-editor-footer-content-right>
|
||||
<umb-button type="submit"
|
||||
button-style="success"
|
||||
state="vm.page.saveButtonState"
|
||||
shortcut="ctrl+s"
|
||||
label="Save"
|
||||
label-key="buttons_save">
|
||||
</umb-button>
|
||||
</umb-editor-footer-content-right>
|
||||
</umb-editor-footer>
|
||||
</umb-editor-view>
|
||||
</form>
|
||||
|
||||
<umb-editor-header
|
||||
name="content.name"
|
||||
name-locked="page.nameLocked"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
hide-alias="true">
|
||||
</umb-editor-header>
|
||||
|
||||
|
||||
<umb-editor-container class="form-horizontal">
|
||||
|
||||
<umb-box>
|
||||
|
||||
<umb-box-content>
|
||||
|
||||
<umb-control-group label="Id" ng-if="showIdentifier">
|
||||
<div>{{content.id}}</div>
|
||||
<small>{{content.key}}</small>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-property property="properties.selectedEditor">
|
||||
<div>
|
||||
<select name="selectedEditor"
|
||||
ng-model="content.selectedEditor"
|
||||
required
|
||||
ng-options="e.alias as e.name for e in content.availableEditors"></select>
|
||||
<span ng-messages="contentForm.selectedEditor.$error" show-validation-on-submit>
|
||||
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</umb-property>
|
||||
|
||||
<umb-property property="properties.selectedEditorId">
|
||||
<div>{{content.selectedEditor}}</div>
|
||||
</umb-property>
|
||||
|
||||
<umb-property property="preValue"
|
||||
ng-repeat="preValue in preValues">
|
||||
<umb-property-editor model="preValue" is-pre-value="true"></umb-property-editor>
|
||||
</umb-property>
|
||||
|
||||
</umb-box-content>
|
||||
|
||||
</umb-box>
|
||||
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-editor-footer>
|
||||
|
||||
<umb-editor-footer-content-right>
|
||||
|
||||
<umb-button
|
||||
type="submit"
|
||||
button-style="success"
|
||||
state="page.saveButtonState"
|
||||
shortcut="ctrl+s"
|
||||
label="Save"
|
||||
label-key="buttons_save">
|
||||
</umb-button>
|
||||
|
||||
</umb-editor-footer-content-right>
|
||||
|
||||
</umb-editor-footer>
|
||||
|
||||
|
||||
</umb-editor-view>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.Editors.DataType.ReferencesController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The controller for the references view of the datatype editor
|
||||
*/
|
||||
function DataTypeReferencesController($scope, $routeParams, dataTypeResource, eventsService, $timeout) {
|
||||
|
||||
var vm = this;
|
||||
var evts = [];
|
||||
var referencesLoaded = false;
|
||||
|
||||
vm.references = {};
|
||||
vm.hasReferences = false;
|
||||
|
||||
vm.view = {};
|
||||
vm.view.loading = true;
|
||||
|
||||
/** Loads in the data type references one time */
|
||||
function loadRelations() {
|
||||
if (!referencesLoaded) {
|
||||
referencesLoaded = true;
|
||||
dataTypeResource.getReferences($routeParams.id)
|
||||
.then(function (data) {
|
||||
vm.view.loading = false;
|
||||
vm.references = data;
|
||||
vm.hasReferences = data.documentTypes.length > 0 || data.mediaTypes.length > 0 || data.memberTypes.length > 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// load data type references when the references tab is activated
|
||||
evts.push(eventsService.on("app.tabChange", function (event, args) {
|
||||
$timeout(function () {
|
||||
if (args.alias === "references") {
|
||||
loadRelations();
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
//ensure to unregister from all events!
|
||||
$scope.$on('$destroy', function () {
|
||||
for (var e in evts) {
|
||||
eventsService.unsubscribe(evts[e]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.DataType.ReferencesController", DataTypeReferencesController);
|
||||
@@ -0,0 +1,112 @@
|
||||
<div ng-controller="Umbraco.Editors.DataType.ReferencesController as vm">
|
||||
|
||||
<umb-load-indicator ng-if="vm.view.loading === true"></umb-load-indicator>
|
||||
|
||||
<umb-box ng-if="vm.view.loading === false && vm.hasReferences === false">
|
||||
<umb-box-header title-key="references_tabName"></umb-box-header>
|
||||
<umb-box-content>
|
||||
<umb-empty-state size="small">
|
||||
<localize key="references_DataTypeNoReferences">This Data Type has no references.</localize>
|
||||
</umb-empty-state>
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
|
||||
|
||||
<div ng-if="vm.view.loading === false && vm.hasReferences === true">
|
||||
|
||||
<!-- Document Types -->
|
||||
|
||||
<div ng-if="vm.references.documentTypes.length > 0">
|
||||
|
||||
<h5 class="mt4" style="margin-bottom: 20px;">
|
||||
<localize key="references_labelUsedByDocumentTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<div class="umb-table">
|
||||
<div class="umb-table-head">
|
||||
<div class="umb-table-row">
|
||||
<div class="umb-table-cell"></div>
|
||||
<div class="umb-table-cell umb-table__name not-fixed"><localize key="general_name">Name</localize></div>
|
||||
<div class="umb-table-cell"><localize key="content_alias">Alias</localize></div>
|
||||
<div class="umb-table-cell"><localize key="references_usedByProperties">Used in</localize></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><localize key="general_open" style="visibility:hidden;">Open</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-table-body">
|
||||
<div class="umb-table-row" ng-repeat="reference in vm.references.documentTypes">
|
||||
<div class="umb-table-cell"><i class="umb-table-body__icon {{reference.icon}}"></i></div>
|
||||
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
|
||||
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
|
||||
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/documentTypes/edit/{{::reference.id}}"><localize key="general_open">Open</localize></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Media Types -->
|
||||
<div ng-if="vm.references.mediaTypes.length > 0">
|
||||
|
||||
<h5 class="mt4" style="margin-bottom: 20px;">
|
||||
<localize key="references_labelUsedByMediaTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<div class="umb-table">
|
||||
<div class="umb-table-head">
|
||||
<div class="umb-table-row">
|
||||
<div class="umb-table-cell"></div>
|
||||
<div class="umb-table-cell umb-table__name not-fixed"><localize key="general_name">Name</localize></div>
|
||||
<div class="umb-table-cell"><localize key="content_alias">Alias</localize></div>
|
||||
<div class="umb-table-cell"><localize key="references_usedByProperties">Used in</localize></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><localize key="general_open" style="visibility:hidden;">Open</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-table-body">
|
||||
<div class="umb-table-row" ng-repeat="reference in vm.references.mediaTypes">
|
||||
<div class="umb-table-cell"><i class="umb-table-body__icon {{reference.icon}}"></i></div>
|
||||
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
|
||||
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
|
||||
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/documentTypes/edit/{{::reference.id}}"><localize key="general_open">Open</localize></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Member Types -->
|
||||
<div ng-if="vm.references.memberTypes.length > 0">
|
||||
|
||||
<h5 class="mt4" style="margin-bottom: 20px;">
|
||||
<localize key="references_labelUsedByMemberTypes"></localize>
|
||||
</h5>
|
||||
|
||||
<div class="umb-table">
|
||||
<div class="umb-table-head">
|
||||
<div class="umb-table-row">
|
||||
<div class="umb-table-cell"></div>
|
||||
<div class="umb-table-cell umb-table__name not-fixed"><localize key="general_name">Name</localize></div>
|
||||
<div class="umb-table-cell"><localize key="content_alias">Alias</localize></div>
|
||||
<div class="umb-table-cell"><localize key="references_usedByProperties">Used in</localize></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><localize key="general_open" style="visibility:hidden;">Open</localize></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="umb-table-body">
|
||||
<div class="umb-table-row" ng-repeat="reference in vm.references.memberTypes">
|
||||
<div class="umb-table-cell"><i class="umb-table-body__icon {{reference.icon}}"></i></div>
|
||||
<div class="umb-table-cell umb-table__name"><span>{{::reference.name}}</span></div>
|
||||
<div class="umb-table-cell"><span title="{{::reference.alias}}">{{::reference.alias}}</span></div>
|
||||
<div class="umb-table-cell --noOverflow"><span>{{::reference.properties | umbCmsJoinArray:', ':'name'}}</span></div>
|
||||
<div class="umb-table-cell umb-table-cell--nano"><a href="#/settings/documentTypes/edit/{{::reference.id}}"><localize key="general_open">Open</localize></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,34 @@
|
||||
<umb-box>
|
||||
|
||||
<umb-box-content>
|
||||
|
||||
<umb-control-group label="Id" ng-if="model.showIdentifier">
|
||||
<div>{{model.content.id}}</div>
|
||||
<small>{{model.content.key}}</small>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-property property="model.properties.selectedEditor">
|
||||
<div>
|
||||
<select name="selectedEditor"
|
||||
ng-model="model.content.selectedEditor"
|
||||
required
|
||||
ng-options="e.alias as e.name for e in model.content.availableEditors"></select>
|
||||
<span ng-messages="contentForm.selectedEditor.$error" show-validation-on-submit>
|
||||
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</umb-property>
|
||||
|
||||
<umb-property property="model.properties.selectedEditorId">
|
||||
<div>{{model.content.selectedEditor}}</div>
|
||||
</umb-property>
|
||||
|
||||
<umb-property property="preValue"
|
||||
ng-repeat="preValue in model.preValues">
|
||||
<umb-property-editor model="preValue" is-pre-value="true"></umb-property-editor>
|
||||
</umb-property>
|
||||
|
||||
</umb-box-content>
|
||||
|
||||
</umb-box>
|
||||
@@ -130,7 +130,7 @@ function MacrosEditController($scope, $q, $routeParams, macroResource, editorSta
|
||||
// navigation
|
||||
vm.labels.settings = values[0];
|
||||
vm.labels.parameters = values[1];
|
||||
|
||||
|
||||
vm.page.navigation = [
|
||||
{
|
||||
"name": vm.labels.settings,
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
(function () {
|
||||
|
||||
describe('umbCmsJoinArray filter', function() {
|
||||
var $umbCmsJoinArray;
|
||||
|
||||
var testCases = [
|
||||
{input:[{param:'a'},{param:'b'},{param:'c'}], separator:', ', prop:'param' , expectedResult: 'a, b, c'},
|
||||
{input:[{param:'a'},{param:'b'},{param:'c'}], separator:' ', prop:'param' , expectedResult: 'a b c'},
|
||||
{input:[{param:'a'},{param:'b'},{param:'c'}], separator:'', prop:'param' , expectedResult: 'abc'},
|
||||
{input:[{param:'a'},{param:'b'},{param:'c'}], separator:'', prop:'wrong' , expectedResult: ''},
|
||||
{input:[], separator:', ', prop:'param' , expectedResult: ''},
|
||||
{input:[{param:'a'},{param:'b'},{param:'c'}], separator:', ', prop:null , expectedResult: ', , '},
|
||||
{input:[{param:'a'},{param:'b'},{param:'c'}], separator:null, prop:'param' , expectedResult: 'abc'},
|
||||
];
|
||||
|
||||
var testCasesWithExpectedError = [
|
||||
{input:'test', separator:', ', prop:'param'},
|
||||
{input:null, separator:', ', prop:'param'},
|
||||
{input:undefined, separator:', ', prop:'param'},
|
||||
];
|
||||
|
||||
beforeEach(module('umbraco'));
|
||||
|
||||
beforeEach(inject(function($filter) {
|
||||
$umbCmsJoinArray = $filter('umbCmsJoinArray');
|
||||
}));
|
||||
|
||||
|
||||
testCases.forEach(function(test){
|
||||
it('Blackbox tests with expected result=\''+test.expectedResult+'\'', function() {
|
||||
expect($umbCmsJoinArray(test.input, test.separator, test.prop)).toBe(test.expectedResult);
|
||||
});
|
||||
});
|
||||
|
||||
testCasesWithExpectedError.forEach(function(test){
|
||||
it('Blackbox tests with expected error. Input=\''+test.input+'\'', function() {
|
||||
expect(function() { $umbCmsJoinArray(test.input, test.separator, test.prop)}).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
@@ -566,6 +566,8 @@
|
||||
<key alias="selectFolder">Vælg den mappe, der skal flyttes</key>
|
||||
<key alias="inTheTree">til i træstrukturen nedenfor</key>
|
||||
<key alias="wasMoved">blev flyttet under</key>
|
||||
<key alias="hasReferencesDeleteConsequence"><![CDATA[Ved sletning af <strong>%0%</strong> fjernes egnskaber og egnskabernes data fra følgende elementer]]></key>
|
||||
<key alias="acceptDeleteConsequence">I understand this action will delete the properties and data based on this Data Type</key>
|
||||
</area>
|
||||
<area alias="errorHandling">
|
||||
<key alias="errorButDataWasSaved">Dine data er blevet gemt, men før du kan udgive denne side er der nogle fejl der skal rettes:</key>
|
||||
@@ -1717,4 +1719,16 @@ Mange hilsner fra Umbraco robotten
|
||||
<key alias="currentLanguage">Aktivt sprog</key>
|
||||
<key alias="switchLanguage">Skift sprog til</key>
|
||||
</area>
|
||||
<area alias="references">
|
||||
<key alias="tabName">Referencer</key>
|
||||
<key alias="DataTypeNoReferences">Denne Data Type har ingen referencer.</key>
|
||||
<key alias="labelsedByDocumentTypes">Brugt i Dokument Typer</key>
|
||||
<key alias="noDocumentTypes">Ingen referencer til Dokument Typer.</key>
|
||||
<key alias="labelUsedByMediaTypes">Brugt i Medie Typer</key>
|
||||
<key alias="noMediaTypes">Ingen referencer til Medie Typer.</key>
|
||||
<key alias="labelUsedByMemberTypes">Brugt i Medlems Typer</key>
|
||||
<key alias="noMemberTypes">Ingen referencer til Medlems Typer.</key>
|
||||
<key alias="usedByProperties">Brugt af</key>
|
||||
</area>
|
||||
|
||||
</language>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -572,6 +572,8 @@
|
||||
<key alias="selectFolder">Select the folder to move</key>
|
||||
<key alias="inTheTree">to in the tree structure below</key>
|
||||
<key alias="wasMoved">was moved underneath</key>
|
||||
<key alias="hasReferencesDeleteConsequence"><![CDATA[Deleting <strong>%0%</strong> will delete the properties and their data from the following items]]></key>
|
||||
<key alias="acceptDeleteConsequence">I understand this action will delete the properties and data based on this Data Type</key>
|
||||
</area>
|
||||
<area alias="errorHandling">
|
||||
<key alias="errorButDataWasSaved">Your data has been saved, but before you can publish this page there are some errors you need to fix first:</key>
|
||||
@@ -2170,4 +2172,15 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
<key alias="currentLanguage">Current language</key>
|
||||
<key alias="switchLanguage">Switch language to</key>
|
||||
</area>
|
||||
<area alias="references">
|
||||
<key alias="tabName">References</key>
|
||||
<key alias="DataTypeNoReferences">This Data Type has no references.</key>
|
||||
<key alias="labelUsedByDocumentTypes">Used in Document Types</key>
|
||||
<key alias="noDocumentTypes">No references to Document Types.</key>
|
||||
<key alias="labelUsedByMediaTypes">Used in Media Types</key>
|
||||
<key alias="noMediaTypes">No references to Media Types.</key>
|
||||
<key alias="labelUsedByMemberTypes">Used in Member Types</key>
|
||||
<key alias="noMemberTypes">No references to Member Types.</key>
|
||||
<key alias="usedByProperties">Used by</key>
|
||||
</area>
|
||||
</language>
|
||||
|
||||
@@ -283,6 +283,60 @@ namespace Umbraco.Web.Editors
|
||||
: Request.CreateNotificationValidationErrorResponse(result.Exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the references (usages) for the data type
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public DataTypeReferences GetReferences(int id)
|
||||
{
|
||||
var result = new DataTypeReferences();
|
||||
var usages = Services.DataTypeService.GetReferences(id);
|
||||
|
||||
foreach(var groupOfEntityType in usages.GroupBy(x => x.Key.EntityType))
|
||||
{
|
||||
//get all the GUIDs for the content types to find
|
||||
var guidsAndPropertyAliases = groupOfEntityType.ToDictionary(i => ((GuidUdi)i.Key).Guid, i => i.Value);
|
||||
|
||||
if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.DocumentType))
|
||||
result.DocumentTypes = GetContentTypeUsages(Services.ContentTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases);
|
||||
else if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.MediaType))
|
||||
result.MediaTypes = GetContentTypeUsages(Services.MediaTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases);
|
||||
else if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.MemberType))
|
||||
result.MemberTypes = GetContentTypeUsages(Services.MemberTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the found content types and usages to the resulting model
|
||||
/// </summary>
|
||||
/// <param name="cts"></param>
|
||||
/// <param name="usages"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<DataTypeReferences.ContentTypeReferences> GetContentTypeUsages(
|
||||
IEnumerable<IContentTypeBase> cts,
|
||||
IReadOnlyDictionary<Guid, IEnumerable<string>> usages)
|
||||
{
|
||||
return cts.Select(x => new DataTypeReferences.ContentTypeReferences
|
||||
{
|
||||
Id = x.Id,
|
||||
Key = x.Key,
|
||||
Alias = x.Alias,
|
||||
Icon = x.Icon,
|
||||
Name = x.Name,
|
||||
Udi = new GuidUdi(ObjectTypes.GetUdiType(UmbracoObjectTypes.DocumentType), x.Key),
|
||||
//only select matching properties
|
||||
Properties = x.PropertyTypes.Where(p => usages[x.Key].InvariantContains(p.Alias))
|
||||
.Select(p => new DataTypeReferences.ContentTypeReferences.PropertyTypeReferences
|
||||
{
|
||||
Alias = p.Alias,
|
||||
Name = p.Name
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#region ReadOnly actions to return basic data - allow access for: content ,media, members, settings, developer
|
||||
/// <summary>
|
||||
/// Gets the content json for all data types
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "dataTypeReferences", Namespace = "")]
|
||||
public class DataTypeReferences
|
||||
{
|
||||
[DataMember(Name = "documentTypes")]
|
||||
public IEnumerable<ContentTypeReferences> DocumentTypes { get; set; } = Enumerable.Empty<ContentTypeReferences>();
|
||||
|
||||
[DataMember(Name = "mediaTypes")]
|
||||
public IEnumerable<ContentTypeReferences> MediaTypes { get; set; } = Enumerable.Empty<ContentTypeReferences>();
|
||||
|
||||
[DataMember(Name = "memberTypes")]
|
||||
public IEnumerable<ContentTypeReferences> MemberTypes { get; set; } = Enumerable.Empty<ContentTypeReferences>();
|
||||
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class ContentTypeReferences : EntityBasic
|
||||
{
|
||||
[DataMember(Name = "properties")]
|
||||
public object Properties { get; set; }
|
||||
|
||||
[DataContract(Name = "property", Namespace = "")]
|
||||
public class PropertyTypeReferences
|
||||
{
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -212,6 +212,7 @@
|
||||
<Compile Include="Media\UploadAutoFillProperties.cs" />
|
||||
<Compile Include="Migrations\PostMigrations\PublishedSnapshotRebuilder.cs" />
|
||||
<Compile Include="Models\AnchorsModel.cs" />
|
||||
<Compile Include="Models\ContentEditing\DataTypeReferences.cs" />
|
||||
<Compile Include="Models\ContentEditing\LinkDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\MacroDisplay.cs" />
|
||||
<Compile Include="Models\ContentEditing\MacroParameterDisplay.cs" />
|
||||
|
||||
Reference in New Issue
Block a user