Merge branch '1.9.x' into dev

Conflicts:
	lib/windowsazure/Microsoft.WindowsAzure.Storage.xml
	src/Orchard.Specs/Hosting/Orchard.Web/Web.config
	src/Orchard.Web/Modules/Orchard.Azure.MediaServices/Web.config
	src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Edit.cshtml
	src/Orchard.Web/Modules/Orchard.Taxonomies/Drivers/TaxonomyFieldDriver.cs
	src/Orchard.Web/Modules/Orchard.Taxonomies/Handlers/TermsPartHandler.cs
	src/Orchard.Web/Modules/Orchard.Taxonomies/Services/TermCountProcessor.cs
	src/Orchard.Web/Web.config
This commit is contained in:
Sebastien Ros
2015-10-15 15:07:19 -07:00
42 changed files with 360 additions and 166 deletions

View File

@@ -561,6 +561,23 @@
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
<Target Name="DeleteDebugFiles" AfterTargets="AfterBuild">
<RemoveDir Directories="Themes;Core;Modules" />
</Target>
<Target Name="CopyDebugFiles" AfterTargets="DeleteDebugFiles" Condition="'$(Configuration)' == 'Debug'">
<PropertyGroup>
<SrcFolder>..\..</SrcFolder>
</PropertyGroup>
<ItemGroup>
<Excluded Include="$(SrcFolder)\**\bin\**\*;$(SrcFolder)\**\obj\**\*;$(SrcFolder)\**\*.user;$(SrcFolder)\**\*.cs;$(SrcFolder)\**\*.csproj;$(SrcFolder)\**\.hg\**\*" />
<Src-Themes Include="$(SrcFolder)\Orchard.Web\Themes\**\*" Exclude="@(Excluded)" />
<Src-Core Include="$(SrcFolder)\Orchard.Web\Core\**\*" Exclude="@(Excluded)" />
<Src-Modules Include="$(SrcFolder)\Orchard.Web\Modules\**\*" Exclude="@(Excluded)" />
</ItemGroup>
<Copy SourceFiles="@(Src-Themes)" DestinationFolder="Themes\%(RecursiveDir)" />
<Copy SourceFiles="@(Src-Core)" DestinationFolder="Core\%(RecursiveDir)" />
<Copy SourceFiles="@(Src-Modules)" DestinationFolder="Modules\%(RecursiveDir)" />
</Target>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
@@ -580,6 +597,11 @@
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
<PropertyGroup>
<PreBuildEvent>del "$(TargetDir)\Modules"
del "$(TargetDir)\Themes"
del "$(TargetDir)\Media"</PreBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -192,7 +192,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.2.0" newVersion="5.0.2.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31BF3856AD364E35" culture="neutral"/>

View File

@@ -205,7 +205,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.2.0" newVersion="5.0.2.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31BF3856AD364E35" culture="neutral"/>

View File

@@ -199,7 +199,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-4.3.0.0" newVersion="4.3.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.2.0" newVersion="5.0.2.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31BF3856AD364E35" culture="neutral"/>

View File

@@ -73,7 +73,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
+ <bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.2.0" newVersion="5.0.2.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>

View File

@@ -355,7 +355,7 @@ namespace Orchard.Azure.Services.FileSystems {
// as opposed to the File System implementation, if nothing is done on the stream
// the file will be emptied, because Azure doesn't implement FileMode.Truncate
_blob.DeleteIfExists();
_blob = _blob.Container.GetBlockBlobReference(_blob.Uri.ToString());
_blob = _blob.Container.GetBlockBlobReference(_blob.Name);
_blob.UploadFromStream(new MemoryStream(new byte[0]));
return OpenWrite();
}

View File

@@ -72,6 +72,7 @@
<ItemGroup>
<Compile Include="ViewModels\NavigationPartViewModel.cs" />
<Content Include="Scripts\ContentPicker.js" />
<Content Include="Scripts\SelectableContentTab.js" />
<Content Include="Styles\content-picker-admin.css" />
<Content Include="Styles\ContentPicker.css" />
<Content Include="Styles\Images\move.gif" />

View File

@@ -5,6 +5,7 @@ namespace Orchard.ContentPicker {
public void BuildManifests(ResourceManifestBuilder builder) {
var manifest = builder.Add();
manifest.DefineScript("ContentPicker").SetUrl("ContentPicker.js", "ContentPicker.js").SetDependencies("jQuery");
manifest.DefineScript("SelectableContentTab").SetUrl("SelectableContentTab.js", "SelectableContentTab.js").SetDependencies("jQuery");
}
}
}

View File

@@ -80,14 +80,26 @@
addButton.click(function() {
addButton.trigger("orchard-admin-contentpicker-open", {
callback: function(data) {
var tmpl = template.replace( /\{contentItemId\}/g , data.id)
.replace( /\{edit-link\}/g , data.editLink)
.replace( /\{status-text}/g , data.published? "" : " - " + notPublishedText);
if (Array.isArray && Array.isArray(data)) {
data.forEach(function (d) {
var tmpl = template.replace(/\{contentItemId\}/g, d.id)
.replace(/\{edit-link\}/g, d.editLink)
.replace(/\{status-text}/g, d.published ? "" : " - " + notPublishedText);
var content = $(tmpl);
$(self).find('table.content-picker tbody').append(content);
});
refreshIds();
$(self).find('.content-picker-message').show();
} else {
var tmpl = template.replace(/\{contentItemId\}/g, data.id)
.replace(/\{edit-link\}/g, data.editLink)
.replace(/\{status-text}/g, data.published ? "" : " - " + notPublishedText);
var content = $(tmpl);
$(self).find('table.content-picker tbody').append(content);
refreshIds();
$(self).find('.content-picker-message').show();
}
},
baseUrl: baseUrl,
part: partName,

View File

@@ -0,0 +1,32 @@
jQuery(function ($) {
Initialize = function () {
$('.button.addSelected').on('click', function () {
var selectedItems = $('.content-picker-itemCheck:checked');
var itemsToAdd = new Array();
$.each(selectedItems, function (index, item) {
var related = $(item).siblings('.content-picker-item').children('.related');
var data = {
id: related.data("id"),
displayText: related.data("display-text"),
editLink: related.data("edit-link"),
editUrl: related.data("edit-url"),
adminUrl: related.data("admin-url"),
displayLink: related.data("display-link"),
published: related.data("published")
};
return itemsToAdd.push(data);
});
window.opener.jQuery[query("callback")](itemsToAdd);
window.close();
});
$('.content-picker-SelectAll').on('click', function () {
$('.content-picker-itemCheck').prop('checked', $(this).prop("checked"));
});
};
$(document).ready(function () {
return Initialize();
});
});

View File

@@ -20,3 +20,36 @@ body {
background: white;
border: 1px solid #E4E5E6;
}
.content-picker-item
{
margin-left: 20px;
}
.content-picker-itemCheck
{
position:absolute;
}
label[for="selectAll"]:before
{
content:'';
display:block;
margin-top:20px;
}
label[for="selectAll"]
{
display:inline;
}
.addSelected
{
margin-bottom: 10px;
}
.page-size-options > div:first-child
{
display:inline-block;
}

View File

@@ -3,6 +3,8 @@
ContentItem contentItem = Model.ContentItem;
}
<div class="summary" itemscope="itemscope" itemid="@contentItem.Id" itemtype="http://orchardproject.net/data/ContentItem">
<input type="checkbox" class="content-picker-itemCheck" />
<div class="content-picker-item">
<div class="properties">
@*<input type="checkbox" value="@contentItem.Id" name="itemIds"/>*@
<h3>@Html.ItemDisplayText(contentItem)</h3> - <div class="contentType">@contentItem.TypeDefinition.DisplayName</div>
@@ -26,4 +28,5 @@
@if (Model.Content != null) {
<div class="primary">@Display(Model.Content)</div>
}
</div>
</div>

View File

@@ -1,5 +1,7 @@
@using Orchard.Core.Contents.ViewModels;
@{
Script.Require("SelectableContentTab");
var typeDisplayName = Model.TypeDisplayName;
var pageTitle = T("Recent Content");
@@ -8,6 +10,7 @@
}
Layout.Title = pageTitle;
}
<div class="manage">
@@ -33,6 +36,7 @@
<fieldset class="contentItems bulk-items">
@Display(Model.ContentItems)
</fieldset>
@Html.Link(T("Add Selected").Text, "#", new { @class = "button addSelected"})
@Display(Model.Pager)
}

View File

@@ -8,19 +8,28 @@ using Orchard.CustomForms.Models;
using Orchard.CustomForms.ViewModels;
using Orchard.ContentManagement.Handlers;
using System;
using Orchard.Core.Contents.Settings;
using Orchard.Security;
using Orchard.Localization;
namespace Orchard.CustomForms.Drivers {
public class CustomFormPartDriver : ContentPartDriver<CustomFormPart> {
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly IOrchardServices _orchardServices;
private readonly IAuthorizationService _authService;
public CustomFormPartDriver(
IContentDefinitionManager contentDefinitionManager,
IOrchardServices orchardServices) {
IOrchardServices orchardServices,
IAuthorizationService authService) {
_contentDefinitionManager = contentDefinitionManager;
_orchardServices = orchardServices;
_authService = authService;
T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
protected override DriverResult Display(CustomFormPart part, string displayType, dynamic shapeHelper) {
// this method is used by the widget to render the form when it is displayed
@@ -55,6 +64,19 @@ namespace Orchard.CustomForms.Drivers {
};
updater.TryUpdateModel(viewModel, Prefix, null, null);
// Warn if the custom form is set to save a content item that is viewable by anonymous users (publicly accessible)
if (viewModel.CustomFormPart.SaveContentItem) {
// If it's draftable then don't display the warning because the generated content items won't be publicly accessible
var typeDefinition = _contentDefinitionManager.ListTypeDefinitions().Where(x => String.Equals(x.Name, viewModel.CustomFormPart.ContentType, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
if (typeDefinition != null && !typeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable) {
// Create a dummy content item of the specified type to check permissions against
if (_authService.TryCheckAccess(Orchard.Core.Contents.Permissions.ViewContent, null, _orchardServices.ContentManager.New(viewModel.CustomFormPart.ContentType))) {
_orchardServices.Notifier.Add(UI.Notify.NotifyType.Warning, T("Your custom form will save data to content items that are publicly accessible."));
}
}
}
return Editor(part, shapeHelper);
}

View File

@@ -14,7 +14,7 @@
// Model is a Shape, calling Display() so that it is rendered using the most specific template for its Shape type
@Display(editor)
@Html.Hidden("returnUrl", Request.RawUrl);
@Html.Hidden("returnUrl", Request.RawUrl, new { id = string.Empty });
<fieldset class="submit-button">
<button type="submit" name="submit.Save" value="submit.Save">@Model.ContentPart.SubmitButtonText</button>

View File

@@ -1,5 +1,4 @@
using Orchard.DynamicForms.Services.Models;
using Orchard.Localization;
using Orchard.Tokens;
namespace Orchard.DynamicForms.Services {

View File

@@ -22,7 +22,7 @@ namespace Orchard.DynamicForms.ValidationRules {
}
private LocalizedString GetValidationMessage(ValidationContext context) {
return T(Tokenize(ErrorMessage.WithDefault(String.Format("{0} must match the value of {1}.", context.FieldName, TargetName)), context));
return new LocalizedString(Tokenize(T(ErrorMessage.WithDefault(String.Format("{0} must match the value of {1}.", context.FieldName, TargetName))).ToString(), context));
}
}
}

View File

@@ -28,7 +28,7 @@ namespace Orchard.DynamicForms.ValidationRules {
}
private LocalizedString GetValidationMessage(ValidationContext context) {
return T(Tokenize(ErrorMessage.WithDefault(String.Format("{0} is not a valid email address.", context.FieldName)), context));
return new LocalizedString(Tokenize(T(ErrorMessage.WithDefault(String.Format("{0} is not a valid email address.", context.FieldName))).ToString(), context));
}
}
}

View File

@@ -18,7 +18,7 @@ namespace Orchard.DynamicForms.ValidationRules {
}
private LocalizedString GetValidationMessage(ValidationContext context) {
return T(Tokenize(ErrorMessage.WithDefault(String.Format("{0} is a mandatory field.", context.FieldName)), context));
return new LocalizedString(Tokenize(T(ErrorMessage.WithDefault(String.Format("{0} is a mandatory field.", context.FieldName))).ToString(), context));
}
}
}

View File

@@ -18,7 +18,7 @@ namespace Orchard.DynamicForms.ValidationRules {
}
private LocalizedString GetValidationMessage(ValidationContext context) {
return T(Tokenize(ErrorMessage.WithDefault(String.Format("An option is required for {0}.", context.FieldName)), context));
return new LocalizedString(Tokenize(T(ErrorMessage.WithDefault(String.Format("An option is required for {0}.", context.FieldName))).ToString(), context));
}
}
}

