Jump to content

Esther E.

Invision Community Team
  • Posts

    87
  • Joined

  • Last visited

  • Days Won

    2

 Content Type 

Downloads

Release Notes

IPS4 Guides

IPS4 Developer Documentation

Invision Community Blog

Development Blog

Deprecation Tracker

Providers Directory

Forums

Events

Store

Gallery

Everything posted by Esther E.

  1. You still have full control if you're building a custom app. If you're creating something new, you still can overload the base class methods and arrange things any way you want. It's just the IPS classes that can no longer be manipulated.
  2. Profile Fields are nodes. Just throwing that out there.
  3. Can you be more specific please?
  4. 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. Features and Functionality UI Extensions are extensions to a single content class - Nodes, Items, Comments, or Reviews. With a UI Extension, you can: Add custom CSS classes and data attributes Add options to content menus Add badges to content item headers Add fields to content forms ACP-related functionality (for nodes) And a couple of other small things that we'll cover here. Setup The Application Developer Center has 4 new options in the Extensions tab: UIComment UIItem UINode UIReview To add a UI extension, simply create a class under the appropriate extension type. Note: We are working on improving this process, however, those changes will likely be introduced at a later date. 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. All UI Extension methods receive the object as the first parameter. UI Extensions for... UI 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. All UI Extensions include the following methods: css Returns an array of CSS classes that will be applied to the object dataAttributes Returns an array of data attributes (as "key=value", "key=value") that will be applied to the object To insert those values into the appropriate location, our templates now contain the following syntax: {$object->ui( 'css' )} and {$object->ui( 'dataAttributes' )} Let's examine the Content::ui() method (identical to Model::ui().) public function ui( string $method, ?array $payload=array(), bool $returnAsArray=false, string $separator = " " ) : array|string 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: foreach( $this->ui( 'menuItems', array(), TRUE ) as $key => $link ) { $links[$key] = $link; } As you can see, in this case the ui method will return the results as an array. Class-Specific Methods Some of our extensions include methods that are applicable only to objects of that type. UIItem::sidebar() Returns HTML that will be included in the contextual sidebar when viewing the item UIComment::authorPanel() Returns HTML to be inserted into the author panel of a post Note: All methods in the UIComment extension are also available in UIReview. 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.
  5. Esther E.

    IC5: Menus

    Action and Moderation Menus can be one of the most tedious development tasks, while also being critical to user experience. 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. 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. 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. New Helper Classes Menus are now built using helper classes, rather than relying on lengthy HTML templates. A menu is created by initializing a new Menu object. $menu = new Menu( 'AccountMenu' ); We can then add elements to the menu. $menu->addTitleField( 'menu_content' ); $menu->add( new Link( $member->url(), 'menu_profile', icon:'fa-solid fa-user' ) ); $menu->addSeparator(); $menu->add( new Link( Url::internal( "app=core&module=system&controller=markread", "front", "mark_site_as_read" )->csrf()->addRef( Request::i()->url() ), 'mark_site_read', dataAttributes: [ 'data-action' => 'markSiteRead', 'data-controller' => 'core.front.core.markRead' ], icon: 'fa-solid fa-eye' ) ); The most common menu element is the Link. This will generate the appropriate <li> element within the menu. You can define the URL, CSS class (default is ipsMenu_item), data attributes, icon, title, among other properties. Titles and Separators are added using the Menu::addTitleField() and Menu::addSeparator() methods respectively. You can also insert custom HTML into the menu using the Menu::addHtml() method. In your HTML template, you would then simply display the menu object as a string. {{if $menu->hasContent()}} {$menu|raw} {{endif}} The menu templates will include the link that opens the menu, as well as the menu itself. You'll notice that the above contains a call to the hasContent() 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. Content Menus 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 \IPS\Content\Item::menu() and \IPS\Content\Comment::menu() 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. Example: if( IPS::classUsesTrait( $this, 'IPS\Content\Pinnable' ) AND $this->canPin( $member ) ) { $links['pin'] = new ContentMenuLink( $this->url()->csrf()->setQueryString( array( 'do' => 'moderate', 'action' => 'pin' ) ), 'pin' ); } Now, the HTML template simply contains: {$entry->menu()|raw} (Note: Yes, these content menus can be extended using... you guessed it. UI Extensions.) Other Menus We have also migrated the following menus to the new structure: User Menu Mobile Menu Create Menu Badges 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 Item::badges() method, we build an array of icons that will be shown. if( IPS::classUsesTrait( $this, Featurable::class ) AND $this->isFeatured() ) { $return['featured'] = new Icon( Badge::BADGE_POSITIVE, 'fa-solid fa-star', Member::loggedIn()->language()->addToStack( 'featured' ) ); } The above generates a badge icon with the badge type ipsBadge--positive (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. In our template HTML, we now have <div class='ipsBadges'> {{foreach $entry->badges() as $badge}}{$badge|raw}{{endforeach}} </div> UserMenu Extension 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. The UserMenu extension contains the following methods: accountMenu 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: content, settings, or logout. mobileMenu Allows you to add elements to the mobile navigation menu. All elements are inserted before the "sign out" option. createMenu Replaces the CreateMenu extension, which has been deprecated. accountSettingsLinks Allows you to add links to the inner sidebar in the Account Settings overview tab (under the link for Notification Settings). Note: No, this does not add tabs to the Account Settings page, but fear not, that's coming. (Not in the UI Extension.) userNav This method will insert HTML in the user bar, to the left of the Create Menu. mobileNav Similar to the userNav method, but inserts HTML into the mobile navigation header. What do you think of our changes to the Menus? Let us know in the comments below.
  6. I can think of other ways to accomplish this, but we're not at a point where I can get into how without causing confusion. Suffice it to say, I do think it's still possible.
  7. This can be handled with a Member listener onProfileUpdate (change to pp_last_visitors) This as well. (members_disable_pm) This one I'll add to the list for discussion.
  8. That's not really something that would be handled by a listener; if anything, it would be in the core code. That said, I'm not entirely sure that what you're suggesting is the correct behavior. What if one moderator approves a post and another moderator disagrees with that approval? I don't want to go off topic here (as I said, it's not really a "listeners" thing), but it would be an interesting point of discussion.
  9. Good ideas, we've started a list.
  10. Maybe a cosplaying me from 20 years ago....
  11. We literally changed code this morning because of your feedback on adding a listener to base classes. 🤔
  12. Perhaps when you have the full picture of all the available tools, you might come up with other ways. Very hard for me to say definitively if I don't know what you're trying to do.
  13. That's the kind of thing we don't really want people doing. 😉 Hooking into a core class like the Text field, where it's referenced literally everywhere, can have unintended consequences. So no, that wouldn't be possible.
  14. Listeners is just one tool in the box. I would suggest waiting until you see the full structure before worrying about what you can and cannot do.
  15. Without looking at the source code of your modification, I am pretty sure it can still be done. Just from looking at the screenshots and the feature list.
  16. That will be dealt with in UIExtensions. We're wrapping some things up on those before the blog entry, but it's coming. We will be removing the block on base classes and you'll be able to add listeners to \IPS\Content\Item (or Comment or Review).
  17. Are you referring to adding fields to the form?
  18. I'm very excited to be posting my first blog entry for IPS, and thrilled that this is what I get to post. When we started planning the new development tools for v5, we looked at existing resources - from the marketplace, from some contributors, and from our own managed clients - and asked, what is it that developers find themselves doing the most often? Matt’s previous blog entry gave a brief summary of some of those functions. Today’s blog entry is going to focus on one of our powerful new tools, Listeners. One common reason for a code hook was to execute custom code when a specific action occurs. For example, when a topic is created, or when a new reply is posted. Listeners allow you to execute your code at key points in the workflow, without the worry of finding exactly the right hook location. Here is an excerpt from the BlogComment listener in our Cloud application: Creating Listeners Let’s start with the application’s Developer Center. We’ve added a new tab for Listeners. This tab lists all your existing listeners, as well as the class on which it is listening. You’ll notice that each listener can only observe a single class. While this may seem slightly tedious, the idea here was that as a developer, you should be conscious of what class you are working with. It also eliminates the need to check what type of object is being passed preventing accidents and unintended consequences when missing class checks. When you add a listener, there are several listener types available. We will discuss each one in detail. Content Listener This listener is fired on an Item or Comment (or Review) class. When adding a Content Listener, you must specify the class you are observing. When you hit Save, there is a validation check in place to ensure that the class exists, and that it is a valid class for the selected listener type. Content Listeners have the following methods available. All methods are included in the default listener file that is generated. onLoad Triggered when an object is loaded (in Content::constructFromData) NOTE: Be careful! This event could be fired at unexpected times. If you’re executing code here, you may want to check the dispatcher instance to verify that your code is running where you expect. onBeforeCreateOrEdit Fired when a form is submitted, before it is saved. This is the equivalent of Item::processBeforeCreate and Item::processBeforeEdit. This method includes a parameter to indicate whether this is a create or edit. onCreateOrEdit Fired after a form is saved. This is the equivalent of Item::processAfterCreate and Item::processAfterEdit. This method includes a parameter to indicate whether this is a create or edit. onDelete Fired after an object is deleted. onStatusChange Fired when any moderation action occurs. For example, when an item is pinned/unpinned, locked/unlocked. The moderation action is passed as a parameter so that your code can take that into account. onMerge Triggered AFTER an item is merged. onItemView Triggered when an item is viewed and the view count is incremented. Invoice Listener An Invoice listener is fired only on the \IPS\nexus\Invoice class. This listener does not require you to specify a class to extend. The Invoice listener includes a single method, onStatusChange. This is fired when the invoice is saved and the status changes (pending/paid/expired/canceled). Member Listener The Member listener will be familiar to many of you. We have taken the MemberSync extension and moved it in its entirety to a listener, as it was a better fit for this structure. All the previous methods that were available are still available here. We have also added the following new methods: onJoinClub Fired when a member joins a club. If approval is required, this is fired after they are approved. onLeaveClub Fired when a member leaves or is removed from a club. onEventRsvp Fired when a member responds to an event. The response is included as a parameter to this method. Commerce Package Listener The Commerce Package listener is fired on any implementation of \IPS\nexus\Invoice\Item. You must specify the class that you are observing. The methods are the same as those that are available in an item extension. onCancel onChange onDelete onExpireWarning onExpire onInvoiceCancel onPaid onPurchaseGenerated onReactivate onRenew onTransfer onUnpaid Some Technical Notes All listener files will be generated in a “listeners” directory within your application. Your application’s data directory will include a listeners.json file that defines all your listeners. The json file is automatically generated when you add/remove a listener. All listener methods are optional. The default class that is generated will include all available methods, but they do not have to be present in your file. All listener methods are return type void. If a listener method fails, an exception will be thrown when IN_DEV is enabled. In production, the exception will be logged with a reference to the listener class and the event on which the exception occurred. Firing Events Your code can fire any existing event using the Event::fire method. Example: Event::fire( 'onBeforeCreateOrEdit', $comment, array( $values ) ); The Event::fire method is called statically and accepts an event name, the object on which the event will be fired, and an array of additional parameters (varies according to the event that you are triggering). This concludes our introduction to Listeners. We do intend to implement additional listener types and events based on your feedback and as the need arises.
  19. What you're asking for is not just "laborious", as you described, but pretty much near impossible. We'd have to list literally every single element of every array (in the example you gave there are probably close to 50 possibilities alone) in every template. Anyone even slightly familiar with the code base would have no issue figuring that out, so this would be a very big effort targeting a very small number of people... and maintaining it would be a nightmare.
×
×
  • Create New...