What it is
An EditorLocations extension allows your application to define a location that an editor instance may be used in. This is important for multiple reasons:
- An administrator may want to allow certain editor buttons to be available in some areas but not others
- You may want to allow HTML to be used in some editors but not others, independent of group permissions
- You may want to allow the administrator to control whether attachments can be uploaded in some editors
- An EditorLocations extension is necessary to properly map where attachment files are being used
How to use
The extension has several required methods, and a few optional methods that you can define. You will also then need to pass this extension classname to your editor form helper instances later.
/**
* Can we use HTML in this editor?
*
* @param \IPS\Member $member The member
* @return bool|null NULL will cause the default value (based on the member's permissions) to be used, and is recommended in most cases. A boolean value will override that.
*/
public function canUseHtml( $member )
{
return NULL;
}
The canUseHtml() method is used to determine if a member can post HTML in the editor instance. Typically you will want to return NULL to let the software determine automatically if the member has permission to post HTML, however in some instances you may wish to explicitly enable or disable the ability to use HTML in an editor.
/**
* Can we use attachments in this editor?
*
* @param \IPS\Member $member The member
* @param \IPS\Helpers\Form\Editor $field The editor field
* @return bool|null NULL will cause the default value (based on the member's permissions) to be used, and is recommended in most cases. A boolean value will override that.
*/
public function canAttach( $member, $field )
{
return NULL;
}
The canAttach() method determines if the user can upload attachments to this editor. You may wish to explicitly disable attachments in specific editors (e.g. if you provide a separate image upload field, in order to prevent confusion), in which case you can return FALSE from this method (or TRUE to explicitly enable attachments). NULL will allow the Invision Community software to determine automatically if the member has permission to upload attachments.
/**
* Permission check for attachments
*
* @param \IPS\Member $member The member
* @param int|null $id1 Primary ID
* @param int|null $id2 Secondary ID
* @param string|null $id3 Arbitrary data
* @param array $attachment The attachment data
* @return bool
*/
public function attachmentPermissionCheck( $member, $id1, $id2, $id3, $attachment )
{
return TRUE;
}
The attachmentPermissionCheck() is passed the $member being checked against, the ID values stored with the attachment, and the attachment data itself, and expects a boolean response to indicate if the member has permission to view the attachment. For private messages, for example, we need to verify if the member is a party to the conversation before permitting them to access the attachment, while for reports in the report center we need to verify the member can access reports before showing them the attachment.
/**
* Attachment lookup
*
* @param int|null $id1 Primary ID
* @param int|null $id2 Secondary ID
* @param string|null $id3 Arbitrary data
* @return \IPS\Http\Url|\IPS\Content|\IPS\Node\Model
* @throws \LogicException
*/
public function attachmentLookup( $id1, $id2, $id3 )
{
// ....
}
The attachmentLookup() method is passed the ID values stored with the attachment and expects, in turn, an instance of \IPS\Http\Url, \IPS\Content or \IPS\Node\Model to be returned. The method is designed to accept some data about an attachment and then return the content that the attachment is attached to. The method is designed to throw a LogicException, so you can call load() against a content item without attempting to catch it. e.g. return \IPS\myapp\ContentItem::load( $id1 ); If the content item does not exist, an exception will be thrown automatically.
/**
* Rebuild content post-upgrade
*
* @param int|null $offset Offset to start from
* @param int|null $max Maximum to parse
* @return int Number completed
* @note This method is optional and will only be called if it exists
*/
public function rebuildContent( $offset, $max )
{
return $this->performRebuild( $offset, $max, array( 'IPS\Text\LegacyParser', 'parseStatic' ) );
}
When a user upgrades to 4.x or higher, all content previously stored must be rebuilt. The rebuildContent() method allows you to control how your legacy content is rebuilt. Most extensions define a central performRebuild() method as you see here (outlined later in this article), so if you do the same you can likely copy the method above verbatim.
/**
* Rebuild attachment images in non-content item areas
*
* @param int|null $offset Offset to start from
* @param int|null $max Maximum to parse
* @return int Number completed
*/
public function rebuildAttachmentImages( $offset, $max )
{
return $this->performRebuild( $offset, $max, array( 'IPS\Text\Parser', 'rebuildAttachmentUrls' ) );
}
When a user upgrades to 4.x or higher, attachments in non-content areas need to rebuilt. The rebuildAttachmentImages() method allows you control how this occurs, however most extensions define a central performRebuild() method (outlined later) and if you do the same you can likely copy this method verbatim.
/**
* Rebuild content to add or remove image proxy
*
* @param int|null $offset Offset to start from
* @param int|null $max Maximum to parse
* @param bool $status Enable/Disable Image Proxy
* @return int Number completed
* @note This method is optional and will only be called if it exists
*/
public function rebuildImageProxy( $offset, $max, $status=TRUE )
{
return $this->performRebuild( $offset, $max, array( 'IPS\Text\Parser', 'parseImageProxy' ) );
}
When toggling the image proxy feature on and off in the AdminCP, the administrator is afforded an opportunity to rebuild their existing URLs. The rebuildImageProxy() method allows you to define how this occurs. As with the previously described two methods, most extensions use a centralized performRebuild() method for this purpose.
/**
* Total content count to be used in progress indicator
*
* @return int Total Count
*/
public function contentCount()
{
return X;
}
When content is being rebuilt through one of the previously described operations, the total number of content to be rebuilt is needed in order to ensure we show an accurate progress indicator. Most times, this method simply selects a count from a database table.
/**
* Perform rebuild - abstracted as the call for rebuildContent() and rebuildAttachmentImages() is nearly identical
*
* @param int|null $offset Offset to start from
* @param int|null $max Maximum to parse
* @param callable $callback Method to call to rebuild content
* @return int Number completed
*/
protected function performRebuild( $offset, $max, $callback )
{
// Execute here...
}
While this method is not part of the extension definition, most extensions process rebuilding attachment URLs, older content and proxy image rebuilds sufficiently similar that the extension defines one central method that the other methods simply call to. Generally when used, this method loops over the content records (i.e. by looping over records in a database table, using the $offset and $max values as the LIMIT offsets for the database query), and then passes the content to the callback that was specified. It is important to catch any InvalidArgument exceptions that may be thrown, as old content can be unpredictable and may contain formatting issues that cause exceptions to be thrown when we attempt to rebuild it.
The rebuilt content should then be stored back in the database by updating the record that is being processed. Finally, the number of records completed should be returned (when 0 is returned, the rebuild process is considered completed).
Specifying the EditorLocations extension to use
The final step to using this extension is to define it when you create an Editor form helper instance.
$form->add( new \IPS\Helpers\Form\Editor( 'content', NULL, TRUE, array( 'app' => 'core', 'key' => 'ExtensionKey', 'autoSaveKey' => '...' ) ) );
Here, the editor instance will look for an EditorLocations key named "ExtensionKey" in the core application. The methods outlined above will then affect the behavior of the editor as previously noted.
Report Document