Jump to content

Ryan Ashbrook

Invision Community Team
  • Posts

    12,727
  • Joined

  • Last visited

  • Days Won

    13

 Content Type 

Downloads

Release Notes

IPS4 Guides

IPS4 Developer Documentation

Invision Community Blog

Development Blog

Deprecation Tracker

Providers Directory

Projects

Release Notes v5

Invision Community 5 Bug Tracker

Forums

Events

Store

Gallery

Everything posted by Ryan Ashbrook

  1. Amazon S3 for storage coupled with Amazon Cloudfront for the CDN is becoming a popular choice, yes.
  2. Invision Community 4.2 has introduced the new Member History feature. This feature allows an application to store a log any time an account is altered from within the application, along with arbitrary data outlining what has changed. Logging a change to an account is as simple as making a called to the \IPS\Member::logHistory() method. /** * Log Member Action * * @param mixed $app The application action applies to * @param string $type Log type * @param mixed $extra Any extra data for the type * @param mixed $by The member performing the action. NULL for currently logged in member or FALSE for no member * * @return void */ public function logHistory( $app, $type, $extra=NULL, $by=NULL ) $app - This is the application key the log is associated with. $type - This is the log "type" which we will use to identify and parse the log for display later. $extra - This is an array of extra arbitrary data that needs to be stored (display name changes, for example, store both the old and the new username in the form of array( 'old' => 'Old Name', 'new' => 'new Name' );). $by - This is the user performing the change. Member History Logs are parsed and displayed in the Admin CP (Members > Edit a Member > Member History) by the use of Extensions. Each application can create multiple extensions, however one extensions can also handle multiple log types. The extension has two methods: /** * Parse LogData column * * @param string $value column value * @param array $row entire log row * @return string */ public function parseLogData( $value, $row ) /** * Parse LogType column * * @param string $value column value * @param array $row entire log row * @return string */ public function parseLogType( $value, $row ) parseLogData() is used to format the log information so that it displays correctly in the Member History table. $value is the data that was passed through to the $extra parameter of \IPS\Member::logHistory(), and $row is the entire row from the core_member_history table. Note that value will be json_encode()'d so the value will need to be manually passed to json_decode() to get the true value. parseLogType() is used to show a simple icon on the table, where supported.
  3. Invision Community 4.2 introduces the new Recommended Replies feature, which allows moderators to recommend comment on content items, that will then appear above all comments, on all pages. To add this feature to your content item, you must first implement the \IPS\Content\MetaData interface to add support for storing Meta Data on your content item. Once Meta Data has implemented, you need to add core_FeaturedComments to your supportedMetaDataTypes() method. public static function supportedMetaDataTypes() { return array( 'core_FeaturedComments' ); } Once Recommend Replies has been added as a supported data type, then you must add the following line to your comments template, where you want Recommend Replies to show. {template="featuredComments" group="global" app="core" params="$item->featuredComments(), $item->url()->setQueryString( 'recommended', 'comments' )"} Or, if you application supports both Comments and Reviews: {template="featuredComments" group="global" app="core" params="$item->featuredComments(), $item->url()->setQueryString('tab', 'comments')->setQueryString('recommended', 'comments')"} Additionally, the featuredComments template supports two additional parameters: $titleLang - This is the title of the recommend replies area (default: recommended_replies). $commentLang - This is the title of each individual comment (default: __defart_comment). Applications that use the comment and commentContainer templates from the System application will not need to do anything further. Applications that use custom templates, however, need to add the following to their comment template, where their moderation menu is: {{if $comment->isFeatured() AND $item->canUnfeatureComment()}} <li class='ipsMenu_item'><a href='{$comment->url('unfeature')->csrf()->setQueryString('page',\IPS\Request::i()->page)}' data-action="unrecommendComment">{lang="unrecommend_content"}</a></li> {{endif}} {{if !$comment->isFeatured() AND $item->canFeatureComment()}} <li class='ipsMenu_item'><a href='{$comment->url('feature')->csrf()->setQueryString('page', \IPS\Request::i()->page)}' data-ipsDialog data-ipsDialog-title='{lang="recommend_comment"}' data-ipsDialog-remoteSubmit data-ipsDialog-size='narrow' data-action="recommendComment">{lang="recommend_content"}</a></li> {{endif}} And then, if you want to highlight recommend content, the following changes need to be made. For commentContainer you need to add the following to your <article> tags class attribute: {{if $comment->isFeatured()}}ipsComment_popular{{endif}} And then in your comment template, add the following immediately inside your opening <div> tag: {{if $comment->isFeatured()}} <strong class='ipsComment_popularFlag' data-ipsTooltip title='{lang="this_is_a_featured_post"}'><i class='fa fa-star'></i></strong> {{endif}}
  4. Invision Community 4.2 introduces the new Content Message feature, which allows moderators to post messages that will appear above all comments, on all pages, on a specific content item. To add this feature to your content item, you must first implement the \IPS\Content\MetaData interface to add support for storing Meta Data on your content item. Once Meta Data has implemented, you need to add core_ContentMessages to your supportedMetaDataTypes() method. public static function supportedMetaDataTypes() { return array( 'core_ContentMessages' ); } Once Content Messages has been added as a supported data type, displaying them within your content items templates is as simple as adding the following line where you want the messages to be displayed: {template="contentItemMessages" group="global" app="core" params="$item->getMessages(), $item"} And then add the following to your Moderation Menu: {{if $item->canOnMessage( 'add' )}} <li class='ipsMenu_item'><a href='{$item->url()->csrf()->setQueryString( array( 'do' => 'messageForm' ) )}' data-ipsDialog data-ipsDialog-title='{lang="add_message"}'>{lang="add_message"}</a></li> {{endif}} And that is all that is required to implement Content Messages.
  5. The Content Meta Data system is a feature that will allow third party developers to store arbitrary data regarding a content item, that may not necessarily need it's own storage space, while also being efficient. This is particularly useful to when your application is intended to support multiple content types, as well as avoiding unnecessary alterations on large databases to add a single column and / or avoid creating small mapping tables that you must join on. To support storing Meta Data, your Content Item Model must implement the \IPS\Content\MetaData interface, and then define meta_data in your column map. namespace IPS\myapp; class _MyClass implements \IPS\Content\MetaData { public static $databaseColumnMap = array( /* etc */ 'meta_data' => 'item_meta_data', ); } Additionally, you must add a BIT(1) column to your content items database table. This column is used to determine if the item has any meta data stored at all. If an item does not have any data, then the software will not attempt to load anything from the core_content_meta table (thus, making it more efficient than automatically attempting to load every time a content item is viewed). ALTER TABLE myapp_content ADD COLUMN item_meta_data bit(1) NOT NULL DEFAULT b'0'; Once done, your Content Item will inherit the following methods: /** * Meta data types supported by this content * * @return array|NULL */ public static function supportedMetaDataTypes() /** * Check if this content has meta data * * @return bool * @throws \BadMethodCallException */ public function hasMetaData() /** * Fetch Meta Data * * @return array * @throws \BadMethodCallException */ public function getMeta() /** * Add Meta Data * * @param string The type of data * @param array The data * @return int * @throws \BadMethodCallException */ public function addMeta( $type, $data ) /** * Edit Meta Data * * @param int The Meta Data ID from core_content_meta * @param array The data * @return void * @throws \BadMethodCallException */ public function editMeta( $id, $data ) /** * Delete Meta Data * * @param int The Meta Data ID from core_content_meta * @return void */ public function deleteMeta( $id ) /** * Delete All Meta Data relating to this content item * * @return void */ public function deleteAllMeta() Important notes regarding specific methods: supportedMetaDataTypes() - Your content item model must define this method in your content item model, with a return value similar too return array( 'myappkey_ExtensionKey' ); with each member of the return array being a specific extension, with the first part being the application the extension belongs too, and the second being the extension key, separated by an underscore. This is useful for checking to ensure that a specific content item support that specific type of data. This method returns NULL by default to indicate the content item does not support storing any data. hasMetaData() - This method does not check against the core_content_meta table to see if there is data present. Instead, it will check against the meta_data column that you have specified in your items column map, which acts as a simple flag and as the return value of this method, type-casted to a boolean (1 will return TRUE, 0 will return FALSE). getMeta() - This method will return all meta data associated with this particular content item in the format of array( 'myappkey_ExtensionKey' => array( X => array( 'data' ) ) ); where "myappkey_ExtensionKey" is the type as defined in supportedMetaDataTypes(), X is the value of meta_id in the core_content_meta table, and then finally the data (which has been json_decode()'d from the database). addMeta() - The $type parameter here must be at least one of the array members as defned by the supportedMetaDataTypes() method. The $data parameter must be an array that will be json_encode()'d and stored in the meta_data column of core_content_meta. editMeta() - This method is exactly the same as addMeta(), however the first parameter is the value of meta_id in core_content_meta, and the second is the new data that should be stored. When called, the method will compare all members of the $data array with the stored version. If it's value differs, then it will automatically store the new value while retaining any that already exist. As such, it is not necessary to specify every member of the array, if only one has changed. deleteMeta() - As with editMeta(), the $id parameter must be the value of meta_id from core_content_meta. The Extensions While the extensions are required, to verify what application a specific type of data belongs too, there is no enforced structure / required methods which must be present within the extension. You are free to use the meta data methods from within \IPS\Content\Item directly, within your own content model, or you can use the extension to define "helper" methods for formatting the data (for a real life example of this, you can reference the two extensions located at /applications/core/extensions/core/MetaData/). Real World Examples The Content Message and Recommended Replies features both utilize the Content Meta Data feature for storing their data.
  6. PHP Traits Invision Community 4.2 has introduced the usage of PHP Traits. While Traits are a fairly standard feature in PHP, due to the way our autoloader and monkey patching works for plugins, the class_uses() function will not work properly. As such, if you need to check if a specific class uses a trait, then you should use \IPS\IPS::classUsesTrait() instead - this will check all classes in the extension chain. /** * Small utility function to check if a class has a trait as PHP doesn't have an operator * for this and the monkey patching means we can't use class_uses() directly * * @param string|object $class The class * @param string $trait Trait name to look for * @return bool */ public static function classUsesTrait( $class, $trait ) Widget Errors Widgets can return now custom error messages in the event their content cannot be displayed. This is useful if, for example, the content of a widget can only be seen by Guests, and that the user should log out to see the content. $this->errorMessage = 'my_custom_error_lang_key';
  7. \IPS\core\modules\front\contact\contact::canUseContactUs() This method is now deprecated, and you should now use \IPS\Member::canUseContactUs() instead. This method will be completely removed in Invision Community 4.3. /** * Is the current user allowed to use the contact us form * * @return bool */ public function canUseContactUs() \IPS\Content::reputation() This method is now deprecated, and you should now use \IPS\Content\Reactable::reactionCount() instead. This method will be completely removed in Invision Community 4.3. ContentRouter Extensions The $member parameter for ContentRouter extensions __construct() method can now accept either an \IPS\Member object, an \IPS\Member\Group object, or NULL. Applications should remove the \IPS\Member type declaration from their extensions.
  8. All Content Items, Comments, and Reviews that implement the \IPS\Content\Hideable interface will automatically work with the Delayed Deletes system. The information below is just for reference only. The \IPS\core\DeletionLog class is an Active Record class that handles content queued for deletion. namespace IPS\core; /** * Deletion Log Model */ class _DeletionLog extends \IPS\Patterns\ActiveRecord { /** * @brief Database Table */ public static $databaseTable = 'core_deletion_log'; /** * @brief Database Prefix */ public static $databasePrefix = 'dellog_'; /** * @brief [ActiveRecord] ID Database Column */ public static $databaseColumnId = 'id'; /** * Set Default Values * * @return void */ public function setDefaultValues() /** * Set deleted date * * @param \IPS\DateTime A DateTime object * @return void */ public function set_deleted_date( \IPS\DateTime $time ) /** * Get deleted date * * @return \IPS\DateTime */ public function get_deleted_date() /** * Set Deleted By * * @param \IPS\Member The member * @return void */ public function set_deleted_by( \IPS\Member $member ) /** * Get Deleted By * * @return \IPS\Member */ public function get__deleted_by() /** * Get Permissions * * @return array|string */ public function get_content_permissions() /** * Get the date the content will be permanently removed on * * @return \IPS\DateTime */ public function get_deletion_date() /** * Set Content and Member * * @param \IPS\Content The content being deleted. * @param \IPS\Member|NULL The member performing the deletion, or NULL for the currently logged in member * @return void * @note Convenience ftw */ public function setContentAndMember( \IPS\Content $content, \IPS\Member $member = NULL ) /** * Save * * @return void */ public function save() /** * Load and check perms * * @param int ID * @return static * @throws * @li \OutOfRangeException */ public static function loadAndCheckPerms( $id ) /** * Can View the deleted content * * @param \IPS\Member|NULL The member, or NULL for currently logged in * @return bool */ public function canView( \IPS\Member $member = NULL ) /** * URL to the deleted content * * @param string|NULL "action" parameter or NULL to go to the content * @return \IPS\Http\Url */ public function url( $action=NULL ) } A new method was added to \IPS\Content which will queue the content for deletion. /** * Log for deletion later * * \IPS\Member|NULL $member The member or NULL for currently logged in * @return void */ public function logDelete( $member = NULL ) To identify content queued for deletion, the \IPS\Content::hidden() method now has the following return values: 1 - This content is currently hidden requiring moderator approval. 0 - This content is visible -1 - This content has been hidden by a moderator -2 - This content has been deleted by a moderator, and will be permanently removed at a later date.
  9. Invision Community 4.2 has introduced the new Clubs feature. This feature allows members to create their own sub-communities within the community. 3rd party developers can extend Clubs to allow users to add Nodes specific to that Club. Implementing Club Support In order to add Club support to your nodes, the following adjustments must be made to your Node model. First, you must add the \IPS\Content\ClubContainer trait to your Model, which will then expose the following methods from the trait. All of which can be overridden in your Model directly, if specific changes or checks need to be made. /** * Get the database column which stores the club ID * * @return string */ public static function clubIdColumn() /** * Get front-end language string * * @return string */ public static function clubFrontTitle() /** * Get acp language string * * @return string */ public static function clubAcpTitle() /** * Get the associated club * * @return \IPS\Member\Club|NULL */ public function club() /** * Set form for creating a node of this type in a club * * @param \IPS\Helpers\Form $form Form object * @return void */ public function clubForm( \IPS\Helpers\Form $form ) /** * Save club form * * @param \IPS\Member\Club $club The club * @param array $values Values * @return void */ public function saveClubForm( \IPS\Member\Club $club, $values ) /** * Class-specific routine when saving club form * * @param \IPS\Member\Club $club The club * @param array $values Values * @return void */ public function _saveClubForm( \IPS\Member\Club $club, $values ) /** * Set the permission index permissions to a specific club * * @param \IPS\Member\Club $club The club * @return void */ public function setPermissionsToClub( \IPS\Member\Club $club ) /** * Check Moderator Permission * * @param string $type 'edit', 'hide', 'unhide', 'delete', etc. * @param \IPS\Member|NULL $member The member to check for or NULL for the currently logged in member * @return bool */ public function modPermission( $type, \IPS\Member $member ) /** * [Node] Get parent list * * @return \SplStack */ public function parents() Some important notes about specific methods: clubIdColumn() - This will be the name of the column without the database prefix. So, if your column is node_club_id and your database prefix is node_, then this method should only return club_id. The default return value from the trait is club_id. clubForm() - By default, this method will simply add a Text field for the name of the Node within the club. You can optionally, however, overload this method to add more elements to the form (such as a description, for example). If your node only needs a title, then it is not necessary to overload this method. _saveClubForm() - By default, this method does nothing. If Nodes need to perform additional processing before the club node is saved, then they should overload this method, rather than saveClubForm(). modPermission() - This method automatically overloads \IPS\Node\Model::modPermission() and will automatically check the permissions of both club specific moderators (as assigned by club leaders), club leaders, club owners,, as well as actual site moderators who are not already a part of the club. parents() - This method automatically overloads \IPS\Node\Mode::parents() and will always return an empty SplStack object if the node is associated with a club. Interface A few alterations to your templates, as well as your controllers, need to be made to fully implements the Clubs look and feel into your nodes. Club nodes will load from within the context of your Application, and thus share the same URL structure as normal nodes, and will also use the same controllers and templates. For your controllers, you need to adjust your breadcrumb navigation to account for the fact that the node is a part of a club, rather than as a normal, standalone, node. If your application uses \IPS\Helpers\Table\Content to display content from within a node, then this will automatically be taken care of for you. Otherwise, implementing proper navigation is as simple as: if ( \IPS\IPS::classUsesTrait( $container, 'IPS\Content\ClubContainer' ) and $club = $container->club() ) { \IPS\core\FrontNavigation::$clubTabActive = TRUE; \IPS\Output::i()->breadcrumb = array(); \IPS\Output::i()->breadcrumb[] = array( \IPS\Http\Url::internal( 'app=core&module=clubs&controller=directory', 'front', 'clubs_list' ), \IPS\Member::loggedIn()->language()->addToStack('module__core_clubs') ); \IPS\Output::i()->breadcrumb[] = array( $club->url(), $club->name ); if ( \IPS\Settings::i()->clubs_header == 'sidebar' ) { \IPS\Output::i()->sidebar['contextual'] = \IPS\Theme::i()->getTemplate( 'clubs', 'core' )->header( $club, $container, 'sidebar' ); } } else { foreach ( $container->parents() as $parent ) { \IPS\Output::i()->breadcrumb[] = array( $parent->url(), $parent->_title ); } } The club header / sidebar, however, is not handled automatically and must be manually inserted into your templates when viewing a node, or content item within a node, that belongs to a club. For example: {{if $club = $node->club()}} {{if settings.clubs and settings.clubs_header == 'full'}} {template="header" app="core" group="clubs" params="$club, $node"} {{endif}} <div id='elClubContainer'> {{endif}} <1-- My Template Content Here --> {{if $node->club()}} </div> {{endif}} If a node is associated with a club, then the inner content must be wrapped in a <div> element, with the id elClubContainer. The same also applies when viewing a content item from within a club node: {{if $club = $item->container()->club()}} {{if settings.clubs and settings.clubs_header == 'full'}} {template="header" app="core" group="clubs" params="$club, $item->container()"} {{endif}} <div id='elClubContainer'> {{endif}} <!-- My Content Item Template Content Here --> {{if $item->container()->club()}} </div> {{endif}} The Clubs All Clubs are an ActiveRecord using the \IPS\Member\Club class. namespace IPS\Member; /** * Club Model */ class _Club extends \IPS\Patterns\ActiveRecord implements \IPS\Content\Embeddable { const TYPE_PUBLIC = 'public'; const TYPE_OPEN = 'open'; const TYPE_CLOSED = 'closed'; const TYPE_PRIVATE = 'private'; const STATUS_MEMBER = 'member'; const STATUS_INVITED = 'invited'; const STATUS_REQUESTED = 'requested'; const STATUS_DECLINED = 'declined'; const STATUS_BANNED = 'banned'; const STATUS_MODERATOR = 'moderator'; const STATUS_LEADER = 'leader'; /** * @brief [ActiveRecord] Multiton Store */ protected static $multitons; /** * @brief [ActiveRecord] Database Table */ public static $databaseTable = 'core_clubs'; /** * Construct ActiveRecord from database row * * @param array $data Row from database table * @param bool $updateMultitonStoreIfExists Replace current object in multiton store if it already exists there? * @return static */ public static function constructFromData( $data, $updateMultitonStoreIfExists = TRUE ) /** * Get all clubs a member can see * * @param \IPS\Member $member The member to base permission off or NULL for all clubs * @param int $limit Number to get * @param string $sortOption The sort option ('last_activity', 'members', 'content' or 'created') * @param bool|\IPS\Member $mineOnly Limit to clubs a particular member has joined (TRUE to use the same value as $member) * @param array $filters Custom field filters * @param mixed $extraWhere Additional WHERE clause * @return \IPS\Patterns\ActiveRecordIterator */ public static function clubs( \IPS\Member $member = NULL, $limit, $sortOption, $mineOnly=FALSE, $filters=array(), $extraWhere=NULL ) /** * Get number clubs a member is leader of * * @param \IPS\Member $member The member * @return int */ public static function numberOfClubsMemberIsLeaderOf( \IPS\Member $member ) /* !ActiveRecord */ /** * Set Default Values * * @return void */ public function setDefaultValues() /** * Get owner * * @return \IPS\Member|NULL */ public function get_owner() /** * Set member * * @param \IPS\Member * @return void */ public function set_owner( \IPS\Member $owner = NULL ) /** * Get created date * * @return \IPS\DateTime */ public function get_created() /** * Set created date * * @param \IPS\DateTime $date The invoice date * @return void */ public function set_created( \IPS\DateTime $date ) /** * Get club URL * * @return \IPS\Http\Url */ public function url() /** * Columns needed to query for search result / stream view * * @return array */ public static function basicDataColumns() /** * Edit Club Form * * @param bool $acp TRUE if editing in the ACP * @param bool $new TRUE if creating new * @param array $availableTypes If creating new, the available types * @return \IPS\Helpers\Form|NULL */ public function form( $acp=FALSE, $new=FALSE, $availableTypes=NULL ) /** * Custom Field Values * * @return array */ public function fieldValues() /** * Cover Photo * * @param bool $getOverlay If FALSE, will not set the overlay, which saves queries if it will not be used (such as in clubCard) * @return \IPS\Helpers\CoverPhoto */ public function coverPhoto( $getOverlay=TRUE, $position='full' ) /** * Location * * @return \IPS\GeoLocation|NULL */ public function location() /** * Get members * * @param array $statuses The membership statuses to get * @param int $limit Number to get * @param string $order ORDER BY clause * @param int $returnType 0 = core_clubs_memberships rows, 1 = core_clubs_memberships plus \IPS\Member::columnsForPhoto(), 2 = full core_members rows, 3 = same as 1 but also getting name of adder/invitee, 4 = count only * @return \IPS\Db\Select|int */ public function members( $statuses = array( 'member', 'moderator', 'leader' ), $limit = 25, $order = 'core_clubs_memberships.joined ASC', $returnType = 1 ) /** * Get basic data of a random ten members in the club (for cards) * * @return array */ public function randomTenMembers() /** * Add a member * * @param \IPS\Member $member The member * @param bool $status Status * @param bool $update Update membership if already a member? * @param \IPS\Member|NULL $addedBy The leader who added them, or NULL if joining themselves * @param \IPS\Member|NULL $invitedBy The member who invited them, or NULL if joining themselves * @return void * @throws \OverflowException Member is already in the club and $update was FALSE */ public function addMember( \IPS\Member $member, $status = 'member', $update = FALSE, \IPS\Member $addedBy = NULL, \IPS\Member $invitedBy = NULL ) /** * Remove a member * * @param \IPS\Member $member The member * @return void */ public function removeMember( \IPS\Member $member ) /** * Recount members * * @return void */ public function recountMembers() /* !Manage Nodes */ /** * Get available features * * @param \IPS\Member|NULL If a member object is provided, will opnly get the types that member can create * @return array */ public static function availableNodeTypes( \IPS\Member $member = NULL ) /** * Get Node names and URLs * * @return array */ public function nodes() /** * Can a member see this club and who's in it? * * @param \IPS\Member $member The member (NULL for currently logged in member) * @return bool */ public function canView( \IPS\Member $member = NULL ) /** * Can a member join (or ask to join) this club? * * @param \IPS\Member $member The member (NULL for currently logged in member) * @return bool */ public function canJoin( \IPS\Member $member = NULL ) /** * Can a member see the posts in this club? * * @param \IPS\Member $member The member (NULL for currently logged in member) * @return bool */ public function canRead( \IPS\Member $member = NULL ) /** * Can a member participate this club? * * @param \IPS\Member $member The member (NULL for currently logged in member) * @return bool */ public function canPost( \IPS\Member $member = NULL ) /** * Can a member invite other members * * @param \IPS\Member $member The member (NULL for currently logged in member) * @return bool */ public function canInvite( \IPS\Member $member = NULL ) /** * Does this user have leader permissions in the club? * * @param \IPS\Member $member The member (NULL for currently logged in member) * @return bool */ public function isLeader( \IPS\Member $member = NULL ) /** * Does this user have moderator permissions in the club? * * @param \IPS\Member $member The member (NULL for currently logged in member) * @return bool */ public function isModerator( \IPS\Member $member = NULL ) /** * Get status of a particular member * * @param \IPS\Member $member The member * @param int $returnType 1 will return a string with the type or NULL if not applicable. 2 will return array with status, joined, accepted_by, invited_by * @return mixed */ public function memberStatus( \IPS\Member $member, $returnType = 1 ) } Custom Fields Clubs can also have Custom Fields defined. These are on a per club basis, rather than per Node. If your Node implements Custom Fields per node as a feature, then you will need to ensure those custom fields are present within your Node models clubForm() method. Aside from the methods defined in the base \IPS\CustomField class, the following methods are defined within the \IPS\Member\Club\CustomField class. namespace IPS\Member\Club; /** * Clubs Customer Field Node */ class _CustomField extends \IPS\CustomField { /** * @brief [ActiveRecord] Multiton Store */ protected static $multitons; /** * @brief [ActiveRecord] Database Table */ public static $databaseTable = 'core_clubs_fields'; /** * @brief [ActiveRecord] Database Prefix */ public static $databasePrefix = 'f_'; /** * @brief [Node] Order Database Column */ public static $databaseColumnOrder = 'position'; /** * @brief [CustomField] Title/Description lang prefix */ protected static $langKey = 'core_clubfield'; /** * @brief [CustomField] Content database table */ protected static $contentDatabaseTable = 'core_clubs_fieldvalues'; /** * @brief [Node] ACP Restrictions * @code array( 'app' => 'core', // The application key which holds the restrictrions 'module' => 'foo', // The module key which holds the restrictions 'map' => array( // [Optional] The key for each restriction - can alternatively use "prefix" 'add' => 'foo_add', 'edit' => 'foo_edit', 'permissions' => 'foo_perms', 'delete' => 'foo_delete' ), 'all' => 'foo_manage', // [Optional] The key to use for any restriction not provided in the map (only needed if not providing all 4) 'prefix' => 'foo_', // [Optional] Rather than specifying each key in the map, you can specify a prefix, and it will automatically look for restrictions with the key "[prefix]_add/edit/permissions/delete" * @endcode */ protected static $restrictions = array( 'app' => 'core', 'module' => 'clubs', 'all' => 'fields_manage' ); /** * @brief [Node] Node Title */ public static $nodeTitle = 'clubs_custom_fields'; /** * @brief [Node] Title prefix. If specified, will look for a language key with "{$key}_title" as the key */ public static $titleLangPrefix = 'core_clubfield_'; /** * @brief [CustomField] Column Map */ public static $databaseColumnMap = array( 'content' => 'extra', 'not_null' => 'required', ); /** * @brief [CustomField] Additional Field Toggles */ public static $additionalFieldToggles = array( 'Checkbox' => array( 'f_filterable' ), 'CheckboxSet' => array( 'f_filterable' ), 'Radio' => array( 'f_filterable' ), 'Select' => array( 'f_filterable' ), 'YesNo' => array( 'f_filterable' ), ); /** * @brief [CustomField] Editor Options */ public static $editorOptions = array( 'app' => 'core', 'key' => 'Clubs' ); /** * @brief [CustomField] Upload Storage Extension */ public static $uploadStorageExtension = 'core_'; /** * Get fields * * @return array */ public static function fields() /** * Get if there are any filterable fields * * @return bool */ public static function areFilterableFields() /** * [Node] Add/Edit Form * * @param \IPS\Helpers\Form $form The form * @return void */ public function form( &$form ) /** * [ActiveRecord] Save Record * * @return void */ public function save() /** * [ActiveRecord] Delete Record * * @return void */ public function delete() /** * [Node] Format form values from add/edit form for save * * @param array $values Values from the form * @return array */ public function formatFormValues( $values ) }
  10. Implementing the new Reactions feature on your content items is relatively straightforward. In your content item class, add the \IPS\Content\Reactable trait to your class. And then defined a reactionType() method within your content class - note that this is required so as to differentiate your content from the rest of the applications installed - this is equivalent to the $reputationType property in IPS 4.1 and, if your application implemented reputation previously, should be the same as the previous property. Not defining this method will result in a BadMethodCallException being thrown. namespace IPS\myapp; class _MyContentClass extends \IPS\Content\Item { use \IPS\Content\Reactable; /** * Reaction Type * @return string */ public static function reactionType() { return 'my_content_id'; } } Once both the trait and the reactionType() method are added, the following methods from the \IPS\Content\Reactable trait will be available to your class automatically. /** * React * * @param \IPS\core\Reaction THe reaction * @param \IPS\Member The member reacting, or NULL * @return void * @throws \DomainException */ public function react( \IPS\Content\Reaction $reaction, \IPS\Member $member = NULL ) /** * Remove Reaction * * @param \IPS\Member|NULL The member, or NULL * @return void */ public function removeReaction( \IPS\Member $member = NULL ) /** * Can React * * @param \IPS\Member|NULL The member, or NULL for currently logged in * @return bool */ public function canReact( \IPS\Member $member = NULL ) /** * Reactions * * @param array|NULL $mixInData If the data is already know, it can be passed here to be manually set * @return array */ public function reactions() /** * Reaction Count * * @return int */ public function reactionCount() /** * Reaction Where Clause * * @param \IPS\Content\Reaction|array|int|NULL This can be any one of the following: An \IPS\Content\Reaction object, an array of \IPS\Content\Reaction objects, and integer, or an array of integers, or NULL * @return array */ public function getReactionWhereClause( $reactions = NULL ) /** * Reaction Table * * @return \IPS\Helpers\Table\Db */ public function reactionTable( $reaction=NULL ) /** * Has reacted? * * @param \IPS\Member|NULL The member, or NULL for currently logged in * @return \IPS\Content\Reaction|FALSE */ public function reacted( \IPS\Member $member = NULL ) /** * React Blurb * * @return string */ public function reactBlurb() /** * Who Reacted * * @param bool|NULL Use like text instead? NULL to automatically determine * @return string */ public function whoReacted( $isLike = NULL ) And then, in your templates, simply add the following where you would like reactions to be shown. {{if $item->hidden() !== 1 && \IPS\IPS::classUsesTrait( $item, 'IPS\Content\Reactable' ) and settings.reputation_enabled}} {template="reputation" group="global" app="core" params="$item"} {{endif}} And your content item will automatically implement reactions. Reactions themselves are Nodes and as such, can be expanded on by hooks themselves. namespace IPS\Content; /** * Reaction Model */ class _Reaction extends \IPS\Node\Model { /** * @brief Database Table */ public static $databaseTable = 'core_reactions'; /** * @brief Database Prefix */ public static $databasePrefix = 'reaction_'; /** * @brief [ActiveRecord] ID Database Column */ public static $databaseColumnId = 'id'; /** * @brief [Node] Node Title */ public static $nodeTitle = 'reactions'; /** * @brief [Node] Sortable */ public static $nodeSortable = TRUE; /** * @brief [Node] Positon Column */ public static $databaseColumnOrder = 'position'; /** * @brief [Node] Title prefix. If specified, will look for a language key with "{$key}_title" as the key */ public static $titleLangPrefix = 'reaction_title_'; /** * @brief [Node] Enabled/Disabled Column */ public static $databaseColumnEnabledDisabled = 'enabled'; /** * Form * * @param \IPS\Helpers\Form The form * @return void */ public function form( &$form ) /** * [Node] Format form values from add/edit form for save * * @param array $values Values from the form * @return array */ public function formatFormValues( $values ) /** * [Node] Does the currently logged in user have permission to delete this node? * * @return bool */ public function canDelete() /** * Get Icon * * @return \IPS\File */ public function get__icon() /** * Get Description * * @return strong */ public function get__description() /** * Fetch All Root Nodes * * @param string|NULL $permissionCheck The permission key to check for or NULl to not check permissions * @param \IPS\Member|NULL $member The member to check permissions for or NULL for the currently logged in member * @param mixed $where Additional WHERE clause * @return array */ public static function roots( $permissionCheck='view', $member=NULL, $where=array() ) /** * [Node] Get whether or not this node is enabled * * @note Return value NULL indicates the node cannot be enabled/disabled * @return bool|null */ protected function get__enabled() /** * Set Enabled * * @return void */ public function set__enabled( $enabled ) /** * Reaction Store * * @return array */ public static function reactionStore() /** * Is Like Mode * * @return bool */ public static function isLikeMode() /** * Load Record * * @see \IPS\Db::build * @param int|string $id ID * @param string $idField The database column that the $id parameter pertains to (NULL will use static::$databaseColumnId) * @param mixed $extraWhereClause Additional where clause(s) (see \IPS\Db::build for details) * @return static * @throws \InvalidArgumentException * @throws \OutOfRangeException */ public static function load( $id, $idField=NULL, $extraWhereClause=NULL ) /** * [ActiveRecord] Delete * * @return void */ public function delete() /** * [ActiveRecord] Save Changed Columns * * @return void */ public function save() /** * [Node] Get buttons to display in tree * Example code explains return value * * @code array( array( 'icon' => 'plus-circle', // Name of FontAwesome icon to use 'title' => 'foo', // Language key to use for button's title parameter 'link' => \IPS\Http\Url::internal( 'app=foo...' ) // URI to link to 'class' => 'modalLink' // CSS Class to use on link (Optional) ), ... // Additional buttons ); * @endcode * @param string $url Base URL * @param bool $subnode Is this a subnode? * @return array */ public function getButtons( $url, $subnode=FALSE ) } Important Notes Reactions can also be implemented on Comments and Reviews in the same way as Content Items - applications that use the default comment / review templates in core/front/global will automatically add the template code if the class uses \IPS\Content\Reactable. Also note that this replaces the reputation feature. As such all methods relating to Reputation in \IPS\Content, \IPS\Content\Item, \IPS\Content\Comment, and \IPS\Content\Review have been removed along with the \IPS\Content\Reputation interface. Previous reputation data will be upgraded automatically, and converted to an appropriate reaction, by the System application depending on the reputation system used (Like Mode, Upvote only, Downvote only, or Upvote / Downvote), so it is not necessary for you to add any upgrade routines for Reactions in your own applications.
  11. Completing long and complex forms online is tedious. It can be off putting having to fill in a lot of information before you can join a site or service. You may find that potential members never bother to convert from a visitor. How to convert guests into regular members is an often asked question. The simple answer is to lower the barrier to entry. Invision Community 4 already allows you to register with Facebook, Twitter, and other networks with ease. "Complete My Profile" is a system that will lower the barrier of conversion. Guests only have to complete a very basic form to gain membership. Members are then asked to complete any custom profile fields you require. You can also set up steps that group items together to encourage existing members to add more information to their public profile. Members with a complete profile and user photo provide others with much more engagement and personality. Registering If we look at registering first. Clicking "Sign Up" will only show a simple modal form with as few fields as possible. If you have required steps, and after any member validation flow, the complete your profile wizard is shown. This enforces required fields and the member cannot skip them or view other pages until completed. Of course, you may have steps that are not set to required. These are available too, but are skippable. Members can complete skipped steps later. A dismissible progress bar shows to members that have uncompleted steps. Once dismissed, it no longer displays in the header of the site. This same progress bar is always shown in the members' settings overview panel, in the user control panel. This will prompt members with incomplete steps. If you set up a new required step, members have to complete the step before being able to browse again. This will ensure that all regular members have completed profiles. Admin Control Panel You will create new steps in the Admin Control Panel. Each step can contain multiple elements of a single group. This step can be set to required to enforce completion or suggested to allow it to be skipped. The basic profile group contains things like user photo, birthday and cover photo. Choose any of these for this step. The custom profile field group contains any fields you have set up already. You can switch off this system if you feel it does not fit your needs. When disabled, you get the normal registration form. Reducing the complexity of membership can only help convert more guests into contributing members. Enforcing required steps ensures that you capture data across your membership. We hope you enjoy this feature and you see an increase in guest conversion with Invision Community 4.2.
  12. This is an entry about our IPS Community Suite 4.2 release We have introduced a feature which will allow moderators, with permission, to copy topics from the Forums application, into any custom database in the Pages application. New Moderation Item This feature will copy the contents of the first post of a topic, and copy it as the body of the new record. The moderator copying the topic will then be given a pre-populated form which will have the Title and Content fields automatically filled with the topic title and first post contents, which then can be modified to add any additional notes (such as an editors note at the bottom). The form will also include any custom fields that can be filled in, as well as all moderation options you would normally see when creating a record. Form Input In addition to all of the normal options you would typically see, there are two additional options. The first is the ability to copy all posts in the topic as comments on the new record. The second is the ability to post the record as the original topic author, or as the moderator copying the topic. This new feature is very useful when you might see a topic in the forums that you want to copy to an article or keep in a permanent knowledge base.
  13. I really need the image unmodified - once it is uploaded to the software, it no longer becomes usable as a test case as it's been altered.
  14. I would recommend submitting a ticket so I can examine your database directly.
  15. Version 4.7.19

    27,020 downloads

    These files are required to use developer mode in IPS Community Suite.
    Free
  16. Version 4.x

    32,681 downloads

    This script will check if you are ready to use Invision Community 4.x on your own server. Simply upload the script either to the directory where your current IP.Board 3.x. installation is (if you want to test if you're ready to upgrade) or anywhere else on your server (if you want to test if you can perform a fresh install) and run it in your web browser.
    Free
  17. Back in June, we announced several updates to our proprietary Spam Service, which includes influences from existing spam mitigation services (Project Honeypot and Stop Forum Spam). Today, we are announcing the release of more options to help you fine-tune the spam service for your site. These features are being released as a part of our new Enterprise Spam Service package, which is available now. Weighting The first feature added, as a part of this new package, is “Weighting.” With this feature, you will be able to adjust how influential the Spam Service is against registrations to your site. As you can see, the slider here presents several options to help fine-tune the service for your site. The options presented are fairly straightforward. If you find your site to be a heavy target of spam, you can adjust the slider to Strict or Very Strict as a means of telling the spam service that registrations to your site should be evaluated more vigorously than normal, and treat all registrations with higher caution than normal. Conversely, if you find the spam service to be too rough on registrations to your site, you can adjust the slider to Loose or Very Loose. Doing so will tell the spam service to take a step back on registrations, and treat them with less verbosity than normal. And finally, the middle option (Normal) will simply tell the spam service to act as it does now, with no preferential influence one way or another. Whitelisting / Blacklisting Another feature added as the ability to define your own custom White and Black Lists for your site, providing even more granular control in addition to weighting. First, you can define your own custom Whitelist entries. Using this interface, you will be able to add any Email Address or IP Address to your own custom whitelist. If a member registers, and is using any IP Address or Email Address defined here, then they will automatically be flagged as Not a Spammer, and no action taken against the account by the spam service. This is useful for Administrators, Moderators, and Developers who frequently test registrations on their own sites, allowing them to do so without turning the service off. Further, you can also define a custom Blacklist. If you find that the spam service may not be catching a newly released spammer fast enough, and need to prevent them from accessing the site immediately, then you may add their email address or IP Address to the Blacklist. Once added, any registrations from either of those will be flagged as a spammer and will be denied registration (depending on your community settings for Code Level 4). Calls from multiple origins As mentioned in the previous entry, this service also allows administrators to use the spam service in Load Balanced and Cloud environments with ease, using the same license key. The Enterprise Spam Mitigation is now available for $100/6 months as an additional add-on to your license. Please feel free to contact Sales for any additional information regarding this new service.
  18. http://community.invisionpower.com/calendar/1-community-calendar
  19. Testing
  20. Testing
×
×
  • Create New...