From 42d5044d36b148457844a4fb4cebd80f3b3ebea3 Mon Sep 17 00:00:00 2001 From: QuackieMackie Date: Wed, 21 Jan 2026 23:37:05 +0000 Subject: [PATCH 01/10] Fix: `canFeatureUnfeature` method signature in documentation. This fixes the obvious error I noted https://github.com/xenforo-ltd/docs/issues/63 after installing the add-on. --- docs/devs/lets-build-an-add-on.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devs/lets-build-an-add-on.md b/docs/devs/lets-build-an-add-on.md index 1e2af14..5da35bd 100644 --- a/docs/devs/lets-build-an-add-on.md +++ b/docs/devs/lets-build-an-add-on.md @@ -758,7 +758,7 @@ namespace Demo\Portal\XF\Entity; class Thread extends XFCP_Thread { - public function canFeatureUnfeature() + public function canFeatureUnfeature(&$error = null): bool { return true; } From 000f6dd5ab752909d50a6c4a83f8ec998d7a7e0a Mon Sep 17 00:00:00 2001 From: QuackieMackie Date: Thu, 22 Jan 2026 00:11:17 +0000 Subject: [PATCH 02/10] Fix: Correct type hint for `AttachmentRepository` in add-on documentation. --- docs/devs/lets-build-an-add-on.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devs/lets-build-an-add-on.md b/docs/devs/lets-build-an-add-on.md index 5da35bd..a59ce6f 100644 --- a/docs/devs/lets-build-an-add-on.md +++ b/docs/devs/lets-build-an-add-on.md @@ -1064,7 +1064,7 @@ Right now, this is going to generate an additional query for each post. So, we s $threads = $featuredThreads->pluckNamed('Thread'); $posts = $threads->pluckNamed('FirstPost', 'first_post_id'); -/** @var \XF\Repository\Attachment $attachRepo */ +/** @var \XF\Repository\AttachmentRepository $attachRepo */ $attachRepo = $this->repository('XF:Attachment'); $attachRepo->addAttachmentsToContent($posts, 'post'); ``` From 012a15c962ca5f53e674eeccb27a8c388abc34f4 Mon Sep 17 00:00:00 2001 From: QuackieMackie Date: Thu, 22 Jan 2026 00:26:08 +0000 Subject: [PATCH 03/10] Update: Replace `Demo-Portal-1.0.0 Alpha.zip` with an updated version. --- static/files/Demo-Portal-1.0.0 Alpha.zip | Bin 20752 -> 21216 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/static/files/Demo-Portal-1.0.0 Alpha.zip b/static/files/Demo-Portal-1.0.0 Alpha.zip index a4da03d27ce49014b6d49960e554f2f1c3ccfa8b..d463b8ea5a5101b66feb4067edfc639e7396bebe 100644 GIT binary patch delta 11217 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 From f0d914921f2079904b0877c8a14ecd506442ba40 Mon Sep 17 00:00:00 2001 From: QuackieMackie Date: Thu, 22 Jan 2026 01:58:44 +0000 Subject: [PATCH 04/10] Fix: Update class names and type hints in documentation. --- docs/devs/lets-build-an-add-on.md | 64 ++++++++++++----------- static/files/Demo-Portal-1.0.0 Alpha.zip | Bin 21216 -> 21258 bytes 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/docs/devs/lets-build-an-add-on.md b/docs/devs/lets-build-an-add-on.md index a59ce6f..8a7c2de 100644 --- a/docs/devs/lets-build-an-add-on.md +++ b/docs/devs/lets-build-an-add-on.md @@ -370,16 +370,16 @@ Finally, click save to save your template modification. If all has gone well, wh We have our column, we have a UI to pass an input to that column, now we have to handle saving data to that column. We will do this by extending the Forum controller and extending a special method which is called when a node and its data are saved. First, let's create a "Class extension" which can be found under the "Development" entry in the Admin CP. Click "Add class extension". -Here we need to specify a "Base class name" which is the name of the class we are extending, which in this case will be `XF\Admin\Controller\Forum`. And we need to specify a "Extension class name" which is the class which will extend the base class. Enter this as `Demo\Portal\XF\Admin\Controller\Forum`. We should create that class before clicking Save. +Here we need to specify a "Base class name" which is the name of the class we are extending, which in this case will be `XF\Admin\Controller\ForumController`. And we need to specify a "Extension class name" which is the class which will extend the base class. Enter this as `Demo\Portal\XF\Admin\Controller\ForumController`. We should create that class before clicking Save. Create a new file in `src/addons/Demo/Portal/XF/Admin/Controller` named `Forum.php`. This might seem like quite a long path, but we recommend a path like this for extended classes. It enables you to more easily identify the files that represent extended classes by virtue of the fact that they are in a directory of the same name as the extended "add-on" ID (in this case `XF`). It also makes it clear exactly which class has been extended as the directory structure follows the same path as the default class. The contents of the file should, for now, look like this: -```php title="src/addons/Demo/Portal/XF/Admin/Controller/Forum.php" +```php title="src/addons/Demo/Portal/XF/Admin/Controller/ForumController.php" demo_portal_auto_feature) { $creator->setFeatureThread(true); @@ -487,7 +487,7 @@ if ($forum->demo_portal_auto_feature) We can now add the `_save()` method to the extended creator class: -```php title="src/addons/Demo/Portal/XF/Service/Thread/Creator.php" +```php title="src/addons/Demo/Portal/XF/Service/Thread/CreatorService.php" protected function _save() { $thread = parent::_save(); @@ -498,7 +498,7 @@ protected function _save() To make sure this thread gets featured, in between the `$thread` line and the `return` line we just need to add: -```php title="src/addons/Demo/Portal/XF/Service/Thread/Creator.php" +```php title="src/addons/Demo/Portal/XF/Service/Thread/CreatorService.php" if ($this->featureThread && $thread->discussion_state == 'visible') { /** @var \Demo\Portal\Entity\FeaturedThread $featuredThread */ @@ -599,8 +599,9 @@ This doesn't return the results of this query. This returns the finder object it Let's now use that in our `actionIndex()` method inside our portal controller. Change the existing `$viewParams = [];` line to the following: ```php title="src/addons/Demo/Portal/Pub/Controller/Portal.php" -/** @var \Demo\Portal\Repository\FeaturedThread $repo */ -$repo = $this->repository('Demo\Portal:FeaturedThread'); +use Demo\Portal\Repository\FeaturedThread; + +$repo = $this->repository(FeaturedThread::class); $finder = $repo->findFeaturedThreadsForPortalView(); @@ -769,16 +770,16 @@ Ok, so, we haven't exactly done much here of value, yet. All the `canFeatureUnfe To test this works so far, open one of the threads you previously featured, and select "Edit thread" from the tools menu. We should see the "Set thread status" checkbox row has the "Featured" checkbox we added, and it should be checked, indicating that this thread is indeed featured. -We can now move on to changing the thread editor service to look for this value and feature or unfeature accordingly. We are going to need two new class extensions for this. Go back to the "Add class extensions" page. The first one will have a base class of `XF\Pub\Controller\Thread` and extension class of `Demo\Portal\XF\Pub\Controller\Thread`. The second one will have a base class of `XF\Service\Thread\Editor` and an extension class of `Demo\Portal\XF\Service\Thread\Editor`. +We can now move on to changing the thread editor service to look for this value and feature or unfeature accordingly. We are going to need two new class extensions for this. Go back to the "Add class extensions" page. The first one will have a base class of `XF\Pub\Controller\ThreadController` and extension class of `Demo\Portal\XF\Pub\Controller\ThreadController`. The second one will have a base class of `XF\Service\Thread\EditorService` and an extension class of `Demo\Portal\XF\Service\Thread\EditorService`. The editor service is actually going to be very similar to the extended creator service we created earlier, so create that in the relevant location. Here is all of the code for the extended class: -```php title="src/addons/Demo/Portal/XF/Service/Thread/Editor.php" +```php title="src/addons/Demo/Portal/XF/Service/Thread/EditorService.php" canFeatureUnfeature(); @@ -854,8 +855,8 @@ We need to extend another method in the thread controller to handle a situation We just need to add the following code below the `setupThreadEdit()` method we added above: -```php title="src/addons/Demo/Portal/XF/Pub/Controller/Thread.php" -public function finalizeThreadReply(\XF\Service\Thread\Replier $replier) +```php title="src/addons/Demo/Portal/XF/Pub/Controller/ThreadController.php" +public function finalizeThreadReply(\XF\Service\Thread\ReplierService $replier) { parent::finalizeThreadReply($replier); @@ -876,7 +877,7 @@ Note that we haven't actually returned anything in this method because it isn't For the final step in manually featuring/unfeaturing a thread, we need to go back to the forum controller and slightly change our existing code so that if featuring isn't automatic, we can handle it manually, instead. This should be fairly straight forward. Head into your extended forum controller, and replace this: -```php title="src/addons/Demo/Portal/XF/Pub/Controller/Thread.php" +```php title="src/addons/Demo/Portal/XF/Pub/Controller/ThreadController.php" if ($forum->demo_portal_auto_feature) { $creator->setFeatureThread(true); @@ -885,7 +886,7 @@ if ($forum->demo_portal_auto_feature) With this: -```php title="src/addons/Demo/Portal/XF/Pub/Controller/Thread.php" +```php title="src/addons/Demo/Portal/XF/Pub/Controller/ThreadController.php" if ($forum->demo_portal_auto_feature) { $creator->setFeatureThread(true); @@ -1061,11 +1062,12 @@ You may have spotted earlier in the demo_portal_view template that each post we Right now, this is going to generate an additional query for each post. So, we should instead try to do a single query for all of the posts we are displaying and add them to the posts in advance. It probably sounds more complicated than it is. Just add the below code beneath the `->slice(0, $perPage, true);` line. ```php title="src/addons/Demo/Portal/Pub/Controller/Portal.php" +use XF\Repository\AttachmentRepository; + $threads = $featuredThreads->pluckNamed('Thread'); $posts = $threads->pluckNamed('FirstPost', 'first_post_id'); -/** @var \XF\Repository\AttachmentRepository $attachRepo */ -$attachRepo = $this->repository('XF:Attachment'); +$attachRepo = $this->repository(AttachmentRepository::class); $attachRepo->addAttachmentsToContent($posts, 'post'); ``` diff --git a/static/files/Demo-Portal-1.0.0 Alpha.zip b/static/files/Demo-Portal-1.0.0 Alpha.zip index d463b8ea5a5101b66feb4067edfc639e7396bebe..3c71f6b1d0f7721c12422c147c8503561df81870 100644 GIT binary patch delta 9623 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 AH2?qr delta 9755 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 Date: Thu, 22 Jan 2026 02:09:46 +0000 Subject: [PATCH 05/10] =?UTF-8?q?Addressed=20reviewed=20feedback:=20-=20Al?= =?UTF-8?q?ign=20filename=20in=20instructions=20with=20code=20block.=20-?= =?UTF-8?q?=20Fix=20article=20usage=20("a"=20=E2=86=92=20"an").=20-=20[Pos?= =?UTF-8?q?sibly]=20Replace=20hard=20tabs=20with=20spaces=20to=20satisfy?= =?UTF-8?q?=20MD010.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/devs/lets-build-an-add-on.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/devs/lets-build-an-add-on.md b/docs/devs/lets-build-an-add-on.md index 8a7c2de..580d7c8 100644 --- a/docs/devs/lets-build-an-add-on.md +++ b/docs/devs/lets-build-an-add-on.md @@ -370,9 +370,9 @@ Finally, click save to save your template modification. If all has gone well, wh We have our column, we have a UI to pass an input to that column, now we have to handle saving data to that column. We will do this by extending the Forum controller and extending a special method which is called when a node and its data are saved. First, let's create a "Class extension" which can be found under the "Development" entry in the Admin CP. Click "Add class extension". -Here we need to specify a "Base class name" which is the name of the class we are extending, which in this case will be `XF\Admin\Controller\ForumController`. And we need to specify a "Extension class name" which is the class which will extend the base class. Enter this as `Demo\Portal\XF\Admin\Controller\ForumController`. We should create that class before clicking Save. +Here we need to specify a "Base class name" which is the name of the class we are extending, which in this case will be `XF\Admin\Controller\ForumController`. And we need to specify an "Extension class name" which is the class which will extend the base class. Enter this as `Demo\Portal\XF\Admin\Controller\ForumController`. We should create that class before clicking Save. -Create a new file in `src/addons/Demo/Portal/XF/Admin/Controller` named `Forum.php`. This might seem like quite a long path, but we recommend a path like this for extended classes. It enables you to more easily identify the files that represent extended classes by virtue of the fact that they are in a directory of the same name as the extended "add-on" ID (in this case `XF`). It also makes it clear exactly which class has been extended as the directory structure follows the same path as the default class. The contents of the file should, for now, look like this: +Create a new file in `src/addons/Demo/Portal/XF/Admin/Controller` named `ForumController.php`. This might seem like quite a long path, but we recommend a path like this for extended classes. It enables you to more easily identify the files that represent extended classes by virtue of the fact that they are in a directory of the same name as the extended "add-on" ID (in this case `XF`). It also makes it clear exactly which class has been extended as the directory structure follows the same path as the default class. The contents of the file should, for now, look like this: ```php title="src/addons/Demo/Portal/XF/Admin/Controller/ForumController.php" canFeatureUnfeature(); From 9fdcfa6ec4b3ecba65669ed3fee83edf77191ae3 Mon Sep 17 00:00:00 2001 From: QuackieMackie Date: Mon, 26 Jan 2026 21:01:30 +0000 Subject: [PATCH 06/10] Fix: directing user to the wrong admin option. `Basic board information` -> `Basic options` --- docs/devs/lets-build-an-add-on.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devs/lets-build-an-add-on.md b/docs/devs/lets-build-an-add-on.md index 580d7c8..1f629fa 100644 --- a/docs/devs/lets-build-an-add-on.md +++ b/docs/devs/lets-build-an-add-on.md @@ -706,7 +706,7 @@ public static function homePageUrl(&$homePageUrl, \XF\Mvc\Router $router) } ``` -Finally, we should consider changing the index page route to our portal page. Go to Admin CP and under Setup click Options followed by "Basic board information". Change the "Index page route" option to `portal/`. +Finally, we should consider changing the index page route to our portal page. Go to Admin CP and under Setup click Options followed by "Basic options". Change the "Index page route" option to `portal/`. While you're in the Admin CP, let's see what happens now when you click on the Board title in the header. This should take you to your index page. All being well, that index page should now be your portal! In addition to that, the Home tab should be visible, and selected. From df49a63ba2859b30999d25ad59c5013a11f440cd Mon Sep 17 00:00:00 2001 From: QuackieMackie Date: Mon, 26 Jan 2026 22:24:53 +0000 Subject: [PATCH 07/10] Fix: Correct controller filename in code block titles. --- docs/devs/lets-build-an-add-on.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devs/lets-build-an-add-on.md b/docs/devs/lets-build-an-add-on.md index 1f629fa..32d34f7 100644 --- a/docs/devs/lets-build-an-add-on.md +++ b/docs/devs/lets-build-an-add-on.md @@ -877,7 +877,7 @@ Note that we haven't actually returned anything in this method because it isn't For the final step in manually featuring/unfeaturing a thread, we need to go back to the forum controller and slightly change our existing code so that if featuring isn't automatic, we can handle it manually, instead. This should be fairly straight forward. Head into your extended forum controller, and replace this: -```php title="src/addons/Demo/Portal/XF/Pub/Controller/ThreadController.php" +```php title="src/addons/Demo/Portal/XF/Pub/Controller/ForumController.php" if ($forum->demo_portal_auto_feature) { $creator->setFeatureThread(true); @@ -886,7 +886,7 @@ if ($forum->demo_portal_auto_feature) With this: -```php title="src/addons/Demo/Portal/XF/Pub/Controller/ThreadController.php" +```php title="src/addons/Demo/Portal/XF/Pub/Controller/ForumController.php" if ($forum->demo_portal_auto_feature) { $creator->setFeatureThread(true); From 1202e213ed3d3e97fd84b3db7784070933966760 Mon Sep 17 00:00:00 2001 From: QuackieMackie Date: Mon, 26 Jan 2026 22:26:05 +0000 Subject: [PATCH 08/10] Fix: formatting inconsistency --- docs/devs/lets-build-an-add-on.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devs/lets-build-an-add-on.md b/docs/devs/lets-build-an-add-on.md index 32d34f7..9f1d689 100644 --- a/docs/devs/lets-build-an-add-on.md +++ b/docs/devs/lets-build-an-add-on.md @@ -1073,7 +1073,7 @@ $attachRepo->addAttachmentsToContent($posts, 'post'); We use the `pluckNamed()` method first to get a collection of threads, then again to get a collection of the posts (keyed by the post ID) from the threads. Once we have the posts, we can just pass them into a special method in the attachment repo, which performs a single query and "hydrates" the Attachments relation for each post. -The final permission related thing to finish up is to create a new permission to control who can feature / unfeature threads manually. To do this, in the Admin CP under "Development" click "Permission definitions" and click "Add permission". The "Permission group" will be "forum", "Permission ID" will be `demoPortalFeature`, "Title" should be `Can feature / unfeature threads`, set "Interface group" to `Forum moderator permissions` and after choosing an appropriate display order and ensuring your add-on is selected, click "Save". +The final permission related thing to finish up is to create a new permission to control who can feature / unfeature threads manually. To do this, in the Admin CP under "Development" click "Permission definitions" and click "Add permission". The "Permission group" will be `forum`, "Permission ID" will be `demoPortalFeature`, "Title" should be `Can feature / unfeature threads`, set "Interface group" to `Forum moderator permissions` and after choosing an appropriate display order and ensuring your add-on is selected, click "Save". To actually use this permission, we need to go back to our extended thread entity to modify the `canFeatureUnfeature()` method. Replace `return true;` with: From 749d0aae5784f1ce734181df182dfcafd9c8e77a Mon Sep 17 00:00:00 2001 From: QuackieMackie Date: Mon, 26 Jan 2026 22:26:33 +0000 Subject: [PATCH 09/10] Fix: Correct option field format from "Spin box" to "Number box" --- docs/devs/lets-build-an-add-on.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devs/lets-build-an-add-on.md b/docs/devs/lets-build-an-add-on.md index 9f1d689..261cc18 100644 --- a/docs/devs/lets-build-an-add-on.md +++ b/docs/devs/lets-build-an-add-on.md @@ -1089,7 +1089,7 @@ We currently display only 5 featured threads per page, but it would be nice to h In the Admin CP under Setup then Options click the "Add option group" button. We'll just call the "Group ID" `demoPortal` and give it a title of "Demo - Portal options". Give it an appropriate ̀"Description" and "Display order" and click "Save". -Now click "Add option". Set the "Option ID" to `demoPortalFeaturedPerPage`, "Title" to `Featured threads per page`, edit format to `Spin box`, "Data type" to `Positive integer` and "Default value" to `10`. Click "Save". +Now click "Add option". Set the "Option ID" to `demoPortalFeaturedPerPage`, "Title" to `Featured threads per page`, edit format to `Number box`, "Data type" to `Positive integer` and "Default value" to `10`. Click "Save". To implement that, go back to the portal controller and change: From b56391c9f9e4436b0b625f0739ae56cf97b4322d Mon Sep 17 00:00:00 2001 From: QuackieMackie Date: Mon, 26 Jan 2026 22:26:55 +0000 Subject: [PATCH 10/10] Fix: Correct function reference in code example from `slice` to `sliceToPage` --- docs/devs/lets-build-an-add-on.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devs/lets-build-an-add-on.md b/docs/devs/lets-build-an-add-on.md index 261cc18..d48bc6e 100644 --- a/docs/devs/lets-build-an-add-on.md +++ b/docs/devs/lets-build-an-add-on.md @@ -1059,7 +1059,7 @@ You may have spotted earlier in the demo_portal_view template that each post we 'attachments': $post.attach_count ? $post.Attachments : [], ``` -Right now, this is going to generate an additional query for each post. So, we should instead try to do a single query for all of the posts we are displaying and add them to the posts in advance. It probably sounds more complicated than it is. Just add the below code beneath the `->slice(0, $perPage, true);` line. +Right now, this is going to generate an additional query for each post. So, we should instead try to do a single query for all of the posts we are displaying and add them to the posts in advance. It probably sounds more complicated than it is. Just add the below code beneath the `->sliceToPage($page, $perPage);` line. ```php title="src/addons/Demo/Portal/Pub/Controller/Portal.php" use XF\Repository\AttachmentRepository;