<?xml version="1.0"?>
<rss version="2.0"><channel><title>Invision Community Development Blog</title><link>https://invisioncommunity.com/developers/devblog/</link><description/><language>en</language><item><title>IC5: The New Editor</title><link>https://invisioncommunity.com/developers/devblog/blog/ic5-the-new-editor-r18/</link><description><![CDATA[
<p><img src="https://media.invisioncic.com/a319035/monthly_2024_05/dev-editor-blog.jpeg.249a00773063a6f26439bf2bf74bfa31.jpeg" /></p>
<p>
	In 2024, a secure WYSIWYG Editor has become a complex intricate thing.
</p>

<p>
	Copy/paste bundle files have largely been phased out in favor of complicated NPM repos and build tools. What was more or less just "HTML Manipulation" has evolved to abstract content models with dynamic rules on how to actually render the content to HTML. Then, for kicks, throw in the requirement that this editor needs to work in non-standard cases like the drag and drop page builder and live topics. The solution for Invision Community 5 is a new, custom, state of the art Editor built using ReactJS and <a href="https://tiptap.dev" rel="external nofollow">Tiptap</a>.
</p>

<p>
	In this article, I'll cover high level advantages, technical highlights and what's possible with 3rd party extensions. 
</p>

<p>
	If you want to know more about the editor functionality, then see <a href="https://invisioncommunity.com/news/invision-community/invision-community-5-the-all-new-editor-r1301/" rel="">my other blog here</a>.
</p>

<p>
	This story begins after we switched to CKEditor5 over a year ago. It is a decent product, but it had several serious limitations in a distributed platform like Community. We compared just about every editor currently on the market, and even considered building an editor completely from scratch, and ultimately landed on creating a custom editor using ReactJS and <a href="https://tiptap.dev" rel="external nofollow">Tiptap</a>.
</p>

<p>
	 
</p>

<p>
	<span style="font-size:18px">A lightweight package</span>
</p>

<p>
	The v5 editor bundle size is small, coming to about 2mb in total (and it's split into separate chunks loaded ad-hoc, each one cached in the browser, so in practice much less data actually "loads"). After optimizing and tweaking CKEditor5, the smallest we could get it was over 50mb; now some build tool experts could probably wrangle that cke package to a smaller size, but I doubt it'd come anywhere close to Tiptap with all the features we added.
</p>

<p>
	It's also worth noting that, during operation, Tiptap had by far the lowest impact on browser memory consumption, and stubborn memory leaks never came up with this architecture. Part of this comes from taking advantage of ReactJS's stateful nature, but I think the most significant thing is that Tiptap doesn't have loose logic hanging around.
</p>

<p>
	 
</p>

<p>
	<span style="font-size:18px">How a modern WYSIWYG works (the short version)</span>
</p>

<p>
	As I previously alluded to, editors today don't directly manipulate HTML at all. Instead, they store a Content Model according to a Schema.
</p>

<p>
	Different platforms may use different terms, but the content schema consists of the following Entities
</p>

<ul>
	<li>
		Nodes - Actual containers to put content in. Think blockquotes, headings or lists. Nodes are then stored in the document in a tree
	</li>
	<li>
		Text and Inline Nodes - Text is, well, text. It is exclusively just a string of characters; one way to think of it is, if it can go in a text message, it's text. Inline nodes, on the other hand, are not true "characters" but displayed inline with characters. Think custom emoticons and mentions.
	</li>
	<li>
		Marks - Styles and/or data that is applied over a range of text. Marks are similar to Nodes except that marks can overlap. For example, it if text is <strong><em>italic and bold</em></strong>, it makes no difference if it's bold inside italic or vise versa. Moreover, it it most accurate to simply say that text is just both italic and bold; this data is stored in Marks. In old editors, like CKEditor4, this was often just thrown into a "style" attribute which led to all sorts of issues and overhead processing. (These are not what CKEditor calls "marks" FYI, they call this concept "text attributes", in case anyone has existing CKE knowledge)
	</li>
</ul>

<p>
	All the above entities rely on Converters to generate them from HTML and then reconvert back into HTML. When the editor starts, it uses those converts to convert the initial data into the Content Model. Then, any change is made to the Content Model directly, which is continuously being converted back into HTML for the user to see.
</p>

<p>
	<span style="font-size:18px">Security and Consistency</span>
</p>

<p>
	The Schema and architecture add a layer of complexity but provide much better security, consistency and reliability because it only allows whitelisted content structures. For example, if I inserted something with the attribute <code>style="color:white; transform: rotate(45deg)"</code>, the <code>transform</code> and <code>color</code> will be stripped out unless there's an existing converter to convert it to a Mark.
</p>

<p>
	Another example good example is simple bold styling: I can define several converters to parse the bold Mark: one for <code>&lt;b&gt;</code> tags, one for <code>&lt;strong&gt;</code> and one for <code>style="font-weight: bold"</code>, and have them all convert back into a <code>&lt;strong&gt;</code> tag.
</p>

<p>
	Lastly, this gives us the assurance that, for any style, there is a user interface to manipulate and manage it. For this reason, source editing was removed because <strong>you won't ever need to fall back to source editing.</strong>
</p>

<p>
	I could go on for hours (seriously, don't get me started) but that's more than enough context to understand the rest of this article.
</p>

<p>
	 
</p>

<p>
	<span style="font-size:18px">Extending the Editor in Invision</span>
</p>

<p>
	Another limitation of CKEditor5 in a distributed platform, where individual admins may want to customize, is that all Plugins have to be present <strong>at build</strong> not at runtime. This would pretty much mean no extending the editor, just rearranging the toolbar and flipping certain builtin features on and off.
</p>

<p>
	Fortunately, in Tiptap we were able to extend their internal node, mark and extension APIs. We'll have a more complete dev guide, but essentially all nodes, marks and extensions, which I'll just call "extensions" collectively, are rolled into the existing app system. There are 2 reasons we did this instead of just "buttons" like Invision 4, where you can just upload the CKEditor4 plugin zip file:
</p>

<ol>
	<li>
		It relies on using PHP to parse JavaScript to get things like the button title and stuff. If you create a valid plugin but define things in a slightly different way than expected, such as adding the name to the wrong line in the file, the plugin upload fails.
	</li>
	<li>
		Plugins/extensions are not buttons. Many CKE plugins have multiple buttons and many have none. This make management difficult because sometimes there isn't a button to remove and you have to reset all plugins, or you want to remove just one and 5 others disappear with it. Also, mostly all plugins add a new type of content; this type of content needs to be whitelisted
	</li>
</ol>

<p>
	Now, you may be thinking, "like the editor itself, won't the server side HTML Parser will strip any content that is not whitelisted"? Well, the answer is yes and we added a new <abbr title="Admin Control Panel"><abbr title="Admin Control Panel">AdminCP</abbr></abbr> Dev Center tool to create Parser Whitelist Rules inside Invision Community Apps (again more on all this later). With the coupling of the Parser Whitelist and Editor Extensions, admins can rest assured that their editors will just "work" when you install Extensions.
</p>

<p>
	If you want to prematurely prepare to create Invision Community 5 Editor Extensions, have a look at <a href="https://tiptap.dev/docs/editor/api" rel="external nofollow" target="_blank">Tiptap's Dev API Documentation</a>, specifically the methods <code>Node.create()</code>, <code>Mark.create()</code> and <code>Extension.create()</code>. We've added wrappers for those three methods, in addition to an interface to add and position buttons in the toolbar.<br>
	<br>
	Please let me know if you have any questions!
</p>
]]></description><guid isPermaLink="false">18</guid><pubDate>Wed, 15 May 2024 11:34:14 +0000</pubDate></item><item><title>IC5: Developer Center</title><link>https://invisioncommunity.com/developers/devblog/blog/ic5-developer-center-r17/</link><description><![CDATA[
<p><img src="https://media.invisioncic.com/a319035/monthly_2024_02/a.dev-center.jpeg.686145c54410ab631e311b23eadd0e6f.jpeg" /></p>
<p>
	As part of our commitment to encourage 3rd party development and extension, we have given our Developer Center a much needed makeover. A picture is worth a thousand words, but how about a video?
