📜 ⬆️ ⬇️

We write the first addon for XenForo. MVC, templates, styles, settings, navigation - we use everything

Cyrus posted an educational addon on the forum where you can demonstrate how to write addons for XenForo. This is what we will do now. This training addon contains a couple of errors, perhaps, so that we show ourselves, discovering and correcting them. We will create the same addon from the very beginning and allow ourselves to slightly improve / correct what happened with Cyrus.



The challenge is this. It is necessary to write an addon, which on a separate page will display a list of forum messages that users liked most (ie, the messages that the users like the most). The administrator should be able to configure the maximum number of messages displayed on the page in the control panel, as well as choose the avatar color in the forum style. The addon should be fully phrasable and built into the main forum menu in a separate tab.
')
First of all, you need to put the forum in debug mode. This can be done by adding a line to the /library/config.php file.
$config['debug'] = true; 


Log into the admin control panel and go to the Development tab that appears. Select "Create add-on" there. As the ID, we need to specify any unique string. Let it be “LikeReviewRus” (without quotes, of course). In the title bar, enter any explanatory text. It does not affect anything, it will just be displayed in the control panel. Save. We got an empty addition.

Now let's create an option to limit the number of posts per page. Go to the Home tab and go to Settings. Add a group of settings. Group ID let it be LikeReviewRusSettingsGroup. Title can be arbitrary. You will see this text among other groups of settings. Do not make it too long. Keep in mind that this is the text to display in the “Master language” language of the forum. In fact, such a language is English, and our addon should ideally also be originally in English. Then we could translate it and distribute the Russian localization along with the addon file. But for the sake of simplicity, let's point out all the lines in the native language for now. As an addition, select our addon and save.

After saving you will be in this group of settings. Initially, it is empty and we need to create a setting here. Click “add option”. For the option name, enter LikeReviewRusMaxToDisplay. Choose a supplement, create a title and explanatory text. We need to enter only integer numbers, therefore, as a format, we should choose the “Number input field with arrows”. In the format options you can specify
 min=1 step=5 max=100 


This means that the minimum setting value is 1, each time you press the arrow, 5 will be added / subtracted, and the maximum number that can be entered there is 100. Let the data type be “Unsigned integer”. The default value is 30. The remaining settings are left as is. Save. You can immediately check what you got.

Now let's create a style setting. Go to the Exterior view tab and click on Style Settings. Create a new group there. Let her ID be LikeReviewRusStyle. All other parameters, including the Addition item, you can choose by yourself. Save and then go to this group. You will immediately enter the dialog for creating a new style property. The property name is LikeReviewRusAvatarBorder. Specify Supplement, title and description. Set the property type to Scalar and select a color. You may also have to manually select the group again. This is a bug and will fix it. Save and see what you got. Class, right? :)

Now we need to create some phrases so that the addon can be translated into other languages. Click the Phrases link on the Appearance page. Create phrases with the captions LikeReviewRus_Header (to display at the very top of the page) and LikeReviewRus_Description to display below as a description. As the text for this last phrase, enter "This is {numPosts} the posts users liked most." Below you will see how the quantity is inserted in the template instead of {numPosts}.

Now we need to create a template for the page that we will show. Click on the Templates section on the Appearance tab. Now create a new template. For the name of the template, specify likereviewrus.css and insert the following contents.
 .mostLikedPosts { } .mostLikedPosts .avatar { float: left; margin-right: 10px; } .mostLikedPosts .avatar img { width: 64px; height: 64px; border: 3px solid {xen:property LikeReviewRusAvatarBorder}; } .mostLikedPosts .likedPost { position: relative; } .mostLikedPosts .primaryContent { padding: 0; padding-top: 10px; } .mostLikedPosts h3 { font-size: 12pt; margin-bottom: 5px; } .mostLikedPosts .likes { display: block; position: absolute; right: 0px; top: 10px; width: 24px; height: 24px; line-height: 24px; text-align: center; border-radius: 13px; font-weight: bold; background: {xen:property primaryLighterStill}; border: 1px solid {xen:property primaryLighter}; } .mostLikedPosts .likes:hover { background-color: {xen:property secondaryLightest}; border-color: {xen:property secondaryLighter}; color: {xen:property secondaryDark}; text-decoration: none; box-shadow: 0 0 10px {xen:property secondaryMedium}; } .mostLikedPosts .meta { font-size: 11px; padding-top: 5px; padding-bottom: 5px; margin-left: 80px; margin-bottom: -1px; margin-top: 10px; border: 1px solid {xen:property primaryLighterStill}; border-right: none; border-top-left-radius: 10px; } .mostLikedPosts .meta dd { margin-right: 10px; } .mostLikedPosts .meta dd strong { font-weight: bold; } 