View File

@@ -27,7 +27,7 @@ namespace Orchard.DynamicForms.ValidationRules {
}
private LocalizedString GetValidationMessage(ValidationContext context) {
return T(Tokenize(ErrorMessage.WithDefault(String.Format("{0} must match the following pattern: {1}.", context.FieldName, Pattern)), context));
return new LocalizedString(Tokenize(T(ErrorMessage.WithDefault(String.Format("{0} must match the following pattern: {1}.", context.FieldName, Pattern))).ToString(), context));
}
}
}

View File

@@ -18,7 +18,7 @@ namespace Orchard.DynamicForms.ValidationRules {
}
private LocalizedString GetValidationMessage(ValidationContext context) {
return T(Tokenize(ErrorMessage.WithDefault(String.Format("{0} is a required field.", context.FieldName)), context));
return new LocalizedString(Tokenize(T(ErrorMessage.WithDefault(String.Format("{0} is a required field.", context.FieldName))).ToString(), context));
}
}
}

View File

@@ -41,7 +41,7 @@ namespace Orchard.DynamicForms.ValidationRules {
private LocalizedString GetValidationMessage(ValidationContext context) {
if (!String.IsNullOrWhiteSpace(ErrorMessage))
return T(Tokenize(String.Format(ErrorMessage, context.FieldName, Minimum, Maximum), context));
return new LocalizedString(Tokenize(String.Format(T(ErrorMessage).ToString(), context.FieldName, Minimum, Maximum), context));
if(Minimum != null && Maximum != null)
return T("{0} must be between {1} and {2} characters long.", context.FieldName, Minimum, Maximum);

View File

@@ -235,7 +235,10 @@ namespace Orchard.Indexing.Services {
// skip items from types which are not indexed
var settings = GetTypeIndexingSettings(item.ContentItem);
if (settings.List.Contains(indexName)) {
documentIndex = ExtractDocumentIndex(item.ContentItem);
if (item.ContentItem.HasPublished()) {
var published = _contentManager.Get(item.Id, VersionOptions.Published);
documentIndex = ExtractDocumentIndex(published);
}
}
else if (settings.List.Contains(indexName + ":latest")) {
var latest = _contentManager.Get(item.Id, VersionOptions.Latest);

View File

@@ -380,7 +380,11 @@ namespace Orchard.Lists.Controllers {
[HttpPost, ActionName("List")]
[FormValueRequired("listViewName")]
public ActionResult ChangeListView(int containerId, string listViewName, PagerParameters pagerParameters) {
var container = _containerService.Get(containerId);
var container = _containerService.Get(containerId, VersionOptions.Latest);
if (container == null || !container.Has<ContainerPart>()) {
return HttpNotFound();
}
container.Record.AdminListViewName = listViewName;
return RedirectToAction("List", new { containerId, page = pagerParameters.Page, pageSize = pagerParameters.PageSize });
}

View File

@@ -156,10 +156,10 @@ namespace Orchard.MediaLibrary.Controllers {
if (!Services.Authorizer.Authorize(Permissions.ManageOwnMedia, T("Cannot view media")))
return new HttpUnauthorizedResult();
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder().MediaPath;
var mediaParts = _mediaLibraryService.GetMediaContentItems(rootMediaFolder, skip, count, order, mediaType);
var mediaPartsCount = _mediaLibraryService.GetMediaContentItemsCount(rootMediaFolder, mediaType);
var rootMediaFolder = _mediaLibraryService.GetRootMediaFolder();
var rootMediaFolderPath = rootMediaFolder == null ? null : rootMediaFolder.MediaPath;
var mediaParts = _mediaLibraryService.GetMediaContentItems(rootMediaFolderPath, skip, count, order, mediaType);
var mediaPartsCount = _mediaLibraryService.GetMediaContentItemsCount(rootMediaFolderPath, mediaType);
var mediaItems = mediaParts.Select(x => new MediaManagerMediaItemViewModel {

View File

@@ -31,7 +31,7 @@ namespace Orchard.MediaLibrary {
},
new PermissionStereotype {
Name = "Author",
Permissions = new[] {ManageMediaContent}
Permissions = new[] {ManageOwnMedia}
},
new PermissionStereotype {
Name = "Contributor",

View File

@@ -71,8 +71,11 @@ $(function () {
var listWidth = $('#media-library-main-list').width();
var listHeight = $('#media-library-main-list').height();
var itemWidth = $('.thumbnail').first().width();
var itemHeight = $('.thumbnail').first().height();
var itemWidth = $('#media-library-main-list li').first().width();
var itemHeight = $('#media-library-main-list li').first().height();
var defaultDimension = $(window).width() < 1420 ? 120 : 200;
if (itemHeight == 0 || itemHeight == null) itemHeight = defaultDimension;
if (itemWidth == 0 || itemWidth == null) itemWidth = defaultDimension;
var draftText = $("#media-library").data("draft-text");
var itemsPerRow = Math.floor(listWidth / itemWidth);

View File

@@ -1,4 +1,4 @@
.zone-content, #content, #main, #layout-content, #layout-main {
.zone-content, #content, #main, #layout-content, #layout-main {
height: 100%;
display: block;
min-height: 400px;
@@ -155,6 +155,10 @@
background-position: center;
}
#media-library-main-list.pending li {
opacity:.3;
}
/* Remove the space for the editor, and the scrollbar in the import page */
#media-library-import #media-library-main-list {
overflow-y: hidden;

View File

@@ -18,7 +18,7 @@ html.dyn #main ul.features button { display:none; }
height:6em;
margin:0 .5% 1% .5%;
position:relative;
width:32.2%;
width:32%;
}
.features.summary-view .missingDependencies{

View File

@@ -14,5 +14,7 @@
$allCheckboxes.prop("checked", false);
}
});
$('input[name=DataProvider]:checked').click();
});
})(jQuery);

View File

@@ -4,6 +4,7 @@
Layout.Title = T("Add New Tenant").ToString();
Script.Require("jQuery").AtFoot();
Script.Require("ShapesBase").AtFoot();
Script.Include(Url.Content("~/Themes/TheAdmin/Scripts/admin.js")).AtFoot();
Script.Include("multi-tenancy.admin.js").AtFoot();
}
@@ -48,7 +49,7 @@
<span class="hint">@T("Example:") @T("Data Source=sqlServerName;Initial Catalog=dbName;User ID=userName;Password=password")</span>
</span>
<span data-controllerid="sqlDatabaseOption">
<label for="DatabaseTablePrefix">@T("Database Table Prefix")</label>
<label for="DatabaseTablePrefix">@T("Database table prefix")</label>
@Html.EditorFor(svm => svm.DatabaseTablePrefix)
</span>
</div>
@@ -111,4 +112,4 @@
<fieldset>
<button class="primaryAction" type="submit">@T("Save")</button>
</fieldset>
}
}

View File

@@ -5,6 +5,7 @@
Layout.Title = T("Edit Tenant").ToString();
Script.Require("jQuery").AtFoot();
Script.Require("ShapesBase").AtFoot();
Script.Include(Url.Content("~/Themes/TheAdmin/Scripts/admin.js")).AtFoot();
Script.Include("multi-tenancy.admin.js").AtFoot();
}
@@ -44,16 +45,18 @@
<div>
@Html.RadioButtonFor(svm => svm.DataProvider, "SqlServer", new { id = "sqlDatabaseOption" })
<label for="sqlDatabaseOption" class="forcheckbox">@T("Use an existing SQL Server (or SQL Express) database")</label>
<div class="options">
<span data-controllerid="sqlDatabaseOption">
<label for="DatabaseConnectionString">@T("Connection string")</label>
@Html.TextBoxFor(svm => svm.DatabaseConnectionString, new { @class = "text large" })
<span class="hint">@T("Example:")<br />@T("Data Source=sqlServerName;Initial Catalog=dbName;User ID=userName;Password=password")</span>
@Html.TextBoxFor(svm => svm.DatabaseConnectionString)
<span class="hint">@T("Example:") @T("Data Source=sqlServerName;Initial Catalog=dbName;Persist Security Info=True;User ID=userName;Password=password")</span>
</span>
<span data-controllerid="sqlDatabaseOption">
<label for="DatabaseTablePrefix">@T("Database Table Prefix")</label>
<label for="DatabaseTablePrefix">@T("Database table prefix")</label>
@Html.EditorFor(svm => svm.DatabaseTablePrefix)
</span>
</div>
</div>
</fieldset>
<fieldset class="available-extensions">

