Fix to ensure we use virtual paths for any blocks with custom views (#8597)

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
Co-authored-by: Shannon <sdeminick@gmail.com>
This commit is contained in:
Warren Buckley
2020-08-18 12:43:29 +01:00
committed by GitHub
parent dde735f75c
commit 6d6f525343
4 changed files with 59 additions and 30 deletions

View File

@@ -15,7 +15,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This will convert a virtual path (i.e. ~/App_Plugins/Blah/Test.html ) to an absolute path
*
*
* @param {string} a virtual path, if this is already an absolute path it will just be returned, if this is a relative path an exception will be thrown
*/
convertVirtualToAbsolutePath: function(virtualPath) {
@@ -31,6 +31,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
return Umbraco.Sys.ServerVariables.application.applicationPath + virtualPath.trimStart("~/");
},
/**
* @ngdoc method
* @name umbraco.services.umbRequestHelper#dictionaryToQueryString
@@ -39,7 +40,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This will turn an array of key/value pairs or a standard dictionary into a query string
*
*
* @param {Array} queryStrings An array of key/value pairs
*/
dictionaryToQueryString: function (queryStrings) {
@@ -76,9 +77,9 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This will return the webapi Url for the requested key based on the servervariables collection
*
*
* @param {string} apiName The webapi name that is found in the servervariables["umbracoUrls"] dictionary
* @param {string} actionName The webapi action name
* @param {string} actionName The webapi action name
* @param {object} queryStrings Can be either a string or an array containing key/value pairs
*/
getApiUrl: function (apiName, actionName, queryStrings) {
@@ -103,7 +104,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* This returns a promise with an underlying http call, it is a helper method to reduce
* the amount of duplicate code needed to query http resources and automatically handle any
* the amount of duplicate code needed to query http resources and automatically handle any
* Http errors. See /docs/source/using-promises-resources.md
*
* @param {object} opts A mixed object which can either be a string representing the error message to be
@@ -117,7 +118,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
* The error callback must return an object containing: {errorMsg: errorMessage, data: originalData, status: status }
*/
resourcePromise: function (httpPromise, opts) {
/** The default success callback used if one is not supplied in the opts */
function defaultSuccess(data, status, headers, config) {
//when it's successful, just return the data
@@ -151,7 +152,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
return httpPromise.then(function (response) {
//invoke the callback
//invoke the callback
var result = callbacks.success.apply(this, [response.data, response.status, response.headers, response.config]);
formHelper.showNotifications(response.data);
@@ -183,7 +184,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
overlayService.ysod(error);
}
else {
//show a simple error notification
//show a simple error notification
notificationsService.error("Server error", "Contact administrator, see log for full details.<br/><i>" + result.errorMsg + "</i>");
}
@@ -209,7 +210,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* Used for saving content/media/members specifically
*
*
* @param {Object} args arguments object
* @returns {Promise} http promise object.
*/
@@ -233,7 +234,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
if (args.showNotifications === null || args.showNotifications === undefined) {
args.showNotifications = true;
}
//save the active tab id so we can set it when the data is returned.
var activeTab = _.find(args.content.tabs, function (item) {
return item.active;
@@ -300,7 +301,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
overlayService.ysod(error);
}
else {
//show a simple error notification
//show a simple error notification
notificationsService.error("Server error", "Contact administrator, see log for full details.<br/><i>" + response.data.ExceptionMessage + "</i>");
}
@@ -331,7 +332,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
});
}
else if (!jsonData.key || !jsonData.value) { throw "jsonData object must have both a key and a value property"; }
return $http({
method: 'POST',
url: url,
@@ -366,7 +367,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
return $q.reject(response);
});
},
/**
* @ngdoc method
* @name umbraco.resources.contentResource#downloadFile
@@ -374,7 +375,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
*
* @description
* Downloads a file to the client using AJAX/XHR
*
*
* @param {string} httpPath the path (url) to the resource being downloaded
* @returns {Promise} http promise object.
*/
@@ -388,7 +389,7 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe
// Use an arraybuffer
return $http.get(httpPath, { responseType: 'arraybuffer' })
.then(function (response) {
var octetStreamMime = 'application/octet-stream';
var success = false;

View File

@@ -160,7 +160,7 @@
},
select: function (node) {
const filepath = decodeURIComponent(node.id.replace(/\+/g, " "));
block.view = filepath;
block.view = "~/" + filepath;
editorService.close();
},
close: function () {
@@ -206,7 +206,7 @@
},
select: function (node) {
const filepath = decodeURIComponent(node.id.replace(/\+/g, " "));
block.stylesheet = filepath;
block.stylesheet = "~/" + filepath;
editorService.close();
},
close: function () {
@@ -252,7 +252,8 @@
return !(i.name.indexOf(".jpg") !== -1 || i.name.indexOf(".jpeg") !== -1 || i.name.indexOf(".png") !== -1 || i.name.indexOf(".svg") !== -1 || i.name.indexOf(".webp") !== -1 || i.name.indexOf(".gif") !== -1);
},
select: function (file) {
block.thumbnail = file.name;
const id = decodeURIComponent(file.id.replace(/\+/g, " "));
block.thumbnail = "~/" + id;
editorService.close();
},
close: function () {

View File

@@ -5,11 +5,11 @@
* @ngdoc directive
* @name umbraco.directives.directive:umbBlockListBlock
* @description
* The component to render the view for a block.
* The component to render the view for a block.
* If a stylesheet is used then this uses a ShadowDom to make a scoped element.
* This way the backoffice styling does not collide with the block style.
*/
angular
.module("umbraco")
.component("umbBlockListBlock", {
@@ -29,16 +29,16 @@
}
);
function BlockListBlockController($scope, $compile, $element) {
function BlockListBlockController($scope, $compile, $element, umbRequestHelper) {
var model = this;
model.$onInit = function () {
// This is ugly and is only necessary because we are not using components and instead
// This is ugly and is only necessary because we are not using components and instead
// relying on ng-include. It is definitely possible to compile the contents
// of the view into the DOM using $templateCache and $http instead of using
// ng - include which means that the controllerAs flows directly to the view.
// This would mean that any custom components would need to be updated instead of relying on $scope.
// Guess we'll leave it for now but means all things need to be copied to the $scope and then all
// of the view into the DOM using $templateCache and $http instead of using
// ng - include which means that the controllerAs flows directly to the view.
// This would mean that any custom components would need to be updated instead of relying on $scope.
// Guess we'll leave it for now but means all things need to be copied to the $scope and then all
// primitives need to be watched.
$scope.block = model.block;
@@ -48,8 +48,7 @@
$scope.valFormManager = model.valFormManager;
if (model.stylesheet) {
// TODO: Not sure why this needs a prefixed /? this means it will never work in a virtual directory
model.stylesheet = "/" + model.stylesheet;
model.stylesheet = umbRequestHelper.convertVirtualToAbsolutePath(model.stylesheet);
var shadowRoot = $element[0].attachShadow({ mode: 'open' });
shadowRoot.innerHTML = `
<style>
@@ -60,7 +59,7 @@
$compile(shadowRoot)($scope);
}
else {
$element.append($compile('<div ng-include="model.view"></div>')($scope));
$element.append($compile('<div ng-include="model.view"></div>')($scope));
}
};
@@ -71,6 +70,6 @@
}
}
}
})();

View File

@@ -6,6 +6,13 @@ describe('umbRequestHelper tests', function () {
beforeEach(inject(function ($injector) {
umbRequestHelper = $injector.get('umbRequestHelper');
// set the Umbraco.Sys.ServerVariables.application.applicationPath
if (!Umbraco) Umbraco = {};
if (!Umbraco.Sys) Umbraco.Sys = {};
if (!Umbraco.Sys.ServerVariables) Umbraco.Sys.ServerVariables = {};
if (!Umbraco.Sys.ServerVariables.application) Umbraco.Sys.ServerVariables.application = {};
Umbraco.Sys.ServerVariables.application.applicationPath = "/mysite/";
}));
describe('formatting Urls', function () {
@@ -34,4 +41,25 @@ describe('umbRequestHelper tests', function () {
});
});
describe('Virtual Paths', function () {
it('can convert virtual path to absolute url', function () {
var result = umbRequestHelper.convertVirtualToAbsolutePath("~/App_Plugins/hello/world.css");
expect(result).toBe("/mysite/App_Plugins/hello/world.css");
});
it('can convert absolute path to absolute url', function () {
var result = umbRequestHelper.convertVirtualToAbsolutePath("/App_Plugins/hello/world.css");
expect(result).toBe("/App_Plugins/hello/world.css");
});
it('throws on invalid virtual path', function () {
var relativePath = "App_Plugins/hello/world.css";
expect(function () {
umbRequestHelper.convertVirtualToAbsolutePath(relativePath);
}).toThrow("The path " + relativePath + " is not a virtual path");
});
});
});