Select add-on, save and exit. We just typed a list of CSS styles that we just use in the template. Tags {xen: property} denote the places where data from the basic style settings will be inserted (find them, by the way). Please note that we put in there the style setting that we created ourselves, correcting the author's mistake (albeit slightly tasteless).

Now create another template and name it likereviewrus_index. Paste the following content into it:

 <xen:title>{xen:phrase LikeReviewRus_Header}</xen:title> <xen:navigation> <xen:breadcrumb href="{xen:link likes-review}">{xen:phrase LikeReviewRus_Header}</xen:breadcrumb> </xen:navigation> <xen:require css="likereviewrus.css" /> <div class="sectionMain mostLikedPosts"> <h2 class="subHeading">{xen:phrase LikeReviewRus_Description, 'numPosts={xen:count $likedPosts}'}</h2> <ol> <xen:foreach loop="$likedPosts" value="$post"> <li class="likedPost"> <div class="primaryContent"> <xen:avatar user="$post" size="m" img="true" /> <h3><a href="{xen:link posts, $post}">{$post.title}</a></h3> <div class="muted">{xen:helper wordTrim, $post.message, 140}</div> <a href="{xen:link posts/likes, $post}" class="likes OverlayTrigger"><strong>{xen:number $post.likes}</strong></a> <dl class="secondaryContent pairsInline meta"> <dt>{xen:phrase posted_by}</dt> <dd><a href="{xen:link members, $post}" class="username">{$post.username}</a></dd> <dt>{xen:phrase date}</dt> <dd><xen:datetime time="$post.post_date" /></dd> <dt>{xen:phrase likes}</dt> <dd><a href="{xen:link posts/likes, $post}" class="OverlayTrigger">{xen:number $post.likes}</a></dd> </dl> </div> </li> </xen:foreach> </ol> <div class="sectionFooter">{xen:phrase showing_x_posts, 'numPosts={xen:count $likedPosts}'}</div> </div> 


Now let's look at all the tags that we met here.

That's all. Please note that the template is incomplete (without, and so on). That is, it's just the inside of the page, outside of which will be the standard elements of XenForo.

Now a little about how XenForo handles requests. If you are familiar with the MVC model, most likely you are well aware of this. If not, in short it looks like this. XenForo parses the HTTP request that arrives to it and, according to the rules of routing, determines which controller to transfer control for its processing. The controller accepts the request, creates the data model (which, for example, simply reads the data from the database with the request), then builds the view (in fact, turns the templates into HTML code) and passes the model with the data to it. The resulting messive is sent to the user's browser.

In order for XenForo to understand which request to display our page with the list of the coolest messages on the forum, we first need to create a routing rule. Go to the Development tab and click the link Prefixes for routing. Create a new public prefix. As prefix string, specify likes-review. In the setting for using the class to generate a link, select Never, for the class name, specify LikeReviewRus_Route_Prefix_LikesReview. Do not forget to choose the right supplement.

Setup in the control panel is finished. Now we get down to coding. First, let's apply our class to handle the routing prefix. Go to the Library folder and create a LikeReviewRus subdirectory there, in it the Route folder inside Prefix. Here truly kashcheeva's death turned out. Now create the file LikesReview.php. Its contents:
 <?php class LikeReviewRus_Route_Prefix_LikesReview implements XenForo_Route_Interface { /** * Match a specific route for an already matched prefix. * * @see XenForo_Route_Interface::match() */ public function match($routePath, Zend_Controller_Request_Http $request, XenForo_Router $router) { return $router->getRouteMatch('LikeReviewRus_ControllerPublic_Index', 'index', 'likes-review'); } } 


In this case, we say that when a likes-review prefix is ​​detected (we set it up in the admin panel), we need to transfer control to the LikeReview_ControllerPublic_Index controller, transfer it as an “index” action, and like-review to the majorsection. Kira's second mistake :)

