Gets the invite fully working along with choosing an avatar, adds more security so that the link can't be used again,

This commit is contained in:
Shannon
2017-06-27 19:55:03 +10:00
parent ab71fa3101
commit d78dcb51c0
17 changed files with 667 additions and 421 deletions

View File

@@ -142,12 +142,14 @@ angular.module("umbraco.directives")
file: file
})
.progress(function(evt) {
// calculate progress in percentage
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
// set percentage property on file
file.uploadProgress = progressPercentage;
// set uploading status on file
file.uploadStatus = "uploading";
if (file.uploadStat !== "done" && file.uploadStat !== "error") {
// calculate progress in percentage
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
// set percentage property on file
file.uploadProgress = progressPercentage;
// set uploading status on file
file.uploadStatus = "uploading";
}
})
.success(function(data, status, headers, config) {
if (data.notifications && data.notifications.length > 0) {
@@ -160,6 +162,7 @@ angular.module("umbraco.directives")
} else {
// set done status on file
file.uploadStatus = "done";
file.uploadProgress = 100;
// set date/time for when done - used for sorting
file.doneDate = new Date();
// Put the file in the done pool

View File

@@ -195,22 +195,7 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
}),
'Password reset code validation failed for userId ' + userId + ', code' + resetCode);
},
performSetInvitedUserPassword: function (newPassword) {
if (!newPassword) {
return angularHelper.rejectedPromise({ errorMsg: 'newPassword cannot be empty' });
}
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"authenticationApiBaseUrl",
"PostSetInvitedUserPassword"),
angular.toJson(newPassword)),
'Failed to change password');
},
/**
* @ngdoc method
* @name umbraco.resources.currentUserResource#getMembershipProviderConfig

View File

@@ -10,6 +10,21 @@ function currentUserResource($q, $http, umbRequestHelper) {
//the factory object returned
return {
performSetInvitedUserPassword: function (newPassword) {
if (!newPassword) {
return angularHelper.rejectedPromise({ errorMsg: 'newPassword cannot be empty' });
}
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"currentUserApiBaseUrl",
"PostSetInvitedUserPassword"),
angular.toJson(newPassword)),
'Failed to change password');
},
/**
* @ngdoc method
* @name umbraco.resources.currentUserResource#changePassword

View File

@@ -1,5 +1,5 @@
angular.module("umbraco").controller("Umbraco.Dialogs.LoginController",
function ($scope, $cookies, $location, currentUserResource, formHelper, localizationService, userService, externalLoginInfo, resetPasswordCodeInfo, $timeout, authResource, dialogService, $q) {
function ($scope, $cookies, $location, currentUserResource, formHelper, mediaHelper, umbRequestHelper, Upload, localizationService, userService, externalLoginInfo, resetPasswordCodeInfo, $timeout, authResource, dialogService, $q) {
$scope.invitedUser = null;
$scope.invitedUserPasswordModel = {
@@ -9,10 +9,19 @@
passwordPolicies: null,
passwordPolicyText: ""
}
$scope.avatarFile = {
filesHolder: null,
uploadStatus: null,
uploadProgress: 0,
maxFileSize: Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB",
acceptedFileTypes: mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes),
uploaded: false
}
function init() {
// Check if it is a new user
if ($location.search().invite) {
var inviteVal = $location.search().invite;
if (inviteVal && (inviteVal === "1" || inviteVal === "2")) {
$q.all([
//get the current invite user
@@ -26,7 +35,7 @@
//get the membership provider config for password policies
authResource.getMembershipProviderConfig().then(function (data) {
$scope.invitedUserPasswordModel.passwordPolicies = data;
//localize the text
localizationService.localize("errorHandling_errorInPasswordFormat",
[
@@ -36,27 +45,105 @@
$scope.invitedUserPasswordModel.passwordPolicyText = data;
});
})
]).then(function() {
$scope.inviteSetPassword = true;
]).then(function () {
$scope.inviteStep = Number(inviteVal);
});
}
}
$scope.changeAvatar = function (files, event) {
if (files && files.length > 0) {
upload(files[0]);
}
};
$scope.getStarted = function() {
$location.search('invite', null);
$scope.submit(true);
}
function upload(file) {
$scope.avatarFile.uploadProgress = 0;
Upload.upload({
url: umbRequestHelper.getApiUrl("currentUserApiBaseUrl", "PostSetAvatar"),
fields: {},
file: file
}).progress(function (evt) {
if ($scope.avatarFile.uploadStatus !== "done" && $scope.avatarFile.uploadStatus !== "error") {
// set uploading status on file
$scope.avatarFile.uploadStatus = "uploading";
// calculate progress in percentage
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
// set percentage property on file
$scope.avatarFile.uploadProgress = progressPercentage;
}
}).success(function (data, status, headers, config) {
$scope.avatarFile.uploadProgress = 100;
// set done status on file
$scope.avatarFile.uploadStatus = "done";
$scope.invitedUser.avatars = data;
$scope.avatarFile.uploaded = true;
}).error(function (evt, status, headers, config) {
// set status done
$scope.avatarFile.uploadStatus = "error";
// If file not found, server will return a 404 and display this message
if (status === 404) {
$scope.avatarFile.serverErrorMessage = "File not found";
}
else if (status == 400) {
//it's a validation error
$scope.avatarFile.serverErrorMessage = evt.message;
}
else {
//it's an unhandled error
//if the service returns a detailed error
if (evt.InnerException) {
$scope.avatarFile.serverErrorMessage = evt.InnerException.ExceptionMessage;
//Check if its the common "too large file" exception
if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) {
$scope.avatarFile.serverErrorMessage = "File too large to upload";
}
} else if (evt.Message) {
$scope.avatarFile.serverErrorMessage = evt.Message;
}
}
});
}
$scope.inviteSavePassword = function () {
if (formHelper.submitForm({ scope: $scope, statusMessage: "Saving..." })) {
$scope.invitedUserPasswordModel.buttonState = "busy";
authResource.performSetInvitedUserPassword($scope.invitedUserPasswordModel.password)
currentUserResource.performSetInvitedUserPassword($scope.invitedUserPasswordModel.password)
.then(function (data) {
//success
formHelper.resetForm({ scope: $scope, notifications: data.notifications });
$scope.invitedUserPasswordModel.buttonState = "success";
$scope.invitedUserPasswordModel.buttonState = "success";
//set the user and set them as logged in
$scope.invitedUser = data;
userService.setAuthenticationSuccessful(data);
$scope.inviteSetPassword = false;
$scope.inviteSetAvatar = true;
$scope.inviteStep = 2;
}, function(err) {

View File

@@ -11,7 +11,7 @@
<div ng-show="invitedUser != null" class="umb-login-container">
<form name="inviteUserPasswordForm" novalidate="" ng-submit="inviteSavePassword()" val-form-manager>
<div class="form" ng-if="inviteSetPassword">
<div class="form" ng-if="inviteStep === 1">
<h1 style="margin-bottom: 10px; text-align: left;">Hi, {{invitedUser.name}}</h1>
<p style="line-height: 1.6; margin-bottom: 25px;">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non libero vel turpis ultrices pharetra.</p>
@@ -44,10 +44,41 @@
</div>
</form>
<div class="form" ng-if="inviteSetAvatar">
<div class="form" ng-if="inviteStep === 2">
<div class="flex justify-center items-center">
<div style="border-radius: 50%; width: 100px; height: 100px; font-size: 50px; text-align: center; display: flex; align-items: center; justify-content: center; background-color: #F3F3F5; border: 2px dashed #A2A1A6; color: #A2A1A6;">+</div>
<ng-form name="avatarForm">
<umb-avatar style="margin-bottom: 10px;"
ng-if="invitedUser.avatars.length > 0"
color="secondary"
size="xxl"
name="{{invitedUser.name}}"
img-src="{{invitedUser.avatars[3]}}"
img-srcset="{{invitedUser.avatars[4]}} 2x, {{invitedUser.avatars[4]}} 3x">
</umb-avatar>
<umb-progress-bar style="max-width: 100px;"
ng-if="avatarFile.uploadStatus === 'uploading'"
progress="{{ avatarFile.uploadProgress }}"
size="s">
</umb-progress-bar>
<div class="umb-info-local-item text-error mt3" ng-if="avatarFile.uploadStatus === 'error'">
{{ avatarFile.serverErrorMessage }}
</div>
<a style="border-radius: 50%; width: 100px; height: 100px; font-size: 50px; text-align: center; display: flex; align-items: center; justify-content: center; background-color: #F3F3F5; border: 2px dashed #A2A1A6; color: #A2A1A6;"
ng-if="avatarFile.uploadStatus !== 'uploading'"
ngf-select
ng-model="avatarFile.filesHolder"
ngf-change="changeAvatar($files, $event)"
ngf-multiple="false"
ngf-pattern="{{avatarFile.acceptedFileTypes}}"
ngf-max-size="{{ avatarFile.maxFileSize }}">+</a>
</ng-form>
</div>
<h1 style="margin-bottom: 10px;">Upload a photo</h1>
@@ -55,9 +86,11 @@
<div class="flex justify-center items-center">
<umb-button type="button"
button-style="success"
label="Get started">
label="Get started"
action="getStarted()">
</umb-button>
<umb-button type="button"
<umb-button ng-if="!avatarFile.uploaded"
type="button"
button-style="link"
label="Skip">
</umb-button>

View File

@@ -68,6 +68,7 @@
// set done status on file
vm.zipFile.uploadStatus = "done";
loadPackage();
vm.zipFile.uploadProgress = 100;
vm.localPackage = data;
}

View File

@@ -258,20 +258,22 @@
file: file
}).progress(function (evt) {
// set uploading status on file
vm.avatarFile.uploadStatus = "uploading";
if (vm.avatarFile.uploadStatus !== "done" && vm.avatarFile.uploadStatus !== "error") {
// set uploading status on file
vm.avatarFile.uploadStatus = "uploading";
// calculate progress in percentage
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
// calculate progress in percentage
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
// set percentage property on file
vm.avatarFile.uploadProgress = progressPercentage;
// set percentage property on file
vm.avatarFile.uploadProgress = progressPercentage;
}
}).success(function (data, status, headers, config) {
// set done status on file
vm.avatarFile.uploadStatus = "done";
vm.avatarFile.uploadProgress = 100;
vm.user.avatars = data;
}).error(function (evt, status, headers, config) {

View File

@@ -182,6 +182,7 @@
<ng-form name="avatarForm" class="flex flex-column justify-center items-center">
<umb-avatar style="margin-bottom: 10px;"
ng-if="invitedUser.avatars.length > 0"
color="secondary"
size="xxl"
name="{{vm.user.name}}"

View File

@@ -1510,7 +1510,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="createUser">Create user</key>
<key alias="inviteEmailCopySubject">Umbraco: Invitation</key>
<key alias="inviteEmailCopyFormat">
<![CDATA[<html><body><p>Hi %0%, you have been invited by %1% to the Umbraco Back Office.</p><p>Message from %1%: <em>%2</em></p><p>Click this <a href="%3" target="_blank">link</a> to accept the invite</p><p><small>If you cannot click on the link, copy and paste this URL into your browser window<br/><br/>%3</small></p></body></html>]]>
<![CDATA[<html><body><p>Hi %0%, you have been invited by %1% to the Umbraco Back Office.</p><p>Message from %1%: <em>%2%</em></p><p>Click this <a href="%3%" target="_blank">link</a> to accept the invite</p><p><small>If you cannot click on the link, copy and paste this URL into your browser window<br/><br/>%3%</small></p></body></html>]]>
</key>
</area>

View File

@@ -1515,7 +1515,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="backToUsers">Back to users</key>
<key alias="inviteEmailCopySubject">Umbraco: Invitation</key>
<key alias="inviteEmailCopyFormat">
<![CDATA[<html><body><p>Hi %0%, you have been invited by %1% to the Umbraco Back Office.</p><p>Message from %1%: <em>%2</em></p><p>Click this <a href="%3" target="_blank">link</a> to accept the invite</p><p><small>If you cannot click on the link, copy and paste this URL into your browser window<br/><br/>%3</small></p></body></html>]]>
<![CDATA[<html><body><p>Hi %0%, you have been invited by %1% to the Umbraco Back Office.</p><p>Message from %1%: <em>%2%</em></p><p>Click this <a href="%3%" target="_blank">link</a> to accept the invite</p><p><small>If you cannot click on the link, copy and paste this URL into your browser window<br/><br/>%3%</small></p></body></html>]]>
</key>
</area>
<area alias="validation">

View File

@@ -152,7 +152,7 @@ namespace Umbraco.Web.Editors
[SetAngularAntiForgeryTokens]
public UserDetail GetCurrentUser()
{
var user = Services.UserService.GetUserById(UmbracoContext.Security.GetUserId());
var user = UmbracoContext.Security.CurrentUser;
var result = Mapper.Map<UserDetail>(user);
var httpContextAttempt = TryGetHttpContext();
if (httpContextAttempt.Success)
@@ -176,7 +176,14 @@ namespace Umbraco.Web.Editors
[SetAngularAntiForgeryTokens]
public UserDetail GetCurrentInvitedUser()
{
var user = Services.UserService.GetUserById(UmbracoContext.Security.GetUserId());
var user = UmbracoContext.Security.CurrentUser;
if (user.IsApproved)
{
//if they are approved, than they are no longer invited and we can return an error
throw new HttpResponseException(Request.CreateUserNoAccessResponse());
}
var result = Mapper.Map<UserDetail>(user);
var httpContextAttempt = TryGetHttpContext();
if (httpContextAttempt.Success)
@@ -367,39 +374,7 @@ namespace Umbraco.Web.Editors
return Request.CreateValidationErrorResponse("Invalid code");
}
}
/// <summary>
/// When a user is invited and they click on the invitation link, they will be partially logged in
/// where they can set their username/password
/// </summary>
/// <param name="newPassword"></param>
/// <returns></returns>
/// <remarks>
/// This only works when the user is logged in (partially)
/// </remarks>
[WebApi.UmbracoAuthorize(requireApproval: false)]
public async Task<HttpResponseMessage> PostSetInvitedUserPassword([FromBody]string newPassword)
{
var result = await UserManager.AddPasswordAsync(Security.GetUserId(), newPassword);
if (result.Succeeded == false)
{
//it wasn't successful, so add the change error to the model state, we've name the property alias _umb_password on the form
// so that is why it is being used here.
ModelState.AddModelError(
"value",
string.Join(", ", result.Errors));
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
//They've successfully set their password, we can now update their user account to be approved
Security.CurrentUser.IsApproved = true;
Services.UserService.Save(Security.CurrentUser);
return Request.CreateResponse(HttpStatusCode.OK);
}
/// <summary>
/// Processes a set password request. Validates the request and sets a new password.
/// </summary>

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.IO;
using System.Linq;
@@ -11,10 +10,8 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;
using System.Web.UI;
using ClientDependency.Core.Config;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
@@ -23,23 +20,18 @@ using Newtonsoft.Json.Linq;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Manifest;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Identity;
using Umbraco.Core.Security;
using Umbraco.Web.HealthCheck;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.PropertyEditors;
using Umbraco.Web.Security.Identity;
using Umbraco.Web.Trees;
using Umbraco.Web.UI.JavaScript;
using Umbraco.Web.WebApi.Filters;
using Umbraco.Web.WebServices;
using Umbraco.Core.Services;
using Umbraco.Web.Security;
using Action = umbraco.BusinessLogic.Actions.Action;
@@ -143,7 +135,7 @@ namespace Umbraco.Web.Editors
await SignInManager.SignInAsync(identityUser, false, false);
return new RedirectResult(Url.Action("Default") + "#/login/false?invite=true");
return new RedirectResult(Url.Action("Default") + "#/login/false?invite=1");
}
/// <summary>
@@ -252,12 +244,7 @@ namespace Umbraco.Web.Editors
return new JsonNetResult { Data = gridConfig.EditorsConfig.Editors, Formatting = Formatting.Indented };
}
private string GetMaxRequestLength()
{
var section = ConfigurationManager.GetSection("system.web/httpRuntime") as HttpRuntimeSection;
if (section == null) return string.Empty;
return section.MaxRequestLength.ToString();
}
/// <summary>
/// Returns the JavaScript object representing the static server variables javascript object
@@ -267,248 +254,19 @@ namespace Umbraco.Web.Editors
[MinifyJavaScriptResult(Order = 1)]
public JavaScriptResult ServerVariables()
{
Func<string> getResult = () =>
{
var defaultVals = new Dictionary<string, object>
{
{
"umbracoUrls", new Dictionary<string, object>
{
//TODO: Add 'umbracoApiControllerBaseUrl' which people can use in JS
// to prepend their URL. We could then also use this in our own resources instead of
// having each url defined here explicitly - we can do that in v8! for now
// for umbraco services we'll stick to explicitly defining the endpoints.
{"externalLoginsUrl", Url.Action("ExternalLogin", "BackOffice")},
{"externalLinkLoginsUrl", Url.Action("LinkLogin", "BackOffice")},
{"legacyTreeJs", Url.Action("LegacyTreeJs", "BackOffice")},
{"manifestAssetList", Url.Action("GetManifestAssetList", "BackOffice")},
{"gridConfig", Url.Action("GetGridConfig", "BackOffice")},
{"serverVarsJs", Url.Action("Application", "BackOffice")},
//API URLs
{
"packagesRestApiBaseUrl", UmbracoConfig.For.UmbracoSettings().PackageRepositories.GetDefault().RestApiUrl
},
{
"redirectUrlManagementApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<RedirectUrlManagementController>(
controller => controller.GetEnableState())
},
{
"embedApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<RteEmbedController>(
controller => controller.GetEmbed("", 0, 0))
},
{
"userApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<UsersController>(
controller => controller.PostSaveUser(null))
},
{
"contentApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ContentController>(
controller => controller.PostSave(null))
},
{
"mediaApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MediaController>(
controller => controller.GetRootMedia())
},
{
"imagesApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ImagesController>(
controller => controller.GetBigThumbnail(0))
},
{
"sectionApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<SectionController>(
controller => controller.GetSections())
},
{
"treeApplicationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ApplicationTreeController>(
controller => controller.GetApplicationTrees(null, null, null, true))
},
{
"contentTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ContentTypeController>(
controller => controller.GetAllowedChildren(0))
},
{
"mediaTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MediaTypeController>(
controller => controller.GetAllowedChildren(0))
},
{
"macroApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MacroController>(
controller => controller.GetMacroParameters(0))
},
{
"authenticationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<AuthenticationController>(
controller => controller.PostLogin(null))
},
{
"currentUserApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<CurrentUserController>(
controller => controller.PostChangePassword(null))
},
{
"legacyApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<LegacyController>(
controller => controller.DeleteLegacyItem(null, null, null))
},
{
"entityApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<EntityController>(
controller => controller.GetById(0, UmbracoEntityTypes.Media))
},
{
"dataTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<DataTypeController>(
controller => controller.GetById(0))
},
{
"dashboardApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<DashboardController>(
controller => controller.GetDashboard(null))
},
{
"logApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<LogController>(
controller => controller.GetEntityLog(0))
},
{
"memberApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MemberController>(
controller => controller.GetByKey(Guid.Empty))
},
{
"packageInstallApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<PackageInstallController>(
controller => controller.Fetch(string.Empty))
},
{
"relationApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<RelationController>(
controller => controller.GetById(0))
},
{
"rteApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<RichTextPreValueController>(
controller => controller.GetConfiguration())
},
{
"stylesheetApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<StylesheetController>(
controller => controller.GetAll())
},
{
"memberTypeApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MemberTypeController>(
controller => controller.GetAllTypes())
},
{
"updateCheckApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<UpdateCheckController>(
controller => controller.GetCheck())
},
{
"tagApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<TagsController>(
controller => controller.GetAllTags(null))
},
{
"templateApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<TemplateController>(
controller => controller.GetById(0))
},
{
"memberTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MemberTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"mediaTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl<MediaTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"contentTreeBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ContentTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"tagsDataBaseUrl", Url.GetUmbracoApiServiceBaseUrl<TagsDataController>(
controller => controller.GetTags(""))
},
{
"examineMgmtBaseUrl", Url.GetUmbracoApiServiceBaseUrl<ExamineManagementApiController>(
controller => controller.GetIndexerDetails())
},
{
"xmlDataIntegrityBaseUrl", Url.GetUmbracoApiServiceBaseUrl<XmlDataIntegrityController>(
controller => controller.CheckContentXmlTable())
},
{
"healthCheckBaseUrl", Url.GetUmbracoApiServiceBaseUrl<HealthCheckController>(
controller => controller.GetAllHealthChecks())
},
{
"templateQueryApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<TemplateQueryController>(
controller => controller.PostTemplateQuery(null))
},
{
"codeFileApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl<CodeFileController>(
controller => controller.GetByPath("", ""))
}
}
},
{
"umbracoSettings", new Dictionary<string, object>
{
{"umbracoPath", GlobalSettings.Path},
{"mediaPath", IOHelper.ResolveUrl(SystemDirectories.Media).TrimEnd('/')},
{"appPluginsPath", IOHelper.ResolveUrl(SystemDirectories.AppPlugins).TrimEnd('/')},
{
"imageFileTypes",
string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes)
},
{
"disallowedUploadFiles",
string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.DisallowedUploadFiles)
},
{
"allowedUploadFiles",
string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.AllowedUploadFiles)
},
{
"maxFileSize",
GetMaxRequestLength()
},
{"keepUserLoggedIn", UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn},
{"cssPath", IOHelper.ResolveUrl(SystemDirectories.Css).TrimEnd('/')},
{"allowPasswordReset", UmbracoConfig.For.UmbracoSettings().Security.AllowPasswordReset},
{"loginBackgroundImage", UmbracoConfig.For.UmbracoSettings().Content.LoginBackgroundImage},
{"emailServerConfigured", GlobalSettings.HasSmtpServerConfigured(HttpContext.Request.ApplicationPath)},
}
},
{
"umbracoPlugins", new Dictionary<string, object>
{
{"trees", GetTreePluginsMetaData()}
}
},
{
"isDebuggingEnabled", HttpContext.IsDebuggingEnabled
},
{
"application", GetApplicationState()
},
{
"externalLogins", new Dictionary<string, object>
{
{
"providers", HttpContext.GetOwinContext().Authentication.GetExternalAuthenticationTypes()
.Where(p => p.Properties.ContainsKey("UmbracoBackOffice"))
.Select(p => new
{
authType = p.AuthenticationType, caption = p.Caption,
//TODO: Need to see if this exposes any sensitive data!
properties = p.Properties
})
.ToArray()
}
}
}
};
//Parse the variables to a string
return ServerVariablesParser.Parse(defaultVals);
};
var serverVars = new BackOfficeServerVariables(Url, ApplicationContext, UmbracoConfig.For.UmbracoSettings());
//cache the result if debugging is disabled
var result = HttpContext.IsDebuggingEnabled
? getResult()
? ServerVariablesParser.Parse(serverVars.GetServerVariables())
: ApplicationContext.ApplicationCache.RuntimeCache.GetCacheItem<string>(
typeof(BackOfficeController) + "ServerVariables",
() => getResult(),
() => ServerVariablesParser.Parse(serverVars.GetServerVariables()),
new TimeSpan(0, 10, 0));
return JavaScript(result);
}
[HttpPost]
@@ -739,71 +497,8 @@ namespace Umbraco.Web.Editors
return false;
}
/// <summary>
/// Returns the server variables regarding the application state
/// </summary>
/// <returns></returns>
private Dictionary<string, object> GetApplicationState()
{
if (ApplicationContext.IsConfigured == false)
return null;
var app = new Dictionary<string, object>
{
{"assemblyVersion", UmbracoVersion.AssemblyVersion}
};
var version = UmbracoVersion.GetSemanticVersion().ToSemanticString();
app.Add("cacheBuster", string.Format("{0}.{1}", version, ClientDependencySettings.Instance.Version).GenerateHash());
app.Add("version", version);
//useful for dealing with virtual paths on the client side when hosted in virtual directories especially
app.Add("applicationPath", HttpContext.Request.ApplicationPath.EnsureEndsWith('/'));
//add the server's GMT time offset in minutes
app.Add("serverTimeOffset", Convert.ToInt32(DateTimeOffset.Now.Offset.TotalMinutes));
return app;
}
/// <summary>
/// A lazy reference to all tree controller types
/// </summary>
/// <remarks>
/// We are doing this because if we constantly resolve the tree controller types from the PluginManager it will re-scan and also re-log that
/// it's resolving which is unecessary and annoying.
/// </remarks>
private static readonly Lazy<IEnumerable<Type>> TreeControllerTypes = new Lazy<IEnumerable<Type>>(() => PluginManager.Current.ResolveAttributedTreeControllers().ToArray());
private IEnumerable<Dictionary<string, string>> GetTreePluginsMetaData()
{
var treeTypes = TreeControllerTypes.Value;
//get all plugin trees with their attributes
var treesWithAttributes = treeTypes.Select(x => new
{
tree = x,
attributes =
x.GetCustomAttributes(false)
}).ToArray();
var pluginTreesWithAttributes = treesWithAttributes
//don't resolve any tree decorated with CoreTreeAttribute
.Where(x => x.attributes.All(a => (a is CoreTreeAttribute) == false))
//we only care about trees with the PluginControllerAttribute
.Where(x => x.attributes.Any(a => a is PluginControllerAttribute))
.ToArray();
return (from p in pluginTreesWithAttributes
let treeAttr = p.attributes.OfType<TreeAttribute>().Single()
let pluginAttr = p.attributes.OfType<PluginControllerAttribute>().Single()
select new Dictionary<string, string>
{
{"alias", treeAttr.Alias}, {"packageFolder", pluginAttr.AreaName}
}).ToArray();
}
/// <summary>
/// Returns the JavaScript blocks for any legacy trees declared

View File

@@ -0,0 +1,395 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;
using ClientDependency.Core.Config;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Web.HealthCheck;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.PropertyEditors;
using Umbraco.Web.Trees;
using Umbraco.Web.WebServices;
namespace Umbraco.Web.Editors
{
/// <summary>
/// Used to collect the server variables for use in the back office angular app
/// </summary>
internal class BackOfficeServerVariables
{
private readonly UrlHelper _urlHelper;
private readonly ApplicationContext _applicationContext;
private readonly HttpContextBase _httpContext;
private readonly IOwinContext _owinContext;
public BackOfficeServerVariables(UrlHelper urlHelper, ApplicationContext applicationContext, IUmbracoSettingsSection umbracoSettings)
{
_urlHelper = urlHelper;
_applicationContext = applicationContext;
_httpContext = _urlHelper.RequestContext.HttpContext;
_owinContext = _httpContext.GetOwinContext();
}
/// <summary>
/// Returns the server variables for non-authenticated users
/// </summary>
/// <returns></returns>
internal Dictionary<string, object> BareMinimumServerVariables()
{
//this is the filter for the keys that we'll keep based on the full version of the server vars
var keepOnlyKeys = new Dictionary<string, string[]>
{
{"umbracoUrls", new[] {"authenticationApiBaseUrl", "serverVarsJs", "externalLoginsUrl", "currentUserApiBaseUrl"}},
{"umbracoSettings", new[] {"allowPasswordReset", "imageFileTypes", "maxFileSize", "loginBackgroundImage"}},
{"application", new[] {"applicationPath", "cacheBuster"}},
{"isDebuggingEnabled", new string[] { }}
};
//now do the filtering...
var defaults = GetServerVariables();
foreach (var key in defaults.Keys.ToArray())
{
if (keepOnlyKeys.ContainsKey(key) == false)
{
defaults.Remove(key);
}
else
{
var asDictionary = defaults[key] as IDictionary;
if (asDictionary != null)
{
var toKeep = keepOnlyKeys[key];
foreach (var k in asDictionary.Keys.Cast<string>().ToArray())
{
if (toKeep.Contains(k) == false)
{
asDictionary.Remove(k);
}
}
}
}
}
//TODO: This is ultra confusing! this same key is used for different things, when returning the full app when authenticated it is this URL but when not auth'd it's actually the ServerVariables address
// so based on compat and how things are currently working we need to replace the serverVarsJs one
((Dictionary<string, object>) defaults["umbracoUrls"])["serverVarsJs"] = _urlHelper.Action("ServerVariables", "BackOffice");
return defaults;
}
/// <summary>
/// Returns the server variables for authenticated users
/// </summary>
/// <returns></returns>
internal Dictionary<string, object> GetServerVariables()
{
var defaultVals = new Dictionary<string, object>
{
{
"umbracoUrls", new Dictionary<string, object>
{
//TODO: Add 'umbracoApiControllerBaseUrl' which people can use in JS
// to prepend their URL. We could then also use this in our own resources instead of
// having each url defined here explicitly - we can do that in v8! for now
// for umbraco services we'll stick to explicitly defining the endpoints.
{"externalLoginsUrl", _urlHelper.Action("ExternalLogin", "BackOffice")},
{"externalLinkLoginsUrl", _urlHelper.Action("LinkLogin", "BackOffice")},
{"legacyTreeJs", _urlHelper.Action("LegacyTreeJs", "BackOffice")},
{"manifestAssetList", _urlHelper.Action("GetManifestAssetList", "BackOffice")},
{"gridConfig", _urlHelper.Action("GetGridConfig", "BackOffice")},
//TODO: This is ultra confusing! this same key is used for different things, when returning the full app when authenticated it is this URL but when not auth'd it's actually the ServerVariables address
{"serverVarsJs", _urlHelper.Action("Application", "BackOffice")},
//API URLs
{
"packagesRestApiBaseUrl", UmbracoConfig.For.UmbracoSettings().PackageRepositories.GetDefault().RestApiUrl
},
{
"redirectUrlManagementApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<RedirectUrlManagementController>(
controller => controller.GetEnableState())
},
{
"embedApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<RteEmbedController>(
controller => controller.GetEmbed("", 0, 0))
},
{
"userApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<UsersController>(
controller => controller.PostSaveUser(null))
},
{
"contentApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ContentController>(
controller => controller.PostSave(null))
},
{
"mediaApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MediaController>(
controller => controller.GetRootMedia())
},
{
"imagesApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ImagesController>(
controller => controller.GetBigThumbnail(0))
},
{
"sectionApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<SectionController>(
controller => controller.GetSections())
},
{
"treeApplicationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ApplicationTreeController>(
controller => controller.GetApplicationTrees(null, null, null, true))
},
{
"contentTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ContentTypeController>(
controller => controller.GetAllowedChildren(0))
},
{
"mediaTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MediaTypeController>(
controller => controller.GetAllowedChildren(0))
},
{
"macroApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MacroController>(
controller => controller.GetMacroParameters(0))
},
{
"authenticationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<AuthenticationController>(
controller => controller.PostLogin(null))
},
{
"currentUserApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<CurrentUserController>(
controller => controller.PostChangePassword(null))
},
{
"legacyApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<LegacyController>(
controller => controller.DeleteLegacyItem(null, null, null))
},
{
"entityApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<EntityController>(
controller => controller.GetById(0, UmbracoEntityTypes.Media))
},
{
"dataTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<DataTypeController>(
controller => controller.GetById(0))
},
{
"dashboardApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<DashboardController>(
controller => controller.GetDashboard(null))
},
{
"logApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<LogController>(
controller => controller.GetEntityLog(0))
},
{
"memberApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MemberController>(
controller => controller.GetByKey(Guid.Empty))
},
{
"packageInstallApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<PackageInstallController>(
controller => controller.Fetch(string.Empty))
},
{
"relationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<RelationController>(
controller => controller.GetById(0))
},
{
"rteApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<RichTextPreValueController>(
controller => controller.GetConfiguration())
},
{
"stylesheetApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<StylesheetController>(
controller => controller.GetAll())
},
{
"memberTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MemberTypeController>(
controller => controller.GetAllTypes())
},
{
"updateCheckApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<UpdateCheckController>(
controller => controller.GetCheck())
},
{
"tagApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<TagsController>(
controller => controller.GetAllTags(null))
},
{
"templateApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<TemplateController>(
controller => controller.GetById(0))
},
{
"memberTreeBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MemberTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"mediaTreeBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MediaTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"contentTreeBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ContentTreeController>(
controller => controller.GetNodes("-1", null))
},
{
"tagsDataBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<TagsDataController>(
controller => controller.GetTags(""))
},
{
"examineMgmtBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ExamineManagementApiController>(
controller => controller.GetIndexerDetails())
},
{
"xmlDataIntegrityBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<XmlDataIntegrityController>(
controller => controller.CheckContentXmlTable())
},
{
"healthCheckBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<HealthCheckController>(
controller => controller.GetAllHealthChecks())
},
{
"templateQueryApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<TemplateQueryController>(
controller => controller.PostTemplateQuery(null))
},
{
"codeFileApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<CodeFileController>(
controller => controller.GetByPath("", ""))
}
}
},
{
"umbracoSettings", new Dictionary<string, object>
{
{"umbracoPath", GlobalSettings.Path},
{"mediaPath", IOHelper.ResolveUrl(SystemDirectories.Media).TrimEnd('/')},
{"appPluginsPath", IOHelper.ResolveUrl(SystemDirectories.AppPlugins).TrimEnd('/')},
{
"imageFileTypes",
string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes)
},
{
"disallowedUploadFiles",
string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.DisallowedUploadFiles)
},
{
"allowedUploadFiles",
string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.AllowedUploadFiles)
},
{
"maxFileSize",
GetMaxRequestLength()
},
{"keepUserLoggedIn", UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn},
{"cssPath", IOHelper.ResolveUrl(SystemDirectories.Css).TrimEnd('/')},
{"allowPasswordReset", UmbracoConfig.For.UmbracoSettings().Security.AllowPasswordReset},
{"loginBackgroundImage", UmbracoConfig.For.UmbracoSettings().Content.LoginBackgroundImage},
{"emailServerConfigured", GlobalSettings.HasSmtpServerConfigured(_httpContext.Request.ApplicationPath)},
}
},
{
"umbracoPlugins", new Dictionary<string, object>
{
{"trees", GetTreePluginsMetaData()}
}
},
{
"isDebuggingEnabled", _httpContext.IsDebuggingEnabled
},
{
"application", GetApplicationState()
},
{
"externalLogins", new Dictionary<string, object>
{
{
"providers", _owinContext.Authentication.GetExternalAuthenticationTypes()
.Where(p => p.Properties.ContainsKey("UmbracoBackOffice"))
.Select(p => new
{
authType = p.AuthenticationType, caption = p.Caption,
//TODO: Need to see if this exposes any sensitive data!
properties = p.Properties
})
.ToArray()
}
}
}
};
return defaultVals;
}
private IEnumerable<Dictionary<string, string>> GetTreePluginsMetaData()
{
var treeTypes = TreeControllerTypes.Value;
//get all plugin trees with their attributes
var treesWithAttributes = treeTypes.Select(x => new
{
tree = x,
attributes =
x.GetCustomAttributes(false)
}).ToArray();
var pluginTreesWithAttributes = treesWithAttributes
//don't resolve any tree decorated with CoreTreeAttribute
.Where(x => x.attributes.All(a => (a is CoreTreeAttribute) == false))
//we only care about trees with the PluginControllerAttribute
.Where(x => x.attributes.Any(a => a is PluginControllerAttribute))
.ToArray();
return (from p in pluginTreesWithAttributes
let treeAttr = p.attributes.OfType<TreeAttribute>().Single()
let pluginAttr = p.attributes.OfType<PluginControllerAttribute>().Single()
select new Dictionary<string, string>
{
{"alias", treeAttr.Alias}, {"packageFolder", pluginAttr.AreaName}
}).ToArray();
}
/// <summary>
/// A lazy reference to all tree controller types
/// </summary>
/// <remarks>
/// We are doing this because if we constantly resolve the tree controller types from the PluginManager it will re-scan and also re-log that
/// it's resolving which is unecessary and annoying.
/// </remarks>
private static readonly Lazy<IEnumerable<Type>> TreeControllerTypes = new Lazy<IEnumerable<Type>>(() => PluginManager.Current.ResolveAttributedTreeControllers().ToArray());
/// <summary>
/// Returns the server variables regarding the application state
/// </summary>
/// <returns></returns>
private Dictionary<string, object> GetApplicationState()
{
if (_applicationContext.IsConfigured == false)
return null;
var app = new Dictionary<string, object>
{
{"assemblyVersion", UmbracoVersion.AssemblyVersion}
};
var version = UmbracoVersion.GetSemanticVersion().ToSemanticString();
app.Add("cacheBuster", string.Format("{0}.{1}", version, ClientDependencySettings.Instance.Version).GenerateHash());
app.Add("version", version);
//useful for dealing with virtual paths on the client side when hosted in virtual directories especially
app.Add("applicationPath", _httpContext.Request.ApplicationPath.EnsureEndsWith('/'));
//add the server's GMT time offset in minutes
app.Add("serverTimeOffset", Convert.ToInt32(DateTimeOffset.Now.Offset.TotalMinutes));
return app;
}
private string GetMaxRequestLength()
{
var section = ConfigurationManager.GetSection("system.web/httpRuntime") as HttpRuntimeSection;
if (section == null) return string.Empty;
return section.MaxRequestLength.ToString();
}
}
}

View File

@@ -17,6 +17,8 @@ using umbraco;
using legacyUser = umbraco.BusinessLogic.User;
using System.Net.Http;
using System.Collections.Specialized;
using Umbraco.Core.Security;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
@@ -28,7 +30,55 @@ namespace Umbraco.Web.Editors
[PluginController("UmbracoApi")]
public class CurrentUserController : UmbracoAuthorizedJsonController
{
/// <summary>
/// When a user is invited and they click on the invitation link, they will be partially logged in
/// where they can set their username/password
/// </summary>
/// <param name="newPassword"></param>
/// <returns></returns>
/// <remarks>
/// This only works when the user is logged in (partially)
/// </remarks>
[WebApi.UmbracoAuthorize(requireApproval: false)]
[OverrideAuthorization]
public async Task<UserDetail> PostSetInvitedUserPassword([FromBody]string newPassword)
{
var result = await UserManager.AddPasswordAsync(Security.GetUserId(), newPassword);
if (result.Succeeded == false)
{
//it wasn't successful, so add the change error to the model state, we've name the property alias _umb_password on the form
// so that is why it is being used here.
ModelState.AddModelError(
"value",
string.Join(", ", result.Errors));
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
//They've successfully set their password, we can now update their user account to be approved
Security.CurrentUser.IsApproved = true;
Services.UserService.Save(Security.CurrentUser);
//now we can return their full object since they are now really logged into the back office
var userDisplay = Mapper.Map<UserDetail>(Security.CurrentUser);
var httpContextAttempt = TryGetHttpContext();
if (httpContextAttempt.Success)
{
//set their remaining seconds
userDisplay.SecondsUntilTimeout = httpContextAttempt.Result.GetRemainingAuthSeconds();
}
return userDisplay;
}
[FileUploadCleanupFilter(false)]
public async Task<HttpResponseMessage> PostSetAvatar()
{
//borrow the logic from the user controller
return await UsersController.PostSetAvatarInternal(Request, Services.UserService, ApplicationContext.ApplicationCache.StaticCache, Security.GetUserId());
}
/// <summary>
/// Changes the users password
/// </summary>
@@ -39,7 +89,7 @@ namespace Umbraco.Web.Editors
public ModelWithNotifications<string> PostChangePassword(ChangingPasswordModel data)
{
var passwordChangeResult = PasswordChangeControllerHelper.ChangePassword(Security.CurrentUser, data, ModelState, Members);
if (passwordChangeResult.Success)
{
//even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword

View File

@@ -17,6 +17,7 @@ using AutoMapper;
using ClientDependency.Core;
using Microsoft.AspNet.Identity;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
@@ -80,7 +81,12 @@ namespace Umbraco.Web.Editors
[FileUploadCleanupFilter(false)]
public async Task<HttpResponseMessage> PostSetAvatar(int id)
{
if (Request.Content.IsMimeMultipartContent() == false)
return await PostSetAvatarInternal(Request, Services.UserService, ApplicationContext.ApplicationCache.StaticCache, id);
}
internal static async Task<HttpResponseMessage> PostSetAvatarInternal(HttpRequestMessage request, IUserService userService, ICacheProvider staticCache, int id)
{
if (request.Content.IsMimeMultipartContent() == false)
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
@@ -90,22 +96,22 @@ namespace Umbraco.Web.Editors
Directory.CreateDirectory(root);
var provider = new MultipartFormDataStreamProvider(root);
var result = await Request.Content.ReadAsMultipartAsync(provider);
var result = await request.Content.ReadAsMultipartAsync(provider);
//must have a file
if (result.FileData.Count == 0)
{
return Request.CreateResponse(HttpStatusCode.NotFound);
return request.CreateResponse(HttpStatusCode.NotFound);
}
var user = Services.UserService.GetUserById(id);
var user = userService.GetUserById(id);
if (user == null)
return Request.CreateResponse(HttpStatusCode.NotFound);
return request.CreateResponse(HttpStatusCode.NotFound);
var tempFiles = new PostedFiles();
if (result.FileData.Count > 1)
return Request.CreateValidationErrorResponse("The request was not formatted correctly, only one file can be attached to the request");
return request.CreateValidationErrorResponse("The request was not formatted correctly, only one file can be attached to the request");
//get the file info
var file = result.FileData[0];
@@ -123,7 +129,7 @@ namespace Umbraco.Web.Editors
FileSystemProviderManager.Current.MediaFileSystem.AddFile(user.Avatar, fs, true);
}
Services.UserService.Save(user);
userService.Save(user);
//track the temp file so the cleanup filter removes it
tempFiles.UploadedFiles.Add(new ContentItemFile
@@ -132,7 +138,7 @@ namespace Umbraco.Web.Editors
});
}
return Request.CreateResponse(HttpStatusCode.OK, user.GetCurrentUserAvatarUrls(Services.UserService, ApplicationContext.ApplicationCache.StaticCache));
return request.CreateResponse(HttpStatusCode.OK, user.GetCurrentUserAvatarUrls(userService, staticCache));
}
public HttpResponseMessage PostClearAvatar(int id)

View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Web;
@@ -20,11 +22,19 @@ namespace Umbraco.Web
/// </summary>
public static class HtmlHelperBackOfficeExtensions
{
[Obsolete("Use the overload with all required parameters instead")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static IHtmlString BareMinimumServerVariablesScript(this HtmlHelper html, UrlHelper uri, string externalLoginsUrl)
{
return html.BareMinimumServerVariablesScript(uri, ApplicationContext.Current, externalLoginsUrl);
}
/// <summary>
/// Outputs a script tag containing the bare minimum (non secure) server vars for use with the angular app
/// </summary>
/// <param name="html"></param>
/// <param name="uri"></param>
/// <param name="appCtx"></param>
/// <param name="externalLoginsUrl">
/// The post url used to sign in with external logins - this can change depending on for what service the external login is service.
/// Example: normal back office login or authenticating upgrade login
@@ -34,28 +44,15 @@ namespace Umbraco.Web
/// These are the bare minimal server variables that are required for the application to start without being authenticated,
/// we will load the rest of the server vars after the user is authenticated.
/// </remarks>
public static IHtmlString BareMinimumServerVariablesScript(this HtmlHelper html, UrlHelper uri, string externalLoginsUrl)
public static IHtmlString BareMinimumServerVariablesScript(this HtmlHelper html, UrlHelper uri, ApplicationContext appCtx, string externalLoginsUrl)
{
var version = UmbracoVersion.GetSemanticVersion().ToSemanticString();
var serverVars = new BackOfficeServerVariables(uri, appCtx, UmbracoConfig.For.UmbracoSettings());
var minVars = serverVars.BareMinimumServerVariables();
var str = @"<script type=""text/javascript"">
var Umbraco = {};
Umbraco.Sys = {};
Umbraco.Sys.ServerVariables = {
""umbracoUrls"": {
""authenticationApiBaseUrl"": """ + uri.GetUmbracoApiServiceBaseUrl<AuthenticationController>(controller => controller.PostLogin(null)) + @""",
""serverVarsJs"": """ + uri.GetUrlWithCacheBust("ServerVariables", "BackOffice") + @""",
""externalLoginsUrl"": """ + externalLoginsUrl + @"""
},
""umbracoSettings"": {
""allowPasswordReset"": " + (UmbracoConfig.For.UmbracoSettings().Security.AllowPasswordReset ? "true" : "false") + @",
""loginBackgroundImage"": """ + UmbracoConfig.For.UmbracoSettings().Content.LoginBackgroundImage + @"""
},
""application"": {
""applicationPath"": """ + html.ViewContext.HttpContext.Request.ApplicationPath + @""",
""cacheBuster"": """ + string.Format("{0}.{1}", version, ClientDependencySettings.Instance.Version).GenerateHash() + @"""
},
""isDebuggingEnabled"" : " + html.ViewContext.HttpContext.IsDebuggingEnabled.ToString().ToLowerInvariant() + @"
};
Umbraco.Sys.ServerVariables = " + JsonConvert.SerializeObject(minVars) + @";
</script>";
return html.Raw(str);

View File

@@ -328,6 +328,7 @@
<Compile Include="Cache\UnpublishedPageCacheRefresher.cs" />
<Compile Include="Cache\UserCacheRefresher.cs" />
<Compile Include="Editors\BackOfficeNotificationsController.cs" />
<Compile Include="Editors\BackOfficeServerVariables.cs" />
<Compile Include="Editors\EditorValidationResolver.cs" />
<Compile Include="Editors\EditorValidator.cs" />
<Compile Include="Editors\FromJsonPathAttribute.cs" />