Since taxonomies can be published/unpublished #2899923: UI for publishing/unpublishing taxonomy terms #2880149: Convert taxonomy terms to be revisionable, unpublished terms should not be shown to the end user on exposed filters.

Example:

  • Taxonomy contains Aus States: QLD NSW WA etc...
  • Unpublish NSW
  • Create a view, add taxonomy as filter and configure filter criterion to expose filter to users (see screenshot 1)
  • All taxonomy terms will be displayed in the filter to the users (see screenshot 2)

Expected behaviour would be only published taxonomy terms to be displayed in the filter.

Screenshot 1:

Screenshot 2:

Comments

JannaKha created an issue. See original summary.

daffie’s picture

Category: Bug report » Feature request
Priority: Critical » Normal

This is not a bug report, but a feature request.

lendude’s picture

Category: Feature request » Bug report
Priority: Normal » Major
Status: Active » Needs review
StatusFileSize
new2.35 KB
new3.93 KB

The fact that you can see unpublished terms as an anon user is a bug in my view and a possible information disclosure. I checked with the security team and they decided not to mark this a security issue, but I still believe this to be something we need to fix ASAP.

So, fix and test for this. It only removes the unpublished terms from the actual exposed filter, not the Views UI. Since there is no 'view unpublished terms' permission, the exact rules about who can see what are a bit fuzzy. But if you can edit Views, being able to see unpublished terms doesn't sound like too much of a problem.

The last submitted patch, 3: 3101738-3-TEST_ONLY.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jannakha’s picture

Hi Lendude,

thanks for patch!
I too think it's a bug.

Patch works only for one-level taxonomies, doesn't work for multi-level.
eg if NSW has child terms (eg Central Coast, Northern Rivers) they will get displayed.
If view has "Show hierarchy in dropdown" then unpublished term is displayed as well (see screenshots):

ac
ac
ac

daffie’s picture

Status: Needs review » Needs work

Looks good, just one remark:

+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
@@ -193,8 +196,13 @@ protected function valueForm(&$form, FormStateInterface $form_state) {
+        if (!\Drupal::currentUser()->hasPermission('administer taxonomy')) {
+          $query->condition('status', 1);
+        }

Should the non-admin user not have the permission "access content"?

lendude’s picture

Title: Add published/unpublished option to taxonomy filter on views » Exposed term filters should not show term options that the user does not have access to
Status: Needs work » Needs review
StatusFileSize
new4.15 KB
new4.19 KB

@daffie nice catch, that pointed me to an easier solution here then juggling permissions ourselves, just check for view access.

@JannaKha thanks for the review! Much appreciated!

if NSW has child terms (eg Central Coast, Northern Rivers) they will get displayed.

I thought about this and I don't think we should make that decision in Views. If the tree loads the terms and the term says we have access to it, Views should respect that and show it. I totally 100% agree that this can lead to strange results, but it's something the editor can fix (by unpublishing the child terms). If we decide here that they are always hidden, and this is NOT what the editor wants, there would be no way for the editor to fix this.

daffie’s picture

I like the new approach. Just one nitpick:

+++ b/core/modules/taxonomy/tests/src/Functional/Views/TaxonomyIndexTidUiTest.php
@@ -87,7 +87,7 @@ protected function setUp($import_test_views = TRUE) {
-  public function testFilterUI() {
+  public function ptestFilterUI() {

@@ -134,7 +134,7 @@ public function testFilterUI() {
-  public function testExposedFilter() {
+  public function ptestExposedFilter() {

Why do we rename these 2 tests?

lendude’s picture

StatusFileSize
new911 bytes
new3.54 KB

Oops! Debugging stuff so I don't run all the test in the file :D

Removed it.

daffie’s picture

Status: Needs review » Reviewed & tested by the community

I am happy now.
The bug has been fixed and a test has been added.
For me it is RTBC.
Thanks everybody!

lendude’s picture

Issue summary: View changes

Small update to the IS to make it more in line with the current fix.

chris matthews’s picture

Should this issue be targeted against 8.8.x-dev or 8.9.x-dev?

alexpott’s picture

I checked that the plugin has the correct cache contexts to deal with a user permissions and it appears to

  public function getCacheContexts() {
    $contexts = parent::getCacheContexts();
    // The result potentially depends on term access and so is just cacheable
    // per user.
    // @todo See https://www.drupal.org/node/2352175.
    $contexts[] = 'user';

    return $contexts;
  }

One concern I have is that some sites have a lot of taxonomy terms so are we sure we want to add this to the loop. OTOH as far as I can see there are no additional queries going to be done by \Drupal\taxonomy\TermAccessControlHandler::checkAccess() so maybe this doesn't matter.

berdir’s picture

Term access by default is less expensive than node access at least when node grants are used, yes. That cache context is already an issue, and we use a patch to remove that because I'd argue that less than 1% of the sites use term access logic that does is not covered by permissions/roles.

That said, the reason this class *already* has the user cache context is because the entity query already ads the taxonomy_term_access tag (which afaik should already be there by default). And just like with nodes, the expectation here is that taxonomy-access modules would implement that query alter hook and add a condition on the term status. So with that argument, this could be closed as a works a designed

Of course, the problem is that core, just like with nodes, doesn't actually implement its own query alter/grants API. Considering that core now does have a term status and access checks, I wonder if a better fix wouldn't be to implement hook_query_taxonomy_term_access_alter(), and add a status = 1 condition if the user doesn't have the administer taxonomy permission. (Which of course could then again break more specific term access modules in D8, but the same could be said of introducing the term status field in the first place).

jibran’s picture

Of course, the problem is that core, just like with nodes, doesn't actually implement its own query alter/grants API.

#2909307: Consider using the taxonomy term status in taxonomy_build_node_index() seems relevant here.

alexpott’s picture

Status: Reviewed & tested by the community » Needs review

I think @Berdir's statement that implementing hook_query_taxonomy_term_access_alter() might break "hook_query_taxonomy_term_access_alter" is a strong point in favour of making the change in #9. I think at some point we need to do a major review of entity access and make it consistent but a D8 bug fix is not the place.

alexpott’s picture

Status: Needs review » Reviewed & tested by the community

Oops - I changed my mind whilst commenting

jannakha’s picture

StatusFileSize
new4.28 KB

So patch #9 was moving in the right direction, but it didn't work in my case because of how core/modules/taxonomy/src/TermAccessControlHandler.php worked.
I have updated how access check is done and enhanced view's logic on which taxonomy terms are displayed and added a flag to force showing only published terms (similar how core hides unpublished terms on create/edit node forms).

Updated:
- core/modules/taxonomy/src/TermAccessControlHandler.php now forbids access to term if it's not published (/taxonomy/term/XXX and XXX is unpublished and user doesn't have edit/manage rights to taxonomy - users would still see that page.
- core/modules/views/src/Plugin/views/filter/InOperator.php added published_only flag to views
- core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php - added filtering of terms and child terms based on parent's status
(based on core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php)

There are quite a few other considerations to keep in mind, I'll try to write them down sometime soon.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 18: 3101738-18.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

lendude’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new3.54 KB

@JannaKha thanks for having a go at this, but I'm going to roll this back to #9 and set that back to RTBC.

I think the ideas in #18 are good but the scope of that patch is MASSIVE (as indicated by the 45 test fails against the current HEAD). We need to do this in little baby steps or it will never get done (that is just how core development works). Every point you make would require its own issue really.

Let's just keep this focussed on users not being able to view terms they don't have view access to and file follow ups if we want to make this better later.

jannakha’s picture

StatusFileSize
new1.33 KB

@Lendude yeah, agreed. that patch is massive and I put it here as an idea only, but #9 didn't work for me... after more investigations I found I've got a module vppr which overrides permissions to term access (I've created an issue for that too). So, yes #9 works fine.

So patch to core/modules/taxonomy/src/TermAccessControlHandler.php can be discarded! yes! that'll get rid of some failed tests.

I've patched core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php to be inline with core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php (hide children of unpublished terms)

I'll create a new issue to include force published only terms in filters.

PS Wrong patch! disregard #21 patch

jannakha’s picture

StatusFileSize
new1.36 KB

Patch which hides children of unpublished terms

alexpott’s picture

Status: Reviewed & tested by the community » Needs work

We need to add the test coverage in #20 back in and we need to add additional test coverage to prove that the changes in #22 are necessary.

lendude’s picture

Status: Needs work » Reviewed & tested by the community
StatusFileSize
new3.54 KB

Re-upping #9 again, since I still think that this is the way to go here.

As I detailed in #7, I don't think we want the changes in #22 for core, if an editor doesn't want the child terms to be shown, they can unpublish the child terms.

vladimiraus’s picture

Status: Reviewed & tested by the community » Needs work

@Lendude: unpublished parent means all children are automatically unpublished even if they are published. That's the logic all my clients are suggesting. I don't agree that the children of unpublished child should be visible to the user with view published content permission.

According to core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php which hides children of unpublished terms, your logic is incorrect.

I'm voting for patch #22.

lendude’s picture

Status: Needs work » Reviewed & tested by the community

@VladimirAus the logic that the patch in #9 follows is that the Views module follows whatever the Taxonomy module gives us. If the Taxonomy module decides that child terms of unpublished Terms should not have 'view' access, Views would follow that logic. It is not up to Views to decide what the access logic for Taxonomy terms is.

The fact that core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php has special code for this, is exactly the type of thing I'm trying to avoid here, that this logic is handled separately in different places in core (which will lead to inconsistent application of this logic). It should be handled in one place so that dependant code doesn't have to think about this at all.

So I agree that child terms of unpublished Terms should probably not be visible, but I strongly disagree that Views should have special logic for this. So to me, #9 is the right thing for Views and we would need a separate issue that removes the special logic in \Drupal\taxonomy\Plugin\EntityReferenceSelection\TermSelection and applies that logic to the 'view' access of taxonomy terms in general. That way the responsibility of Term access stays within the Taxonomy module, where it belongs.

But if you really want to push for #22, it needs the test coverage back in and more added for the new logic.

I'm leaving the re-up of #9 RTBC and I'll see if we can get some more eyes on this.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 24: 3101738-9.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

alexpott’s picture

Status: Needs work » Reviewed & tested by the community

Random unrelated fail.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 24: 3101738-9.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

alexpott’s picture

Status: Needs work » Reviewed & tested by the community

Another one... unlucky.

catch’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
@@ -181,6 +181,9 @@ protected function valueForm(&$form, FormStateInterface $form_state) {
 
         if ($tree) {
           foreach ($tree as $term) {
+            if (!$term->access('view')) {
+              continue;
+            }
             $choice = new \stdClass();
             $choice->option = [$term->id() => str_repeat('-', $term->depth) . \Drupal::service('entity.repository')->getTranslationFromContext($term)->label()];
             $options[] = $choice;
@@ -200,6 +203,9 @@ protected function valueForm(&$form, FormStateInterface $form_state) {

@@ -200,6 +203,9 @@ protected function valueForm(&$form, FormStateInterface $form_state) {
         }
         $terms = Term::loadMultiple($query->execute());
         foreach ($terms as $term) {
+          if (!$term->access('view')) {
+            continue;
+          }
           $options[$term->id()] = \Drupal::service('entity.repository')->getTranslationFromContext($term)->label();
         }
       }

I'm not sure about this change.

If we consider the filter to be a list of terms, then we should be relying on the views configuration or entity query to provide terms with the correct access - i.e. in the specific case mentioned, there should be a filter on published.

With node listings and similar we always rely on published status + node grants, and don't check node view access.

If we can't do that, then we need a comment explaining why we're diverging here.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

lendude’s picture

Status: Needs work » Needs review
StatusFileSize
new2.1 KB
new3.17 KB

Not 100% sold on the hardcoded filtering on published, still feels like Views is designing its own taxonomy access check, but ¯\_(ツ)_/¯

Here we go with a check on published.

vijaycs85’s picture

If we consider the filter to be a list of terms, then we should be relying on the views configuration or entity query to provide terms with the correct access - i.e. in the specific case mentioned, there should be a filter on published.

+1. wonder if it would be a good idea to add a label for published/unpublished in the filter settings instead of trying to hide them. e.g.


diff --git a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
index 5829681767..30d3bdca7c 100644
--- a/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
@@ -200,7 +200,7 @@ protected function valueForm(&$form, FormStateInterface $form_state) {
         }
         $terms = Term::loadMultiple($query->execute());
         foreach ($terms as $term) {
-          $options[$term->id()] = \Drupal::service('entity.repository')->getTranslationFromContext($term)->label();
+          $options[$term->id()] = \Drupal::service('entity.repository')->getTranslationFromContext($term)->label() . !$term->isPublished() ? ' (Unpublished)' : NULL;
         }
       }
+++ b/core/modules/taxonomy/src/Plugin/views/filter/TaxonomyIndexTid.php
@@ -181,6 +181,9 @@ protected function valueForm(&$form, FormStateInterface $form_state) {
+            if (!$term->isPublished()) {

might not work for the use cases where admin view to filter by unpublished terms.

danielveza’s picture

Just throwing my 2c in, this feels like a security issue to me too. I agree with lendude.

Especially when you consider that a precedent was set with https://www.drupal.org/sa-contrib-2020-018 & https://www.drupal.org/sa-contrib-2020-016. They seem similar to me

bkosborne’s picture

Status: Needs review » Reviewed & tested by the community

Heh, I came across this issue and thought it'd be a simple fix, and after reading the comments it's clear there's a lot of good reasons why this is not a simple fix.

I agree with catch in #31. Yes, it feels wrong to have these direct checks for publishing status only (which completely ignores the entity access API), but it's really a temporary bug fix for the real problem described in #14 by Berdir.

If we implemented the direct entity access checks for each term in the loop, while it's not slow NOW, I think it will be slow in the future when #777578: Add an entity query access API and deprecate hook_query_ENTITY_TYPE_access_alter() is implemented, because it could mean an additional DB query for each term in the loop to check entity access grant table for taxonomy term.

So +1 to this solution. I also tested it and it works well.

bkosborne’s picture

  • catch committed 639a8ec on 9.1.x
    Issue #3101738 by Lendude, jannakha, alexpott, daffie, bkosborne, Berdir...

  • catch committed 478f339 on 9.0.x
    Issue #3101738 by Lendude, jannakha, alexpott, daffie, bkosborne, Berdir...

  • catch committed efbd86d on 8.9.x
    Issue #3101738 by Lendude, jannakha, alexpott, daffie, bkosborne, Berdir...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 9.1.x and cherry-picked back to 8.9.x, thanks!

  • catch committed cd04aa1 on 9.1.x
    Revert "Issue #3101738 by Lendude, jannakha, alexpott, daffie, bkosborne...

  • catch committed dc5b738 on 9.0.x
    Revert "Issue #3101738 by Lendude, jannakha, alexpott, daffie, bkosborne...
catch’s picture

Status: Fixed » Needs review

I had a nagging feeling about this issue which I couldn't figure out what it is, and just realised after commit...

Should we be checking 'administer taxonomy' before adding the published status check? Then if you have an admin view with a term filter, the patch won't affect you.

Rolled back for now to discuss this, if the answer is 'no' we can recommit as-is.

  • catch committed 83b729a on 8.9.x
    Revert "Issue #3101738 by Lendude, jannakha, alexpott, daffie, bkosborne...
lendude’s picture

StatusFileSize
new4.68 KB
new5.99 KB

I think the check should be in line with what the 'view' access check for terms currently does, and that means #44 sounds like a good idea to me.

We had test coverage for this before but no idea why I took that out ¯\_(ツ)_/¯

This adds a check for 'administer taxonomy' to both logic paths and test coverage for that.

jonathanshaw’s picture

Status: Needs review » Reviewed & tested by the community

Fix and test coverage are good.

You may want to consider removing this todo in TaxonomyIndexTid and closing #2352175: Taxonomy views filters should not have user context:

  public function getCacheContexts() {
    $contexts = parent::getCacheContexts();
    // The result potentially depends on term access and so is just cacheable
    // per user.
    // @todo See https://www.drupal.org/node/2352175.
    $contexts[] = 'user';

    return $contexts;
  }
lendude’s picture

@jonathanshaw thanks for pointing that one out! I think we should definitely post a comment there if this lands, but I think we need more feedback on it before removing and closing it.

  • catch committed 6c878ed on 9.1.x
    Issue #3101738 by Lendude, jannakha, alexpott, daffie, catch, bkosborne...

  • catch committed cd6a9cd on 9.0.x
    Issue #3101738 by Lendude, jannakha, alexpott, daffie, catch, bkosborne...

  • catch committed fc6ae2a on 8.9.x
    Issue #3101738 by Lendude, jannakha, alexpott, daffie, catch, bkosborne...
catch’s picture

Status: Reviewed & tested by the community » Fixed
Related issues: +#2352175: Taxonomy views filters should not have user context

Thanks for #46. Committed/pushed to 9.1.x and cherry-picked back to 8.9.x, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

carlitus’s picture

In the screenshoot i see that the filter was created using a field and maybe it's the only way now but ¿this works with "has taxonomy term" filter? I have 8.9.15 and with has taxonomy term filter the unpublished terms are showing.

thanks

carlitus’s picture

(sorry, I created this comment by mistake and i cannot delete now)

krzysztof domański’s picture