umbraco / Umbraco-CMS

Umbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.
https://umbraco.com
MIT License
4.37k stars 2.64k forks source link

Search result in tree doesn't respect filter css class #13337

Closed bjarnef closed 1 year ago

bjarnef commented 1 year ago

Which Umbraco version are you using? (Please write the exact version, example: 10.1.0)

10.2.1

Bug summary

When using treepicker infinite editor it is possible the use filterCssClass:

For example:

function openCategoryPicker() {

      const dialog = {
          entityType: "Document",
          filterCssClass: "not-allowed not-published",
          filter: function (item) {
              if (item.metaData.contentType !== "category" || item.hasChildren)
                  return true;

              return item.nodeType === "container" || item.metaData.isElement || !!_.findWhere(vm.itemTypes, { udi: item.udi });
          },
          currentNode: editorState ? editorState.current : null,
          submit: model => {
              if (Utilities.isArray(model.selection)) {
                  Utilities.forEach(model.selection, (item, i) => {
                      vm.add(item);
                  });
              } else {
                  vm.clear();
                  vm.add(model);
              }
              setDirty();
          },
          treeAlias: "content",
          section: "content",
          idType: "udi",
          close: () => editorService.close()
      };

      editorService.contentPicker(dialog);
}

In this example tree nodes with children are not allowed to select (only the ones without children).

However when searching in search input, it is possible to select these nodes anyway.

Specifics

No response

Steps to reproduce

Use treepicker infinite editor with filterCssClass property and filter function and notice than when search it is possible to select the nodes even though then should not be allowed to select.

Expected result / actual result

No response

github-actions[bot] commented 1 year ago

Hi there @bjarnef!

Firstly, a big thank you for raising this issue. Every piece of feedback we receive helps us to make Umbraco better.

We really appreciate your patience while we wait for our team to have a look at this but we wanted to let you know that we see this and share with you the plan for what comes next.

We wish we could work with everyone directly and assess your issue immediately but we're in the fortunate position of having lots of contributions to work with and only a few humans who are able to do it. We are making progress though and in the meantime, we will keep you in the loop and let you know when we have any questions.

Thanks, from your friendly Umbraco GitHub bot :robot: :slightly_smiling_face:

Zeegaan commented 1 year ago

Hey @bjarnef, thanks for reporting! As a non-frontend person, I need some more information here before being able to test 🤣 Where should I implement this treepicker with infinite editor ? 🤔 Is it in my own package or? If i could trouble you for a test setup, that would help a lot 🙏

bjarnef commented 1 year ago

Hi @Zeegaan

It can be anywhere. In this case it was in a property editor, but could be in any dashboard or other custom view. E.g. a button to trigger the function, which open the infinite editor.

You could also just modify this contentpicker in core used for Content Picker and MNTP, e.g. filter to a specific document type where others shouldn't be selected in tree picker, but on search it is possible to select. https://github.com/umbraco/Umbraco-CMS/blob/v11/contrib/src/Umbraco.Web.UI.Client/src/views/propertyeditors/contentpicker/contentpicker.controller.js#L259-L292

Zeegaan commented 1 year ago

@bjarnef I might need a little bit more handholding here, my contentpicker.controller $scope.openCurrentPicker now looks like this:

  //dialog
  $scope.openCurrentPicker = function () {
    $scope.currentPicker = dialogOptions;

    $scope.currentPicker.submit = function (model) {
      if (Utilities.isArray(model.selection)) {
        _.each(model.selection, function (item, i) {
          $scope.add(item);
        });
        setDirty();
      }
      setDirty();
      editorService.close();
    }

    $scope.currentPicker.close = function () {
      editorService.close();
    }

    //open the correct editor based on the entity type
    switch (entityType) {
      case "Document":
        const dialog = {
          entityType: "Document",
          filterCssClass: "not-allowed not-published",
          filter: function (item) {
            if (item.metaData.contentType !== "category" || item.hasChildren)
              return true;

            return item.nodeType === "container" || item.metaData.isElement || !!_.findWhere(vm.itemTypes, {udi: item.udi});
          },
          currentNode: editorState ? editorState.current : null,
          submit: model => {
            if (Utilities.isArray(model.selection)) {
              Utilities.forEach(model.selection, (item, i) => {
                vm.add(item);
              });
            } else {
              vm.clear();
              vm.add(model);
            }
            setDirty();
          },
          treeAlias: "content",
          section: "content",
          idType: "udi",
          close: () => editorService.close()
        };

        editorService.contentPicker(dialog);
        break;
      case "Media":
        editorService.mediaPicker($scope.currentPicker);
        break;
      case "Member":
        editorService.memberPicker($scope.currentPicker);
        break;

      default:
    }

  };

