Please upgrade here. These earlier versions are no longer being updated and have security issues.
HackerOne users: Testing against this community violates our program's Terms of Service and will result in your bounty being denied.

Excluding discussion records from display without the use of css or custom views

I would like to develop a plugin that can exclude discussion records from display without the use of css or custom views. The exclusion controlled by the plugin should be total - namely the plugin could prevent the user from accessing excluded discussions regardless of whether he is reaching discussions from the discussion controller, categories controller, profile, activity, etc.

Appreciate any ideas, insights.

«1

Comments

  • R_JR_J Admin

    Use categories with custom permissions

  • rbrahmsonrbrahmson ✭✭✭

    OK, that may work for my purpose if there was a hook on the permission check so I could dynamically allow/disallow access to a specific user to the category.

  • R_JR_J Admin

    Developers should use standard models to get data, but they do not have to. So in order to be really sure, you have to use the access restriction that is in place and that are category permissions and roles.

    Permission is checked on different ways, too.

    You could try to hook in very early events, but all in all this doesn't sound like a clean way. What do you try to achieve in detail?

  • rbrahmsonrbrahmson ✭✭✭

    I want to be able to send invites to specific discussions (through a "SHARE" button) without having to open the entire forum or discussions to the invited party.

    So if categories are already tightly controlled, I could move the shared discussion to a "shared" category and then use the plugin to decide on a case-by-case basis access to the shared discussion. But using categories is a workaround. The best would be to be able to control access to the discussion regardless of which category it is.

  • You can modify the permissions a user has as soon as the session is started.

    Yaga does exactly this to provide the perks functionality. Here is the relevant code: https://github.com/hgtonight/Application-Yaga/blob/4d99c42fd11ebf5b946d345b96438e9c59183882/settings/class.hooks.php#L447-L546

    Search first

    Check out the Documentation! We are always looking for new content and pull requests.

    Click on insightful, awesome, and funny reactions to thank community volunteers for their valuable posts.

  • rbrahmsonrbrahmson ✭✭✭

    That's great. Clearly I can define specific permissions in my "share" plugin and with your example grant specific permission when the invitee logs on. That is a much better approach than the one I envisioned which was to manage permission for this function on my own.

    But regardless of the method, the stumbling block is how to use that permission in the plugin to offer access to a specific discussion (rather than the category to which it belongs). Seems exceedingly "wrong" to create a category for each invited user (and move discussion in there). BTW (and I know this is for another discussion), if discussions could be assigned to multiple categories (a "shortcut"?) then the move step with all it's issues would not be necessary...

  • @rbrahmson said:
    BTW (and I know this is for another discussion), if discussions could be assigned to multiple categories (a "shortcut"?) then the move step with all it's issues would not be necessary...

    This is a clue that you should be using Tags rather than categories.

    Categories is one to many discussions. Tags is many to many discussions.

    Search first

    Check out the Documentation! We are always looking for new content and pull requests.

    Click on insightful, awesome, and funny reactions to thank community volunteers for their valuable posts.

  • rbrahmsonrbrahmson ✭✭✭
    1. Indeed one to many (and I read somewhere that performance wise it is practical to have many tags but not so practical to have a large number of categories).
    2. I wish there was a way to permit viewing and commenting on a discussion based on its tags. Then I'd have implemented the share-Discussion function by creating special tags for the sharing function and permit access to the invitee to the tag.
    3. I would still need to hide these tags from the tag cloud and from the meta areas (probably replace with "Shared" in the meta area). But that is a simpler problem.

    So my problem is still finding a solution to exclude discussion records without custom views and css.
    Am I getting closer to the solution???

  • rbrahmsonrbrahmson ✭✭✭

    Refocusing on the original issue - did I miss the answer or clue to it, or there really isn't any way to exclude discussion records from display through a plugin (that indicates yea/nay) for all the official Vanilla code? (I can live with other plugins going to these excluded discussions directly since I control which plugins I enable)

  • You can exclude discussions via a category permission, site wide.

    You can exclude discussions via a base discussionModel_beforeGet_handler hook as well.

    Search first

    Check out the Documentation! We are always looking for new content and pull requests.

    Click on insightful, awesome, and funny reactions to thank community volunteers for their valuable posts.

  • rbrahmsonrbrahmson ✭✭✭

    Thank you @hgtonight (and @R_J who hinted to that in another comment). I went further in research and found this discussion insightful.
    I think that it leads me to believe that I can create a separate "Sharing" table and use the joining technique from that discussion to exclude discussions that are not shared (only for the Invitee user who is not yet a full member of the forum).
    Any warning/insights before I start on the work?

  • R_JR_J Admin

    I think you will have no success with this approach. It is quite easy to add something to the sql with the BeforeGet event, but the sql will also contain a where d.Categrory in (allowed categoriess). I see no sql way to get a) all discussions from allowed categories b) together with the permitted discussions from a closed category because of that restriction.
    I've tried to find a solution but gave up.

    Joining another table wouldn't work for the same reasons.

    There is another way, though, but it requires you to "copy" a lot of code. When the discussionmodels function is called, you can fool Vanilla and temporarily change a config setting that no categories are used. This way, Vanilla would skip the category permission check altogether.

    I've started to write such a plugin in order to see how much work it would be, but stopped when I realized that the copied code from both index methods would make the plugin not future proof. It would need a lot of code changes foreach update. I wouldn't want to publish such a plugin.

    See it as a proof of concept...

    https://github.com/R-J/leak/tree/master

    But I really believe there must be a better way...

  • rbrahmsonrbrahmson ✭✭✭

    I'm not sure the where clause I'm considering is the one you were thinking of.
    First step, the plugin will check that the user is an Invitee (a special Role for that). The sharing process will create an entry in a SharingTable with the Invitee ID as key and an array of discussion IDs he is allowed to participate in.

    Once the Invited user logs in (having a special Sharing Role), their ability to access Discussions will be controlled through (1) Having the Category of the discussion accessible to the Sharing role -- that is standard and unchanged by the plugin, and (2) Through the additional Where set by the plugin. Here is a general way I was considering:

    public function DiscussionModel_BeforeGet_Handler($Sender) {
        if (CurrentUseridRole != 'Invitee') return;  //Obviously this is pseudo code
        $Sql2 = clone $Sender->SQL;  //Use different SQL to fetch shared discussion IDs for the Invitee
        $Sql2->Reset();
        $Sql2->Select('SharedDicsussionIDs')
                 ->From('SharingTable')
                 ->Where('InvitedID','$InviteeUserid)
                 ->Get();
        if ($Sql2->numRows() > 0) {
           $Row = $Sql2->firstRow();
           $SharingArray = $Row->SharedDicsussionIDs;//Array of Discussion IDs Invitee can see
           $Sender->SQL->Where('where d.DiscussionID in', $SharedDicsussionIDs);
        }
    }
    

    Would that work?
    Is my SQL syntax correct?

  • R_JR_J Admin

    If you open the index of the discussions controller (yourforum.com/discussions), the following sql is used to get all discussions (taken from my test install):

    DiscussionModel->getWhere(array(), 0, 25) slave 0.001010s
    
    select d2.*, w.UserID as `WatchUserID`, w.DateLastViewed as `DateLastViewed`, w.Dismissed as `Dismissed`, w.Bookmarked as `Bookmarked`, w.CountComments as `CountCommentWatch`, w.Participated as `Participated`
    from GDN_Discussion d
    join GDN_Discussion d2 on d.DiscussionID = d2.DiscussionID
    left join GDN_UserDiscussion w on w.DiscussionID = d2.DiscussionID and w.UserID = 2
    where d.CategoryID in ('-1', '10', '1', '2', '3', '7', '4', '6', '8')
    order by d.DateLastComment desc
    limit 25;
    

    If you look at the sql when the BeforeGet event is fired

        public function discussionModel_beforeGet_handler($sender, $args) {
            decho(Gdn::sql()->getSelect());
        }
    

    You'll see that it is "empty" at that time. So whatever you do with a cloned sql will not influence the result of the function that fires the event. The only EventArguments are sort criteria and a where array.

    If you add anything to Gdn::sql(), it will be added to the sql that is created (example above).

    Given that you have a discussion with id = 15 and category 5, where category 5 is a restricted category,
    you would end up with a sql like the above and those lines:
    where d.DiscussionID in (15, ...)
    and d.CategoryID in (everything but not 5)

    So the result would not contain discussion 15, because of the category restriction.

  • R_JR_J Admin

    By the way: getting a discussion is only the first step. In order to be able to comment, react, etc. on a discussion, you have to tweek much more.

    As @hgtonight said, you can change the permissions of a user on the fly. I wouldn't like to work like that, but if you only use your plugin in your own setup, you might consider to do a base_render_before function that adds the category permission and then try to restrict where it should be used.
    Match Gdn::controller()->ControllerName and Gdn::controller()->RequestMethod against a white list (that you need find out with try and error) and do the the user/invited check in the second step.

    Only if a) category can be used for invitations, b) user is not allowed to view category, c) user has been invited to this discussion, change the users permissions for the restricted category.

  • rbrahmsonrbrahmson ✭✭✭
    1. Sql2 was done intentionally not to interfere with whatever prep is done to the Gdn::sql(). Just trying to be safe.
    2. While Sql2 is independent of Gdn::sql(),note that my line 12 is not using Sql2:
      $Sender->SQL->Where('where d.DiscussionID in', $SharedDicsussionIDs);
    3. I intentionally support the category permissions. The plan is to have a special role for Invitees and then let the admin decide which categories can be Shared. The Shared process will not allow sharing to categories not permitted in the Invitee role. I think it is a proper approach and doesn't even require to change permissions on the fly.
    4. Do you concur that for those categories where the Invitee role is accessible, the discussion list will be limited to the discussions in the Shared table?
  • R_JR_J Admin
    1. Okay, understood. Hint: if you do not have many of those discussions, a table could be overkill. Consider using the UserMeta table
    2. Your sql is wrong. There is a dedicated function for "where in" Gdn::sql()->whereIn('d.DiscussionID', $SharedDiscussionIDs);
    3. "The Shared process will not allow sharing to categories not permitted in the Invitee role" so the user has access to all discussions? You want to restrict that to "all invited discussions"?
    4. Adding the whereIn from 2. would work for /discussions, yes. But...

    If you do not restrict access to discussions by permissions, you have to set up your own security conception, never knowing which plugin/core function gives access to a discussion. Without further scanning or thinking, I would guess you have problems because:

    • discussions in profile are not restricted
    • creating new discussions in that category would be possible
    • I guess the search results wouldn't be restricted
    • I'd bet it would be possible to access all comments in that category

    And many, many more places to look at...

    Maybe creating a conversation as a follow up to a discussion is simpler? You could focus your energy on a plugin that offers to base a conversation on a discussion, converting discussion and comments in messages. If the plugin offers to close the discussion, there is no risk of cluttered communication.

  • R_JR_J Admin

    There is an additional problem : you restrict it to only include the shared discussions. You would have to exclude the not-shared discussions from that category

  • rbrahmsonrbrahmson ✭✭✭

    @R_J - Thanks for spending time on this. This is a bit discouraging... My thinking is that the Share function would be a vehicle to get unregistered users interested in joining in, not by scanning content (as is the case today) but by being invited to comment on a specific discussion by the author (or a commenter) who knows the invitee and think the subject would be of interest. So basically _this is a different mode_l of promoting a forum and engaging new users.

    So my goal was to offer a relatively similar discussion/commenting experience and allow promotion from "Invitee" to full-member by request of either the invitee or the sharing person (Admin may or may not be required to further access the request to join - that is a separate issue).

    So the discouraging part is the holes in the scheme. You clearly see why I initially wrote " The exclusion controlled by the plugin should be total..." - I was afraid of the holes you highlighted.

    I am not sure what's the best way to proceed. I share your dislike of cloning code and be subjected to constant version revision updates,and I'm not crazy about dynamic permission setting either although it looks like the lesser evil.

  • R_JR_J Admin

    @rbrahmson said:
    @R_J - Thanks for spending time on this. This is a bit discouraging... My thinking is that the Share function would be a vehicle to get unregistered users interested in joining in, not by scanning content (as is the case today) but by being invited to comment on a specific discussion by the author (or a commenter) who knows the invitee and think the subject would be of interest. So basically _this is a different mode_l of promoting a forum and engaging new users.

    You would have had a lot more problems:
    Sending an invitation would have implied the automatic creation of a user or at least the automatic creation when that invitee first time uses the invitation link. Without user id, this person at least would never had the possibility to comment.

    If you create a user, you need some minimum information: username and password. So if a person is invited to a discussion, he would have felt to be forced to register (which he in fact is)

    I guess a combination of sending someone the user link together with a plugin like the CommentBeforeRegister (don't know if that is the correct name), would be better.

    @rbrahmson said:
    So my goal was to offer a relatively similar discussion/commenting experience and allow promotion from "Invitee" to full-member by request of either the invitee or the sharing person (Admin may or may not be required to further access the request to join - that is a separate issue).

    Only a promotion of the role could be done, but that's allow

    @rbrahmson said:
    So the discouraging part is the holes in the scheme. You clearly see why I initially wrote " The exclusion controlled by the plugin should be total..." - I was afraid of the holes you highlighted.

    You have to see it the other way around: you are the one who is creating a hole in the scheme. The permission conception is based on categories. You can break that if you try hard (but it is the security aspect, which should make it nearly unneded to really break it anyway), but you can only break it partially. If you do not need it, you can deactivate it. But if it is needed, it should work anywhere! So if you want to find a way around, you really have to work hard.
    You should feel comfortable to see that the permission conception is deeply integrated.

    @rbrahmson said:
    I am not sure what's the best way to proceed. I share your dislike of cloning code and be subjected to constant version revision updates,and I'm not crazy about dynamic permission setting either although it looks like the lesser evil.

    You like to invite non-users to discussions that are in a category that also contains discussions the user should not be allowed to see?

    This is abusing the user-idea as well as the permission-idea. I guess it would be hard work to accomplish that. I could think of ways for both, but the effort to do so is not worth the profit.

Sign In or Register to comment.