From 84aa861a5bca9263eae6c89927f6948ecbb96b50 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 28 Jun 2019 13:03:36 +1000 Subject: [PATCH] Merge remote-tracking branch 'origin/v7/dev' into v8/dev - Iniital commit (broken) # Conflicts: # build/NuSpecs/tools/Web.config.install.xdt # src/Umbraco.Core/Constants-DataTypes.cs # src/Umbraco.Core/Models/DataTypeDefinition.cs # src/Umbraco.Core/Models/DataTypeExtensions.cs # src/Umbraco.Core/Models/IDataTypeDefinition.cs # src/Umbraco.Core/Models/UmbracoEntity.cs # src/Umbraco.Core/Models/UserExtensions.cs # src/Umbraco.Core/Persistence/Factories/DataTypeDefinitionFactory.cs # src/Umbraco.Core/Persistence/Factories/UmbracoEntityFactory.cs # src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs # src/Umbraco.Core/Persistence/Repositories/EntityRepository.cs # src/Umbraco.Core/Services/ContentService.cs # src/Umbraco.Core/Services/DataTypeService.cs # src/Umbraco.Core/Services/EntityService.cs # src/Umbraco.Core/Services/IContentService.cs # src/Umbraco.Core/Services/IDataTypeService.cs # src/Umbraco.Core/Services/IEntityService.cs # src/Umbraco.Core/Services/IRelationService.cs # src/Umbraco.Core/Services/Implement/RelationService.cs # src/Umbraco.Tests/Models/UmbracoEntityTests.cs # src/Umbraco.Tests/Plugins/PluginManagerTests.cs # src/Umbraco.Tests/Services/EntityServiceTests.cs # src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js # src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js # src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js # src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js # src/Umbraco.Web.UI.Client/src/common/services/search.service.js # src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js # src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.controller.js # src/Umbraco.Web.UI.Client/src/views/common/dialogs/linkpicker.html # src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.controller.js # src/Umbraco.Web.UI.Client/src/views/common/dialogs/treepicker.controller.js # src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js # src/Umbraco.Web.UI.Client/src/views/common/overlays/contentpicker/contentpicker.html # src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.controller.js # src/Umbraco.Web.UI.Client/src/views/common/overlays/linkpicker/linkpicker.html # src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.controller.js # src/Umbraco.Web.UI.Client/src/views/common/overlays/treepicker/treepicker.html # src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/rte.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/mediapicker/mediapicker.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/relatedlinks/relatedlinks.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.controller.js # src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html # src/Umbraco.Web.UI/packages.config # src/Umbraco.Web.UI/web.Template.config # src/Umbraco.Web/Editors/ContentController.cs # src/Umbraco.Web/Editors/EntityController.cs # src/Umbraco.Web/Editors/MediaController.cs # src/Umbraco.Web/HtmlHelperRenderExtensions.cs # src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyBasicConverter.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyDisplayConverter.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyDtoConverter.cs # src/Umbraco.Web/Models/Mapping/ContentPropertyModelMapper.cs # src/Umbraco.Web/Models/Mapping/PreValueDisplayResolver.cs # src/Umbraco.Web/Mvc/RenderRouteHandler.cs # src/Umbraco.Web/PropertyEditors/ContentPicker2PropertyEditor.cs # src/Umbraco.Web/PropertyEditors/GridPropertyEditor.cs # src/Umbraco.Web/PropertyEditors/MediaPicker2PropertyEditor.cs # src/Umbraco.Web/PropertyEditors/MultiNodeTreePicker2PropertyEditor.cs # src/Umbraco.Web/PropertyEditors/MultiUrlPickerPropertyEditor.cs # src/Umbraco.Web/PropertyEditors/RelatedLinks2PropertyEditor.cs # src/Umbraco.Web/PropertyEditors/RichTextPreValueEditor.cs # src/Umbraco.Web/Search/UmbracoTreeSearcher.cs # src/Umbraco.Web/Security/UmbracoAntiForgeryAdditionalDataProvider.cs # src/Umbraco.Web/Trees/ContentTreeController.cs # src/Umbraco.Web/Trees/ContentTreeControllerBase.cs # src/Umbraco.Web/Trees/MediaTreeController.cs # src/Umbraco.Web/Trees/TreeControllerBase.cs # src/Umbraco.Web/Trees/TreeQueryStringParameters.cs # src/Umbraco.Web/UmbracoHelper.cs # src/Umbraco.Web/WebApi/Filters/EnsureUserPermissionForContentAttribute.cs # src/Umbraco.Web/WebBootManager.cs # src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseMediaTree.cs # src/Umbraco.Web/umbraco.presentation/umbraco/Trees/BaseTree.cs # src/umbraco.cms/businesslogic/web/Access.cs --- .../tools/Views.Web.config.install.xdt | 10 +- src/Umbraco.Core/Constants-DataTypes.cs | 292 ++++++- src/Umbraco.Core/Models/DataTypeExtensions.cs | 48 ++ src/Umbraco.Core/Models/UserExtensions.cs | 13 - src/Umbraco.Core/Services/IContentService.cs | 3 + .../Plugins/PluginManagerTests.cs | 2 +- .../tree/umbtreesearchbox.directive.js | 7 + .../src/common/resources/content.resource.js | 602 +++++++------- .../src/common/resources/entity.resource.js | 176 ++-- .../src/common/resources/media.resource.js | 752 +++++++++--------- .../common/services/mediahelper.service.js | 38 +- .../src/common/services/search.service.js | 20 +- .../src/common/services/tinymce.service.js | 32 - .../mediapicker/mediapicker.controller.js | 60 +- .../contentpicker/contentpicker.controller.js | 10 +- .../grid/editors/media.controller.js | 15 +- .../multiurlpicker.controller.js | 2 + .../propertyeditors/rte/rte.prevalues.html | 1 + src/Umbraco.Web/Editors/EntityController.cs | 204 ++++- src/Umbraco.Web/HtmlHelperRenderExtensions.cs | 5 +- .../ContentEditing/ContentPropertyBasic.cs | 6 +- src/Umbraco.Web/Mvc/RenderRouteHandler.cs | 2 + src/Umbraco.Web/Search/UmbracoTreeSearcher.cs | 9 +- .../Trees/ContentTreeController.cs | 1 - .../Trees/ContentTreeControllerBase.cs | 50 +- src/Umbraco.Web/Trees/MediaTreeController.cs | 2 +- src/Umbraco.Web/Trees/TreeControllerBase.cs | 1 + .../Trees/TreeQueryStringParameters.cs | 1 + src/Umbraco.Web/UmbracoHelper.cs | 1 + 29 files changed, 1469 insertions(+), 896 deletions(-) diff --git a/build/NuSpecs/tools/Views.Web.config.install.xdt b/build/NuSpecs/tools/Views.Web.config.install.xdt index 4d660301a8..828bb8612f 100644 --- a/build/NuSpecs/tools/Views.Web.config.install.xdt +++ b/build/NuSpecs/tools/Views.Web.config.install.xdt @@ -8,7 +8,7 @@ - + @@ -18,13 +18,13 @@ - + diff --git a/src/Umbraco.Core/Constants-DataTypes.cs b/src/Umbraco.Core/Constants-DataTypes.cs index f2b31be28f..1e9115cde9 100644 --- a/src/Umbraco.Core/Constants-DataTypes.cs +++ b/src/Umbraco.Core/Constants-DataTypes.cs @@ -1,4 +1,6 @@ -namespace Umbraco.Core +using System; + +namespace Umbraco.Core { public static partial class Constants { @@ -22,6 +24,294 @@ public const int DefaultMembersListView = -97; public const int Tags = 1041; + + /// + /// Defines the identifiers for Umbraco data types as constants for easy centralized access/management. + /// + internal static class BuiltInDataTypes + { + + public static class ReservedPreValueKeys + { + public const string IgnoreUserStartNodes = "ignoreUserStartNodes"; + } + + /// + /// Guid for Content Picker as string + /// + public const string ContentPicker = "FD1E0DA5-5606-4862-B679-5D0CF3A52A59"; + + /// + /// Guid for Content Picker + /// + public static readonly Guid ContentPickerGuid = new Guid(ContentPicker); + + + /// + /// Guid for Member Picker as string + /// + public const string MemberPicker = "1EA2E01F-EBD8-4CE1-8D71-6B1149E63548"; + + /// + /// Guid for Member Picker + /// + public static readonly Guid MemberPickerGuid = new Guid(MemberPicker); + + + /// + /// Guid for Media Picker as string + /// + public const string MediaPicker = "135D60E0-64D9-49ED-AB08-893C9BA44AE5"; + + /// + /// Guid for Media Picker + /// + public static readonly Guid MediaPickerGuid = new Guid(MediaPicker); + + + /// + /// Guid for Multiple Media Picker as string + /// + public const string MultipleMediaPicker = "9DBBCBBB-2327-434A-B355-AF1B84E5010A"; + + /// + /// Guid for Multiple Media Picker + /// + public static readonly Guid MultipleMediaPickerGuid = new Guid(MultipleMediaPicker); + + + /// + /// Guid for Related Links as string + /// + public const string RelatedLinks = "B4E3535A-1753-47E2-8568-602CF8CFEE6F"; + + /// + /// Guid for Related Links + /// + public static readonly Guid RelatedLinksGuid = new Guid(RelatedLinks); + + + /// + /// Guid for Member as string + /// + public const string Member = "d59be02f-1df9-4228-aa1e-01917d806cda"; + + /// + /// Guid for Member + /// + public static readonly Guid MemberGuid = new Guid(Member); + + + /// + /// Guid for Image Cropper as string + /// + public const string ImageCropper = "1df9f033-e6d4-451f-b8d2-e0cbc50a836f"; + + /// + /// Guid for Image Cropper + /// + public static readonly Guid ImageCropperGuid = new Guid(ImageCropper); + + + /// + /// Guid for Tags as string + /// + public const string Tags = "b6b73142-b9c1-4bf8-a16d-e1c23320b549"; + + /// + /// Guid for Tags + /// + public static readonly Guid TagsGuid = new Guid(Tags); + + + /// + /// Guid for List View - Content as string + /// + public const string ListViewContent = "C0808DD3-8133-4E4B-8CE8-E2BEA84A96A4"; + + /// + /// Guid for List View - Content + /// + public static readonly Guid ListViewContentGuid = new Guid(ListViewContent); + + + /// + /// Guid for List View - Media as string + /// + public const string ListViewMedia = "3A0156C4-3B8C-4803-BDC1-6871FAA83FFF"; + + /// + /// Guid for List View - Media + /// + public static readonly Guid ListViewMediaGuid = new Guid(ListViewMedia); + + + /// + /// Guid for List View - Members as string + /// + public const string ListViewMembers = "AA2C52A0-CE87-4E65-A47C-7DF09358585D"; + + /// + /// Guid for List View - Members + /// + public static readonly Guid ListViewMembersGuid = new Guid(ListViewMembers); + + + /// + /// Guid for Date Picker with time as string + /// + public const string DatePickerWithTime = "e4d66c0f-b935-4200-81f0-025f7256b89a"; + + /// + /// Guid for Date Picker with time + /// + public static readonly Guid DatePickerWithTimeGuid = new Guid(DatePickerWithTime); + + + /// + /// Guid for Approved Color as string + /// + public const string ApprovedColor = "0225af17-b302-49cb-9176-b9f35cab9c17"; + + /// + /// Guid for Approved Color + /// + public static readonly Guid ApprovedColorGuid = new Guid(ApprovedColor); + + + /// + /// Guid for Dropdown multiple as string + /// + public const string DropdownMultiple = "f38f0ac7-1d27-439c-9f3f-089cd8825a53"; + + /// + /// Guid for Dropdown multiple + /// + public static readonly Guid DropdownMultipleGuid = new Guid(DropdownMultiple); + + + /// + /// Guid for Radiobox as string + /// + public const string Radiobox = "bb5f57c9-ce2b-4bb9-b697-4caca783a805"; + + /// + /// Guid for Radiobox + /// + public static readonly Guid RadioboxGuid = new Guid(Radiobox); + + + /// + /// Guid for Date Picker as string + /// + public const string DatePicker = "5046194e-4237-453c-a547-15db3a07c4e1"; + + /// + /// Guid for Date Picker + /// + public static readonly Guid DatePickerGuid = new Guid(DatePicker); + + + /// + /// Guid for Dropdown as string + /// + public const string Dropdown = "0b6a45e7-44ba-430d-9da5-4e46060b9e03"; + + /// + /// Guid for Dropdown + /// + public static readonly Guid DropdownGuid = new Guid(Dropdown); + + + /// + /// Guid for Checkbox list as string + /// + public const string CheckboxList = "fbaf13a8-4036-41f2-93a3-974f678c312a"; + + /// + /// Guid for Checkbox list + /// + public static readonly Guid CheckboxListGuid = new Guid(CheckboxList); + + + /// + /// Guid for Checkbox as string + /// + public const string Checkbox = "92897bc6-a5f3-4ffe-ae27-f2e7e33dda49"; + + /// + /// Guid for Checkbox + /// + public static readonly Guid CheckboxGuid = new Guid(Checkbox); + + + /// + /// Guid for Numeric as string + /// + public const string Numeric = "2e6d3631-066e-44b8-aec4-96f09099b2b5"; + + /// + /// Guid for Dropdown + /// + public static readonly Guid NumericGuid = new Guid(Numeric); + + + /// + /// Guid for Richtext editor as string + /// + public const string RichtextEditor = "ca90c950-0aff-4e72-b976-a30b1ac57dad"; + + /// + /// Guid for Richtext editor + /// + public static readonly Guid RichtextEditorGuid = new Guid(RichtextEditor); + + + /// + /// Guid for Textstring as string + /// + public const string Textstring = "0cc0eba1-9960-42c9-bf9b-60e150b429ae"; + + /// + /// Guid for Textstring + /// + public static readonly Guid TextstringGuid = new Guid(Textstring); + + + /// + /// Guid for Textarea as string + /// + public const string Textarea = "c6bac0dd-4ab9-45b1-8e30-e4b619ee5da3"; + + /// + /// Guid for Dropdown + /// + public static readonly Guid TextareaGuid = new Guid(Textarea); + + + /// + /// Guid for Upload as string + /// + public const string Upload = "84c6b441-31df-4ffe-b67e-67d5bc3ae65a"; + + /// + /// Guid for Upload + /// + public static readonly Guid UploadGuid = new Guid(Upload); + + + /// + /// Guid for Label as string + /// + public const string Label = "f0bc4bfb-b499-40d6-ba86-058885a5178c"; + + /// + /// Guid for Label + /// + public static readonly Guid LabelGuid = new Guid(Label); + + + } } } } diff --git a/src/Umbraco.Core/Models/DataTypeExtensions.cs b/src/Umbraco.Core/Models/DataTypeExtensions.cs index df8a3caea8..a2603ad3df 100644 --- a/src/Umbraco.Core/Models/DataTypeExtensions.cs +++ b/src/Umbraco.Core/Models/DataTypeExtensions.cs @@ -35,5 +35,53 @@ namespace Umbraco.Core.Models throw new InvalidCastException($"Cannot cast dataType configuration, of type {configuration.GetType().Name}, to {typeof(T).Name}."); } + + private static readonly ISet IdsOfBuildInDataTypes = new HashSet() + { + Constants.DataTypes.ContentPickerGuid, + Constants.DataTypes.MemberPickerGuid, + Constants.DataTypes.MediaPickerGuid, + Constants.DataTypes.MultipleMediaPickerGuid, + Constants.DataTypes.RelatedLinksGuid, + Constants.DataTypes.MemberGuid, + Constants.DataTypes.ImageCropperGuid, + Constants.DataTypes.TagsGuid, + Constants.DataTypes.ListViewContentGuid, + Constants.DataTypes.ListViewMediaGuid, + Constants.DataTypes.ListViewMembersGuid, + Constants.DataTypes.DatePickerWithTimeGuid, + Constants.DataTypes.ApprovedColorGuid, + Constants.DataTypes.DropdownMultipleGuid, + Constants.DataTypes.RadioboxGuid, + Constants.DataTypes.DatePickerGuid, + Constants.DataTypes.DropdownGuid, + Constants.DataTypes.CheckboxListGuid, + Constants.DataTypes.CheckboxGuid, + Constants.DataTypes.NumericGuid, + Constants.DataTypes.RichtextEditorGuid, + Constants.DataTypes.TextstringGuid, + Constants.DataTypes.TextareaGuid, + Constants.DataTypes.UploadGuid, + Constants.DataTypes.LabelGuid, + }; + + /// + /// Returns true if this date type is build-in/default. + /// + /// The data type definition. + /// + internal static bool IsBuildInDataType(this IDataTypeDefinition dataType) + { + return IsBuildInDataType(dataType.Key); + } + + /// + /// Returns true if this date type is build-in/default. + /// + internal static bool IsBuildInDataType(Guid key) + { + return IdsOfBuildInDataTypes.Contains(key); + } + } } diff --git a/src/Umbraco.Core/Models/UserExtensions.cs b/src/Umbraco.Core/Models/UserExtensions.cs index 0f83cf78a4..cf7df4fb86 100644 --- a/src/Umbraco.Core/Models/UserExtensions.cs +++ b/src/Umbraco.Core/Models/UserExtensions.cs @@ -193,19 +193,6 @@ namespace Umbraco.Core.Models return ContentPermissionsHelper.HasPathAccess(entity.Path, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia); } - internal static bool IsInBranchOfStartNode(this IUser user, IUmbracoEntity entity, IEntityService entityService, int recycleBinId, out bool hasPathAccess) - { - switch (recycleBinId) - { - case Constants.System.RecycleBinMedia: - return ContentPermissionsHelper.IsInBranchOfStartNode(entity.Path, user.CalculateMediaStartNodeIds(entityService), user.GetMediaStartNodePaths(entityService), out hasPathAccess); - case Constants.System.RecycleBinContent: - return ContentPermissionsHelper.IsInBranchOfStartNode(entity.Path, user.CalculateContentStartNodeIds(entityService), user.GetContentStartNodePaths(entityService), out hasPathAccess); - default: - throw new NotSupportedException("Path access is only determined on content or media"); - } - } - /// /// Determines whether this user has access to view sensitive data /// diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index 48e577a8f0..164edca1e6 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -526,5 +526,8 @@ namespace Umbraco.Core.Services OperationResult Rollback(int id, int versionId, string culture = "*", int userId = Constants.Security.SuperUserId); #endregion + + IEnumerable GetAnchorValuesFromRTEs(int id); + IEnumerable GetAnchorValuesFromRTEContent(string rteContent); } } diff --git a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs index 809354bd74..44879eae2f 100644 --- a/src/Umbraco.Tests/Plugins/PluginManagerTests.cs +++ b/src/Umbraco.Tests/Plugins/PluginManagerTests.cs @@ -327,7 +327,7 @@ AnotherContentFinder public void Resolves_RestExtensions() { var types = _manager.ResolveRestExtensions(); - Assert.AreEqual(3, types.Count()); + Assert.AreEqual(2, types.Count()); } [Test] diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js index 4ba4cf96bb..3b6fb5f9cd 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbtreesearchbox.directive.js @@ -12,6 +12,7 @@ function treeSearchBox(localizationService, searchService, $q) { searchFromName: "@", showSearch: "@", section: "@", + datatypeId: "@", hideSearchCallback: "=", searchCallback: "=" }, @@ -34,6 +35,7 @@ function treeSearchBox(localizationService, searchService, $q) { scope.showSearch = "false"; } + //used to cancel any request in progress if another one needs to take it's place var canceler = null; @@ -60,6 +62,11 @@ function treeSearchBox(localizationService, searchService, $q) { searchArgs["searchFrom"] = scope.searchFromId; } + //append dataTypeId value if there is one + if (scope.datatypeId) { + searchArgs["dataTypeId"] = scope.datatypeId; + } + searcher(searchArgs).then(function (data) { scope.searchCallback(data); //set back to null so it can be re-created 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 b807a4dc31..d714ea4938 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 @@ -1,27 +1,27 @@ /** - * @ngdoc service - * @name umbraco.resources.contentResource - * @description Handles all transactions of content data - * from the angular application to the Umbraco database, using the Content WebApi controller - * - * all methods returns a resource promise async, so all operations won't complete untill .then() is completed. - * - * @requires $q - * @requires $http - * @requires umbDataFormatter - * @requires umbRequestHelper - * - * ##usage - * To use, simply inject the contentResource into any controller or service that needs it, and make - * sure the umbraco.resources module is accesible - which it should be by default. - * - *
-  *    contentResource.getById(1234)
-  *          .then(function(data) {
-  *              $scope.content = data;
+ * @ngdoc service
+ * @name umbraco.resources.contentResource
+ * @description Handles all transactions of content data
+ * from the angular application to the Umbraco database, using the Content WebApi controller
+ *
+ * all methods returns a resource promise async, so all operations won't complete untill .then() is completed.
+ *
+ * @requires $q
+ * @requires $http
+ * @requires umbDataFormatter
+ * @requires umbRequestHelper
+ *
+ * ##usage
+ * To use, simply inject the contentResource into any controller or service that needs it, and make
+ * sure the umbraco.resources module is accesible - which it should be by default.
+ *
+ * 
+ *    contentResource.getById(1234)
+ *          .then(function(data) {
+ *              $scope.content = data;
   *          });
   * 
- **/ + **/ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { @@ -79,27 +79,27 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#sort - * @methodOf umbraco.resources.contentResource - * - * @description - * Sorts all children below a given parent node id, based on a collection of node-ids - * - * ##usage - *
-          * var ids = [123,34533,2334,23434];
-          * contentResource.sort({ parentId: 1244, sortedIds: ids })
-          *    .then(function() {
-          *        $scope.complete = true;
-          *    });
+         * @ngdoc method
+         * @name umbraco.resources.contentResource#sort
+         * @methodOf umbraco.resources.contentResource
+         *
+         * @description
+         * Sorts all children below a given parent node id, based on a collection of node-ids
+         *
+         * ##usage
+         * 
+         * var ids = [123,34533,2334,23434];
+         * contentResource.sort({ parentId: 1244, sortedIds: ids })
+         *    .then(function() {
+         *        $scope.complete = true;
+         *    });
           * 
- * @param {Object} args arguments object - * @param {Int} args.parentId the ID of the parent node - * @param {Array} options.sortedIds array of node IDs as they should be sorted - * @returns {Promise} resourcePromise object. - * - */ + * @param {Object} args arguments object + * @param {Int} args.parentId the ID of the parent node + * @param {Array} options.sortedIds array of node IDs as they should be sorted + * @returns {Promise} resourcePromise object. + * + */ sort: function (args) { if (!args) { throw "args cannot be null"; @@ -121,28 +121,28 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#move - * @methodOf umbraco.resources.contentResource - * - * @description - * Moves a node underneath a new parentId - * - * ##usage - *
-          * contentResource.move({ parentId: 1244, id: 123 })
-          *    .then(function() {
-          *        alert("node was moved");
-          *    }, function(err){
+         * @ngdoc method
+         * @name umbraco.resources.contentResource#move
+         * @methodOf umbraco.resources.contentResource
+         *
+         * @description
+         * Moves a node underneath a new parentId
+         *
+         * ##usage
+         * 
+         * contentResource.move({ parentId: 1244, id: 123 })
+         *    .then(function() {
+         *        alert("node was moved");
+         *    }, function(err){
           *      alert("node didnt move:" + err.data.Message);
-          *    });
+         *    });
           * 
- * @param {Object} args arguments object - * @param {Int} args.idd the ID of the node to move - * @param {Int} args.parentId the ID of the parent node to move to - * @returns {Promise} resourcePromise object. - * - */ + * @param {Object} args arguments object + * @param {Int} args.idd the ID of the node to move + * @param {Int} args.parentId the ID of the parent node to move to + * @returns {Promise} resourcePromise object. + * + */ move: function (args) { if (!args) { throw "args cannot be null"; @@ -164,29 +164,29 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#copy - * @methodOf umbraco.resources.contentResource - * - * @description - * Copies a node underneath a new parentId - * - * ##usage - *
-          * contentResource.copy({ parentId: 1244, id: 123 })
-          *    .then(function() {
-          *        alert("node was copied");
-          *    }, function(err){
+         * @ngdoc method
+         * @name umbraco.resources.contentResource#copy
+         * @methodOf umbraco.resources.contentResource
+         *
+         * @description
+         * Copies a node underneath a new parentId
+         *
+         * ##usage
+         * 
+         * contentResource.copy({ parentId: 1244, id: 123 })
+         *    .then(function() {
+         *        alert("node was copied");
+         *    }, function(err){
           *      alert("node wasnt copy:" + err.data.Message);
-          *    });
+         *    });
           * 
- * @param {Object} args arguments object - * @param {Int} args.id the ID of the node to copy - * @param {Int} args.parentId the ID of the parent node to copy to - * @param {Boolean} args.relateToOriginal if true, relates the copy to the original through the relation api - * @returns {Promise} resourcePromise object. - * - */ + * @param {Object} args arguments object + * @param {Int} args.id the ID of the node to copy + * @param {Int} args.parentId the ID of the parent node to copy to + * @param {Boolean} args.relateToOriginal if true, relates the copy to the original through the relation api + * @returns {Promise} resourcePromise object. + * + */ copy: function (args) { if (!args) { throw "args cannot be null"; @@ -205,26 +205,26 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method + * @ngdoc method * @name umbraco.resources.contentResource#unpublish - * @methodOf umbraco.resources.contentResource - * - * @description - * Unpublishes a content item with a given Id - * - * ##usage - *
+         * @methodOf umbraco.resources.contentResource
+         *
+         * @description
+         * Unpublishes a content item with a given Id
+         *
+         * ##usage
+         * 
           * contentResource.unpublish(1234)
-          *    .then(function() {
-          *        alert("node was unpulished");
-          *    }, function(err){
+         *    .then(function() {
+         *        alert("node was unpulished");
+         *    }, function(err){
           *      alert("node wasnt unpublished:" + err.data.Message);
-          *    });
+         *    });
           * 
- * @param {Int} id the ID of the node to unpublish - * @returns {Promise} resourcePromise object. - * - */ + * @param {Int} id the ID of the node to unpublish + * @returns {Promise} resourcePromise object. + * + */ unpublish: function (id, cultures) { if (!id) { throw "id cannot be null"; @@ -242,7 +242,7 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { 'Failed to publish content with id ' + id); }, /** - * @ngdoc method + * @ngdoc method * @name umbraco.resources.contentResource#getCultureAndDomains * @methodOf umbraco.resources.contentResource * @@ -281,23 +281,23 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** * @ngdoc method - * @name umbraco.resources.contentResource#emptyRecycleBin - * @methodOf umbraco.resources.contentResource - * - * @description - * Empties the content recycle bin - * - * ##usage - *
-          * contentResource.emptyRecycleBin()
-          *    .then(function() {
-          *        alert('its empty!');
-          *    });
+         * @name umbraco.resources.contentResource#emptyRecycleBin
+         * @methodOf umbraco.resources.contentResource
+         *
+         * @description
+         * Empties the content recycle bin
+         *
+         * ##usage
+         * 
+         * contentResource.emptyRecycleBin()
+         *    .then(function() {
+         *        alert('its empty!');
+         *    });
           * 
* - * @returns {Promise} resourcePromise object. - * - */ + * @returns {Promise} resourcePromise object. + * + */ emptyRecycleBin: function () { return umbRequestHelper.resourcePromise( $http.post( @@ -308,25 +308,25 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#deleteById - * @methodOf umbraco.resources.contentResource - * - * @description - * Deletes a content item with a given id - * - * ##usage - *
-          * contentResource.deleteById(1234)
-          *    .then(function() {
-          *        alert('its gone!');
-          *    });
+         * @ngdoc method
+         * @name umbraco.resources.contentResource#deleteById
+         * @methodOf umbraco.resources.contentResource
+         *
+         * @description
+         * Deletes a content item with a given id
+         *
+         * ##usage
+         * 
+         * contentResource.deleteById(1234)
+         *    .then(function() {
+         *        alert('its gone!');
+         *    });
           * 
* * @param {Int} id id of content item to delete - * @returns {Promise} resourcePromise object. - * - */ + * @returns {Promise} resourcePromise object. + * + */ deleteById: function (id) { return umbRequestHelper.resourcePromise( $http.post( @@ -348,27 +348,27 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#getById - * @methodOf umbraco.resources.contentResource - * - * @description - * Gets a content item with a given id - * - * ##usage - *
-          * contentResource.getById(1234)
-          *    .then(function(content) {
+         * @ngdoc method
+         * @name umbraco.resources.contentResource#getById
+         * @methodOf umbraco.resources.contentResource
+         *
+         * @description
+         * Gets a content item with a given id
+         *
+         * ##usage
+         * 
+         * contentResource.getById(1234)
+         *    .then(function(content) {
           *        var myDoc = content;
-          *        alert('its here!');
-          *    });
+         *        alert('its here!');
+         *    });
           * 
* * @param {Int} id id of content item to return * @param {Int} culture optional culture to retrieve the item in - * @returns {Promise} resourcePromise object containing the content item. - * - */ + * @returns {Promise} resourcePromise object containing the content item. + * + */ getById: function (id) { return umbRequestHelper.resourcePromise( $http.get( @@ -419,26 +419,26 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#getByIds - * @methodOf umbraco.resources.contentResource - * - * @description - * Gets an array of content items, given a collection of ids - * - * ##usage - *
-          * contentResource.getByIds( [1234,2526,28262])
-          *    .then(function(contentArray) {
+         * @ngdoc method
+         * @name umbraco.resources.contentResource#getByIds
+         * @methodOf umbraco.resources.contentResource
+         *
+         * @description
+         * Gets an array of content items, given a collection of ids
+         *
+         * ##usage
+         * 
+         * contentResource.getByIds( [1234,2526,28262])
+         *    .then(function(contentArray) {
           *        var myDoc = contentArray;
-          *        alert('they are here!');
-          *    });
+         *        alert('they are here!');
+         *    });
           * 
* * @param {Array} ids ids of content items to return as an array - * @returns {Promise} resourcePromise object containing the content items array. - * - */ + * @returns {Promise} resourcePromise object containing the content items array. + * + */ getByIds: function (ids) { var idQuery = ""; @@ -464,37 +464,37 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { /** - * @ngdoc method - * @name umbraco.resources.contentResource#getScaffold - * @methodOf umbraco.resources.contentResource + * @ngdoc method + * @name umbraco.resources.contentResource#getScaffold + * @methodOf umbraco.resources.contentResource + * + * @description + * Returns a scaffold of an empty content item, given the id of the content item to place it underneath and the content type alias. * - * @description - * Returns a scaffold of an empty content item, given the id of the content item to place it underneath and the content type alias. - * - * - Parent Id must be provided so umbraco knows where to store the content + * - Parent Id must be provided so umbraco knows where to store the content * - Content Type alias must be provided so umbraco knows which properties to put on the content scaffold * - * The scaffold is used to build editors for content that has not yet been populated with data. + * The scaffold is used to build editors for content that has not yet been populated with data. * - * ##usage - *
-          * contentResource.getScaffold(1234, 'homepage')
-          *    .then(function(scaffold) {
-          *        var myDoc = scaffold;
+         * ##usage
+         * 
+         * contentResource.getScaffold(1234, 'homepage')
+         *    .then(function(scaffold) {
+         *        var myDoc = scaffold;
           *        myDoc.name = "My new document";
-          *
-          *        contentResource.publish(myDoc, true)
-          *            .then(function(content){
-          *                alert("Retrieved, updated and published again");
-          *            });
-          *    });
+         *
+         *        contentResource.publish(myDoc, true)
+         *            .then(function(content){
+         *                alert("Retrieved, updated and published again");
+         *            });
+         *    });
           * 
* - * @param {Int} parentId id of content item to return + * @param {Int} parentId id of content item to return * @param {String} alias contenttype alias to base the scaffold on - * @returns {Promise} resourcePromise object containing the content scaffold. - * - */ + * @returns {Promise} resourcePromise object containing the content scaffold. + * + */ getScaffold: function (parentId, alias) { return umbRequestHelper.resourcePromise( @@ -524,25 +524,25 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#getNiceUrl - * @methodOf umbraco.resources.contentResource - * - * @description - * Returns a url, given a node ID - * - * ##usage - *
-          * contentResource.getNiceUrl(id)
-          *    .then(function(url) {
-          *        alert('its here!');
-          *    });
+         * @ngdoc method
+         * @name umbraco.resources.contentResource#getNiceUrl
+         * @methodOf umbraco.resources.contentResource
+         *
+         * @description
+         * Returns a url, given a node ID
+         *
+         * ##usage
+         * 
+         * contentResource.getNiceUrl(id)
+         *    .then(function(url) {
+         *        alert('its here!');
+         *    });
           * 
* - * @param {Int} id Id of node to return the public url to - * @returns {Promise} resourcePromise object containing the url. - * - */ + * @param {Int} id Id of node to return the public url to + * @returns {Promise} resourcePromise object containing the url. + * + */ getNiceUrl: function (id) { return umbRequestHelper.resourcePromise( $http.get( @@ -554,33 +554,33 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#getChildren - * @methodOf umbraco.resources.contentResource - * - * @description - * Gets children of a content item with a given id - * - * ##usage - *
-          * contentResource.getChildren(1234, {pageSize: 10, pageNumber: 2})
-          *    .then(function(contentArray) {
+         * @ngdoc method
+         * @name umbraco.resources.contentResource#getChildren
+         * @methodOf umbraco.resources.contentResource
+         *
+         * @description
+         * Gets children of a content item with a given id
+         *
+         * ##usage
+         * 
+         * contentResource.getChildren(1234, {pageSize: 10, pageNumber: 2})
+         *    .then(function(contentArray) {
           *        var children = contentArray;
-          *        alert('they are here!');
-          *    });
+         *        alert('they are here!');
+         *    });
           * 
* - * @param {Int} parentid id of content item to return children of - * @param {Object} options optional options object - * @param {Int} options.pageSize if paging data, number of nodes per page, default = 0 - * @param {Int} options.pageNumber if paging data, current page index, default = 0 - * @param {String} options.filter if provided, query will only return those with names matching the filter - * @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending` - * @param {String} options.orderBy property to order items by, default: `SortOrder` + * @param {Int} parentid id of content item to return children of + * @param {Object} options optional options object + * @param {Int} options.pageSize if paging data, number of nodes per page, default = 0 + * @param {Int} options.pageNumber if paging data, current page index, default = 0 + * @param {String} options.filter if provided, query will only return those with names matching the filter + * @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending` + * @param {String} options.orderBy property to order items by, default: `SortOrder` * @param {String} options.cultureName if provided, the results will be for this specific culture/variant - * @returns {Promise} resourcePromise object containing an array of content items. - * - */ + * @returns {Promise} resourcePromise object containing an array of content items. + * + */ getChildren: function (parentId, options) { var defaults = { @@ -651,34 +651,34 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#save - * @methodOf umbraco.resources.contentResource - * - * @description - * Saves changes made to a content item to its current version, if the content item is new, the isNew paramater must be passed to force creation + * @ngdoc method + * @name umbraco.resources.contentResource#save + * @methodOf umbraco.resources.contentResource + * + * @description + * Saves changes made to a content item to its current version, if the content item is new, the isNew paramater must be passed to force creation * if the content item needs to have files attached, they must be provided as the files param and passed separately * * - * ##usage - *
-          * contentResource.getById(1234)
-          *    .then(function(content) {
-          *          content.name = "I want a new name!";
-          *          contentResource.save(content, false)
-          *            .then(function(content){
-          *                alert("Retrieved, updated and saved again");
-          *            });
-          *    });
+         * ##usage
+         * 
+         * contentResource.getById(1234)
+         *    .then(function(content) {
+         *          content.name = "I want a new name!";
+         *          contentResource.save(content, false)
+         *            .then(function(content){
+         *                alert("Retrieved, updated and saved again");
+         *            });
+         *    });
           * 
* - * @param {Object} content The content item object with changes applied + * @param {Object} content The content item object with changes applied * @param {Bool} isNew set to true to create a new item or to update an existing * @param {Array} files collection of files for the document * @param {Bool} showNotifications an option to disable/show notifications (default is true) - * @returns {Promise} resourcePromise object containing the saved content item. - * - */ + * @returns {Promise} resourcePromise object containing the saved content item. + * + */ save: function (content, isNew, files, showNotifications) { var endpoint = umbRequestHelper.getApiUrl( "contentApiBaseUrl", @@ -694,34 +694,34 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#publish - * @methodOf umbraco.resources.contentResource - * - * @description - * Saves and publishes changes made to a content item to a new version, if the content item is new, the isNew paramater must be passed to force creation + * @ngdoc method + * @name umbraco.resources.contentResource#publish + * @methodOf umbraco.resources.contentResource + * + * @description + * Saves and publishes changes made to a content item to a new version, if the content item is new, the isNew paramater must be passed to force creation * if the content item needs to have files attached, they must be provided as the files param and passed separately * * - * ##usage - *
-          * contentResource.getById(1234)
-          *    .then(function(content) {
-          *          content.name = "I want a new name, and be published!";
-          *          contentResource.publish(content, false)
-          *            .then(function(content){
-          *                alert("Retrieved, updated and published again");
-          *            });
-          *    });
+         * ##usage
+         * 
+         * contentResource.getById(1234)
+         *    .then(function(content) {
+         *          content.name = "I want a new name, and be published!";
+         *          contentResource.publish(content, false)
+         *            .then(function(content){
+         *                alert("Retrieved, updated and published again");
+         *            });
+         *    });
           * 
* - * @param {Object} content The content item object with changes applied + * @param {Object} content The content item object with changes applied * @param {Bool} isNew set to true to create a new item or to update an existing * @param {Array} files collection of files for the document * @param {Bool} showNotifications an option to disable/show notifications (default is true) - * @returns {Promise} resourcePromise object containing the saved content item. - * - */ + * @returns {Promise} resourcePromise object containing the saved content item. + * + */ publish: function (content, isNew, files, showNotifications) { var endpoint = umbRequestHelper.getApiUrl( "contentApiBaseUrl", @@ -743,31 +743,31 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#sendToPublish - * @methodOf umbraco.resources.contentResource + * @ngdoc method + * @name umbraco.resources.contentResource#sendToPublish + * @methodOf umbraco.resources.contentResource + * + * @description + * Saves changes made to a content item, and notifies any subscribers about a pending publication * - * @description - * Saves changes made to a content item, and notifies any subscribers about a pending publication - * - * ##usage - *
-          * contentResource.getById(1234)
-          *    .then(function(content) {
-          *          content.name = "I want a new name, and be published!";
-          *          contentResource.sendToPublish(content, false)
-          *            .then(function(content){
-          *                alert("Retrieved, updated and notication send off");
-          *            });
-          *    });
+         * ##usage
+         * 
+         * contentResource.getById(1234)
+         *    .then(function(content) {
+         *          content.name = "I want a new name, and be published!";
+         *          contentResource.sendToPublish(content, false)
+         *            .then(function(content){
+         *                alert("Retrieved, updated and notication send off");
+         *            });
+         *    });
           * 
* - * @param {Object} content The content item object with changes applied + * @param {Object} content The content item object with changes applied * @param {Bool} isNew set to true to create a new item or to update an existing * @param {Array} files collection of files for the document - * @returns {Promise} resourcePromise object containing the saved content item. - * - */ + * @returns {Promise} resourcePromise object containing the saved content item. + * + */ sendToPublish: function (content, isNew, files, showNotifications) { var endpoint = umbRequestHelper.getApiUrl( "contentApiBaseUrl", @@ -797,25 +797,25 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { }, /** - * @ngdoc method - * @name umbraco.resources.contentResource#publishByid - * @methodOf umbraco.resources.contentResource + * @ngdoc method + * @name umbraco.resources.contentResource#publishByid + * @methodOf umbraco.resources.contentResource + * + * @description + * Publishes a content item with a given ID * - * @description - * Publishes a content item with a given ID - * - * ##usage - *
-          * contentResource.publishById(1234)
-          *    .then(function(content) {
-          *        alert("published");
-          *    });
+         * ##usage
+         * 
+         * contentResource.publishById(1234)
+         *    .then(function(content) {
+         *        alert("published");
+         *    });
           * 
* - * @param {Int} id The ID of the conten to publish - * @returns {Promise} resourcePromise object containing the published content item. - * - */ + * @param {Int} id The ID of the conten to publish + * @returns {Promise} resourcePromise object containing the published content item. + * + */ publishById: function (id) { if (!id) { diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js index 753d180880..95bfd1e49b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/entity.resource.js @@ -2,10 +2,10 @@ * @ngdoc service * @name umbraco.resources.entityResource * @description Loads in basic data for all entities - * + * * ##What is an entity? * An entity is a basic **read-only** representation of an Umbraco node. It contains only the most - * basic properties used to display the item in trees, lists and navigation. + * basic properties used to display the item in trees, lists and navigation. * * ##What is the difference between entity and content/media/etc...? * the entity only contains the basic node data, name, id and guid, whereas content @@ -15,7 +15,7 @@ * * ##Entity object types? * You need to specify the type of object you want returned. - * + * * The core object types are: * * - Document @@ -35,7 +35,7 @@ function entityResource($q, $http, umbRequestHelper) { //the factory object returned return { - + getSafeAlias: function (value, camelCase) { if (!value) { @@ -64,10 +64,10 @@ function entityResource($q, $http, umbRequestHelper) { * .then(function(pathArray) { * alert('its here!'); * }); - *
- * + *
+ * * @param {Int} id Id of node to return the public url to - * @param {string} type Object type name + * @param {string} type Object type name * @returns {Promise} resourcePromise object containing the url. * */ @@ -100,8 +100,8 @@ function entityResource($q, $http, umbRequestHelper) { * .then(function(url) { * alert('its here!'); * }); - *
- * + *
+ * * @param {Int} id Id of node to return the public url to * @param {string} type Object type name * @returns {Promise} resourcePromise object containing the url. @@ -135,17 +135,17 @@ function entityResource($q, $http, umbRequestHelper) { * //get media by id * entityResource.getEntityById(0, "Media") * .then(function(ent) { - * var myDoc = ent; + * var myDoc = ent; * alert('its here!'); * }); - *
- * + *
+ * * @param {Int} id id of entity to return - * @param {string} type Object type name + * @param {string} type Object type name * @returns {Promise} resourcePromise object containing the entity. * */ - getById: function (id, type) { + getById: function (id, type) { if (id === -1 || id === "-1") { return null; @@ -160,6 +160,40 @@ function entityResource($q, $http, umbRequestHelper) { 'Failed to retrieve entity data for id ' + id); }, + + getUrlAndAnchors: function (id) { + + if (id === -1 || id === "-1") { + return null; + } + + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "entityApiBaseUrl", + "GetUrlAndAnchors", + { id: id })), + 'Failed to retrieve url and anchors data for id ' + id); + }, + + + getAnchors: function (rteContent) { + + if (!rteContent || rteContent.length === 0) { + return []; + } + + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "entityApiBaseUrl", + 'GetAnchors'), + { + rteContent: rteContent + }), + 'Failed to anchors data for rte content ' + rteContent); + }, + /** * @ngdoc method * @name umbraco.resources.entityResource#getByIds @@ -173,18 +207,18 @@ function entityResource($q, $http, umbRequestHelper) { * //Get templates for ids * entityResource.getEntitiesByIds( [1234,2526,28262], "Template") * .then(function(templateArray) { - * var myDoc = contentArray; + * var myDoc = contentArray; * alert('they are here!'); * }); - *
- * + *
+ * * @param {Array} ids ids of entities to return as an array - * @param {string} type type name + * @param {string} type type name * @returns {Promise} resourcePromise object containing the entity array. * */ getByIds: function (ids, type) { - + var query = "type=" + type; return umbRequestHelper.resourcePromise( @@ -212,14 +246,14 @@ function entityResource($q, $http, umbRequestHelper) { * //get content by xpath * entityResource.getByQuery("$current", -1, "Document") * .then(function(ent) { - * var myDoc = ent; + * var myDoc = ent; * alert('its here!'); * }); - *
- * + *
+ * * @param {string} query xpath to use in query * @param {Int} nodeContextId id id to start from - * @param {string} type Object type name + * @param {string} type Object type name * @returns {Promise} resourcePromise object containing the entity. * */ @@ -247,12 +281,12 @@ function entityResource($q, $http, umbRequestHelper) { * //Only return media * entityResource.getAll("Media") * .then(function(ent) { - * var myDoc = ent; + * var myDoc = ent; * alert('its here!'); * }); - *
- * - * @param {string} type Object type name + *
+ * + * @param {string} type Object type name * @param {string} postFilter optional filter expression which will execute a dynamic where clause on the server * @returns {Promise} resourcePromise object containing the entity. * @@ -277,24 +311,33 @@ function entityResource($q, $http, umbRequestHelper) { * * @description * Gets ancestor entities for a given item - * - * + * + * * @param {string} type Object type name * @param {string} culture Culture * @returns {Promise} resourcePromise object containing the entity. * */ - getAncestors: function (id, type, culture) { + getAncestors: function (id, type, culture, options) { if (culture === undefined) culture = ""; + var args = [ + { id: id }, + { type: type }, + { culture: culture} + ]; + if (options && options.dataTypeId) { + args.push({ dataTypeId: options.dataTypeId }); + } + return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "entityApiBaseUrl", "GetAncestors", - [{ id: id }, { type: type }, { culture: culture }])), - 'Failed to retrieve ancestor data for id ' + id); + args)), + 'Failed to retrieve ancestor data for id ' + id); }, - + /** * @ngdoc method * @name umbraco.resources.entityResource#getChildren @@ -302,20 +345,25 @@ function entityResource($q, $http, umbRequestHelper) { * * @description * Gets children entities for a given item - * + * * @param {Int} parentid id of content item to return children of - * @param {string} type Object type name + * @param {string} type Object type name * @returns {Promise} resourcePromise object containing the entity. * */ - getChildren: function (id, type) { + getChildren: function (id, type, options) { + + var args = [{ id: id }, { type: type }]; + if (options && options.dataTypeId) { + args.push({ dataTypeId: options.dataTypeId }); + } return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( "entityApiBaseUrl", "GetChildren", - [{ id: id }, { type: type }])), + args)), 'Failed to retrieve child data for id ' + id); }, @@ -331,11 +379,11 @@ function entityResource($q, $http, umbRequestHelper) { *
           * entityResource.getPagedChildren(1234, "Content", {pageSize: 10, pageNumber: 2})
           *    .then(function(contentArray) {
-          *        var children = contentArray; 
+          *        var children = contentArray;
           *        alert('they are here!');
           *    });
-          * 
- * + *
+ * * @param {Int} parentid id of content item to return children of * @param {string} type Object type name * @param {Object} options optional options object @@ -383,7 +431,8 @@ function entityResource($q, $http, umbRequestHelper) { pageSize: options.pageSize, orderBy: options.orderBy, orderDirection: options.orderDirection, - filter: encodeURIComponent(options.filter) + filter: encodeURIComponent(options.filter), + dataTypeId: options.dataTypeId } )), 'Failed to retrieve child data for id ' + parentId); @@ -401,11 +450,11 @@ function entityResource($q, $http, umbRequestHelper) { *
           * entityResource.getPagedDescendants(1234, "Document", {pageSize: 10, pageNumber: 2})
           *    .then(function(contentArray) {
-          *        var children = contentArray; 
+          *        var children = contentArray;
           *        alert('they are here!');
           *    });
-          * 
- * + *
+ * * @param {Int} parentid id of content item to return descendants of * @param {string} type Object type name * @param {Object} options optional options object @@ -424,7 +473,8 @@ function entityResource($q, $http, umbRequestHelper) { pageNumber: 1, filter: '', orderDirection: "Ascending", - orderBy: "SortOrder" + orderBy: "SortOrder", + dataTypeId: null }; if (options === undefined) { options = {}; @@ -453,12 +503,14 @@ function entityResource($q, $http, umbRequestHelper) { pageSize: options.pageSize, orderBy: options.orderBy, orderDirection: options.orderDirection, - filter: encodeURIComponent(options.filter) + filter: encodeURIComponent(options.filter), + dataTypeId: options.dataTypeId } )), 'Failed to retrieve child data for id ' + parentId); }, - + + /** * @ngdoc method * @name umbraco.resources.entityResource#search @@ -471,23 +523,27 @@ function entityResource($q, $http, umbRequestHelper) { *
          * entityResource.search("news", "Media")
          *    .then(function(mediaArray) {
-         *        var myDoc = mediaArray; 
+         *        var myDoc = mediaArray;
          *        alert('they are here!');
          *    });
-         * 
- * - * @param {String} Query search query - * @param {String} Type type of conten to search + *
+ * + * @param {String} Query search query + * @param {String} Type type of conten to search * @returns {Promise} resourcePromise object containing the entity array. * */ - search: function (query, type, searchFrom, canceler) { + search: function (query, type, searchFrom, canceler, dataTypeId) { var args = [{ query: query }, { type: type }]; if (searchFrom) { args.push({ searchFrom: searchFrom }); } + if (dataTypeId) { + args.push({ dataTypeId: dataTypeId }); + } + var httpConfig = {}; if (canceler) { httpConfig["timeout"] = canceler; @@ -502,7 +558,7 @@ function entityResource($q, $http, umbRequestHelper) { httpConfig), 'Failed to retrieve entity data for query ' + query); }, - + /** * @ngdoc method @@ -516,12 +572,12 @@ function entityResource($q, $http, umbRequestHelper) { *
          * entityResource.searchAll("bob")
          *    .then(function(array) {
-         *        var myDoc = array; 
+         *        var myDoc = array;
          *        alert('they are here!');
          *    });
-         * 
- * - * @param {String} Query search query + *
+ * + * @param {String} Query search query * @returns {Promise} resourcePromise object containing the entity array. * */ @@ -541,7 +597,9 @@ function entityResource($q, $http, umbRequestHelper) { httpConfig), 'Failed to retrieve entity data for query ' + query); } - + + + }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js index 1d6d5171a1..ca7700c188 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js @@ -1,16 +1,16 @@ /** - * @ngdoc service - * @name umbraco.resources.mediaResource - * @description Loads in data for media - **/ + * @ngdoc service + * @name umbraco.resources.mediaResource + * @description Loads in data for media + **/ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { /** internal method process the saving of data and post processing the result */ function saveMediaItem(content, action, files) { return umbRequestHelper.postSaveContent({ restApiUrl: umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "PostSave"), + "mediaApiBaseUrl", + "PostSave"), content: content, action: action, files: files, @@ -24,35 +24,35 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { getRecycleBin: function () { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "GetRecycleBin")), - 'Failed to retrieve data for media recycle bin'); + $http.get( + umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "GetRecycleBin")), + 'Failed to retrieve data for media recycle bin'); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#sort - * @methodOf umbraco.resources.mediaResource - * - * @description - * Sorts all children below a given parent node id, based on a collection of node-ids - * - * ##usage - *
-          * var ids = [123,34533,2334,23434];
-          * mediaResource.sort({ sortedIds: ids })
-          *    .then(function() {
-          *        $scope.complete = true;
-          *    });
-          * 
- * @param {Object} args arguments object - * @param {Int} args.parentId the ID of the parent node - * @param {Array} options.sortedIds array of node IDs as they should be sorted - * @returns {Promise} resourcePromise object. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#sort + * @methodOf umbraco.resources.mediaResource + * + * @description + * Sorts all children below a given parent node id, based on a collection of node-ids + * + * ##usage + *
+         * var ids = [123,34533,2334,23434];
+         * mediaResource.sort({ sortedIds: ids })
+         *    .then(function() {
+         *        $scope.complete = true;
+         *    });
+         * 
+ * @param {Object} args arguments object + * @param {Int} args.parentId the ID of the parent node + * @param {Array} options.sortedIds array of node IDs as they should be sorted + * @returns {Promise} resourcePromise object. + * + */ sort: function (args) { if (!args) { throw "args cannot be null"; @@ -65,37 +65,37 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { } return umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostSort"), - { - parentId: args.parentId, - idSortOrder: args.sortedIds - }), - 'Failed to sort media'); + $http.post(umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostSort"), + { + parentId: args.parentId, + idSortOrder: args.sortedIds + }), + 'Failed to sort media'); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#move - * @methodOf umbraco.resources.mediaResource - * - * @description - * Moves a node underneath a new parentId - * - * ##usage - *
-          * mediaResource.move({ parentId: 1244, id: 123 })
-          *    .then(function() {
-          *        alert("node was moved");
-          *    }, function(err){
-          *      alert("node didnt move:" + err.data.Message);
-          *    });
-          * 
- * @param {Object} args arguments object - * @param {Int} args.idd the ID of the node to move - * @param {Int} args.parentId the ID of the parent node to move to - * @returns {Promise} resourcePromise object. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#move + * @methodOf umbraco.resources.mediaResource + * + * @description + * Moves a node underneath a new parentId + * + * ##usage + *
+         * mediaResource.move({ parentId: 1244, id: 123 })
+         *    .then(function() {
+         *        alert("node was moved");
+         *    }, function(err){
+         *      alert("node didnt move:" + err.data.Message);
+         *    });
+         * 
+ * @param {Object} args arguments object + * @param {Int} args.idd the ID of the node to move + * @param {Int} args.parentId the ID of the parent node to move to + * @returns {Promise} resourcePromise object. + * + */ move: function (args) { if (!args) { throw "args cannot be null"; @@ -108,121 +108,121 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { } return umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostMove"), - { - parentId: args.parentId, - id: args.id + $http.post(umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostMove"), + { + parentId: args.parentId, + id: args.id }, {responseType: 'text'}), - { - error: function(data){ - var errorMsg = 'Failed to move media'; - if (data.id !== undefined && data.parentId !== undefined) { - if (data.id === data.parentId) { - errorMsg = 'Media can\'t be moved into itself'; - } - } - else if (data.notifications !== undefined) { - if (data.notifications.length > 0) { - if (data.notifications[0].header.length > 0) { - errorMsg = data.notifications[0].header; - } - if (data.notifications[0].message.length > 0) { - errorMsg = errorMsg + ": " + data.notifications[0].message; - } + { + error: function(data){ + var errorMsg = 'Failed to move media'; + if (data.id !== undefined && data.parentId !== undefined) { + if (data.id === data.parentId) { + errorMsg = 'Media can\'t be moved into itself'; + } + } + else if (data.notifications !== undefined) { + if (data.notifications.length > 0) { + if (data.notifications[0].header.length > 0) { + errorMsg = data.notifications[0].header; + } + if (data.notifications[0].message.length > 0) { + errorMsg = errorMsg + ": " + data.notifications[0].message; } } + } - return { - errorMsg: errorMsg - }; - } - }); + return { + errorMsg: errorMsg + }; + } + }); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#getById - * @methodOf umbraco.resources.mediaResource - * - * @description - * Gets a media item with a given id - * - * ##usage - *
-          * mediaResource.getById(1234)
-          *    .then(function(media) {
-          *        var myMedia = media;
-          *        alert('its here!');
-          *    });
-          * 
- * - * @param {Int} id id of media item to return - * @returns {Promise} resourcePromise object containing the media item. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#getById + * @methodOf umbraco.resources.mediaResource + * + * @description + * Gets a media item with a given id + * + * ##usage + *
+         * mediaResource.getById(1234)
+         *    .then(function(media) {
+         *        var myMedia = media;
+         *        alert('its here!');
+         *    });
+         * 
+ * + * @param {Int} id id of media item to return + * @returns {Promise} resourcePromise object containing the media item. + * + */ getById: function (id) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "GetById", - [{ id: id }])), - 'Failed to retrieve data for media id ' + id); + $http.get( + umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "GetById", + [{ id: id }])), + 'Failed to retrieve data for media id ' + id); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#deleteById - * @methodOf umbraco.resources.mediaResource - * - * @description - * Deletes a media item with a given id - * - * ##usage - *
-          * mediaResource.deleteById(1234)
-          *    .then(function() {
-          *        alert('its gone!');
-          *    });
-          * 
- * - * @param {Int} id id of media item to delete - * @returns {Promise} resourcePromise object. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#deleteById + * @methodOf umbraco.resources.mediaResource + * + * @description + * Deletes a media item with a given id + * + * ##usage + *
+         * mediaResource.deleteById(1234)
+         *    .then(function() {
+         *        alert('its gone!');
+         *    });
+         * 
+ * + * @param {Int} id id of media item to delete + * @returns {Promise} resourcePromise object. + * + */ deleteById: function (id) { return umbRequestHelper.resourcePromise( - $http.post( - umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "DeleteById", - [{ id: id }])), - 'Failed to delete item ' + id); + $http.post( + umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "DeleteById", + [{ id: id }])), + 'Failed to delete item ' + id); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#getByIds - * @methodOf umbraco.resources.mediaResource - * - * @description - * Gets an array of media items, given a collection of ids - * - * ##usage - *
-          * mediaResource.getByIds( [1234,2526,28262])
-          *    .then(function(mediaArray) {
-          *        var myDoc = contentArray;
-          *        alert('they are here!');
-          *    });
-          * 
- * - * @param {Array} ids ids of media items to return as an array - * @returns {Promise} resourcePromise object containing the media items array. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#getByIds + * @methodOf umbraco.resources.mediaResource + * + * @description + * Gets an array of media items, given a collection of ids + * + * ##usage + *
+         * mediaResource.getByIds( [1234,2526,28262])
+         *    .then(function(mediaArray) {
+         *        var myDoc = contentArray;
+         *        alert('they are here!');
+         *    });
+         * 
+ * + * @param {Array} ids ids of media items to return as an array + * @returns {Promise} resourcePromise object containing the media items array. + * + */ getByIds: function (ids) { var idQuery = ""; @@ -231,96 +231,96 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { }); return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "GetByIds", - idQuery)), - 'Failed to retrieve data for media ids ' + ids); + $http.get( + umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "GetByIds", + idQuery)), + 'Failed to retrieve data for media ids ' + ids); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#getScaffold - * @methodOf umbraco.resources.mediaResource - * - * @description - * Returns a scaffold of an empty media item, given the id of the media item to place it underneath and the media type alias. - * - * - Parent Id must be provided so umbraco knows where to store the media - * - Media Type alias must be provided so umbraco knows which properties to put on the media scaffold - * - * The scaffold is used to build editors for media that has not yet been populated with data. - * - * ##usage - *
-          * mediaResource.getScaffold(1234, 'folder')
-          *    .then(function(scaffold) {
-          *        var myDoc = scaffold;
-          *        myDoc.name = "My new media item";
-          *
-          *        mediaResource.save(myDoc, true)
-          *            .then(function(media){
-          *                alert("Retrieved, updated and saved again");
-          *            });
-          *    });
-          * 
- * - * @param {Int} parentId id of media item to return - * @param {String} alias mediatype alias to base the scaffold on - * @returns {Promise} resourcePromise object containing the media scaffold. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#getScaffold + * @methodOf umbraco.resources.mediaResource + * + * @description + * Returns a scaffold of an empty media item, given the id of the media item to place it underneath and the media type alias. + * + * - Parent Id must be provided so umbraco knows where to store the media + * - Media Type alias must be provided so umbraco knows which properties to put on the media scaffold + * + * The scaffold is used to build editors for media that has not yet been populated with data. + * + * ##usage + *
+         * mediaResource.getScaffold(1234, 'folder')
+         *    .then(function(scaffold) {
+         *        var myDoc = scaffold;
+         *        myDoc.name = "My new media item";
+         *
+         *        mediaResource.save(myDoc, true)
+         *            .then(function(media){
+         *                alert("Retrieved, updated and saved again");
+         *            });
+         *    });
+         * 
+ * + * @param {Int} parentId id of media item to return + * @param {String} alias mediatype alias to base the scaffold on + * @returns {Promise} resourcePromise object containing the media scaffold. + * + */ getScaffold: function (parentId, alias) { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "GetEmpty", - [{ contentTypeAlias: alias }, { parentId: parentId }])), - 'Failed to retrieve data for empty media item type ' + alias); + $http.get( + umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "GetEmpty", + [{ contentTypeAlias: alias }, { parentId: parentId }])), + 'Failed to retrieve data for empty media item type ' + alias); }, rootMedia: function () { return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "GetRootMedia")), - 'Failed to retrieve data for root media'); + $http.get( + umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "GetRootMedia")), + 'Failed to retrieve data for root media'); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#getChildren - * @methodOf umbraco.resources.mediaResource - * - * @description - * Gets children of a media item with a given id - * - * ##usage - *
-          * mediaResource.getChildren(1234, {pageSize: 10, pageNumber: 2})
-          *    .then(function(contentArray) {
-          *        var children = contentArray;
-          *        alert('they are here!');
-          *    });
-          * 
- * - * @param {Int} parentid id of content item to return children of - * @param {Object} options optional options object - * @param {Int} options.pageSize if paging data, number of nodes per page, default = 0 - * @param {Int} options.pageNumber if paging data, current page index, default = 0 - * @param {String} options.filter if provided, query will only return those with names matching the filter - * @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending` - * @param {String} options.orderBy property to order items by, default: `SortOrder` - * @returns {Promise} resourcePromise object containing an array of content items. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#getChildren + * @methodOf umbraco.resources.mediaResource + * + * @description + * Gets children of a media item with a given id + * + * ##usage + *
+         * mediaResource.getChildren(1234, {pageSize: 10, pageNumber: 2})
+         *    .then(function(contentArray) {
+         *        var children = contentArray;
+         *        alert('they are here!');
+         *    });
+         * 
+ * + * @param {Int} parentid id of content item to return children of + * @param {Object} options optional options object + * @param {Int} options.pageSize if paging data, number of nodes per page, default = 0 + * @param {Int} options.pageNumber if paging data, current page index, default = 0 + * @param {String} options.filter if provided, query will only return those with names matching the filter + * @param {String} options.orderDirection can be `Ascending` or `Descending` - Default: `Ascending` + * @param {String} options.orderBy property to order items by, default: `SortOrder` + * @returns {Promise} resourcePromise object containing an array of content items. + * + */ getChildren: function (parentId, options) { var defaults = { @@ -361,111 +361,111 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { } return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "GetChildren", - [ - { id: parentId }, - { pageNumber: options.pageNumber }, - { pageSize: options.pageSize }, - { orderBy: options.orderBy }, - { orderDirection: options.orderDirection }, - { orderBySystemField: toBool(options.orderBySystemField) }, - { filter: options.filter } - ])), - 'Failed to retrieve children for media item ' + parentId); + $http.get( + umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "GetChildren", + [ + { id: parentId }, + { pageNumber: options.pageNumber }, + { pageSize: options.pageSize }, + { orderBy: options.orderBy }, + { orderDirection: options.orderDirection }, + { orderBySystemField: toBool(options.orderBySystemField) }, + { filter: options.filter } + ])), + 'Failed to retrieve children for media item ' + parentId); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#save - * @methodOf umbraco.resources.mediaResource - * - * @description - * Saves changes made to a media item, if the media item is new, the isNew paramater must be passed to force creation - * if the media item needs to have files attached, they must be provided as the files param and passed separately - * - * - * ##usage - *
-          * mediaResource.getById(1234)
-          *    .then(function(media) {
-          *          media.name = "I want a new name!";
-          *          mediaResource.save(media, false)
-          *            .then(function(media){
-          *                alert("Retrieved, updated and saved again");
-          *            });
-          *    });
-          * 
- * - * @param {Object} media The media item object with changes applied - * @param {Bool} isNew set to true to create a new item or to update an existing - * @param {Array} files collection of files for the media item - * @returns {Promise} resourcePromise object containing the saved media item. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#save + * @methodOf umbraco.resources.mediaResource + * + * @description + * Saves changes made to a media item, if the media item is new, the isNew paramater must be passed to force creation + * if the media item needs to have files attached, they must be provided as the files param and passed separately + * + * + * ##usage + *
+         * mediaResource.getById(1234)
+         *    .then(function(media) {
+         *          media.name = "I want a new name!";
+         *          mediaResource.save(media, false)
+         *            .then(function(media){
+         *                alert("Retrieved, updated and saved again");
+         *            });
+         *    });
+         * 
+ * + * @param {Object} media The media item object with changes applied + * @param {Bool} isNew set to true to create a new item or to update an existing + * @param {Array} files collection of files for the media item + * @returns {Promise} resourcePromise object containing the saved media item. + * + */ save: function (media, isNew, files) { return saveMediaItem(media, "save" + (isNew ? "New" : ""), files); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#addFolder - * @methodOf umbraco.resources.mediaResource - * - * @description - * Shorthand for adding a media item of the type "Folder" under a given parent ID - * - * ##usage - *
-          * mediaResource.addFolder("My gallery", 1234)
-          *    .then(function(folder) {
-          *        alert('New folder');
-          *    });
-          * 
- * - * @param {string} name Name of the folder to create - * @param {int} parentId Id of the media item to create the folder underneath - * @returns {Promise} resourcePromise object. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#addFolder + * @methodOf umbraco.resources.mediaResource + * + * @description + * Shorthand for adding a media item of the type "Folder" under a given parent ID + * + * ##usage + *
+         * mediaResource.addFolder("My gallery", 1234)
+         *    .then(function(folder) {
+         *        alert('New folder');
+         *    });
+         * 
+ * + * @param {string} name Name of the folder to create + * @param {int} parentId Id of the media item to create the folder underneath + * @returns {Promise} resourcePromise object. + * + */ addFolder: function (name, parentId) { return umbRequestHelper.resourcePromise( - $http.post(umbRequestHelper - .getApiUrl("mediaApiBaseUrl", "PostAddFolder"), - { - name: name, - parentId: parentId - }), - 'Failed to add folder'); + $http.post(umbRequestHelper + .getApiUrl("mediaApiBaseUrl", "PostAddFolder"), + { + name: name, + parentId: parentId + }), + 'Failed to add folder'); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#getChildFolders - * @methodOf umbraco.resources.mediaResource - * - * @description - * Retrieves all media children with types used as folders. - * Uses the convention of looking for media items with mediaTypes ending in - * *Folder so will match "Folder", "bannerFolder", "secureFolder" etc, - * + * @ngdoc method + * @name umbraco.resources.mediaResource#getChildFolders + * @methodOf umbraco.resources.mediaResource + * + * @description + * Retrieves all media children with types used as folders. + * Uses the convention of looking for media items with mediaTypes ending in + * *Folder so will match "Folder", "bannerFolder", "secureFolder" etc, + * * NOTE: This will return a page of max 500 folders, if more is required it needs to be paged * and then folders are in the .items property of the returned promise data - * - * ##usage - *
-          * mediaResource.getChildFolders(1234)
+         *
+         * ##usage
+         * 
+         * mediaResource.getChildFolders(1234)
           *    .then(function(page) {
-          *        alert('folders');
-          *    });
-          * 
- * - * @param {int} parentId Id of the media item to query for child folders - * @returns {Promise} resourcePromise object. - * - */ + * alert('folders'); + * }); + *
+ * + * @param {int} parentId Id of the media item to query for child folders + * @returns {Promise} resourcePromise object. + * + */ getChildFolders: function (parentId) { if (!parentId) { parentId = -1; @@ -473,69 +473,69 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { //NOTE: This will return a max of 500 folders, if more is required it needs to be paged return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "GetChildFolders", - { + $http.get( + umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "GetChildFolders", + { id: parentId, pageNumber: 1, pageSize: 500 - })), + })), 'Failed to retrieve child folders for media item ' + parentId); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#emptyRecycleBin - * @methodOf umbraco.resources.mediaResource - * - * @description - * Empties the media recycle bin - * - * ##usage - *
-          * mediaResource.emptyRecycleBin()
-          *    .then(function() {
-          *        alert('its empty!');
-          *    });
-          * 
- * - * @returns {Promise} resourcePromise object. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#emptyRecycleBin + * @methodOf umbraco.resources.mediaResource + * + * @description + * Empties the media recycle bin + * + * ##usage + *
+         * mediaResource.emptyRecycleBin()
+         *    .then(function() {
+         *        alert('its empty!');
+         *    });
+         * 
+ * + * @returns {Promise} resourcePromise object. + * + */ emptyRecycleBin: function () { return umbRequestHelper.resourcePromise( - $http.post( - umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "EmptyRecycleBin")), - 'Failed to empty the recycle bin'); + $http.post( + umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "EmptyRecycleBin")), + 'Failed to empty the recycle bin'); }, /** - * @ngdoc method - * @name umbraco.resources.mediaResource#search - * @methodOf umbraco.resources.mediaResource - * - * @description - * Paginated search for media items starting on the supplied nodeId - * - * ##usage - *
-          * mediaResource.search("my search", 1, 100, -1)
-          *    .then(function(searchResult) {
-          *        alert('it's here!');
-          *    });
-          * 
- * - * @param {string} query The search query - * @param {int} pageNumber The page number - * @param {int} pageSize The number of media items on a page - * @param {int} searchFrom NodeId to search from (-1 for root) - * @returns {Promise} resourcePromise object. - * - */ + * @ngdoc method + * @name umbraco.resources.mediaResource#search + * @methodOf umbraco.resources.mediaResource + * + * @description + * Paginated search for media items starting on the supplied nodeId + * + * ##usage + *
+         * mediaResource.search("my search", 1, 100, -1)
+         *    .then(function(searchResult) {
+         *        alert('it's here!');
+         *    });
+         * 
+ * + * @param {string} query The search query + * @param {int} pageNumber The page number + * @param {int} pageSize The number of media items on a page + * @param {int} searchFrom NodeId to search from (-1 for root) + * @returns {Promise} resourcePromise object. + * + */ search: function (query, pageNumber, pageSize, searchFrom) { var args = [ @@ -546,12 +546,12 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) { ]; return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "mediaApiBaseUrl", - "Search", - args)), - 'Failed to retrieve media items for search: ' + query); + $http.get( + umbRequestHelper.getApiUrl( + "mediaApiBaseUrl", + "Search", + args)), + 'Failed to retrieve media items for search: ' + query); } }; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js index d85a59d836..9350af1c47 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/mediahelper.service.js @@ -3,7 +3,7 @@ * @name umbraco.services.mediaHelper * @description A helper object used for dealing with media items **/ -function mediaHelper(umbRequestHelper) { +function mediaHelper(umbRequestHelper, $log) { //container of fileresolvers var _mediaFileResolvers = {}; @@ -13,11 +13,11 @@ function mediaHelper(umbRequestHelper) { * @ngdoc function * @name umbraco.services.mediaHelper#getImagePropertyValue * @methodOf umbraco.services.mediaHelper - * @function + * @function * * @description * Returns the file path associated with the media property if there is one - * + * * @param {object} options Options object * @param {object} options.mediaModel The media object to retrieve the image path from * @param {object} options.imageOnly Optional, if true then will only return a path if the media item is an image @@ -80,11 +80,11 @@ function mediaHelper(umbRequestHelper) { * @ngdoc function * @name umbraco.services.mediaHelper#getImagePropertyValue * @methodOf umbraco.services.mediaHelper - * @function + * @function * * @description * Returns the actual image path associated with the image property if there is one - * + * * @param {object} options Options object * @param {object} options.imageModel The media object to retrieve the image path from */ @@ -104,11 +104,11 @@ function mediaHelper(umbRequestHelper) { * @ngdoc function * @name umbraco.services.mediaHelper#getThumbnail * @methodOf umbraco.services.mediaHelper - * @function + * @function * * @description * formats the display model used to display the content to the model used to save the content - * + * * @param {object} options Options object * @param {object} options.imageModel The media object to retrieve the image path from */ @@ -133,18 +133,20 @@ function mediaHelper(umbRequestHelper) { * @ngdoc function * @name umbraco.services.mediaHelper#resolveFileFromEntity * @methodOf umbraco.services.mediaHelper - * @function + * @function * * @description * Gets the media file url for a media entity returned with the entityResource - * + * * @param {object} mediaEntity A media Entity returned from the entityResource * @param {boolean} thumbnail Whether to return the thumbnail url or normal url */ resolveFileFromEntity: function (mediaEntity, thumbnail) { if (!angular.isObject(mediaEntity.metaData) || !mediaEntity.metaData.MediaPath) { - throw "Cannot resolve the file url from the mediaEntity, it does not contain the required metaData"; + //don't throw since this image legitimately might not contain a media path, but output a warning + $log.warn("Cannot resolve the file url from the mediaEntity, it does not contain the required metaData"); + return null; } if (thumbnail) { @@ -164,11 +166,11 @@ function mediaHelper(umbRequestHelper) { * @ngdoc function * @name umbraco.services.mediaHelper#resolveFile * @methodOf umbraco.services.mediaHelper - * @function + * @function * * @description * Gets the media file url for a media object returned with the mediaResource - * + * * @param {object} mediaEntity A media Entity returned from the entityResource * @param {boolean} thumbnail Whether to return the thumbnail url or normal url */ @@ -240,11 +242,11 @@ function mediaHelper(umbRequestHelper) { * @ngdoc function * @name umbraco.services.mediaHelper#scaleToMaxSize * @methodOf umbraco.services.mediaHelper - * @function + * @function * * @description * Finds the corrct max width and max height, given maximum dimensions and keeping aspect ratios - * + * * @param {number} maxSize Maximum width & height * @param {number} width Current width * @param {number} height Current height @@ -283,11 +285,11 @@ function mediaHelper(umbRequestHelper) { * @ngdoc function * @name umbraco.services.mediaHelper#getThumbnailFromPath * @methodOf umbraco.services.mediaHelper - * @function + * @function * * @description * Returns the path to the thumbnail version of a given media library image path - * + * * @param {string} imagePath Image path, ex: /media/1234/my-image.jpg */ getThumbnailFromPath: function (imagePath) { @@ -310,11 +312,11 @@ function mediaHelper(umbRequestHelper) { * @ngdoc function * @name umbraco.services.mediaHelper#detectIfImageByExtension * @methodOf umbraco.services.mediaHelper - * @function + * @function * * @description * Returns true/false, indicating if the given path has an allowed image extension - * + * * @param {string} imagePath Image path, ex: /media/1234/my-image.jpg */ detectIfImageByExtension: function (imagePath) { 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 04c431767c..cdba3647a3 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 @@ -2,7 +2,7 @@ * @ngdoc service * @name umbraco.services.searchService * - * + * * @description * Service for handling the main application search, can currently search content, media and members * @@ -15,10 +15,10 @@ * angular.forEach(results, function(result){ * //returns: * {name: "name", id: 1234, menuUrl: "url", editorPath: "url", metaData: {}, subtitle: "/path/etc" } - * }) - * var result = - * }) - * + * }) + * var result = + * }) + * */ angular.module('umbraco.services') .factory('searchService', function ($q, $log, entityResource, contentResource, umbRequestHelper, $injector, searchResultFormatter) { @@ -67,7 +67,7 @@ angular.module('umbraco.services') throw "args.term is required"; } - return entityResource.search(args.term, "Document", args.searchFrom, args.canceler).then(function (data) { + return entityResource.search(args.term, "Document", args.searchFrom, args.canceler, args.dataTypeId).then(function (data) { _.each(data, function (item) { searchResultFormatter.configureContentResult(item); }); @@ -92,7 +92,7 @@ angular.module('umbraco.services') throw "args.term is required"; } - return entityResource.search(args.term, "Media", args.searchFrom).then(function (data) { + return entityResource.search(args.term, "Media", args.searchFrom, args.canceler, args.dataTypeId).then(function (data) { _.each(data, function (item) { searchResultFormatter.configureMediaResult(item); }); @@ -122,7 +122,7 @@ angular.module('umbraco.services') _.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 + // 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... @@ -143,7 +143,7 @@ angular.module('umbraco.services') _.each(resultByType.results, function (item) { formatterMethod.apply(this, [item, resultByType.treeAlias, resultByType.appAlias]); }); - + }); return data; @@ -157,4 +157,4 @@ angular.module('umbraco.services') var currentSection = sectionAlias; } }; - }); \ No newline at end of file + }); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index f72c447627..01eef916b1 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -895,38 +895,6 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s }, - /** - * @ngdoc method - * @name umbraco.services.tinyMceService#getAnchorNames - * @methodOf umbraco.services.tinyMceService - * - * @description - * From the given string, generates a string array where each item is the id attribute value from a named anchor - * 'some string with a named anchor' returns ['anchor'] - * - * @param {string} input the string to parse - */ - getAnchorNames: function (input) { - var anchors = []; - if (!input) { - return anchors; - } - - var anchorPattern = //gi; - var matches = input.match(anchorPattern); - - - if (matches) { - anchors = matches.map(function (v) { - return v.substring(v.indexOf('"') + 1, v.lastIndexOf('\\')); - }); - } - - return anchors.filter(function(val, i, self) { - return self.indexOf(val) === i; - }); - }, - insertLinkInEditor: function (editor, target, anchorElm) { var href = target.url; diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js index 3a0deb812e..23d9cef9a1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Editors.MediaPickerController", - function($scope, mediaResource, entityResource, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService) { + function($scope, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService) { if (!$scope.model.title) { localizationService.localizeMany(["defaultdialogs_selectMedia", "general_includeFromsubFolders"]) @@ -25,6 +25,8 @@ angular.module("umbraco") $scope.lockedFolder = true; $scope.allowMediaEdit = dialogOptions.allowMediaEdit ? dialogOptions.allowMediaEdit : false; + var userStartNodes = []; + var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings; var allowedUploadFiles = mediaHelper.formatFileTypes(umbracoSettings.allowedUploadFiles); if ($scope.onlyImages) { @@ -54,7 +56,8 @@ angular.module("umbraco") pageSize: 100, totalItems: 0, totalPages: 0, - filter: '' + filter: '', + dataTypeId: $scope.model.dataTypeId }; //preload selected item @@ -64,15 +67,19 @@ angular.module("umbraco") } function onInit() { - if ($scope.startNodeId !== -1) { - entityResource.getById($scope.startNodeId, "media") - .then(function (ent) { - $scope.startNodeId = ent.id; - run(); - }); - } else { - run(); - } + userService.getCurrentUser().then(function (userData) { + userStartNodes = userData.startMediaIds; + + if ($scope.startNodeId !== -1) { + entityResource.getById($scope.startNodeId, "media") + .then(function (ent) { + $scope.startNodeId = ent.id; + run(); + }); + } else { + run(); + } + }); } function run() { @@ -89,7 +96,7 @@ angular.module("umbraco") //media object so we need to look it up var id = $scope.target.udi ? $scope.target.udi : $scope.target.id; var altText = $scope.target.altText; - mediaResource.getById(id) + entityResource.getById(id, "Media") .then(function (node) { $scope.target = node; if (ensureWithinStartNode(node)) { @@ -153,7 +160,7 @@ angular.module("umbraco") } if (folder.id > 0) { - entityResource.getAncestors(folder.id, "media") + entityResource.getAncestors(folder.id, "media", { dataTypeId: $scope.model.dataTypeId }) .then(function(anc) { $scope.path = _.filter(anc, function(f) { @@ -169,7 +176,7 @@ angular.module("umbraco") $scope.path = []; } - $scope.lockedFolder = folder.id === -1 && $scope.model.startNodeIsVirtual; + $scope.lockedFolder = (folder.id === -1 && $scope.model.startNodeIsVirtual) || hasFolderAccess(folder) === false; $scope.currentFolder = folder; localStorageService.set("umbLastOpenedMediaNodeId", folder.id); @@ -265,6 +272,17 @@ angular.module("umbraco") } } + function hasFolderAccess(node) { + var nodePath = node.path ? node.path.split(',') : [node.id]; + + for (var i = 0; i < nodePath.length; i++) { + if (userStartNodes.indexOf(parseInt(nodePath[i])) !== -1) + return true; + } + + return false; + } + function gotoStartNode(err) { $scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" }); } @@ -299,7 +317,8 @@ angular.module("umbraco") pageSize: 100, totalItems: 0, totalPages: 0, - filter: '' + filter: '', + dataTypeId: $scope.model.dataTypeId }; getChildren($scope.currentFolder.id); } @@ -369,10 +388,17 @@ angular.module("umbraco") function getChildren(id) { $scope.loading = true; - return mediaResource.getChildren(id) + return entityResource.getChildren(id, "Media", $scope.searchOptions) .then(function(data) { + + for (i=0;i= 0; @@ -210,6 +211,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper //dialog $scope.openCurrentPicker = function () { $scope.currentPicker = dialogOptions; + $scope.contentPickerOverlay.dataTypeId = $scope.model.dataTypeId; $scope.currentPicker.submit = function (model) { if (angular.isArray(model.selection)) { @@ -401,7 +403,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper // get url for content and media items if (entityType !== "Member") { entityResource.getUrl(entity.id, entityType).then(function (data) { - // update url + // update url angular.forEach($scope.renderModel, function (item) { if (item.id === entity.id) { if (entity.trashed) { @@ -447,7 +449,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper "url": item.url, "trashed": item.trashed, "published": (item.metaData && item.metaData.IsPublished === false && entityType === "Document") ? false : true - // only content supports published/unpublished content so we set everything else to published so the UI looks correct + // only content supports published/unpublished content so we set everything else to published so the UI looks correct }); setEntityUrl(item); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js index eb1032a9c7..2d8e15ea57 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/editors/media.controller.js @@ -7,15 +7,22 @@ angular.module("umbraco") if (!$scope.model.config.startNodeId) { - userService.getCurrentUser().then(function (userData) { - $scope.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0]; - $scope.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1; - }); + if ($scope.model.config.ignoreUserStartNodes === "1" ) { + $scope.model.config.startNodeId = -1; + $scope.model.config.startNodeIsVirtual = true; + + } else { + userService.getCurrentUser().then(function (userData) { + $scope.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0]; + $scope.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1; + }); + } } $scope.setImage = function(){ var startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : undefined; var startNodeIsVirtual = startNodeId ? $scope.model.config.startNodeIsVirtual : undefined; + $scope.mediaPickerOverlay.dataTypeId = $scope.model.dataTypeId; var mediaPicker = { startNodeId: startNodeId, diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js index 734b06536d..dccf9887ec 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/multiurlpicker/multiurlpicker.controller.js @@ -71,6 +71,8 @@ function multiUrlPickerController($scope, angularHelper, localizationService, en var linkPicker = { currentTarget: target, + dataTypeId: $scope.model.dataTypeId, + ignoreUserStartNodes : $scope.model.config.ignoreUserStartNodes, submit: function (model) { if (model.target.url || model.target.anchor) { // if an anchor exists, check that it is appropriately prefixed diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html index 4aa424396d..ade75a1cd6 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html @@ -47,4 +47,5 @@ + diff --git a/src/Umbraco.Web/Editors/EntityController.cs b/src/Umbraco.Web/Editors/EntityController.cs index dbd17205bb..d83ade5a13 100644 --- a/src/Umbraco.Web/Editors/EntityController.cs +++ b/src/Umbraco.Web/Editors/EntityController.cs @@ -36,7 +36,12 @@ namespace Umbraco.Web.Editors /// The API controller used for getting entity objects, basic name, icon, id representation of umbraco objects that are based on CMSNode /// /// - /// Some objects such as macros are not based on CMSNode + /// + /// This controller allows resolving basic entity data for various entities without placing the hard restrictions on users that may not have access + /// to the sections these entities entities exist in. This is to allow pickers, etc... of data to work for all users. In some cases such as accessing + /// Members, more explicit security checks are done. + /// + /// Some objects such as macros are not based on CMSNode /// [EntityControllerConfiguration] [PluginController("UmbracoApi")] @@ -98,19 +103,20 @@ namespace Umbraco.Web.Editors /// /// A starting point for the search, generally a node id, but for members this is a member type alias /// + /// If set used to look up whether user and group start node permissions will be ignored. /// [HttpGet] - public IEnumerable Search(string query, UmbracoEntityTypes type, string searchFrom = null) + public IEnumerable Search(string query, UmbracoEntityTypes type, string searchFrom = null, Guid? dataTypeId = null) { - // TODO: Should we restrict search results based on what app the user has access to? - // - Theoretically you shouldn't be able to see member data if you don't have access to members right? + // NOTE: Theoretically you shouldn't be able to see member data if you don't have access to members right? ... but there is a member picker, so can't really do that if (string.IsNullOrEmpty(query)) return Enumerable.Empty(); //TODO: This uses the internal UmbracoTreeSearcher, this instead should delgate to the ISearchableTree implementation for the type - return ExamineSearch(query, type, searchFrom); + var ignoreUserStartNodes = dataTypeId.HasValue && Services.DataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeId.Value); + return ExamineSearch(query, type, searchFrom, ignoreUserStartNodes); } /// @@ -204,6 +210,9 @@ namespace Umbraco.Web.Editors /// Int id of the entity to fetch URL for /// The type of entity such as Document, Media, Member /// The URL or path to the item + /// + /// We are not restricting this with security because there is no sensitive data + /// public HttpResponseMessage GetUrl(int id, UmbracoEntityTypes type) { var returnUrl = string.Empty; @@ -277,6 +286,29 @@ namespace Umbraco.Web.Editors publishedContentExists: i => Umbraco.Content(i) != null); } + + [HttpGet] + public UrlAndAnchors GetUrlAndAnchors([FromUri]int id) + { + var url = Umbraco.Url(id); + var anchorValues = Services.ContentService.GetAnchorValuesFromRTEs(id); + return new UrlAndAnchors(url, anchorValues); + } + + public class AnchorsModel + { + public string RteContent { get; set; } + } + + [HttpGet] + [HttpPost] + public IEnumerable GetAnchors(AnchorsModel model) + { + var anchorValues = Services.ContentService.GetAnchorValuesFromRTEContent(model.RteContent); + return anchorValues; + } + + #region GetById /// @@ -397,9 +429,52 @@ namespace Umbraco.Web.Editors } #endregion - public IEnumerable GetChildren(int id, UmbracoEntityTypes type) + public IEnumerable GetChildren(int id, UmbracoEntityTypes type, Guid? dataTypeId = null) { - return GetResultForChildren(id, type); + var objectType = ConvertToObjectType(type); + if (objectType.HasValue) + { + //TODO: Need to check for Object types that support hierarchy here, some might not. + + int[] startNodes = null; + switch (type) + { + case UmbracoEntityTypes.Document: + startNodes = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService); + break; + case UmbracoEntityTypes.Media: + startNodes = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); + break; + } + + var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes(dataTypeId); + + // root is special: we reduce it to start nodes if the user's start node is not the default, then we need to return their start nodes + if (id == Constants.System.Root && startNodes.Length > 0 && startNodes.Contains(Constants.System.Root) == false && !ignoreUserStartNodes) + { + var nodes = Services.EntityService.GetAll(objectType.Value, startNodes).ToArray(); + if (nodes.Length == 0) + return Enumerable.Empty(); + var pr = new List(nodes.Select(Mapper.Map)); + return pr; + } + + // else proceed as usual + + return Services.EntityService.GetChildren(id, objectType.Value) + .WhereNotNull() + .Select(Mapper.Map); + } + //now we need to convert the unknown ones + switch (type) + { + case UmbracoEntityTypes.Domain: + case UmbracoEntityTypes.Language: + case UmbracoEntityTypes.User: + case UmbracoEntityTypes.Macro: + default: + throw new NotSupportedException("The " + typeof(EntityController) + " does not currently support data for the type " + type); + } } /// @@ -420,7 +495,8 @@ namespace Umbraco.Web.Editors int pageSize, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, - string filter = "") + string filter = "", + Guid? dataTypeId = null) { if (int.TryParse(id, out var intId)) { @@ -445,7 +521,7 @@ namespace Umbraco.Web.Editors //the EntityService can search paged members from the root intId = -1; - return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter); + return GetPagedChildren(intId, type, pageNumber, pageSize, orderBy, orderDirection, filter, dataTypeId); } //the EntityService cannot search members of a certain type, this is currently not supported and would require @@ -480,7 +556,8 @@ namespace Umbraco.Web.Editors int pageSize, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, - string filter = "") + string filter = "", + Guid? dataTypeId = null) { if (pageNumber <= 0) throw new HttpResponseException(HttpStatusCode.NotFound); @@ -490,12 +567,47 @@ namespace Umbraco.Web.Editors var objectType = ConvertToObjectType(type); if (objectType.HasValue) { - var entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out var totalRecords, + IEnumerable entities; + long totalRecords; + + int[] startNodes = null; + switch (type) + { + case UmbracoEntityTypes.Document: + startNodes = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService); + break; + case UmbracoEntityTypes.Media: + startNodes = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); + break; + } + + var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes(dataTypeId); + + // root is special: we reduce it to start nodes if the user's start node is not the default, then we need to return their start nodes + if (id == Constants.System.Root && startNodes.Length > 0 && startNodes.Contains(Constants.System.Root) == false && !ignoreUserStartNodes) + { + if (pageNumber > 0) + return new PagedResult(0, 0, 0); + var nodes = Services.EntityService.GetAll(objectType.Value, startNodes).ToArray(); + if (nodes.Length == 0) + return new PagedResult(0, 0, 0); + if (pageSize < nodes.Length) pageSize = nodes.Length; // bah + var pr = new PagedResult(nodes.Length, pageNumber, pageSize) + { + Items = nodes.Select(Mapper.Map) + }; + return pr; + } + + // else proceed as usual + //entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out totalRecords, orderBy, orderDirection, filter); + entities = Services.EntityService.GetPagedChildren(id, objectType.Value, pageNumber - 1, pageSize, out var totalRecords, filter.IsNullOrWhiteSpace() ? null : SqlContext.Query().Where(x => x.Name.Contains(filter)), Ordering.By(orderBy, orderDirection)); + if (totalRecords == 0) { return new PagedResult(0, 0, 0); @@ -532,6 +644,7 @@ namespace Umbraco.Web.Editors } } + public PagedResult GetPagedDescendants( int id, UmbracoEntityTypes type, @@ -539,7 +652,8 @@ namespace Umbraco.Web.Editors int pageSize, string orderBy = "SortOrder", Direction orderDirection = Direction.Ascending, - string filter = "") + string filter = "", + Guid? dataTypeId = null) { if (pageNumber <= 0) throw new HttpResponseException(HttpStatusCode.NotFound); @@ -567,7 +681,8 @@ namespace Umbraco.Web.Editors break; } - entities = aids == null || aids.Contains(Constants.System.Root) + var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes(dataTypeId); + entities = aids == null || aids.Contains(Constants.System.Root) || ignoreUserStartNodes ? Services.EntityService.GetPagedDescendants(objectType.Value, pageNumber - 1, pageSize, out totalRecords, SqlContext.Query().Where(x => x.Name.Contains(filter)), Ordering.By(orderBy, orderDirection), includeTrashed: false) @@ -609,6 +724,8 @@ namespace Umbraco.Web.Editors } } + private bool IsDataTypeIgnoringUserStartNodes(Guid? dataTypeId) => dataTypeId.HasValue && Services.DataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeId.Value); + public IEnumerable GetAncestors(int id, UmbracoEntityTypes type, [ModelBinder(typeof(HttpQueryStringModelBinder))]FormDataCollection queryStrings) { return GetResultForAncestors(id, type, queryStrings); @@ -620,10 +737,11 @@ namespace Umbraco.Web.Editors /// /// /// + /// If set to true, user and group start node permissions will be ignored. /// - private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null) + private IEnumerable ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null, bool ignoreUserStartNodes = false) { - return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, searchFrom); + return _treeSearcher.ExamineSearch(query, entityType, 200, 0, out _, searchFrom, ignoreUserStartNodes); } private IEnumerable GetResultForChildren(int id, UmbracoEntityTypes entityType) @@ -658,35 +776,39 @@ namespace Umbraco.Web.Editors var ids = Services.EntityService.Get(id).Path.Split(',').Select(int.Parse).Distinct().ToArray(); - int[] aids = null; - switch (entityType) + var ignoreUserStartNodes = IsDataTypeIgnoringUserStartNodes(dataTypeId); + if (ignoreUserStartNodes == false) { - case UmbracoEntityTypes.Document: - aids = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService); - break; - case UmbracoEntityTypes.Media: - aids = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); - break; - } - - if (aids != null) - { - var lids = new List(); - var ok = false; - foreach (var i in ids) + int[] aids = null; + switch (entityType) { - if (ok) - { - lids.Add(i); - continue; - } - if (aids.Contains(i)) - { - lids.Add(i); - ok = true; - } + case UmbracoEntityTypes.Document: + aids = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService); + break; + case UmbracoEntityTypes.Media: + aids = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); + break; + } + + if (aids != null) + { + var lids = new List(); + var ok = false; + foreach (var i in ids) + { + if (ok) + { + lids.Add(i); + continue; + } + if (aids.Contains(i)) + { + lids.Add(i); + ok = true; + } + } + ids = lids.ToArray(); } - ids = lids.ToArray(); } var culture = queryStrings?.GetValue("culture"); diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs index 274cc06dd2..e9b731348d 100644 --- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs @@ -230,8 +230,10 @@ namespace Umbraco.Web //that each token is unique to the controller/action/area instead of the default ASP.Net implementation which is that the token is unique //per user. _viewContext.HttpContext.Items["ufprt"] = _encryptedString; + } + private readonly ViewContext _viewContext; private readonly FormMethod _method; private bool _disposed; @@ -243,7 +245,6 @@ namespace Umbraco.Web if (this._disposed) return; this._disposed = true; - //Detect if the call is targeting UmbRegisterController/UmbProfileController/UmbLoginStatusController/UmbLoginController and if it is we automatically output a AntiForgeryToken() // We have a controllerName and area so we can match if (_controllerName == "UmbRegister" @@ -251,7 +252,7 @@ namespace Umbraco.Web || _controllerName == "UmbLoginStatus" || _controllerName == "UmbLogin") { - _viewContext.Writer.Write(AntiForgery.GetHtml().ToString()); + _viewContext.Writer.Write(AntiForgery.GetHtml().ToString()); } //write out the hidden surface form routes diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs index d9d730956a..7d6d066e35 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentPropertyBasic.cs @@ -1,4 +1,5 @@ -using System.ComponentModel; +using System; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; using Umbraco.Core.PropertyEditors; @@ -21,6 +22,9 @@ namespace Umbraco.Web.Models.ContentEditing [Required] public int Id { get; set; } + [DataMember(Name = "dataTypeId", IsRequired = false)] + public Guid? DataTypeId { get; set; } + [DataMember(Name = "value")] public object Value { get; set; } diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs index aa2e5f18a7..284c1344b6 100644 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs @@ -155,6 +155,8 @@ namespace Umbraco.Web.Mvc }; } + + /// /// Handles a posted form to an Umbraco Url and ensures the correct controller is routed to and that /// the right DataTokens are set. diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs index 43db9ff0ba..19c0d0ed03 100644 --- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs +++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs @@ -49,6 +49,7 @@ namespace Umbraco.Web.Search /// /// /// + /// If set to true, user and group start node permissions will be ignored. /// public IEnumerable ExamineSearch( string query, @@ -85,12 +86,12 @@ namespace Umbraco.Web.Search case UmbracoEntityTypes.Media: type = "media"; var allMediaStartNodes = _umbracoContext.Security.CurrentUser.CalculateMediaStartNodeIds(_entityService); - AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, _entityService); + AppendPath(sb, UmbracoObjectTypes.Media, allMediaStartNodes, searchFrom, ignoreUserStartNodes, _entityService); break; case UmbracoEntityTypes.Document: type = "content"; var allContentStartNodes = _umbracoContext.Security.CurrentUser.CalculateContentStartNodeIds(_entityService); - AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, _entityService); + AppendPath(sb, UmbracoObjectTypes.Document, allContentStartNodes, searchFrom, ignoreUserStartNodes, _entityService); break; default: throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType); @@ -288,7 +289,7 @@ namespace Umbraco.Web.Search } } - private void AppendPath(StringBuilder sb, UmbracoObjectTypes objectType, int[] startNodeIds, string searchFrom, IEntityService entityService) + private void AppendPath(StringBuilder sb, UmbracoObjectTypes objectType, int[] startNodeIds, string searchFrom, bool ignoreUserStartNodes, IEntityService entityService) { if (sb == null) throw new ArgumentNullException(nameof(sb)); if (entityService == null) throw new ArgumentNullException(nameof(entityService)); @@ -311,7 +312,7 @@ namespace Umbraco.Web.Search // make sure we don't find anything sb.Append("+__Path:none "); } - else if (startNodeIds.Contains(-1) == false) // -1 = no restriction + else if (startNodeIds.Contains(-1) == false && ignoreUserStartNodes == false) // -1 = no restriction { var entityPaths = entityService.GetAllPaths(objectType, startNodeIds); diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 9e481fc4c9..970191e510 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -217,7 +217,6 @@ namespace Umbraco.Web.Trees return result; } - /// /// Returns a collection of all menu items that can be on a content node /// diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 1b8f3b1434..20d7389da1 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -69,7 +69,7 @@ namespace Umbraco.Web.Trees { var node = base.CreateRootNode(queryStrings); - if (IsDialog(queryStrings) && UserStartNodes.Contains(Constants.System.Root) == false) + if (IsDialog(queryStrings) && UserStartNodes.Contains(Constants.System.Root) == false && IgnoreUserStartNodes(queryStrings) == false) { node.AdditionalData["noAccess"] = true; } @@ -87,7 +87,8 @@ namespace Umbraco.Web.Trees /// /// /// - internal TreeNode GetSingleTreeNodeWithAccessCheck(IEntitySlim e, string parentId, FormDataCollection queryStrings) + internal TreeNode GetSingleTreeNodeWithAccessCheck(IEntitySlim e, string parentId, FormDataCollection queryStrings, + int[] startNodeIds, string[] startNodePaths, bool ignoreUserStartNodes) { var entityIsAncestorOfStartNodes = Security.CurrentUser.IsInBranchOfStartNode(e, Services.EntityService, RecycleBinId, out var hasPathAccess); if (entityIsAncestorOfStartNodes == false) @@ -101,6 +102,23 @@ namespace Umbraco.Web.Trees return treeNode; } + private void GetUserStartNodes(out int[] startNodeIds, out string[] startNodePaths) + { + switch (RecycleBinId) + { + case Constants.System.RecycleBinMedia: + startNodeIds = Security.CurrentUser.CalculateMediaStartNodeIds(Services.EntityService); + startNodePaths = Security.CurrentUser.GetMediaStartNodePaths(Services.EntityService); + break; + case Constants.System.RecycleBinContent: + startNodeIds = Security.CurrentUser.CalculateContentStartNodeIds(Services.EntityService); + startNodePaths = Security.CurrentUser.GetContentStartNodePaths(Services.EntityService); + break; + default: + throw new NotSupportedException("Path access is only determined on content or media"); + } + } + /// /// Returns the /// @@ -127,6 +145,8 @@ namespace Umbraco.Web.Trees ? queryStrings.GetValue(TreeQueryStringParameters.StartNodeId) : string.Empty; + var ignoreUserStartNodes = IgnoreUserStartNodes(queryStrings); + if (string.IsNullOrEmpty(startNodeId) == false && startNodeId != "undefined" && startNodeId != rootIdString) { // request has been made to render from a specific, non-root, start node @@ -134,7 +154,7 @@ namespace Umbraco.Web.Trees // ensure that the user has access to that node, otherwise return the empty tree nodes collection // TODO: in the future we could return a validation statement so we can have some UI to notify the user they don't have access - if (HasPathAccess(id, queryStrings) == false) + if (ignoreUserStartNodes == false && HasPathAccess(id, queryStrings) == false) { Logger.Warn("User {Username} does not have access to node with id {Id}", Security.CurrentUser.Username, id); return nodes; @@ -152,7 +172,11 @@ namespace Umbraco.Web.Trees // get child entities - if id is root, but user's start nodes do not contain the // root node, this returns the start nodes instead of root's children var entities = GetChildEntities(id, queryStrings).ToList(); - nodes.AddRange(entities.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings)).Where(x => x != null)); + + //get the current user start node/paths + GetUserStartNodes(out var userStartNodes, out var userStartNodePaths); + + nodes.AddRange(entities.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths, ignoreUserStartNodes)).Where(x => x != null)); // if the user does not have access to the root node, what we have is the start nodes, // but to provide some context we also need to add their topmost nodes when they are not @@ -163,7 +187,7 @@ namespace Umbraco.Web.Trees if (topNodeIds.Length > 0) { var topNodes = Services.EntityService.GetAll(UmbracoObjectType, topNodeIds.ToArray()); - nodes.AddRange(topNodes.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings)).Where(x => x != null)); + nodes.AddRange(topNodes.Select(x => GetSingleTreeNodeWithAccessCheck(x, id, queryStrings, userStartNodes, userStartNodePaths, ignoreUserStartNodes)).Where(x => x != null)); } } @@ -504,5 +528,21 @@ namespace Umbraco.Web.Trees } private readonly ConcurrentDictionary _entityCache = new ConcurrentDictionary(); + + /// + /// If the request should allows a user to choose nodes that they normally don't have access to + /// + /// + /// + internal bool IgnoreUserStartNodes(FormDataCollection queryStrings) + { + var dataTypeId = queryStrings.GetValue(TreeQueryStringParameters.DataTypeId); + if (dataTypeId.HasValue) + { + return Services.DataTypeService.IsDataTypeIgnoringUserStartNodes(dataTypeId.Value); + } + + return false; + } } } diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index 22ad4ed355..f4f373f9a4 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -166,6 +166,6 @@ namespace Umbraco.Web.Trees { return _treeSearcher.ExamineSearch(query, UmbracoEntityTypes.Media, pageSize, pageIndex, out totalFound, searchFrom); } - + } } diff --git a/src/Umbraco.Web/Trees/TreeControllerBase.cs b/src/Umbraco.Web/Trees/TreeControllerBase.cs index 4acf807b77..0f9b61469e 100644 --- a/src/Umbraco.Web/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/TreeControllerBase.cs @@ -15,6 +15,7 @@ using Umbraco.Core.Services; using Umbraco.Web.Models.Trees; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; +using Umbraco.Core.Services; namespace Umbraco.Web.Trees { diff --git a/src/Umbraco.Web/Trees/TreeQueryStringParameters.cs b/src/Umbraco.Web/Trees/TreeQueryStringParameters.cs index 466aff5a1f..9d012cb25c 100644 --- a/src/Umbraco.Web/Trees/TreeQueryStringParameters.cs +++ b/src/Umbraco.Web/Trees/TreeQueryStringParameters.cs @@ -8,6 +8,7 @@ public const string Use = "use"; public const string Application = "application"; public const string StartNodeId = "startNodeId"; + public const string DataTypeId = "dataTypeId"; //public const string OnNodeClick = "OnNodeClick"; //public const string RenderParent = "RenderParent"; } diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs index a330f66c43..bf017c73cc 100644 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ b/src/Umbraco.Web/UmbracoHelper.cs @@ -5,6 +5,7 @@ using System.Web; using System.Xml.XPath; using Umbraco.Core; using Umbraco.Core.Dictionary; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Xml;