Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Jesse Andrews 191 posts 716 karma points c-trib
    Apr 20, 2023 @ 00:24
    Jesse Andrews
    0

    Translation Manager is blocking content publishing

    I have a custom value mapper that works fine when sending translation jobs, but for some reason prevents umbraco from publishing (was able to confirm my code was the cause, since publishing started working when I comment it out). When testing I discovered that Translation Manager is calling GetSourceValue on publish, but no errors occur in my code and no errors are reported by translation manager, so I'm at a bit of a loss as to what is going wrong. I also set the logs of Jumoo.TranslationManager to "Debug," but that didn't reveal anything useful.

    Any ideas on what the problem is? Below is my value mapper code for reference.

    using Jumoo.TranslationManager.Core.Models;
    using Jumoo.TranslationManager.Core.ValueMappers;
    using Newtonsoft.Json.Serialization;
    using Newtonsoft.Json;
    using Seed.DataTypes.FlexibleLinks.Core;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Umbraco.Core.Services;
    using Umbraco.Core.Logging;
    using Seed.Sections.Widgets.Services;
    using Newtonsoft.Json.Linq;
    using Umbraco.Core;
    using Umbraco.Core.Models;
    using Jumoo.TranslationManager.Core;
    using Jumoo.TranslationManager.Core.Extensions;
    
    namespace Seed.Components.TranslationExtensions {
        public class WidgetValueMapper : BaseValueMapper, IValueMapper {
            private readonly IDataTypeService _dataTypeService;
            private readonly IContentTypeService _contentTypeService;
            private readonly IWidgetVariantService _widgetVariantService;
            public WidgetValueMapper(IContentService contentService, IDataTypeService dataTypeService, IContentTypeService contentTypeService, ILogger logger, IWidgetVariantService widgetVariantService) : base(contentService, dataTypeService, contentTypeService, logger) {
                _dataTypeService = dataTypeService;
                _contentTypeService = contentTypeService;
                _widgetVariantService = widgetVariantService;
            }
            public string Name => "Widget";
            public override string[] Editors => new string[] { "Umbraco.Grid.widget", "Umbraco.Grid.widgetEditor" };
    
            public TranslationValue GetSourceValue(string displayName, string propertyTypeAlias, object value, CultureInfoView culture) {
                return GetSourceValue(displayName, propertyTypeAlias, string.Empty, value, culture);
            }
            public TranslationValue GetSourceValue(string displayName, string propertyTypeAlias, string path, object value, CultureInfoView culture) {
                try {
                    if (value == null) {
                        return null;
                    }
    
                    Attempt<string> attempt = value.TryConvertTo<string>();
                    if (!attempt.Success) {
                        return null;
                    }
    
                    string result = attempt.Result;
                    if (result.IsNullOrWhiteSpace()) {
                        return null;
                    }
    
                    JObject jObject = JsonConvert.DeserializeObject<JObject>(result);
                    if (jObject == null || jObject["properties"] == null || jObject["variantKey"] == null) {
                        return null;
                    }
                    var variant = _widgetVariantService.GetByKey(Guid.Parse(jObject["variantKey"].ToString()));
                    if (variant == null) {
                        return null;
                    }
                    var contentType = _contentTypeService.Get(variant.DocumentTypeAlias);
                    if (contentType == null) {
                        return null;
                    }
                    path.AppendPath(variant.WidgetName);
                    var translationValue = new TranslationValue(displayName, propertyTypeAlias, path);
                    var properties = (JObject)jObject["properties"];
                    var count = 0;
                    foreach (var prop in contentType.CompositionPropertyTypes) {
                        count++;
                        if (!properties.ContainsKey(prop.Alias) || variant.Properties[prop.Alias].AccessLevel != Sections.Widgets.Core.Enums.PropertyAccessLevel.Open) {
                            continue;
                        }
                        var propVal = properties[prop.Alias];
                        if (propVal != null) {
                            TranslationValue mapperSource = ValueMapperFactory.GetMapperSource($"{displayName} ({variant.WidgetName} - {variant.Name}) {prop.Name}", prop.PropertyEditorAlias, path.AppendPath(prop.Alias), propVal.ToString(), culture);
                            if (mapperSource != null) {
                                mapperSource.SortOrder = count;
                                translationValue.InnerValues.Add(count.ToString(), mapperSource);
                            }
                        }
                    }
                    return translationValue;
                }
                catch (Exception e) {
                    logger.Error<WidgetValueMapper>("Error Retrieving Data", new object[1] { e });
                    return null;
                }
            }
    
    
            public object GetTargetValue(string propertyTypeAlias, object sourceValue, TranslationValue value, CultureInfoView sourceCulture, CultureInfoView targetCulture) {
                if (sourceValue == null) {
                    return sourceValue;
                }
    
                Attempt<string> attempt = sourceValue.TryConvertTo<string>();
                if (!attempt.Success) {
                    return sourceValue;
                }
    
                string result = attempt.Result;
                if (result.IsNullOrWhiteSpace()) {
                    return sourceValue;
                }
    
                JObject jObject = JsonConvert.DeserializeObject<JObject>(result);
                if (jObject == null || jObject["properties"] == null || jObject["variantKey"] == null) {
                    return sourceValue;
                }
                var variant = _widgetVariantService.GetByKey(Guid.Parse(jObject["variantKey"].ToString()));
                if (variant == null) {
                    return sourceValue;
                }
                var contentType = _contentTypeService.Get(variant.DocumentTypeAlias);
                if (contentType == null) {
                    return sourceValue;
                }
                var properties = jObject["properties"];
                var count = 0;
                foreach (var prop in contentType.CompositionPropertyTypes) {
                    count++;
                    if (variant.Properties[prop.Alias].AccessLevel != Sections.Widgets.Core.Enums.PropertyAccessLevel.Open) {
                        continue;
                    }
                    var propValue = value.GetInnerValue(count);
                    var dataType = _dataTypeService.GetDataType(prop.DataTypeId);
                    if (dataType != null) {
                        string editorAlias = dataType.EditorAlias;
                        IValueMapper mapper = ValueMapperFactory.GetMapper(editorAlias);
                        var val = properties[prop.Alias]?.ToString();
                        if (mapper == null || string.IsNullOrWhiteSpace(val)) {
                            continue;
                        }
                        var text = (string)mapper.GetTargetValue(editorAlias, val, propValue, sourceCulture, targetCulture);
                        if (text == null) {
                            continue;
                        }
                        if (IsJson(text)) {
                            try {
                                jObject[prop.Alias] = text.GetJsonTokenValue().ExpandAllJsonInToken();
                            }
                            catch (Exception e) {
                                logger.Error<WidgetValueMapper>("Error Parsing JSON", new object[1] { e });
                                jObject[prop.Alias] = text;
                            }
                        }
                        else {
                            jObject[prop.Alias] = text;
                        }
                    }
                }
                return JsonConvert.SerializeObject(jObject, Formatting.Indented);
            }
            private bool IsJson(string val) {
                val = val.Trim();
                if (!val.StartsWith("{") || !val.EndsWith("}")) {
                    if (val.StartsWith("[")) {
                        return val.EndsWith("]");
                    }
    
                    return false;
                }
    
                return true;
            }
        }
    }
    

    The data being translated is very similar to what's in the DocTypeGridEditorMapper, just with a custom "AccessLevel" property on the properties. The service calls are retrieving some cached data about the control, as that information isn't stored in the content like it is in DocTypeGridEditorMapper.

    Versions

    • Umbraco: 8.18.6
    • Translation Manager: 8.7.11
  • Jesse Andrews 191 posts 716 karma points c-trib
    Apr 20, 2023 @ 16:22
    Jesse Andrews
    0

    Forgot to mention that it seems to publish correctly, as no errors are thrown by the PostSave api call, but when I check the info tab, the update isn't recorded there and when I do a hard refresh, the changes I made disappear.

  • Kevin Jump 2311 posts 14697 karma points MVP 7x c-trib
    Apr 20, 2023 @ 16:48
    Kevin Jump
    0

    Hi,

    Super odd it doesn't log anything :( just like your code the translation manager code is wrapped in a try catch to log anything that might happen on save too.

    The actual event is being called if you have "Translate on Save" checked in the global settings

    (see https://docs.jumoo.co.uk/tm/8.x/reference/global)

    This is only needed if you are using the pending feature, if you are not you can turn this off.

    but getting to the bottom of why it might be failing. maybe remove the try catch, see if the Translation manager on then picks up the issue ?

  • Jesse Andrews 191 posts 716 karma points c-trib
    Apr 28, 2023 @ 21:31
    Jesse Andrews
    0

    Dug into this a bit more and it appears to be an issue caused by a nested scope. It's making a call to a service that retrieves some data from the database. Below is the code for the service method being called.

    public BasicWidgetVariant GetByKey(Guid key) {
        using (var scope = _scopeProvider.CreateScope()) {
            var query = scope.Database.SingleOrDefault<WidgetVariantWithWidgetName>($@"
                    select v.*, w.documentTypeAlias, w.name as 'WidgetName'
                    from [dbo].[SeedWidgetVariants] v
                    left join [dbo].[SeedWidgets] w
                    on v.WidgetId = w.Id
                    where v.UniqueId = '{key.ToString().ToUpper()}'
                ");
            var result = new BasicWidgetVariant {
                Id = query.Id,
                Name = query.Name,
                Key = query.UniqueId,
                DocumentTypeAlias = query.DocumentTypeAlias,
                WidgetName = query.WidgetName,
            };
            if (!string.IsNullOrEmpty(query.Properties)) {
                result.Properties = JsonConvert.DeserializeObject<Dictionary<string, WidgetVariantProperty>>(query.Properties);
            }
            return result;
        }
    }
    

    This executes without error, but umbraco fails to publish properly. I then stripped out the using clause and passed back a hard coded object and publishing worked as expected. Not sure whether this is an umbraco problem or an issue with translation manager.

    For now I'll turn off "Translate on Save," as I don't think that will cause problems for my use case.

Please Sign in or register to post replies

Write your reply to:

Draft