+
+
+
{{error.errorMsg}}
+
{{error.data.message}}
+
+
+
Are you sure you want to delete {{currentNode.name}} ?
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js
index 17be7718d6..79ae34ea21 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.controller.js
@@ -140,6 +140,11 @@ function ColorPickerController($scope) {
if (!$scope.model.value)
return;
+ // Backwards compatibility, the color used to be stored as a hex value only
+ if (typeof $scope.model.value === "string") {
+ $scope.model.value = { value: $scope.model.value, label: $scope.model.value };
+ }
+
// Complex color (value and label)?
if (!$scope.model.value.hasOwnProperty("value"))
return;
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js
index 5fc986dca9..af3ce50609 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js
@@ -98,7 +98,10 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
opacity: 0.7,
tolerance: "pointer",
scroll: true,
- zIndex: 6000
+ zIndex: 6000,
+ update: function (e, ui) {
+ angularHelper.getCurrentForm($scope).$setDirty();
+ }
};
if ($scope.model.config) {
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js
index 96e7ccdbfd..c84644d251 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/listview/listview.controller.js
@@ -1,4 +1,4 @@
-function listViewController($scope, $routeParams, $injector, $timeout, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, $location, listViewHelper, navigationService, editorService, overlayService, languageResource) {
+function listViewController($scope, $routeParams, $injector, $timeout, currentUserResource, notificationsService, iconHelper, editorState, localizationService, appState, $location, listViewHelper, navigationService, editorService, overlayService, languageResource, mediaHelper) {
//this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content
// that isn't created yet, if we continue this will use the parent id in the route params which isn't what
@@ -279,10 +279,12 @@ function listViewController($scope, $routeParams, $injector, $timeout, currentUs
$scope.listViewResultSet = data;
//update all values for display
+ var section = appState.getSectionState("currentSection");
if ($scope.listViewResultSet.items) {
_.each($scope.listViewResultSet.items, function (e, index) {
setPropertyValues(e);
- if (e.contentTypeAlias === 'Folder') {
+ // create the folders collection (only for media list views)
+ if (section === "media" && !mediaHelper.hasFilePropertyType(e)) {
$scope.folders.push(e);
}
});
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js
index 6e67d2d251..79e00fa453 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.controller.js
@@ -81,25 +81,6 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
: undefined;
});
- $scope.editIconTitle = '';
- $scope.moveIconTitle = '';
- $scope.deleteIconTitle = '';
-
- // localize the edit icon title
- localizationService.localize('general_edit').then(function (value) {
- $scope.editIconTitle = value;
- });
-
- // localize the delete icon title
- localizationService.localize('general_delete').then(function (value) {
- $scope.deleteIconTitle = value;
- });
-
- // localize the move icon title
- localizationService.localize('actions_move').then(function (value) {
- $scope.moveIconTitle = value;
- });
-
$scope.nodes = [];
$scope.currentNode = undefined;
$scope.realCurrentNode = undefined;
@@ -116,6 +97,11 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
$scope.showIcons = $scope.model.config.showIcons || true;
$scope.wideMode = $scope.model.config.hideLabel == "1";
+ $scope.labels = {};
+ localizationService.localizeMany(["grid_insertControl"]).then(function(data) {
+ $scope.labels.docTypePickerTitle = data[0];
+ });
+
// helper to force the current form into the dirty state
$scope.setDirty = function () {
if ($scope.propertyForm) {
@@ -138,7 +124,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
}
$scope.overlayMenu = {
- title: localizationService.localize('grid_insertControl'),
+ title: $scope.labels.docTypePickerTitle,
show: false,
style: {},
filter: $scope.scaffolds.length > 15 ? true : false,
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html
index 83076b54a0..0cf67022c6 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.editor.html
@@ -1,5 +1,5 @@
-
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html
index 1ff6666907..572021aebd 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/nestedcontent/nestedcontent.html
@@ -12,13 +12,13 @@
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js
index 688ac7693f..a61930f877 100644
--- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/tags/tags.controller.js
@@ -1,9 +1,12 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.TagsController",
- function ($scope) {
+ function ($scope, angularHelper) {
$scope.valueChanged = function(value) {
$scope.model.value = value;
+ // the model value seems to be a reference to the same array, so we need
+ // to set the form as dirty explicitly when the content of the array changes
+ angularHelper.getCurrentForm($scope).$setDirty();
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/templates/delete.controller.js
index 8995cb1a31..d019a44a10 100644
--- a/src/Umbraco.Web.UI.Client/src/views/templates/delete.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/templates/delete.controller.js
@@ -12,6 +12,10 @@ function TemplatesDeleteController($scope, templateResource , treeService, navig
//mark it for deletion (used in the UI)
$scope.currentNode.loading = true;
+
+ // Reset the error message
+ $scope.error = null;
+
templateResource.deleteById($scope.currentNode.id).then(function () {
$scope.currentNode.loading = false;
@@ -21,6 +25,9 @@ function TemplatesDeleteController($scope, templateResource , treeService, navig
//TODO: Need to sync tree, etc...
treeService.removeNode($scope.currentNode);
navigationService.hideMenu();
+ }, function (err) {
+ $scope.currentNode.loading = false;
+ $scope.error = err;
});
};
diff --git a/src/Umbraco.Web.UI.Client/src/views/templates/delete.html b/src/Umbraco.Web.UI.Client/src/views/templates/delete.html
index 34648aa43e..c98677f764 100644
--- a/src/Umbraco.Web.UI.Client/src/views/templates/delete.html
+++ b/src/Umbraco.Web.UI.Client/src/views/templates/delete.html
@@ -1,11 +1,18 @@
+
+
+
{{error.errorMsg}}
+
{{error.data.message}}
+
+
+
Are you sure you want to delete {{currentNode.name}} ?
-
+
diff --git a/src/Umbraco.Web.UI/.eslintignore b/src/Umbraco.Web.UI/.eslintignore
deleted file mode 100644
index 6cac59fac0..0000000000
--- a/src/Umbraco.Web.UI/.eslintignore
+++ /dev/null
@@ -1,3 +0,0 @@
-
-/Umbraco/**
-/Umbraco_Client/**
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index 8787d78444..9d3297dbc2 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -88,7 +88,7 @@
-
+
@@ -206,7 +206,6 @@
-
404handlers.config
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
index 383dc855fd..7a370ba90e 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
@@ -180,6 +180,11 @@
Overførsel af egenskaber kunne ikke fuldføres, da en eller flere egenskaber er indstillet til at blive overført mere end én gang.
Kun andre dokumenttyper, der er gyldige på denne placering, vises.
+
+
Oprettelse af mappen under parent med ID %0% fejlede
+
Oprettelse af mappen under parent med navnet %0% fejlede
+
Sletning af filen/mappen fejlede: %0%
+
Udgivet
Om siden
@@ -276,6 +281,7 @@
Hvor ønsker du at oprette den nye %0%
Opret under
Vælg den dokumenttype, du vil oprette en indholdsskabelon til
+
Angiv et navn for mappen
Vælg en type og skriv en titel
"dokument typer".]]>
"media typer".]]>
@@ -1062,6 +1068,7 @@ Mange hilsner fra Umbraco robotten
Editor
+
Sletning af skabelonen med ID %0% fejlede
Rediger skabelon
Sektioner
Indsæt indholdsområde
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
index 69f106a775..386d3af518 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
@@ -189,6 +189,11 @@
Could not complete property mapping as one or more properties have more than one mapping defined.
Only alternate types valid for the current location are displayed.
+
+
Failed to create a folder under parent with ID %0%
+
Failed to create a folder under parent with name %0%
+
Failed to delete item: %0%
+
Is Published
About this page
@@ -291,6 +296,7 @@
Where do you want to create the new %0%
Create an item under
Select the document type you want to make a content template for
+
Enter a folder name
Choose a type and a title
"document types".]]>
"media types".]]>
@@ -1248,6 +1254,7 @@ To manage your website, simply open the Umbraco back office and start adding con
This Content Type uses
as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself
No properties defined on this tab. Click on the "add a new property" link at the top to create a new property.
+
Create matching template
Add icon
@@ -1347,6 +1354,7 @@ To manage your website, simply open the Umbraco back office and start adding con
Editor
+
Failed to delete template with ID %0%
Edit template
Sections
Insert content area
@@ -1968,8 +1976,8 @@ To manage your website, simply open the Umbraco back office and start adding con
Trashed content with Id: {0} related to original parent content with Id: {1}
Trashed media with Id: {0} related to original parent media item with Id: {1}
Cannot automatically restore this item
-
There is no 'restore' relation found for this node. Use the Move menu item to move it manually.
-
The item you want to restore it under ('%0%') is in the recycle bin. Use the Move menu item to move the item manually.
+
There is no location where this item can be automatically restored. You can move the item manually using the tree below.
+
was restored under
Direction
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
index eaa1c6c39e..5de373f571 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
@@ -196,6 +196,11 @@
Could not complete property mapping as one or more properties have more than one mapping defined.
Only alternate types valid for the current location are displayed.
+
+
Failed to create a folder under parent with ID %0%
+
Failed to create a folder under parent with name %0%
+
Failed to delete item: %0%
+
Is Published
About this page
@@ -316,6 +321,7 @@
Where do you want to create the new %0%
Create an item under
Select the document type you want to make a content template for
+
Enter a folder name
Choose a type and a title
"document types".]]>
"media types".]]>
@@ -1272,6 +1278,7 @@ To manage your website, simply open the Umbraco back office and start adding con
This Content Type uses
as a Master Content Type. Tabs from Master Content Types are not shown and can only be edited on the Master Content Type itself
No properties defined on this tab. Click on the "add a new property" link at the top to create a new property.
+
Create matching template
Add icon
@@ -1387,6 +1394,7 @@ To manage your website, simply open the Umbraco back office and start adding con
How the text will look like in the rich text editor.
+
Failed to delete template with ID %0%
Edit template
Sections
Insert content area
@@ -2024,8 +2032,8 @@ To manage your website, simply open the Umbraco back office and start adding con
Trashed content with Id: {0} related to original parent content with Id: {1}
Trashed media with Id: {0} related to original parent media item with Id: {1}
Cannot automatically restore this item
-
There is no 'restore' relation found for this node. Use the Move menu item to move it manually.
-
The item you want to restore it under ('%0%') is in the recycle bin. Use the Move menu item to move the item manually.
+
There is no location where this item can be automatically restored. You can move the item manually using the tree below.
+
was restored under
Direction
diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs b/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs
index 81b133b9ef..d522e54de6 100644
--- a/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs
+++ b/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs
@@ -142,8 +142,8 @@ namespace Umbraco.Web.Cache
() => ContentService.Saved -= ContentService_Saved);
Bind(() => ContentService.Copied += ContentService_Copied, // needed for permissions
() => ContentService.Copied -= ContentService_Copied);
- Bind(() => ContentService.TreeChanged += ContentService_Changed,// handles all content changes
- () => ContentService.TreeChanged -= ContentService_Changed);
+ Bind(() => ContentService.TreeChanged += ContentService_TreeChanged,// handles all content changes
+ () => ContentService.TreeChanged -= ContentService_TreeChanged);
// TreeChanged should also deal with this
//Bind(() => ContentService.SavedBlueprint += ContentService_SavedBlueprint,
@@ -206,7 +206,7 @@ namespace Umbraco.Web.Cache
{
}
- private void ContentService_Changed(IContentService sender, TreeChange
.EventArgs args)
+ private void ContentService_TreeChanged(IContentService sender, TreeChange.EventArgs args)
{
_distributedCache.RefreshContentCache(args.Changes.ToArray());
}
diff --git a/src/Umbraco.Web/Composing/ModuleInjector.cs b/src/Umbraco.Web/Composing/ModuleInjector.cs
index 01930d55fd..57ef766dea 100644
--- a/src/Umbraco.Web/Composing/ModuleInjector.cs
+++ b/src/Umbraco.Web/Composing/ModuleInjector.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Web;
+using System.Web;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Exceptions;
@@ -11,7 +10,7 @@ namespace Umbraco.Web.Composing
///
/// The type of the injected module.
public abstract class ModuleInjector : IHttpModule
- where TModule : IHttpModule
+ where TModule : class, IHttpModule
{
protected TModule Module { get; private set; }
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs
index 9458ec6b8d..3b361fcf1e 100644
--- a/src/Umbraco.Web/Editors/ContentController.cs
+++ b/src/Umbraco.Web/Editors/ContentController.cs
@@ -357,7 +357,9 @@ namespace Umbraco.Web.Editors
var mapped = MapToDisplay(emptyContent);
// translate the content type name if applicable
mapped.ContentTypeName = Services.TextService.UmbracoDictionaryTranslate(mapped.ContentTypeName);
- mapped.DocumentType.Name = Services.TextService.UmbracoDictionaryTranslate(mapped.DocumentType.Name);
+ // if your user type doesn't have access to the Settings section it would not get this property mapped
+ if(mapped.DocumentType != null)
+ mapped.DocumentType.Name = Services.TextService.UmbracoDictionaryTranslate(mapped.DocumentType.Name);
//remove the listview app if it exists
mapped.ContentApps = mapped.ContentApps.Where(x => x.Alias != "umbListView").ToList();
diff --git a/src/Umbraco.Web/Editors/Filters/UserGroupEditorAuthorizationHelper.cs b/src/Umbraco.Web/Editors/Filters/UserGroupEditorAuthorizationHelper.cs
index 2b2bf337de..985c42bbbf 100644
--- a/src/Umbraco.Web/Editors/Filters/UserGroupEditorAuthorizationHelper.cs
+++ b/src/Umbraco.Web/Editors/Filters/UserGroupEditorAuthorizationHelper.cs
@@ -53,6 +53,16 @@ namespace Umbraco.Web.Editors.Filters
if (currentUser.IsAdmin())
return Attempt.Succeed();
+ var existingGroups = _userService.GetUserGroupsByAlias(groupAliases);
+
+ if(!existingGroups.Any())
+ {
+ // We're dealing with new groups,
+ // so authorization should be given to any user with access to Users section
+ if (currentUser.AllowedSections.Contains(Constants.Applications.Users))
+ return Attempt.Succeed();
+ }
+
var userGroups = currentUser.Groups.Select(x => x.Alias).ToArray();
var missingAccess = groupAliases.Except(userGroups).ToArray();
return missingAccess.Length == 0
diff --git a/src/Umbraco.Web/Editors/UserGroupsController.cs b/src/Umbraco.Web/Editors/UserGroupsController.cs
index 677890bcc4..5069d99cfe 100644
--- a/src/Umbraco.Web/Editors/UserGroupsController.cs
+++ b/src/Umbraco.Web/Editors/UserGroupsController.cs
@@ -31,6 +31,7 @@ namespace Umbraco.Web.Editors
//authorize that the user has access to save this user group
var authHelper = new UserGroupEditorAuthorizationHelper(
Services.UserService, Services.ContentService, Services.MediaService, Services.EntityService);
+
var isAuthorized = authHelper.AuthorizeGroupAccess(Security.CurrentUser, userGroupSave.Alias);
if (isAuthorized == false)
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, isAuthorized.Result));
@@ -51,6 +52,14 @@ namespace Umbraco.Web.Editors
if (isAuthorized == false)
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.Unauthorized, isAuthorized.Result));
+ //current user needs to be added to a new group if not an admin (possibly only if no other users are added?) to avoid a 401
+ if(!Security.CurrentUser.IsAdmin() && (userGroupSave.Id == null || Convert.ToInt32(userGroupSave.Id) >= 0)/* && !userGroupSave.Users.Any() */)
+ {
+ var userIds = userGroupSave.Users.ToList();
+ userIds.Add(Security.CurrentUser.Id);
+ userGroupSave.Users = userIds;
+ }
+
//save the group
Services.UserService.Save(userGroupSave.PersistedUserGroup, userGroupSave.Users.ToArray());
diff --git a/src/Umbraco.Web/ExamineExtensions.cs b/src/Umbraco.Web/ExamineExtensions.cs
index f1ed6c0659..9a9fa98d95 100644
--- a/src/Umbraco.Web/ExamineExtensions.cs
+++ b/src/Umbraco.Web/ExamineExtensions.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using Examine;
using Umbraco.Core;
diff --git a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs
index 2e82fe252d..9c30f74ea7 100644
--- a/src/Umbraco.Web/HtmlHelperRenderExtensions.cs
+++ b/src/Umbraco.Web/HtmlHelperRenderExtensions.cs
@@ -12,6 +12,7 @@ using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
+using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
using Umbraco.Web.Security;
using Current = Umbraco.Web.Composing.Current;
@@ -831,5 +832,37 @@ namespace Umbraco.Web
#endregion
+ #region RelatedLink
+
+ ///
+ /// Renders an anchor element for a RelatedLink instance.
+ /// Format: <a href="relatedLink.Link" target="_blank/_self">relatedLink.Caption</a>
+ ///
+ /// The HTML helper instance that this method extends.
+ /// The RelatedLink instance
+ /// An anchor element
+ public static MvcHtmlString GetRelatedLinkHtml(this HtmlHelper htmlHelper, RelatedLink relatedLink)
+ {
+ return htmlHelper.GetRelatedLinkHtml(relatedLink, null);
+ }
+
+ ///
+ /// Renders an anchor element for a RelatedLink instance, accepting htmlAttributes.
+ /// Format: <a href="relatedLink.Link" target="_blank/_self" htmlAttributes>relatedLink.Caption</a>
+ ///
+ /// The HTML helper instance that this method extends.
+ /// The RelatedLink instance
+ /// An object that contains the HTML attributes to set for the element.
+ ///
+ public static MvcHtmlString GetRelatedLinkHtml(this HtmlHelper htmlHelper, RelatedLink relatedLink, object htmlAttributes)
+ {
+ var tagBuilder = new TagBuilder("a");
+ tagBuilder.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
+ tagBuilder.MergeAttribute("href", relatedLink.Link);
+ tagBuilder.MergeAttribute("target", relatedLink.NewWindow ? "_blank" : "_self");
+ tagBuilder.InnerHtml = HttpUtility.HtmlEncode(relatedLink.Caption);
+ return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
+ }
+ #endregion
}
}
diff --git a/src/Umbraco.Web/IPublishedContentQuery.cs b/src/Umbraco.Web/IPublishedContentQuery.cs
index c3a065b9dc..76e7be5e97 100644
--- a/src/Umbraco.Web/IPublishedContentQuery.cs
+++ b/src/Umbraco.Web/IPublishedContentQuery.cs
@@ -40,6 +40,7 @@ namespace Umbraco.Web
/// Optional index name.
///
/// When the is not specified, all cultures are searched.
+ /// While enumerating results, the ambient culture is changed to be the searched culture.
///
IEnumerable Search(string term, string culture = null, string indexName = null);
@@ -54,17 +55,24 @@ namespace Umbraco.Web
/// Optional index name.
///
/// When the is not specified, all cultures are searched.
+ /// While enumerating results, the ambient culture is changed to be the searched culture.
///
IEnumerable Search(string term, int skip, int take, out long totalRecords, string culture = null, string indexName = null);
///
/// Executes the query and converts the results to PublishedSearchResult.
///
+ ///
+ /// While enumerating results, the ambient culture is changed to be the searched culture.
+ ///
IEnumerable Search(IQueryExecutor query);
///
/// Executes the query and converts the results to PublishedSearchResult.
///
+ ///
+ /// While enumerating results, the ambient culture is changed to be the searched culture.
+ ///
IEnumerable Search(IQueryExecutor query, int skip, int take, out long totalRecords);
}
}
diff --git a/src/Umbraco.Web/Media/Exif/ImageFile.cs b/src/Umbraco.Web/Media/Exif/ImageFile.cs
index d0c3ef7411..acd8ce8eec 100644
--- a/src/Umbraco.Web/Media/Exif/ImageFile.cs
+++ b/src/Umbraco.Web/Media/Exif/ImageFile.cs
@@ -137,7 +137,8 @@ namespace Umbraco.Web.Media.Exif
return new SvgFile(stream);
}
- throw new NotValidImageFileException();
+ // We don't know
+ return null;
}
#endregion
}
diff --git a/src/Umbraco.Web/Media/ImageHelper.cs b/src/Umbraco.Web/Media/ImageHelper.cs
index 3fbc1e060a..5a5724dc7d 100644
--- a/src/Umbraco.Web/Media/ImageHelper.cs
+++ b/src/Umbraco.Web/Media/ImageHelper.cs
@@ -33,7 +33,8 @@ namespace Umbraco.Web.Media
//Try to load with exif
var jpgInfo = ImageFile.FromStream(stream);
- if (jpgInfo.Format != ImageFileFormat.Unknown
+ if (jpgInfo != null
+ && jpgInfo.Format != ImageFileFormat.Unknown
&& jpgInfo.Properties.ContainsKey(ExifTag.PixelYDimension)
&& jpgInfo.Properties.ContainsKey(ExifTag.PixelXDimension))
{
diff --git a/src/Umbraco.Web/Media/TypeDetector/JpegDetector.cs b/src/Umbraco.Web/Media/TypeDetector/JpegDetector.cs
index 287709f9c6..d06671f667 100644
--- a/src/Umbraco.Web/Media/TypeDetector/JpegDetector.cs
+++ b/src/Umbraco.Web/Media/TypeDetector/JpegDetector.cs
@@ -7,8 +7,7 @@ namespace Umbraco.Web.Media.TypeDetector
public static bool IsOfType(Stream fileStream)
{
var header = GetFileHeader(fileStream);
-
- return header[0] == 0xff && header[1] == 0xD8;
+ return header != null && header[0] == 0xff && header[1] == 0xD8;
}
}
}
diff --git a/src/Umbraco.Web/Media/TypeDetector/RasterizedTypeDetector.cs b/src/Umbraco.Web/Media/TypeDetector/RasterizedTypeDetector.cs
index 4bb074e5a2..a4ded5f8c4 100644
--- a/src/Umbraco.Web/Media/TypeDetector/RasterizedTypeDetector.cs
+++ b/src/Umbraco.Web/Media/TypeDetector/RasterizedTypeDetector.cs
@@ -7,9 +7,13 @@ namespace Umbraco.Web.Media.TypeDetector
public static byte[] GetFileHeader(Stream fileStream)
{
fileStream.Seek(0, SeekOrigin.Begin);
- byte[] header = new byte[8];
+ var header = new byte[8];
fileStream.Seek(0, SeekOrigin.Begin);
+ // Invalid header
+ if (fileStream.Read(header, 0, header.Length) != header.Length)
+ return null;
+
return header;
}
}
diff --git a/src/Umbraco.Web/Media/TypeDetector/TIFFDetector.cs b/src/Umbraco.Web/Media/TypeDetector/TIFFDetector.cs
index 08126136b8..7adb1cd9de 100644
--- a/src/Umbraco.Web/Media/TypeDetector/TIFFDetector.cs
+++ b/src/Umbraco.Web/Media/TypeDetector/TIFFDetector.cs
@@ -7,17 +7,17 @@ namespace Umbraco.Web.Media.TypeDetector
{
public static bool IsOfType(Stream fileStream)
{
- string tiffHeader = GetFileHeader(fileStream);
-
- return tiffHeader == "MM\x00\x2a" || tiffHeader == "II\x2a\x00";
+ var tiffHeader = GetFileHeader(fileStream);
+ return tiffHeader != null && tiffHeader == "MM\x00\x2a" || tiffHeader == "II\x2a\x00";
}
public static string GetFileHeader(Stream fileStream)
{
var header = RasterizedTypeDetector.GetFileHeader(fileStream);
+ if (header == null)
+ return null;
- string tiffHeader = Encoding.ASCII.GetString(header, 0, 4);
-
+ var tiffHeader = Encoding.ASCII.GetString(header, 0, 4);
return tiffHeader;
}
}
diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
index 4ebddee6dc..dc8fcf8c0e 100755
--- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
+++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
-using System.Web.Hosting;
using CSharpTest.Net.Collections;
using Newtonsoft.Json;
using Umbraco.Core;
@@ -15,7 +14,6 @@ using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence;
-using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.Repositories.Implement;
@@ -26,7 +24,6 @@ using Umbraco.Core.Services.Implement;
using Umbraco.Web.Cache;
using Umbraco.Web.Install;
using Umbraco.Web.PublishedCache.NuCache.DataSource;
-using Umbraco.Web.PublishedCache.XmlPublishedCache;
using Umbraco.Web.Routing;
namespace Umbraco.Web.PublishedCache.NuCache
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
index a845be286f..6f6a39144a 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/DictionaryPublishedContent.cs
@@ -52,7 +52,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
LoadedFromExamine = fromExamine;
ValidateAndSetProperty(valueDictionary, val => _id = Int32.Parse(val), "id", "nodeId", "__NodeId"); //should validate the int!
- ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key");
+ ValidateAndSetProperty(valueDictionary, val => _key = Guid.Parse(val), "key", "__key", "__Key");
//ValidateAndSetProperty(valueDictionary, val => _templateId = int.Parse(val), "template", "templateId");
ValidateAndSetProperty(valueDictionary, val => _sortOrder = Int32.Parse(val), "sortOrder");
ValidateAndSetProperty(valueDictionary, val => _name = val, "nodeName");
diff --git a/src/Umbraco.Web/PublishedContentQuery.cs b/src/Umbraco.Web/PublishedContentQuery.cs
index 3d8f36ec1a..2c4d08502e 100644
--- a/src/Umbraco.Web/PublishedContentQuery.cs
+++ b/src/Umbraco.Web/PublishedContentQuery.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -24,16 +25,19 @@ namespace Umbraco.Web
{
private readonly IPublishedContentCache _contentCache;
private readonly IPublishedMediaCache _mediaCache;
+ private readonly IVariationContextAccessor _variationContextAccessor;
///
/// Constructor used to return results from the caches
///
///
///
- public PublishedContentQuery(IPublishedContentCache contentCache, IPublishedMediaCache mediaCache)
+ ///
+ public PublishedContentQuery(IPublishedContentCache contentCache, IPublishedMediaCache mediaCache, IVariationContextAccessor variationContextAccessor)
{
_contentCache = contentCache ?? throw new ArgumentNullException(nameof(contentCache));
_mediaCache = mediaCache ?? throw new ArgumentNullException(nameof(mediaCache));
+ _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor));
}
#region Content
@@ -201,6 +205,9 @@ namespace Umbraco.Web
// default to max 500 results
var count = skip == 0 && take == 0 ? 500 : skip + take;
+ //set this to the specific culture or to the culture in the request
+ culture = culture ?? _variationContextAccessor.VariationContext.Culture;
+
ISearchResults results;
if (culture.IsNullOrWhiteSpace())
{
@@ -209,23 +216,15 @@ namespace Umbraco.Web
else
{
//get all index fields suffixed with the culture name supplied
- var cultureFields = new List();
- var fields = umbIndex.GetFields();
- var qry = searcher.CreateQuery().Field(UmbracoContentIndex.VariesByCultureFieldName, 1); //must vary by culture
- // ReSharper disable once LoopCanBeConvertedToQuery
- foreach (var field in fields)
- {
- var match = CultureIsoCodeFieldName.Match(field);
- if (match.Success && match.Groups.Count == 2 && culture.InvariantEquals(match.Groups[1].Value))
- cultureFields.Add(field);
- }
-
+ var cultureFields = umbIndex.GetCultureFields(culture);
+ var qry = searcher.CreateQuery().Field(UmbracoContentIndex.VariesByCultureFieldName, "y"); //must vary by culture
qry = qry.And().ManagedQuery(term, cultureFields.ToArray());
results = qry.Execute(count);
}
totalRecords = results.TotalItemCount;
- return results.ToPublishedSearchResults(_contentCache);
+
+ return new CultureContextualSearchResults(results.ToPublishedSearchResults(_contentCache), _variationContextAccessor, culture);
}
///
@@ -246,12 +245,76 @@ namespace Umbraco.Web
}
///
- /// Matches a culture iso name suffix
+ /// This is used to contextualize the values in the search results when enumerating over them so that the correct culture values are used
///
- ///
- /// myFieldName_en-us will match the "en-us"
- ///
- private static readonly Regex CultureIsoCodeFieldName = new Regex("^[_\\w]+_([a-z]{2}-[a-z0-9]{2,4})$", RegexOptions.Compiled);
+ private class CultureContextualSearchResults : IEnumerable
+ {
+ private readonly IEnumerable _wrapped;
+ private readonly IVariationContextAccessor _variationContextAccessor;
+ private readonly string _culture;
+
+ public CultureContextualSearchResults(IEnumerable wrapped, IVariationContextAccessor variationContextAccessor, string culture)
+ {
+ _wrapped = wrapped;
+ _variationContextAccessor = variationContextAccessor;
+ _culture = culture;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ //We need to change the current culture to what is requested and then change it back
+ var originalContext = _variationContextAccessor.VariationContext;
+ if (!_culture.IsNullOrWhiteSpace() && !_culture.InvariantEquals(originalContext.Culture))
+ _variationContextAccessor.VariationContext = new VariationContext(_culture);
+
+ //now the IPublishedContent returned will be contextualized to the culture specified and will be reset when the enumerator is disposed
+ return new CultureContextualSearchResultsEnumerator(_wrapped.GetEnumerator(), _variationContextAccessor, originalContext);
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ ///
+ /// Resets the variation context when this is disposed
+ ///
+ private class CultureContextualSearchResultsEnumerator : IEnumerator
+ {
+ private readonly IEnumerator _wrapped;
+ private readonly IVariationContextAccessor _variationContextAccessor;
+ private readonly VariationContext _originalContext;
+
+ public CultureContextualSearchResultsEnumerator(IEnumerator wrapped, IVariationContextAccessor variationContextAccessor, VariationContext originalContext)
+ {
+ _wrapped = wrapped;
+ _variationContextAccessor = variationContextAccessor;
+ _originalContext = originalContext;
+ }
+
+ public void Dispose()
+ {
+ _wrapped.Dispose();
+ //reset
+ _variationContextAccessor.VariationContext = _originalContext;
+ }
+
+ public bool MoveNext()
+ {
+ return _wrapped.MoveNext();
+ }
+
+ public void Reset()
+ {
+ _wrapped.Reset();
+ }
+
+ public PublishedSearchResult Current => _wrapped.Current;
+ object IEnumerator.Current => Current;
+ }
+ }
+
+
#endregion
diff --git a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs
index 99b4e22b5a..1f01270bc6 100644
--- a/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs
+++ b/src/Umbraco.Web/Routing/ContentFinderByLegacy404.cs
@@ -63,7 +63,7 @@ namespace Umbraco.Web.Routing
var error404 = NotFoundHandlerHelper.GetCurrentNotFoundPageId(
_contentConfigSection.Error404Collection.ToArray(),
_entityService,
- new PublishedContentQuery(frequest.UmbracoContext.ContentCache, frequest.UmbracoContext.MediaCache),
+ new PublishedContentQuery(frequest.UmbracoContext.ContentCache, frequest.UmbracoContext.MediaCache, frequest.UmbracoContext.VariationContextAccessor),
errorCulture);
IPublishedContent content = null;
diff --git a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
index 47e73d383c..a60e5f1d1b 100644
--- a/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
+++ b/src/Umbraco.Web/Search/UmbracoTreeSearcher.cs
@@ -367,9 +367,9 @@ namespace Umbraco.Web.Search
{
m.AdditionalData["Email"] = result.Values["email"];
}
- if (result.Values.ContainsKey("__key") && result.Values["__key"] != null)
+ if (result.Values.ContainsKey(UmbracoExamineIndex.NodeKeyFieldName) && result.Values[UmbracoExamineIndex.NodeKeyFieldName] != null)
{
- if (Guid.TryParse(result.Values["__key"], out var key))
+ if (Guid.TryParse(result.Values[UmbracoExamineIndex.NodeKeyFieldName], out var key))
{
m.Key = key;
}
@@ -416,7 +416,7 @@ namespace Umbraco.Web.Search
if (intId.Success)
{
//if it varies by culture, return the default language URL
- if (result.Values.TryGetValue(UmbracoContentIndex.VariesByCultureFieldName, out var varies) && varies == "1")
+ if (result.Values.TryGetValue(UmbracoContentIndex.VariesByCultureFieldName, out var varies) && varies == "y")
{
entity.AdditionalData["Url"] = _umbracoHelper.Url(intId.Result, defaultLang);
}
diff --git a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js
index 75ca46437a..62d294ee6f 100644
--- a/src/Umbraco.Web/UI/JavaScript/JsInitialize.js
+++ b/src/Umbraco.Web/UI/JavaScript/JsInitialize.js
@@ -23,8 +23,11 @@
'lib/ng-file-upload/ng-file-upload.min.js',
'lib/angular-local-storage/angular-local-storage.min.js',
+ 'lib/chart.js/chart.min.js',
+ 'lib/angular-chart.js/angular-chart.min.js',
+
'lib/umbraco/Extensions.js',
-
+
'lib/umbraco/NamespaceManager.js',
'lib/umbraco/LegacySpeechBubble.js',
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index 36550f9b54..f8f33ca0fa 100755
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -63,7 +63,7 @@
-
+
2.6.2.25
diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs
index 6914efb3e2..f4be2a1700 100644
--- a/src/Umbraco.Web/UmbracoHelper.cs
+++ b/src/Umbraco.Web/UmbracoHelper.cs
@@ -106,7 +106,7 @@ namespace Umbraco.Web
/// Gets the query context.
///
public IPublishedContentQuery ContentQuery => _query ??
- (_query = new PublishedContentQuery(UmbracoContext.ContentCache, UmbracoContext.MediaCache));
+ (_query = new PublishedContentQuery(UmbracoContext.ContentCache, UmbracoContext.MediaCache, UmbracoContext.VariationContextAccessor));
///
/// Gets the Umbraco context.