</p>

<div class="ipsEmbeddedVideo" contenteditable="false">
	<div>
		<iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" frameborder="0" height="113" src="https://www.youtube-nocookie.com/embed/XYgAyYBuOg8?feature=oembed" title="Invision Community 5: Developer's Center" width="200" loading="lazy"></iframe>
	</div>
</div>

<p>
	 
</p>

<p>
	<strong>Highlights</strong>
</p>

<ul>
	<li>
		The Developer Center now has its own dedicated tab in the <abbr title="Admin Control Panel"><abbr title="Admin Control Panel">ACP</abbr></abbr>.
	</li>
	<li>
		What were previously tabs are now displayed on individual screens, making for a far less cluttered UI.
	</li>
	<li>
		You can easily switch from one application to another using the main menu or the button at the top right of the screen.
	</li>
	<li>
		We've implemented UI for some JSON files that previously had to be manually created; specifically<em> </em>acpsearch.json and furl.json
	</li>
	<li>
		We've replaced the "Support" button at the top of the <abbr title="Admin Control Panel"><abbr title="Admin Control Panel">ACP</abbr></abbr> with a more helpful "Quick Links" dropdown menu. While this is not specific to developers, with easy access to things like the Task Manager and clearing caches, it's very handy for troubleshooting!
	</li>
</ul>

<p>
	 
</p>

<p>
	<strong>Application Landing Page</strong>
</p>

<p>
	When you open the Developer Center for a particular application, the landing page is designed to help you quickly access common functions, and to help you find any potential issues within your code.
</p>

<p>
	<a alt="Could contain: Page, Text, File, Webpage" class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="198289" href="//media.invisioncic.com/a319035/monthly_2024_02/image.png.b1fcf59fe383cd8025556cafdfe266f1.png" rel=""><img alt="Could contain: Page, Text, File, Webpage" class="ipsImage ipsImage_thumbnailed" data-fileid="198289" data-unique="qx48i4vsy" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2024_02/image.thumb.png.d3786a2875c5445a63f5681208f1fb15.png" loading="lazy" height="500"></a>
</p>

<p>
	 
</p>

<p>
	The Application Scanner currently checks for:
</p>

<ul>
	<li>
		Missing Admin CP Language Strings
	</li>
	<li>
		Missing Front-End Language Strings
	</li>
	<li>
		Missing EditorLocations extensions
	</li>
	<li>
		Missing FileStorage extensions
	</li>
	<li>
		Missing FrontNavigation extensions
	</li>
	<li>
		Missing FURLs
	</li>
	<li>
		Missing Email Templates
	</li>
</ul>

<p>
	 
</p>

<p>
	Language strings are grouped so that can you easily see where the missing strings were detected.
</p>

<p>
	<a alt="Could contain: Page, Text, White Board" class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="198291" href="//media.invisioncic.com/a319035/monthly_2024_02/image.png.116ab4127a2e546dee1a00acacc48817.png" rel=""><img alt="Could contain: Page, Text, White Board" class="ipsImage ipsImage_thumbnailed" data-fileid="198291" data-unique="he9600jbm" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2024_02/image.thumb.png.8d28bf5565a50b3cfd7f456fb6eaa92e.png" loading="lazy" height="430"></a>
</p>

<p>
	 
</p>

<p>
	We will continue to expand the scanners over the course of the next few months.
</p>

<p>
	 
</p>

<p>
	<strong>Thoughts?</strong>
</p>

<p>
	What do you think? Are there any other useful features you'd like to see added to the Dev Center? What kind of scans can we implement on the landing page?
</p>
]]></description><guid isPermaLink="false">17</guid><pubDate>Fri, 09 Feb 2024 16:56:34 +0000</pubDate></item><item><title>IC5: Commerce</title><link>https://invisioncommunity.com/developers/devblog/blog/ic5-commerce-r16/</link><description><![CDATA[
<p><img src="https://media.invisioncic.com/a319035/monthly_2024_01/commerce.jpeg.92050baa98852a793a53ef1d128af1c3.jpeg" /></p>
<p>
	One of the areas we have reviewed in Commerce is the way that we handle custom items. Previously, many of our features were limited to Commerce Products. Even items such as Subscriptions and Download Files were not fully integrated. In Invision Community 5, we have looked at ways to improve the overall experience.
</p>

<p>
	 
</p>

<p>
	<strong>Icons and Images</strong>
</p>

<p>
	A small, but important change: displaying the item icon when an image is not available. In previous versions, if no image was available, the checkout and client area displayed a standard "box" icon for all items. In IC5, we now show the icon defined in your Item extension.
</p>

<p>
	<a alt="Could contain: Page, Text" class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="198105" href="//media.invisioncic.com/a319035/monthly_2024_01/image.png.df4b7291d4fbba86cb2eb8cb6420d3d8.png" rel=""><img alt="Could contain: Page, Text" class="ipsImage ipsImage_thumbnailed" data-fileid="198105" data-unique="028rkb8ul" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2024_01/image.thumb.png.c0c7f9c19076854f62b07e466c7b5053.png" loading="lazy" height="470"></a>
</p>

<p>
	 
</p>

<p>
	<strong>Product Details</strong>
</p>

<p>
	Line item details are displayed on the checkout screens, as well as on the Invoice. However, this functionality was restricted to custom package fields, which are only available for Commerce Products. This logic has been moved to a new extension method, <span style="font-family:Courier New,Courier,monospace;">detailsForDisplay</span>.
</p>

<p>
	<img alt="Could contain: Page, Text" class="ipsImage ipsImage_thumbnailed" data-fileid="198106" data-unique="gmtk1o9cr" style="height: auto;" width="820" src="//media.invisioncic.com/a319035/monthly_2024_01/image.png.5a3b871208dfc503e11d7445b392eca8.png" loading="lazy" height="459.2">
</p>

<p>
	 
</p>

<p>
	<strong>Coupons</strong>
</p>

<p>
	A very popular request is to create coupons for specific items that are not Commerce Products. Previously, coupons could either be applied to the entire purchase or to specific packages. We have added the following methods to the Item extensions to allow you to integrate your items with the coupon form.
</p>

<ul>
	<li>
		<em>couponFormElements</em><br>
		Returns an array of elements that will be shown on the coupon form. If no elements are returned, your item will not be listed.
	</li>
	<li>
		<em>saveCouponForm</em><br>
		Process the values of the fields defined in your <span style="font-family:Courier New,Courier,monospace;">couponFormElements </span>method. This method returns an array of data that will be stored with the coupon.
	</li>
	<li>
		<em>isCouponValid</em><br>
		Check if the coupon is valid for this item.
	</li>
</ul>

<p>
	New default coupon form:
</p>

<p>
	<a alt="Could contain: Page, Text, File, Webpage" class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="198107" href="//media.invisioncic.com/a319035/monthly_2024_01/image.png.625afe58944acbca45c586879fd2689c.png" rel=""><img alt="Could contain: Page, Text, File, Webpage" class="ipsImage ipsImage_thumbnailed" data-fileid="198107" data-unique="756adnuum" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2024_01/image.thumb.png.bacc8ab02d1c23e82ff893ed5df2d095.png" loading="lazy" height="510"></a>
</p>

<p>
	 
</p>

<p>
	 
</p>

<p>
	<strong>Autopay</strong>
</p>

<p>
	When Commerce generates renewal invoices, we attempt to take a payment if a user has a card on file. We have moved this functionality to <span style="font-family:Courier New,Courier,monospace;">\IPS\nexus\Gateway::autopay()</span>. Your gateway must also have the <span style="font-family:Courier New,Courier,monospace;">SUPPORTS_AUTOPAY</span> constant set to true in order for this to work.
</p>

<p>
	With the new logic, when a renewal invoice is generated, the task will loop through all available methods. If autopay is supported, it will attempt to take payment using that payment method.
</p>

