-
Posts
24,413 -
Joined
-
Last visited
-
Days Won
84
Content Type
Downloads
Release Notes
IPS4 Guides
IPS4 Developer Documentation
Invision Community Blog
Development Blog
Deprecation Tracker
Providers Directory
Projects
Release Notes v5
Forums
Events
Store
Gallery
Everything posted by Rikki
-
Inheritance chain Your comment model extends several classes. In turn, these are: \IPS\Content\Comment Provides the features of Comments. The rest of the guides in this section cover implementing these features. \IPS\Content Provides a small number of features which are common to both Content Item models and Content Comment models such as getting the author and working with $databaseColumnMap. \IPS\Patterns\ActiveRecord Provides the functionality to load items from the database, work with their properties, save and delete them. See the Active Records guide for more information on this class. Basic skeleton namespace IPS\yourApp; class _Comment extends \IPS\Content\Comment { /** * @brief [ActiveRecord] Multiton Store */ protected static $multitons; /** * @brief Default Values */ protected static $defaultValues = NULL; /** * @brief [Content\Comment] Item Class */ public static $itemClass = 'IPS\yourApp\YourClass'; /** * @brief [ActiveRecord] Database Table */ public static $databaseTable = 'yourapp_comments'; /** * @brief [ActiveRecord] Database Prefix */ public static $databasePrefix = 'comment_'; /** * @brief Title */ public static $title = ‘thing_comments’; /** * @brief Database Column Map */ public static $databaseColumnMap = array( 'item' => 'fid', 'author' => 'mid', 'author_name' => 'author', 'content' => 'text', 'date' => 'date', 'ip_address' => 'ip_address' ); } Specifying your class properties Comment models require a few static properties to configure their behavior, most of which are identical to the Content Item model. Many come from the Active Record class. public static $application = 'string'; Required. The application key that the comment belongs to. public static $module = 'string'; Required. The module key that the comment belongs to. public static $multitons = array(); public static $defaultValues = NULL; Required. Inherited from \IPS\Patterns\ActiveRecord. These two properties are requirements of \IPS\Patterns\ActiveRecord. They don't need a value assigned; they simply need to be defined. public static $databaseTable = 'string'; Required. Inherited from \IPS\Patterns\ActiveRecord. The name of the database table that stores these comments. public static $databasePrefix = 'string'; Optional. Inherited from \IPS\Patterns\ActiveRecord. Specifies the field prefix this table uses. public static $databaseColumnMap = array(); Required. Features provided by the higher classes your model extends will examine this array to find out what columns certain things are stored as in your database. The following elements are required, and in all cases the value is with the $databasePrefix omitted: item Required. Should contain the column name that holds the ID value of the content item this comment belongs to. author Required. Should contain the column name that holds the ID number of the member who posted the content. content Required. Should contain the column name that holds the actual comment text. date Required. Should contain the column name that holds a unix timestamp of when the comment was posted. ip_address Required. Should contain the column name that holds the IP address of the user who posted the comment. author_name Optional. Can contain the column name that holds the username of the member who posted the comment. first Optional. Can contain the column name that holds a boolean value indicating if this is the first comment on the item. public static $commentTemplate = 'string'; Optional. Specifies the template to be used when displaying the comment (see the html() method below). For example: array( array( 'global', 'core', 'front' ), 'commentContainer' ); public static $formTemplate = 'string'; Optional. Specifies the template to be used to display the comment form. For example: array( array( 'forms', 'core', 'front' ), 'commentTemplate' ); public static $formLangPrefix = 'string'; Optional. Specifies a prefix to add to language strings used by the model, allowing you to create custom language strings for your purpose. Available methods on the Comment model In addition to those provided by \IPS\Patterns\ActiveRecord (which work exactly the same as for content item models), a number of additional methods are available. \IPS\Http\Url url( string $action ) Returns the URL to the comment, automatically calculating the the correct page number and including an anchor to the correct page location. This method is already defined for you. $action (string, optional) If passed, will add a 'do=value' parameter to the URL. \IPS\Content\Item item() Returns the content item that this comment belongs to. IPS\Member author() Returns the \IPS\Member object for the user that posted the content item. For example: $comment = YourClass::load( 1 ); $user = $comment->author(); echo $comment->name; string truncated( [ boolean $oneLine ] ) Returns the content in a format suitable for use with the data-ipsTruncate widget. $oneLine (boolean, optional, default FALSE) By default, paragraphs are turned into line break. If this parameter is TRUE, paragraphs are replaced with spaces instead of line breaks, making the truncated content suitable for a one-line display. boolean canEdit( [ \IPS\Member $member ] ) Returns a boolean value indicating if the provided member can edit the content item. $member (\IPS\Member, optional) If provided, uses this member's permissions when performing the check. By default, the currently-logged in member will be used. boolean canDelete( [ \IPS\Member $member ] ) Returns a boolean value indicating if the provided member can delete the content item $member (\IPS\Member, optional) If provided, uses this member's permissions when performing the check. By default, the currently-logged in member will be used. boolean static modPermission( string $type [, \IPS\Member $member [, \IPS\Node\Model $container ] ] ) Returns a boolean value indicating if the provided member has permission to perform the action specified by the $type param in the specified $container (if provided) $type (string, required) The type of permission being checked. Acceptable values are: edit delete move hide (if Hiding/Approving is enabled) unhide (if Hiding/Approving is enabled) view_hidden (if Hiding/Approving is enabled) $member (\IPS\Member, optional) If provided, uses this member's permissions when performing the check. By default, the currently-logged in member will be used. $container (\IPS\Node\Model, optional) If provided, checks the permission specifically in this container node. void modAction( string $type [, \IPS\Member $member [, string $reason ] ] ) Performs the specified moderation action. Throws OutOfRangeException if the member does not have permission to perform this action. $type (string, required) The type of moderation action being performed. Consult the list in the previous method for acceptable values. $member (\IPS\Member, optional) If provided, uses this member's permissions when performing the check. By default, the currently-logged in member will be used. $reason (string, optional) Used only for hide/unhide actions; specifies the reason the action is being taken. boolean isFirst() Returns true or false indicating whether this is the first comment on the content item. boolean isIgnored( [ \IPS\Member $member ] ) Indicates whether the member is ignoring the author of the comment. $member (\IPS\Member, optional) If provided, uses this members ignore preference when checking for ignore status. By default, the currently-logged in member will be used. string dateLine() Returns a string that can be used in templates to show when the comment was posted, e.g. "Posted 2 hours ago". string html() Returns the HTML to display the comment, wrapped in its comment template.
-
How to implement reputation First, your comment model must implement the reputation interface, like so: implements \IPS\Content\Reputation Next, you need to add a property to your model: public static $reputationType = ‘string’; This property identifies this kind of content from others within the reputation system. It can be anything you like, but the convention is to use the name of the column in your database that stores the ID. For example: public static $reputationType = ‘comment_id’; Additional model methods available boolean canGiveReputation( integer $type [, \IPS\Member $member ] ) Indicates whether the user can give reputation to the creator of the content item. $type (integer, required) The type of reputation to be given. Either 1 or -1, where 1 is positive reputation and -1 is negative reputation. $member (\IPS\Member, optional) The member whose permissions should be used when checking. By default, the currently-logged in member will be used. void giveReputation( integer $type [, \IPS\Member $member ] ) Gives reputation to the creator of the content item. Throws a DomainException if the member does not have permission to give reputation to this item. $type (integer, required) The type of reputation to be given. Either 1 or -1, where 1 is positive reputation and -1 is negative reputation. $member (\IPS\Member, optional) The member whose permissions should be used when checking. By default, the currently-logged in member will be used. integer repGiven( [ \IPS\Member $member ] ) Returns a value indicating whether (and what type of) reputation the member has given to the item. The return values are: 1 - positive reputation has been given -1 - negative reputation has been given 0 - no reputation has been given $member (\IPS\Member, optional) The member whose permissions should be used when checking. By default, the currently-logged in member will be used. Changed behavior after implementation Reputation buttons will automatically appear next to comments.
-
Comments can be hidden to non-staff members. This can be used to require approval by staff before comments are shown, or as a way for staff to reactively hide comments deemed undesirable and unsuitable for public display. How to implement hiding/approving First, you will need to implement the hiding/approving interface in your comment model, like so: implements \IPS\Content\Hideable Next, add either a hidden or approved key to your $databaseColumnMap property, with the value being the name of the database column that stores the hidden status of your comments. Optionally, you can also add an additional key, unapproved_comments to the content item model, whose value should be the column name that stores a count of the number of unapproved comments on each item. If a user or their group is configured to require approval of content they post, comments will be hidden automatically for you. You can override the moderateNewComments() method of the content item class if you want to change this behavior, for example to require all content within certain nodes to be approved regardless of the user's permissions. Don't forget to call the parent method. public static function moderateNewComments( \IPS\Member $member ) { if ( $this->item()->container()->bitoptions[‘moderation’] and !static::modPermission( 'unhide', $member, $this->item()->container() ) ) { return TRUE; } return parent::moderateNewComments( $member ); } Values stored for hidden status When checking the hidden status of an item, there are three possible values; they depend on whether your model uses hidden or approved in the $databaseColumnMap. Element Value if hidden (normal, default) Value if hidden (and was previously unhidden) Value if pending approval hidden 0 -1 1 approved 1 -1 0 Additional model methods available integer hidden() Returns the status of the comment: -1 indicates the comment is now hidden, but was previous unhidden (i.e. it has changed state) 0 indicates the comment is unhidden (the default status) 1 indicates the comment is hidden and was automatically set as hidden at the time of posting (i.e. it hasn't changed state) boolean canHide( [ \IPS\Member $member ] ) Indicates whether the user can hide the comment. This method takes into account whether the comment is already hidden. $member (\IPS\Member, optional) The member whose permissions will be used when checking. By default, the currently-logged in member will be used. boolean canUnhide( [ \IPS\Member $member ] ) Indicates whether the user can unhide the comment. This method takes into account whether the comment is already visible. $member (\IPS\Member, optional) The member whose permissions will be used when checking. By default, the currently-logged in member will be used. An additional method is also available on the content item model: boolean canViewHiddenComments( [ \IPS\Member $member ] ) Indicates whether the user can view hidden comments on the item. $member (\IPS\Member, optional) The member whose permissions will be used when checking. By default, the currently-logged in member will be used. Behavior changes after implementation loadAndCheckPerms() will now throw an OutOfRangeException if a hidden content item is loaded and the currently logged in user does not have permission to view is hidden content. Hidden comments will only show to users who have permission to view hidden comments. Users with appropriate moderator permission will see buttons to hide/unhide in comments. Users who are set to have all new content moderated will have their comments hidden until it is manually approved.
-
Comments can use the built-in edit history functionality to log when the comment text is edited, as well as track versions of the comment. Implementing edit history First, your comment model will need to implement the edit history interface, like so: implements \IPS\Content\EditHistory Next, you will need to add additional elements to your $databaseColumnMap property: edit_time Required. Should contain the column name that holds a unix timestamp of when the comment was edited. edit_show Required. Should contain the column name that holds a boolean indicating whether the edit message should show. edit_member_name Required. Should contain the column name that holds the username of the member that made the edit. edit_reason Optional. Should contain the column name that holds the reason given for the edit. edit_member_id Optional. Should contain the column name that holds the member ID that made the edit. Additional model methods available string editLine() Returns a string of HTML that can be displayed in the comment to show the edit, for example "Edited <x> days ago by <user>". \IPS\Db\Select editHistory( [ boolean $staff ] ) Returns an iterator query result that can be used to loop through each item in the edit history. $staff (boolean, optional, default FALSE) Will return the full log, which includes edits not made by the author and private edits. Set to TRUE for moderators. Changed behavior after implementation When editing, the details of the edit will be logged and displayed on the comment. The exact behavior depends on the site’s configuration.
-
How to implement reporting for comments Firstly, your comment model needs to implement the reporting interface, like so: implements \IPS\Content\ReportCenter You will also need to define a new property on your model: public static $icon = 'string'; This property specifies a FontAwesome icon that will be used to identify this content in the report center. The value should be the name of the icon, without the fa- prefix. Additional model methods available boolean canReport( [ \IPS\Member $member ] ) Indicates whether the member has permission to report the comment, considering whether their group has permission to report content, whether they can view the content, whether they have already reported the content, and whether they are the original author. $member (\IPS\Member, optional) The member whose permissions should be checked. By default, the currently-logged in member will be used. \IPS\core\Reports\Report report( string $reportContent ) Reports a comment, returning the report object. $reportContent (string, required) The text of the report. Changed behavior after implementation A “Report” button will automatically appear next to the comment for members who have permission to report the comment.
-
Note: In order to support searching of comments, your content items must also be searchable. Consult the searching content items guide for more information. Implementing search All that is required to support searching of your item's comments is to implement the search interface, like so: implements \IPS\Content\Searchable Changed behaviors after implementation Comments will be indexed and included in search results and the activity stream. The index will automatically be updated when comments are created, edited, moved, hidden, etc.
-
What it does Since a lot of functionality in the IPS Community Suite (such as reports, warnings, and more) is handled centrally, other applications (both IPS and third-party) need some way to register themselves with the core so that it can be handled appropriately. The ContentRouter extension is where this happens. At its most basic, the ContentRouter simply registers the content item classes so that the core can work with them. For more information on the ContentRouter, consult this step of the Content Items guide. How it works At its most basic, only one class property needs to be defined: $classes. That said, this extension supports some other less-used capabilities as well. /** * @brief Content Item Classes */ public $classes = array(); The $classes array defines the content item classes supported by the extension. Typically, this is populated by the constructor after checking member permissions to ensure that the current viewing member can access the content items as well. /** * @brief Owned Node Classes */ public $ownedNodes = array( 'IPS\blog\Blog' ); If your application supports owned nodes (for instance, blogs and gallery albums are nodes which are owned by members) you should define the node classes here. /** * @brief Embeddable Classes */ public $embeddableContent = array(); If your application supports a type of embeddable content that is not a content item (implementing \IPS\Content\Embeddable) you can define it using the $embeddableContent type. Some practical examples include clubs and commerce product reviews. Note that your class will need to implement \IPS\Content\Embeddable (even if it is not a content item), will need to support a url() method, a loadFromUrl() method, and will need to represent the embedded content output when called with the ?do=embed query string parameter. /** * Constructor * * @param \IPS\Member|IPS\Member\Group|NULL $member If checking access, the member/group to check for, or NULL to not check access * @return void */ public function __construct( $member = NULL ) { if ( $member === NULL or $member->canAccessModule( \IPS\Application\Module::get( 'blog', 'blogs', 'front' ) ) ) { $this->classes[] = 'IPS\blog\Entry'; } } The constructor is passed a member or group object (or NULL), and it is expected that your extension verifies the member or group (if supplied) has access to the content item classes before the $classes property is populated. In the example above, you will see that we only populate the blog Entry content item class if we are not checking permissions, or if the member can access blog entries. /** * Use a custom table helper when building content item tables * * @param string $className The content item class * @param \IPS\Http\Url $url The URL to use for the table * @param array $where Custom where clause to pass to the table helper * @return \IPS\Helpers\Table|void Custom table helper class to use */ public function customTableHelper( $className, $url, $where=array() ) { // Adjust table, etc. } The customTableHelper() method allows you to adjust certain tables of content items that are centrally generated, if needed. You may wish to add an extra CSS class to the table so you can style it differently, for example, and this method allows you to do that. Gallery uses this method to ensure the content item tables generated centrally still display images in an expected fashion, for instance.
-
Creating an "Add Item" form You can create a form that allows users to create new content items by simply calling the following in a controller method: \IPS\Output::i()->output = \IPS\yourApp\YourModelClass::create( $node ); You must pass the node into which the content item is being created as a parameter (or pass NULL if you are creating content items independent of nodes, or if you will add a form element from which the user can select the node themselves). The create() method will automatically handle showing the form, creating the item, and then redirecting the user to it after creation. The form elements shown on the form are obtained from the formElements() method inherited by your model from the content item model. You can override this method in your own model to add additional fields that will show on both the create form and edit form. The method automatically adds fields for: Item title Tags (if your model implements tagging) An editor field for the first comment on your item (if commenting is supported by your model and the first comment is required). If you override the formElements() method, be sure to also call the parent method so that these automatic fields are included. The names for the form elements are prefixed with the value of the static property $formLangPrefix (which is empty by default, unless overwritten). You can set this property in order to use custom language strings to customize the output of your form. Lifecycle methods There are several lifecycle methods that can be overridden to perform actions at certain points of creation. boolean canCreate( \IPS\Member $member [, \IPS\Node\Model $container=NULL [, boolean $showError=FALSE ] ] ) Before the form is shown, this method is called to determine if the member has permission to create new items. Most permission checking is handled automatically, so usually you do not need to override this method, but if you have the need to perform additional checks they can be done here. For example, the messenger module has a setting that can restrict the number of messages sent per day, so that limit is checked in this method of the messenger models. $member (\IPS\Member, required ) The member whose permissions are being checked $container (\IPS\Node\Model, optional) The container into which the item is going to be created, if appropriate $showError (boolean, optional, default FALSE) By default, this method will return a true/false value. If $showError is TRUE, instead of returning a value, this method will show an error to the user. void processBeforeCreate( array $values ) This method is called just before the data is inserted into the database, but after the object has been created in memory, and default values such as the author, date and any fields which can be set by virtue of having the same key as a database column have been set. It's important to note this method only runs for new items. $values (array, required) Array of values from the form. void processForm( array $values ) This method is called immediately after processBeforeCreate (see above). It differs from that method in that it also runs for items being edited. You should use it to set any fields you added in formElements() which are more complicated than just setting the direct value (which would be done automatically). The default method sets the title, tags (if enabled - see Tags section) and other data, so ensure you call the method in the parent class to not override this. $values (array, required) Array of values from the form. void processAfterCreate( \IPS\Comment\Content $comment=NULL, array $values ) Called after the content item has been inserted into the database and all processing has been completed, including creating the first comment, if enabled. $comment (\IPS\Comment\Content, required, default NULL) The object for the first comment, if applicable $values (array, required) Array of values from the form. Incrementing post counts By default, when a user posts a new content item, their post count is incremented by one. This might not be desirable depending on the purpose of your application, so by defining an incrementPostCount method on your model, you can control this behavior. boolean incrementPostCount( \IPS\Node\Model $container=NULL ) $container (\IPS\Node\Model, optional, default NULL) The container into which the content item is being created, if applicable. Return true or false from this method to dictate whether the user's post count should be incremented.
-
Here is an example of a content item model that makes use of several of the features discussed in this section. <?php namespace IPS\downloads; /** * File Model */ class _File extends \IPS\Content\Item implements \IPS\Content\Permissions, \IPS\Content\Tags, \IPS\Content\Reputation, \IPS\Content\Followable, \IPS\Content\ReportCenter, \IPS\Content\ReadMarkers, \IPS\Content\Hideable, \IPS\Content\Featurable, \IPS\Content\Pinnable, \IPS\Content\Lockable, \IPS\Content\Shareable { /** * @brief Application */ public static $application = 'downloads'; /** * @brief Module */ public static $module = 'downloads'; /** * @brief Database Table */ public static $databaseTable = 'downloads_files'; /** * @brief Database Prefix */ public static $databasePrefix = 'file_'; /** * @brief Multiton Store */ protected static $multitons; /** * @brief Default Values */ protected static $defaultValues = NULL; /** * @brief Node Class */ public static $containerNodeClass = 'IPS\downloads\Category'; /** * @brief Comment Class */ public static $commentClass = 'IPS\downloads\File\Comment'; /** * @brief Review Class */ public static $reviewClass = 'IPS\downloads\File\Review'; /** * @brief Database Column Map */ public static $databaseColumnMap = array( 'container' => 'cat', 'author' => 'submitter', 'views' => 'views', 'title' => 'name', 'content' => 'desc', 'num_comments' => 'comments', 'num_reviews' => 'reviews', 'last_comment' => 'last_comment', 'last_review' => 'last_review', 'date' => 'submitted', 'updated' => 'updated', 'rating' => 'rating', 'approved' => 'open', 'approved_by' => 'approver', 'approved_date' => 'approvedon', 'pinned' => 'pinned', 'featured' => 'featured', 'locked' => 'locked', 'ip_address' => 'ipaddress' ); /** * @brief Title */ public static $title = 'downloads_file'; /** * @brief Icon */ public static $icon = 'download'; /** * @brief Form Lang Prefix */ public static $formLangPrefix = 'file_'; /** * @brief Reputation Type */ public static $reputationType = 'file_id'; /** * @brief Follow Area Key */ public static $followArea = 'file'; /** * Get URL * * @param string|NULL $action Action * @return \IPS\Http\Url */ public function url( $action=NULL ) { $url = \IPS\Http\Url::internal( "app=downloads&module=downloads&controller=view&id={$this->id}", 'front', 'downloads_file', $this->name_furl ); if ( $action ) { $url = $url->setQueryString( 'do', $action ); } return $url; } /** * Should new items be moderated? * * @param \IPS\Member $member The member posting * @param \IPS\Node\Model $container The container * @return bool */ public static function moderateNewItems( \IPS\Member $member, \IPS\Node\Model $container = NULL ) { if ( $container and $container->bitoptions['moderation'] and !static::modPermission( 'unhide', $member, $container ) ) { return TRUE; } return parent::moderateNewItems( $member, $container ); } /** * Should new comments be moderated? * * @param \IPS\Member $member The member posting * @return bool */ public function moderateNewComments( \IPS\Member $member ) { $commentClass = static::$commentClass; return $this->container()->bitoptions['comment_moderation'] and !$commentClass::modPermission( 'unhide', $member, $this->container() ); } /** * Should new reviews be moderated? * * @param \IPS\Member $member The member posting * @return bool */ public function moderateNewReviews( \IPS\Member $member ) { $reviewClass = static::$reviewClass; return $this->container()->bitoptions['reviews_mod'] and !$reviewClass::modPermission( 'unhide', $member, $this->container() ); } /** * Get elements for add/edit form * * @param \IPS\Content\Item|NULL $item The current item if editing or NULL if creating * @param int $container Container (e.g. forum) ID, if appropriate * @return array */ public static function formElements( $item=NULL, \IPS\Node\Model $container=NULL ) { $return = parent::formElements( $item, $container ); $return['file_desc'] = new \IPS\Helpers\Form\Editor( 'file_desc', $item ? $item->desc : NULL, TRUE, array( 'app' => 'downloads', 'key' => 'Downloads', 'autoSaveKey' => 'downloads-new-file' ) ); return $return; } }
-
Throughout the IPS Community Suite, content items can be embedded in other content, generating a small preview of the content. You can support this kind of embedding in your own content items, allowing them to be embedded elsewhere in the suite. Note: You must have implemented the ContentRouter extension for embedding to be supported. How to implement embedding First, you need to implement the embedding interface in your content item model, like so: implements \IPS\Content\Embeddable Next, ensure that the URLs to your content contain an “id” parameter which is the primary ID of your content. If this is not the case, you will need to override the loadFromUrl() method in your class (this is defined in \IPS\Patterns\ActiveRecord): public function loadFromUrl( \IPS\Http\Url $url ) { return static::load( ... ); } The controller and model will automatically support embeds using a generic template. If you want to provide a custom template, override the embedContent() method on your model and return different HTML: string embedContent( array $params ) $params (array, required) Additional parameters that were passed in the embed URL.
-
Ratings allow members to rate content items out of 5 or 10 (depending on settings) stars. How to implement ratings First, you need to implement the rating interface in your content item model, like so: implements \IPS\Content\Ratings Next, add the following keys to the $databaseColumnMap property of your model. These are optional, but adding them will make your application much more efficient at returning the average rating when viewed. As usual, the value of each key should be the name of the database column that holds the value. rating_average - the current average rating of the item rating_total - the total sum of all ratings for the item rating_hits - the number of votes the item has received Finally, display the rating in your content view template. For example: {$item->rating()|raw} Additional model methods available boolean canRate( [ \IPS\Member $member ] ) Indicates whether the member has permission to rate this item. $member (\IPS\Member, optional) The member whose permissions should be checked. By default, the currently-logged in member is used. float averageRating() Returns the average rating of the items (total rating divided by number of votes). string rating() Displays star icons that indicate the current average rating. If the member has permission to rate the item, these icons will be interactive allowing the member to click them to vote.
-
By implementing polls in your application, members will be able to attach a poll when creating a new content item. The process is automatic; a new tab will be displayed on the create item form that allows the poll to be managed How to implement polls First, your content item model needs to implement the poll interface, like so: implements \IPS\Content\Polls Next, add a poll key to your $databaseColumnMap with the value being the name of the database column that stores the poll ID for this item (note, you can only have one poll per content item). Finally, display the poll in your content item view. For example: {$item->getPoll()|raw} Performing additional actions after receiving a vote Optionally, your model can be notified when a member votes on a poll. To add support for this, you first need to implement the SPL Observer: implements \SplObserver In your model, add an update method, which will be called when a poll receives a vote: void update( \IPS\Poll $poll ) $poll (\IPS\Poll) The poll that has been voted on. Finally, when displaying your content item, attach the item to the poll. For example: if ( $poll = $item->getPoll() ) { $poll->attach( $item ); } Additional model methods available boolean static canCreatePoll( [ \IPS\Member $member [, \IPS\Node\Model $container ] ] ) Indicates whether the member can create a poll. $member (\IPS\Member, optional) The member whose permissions should be checked. By default, the currently-logged in member will be used. $container (\IPS\Node\Model, optional) Specifically checks whether polls can be created in this container. \IPS\Poll getPoll() Returns the poll for this item (or NULL if no poll is attached).
-
Content read markers allow members to easily see which content items they've viewed through the use of a small icon in the content list view. They are a standard feature of the IPS Community Suite, and you should implement them if your application deals with user-generated content in any way. How to implement read marking First, your content item model needs to implement the read marking interface, like so: implements \IPS\Content\ReadMarkers Next, you should add a key to the $databaseColumnMap property of your model. The key(s) you should use depends on other features you might implement, but in all cases the value should be the name of the database column that stores the timestamp for the associated action. Add an updated key Add a last_comment key if you are using comments Add a last_review key if you are using reviews Finally, make sure that when a user views the item, the read marking is accounted for. In your controller's manage() method, ensure you call the parent's method: parent::manage(); Or, manually call $item->markRead() to trigger the marking action. Additional model methods available Note: in order to use the containerUnread() and markContainerRead() methods shown below, your container node model must have implemented the following methods. See the container nodes guide for more information. get__items() get__comments() setLastComment() getLastCommentTime() boolean static containerUnread( \IPS\Node\Model $container [, \IPS\Member $member ] ) Indicates whether the specified container (including children) has any unread content for the member. $container (\IPS\Node\Model, required) The container to check. $member (\IPS\Member, optional) The member whose read status should be checked. By default, the currently-logged in member will be used. void static markContainerRead( \IPS\Node\Model $container [, \IPS\Member $member [, boolean $children ] ] ) Marks the container (and optionally, children) as read. $container (\IPS\Node\Model, required) The container to mark read. $member (\IPS\Member, optional) The member whose read status should be changed. By default, the currently-logged in member will be used. $children (boolean, optional, default TRUE) Should children of $container be marked as read too? integer unread( [ \IPS\Member $member ] ) Returns an integer indicating the read status of the item. Return values are: 0 if the item is read -1 if the item is unread and has never been read 1 if the item is unread but has previously been read (i.e. there has been new activity since the last read). Only applies if comments are used. $member ( \IPS\Member, optional ) The member whose read status should be checked. By default, the currently-logged in member will be used. void markRead( [ \IPS\Member $member [, integer $time [, mixed $extraContainerWhere ] ] ] ) Marks an item as read. $member (\IPS\Member, optional) The member whose read status should be updated. By default, the currently-logged in member will be used. $time (integer, optional) The timestamp to to set as 'last read'. By default, or if NULL is passed, the current time will be used. $extraContainerWhere (mixed, optional) An array or string containing additional WHERE clauses to be passed into the underlying query. \IPS\DateTime timeLastRead( [ \IPS\Member $member ] ) Returns a DateTime object indicating the last time the item was read by the member (or NULL if it hasn't been read yet) $member (\IPS\Member, optional) The member whose last read time should be returned. By default, the currently-logged in member will be used.
-
Share links allow users to share content items to various social networks, as configured by the administrator. How to implement share links First, your content item model needs to implement the share links interface, like so: implements \IPS\Content\Shareable Finally, include the share links template inside your content item view: {template="sharelinks" app="core" group="global" params="$item"} Additional model methods available array sharelinks() Returns an array of share service URLs for the content item.
-
Your content item can automatically track the number of times it is viewed by implementing the page view interface. Note that guest page caching may render the count inaccurate since a cached page won't increase the view count. Implementing page view tracking First, you need to implement the page view interface in your content item model, like so: implements \IPS\Content\Views Next, add a views key to your $databaseColumnMap, with the value being the name of the database column that stores the view count for your items. Finally, be sure that the manage() method in your content item controller calls the parent manage() method: parent::manage(); Additional model methods available array stats( [ boolean $includeFirstComment ] ) Returns an array of stats about the item, with a num_views key containing the view count. $includeFirstComment (boolean, optional, default TRUE) Determines whether the first comment is counted in the item's comment count.
-
How to implement following Note: Your application must also implement searching (\IPS\Content\Searchable) in order to implement following. The first step is to implement the followable interface in your content item model, like so: implements \IPS\Content\Followable Next, you need to insert the template includes that will display the follow button inside your nodes and inside the content items themselves. In your node view (e.g. forum view), insert this tag: {template="follow" app="core" group="global" params=“'yourApp','yourNodeClass', $node->_id, \IPS\yourApp\YourContentClass::containerFollowers( $node )->count( TRUE )"} In this tag, the params are: yourApp - your application key yourNodeClass - the name of your container node class $node->_id - the ID of your container (e.g. the forum, in the Forums app) The final param should statically call the containerFollowers (see below) on your content item class in order to pass in the current number of followers. In your content item view (e.g. topic topic), insert this tag: {template="follow" app="core" group="global" params="'yourApp', 'yourContentItemClass', $item->id, $item->followersCount()"} In this tag, the params are: yourApp - your application key yourContentItemClass - the name of your content item class $item->id - the ID of the relevant content item The final parameter simply calls $item->followersCount() to pass in the current number of followers. Changes after implementation After posting a new content item, members who are following the node it was posted into will receive a notification. If the content needs to be approved by a moderator, the notifications will be delayed until the content has been approved. Additional model methods available integer static containerFollowers( \IPS\Node\Model $node [, integer $privacy [, array $frequencyTypes [, \IPS\DateTime $date [, integer|array $limit [, string $order [, integer $flags ] ] ] ] ] ] ) Returns the followers of a node. $node (\IPS\Node\Model, required) The node from which followers will be returned. $privacy (integer, optional, default 3) A bitwise value representing the types of follows that should be returned (see below for more information). The default value 3 includes both anonymous and public followers. $frequencyTypes (array, optional, default array( 'none', 'immediate', 'daily', 'weekly' ) ) Allows you to narrow the returned followers to specific types of follow. By default, all types are counted. $limit (integer or NULL, optional, default NULL) Allows you to limit the number of followers returned. Value is passed as the LIMIT clause in the resulting query. $order (string, optional, default NULL) Allows you to determine the column on which the results will be ordered. $flags (integer or NULL, optional, default NULL) SQL flags that will be passed to \IPS\Db when the underlying query is performed. Bitwise follow privacy values The privacy setting on follows allows a user to specify whether their follow should be 'public' or 'anonymous' - meaning their follow counts towards the total, but they won't be listed by name. The values are: const FOLLOW_PUBLIC = 1; const FOLLOW_ANONYMOUS = 2;
-
How to implement searching Search functionality (and related functionality like Activity Streams) is automatically handled for you by the core of IPS Community Suite. You simply need to indicate that your content items should be searchable by implementing the interface: implements \IPS\Content\Searchable Note that you must also have implemented the ContentRouter extension. Changes after implementation Content will be indexed and included in search results and the activity stream. The index will automatically be updated when content is created, edited, moved, hidden, etc.
-
Content items can be tagged by members. Tags can be used to find other different types of content with the same tags. A prefix is one of the item's tags which is shown highlighted and prefixes the title when displayed. How to implement tagging Tagging requires that your content items belong inside a container node. First, you need to implement the tagging interface: implements \IPS\Content\Tags Then, you can display the prefix and tags in the view for your item, like so: {{if $item->prefix()}} <a href="{url="app=core&module=system&controller=tags&tag={$item->prefix()}" seoTemplate="tags"}">{$item->prefix()}</a> {{endif}} {{if count( $file->tags() )}} <ul> {{foreach $file->tags() as $tag}} <li> <a href="{url="app=core&module=system&controller=tags&tag={$tag}" seoTemplate="tags"}">{$tag}</a> </li> {{endforeach}} </ul> {{endif}} Changes after implementation A field to input tags will be added to the create/edit form. Prefixes and tags will be displayed in content tables. Additional model methods available boolean static canTag( [ \IPS\Member $member [, \IPS\Node\Model $container ] ] ) Indicates whether the user has permission to tag items. $member (\IPS\Member, optional) If provided, will use this member's permissions when checking. By default, the currently-logged in member will be used. $container (\IPS\Node\Model, optional) If provided, checks whether the member is able to tag in this specific node. boolean static canPrefix( [ \IPS\Member $member [, \IPS\Node\Model $container ] ] ) Indicates whether the user has permission to select a tag as a prefix on items. $member (\IPS\Member, optional) If provided, will use this member's permissions when checking. By default, the currently-logged in member will be used. $container (\IPS\Node\Model, optional) If provided, checks whether the member is able to select a prefix on items in this specific node. array tags() Returns an array of tags on the item. string prefix( [ boolean $encode ] ) Returns the prefix on the item (NULL if the item has no prefix) $encode (boolean, optional, default FALSE) If true, will url-encode the returned prefix.
-
Content Items can be hidden to non-staff members. This can be used to require staff approval before content can be viewed, and as a way for staff members to hide undesirable content. How to implement hiding and approving To support featuring content items in your application, you first need to implement the interface in your content item model: implements \IPS\Content\Hideable Next, add either a hidden or approved key to your $databaseColumnMap property, with the value being the name of the database column that stores the hidden status of your items. Optionally, you can also add approved_by and approved_date to store some additional metadata about the action. Next, you need to add support to your templates. For example: {{if $item->canHide()}} <li><a href='{$item->url()->setQueryString( array( 'do' => 'moderate', 'action' => 'hide' ) )}'>{lang="hide"}</a></li> {{endif}} {{if $item->canUnhide()}} <li><a href='{$item->url()->setQueryString( array( 'do' => 'moderate', 'action' => 'unhide' ) )}'>{{if $item->hidden() === 1}}{lang="approve"}{{else}}{lang="unhide"}{{endif}}</a></li> {{endif}} Overriding default moderated content behavior If a user is configured to have their content approved, their content will be hidden by default automatically. You may want to override the method that handles this so, for example you can make certain nodes have all content posted to them require approval. To do this, override the moderateNewItems() method - for example: public static function moderateNewItems( \IPS\Member $member, \IPS\Node\Model $container = NULL ) { if ( $container and $container->bitoptions['moderation'] and !static::modPermission( 'unhide', $member, $container ) ) { return TRUE; } return parent::moderateNewItems( $member, $container ); } Values stored for hidden status When checking the hidden status of an item, there are three possible values; they depend on whether your model uses hidden or approved in the $databaseColumnMap. Element Value if hidden (normal, default) Value if hidden (and was previously unhidden) Value if pending approval hidden 0 -1 1 approved 1 -1 0 Changes after implementation canView() will return FALSE for an item that is hidden if the member cannot view hidden items. This will also cause loadAndCheckPerms() to throw an OutOfRange exception if such an object is loaded. Hidden items will only show to users who have permission to view hidden content. Users with appropriate moderator permission will see tools to lock/unlock in content item tables. Users who are set to have all new content moderated will have their content hidden until it is manually approved. Additional model methods available integer hidden() Returns the status of the item: -1 indicates the content is now hidden, but was previous unhidden (i.e. it has changed state) 0 indicates the content is unhidden (the default status) 1 indicates the content is hidden and was automatically set as hidden at the time of posting (i.e. it hasn't changed state) boolean canHide( [ \IPS\Member $member ] ) Indicates whether the user can hide the content item. This method takes into account whether the item is already hidden. $member (\IPS\Member, optional) The member whose permissions will be used when checking. By default, the currently-logged in member will be used. boolean canUnhide( [ \IPS\Member $member ] ) Indicates whether the user can unhide the content item. This method takes into account whether the item is already visible. $member (\IPS\Member, optional) The member whose permissions will be used when checking. By default, the currently-logged in member will be used. boolean static canViewHiddenItems( [ \IPS\Member $member ] ) Indicates whether the user can view hidden items. To check this permission for an individual item, use the canView() method of the item. $member (\IPS\Member, optional) The member whose permissions will be used when checking. By default, the currently-logged in member will be used.
-
In order to support locking of content items, you need to have implemented comments. How to implement locking To support locking and unlocking content items in your application, you first need to implement the interface in your content item model: implements \IPS\Content\Lockable Next, you need to add a locked key to your $databaseColumnMap property, with the value being the name of the database column that stores the locked state of your content items. Finally, you need to add support for locking and unlocking in your templates. For example: {{if $item->canLock()}} <a href='{$item->url()->setQueryString( array( 'do' => 'moderate', 'action' => 'lock' ) )}'>{lang="lock"}</a> {{endif}} {{if $item->canUnlock()}} <a href='{$item->url()->setQueryString( array( 'do' => 'moderate', 'action' => 'unlock' ) )}'>{lang="unlock"}</a> {{endif}} Changes after implementation Locked items will no longer allow comments, unless the user has permission to comment on locked items. Moderators with the appropriate permission will be able to see and use tools to lock and unlock content when selecting items in content item tables. Additional model methods available boolean canLock( \IPS\Member $member ) Indicates whether the user has permission to lock the item. This method takes into account whether the item is already locked. $member (\IPS\Member, optional) If provided, this member's permissions will be checked. By default, the currently-logged in member will be used. boolean canUnlock( \IPS\Member $member ) Indicates whether the user has permission to unlock the item. This method takes into account whether the item is already unlocked. $member (\IPS\Member, optional) If provided, this member's permissions will be checked. By default, the currently-logged in member will be used.
-
How to implement pinning In order to support pinning content items in your model, you need to implement the following interface: implements \IPS\Content\Pinnable Next, you should add a pinned key to your $databaseColumnMap, with the value being the name of the database column that stores the pinned status of your items. Finally, you need to add support to your templates. For example: {{if $item->canPin()}} <a href='{$item->url()->setQueryString( array( 'do' => 'moderate', 'action' => 'pin' ) )}'>{lang="pin"}</a> {{endif}} {{if $item->canUnpin()}} <a href='{$item->url()->setQueryString( array( 'do' => 'moderate', 'action' => 'unpin' ) )}'>{lang="unpin"}</a> {{endif}} Changes after implementation Pinned items will appear at the top of the content item table views Moderators with the appropriate permission will be able to see and use tools to pin content when selecting items in content item tables. Additional model methods available boolean canPin( \IPS\Member $member ) Indicates whether the user has permission to pin items. This method takes into account whether the item is already pinned. $member (\IPS\Member, optional) If provided, this member's permissions will be checked. By default, the currently-logged in member will be used. boolean canUnpin( \IPS\Member $member ) Indicates whether the user has permission to unpin items. This method takes into account whether the item is already unpinned. $member (\IPS\Member, optional) If provided, this member's permissions will be checked. By default, the currently-logged in member will be used.
-
How to implement featuring To support featuring content items in your application, you first need to implement the interface in your content item model: implements \IPS\Content\Featurable Next, you need to add a featured key to your $databaseColumnMap, with the value being the name of the column in your database table that stores the featured status of the item. Finally, you need to add support to your templates. For example: {{if $item->canFeature()}} <a href='{$item->url()->setQueryString( array( 'do' => 'moderate', 'action' => 'feature' ) )}'>{lang="feature"}</a> {{endif}} {{if $item->canUnfeature()}} <a href='{$item->url()->setQueryString( array( 'do' => 'moderate', 'action' => 'unfeature' ) )}'>{lang="unfeature"}</a> {{endif}} Changes after implementation After implementing the \IPS\Content\Featurable interface, moderators with the appropriate permission will be able to see and use tools to feature content when selecting items in content item tables. Additional model methods available \IPS\Patterns\AciveRecordIterator static featured( [ integer $limit [, string $order [, \IPS\Node\Model $container ] ] ] ) Returns featured items. If permissions are supported in your model, only the items the user has permission to view are returned. $limit (integer, optional, default 10) Number of items to return. $order (string, optional, default 'RAND()') Order clause to be used in the query. $container (\IPS\Node\Model, optional, default NULL) If provided, only featured items from this container will be returned. boolean canFeature( \IPS\Member $member ) Indicates whether the user has permission to feature the item. This method takes into account whether the item is already featured. $member (\IPS\Member, optional) If provided, this member's permissions will be checked. By default, the currently-logged in member will be used. boolean canUnfeature( \IPS\Member $member ) Indicates whether the user has permission to unfeature the item. This method takes into account whether the item is already unfeatured. $member (\IPS\Member, optional) If provided, this member's permissions will be checked. By default, the currently-logged in member will be used.
-
If you use content items in your application, it probably goes without saying that you'll want to use the content within those items at some point. There's two possibilities depending on whether your model uses a specific column for content, or expects the first comment to act as the content (such as in the Forums application). For a specific content field Simply add a content key to the $databaseColumnMap, with the value being the column in the database that contains the content. For comments as the content When you implement content comments, the first comment will automatically be treated as the item's content. Methods available for working with content string content() Returns the value of the content. string truncated( [ boolean $oneLine=FALSE ] ) Returns the content in a format suitable for use with the data-ipsTruncate widget. $oneLine (boolean, optional, default FALSE) By default, paragraphs are turned into line break. If this parameter is TRUE, paragraphs are replaced with spaces instead of line breaks, making the truncated content suitable for a one-line display.
-
Many features of IPS4 are handled centrally and automatically, such as warnings and moderator permissions. In order to support this, you need to register your content item types with the core, and this is done via a simple Content Router extension. For more information on the extensions, see the extensions guide. In order for this functionality to work, you need to support containers in your content items and in your node models. How to implement a ContentRouter extension The first step to implementing your content router is to create the ContentRouter extension in the Developer Center of your application. The classname should be relevant to your model's purpose, for example Topics. Open the created file, and add code like this (changing the values as appropriate): namespace IPS\yourApp\extensions\core\ContentRouter; class _YourClass { public $classes = array( 'IPS\yourApp\YourContentItemClass' ); } Next, add the following property to your node model (not the content item model): static $modPerm = 'string'; The value is used as the key to store nodes that a moderator has permission to work with. It can be anything you like. Create a language string in your lang.php file with this same key - this will be the textual representation of what your nodes are called (for example, 'Categories'). Finally, add a language string to your lang.php file for each of the moderation actions, using this format: can_<action>_<title> where <title> is the key defined by the static $title property from your content item model. The actions you'll need to define will depend on which content item features you support in your model, but may include: pin and unpin (if pinning is supported) feature and unfeature (if featuring is supported) hide, unhide and view_hidden (if hiding and approving is supported) lock, unlock and reply_to_locked (if locking is supported) edit move delete Note: If your content item supports comments, repeat this last step for actions that can be performed on comments (edit, delete, hide, unhide and view_hidden), where <title> is the key defined by the static $title property from your content comment model. As an example, assume the $title of our content item model is 'myapp_entry'. We would create language strings like this: 'can_edit_myapp_entry' => "Can edit entries?", 'can_move_myapp_entry' => "Can move entries?", 'can_delete_myapp_entry' => "Can delete entries?" ...and so on. Behavior that changes after implementing a ContentRouter extension When creating a moderator, administrators will be able to setup moderator permissions for your content items. When viewing a user’s profile, showing all content posted by that user will include your content items. If you support hidden content items, when viewing the moderator control panel, hidden content items from your application will be shown. When a moderator gives a user a warning, if the warning was issued from a piece of content in your application, that content will be associated with the warning. When rebuilding a member’s post count, your content items will be accounted for. When deleting all content posted by a member, your content items will be accounted for.
-
Content items can automatically check if the user has permission to perform certain actions by examining the container node object. For this functionality to be supported, as you might expect, your content items need to be using container nodes. Consult the documentation on container nodes and supporting containers in content items for more information. Supporting permissions in content items All you need to do to support permissions is implement the permissions interface in your content item model class: implements \IPS\Content\Permissions Changes to model methods after implementing permissions Several methods in your content item model change behavior slightly when permissions are implemented. canView() will return FALSE for an item a node the member does not have permission to view. loadAndCheckPerms() will throw an OutOfRange exception if an object is loaded that the member does not have permission to view. All places where features do things will check the permission. For example, canCreate() will now return FALSE if the member does not have permission to create content items in the specified node. getItemsWithPermission() will only return items that the currently logged in user has permission to view (or other permission as specified by parameters). Additional model methods available boolean can( string $permission [, \IPS\Member $member=NULL ] ) Returns a boolean indicating whether the user has permission to perform the specified action. $permission (string, required) The permission key to be checked. The action should match a value from the static $permissionMap property of the container class. $member (\IPS\Member, optional) The member object of the member whose permissions should be checked. You can also pass an \IPS\Member\Group object to check a group's permissions instead. If no value is passed, the currently-logged in member is used. integer permId() Returns the permission index ID for this item model.