diff --git a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs index ded7c60676..86594836db 100644 --- a/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs @@ -36,7 +36,7 @@ namespace Umbraco.Core.Persistence.Factories entity.DisableChangeTracking(); entity.CreateDate = d.createDate; - entity.CreatorId = d.nodeUser; + entity.CreatorId = d.nodeUser == null ? 0 : d.nodeUser; entity.Id = d.id; entity.Key = d.uniqueID; entity.Level = d.level; diff --git a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj index f474e79876..64fef90ffa 100644 --- a/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj +++ b/src/Umbraco.Tests.Benchmarks/Umbraco.Tests.Benchmarks.csproj @@ -116,7 +116,6 @@ - @@ -136,10 +135,6 @@ - - - - diff --git a/src/Umbraco.Web.UI.Client/src/common/services/search.service.js b/src/Umbraco.Web.UI.Client/src/common/services/search.service.js index f945070a2a..c692fda7ba 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/search.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/search.service.js @@ -21,154 +21,140 @@ * */ angular.module('umbraco.services') -.factory('searchService', function ($q, $log, entityResource, contentResource, umbRequestHelper) { + .factory('searchService', function ($q, $log, entityResource, contentResource, umbRequestHelper, $injector, searchResultFormatter) { - function configureMemberResult(member) { - member.menuUrl = umbRequestHelper.getApiUrl("memberTreeBaseUrl", "GetMenu", [{ id: member.id }, { application: 'member' }]); - member.editorPath = "member/member/edit/" + (member.key ? member.key : member.id); - angular.extend(member.metaData, { treeAlias: "member" }); - member.subTitle = member.metaData.Email; - } - - function configureMediaResult(media) - { - media.menuUrl = umbRequestHelper.getApiUrl("mediaTreeBaseUrl", "GetMenu", [{ id: media.id }, { application: 'media' }]); - media.editorPath = "media/media/edit/" + media.id; - angular.extend(media.metaData, { treeAlias: "media" }); - } - - function configureContentResult(content) { - content.menuUrl = umbRequestHelper.getApiUrl("contentTreeBaseUrl", "GetMenu", [{ id: content.id }, { application: 'content' }]); - content.editorPath = "content/content/edit/" + content.id; - angular.extend(content.metaData, { treeAlias: "content" }); - content.subTitle = content.metaData.Url; - } + return { - return { + /** + * @ngdoc method + * @name umbraco.services.searchService#searchMembers + * @methodOf umbraco.services.searchService + * + * @description + * Searches the default member search index + * @param {Object} args argument object + * @param {String} args.term seach term + * @returns {Promise} returns promise containing all matching members + */ + searchMembers: function (args) { - /** - * @ngdoc method - * @name umbraco.services.searchService#searchMembers - * @methodOf umbraco.services.searchService - * - * @description - * Searches the default member search index - * @param {Object} args argument object - * @param {String} args.term seach term - * @returns {Promise} returns promise containing all matching members - */ - searchMembers: function(args) { + if (!args.term) { + throw "args.term is required"; + } - if (!args.term) { - throw "args.term is required"; - } - - return entityResource.search(args.term, "Member", args.searchFrom).then(function (data) { - _.each(data, function(item) { - configureMemberResult(item); - }); - return data; - }); - }, - - /** - * @ngdoc method - * @name umbraco.services.searchService#searchContent - * @methodOf umbraco.services.searchService - * - * @description - * Searches the default internal content search index - * @param {Object} args argument object - * @param {String} args.term seach term - * @returns {Promise} returns promise containing all matching content items - */ - searchContent: function(args) { - - if (!args.term) { - throw "args.term is required"; - } - - return entityResource.search(args.term, "Document", args.searchFrom, args.canceler).then(function (data) { - _.each(data, function (item) { - configureContentResult(item); + return entityResource.search(args.term, "Member", args.searchFrom).then(function (data) { + _.each(data, function (item) { + configureMemberResult(item); + }); + return data; }); - return data; - }); - }, + }, - /** - * @ngdoc method - * @name umbraco.services.searchService#searchMedia - * @methodOf umbraco.services.searchService - * - * @description - * Searches the default media search index - * @param {Object} args argument object - * @param {String} args.term seach term - * @returns {Promise} returns promise containing all matching media items - */ - searchMedia: function(args) { + /** + * @ngdoc method + * @name umbraco.services.searchService#searchContent + * @methodOf umbraco.services.searchService + * + * @description + * Searches the default internal content search index + * @param {Object} args argument object + * @param {String} args.term seach term + * @returns {Promise} returns promise containing all matching content items + */ + searchContent: function (args) { - if (!args.term) { - throw "args.term is required"; - } + if (!args.term) { + throw "args.term is required"; + } - return entityResource.search(args.term, "Media", args.searchFrom).then(function (data) { - _.each(data, function (item) { - configureMediaResult(item); + return entityResource.search(args.term, "Document", args.searchFrom, args.canceler).then(function (data) { + _.each(data, function (item) { + configureContentResult(item); + }); + return data; }); - return data; - }); - }, + }, - /** - * @ngdoc method - * @name umbraco.services.searchService#searchAll - * @methodOf umbraco.services.searchService - * - * @description - * Searches all available indexes and returns all results in one collection - * @param {Object} args argument object - * @param {String} args.term seach term - * @returns {Promise} returns promise containing all matching items - */ - searchAll: function (args) { - - if (!args.term) { - throw "args.term is required"; - } + /** + * @ngdoc method + * @name umbraco.services.searchService#searchMedia + * @methodOf umbraco.services.searchService + * + * @description + * Searches the default media search index + * @param {Object} args argument object + * @param {String} args.term seach term + * @returns {Promise} returns promise containing all matching media items + */ + searchMedia: function (args) { - return entityResource.searchAll(args.term, args.canceler).then(function (data) { + if (!args.term) { + throw "args.term is required"; + } - _.each(data, function(resultByType) { - switch(resultByType.type) { - case "Document": - _.each(resultByType.results, function (item) { - configureContentResult(item); - }); - break; - case "Media": - _.each(resultByType.results, function (item) { - configureMediaResult(item); - }); - break; - case "Member": - _.each(resultByType.results, function (item) { - configureMemberResult(item); - }); - break; - } + return entityResource.search(args.term, "Media", args.searchFrom).then(function (data) { + _.each(data, function (item) { + configureMediaResult(item); + }); + return data; + }); + }, + + /** + * @ngdoc method + * @name umbraco.services.searchService#searchAll + * @methodOf umbraco.services.searchService + * + * @description + * Searches all available indexes and returns all results in one collection + * @param {Object} args argument object + * @param {String} args.term seach term + * @returns {Promise} returns promise containing all matching items + */ + searchAll: function (args) { + + if (!args.term) { + throw "args.term is required"; + } + + return entityResource.searchAll(args.term, args.canceler).then(function (data) { + + _.each(data, function (resultByType) { + + //we need to format the search result data to include things like the subtitle, urls, etc... + // this is done with registered angular services as part of the SearchableTreeAttribute, if that + // is not found, than we format with the default formatter + var formatterMethod = searchResultFormatter.configureDefaultResult; + //check if a custom formatter is specified... + if (resultByType.jsSvc) { + var searchFormatterService = $injector.get(resultByType.jsSvc); + if (searchFormatterService) { + if (!resultByType.jsMethod) { + resultByType.jsMethod = "format"; + } + formatterMethod = searchFormatterService[resultByType.jsMethod]; + + if (!formatterMethod) { + throw "The method " + resultByType.jsMethod + " on the angular service " + resultByType.jsSvc + " could not be found"; + } + } + } + //now apply the formatter for each result + _.each(resultByType.results, function (item) { + formatterMethod.apply(this, [item, resultByType.treeAlias, resultByType.appAlias]); + }); + + }); + + return data; }); - return data; - }); - - }, + }, - //TODO: This doesn't do anything! - setCurrent: function(sectionAlias) { + //TODO: This doesn't do anything! + setCurrent: function (sectionAlias) { - var currentSection = sectionAlias; - } - }; -}); \ No newline at end of file + var currentSection = sectionAlias; + } + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/searchresultformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/searchresultformatter.service.js new file mode 100644 index 0000000000..9bf9f3762c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/searchresultformatter.service.js @@ -0,0 +1,37 @@ + +function searchResultFormatter(umbRequestHelper) { + + function configureDefaultResult(content, treeAlias, appAlias) { + content.editorPath = appAlias + "/" + treeAlias + "/edit/" + content.id; + angular.extend(content.metaData, { treeAlias: treeAlias }); + } + + function configureContentResult(content, treeAlias, appAlias) { + content.menuUrl = umbRequestHelper.getApiUrl("contentTreeBaseUrl", "GetMenu", [{ id: content.id }, { application: appAlias }]); + content.editorPath = appAlias + "/" + treeAlias + "/edit/" + content.id; + angular.extend(content.metaData, { treeAlias: treeAlias }); + content.subTitle = content.metaData.Url; + } + + function configureMemberResult(member, treeAlias, appAlias) { + member.menuUrl = umbRequestHelper.getApiUrl("memberTreeBaseUrl", "GetMenu", [{ id: member.id }, { application: appAlias }]); + member.editorPath = appAlias + "/" + treeAlias + "/edit/" + (member.key ? member.key : member.id); + angular.extend(member.metaData, { treeAlias: treeAlias }); + member.subTitle = member.metaData.Email; + } + + function configureMediaResult(media, treeAlias, appAlias) { + media.menuUrl = umbRequestHelper.getApiUrl("mediaTreeBaseUrl", "GetMenu", [{ id: media.id }, { application: appAlias }]); + media.editorPath = appAlias + "/" + treeAlias + "/edit/" + media.id; + angular.extend(media.metaData, { treeAlias: treeAlias }); + } + + return { + configureContentResult: configureContentResult, + configureMemberResult: configureMemberResult, + configureMediaResult: configureMediaResult, + configureDefaultResult: configureDefaultResult + }; +} + +angular.module('umbraco.services').factory('searchResultFormatter', searchResultFormatter); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js index 54519f353b..d5d1ca58e2 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/search.controller.js @@ -15,21 +15,21 @@ function SearchController($scope, searchService, $log, $location, navigationServ $scope.selectedResult = -1; - $scope.navigateResults = function(ev){ + $scope.navigateResults = function (ev) { //38: up 40: down, 13: enter - switch(ev.keyCode){ + switch (ev.keyCode) { case 38: - iterateResults(true); + iterateResults(true); break; case 40: - iterateResults(false); + iterateResults(false); break; case 13: if ($scope.selectedItem) { $location.path($scope.selectedItem.editorPath); navigationService.hideSearch(); - } + } break; } }; @@ -39,50 +39,50 @@ function SearchController($scope, searchService, $log, $location, navigationServ var groupIndex = -1; var itemIndex = -1; $scope.selectedItem = undefined; - - function iterateResults(up){ + + function iterateResults(up) { //default group - if(!group){ + if (!group) { group = $scope.groups[0]; groupIndex = 0; } - if(up){ - if(itemIndex === 0){ - if(groupIndex === 0){ - gotoGroup($scope.groups.length-1, true); - }else{ - gotoGroup(groupIndex-1, true); + if (up) { + if (itemIndex === 0) { + if (groupIndex === 0) { + gotoGroup($scope.groups.length - 1, true); + } else { + gotoGroup(groupIndex - 1, true); } - }else{ - gotoItem(itemIndex-1); + } else { + gotoItem(itemIndex - 1); } - }else{ - if(itemIndex < group.results.length-1){ - gotoItem(itemIndex+1); - }else{ - if(groupIndex === $scope.groups.length-1){ + } else { + if (itemIndex < group.results.length - 1) { + gotoItem(itemIndex + 1); + } else { + if (groupIndex === $scope.groups.length - 1) { gotoGroup(0); - }else{ - gotoGroup(groupIndex+1); + } else { + gotoGroup(groupIndex + 1); } } } } - function gotoGroup(index, up){ + function gotoGroup(index, up) { groupIndex = index; group = $scope.groups[groupIndex]; - - if(up){ - gotoItem(group.results.length-1); - }else{ - gotoItem(0); + + if (up) { + gotoItem(group.results.length - 1); + } else { + gotoItem(0); } } - function gotoItem(index){ + function gotoItem(index) { itemIndex = index; $scope.selectedItem = group.results[itemIndex]; } @@ -91,7 +91,7 @@ function SearchController($scope, searchService, $log, $location, navigationServ var canceler = null; $scope.$watch("searchTerm", _.debounce(function (newVal, oldVal) { - $scope.$apply(function() { + $scope.$apply(function () { if ($scope.searchTerm) { if (newVal !== null && newVal !== undefined && newVal !== oldVal) { $scope.isSearching = true; @@ -107,8 +107,16 @@ function SearchController($scope, searchService, $log, $location, navigationServ canceler = $q.defer(); } - searchService.searchAll({ term: $scope.searchTerm, canceler: canceler }).then(function(result) { - $scope.groups = _.filter(result, function (group) { return group.results.length > 0; }); + searchService.searchAll({ term: $scope.searchTerm, canceler: canceler }).then(function (result) { + + //result is a dictionary of group Title and it's results + var filtered = {}; + _.each(result, function (value, key) { + if (value.results.length > 0) { + filtered[key] = value; + } + }); + $scope.groups = filtered; //set back to null so it can be re-created canceler = null; }); diff --git a/src/Umbraco.Web.UI.Client/src/less/tree.less b/src/Umbraco.Web.UI.Client/src/less/tree.less index df1bc4114a..e5d367bbd2 100644 --- a/src/Umbraco.Web.UI.Client/src/less/tree.less +++ b/src/Umbraco.Web.UI.Client/src/less/tree.less @@ -53,22 +53,21 @@ border-color: @turquoise-d1; } -.umb-tree li.root > div { - padding: 0; +.umb-tree li.root > div:first-child { + padding: 0; } -.umb-tree li.root > div h5 { - margin: 0; - width: 100%; - - display: flex; - align-items: center; +.umb-tree li.root > div h5, .umb-tree li.root > div h6 { + margin: 0; + width: 100%; + display: flex; + align-items: center; } -.umb-tree li.root > div h5 > a, .umb-tree-header { - display: flex; - padding: 20px 0 20px 20px; - box-sizing: border-box; +.umb-tree li.root > div:first-child h5 > a, .umb-tree-header { + display: flex; + padding: 20px 0 20px 20px; + box-sizing: border-box; } .umb-tree * { @@ -96,14 +95,22 @@ text-decoration: none } -.umb-tree div { +/*.umb-tree div.tree-node { padding: 5px 0 5px 0; position: relative; overflow: hidden; display: flex; flex-wrap: nowrap; align-items: center; +}*/ +.umb-tree div { + padding: 5px 0 5px 0; + position: relative; + overflow: hidden; + display: flex; + flex-wrap: nowrap; + align-items: center; } .umb-tree a.noSpr { @@ -192,6 +199,21 @@ /*color:@turquoise;*/ } +.umb-tree div.umb-search-group { + position: inherit; + display: inherit; +} + +.umb-tree div.umb-search-group:hover { + background: inherit; +} +.umb-tree div.umb-search-group h6 { + /*color: @gray-5;*/ + padding: 10px 0 10px 20px; + font-weight: inherit; + background:@gray-10; +} + .umb-tree .umb-search-group-item { padding-left: 20px; } diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html index db8fd92008..4f07a2b976 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-navigation.html @@ -1,104 +1,106 @@
+ ng-mouseleave="leaveTree($event)" ng-mouseenter="enterTree($event)"> - - + + - -