Merge branch 'temp8' into temp8-fixme-get-all-entities-with-postfilter

# Conflicts:
#	src/Umbraco.Web/Editors/EntityController.cs
This commit is contained in:
Shannon
2019-01-30 20:09:07 +11:00
958 changed files with 5274 additions and 12481 deletions

View File

@@ -69,8 +69,8 @@ namespace Umbraco.Web.Editors
[WebApi.UmbracoAuthorize(requireApproval: false)]
public IDictionary<string, object> GetMembershipProviderConfig()
{
//TODO: Check if the current PasswordValidator is an IMembershipProviderPasswordValidator, if
//it's not than we should return some generic defaults
// TODO: Check if the current PasswordValidator is an IMembershipProviderPasswordValidator, if
// it's not than we should return some generic defaults
var provider = Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider();
return provider.GetConfiguration(Services.UserService);
}
@@ -194,7 +194,7 @@ namespace Umbraco.Web.Editors
if (user.IsApproved)
{
//if they are approved, than they are no longer invited and we can return an error
// if they are approved, than they are no longer invited and we can return an error
throw new HttpResponseException(Request.CreateUserNoAccessResponse());
}
@@ -202,14 +202,14 @@ namespace Umbraco.Web.Editors
var httpContextAttempt = TryGetHttpContext();
if (httpContextAttempt.Success)
{
//set their remaining seconds
// set their remaining seconds
result.SecondsUntilTimeout = httpContextAttempt.Result.GetRemainingAuthSeconds();
}
return result;
}
//TODO: This should be on the CurrentUserController?
// TODO: This should be on the CurrentUserController?
[WebApi.UmbracoAuthorize]
[ValidateAngularAntiForgeryToken]
public async Task<Dictionary<string, string>> GetCurrentUserLinkedLogins()
@@ -228,8 +228,8 @@ namespace Umbraco.Web.Editors
var http = EnsureHttpContext();
var owinContext = TryGetOwinContext().Result;
//Sign the user in with username/password, this also gives a chance for developers to
//custom verify the credentials and auto-link user accounts with a custom IBackOfficePasswordChecker
// Sign the user in with username/password, this also gives a chance for developers to
// custom verify the credentials and auto-link user accounts with a custom IBackOfficePasswordChecker
var result = await SignInManager.PasswordSignInAsync(
loginModel.Username, loginModel.Password, isPersistent: true, shouldLockout: true);
@@ -237,7 +237,7 @@ namespace Umbraco.Web.Editors
{
case SignInStatus.Success:
//get the user
// get the user
var user = Services.UserService.GetByUsername(loginModel.Username);
UserManager.RaiseLoginSuccessEvent(user.Id);
@@ -268,7 +268,7 @@ namespace Umbraco.Web.Editors
var attemptedUser = Services.UserService.GetByUsername(loginModel.Username);
//create a with information to display a custom two factor send code view
// create a with information to display a custom two factor send code view
var verifyResponse = Request.CreateResponse(HttpStatusCode.PaymentRequired, new
{
twoFactorView = twofactorView,
@@ -282,10 +282,10 @@ namespace Umbraco.Web.Editors
case SignInStatus.LockedOut:
case SignInStatus.Failure:
default:
//return BadRequest (400), we don't want to return a 401 because that get's intercepted
// return BadRequest (400), we don't want to return a 401 because that get's intercepted
// by our angular helper because it thinks that we need to re-perform the request once we are
// authorized and we don't want to return a 403 because angular will show a warning msg indicating
// that the user doesn't have access to perform this function, we just want to return a normal invalid msg.
// authorized and we don't want to return a 403 because angular will show a warning message indicating
// that the user doesn't have access to perform this function, we just want to return a normal invalid message.
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
}
@@ -314,13 +314,13 @@ namespace Umbraco.Web.Editors
var callbackUrl = ConstructCallbackUrl(identityUser.Id, code);
var message = Services.TextService.Localize("resetPasswordEmailCopyFormat",
//Ensure the culture of the found user is used for the email!
// Ensure the culture of the found user is used for the email!
UserExtensions.GetUserCulture(identityUser.Culture, Services.TextService, GlobalSettings),
new[] { identityUser.UserName, callbackUrl });
await UserManager.SendEmailAsync(identityUser.Id,
Services.TextService.Localize("login/resetPasswordEmailCopySubject",
//Ensure the culture of the found user is used for the email!
// Ensure the culture of the found user is used for the email!
UserExtensions.GetUserCulture(identityUser.Culture, Services.TextService, GlobalSettings)),
message);
@@ -332,7 +332,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Used to retrived the 2FA providers for code submission
/// Used to retrieve the 2FA providers for code submission
/// </summary>
/// <returns></returns>
[SetAngularAntiForgeryTokens]
@@ -417,7 +417,7 @@ namespace Umbraco.Web.Editors
{
Logger.Info<AuthenticationController>("User {UserId} is currently locked out, unlocking and resetting AccessFailedCount", model.UserId);
//var user = await UserManager.FindByIdAsync(model.UserId);
//// var user = await UserManager.FindByIdAsync(model.UserId);
var unlockResult = await UserManager.SetLockoutEndDateAsync(model.UserId, DateTimeOffset.Now);
if (unlockResult.Succeeded == false)
{
@@ -431,18 +431,18 @@ namespace Umbraco.Web.Editors
}
}
//They've successfully set their password, we can now update their user account to be confirmed
//if user was only invited, then they have not been approved
//but a successful forgot password flow (e.g. if their token had expired and they did a forgot password instead of request new invite)
//means we have verified their email
// They've successfully set their password, we can now update their user account to be confirmed
// if user was only invited, then they have not been approved
// but a successful forgot password flow (e.g. if their token had expired and they did a forgot password instead of request new invite)
// means we have verified their email
if (!UserManager.IsEmailConfirmed(model.UserId))
{
await UserManager.ConfirmEmailAsync(model.UserId, model.ResetCode);
}
//if the user is invited, enable their account on forgot password
// if the user is invited, enable their account on forgot password
var identityUser = await UserManager.FindByIdAsync(model.UserId);
//invited is not approved, never logged in, invited date present
// invited is not approved, never logged in, invited date present
/*
if (LastLoginDate == default && IsApproved == false && InvitedDate != null)
return UserState.Invited;
@@ -450,7 +450,7 @@ namespace Umbraco.Web.Editors
if (identityUser != null && !identityUser.IsApproved)
{
var user = Services.UserService.GetByUsername(identityUser.UserName);
//also check InvitedDate and never logged in, otherwise this would allow a disabled user to reactivate their account with a forgot password
// also check InvitedDate and never logged in, otherwise this would allow a disabled user to reactivate their account with a forgot password
if (user.LastLoginDate == default && user.InvitedDate != null)
{
user.IsApproved = true;
@@ -505,13 +505,13 @@ namespace Umbraco.Web.Editors
if (principal == null) throw new ArgumentNullException(nameof(principal));
var userDetail = Mapper.Map<UserDetail>(user);
//update the userDetail and set their remaining seconds
// update the userDetail and set their remaining seconds
userDetail.SecondsUntilTimeout = TimeSpan.FromMinutes(GlobalSettings.TimeOutInMinutes).TotalSeconds;
//create a response with the userDetail object
// create a response with the userDetail object
var response = Request.CreateResponse(HttpStatusCode.OK, userDetail);
//ensure the user is set for the current request
// ensure the user is set for the current request
Request.SetPrincipalForRequest(principal);
return response;

View File

@@ -11,16 +11,27 @@ namespace Umbraco.Web.Editors
public class BackOfficeAssetsController : UmbracoAuthorizedJsonController
{
private readonly IFileSystem _jsLibFileSystem = new PhysicalFileSystem(SystemDirectories.Umbraco + IOHelper.DirSepChar + "lib");
[HttpGet]
public IEnumerable<string> GetSupportedMomentLocales()
public object GetSupportedLocales()
{
const string momentLocaleFolder = "moment";
var cultures = _jsLibFileSystem.GetFiles(momentLocaleFolder, "*.js").ToList();
const string flatpickrLocaleFolder = "flatpickr/l10n";
return new
{
moment = GetLocales(momentLocaleFolder),
flatpickr = GetLocales(flatpickrLocaleFolder)
};
}
private IEnumerable<string> GetLocales(string path)
{
var cultures = _jsLibFileSystem.GetFiles(path, "*.js").ToList();
for (var i = 0; i < cultures.Count; i++)
{
cultures[i] = cultures[i]
.Substring(cultures[i].IndexOf(momentLocaleFolder, StringComparison.Ordinal) + momentLocaleFolder.Length + 1);
.Substring(cultures[i].IndexOf(path, StringComparison.Ordinal) + path.Length + 1);
}
return cultures;
}

View File

@@ -21,10 +21,10 @@ using Umbraco.Core.Manifest;
using Umbraco.Core.Models.Identity;
using Umbraco.Web.Models;
using Umbraco.Web.Mvc;
using Umbraco.Web.UI.JavaScript;
using Umbraco.Core.Services;
using Umbraco.Web.Composing;
using Umbraco.Web.Features;
using Umbraco.Web.JavaScript;
using Umbraco.Web.Security;
using Constants = Umbraco.Core.Constants;
using JArray = Newtonsoft.Json.Linq.JArray;
@@ -176,7 +176,7 @@ namespace Umbraco.Web.Editors
var textForCulture = Services.TextService.GetAllStoredValues(cultureInfo)
//the dictionary returned is fine but the delimiter between an 'area' and a 'value' is a '/' but the javascript
// in the back office requres the delimiter to be a '_' so we'll just replace it
// in the back office requires the delimiter to be a '_' so we'll just replace it
.ToDictionary(key => key.Key.Replace("/", "_"), val => val.Value);
return new JsonNetResult { Data = textForCulture, Formatting = Formatting.Indented };
@@ -390,7 +390,7 @@ namespace Umbraco.Web.Editors
var user = await UserManager.FindAsync(loginInfo.Login);
if (user != null)
{
//TODO: It might be worth keeping some of the claims associated with the ExternalLoginInfo, in which case we
// TODO: It might be worth keeping some of the claims associated with the ExternalLoginInfo, in which case we
// wouldn't necessarily sign the user in here with the standard login, instead we'd update the
// UseUmbracoBackOfficeExternalCookieAuthentication extension method to have the correct provider and claims factory,
// ticket format, etc.. to create our back office user including the claims assigned and in this method we'd just ensure
@@ -416,7 +416,7 @@ namespace Umbraco.Web.Editors
{
if (await AutoLinkAndSignInExternalAccount(loginInfo, autoLinkOptions) == false)
{
ViewData[TokenExternalSignInError] = new[] { "The requested provider (" + loginInfo.Login.LoginProvider + ") has not been linked to to an account" };
ViewData[TokenExternalSignInError] = new[] { "The requested provider (" + loginInfo.Login.LoginProvider + ") has not been linked to an account" };
}
//Remove the cookie otherwise this message will keep appearing

View File

@@ -3,6 +3,7 @@ using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Runtime.Serialization;
using System.Web;
using System.Web.Configuration;
using System.Web.Mvc;
@@ -87,7 +88,7 @@ namespace Umbraco.Web.Editors
}
}
//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
// 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");
@@ -105,7 +106,7 @@ namespace Umbraco.Web.Editors
{
"umbracoUrls", new Dictionary<string, object>
{
//TODO: Add 'umbracoApiControllerBaseUrl' which people can use in JS
// 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.
@@ -114,7 +115,7 @@ namespace Umbraco.Web.Editors
{"externalLinkLoginsUrl", _urlHelper.Action("LinkLogin", "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
// 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
{
@@ -158,7 +159,7 @@ namespace Umbraco.Web.Editors
},
{
"treeApplicationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ApplicationTreeController>(
controller => controller.GetApplicationTrees(null, null, null))
controller => controller.GetApplicationTrees(null, null, null, TreeUse.None))
},
{
"contentTypeApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ContentTypeController>(
@@ -183,11 +184,7 @@ namespace Umbraco.Web.Editors
{
"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))
@@ -268,10 +265,6 @@ namespace Umbraco.Web.Editors
"examineMgmtBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<ExamineManagementController>(
controller => controller.GetIndexerDetails())
},
{
"xmlDataIntegrityBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<XmlDataIntegrityController>(
controller => controller.CheckContentXmlTable())
},
{
"healthCheckBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<HealthCheckController>(
controller => controller.GetAllHealthChecks())
@@ -302,7 +295,7 @@ namespace Umbraco.Web.Editors
},
{
"backOfficeAssetsApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<BackOfficeAssetsController>(
controller => controller.GetSupportedMomentLocales())
controller => controller.GetSupportedLocales())
},
{
"languageApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<LanguageController>(
@@ -352,7 +345,10 @@ namespace Umbraco.Web.Editors
{
"umbracoPlugins", new Dictionary<string, object>
{
{"trees", GetTreePluginsMetaData()}
// for each tree that is [PluginController], get
// alias -> areaName
// so that routing (route.js) can look for views
{ "trees", GetPluginTrees().ToArray() }
}
},
{
@@ -370,7 +366,7 @@ namespace Umbraco.Web.Editors
.Select(p => new
{
authType = p.AuthenticationType, caption = p.Caption,
//TODO: Need to see if this exposes any sensitive data!
// TODO: Need to see if this exposes any sensitive data!
properties = p.Properties
})
.ToArray()
@@ -393,43 +389,42 @@ namespace Umbraco.Web.Editors
return defaultVals;
}
private IEnumerable<Dictionary<string, string>> GetTreePluginsMetaData()
[DataContract]
private class PluginTree
{
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.TreeAlias}, {"packageFolder", pluginAttr.AreaName}
}).ToArray();
[DataMember(Name = "alias")]
public string Alias { get; set; }
[DataMember(Name = "packageFolder")]
public string PackageFolder { get; set; }
}
/// <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>>(() => Current.TypeLoader.GetAttributedTreeControllers().ToArray()); // todo inject
private IEnumerable<PluginTree> GetPluginTrees()
{
// used to be (cached)
//var treeTypes = Current.TypeLoader.GetAttributedTreeControllers();
//
// ie inheriting from TreeController and marked with TreeAttribute
//
// do this instead
// inheriting from TreeControllerBase and marked with TreeAttribute
var trees = Current.Factory.GetInstance<TreeCollection>();
foreach (var tree in trees)
{
var treeType = tree.TreeControllerType;
// exclude anything marked with CoreTreeAttribute
var coreTree = treeType.GetCustomAttribute<CoreTreeAttribute>(false);
if (coreTree != null) continue;
// exclude anything not marked with PluginControllerAttribute
var pluginController = treeType.GetCustomAttribute<PluginControllerAttribute>(false);
if (pluginController == null) continue;
yield return new PluginTree { Alias = tree.TreeAlias, PackageFolder = pluginController.AreaName };
}
}
/// <summary>
/// Returns the server variables regarding the application state

View File

@@ -50,7 +50,7 @@ namespace Umbraco.Web.Editors.Binders
}
}
//TODO: anything after 3 parts we can put in metadata
// TODO: anything after 3 parts we can put in metadata
var fileName = file.Headers.ContentDisposition.FileName.Trim('\"');

View File

@@ -82,7 +82,7 @@ namespace Umbraco.Web.Editors.Binders
throw new InvalidOperationException("Could not find member with key " + model.Key);
}
//TODO: Support this scenario!
// TODO: Support this scenario!
//if (scenario == MembershipScenario.CustomProviderWithUmbracoLink)
//{
// //if there's a 'Member' type then we should be able to just go get it from the db since it was created with a link
@@ -140,7 +140,7 @@ namespace Umbraco.Web.Editors.Binders
var contentType = _services.MemberTypeService.Get(model.ContentTypeAlias);
if (contentType == null)
{
throw new InvalidOperationException("No member type found wth alias " + model.ContentTypeAlias);
throw new InvalidOperationException("No member type found with alias " + model.ContentTypeAlias);
}
//remove all membership properties, these values are set with the membership provider.
@@ -176,7 +176,7 @@ namespace Umbraco.Web.Editors.Binders
/// <summary>
/// This will remove all of the special membership provider properties which were required to display the property editors
/// for editing - but the values have been mapped back ot the MemberSave object directly - we don't want to keep these properties
/// for editing - but the values have been mapped back to the MemberSave object directly - we don't want to keep these properties
/// on the IMember because they will attempt to be persisted which we don't want since they might not even exist.
/// </summary>
/// <param name="contentType"></param>

View File

@@ -23,7 +23,7 @@ using StylesheetRule = Umbraco.Web.Models.ContentEditing.StylesheetRule;
namespace Umbraco.Web.Editors
{
//TODO: Put some exception filters in our webapi to return 404 instead of 500 when we throw ArgumentNullException
// TODO: Put some exception filters in our webapi to return 404 instead of 500 when we throw ArgumentNullException
// ref: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/
[PluginController("UmbracoApi")]
[PrefixlessBodyModelValidator]
@@ -68,21 +68,24 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Used to create a container/folder in 'partialViews', 'partialViewMacros' or 'scripts'
/// Used to create a container/folder in 'partialViews', 'partialViewMacros', 'scripts' or 'stylesheets'
/// </summary>
/// <param name="type">'partialViews', 'partialViewMacros' or 'scripts'</param>
/// <param name="parentId">The virtual path of the parent.</param>
/// <param name="name">The name of the container/folder</param>
/// <returns></returns>
[HttpPost]
public CodeFileDisplay PostCreateContainer(string type, string parentId, string name)
public HttpResponseMessage PostCreateContainer(string type, string parentId, string name)
{
if (string.IsNullOrWhiteSpace(type)) throw new ArgumentException("Value cannot be null or whitespace.", "type");
if (string.IsNullOrWhiteSpace(parentId)) throw new ArgumentException("Value cannot be null or whitespace.", "parentId");
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name");
if (name.ContainsAny(Path.GetInvalidPathChars())) {
return Request.CreateNotificationValidationErrorResponse(Services.TextService.Localize("codefile/createFolderIllegalChars"));
}
// if the parentId is root (-1) then we just need an empty string as we are
// creating the path below and we don't wan't -1 in the path
// creating the path below and we don't want -1 in the path
if (parentId == Core.Constants.System.Root.ToInvariantString())
{
parentId = string.Empty;
@@ -111,14 +114,18 @@ namespace Umbraco.Web.Editors
virtualPath = NormalizeVirtualPath(name, SystemDirectories.Scripts);
Services.FileService.CreateScriptFolder(virtualPath);
break;
case Core.Constants.Trees.Stylesheets:
virtualPath = NormalizeVirtualPath(name, SystemDirectories.Css);
Services.FileService.CreateStyleSheetFolder(virtualPath);
break;
}
return new CodeFileDisplay
return Request.CreateResponse(HttpStatusCode.OK, new CodeFileDisplay
{
VirtualPath = virtualPath,
Path = Url.GetTreePathFromFilePath(virtualPath)
};
});
}
/// <summary>
@@ -189,7 +196,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Used to get a list of available templates/snippets to base a new Partial View og Partial View Macro from
/// Used to get a list of available templates/snippets to base a new Partial View or Partial View Macro from
/// </summary>
/// <param name="type">This is a string but will be 'partialViews', 'partialViewMacros'</param>
/// <returns>Returns a list of <see cref="SnippetDisplay"/> if a correct type is sent</returns>
@@ -328,6 +335,11 @@ namespace Umbraco.Web.Editors
return Request.CreateErrorResponse(HttpStatusCode.NotFound, "No Script or folder found with the specified path");
case Core.Constants.Trees.Stylesheets:
if (IsDirectory(virtualPath, SystemDirectories.Css))
{
Services.FileService.DeleteStyleSheetFolder(virtualPath);
return Request.CreateResponse(HttpStatusCode.OK);
}
if (Services.FileService.GetStylesheetByName(virtualPath) != null)
{
Services.FileService.DeleteStylesheet(virtualPath, Security.CurrentUser.Id);

View File

@@ -104,14 +104,14 @@ namespace Umbraco.Web.Editors
/// <param name="saveModel"></param>
/// <returns></returns>
/// <remarks>
/// Permission check is done for letter 'R' which is for <see cref="ActionRights"/> which the user must have access to to update
/// Permission check is done for letter 'R' which is for <see cref="ActionRights"/> which the user must have access to update
/// </remarks>
[EnsureUserPermissionForContent("saveModel.ContentId", 'R')]
public IEnumerable<AssignedUserGroupPermissions> PostSaveUserGroupPermissions(UserGroupPermissionsSave saveModel)
{
if (saveModel.ContentId <= 0) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
//TODO: Should non-admins be alowed to set granular permissions?
// TODO: Should non-admins be allowed to set granular permissions?
var content = Services.ContentService.GetById(saveModel.ContentId);
if (content == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
@@ -166,7 +166,7 @@ namespace Umbraco.Web.Editors
/// <param name="contentId"></param>
/// <returns></returns>
/// <remarks>
/// Permission check is done for letter 'R' which is for <see cref="ActionRights"/> which the user must have access to to view
/// Permission check is done for letter 'R' which is for <see cref="ActionRights"/> which the user must have access to view
/// </remarks>
[EnsureUserPermissionForContent("contentId", 'R')]
public IEnumerable<AssignedUserGroupPermissions> GetDetailedPermissions(int contentId)
@@ -175,7 +175,7 @@ namespace Umbraco.Web.Editors
var content = Services.ContentService.GetById(contentId);
if (content == null) throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound));
//TODO: Should non-admins be able to see detailed permissions?
// TODO: Should non-admins be able to see detailed permissions?
var allUserGroups = Services.UserService.GetAllUserGroups();
@@ -271,7 +271,7 @@ namespace Umbraco.Web.Editors
content.AllowedActions = new[] { "A" };
content.IsBlueprint = true;
//todo - exclude the content apps here
// TODO: exclude the content apps here
//var excludeProps = new[] { "_umb_urls", "_umb_releasedate", "_umb_expiredate", "_umb_template" };
//var propsTab = content.Tabs.Last();
//propsTab.Properties = propsTab.Properties
@@ -433,7 +433,7 @@ namespace Umbraco.Web.Editors
[FilterAllowedOutgoingContent(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>), "Items")]
public PagedResult<ContentItemBasic<ContentPropertyBasic>> GetChildren(
int id,
int pageNumber = 0, //TODO: This should be '1' as it's not the index
int pageNumber = 0, // TODO: This should be '1' as it's not the index
int pageSize = 0,
string orderBy = "SortOrder",
Direction orderDirection = Direction.Ascending,
@@ -457,7 +457,7 @@ namespace Umbraco.Web.Editors
Direction orderDirection = Direction.Ascending,
bool orderBySystemField = true,
string filter = "",
string cultureName = "") // TODO it's not a NAME it's the ISO CODE
string cultureName = "") // TODO: it's not a NAME it's the ISO CODE
{
long totalChildren;
List<IContent> children;
@@ -583,7 +583,7 @@ namespace Umbraco.Web.Editors
private ContentItemDisplay PostSaveInternal(ContentItemSave contentItem, Func<IContent, OperationResult> saveMethod)
{
//Recent versions of IE/Edge may send in the full clientside file path instead of just the file name.
//Recent versions of IE/Edge may send in the full client side file path instead of just the file name.
//To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all
//uploaded files to being *only* the actual file name (as it should be).
if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any())
@@ -647,7 +647,7 @@ namespace Umbraco.Web.Editors
.Any(x => x == false))
{
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
// add the modelstate to the outgoing object and throw a validation message
// add the model state to the outgoing object and throw a validation message
var forDisplay = MapToDisplay(contentItem.PersistedContent);
forDisplay.Errors = ModelState.ToErrorDictionary();
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
@@ -797,7 +797,7 @@ namespace Umbraco.Web.Editors
v.Notifications.AddRange(n.Notifications);
}
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
//lastly, if it is not valid, add the model state to the outgoing object and throw a 403
HandleInvalidModelState(display);
if (wasCancelled)
@@ -1040,7 +1040,7 @@ namespace Umbraco.Web.Editors
/// <param name="msg"></param>
/// <remarks>
/// global notifications will be shown if all variant processing is successful and the save/publish dialog is closed, otherwise
/// variant specific notifications are used to show success messagse in the save/publish dialog.
/// variant specific notifications are used to show success messages in the save/publish dialog.
/// </remarks>
private static void AddSuccessNotification(IDictionary<string, SimpleNotificationModel> notifications, string culture, string header, string msg)
{
@@ -1091,7 +1091,7 @@ namespace Umbraco.Web.Editors
{
//its invariant, proceed normally
var publishStatus = Services.ContentService.SaveAndPublishBranch(contentItem.PersistedContent, force, userId: Security.CurrentUser.Id);
//TODO: Deal with multiple cancelations
// TODO: Deal with multiple cancellations
wasCancelled = publishStatus.Any(x => x.Result == PublishResultType.FailedPublishCancelledByEvent);
successfulCultures = Array.Empty<string>();
return publishStatus;
@@ -1124,7 +1124,7 @@ namespace Umbraco.Web.Editors
{
//proceed to publish if all validation still succeeds
var publishStatus = Services.ContentService.SaveAndPublishBranch(contentItem.PersistedContent, force, culturesToPublish, Security.CurrentUser.Id);
//TODO: Deal with multiple cancelations
// TODO: Deal with multiple cancellations
wasCancelled = publishStatus.Any(x => x.Result == PublishResultType.FailedPublishCancelledByEvent);
successfulCultures = contentItem.Variants.Where(x => x.Publish).Select(x => x.Culture).ToArray();
return publishStatus;
@@ -1431,7 +1431,7 @@ namespace Umbraco.Web.Editors
if (!sortResult.Success)
{
Logger.Warn<ContentController>("Content sorting failed, this was probably caused by an event being cancelled");
//TODO: Now you can cancel sorting, does the event messages bubble up automatically?
// TODO: Now you can cancel sorting, does the event messages bubble up automatically?
return Request.CreateValidationErrorResponse("Content sorting failed, this was probably caused by an event being cancelled");
}
@@ -1732,7 +1732,7 @@ namespace Umbraco.Web.Editors
/// <param name="contentSave"></param>
private void MapValuesForPersistence(ContentItemSave contentSave)
{
//inline method to determine if a property type varies
// inline method to determine if a property type varies
bool Varies(Property property) => property.PropertyType.VariesByCulture();
var variantIndex = 0;
@@ -1865,7 +1865,7 @@ namespace Umbraco.Web.Editors
/// <param name="status"></param>
/// <param name="display"></param>
/// <param name="successfulCultures">
/// This is null when dealing with invariant content, else it's the cultures that were succesfully published
/// This is null when dealing with invariant content, else it's the cultures that were successfully published
/// </param>
private void AddMessageForPublishStatus(IEnumerable<PublishResult> statuses, INotificationModel display, string[] successfulCultures = null)
{
@@ -1907,7 +1907,7 @@ namespace Umbraco.Web.Editors
{
case PublishResultType.SuccessPublishAlready:
{
//TODO: Here we should have messaging for when there are release dates specified like https://github.com/umbraco/Umbraco-CMS/pull/3507
// TODO: Here we should have messaging for when there are release dates specified like https://github.com/umbraco/Umbraco-CMS/pull/3507
// but this will take a bit of effort because we need to deal with variants, different messaging, etc... A quick attempt was made here:
// http://github.com/umbraco/Umbraco-CMS/commit/9b3de7b655e07c612c824699b48a533c0448131a
@@ -1938,7 +1938,7 @@ namespace Umbraco.Web.Editors
break;
case PublishResultType.SuccessPublish:
{
//TODO: Here we should have messaging for when there are release dates specified like https://github.com/umbraco/Umbraco-CMS/pull/3507
// TODO: Here we should have messaging for when there are release dates specified like https://github.com/umbraco/Umbraco-CMS/pull/3507
// but this will take a bit of effort because we need to deal with variants, different messaging, etc... A quick attempt was made here:
// http://github.com/umbraco/Umbraco-CMS/commit/9b3de7b655e07c612c824699b48a533c0448131a
@@ -2038,7 +2038,7 @@ namespace Umbraco.Web.Editors
return display;
}
[EnsureUserPermissionForContent("contentId", 'R')]
[EnsureUserPermissionForContent("contentId", ActionBrowse.ActionLetter)]
public IEnumerable<NotifySetting> GetNotificationOptions(int contentId)
{
var notifications = new List<NotifySetting>();
@@ -2148,7 +2148,7 @@ namespace Umbraco.Web.Editors
default:
notificationModel.AddErrorNotification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
null); //TODO: There is no specific failed to save error message AFAIK
null); // TODO: There is no specific failed to save error message AFAIK
break;
case OperationResultType.FailedCancelledByEvent:
notificationModel.AddErrorNotification(

View File

@@ -101,7 +101,7 @@ namespace Umbraco.Web.Editors
protected virtual void HandleInvalidModelState(IErrorModel display)
{
//lastly, if it is not valid, add the modelstate to the outgoing object and throw a 403
//lastly, if it is not valid, add the model state to the outgoing object and throw a 403
if (!ModelState.IsValid)
{
display.Errors = ModelState.ToErrorDictionary();
@@ -117,7 +117,7 @@ namespace Umbraco.Web.Editors
/// <param name="getFromService"></param>
/// <returns></returns>
/// <remarks>
/// This is useful for when filters have alraedy looked up a persisted entity and we don't want to have
/// This is useful for when filters have already looked up a persisted entity and we don't want to have
/// to look it up again.
/// </remarks>
protected TPersisted GetObjectFromRequest<TPersisted>(Func<TPersisted> getFromService)
@@ -148,7 +148,7 @@ namespace Umbraco.Web.Editors
string[] messageParams = null)
{
//if there's already a default event message, don't add our default one
//todo inject
// TODO: inject
var msgs = Current.EventMessages;
if (msgs != null && msgs.GetAll().Any(x => x.IsDefaultEventMessage)) return;

View File

@@ -22,12 +22,9 @@ using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Web.Composing;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.UI;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
@@ -35,7 +32,7 @@ using Notification = Umbraco.Web.Models.ContentEditing.Notification;
namespace Umbraco.Web.Editors
{
//TODO: We'll need to be careful about the security on this controller, when we start implementing
// TODO: We'll need to be careful about the security on this controller, when we start implementing
// methods to modify content types we'll need to enforce security on the individual methods, we
// cannot put security on the whole controller because things like
// GetAllowedChildren, GetPropertyTypeScaffold, GetAllPropertyTypeAliases are required for content editing.
@@ -82,7 +79,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Deletes a document type wth a given ID
/// Deletes a document type with a given ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
@@ -128,7 +125,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Returns the avilable compositions for this content type
/// Returns the available compositions for this content type
/// This has been wrapped in a dto instead of simple parameters to support having multiple parameters in post request body
/// </summary>
/// <param name="filter"></param>
@@ -187,7 +184,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Deletes a document type container wth a given ID
/// Deletes a document type container with a given ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
@@ -293,7 +290,7 @@ namespace Umbraco.Web.Editors
saveContentType: type => Services.ContentTypeService.Save(type),
beforeCreateNew: ctSave =>
{
//create a default template if it doesnt exist -but only if default template is == to the content type
//create a default template if it doesn't exist -but only if default template is == to the content type
if (ctSave.DefaultTemplate.IsNullOrWhiteSpace() == false && ctSave.DefaultTemplate == ctSave.Alias)
{
var template = CreateTemplateForContentType(ctSave.Alias, ctSave.Name);
@@ -413,7 +410,7 @@ namespace Umbraco.Web.Editors
types = Services.ContentTypeService.GetAll(ids).ToList();
}
var basics = types.Select(Mapper.Map<IContentType, ContentTypeBasic>).ToList();
var basics = types.Where(type => type.IsElement == false).Select(Mapper.Map<IContentType, ContentTypeBasic>).ToList();
var localizedTextService = Services.TextService;
foreach (var basic in basics)
@@ -573,7 +570,7 @@ namespace Umbraco.Web.Editors
model.Notifications.Add(new Notification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
Services.TextService.Localize("media/disallowedFileType"),
SpeechBubbleIcon.Warning));
NotificationStyle.Warning));
}
return model;

View File

@@ -259,7 +259,7 @@ namespace Umbraco.Web.Editors
ModelState.AddModelError("Alias", Services.TextService.Localize("editcontenttype/aliasAlreadyExists"));
}
// execute the externam validators
// execute the external validators
EditorValidator.Validate(ModelState, contentTypeSave);
if (ModelState.IsValid == false)

View File

@@ -177,7 +177,7 @@ namespace Umbraco.Web.Editors
var userMgr = this.TryGetOwinContext().Result.GetBackOfficeUserManager();
//raise the reset event
//TODO: I don't think this is required anymore since from 7.7 we no longer display the reset password checkbox since that didn't make sense.
// TODO: I don't think this is required anymore since from 7.7 we no longer display the reset password checkbox since that didn't make sense.
if (data.Reset.HasValue && data.Reset.Value)
{
userMgr.RaisePasswordResetEvent(Security.CurrentUser.Id);

View File

@@ -6,8 +6,8 @@ using Umbraco.Web.Mvc;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
using System.Net.Http;
using System.Web.Http;
using System;
using System.Linq;
using System.Net;
using System.Text;
using Umbraco.Core.Cache;
@@ -16,6 +16,8 @@ using Umbraco.Web.WebApi.Filters;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Core.Dashboards;
using Umbraco.Web.Services;
namespace Umbraco.Web.Editors
{
@@ -25,9 +27,10 @@ namespace Umbraco.Web.Editors
[AngularJsonOnlyConfiguration]
[IsBackOffice]
[WebApi.UmbracoAuthorize]
public class DashboardController : UmbracoApiController
{
private readonly Dashboards _dashboards;
private readonly IDashboardService _dashboardService;
/// <summary>
/// Initializes a new instance of the <see cref="DashboardController"/> with auto dependencies.
@@ -38,10 +41,10 @@ namespace Umbraco.Web.Editors
/// <summary>
/// Initializes a new instance of the <see cref="DashboardController"/> with all its dependencies.
/// </summary>
public DashboardController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, Dashboards dashboards)
public DashboardController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, IDashboardService dashboardService)
: base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState)
{
_dashboards = dashboards;
_dashboardService = dashboardService;
}
//we have just one instance of HttpClient shared for the entire application
@@ -79,7 +82,7 @@ namespace Umbraco.Web.Editors
}
catch (HttpRequestException ex)
{
Logger.Error<DashboardController>(ex.InnerException ?? ex, "Error getting dashboard content from '{Url}'", url);
Logger.Error<DashboardController>(ex.InnerException ?? ex, "Error getting dashboard content from {Url}", url);
//it's still new JObject() - we return it like this to avoid error codes which triggers UI warnings
AppCaches.RuntimeCache.InsertCacheItem<JObject>(key, () => result, new TimeSpan(0, 5, 0));
@@ -117,7 +120,7 @@ namespace Umbraco.Web.Editors
}
catch (HttpRequestException ex)
{
Logger.Error<DashboardController>(ex.InnerException ?? ex, "Error getting dashboard CSS from '{Url}'", url);
Logger.Error<DashboardController>(ex.InnerException ?? ex, "Error getting dashboard CSS from {Url}", url);
//it's still string.Empty - we return it like this to avoid error codes which triggers UI warnings
AppCaches.RuntimeCache.InsertCacheItem<string>(key, () => result, new TimeSpan(0, 5, 0));
@@ -130,11 +133,89 @@ namespace Umbraco.Web.Editors
};
}
public async Task<HttpResponseMessage> GetRemoteXml(string site, string url)
{
// This is used in place of the old feedproxy.config
// Which was used to grab data from our.umbraco.com, umbraco.com or umbraco.tv
// for certain dashboards or the help drawer
var urlPrefix = string.Empty;
switch (site.ToUpper())
{
case "TV":
urlPrefix = "https://umbraco.tv/";
break;
case "OUR":
urlPrefix = "https://our.umbraco.org/";
break;
case "COM":
urlPrefix = "https://umbraco.com/";
break;
default:
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
//Make remote call to fetch videos or remote dashboard feed data
var key = $"umbraco-XML-feed-{site}-{url.ToCleanString(Core.Strings.CleanStringType.UrlSegment)}";
var content = AppCaches.RuntimeCache.GetCacheItem<string>(key);
var result = string.Empty;
if (content != null)
{
result = content;
}
else
{
//content is null, go get it
try
{
//fetch remote css
content = await HttpClient.GetStringAsync($"{urlPrefix}{url}");
//can't use content directly, modified closure problem
result = content;
//save server content for 30 mins
AppCaches.RuntimeCache.InsertCacheItem<string>(key, () => result, new TimeSpan(0, 30, 0));
}
catch (HttpRequestException ex)
{
Logger.Error<DashboardController>(ex.InnerException ?? ex, "Error getting remote dashboard data from {UrlPrefix}{Url}", urlPrefix, url);
//it's still string.Empty - we return it like this to avoid error codes which triggers UI warnings
AppCaches.RuntimeCache.InsertCacheItem<string>(key, () => result, new TimeSpan(0, 5, 0));
}
}
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(result, Encoding.UTF8, "text/xml")
};
}
// return IDashboardSlim - we don't need sections nor access rules
[ValidateAngularAntiForgeryToken]
[OutgoingEditorModelEvent]
public IEnumerable<Tab<DashboardControl>> GetDashboard(string section)
public IEnumerable<Tab<IDashboardSlim>> GetDashboard(string section)
{
return _dashboards.GetDashboards(section, Security.CurrentUser);
return _dashboardService.GetDashboards(section, Security.CurrentUser).Select(x => new Tab<IDashboardSlim>
{
Id = x.Id,
Alias = x.Alias,
Label = x.Label,
Expanded = x.Expanded,
IsActive = x.IsActive,
Properties = x.Properties.Select(y => new DashboardSlim
{
Alias = y.Alias,
View = y.View
})
});
}
}
}

View File

@@ -1,115 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Configuration.Dashboard;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
using Umbraco.Web.Services;
namespace Umbraco.Web.Editors
{
/// <summary>
/// A utility class for determine dashboard security
/// </summary>
internal class DashboardSecurity
{
//TODO: Unit test all this!!! :/
public static bool AuthorizeAccess(ISection dashboardSection, IUser user, ISectionService sectionService)
{
return CheckUserAccessByRules(user, sectionService, dashboardSection.AccessRights.Rules);
}
public static bool AuthorizeAccess(IDashboardTab dashboardTab, IUser user, ISectionService sectionService)
{
return CheckUserAccessByRules(user, sectionService, dashboardTab.AccessRights.Rules);
}
public static bool AuthorizeAccess(IDashboardControl dashboardControl, IUser user, ISectionService sectionService)
{
return CheckUserAccessByRules(user, sectionService, dashboardControl.AccessRights.Rules);
}
private static (IAccessRule[], IAccessRule[], IAccessRule[]) GroupRules(IEnumerable<IAccessRule> rules)
{
IAccessRule[] denyRules = null, grantRules = null, grantBySectionRules = null;
var groupedRules = rules.GroupBy(x => x.Type);
foreach (var group in groupedRules)
{
var a = group.ToArray();
switch (group.Key)
{
case AccessRuleType.Deny:
denyRules = a;
break;
case AccessRuleType.Grant:
grantRules = a;
break;
case AccessRuleType.GrantBySection:
grantBySectionRules = a;
break;
default:
throw new Exception("panic");
}
}
return (denyRules ?? Array.Empty<IAccessRule>(), grantRules ?? Array.Empty<IAccessRule>(), grantBySectionRules ?? Array.Empty<IAccessRule>());
}
public static bool CheckUserAccessByRules(IUser user, ISectionService sectionService, IEnumerable<IAccessRule> rules)
{
if (user.Id == Constants.Security.SuperUserId)
return true;
var (denyRules, grantRules, grantBySectionRules) = GroupRules(rules);
var hasAccess = true;
string[] assignedUserGroups = null;
// if there are no grant rules, then access is granted by default, unless denied
// otherwise, grant rules determine if access can be granted at all
if (grantBySectionRules.Length > 0 || grantRules.Length > 0)
{
hasAccess = false;
// check if this item has any grant-by-section arguments.
// if so check if the user has access to any of the sections approved, if so they will be allowed to see it (so far)
if (grantBySectionRules.Length > 0)
{
var allowedSections = sectionService.GetAllowedSections(user.Id).Select(x => x.Alias).ToArray();
var wantedSections = grantBySectionRules.SelectMany(g => g.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)).ToArray();
if (wantedSections.Intersect(allowedSections).Any())
hasAccess = true;
}
// if not already granted access, check if this item as any grant arguments.
// if so check if the user is in one of the user groups approved, if so they will be allowed to see it (so far)
if (hasAccess == false && grantRules.Any())
{
assignedUserGroups = user.Groups.Select(x => x.Alias).ToArray();
var wantedUserGroups = grantRules.SelectMany(g => g.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)).ToArray();
if (wantedUserGroups.Intersect(assignedUserGroups).Any())
hasAccess = true;
}
}
if (!hasAccess || denyRules.Length == 0)
return false;
// check if this item has any deny arguments, if so check if the user is in one of the denied user groups, if so they will
// be denied to see it no matter what
assignedUserGroups = assignedUserGroups ?? user.Groups.Select(x => x.Alias).ToArray();
var deniedUserGroups = denyRules.SelectMany(g => g.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)).ToArray();
if (deniedUserGroups.Intersect(assignedUserGroups).Any())
hasAccess = false;
return hasAccess;
}
}
}

View File

@@ -1,160 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Configuration.Dashboard;
using Umbraco.Core.IO;
using Umbraco.Core.Manifest;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Services;
namespace Umbraco.Web.Editors
{
public class Dashboards
{
private readonly ISectionService _sectionService;
private readonly IDashboardSection _dashboardSection;
private readonly ManifestParser _manifestParser;
public Dashboards(ISectionService sectionService, IDashboardSection dashboardSection, ManifestParser manifestParser)
{
_sectionService = sectionService ?? throw new ArgumentNullException(nameof(sectionService));
_dashboardSection = dashboardSection;
_manifestParser = manifestParser;
}
/// <summary>
/// Gets all dashboards, organized by section, for a user.
/// </summary>
public IDictionary<string, IEnumerable<Tab<DashboardControl>>> GetDashboards(IUser currentUser)
{
return _sectionService.GetSections().ToDictionary(x => x.Alias, x => GetDashboards(x.Alias, currentUser));
}
/// <summary>
/// Returns dashboards for a specific section, for a user.
/// </summary>
public IEnumerable<Tab<DashboardControl>> GetDashboards(string section, IUser currentUser)
{
var tabId = 1;
var configDashboards = GetDashboardsFromConfig(ref tabId, section, currentUser);
var pluginDashboards = GetDashboardsFromPlugins(ref tabId, section, currentUser);
// merge dashboards
// both collections contain tab.alias -> controls
var dashboards = configDashboards;
// until now, it was fine to have duplicate tab.aliases in configDashboard
// so... the rule should be - just merge whatever we get, don't be clever
dashboards.AddRange(pluginDashboards);
// re-sort by id
dashboards.Sort((tab1, tab2) => tab1.Id > tab2.Id ? 1 : 0);
// re-assign ids (why?)
var i = 1;
foreach (var tab in dashboards)
{
tab.Id = i++;
tab.IsActive = tab.Id == 1;
}
return configDashboards;
}
// note:
// in dashboard.config we have 'sections' which define 'tabs' for 'areas'
// and 'areas' are the true UI sections - and each tab can have more than
// one control
// in a manifest, we directly have 'dashboards' which map to a unique
// control in a tab
// gets all tabs & controls from the config file
private List<Tab<DashboardControl>> GetDashboardsFromConfig(ref int tabId, string section, IUser currentUser)
{
var tabs = new List<Tab<DashboardControl>>();
// disable packages section dashboard
if (section == "packages") return tabs;
foreach (var dashboardSection in _dashboardSection.Sections.Where(x => x.Areas.InvariantContains(section)))
{
// validate access to this section
if (!DashboardSecurity.AuthorizeAccess(dashboardSection, currentUser, _sectionService))
continue;
foreach (var tab in dashboardSection.Tabs)
{
// validate access to this tab
if (!DashboardSecurity.AuthorizeAccess(tab, currentUser, _sectionService))
continue;
var dashboardControls = new List<DashboardControl>();
foreach (var control in tab.Controls)
{
// validate access to this control
if (!DashboardSecurity.AuthorizeAccess(control, currentUser, _sectionService))
continue;
// create and add control
var dashboardControl = new DashboardControl
{
Caption = control.PanelCaption,
Path = IOHelper.FindFile(control.ControlPath.Trim())
};
if (dashboardControl.Path.InvariantEndsWith(".ascx"))
throw new NotSupportedException("Legacy UserControl (.ascx) dashboards are no longer supported.");
dashboardControls.Add(dashboardControl);
}
// create and add tab
tabs.Add(new Tab<DashboardControl>
{
Id = tabId++,
Alias = tab.Caption.ToSafeAlias(),
Label = tab.Caption,
Properties = dashboardControls
});
}
}
return tabs;
}
private List<Tab<DashboardControl>> GetDashboardsFromPlugins(ref int tabId, string section, IUser currentUser)
{
var tabs = new List<Tab<DashboardControl>>();
foreach (var dashboard in _manifestParser.Manifest.Dashboards.Where(x => x.Sections.InvariantContains(section)).OrderBy(x => x.Weight))
{
// validate access
if (!DashboardSecurity.CheckUserAccessByRules(currentUser, _sectionService, dashboard.AccessRules))
continue;
var dashboardControl = new DashboardControl
{
Caption = "",
Path = IOHelper.FindFile(dashboard.View.Trim())
};
if (dashboardControl.Path.InvariantEndsWith(".ascx"))
throw new NotSupportedException("Legacy UserControl (.ascx) dashboards are no longer supported.");
tabs.Add(new Tab<DashboardControl>
{
Id = tabId++,
Alias = dashboard.Alias.ToSafeAlias(),
Label = dashboard.Name,
Properties = new[] { dashboardControl }
});
}
return tabs;
}
}
}

View File

@@ -68,7 +68,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Deletes a data type wth a given ID
/// Deletes a data type with a given ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
@@ -120,7 +120,7 @@ namespace Umbraco.Web.Editors
{
var dt = Services.DataTypeService.GetDataType(Constants.Conventions.DataTypes.ListViewPrefix + contentTypeAlias);
//if it doesnt exist yet, we will create it.
//if it doesn't exist yet, we will create it.
if (dt == null)
{
var editor = _propertyEditors[Constants.PropertyEditors.Aliases.ListView];
@@ -172,7 +172,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Deletes a data type container wth a given ID
/// Deletes a data type container with a given ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
@@ -204,7 +204,7 @@ namespace Umbraco.Web.Editors
{
//If we've made it here, then everything has been wired up and validated by the attribute
//TODO: Check if the property editor has changed, if it has ensure we don't pass the
// TODO: Check if the property editor has changed, if it has ensure we don't pass the
// existing values to the new property editor!
// get the current configuration,

View File

@@ -8,7 +8,6 @@ using AutoMapper;
using Umbraco.Core.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.UI;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
@@ -22,7 +21,7 @@ namespace Umbraco.Web.Editors
/// </summary>
/// <remarks>
/// The security for this controller is defined to allow full CRUD access to dictionary if the user has access to either:
/// Dictionar
/// Dictionary
/// </remarks>
[PluginController("UmbracoApi")]
[UmbracoTreeAuthorize(Constants.Trees.Dictionary)]
@@ -30,7 +29,7 @@ namespace Umbraco.Web.Editors
public class DictionaryController : BackOfficeNotificationsController
{
/// <summary>
/// Deletes a data type wth a given ID
/// Deletes a data type with a given ID
/// </summary>
/// <param name="id"></param>
/// <returns><see cref="HttpResponseMessage"/></returns>
@@ -49,7 +48,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Creates a new dictoinairy item
/// Creates a new dictionary item
/// </summary>
/// <param name="parentId">
/// The parent id.
@@ -65,7 +64,7 @@ namespace Umbraco.Web.Editors
{
if (string.IsNullOrEmpty(key))
return Request
.CreateNotificationValidationErrorResponse("Key can not be empty;"); // TODO translate
.CreateNotificationValidationErrorResponse("Key can not be empty;"); // TODO: translate
if (Services.LocalizationService.DictionaryItemExists(key))
{
@@ -108,7 +107,7 @@ namespace Umbraco.Web.Editors
/// The <see cref="DictionaryDisplay"/>.
/// </returns>
/// <exception cref="HttpResponseException">
/// Returrns a not found response when dictionary item does not exist
/// Returns a not found response when dictionary item does not exist
/// </exception>
public DictionaryDisplay GetById(int id)
{
@@ -172,7 +171,7 @@ namespace Umbraco.Web.Editors
model.Notifications.Add(new Notification(
Services.TextService.Localize("speechBubbles/dictionaryItemSaved", userCulture), string.Empty,
SpeechBubbleIcon.Success));
NotificationStyle.Success));
return model;
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Web.Http.Filters;
using Umbraco.Core.Dashboards;
using Umbraco.Core.Events;
using Umbraco.Web.Models.ContentEditing;
@@ -14,9 +15,9 @@ namespace Umbraco.Web.Editors
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<MediaItemDisplay>> SendingMediaModel;
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<MemberDisplay>> SendingMemberModel;
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<UserDisplay>> SendingUserModel;
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<IEnumerable<Tab<DashboardControl>>>> SendingDashboardModel;
public static event TypedEventHandler<HttpActionExecutedContext, EditorModelEventArgs<IEnumerable<Tab<IDashboard>>>> SendingDashboardModel;
private static void OnSendingDashboardModel(HttpActionExecutedContext sender, EditorModelEventArgs<IEnumerable<Tab<DashboardControl>>> e)
private static void OnSendingDashboardModel(HttpActionExecutedContext sender, EditorModelEventArgs<IEnumerable<Tab<IDashboard>>> e)
{
var handler = SendingDashboardModel;
handler?.Invoke(sender, e);
@@ -65,8 +66,8 @@ namespace Umbraco.Web.Editors
if (e.Model is UserDisplay)
OnSendingUserModel(sender, new EditorModelEventArgs<UserDisplay>(e));
if (e.Model is IEnumerable<Tab<DashboardControl>>)
OnSendingDashboardModel(sender, new EditorModelEventArgs<IEnumerable<Tab<DashboardControl>>>(e));
if (e.Model is IEnumerable<IDashboard>)
OnSendingDashboardModel(sender, new EditorModelEventArgs<IEnumerable<Tab<IDashboard>>>(e));
}
}
}

View File

@@ -16,7 +16,7 @@ namespace Umbraco.Web.Editors
{
var modelType = model.GetType();
var validationResults = Current.EditorValidators // todo inject
var validationResults = Current.EditorValidators // TODO: inject
.Where(x => x.ModelType == modelType)
.SelectMany(x => x.Validate(model))
.Where(x => !string.IsNullOrWhiteSpace(x.ErrorMessage) && x.MemberNames.Any());

View File

@@ -107,7 +107,7 @@ namespace Umbraco.Web.Editors
[HttpGet]
public IEnumerable<EntityBasic> Search(string query, UmbracoEntityTypes type, string searchFrom = null)
{
//TODO: Should we restrict search results based on what app the user has access to?
// TODO: Should we restrict search results based on what app the user has access to?
// - Theoretically you shouldn't be able to see member data if you don't have access to members right?
if (string.IsNullOrEmpty(query))
@@ -207,7 +207,7 @@ namespace Umbraco.Web.Editors
/// Gets the url of an entity
/// </summary>
/// <param name="id">Int id of the entity to fetch URL for</param>
/// <param name="type">The tpye of entity such as Document, Media, Member</param>
/// <param name="type">The type of entity such as Document, Media, Member</param>
/// <returns>The URL or path to the item</returns>
public HttpResponseMessage GetUrl(int id, UmbracoEntityTypes type)
{
@@ -252,11 +252,11 @@ namespace Umbraco.Web.Editors
/// <returns></returns>
public EntityBasic GetByQuery(string query, int nodeContextId, UmbracoEntityTypes type)
{
//TODO: Rename this!!! It's misleading, it should be GetByXPath
// TODO: Rename this!!! It's misleading, it should be GetByXPath
if (type != UmbracoEntityTypes.Document)
throw new ArgumentException("Get by query is only compatible with enitities of type Document");
throw new ArgumentException("Get by query is only compatible with entities of type Document");
var q = ParseXPathQuery(query, nodeContextId);
@@ -268,7 +268,7 @@ namespace Umbraco.Web.Editors
return GetById(node.Id, type);
}
//PP: wip in progress on the query parser
// PP: Work in progress on the query parser
private string ParseXPathQuery(string query, int id)
{
return UmbracoXPathPathSyntaxParser.ParseXPathQuery(
@@ -628,7 +628,7 @@ namespace Umbraco.Web.Editors
var objectType = ConvertToObjectType(entityType);
if (objectType.HasValue)
{
//TODO: Need to check for Object types that support hierarchic here, some might not.
// TODO: Need to check for Object types that support hierarchic here, some might not.
return Services.EntityService.GetChildren(id, objectType.Value)
.WhereNotNull()
@@ -651,7 +651,7 @@ namespace Umbraco.Web.Editors
var objectType = ConvertToObjectType(entityType);
if (objectType.HasValue)
{
//TODO: Need to check for Object types that support hierarchic here, some might not.
// TODO: Need to check for Object types that support hierarchic here, some might not.
var ids = Services.EntityService.Get(id).Path.Split(',').Select(int.Parse).Distinct().ToArray();
@@ -885,7 +885,7 @@ namespace Umbraco.Web.Editors
var objectType = ConvertToObjectType(entityType);
if (objectType.HasValue)
{
//TODO: Should we order this by something ?
// TODO: Should we order this by something ?
var entities = Services.EntityService.GetAll(objectType.Value).WhereNotNull().Select(Mapper.Map<EntityBasic>);
return ExecutePostFilter(entities, postFilter);
}

View File

@@ -133,7 +133,7 @@ namespace Umbraco.Web.Editors
_logger.Info<ExamineManagementController>("Rebuilding index '{IndexName}'", indexName);
//remove it in case there's a handler there alraedy
//remove it in case there's a handler there already
index.IndexOperationComplete -= Indexer_IndexOperationComplete;
//now add a single handler

View File

@@ -96,7 +96,7 @@ namespace Umbraco.Web.Editors.Filters
{
if (persistedProperties.Any(property => property.Alias == p.Alias) == false)
{
//TODO: Do we return errors here ? If someone deletes a property whilst their editing then should we just
// TODO: Do we return errors here ? If someone deletes a property whilst their editing then should we just
//save the property data that remains? Or inform them they need to reload... not sure. This problem exists currently too i think.
var message = $"property with alias: {p.Alias} was not found";
@@ -117,7 +117,7 @@ namespace Umbraco.Web.Editors.Filters
/// <param name="modelState"></param>
/// <returns></returns>
/// <remarks>
/// All property data validation goes into the modelstate with a prefix of "Properties"
/// All property data validation goes into the model state with a prefix of "Properties"
/// </remarks>
public virtual bool ValidatePropertyData(
TModelSave model,

View File

@@ -152,7 +152,7 @@ namespace Umbraco.Web.Editors.Filters
case ContentSaveAction.PublishWithDescendantsNew:
case ContentSaveAction.PublishWithDescendantsForceNew:
//Publish new requires both ActionNew AND ActionPublish
//TODO: Shoudn't publish also require ActionUpdate since it will definitely perform an update to publish but maybe that's just implied
// TODO: Shouldn't publish also require ActionUpdate since it will definitely perform an update to publish but maybe that's just implied
permissionToCheck.Add(ActionNew.ActionLetter);
permissionToCheck.Add(ActionPublish.ActionLetter);

View File

@@ -27,7 +27,7 @@ namespace Umbraco.Web.Editors.Filters
_userService = userService;
}
private IUserService UserService => _userService ?? Current.Services.UserService; // todo inject
private IUserService UserService => _userService ?? Current.Services.UserService; // TODO: inject
public override void OnActionExecuting(HttpActionContext actionContext)
{
@@ -70,7 +70,7 @@ namespace Umbraco.Web.Editors.Filters
actionContext.ModelState.AddModelError("Alias", "A user group with this alias already exists");
}
//TODO: Validate the name is unique?
// TODO: Validate the name is unique?
if (actionContext.ModelState.IsValid == false)
{

View File

@@ -1,16 +1,13 @@
using System.Runtime.Serialization;
using System.Web.Http;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
namespace Umbraco.Web.Editors
{
// fixme/task - deal with this
// this is not authenticated, and therefore public, and therefore reveals we
// are running Umbraco - but, all requests should come from localhost really,
// so there should be a way to 404 when the request comes from the outside.
public class KeepAliveController : UmbracoApiController
{
[OnlyLocalRequests]
[HttpGet]
public KeepAlivePingResult Ping()
{

View File

@@ -100,7 +100,7 @@ namespace Umbraco.Web.Editors
if (!ModelState.IsValid)
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
// this is prone to race conds but the service will not let us proceed anyways
// this is prone to race conditions but the service will not let us proceed anyways
var existing = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode);
if (existing != null && language.Id != existing.Id)
@@ -150,7 +150,7 @@ namespace Umbraco.Web.Editors
existing.FallbackLanguageId = language.FallbackLanguageId;
// modifying an existing language can create a fallback, verify
// note that the service will check again, dealing with race conds
// note that the service will check again, dealing with race conditions
if (existing.FallbackLanguageId.HasValue)
{
var languages = Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x);

View File

@@ -1,59 +0,0 @@
using System;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi.Filters;
using Umbraco.Web._Legacy.UI;
namespace Umbraco.Web.Editors
{
/// <summary>
/// The API controller used for dealing with legacy content
/// </summary>
[PluginController("UmbracoApi")]
[ValidationFilter]
public class LegacyController : UmbracoAuthorizedJsonController
{
/// <summary>
/// This will perform the delete operation for legacy items which include any item that
/// has functionality included in the ui.xml structure.
/// </summary>
/// <returns></returns>
[HttpPost]
public HttpResponseMessage DeleteLegacyItem(string nodeId, string alias, string nodeType)
{
//U4-2686 - alias is html encoded, make sure to decode
alias = HttpUtility.HtmlDecode(alias);
//In order to process this request we MUST have an HttpContext available
var httpContextAttempt = TryGetHttpContext();
if (httpContextAttempt.Success)
{
//this is a hack check based on legacy
if (nodeType == "memberGroups")
{
LegacyDialogHandler.Delete(httpContextAttempt.Result, Security.CurrentUser, nodeType, 0, alias);
return Request.CreateResponse(HttpStatusCode.OK);
}
int id;
if (int.TryParse(nodeId, out id))
{
LegacyDialogHandler.Delete(httpContextAttempt.Result, Security.CurrentUser, nodeType, id, alias);
return Request.CreateResponse(HttpStatusCode.OK);
}
//the way this legacy stuff used to work is that if the node id didn't parse, we would
//pass the node id as the alias with an id of zero = sure whatevs.
LegacyDialogHandler.Delete(httpContextAttempt.Result, Security.CurrentUser, nodeType, 0, nodeId);
return Request.CreateResponse(HttpStatusCode.OK);
}
//We must have an HttpContext available for this to work.
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, new InvalidOperationException("No HttpContext found in the current request"));
}
}
}

View File

@@ -60,7 +60,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Gets a rendered macro as html for rendering in the rich text editor
/// Gets a rendered macro as HTML for rendering in the rich text editor
/// </summary>
/// <param name="macroAlias"></param>
/// <param name="pageId"></param>
@@ -78,8 +78,8 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Gets a rendered macro as html for rendering in the rich text editor.
/// Using HTTP POST instead of GET allows for more parameters to be passed as it's not dependant on URL-length limitations like GET.
/// Gets a rendered macro as HTML for rendering in the rich text editor.
/// Using HTTP POST instead of GET allows for more parameters to be passed as it's not dependent on URL-length limitations like GET.
/// The method using GET is kept to maintain backwards compatibility
/// </summary>
/// <param name="model"></param>
@@ -116,14 +116,14 @@ namespace Umbraco.Web.Editors
if (macro.RenderInEditor == false)
{
var response = Request.CreateResponse();
//need to create a specific content result formatted as html since this controller has been configured
//need to create a specific content result formatted as HTML since this controller has been configured
//with only json formatters.
response.Content = new StringContent(string.Empty, Encoding.UTF8, "text/html");
return response;
}
//because macro's are filled with insane legacy bits and pieces we need all sorts of wierdness to make them render.
//because macro's are filled with insane legacy bits and pieces we need all sorts of weirdness to make them render.
//the 'easiest' way might be to create an IPublishedContent manually and populate the legacy 'page' object with that
//and then set the legacy parameters.
@@ -137,7 +137,7 @@ namespace Umbraco.Web.Editors
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(culture.Culture);
}
var legacyPage = new global::umbraco.page(doc, _variationContextAccessor);
var legacyPage = new global::Umbraco.Web.Macros.PublishedContentHashtableConverter(doc, _variationContextAccessor);
UmbracoContext.HttpContext.Items["pageElements"] = legacyPage.Elements;
UmbracoContext.HttpContext.Items[Core.Constants.Conventions.Url.AltTemplate] = null;
@@ -145,7 +145,7 @@ namespace Umbraco.Web.Editors
var renderer = new UmbracoComponentRenderer(UmbracoContext);
var result = Request.CreateResponse();
//need to create a specific content result formatted as html since this controller has been configured
//need to create a specific content result formatted as HTML since this controller has been configured
//with only json formatters.
result.Content = new StringContent(
renderer.RenderMacro(macro, macroParams, legacyPage).ToString(),

View File

@@ -1,27 +1,24 @@
using Umbraco.Core.Services;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Web.Composing;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Editors
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Web.Composing;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.UI;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
{
/// <summary>
/// The API controller used for editing dictionary items
@@ -182,7 +179,7 @@ namespace Umbraco.Web.Editors
macroDisplay.Notifications.Clear();
macroDisplay.Notifications.Add(new Models.ContentEditing.Notification("Success", "Macro saved", SpeechBubbleIcon.Success));
macroDisplay.Notifications.Add(new Models.ContentEditing.Notification("Success", "Macro saved", NotificationStyle.Success));
return this.Request.CreateResponse(HttpStatusCode.OK, macroDisplay);
}

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Net.Http;
@@ -18,7 +17,6 @@ using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Mapping;
using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using System.Linq;
@@ -26,9 +24,7 @@ using System.Web.Http.Controllers;
using Umbraco.Core.Composing;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Web.UI;
using Notification = Umbraco.Web.Models.ContentEditing.Notification;
using Umbraco.Core.Persistence;
using Umbraco.Core.Configuration.UmbracoSettings;
@@ -227,7 +223,7 @@ namespace Umbraco.Web.Editors
[FilterAllowedOutgoingMedia(typeof(IEnumerable<ContentItemBasic<ContentPropertyBasic>>))]
public IEnumerable<ContentItemBasic<ContentPropertyBasic>> GetRootMedia()
{
//TODO: Add permissions check!
// TODO: Add permissions check!
return Services.MediaService.GetRootMedia()
.Select(Mapper.Map<IMedia, ContentItemBasic<ContentPropertyBasic>>);
@@ -432,7 +428,7 @@ namespace Umbraco.Web.Editors
if (sourceParentID == destinationParentID)
{
return Request.CreateValidationErrorResponse(new SimpleNotificationModel(new Notification("",Services.TextService.Localize("media/moveToSameFolderFailed"),SpeechBubbleIcon.Error)));
return Request.CreateValidationErrorResponse(new SimpleNotificationModel(new Notification("",Services.TextService.Localize("media/moveToSameFolderFailed"),NotificationStyle.Error)));
}
if (moveResult == false)
{
@@ -457,7 +453,7 @@ namespace Umbraco.Web.Editors
[ModelBinder(typeof(MediaItemBinder))]
MediaItemSave contentItem)
{
//Recent versions of IE/Edge may send in the full clientside file path instead of just the file name.
//Recent versions of IE/Edge may send in the full client side file path instead of just the file name.
//To ensure similar behavior across all browsers no matter what they do - we strip the FileName property of all
//uploaded files to being *only* the actual file name (as it should be).
if (contentItem.UploadedFiles != null && contentItem.UploadedFiles.Any())
@@ -500,7 +496,7 @@ namespace Umbraco.Web.Editors
&& (contentItem.Action == ContentSaveAction.SaveNew))
{
//ok, so the absolute mandatory data is invalid and it's new, we cannot actually continue!
// add the modelstate to the outgoing object and throw validation response
// add the model state to the outgoing object and throw validation response
var forDisplay = Mapper.Map<MediaItemDisplay>(contentItem.PersistedContent);
forDisplay.Errors = ModelState.ToErrorDictionary();
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
@@ -513,7 +509,7 @@ namespace Umbraco.Web.Editors
//return the updated model
var display = Mapper.Map<MediaItemDisplay>(contentItem.PersistedContent);
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
//lastly, if it is not valid, add the model state to the outgoing object and throw a 403
HandleInvalidModelState(display);
//put the correct msgs in
@@ -752,7 +748,7 @@ namespace Umbraco.Web.Editors
tempFiles.Notifications.Add(new Notification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
Services.TextService.Localize("media/disallowedFileType"),
SpeechBubbleIcon.Warning));
NotificationStyle.Warning));
}
}
@@ -844,7 +840,7 @@ namespace Umbraco.Web.Editors
new SimpleNotificationModel(new Notification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"),
SpeechBubbleIcon.Warning))));
NotificationStyle.Warning))));
}
return intParentId;
@@ -928,7 +924,7 @@ namespace Umbraco.Web.Editors
if (media == null && nodeId != Constants.System.Root && nodeId != Constants.System.RecycleBinMedia)
{
media = mediaService.GetById(nodeId);
//put the content item into storage so it can be retreived
//put the content item into storage so it can be retrieved
// in the controller (saves a lookup)
storage[typeof(IMedia).ToString()] = media;
}

View File

@@ -24,7 +24,7 @@ using Umbraco.Web.Composing;
namespace Umbraco.Web.Editors
{
//TODO: We'll need to be careful about the security on this controller, when we start implementing
// TODO: We'll need to be careful about the security on this controller, when we start implementing
// methods to modify content types we'll need to enforce security on the individual methods, we
// cannot put security on the whole controller because things like GetAllowedChildren are required for content editing.
@@ -92,7 +92,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Returns the avilable compositions for this content type
/// Returns the available compositions for this content type
/// This has been wrapped in a dto instead of simple parameters to support having multiple parameters in post request body
/// </summary>
/// <param name="filter.contentTypeId"></param>
@@ -154,7 +154,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Deletes a media type container wth a given ID
/// Deletes a media type container with a given ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>

View File

@@ -179,7 +179,7 @@ namespace Umbraco.Web.Editors
return Mapper.Map<MemberDisplay>(foundMember);
case MembershipScenario.CustomProviderWithUmbracoLink:
//TODO: Support editing custom properties for members with a custom membership provider here.
// TODO: Support editing custom properties for members with a custom membership provider here.
//foundMember = Services.MemberService.GetByKey(key);
//if (foundMember == null)
@@ -238,7 +238,7 @@ namespace Umbraco.Web.Editors
emptyContent.AdditionalData["NewPassword"] = Membership.GeneratePassword(provider.MinRequiredPasswordLength, provider.MinRequiredNonAlphanumericCharacters);
return Mapper.Map<MemberDisplay>(emptyContent);
case MembershipScenario.CustomProviderWithUmbracoLink:
//TODO: Support editing custom properties for members with a custom membership provider here.
// TODO: Support editing custom properties for members with a custom membership provider here.
case MembershipScenario.StandaloneCustomProvider:
default:
@@ -275,7 +275,7 @@ namespace Umbraco.Web.Editors
{
ModelState.Remove("ContentTypeAlias");
//TODO: We're removing this because we are not displaying it but when we support the CustomProviderWithUmbracoLink scenario
// TODO: We're removing this because we are not displaying it but when we support the CustomProviderWithUmbracoLink scenario
// we will be able to have a real name associated so do not remove this state once that is implemented!
ModelState.Remove("Name");
}
@@ -291,7 +291,7 @@ namespace Umbraco.Web.Editors
throw new HttpResponseException(Request.CreateValidationErrorResponse(forDisplay));
}
//TODO: WE need to support this! - requires UI updates, etc...
// TODO: WE need to support this! - requires UI updates, etc...
if (_provider.RequiresQuestionAndAnswer)
{
throw new NotSupportedException("Currently the member editor does not support providers that have RequiresQuestionAndAnswer specified");
@@ -307,7 +307,7 @@ namespace Umbraco.Web.Editors
string generatedPassword = null;
//Depending on the action we need to first do a create or update using the membership provider
// this ensures that passwords are formatted correclty and also performs the validation on the provider itself.
// this ensures that passwords are formatted correctly and also performs the validation on the provider itself.
switch (contentItem.Action)
{
case ContentSaveAction.Save:
@@ -334,7 +334,7 @@ namespace Umbraco.Web.Editors
}
//save the IMember -
//TODO: When we support the CustomProviderWithUmbracoLink scenario, we'll need to save the custom properties for that here too
// TODO: When we support the CustomProviderWithUmbracoLink scenario, we'll need to save the custom properties for that here too
if (MembershipScenario == MembershipScenario.NativeUmbraco)
{
//save the item
@@ -360,7 +360,7 @@ namespace Umbraco.Web.Editors
Roles.AddUserToRoles(contentItem.PersistedContent.Username, toAdd);
}
//set the generated password (if there was one) - in order to do this we'll chuck the gen'd password into the
//set the generated password (if there was one) - in order to do this we'll chuck the generated password into the
// additional data of the IUmbracoEntity of the persisted item - then we can retrieve this in the model mapper and set
// the value to be given to the UI. Hooray for AdditionalData :)
contentItem.PersistedContent.AdditionalData["GeneratedPassword"] = generatedPassword;
@@ -368,11 +368,11 @@ namespace Umbraco.Web.Editors
//return the updated model
var display = Mapper.Map<MemberDisplay>(contentItem.PersistedContent);
//lasty, if it is not valid, add the modelstate to the outgoing object and throw a 403
//lastly, if it is not valid, add the model state to the outgoing object and throw a 403
HandleInvalidModelState(display);
var localizedTextService = Services.TextService;
//put the correct msgs in
//put the correct messages in
switch (contentItem.Action)
{
case ContentSaveAction.Save:
@@ -584,7 +584,7 @@ namespace Umbraco.Web.Editors
UpdateName(contentItem);
//re-assign the mapped values that are not part of the membership provider properties.
// re-assign the mapped values that are not part of the membership provider properties.
var builtInAliases = Constants.Conventions.Member.GetStandardPropertyTypeStubs().Select(x => x.Key).ToArray();
foreach (var p in contentItem.PersistedContent.Properties)
{
@@ -593,17 +593,17 @@ namespace Umbraco.Web.Editors
{
p.SetValue(valueMapped.GetValue());
// fixme/task - ok, I give up, at that point tags are dead here, until we figure it out
//p.TagChanges.Behavior = valueMapped.TagChanges.Behavior;
//p.TagChanges.Enable = valueMapped.TagChanges.Enable;
//p.TagChanges.Tags = valueMapped.TagChanges.Tags;
// FIXME: /task - ok, I give up, at that point tags are dead here, until we figure it out
// p.TagChanges.Behavior = valueMapped.TagChanges.Behavior;
// p.TagChanges.Enable = valueMapped.TagChanges.Enable;
// p.TagChanges.Tags = valueMapped.TagChanges.Tags;
}
}
}
/// <summary>
/// Following a refresh of member data called during an update if the membership provider has changed some underlying data,
/// we don't want to lose the provided, and potentiallly changed, username
/// we don't want to lose the provided, and potentially changed, username
/// </summary>
/// <param name="contentItem"></param>
/// <param name="providedUserName"></param>
@@ -637,7 +637,7 @@ namespace Umbraco.Web.Editors
/// we create an empty IMember instance first (of type 'Member'), this gives us a unique ID (GUID)
/// that we then use to create the member in the custom membership provider. This acts as the link between Umbraco data and
/// the custom membership provider data. This gives us the ability to eventually have custom membership properties but still use
/// a custom memberhip provider. If there is no 'Member' member type, then we will simply just create the membership provider member
/// a custom membership provider. If there is no 'Member' member type, then we will simply just create the membership provider member
/// with no link to our data.
///
/// If this is successful, it will go and re-fetch the IMember from the db because it will now have an ID because the Umbraco provider
@@ -652,7 +652,7 @@ namespace Umbraco.Web.Editors
case MembershipScenario.NativeUmbraco:
//We are using the umbraco membership provider, create the member using the membership provider first.
var umbracoMembershipProvider = (UmbracoMembershipProviderBase)_provider;
//TODO: We are not supporting q/a - passing in empty here
// TODO: We are not supporting q/a - passing in empty here
membershipUser = umbracoMembershipProvider.CreateUser(
contentItem.ContentTypeAlias, contentItem.Username,
contentItem.Password.NewPassword,
@@ -668,7 +668,7 @@ namespace Umbraco.Web.Editors
//create it - this persisted item has already been set in the MemberBinder based on the 'Member' member type:
Services.MemberService.Save(contentItem.PersistedContent);
//TODO: We are not supporting q/a - passing in empty here
// TODO: We are not supporting q/a - passing in empty here
membershipUser = _provider.CreateUser(
contentItem.Username,
contentItem.Password.NewPassword,
@@ -685,7 +685,7 @@ namespace Umbraco.Web.Editors
// link back to the umbraco data
var newKey = Guid.NewGuid();
//TODO: We are not supporting q/a - passing in empty here
// TODO: We are not supporting q/a - passing in empty here
membershipUser = _provider.CreateUser(
contentItem.Username,
contentItem.Password.NewPassword,
@@ -701,7 +701,7 @@ namespace Umbraco.Web.Editors
throw new ArgumentOutOfRangeException();
}
//TODO: Localize these!
// TODO: Localize these!
switch (status)
{
case MembershipCreateStatus.Success:

View File

@@ -51,7 +51,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Deletes a document type wth a given ID
/// Deletes a document type with a given ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
@@ -71,7 +71,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Returns the avilable compositions for this content type
/// Returns the available compositions for this content type
/// </summary>
/// <param name="contentTypeId"></param>
/// <param name="filterContentTypes">

View File

@@ -1,39 +1,29 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Xml;
using System.Xml.Linq;
using Semver;
using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Editors;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Web.Composing;
using Umbraco.Web.JavaScript;
using Umbraco.Web.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.UI;
using Umbraco.Web.UI.JavaScript;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
using File = System.IO.File;
using Notification = Umbraco.Web.Models.ContentEditing.Notification;
using Version = System.Version;
namespace Umbraco.Web.Editors
{
@@ -52,7 +42,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// This checks if this package & version is alraedy installed
/// This checks if this package & version is already installed
/// </summary>
/// <param name="name"></param>
/// <param name="version"></param>
@@ -200,7 +190,7 @@ namespace Umbraco.Web.Editors
model.Notifications.Add(new Notification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
Services.TextService.Localize("media/disallowedFileType"),
SpeechBubbleIcon.Warning));
NotificationStyle.Warning));
}
}

View File

@@ -254,7 +254,7 @@ namespace Umbraco.Web.Editors
if (passwordModel.OldPassword.IsNullOrWhiteSpace() == false)
{
//if an old password is suplied try to change it
//if an old password is supplied try to change it
try
{

View File

@@ -6,10 +6,10 @@ using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Web.Composing;
using Umbraco.Web.Features;
using Umbraco.Web.JavaScript;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.UI.JavaScript;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Editors

View File

@@ -17,9 +17,6 @@ namespace Umbraco.Web.Editors
[HttpGet]
public string GetPublishedStatusUrl()
{
if (_publishedSnapshotService is PublishedCache.XmlPublishedCache.PublishedSnapshotService)
return "views/dashboard/settings/xmldataintegrityreport.html";
//if (service is PublishedCache.PublishedNoCache.PublishedSnapshotService)
// return "views/dashboard/developer/nocache.html";

View File

@@ -22,15 +22,15 @@ namespace Umbraco.Web.Editors
[PluginController("UmbracoApi")]
public class SectionController : UmbracoAuthorizedJsonController
{
private readonly Dashboards _dashboards;
private readonly IDashboardService _dashboardService;
private readonly ISectionService _sectionService;
private readonly ITreeService _treeService;
public SectionController(IGlobalSettings globalSettings, UmbracoContext umbracoContext, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState,
Dashboards dashboards, ISectionService sectionService, ITreeService treeService)
IDashboardService dashboardService, ISectionService sectionService, ITreeService treeService)
: base(globalSettings, umbracoContext, sqlContext, services, appCaches, logger, runtimeState)
{
_dashboards = dashboards;
_dashboardService = dashboardService;
_sectionService = sectionService;
_treeService = treeService;
}
@@ -48,7 +48,7 @@ namespace Umbraco.Web.Editors
ControllerContext = ControllerContext
};
var dashboards = _dashboards.GetDashboards(Security.CurrentUser);
var dashboards = _dashboardService.GetDashboards(Security.CurrentUser);
//now we can add metadata for each section so that the UI knows if there's actually anything at all to render for
//a dashboard for a given section, then the UI can deal with it accordingly (i.e. redirect to the first tree)

View File

@@ -52,7 +52,7 @@ namespace Umbraco.Web.Editors
}
/// <summary>
/// Deletes a template wth a given ID
/// Deletes a template with a given ID
/// </summary>
/// <param name="id"></param>
/// <returns></returns>

View File

@@ -120,6 +120,7 @@ namespace Umbraco.Web.Editors
//x is passed in as the parameter alias for the linq where statement clause
var operation = condition.BuildCondition<IPublishedContent>("x");
//for review - this uses a tonized query rather then the normal linq query.
contents = contents.Where(operation.Compile());
queryExpression.Append(indent);
queryExpression.AppendFormat(".Where({0})", operation);

View File

@@ -112,7 +112,7 @@ namespace Umbraco.Web.Editors
if (onlyCurrentUserGroups == false)
{
//this user is not an admin so in that case we need to exlude all admin users
//this user is not an admin so in that case we need to exclude all admin users
allGroups.RemoveAt(allGroups.IndexOf(allGroups.Find(basic => basic.Alias == Constants.Security.AdminGroupAlias)));
return allGroups;
}

View File

@@ -200,7 +200,7 @@ namespace Umbraco.Web.Editors
var isAdmin = Security.CurrentUser.IsAdmin();
if (isAdmin == false)
{
//this user is not an admin so in that case we need to exlude all admin users
//this user is not an admin so in that case we need to exclude all admin users
excludeUserGroups = new[] {Constants.Security.AdminGroupAlias};
}

View File

@@ -1,61 +0,0 @@
using System;
using System.Web.Http;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.PublishedCache.XmlPublishedCache;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
namespace Umbraco.Web.Editors
{
[ValidateAngularAntiForgeryToken]
public class XmlDataIntegrityController : UmbracoAuthorizedApiController
{
private readonly PublishedSnapshotService _publishedSnapshotService;
public XmlDataIntegrityController(IPublishedSnapshotService publishedSnapshotService)
{
if (publishedSnapshotService == null) throw new ArgumentNullException(nameof(publishedSnapshotService));
_publishedSnapshotService = publishedSnapshotService as PublishedSnapshotService;
if (_publishedSnapshotService == null) throw new NotSupportedException("Unsupported IPublishedSnapshotService, only the Xml one is supported.");
}
[HttpPost]
public bool FixContentXmlTable()
{
_publishedSnapshotService.RebuildContentAndPreviewXml();
return _publishedSnapshotService.VerifyContentAndPreviewXml();
}
[HttpPost]
public bool FixMediaXmlTable()
{
_publishedSnapshotService.RebuildMediaXml();
return _publishedSnapshotService.VerifyMediaXml();
}
[HttpPost]
public bool FixMembersXmlTable()
{
_publishedSnapshotService.RebuildMemberXml();
return _publishedSnapshotService.VerifyMemberXml();
}
[HttpGet]
public bool CheckContentXmlTable()
{
return _publishedSnapshotService.VerifyContentAndPreviewXml();
}
[HttpGet]
public bool CheckMediaXmlTable()
{
return _publishedSnapshotService.VerifyMediaXml();
}
[HttpGet]
public bool CheckMembersXmlTable()
{
return _publishedSnapshotService.VerifyMemberXml();
}
}
}