<p>
	<img alt="Could contain: Page, Text" class="ipsImage ipsImage_thumbnailed" data-fileid="198108" data-unique="waw1oxjhp" style="height: auto;" width="909" src="//media.invisioncic.com/a319035/monthly_2024_01/image.png.2178156f2e267a836f90ed38424c57b7.png" loading="lazy" height="554.49">
</p>

<p>
	 
</p>

<p>
	We've tried to include the most popular requests that we've seen for custom items and payments. What do you think? Have we missed anything? What are some of the requests you've received for custom item integration?
</p>
]]></description><guid isPermaLink="false">16</guid><pubDate>Wed, 31 Jan 2024 16:14:01 +0000</pubDate></item><item><title>IC5: Updating your Applications</title><link>https://invisioncommunity.com/developers/devblog/blog/ic5-updating-your-applications-r15/</link><description><![CDATA[
<p><img src="https://media.invisioncic.com/a319035/monthly_2023_12/AdobeStock_447199439.jpeg.b31562de105a2f02098c95b3a2107a36.jpeg" /></p>
<p>
	As we get closer to our first release, we'll be discussing how to update your custom applications to be compatible with IPS v5. We know this can seem like a daunting task, especially since not all changes will be immediately obvious, so we'll be walking through this step by step.
</p>

<p>
	 
</p>

<p>
	<span style="font-size:18px;"><strong>Updating Source Classes</strong></span>
</p>

<ul>
	<li>
		Classnames should no longer start with an underscore.
	</li>
	<li>
		All our source classes are now strictly typed, so any of your classes that extend pretty much anything (Content Items, Nodes, Active Record) will need to be updated with the correct method signatures and property types.
	</li>
	<li>
		Almost all Content interfaces have been converted to traits (e.g. <span style="font-family:Courier New,Courier,monospace;">\IPS\Content\Pinnable</span>, <span style="font-family:Courier New,Courier,monospace;">\IPS\Content\Lockable</span>). Your content classes should have <span style="font-family:Courier New,Courier,monospace;">use</span> statements instead of <span style="font-family:Courier New,Courier,monospace;">implements</span>, and if you are overloading any trait methods, verify that it is properly declared.<br>
		The following interfaces have <em>not</em> been moved to traits:<br>
		<span style="font-family:Courier New,Courier,monospace;">\IPS\Content\Embeddable</span><br>
		<span style="font-family:Courier New,Courier,monospace;">\IPS\Content\Filter</span><br>
		<span style="font-family:Courier New,Courier,monospace;">\IPS\Node\Permissions</span>
	</li>
	<li>
		<span style="font-family:Courier New,Courier,monospace;">\IPS\Content\Searchable</span> has been removed entirely and replaced with a SearchContent extension.
	</li>
	<li>
		Recommended: We no longer use FQN in our code. This is not required for v5 compatibility, but a recommendation for best practice.
	</li>
</ul>

<p>
	<a alt="Could contain: Page, Text, File" class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="197344" href="//media.invisioncic.com/a319035/monthly_2023_12/CleanShot2023-12-13at15_45.37@2x.png.36dfd868b4e51c82da3c7a3e4ecc66c9.png" rel=""><img alt="Could contain: Page, Text, File" class="ipsImage ipsImage_thumbnailed" data-fileid="197344" data-unique="moifodbbq" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2023_12/CleanShot2023-12-13at15_45.37@2x.thumb.png.cd47c25352ebbdf7fdacecc8e962900a.png" loading="lazy" height="880"></a>
</p>

<p>
	<a alt="Could contain: Text, Page, Chart, Plot" class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="197346" href="//media.invisioncic.com/a319035/monthly_2023_12/CleanShot2023-12-13at15_47.10@2x.png.39c4e21de3b21895ef3b3993731d2561.png" rel=""><img alt="Could contain: Text, Page, Chart, Plot" class="ipsImage ipsImage_thumbnailed" data-fileid="197346" data-unique="nyz20aajd" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2023_12/CleanShot2023-12-13at15_47.10@2x.thumb.png.e9dec6d32b60c222520cc03061f1b57a.png" loading="lazy" height="450"></a>
</p>

<p style="text-align: center;">
	<span style="color:#7f8c8d;font-weight:normal"><span style="font-size:11px;">A few examples of the changes to the code base, showing strictly typed function signatures and using traits versus interfaces</span></span>
</p>

<p>
	<span style="font-size:18px;"><strong>Updating Extensions</strong></span>
</p>

<ul>
	<li>
		All extensions now have an abstract class that should be extended. Most of these can be found under <span style="font-family:Courier New,Courier,monospace;">\IPS\Extensions</span>. The abstract class is typically the same name as the extension type (e.g. EditorLocations extend <span style="font-family:Courier New,Courier,monospace;">EditorLocationsAbstract</span>). Verify that your extensions use the correct abstract class and that all your method signatures and properties are declared correctly.
	</li>
	<li>
		Remove deprecated extensions. See <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-extensions-r13/" rel="">this blog entry</a> for a complete list.
	</li>
	<li>
		Convert your CreateMenu extensions to a <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-menus-r9/" rel="">UserMenu</a> extension.
	</li>
	<li>
		Convert your MemberSync extensions to a <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-introduction-to-listeners-r8/" rel="">Listener</a>.
	</li>
</ul>

<p>
	 
</p>

<p>
	<strong><span style="font-size:18px;">Replacing Code Hooks</span></strong>
</p>

<p>
	The following is a general list of the most common types of code hooks. Obviously, we cannot predict, nor support, all possibilities, but we have tried to cover the basics here.
</p>

<ul>
	<li>
		Hooks on content classes should be replaced with <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-introduction-to-listeners-r8/" rel="">Listeners</a> or <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-ii-r11/" rel="">UI Extensions</a>.
	</li>
	<li>
		Hooks on the Dispatcher (e.g. for loading JS/CSS) should be converted to <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-extensions-r13/" rel="">Loader Extensions</a>.
	</li>
	<li>
		Hooks on commerce items should be replaced with Listeners.
	</li>
	<li>
		Hooks that add functionality (e.g. Commerce gateways, Login handlers) should be moved to the <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-extensions-r13/" rel="">appropriate extensions</a>.
	</li>
</ul>

<p>
	 
</p>

<p>
	<span style="font-size:18px;"><strong>Replacing Theme Hooks</strong></span>
</p>

<p>
	As with code hooks, below is a list of common uses for theme hooks.
</p>

<ul>
	<li>
		Theme hooks that add to the user dropdown menu should be moved to a <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-menus-r9/" rel="">UserMenu</a> extension.
	</li>
	<li>
		Theme hooks that add classes or attributes should be moved to <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-i-r10/" rel="">UI Extensions</a>.
	</li>
	<li>
		Theme hooks that insert HTML should be moved to <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-theme-tools-r14/" rel="">template hooks</a>.
	</li>
</ul>

<p>
	 
</p>

<p>
	Please let us know in the comments if there is anything that we may have missed, or if something is unclear. We would like to make this transition as smooth as possible.
</p>
]]></description><guid isPermaLink="false">15</guid><pubDate>Wed, 13 Dec 2023 16:01:19 +0000</pubDate></item><item><title>IC5: Theme Tools</title><link>https://invisioncommunity.com/developers/devblog/blog/ic5-theme-tools-r14/</link><description><![CDATA[
<p><img src="https://media.invisioncic.com/a319035/monthly_2023_10/AdobeStock_474607701.jpeg.653842147c645f8abac780e26198cb78.jpeg" /></p>
<p>
	<span style="font-size:16px;font-weight:normal">Theming has been a core component of Invision Community since its inception, and this continues with Invision Community 5, but in a very different way.</span>
</p>

<p>
	If you haven't already seen <a href="https://invisioncommunity.com/news/invision-community/invision-community-5-the-all-new-theme-editor-r1289/" rel="">Ehren's blog on the new Theme Editor</a>, please do take the time to watch it. The all-new theme editor reduces the complexity of theming by taking complex concepts like HSL CSS variables into a pretty slick UI that almost any Invision Community owner can use to personalise or brand match to any existing properties.
</p>

<p>
	Ehren will talk more on the technology behind the theme editor in another developer blog soon, but the short version is that the CSS framework has been completely rewritten from scratch with a new approach to how CSS classes interact with page elements.
</p>

<p>
	Of course, if you're reading this, you'll want to know what tools you have for more advanced theming in v5.
</p>

<p>
	<span style="font-size:16px;">Custom templates and template hooks</span><br>
	Invision Community 5 merges the concepts of custom templates and template hooks into a single feature. In the past, you could edit templates directly and create theme hooks. With Invision Community 5, these features are replaced with the new custom template system.
</p>

<p>
	<a alt="Could contain: File, Webpage, Computer, Electronics, Pc, Text" class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="196219" href="//media.invisioncic.com/a319035/monthly_2023_10/Theme_templates_overview.jpg.0d057ce5d02479ea556d0bb3c8effc2c.jpg" rel=""><img alt="Could contain: File, Webpage, Computer, Electronics, Pc, Text" class="ipsImage ipsImage_thumbnailed" data-fileid="196219" data-unique="i13sbuipx" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2023_10/Theme_templates_overview.thumb.jpg.1c17845ddab5aa826aabaf1305a2ed10.jpg" loading="lazy" height="540"></a>
</p>

<p>
	You can create new templates, which you can use in other custom templates via the short tag: <span style="font-family:Courier New,Courier,monospace;">{customtemplate='key'}</span>. 
</p>

<p>
	You can also hook into specific areas with a custom template allowing you to insert code before the opening tag, after the opening tag, before the closing tag or after the closing tag.
</p>

<p>
	 
</p>

<p>
	<video class="ipsEmbeddedVideo" controls="" data-fileid="196221" data-unique="59z57tkk3" id="ips_uid_9999_6" src="https://content.invisioncic.com/a319035/monthly_2023_10/CleanShot2023-10-19at13_17_16.mp4.6ee0b83ed476cf01f4a9f8ee8146ab9a.mp4" data-controller="core.global.core.embeddedvideo" preload="metadata">
		<source type="video/mp4" src="https://content.invisioncic.com/a319035/monthly_2023_10/CleanShot2023-10-19at13_17_16.mp4.6ee0b83ed476cf01f4a9f8ee8146ab9a.mp4"><a class="ipsAttachLink" data-fileext="mp4" data-fileid="196221" href="https://invisioncommunity.com/applications/core/interface/file/attachment.php?id=196221&amp;key=f21a71494f25a1c8395235b44c881cbd" rel="">CleanShot 2023-10-19 at 13.17.16.mp4</a>
	</source></video>
</p>

<p>
	For example, if you wanted to add something custom before the reply editor when viewing a topic, you would target that area like so:
</p>

<p>
	<a alt="Could contain: Page, Text, File, Computer Hardware, Electronics, Hardware, Monitor, Screen" class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="196218" href="//media.invisioncic.com/a319035/monthly_2023_10/theme_hook_topicreply.jpg.3ef145cda4cae046c9b0c66124018d69.jpg" rel=""><img alt="Could contain: Page, Text, File, Computer Hardware, Electronics, Hardware, Monitor, Screen" class="ipsImage ipsImage_thumbnailed" data-fileid="196218" data-unique="pmycxfl2u" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2023_10/theme_hook_topicreply.thumb.jpg.557c4f0b6b78f8b8286f92e33e27a4f9.jpg" loading="lazy" height="860"></a>
</p>

<p>
	The result, when viewed on the front end, is as follows:
</p>

<p>
	<a alt="Could contain: Text, Person, Head" class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="196217" href="//media.invisioncic.com/a319035/monthly_2023_10/theme_front-end-change.png.d28453c1529ac51f413c89d6d84b3bba.png" rel=""><img alt="Could contain: Text, Person, Head" class="ipsImage ipsImage_thumbnailed" data-fileid="196217" data-unique="pv7wvgxde" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2023_10/theme_front-end-change.thumb.png.84da2cfed10e0d7d9ebd6d6da310104e.png" loading="lazy" height="580"></a>
</p>

<p>
	These hookable areas are defined by a special tag that we add to the core templates. We would expect a lot of requests through the beta release and will likely create a request form so we can process them. We will try and accommodate as many areas as possible.
</p>

<p>
	While direct template editing is no longer possible in Invision Community v5, the new custom template and hook system allows you to add new functionality, while the new CSS framework makes it easier to target and change elements without the need to edit templates.
</p>

<p>
	We also added a suite of <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-i-r10/" rel="">new development tools</a> to enable you to target menus, data attributes and other areas where developers commonly had to edit templates before.
</p>

<p>
	The good news is that now custom templates are not built on top of our 'master' template engine; they are virtually upgrade-proof and do not require manual merging.
</p>

<p>
	<span style="font-size:16px;">Theme Designer Mode</span><br>
	Those who create themes for others have some extra tooling to enable them to build truly custom themes.
</p>

<p>
	Even though the theme editor has space for custom CSS, there is always a need for CSS that your customers cannot edit, and Invision Community 5 has a special area for that once Theme Designer Mode has been enabled.
</p>

<p>
	<a alt="Could contain: File, Page, Text, Computer Hardware, Electronics, Hardware, Monitor, Screen, Webpage" class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="196215" href="//media.invisioncic.com/a319035/monthly_2023_10/theme_designer.jpg.aa39f665cb1b939361d1e61dac05c3c2.jpg" rel=""><img alt="Could contain: File, Page, Text, Computer Hardware, Electronics, Hardware, Monitor, Screen, Webpage" class="ipsImage ipsImage_thumbnailed" data-fileid="196215" data-unique="uga86ahv5" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2023_10/theme_designer.thumb.jpg.153eb31f62a39ef6af31395df049a0a2.jpg" loading="lazy" height="960"></a>
</p>

<p>
	You also can add any ad-hoc javascript for when you want to hide elements or provide custom interactions.
</p>

<p>
	As direct CSS editing and direct template editing are no longer possible with Invision Community 5, there is no need for a 'sync' tool to copy from the filesystem.
</p>

<p>
	<span style="font-size:16px;">Conclusion</span><br>
	The new front-end theme editor is now the primary way to manage themes. This is where you upload logos and toggle settings.
</p>

<p>
	As you can see, theming may look different in Invision Community v5. Still, the new custom templates, theme designer tools and UI extensions provide a lot of functionality that means you can do nearly everything you did in v4, but often in an easier way.
</p>

<p>
	I'm sure you'll have many questions, so please add them below, and we'll do our best to answer them for you.
</p>

<p>
	<a alt="Could contain: File, Webpage, Computer, Electronics, Pc, Text" class="ipsAttachLink ipsAttachLink_image" data-fileext="jpg" data-fileid="196216" href="//media.invisioncic.com/a319035/monthly_2023_10/theme_editing_last.jpg.048991e2cb682c392c13c035342ef04d.jpg" rel="" title="Could contain: File, Webpage, Computer, Electronics, Pc, Text"><img alt="Could contain: File, Webpage, Computer, Electronics, Pc, Text" class="ipsImage ipsImage_thumbnailed" data-fileid="196216" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2023_10/theme_editing_last.thumb.jpg.83bc0a925a758d02340547a2d07e4d71.jpg" loading="lazy" height="650"></a>
</p>
]]></description><guid isPermaLink="false">14</guid><pubDate>Thu, 19 Oct 2023 14:12:27 +0000</pubDate></item><item><title>IC5: Extensions</title><link>https://invisioncommunity.com/developers/devblog/blog/ic5-extensions-r13/</link><description><![CDATA[
<p><img src="https://media.invisioncic.com/a319035/monthly_2023_09/dev.jpeg.124e78b26b9d4812bacdf2fb8ebf7fa6.jpeg" /></p>
<p>
	We've been dropping hints about various development features that haven't yet made their appearance in our previous blog entries. Now that hooks are no longer a possibility, we've expanded our Extensions system to allow developers to integrate with other areas within the framework.
</p>

<p>
	This blog entry will give an overview of our new Extensions. We are working on updating our developer documentation to include these changes.
</p>

