Uploaded image for project: 'Hippo CMS'
  1. Hippo CMS
  2. CMS-13997

BaseHstDynamicComponent issues in Channel Mgr and for targeting wrt picked documents

    XMLWordPrintable

Details

    • Bug
    • Status: Closed
    • Top
    • Resolution: Fixed
    • None
    • 14.3.2, 14.4.0
    • None
    • None

    Description

      Problem description

      The BaseHstDynamicComponent sets any picked documents as model on the HstRequest such that referenced documents are available in FTLs or in the PageModelAPI, both v0.9 and v1.0. The code that does this is:

      for (DynamicParameter param: componentParametersInfo.getDynamicComponentParameters()) {
                  try {
                      DynamicParameterConfig componentParameterConfig = param.getComponentParameterConfig();
                      if (componentParameterConfig instanceof JcrPathParameterConfig) {
                          HippoBean bean = getContentBeanForPath(getComponentParameter(param.getName()), request,
                              ((JcrPathParameterConfig)componentParameterConfig).isRelative());
                          request.setModel(param.getName(), bean);
                      }
                  } catch (ObjectBeanManagerException obme) {
                      log.error("Problem fetching or converting bean", obme);
                  }
              }
      

      The above makes sure that if there is a dynamic JcrPathParameterConfig component parameter, the HstRequest gets the referenced bean if present set on the model for the name of the parameter. As a result, assume the parameter is called 'document' in the dynamic component parameters, for example the PMA v1.0 should contain something like this:

      "page" : {
        "uid0" : {
           "id" : "r27",
           "name" : "homepage",
           "type" : "component",
           "componentClass" : "org.hippoecm.hst.component.support.bean.dynamic.BaseHstDynamicComponent",
           "document" : {
              "$ref" : "/page/uid1"
           }
        }
        "uid1" : {
          "type" : "document",
          "data" : {
            "name" : "homepage",
            "displayName" : "Home Page",
            "summary" : "Summary of the homepage",
            "date" : 1249891507580,
            "title" : "This is the homepage",
            }
         }
      }
      

      In general, the above code works and results in the desired output, except in three cases:

      • use case 1: When changing the picked document in the CM, it does not update in the rendered page directly: only after saving and navigating to another page and back results in the picked document to become visible
      • use case 2: For targeting in case the targeted variants points to, say, a different banner, the targeted banner won't be put as model but just the default banner
      • use case 3: in case the picked document shouldn't be on the request or serialized at all

      The reason is very delicate: In the code above, there is:

      getComponentParameter(param.getName())
      

      This however does not take into account

      1. that via a query param you can override a specific parameter value in case
        HstRequestUtils#isComponentRenderingPreviewRequest' 
      2. that targeting can indicate that not param.getName() should used but a prefixed name for the specific variant

      The reason why this normally all 'just' works (worked) is that properties which are modifiable in the CM were always backed by an HstComponent ParametersInfo interface, and fetching the values was always done via the BaseHstComponent

          protected <T> T getComponentParametersInfo(final HstRequest request) {
              return (T) ParameterUtils.getParametersInfo(this, getComponentConfiguration(), request);
          }
      

      The above usage for example looks something like:

      ListViewInfo info = getComponentParametersInfo(request);
      String scope = info.getScope();
      

      Now the crux is that the 'ParameterUtils.getParametersInfo' not just creates a proxy instance for the ParametersInfo interface and delegates it to )getComponentParameter(param.getName())_ . The proxy invocation handler is way more sophisticated and handles also use case 1 and 2 above. You can see this in org.hippoecm.hst.core.component.HstParameterInfoProxyFactoryImpl.ParameterInfoInvocationHandler#getParameterValue for the 'query param overriding value logic' and you can see in org.hippoecm.hst.core.component.HstParameterInfoProxyFactoryImpl.ParameterInfoInvocationHandler#getPrefixedParameterName the targeting support.

      What about use case 3? Take a look at

      DocumentQueryDynamicComponentInfo 
      

      The scope part in there:

      @Parameter(name = "scope")
          @JcrPath(
                  isRelative = true,
                  pickerSelectableNodeTypes = {"hippostd:folder"}
          )
          String getScope();
      

      means that 'scope' is obviously not a residual property, but why should it ever be set as a model on the HstRequest? It is input for a search. A subclass should decide what the relevant Model objects are for non-residual properties.

      Solution details

      Use case 1: BaseHstDynamicComponent is not subclassed

      If the BaseHstDynamicComponent is not subclassed, the parameters info interface is DynamicComponentInfo resulting in that there are only residual properties. The

      Map<String, Object> getResidualParameterValues();
      

      does return the correct values for the use case 1 and 2 described above, this is handled in org.hippoecm.hst.core.component.HstParameterInfoProxyFactoryImpl.ParameterInfoInvocationHandler#invoke . Therefore, the logic

      getComponentParameter(param.getName())
      

      should be replaced by fetching the value via 'getResidualParameterValues' (these are for example also the values which are used in the PageModelAPI, not the 'getComponentParameter(param.getName())'

      Use case 2: BaseHstDynamicComponent is subclassed

      In this case, there is a subclass MyComponent of BaseHstDynamicComponent . If that subclass still has

      @ParametersInfo(type = DynamicComponentInfo.class)
      

      then it works the same as use case 1: all parameters are residual. If however the subclass also has its own parameter info interface, for example:

      @ParametersInfo(type = MyComponentInfo.class)
      

      and MyComponentInfo looks something like:

      public interface MyComponentInfo extends DynamicComponentInfo {
          @Parameter(name = "newsdoc")
          @JcrPath(
                  isRelative = true,
                  pickerSelectableNodeTypes = {"hippostd:folder"}
          )
         String getNewsDoc();
      }
      

      then the HstComponent class should decide whether or not the 'getNewsDoc()' is set as model. It is up to the java code and should not be done automatically. This thus also means that the DocumentQueryDynamicComponentInfo#getScope example from above should not be set as model automatically

      Implementation details

      The implementation is easy after all the input from above. Note

      getComponentParameter(param.getName())
      

      should be used to fetch a value, but

      componentParametersInfo.getResidualParameterValues().get(param.getName());
      

      TODO Required e2e test

      Attachments

        Activity

          People

            Unassigned Unassigned
            aschrijvers Ard Schrijvers
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: