For an upgrade to Web 8.5 and DXA 1.7 (and not using the default TBBs), it is not possible to edit linked Components as XPM can only edit Component Presentations, not Components. This is discussed in forum thread "Are Component Links supported for inline edit in XPM?".

The workaround employed for this is to use a dummy Component Template URI to refer to a Component Presentation, which was the second option suggested in the second answer to the forum thread. However, after adding the workaround to a CustomModelBuilder as below, images in the linked Components are not being rendered. When code is explicitly added to render the images, they do appear, but inline editing for linked Components is not possible. The component template code added to display images as below.

Additonal details:

  • Both the image and the tested page are from the same hardcoded publication id.
  • No error is thrown and the entire image tag did not render.

Component Template snippet w/code to render images

if (entityModel is Link)
{
Link link = (Link)entityModel;
if (String.IsNullOrEmpty(link.Url))
{
link.Url = SiteConfiguration.LinkResolver.ResolveLink(component.Id);
}
}
//// Linked component XPM metadata code
//if (localization.IsStaging)
//{
// entityModel.XpmMetadata = GetXpmMetadata(component);
//}
// Set the Entity Model's default View (if any) after it has been fully initialized.
entityModel.MvcData = entityModel.GetDefaultView(localization);

CustomModelBuilder

namespace Sdl.Web.Tridion.Mapping
{
  public class CustomModelBuilder : DefaultModelBuilder
  { 
    override public void BuildEntityModel(ref EntityModel entityModel, IComponent component, Type baseModelType, Localization localization)
    {
      using (new Tracer(entityModel, component, baseModelType, localization))
      {
        string[] schemaTcmUriParts = component.Schema.Id.Split('-');
        SemanticSchema semanticSchema = SemanticMapping.GetSchema(schemaTcmUriParts[1], localization);
        // The semantic mapping may resolve to a more specific model type than specified by the View Model itself (e.g. Image instead of just MediaItem for Teaser.Media)
        Type modelType = semanticSchema.GetModelTypeFromSemanticMapping(baseModelType);
        MappingData mappingData = new MappingData
        {
          SemanticSchema = semanticSchema,
          EntityNames = semanticSchema.GetEntityNames(),
          TargetEntitiesByPrefix = GetEntityDataFromType(modelType),
          Content = component.Fields,
          Meta = component.MetadataFields,
          TargetType = modelType,
          SourceEntity = component,
          Localization = localization
        };
        entityModel = (EntityModel)CreateViewModel(mappingData);
        entityModel.Id = GetDxaIdentifierFromTcmUri(component.Id);
        if (localization.IsStaging)
        {
          entityModel.XpmMetadata = GetXpmMetadata(component);
        }
        // Set the Entity Model’s default View (if any) after it has been fully initialized.
        entityModel.MvcData = entityModel.GetDefaultView(localization);
      }
    }

    internal new static string GetDxaIdentifierFromTcmUri(string tcmUri, string templateTcmUri = null)
    {
      // Return the Item (Reference) ID part of the TCM URI.
      string result = tcmUri.Split('-')[1];
      if (templateTcmUri != null)
      {
        result += "-" + templateTcmUri.Split('-')[1];
      }
      return result;
    }

    protected virtual IDictionary<string, object> GetXpmMetadata(IComponent cp)
    {
      IDictionary<string, object> result = new Dictionary<string, object>();
      result.Add("ComponentID", cp.Id);
      result.Add("ComponentModified", cp.RevisionDate.ToString("yyyy - MM - ddTHH:mm: ss"));
      result.Add("ComponentTemplateID", "tcm:10-194307-32" /*cp.ComponentTemplate.Id*/);//set to dummy template id. -- 10-194307-32   ----10-164516-32
      result.Add("ComponentTemplateModified", "2018-03-21T10: 39:58" /*cp.ComponentTemplate.RevisionDate.ToString(“yyyy-MM-ddTHH:mm:ss”)*/); //set to dummy timestamp
      result.Add("IsRepositoryPublished", false);
      return result;
    }
  }
}

Any thoughts on what changes might be necessary to both enable inline editing for linked Components and render images?

  • It will be interesting to know if there are any errors when the images are not resolved, also when exactly this default model builder is being used, and what what code is used to render the images (which makes the XPM not editable again)? Last, what does the HTML source look like when the linked Component is not editable, are the correct XPM tags added or not (a trimmed down example would be nice, only showing the relevant items and XPM tags of the CP and a field)? But maybe we should only focus on the scenario where images are resolved and the XPM is not editable anymore? – Bart Koopman Apr 23 at 5:59
  • 1
    One comment with regards to the dummy Component Template being added, when hardcoding it like in this example, it will be very tricky, since the Publication ID is hardcoded, which could lead to issues. I would suggest to either reuse the CT URI of the parent Component Template, or to ensure the Publication ID is taken from the Component URI, so it is at least dynamically set to the correct context Publication (without that XPM will fail for sure). – Bart Koopman Apr 23 at 6:01
  • Updating this post with likely solution (further testing needed), based on @BartKoopman's and Rick's feedback. Removed duplicate and unnecessary GetDxaIdentifierFromTcmUri from CustomModelBuilder. – Terry Kim Apr 25 at 18:21

It is unclear to me what your CustomModelBuilder is doing, exactly.

I guess the essence is in adding ComponentTemplateID and ComponentTemplateModified to the XpmMetadata. The easiest way to achieve that would be to override the GetXpmMetadata method. But instead of that:

  • The GetXpmMetadata method is not overridden, but reintroduced
  • The BuildEntityModel method is overridden, but it seems its implementation is copy-pasted from DefaultModelBuilder ?
  • The GetDxaIdentifierFromTcmUri method is reintroduced (2 times) ??

I would try to use a "clean" custom model builder which only overrides the GetXpmMetadata method. And, as Bart mentioned, don't use hard-coded TCM URIs.

Images are rendering and editing of linked components working when overriding GetXpmMetadata as per Rick's answer, although further testing is needed. Also should add logic to handle links and EclItem as handled by the DefaultModelBuilder. Possibly it would be better to just tweak the DefaultModelBuilder to set the XpmMetadata in method - BuildEntityModel before setting the MvcData.

        override protected IDictionary<string, object> GetXpmMetadata(IComponent cp)
    {
        IDictionary<string, object> result = new Dictionary<string, object>();
        result.Add("ComponentID", cp.Id);
        result.Add("ComponentModified", cp.RevisionDate.ToString("yyyy - MM - ddTHH:mm: ss"));
        result.Add("ComponentTemplateID", "tcm:10-194307-32" /*cp.ComponentTemplate.Id*/);//set to dummy template id. -- 10-194307-32   ----10-164516-32
        result.Add("ComponentTemplateModified", "2018-03-21T10: 39:58" /*cp.ComponentTemplate.RevisionDate.ToString(“yyyy-MM-ddTHH:mm:ss”)*/); //set to dummy timestamp
        result.Add("IsRepositoryPublished", false);
        return result;
    }
  • You should add a key IsQueryBased with value true to the XPM metadata of such fake Component Presentations. – Rick Pannekoek Apr 26 at 17:42

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.