Now create the path \ library \ LikeReviewRus \ ControllerPublic, go there and create the Index.php file there. Its contents are:
 <?php class LikeReviewRus_ControllerPublic_Index extends XenForo_ControllerPublic_Abstract { public function actionIndex() { $maxResults = XenForo_Application::get('options')->LikeReviewRusMaxToDisplay; $likedPosts = $this->_getLikeReviewModel()->getMostLikedPosts($maxResults); $viewParams = array( 'likedPosts' => $likedPosts ); return $this->responseView('LikeReviewRus_ViewPublic_Index', 'likereviewrus_index', $viewParams); } /** * @return LikeReview_Model_LikeReview */ protected function _getLikeReviewModel() { return $this->getModelFromCache('LikeReviewRus_Model_LikeReview'); } } 


What's going on here? This controller has only one action defined (index, camelCase is used in the method name). This action extracts the contents of the settings for the maximum number of results (remember, we created it). Then a data model is created for the list of the coolest posts, then parameters are created to be passed to the view (just the parameters for the template, $ viewParams) and then the view is built (the likereview_index template, responseView is rendered) and parameters are passed to it. The _getLikeReviewModel helper method simply pulls the model out of the shared cache if it has been cached.

Now go to our data model. Create a library \ LikeReviewRus \ Model path and log in there. Create a LikeReview file. Its contents are:

 <?php class LikeReviewRus_Model_LikeReview extends XenForo_Model { /** * Gets the most liked posts in descending order * * @param integer Maximum posts to fetch * * @return array */ public function getMostLikedPosts($limit) { //    -    xf_liked_content, //    $limitedSql = $this->limitQueryResults(" SELECT content_id, COUNT(*) AS likes FROM xf_liked_content WHERE content_type = 'post' GROUP BY content_id ORDER BY likes DESC ", $limit); //       (fetchCol)  ID     $postIds = $this->_getDb()->fetchCol($limitedSql); //            ID  //,            $postResults = $this->_getPostModel()->getPostsByIds($postIds, array ( 'join' => XenForo_Model_Post::FETCH_THREAD | XenForo_Model_Post::FETCH_USER )); //   ,       "" . $posts = array(); foreach ($postResults AS $post) { $posts["$post[likes].$post[post_date]"] = $post; } // krsort($posts); // return $posts; } /** * @return XenForo_Model_Post */ protected function _getPostModel() { //       return $this->getModelFromCache('XenForo_Model_Post'); } } 


Now try to go to www.xenforo.local / likes-review . You should see a list of posts with thanks. Pay attention to the thick ugly frame of avatars :)

Now we will try to add a bookmark on this page to the main navigation. Go to the admin panel on the Development tab and create a new event handler. We will process the navigation_tabs event. The name of the handler class LikeReviewRus_Tabs_MainTab, the name of the addTab method.

Create another template likereviewrus_links with the following content:
 <ul class="secondaryContent blockLinksList"> <li><a href="{xen:link likes-review}">{xen:phrase LikeReviewRus_Header}</a></li> </ul> 

This is a template for creating sub-links for the main section. For now, we only have one main link there.

Create the library \ LikeReviewRus \ Tabs path, go there and create the MainTab.php file there. Its contents are:
 <?php class LikeReviewRus_Tabs_MainTab { public static function addTab(array &$extraTabs, $selected) { $extraTabs['likes-review'] =array( 'title' => new XenForo_Phrase('LikeReviewRus_Header'), //   'href' => XenForo_Link::buildPublicLink('likes-review'), //   'selected' => ($selected == 'likes-review'), //,      'linksTemplate' => 'likesreviewrus_links', //    - ); } } 


Navigation is ready. In order for it to be beautifully displayed, you should include CNC references in the SEO forum settings. In order to see which requests the page uses, open it by adding / _debug = 1 to the end. In particular, you will see that calling new XenForo_Phrase generates a separate request, which is not good. In order to avoid this, open the phrase LikeReviewRus_Header and check it so that it is added to the global cache.

That's all. I hope you liked it :) If any typos - report. However, I think, after everything described, it will not be difficult for you to fix them yourself, if there are any.

PS By the way, the Russian community XenForo began developing its add-ons. In particular, recently an analogue of the add-on VBulletin appeared on our forum for pasting posts posted by the user , written by Pepelac .

Source: https://habr.com/ru/post/106046/


All Articles