 All right, I don't quite know what happened there, but the recording stopped for some reason. Usually I have to hit record. Maybe it started by itself. I'm not too sure of myself, but welcome everybody. I hope you are well today on this Thursday. So today we're gonna be chatting about common WordPress API, specifically the dashboard widgets API. But while you are joining, please do let us know in the chat where you are joining us from while I do my introduction. So for those of you who don't know me, my name is Jonathan. I live in Cape Town in South Africa. It is a small country right at the bottom of Africa and the city is right at the bottom of South Africa. I am currently a developer educator at Automatic and I'm spotted to work full-time with the training team. That is the team within the WordPress community that works on learn.wordpress.org. We run these workshops, we create tutorials, we create lesson plans for teachers and educators who wanna teach WordPress, and then we create courses as well. So we have, I think it's Mark from Issaquah. I think that's right, your name is Mark, I think. We have a home folder from, I'm gonna try to pronounce this, Lena P. Hoking, now known as Bergen County, New Jersey, USA. Tracy, welcome. Tracy's been a co-host of mine a few times, so I definitely recognize that name. Who else do we have with us today? Seeing some new faces, I'm seeing some old faces, it's excellent. All right, I've got Ben from Austin, Texas. Ben, I'm led to believe that Texas has the best barbecue. Let me know if I'm correct on that one or not. I've never tasted American barbecue before. I've actually never been to the States before, so Ben says that I'm correct. John says, from Chicago, welcome, John. I do hope to travel to the US, to the Southern United States and try barbecue before because I have seen barbecue on many TV shows. I think it's barbecue masters as one of them, and a few others, and I'm hoping to try American. Anatoly from Ukraine, welcome. If you saw my screen go dark and then light again, it's because of the power I went off, and my light took a while to get in, so that seems to have switched on again now, so I at least have some light. Jean, welcome from Lake Lenape, Sussex County, Kunal from India, Sharon from Galveston, Texas, and Sharon agrees Texas has the best barbecue. It sounds like I need to go to Texas. All right, if you're still joining, you're welcome to let us know where you're from, otherwise I'm going to move on. As I mentioned before, we're talking about dashboard widgets today, so hopefully you're here to learn about those. If you're not, you're welcome to hang around, but that's what our focus is going to be today. Before we get started, a few announcements. Just again, welcome to everybody, and I wanna say thank you to Assad, who's co-hosting with me here today. Assad has chosen to keep his camera off, and I respect that, but I do thank him for co-hosting with me today. As you join, if you are not seeing my slides right now, please do let me know, or if at any point in time you can't see my slides or my screen, please do let me know, and I will re-enable the screen share. What I will also do, and I decided earlier today that I'm going to start doing this, is I'm going to start also sharing the link to the slides at the start of these sessions, because I think they're handy for folks to have if they haven't picked them up from the comments. I usually leave them in the comments of the meetup group, so I will copy the link and pop it into the chat. Thank you, Mark. Home folder says I'm actually on the lands of, I'm going to mess this up as well. Ramapo, Mansi, Lenape. I hope that is correct. Okay, so there are the slides. I've shared that with everybody. You're welcome to open them up if you would like to. All right. Then just a few minor admin things. We are presenting in focus mode, what this means is that you can see my video and possibly Assad's name on his video, but you can't see each other. And this is just to prevent any kind of Zoom bombing problems. We have had a few incidents in the past, so this is to protect yourselves from any kind of shenanigans. As always, welcome to ask questions. You're welcome to either post your questions in the chat or unmute to ask questions. The only thing that I do ask is if your question does not specifically pertain to something we're doing on screen at the time, please either wait for the moments where I leave for questions or the easier way to do it is just post it in the chat. And then I will make sure I scroll back and check for those questions. But questions are always welcome. Questions often lead to new workshop ideas. So please, I firmly believe that there are no silly or stupid questions, only unanswered ones. Then if you would like to code along with me, make sure you've got your local install ready. You don't need anything much more than just a local WordPress install and a text editor today. But we'll jump through the requirements in a second. As always, if I start speaking too fast, please do let me know. And I will slow down or if I haven't explained something, if you're not quite understanding something because I've rushed off like I tend to do, or if we're doing some code on screen and I'm rushing off and you need to catch up, please do let me know. Always happy to pause if need be and just show what's on screen. And then last but not least, we are post, we are recording this and sharing it to WordPress TV afterward. So if you need to jump out any time during the session, no matter it will be on WordPress TV tomorrow, I usually try and post it on the Friday after the Thursday. If you're looking for more WordPress related content, please do visit learn.wordpress.org. And then if you're looking for WordPress developer news and updates, please do consider visiting developer.wordpress.org forward slash news. I'm going to open up that link very quickly because there's a very specific type of article that regularly gets posted here that I want to just share with everybody. And it's very specific. This happened last time. It didn't load. And now it's going to do it to me again. Let me just try and type it out. Developer news. Dear you, dear you, me. It doesn't seem to be working. Anyway, once it loads, there's very specifically a what's new for developers blog post that usually gets posted along with every release. I'm not sure what's happening yet. Maybe it's the time. There we go. It's not working. Maybe it's the time of my workshops. But if you have a look through here, even if you search for what's new, there's usually a what's new for developers blog post. I see the most recent one seems to be in May. It's usually coincides with the WordPress releases, but it's a very good way of keeping up to date specifically with developer focused content within WordPress. So I highly recommend subscribing to the developer blog. I'm going to actually just pop that link into the chat if anybody wants to go and check that out. Okay. Thank you, Stuart for showing that. There was a June one. I don't know why it's not showing up here, but thanks for sharing that link, Stuart. Okay. Right. Our learning outcome today. We're very specifically going to be focusing on one thing, the dashboard widgets API. For reference, the common APIs in WordPress are probably going to be the topics of my workshops for the next couple of weeks, maybe even the next few months. There is, and I'll share it with you in a second. There is specifically a section on the developer documentation that talks about all the common APIs, and we're effectively just going to be going through them one by one. We're going to look at what is the dashboard widgets API and why it's useful, how to use it, and then specifically we're going to build a dashboard widget. So we're going to show you how to, I'm going to show you how to add one to your WordPress dashboard, how to add some controls for your widget. If your widget needs any kind of data to manipulate what is displayed. We're going to talk about widget callback arguments, and then we're going to talk about widget context and priority, which helps you define where your widget displays on your site, or at least on the WordPress site where your plugin is installed. In terms of requirements for today, if you want to code along with me, you will need a local WordPress install or some way to edit WordPress type files. It doesn't have to be local, but I just find it easier working locally. You can also code live if you want to. That's your choice. You will need some form of text editor. I will be using Visual Code Studio in this session. I'm having some trouble with my Visual Code Studio. My code beautifier extension suddenly broke. So we might have one or two backups there, but not a huge issue. And then some post data, because we're going to be working with displaying posts in the dashboard widget today. If you have a default WordPress install that you've maybe just installed clean, you might not have very many posts. By default, WordPress installs with just one post, hello world, and that's it. So the plugin that I use, if I ever need to generate content, is called FakerPress. And you can either download it from this URL, which I will copy and share in the chat, or you can just install it through your plugin screen. So I'm going to go through that process now very quickly so that if you want to follow up, so that if you want to follow along with me, you can do. This is a very handy plugin for development, for generating content. So I just basically go through, click on add new, and I type in the word FakerPress or one word. And it's usually the first result. There we go. There are no others with that name. And because it's one word, that's why I search for it all in one word. Install it, activate it, and then I just generate a bunch of posts or pages, depending on what I want to do. So while this is installing, I'm going to close down that tab and you watch. There you go. It's installed. I can now activate it. And it adds a top-level menu called FakerPress in your WordPress menu. And then in the FakerPress option, you have settings, comments, posts, terms, and uses. I just generally go to posts. And then what I do is I simply add how many posts I want to add. Today I'm going to add 10 posts, just so that we have a nice round number. I typically choose from the last year. So it starts at the beginning of this year and then I'll generally change the end date to the first of whatever month we're in. And the reason I do that is because if you choose a date after today's date, it'll create those posts as scheduled posts, not published posts. And I want published posts. Posts, we can leave as default. I usually disable comments just because it's quicker if I disable comments. And then I remove most of the HTML tags. So I usually leave H1 and H2. I take out the list. I leave a div, a paragraph. I take out block quote. If I want images, I leave images, but I don't want images today. So I'm going to take that out. I'm going to take out the HR. I'm going to take out the more. Because I don't want images, I'm also going to remove the default image providers. It just makes it faster because it doesn't have to go and fetch images. It's just generating content. I leave the rest default. And then I'm going to remove the taxonomy rules because I'm not worried about taxonomies. I'm not worried about metadata today. You can obviously leave that default if you want more complex posts to be generated. But for our purposes today, just a couple of text posts is perfectly fine because we're literally just going to be working with the titles. And then I hit Generate and it'll run and it'll, there you go. It's faked. It says at the bottom there, it's faked the 10 posts that we need. So that's all good. So if I pop on over to my fake 10 posts, yes, if I pop on over to my posts, I will see where the posts are. They're using Lorem Ipsum as the text, as text, I should I say. That's one thing I would like to consider contributing to this plugin is that it actually uses English text or a language. You could specify a language instead of using Lorem Ipsum. But anyway, that is what it is. All right. So I've got some test data now. So we've got everything we need. All right. Let's dive into dashboard widgets. So if you've never seen a dashboard widget before, it's, you probably have, you've just maybe never taken notice, but it's these little boxes of information that appear in the dashboard of WordPress. They're called dashboard widgets. Underneath the hood, they use the WordPress meta box functionality. They are essentially what's known as meta boxes, but they're specifically meta boxes that appear on the dashboard. And so therefore our dashboard widgets. Now, one good example that I always use of dashboard widgets, and I'm going to very quickly log into the admin interface of my personal site. I don't mind sharing it. There's nothing there that is super secretive and I don't need to log in, so you won't see my password. But some good examples of dashboard widgets that I have on my site, for example, I have my Jetpack stats. So I can see the stats of my posts and how many visitors are coming through and all of that. This was very useful when I was still freelancing, not too useful for me today. It also shows my top 10 posts and pages and top referrers and that kind of thing. Then I also have this dashboard widget, which is the stats for my podcast. Now my podcast is one that is not currently active. I'm hoping to activate it very soon, but it shows listener numbers over time. And I can see there was a nice big spike there on June the 26th, for some reason, possibly bots. I don't know. But over the rest of the time, it's fairly low key. And then I can see listeners today, listeners this week, and listeners last week. Then below that, and this is... So the Seriously Simple Stats dashboard widget is one that was created before I started working at Castos. It's a company I used to work for. We managed the Seriously Simple Podcasting plugin. One of the ones that we added was this Castos News widget. And this is basically just an RSS feed widget, which pulls in the blog posts from our company, Weblight. And we thought this was a cool way to kind of market our blog, get folks to come and read our blog. We could then create content for podcasters that podcasters might find useful and they would get it in their site. So it's another great way of how you can add functionality to your users' sites that is useful to them. And that's one of the cool things that dashboard widgets are useful for. So if you want to market your product, let's say you're selling a plugin, you're selling a theme, and you have a blog where you post content regularly, creating this kind of dashboard widget is a great way to do that. If you have some functionality that you want to quickly show to your user when they log into their WordPress site, a visual representation of some data, like this is very handy or like the stats is very handy. The other example is the WordPress News and Events widget. This is very cool because I can actually tell it that I am in Durbanville, which is the suburb of Cape Town where I live in. And then based on my location, it'll actually tell me what events are happening near me. And I can see what meetups are happening, what wordcamps are happening. And so next week or the week after, it actually says to me, there are no events scheduled near Durbanville. Do I want to organize one? And it gets me to go and sign up to be a meetup organizer. So that's another way that you can use dashboard widgets to create benefit for your users of your products or if you want to do things like security stats, I know the security plugins do them. There's a lot of cool things that you can do with your dashboard widgets. Okay, I'm going to pause for a second there and check if there are any questions at this point in time. Otherwise, we're going to move on and actually start building our first dashboard widget. All right, so we don't seem to have any questions. I'm hopefully everybody's here to build, if not necessarily listen to me waffle. So if you go to, let me use this tab over here. If you go to developer.wordpress.org, this is a great place to start development because this is the developer resources page. There are a few main headings, a few main sections. The first one is the code reference. The second one is all about coding standards. The third one is about the block editor and the fourth one is about the common APIs. So this is the world that we're going to be living in today. And you'll see that there are a number of common APIs. I haven't counted them yet, but there's quite a few down the left hand side of my page here. The first one is responsive images, which I decided not to do this week or reasons I don't know. I just picked one randomly. The second one is the dashboard widgets API. So that's what we're going to be working with. So this is the documentation we're working off today. I'm going to be covering some of the content in this documentation, not all of it, hopefully enough to get you excited about playing with dashboard widgets. And hopefully you'll then go and read up about how to do more with them. But you'll see that it was something that was added in WordPress version 2.7. And the API makes it simple to add new widgets to the dashboard. Now, something to remember when we talk about APIs, the REST API is just one type of API. It's not the only API that exists. An API is an application programming interface. It is any way that you can interact with a certain piece of functionality. So when we talk about APIs in WordPress, we talk about ways to interact with something that is made easier for us. Adding a dashboard widget, as I mentioned earlier, uses the meta box functionality under the hood. And so the dashboard widgets API kind of builds on top of the meta box functionality, exposes an action and a couple of functions that you can use to very quickly and easily build dashboard widgets as opposed to doing it the hard way, which is building them yourself from scratch. The main function that you're going to be using to add any dashboard widgets API is very simply the wpadd dashboard widget function. So the name kind of explains what it does. And it has a few parameters. The parameters are the widget ID, which you'll see here is a string. So it doesn't mean the ID of the widget in the database as an integer. It is the identifier of the widget. And you will also see that it is used. The ID that you specify in the code is also used for the ID attribute when the widget is generated. So you can use that to style the widget if you want to or apply certain functionality to it using JavaScript or anything like that. The next one is the widget name. And so this will be the name that is displayed in the widget header over here. After that, we have the callback function. So the callback function is what allows you to generate the widget content. And you'll see that the function should always echo its output, not return, all right. Then there is a control callback. This is a function that allows you to manage the controls for your widget, which we'll dive into in a second. And then the third parameter are the callback arguments. You'll see here they specify, they shorten arguments to args. And these arguments that you can pass to your widget callback function to add some kind of functionality to the output. Then the last two are the context and the priority. And you'll see that, sorry, I've got my little sidebar popping up. I'm going to just close it. You'll see that context and priority has to do with where the box is displayed on the dashboard. So you've got normal, side, column three or column four. So those are the three positions across. And priority, you've got core, high, default, or lower. And that determines in the column where it's going to display on the dashboard. All right. So we're going to start by bolding a widget first, just a very simple widget that's going to get some posts. And then we're going to look at how the controls work. And then we're going to look at the arguments, the context, and the priority. In order to add a dashboard widget, you need to be able to use the function that we've spoken about there. Sorry, I'm just hiding some controls here. But then you also need to hook that function into the WP dashboard setup action hook. So this is like any other action hook in WordPress. It allows you to add specific functionality at a specific point in time in code execution. If you call the WP add dashboard widget function anywhere else, it'll probably give you a doing it wrong error. So you should always hook into the WP dashboard setup action and then hook your custom function into that. And inside of your custom function, then you call WP add dashboard widget to create your widgets. So let me show you what that looks like. If you want to code along with me, now's the time to get your code editor to open. Here is my Visual Code Studio. I've opened it up already to the LearnPress site that I'm using. And inside of the site, I'm just going to open up the WP content folder, which is where the plugins themes and uploads should exist. I'm going to open up the plugins folder. And inside of the plugins folder, I'm going to create a new folder. And I'm going to call it WP learn dashboard widgets. If you have followed any of my workshops, you'll know my naming conventions are very plain and fairly descriptive of what we're doing. And then inside of that, I'm going to create a new PHP file, WP-learn-dashboard widgets, exactly the same name as the parent folder. So this is going to be my empty plugin where I'm going to write all my plugin code. To start this plugin, I need to specify the open PHP tags, which is the smaller than sign question mark PHP or underscore. That allows me to start writing PHP code. Then for an active plugin, I need to start the plugin header, which is a forward slash and two asterisks. And then the closing is an asterisk and a forward slash right next to each other. And then you can have different lines with an asterisk like that. You can have your asterisks without spaces there, but I just think it looks a bit cleaner with one space on either side. So this is what your code block looks like. I will share this code with you in the chat if you need to check yours. Then the first thing you will need is a plugin name. My co-pilot is suggesting a plugin name for me, WP-learn-dashboard widgets, which I'm happy to accept. That is the minimum you need for a plugin to work, but what I like to do is I like to also add a description just so I can visually see in the plugin list what this plugin is doing. And for mine, I'm going to call it a plugin to add a widget to the dashboard. That's all I need. It doesn't need to be more descriptive than that today. And if I've done all of that, let me pop this code into the chat if anybody wants to grab it. And I refresh my... That's the wrong window. There we go. And I refresh my plugins list. I should now see my learn dashboard widgets plugin in the list, and I should be able to activate it. I'm not going to do that just yet. I know at least now it's there and it should work. I'm not going to activate it just yet. All right. As I was saying earlier, what we then need to do is we need to use the WP dashboard setup hook. So let's start with that. I like to register my hook callbacks early on and then have the callback function below. So WP dashboard setup. And I'm just going to call it WP learn dashboard widget callback. Actually, I saw your question. I'll answer that in a second. So there is my action that I'm hooking into and there is the function that is going to be called when that action hook runs. Any other functions that are hooked into that callback will run at the same time. So now I need to create the function. There it is WP learn callback. And there is my function code sitting ready to rock and roll. I'm going to copy this out into the chat for anybody who wants that while I answer. Actually, I have GitHub co-pilot installed on my Visual Code Studio installation. It is a GitHub plugin that is based on an LLM. And it reads, as I understand it, it reads any kind of public open source code and then makes suggestions for you when you're coding yourself. I use it for example code because it works really, really well for example code. I do suggest if you are a developer and you're writing code every day, looking into it as an option. My only recommendation is to not always trust the code because sometimes, and I've seen this a few times, sometimes it gives you very bad code or very insecure code. So use it to kind of scaffold code and come up with code ideas but make sure that you check what you're doing or you know that what you're doing works and you've tested it and you've made sure it's secure. Okay, Stephen says, how can I define with user roles can see and interact with it or the connected plugin? Sorry, Stephen or Stefan, I don't know if I've gotten that correct with user role. Do you mean so how can you define whether or not a user can see the widget when they log into the dashboard? Is that your question? Okay, excellent. So I don't know the answer to that but let's see if we can work it out together while we're building this. How about that? So I'm not going to answer it just yet but that's a great little mad experiment that we can try later. I think I know how it would work but I don't want to dive into it just yet. Let's build the first widget and then after we've built the widget then we can have a look at whether what I think is going to work will work if that's okay with you. All right. And so now I can just basically in this callback I can use the WP add dashboard widget function to register my dashboard widget. And as I said earlier, it has those four parameters. I'm going to come back to this in a second. Here we go. So the parameters are the widget ID, the widget name, the callback. Those are the three. Sorry. Those are the three required parameters that I must pass for it to generate some kind of widget. So ID name and then some kind of callback and the callback will then determine the output. So if I go back to the code I'm going to pop this all on one line here. Why is this giving me WP add dashboard widget? Why is it giving me an error? There we go. Okay. That seems fine. So the first one is the ID. You'll see that Github Copilot is recommending some suggestions for me. So I'm just going to accept them. So the first one is the ID. This one has gone with WP learn dashboard widget ID. That's very, very long. It could even be as short as widget ID. That wouldn't make sense because widget ID might get used over and over again. We might want to just say WP learn widget ID. We might want to do that. That's also fine. To note, it will be used in the front end. So we need to make it fairly descriptive. In our case, the title is anything that you want to show in that header. So in this case, deleting it as WP learn dashboard widget is perfectly fine. And then we'll have a callback function specified. So I like to actually specify my callback functions as callback functions, or actually I just leave it shorter than I call it callback. That's just a personal preference. It doesn't have to be that way for your environment. What I do recommend though is if you are working with a procedural plugin, like I'm doing here, not using classes, to make sure that you prefix it with something unique, which I'm doing with WP underscore learn over here. All right. So that's going to add our widget, but we need to also specify some content inside of the callback function. So that's this one that we're specifying here. So we actually need to create that function now. So we will say WP learn dashboard callback. That's exactly the same content as there. And then for now, we're just going to echo out that content. Seeing an error here for some reason. I wonder why it's giving me... Oh, that's why. Because these are all the same. So let's do it this way. I should probably change this one to dashboard widget. Actually, what I'm rather going to do is I'm going to change this one to dashboard content callback because this is the content of this widget. Obviously, the more widgets you need to register, the more complex things start getting, you might want to switch to a class-based solution for that, or you might want to change your naming. But for the purposes today, this will be fine. And then we're just going to echo out this paragraph with WP learn dashboard widget. Let me share this code with you in the chat so that you can grab it in case you need it. So let's run through again. So it's creating the initial function which will be hooked into the dashboard set of ActionHook. That then calls the WP add dashboard widget, which will create the widget with the ID, the title, and its content callback. And then the content callback manages the actual content of the widget. So any future changes we make will most likely be with the content callback. So let's have a look at what this looks like in the dashboard. So if I go over to my plugins and I activate this, I did see the question there, so I will get to it in a second. So I've activated this. I didn't see any errors, so that's good. And if I now switch on over to the dashboard, you will see that if I scroll right down to the bottom, there it is, WP learn dashboard widget with the content that we specified. So there it is over there. Notice that it added it to the first column right at the bottom. That's because the context is defaults to normal, which is the first column. And priority defaults to core, which means loaded after any other core widgets have been loaded in that column. And because this is a default WordPress install, these are all core widgets, so it's loading it at the end of those core widgets. Okay. Mark says what would a class-based solution look like? So it depends on how you want to do it. I don't know if we're going to have a lot of time to dive into what that might look like, but essentially what you would have is you would have a class somewhere else that handles the callback functions and everything else. And then you would either in your initialization class, you would hook into the action hook, and then you would, instead of just passing a simple function name that exists in this file, you would pass in an array, and the first parameter in the array would be the class that you're calling it from. So it would be like a new instance of that class. Let's call it Bob or whatever the case may be. And then you would call a function with inside Bob. So then you wouldn't have to use the WP learn prefix stuff. You could simply just call it something like widget callback inside of that class, and then things are namespace of the class. And then inside of a class file, you would have a widget callback function and it would handle the dashboard registration and the callbacks and everything else. I am planning on doing some object orientated, or let me try that again. Some object orientated programming for WordPress at a later stage, but it's not something that I'm doing right now. So if you want to kind of get an idea of what that might look like, there is quite a bit. There's actually a plugin template that I can sort of suggest that you look into this too. So I'm going to share these with you in a second. Share them with you now. The first is the WordPress boilerplate plugin template. I'm not going to find it now. WordPress boilerplate plugin. Let's find that very quickly. Here we go. I don't want to generate. I want the plugin itself. Here we go. So this will give you this. This is a sort of class-based object orientated base set up for a plugin. So kind of you'd have to dive through that and kind of see how that all works. The other one is one that a friend of mine wrote. It's called the WordPress plugin template. It also uses more of a class-based structure. So you can take a look at that and see how that works. Yeah, there we go. All right. Okay. So we've got our dashboard widget. It is there. It is useful. It does something. Whether that's something is usable or not is a different conversation, but it works. All right. Let's talk about the kind of things that you can do inside of your dashboard widget. And honestly, the truth of it is I'm going to just fix my code here because it's showing me all kinds of fun areas. The truth of it is, as you'll see here, I'm simply echoing some HTML. There are no real restrictions over what I can or can't do. So I can grab a list of posts from the current sites or grab a list of anything that the current user can do. I can display here. Now, getting back to someone's question earlier, I know Stefan's question earlier about how you can specify whether or not a user can see something. My understanding of it is, if you, in this point here, if you inside of your learn dashboard widget callback area, if you specify something like this, if you say if not, current user can manage options and then you, in this case, manage options as the administrator and you then return, then the rest of this code won't fire. So let's test this out and make sure this is indeed the case. So I'm currently logged in as the admin user. The admin user can manage options. That's why I can see the dashboard widget. So let's test that very quickly. Let me refresh here. And I can see the widget. Now let's create a editor user, for example. Let's go here and let's just create, let me just check which one is the next one. Yeah, editor is the next one after administrator. I'm going to keep it super simple and just call him editor. Editor at learnpress.test. He's going to be our editor user. And he doesn't need a website. I'm going to keep the password simple so that I can remember to log in. And then I'm going to add this user. Right, so there's the editor user. This user has been created. I'm now going to log out and I'm going to log in as the editor and use the password. And as you can see, the editor cannot see that widget. So it is, it is as simple as just in front, just before you add the widget. You can do some current user checks. You could also do something like if current user can manage options and something else or something else, depending on user roles, then show the dashboard widget. Otherwise don't. So it really is as simple as doing that, which I discovered now. I've never tried this before, but I did assume that would be the case. So hopefully that answers that question. Stefan says, okay, so it wasn't, so it wasn't a VS code extension specifically. The extension is the GitHub co-pilot extension, but it's actually just GitHub co-pilots. Or it's just a general service that GitHub offers. You can install it into most editing platforms. I use it in VS code and PHP storm. There is, I think it's $10 a month. And then you install the plugin for VS code and connect your account and you're off to the races. Karthik says, how are you using the test extension? Is that your local setup? Yes, it is my local setup. I don't mind sharing this, but it is a little bit out there. I have created my own, I'm a weird person like that. I've created my own local development environment. It's called WP local EMV. It runs on something called multi-pass, which is a canonical product, canonical of the folks behind Ubuntu. And basically it's a very quick and easy way to set up an Ubuntu virtual server on your local machine. It runs on Mac OS, Windows and Linux. I started using it when I was still working on Ubuntu full-time. When I switched to Mac, it was perfect to bring it over to Mac. I'm currently trying to get support for it on Windows as well. You might see that screen behind me there. That's a dual-boot-virus PC. And I'm trying to get Windows support working as well. But basically it just works out of the box. And it configures all my test sites with .test domains. So there we go. Jose says, I don't know how to pronounce that. .idm is free, uses as a code pilot. Absolutely, that's an option. Can you share the link? Yes, more than happy to. Bonus points if you develop on Windows or Codium. Okay, there we go. Bonus points if you work on Windows and you're able to help me get Windows support working. I do have a branch art that's called Windows support. And the problem I'm having, I'm taking up workshop time for this, but I'll just mention the problem I'm having is in Windows, you don't have a similar thing to Pseudo as you do in Mac OS and Linux. And you're either running as a user or in PowerShell or running as administrator. And I haven't quite figured that out yet. So if you're into a little bit of working with multi-pass and command-lining things and you feel interested in working on this, let me know. But this is what I use for my local environment. If you want to give it a try, please feel free. I'd love to get feedback if it still works. The last official release was in December 2022. So it might not work anymore. I don't know. I'm still using that install of my current setup. Okay, let's go back to our dashboard wishes. I'm glad we set an hour and a half today. We've got time to dive into things. Okay, so as I was saying earlier, it is, you can pretty much do anything. I'm sorry, John, I can't do that too. There is another question from Snowmish. Could you please answer that? Sure, let me just see. I think that was the link to my local Dev environment question, which I have shared. Nope, Snowmish asks, use WSO2. Oh, there. Sorry. Sorry. Yes, I see it. Yes, you could use WSO2 on Windows. The only reason I didn't use it to begin with was because I didn't like the way there's two things. I didn't like the WSO2 map the drives. That was a personal bug there. And there was something else that I didn't like, something to do with browsing to the directories. So that's why I ended up holding my own. That just, and the other thing, I wanted something that would just work across any platform. WSO2 is only Windows. I like the fact that MultiPos works across all platforms. So I'm trying to build something that you can just install it and use it the same way across all operating systems. That's a big goal for me personally. But yes, WSO2 is definitely an option. My preference is using Ubuntu as is. I primarily work on Ubuntu. I use a Mac now because of work. Okay. So let me get back to this. As I was saying, you can pretty much do anything in here. You can get a bunch of posts. You can create HTTP requests to get external data. But for our purposes, all I want to do is I want to generate some posts, or at least not generate some posts. I want to get some posts and I want to display them in the dashboard widget. And so to do that, there's this great function in WordPress called WPGetRecentPosts. It allows you to... I saw that question there just now. I'll get back to that in a second. It allows you to get the more recent posts. So in other words, the ones that were published most recently. It requires the same kind of arguments as the getPost function does. But it's an easier way of doing like a date filter and get by most recent dates so we can use that. So this is kind of what the code could look like to set that up. I'm going to copy this out of my example code here. I will share this with you in the chat once we're done. So I'm going to specify some arguments. I'm actually going to specify these as post arguments for now and you'll see why in a second. In the post arguments array, I'm going to specify how many posts I want to get. I want to get five. And then I want to make sure I get only published posts. And then I'm going to basically call WPGetRecentPosts. I'm going to pass in those post arguments. And that will give me a list of all recent posts. Then I can simply loop through that array and echo and for our purposes, I just want to echo the post title in a clickable link. So I'm going to again just copy paste this code into the chat and I will share it with you in a second. But essentially what I'm going to do these now is I'm going to echo the start of an unordered list. Loop through the recent posts to get a single post item. I prefer to call this post or recent post just because it's a little bit clearer. So I'm going to change this to recent post. And then I'm going to echo the list item and anchor tag with a link to the post using the getPermalink function. Then the recent post post title close off the anchor tag and close off the list item. So that's what we're doing over there. So let me copy all of this code out into the chat if you'd like it. There we go. There is the comment there. I just said I will get back to your question. I'm just going to finish up what we're busy with here. And with this now enabled, if we refresh our dashboard, we should see, and I'm going to need to log out as the editor user, because the editor user cannot see these things. So let's log out very quickly and let's log back in as my admin user. As you can see, my admin username is just a simple. I keep it very simple for local sites. And if we scroll down, there's our dashboard widget. And there are my most recent posts. So now I know this is working. It's getting the posts and it's popping them into my dashboard widget. Okay. I'm going to leave the code on screen if anybody wants to catch up with that while I answer Jose's question. Is it possible to have a dashboard widget full width? Now, by full width, do you mean across the whole dashboard? Is that what you're asking? Yes. Okay. The answer to that is I don't know. I never tried. They do, when you add your dashboard widgets, as I mentioned earlier, in that function, they do specify a context and a priority. So that just does determine where the dashboard widget goes. But ultimately, a dashboard widget is just some HTML. So I don't see why you couldn't, for example, create some... It all depends on how you see. This is inside of a normal sortables div. The position is relative. So you might be able to do something like, let me see if it'll make it wide here. Let's say 1000 pixels. Yes. You can do that, but it might break things. Actually, that's on the wrong thing. Let's go down to the actual div itself. There we go. There's the postbox header. So there's the dashboard widget. Okay. So I set that... So let me move it up here, rather. Let's see what happens if I specify 1000 pixels. So there you go. So I specified 1000 pixels specifically on the widget. So that's going to stretch it over all the columns. The downside to that is, if you have other widgets in your second column, it's going to overlay those widgets. So you're probably going to need to change the width of the column that you're putting it into first, and then change the width of your widget. So theoretically, I would say, yes, it probably is possible. You would just need to make sure that you test it so that it doesn't break other things. Let me show you what I mean. If I take this widget here and bring it into this column, yeah, you see, it's actually putting that my extended dashboard widget under the WordPress News and Events. So what I might then want to need to do is I might want to then go to the main div that's holding that and set this width accordingly. I'm just going to set it to 1,000 now. And again, it's giving you problems because there's obviously custom styling on the specific columns. Again, not impossible. But you will be messing with the default functionality of how widgets work, which may or may not upset your users. So theoretically possible, yes, whether I would do it or not, I don't know. You can order widgets and we'll get to widget ordering in a second, Sharon. So bear with us on that one. But yes, I always say when it comes to Web Dev, when it comes to HTML and CSS, pretty much anything is possible. You can change structure how you want. But yeah, you're welcome to. It's not something I want to dive into now, but you're welcome to share the hint. Not a problem at all. Okay, maybe we can keep that for the end of the workshop. There's some things that I do want to kind of dive through if that's okay with you. If there's time at the end, we can have a look at that because I want to make sure we finish the workshop. So keep that idea in mind and we'll get down to that in a second. Okay, all right. Let me close off here and I'll come back to this show action hooks in a second. All right. So the next thing I want to chat about is widget controls. So widget controls are a way of storing and using some custom options or data for your widgets specifically. So looking at this example, let's say we wanted to allow the user to control how many posts appeared in their widget. It would be really cool if there was a way that we could do that. And the control callback is how we would manage that. So in your initial widget registration, up over here, we have the content callback and then we can specify a control callback. So in this case, I'll just go with what GitHub is suggesting me, WP learn dashboard widget control callback. That's fine. And then we need to set up, sorry, jump down ahead here. We need to set up that function. So let's go function WP learn dashboard control widget callback. And what this does is if you, and I'm just going to go with, here is the control panel for now. If you do that, there is the code of what that looks like and we've added it to the widget callback over there. If you do that, what WordPress does is it enables a configuration option on the widget to move these widgets around a bit so we can kind of see what's going on. So you don't see it now, but if you hover over the title, you'll see there's a little configure button that pops up. You have to hover over the title for it to appear. That's kind of confused me when I was preparing for this because I wasn't aware of the control callback. I'd never used it before. And so I was refreshing my code, my page wondering why it wasn't showing up. But when you do that and you click on it, it then shows what is defined in the control callback. It also shows a save changes by default. So what this means is you can put a form field in here and I want to just show you what the save changes does. So let's inspect this here quickly. You'll see that it adds a form. There's the form there. It's a post. It adds a nonce. So it's handling security for us. It adds a WBHTP referrer. So when the form is posted, it checks that it's coming from the right place. There's the widget ID added as well. And then there is the submit button. And you'll see that the here is the control panel is added above all of that in the form. So we can now add form fields to this control. It'll render those fields. And when the form is submitted, we can work with those fields in the control callback. So let me show you what that means when I say that. So instead of just echoing, here is the control panel. I just want to back over here quickly. Yeah, that's fine. What we can do is we can do something like this. We can say we can maybe make this a label. And we can say enter number of posts to display. And then we can specify that we want to echo an input box. So if the input type, let's make just make a text for now. Text will leave value as is. We're going to want to give it a name. So I'll specify what that is in a second. And because it's input, we don't have to worry about the closing tag. We just need to close off the tag like that. And for the name, let's just call it WP Learn Dashboard Widgets Number of Posts. WP Dashboard Widgets Number of Posts. There we go. Okay. So let's see what that does when we render that dashboard widget. So if I go back to my dashboard, let me share that code before I forget. There we go. There's the callback. Where did I go? There we go. So now when I click on Configure, you will see that it displays that form that I added, that form field. So now I could put a number in here, and then I can capture that number in the post of the form. When the dashboard widget posts this form, it's going to post it back to the control callback function. So inside of that function, we're going to need to do something where we get that data and then do something with that data. So because we specified the input name as WP Learn Dashboard Widgets Number of Posts, the one thing that we could do is we could check has this been posted to the control callback up front. So over here, we could do something like this. We can say, and I'm just checking my notes here. That's why I'm looking off screen. We can say if is set, and we can use the post array. And we can say if the Learn Dashboard Widgets Number of Posts is set, then we might want to store that again somewhere. We might want to store that somewhere. Now, what you should also do is you should check, does it have a value? You should do some sanitization on that value in case somebody tries to submit some dodgy data. All of that stuff is in the plug-in security and common vulnerabilities tutorials and workshops I did a while back. But for now, I'm just going to leave it simple. I'm not going to worry about security checks or anything like that. So if this data is set, then I want to store that data into a variable, or at least I want to update that data somewhere into the database. And the best way to do that is to use the update option function. Now, the update option function, I'm going to share the documentation link with you. This is a function that allows you to store data in the WordPress options table. You can store single pieces of data. You can store arrays. But basically, it's a great way to store these kind of options. So what we can do is we can say if it is set, then update the option and let's call it wp-learn-dashboard-widget-number-of-posts. And I'm going to very badly, very naughtily, I'm just going to store the value as is for now. I don't recommend doing this in the real world. I recommend doing some form of sanitization. In fact, let me just actually do it now. So a good one to do would be to sanitize text. Where is text? I'm trying to go sanitize key and then I think it's just text. Let me just check that and make sure it's been a while since I've done these things. Sanitize text field. You would sanitize text something. So sanitize text field. And that'll just make sure that the data being stored is a little bit safer than just random raw content. Sorry. And now, come on, where's my cursor gone? There we go. So we'll just pop that in there. Sanitize text field. There we go. That at least now is a little bit safer. So there we go. So update options, sanitize text field. Pop in the post data. There we go. So what this will do is this will check was the number of posts value posted. If it was update that value to the database. Then it might be nice to actually receive that value and use it in our post arguments. So now we can say get the option if it exists. And we might want to also then pass a default value of let's say five. So that if the first time we run this dashboard widget, it doesn't have a value stored in the database, we want to at least use something. And then we can replace this value five there with this variable over here. So number of posts is five. So basically, if we ignore all of this, the first time this widget runs, it's going to check if there's a number of post option. If it's not, that's going to set that variable to five. And then it's going to pass that into the number of posts. If we then change it and we configure it and we go in and set it to something else, it'll store that to the database and then use that value when it's rendering the dashboard widget. So I will share this in the chat with you so that you can grab to see if you need it. So there we go. And let's test this. So let's refresh our dashboard. So now it should be five. So we should see, all right, let me cancel this. There we go. So there's the first five. So we expect that's happening what needs to happen. If we configure it and let's set it to say three and save those changes, it looks like we've got to plug in our code somewhere. It doesn't seem to be saving those changes. Maybe the value that I've passed is incorrect. So let's have a look. If I go back to my field, let's see if I've done something wrong. So there's the input type text name. I've done something wrong. WP learn dashboard, which is number of posts. Let's check in the code if I've done something wrong somewhere. If it's a post number of posts, this is the contact callback. So we should be seeing it there. So what I'm going to do here is I'm going to just quickly error log the post. If you have, if you joined me for my debugging WordPress, you should recognize this. And then I'm also going to make sure that I've got debugging on in WP config, which I don't think I have got on. So let's turn that on quickly. So live debugging folks, always fun. WP debug, display false, debug true and debug log true. All right. So that's that done. And so now I want to see what is coming through in the post, in the content callback. I might have done something wrong somewhere. Update option, number of posts, rigid number of posts. Okay. Let's have a look and see what's happening there. So let me close this. Let me set this now to seven, for example. I will save those changes. Let me check my debug log. Okay. There's nothing in that array. So I've definitely done something wrong somewhere. I wonder, let me just check my own code here. Name. Oh, hang on. No. If any name. If anybody can spot this error, if they're testing on the inside, let me know. But I'm not quite sure. Looks like it's not posting, which I'm not sure why that is. So let me just inspect this. Save changes, submit. This is a form. Don't be getting there. There's WP learn. Oh, number posts. That's why. All right. Wait, wait, wait, wait, wait. I'm seeing something interesting there. And I know it is number posts. There's number posts. WP learn dashboard number posts. Why am I not getting any post data? This is the content callback. See, I'm not getting any data in that array. That's interesting. I should be getting data in that array. This is one of those scenarios where it was working earlier, and now it's not, and I can't figure out why. Let's have a look here. Joseph said, maybe you have to set the format. That is very possible. So I didn't have to do that earlier. So as far as I know, posts to the current page, let me just see what's happening when I save the changes. Let me see. Let me inspect the network. So there we go. Refreshers, of course. Sorry, I'm trying to get my things where I can see things. Okay, so let's go back to elements. Let's go back to configure. That's definitely the form. See, what it's going to do is it's going to post to the current URL, and then it's supposed to, based on the widget ID and things, it's supposed to submit and then call the control callback. Oh, that's what I'm doing wrong. The control callback is what manages the post, not the content callback. That's what I'm doing wrong. So I'm doing this bit of code. I'm doing the right code. I'm doing it in the wrong place. So here we go over here. So it's supposed to happen. I think I actually said this earlier in the workshop. So I'm supposed to receive that post data in the control callback, not the content callback. The content callback all it should do is get the option and update accordingly. So that's why that was not working. So let's go back over here. Let's close down all of this and let's refresh the dashboard. So there's the dashboard there. So it's currently working on five. If we now change this to three, this should now change. And there we go. Three are displaying. I thought for a second I had lost my mind because it was definitely working earlier, but I was putting the code in the wrong place. So there we go. So the control callback is what manages all of that functionality. So echoing your content happens in the control callback and then receiving that content and saving it wherever you need to save it happens in that content. And when you believe that I'm looking at my notes now and I should have seen because it's in the control function that my notes are set. So there we go. Okay. All right. So that's how you can control. You can store data. You can do various things. You don't have to just do options. You can store data and post if you need to. The quick press, there's a quick press dashboard widget, the quick draft one. This is actually a form that just posts. So this form is in the content callback. And then when this is saved, it posts it to a form table somewhere. So you don't have to do your controls inside of the control area. You can have forms inside of your regular display, but you have that option as well if you want to enable it. Okay. Then very quickly, just to wrap up, let's look at how the arguments... Yes, I will share and that's actually very... I will share the whole PHP code once we're done. I'll share it in the chat once we wrap up. The other thing I wanted to chat very briefly about is using the callback arguments. So you can use the arguments to pass data to the callback function, the display callback, the content callback if you will. So for example here, when I add the dashboard widget, the next parameter is the callback arguments. This is an array and I could do something as simple as add some content to the callback option. And the reason you might do this is you might want to get that content from somewhere else and then pass it to the function or whatever the case may be. So here you could do something like... This is a list of your last X posts or whatever and then you might want to update that inside or whatever the case may be. Let's say your most recent posts. So this gets passed as arguments to the content callback. You need to specify the variable in the function, which is the argument that receives the arguments. The default in the documentation is to use the word args instead of the full word arguments. I prefer to use arguments. I prefer to use the full name. It just makes it easier for me to read. But you can just use args. And this is the reason why when I set up the post arguments, I prefaced it with the word post because then you don't get confused with these args and these args. Another way you could do it is you could call this widget args or widget arguments that's even more descriptive. I do recommend using descriptive names for your variable names. I also recommend separating number of posts and number of posts, but that's just a personal preference you don't have to. I think it's also a WordPress coding standard requirement. So now with the widget arguments, because I'm passing in this content, I can use this in the callback. The one thing to note in the documentation for WP ad dashboard widget, they say that the callback arguments data that should be set as the args property of the widget array, which is the second parameter passed to your callback. So that means there's something else that is passed to the callback first. And then the arguments, this array, is passed as the second parameter. And they don't, in the documentation, they don't say what's passed first. But fortunately, if we scroll down a bit, a number of years ago, let me find you quickly, not Casper's here, Baida or Bada or whoever that was two years ago, posted above doc states and then talks about the second parameter, highlights the second parameter. And then for anybody wondering what the first parameter is, it is the current screen object. So if this is used in the dashboard, which widgets will be, then that first parameter is just an empty value. And the reason for that is because, as I mentioned earlier, dashboard widgets use meta boxes, meta boxes have a screen object. The screen object is whatever screen you're currently on in the dashboard. And depending on what screen you're on, you can do different things. So when you're passing these arguments, this one caught me when I was preparing for this. You do need to set up the screen. You can call it something if you want, or just call it screen because it's a little bit more clear what it is. You do need to specify that first as the first one to be received. Because this is a dashboard widget, it will be empty anyway. And then you can pass the widget arguments as the second argument in that function. And then we can use this content. So for example, what I'm going to do is over here, I'm just going to echo out a header. We'll go with H3. That's fine by me. This is not 100% correct. It's just widget args and then recall the content array item because that's what we specified there. And then we close it with an H3 tag. So widget args content and this will give us the content that we specified. So let's have a look at that. I will copy this function out again. For those who want the update, I will do the whole code at the end. Not to worry, I haven't forgotten that. So if we go back to our dashboard, which is not that one, it's this one. And we refresh this. We don't see the content, which is weird. This is another thing where I've done something wrong. You watch. This should be working. Why is this not working? Widget args content. Widget args checks notes. This is definitely the content callback. Dear, dear, dear. I wonder if it's because you have to specify it as args. I don't think that's the reason, but let's try. Let's see if that is the reason. It might be the reason. It's not the reason. All right, let's go back and test. So arguments, let's see what we're getting in arguments here. It's definitely an array. So we'll error log this as well. No, we don't want a screen. We want args. Here's args. Okay. And I'm just going to empty out my debug log for now and let's refresh this args content, check my notes. Don't see any errors. So let's have a look. I'll go back to here. It's definitely parsing in content. Maybe I've spelled something wrong somewhere. Nope. So I'm definitely getting it, but it's not... Maybe it is displaying and it's just hidden somehow. Let's have a look here. Oh, so the h3 is displaying. So the h3 is displaying. Oh, wait, hang on. Did I? Did I? Oh, it is args content. Copilot was right. I was wrong. So it is args, args content. That's interesting. So if we make this back to widget args, that back to that. And that back to that. Let's see. That should now work. There we go. Okay. So I apologize, because I had that completely wrong. Looks like I made a mistake in my notes. So it is I need to get the arguments array from the widget arguments, and then I need to get the content from the arguments array and then it displays. Okay. Let me update that code, because the first one I pasted was incorrect. And then let's talk about the last two, which are the priority and the context. I'm going kind of fast. So if you do have questions, you're welcome to ask them, but I want to make sure that I get everybody's question and that question that you shared with us earlier I'm keen to dive into. So stop me if I'm running off too fast. If you have any questions, but I'm just going to continue. So we've got the context parameter of WPA dashboard widget. The options are normal side column three or column four. Very quickly, what that does, it's currently set to normal. And what I'm going to do is I'm going to move my dashboard around a bit. So you can actually see this happening. I'm going to close activity. Sorry, not close it. Close it there and move activity up to there and close that. And I close these two. So you can see what happens when I make these changes. So there's my dashboard widget. I'm going to refresh this. Everything is closed. That's great. So let's make some changes. So if I specify the first context as side, that's all I'm doing over there, specifying it as side and I refresh. You'll notice that it stays where it is. It hasn't really changed much. The other option that I have is I can change it from side to what was the other one. Where are we? Yeah, here we go. Normal side column three, column four, defaults to normal. Change, you decided to do much. Let's see what happens when we change it to column three. Column three. And if we, and I think this is actually not going to change because I think I moved this around. Yes, I moved it around. So it's not actually going to change anything. So let's take this all back a step. I'm going to activate this on a brand new site very quickly. And this is actually a good, I was going to chat about this later, but we can chat about it now. Once a user moves your dashboard widget around, it's going to save that location. So when you are changing where the dashboard is being displayed in your initial code, you're only changing it for the first time that dashboard widget loads, or if the user doesn't move it around. So let me, this is a different site that I'm going to open up very quickly. I'm going to just grab all of this code from this site and open up that site and show you how that looks. So let's go and open up my WordPress site. Where is it? There it is. And I'm going to close the demo plugin. And I'm just going to pop it into a single file. You can do this as well if you want to. There we go. I'm going to pop all that code in there. Okay. So this is now a clean code, clean plugin, clean sites. I'm going to install and activate this plugin. So there it is activated. And just because I want to show you what it does, so there we go. So now the normal has put it in that location. Well, sorry, the normal. It wasn't normal, it was column three. So let's go back to normal. So we see what normal does. So normal pops it in first column at the bottom. Side, second column. And then column three, third column, column four, fourth column. We saw column three a while ago. So let's do column three, column four now, column four. There we go. And that pops it into column four. But as I said, that's only if the user doesn't then move it around. The minute the user moves it, that gets overwritten database. And then that is the value that stays like that. So basically once it's installed, the default functionality is to place it in one of those three places. So normal, side, I don't know whether they probably went normal inside because there were only two columns to begin with. And then over time, they've added additional columns. So then they went column three, column four, and that's how that works. The other one is the priority options. And so you have, you have call, you have high, you have deep, and you have low. So let me show you how that looks if we move this back to normal, which will place it, okay, not that one, but that one. That'll place it back on the left-hand side. There's more columns in that, in that, sorry, there's more widgets in that column. So we said our options were call, default, high and low. So if you switch it to, if you switch it to default, that's kind of the same as call. It'll basically load it after any other default widget in the dashboard. In this case, it just leaves it where it is because there are no other defaults loaded there. But you can also use high, which will place it ideally above your call and your default. But generally, if any other widgets have been registered as high as well, the thing on the order, it'll load it in that order. So if you have a plugin that fires before yours, it'll be below that plugin. And then obviously low will place it at the bottom of the list, along with all the other priorities of low. In this case, it works out to be the same as normal or call. There is a way, and we're not going to dive into that today, but there is a way, if you have a look at the dashboard widgets API page, where you can actually force your widget to the top. There's a whole section on this, and I'm going to share this with you in the chat. But basically what it does is it allows you to hook into a function that will try to put your widget before the default ones. It does this by manually altering the internal array of meta boxes of which dashboard widgets is one type, and then puts in your widget at the top of the list so it shows first. So you basically hook into it. You hook into the, let me find the thing at the bottom here, you hook into the dashboard set up, you then get all the WP meta boxes, as I mentioned to you earlier, widgets are built on top of meta boxes, and then you go through and you re-sort them and you change things and you do some extra code. So it is possible. However, and this is purely my opinion, I'm not saying you should or shouldn't do this, if you do go to all that trouble and then your user moves it anyway, then all of that code is for naught. So it's cool if you really want that to show as like your first widget, the first time the user installs the plugin and starts using it, but the minute they move it out, it's probably going to affect that because it's going to save it some way. You can probably write code to make sure it's always on top, but in my opinion, it's a lot of code to run just to make sure it's on top every time. And if the user, some users might find it annoying, but it's always on top. They might want it at the bottom some way. So you're kind of, again, you're affecting sort of WordPress call functionality, which some folks don't mind, but some folks do have a problem with. Okay, so that's how you can change some things around, change where your widgets load, where they fire when the dashboard loads. All right, let me go back here and I'm going to take out these two and leave this like this. And I'm going to copy this entire bit of code. What I'm going to do is I'm going to stick it into a GitHub gist, and then I'm going to share that just with you all in the chat. I will also look at adding this to my, let me just say here, WP learn dashboard widgets, HP. I will look at adding this as a comment once the video is uploaded to WordPress TV. But there is the final code for today's example plugin. Okay, and it's only says for function will be logic and count by default. Maybe echo the name of posts. Yes, you could, you could do that as well. If you wanted to, you could, you could echo the number of posts by default. If you wanted to pass that to your call back and echo it out, those kinds of things, you would basically get the number of posts and then use it in the field. That's another thing you can do. There's many, this is the thing. These workshops are very much like an introduction to these things and then how you decide to use them is entirely up to you. But yes, there's no reason why you couldn't give it a default value of the current stored number of posts. So the user knows what that number of posts is. That's definitely an option. Okay, getting back to, I did see there was a question from Sharon about the GPL. I'm going to get back to that. Getting back to Jose's question, he mentioned a specific plugin. I'm trying to scroll back to find that. Sorry, I'm finding the shirt. The plugin name is short actions. Show action hooks, okay. I'm guessing there's a specific hook that he wants us to hook into. And then we can use that to change things, which is one of the wonderful things about WordPress is you can do pretty much anything. So you say show action hooks. I'm guessing is what it is. Any specific one in this list? Is it show actions hooks or show action hooks? There is no hook. Okay, never mind. There probably is a way that you can do it. What we must remember with WordPress is just PHP code is just running. There are many ways. You could do it in jQuery. You could do it in CSS. There's many ways we could do it. The purposes of these workshops is to hopefully introduce you to some of these ideas and then hopefully you go off and explore and find out weird and wonderful ways to make your things work. So yes, there are many ways that this can be done. Okay, I want to just make sure I've gotten to all of the questions. There was a question about the GPL that I want to scroll back to very quickly. Yes, Sharon asked this. Yes. So Sharon said, here we go. No, it wasn't there. Do we need to include the GPL info in the plugin description? This was your question. Oh, that was Sharon's question. Yes. Okay, so yes, you should buy default. And let's actually grab an example plugin. So let's go to WordPress.org slash plugins. And I'm going to go to my favorite plugin, which is seriously simple podcasting because it's the plugin that I've spent the most time working on. I was paid to maintain it and it was great fun maintaining that plugin. If you want to host a podcast on WordPress, I highly recommend checking it out. I'm not paid to say that. It's just my favorite plugin for doing it. If you have a look, actually, let me go here to development. Let me go to... It'll be easier if we do the GitHub and then you find it here quickly. There we go. No, I don't want to go straight to that. The general way to do this, Sharon, is in your... There's two ways you can do it. You can either include the license file, which then just includes the GPL license in that file in the root of the plugin. That's the one way. Or what some folks do is they include it in the main plugin. So they will include a link in here somewhere to say that that is the license. So that's the other way that you can do it. And then the third way is in read me, folks include the license URL there. This is more specific to installing it on the... Or at least uploading it to the plugin's repo, but that's another way that you can do it. So as long as you've got either the link to the GPL license somewhere, or you've got the license file bundled with your plugin, the GNU public license, this one version two, then you are good to go. Okay. And that's how he says, please try and open, understand what I'm right. Yeah, that was about the... I think what you were trying to say was this. So let's go and do that. So what she was saying was it would be very cool if we could get that value in the config. Let me take this one over here. That's the content callback. So what you were saying was you would like to do something like this. So we could do it over here. We could get the number of posts and then actually echo this number art in this. So something like, no, not that one, that one. Have we done this right? No. Took a single quotes there one second folks. That's what I need. This is what I need. And then if we do that, what that'll do is that gets the number of posts from the options table and puts it into the value of the field. So that if we install this for the first time, it'll actually store that in, it'll show it in the config. So let's go back to the dashboard. Let's refresh. That's the wrong site. That's refresh. No, that's just the wrong site. Sorry folks, I went to a different site. If I click on configure there, then it has that value already pre-populated in the text field. So that gives your user feedback as to what it currently is and then they can change it according. So yes, we can definitely do that as well. Let's add that to the just why not. So we've got the final bits of code so that folks can see the whole project. Where did I put that note? Wasn't that one? Was this one going to edit this? Why is this secret? I need to have made it secret. I need to make it public. I don't know why it went secret by default. Let's make it public. There we go. Okay. I'll copy and paste this in. If folks want to grab that code. There we go. All right. So I hope that was interesting. I hope that was, hope that sparks some ideas the next time. Sorry, I just bashed my microphone. The next time you're looking for a way to show data to your users when they log into the dashboard, I would love to see more plugins specifically that provide functionality to users. I would love to see them using dashboard widgets because the dashboard could be very boring if it just has default ones installed. And I like plugins, especially plugins that I am using, that I need to check some quick dates on. Going back to mine, I really like the stats plugin for my podcast. So I can see what's been happening over the last week. I really like the news plugin for Castile. So not only because I built it, but because I like to keep up to date with what they're doing and then I see this information when I log into my dashboard. So the next time you're building a product or a plugin and you're thinking about how you can add some value to your users, consider building a dashboard widget. I think they're really, really cool. And you can go from there. All right. Are there any other questions? I feel like I need a sip of water. I feel like I've been talking, talking, talking. So I'm going to grab a sip of water if there are any other questions. Otherwise, we can wrap up for the day. One thing that I actually want to build for myself, which is interesting is I have this page on my site. I'm going to show it to you very quickly. It's kind of my personal random access memory. And it has some links to some common blog posts on my site that I use. And it has some words that I can never remember to spell correctly. I'd love to set that up as my own dashboard widget and I can log into my site and I can see it. So that's an idea. And some study says specifically did this mistakes to show that even senior developers know how to switch on WVD back. I didn't do it on purpose, but I'm glad it looked like I did it. I'm glad you enjoyed that. Great. Awesome. Great. Yes. Once again, thank you all so much for co-hosting with me today. You were an excellent co-host. I really appreciate that. It's so nice having a co-host joining for these sessions. Folks, I want to thank you for joining me here today. Next week, we will be continuing with another common APIs workshop. I think I've planned to do it on... I'm going to forget now. Let me have a look quickly at the common APIs and see if it can spark my memory as to what we're doing. I think next week I'm doing the database API or the file system API. One of those two I can't remember. But hopefully you enjoyed that. Yeah, and it totally says we need more. I would love to chat for hours and hours and hours, but unfortunately, we have a limited time for these workshops. But yes, please join me next week. If you want to dive into whatever I'm doing, I'll post it sometime tomorrow. Thank you for your time. It was lovely to meet the new folks and see some of the old folks. I will see you next week with one of the common APIs as a workshop. Thank you very much. Enjoy your Thursday and the rest of your week. Goodbye. Thank you, Jonathan. Thank you. No, I've got to remember how this thing works. I'm always for you too, this voice. Thank you very much. Very interested. But interesting in something more difficult solutions because on the practices, it's not necessary for client. Maybe if we show on widget something set information from outside for administrators. We can definitely do something like that. Yeah, we can definitely do something like that. So at the moment, my focus is very much on beginner level content. And once I've cleared, I'm actually happy to share this if folks are interested. I am going to, what I'm going to do is I'm just going to stop the recording quickly. Not because I don't want to chat about this on recording, but I don't think it makes sense to put it on WordPress TV. I'm just going to stop the recording.