Skip to content

Conversation

ArthurMinfotel
Copy link
Contributor

@ArthurMinfotel ArthurMinfotel commented Jul 24, 2024

Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets

Add option to choose targets for private saved searches.
Modify CommonDBVisible to make it possible to restrict the types of target available, and to choose the right used to determine the availability of user type targets.
For a saved search, a target can either be a user or a group.
User and group dropdown take into account entity of the item when loading available targets.

The class used for the user target might need to be changed (SavedSearch_User is already used so I couldn't follow the naming convention set by the other CommonDBVisible items, so I created SavedSearch_UserTarget for now).
Also the new method getVisibilityRight could be used to dynamically get the right for the itemtype associated with the saved search (to only be able to targets users with the read right on the itemtype).

Example :
Target tab for SavedSearch :
image
Corresponding record in DB :
image
Available saved searches for the itemtype for users 2 without any target set :
image
User 2 added as a target :
image
Available saved searches on the itemtype for user 2 after being added added as a target :
image

Type of targets available set through the static variable $types :
image

Restrictions on groups available as targets based on the item's entity (for the example entity set as 1 instead of root) :
List of entities :
image
List of groups with entity restrictions :
image
Visibility for groups :
image
Subvisibility :
image

@cedric-anne
Copy link
Member

@ArthurMinfotel

Could you add some screenshots to your PR description?

@ArthurMinfotel
Copy link
Contributor Author

@ArthurMinfotel

Could you add some screenshots to your PR description?

I've edited it to add several screenshoots, showing an example and the added functionnalities.

Copy link
Member

@cedric-anne cedric-anne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed about it with @orthagh and we think that the is_private flag should be removed from the database. Indeed, having both a public/private management + a entity/groups/users visibility management is redundant and can be a bit complex to handle for the end user.

At creation, if the saved search is created as public, then it a visibility on the root entity + recursive should be automatically created. If it is created as private, the lack of visibility filters should be sufficient to make it private.

It would require to add a migration to add a new visibility filter to all existing public searches.

@ArthurMinfotel
Copy link
Contributor Author

ArthurMinfotel commented Aug 1, 2024

We discussed about it with @orthagh and we think that the is_private flag should be removed from the database. Indeed, having both a public/private management + a entity/groups/users visibility management is redundant and can be a bit complex to handle for the end user.

At creation, if the saved search is created as public, then it a visibility on the root entity + recursive should be automatically created. If it is created as private, the lack of visibility filters should be sufficient to make it private.

It would require to add a migration to add a new visibility filter to all existing public searches.

The current behavior of the public searches is to have them available for all users within it's entity (and child entities if it is recursive), so shouldn't I rather make it so that it automaticaly create a link with the active entity rather than the root entity if it is meant to be a public search ?

@cedric-anne
Copy link
Member

We discussed about it with @orthagh and we think that the is_private flag should be removed from the database. Indeed, having both a public/private management + a entity/groups/users visibility management is redundant and can be a bit complex to handle for the end user.
At creation, if the saved search is created as public, then it a visibility on the root entity + recursive should be automatically created. If it is created as private, the lack of visibility filters should be sufficient to make it private.
It would require to add a migration to add a new visibility filter to all existing public searches.

The current behavior of the public searches is to have them available for all users within it's entity (and child entities if it is recursive), so shouldn't I rather make it so that it automaticaly create a link with the active entity rather than the root entity if it is meant to be a public search ?

As we add visibility specs to saved searches, the idea was to consider that a saved search with no visibility is private. In this case, the is_private flag is no more useful.

@ArthurMinfotel
Copy link
Contributor Author

@cedric-anne Could you tell me if what I've added for the creation of public search matches what you talked about ? Same for the migration. Thanks in advance.

@cedric-anne
Copy link
Member

@cedric-anne Could you tell me if what I've added for the creation of public search matches what you talked about ? Same for the migration. Thanks in advance.

It seems to match our functional request, but I do not have time right now to do a complete review.

Copy link
Contributor

@AdrienClairembault AdrienClairembault left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a fatal error when trying to access the saved searches page:

[2024-09-05 12:20:25] glpiphplog.WARNING:   *** PHP Warning (2): Undefined array key "LEFT JOIN" 
in /glpi/main/src/Glpi/Search/Provider/SQLProvider.php at line 2231
  Backtrace :
  src/Search.php:679                                 Glpi\Search\Provider\SQLProvider::getDefaultJoinCriteria()
  src/Glpi/Search/Provider/SQLProvider.php:3804      Search::addDefaultJoin()
  src/Glpi/Search/SearchEngine.php:641               Glpi\Search\Provider\SQLProvider::constructSQL()
  src/Glpi/Search/SearchEngine.php:657               Glpi\Search\SearchEngine::getData()
  src/Glpi/Search/SearchEngine.php:614               Glpi\Search\SearchEngine::showOutput()
  src/Search.php:165                                 Glpi\Search\SearchEngine::show()
  front/savedsearch.php:55                           Search::show()
  ...Glpi/Controller/LegacyFileLoadController.php:57 require()
  vendor/symfony/http-kernel/HttpKernel.php:101      Glpi\Controller\LegacyFileLoadController->Glpi\Controller\{closure}()
  ...ymfony/http-foundation/StreamedResponse.php:106 Symfony\Component\HttpKernel\HttpKernel::Symfony\Component\HttpKernel\{closure}()
  vendor/symfony/http-foundation/Response.php:423    Symfony\Component\HttpFoundation\StreamedResponse->sendContent()
  public/index.php:58                                Symfony\Component\HttpFoundation\Response->send()
  
[2024-09-05 12:20:25] glpiphplog.CRITICAL:   *** Uncaught Exception TypeError: array_key_exists(): Argument #2 ($array) must be
of type array, null given in /glpi/main/src/Glpi/Search/Provider/SQLProvider.php at line 2232
  Backtrace :
  src/Glpi/Search/Provider/SQLProvider.php:2232      array_key_exists()
  src/Search.php:679                                 Glpi\Search\Provider\SQLProvider::getDefaultJoinCriteria()
  src/Glpi/Search/Provider/SQLProvider.php:3804      Search::addDefaultJoin()
  src/Glpi/Search/SearchEngine.php:641               Glpi\Search\Provider\SQLProvider::constructSQL()
  src/Glpi/Search/SearchEngine.php:657               Glpi\Search\SearchEngine::getData()
  src/Glpi/Search/SearchEngine.php:614               Glpi\Search\SearchEngine::showOutput()
  src/Search.php:165                                 Glpi\Search\SearchEngine::show()
  front/savedsearch.php:55                           Search::show()
  ...Glpi/Controller/LegacyFileLoadController.php:57 require()
  vendor/symfony/http-kernel/HttpKernel.php:101      Glpi\Controller\LegacyFileLoadController->Glpi\Controller\{closure}()
  ...ymfony/http-foundation/StreamedResponse.php:106 Symfony\Component\HttpKernel\HttpKernel::Symfony\Component\HttpKernel\{closure}()
  vendor/symfony/http-foundation/Response.php:423    Symfony\Component\HttpFoundation\StreamedResponse->sendContent()
  public/index.php:58                                Symfony\Component\HttpFoundation\Response->send()

I also have some errors related to a missing is_private field when accessing a singular saved search

[2024-09-05 12:21:45] glpiphplog.WARNING:   *** PHP Warning (2): Undefined array key
"is_private" in /glpi/main/src/SavedSearch.php at line 176
  Backtrace :
  src/CommonDBTM.php:2957                            SavedSearch->canViewItem()
  src/CommonDBTM.php:1413                            CommonDBTM->can()
  src/CommonDBTM.php:1484                            CommonDBTM->getLink()
  src/CommonDBTM.php:1324                            CommonDBTM->addMessageOnAddAction()
  front/savedsearch.form.php:50                      CommonDBTM->add()
  ...Glpi/Controller/LegacyFileLoadController.php:57 require()
  vendor/symfony/http-kernel/HttpKernel.php:101      Glpi\Controller\LegacyFileLoadController->Glpi\Controller\{closure}()
  ...ymfony/http-foundation/StreamedResponse.php:106 Symfony\Component\HttpKernel\HttpKernel::Symfony\Component\HttpKernel\{closure}()
  vendor/symfony/http-foundation/Response.php:423    Symfony\Component\HttpFoundation\StreamedResponse->sendContent()
  public/index.php:58                                Symfony\Component\HttpFoundation\Response->send()
  
[2024-09-05 12:21:46] glpiphplog.WARNING:   *** PHP Warning (2): Undefined array key
"is_private" in /glpi/main/src/SavedSearch.php at line 176
  Backtrace :
  src/CommonDBTM.php:2957                            SavedSearch->canViewItem()
  src/CommonDBTM.php:6521                            CommonDBTM->can()
  front/savedsearch.form.php:111                     CommonDBTM::displayFullPageForItem()
  ...Glpi/Controller/LegacyFileLoadController.php:57 require()
  vendor/symfony/http-kernel/HttpKernel.php:101      Glpi\Controller\LegacyFileLoadController->Glpi\Controller\{closure}()
  ...ymfony/http-foundation/StreamedResponse.php:106 Symfony\Component\HttpKernel\HttpKernel::Symfony\Component\HttpKernel\{closure}()
  vendor/symfony/http-foundation/Response.php:423    Symfony\Component\HttpFoundation\StreamedResponse->sendContent()
  public/index.php:58                                Symfony\Component\HttpFoundation\Response->send()
  
[2024-09-05 12:21:46] glpiphplog.WARNING:   *** PHP Warning (2): Undefined array key
"is_private" in /glpi/main/src/SavedSearch.php at line 167
  Backtrace :
  src/CommonDBTM.php:2997                            SavedSearch->canCreateItem()
  src/CommonDBTM.php:2491                            CommonDBTM->can()
  .../twig/twig/src/Extension/CoreExtension.php:1629 CommonDBTM->canEdit()
  ...tes/ac/ac4e6f347f3439630bec9c113c2e2d08.php:131 Twig\Extension\CoreExtension::getAttribute()
  vendor/twig/twig/src/Template.php:360              __TwigTemplate_57914519ef8b5fd53f2488a6f974814c->doDisplay()
  vendor/twig/twig/src/Template.php:335              Twig\Template->yield()
  vendor/twig/twig/src/TemplateWrapper.php:38        Twig\Template->render()
  src/Glpi/Application/View/TemplateRenderer.php:164 Twig\TemplateWrapper->render()
  src/CommonGLPI.php:1117                            Glpi\Application\View\TemplateRenderer->render()
  src/CommonGLPI.php:1235                            CommonGLPI->showNavigationHeader()
  src/CommonDBTM.php:6546                            CommonGLPI->display()
  front/savedsearch.form.php:111                     CommonDBTM::displayFullPageForItem()
  ...Glpi/Controller/LegacyFileLoadController.php:57 require()
  vendor/symfony/http-kernel/HttpKernel.php:101      Glpi\Controller\LegacyFileLoadController->Glpi\Controller\{closure}()
  ...ymfony/http-foundation/StreamedResponse.php:106 Symfony\Component\HttpKernel\HttpKernel::Symfony\Component\HttpKernel\{closure}()
  vendor/symfony/http-foundation/Response.php:423    Symfony\Component\HttpFoundation\StreamedResponse->sendContent()
  public/index.php:58                                Symfony\Component\HttpFoundation\Response->send()
  
[2024-09-05 12:21:47] glpiphplog.WARNING:   *** PHP Warning (2): Undefined array key
"is_private" in /glpi/main/src/SavedSearch.php at line 176
  Backtrace :
  src/CommonDBTM.php:2957                            SavedSearch->canViewItem()
  ajax/common.tabs.php:86                            CommonDBTM->can()
  ...Glpi/Controller/LegacyFileLoadController.php:57 require()
  vendor/symfony/http-kernel/HttpKernel.php:101      Glpi\Controller\LegacyFileLoadController->Glpi\Controller\{closure}()
  ...ymfony/http-foundation/StreamedResponse.php:106 Symfony\Component\HttpKernel\HttpKernel::Symfony\Component\HttpKernel\{closure}()
  vendor/symfony/http-foundation/Response.php:423    Symfony\Component\HttpFoundation\StreamedResponse->sendContent()
  public/index.php:58                                Symfony\Component\HttpFoundation\Response->send()
  
[2024-09-05 12:21:47] glpiphplog.WARNING:   *** PHP Warning (2): Undefined array key
"is_private" in /glpi/main/src/SavedSearch.php at line 167
  Backtrace :
  src/CommonDBTM.php:2997                            SavedSearch->canCreateItem()
  src/CommonDBTM.php:2491                            CommonDBTM->can()
  .../twig/twig/src/Extension/CoreExtension.php:1629 CommonDBTM->canEdit()
  ...ates/5e/5e2f397445e8802640ce2e365a65894a.php:89 Twig\Extension\CoreExtension::getAttribute()
  vendor/twig/twig/src/Template.php:360              __TwigTemplate_1d461d3a79b2305bbc255169bd25d6b1->doDisplay()
  vendor/twig/twig/src/Template.php:335              Twig\Template->yield()
  vendor/twig/twig/src/TemplateWrapper.php:38        Twig\Template->render()
  .../twig/twig/src/Extension/CoreExtension.php:1332 Twig\TemplateWrapper->render()
  ...ates/c7/c7414374327f0c283149304630a5cfa7.php:65 Twig\Extension\CoreExtension::include()
  vendor/twig/twig/src/Template.php:360              __TwigTemplate_2c34125655dbfbbe4ab849d98eabeeda->doDisplay()
  ...ates/28/28f78152b8644bd2f1f8c74ec7039cba.php:51 Twig\Template->yield()
  vendor/twig/twig/src/Template.php:360              __TwigTemplate_594809dc661d7cef6018583aa7f0fd08->doDisplay()
  vendor/twig/twig/src/Template.php:327              Twig\Template->yield()
  vendor/twig/twig/src/TemplateWrapper.php:45        Twig\Template->display()
  src/Glpi/Application/View/TemplateRenderer.php:185 Twig\TemplateWrapper->display()
  src/SavedSearch.php:453                            Glpi\Application\View\TemplateRenderer->display()
  src/CommonGLPI.php:654                             SavedSearch->showForm()
  ajax/common.tabs.php:110                           CommonGLPI::displayStandardTab()
  ...Glpi/Controller/LegacyFileLoadController.php:57 require()
  vendor/symfony/http-kernel/HttpKernel.php:101      Glpi\Controller\LegacyFileLoadController->Glpi\Controller\{closure}()
  ...ymfony/http-foundation/StreamedResponse.php:106 Symfony\Component\HttpKernel\HttpKernel::Symfony\Component\HttpKernel\{closure}()
  vendor/symfony/http-foundation/Response.php:423    Symfony\Component\HttpFoundation\StreamedResponse->sendContent()
  public/index.php:58                                Symfony\Component\HttpFoundation\Response->send()

Copy link
Contributor

@AdrienClairembault AdrienClairembault left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't add a target:

add_user

It fails for all possible choices (user, group and entity).

@ArthurMinfotel
Copy link
Contributor Author

ArthurMinfotel commented Nov 14, 2024

Made some changes to take the deletion of savedsearch.form.php into account (what was causing issue when I last requested a review, didn't notice the deletion when I merged from main and didn't do any functionnal tests for the minor changes I did after).

I created a new controller to include the action needed for items extending CommonDBVisible : it extends GenericFormController and deal with the 'addvisibility' action.

There are some right issues when loading a saved search with a profile which doesn't have the read right on bookmark_public, which I temporarely fixed by overriding the canView method in SavedSearch to always return true. But I also add to override getMenuContent because of that (which only stop the link from appearing in the menu, the list can still be accessed).
I left it with this temporary solution because I think the rights might get reworked with the deletion of the private property, but if the rights are left unchanged I thought of maybe reworking front/savedsearch.php this way :
image

@ArthurMinfotel
Copy link
Contributor Author

If everything is OK from a pratical point of view, I will start working on updating the current test functions and adding a new one for the targets.

@cedric-anne cedric-anne requested a review from orthagh January 30, 2025 14:26
@ArthurMinfotel
Copy link
Contributor Author

Tests are updated.
Things that are tested are :

  1. Automatically adding the saved search's entity as target if input contains is_private with a falsy value.
  2. For each type of target, visibility of a saved search to a user other than the creator when using SavedSearch::getMine().

@AdrienClairembault
Copy link
Contributor

It seems like there are still some small lint issues.

For the sonar cloud report, it does not like that you are created an item using a class supplied by the browser:

image

This has created security issues in the past as malicious users can supply some unwanted class names.
You can fix it by calling getItemForItemtype instead, which will make sure the class is at least a CommonGLPI item which should be safe.

@ArthurMinfotel
Copy link
Contributor Author

ArthurMinfotel commented Feb 10, 2025

I've updated the controller to fix the issue with the class instance.

For the lint, I believe those are not fixable :
image
The first and second are called in getTabNameForItem and displayTabContentForItem, and so lint doesn't recognise the methods from the class CommonDBVisible, which are called when $item is an instance of SavedSearch , since its parameter $item is typed as CommonGLPI.
As the third one, it's a right check, so I don't even know what the cause might be.

@AdrienClairembault
Copy link
Contributor

I've added suggestions that should fix the typing issues.

For the "always true" issue, this is because the condition exist twice:

image

So when you reach the second condition, it will always be true because if the first one was false then the function would have returned a value already.

I'll let you review to see which one you need to keep.

@ArthurMinfotel
Copy link
Contributor Author

I added the suggested fix. I will remember those for future developments.

Copy link
Contributor

@AdrienClairembault AdrienClairembault left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok for me besides the right check.

@cedric-anne cedric-anne force-pushed the targeted-saved-search-main branch from e58ae09 to 02cf893 Compare April 15, 2025 14:56
@cedric-anne
Copy link
Member

I rebased the branch to fix conflicts and revert some whitespace changes to reduce the diff. If @orthagh approves the feature, I will do a last technical review and merge it.

@flegastelois
Copy link
Member

I tested the feature:

  • with the "glpi" user, I created a savedsearch on the Tickets
  • shared it to a "normal" target user
  • logged in with the "normal" user
  • attempting to access the savedsearch from the Tickets results in a fatal error

By the way, the left menu points to the "Tools" section, instead of remaining in the Tickets section.

Note that if I add the "READ" (Saved Searches) permission for the "normal" user profile (Observer by default), it doesn't change anything.

image

User can not access the SavedSearch 1
In ./front/savedsearch.php(53)
#0 ./src/Glpi/Controller/LegacyFileLoadController.php(59): require()
#1 ./vendor/symfony/http-kernel/HttpKernel.php(101): Glpi\Controller\LegacyFileLoadController->Glpi\Controller\{closure}()
#2 ./vendor/symfony/http-foundation/StreamedResponse.php(106): Symfony\Component\HttpKernel\HttpKernel::Symfony\Component\HttpKernel\{closure}()
#3 ./vendor/symfony/http-foundation/Response.php(423): Symfony\Component\HttpFoundation\StreamedResponse->sendContent()
#4 ./src/Glpi/Kernel/Kernel.php(266): Symfony\Component\HttpFoundation\Response->send()
#5 ./public/index.php(58): Glpi\Kernel\Kernel->sendResponse()
#6 {main}

Copy link
Contributor

@trasher trasher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feature not working, see comments.

@cedric-anne
Copy link
Member

Replaced by #20247

@cedric-anne cedric-anne closed this Jul 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants