Minor fixes for localizations (#8726)

* wip fixing Fields/TaxonomyFieldList.cshtml

* restored selector for expando label/handle

* cleanup

* fixed comparison to find parent blog

# Conflicts:
#	src/Orchard.Web/Modules/Orchard.Blogs/BlogsLocalizationExtensions/Handlers/BlogPostPartHandler.cs

* fixed initialization of localizedtaxonomyfields

* Checks culture equality based on strings instead of culture records

* If term is culture neutral it is added to the applied terms list without looking for its localization.

# Conflicts:
#	src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/LocalizedTaxonomyController.cs

* Added AdminLocalizedTaxonomyController to properly managed localized taxonomies back office calls.

# Conflicts:
#	src/Orchard.Web/Modules/Orchard.Taxonomies/Controllers/LocalizedTaxonomyController.cs
#	src/Orchard.Web/Modules/Orchard.Taxonomies/Views/EditorTemplates/Fields/TaxonomyFieldList.cshtml

* Added selectedValues parameter to controller call

---------

Co-authored-by: HermesSbicego-Laser <hermes.sbicego@laser-group.com>
Co-authored-by: Andrea Piovanelli <andrea.piovanelli@laser-group.com>
This commit is contained in:
Matteo Piovanelli 2024-04-02 08:56:47 +02:00 committed by GitHub
parent 4a05e77775
commit 151bb12ce6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 212 additions and 98 deletions

View File

@ -64,7 +64,8 @@ namespace Orchard.Blogs.BlogsLocalizationExtensions.Handlers {
var blogids = new HashSet<int> { blog.As<BlogPart>().ContentItem.Id };
//seek for same culture blog
var realBlog = _localizationService.GetLocalizations(blog).SingleOrDefault(w => w.As<LocalizationPart>().Culture == blogPostCulture);
var realBlog = _localizationService.GetLocalizations(blog)
.SingleOrDefault(w => w.Culture?.Culture == blogPostCulture.Culture);
if (realBlog.Has<LocalizationPart>() && realBlog.As<LocalizationPart>().Culture.Id == blogPostCulture.Id) {
blogPost.As<ICommonPart>().Container = realBlog;
if (blogPost.Has<AutoroutePart>()) {

View File

@ -0,0 +1,34 @@
using System.Web.Mvc;
using System.Web.Routing;
using Orchard.ContentManagement.MetaData;
using Orchard.Environment.Extensions;
using Orchard.Localization.Services;
using Orchard.Taxonomies.Services;
using Orchard.UI.Admin;
namespace Orchard.Taxonomies.Controllers {
[OrchardFeature("Orchard.Taxonomies.LocalizationExtensions")]
public class AdminLocalizedTaxonomyController : LocalizedTaxonomyController {
private readonly RequestContext _requestContext;
public AdminLocalizedTaxonomyController(IContentDefinitionManager contentDefinitionManager,
ILocalizationService localizationService,
ITaxonomyService taxonomyService,
ITaxonomyExtensionsService
taxonomyExtensionsService,
RequestContext requestContext) : base(contentDefinitionManager,
localizationService,
taxonomyService,
taxonomyExtensionsService) {
_requestContext = requestContext;
}
[OutputCache(NoStore = true, Duration = 0)]
public new ActionResult GetTaxonomy(string contentTypeName, string taxonomyFieldName, int contentId, string culture, string selectedValues) {
AdminFilter.Apply(_requestContext);
return GetTaxonomyInternal(contentTypeName, taxonomyFieldName, contentId, culture, selectedValues);
}
}
}

View File

@ -1,13 +1,12 @@
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Web.UI.WebControls;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.Environment.Extensions;
using Orchard.Localization.Models;
using Orchard.Localization.Services;
using Orchard.Taxonomies.Drivers;
using Orchard.Taxonomies.Fields;
using Orchard.Taxonomies.Helpers;
using Orchard.Taxonomies.Models;
using Orchard.Taxonomies.Services;
@ -24,10 +23,11 @@ namespace Orchard.Taxonomies.Controllers {
private readonly ITaxonomyExtensionsService _taxonomyExtensionsService;
public LocalizedTaxonomyController(
IContentDefinitionManager contentDefinitionManager,
ILocalizationService localizationService,
ITaxonomyService taxonomyService,
ITaxonomyExtensionsService taxonomyExtensionsService) {
IContentDefinitionManager contentDefinitionManager,
ILocalizationService localizationService,
ITaxonomyService taxonomyService,
ITaxonomyExtensionsService taxonomyExtensionsService) {
_taxonomyService = taxonomyService;
_taxonomyExtensionsService = taxonomyExtensionsService;
_contentDefinitionManager = contentDefinitionManager;
@ -35,14 +35,19 @@ namespace Orchard.Taxonomies.Controllers {
}
[OutputCache(NoStore = true, Duration = 0)]
public ActionResult GetTaxonomy(string contentTypeName, string taxonomyFieldName, int contentId, string culture) {
public ActionResult GetTaxonomy(string contentTypeName, string taxonomyFieldName, int contentId, string culture, string selectedValues) {
return GetTaxonomyInternal(contentTypeName, taxonomyFieldName, contentId, culture, selectedValues);
}
protected ActionResult GetTaxonomyInternal (string contentTypeName, string taxonomyFieldName, int contentId, string culture, string selectedValues) {
var viewModel = new TaxonomyFieldViewModel();
bool autocomplete = false;
var contentDefinition = _contentDefinitionManager.GetTypeDefinition(contentTypeName);
if (contentDefinition != null) {
var taxonomyField = contentDefinition.Parts.SelectMany(p => p.PartDefinition.Fields).Where(x => x.FieldDefinition.Name == "TaxonomyField" && x.Name == taxonomyFieldName).FirstOrDefault();
var contentTypePartDefinition = contentDefinition.Parts.Where(x => x.PartDefinition.Fields.Any(a => a.FieldDefinition.Name == "TaxonomyField" && a.Name == taxonomyFieldName)).FirstOrDefault();
ViewData.TemplateInfo.HtmlFieldPrefix = contentTypePartDefinition.PartDefinition.Name + "." + taxonomyField.Name;
var fieldPrefix = contentTypePartDefinition.PartDefinition.Name + "." + taxonomyField.Name;
ViewData.TemplateInfo.HtmlFieldPrefix = fieldPrefix;
if (taxonomyField != null) {
var taxonomySettings = taxonomyField.Settings.GetModel<TaxonomyFieldSettings>();
// Getting the translated taxonomy and its terms
@ -60,7 +65,33 @@ namespace Orchard.Taxonomies.Controllers {
List<TermPart> appliedTerms = new List<TermPart>();
int firstTermIdForCulture = 0;
if (contentId > 0) {
appliedTerms = _taxonomyService.GetTermsForContentItem(contentId, taxonomyFieldName, VersionOptions.Published).Distinct(new TermPartComparer()).ToList();
var selectedIds = selectedValues.Split(',');
var destinationTaxonomyCulture = taxonomy.As<LocalizationPart>()?.Culture?.Culture;
foreach (var id in selectedIds) {
if (!string.IsNullOrWhiteSpace(id)) {
var intId = 0;
int.TryParse(id, out intId);
var originalTerm = _taxonomyService.GetTerm(intId);
// The original term has to be added to applied terms in the following scenarios:
// When the original term has no LocalizationPart, which means that, when creating the taxonomy, terms have been set to be culture neutral.
// When the culture of the original term matches the culture of the taxonomy.
// In any other scenario, get the localized term and add it to the applied terms list.
// If no localization is found, nothing is added to the list for the current id.
var otCulture = originalTerm.As<LocalizationPart>()?.Culture?.Culture;
if (!originalTerm.Has<LocalizationPart>() || string.Equals(destinationTaxonomyCulture, otCulture)) {
appliedTerms.Add(originalTerm);
} else {
// Get the localized term. If no localized term is found, no term should be added to applied terms list.
var t = _localizationService.GetLocalizedContentItem(originalTerm, culture);
if (t != null) {
// Localized term has been found
appliedTerms.Add(t.As<TermPart>());
}
}
}
}
// It takes the first term localized with the culture in order to set correctly the TaxonomyFieldViewModel.SingleTermId
var firstTermForCulture = appliedTerms.FirstOrDefault(x => x.As<LocalizationPart>() != null && x.As<LocalizationPart>().Culture != null && x.As<LocalizationPart>().Culture.Culture == culture);
@ -86,16 +117,13 @@ namespace Orchard.Taxonomies.Controllers {
TaxonomyId = taxonomy != null ? taxonomy.Id : 0,
HasTerms = taxonomy != null && _taxonomyService.GetTermsCount(taxonomy.Id) > 0
};
if (taxonomySettings.Autocomplete)
if (taxonomySettings.Autocomplete) {
autocomplete = true;
}
}
}
var templateName = autocomplete ? "../EditorTemplates/Fields/TaxonomyField.Autocomplete" : "../EditorTemplates/Fields/TaxonomyField";
return View(templateName, viewModel);
}
private IEnumerable<TermPart> GetAppliedTerms(ContentPart part, TaxonomyField field = null, VersionOptions versionOptions = null) {
string fieldName = field != null ? field.Name : string.Empty;
return _taxonomyService.GetTermsForContentItem(part.ContentItem.Id, fieldName, versionOptions ?? VersionOptions.Published).Distinct(new TermPartComparer());
return PartialView(templateName, viewModel);
}
}
}

View File

@ -90,7 +90,7 @@ namespace Orchard.Taxonomies.Drivers {
if (localizedParentTerm != null && localizedParentTerm != parentTerm)
_notifier.Add(NotifyType.Information, T("The parent term has been changed to its localized version associated to the culture {0}.", localizedParentTerm.As<LocalizationPart>().Culture.Culture));
}
else if (termCulture != taxonomyCulture && taxonomyCulture != null && _localizationService.GetLocalizations(parentTaxonomy).Count() > 0) {
else if (taxonomyCulture != null && termCulture.Culture != taxonomyCulture.Culture && _localizationService.GetLocalizations(parentTaxonomy).Count() > 0) {
//I can associate to a taxonomy a term of a different culture only if the taxonomy is unlocalized or has no translations
updater.AddModelError("WrongTaxonomyLocalization", T("A localization of the taxonomy in the specified language does not exist. Please create it first."));
}

View File

@ -126,6 +126,7 @@
<ItemGroup>
<Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" />
<Compile Include="Controllers\AdminLocalizedTaxonomyController.cs" />
<Compile Include="Controllers\LocalizedTaxonomyController.cs" />
<Compile Include="Drivers\LocalizedTaxonomyFieldDriver.cs" />
<Compile Include="Drivers\LocalizedTaxonomyPartDriver.cs" />

View File

@ -1,4 +1,5 @@
@using Orchard.Utility.Extensions;
@using Orchard.UI.Admin;
@model Orchard.Taxonomies.ViewModels.LocalizedTaxonomiesViewModel
@{
@ -8,117 +9,159 @@
var partField = Model.PartName + "_" + Model.FieldName;
var functionName = "filterTaxonomyCulture_" + partField;
var isAdmin = AdminFilter.IsApplied(Request.RequestContext);
var controllerUrl = Url.Action("GetTaxonomy", "LocalizedTaxonomy", new { area = "Orchard.Taxonomies" });
if (isAdmin) {
controllerUrl = Url.Action("GetTaxonomy", "AdminLocalizedTaxonomy", new { area = "Orchard.Taxonomies" });
}
using (Script.Foot()) {
<script type="text/javascript">
//<![CDATA[
//<![CDATA[
$(document).ready(function () {
var pageurl = '@Url.Action("GetTaxonomy", "LocalizedTaxonomy", new { area = "Orchard.Taxonomies" })';
var pageurl = '@controllerUrl';
function @(functionName)(culture) {
// Read currently selected values
var checkedTerms = $("input[type!='hidden']:checked", $(".taxonomy-wrapper[data-id-prefix='@Taxonomyprefix']"));
var selectedValues = "";
$(checkedTerms).each(function () {
var sel = $("input[type=hidden]", $(this).parent()).attr("value");
if (sel) {
selectedValues = selectedValues + "," + sel;
}
});
$.ajax({
url: pageurl,
data: { contentTypeName: '@Model.ContentType', taxonomyFieldName: '@Model.FieldName', contentId: @Model.Id, culture: culture },
data: {
contentTypeName: '@Model.ContentType',
taxonomyFieldName: '@Model.FieldName',
contentId: @Model.Id,
culture: culture,
selectedValues: selectedValues,
success: function (html) {
var container = $(".taxonomy-wrapper[data-id-prefix='@Taxonomyprefix']");
// Take the terms previously checked
var checkedTerms = $("input[checked='checked']", container);
$(".taxonomy-wrapper[data-id-prefix='@Taxonomyprefix']").replaceWith(html);
$(".taxonomy-wrapper[data-id-prefix='@Taxonomyprefix'] legend").expandoControl(function (controller) { return controller.nextAll(".expando"); }, { collapse: true, remember: false });
@if (Model.Setting.Autocomplete) {
// try to init expando controls until we manage to
var interval = setInterval(InitExpando, 10);
function InitExpando() {
// the script to initialize expando controls requires them to have a rendered size on screen.
// That is not the case when they are in a different tab, because in that case they are basically
// set to display=none. By retrying here until they get on screen we get around that issue.
$(".taxonomy-wrapper[data-id-prefix='@Taxonomyprefix'] legend")
.expandoControl(function (controller) {
return controller.nextAll(".expando");
}, { collapse: true, remember: false });
var glyph = $(".taxonomy-wrapper[data-id-prefix='@Taxonomyprefix'] legend .expando-glyph-container");
if (glyph && glyph.length) {
clearInterval(interval);
}
}
@if (Model.Setting.Autocomplete) {
<text>
var createTermCheckbox = function($wrapper, tag, checked) {
var $ul = $("ul.terms", $wrapper);
var singleChoice = $(".terms-editor", $wrapper).data("singlechoice");
var namePrefix = $wrapper.data("name-prefix");
var idPrefix = $wrapper.data("id-prefix");
var nextIndex = $("li", $ul).length;
var id = isNaN(tag.value) ? -nextIndex : tag.value;
var checkboxId = idPrefix + "_Terms_" + nextIndex + "__IsChecked";
var checkboxName = namePrefix + ".Terms[" + nextIndex + "].IsChecked";
var radioName = namePrefix + ".SingleTermId";
var checkboxHtml = "<input type=\"checkbox\" value=\"" + (checked ? "true\" checked=\"checked\"" : "false") + " data-term=\"" + tag.label + "\" data-term-identity=\"" + tag.label.toLowerCase() + "\" id=\"" + checkboxId + "\" name=\"" + checkboxName + "\" />";
var radioHtml = "<input type=\"radio\" value=\"" + id + (checked ? "\" checked=\"checked\"" : "\"") + " data-term=\"" + tag.label + "\" data-term-identity=\"" + tag.label.toLowerCase() + "\" id=\"" + checkboxId + "\" name=\"" + radioName + "\" />";
var inputHtml = singleChoice ? radioHtml : checkboxHtml;
var $li = $("<li>" +
inputHtml +
"<input type=\"hidden\" value=\"" + id + "\" id=\"" + idPrefix + "_Terms_" + nextIndex + "__Id" + "\" name=\"" + namePrefix + ".Terms[" + nextIndex + "].Id" + "\" />" +
"<input type=\"hidden\" value=\"" + tag.label + "\" id=\"" + idPrefix + "_Terms_" + nextIndex + "__Name" + "\" name=\"" + namePrefix + ".Terms[" + nextIndex + "].Name" + "\" />" +
"<label class=\"forcheckbox\" for=\"" + checkboxId + "\">" + tag.label + "</label>" +
"</li>").hide();
var $ul = $("ul.terms", $wrapper);
var singleChoice = $(".terms-editor", $wrapper).data("singlechoice");
var namePrefix = $wrapper.data("name-prefix");
var idPrefix = $wrapper.data("id-prefix");
var nextIndex = $("li", $ul).length;
var id = isNaN(tag.value) ? -nextIndex : tag.value;
var checkboxId = idPrefix + "_Terms_" + nextIndex + "__IsChecked";
var checkboxName = namePrefix + ".Terms[" + nextIndex + "].IsChecked";
var radioName = namePrefix + ".SingleTermId";
var checkboxHtml = "<input type=\"checkbox\" value=\"" + (checked ? "true\" checked=\"checked\"" : "false") + " data-term=\"" + tag.label + "\" data-term-identity=\"" + tag.label.toLowerCase() + "\" id=\"" + checkboxId + "\" name=\"" + checkboxName + "\" />";
var radioHtml = "<input type=\"radio\" value=\"" + id + (checked ? "\" checked=\"checked\"" : "\"") + " data-term=\"" + tag.label + "\" data-term-identity=\"" + tag.label.toLowerCase() + "\" id=\"" + checkboxId + "\" name=\"" + radioName + "\" />";
var inputHtml = singleChoice ? radioHtml : checkboxHtml;
var $li = $("<li>" +
inputHtml +
"<input type=\"hidden\" value=\"" + id + "\" id=\"" + idPrefix + "_Terms_" + nextIndex + "__Id" + "\" name=\"" + namePrefix + ".Terms[" + nextIndex + "].Id" + "\" />" +
"<input type=\"hidden\" value=\"" + tag.label + "\" id=\"" + idPrefix + "_Terms_" + nextIndex + "__Name" + "\" name=\"" + namePrefix + ".Terms[" + nextIndex + "].Name" + "\" />" +
"<label class=\"forcheckbox\" for=\"" + checkboxId + "\">" + tag.label + "</label>" +
"</li>").hide();
if (checked && singleChoice) {
if (checked && singleChoice) {
$("input[type='radio']", $ul).removeAttr("checked");
$("input[type='radio'][name$='IsChecked']", $ul).val("false");
}
}
$ul.append($li);
$li.fadeIn();
};
};
/* Event handlers
**********************************************************************/
var onTagsChanged = function(tagLabelOrValue, action, tag) {
/* Event handlers
**********************************************************************/
var onTagsChanged = function(tagLabelOrValue, action, tag) {
if (tagLabelOrValue == null)
return;
if (tagLabelOrValue == null)
return;
var $input = this.appendTo;
var $wrapper = $input.parents("fieldset:first");
var $tagIt = $("ul.tagit", $wrapper);
var singleChoice = $(".terms-editor", $wrapper).data("singlechoice");
var $terms = $("ul.terms", $wrapper);
var initialTags = $(".terms-editor", $wrapper).data("selected-terms");
var $input = this.appendTo;
var $wrapper = $input.parents("fieldset:first");
var $tagIt = $("ul.tagit", $wrapper);
var singleChoice = $(".terms-editor", $wrapper).data("singlechoice");
var $terms = $("ul.terms", $wrapper);
var initialTags = $(".terms-editor", $wrapper).data("selected-terms");
if (singleChoice && action == "added") {
if (singleChoice && action == "added") {
$tagIt.tagit("fill", tag);
}
}
$terms.empty();
var tags = $tagIt.tagit("tags");
var tags = $tagIt.tagit("tags");
$(tags).each(function(index, tag) {
createTermCheckbox($wrapper, tag, true);
});
createTermCheckbox($wrapper, tag, true);
});
// Add any tags that are no longer selected but were initially on page load.
// These are required to be posted back so they can be removed.
var removedTags = $.grep(initialTags, function(initialTag) { return $.grep(tags, function(tag) { return tag.value === initialTag.value }).length === 0 });
// Add any tags that are no longer selected but were initially on page load.
// These are required to be posted back so they can be removed.
var removedTags = $.grep(initialTags, function(initialTag) { return $.grep(tags, function(tag) { return tag.value === initialTag.value }).length === 0 });
$(removedTags).each(function(index, tag) {
createTermCheckbox($wrapper, tag, false);
});
createTermCheckbox($wrapper, tag, false);
});
$(".no-terms", $wrapper).hide();
};
};
var renderAutocompleteItem = function(ul, item) {
var renderAutocompleteItem = function(ul, item) {
var label = item.label;
var label = item.label;
for (var i = 0; i < item.levels; i++) {
label = "<span class=\"gap\">&nbsp;</span>" + label;
}
for (var i = 0; i < item.levels; i++) {
label = "<span class=\"gap\">&nbsp;</span>" + label;
}
var li = item.disabled ? "<li class=\"disabled\"></li>" : "<li></li>";
var li = item.disabled ? "<li class=\"disabled\"></li>" : "<li></li>";
return $(li)
.data("item.autocomplete", item)
.append($("<a></a>").html(label))
.appendTo(ul);
};
return $(li)
.data("item.autocomplete", item)
.append($("<a></a>").html(label))
.appendTo(ul);
};
/* Initialization
**********************************************************************/
$(".terms-editor").each(function() {
var selectedTerms = $(this).data("selected-terms");
var selectedTerms = $(this).data("selected-terms");
var autocompleteUrl = $(this).data("autocomplete-url");
var autocompleteUrl = $(this).data("autocomplete-url");
var $tagit = $("> ul", this).tagit({
tagSource: function(request, response) {
var termsEditor = $(this.element).parents(".terms-editor");
var $tagit = $("> ul", this).tagit({
tagSource: function(request, response) {
var termsEditor = $(this.element).parents(".terms-editor");
$.getJSON(autocompleteUrl, { taxonomyId: termsEditor.data("taxonomy-id"), leavesOnly: termsEditor.data("leaves-only"), query: request.term }, function(data, status, xhr) {
response(data);
});
},
response(data);
});
},
initialTags: selectedTerms,
triggerKeys: ['enter', 'tab'], // default is ['enter', 'space', 'comma', 'tab'] but we remove comma and space to allow them in the terms
allowNewTags: $(this).data("allow-new-terms"),
@ -137,24 +180,31 @@
</text>
}
///////////////////////////
}
}).fail(function () {
alert("@T("Loading taxonomy fail, Retry")");
});
alert("@T("Loading taxonomy fail, Retry")");
});
}
$("#Localization_SelectedCulture").on('change', function (p) {
var optionSelected = $("option:selected", this);
var refreshTerms = false;
if (p != undefined) {
if (p == "@(partField)") {
refreshTerms = true;
}
}
if (refreshTerms) {
$("#Localization_SelectedCulture").on('change', function (ev, pf) {
//ev: jquery event
//pf: Orchard's field differentiator
var refreshTerms = false;
if (pf != undefined) {
// refresh of terms invoked from a field
if (pf == "@(partField)") {
// refresh of terms invoked from this field
refreshTerms = true;
}
} else {
// refresh of terms invoked by changing the content's culture
refreshTerms = true;
}
if (refreshTerms) {
@(functionName)($("#Localization_SelectedCulture").val());
}
});
}
});
$("#Localization_SelectedCulture").trigger("change", "@(partField)");
});
//]]>