<p>
	 
</p>

<p>
	<span style="font-size:18px;"><strong>AccountSettings</strong></span>
</p>

<p>
	Allows you to add tabs to the Account Settings page. This extension contains two methods:
</p>

<ul>
	<li>
		<em>getTab</em><br>
		Returns the key for the tab (or <em>null</em> to hide)
	</li>
	<li>
		<em>getContent</em><br>
		The tab content
	</li>
</ul>

<p>
	 
</p>

<p>
	<span style="font-size:18px;"><strong>Loader</strong></span>
</p>

<p>
	This extension was created primarily to allow you to load Javascript and CSS files in areas outside of your application, but has since been expanded to include other functionality. It is essentially an extension on the Dispatcher.
</p>

<p>
	Available methods:
</p>

<ul>
	<li>
		<em>css</em><br>
		Returns an array of CSS files to load
	</li>
	<li>
		<em>js</em><br>
		Returns an array of JS files to load
	</li>
	<li>
		<em>checkForRedirect</em><br>
		Redirect users to another location. This is especially useful for custom applications that would have previously bypassed an existing controller.
	</li>
	<li>
		<em>customError</em><br>
		Show a custom error message to the user instead of the standard IPS error messages.
	</li>
</ul>

<p>
	 
</p>

<p>
	<span style="font-size:18px;"><strong>SearchContent</strong></span>
</p>

<p>
	This is used to allow your application's content to the Search Index. Previously, the framework relied on the ContentRouter extension for this, and the use of the <span style="font-family:Courier New,Courier,monospace;">\IPS\Content\Searchable</span> interface. The interface has been removed, and searchable classes are determined by the new extension.
</p>

<p>
	The SearchContent extension contains one required method,<strong><em> </em></strong><em>supportedClasses</em>. You can also use this extension to override our default search logic for your content.
</p>

<p>
	 
</p>

<p>
	<span style="font-size:18px;"><strong>Other New Extensions</strong></span>
</p>

<ul>
	<li>
		<em>LoginHandler</em><br>
		Allows you to create additional Login methods
	</li>
	<li>
		<em>UIComment/UIItem/UINode/UIReview</em><br>
		UI Extensions, described <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-i-r10/" rel="">here</a>, <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-ii-r11/" rel="">here</a>, and <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-iii-r12/" rel="">here</a>
	</li>
	<li>
		<em>UserMenu</em><br>
		Allows you to add content to various menus. Additional information can be found <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-menus-r9/" rel="">here</a>.
	</li>
</ul>

<p>
	 
</p>

<p>
	<span style="font-size:18px;"><strong>New Commerce Extensions</strong></span>
</p>

<p>
	The following new extensions have been added to Commerce:
</p>

<ul>
	<li>
		<em>Gateway</em><br>
		Allows you to create new payment gateways
	</li>
	<li>
		<em>LicenseKey</em><br>
		Allows you to add new methods for generating and managing license keys
	</li>
	<li>
		<em>Payout</em><br>
		Allows you to create gateways for payouts
	</li>
</ul>

<p>
	 
</p>

<p>
	<span style="font-size:18px;"><strong>Deprecated Extensions</strong></span>
</p>

<p>
	The following Extensions are no longer supported and have been removed:
</p>

<ul>
	<li>
		BBCode
	</li>
	<li>
		ContentModeratorPermissions (use ModeratorPermissions instead)
	</li>
	<li>
		CreateMenu (replaced by UserMenu)
	</li>
	<li>
		IncomingEmail
	</li>
	<li>
		MemberForm
	</li>
</ul>

<p>
	 
</p>

<p>
	<strong>Reminder: Send us your Feedback</strong>
</p>

<p>
	Send us your questions or use-cases by submitting a topic <a href="https://invisioncommunity.com/forums/forum/533-invision-community-5-modification-questions/" rel="">here</a>. Please note that this is a private forum; you will not see topics posted by others, so you are free to share code samples if necessary. We will review your questions, and then aggregate them into an FAQ. The deadline for your question to be considered for the FAQ is October 15. After that you may still submit questions in the Contributor forum, where we will do our best to respond.
</p>

<p>
	 
