diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Config/log4net.config b/src/Orchard.Azure/Orchard.Azure.Web/Config/log4net.config index 7f77d5e2d..1aaf635be 100644 --- a/src/Orchard.Azure/Orchard.Azure.Web/Config/log4net.config +++ b/src/Orchard.Azure/Orchard.Azure.Web/Config/log4net.config @@ -13,7 +13,7 @@ - + diff --git a/src/Orchard.Web/Core/Common/Drivers/CommonPartDriver.cs b/src/Orchard.Web/Core/Common/Drivers/CommonPartDriver.cs index de9a436b7..0c738c082 100644 --- a/src/Orchard.Web/Core/Common/Drivers/CommonPartDriver.cs +++ b/src/Orchard.Web/Core/Common/Drivers/CommonPartDriver.cs @@ -51,11 +51,6 @@ namespace Orchard.Core.Common.Drivers { } protected override DriverResult Editor(CommonPart part, IUpdateModel updater, dynamic shapeHelper) { - var currentUser = _authenticationService.GetAuthenticatedUser(); - if (!_authorizationService.TryCheckAccess(StandardPermissions.SiteOwner, currentUser, part)) { - return null; - } - var model = new ContainerEditorViewModel(); if (part.Container != null) model.ContainerId = part.Container.ContentItem.Id; diff --git a/src/Orchard.Web/Core/Common/Handlers/IdentityPartHandler.cs b/src/Orchard.Web/Core/Common/Handlers/IdentityPartHandler.cs index dd2fb4055..ae461bdca 100644 --- a/src/Orchard.Web/Core/Common/Handlers/IdentityPartHandler.cs +++ b/src/Orchard.Web/Core/Common/Handlers/IdentityPartHandler.cs @@ -12,6 +12,10 @@ namespace Orchard.Core.Common.Handlers { IContentManager contentManager) { Filters.Add(StorageFilter.For(identityRepository)); OnInitializing(AssignIdentity); + + OnIndexing((context, part) => { + context.DocumentIndex.Add("identifier", part.Identifier).Store(); + }); } protected void AssignIdentity(InitializingContentContext context, IdentityPart part) { diff --git a/src/Orchard.Web/Modules/Orchard.Autoroute/Views/EditorTemplates/Parts.Autoroute.Edit.cshtml b/src/Orchard.Web/Modules/Orchard.Autoroute/Views/EditorTemplates/Parts.Autoroute.Edit.cshtml index 39c31abed..62e139c0f 100644 --- a/src/Orchard.Web/Modules/Orchard.Autoroute/Views/EditorTemplates/Parts.Autoroute.Edit.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Autoroute/Views/EditorTemplates/Parts.Autoroute.Edit.cshtml @@ -26,8 +26,16 @@ @T("Save the current item and leave the input empty to have it automatically generated using the pattern {0} e.g., {1}", defaultPattern.Name, defaultPattern.Description) } else { - @Url.MakeAbsolute("/")@urlPrefix - @T("Save the current item and the url will be generated using the pattern {0} e.g., {1}", defaultPattern.Name, defaultPattern.Description) + var hintClass = string.Empty; + if (!string.IsNullOrEmpty(Model.CurrentUrl)) { + hintClass = "hint"; + @Url.MakeAbsolute("/")@urlPrefix@Model.CurrentUrl + } + + if (string.IsNullOrEmpty(Model.CurrentUrl) + || (!string.IsNullOrEmpty(Model.CurrentUrl) && Model.Settings.AutomaticAdjustmentOnEdit)) { + @T("Save the current item and the url will be generated using the pattern {0} e.g., {1}", defaultPattern.Name, defaultPattern.Description) + } } @if (!String.IsNullOrEmpty(Model.CurrentUrl)) { diff --git a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Orchard.Azure.MediaServices.csproj b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Orchard.Azure.MediaServices.csproj index 39e342c13..dd2b9891d 100644 --- a/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Orchard.Azure.MediaServices.csproj +++ b/src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Orchard.Azure.MediaServices.csproj @@ -184,15 +184,15 @@ - - - - - - - - - + + + + + + + + + @@ -290,9 +290,9 @@ - - - + + + @@ -446,9 +446,9 @@ - - - + + + @@ -551,7 +551,7 @@ - + diff --git a/src/Orchard.Web/Modules/Orchard.ContentPicker/Drivers/ContentPickerFieldDriver.cs b/src/Orchard.Web/Modules/Orchard.ContentPicker/Drivers/ContentPickerFieldDriver.cs index 50ebda63b..9fb400c2f 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentPicker/Drivers/ContentPickerFieldDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentPicker/Drivers/ContentPickerFieldDriver.cs @@ -50,7 +50,7 @@ namespace Orchard.ContentPicker.Drivers { } protected override DriverResult Editor(ContentPart part, Fields.ContentPickerField field, IUpdateModel updater, dynamic shapeHelper) { - var model = new ContentPickerFieldViewModel(); + var model = new ContentPickerFieldViewModel { SelectedIds = string.Join(",", field.Ids) }; updater.TryUpdateModel(model, GetPrefix(field, part), null, null); diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/Controllers/AdminController.cs index 4c2192a38..f56f043d7 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Controllers/AdminController.cs @@ -284,11 +284,14 @@ namespace Orchard.ContentTypes.Controllers { if (typeViewModel == null) return HttpNotFound(); + var typePartNames = new HashSet(typeViewModel.Parts.Select(tvm => tvm.PartDefinition.Name)); + var viewModel = new AddPartsViewModel { Type = typeViewModel, PartSelections = _contentDefinitionService.GetParts(false/*metadataPartsOnly*/) - .Where(cpd => !typeViewModel.Parts.Any(p => p.PartDefinition.Name == cpd.Name) && cpd.Settings.GetModel().Attachable) - .Select(cpd => new PartSelectionViewModel { PartName = cpd.Name, PartDisplayName = cpd.DisplayName, PartDescription = cpd.Description}) + .Where(cpd => !typePartNames.Contains(cpd.Name) && cpd.Settings.GetModel().Attachable) + .Select(cpd => new PartSelectionViewModel { PartName = cpd.Name, PartDisplayName = cpd.DisplayName, PartDescription = cpd.Description }) + .ToList() }; return View(viewModel); diff --git a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs index 0c24c667f..a4e229447 100644 --- a/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs +++ b/src/Orchard.Web/Modules/Orchard.ContentTypes/Services/ContentDefinitionService.cs @@ -195,22 +195,30 @@ namespace Orchard.ContentTypes.Services { } public IEnumerable GetParts(bool metadataPartsOnly) { - var typeNames = GetTypes().Select(ctd => ctd.Name); + var typeNames = new HashSet(GetTypes().Select(ctd => ctd.Name)); // user-defined parts // except for those parts with the same name as a type (implicit type's part or a mistake) - var userContentParts = _contentDefinitionManager - .ListPartDefinitions() + var userContentParts = _contentDefinitionManager.ListPartDefinitions() .Where(cpd => !typeNames.Contains(cpd.Name)) - .Select(cpd => new EditPartViewModel(cpd)); + .Select(cpd => new EditPartViewModel(cpd)) + .ToDictionary( + k => k.Name, + v => v); // code-defined parts - var codeDefinedParts = metadataPartsOnly ? - Enumerable.Empty() : - _contentPartDrivers.SelectMany(d => d.GetPartInfo().Where(cpd => !userContentParts.Any(m => m.Name == cpd.PartName)).Select(cpi => new EditPartViewModel { Name = cpi.PartName })); + var codeDefinedParts = metadataPartsOnly + ? Enumerable.Empty() + : _contentPartDrivers + .SelectMany(d => d.GetPartInfo() + .Where(cpd => !userContentParts.ContainsKey(cpd.PartName)) + .Select(cpi => new EditPartViewModel { Name = cpi.PartName, DisplayName = cpi.PartName })) + .ToList(); // Order by display name - return userContentParts.Union(codeDefinedParts).OrderBy(m => m.DisplayName); + return codeDefinedParts + .Union(userContentParts.Values) + .OrderBy(m => m.DisplayName); } public EditPartViewModel GetPart(string name) { diff --git a/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml b/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml index e95c2aa93..5dc77d917 100644 --- a/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Lists/Views/Admin/List.cshtml @@ -9,7 +9,7 @@ Script.Require("ContentPicker").AtFoot(); Script.Require("jQueryUI_Sortable").AtFoot(); Script.Require("jQueryColorBox"); - Script.Include("nprogress.js", "mprogress.min.js").AtFoot(); + Script.Include("nprogress.js", "nprogress.min.js").AtFoot(); Script.Include("orchard-lists-admin.js", "orchard-lists-admin.min.js").AtFoot(); var containerId = ((int?)Model.ContainerId).GetValueOrDefault(); diff --git a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Fields/MediaLibraryPickerField.cs b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Fields/MediaLibraryPickerField.cs index 59a422488..d2ef6c94f 100644 --- a/src/Orchard.Web/Modules/Orchard.MediaLibrary/Fields/MediaLibraryPickerField.cs +++ b/src/Orchard.Web/Modules/Orchard.MediaLibrary/Fields/MediaLibraryPickerField.cs @@ -21,6 +21,19 @@ namespace Orchard.MediaLibrary.Fields { } } + /// + /// Gets the MediaUrl property of the first Media, or null if none + /// + public string FirstMediaUrl { + get { + if (!MediaParts.Any()) { + return null; + } + + return MediaParts.First().MediaUrl; + } + } + private string EncodeIds(ICollection ids) { if (ids == null || !ids.Any()) { return string.Empty; diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Commands/FeatureCommands.cs b/src/Orchard.Web/Modules/Orchard.Modules/Commands/FeatureCommands.cs index 78555e4f3..de30bea92 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Commands/FeatureCommands.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Commands/FeatureCommands.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Orchard.Commands; using Orchard.Environment.Descriptor.Models; @@ -57,11 +58,11 @@ namespace Orchard.Modules.Commands { [CommandName("feature enable")] public void Enable(params string[] featureNames) { Context.Output.WriteLine(T("Enabling features {0}", string.Join(",", featureNames))); - bool listAvailableFeatures = false; - List featuresToEnable = new List(); - string[] availableFeatures = _featureManager.GetAvailableFeatures().Select(x => x.Id).ToArray(); + var listAvailableFeatures = false; + var featuresToEnable = new List(); + var availableFeatures = _featureManager.GetAvailableFeatures().Select(x => x.Id).OrderBy(x => x).ToArray(); foreach (var featureName in featureNames) { - if (availableFeatures.Contains(featureName)) { + if (availableFeatures.Contains(featureName, StringComparer.OrdinalIgnoreCase)) { featuresToEnable.Add(featureName); } else { diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs b/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs index 6c9fd9da8..4f3a3d6ee 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs @@ -92,7 +92,7 @@ namespace Orchard.Modules.Services { /// Boolean parameter indicating if the feature should disable the features which depend on it if required or fail otherwise. public void DisableFeatures(IEnumerable featureIds, bool force) { foreach (string featureId in _featureManager.DisableFeatures(featureIds, force)) { - var featureName = _featureManager.GetAvailableFeatures().Where(f => f.Id == featureId).First().Name; + var featureName = _featureManager.GetAvailableFeatures().Single(f => f.Id.Equals(featureId, StringComparison.OrdinalIgnoreCase)).Name; Services.Notifier.Information(T("{0} was disabled", featureName)); } } diff --git a/src/Orchard.Web/Modules/Orchard.Tags/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Tags/Controllers/AdminController.cs index 2df762286..5f13ae59e 100644 --- a/src/Orchard.Web/Modules/Orchard.Tags/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Tags/Controllers/AdminController.cs @@ -143,9 +143,6 @@ namespace Orchard.Tags.Controllers { } public JsonResult FetchSimilarTags(string snippet) { - if (!Services.Authorizer.Authorize(Permissions.ManageTags, T("Not authorized to fetch tags"))) - return Json(null); - return Json( _tagService.GetTagsByNameSnippet(snippet).Select(tag => tag.TagName).ToList(), JsonRequestBehavior.AllowGet diff --git a/src/Orchard.Web/Modules/Orchard.Tokens/Providers/TextTokens.cs b/src/Orchard.Web/Modules/Orchard.Tokens/Providers/TextTokens.cs index d380d6679..89f77c018 100644 --- a/src/Orchard.Web/Modules/Orchard.Tokens/Providers/TextTokens.cs +++ b/src/Orchard.Web/Modules/Orchard.Tokens/Providers/TextTokens.cs @@ -66,20 +66,22 @@ namespace Orchard.Tokens.Providers { return String.Empty; } - var index = param.IndexOf(','); + int index = param.IndexOf(','); + int limit = index == -1 ? Int32.Parse(param) : Int32.Parse(param.Substring(0, index)); - // no ellipsis + if (token.Length <= limit) { + // no limit + return token; + } if (index == -1) { - var limit = Int32.Parse(param); - token = token.Substring(0, limit); + // no ellipsis + return token.Substring(0, limit); } else { - var limit = Int32.Parse(param.Substring(0, index)); + // ellipsis var ellipsis = param.Substring(index + 1); - token = token.Substring(0, limit) + ellipsis; + return token.Substring(0, limit) + ellipsis; } - - return token; } } diff --git a/src/Orchard/Environment/DefaultOrchardHost.cs b/src/Orchard/Environment/DefaultOrchardHost.cs index 195c54ef0..aae23901d 100644 --- a/src/Orchard/Environment/DefaultOrchardHost.cs +++ b/src/Orchard/Environment/DefaultOrchardHost.cs @@ -38,7 +38,7 @@ namespace Orchard.Environment { IExtensionLoaderCoordinator extensionLoaderCoordinator, IExtensionMonitoringCoordinator extensionMonitoringCoordinator, ICacheManager cacheManager, - IHostLocalRestart hostLocalRestart ) { + IHostLocalRestart hostLocalRestart) { _shellSettingsManager = shellSettingsManager; _shellContextFactory = shellContextFactory; _runningShellTable = runningShellTable; @@ -90,8 +90,10 @@ namespace Orchard.Environment { MonitorExtensions(); BuildCurrent(); + var shellContext = CreateShellContext(shellSettings); - return shellContext.LifetimeScope.CreateWorkContextScope(); + var workContext = shellContext.LifetimeScope.CreateWorkContextScope(); + return new StandaloneEnvironmentWorkContextScopeWrapper(workContext, shellContext); } /// @@ -153,17 +155,17 @@ namespace Orchard.Environment { /// Starts a Shell and registers its settings in RunningShellTable /// private void ActivateShell(ShellContext context) { - Logger.Debug("Activating context for tenant {0}", context.Settings.Name); + Logger.Debug("Activating context for tenant {0}", context.Settings.Name); context.Shell.Activate(); _shellContexts = (_shellContexts ?? Enumerable.Empty()) .Where(c => c.Settings.Name != context.Settings.Name) .Concat(new[] { context }) - .ToArray(); - + .ToArray(); + _runningShellTable.Add(context.Settings); } - + /// /// Creates a transient shell for the default tenant's setup /// @@ -214,7 +216,7 @@ namespace Orchard.Environment { if (_shellContexts != null) { foreach (var shellContext in _shellContexts) { shellContext.Shell.Terminate(); - shellContext.LifetimeScope.Dispose(); + shellContext.Dispose(); } } } @@ -275,7 +277,7 @@ namespace Orchard.Environment { // terminate the shell if the tenant was disabled else if (settings.State == TenantState.Disabled) { shellContext.Shell.Terminate(); - shellContext.LifetimeScope.Dispose(); + shellContext.Dispose(); _runningShellTable.Remove(settings); _shellContexts = _shellContexts.Where(shell => shell.Settings.Name != settings.Name); @@ -284,7 +286,7 @@ namespace Orchard.Environment { else { // dispose previous context shellContext.Shell.Terminate(); - shellContext.LifetimeScope.Dispose(); + shellContext.Dispose(); var context = _shellContextFactory.CreateShellContext(settings); @@ -326,5 +328,33 @@ namespace Orchard.Environment { Logger.Debug("Adding tenant to restart: " + tenant); _tenantsToRestart.GetState().Add(context.Settings); } + + // To be used from CreateStandaloneEnvironment(), also disposes the ShellContext LifetimeScope. + private class StandaloneEnvironmentWorkContextScopeWrapper : IWorkContextScope { + private readonly ShellContext _shellContext; + private readonly IWorkContextScope _workContextScope; + + public WorkContext WorkContext { + get { return _workContextScope.WorkContext; } + } + + public StandaloneEnvironmentWorkContextScopeWrapper(IWorkContextScope workContextScope, ShellContext shellContext) { + _workContextScope = workContextScope; + _shellContext = shellContext; + } + + public TService Resolve() { + return _workContextScope.Resolve(); + } + + public bool TryResolve(out TService service) { + return _workContextScope.TryResolve(out service); + } + + public void Dispose() { + _workContextScope.Dispose(); + _shellContext.Dispose(); + } + } } } diff --git a/src/Orchard/Environment/ShellBuilders/ShellContext.cs b/src/Orchard/Environment/ShellBuilders/ShellContext.cs index ede74b266..34fd3061c 100644 --- a/src/Orchard/Environment/ShellBuilders/ShellContext.cs +++ b/src/Orchard/Environment/ShellBuilders/ShellContext.cs @@ -1,14 +1,42 @@ +using System; using Autofac; using Orchard.Environment.Configuration; using Orchard.Environment.Descriptor.Models; using Orchard.Environment.ShellBuilders.Models; namespace Orchard.Environment.ShellBuilders { - public class ShellContext { + public class ShellContext : IDisposable { + private bool _disposed = false; + public ShellSettings Settings { get; set; } public ShellDescriptor Descriptor { get; set; } public ShellBlueprint Blueprint { get; set; } public ILifetimeScope LifetimeScope { get; set; } public IOrchardShell Shell { get; set; } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { + if (!_disposed) { + + if (disposing) { + LifetimeScope.Dispose(); + } + + Settings = null; + Descriptor = null; + Blueprint = null; + Shell = null; + + _disposed = true; + } + } + + ~ShellContext() { + Dispose(false); + } } } \ No newline at end of file diff --git a/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs b/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs index 9010c13fc..35423f335 100644 --- a/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs +++ b/src/Orchard/Mvc/Html/HtmlHelperExtensions.cs @@ -105,34 +105,62 @@ namespace Orchard.Mvc.Html { } private static IHtmlString UnorderedList(IEnumerable items, Func generateContent, string cssClass, Func generateItemCssClass, Func generateAlternatingItemCssClass) { - if (items == null || !items.Any()) return new HtmlString(string.Empty); + if(items == null) { + return new HtmlString(string.Empty); + } + + // prevent multiple evaluations of the enumeration + items = items.ToArray(); + + if (!items.Any()) { + return new HtmlString(string.Empty); + } var sb = new StringBuilder(250); int counter = 0, count = items.Count() - 1; - sb.AppendFormat( - !string.IsNullOrEmpty(cssClass) ? "
    " : "
      ", - cssClass - ); + if(string.IsNullOrEmpty(cssClass)) { + sb.Append("
        "); + } + else { + sb.Append("
          "); + } foreach (var item in items) { var sbClass = new StringBuilder(50); - if (counter == 0) + if (counter == 0) { sbClass.Append("first "); - if (counter == count) - sbClass.Append("last "); - if (generateItemCssClass != null) - sbClass.AppendFormat("{0} ", generateItemCssClass(item)); - if (counter % 2 != 0 && generateAlternatingItemCssClass != null) - sbClass.AppendFormat("{0} ", generateAlternatingItemCssClass(item)); + } - sb.AppendFormat( - sbClass.Length > 0 - ? string.Format("
        • {{0}}
        • ", sbClass.ToString().TrimEnd()) - : "
        • {0}
        • ", - generateContent(item, counter) - ); + if (counter == count) { + sbClass.Append("last "); + } + + if (generateItemCssClass != null) { + sbClass.Append(generateItemCssClass(item)).Append(" "); + } + + if (counter % 2 != 0 && generateAlternatingItemCssClass != null) { + sbClass.Append(generateAlternatingItemCssClass(item)).Append(" "); + } + + var clss = sbClass.ToString().TrimEnd(); + + if(String.IsNullOrWhiteSpace(clss)) { + sb.Append("
        • ") + .Append(generateContent(item, counter)) + .Append("
        • "); + } + else { + sb.Append("
        • ") + .Append(generateContent(item, counter)) + .Append("
        • "); + } counter++; } diff --git a/src/Tools/MSBuild.Orchard.Tasks/ValidateExtensionProjectFiles.cs b/src/Tools/MSBuild.Orchard.Tasks/ValidateExtensionProjectFiles.cs index 07456659e..7cba3cffb 100644 --- a/src/Tools/MSBuild.Orchard.Tasks/ValidateExtensionProjectFiles.cs +++ b/src/Tools/MSBuild.Orchard.Tasks/ValidateExtensionProjectFiles.cs @@ -96,17 +96,27 @@ namespace MSBuild.Orchard.Tasks { .Elements(None); foreach (var element in elements) { - var include = (element.Attribute(Include) == null ? null : element.Attribute(Include).Value); - bool isValid = string.IsNullOrEmpty(include); + var filePath = (element.Attribute(Include) == null ? null : element.Attribute(Include).Value); + bool isValid = IsValidExcludeFile(filePath); if (!isValid) { string message = string.Format( "\"{0}\" element name for include \"{1}\" should be \"{2}\".", - element.Name.LocalName, include, Content.LocalName); + element.Name.LocalName, filePath, Content.LocalName); AddValidationError(element, message); } } } + private static bool IsValidExcludeFile(string filePath) { + var validExtensions = new[] { ".sass", ".scss", ".less", ".coffee", ".ls", ".ts", ".md", ".docx" }; + if (string.IsNullOrEmpty(filePath)) return true; + + var fileExtension = Path.GetExtension(filePath); + if (string.IsNullOrEmpty(fileExtension)) return false; + + return validExtensions.Contains(fileExtension, StringComparer.InvariantCultureIgnoreCase); + } + private void CheckCodeAnalysisRuleSet(XDocument document) { const string orchardbasiccorrectnessRuleset = "OrchardBasicCorrectness.ruleset";