View File

@@ -69,10 +69,6 @@ namespace Orchard.Search {
public int Create() {
_indexManager.GetSearchIndexProvider().CreateIndex("Admin");
ContentDefinitionManager.AlterTypeDefinition("Page", cfg => cfg.WithSetting("TypeIndexing.Indexes", "Page:latest"));
return 1;
}
}

View File

@@ -1,4 +1,6 @@
@{
Script.Require("SelectableContentTab");
var pageTitle = T("Search Content");
Layout.Title = pageTitle;
}
@@ -11,10 +13,18 @@
<button type="submit">@T("Search")</button>
</fieldset>
if (Model.ContentItems.Items.Count > 0)
{
<label for="selectAll">@T("Select All")</label>
<input name="selectAll" type="checkbox" class="content-picker-SelectAll" />
}
<fieldset class="contentItems bulk-items">
@Display(Model.ContentItems)
</fieldset>
if (Model.ContentItems.Items.Count > 0)
{
@Html.Link(T("Add Selected").Text, "#", new { @class = "button addSelected"})
}
@Display(Model.Pager)
}

View File

@@ -56,30 +56,8 @@ namespace Orchard.Taxonomies.Drivers {
}
protected override DriverResult Editor(ContentPart part, TaxonomyField field, dynamic shapeHelper) {
return ContentShape("Fields_TaxonomyField_Edit", GetDifferentiator(field, part), () => {
var settings = field.PartFieldDefinition.Settings.GetModel<TaxonomyFieldSettings>();
var appliedTerms = GetAppliedTerms(part, field, VersionOptions.Latest).ToDictionary(t => t.Id, t => t);
var taxonomy = _taxonomyService.GetTaxonomyByName(settings.Taxonomy);
var terms = taxonomy != null && !settings.Autocomplete
? _taxonomyService.GetTerms(taxonomy.Id).Where(t => !string.IsNullOrWhiteSpace(t.Name)).Select(t => t.CreateTermEntry()).ToList()
: new List<TermEntry>(0);
return BuildEditorShape(part, field, shapeHelper);
terms.ForEach(t => t.IsChecked = appliedTerms.ContainsKey(t.Id));
var viewModel = new TaxonomyFieldViewModel {
DisplayName = field.DisplayName,
Name = field.Name,
Terms = terms,
SelectedTerms = appliedTerms.Select(t => t.Value),
Settings = settings,
SingleTermId = appliedTerms.Select(t => t.Key).FirstOrDefault(),
TaxonomyId = taxonomy != null ? taxonomy.Id : 0,
HasTerms = taxonomy != null && _taxonomyService.GetTermsCount(taxonomy.Id) > 0
};
var templateName = settings.Autocomplete ? "Fields/TaxonomyField.Autocomplete" : "Fields/TaxonomyField";
return shapeHelper.EditorTemplate(TemplateName: templateName, Model: viewModel, Prefix: GetPrefix(field, part));
});
}
protected override DriverResult Editor(ContentPart part, TaxonomyField field, IUpdateModel updater, dynamic shapeHelper) {
@@ -103,7 +81,40 @@ namespace Orchard.Taxonomies.Drivers {
_taxonomyService.UpdateTerms(part.ContentItem, checkedTerms, field.Name);
}
return Editor(part, field, shapeHelper);
return BuildEditorShape(part, field, shapeHelper, viewModel);
}
private ContentShapeResult BuildEditorShape(ContentPart part, TaxonomyField field, dynamic shapeHelper, TaxonomyFieldViewModel appliedViewModel = null) {
return ContentShape("Fields_TaxonomyField_Edit", GetDifferentiator(field, part), () => {
var settings = field.PartFieldDefinition.Settings.GetModel<TaxonomyFieldSettings>();
var taxonomy = _taxonomyService.GetTaxonomyByName(settings.Taxonomy);
var terms = taxonomy != null && !settings.Autocomplete
? _taxonomyService.GetTerms(taxonomy.Id).Where(t => !string.IsNullOrWhiteSpace(t.Name)).Select(t => t.CreateTermEntry()).ToList()
: new List<TermEntry>(0);
// Ensure the modified taxonomy items are not lost if a model validation error occurs
if (appliedViewModel != null) {
terms.ForEach(t => t.IsChecked = appliedViewModel.Terms.Any(at => at.Id == t.Id && at.IsChecked) || t.Id == appliedViewModel.SingleTermId);
}
else {
var appliedTerms = GetAppliedTerms(part, field, VersionOptions.Latest).ToDictionary(t => t.Id, t => t);
terms.ForEach(t => t.IsChecked = appliedTerms.ContainsKey(t.Id));
}
var viewModel = new TaxonomyFieldViewModel {
DisplayName = field.DisplayName,
Name = field.Name,
Terms = terms,
SelectedTerms = appliedTerms.Select(t => t.Value),
Settings = settings,
SingleTermId = appliedTerms.Select(t => t.Key).FirstOrDefault(),
TaxonomyId = taxonomy != null ? taxonomy.Id : 0,
HasTerms = taxonomy != null && _taxonomyService.GetTermsCount(taxonomy.Id) > 0
};
var templateName = settings.Autocomplete ? "Fields/TaxonomyField.Autocomplete" : "Fields/TaxonomyField";
return shapeHelper.EditorTemplate(TemplateName: templateName, Model: viewModel, Prefix: GetPrefix(field, part));
});
}
protected override void Exporting(ContentPart part, TaxonomyField field, ExportContentContext context) {

View File

@@ -18,6 +18,8 @@ namespace Orchard.Taxonomies.Handlers {
private readonly IContentDefinitionManager _contentDefinitionManager;
private readonly IContentManager _contentManager;
private readonly HashSet<int> _processedTermParts = new HashSet<int>();
public TermsPartHandler(
IContentDefinitionManager contentDefinitionManager,
IRepository<TermsPartRecord> repository,
@@ -88,12 +90,17 @@ namespace Orchard.Taxonomies.Handlers {
});
}
// Fires off a processing engine task to run the count processing after the request so it's non-blocking.
private void RecalculateCount(IProcessingEngine processingEngine, ShellSettings shellSettings, IShellDescriptorManager shellDescriptorManager, TermsPart part) {
var termPartRecordIds = part.Terms.Select(t => t.TermRecord.Id).ToArray();
processingEngine.AddTask(shellSettings, shellDescriptorManager.GetShellDescriptor(), "ITermCountProcessor.Process", new Dictionary<string, object> { { "termPartRecordIds", termPartRecordIds } });
if (termPartRecordIds.Any()) {
if (!_processedTermParts.Any()) {
processingEngine.AddTask(shellSettings, shellDescriptorManager.GetShellDescriptor(), "ITermCountProcessor.Process", new Dictionary<string, object> { { "termPartRecordIds", _processedTermParts } });
}
foreach (var termPartRecordId in termPartRecordIds) {
_processedTermParts.Add(termPartRecordId);
}
}
}
protected override void Activating(ActivatingContentContext context) {

View File

@@ -7,6 +7,6 @@ using Orchard.Events;
namespace Orchard.Taxonomies.Services {
public interface ITermCountProcessor : IEventHandler {
void Process(params int[] termPartRecordIds);
void Process(IEnumerable<int> termPartRecordIds);
}
}

View File

@@ -30,6 +30,7 @@ namespace Orchard.Taxonomies.Services {
private readonly ShellSettings _shellSettings;
private readonly IShellDescriptorManager _shellDescriptorManager;
private readonly HashSet<int> _processedTermParts = new HashSet<int>();
public TaxonomyService(
IRepository<TermContentItem> termContentItemRepository,
@@ -270,9 +271,14 @@ namespace Orchard.Taxonomies.Services {
}
var termPartRecordIds = termList.Select(t => t.Term.TermRecord.Id).ToArray();
_processingEngine.AddTask(_shellSettings, _shellDescriptorManager.GetShellDescriptor(), "ITermCountProcessor.Process", new Dictionary<string, object> { { "termPartRecordIds", termPartRecordIds } });
if (termPartRecordIds.Any()) {
if (!_processedTermParts.Any()) {
_processingEngine.AddTask(_shellSettings, _shellDescriptorManager.GetShellDescriptor(), "ITermCountProcessor.Process", new Dictionary<string, object> { { "termPartRecordIds", _processedTermParts } });
}
foreach (var termPartRecordId in termPartRecordIds) {
_processedTermParts.Add(termPartRecordId);
}
}
}
public IContentQuery<TermsPart, TermsPartRecord> GetContentItemsQuery(TermPart term, string fieldName = null) {

View File

@@ -1,3 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Orchard.ContentManagement;
using Orchard.Taxonomies.Models;
@@ -9,18 +13,29 @@ namespace Orchard.Taxonomies.Services {
_taxonomyService = taxonomyService;
}
public void Process(params int[] termPartRecordIds) {
public void Process(IEnumerable<int> termPartRecordIds)
{
var processedTermPartRecordIds = new List<int>();
foreach (var id in termPartRecordIds) {
if (!processedTermPartRecordIds.Contains(id)) {
var termPart = _taxonomyService.GetTerm(id);
while (termPart != null) {
termPart.Count = (int)_taxonomyService.GetContentItemsCount(termPart);
if (termPart != null) {
ProcessTerm(termPart, processedTermPartRecordIds);
}
}
}
}
// compute count for the hierarchy too
private void ProcessTerm(TermPart termPart, ICollection<int> processedTermPartRecordIds)
{
termPart.Count = (int)_taxonomyService.GetContentItemsCount(termPart);
processedTermPartRecordIds.Add(termPart.Id);
// Look for a parent term that has not yet been processed
if (termPart.Container != null) {
var parentTerm = termPart.Container.As<TermPart>();
termPart = parentTerm;
}
if (parentTerm != null && !processedTermPartRecordIds.Contains(parentTerm.Id)) {
ProcessTerm(parentTerm, processedTermPartRecordIds);
}
}
}

View File

@@ -189,7 +189,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.WindowsAzure.Storage" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.2.0" newVersion="5.0.2.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Data.Edm" publicKeyToken="31BF3856AD364E35" culture="neutral"/>