</p>
]]></description><guid isPermaLink="false">13</guid><pubDate>Fri, 22 Sep 2023 20:42:05 +0000</pubDate></item><item><title>IC5: UI Extensions, Part III</title><link>https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-iii-r12/</link><description><![CDATA[
<p><img src="https://media.invisioncic.com/a319035/monthly_2023_08/519746372.jpeg.54a8f109eb90ddc918c117d91b87d38d.jpeg" /></p>
<p>
	By now you might be getting a little tired of hearing about our UI Extensions, but we still have a few more features to talk about. We showed you how to add <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-i-r10/" rel="">CSS and data attributes</a> to content. We discussed how to <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-ii-r11/" rel="">add form fields and menu items</a>. In this final entry on this tool, we'll talk about working with Nodes.
</p>

<p>
	 
</p>

<p>
	<strong><abbr title="Admin Control Panel"><abbr title="Admin Control Panel">ACP</abbr></abbr> Tools</strong>
</p>

<p>
	Nodes are different than the Items and Comments, as they are typically managed in the <abbr title="Admin Control Panel"><abbr title="Admin Control Panel">ACP</abbr></abbr> and not on the front-end. Therefore, we needed additional extension methods to allow developers to handle custom actions.
</p>

<p>
	Let's take a look at Nodes in the <abbr title="Admin Control Panel"><abbr title="Admin Control Panel">ACP</abbr></abbr>.
</p>

<p>
	<a alt="Could contain: Page, Text, File" class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="194241" href="//media.invisioncic.com/a319035/monthly_2023_07/image.png.e36859676dde9fd141d6d6ddf689d0ed.png" rel=""><img alt="Could contain: Page, Text, File" class="ipsImage ipsImage_thumbnailed" data-fileid="194241" data-unique="065v0eopi" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2023_07/image.thumb.png.5f00c8464e6ac1a3eb73ef53f8b5d5ba.png" loading="lazy" height="290"></a>
</p>

<p>
	There are a few areas where developers may need to insert custom logic. The UINode extension includes the following methods:
</p>

<ul><li>
		rowButtons<br>
		Returns an array of action buttons that will be added to the control strip on the right. This method functions identically to the <span style="font-family:Courier New,Courier,monospace;">Model::getButtons()</span> method.
	</li>
	<li>
		rowHtml<br>
		Inserts custom HTML to the left of the control strip.
	</li>
	<li>
		rowBadge<br>
		If a badge should be displayed, return an array with the CSS class and language string. Note that anything defined in the base class <span style="font-family:Courier New,Courier,monospace;">get__badge()</span> method will take precedence over your extension.
	</li>
</ul><p>
	 
</p>

<p>
	<span class="ipsBadge ipsBadge_icon ipsBadge_warning"><i class="fa fa-exclamation-circle"></i></span>  <strong>No Entry</strong>
</p>

<p>
	It is important to note that not all Node classes support the form-related methods. We have of course added support for all content containers, such as Forums and Calendars. We will consider including support for other classes upon request, however, nodes that handle infrastructure (e.g. Theme, Lang) will not be supported.
</p>

<p>
	We have also locked down some system-level classes (not all) where any extension is potentially destructive. Classes such as <span style="font-family:Courier New,Courier,monospace;">Application </span>and <span style="font-family:Courier New,Courier,monospace;">Module </span>cannot be extended.
</p>

<p>
	 
</p>

<p>
	<strong>OK, so what CAN I still do? AMA (well, almost anything).</strong>
</p>

<p>
	We still have a few more tools to show you, but at this point we'd like to hear from you. We know there is still a lot of uncertainty around how to migrate your modifications from v4 to v5, and we're here to assist with that. Send us your questions or use-cases by submitting a topic <a href="https://invisioncommunity.com/forums/forum/533-invision-community-5-modification-questions/" rel="">here</a>. Please note that this is a private forum; you will not see topics posted by others, so you are free to share code samples if necessary. We will review your questions, and then aggregate them into an FAQ. The deadline for your question to be considered for the FAQ is September 1. After that you may still submit questions in the Contributor forum, where we will do our best to respond.
</p>

<p>
	 
</p>

<p>
	 
</p>
]]></description><guid isPermaLink="false">12</guid><pubDate>Tue, 15 Aug 2023 12:48:00 +0000</pubDate></item><item><title>IC5: UI Extensions, Part II</title><link>https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-ii-r11/</link><description><![CDATA[
<p><img src="https://media.invisioncic.com/a319035/monthly_2023_08/p2.jpeg.0b556a58ecf914e50f4c05daaadfd32c.jpeg" /></p>
<p>
	In our  <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-i-r10/" rel="">previous blog entry</a>, we described the UI Extension and its overall capabilities. Today, we'll talk about how to use this new tool to extend content forms and menus.
</p>

<p>
	<a class="ipsAttachLink ipsAttachLink_image" href="//media.invisioncic.com/a319035/monthly_2023_08/CleanShot2023-08-03at16_38.03@2x.png.4ed9f2d6bbad3de39cccfc6310610d1e.png" data-fileid="194375" data-fileext="png" alt="Could contain: Page, Text, Business Card, Paper" rel=""><img class="ipsImage ipsImage_thumbnailed" data-fileid="194375" data-unique="xatefnb68" width="1000" alt="Could contain: Page, Text, Business Card, Paper" src="//media.invisioncic.com/a319035/monthly_2023_08/CleanShot2023-08-03at16_38.03@2x.thumb.png.29f90b996089531e3a96ce08d2206aec.png" loading="lazy" height="650"></a>
</p>

<p>
	<strong>Form Fields</strong>
</p>

<p>
	A popular modification request is to add fields to a Content Item, such as a Topic. All UI extension classes contain the following methods:
</p>

<ul><li>
		formElements<br>
		Returns an array of form elements that will be added to the form.
	</li>
</ul><p style="background-color:#eee;border:1px solid #ccc;padding:5px;">
	<strong>Note</strong>: Unlike other UI Extension methods, the first parameter of this method can be NULL, if you are creating a new item.
</p>

<ul><li>
		formPostSave<br>
		Allows you to process the fields that were added in the formElements method. Note that this method is triggered after the form is saved.
	</li>
</ul><p style="background-color:#eee;border:1px solid #ccc;padding:5px;">
	<strong>Note</strong>: This method has the same function as the ContentListener method <span style="font-family:Courier New,Courier,monospace;">onCreateOrEdit</span>, but we've added support for it here as well to avoid the creation of multiple classes, and to keep related code in the same place.
</p>

<p>
	 
</p>

<p>
	<strong>Element Placement</strong>
</p>

<p>
	By default, all new form elements will be inserted at the end of the form. However, there are times that you may want to insert your fields into specific positions within the form. You can use the new <span style="font-family:Courier New,Courier,monospace;">FormAbstract::setPosition()</span> method to specify where the element should be inserted.
</p>

<p>
	Let's look at an example.
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_2451_8" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> formElements</span><span class="pun">(</span><span class="pln"> </span><span class="pun">?</span><span class="typ">BaseItem</span><span class="pln"> $item</span><span class="pun">,</span><span class="pln"> </span><span class="pun">?</span><span class="typ">Model</span><span class="pln"> $container </span><span class="pun">):</span><span class="pln"> array
</span><span class="pun">{</span><span class="pln">
	</span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
		</span><span class="str">'my_new_field'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">(</span><span class="pln"> </span><span class="str">'my_new_field'</span><span class="pun">,</span><span class="pln"> $item </span><span class="pun">?</span><span class="pln"> $item</span><span class="pun">-&gt;</span><span class="pln">new_field </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
	</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	The above will append a Text field called "my_new_field" at the end of the Topic form.
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_9020_8" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> formElements</span><span class="pun">(</span><span class="pln"> </span><span class="pun">?</span><span class="typ">BaseItem</span><span class="pln"> $item</span><span class="pun">,</span><span class="pln"> </span><span class="pun">?</span><span class="typ">Model</span><span class="pln"> $container </span><span class="pun">):</span><span class="pln"> array
</span><span class="pun">{</span><span class="pln">
	$field </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Text</span><span class="pun">(</span><span class="pln"> </span><span class="str">'my_new_field'</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">null</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">true</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
	</span><span class="kwd">return</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
		</span><span class="str">'my_new_field'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> $field</span><span class="pun">-&gt;</span><span class="pln">setPosition</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Topic</span><span class="pun">::</span><span class="pln">$formLangPrefix </span><span class="pun">.</span><span class="pln"> </span><span class="str">'tags'</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Topic</span><span class="pun">::</span><span class="pln">$formLangPrefix </span><span class="pun">.</span><span class="pln"> </span><span class="str">'mainTab'</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
	</span><span class="pun">];</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	By using setPosition, I've placed the new field after the "tags" field, on the main tab in the form.
</p>

<p>
	 
</p>

<p>
	<strong>Extending Content Menus</strong>
</p>

<p>
	In <a href="https://invisioncommunity.com/developers/devblog/blog/ic5-menus-r9/" rel="">this blog entry</a>, we introduced our new approach to building menus. With UI extensions, you can add your own items to Item and Comment menus using the following methods:
</p>

<ul><li>
		UIItem::menuItems
	</li>
	<li>
		UIComment::menuItems
	</li>
</ul><p>
	We will discuss nodes and their menus in a future blog entry.
</p>

<p>
	 
</p>

<p>
	<strong>Extending Item Badges</strong>
</p>

<p>
	Similarly, you can use the UI Extension to add badges to the item header. The <span style="font-family:Courier New,Courier,monospace;">UIItem::badges()</span> method returns an array of badges and/or icons to be appended to the badge display.
</p>

<p>
	 
</p>

<p>
	This concludes our discussion of UI Extension features related to Items and Comments. In our next blog entry, we'll discuss Nodes and what additional methods are available.
</p>
]]></description><guid isPermaLink="false">11</guid><pubDate>Thu, 03 Aug 2023 15:00:00 +0000</pubDate></item><item><title>IC5: UI Extensions, Part I</title><link>https://invisioncommunity.com/developers/devblog/blog/ic5-ui-extensions-part-i-r10/</link><description><![CDATA[
<p><img src="https://media.invisioncic.com/a319035/monthly_2023_08/ui1.jpeg.41fe44a344165b4154f5e42fcc225e23.jpeg" /></p>
<p>
	Only a little over a week into our development tools announcements, and there is already lots of buzz about our new UI Extensions. The UI Extensions handle a variety of functionality, and we will discuss them all in detail, over the course of multiple blog entries. For today, we'll start with an introduction and an overview of some of the basic usage.
</p>

<p>
	 
</p>

<p>
	<strong>Features and Functionality</strong>
</p>

<p>
	UI Extensions are extensions to a single content class - Nodes, Items, Comments, or Reviews. With a UI Extension, you can:
</p>

<ul><li>
		Add custom CSS classes and data attributes
	</li>
	<li>
		Add options to content menus
	</li>
	<li>
		Add badges to content item headers
	</li>
	<li>
		Add fields to content forms
	</li>
	<li>
		<abbr title="Admin Control Panel"><abbr title="Admin Control Panel">ACP</abbr></abbr>-related functionality (for nodes)
	</li>
</ul><p>
	And a couple of other small things that we'll cover here.
</p>

<p>
	<a alt="Could contain: Computer, Electronics, Tablet Computer, Page, Text" class="ipsAttachLink ipsAttachLink_image" data-fileext="png" data-fileid="194323" href="//media.invisioncic.com/a319035/monthly_2023_08/image.png.87d707d47877dbca730d73134d5eb18e.png" rel=""><img alt="Could contain: Computer, Electronics, Tablet Computer, Page, Text" class="ipsImage ipsImage_thumbnailed" data-fileid="194323" data-unique="088xi17aj" style="height: auto;" width="1000" src="//media.invisioncic.com/a319035/monthly_2023_08/image.thumb.png.5d0a449ed1c57528040628fc46866d34.png" loading="lazy" height="700"></a>
</p>

<p>
	<strong>Setup</strong>
</p>

<p>
	The Application Developer Center has 4 new options in the Extensions tab:
</p>

<ul><li>
		UIComment
	</li>
	<li>
		UIItem
	</li>
	<li>
		UINode
	</li>
	<li>
		UIReview
	</li>
</ul><p>
	To add a UI extension, simply create a class under the appropriate extension type.
</p>

<p style="background-color:#eee;border:1px solid #ccc;padding:5px;">
	<strong>Note</strong>: We are working on improving this process, however, those changes will likely be introduced at a later date.
</p>

<p>
	The default extension file will be generated in the appropriate directory within your application's extensions folder. All UI Extensions are abstracted, so if you omit a method from your class, or if new methods are introduced later on, your extension will not throw an error.
</p>

<p>
	All UI Extension methods receive the object as the first parameter.
</p>

<p>
	 
</p>

<p>
	<strong>UI Extensions for... UI</strong>
</p>

<p>
	The idea for UI Extensions was born when we were reviewing the most common third-party development hooks. With the removal of theme hooks, we needed a way for developers to add custom CSS and attributes to areas such as topic rows, the author panel, among others. Even with the theme hooks in v4, the way to accomplish this was less than ideal. Adding a CSS class typically required at least 2 hooks per template - one to determine the correct class (usually based on custom logic) and one to insert the class as a variable. It was tedious and finicky at best.
</p>

<p>
	All UI Extensions include the following methods:
</p>

<ul><li>
		css<br>
		Returns an array of CSS classes that will be applied to the object
	</li>
	<li>
		dataAttributes<br>
		Returns an array of data attributes (as "key=value", "key=value") that will be applied to the object
	</li>
</ul><p>
	To insert those values into the appropriate location, our templates now contain the following syntax:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2203_11" style=""><span class="pln">{$object-&gt;ui( 'css' )}</span></pre>

<p>
	and
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_2203_9" style=""><span class="pln">{$object-&gt;ui( 'dataAttributes' )}</span></pre>

<p>
	Let's examine the <span style="font-family:Courier New,Courier,monospace;">Content::ui()</span> method (identical to <span style="font-family:Courier New,Courier,monospace;">Model::ui()</span>.)
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_2203_13" style=""><span class="kwd">public</span><span class="pln"> </span><span class="kwd">function</span><span class="pln"> ui</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">string</span><span class="pln"> $method</span><span class="pun">,</span><span class="pln"> </span><span class="pun">?</span><span class="pln">array $payload</span><span class="pun">=</span><span class="pln">array</span><span class="pun">(),</span><span class="pln"> </span><span class="kwd">bool</span><span class="pln"> $returnAsArray</span><span class="pun">=</span><span class="kwd">false</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">string</span><span class="pln"> $separator </span><span class="pun">=</span><span class="pln"> </span><span class="str">" "</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> array</span><span class="pun">|</span><span class="kwd">string</span></pre>

<p>
	The ui method accepts 4 arguments: the method name, an optional payload (determined based on the method being called), the option to return as an array (vs a string), and an optional separator (when data is returned as a string). The defaults for this method were set for the simplest, shortest syntax within the templates, as seen above. You may also see syntax like this:
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_2233_7" style=""><span class="kwd">foreach</span><span class="pun">(</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">ui</span><span class="pun">(</span><span class="pln"> </span><span class="str">'menuItems'</span><span class="pun">,</span><span class="pln"> array</span><span class="pun">(),</span><span class="pln"> TRUE </span><span class="pun">)</span><span class="pln"> </span><span class="kwd">as</span><span class="pln"> $key </span><span class="pun">=&gt;</span><span class="pln"> $link </span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
	$links</span><span class="pun">[</span><span class="pln">$key</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> $link</span><span class="pun">;</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	As you can see, in this case the ui method will return the results as an array.
</p>

<p>
	 
</p>

<p>
	<strong>Class-Specific Methods</strong>
</p>

<p>
	Some of our extensions include methods that are applicable only to objects of that type.
</p>

<ul><li>
		UIItem::sidebar()<br>
		Returns HTML that will be included in the contextual sidebar when viewing the item
	</li>
	<li>
		UIComment::authorPanel()<br>
		Returns HTML to be inserted into the author panel of a post
	</li>
</ul><p style="background-color:#eee;border:1px solid #ccc;padding:5px;">
	<strong>Note</strong>: All methods in the UIComment extension are also available in UIReview.
</p>

<p>
	 
</p>

<p>
	We hope this entry gave you a glimpse into what will be available with this new tool, and a better understanding of the direction we are taking with our toolkit. We will discuss the other features of this tool in upcoming blog entries.
</p>
]]></description><guid isPermaLink="false">10</guid><pubDate>Wed, 02 Aug 2023 09:33:00 +0000</pubDate></item><item><title>IC5: Menus</title><link>https://invisioncommunity.com/developers/devblog/blog/ic5-menus-r9/</link><description><![CDATA[
<p><img src="https://media.invisioncic.com/a319035/monthly_2023_07/menus.jpeg.07dd78d59a4b897de4030ad374b6b194.jpeg" /></p>
<p>
	<span style="font-size:18px;font-weight:normal">Action and Moderation Menus can be one of the most tedious development tasks, while also being critical to user experience.</span>
</p>

<p>
	For example, we may add support for pinning/unpinning content, then we need to remember to include the ability to pin/unpin that content in all the HTML templates. 3rd party developers add screens inside their applications, and then they need to add a link to the User Menu to make that accessible.
</p>

<p>
	With Invision Community 4, this would require a template hook targeting fairly complex classes and children that was susceptible to structural changes to templates between versions.
</p>

<p>
	In Invision Community 5, we have made significant changes to menu creation in order to streamline the process, and also to allow 3rd party developers better accessibility.
</p>

<p>
	 
</p>

<p>
	<strong>New Helper Classes</strong>
</p>

<p>
	Menus are now built using helper classes, rather than relying on lengthy HTML templates. A menu is created by initializing a new Menu object.
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_4379_13" style=""><span class="pln">$menu </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Menu</span><span class="pun">(</span><span class="pln"> </span><span class="str">'AccountMenu'</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	We can then add elements to the menu.
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_4379_11" style=""><span class="pln">$menu</span><span class="pun">-&gt;</span><span class="pln">addTitleField</span><span class="pun">(</span><span class="pln"> </span><span class="str">'menu_content'</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
$menu</span><span class="pun">-&gt;</span><span class="kwd">add</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Link</span><span class="pun">(</span><span class="pln"> $member</span><span class="pun">-&gt;</span><span class="pln">url</span><span class="pun">(),</span><span class="pln"> </span><span class="str">'menu_profile'</span><span class="pun">,</span><span class="pln"> icon</span><span class="pun">:</span><span class="str">'fa-solid fa-user'</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
$menu</span><span class="pun">-&gt;</span><span class="pln">addSeparator</span><span class="pun">();</span><span class="pln">
$menu</span><span class="pun">-&gt;</span><span class="kwd">add</span><span class="pun">(</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Link</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Url</span><span class="pun">::</span><span class="kwd">internal</span><span class="pun">(</span><span class="pln"> </span><span class="str">"app=core&amp;module=system&amp;controller=markread"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"front"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"mark_site_as_read"</span><span class="pln"> </span><span class="pun">)-&gt;</span><span class="pln">csrf</span><span class="pun">()-&gt;</span><span class="pln">addRef</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Request</span><span class="pun">::</span><span class="pln">i</span><span class="pun">()-&gt;</span><span class="pln">url</span><span class="pun">()</span><span class="pln"> </span><span class="pun">),</span><span class="pln"> </span><span class="str">'mark_site_read'</span><span class="pun">,</span><span class="pln"> dataAttributes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">[</span><span class="pln">
			</span><span class="str">'data-action'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'markSiteRead'</span><span class="pun">,</span><span class="pln">
			</span><span class="str">'data-controller'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'core.front.core.markRead'</span><span class="pln">
		</span><span class="pun">],</span><span class="pln"> icon</span><span class="pun">:</span><span class="pln"> </span><span class="str">'fa-solid fa-eye'</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span></pre>

<p>
	The most common menu element is the Link. This will generate the appropriate &lt;li&gt; element within the menu. You can define the URL, CSS class (default is ipsMenu_item), data attributes, icon, title, among other properties.
</p>

<p>
	Titles and Separators are added using the <span style="font-family:Courier New,Courier,monospace;">Menu::addTitleField()</span> and <span style="font-family:Courier New,Courier,monospace;">Menu::addSeparator()</span> methods respectively.
</p>

<p>
	You can also insert custom HTML into the menu using the <span style="font-family:Courier New,Courier,monospace;">Menu::addHtml()</span> method.
</p>

<p>
	In your HTML template, you would then simply display the menu object as a string.
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4379_25" style=""><span class="pln">{{if $menu-&gt;hasContent()}}
{$menu|raw}
{{endif}}</span></pre>

<p>
	The menu templates will include the link that opens the menu, as well as the menu itself.
</p>

<p>
	You'll notice that the above contains a call to the <span style="font-family:Courier New,Courier,monospace;">hasContent() </span>method. This method will check if the menu object has any elements inside; if a user does not have permission to any of the elements, nothing will be displayed.
</p>

<p>
	 
</p>

<p>
	<strong>Content Menus</strong>
</p>

<p>
	Almost all content items and comments (and reviews) require some kind of menu for moderation. Previously, this meant creating large chunks of redundant HTML code throughout the codebase. We've implemented <span style="font-family:Courier New,Courier,monospace;">\IPS\Content\Item::menu()</span> and <span style="font-family:Courier New,Courier,monospace;">\IPS\Content\Comment::menu()</span> to build these menus in a central location. The new methods include checks for whether a feature is in use and whether the user has permission to perform this action.
</p>

<p>
	Example:
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_4379_17" style=""><span class="kwd">if</span><span class="pun">(</span><span class="pln"> IPS</span><span class="pun">::</span><span class="pln">classUsesTrait</span><span class="pun">(</span><span class="pln"> $this</span><span class="pun">,</span><span class="pln"> </span><span class="str">'IPS\Content\Pinnable'</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> AND $this</span><span class="pun">-&gt;</span><span class="pln">canPin</span><span class="pun">(</span><span class="pln"> $member </span><span class="pun">)</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
	$links</span><span class="pun">[</span><span class="str">'pin'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">ContentMenuLink</span><span class="pun">(</span><span class="pln"> $this</span><span class="pun">-&gt;</span><span class="pln">url</span><span class="pun">()-&gt;</span><span class="pln">csrf</span><span class="pun">()-&gt;</span><span class="pln">setQueryString</span><span class="pun">(</span><span class="pln"> array</span><span class="pun">(</span><span class="pln"> </span><span class="str">'do'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'moderate'</span><span class="pun">,</span><span class="pln"> </span><span class="str">'action'</span><span class="pln"> </span><span class="pun">=&gt;</span><span class="pln"> </span><span class="str">'pin'</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">),</span><span class="pln"> </span><span class="str">'pin'</span><span class="pln">  </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	Now, the HTML template simply contains:
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4379_19" style=""><span class="pln">{$entry-&gt;menu()|raw}</span></pre>

<p style="background-color:#eee;border:1px solid #ccc;padding:5px;">
	(<strong>Note</strong>: Yes, these content menus can be extended using... you guessed it. UI Extensions.)
</p>

<p>
	 
</p>

<p>
	<strong>Other Menus</strong>
</p>

<p>
	We have also migrated the following menus to the new structure:
</p>

<ul><li>
		User Menu
	</li>
	<li>
		Mobile Menu
	</li>
	<li>
		Create Menu
	</li>
</ul><p>
	 
</p>

<p>
	<strong>Badges</strong>
</p>

<p>
	Another area with redundant HTML was our content badges. For example, pinned topics will display an icon on both the topic list and the topic header. We have created helper classes for badges and badge icons to centralize this logic. Within the new <span style="font-family:Courier New,Courier,monospace;">Item::badges()</span> method, we build an array of icons that will be shown.
</p>

<pre class="ipsCode prettyprint lang-php prettyprinted" id="ips_uid_9806_7" style=""><span class="kwd">if</span><span class="pun">(</span><span class="pln"> IPS</span><span class="pun">::</span><span class="pln">classUsesTrait</span><span class="pun">(</span><span class="pln"> $this</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Featurable</span><span class="pun">::</span><span class="kwd">class</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> AND $this</span><span class="pun">-&gt;</span><span class="pln">isFeatured</span><span class="pun">()</span><span class="pln"> </span><span class="pun">)</span><span class="pln">
</span><span class="pun">{</span><span class="pln">
	$return</span><span class="pun">[</span><span class="str">'featured'</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">Icon</span><span class="pun">(</span><span class="pln"> </span><span class="typ">Badge</span><span class="pun">::</span><span class="pln">BADGE_POSITIVE</span><span class="pun">,</span><span class="pln"> </span><span class="str">'fa-solid fa-star'</span><span class="pun">,</span><span class="pln"> </span><span class="typ">Member</span><span class="pun">::</span><span class="pln">loggedIn</span><span class="pun">()-&gt;</span><span class="pln">language</span><span class="pun">()-&gt;</span><span class="pln">addToStack</span><span class="pun">(</span><span class="pln"> </span><span class="str">'featured'</span><span class="pln"> </span><span class="pun">)</span><span class="pln"> </span><span class="pun">);</span><span class="pln">
</span><span class="pun">}</span></pre>

<p>
	The above generates a badge icon with the badge type <span style="font-family:Courier New,Courier,monospace;">ipsBadge--positive</span> (constants have been declared for all common types, but any class can be used), the star icon, and the "featured" language string as the tooltip.
</p>

<p>
	In our template HTML, we now have
</p>

<pre class="ipsCode prettyprint lang-html prettyprinted" id="ips_uid_4155_11" style=""><span class="tag">&lt;div</span><span class="pln"> </span><span class="atn">class</span><span class="pun">=</span><span class="atv">'ipsBadges'</span><span class="tag">&gt;</span><span class="pln">
	{{foreach $entry-&gt;badges() as $badge}}{$badge|raw}{{endforeach}}
</span><span class="tag">&lt;/div&gt;</span></pre>

<p>
	 
</p>

<p>
	<strong>UserMenu Extension</strong>
</p>

<p>
	Now that we've moved the menus into the codebase and out of the templates, we needed a way to allow 3rd party developers to insert their own menu items. We've introduced a new extension called UserMenu.
</p>

<p>
	The UserMenu extension contains the following methods:
</p>

<ul><li>
		accountMenu<br>
		This method allows you to add menu elements to the user menu. The method includes a parameter to allow you to add elements in one of 3 places within the menu: <em>content, settings, </em>or <em>logout</em>.
	</li>
	<li>
		mobileMenu<br>
		Allows you to add elements to the mobile navigation menu. All elements are inserted before the "sign out" option.
	</li>
	<li>
		createMenu<br>
		Replaces the CreateMenu extension, which has been deprecated.
	</li>
	<li>
		accountSettingsLinks<br>
		Allows you to add links to the inner sidebar in the Account Settings overview tab (under the link for Notification Settings).
	</li>
</ul><p style="background-color:#eee;border:1px solid #ccc;padding:5px;">
	<strong>Note:</strong> No, this does not add tabs to the Account Settings page, but<em> </em>fear not, that's coming. (Not in the UI Extension.)
</p>

<ul><li>
		userNav<br>
		This method will insert HTML in the user bar, to the left of the Create Menu.
	</li>
	<li>
		mobileNav<br>
		Similar to the userNav method, but inserts HTML into the mobile navigation header.
	</li>
</ul><p>
	 
</p>

<p>
	What do you think of our changes to the Menus? Let us know in the comments below.
</p>
]]></description><guid isPermaLink="false">9</guid><pubDate>Mon, 31 Jul 2023 11:27:00 +0000</pubDate></item></channel></rss>
