diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs index 3e633fc020..6aeab13f3b 100644 --- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs @@ -43,7 +43,7 @@ namespace Umbraco.Core.Models /// [UmbracoObjectType(Constants.ObjectTypes.DocumentBlueprint, typeof(IContent))] [FriendlyName("DocumentBlueprint")] - [UmbracoUdiType(Constants.UdiEntityType.Document)] + [UmbracoUdiType(Constants.UdiEntityType.DocumentBluePrint)] DocumentBlueprint, /// diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs index d2a2a780b7..dee667675b 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs @@ -289,7 +289,7 @@ namespace Umbraco.Core.Persistence.Repositories //now delete the items that shouldn't be there var sqlAllIds = translate(0, GetBaseQuery(BaseQueryType.Ids)); var allContentIds = Database.Fetch(sqlAllIds); - var docObjectType = Guid.Parse(Constants.ObjectTypes.Document); + var docObjectType = NodeObjectTypeId; var xmlIdsQuery = new Sql() .Select("DISTINCT cmsContentXml.nodeId") .From(SqlSyntax) @@ -820,7 +820,7 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder", XmlElement last = null; //NOTE: Query creates a reader - does not load all into memory - foreach (var row in Database.Query(sql, new { type = new Guid(Constants.ObjectTypes.Document) })) + foreach (var row in Database.Query(sql, new { type = NodeObjectTypeId })) { string parentId = ((int)row.parentID).ToInvariantString(); string xml = row.xml; diff --git a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs index 985f9446b7..96641ba715 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ContentTypeRepository.cs @@ -110,6 +110,19 @@ namespace Umbraco.Core.Persistence.Repositories return Database.Fetch(sql); } + public IEnumerable GetAllContentTypeIds(string[] aliases) + { + if (aliases.Length == 0) return Enumerable.Empty(); + + var sql = new Sql().Select("cmsContentType.nodeId") + .From(SqlSyntax) + .InnerJoin(SqlSyntax) + .On(SqlSyntax, dto => dto.NodeId, dto => dto.NodeId) + .Where(dto => aliases.Contains(dto.Alias), SqlSyntax); + + return Database.Fetch(sql); + } + protected override Sql GetBaseQuery(bool isCount) { var sql = new Sql(); diff --git a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs index f118be3b76..5cb3faebbb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Interfaces/IContentTypeRepository.cs @@ -47,5 +47,7 @@ namespace Umbraco.Core.Persistence.Repositories /// The original alias with a number appended to it, so that it is unique. /// /// Unique accross all content, media and member types. string GetUniqueAlias(string alias); + + IEnumerable GetAllContentTypeIds(string[] aliases); } } \ No newline at end of file diff --git a/src/Umbraco.Core/Services/ContentTypeService.cs b/src/Umbraco.Core/Services/ContentTypeService.cs index 1b6735153d..895d97f658 100644 --- a/src/Umbraco.Core/Services/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/ContentTypeService.cs @@ -367,6 +367,15 @@ namespace Umbraco.Core.Services } } + public IEnumerable GetAllContentTypeIds(string[] aliases) + { + using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) + { + var repository = RepositoryFactory.CreateContentTypeRepository(uow); + return repository.GetAllContentTypeIds(aliases); + } + } + /// /// Copies a content type as a child under the specified parent if specified (otherwise to the root) /// diff --git a/src/Umbraco.Core/Services/IContentTypeService.cs b/src/Umbraco.Core/Services/IContentTypeService.cs index 46ee8520c6..f470bbce05 100644 --- a/src/Umbraco.Core/Services/IContentTypeService.cs +++ b/src/Umbraco.Core/Services/IContentTypeService.cs @@ -61,6 +61,13 @@ namespace Umbraco.Core.Services /// /// IEnumerable GetAllContentTypeAliases(params Guid[] objectTypes); + + /// + /// Returns all content type Ids for the aliases given + /// + /// + /// + IEnumerable GetAllContentTypeIds(string[] aliases); /// /// Copies a content type as a child under the specified parent if specified (otherwise to the root) diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js index 60dddff3a5..1b88ea4843 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/content/edit.controller.js @@ -14,7 +14,7 @@ scope.page.menu.currentNode = null; scope.page.menu.currentSection = appState.getSectionState("currentSection"); scope.page.listViewPath = null; - scope.page.isNew = scope.createOptions ? true : false; + scope.page.isNew = scope.isNew ? true : false; scope.page.buttonGroupState = "init"; function init(content) { @@ -116,7 +116,7 @@ scope.page.loading = true; //we are creating so get an empty content item - contentResource.getScaffold(scope.contentId, scope.createOptions.docType) + scope.getScaffoldMethod()() .then(function (data) { scope.content = data; @@ -230,11 +230,12 @@ templateUrl: 'views/components/content/edit.html', scope: { contentId: "=", + isNew: "=?", treeAlias: "@", - createOptions: "=?", page: "=?", saveMethod: "&", - getMethod: "&" + getMethod: "&", + getScaffoldMethod: "&?" }, link: link }; diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 2df3c411e6..e629bb0beb 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -399,6 +399,17 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { 'Failed to retrieve data for empty content item type ' + alias); }, + getBlueprintScaffold: function (blueprintId) { + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "contentApiBaseUrl", + "GetEmpty", + [{ blueprintId: blueprintId }])), + 'Failed to retrieve blueprint for id ' + blueprintId); + }, + /** * @ngdoc method * @name umbraco.resources.contentResource#getNiceUrl diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index ba395becfc..2ffecaddef 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -770,9 +770,9 @@ function umbDataFormatter() { var propTemplate = _.find(genericTab.properties, function (item) { return item.alias === "_umb_template"; }); - saveModel.expireDate = propExpireDate.value; - saveModel.releaseDate = propReleaseDate.value; - saveModel.templateAlias = propTemplate.value; + saveModel.expireDate = propExpireDate ? propExpireDate.value : null; + saveModel.releaseDate = propReleaseDate ? propReleaseDate.value : null; + saveModel.templateAlias = propTemplate ? propTemplate.value : null; return saveModel; } diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js index 2c2183695f..94562c6a32 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.edit.controller.js @@ -8,16 +8,19 @@ */ function ContentEditController($scope, $routeParams, contentResource) { + function scaffoldEmpty() { + return contentResource.getScaffold($routeParams.id, $routeParams.doctype); + } + function scaffoldBlueprint() { + return contentResource.getBlueprintScaffold($routeParams.blueprintId); + } + $scope.contentId = $routeParams.id; $scope.saveMethod = contentResource.save; $scope.getMethod = contentResource.getById; + $scope.getScaffoldMethod = $routeParams.blueprintId ? scaffoldBlueprint : scaffoldEmpty; $scope.page = $routeParams.page; - $scope.createOptions = null; - if ($routeParams.create && $routeParams.doctype) { - $scope.createOptions = { - docType: $routeParams.doctype - } - } + $scope.isNew = $routeParams.create; } angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/edit.html b/src/Umbraco.Web.UI.Client/src/views/content/edit.html index bd764628b4..2ad4534115 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/edit.html @@ -4,6 +4,8 @@ page="page" save-method="saveMethod" get-method="getMethod" - tree-alias="content"> + get-scaffold-method="getScaffoldMethod" + tree-alias="content" + is-new="isNew"> diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 5021e1b0a9..a0b8382437 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -115,6 +115,11 @@ namespace Umbraco.Web.Editors var content = Mapper.Map(foundContent); + content.AllowPreview = false; + + //set a custom path since the tree that renders this has the content type id as the parent + content.Path = string.Format("-1,{0},{1}", foundContent.ContentTypeId, content.Id); + content.AllowedActions = new[] {'A'}; var excludeProps = new[] {"_umb_urls", "_umb_releasedate", "_umb_expiredate", "_umb_template"}; @@ -184,6 +189,26 @@ namespace Umbraco.Web.Editors return mapped; } + [OutgoingEditorModelEvent] + public ContentItemDisplay GetEmpty(int blueprintId) + { + var blueprint = Services.ContentService.GetBlueprintById(blueprintId); + if (blueprint == null) + { + throw new HttpResponseException(HttpStatusCode.NotFound); + } + + blueprint.Id = 0; + blueprint.Name = string.Empty; + + var mapped = Mapper.Map(blueprint); + + //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; + } + /// /// Gets the Url for a given node ID /// diff --git a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs index 7b18b015ea..65538778e4 100644 --- a/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentBlueprintTreeController.cs @@ -35,28 +35,48 @@ namespace Umbraco.Web.Trees { var nodes = new TreeNodeCollection(); - //check if we're rendering the root + //get all blueprints + var entities = Services.EntityService.GetChildren(Constants.System.Root, UmbracoObjectTypes.DocumentBlueprint).ToArray(); + + //check if we're rendering the root in which case we'll render the content types that have blueprints if (id == Constants.System.Root.ToInvariantString()) { - var altStartId = string.Empty; + //get all blueprint content types + var contentTypeAliases = entities.Select(x => ((UmbracoEntity) x).ContentTypeAlias).Distinct(); + //get the ids + var contentTypeIds = Services.ContentTypeService.GetAllContentTypeIds(contentTypeAliases.ToArray()); + //now get the entities ... it's a bit round about but still smaller queries than getting all document types + var docTypeEntities = Services.EntityService.GetAll(UmbracoObjectTypes.DocumentType, contentTypeIds.ToArray()).ToArray(); - if (queryStrings.HasKey(TreeQueryStringParameters.StartNodeId)) - altStartId = queryStrings.GetValue(TreeQueryStringParameters.StartNodeId); - - //check if a request has been made to render from a specific start node - if (string.IsNullOrEmpty(altStartId) == false && altStartId != "undefined" && altStartId != Constants.System.Root.ToString(CultureInfo.InvariantCulture)) - { - id = altStartId; - } - - var entities = Services.EntityService.GetChildren(Constants.System.Root, UmbracoObjectTypes.DocumentBlueprint).ToArray(); - - nodes.AddRange(entities - .Select(entity => CreateTreeNode(entity, Constants.ObjectTypes.DocumentBlueprintGuid, id, queryStrings, "icon-blueprint", false)) - .Where(node => node != null)); + nodes.AddRange(docTypeEntities + .Select(entity => + { + var treeNode = CreateTreeNode(entity, Constants.ObjectTypes.DocumentBlueprintGuid, id, queryStrings, "icon-item-arrangement", true); + treeNode.Path = string.Format("-1,{0}", entity.Id); + treeNode.NodeType = "contentType"; + //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. + treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);"; + return treeNode; + })); return nodes; } + else + { + var intId = id.TryConvertTo(); + //Get the content type + var ct = Services.ContentTypeService.GetContentType(intId.Result); + if (ct == null) return nodes; + + var blueprintsForDocType = entities.Where(x => ct.Alias == ((UmbracoEntity) x).ContentTypeAlias); + nodes.AddRange(blueprintsForDocType + .Select(entity => + { + var treeNode = CreateTreeNode(entity, Constants.ObjectTypes.DocumentBlueprintGuid, id, queryStrings, "icon-blueprint", false); + treeNode.Path = string.Format("-1,{0},{1}", ct.Id, entity.Id); + return treeNode; + })); + } return nodes; } @@ -71,9 +91,13 @@ namespace Umbraco.Web.Trees menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionRefresh.Instance.Alias)), true); return menu; } + var ct = Services.EntityService.Get(int.Parse(id), UmbracoObjectTypes.DocumentType); + //no menu if it's a content type + if (ct != null) return null; menu.Items.Add(Services.TextService.Localize(string.Format("actions/{0}", ActionDelete.Instance.Alias))); + return menu; }