Merge branch 'dev-v7-contenttypeeditor' into dev-v7-contenttypeeditor-validation
This commit is contained in:
@@ -36,11 +36,9 @@ namespace Umbraco.Core.Persistence.Repositories
|
||||
_contentTypeRepository = contentTypeRepository;
|
||||
_preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax);
|
||||
|
||||
EnsureUniqueNaming = true;
|
||||
}
|
||||
|
||||
public bool EnsureUniqueNaming { get; set; }
|
||||
|
||||
|
||||
#region Overrides of RepositoryBase<int,DataTypeDefinition>
|
||||
|
||||
protected override IDataTypeDefinition PerformGet(int id)
|
||||
@@ -429,8 +427,7 @@ AND umbracoNode.id <> @id",
|
||||
|
||||
private string EnsureUniqueNodeName(string nodeName, int id = 0)
|
||||
{
|
||||
if (EnsureUniqueNaming == false)
|
||||
return nodeName;
|
||||
|
||||
|
||||
var sql = new Sql();
|
||||
sql.Select("*")
|
||||
|
||||
@@ -26,11 +26,8 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static class TypeFinder
|
||||
{
|
||||
private static readonly HashSet<Assembly> LocalFilteredAssemblyCache = new HashSet<Assembly>();
|
||||
private static readonly ReaderWriterLockSlim LocalFilteredAssemblyCacheLocker = new ReaderWriterLockSlim();
|
||||
private static HashSet<Assembly> _allAssemblies = null;
|
||||
private static HashSet<Assembly> _binFolderAssemblies = null;
|
||||
private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
|
||||
private static volatile HashSet<Assembly> _localFilteredAssemblyCache = null;
|
||||
private static readonly object LocalFilteredAssemblyCacheLocker = new object();
|
||||
|
||||
/// <summary>
|
||||
/// lazily load a reference to all assemblies and only local assemblies.
|
||||
@@ -46,162 +43,97 @@ namespace Umbraco.Core
|
||||
/// </remarks>
|
||||
internal static HashSet<Assembly> GetAllAssemblies()
|
||||
{
|
||||
using (var lck = new UpgradeableReadLock(Locker))
|
||||
return AllAssemblies.Value;
|
||||
}
|
||||
|
||||
//Lazy access to the all assemblies list
|
||||
private static readonly Lazy<HashSet<Assembly>> AllAssemblies = new Lazy<HashSet<Assembly>>(() =>
|
||||
{
|
||||
HashSet<Assembly> assemblies = null;
|
||||
try
|
||||
{
|
||||
if (_allAssemblies == null)
|
||||
var isHosted = HttpContext.Current != null;
|
||||
|
||||
try
|
||||
{
|
||||
if (isHosted)
|
||||
{
|
||||
assemblies = new HashSet<Assembly>(BuildManager.GetReferencedAssemblies().Cast<Assembly>());
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
if (!(e.InnerException is SecurityException))
|
||||
throw;
|
||||
}
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
if (assemblies == null)
|
||||
{
|
||||
//NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have
|
||||
// already been loaded in to the app domain, instead we will look directly into the bin folder and load each one.
|
||||
var binFolder = IOHelper.GetRootDirectoryBinFolder();
|
||||
var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList();
|
||||
//var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory;
|
||||
//var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList();
|
||||
assemblies = new HashSet<Assembly>();
|
||||
foreach (var a in binAssemblyFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
var assName = AssemblyName.GetAssemblyName(a);
|
||||
var ass = Assembly.Load(assName);
|
||||
assemblies.Add(ass);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is SecurityException || e is BadImageFormatException)
|
||||
{
|
||||
//swallow these exceptions
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<Assembly> assemblies = null;
|
||||
//if for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies.
|
||||
if (!assemblies.Any())
|
||||
{
|
||||
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
assemblies.Add(a);
|
||||
}
|
||||
}
|
||||
|
||||
//here we are trying to get the App_Code assembly
|
||||
var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported
|
||||
var appCodeFolder = new DirectoryInfo(IOHelper.MapPath(IOHelper.ResolveUrl("~/App_code")));
|
||||
//check if the folder exists and if there are any files in it with the supported file extensions
|
||||
if (appCodeFolder.Exists && (fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())))
|
||||
{
|
||||
try
|
||||
{
|
||||
var isHosted = HttpContext.Current != null;
|
||||
|
||||
try
|
||||
{
|
||||
if (isHosted)
|
||||
{
|
||||
assemblies = new HashSet<Assembly>(BuildManager.GetReferencedAssemblies().Cast<Assembly>());
|
||||
}
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
if (!(e.InnerException is SecurityException))
|
||||
throw;
|
||||
}
|
||||
|
||||
|
||||
if (assemblies == null)
|
||||
{
|
||||
//NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have
|
||||
// already been loaded in to the app domain, instead we will look directly into the bin folder and load each one.
|
||||
var binFolder = IOHelper.GetRootDirectoryBinFolder();
|
||||
var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList();
|
||||
//var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory;
|
||||
//var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList();
|
||||
assemblies = new HashSet<Assembly>();
|
||||
foreach (var a in binAssemblyFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
var assName = AssemblyName.GetAssemblyName(a);
|
||||
var ass = Assembly.Load(assName);
|
||||
assemblies.Add(ass);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is SecurityException || e is BadImageFormatException)
|
||||
{
|
||||
//swallow these exceptions
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies.
|
||||
if (!assemblies.Any())
|
||||
{
|
||||
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
assemblies.Add(a);
|
||||
}
|
||||
}
|
||||
|
||||
//here we are trying to get the App_Code assembly
|
||||
var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported
|
||||
var appCodeFolder = new DirectoryInfo(IOHelper.MapPath(IOHelper.ResolveUrl("~/App_code")));
|
||||
//check if the folder exists and if there are any files in it with the supported file extensions
|
||||
if (appCodeFolder.Exists && (fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any())))
|
||||
{
|
||||
var appCodeAssembly = Assembly.Load("App_Code");
|
||||
if (!assemblies.Contains(appCodeAssembly)) // BuildManager will find App_Code already
|
||||
assemblies.Add(appCodeAssembly);
|
||||
}
|
||||
|
||||
//now set the _allAssemblies
|
||||
_allAssemblies = new HashSet<Assembly>(assemblies);
|
||||
|
||||
var appCodeAssembly = Assembly.Load("App_Code");
|
||||
if (!assemblies.Contains(appCodeAssembly)) // BuildManager will find App_Code already
|
||||
assemblies.Add(appCodeAssembly);
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
catch (FileNotFoundException ex)
|
||||
{
|
||||
if (!(e.InnerException is SecurityException))
|
||||
throw;
|
||||
|
||||
_binFolderAssemblies = _allAssemblies;
|
||||
//this will occur if it cannot load the assembly
|
||||
LogHelper.Error(typeof(TypeFinder), "Could not load assembly App_Code", ex);
|
||||
}
|
||||
}
|
||||
|
||||
return _allAssemblies;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns only assemblies found in the bin folder that have been loaded into the app domain.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This will be used if we implement App_Plugins from Umbraco v5 but currently it is not used.
|
||||
/// </remarks>
|
||||
internal static HashSet<Assembly> GetBinAssemblies()
|
||||
{
|
||||
|
||||
if (_binFolderAssemblies == null)
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
using (new WriteLock(Locker))
|
||||
{
|
||||
var assemblies = GetAssembliesWithKnownExclusions().ToArray();
|
||||
var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory;
|
||||
var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList();
|
||||
var domainAssemblyNames = binAssemblyFiles.Select(AssemblyName.GetAssemblyName);
|
||||
var safeDomainAssemblies = new HashSet<Assembly>();
|
||||
var binFolderAssemblies = new HashSet<Assembly>();
|
||||
|
||||
foreach (var a in assemblies)
|
||||
{
|
||||
try
|
||||
{
|
||||
//do a test to see if its queryable in med trust
|
||||
var assemblyFile = a.GetAssemblyFile();
|
||||
safeDomainAssemblies.Add(a);
|
||||
}
|
||||
catch (SecurityException)
|
||||
{
|
||||
//we will just ignore this because this will fail
|
||||
//in medium trust for system assemblies, we get an exception but we just want to continue until we get to
|
||||
//an assembly that is ok.
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var assemblyName in domainAssemblyNames)
|
||||
{
|
||||
try
|
||||
{
|
||||
var foundAssembly =
|
||||
safeDomainAssemblies.FirstOrDefault(a => a.GetAssemblyFile() == assemblyName.GetAssemblyFile());
|
||||
if (foundAssembly != null)
|
||||
{
|
||||
binFolderAssemblies.Add(foundAssembly);
|
||||
}
|
||||
}
|
||||
catch (SecurityException)
|
||||
{
|
||||
//we will just ignore this because if we are trying to do a call to:
|
||||
// AssemblyName.ReferenceMatchesDefinition(a.GetName(), assemblyName)))
|
||||
//in medium trust for system assemblies, we get an exception but we just want to continue until we get to
|
||||
//an assembly that is ok.
|
||||
}
|
||||
}
|
||||
|
||||
_binFolderAssemblies = new HashSet<Assembly>(binFolderAssemblies);
|
||||
}
|
||||
if (!(e.InnerException is SecurityException))
|
||||
throw;
|
||||
}
|
||||
return _binFolderAssemblies;
|
||||
}
|
||||
|
||||
return assemblies;
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Return a list of found local Assemblies excluding the known assemblies we don't want to scan
|
||||
@@ -213,20 +145,23 @@ namespace Umbraco.Core
|
||||
internal static HashSet<Assembly> GetAssembliesWithKnownExclusions(
|
||||
IEnumerable<Assembly> excludeFromResults = null)
|
||||
{
|
||||
using (var lck = new UpgradeableReadLock(LocalFilteredAssemblyCacheLocker))
|
||||
if (_localFilteredAssemblyCache == null)
|
||||
{
|
||||
if (LocalFilteredAssemblyCache.Any()) return LocalFilteredAssemblyCache;
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
|
||||
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
|
||||
foreach (var a in assemblies)
|
||||
lock (LocalFilteredAssemblyCacheLocker)
|
||||
{
|
||||
LocalFilteredAssemblyCache.Add(a);
|
||||
//double check
|
||||
if (_localFilteredAssemblyCache == null)
|
||||
{
|
||||
_localFilteredAssemblyCache = new HashSet<Assembly>();
|
||||
var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter);
|
||||
foreach (var a in assemblies)
|
||||
{
|
||||
_localFilteredAssemblyCache.Add(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return LocalFilteredAssemblyCache;
|
||||
}
|
||||
return _localFilteredAssemblyCache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,13 +2,32 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.services.contentEditingHelper
|
||||
* @description A helper service for most editors, some methods are specific to content/media/member model types but most are used by
|
||||
* @description A helper service for most editors, some methods are specific to content/media/member model types but most are used by
|
||||
* all editors to share logic and reduce the amount of replicated code among editors.
|
||||
**/
|
||||
function contentEditingHelper(fileManager, $q, $location, $routeParams, notificationsService, serverValidationManager, dialogService, formHelper, appState, keyboardService) {
|
||||
|
||||
function isValidIdentifier(id){
|
||||
//empty id <= 0
|
||||
if(angular.isNumber(id) && id > 0){
|
||||
return true;
|
||||
}
|
||||
|
||||
//empty guid
|
||||
if(id === "00000000-0000-0000-0000-000000000000"){
|
||||
return false;
|
||||
}
|
||||
|
||||
//empty string / alias
|
||||
if(id === ""){
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
|
||||
/** Used by the content editor and mini content editor to perform saving operations */
|
||||
contentEditorPerformSave: function (args) {
|
||||
if (!angular.isObject(args)) {
|
||||
@@ -30,7 +49,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
var self = this;
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
||||
|
||||
if (!args.scope.busy && formHelper.submitForm({ scope: args.scope, statusMessage: args.statusMessage })) {
|
||||
|
||||
args.scope.busy = true;
|
||||
@@ -71,9 +90,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
|
||||
/** Returns the action button definitions based on what permissions the user has.
|
||||
The content.allowedActions parameter contains a list of chars, each represents a button by permission so
|
||||
The content.allowedActions parameter contains a list of chars, each represents a button by permission so
|
||||
here we'll build the buttons according to the chars of the user. */
|
||||
configureContentEditorButtons: function (args) {
|
||||
|
||||
@@ -136,7 +155,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
handler: args.methods.unPublish
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,8 +179,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
|
||||
//Now we need to make the drop down button list, this is also slightly tricky because:
|
||||
//We cannot have any buttons if there's no default button above.
|
||||
//We cannot have the unpublish button (Z) when there's no publish permission.
|
||||
//We cannot have the unpublish button (Z) when the item is not published.
|
||||
//We cannot have the unpublish button (Z) when there's no publish permission.
|
||||
//We cannot have the unpublish button (Z) when the item is not published.
|
||||
if (buttons.defaultButton) {
|
||||
|
||||
//get the last index of the button order
|
||||
@@ -174,7 +193,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
}
|
||||
|
||||
|
||||
//if we are not creating, then we should add unpublish too,
|
||||
//if we are not creating, then we should add unpublish too,
|
||||
// so long as it's already published and if the user has access to publish
|
||||
if (!args.create) {
|
||||
if (args.content.publishDate && _.contains(args.content.allowedActions, "U")) {
|
||||
@@ -235,13 +254,13 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
actions.push(defaultAction);
|
||||
|
||||
//Now we need to make the drop down button list, this is also slightly tricky because:
|
||||
//We cannot have any buttons if there's no default button above.
|
||||
//We cannot have the unpublish button (Z) when there's no publish permission.
|
||||
//We cannot have the unpublish button (Z) when the item is not published.
|
||||
//We cannot have the unpublish button (Z) when there's no publish permission.
|
||||
//We cannot have the unpublish button (Z) when the item is not published.
|
||||
if (defaultAction) {
|
||||
//get the last index of the button order
|
||||
var lastIndex = _.indexOf(actionOrder, defaultAction);
|
||||
@@ -253,7 +272,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
}
|
||||
}
|
||||
|
||||
//if we are not creating, then we should add unpublish too,
|
||||
//if we are not creating, then we should add unpublish too,
|
||||
// so long as it's already published and if the user has access to publish
|
||||
if (!creating) {
|
||||
if (content.publishDate && _.contains(content.allowedActions,"U")) {
|
||||
@@ -329,7 +348,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
var allOrigProps = this.getAllProps(origContent);
|
||||
var allNewProps = this.getAllProps(savedContent);
|
||||
|
||||
function getNewProp(alias) {
|
||||
function getNewProp(alias) {
|
||||
return _.find(allNewProps, function (item) {
|
||||
return item.alias === alias;
|
||||
});
|
||||
@@ -343,12 +362,12 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
};
|
||||
//check for changed built-in properties of the content
|
||||
for (var o in origContent) {
|
||||
|
||||
|
||||
//ignore the ones listed in the array
|
||||
if (shouldIgnore(o)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!_.isEqual(origContent[o], savedContent[o])) {
|
||||
origContent[o] = savedContent[o];
|
||||
}
|
||||
@@ -362,8 +381,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
//they have changed so set the origContent prop to the new one
|
||||
var origVal = allOrigProps[p].value;
|
||||
allOrigProps[p].value = newProp.value;
|
||||
|
||||
//instead of having a property editor $watch their expression to check if it has
|
||||
|
||||
//instead of having a property editor $watch their expression to check if it has
|
||||
// been updated, instead we'll check for the existence of a special method on their model
|
||||
// and just call it.
|
||||
if (angular.isFunction(allOrigProps[p].onValueChanged)) {
|
||||
@@ -388,7 +407,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
* A function to handle what happens when we have validation issues from the server side
|
||||
*/
|
||||
handleSaveError: function (args) {
|
||||
|
||||
|
||||
if (!args.err) {
|
||||
throw "args.err cannot be null";
|
||||
}
|
||||
@@ -402,7 +421,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
if (args.err.status === 400) {
|
||||
//now we need to look through all the validation errors
|
||||
if (args.err.data && (args.err.data.ModelState)) {
|
||||
|
||||
|
||||
//wire up the server validation errs
|
||||
formHelper.handleServerValidation(args.err.data.ModelState);
|
||||
|
||||
@@ -414,7 +433,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
if (args.rebindCallback && angular.isFunction(args.rebindCallback)) {
|
||||
args.rebindCallback();
|
||||
}
|
||||
|
||||
|
||||
serverValidationManager.executeAndClearAllSubscriptions();
|
||||
}
|
||||
|
||||
@@ -453,7 +472,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
}
|
||||
|
||||
if (!this.redirectToCreatedContent(args.redirectId ? args.redirectId : args.savedContent.id)) {
|
||||
|
||||
|
||||
//we are not redirecting because this is not new content, it is existing content. In this case
|
||||
// we need to detect what properties have changed and re-bind them with the server data.
|
||||
//call the callback
|
||||
@@ -471,14 +490,14 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
*
|
||||
* @description
|
||||
* Changes the location to be editing the newly created content after create was successful.
|
||||
* We need to decide if we need to redirect to edito mode or if we will remain in create mode.
|
||||
* We need to decide if we need to redirect to edito mode or if we will remain in create mode.
|
||||
* We will only need to maintain create mode if we have not fulfilled the basic requirements for creating an entity which is at least having a name and ID
|
||||
*/
|
||||
redirectToCreatedContent: function (id, modelState) {
|
||||
|
||||
//only continue if we are currently in create mode and if there is no 'Name' modelstate errors
|
||||
// since we need at least a name to create content.
|
||||
if ($routeParams.create && (id > 0 && (!modelState || !modelState["Name"]))) {
|
||||
if ($routeParams.create && (isValidIdentifier(id) && (!modelState || !modelState["Name"]))) {
|
||||
|
||||
//need to change the location to not be in 'create' mode. Currently the route will be something like:
|
||||
// /belle/#/content/edit/1234?doctype=newsArticle&create=true
|
||||
@@ -487,7 +506,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
|
||||
//clear the query strings
|
||||
$location.search("");
|
||||
|
||||
|
||||
//change to new path
|
||||
$location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id);
|
||||
//don't add a browser history for this
|
||||
@@ -498,4 +517,4 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
|
||||
}
|
||||
};
|
||||
}
|
||||
angular.module('umbraco.services').factory('contentEditingHelper', contentEditingHelper);
|
||||
angular.module('umbraco.services').factory('contentEditingHelper', contentEditingHelper);
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Umbraco.Web.Trees
|
||||
Constants.Applications.Media,
|
||||
Constants.Applications.Members)]
|
||||
[LegacyBaseTree(typeof (loadMembers))]
|
||||
[Tree(Constants.Applications.Members, Constants.Trees.Members, "Members")]
|
||||
[Tree(Constants.Applications.Members, Constants.Trees.Members)]
|
||||
[PluginController("UmbracoTrees")]
|
||||
[CoreTree]
|
||||
public class MemberTreeController : TreeController
|
||||
|
||||
@@ -8,6 +8,16 @@ namespace Umbraco.Web.Trees
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public class TreeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TreeAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="appAlias">The app alias.</param>
|
||||
/// <param name="alias">The alias.</param>
|
||||
public TreeAttribute(string appAlias,
|
||||
string alias) : this(appAlias, alias, null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TreeAttribute"/> class.
|
||||
/// </summary>
|
||||
@@ -35,6 +45,8 @@ namespace Umbraco.Web.Trees
|
||||
SortOrder = sortOrder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public string ApplicationAlias { get; private set; }
|
||||
public string Alias { get; private set; }
|
||||
public string Title { get; private set; }
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Threading;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Models.Trees;
|
||||
using Umbraco.Web.Mvc;
|
||||
|
||||
@@ -37,7 +41,22 @@ namespace Umbraco.Web.Trees
|
||||
/// </summary>
|
||||
public override string RootNodeDisplayName
|
||||
{
|
||||
get { return _attribute.Title; }
|
||||
get
|
||||
{
|
||||
//if title is defined, return that
|
||||
if(string.IsNullOrEmpty(_attribute.Title) == false)
|
||||
return _attribute.Title;
|
||||
|
||||
|
||||
//try to look up a tree header matching the tree alias
|
||||
var culture = Security.CurrentUser.GetUserCulture(Services.TextService);
|
||||
var localizedLabel = Services.TextService.Localize("treeHeaders/" + _attribute.Alias, culture);
|
||||
if (string.IsNullOrEmpty(localizedLabel) == false)
|
||||
return localizedLabel;
|
||||
|
||||
//is returned to signal that a label was not found
|
||||
return "[" + _attribute.Alias + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace umbraco.uicontrols {
|
||||
base.CreateChildControls();
|
||||
|
||||
_tabList.TagName = "ul";
|
||||
_tabList.Attributes.Add("class", "nav nav-tabs umb-nav-tabs span12");
|
||||
_tabList.Attributes.Add("class", "nav nav-tabs umb-nav-tabs span12 -padding-left");
|
||||
base.row.Controls.Add(_tabList);
|
||||
|
||||
_body.TagName = "div";
|
||||
@@ -41,7 +41,7 @@ namespace umbraco.uicontrols {
|
||||
base.Controls.Add(_body);
|
||||
|
||||
_tabsHolder.TagName = "div";
|
||||
_tabsHolder.Attributes.Add("class", "tab-content form-horizontal umb-tab-content");
|
||||
_tabsHolder.Attributes.Add("class", "tab-content form-horizontal");
|
||||
_tabsHolder.ID = this.ID + "_content";
|
||||
_body.Controls.Add(_tabsHolder);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user