What do i now do in the backoffice to replicate the issue 🤔

bjarnef commented 1 year ago

If you add console.log("item, item) inside filter function you should be able to see entries. Then filter on item.metaData.contentType by returning true or false.

Depending on this nodes should be greyed... but on search these would still be selectable.

Zeegaan commented 1 year ago

Alright I think I got it working, thanks! I am however unable to reproduce the issue 🤔 my search just does not show any of the nodes: image image

Zeegaan commented 1 year ago

My $scope.openCurrentPicker function:

 //dialog
  $scope.openCurrentPicker = function () {
    $scope.currentPicker = dialogOptions;

    $scope.currentPicker.submit = function (model) {
      if (Utilities.isArray(model.selection)) {
        _.each(model.selection, function (item, i) {
          $scope.add(item);
        });
        setDirty();
      }
      setDirty();
      editorService.close();
    }

    $scope.currentPicker.close = function () {
      editorService.close();
    }

    //open the correct editor based on the entity type
    switch (entityType) {
      case "Document":
        const dialog = {
          entityType: "Document",
          filterCssClass: "not-allowed not-published",
          filter: function (item) {
            console.log("item", item);
            return item.metaData.contentType === "myTestDoc";

          },
          currentNode: editorState ? editorState.current : null,
          submit: model => {
            if (Utilities.isArray(model.selection)) {
              Utilities.forEach(model.selection, (item, i) => {
                vm.add(item);
              });
            } else {
              vm.clear();
              vm.add(model);
            }
            setDirty();
          },
          treeAlias: "content",
          section: "content",
          idType: "udi",
          close: () => editorService.close()
        };

        editorService.contentPicker(dialog);
        break;
      case "Media":
        editorService.mediaPicker($scope.currentPicker);
        break;
      case "Member":
        editorService.memberPicker($scope.currentPicker);
        break;

      default:
    }

  };
bjarnef commented 1 year ago

Strange I think it should filter based on name as I recall.

Zeegaan commented 1 year ago

@bjarnef Really strange indeed, could you reproduce this behavior with my code ? 🤔

bjarnef commented 1 year ago

@Zeegaan I noticed when expanding tree to products I get metaData.ContentTypeAlias:

image

but for Home Page entity I get metaData.contentType

image

I would have expected each item to have same structure, I think it had in past.

bjarnef commented 1 year ago

However this example dashboard works - just unzip to App_Plugins folder. MyDashboard.zip

https://user-images.githubusercontent.com/2919859/204795721-5c473204-b2cf-4ee3-9593-20088cf7607a.mp4

It also seem the search filter doesn't return results of other types now.

Zeegaan commented 1 year ago

@bjarnef Hmm strange indeed, it should still show stuff greyed out 😅 Seems like an issue to me!

bjarnef commented 1 year ago

@Zeegaan could you try with the test dashboard attached here https://github.com/umbraco/Umbraco-CMS/issues/13337#issuecomment-1332069104 and the default starter kit.

When filtering "home" I also get that node in filter function, but excluded since its alias is not product. This is tested on v11/contrib branch.

image

However this issue was reported in v10.2.1, where it seemed the filter still returned the results, maybe because I only had item.metaData.contentType === "category and not also check on item.metaData.ContentTypeAlias === "category".

Zeegaan commented 1 year ago

@bjarnef Same for me: image

bjarnef commented 1 year ago

@Zeegaan I think the meta data here is just set as dictionary, so I think it hasn't been consistent between entity types.

Zeegaan commented 1 year ago

@bjarnef I think you are right, but I'm having a hard time following what the issue is here 🤔 So can you clarify what is the issue here? 🤔

bjarnef commented 1 year ago

@Zeegaan in the project I am working on using v10.3.2 when implementing the picker it was limited to a specific entity type, but on search I could e.g. search home page node and select that. However at that time the project was on 10.2.1 and has been updated to 10.3.2 since then.

When I now test again using this:

filter: function (item) {
    if (!(item.metaData.contentType === "course" || item.metaData.ContentTypeAlias === "course")) {
        return true;
    }

    return false;
}

or this:

filter: function (item) {
    if (item.metaData.contentType !== "course" || item.hasChildren)
        return true;

    return item.nodeType === "container" || item.metaData.isElement || !!_.findWhere(vm.itemTypes, { udi: item.udi });
}

it seems I can't reproduce it as I only get filtered results of type course.

Maybe something else fixed that.

Zeegaan commented 1 year ago

@bjarnef Makes sense, so is it okay to close this then ? 😁

bjarnef commented 1 year ago

Yes, let's close this for now 😁