Building with XenForo 2 - Part 13: Working with permissions
i said when we started this series of videos that i didn't really know where the add-on was going to go and having looked at what we still need to cover i am going to fairly dramatically change the course of what it's going to be doing because i want to show various things that involve users interacting so whereas the current code is essentially a single user application and it just lets one user post notes for them for their own purposes i'm going to change that round so that multiple users can all post to this one shared space and of course that's sort of duplicative of the forum itself but we're only making a demo here so who cares so the first thing that we could look at doing would be removing the requirement to be registered so let's just comment that out for now h so maybe we could say that guests can view notes that are posted by anyone but we wouldn't want guests to be able to post their own notes so we're going to need to set up permissions the permission system and the ability for user groups to apply those permissions to different groups of users represents a huge part of XenForo's functionality the ability to control who can do what is at the heart of an awful lot of the power that is at your disposal so it's really important when you're building an add-on to understand what the permission system can do and how it works and how to add your own permission system for your own content so looking here at the user group for the registered users you can see that we've got some general permissions and then we've got some general moderator permissions and some bookmark permissions and the list goes on and on but we want to create our own group of permissions for the purposes of our note system so let's head down into the development zone and go to permission definitions and here we can see that we've got that general permissions list we just looked at and the general moderator permissions and these are what we call permission interface groups and they are a way to group permissions together in a visual fashion for the purpose of management so we're going to start off by creating our own interface group now you'll see that this has a little one next to it and that represents the display order so that's going to be displayed first on the page and then we've got two and then five but what i want to do just for the purposes of the demo is i'm going to give it a very small display order so it appears at the top of the page but when you're doing your own you should set something that is a little more sensible so that it doesn't interject itself right in the middle of everything else so let's start off with adding an interface group and i'm going to call this notes permissions and i'll give it a title of demo scratch pad note pad permissions because why not and i'm going to try giving it a display order of zero just so that it appears at the top it doesn't contain moderator permissions so that's fine for me and there it is right at the top which is very convenient for the purpose of the demo now let's go ahead and actually get some permissions in there so we'll because as you can see it says we don't have any permissions so we'll add one now the permission group is not the same as the interface group this is something that is handled at the permissions level and the groups are just defined by virtue of being referenced so i'm just going to call it notes and the permission id is going to be well let's talk about what permission we're actually going to implement first let's assume that all users can view notes but only some people can view other people's notes let's let's do that let's let's do both so let's just have a view own and we'll call it view own notes it doesn't depend on a permission id and we'll give it a display order of one that's fine now that will create a permission that the system behind the scenes there has also created a phrase so we don't need to worry about language independence with that and now let's add another one to go along with that and we'll call it view any and oh no that's not going that that should be notes and then that should go in there view any so this is going to refer to the ability for a user to view notes that have been posted by other people so let's call that view others notes depending on permission id yes it depends on view own because if you can't view own then maybe you should only be able to view any but maybe actually you want some sort of dropbox system where you can only view other people's but in this particular case you don't so we'll say that view any depends on view own and then the display order well let's associate it with the next one and just have it a little higher up so now we've got view own and view others notes now i'm going to carry on and add some more of those and then we'll come back and actually use some of these so you can see here i've gone ahead and added three new permissions for post new notes edit own notes and delete own notes but i also want to add a set of permissions that are essentially moderate permissions that allow people to modify other people's content but i'm going to put those into their own interface group so i'm going to add a new interface group here and i'm going to call it notes permissions let's call it notes moderator permissions and we'll just take that and add the word moderator in there and i'm going to add a zero in there to hopefully get it up with the other group of notes permissions and then i'm going to say that this group does contain moderator permissions because it does hit save and it's added it above but that's fine for now so i'm going to add in a collection of permissions for this to act as a sort of counter to these edit own and delete own notes so let's start off with edit others notes so it's still going to be part of the notes group the permission id is going to be edit others or let's call it edit any edit others notes and it depends on permission id no i don't think it does and we'll give that a display order of 11 and then i'll go ahead and add a delete others as well in the same way so with those new permissions set up we need some other data in here so that we can demonstrate the interaction of self-owned data and data that's owned by other users so i've logged a couple of other users into other browsers so here's another copy of Chrome with Mr Spammer logged in and two posts added for him and here is what's that that's Firefox with Trolleta logged in and two notes added there but we also need to identify who belongs to each one of these notes or which which note belongs to which person so let's come in here and edit the demo_pad_note template which has got the note macro in it and then i think i'm just going to put a username and maybe even an avatar into that header section there so we can upload this a bit later but let's just have an <xf:username> and the username this again is obviously a bit of XF template syntax magic which is going to create me a linked username with various other bits and pieces the user it's expecting will be the user relation for the current note because we're currently in a for each outside of here and we're passing in the note so it's going to be $note.User (uppercase 'U') because that's the name of the relation and then the link is going to be a link to that user's member profile page so that's just members and then we pass in the $note.User as the parameter for that and what we should expect to see now is just a link to that user like that so that's all good now obviously at this point if you recall the logic the note controller is currently limiting this to only show data that belongs to the current visitor so we need to change that round but before we do that we need to check that we have permission to view notes at all so let's do that first so it looks a bit like this if not the \XF::visitor()->hasPermission and then we need to say which permission we're checking and if we go back to the control panel here the permission we're checking is view own notes so it's notes view own so the permission group is notes and the promotion itself is view own and if you do not have permission to view your own notes then we're essentially going to say you can't go any further so we'll return out of that with this and a special kind of of error called noPermission() and then normally you'd return a phrase as the parameter here but we're not doing phrasing yet we're just throwing a demo together so we'll just say you can not view notes with a particular test message there so if i were to come back here and refresh that page i'm going to expect to get that error message and that is of course because we haven't actually given those permissions to anyone so let's come back to the groups and permissions page and go and assign some permissions to the registered user group so the registered user group is going to need to be able to view its own notes and we'll say that they need to view other notes in a moment but we'll just tick those three for now and save that and we'll come back to that in a moment so now we should get back to roughly where we were before so the next step is going to be to limit those who can't view everyone's notes to be able to only view their own which is a slight change around from what we've got here but essentially we want this parameter or this this where condition to be conditional upon whether or not they have that permission so let's just pop that into the clipboard and we'll check it afterwards because obviously the $noteFinder is being built up in a sort of chained fashion so we can we don't have to do everything with it in that one string we can do it afterwards so let's check the permission if not \XF::visitor() and this time it's notes and i believe it was view any and i have faith that it was but it'll all blow up if it wasn't if that's not the case then i don't want to return a no permission error i just want to constrain the note finder slightly so let's say note finder and then hopefully my clipboard is still available there we go so the logic now says set up the note finder for demo_pad_note and order it in ascending order then let's i'm actually going to change that back to descending order so that the newest stuff appears on the front page and then if the visitor doesn't have the view any permission on the notes group then add an additional constraint to the note finder to say only show me my own notes let's take a look and see because right now if you recall we haven't actually applied that permission so we shouldn't see any differences at this point except for the fact that the order will be reversed so let's go and see the debug info here and you can see under here that i've got a query on the notes table here it is so SELECT demo_pad _note LEFT JOIN xf_user WHERE the demo_pad_note user_id is one so that's this is the result of the finder query so now let's go and apply that permission to my user group hit save on that and now when i come back here i'll refresh this rather than going and look at the actually the azure interface and you can see that that where clause has disappeared so now it's just showing me everything from the demo pad note table with no constraints although obviously there is a constraint by virtue of it being split into pages so show me the first eight posts with no other constraints if i head back into here and let that refresh then i should now see the posts from Mr Spammer and Trolletta which are more recent than my own so that's the first permission up and running correctly let's improve the visuals a little by adding an avatar next to the username here that's really very straightforward we just come back to the template and right next to the where we use the <xf:username> there we'll use an <xf:avatar> and this time we'll pass the $note .User again and we'll select a small avatar and then i happen to know that the pad note header class is display flex so we should be able to just wrap the next bit into a single div and then maybe wrap that one with a div as well and then we'll get something that looks a little more pleasing having implemented the view permissions we now need to go and implement the edit permissions because as you recall this code was built with the anticipation that you would have one user the visitor being able to view and edit their own notes but with no access to anyone else's so the code currently was not going to stop me who's logged in as add this admin user from editing and saving this notes here so we need to control that with permissions now instead of doing all of my permission checks in the controller like i did for the view action here i'm actually going to move my permission checking into the entity and i may even fix that up in a moment to change things around but why would i do that well the entity contains all sorts of information about itself so it makes sense that it would be the final controlling structure to say whether or not a particular user or user group has the ability to do something to it so let's start off with a quick can edit function so we'll have a public function called can edit and then i'm going to pass it by reference a parameter called error so that i can not only return true or false for can you edit or can't you but i can also assign some sort of error code or string to that variable i've passed by reference and then the calling code would be able to reference that so whose permissions are we going to be checking well we're going to be checking the visitor so let's assign him to a variable and then let's do the actual checking so if the visitor has not vista visit if the visitor has permission and we're checking the notes edit own permission this time if the visitor does not have that permission then the error string will be you may not edit this note that'll do and we'll return false but recall this is not quite correct because this is going to return false if you can't edit your own note regardless of who is the owner so we actually need to add more conditions to this this if here so what we're actually going to say is if the visit user id is the same as the user id aside to the entity and you don't have that permission then you get that error but that's still not the entire story because we've got another permission for edit we've got edit any so this time it's going to be let's reorganize this slightly we'll take that out and we'll say if you don't have the permission then through the error and otherwise we'll just fall through and then we'll have an else condition here and the else being that the visitor user id is is not the same as the entity user id and in that case we'll say if not visitor has permission or notes to edit any then the error is you may not edit this note either i guess we'll just standardize on that for now and we'll return false there and i guess if you've got past all of those checks then you must be able to edit so we'll return true so now we can head back to the controller and just call on canedit and then throw errors if not so back to the controller and we'll look into action edit and so the first thing we'll say is that we'll get the note but then we'll insert our permission check so if not note can edit and then we've got our reference there then return this we're doing no no permissioner can't we no permission and then we'll pass the error that we got out of the entity as the option and we're done now we're also going to need to put this exact same thing into the action save in the case that it's an update so that's here so returning false if we can't edit the note that we got there and then i guess we need to do the same sort of thing for deletion so we'll come back to the entity note and we'll take the whole of action oh sorry of can edit and we'll make can delete and then we just need to say delete own you may not delete this note notes delete any you may not delete this note and then we can come into here and just stick in the check like this so just checking the logic we're going to get the same bit of code whether or not we're looking for the confirmation or the actual action to do the deletion so we're saying if you can delete it then if you can't delete it then show no permission either way and this will handle whether you're trying to delete someone else's or your own so that's all handled through the entity i'm back in the entity here and having said that i might move the permission checks from the index view from here in the controller into the entity that actually makes no sense at all because the entity is concerned with how to deal with one particular item and this is all about which ones to fetch so we don't deal with an entity at that point so disregard that that was nonsense that said i have implemented a can view action in the entity which allows the controller to quickly check whether or not you should be able to view one particular item there's just one final permission to implement from the selection that we built earlier and that is the permission to create new notes so we're just going to do this in the controller because at this point we don't have the ability to query a particular entity we're just creating a skeleton here so we'll do a standard permission check if not the \XF::visitor()->hasPermission for notes i think we called it post but let's just go and check notes post yeah that's the one if you don't have the notes post permission then return this no permission you may not post new notes and then we'll do the same in the first part of the action save where we're looking to actually do the savings so this is the editing action and this is the creation action so if you can't do that then oh what happened there let's come back and have that let's have that let we could actually put it before we even create the entity here so let's do the same in the save action so if you don't have the post permission then don't even get to that point so now that we've actually made use of those permissions we need to prepare for exporting our add-on and to make sure that we don't run into that issue we found earlier on where we've implemented the permissions but we haven't actually awarded them to any user groups and in order to do that we're going to have to add some code to our setup script so these are the permissions that we need to set up and we need to make a note of all of those and then go to the setup script and award them to the appropriate user groups as part of the setup process for this add-on so let's just move that out of the way so that we can keep a reference there and then bring up our setup.php and let's add a new public function to be installStep2(), no parameters required and then we're going to use a function called applyGlobalPermission() it looks like this this applyGlobalPermission and you can see that it's suggested for me there and what do we want to apply well the first argument is the apply group id now all of the permissions that we were working on just now were set to the permission group of notes and then let's go through them one by one let's start off with view own and then let's have it depend upon the user group that we're targeting having some other permission i think a reasonable permission to base this off would be the ability to view the forum at all so let's scroll up to the top and we can see that that is general view so let's add that we are looking for just to remind ourselves first of all the group we depend on and then the permission id so it's going to be general and view so what this will do is any group that has permission to view the forum as defined by the view permission in the general group will be awarded the view owned permission in the notes group so let's go ahead and do the rest for these other permissions let's just close that up so we've got some room here and we want view others notes post new notes edit notes and delete own so i'm just going to duplicate that a bunch of times view any post edit own and delete own so now just need to find out which permissions to build those from so i think view any is going to be something like general permissions or forum permissions view of threads by others so let's do forum view others on that one and then post new let's post new thread so that's forum and post thread edit own edit own post a forum edit and post and delete own we can have that as forum delete and post then we can go through and do the same for the moderator permissions here and boast those off groups that also have those moderator permissions and essentially that's our work done once we export this plugin anytime the install script is run then these permissions will be applied to any group that matches the criteria we've set up and you're good to go
2021-05-25 07:21