IPS Staff
  • Content count

  • Joined

  • Last visited

About Mark

  • Rank
    Meet Jay
  • Birthday March 04

IPS Marketplace

  • Resources Contributor Total file submissions: 5

Profile Information

  • Gender Male
  • Location Colchester, UK

Recent Profile Visitors

126,278 profile views

Mark's Activity

  1. Mark added a article in Development   

    Pluralized Language Strings
    Some language strings in IPS Community Suite have a format like this:
    {# [1:post][?:posts]} This is to accommodate how words can change depending on the number of things they are referring to. In this example, the string may display "1 post" or "2 post s " depending on the number passed to it. Although in English there is usually only 2 forms (singular and plural), the syntax is designed to be flexible so that any language can be accommodated.
    Basic Format
    The basic format is:
    {#[x:value]} The meaning of each section is:
    The curly braces mark the boundaries and are required. The # at the start is where the number will go and is also required and must be at the front. Each set of square brackets represents a possible value. It contains the value, followed by a colon, followed by the word(s) to display if that is the value. At the start of the square brackets, the value (represented here by "x") can be a number, or "?" for all other values not specified.  
    Hiding or moving the number
    If you do not want to display the number, you can add an ! before the #. For example:
    %s {!#[1:likes][?:like]} this Will display "%s likes this" if the value passed is 1 or "%s like this" for any other value.
    If you do this, you can display the number elsewhere by placing another # where you want the number to display. For example:
    {!#[1:week][?:# weeks]} Will display "week" if the value passed is 1, or " x  weeks" for any other value.
    Including additional content
    You can include additional characters where desired within the syntax. For example:
    {# anonymous [1:member][?:members]} Will display "1 anonymous member" is the value passed is 1 or " x  anonymous members" for any other value.
    Rather than provide a specific number or "?" for all other values, you can also use wildcards. * will match anything which begins with the following number and % will match anything that ends with it. Though these are never used in English, they are necessary in some languages.
    For example:
    {#[*1:posts][*2:posts][?:posts]} Though all display the same value, the first block will match any number ending with 1 (for example: 1, 21, 171), the second block any number ending with 2, and the final block matching any number.
    {#[%1:posts][%2:posts][?:posts]} Though all display the same value, the first block will match any number beginning with 1 (for example: 1, 12, 108), the second block any number beginning with 2, and the final block matching any number.
    Multiple Numbers
    You can create strings which accept multiple numbers, and prefix any #s with an index.
    For example:
    {0# [1:post][*2:posts]} and {1# [1:view][*2:views]} Will expect two values. If passed 1 and 2, it will return "1 post and 2 views".
    • 0 replies
  2. Mark added a post in a topic: External tools for advanced users   

    Deleting the stored files should cause them to be rebuilt. There was an issue with how this is handled for theme-related files which will be fixed in the next beta  
    And yeah, isn't it awesome? Just include init.php and you're away  
  3. Mark added a article in Development   

    Translatable Text Fields
    Translatable text fields can be used to allow the user to provide a different value for all of the different languages they have on their community (if they have only one, they will appear to be a regular text field). They are commonly used for when an administrator has to provide the name for something which may need to be different depending on the language. They are not designed to be used outside of the ACP .
    To use translatable fields in your code, you use the \IPS\Helpers\Form\Translatable class within the  Form Helper . U sing a Translatable field is slightly more complicated than most other form types .
    Creating the element
    When creating the element you must provide an $options parameter specifying an application which "owns" the language string and a key. If you are displaying a "create" form for something which hasn't been created yet, you can pass NULL as the key.
    Unlike other fields, $defaultValue must be NULL. The system will automatically fill in the current value for the key.
    For example,  the code to create your element will look something like:
    $form->add( new \IPS\Helpers\Form\Translatable( 'my_translatable_field', NULL, TRUE, array( 'app' => 'app' 'key' => 'my_language_string' ) ) );  
    Handling Submissions
    You must save the returned value manually like so: \IPS\Lang::saveCustom( 'app', 'my_language_string', $values['my_translatable_field'] );  
    Text area and editors
    You can make the field a texture rather than single-line textbook by setting the "textArea" element in $options to TRUE. To use a full WYSIWG editor, you must make  all the normal considerations for an editor field . Then set the "editor" element in $options to what you would normally set as $options in the \IPS\Helpers\Form\Editor object.
    • 0 replies
  4. Mark added a bug in IPS4 Bug Tracker   

    Content databases auto save
    When I create a new database record, I get the autosaved content of the record I was last editing. I'm guessing it's mistakenly using the same autoSaveKey for edits as for creates.
    • 0 replies
  5. Mark added a article in Development   

    File Uploads
    To allow file uploads in your code, you use the \IPS\Helpers\Form\Upload class within the  Form Helper .
    The administrator has the ability to control how to store different types of file - due to this, using an Upload field is slightly more complicated than most other form types.
    The FileStorage Extension
    You are required to create an FileStorage extension within your application which is mostly used to provide callbacks to locate files uploaded by your field.
    To get started, create an FileStorage extension file through the developer center for your application. A skeleton file will be created in the applications/app/extensions/core/ FileStorage folder with example code. You will need to provide code for all the methods.
    For example, if you are storing each file in a row in a database table, the code might look something like this:
    <?php namespace IPS\forums\extensions\core\FileStorage; class _Key { /** * Count stored files * * @return int */ public function count() { return \IPS\Settings::i()->setting_key ? 1 : 0; } /** * Move stored files * * @param int $offset This will be sent starting with 0, increasing to get all files stored by this extension * @param int $storageConfiguration New storage configuration ID * @param int|NULL $oldConfiguration Old storage configuration ID * @throws \Underflowexception When file record doesn't exist. Indicating there are no more files to move * @return void */ public function move( $offset, $storageConfiguration, $oldConfiguration=NULL ) { $thing = \IPS\Db::i()->select( '*', 'my_table', 'image IS NOT NULL', 'id', array( $offset, 1 ) )->first(); \IPS\Db::i()->update( 'my_table', array( 'image' => (string) \IPS\File::get( $oldConfiguration ?: 'app_Key', $thing['image'] )->move( $storageConfiguration ) ), array( 'id=?', $thing['id'] ) ); } /** * Check if a file is valid * * @param \IPS\Http\Url $file The file to check * @return bool */ public function isValidFile( $file ) { try { \IPS\Db::i()->select( 'id', 'my_table', array( 'image=?', $file ) )->first(); return TRUE; } catch ( \UnderflowException $e ) { return FALSE; } } /** * Delete all stored files * * @return void */ public function delete() { foreach( \IPS\Db::i()->select( '*', 'my_table', "image IS NOT NULL" ) as $forum ) { try { \IPS\File::get( 'app_Key', $forum['image'] )->delete(); } catch( \Exception $e ){} } } } However the appropriate code to use will depend on the nature of how the content created by your files are stored.
    Creating the element
    When creating the element you must provide an $options parameter specifying the extension you just created.  For example, the code to create your element will look something like:
    $form->add( new \IPS\Helpers\Form\Upload( 'my_upload_field', NULL, TRUE, array( 'storageExtension' => 'app_Key' ) ) ); Additional options are available to allow multiple file uploads, to restrict the allowed extensions, the maximum file size and more. See the source code for all the available options.
    Handling Submissions
    The value returned will be an object of \IPS\File (or an array of \IPS\File objects if the field allows multiple file uploads). You do not need to do anything with the file itself, as it has already been stored according to the administrators preference. You do however, have to save the URL to it (which you can get by casting the \IPS\File object to a string) as that is what you will need to get and manipulate the file later, and use within the extension you created earlier. For example, your code might look like:
    $form = new \IPS\Helpers\Form; $form->add( new \IPS\Helpers\Form\Upload( 'my_upload_field', NULL, TRUE, array( 'storageExtension' => 'app_Key' ) ) ); if ( $values = $form->values() ) { \IPS\Db::i()->insert( 'my_table', array( 'image' => (string) $values['my_upload_field'] ) ); }  
    Manipulating the file later
    To get the \IPS\File object back, you simply call:
    $file = \IPS\File::get( 'app_Key', $url ); The first parameter being your extension, and the second being the URL you obtained when saving the form. You can then use this object to get the contents of the file, delete it, etc. See the phpDocs in \IPS\File for more information on what you can do with files.
    If it is an image file, you can also create an \IPS\Image object to resize, add a watermark, etc. To do this you call:
    $image = \IPS\Image::create( $file->contents() );  See the phpDocs in \IPS\Image for more information on what you can do with images. 
    • 0 replies
  6. Mark added a article in Development   

    To use the WYSIWG editor in your code, you use the \IPS\Helpers\Form\Editor class within the Form Helper .
    Editors automatically have the ability to support attachments, and the administrator can customise the editor to make certain features available in some areas and not others - due to these concerns, using an Editor is slightly more complicated than most other form types.
    The EditorLocations Extension
    You are required to create an EditorLocations extension within your application which is mostly used to provide callbacks to locate attachments uploaded to that editor.
    To get started, create an EditorLocations extension file through the developer center for your application. A skeleton file will be created in the applications/app/extensions/core/EditorLocations folder with example code. You will need to provide code for the attachmentPermissionCheck() and attachmentLookup() methods.
    For example, if you are using this editor for an ACP setting, the value of which will be displayed on a certain page which is visible to all members who have access to the application, the code might look something like this:
    <?php namespace IPS\app\extensions\core\EditorLocations; class _Key { /** * 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 * @return bool */ public function attachmentPermissionCheck( $member, $id1, $id2, $id3 ) { return $member->canAccessModule( \IPS\Application\Module::get( 'app', 'module' ) ); } /** * 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 ) { return \IPS\Http\Url::internal( 'app=app&module=module&controller=controller', 'front' ); } } Or if you are using this editor for the description for  Content Items that members create, the code might look something like:
    <?php namespace IPS\app\extensions\core\EditorLocations; class _Key { /** * 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 * @return bool */ public function attachmentPermissionCheck( $member, $id1, $id2, $id3 ) { try { return \IPS\app\Thing::load( $id1 )->canView( $member ); } catch ( \OutOfRangeException $e ) { return FALSE; } } /** * 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 ) { return \IPS\app\Thing::load( $id1 )->url(); } } However the appropriate code to use will depend on the nature of how the content created by your editor will be used.
    Note that you do not have to (and shouldn't) create a separate extension for every single editor. It is common practice for example, to use one extension for every setting field within your application. The $id parameters allow you to know specifically what piece of content is referenced, as explained below.
    You must also create a language string which identfies your editor with the key "editor__ app _ Key ". This is used to display to the admin when they are configuring which buttons show up in which areas. For example, in the core application, the key "editor__core_Contact" is defined as "Contact Form".
    Creating the element
    When creating the element you must provide an $options parameter specifying the extension you just created, along with some additional information:
    autoSaveKey  is a string which identifies this editor's purpose. For example, if the editor is for replying to a topic with ID 5, you could use "topic-reply-5". Make sure you pass the same key every time, but a different key for different editors. attachIds are discussed below. Can contain up to 3 elements, and the first 2 must be numeric, but the last can be a string For example, the code to create your element will look something like:
    $form->add( new \IPS\Helpers\Form\Editor( 'my_editor', NULL, TRUE, array( 'app' => 'app', 'key' => 'Key', 'autoSaveKey' => 'my-editor-field', 'attachIds' => array( ... ) ) );  
    Claiming Attachments
    Generally speaking, there are two types of content: things which always exist (like settings) and content which is created and deleted. Attachments are handled differently for each:
    Things which always exist
    Pass an identifier to the attachIds parameter. For example, you might do:
    'attachIds' => array( 1 ) And then in your extension you will look at $id1 and know that 1 is for this instance of the editor. Then you would use different numbers for other editors using the same extension.
    Even if there is only one editor using this extension, you must provide a value for attachIds. The system will then automatically handle claiming attachments.
    Things which are created and deleted
    When displaying the editor on the "create" screen you will pass NULL for attachIds (because of course at this point you don't know what ID you will save it with). Then, in the code for your form which handles creating the content, after you have created the content and therefore have an ID for it, you call this code:
    \IPS\File::claimAttachments( $autoSaveKey, $id1, $id2, $id3 ); $autoSaveKey is the same value used for the autoSaveKey - each of the $id fields are optional but you must provide at least one. They are what will be passed to the method in your extension.
    When displaying the editor on the "edit" screen, you pass the ID values to attachIds and do not call claimAttachments.
    For example:
    $editing = NULL; if ( \IPS\Request::i()->id ) { try { $editing = \IPS\app\Thing::load( \IPS\Request::i()->id ); } catch ( \OutOfRangeException $e ) { \IPS\Output::i()->error( ... ); } } $form = new \IPS\Helpers\Form; $form->add( new \IPS\Helpers\Form\Editor( 'my_editor', NULL, TRUE, array( 'app' => 'app', 'key' => 'Key', 'autoSaveKey' => $editing ? 'creating-thing' : "editing-thing-{$editing->id}", 'attachIds' => $editing ? array( $editing->id ) : NULL ) ) ); if ( $values = $form->values() ) { if ( !$editing ) { $item = new \IPS\app\Thing; $item->content = $values['my_editor']; $item->save(); \IPS\File::claimAttachments( 'creating-thing', $item->id ); } else { $editing->content = $values['my_editor']; $editing->save(); } }  
    • 0 replies
  7. Mark added a article in Development   

    Form Helper
    IPS Community Suite has a powerful Form helper class allowing developers to create forms easily, with automatic validation and security. Forms can include file uploads, can be tabbed, are HTML5 ready and have lots of other features. If you are asking for user-input, you should  always  use the form helper,  never  write such functionality manually.
    Your form code will usually look something like this:
    $form = new \IPS\Helpers\Form; $form->add( ... ); $form->add( ... ); if ( $values = $form->values() ) { // Form submitted } \IPS\Output::i()->output = $form;  
    Adding form elements
    Adding an element a form is done by the $form->add() method. You pass it an object of the element you want - for example, to add a text input to your form, you can do:
    $form->add( new \IPS\Helpers\Form\Text('name') ); Some of the classes available are:
    \IPS\Helpers\Form\Text for normal text input \IPS\Helpers\Form\Editor for WYSIWG text input \IPS\Helpers\Form\Upload for file uploads \IPS\Helpers\Form\Date for dates \IPS\Helpers\Form\Select for a select box \IPS\Helpers\Form\YesNo for yes/no radio buttons The constructor for all of these classes is:
    /** * Constructor * * @param string $name Name * @param mixed $defaultValue Default value * @param bool|NULL $required Required? (NULL for not required, but appears to be so) * @param array $options Type-specific options * @param callback $customValidationCode Custom validation code * @param string $prefix HTML to show before input field * @param string $suffix HTML to show after input field * @param string $id The ID to add to the row * @return void */ public function __construct( $name, $defaultValue=NULL, $required=FALSE, $options=array(), $customValidationCode=NULL, $prefix=NULL, $suffix=NULL, $id=NULL ) For all of the available classes, look at the files in the system/Helpers/Form/ directory. The values acceptable for $options are documented in the source code for each. Be aware that some extend others (for example CheckboxSet extends Select, and has the same $options).
    For example, to create a multi-select box you would do something like:
    $form->add( new \IPS\Helpers\Form\Select( 'my_select_box', NULL, TRUE, array( 'options' => array( 0 => 'Foo', 1 => 'Bar', 2=> 'Baz' ), 'multiple' => TRUE ) ); Some classes, due to their complexity have further documentation available:
    \IPS\Helpers\Form\Editor \IPS\Helpers\Form\Upload \IPS\Helpers\Form\Translatable  
    Labels and Descriptions
    The $name property, in addition to being the name used for the HTML field, is also used for the label to display. The form helper will automatically look for a language string with the same key to use as the label.
    It will also look for a language string appended with "_desc" for the description. For example, if the $name for your field is "my_field", it will use the language string "my_field_desc" as the description. If a language string with that key doesn't exist, no description will be used.
    It will also look for a language string appended with "_warning" for a warning block (again if it doesn't exist none is shown). This is normally only ever used with toggles (see below) for example to display a warning when the user selects a particularly dangerous option.
    Most classes will provide automatic validation, and their $options provide ways of customising this. For example, if you create an \IPS\Helpers\Form\Number element - it will automatically check if the value is a number, and you can use $options to control the maximum and minimum along with the number of allowed decimal points. The system will automatically display the form again with an inline error message if any of the elements don't validate with no extra code required from you. If however, you want to include custom validation, you can do this with the $customValidationCode property - you simply provide a callback method which throws a DomainException if there's an error. For example, if you wanted a number field where the number 7 is specifically not allowed you could do this like so:
    $form->add( new \IPS\Helpers\Form\Number( 'my_field', NULL, TRUE, array(), function( $val ) { if ( $val == 7 ) { throw new \DomainException('form_bad_value'); } } ) );  
    Some fields like radios, select boxes and yes/no fields provide a feature called "toggles" which allow you to show or hide other elements depending on the selected value. For example, you might have a yes/no field to turn a feature on, and only when it is set to "yes" do other settings related to it show.
    The options available for this depends on the field type. For example, YesNo has two options: togglesOn (which controls which elements to show when the setting is set to "Yes") and togglesOff  (which controls which elements to show when the setting is set to "No"). Select has one toggles option which accepts an array, specifying which elements should show for each of the available values. Number has an  unlimitedToggles  which specifies which elements show when the "Unlimited" checkbox is checked and a  unlimitedToggleOn option to reverse that behaviour to when the checkbox is unchecked. For more information, see the source code for each element type.
    All of these options accept the HTML ID for what they should show/hide. To make other form elements show/hide you will need to provide IDs for them in the constructor. For example, this form has a YesNo field which when set to "Yes" shows a text input field:
    $form->add( new \IPS\Helpers\Form\YesNo( 'yes_no_field', NULL, TRUE, array( 'togglesOn' => array( 'text_field_container' ) ) ) ); $form->add( new \IPS\Helpers\Form\Text( 'text_field', NULL, TRUE, array(), NULL, NULL, NULL, 'text_field_container' ) );  
    Handling Submissions
    When your form is submitted $form->values() will return an array with the values of each element (if the form has not been submitted or validation fails, it returns FALSE).
    The value returned for each element depends on the type, and sometimes the options. For example, an \IPS\Helpers\Form\Text element always returns a string as it's value. However, \IPS\Helpers\Form\Number might return an integer or a float. \IPS\Helpers\Form\Upload, on the other hand, returns an \IPS\File object (or even an array of them if it's a multiple file upload field).
    If you prefer to only receive string values (for example, you want to save the values as a JSON object), you can pass TRUE to the $form->values() method.
    Tabs, Headers and Separators
    The \IPS\Helpers\Form object provides a number of other methods to create tabbed forms, include headers, etc. For example:
    $form->addTab('Tab1'); $form->addHeader('Header'); $form->add( new \IPS\Helpers\Form\YesNo( 'yes_no_field' ) ); $form->add( new \IPS\Helpers\Form\Text( 'text_field' ) ); $form->addHeader('Header'); $form->add( new \IPS\Helpers\Form\Text( 'another_text_field' ) ); $form->addTab('Tab2'); $form->add( new \IPS\Helpers\Form\Select( 'select_field', NULL, FALSE, array( 'options' => array( 0, 1, 2, 3 ) ) ) ); For more information on the available methods, see the phpDocs in \IPS\Helpers\Form.
    Custom Display HTML
    Casting the $form object to a string returns the HTML to display the form. By default, the form is "horizontal". To use "vertical", or to apply any other classes to the form, you can do:
    $form->class = 'ipsForm_vertical'; For further customisation, you can call $form->customTemplate() passing a callback with a template to use. This allows you to totally customise the look of the form.
    A common use of this is to use a template that looks better in modals:
    \IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) ); If you want to create a custom template, you could use the popupTemplate as an example.
    Advice and Best Practices
    Forms make up a large portion of the UI within the IPS Community. It is important to remember to present a UI that is consistent with other areas of the suite. To this end, we recommend the following best practices:
    Always phrase settings in the positive. For example, say "Enable feature?", don't say "Disable feature?". "Yes" should always mean something is "On". Make labels short and concise and use descriptions only if necessary. For example, don't have a field where the label is "Enable feature?" and the description is "Set this to yes to enable the feature" - that description isn't necessary. Use prefixes and suffixes rather than adding information to the label or description where possible. For example, don't have a label that says "Number of days before deleting" - make the label "Delete after" and the suffix that appears after the field say "days". Never refer to other settings in labels or descriptions. For example, do not have a description that says "Only applies if the above setting is on". Use toggles to indicate this to the user. Never make entering a particular value do something special. For example, do not have a description that says "Leave blank for unlimited" - use an unlimited checkbox or a separate setting which toggles other settings.
    • 0 replies
  8. Mark added a article in Development   

    Template Tags
    Template tags are short tags that can be used in templates. Some can also be used in CSS files.
    Template tags look like this:
    {tag="value"} Some template tags accept options:
    {tag="value" option="option_value" other_option="other_value"} Useful links for further reading :
    Template Logic  
    Language Strings
    {lang="key"} Returns the language string for the language being used by the member that is currently logged in.
    Available options:
    sprintf  can be used to pass values to be replaced using sprint . The values will be escaped to prevent XSS; if this is undesired,  htmlsprintf  does the same without the escaping (be careful not to introduce XSS vulnerabilities in this way). pluralize  can be used to pass values if the string uses pluralizing logic . wordbreak  inserts <wbr> tags throughout the returned value and should be used on user-supplied values which might cause the layout to break  
    Dates & Times
    {date="699753360"} Returns a value for displaying a date. By default is automatically relative (e.g. may display "5 minutes ago"), uses HTML5 <time> tags, will automatically condense on smaller displays and is locale-aware. It can be passed either a unix timestamp or an instance of \IPS\DateTime.
    Available options:
    norelative  just displays the date and time in a locale-aware format, without using <time> tags or the other features. You only need to specify norelative="true". dateonly  works like norelative but displays just the date and not the time.  
    Member Data
    {member="name"} Returns the value of the specified parameter for the currently logged in member. In this example, returns the value of \IPS\Member::loggedIn()->name.
    Available options:
    group  will return the value from the member's group information i.e. \IPS\Member::loggedIn()->group[ $key ] - you only need to specify group="true". id  will make it use the member with the specified ID rather than the currently logged in member. raw  will prevent the returned value from being escaped to prevent XSS vulnerabilities. You only need to specify raw="true". Be careful when doing this that you do not introduce XSS vulnerabilities.  
    {setting="board_name"} Returns the value of the setting with the specified key.
    Theme Settings
    {theme="selected"} Returns the value of the theme setting with the specified key.
    URLs  (can be used in CSS)
    {url="app=core&module=system&controller=login"} Returns a URL.
    Available options:
    seoTemplate should be the FURL template to use. seoTitle should be the title to use in the friendly URL, if applicable. If there needs to be more than one,  seoTitle s can be provided with comma-delimited values. csrf  if provided, will add the CSRF key to the URL. You only need to specify csrf="true". fragment  if provided, will add a #fragment to the end of the URL with the provided value. noprotocol  if provided, will return the URL without a protocol. You only need to specify noprotocol="true". plain  will provide the URL in plaintext rather than HTML-entity encoded. If usually only used by plaintext email templates. You only need to specify plain="true".  
    {number="1000"} Returns the number formatted according to the user's locale. For example "1,000".
    File Size
    {filesize="1000000"} Returns a human-readable representation of a number of bytes. For example, "1MB".
    Available options:
    decimal  will make it use decimal (1000 bytes = 1kB) rather than binary (1024 bytes = 1kB).  
    CSS Prefixes  (can be used in CSS)
    {prefix="transition" value="0.1s all linear"} Returns a block of CSS code with all the browser-specific prefixes ("-webkit-", "-moz-", "-ms-" and "-o-"). The option is required.
    {template="userPhoto" group="global" app="core" params="$entry->author(), 'small'"} Includes another template. All options are required.
    Images  (can be used in CSS)
    {image="image.png" app="core" location="front"} Returns the URL to the image. All options are required.
    Expression  (can be used in CSS)
    {expression="1+1"} Executes the provided value as PHP and displays the return value.
    Available options:
    raw  will prevent the returned value from being escaped to prevent XSS vulnerabilities. You only need to specify raw="true". Be careful when doing this that you do not introduce XSS vulnerabilities.  
    Wordbreak  (can be used in CSS)
    {wordbreak="This is some very long text..."} Returns the value with <wbr> tags inserted throughout it so as to avoid breaking the layout for user-provided content which may be very long.
    Truncate  (can be used in CSS)
    {truncate="Some very long text..." length="10"} Returns the value truncated to the specified length. Option is required.
    Additional available options:
    start  can be provided as an offset. append  can be provided to provide an alternate value to use instead of &hellip; to add to the end of truncated text.  
    {advertisement="location"} Displays whatever advertisement is configured to display at the specified location.
    • 0 replies
  9. Mark added a article in Development   

    The Autoloader
    Classes in IPS Community Suite 4 are "autoloaded". This means you never have to include or require an IPS4 source file.
    For reference, the autoload method is \IPS\IPS::autoloader() which is located in the init.php file of the root directory.
    Locating Classes
    Classes must be located in the correct location and be named properly so the autoloader can find them. There are generally three locations:
    Framework classes
    Classname structure: \IPS\Namespace\Class
    Location on disk: system/Namespace/Class.php
      Application classes
    Classname structure: \IPS\app\Namespace\Class  (note that the application key is lowercase, but the parts after are PascalCase)
    Location on disk: applications/app/system/Namespace/Class.php
      Application extensions and modules
    Classname structure: \IPS\app\modules\front\module\controller  (note all the parts are lowercase)
    Location on disk: applications/app/modules/front/module/controller.php For Framework classes and Application classes, the final file must always be in within a folder (not loose in the system directory). If only one-level deep, the system will look for a file in a directory of the same name. For example \IPS\Member is located in system/Member/Member.php while \IPS\Member\Group is located in system/Member/Group.php
    Monkey Patching
    When declared, classes always start with an underscore. For example, throughout the IPS Community Suite, you call \IPS\Member, however, if you look in the source file you will see it declared like this:
    namespace IPS; class _Member { ... This is a technicality of a feature called  monkey patching which allows third party developers to overload any class without any consideration throughout the code and in a way where hooks do not conflict with one another.
    In this example, the system will execute, right after autoloading the Member.php file, code like this:
    namespace IPS; class Member extends \IPS\_Member { }  
    If a third party developer wants to overload \IPS\Member, the system will inject this in between, so you end up with a structure like so:
    \IPS\Member extends hook1 hook1 extends \IPS\_Member   
    Or if two hooks wanted to overload \IPS\Member:
    \IPS\Member extends hook1 hook1 extends hook2 hook2 extends \IPS\_Member This means that the framework, and any third party code only ever has to call \IPS\Member, and the system will not only automatically autoload the source, but create a chain of any hooks wanting to overload the class.
    If the mechanics of this are confusing (it is an unusual practice) - it is not essential to thoroughly understand. You only need to know that classes must be prefixed with an underscore when declared, but that underscore is never used when actually calling the class.
    It also means that rather than calling "self::" within the class, you should call "static::" so you are calling the overloaded class, not the original.
    Third Party Libraries
    If you want to use a third-party PHP library they will of course need to be included manually and cannot be monkey patched.
    If the library follows the PSR-0  standard for naming, you can add it to \IPS\IPS::$PSR0Namespaces like so, and the autoloader will then autoload it:
    \IPS\IPS::$PSR0Namespaces['Libary'] = \IPS\ROOT_PATH . '/applications/app/system/3rd_party/Library'; Otherwise, you will need to include the source files manually.
    • 0 replies
  10. Mark added a article in Development   

    Right-to-Left Language Support
    In the most scripts, including the Latin script used in most Western and Central European languages, writing starts from the left side of the page and continues to the right. This is however, always the case. For example, in the Arabic script (used for the Arabic, Persian and many other languages) and the Hebrew script start at the right side of the page and continue to the left.

    This influences many design decisions. For example, a web page will usually have its logo in the top-left, as this is the first place users usually look. However, speakers of languages which use right-to-left scripts will expect the logo in the top-right. Similarly, the "close" button usually found in the top-right of popups will need to be on the top-left, and progress bars will need to fill the other way around. In fact, when using a right-to-left script, the entire page should be like a mirror image of how it usually is.

    IPS Community Suite fully supports right-to-left scripts. As a designer creating themes, or a developer creating applications and plugins, it is important that you consider right-to-left scripts and maintain full support for them. Fortunately, doing so is very easy. There are just 3 considerations you need to make.

    Step One: Add RTL-Specific Declaration Blocks to your CSS

    In your CSS, almost everywhere where you use the words "left" or "right" or make a declaration which affects either the left or right (such as "margin", "padding" or "border") you must change your CSS to reverse this for RTL scripts. IPS Community Suite will always have a "dir" attribute on the HTML tag specifying "ltr" or "rtl" so it is easy to add these declarations.

    For example, if you have this in your CSS:
    .myClass { color: red; font-size: 2em; float: left; padding: 0px 10px 0px 50px; margin-right: 5px; } You will need to change this to:
    .myClass { color: red; font-size: 2em; } html[dir="ltr"] .myClass { float: left; padding: 0px 10px 0px 50px; margin-right: 5px; } html[dir="rtl"] .myClass { float: right; padding: 0px 50px 0px 10px; margin-left: 5px; } The first block is making declarations which apply regardless of the direction. The next two blocks are declaring styles which will only apply if the text direction is left-to-right or right-to-left, respectively.

    Step Two: Flip Images

    If you use any images which indicate a direction, these will need to be flipped for right-to-left scripts. For example, if you have a right arrow, this will probably need to be changed to a left arrow, otherwise it will be pointing in the wrong direction.

    If you use FontAwesome icons in your HTML, these are usually automatically flipped. However, if you declare them in CSS, you may need to add conditional statements again. You will also need to manually flip any other images you are using.

    For example, if you have this CSS declaration:
    .nextLink:after { font-family: 'FontAwesome'; content: '\\f105'; } You will need to change this to:
    .nextLink:after { font-family: 'FontAwesome'; } html[dir="ltr"] .nextLink:after { content: '\\f105'; } html[dir="rtl"] .nextLink:after { content: '\\f104'; } Or if you have:
    .nextLink { background-image: url('right_arrow.png'); } You will need to change this to:
    html[dir="ltr"] .nextLink { background-image: url('right_arrow.png'); } html[dir="rtl"] .nextLink { background-image: url('left_arrow.png'); } Step Three: Consider JavaScript

    This usually will not require any action. However, if you have any custom JavaScript which adds user interaction, consider if any changes need to be made. For example, if you have a menu which opens from the left, it may need to open from the right. If you are only using the UI widgets already in the IPS Community Suite, these already make all such considerations so no action will be necessary.
    • 0 replies
  11. Mark added a article in Development   

    Template Logic
    This article lists the describes the template logic syntax that can be used in templates.

    Common Control Structures

    For most control structures, the syntax is similar to  PHP's alternative syntax for control structures  with the PHP statements contained within double curly braces. In most cases though, brackets, colons and semi-colons are not required.

    {{if $var}} HTML to display {{endif}} {{if $foo}} HTML to display {{elseif $bar}} HTML to display {{elseif $baz}} HTML to display {{else}} HTML to display {{endif}} {{foreach $foo as $bar}} HTML to display {{endforeach}} Shortcuts

    A number of shortcuts are available for use within control structures. For example rather than doing:
    {{if \IPS\Member::loggedIn()->member_id}} HTML to display {{endif}} You can do:
    {{if member.member_id}} HTML to display {{endif}} The available shortcuts are:
    request. var  is equivalent too  \IPS\Request::i()->var member. var  is equivalent too  \IPS\Member::loggedIn()->var settings. var  is equivalent too  \IPS\Settings::i()->var theme. var  is equivalent too  \IPS\Theme::i()->settings['var'] cookie. var  is equivalent too  \IPS\Request::i()->cookie['var']
    Template Tags

    Almost anywhere were template logic can be used,  template tags  can also be used for formatting numbers, dates, getting language strings, etc.

    Raw PHP

    You can execute raw PHP by enclosing it in double-curly braces. Beware of introducing XSS security vulnerabilities if doing this.
    {{$foo = array();}} Display template contents

    When in  developer mode  there is a special tag which can be inserted anywhere in a template which will cause it's compiled content to be displayed. This can be useful for debugging:
    • 0 replies
  12. Mark added a article in Development   

    Plugins: An Example
    This guide will demonstrate step-by-step creating how to create simple plugin making use of most of the features provided. For this guide, you'll be shown how to create a plugin which displays a message at the top of every page on the community.

    Useful links for further reading:
    Plugins - Details information about Plugins and the features provided. Step 1: Creating the Plugin

    To begin, you'll need to have a test installation  in developer mode . Once developer mode is enabled, a "Create Plugin" button will appear in the Admin CP under System --> Site Features --> Plugin. Use this tool to create your plugin, after which you'll be taken into the Plugin Developer Center.

    Step 2: Creating the Theme Hook

    The easiest way to display a message at the top of every page is to create a theme hook. Theme hooks allow you to modify the content of a template. The template we'll be modifying is "globalTemplate" which is in the "global" template in the "front" location in the "core" application.

    Under the "Hooks" tab, create a new hook and choose "core --> front: global" as the template group. Once it has been created, click the edit button, then choose "globalTemplate" from the menu on the left.

    Choose "Select Element" to bring up the contents of the template and choose the point you want to hook on - a good place is to select  <div id="ipsLayout_mainArea">  and then for the content position choose "Insert content inside the chosen element(s), at the start."

    For now, specify the contents manually:
    <div class="ipsMessage ipsMessage_information">This is the global message.</div> After saving you can go to the homepage and you will immediately see the message you've just created. Congratulations, you've just created a simple Plugin!

    The remaining steps will show how to expand this functionality.

    Step 3: Using Templates

    It's good practice to keep all HTML in templates. While the current approach works fine, the HTML content is buried within the code of your plugin, so if someone installing the plugin wanted to modify it in some way (perhaps change the CSS classes on it), it would be difficult to do so.

    Fortunately, creating a HTML template is really easy. If you look into the directory on your computer/server where IPS Community Suite is installed, you'll notice a "plugins" directory - inside this you'll find a directory has been created for your plugin with the name you specified in Step 1. Inside this is a folder navigate to dev/html. Within this folder, files you create will become available as a template.

    Create a file called "globalMessage.phtml" - and set the following as it's contents:
    <ips:template parameters="" /> <div class="ipsMessage ipsMessage_information"> Now using a template! </div> The first line is just to name any parameters that will be passed into the template - which is not needed in this case.

    Once the file has been created, edit your theme hook and change the contents it inserts to use this template by using this code:
    {template="globalMessage" group="plugins" location="global" app="core"} This is a  template tag  which pulls in the contents of a template. All templates created by plugins are created in the "plugins" group in the "global" location in the "core" application.

    Once this is done, you should see the message has changed to "Now using a template!"

    As a side-note, if you wanted to add CSS code, you can just add css files into the dev/css folder and they will automatically be included.

    Step 4: Settings & Language Strings

    Now you have a global message - but currently there's no way to customise what it says. It would be handy if the plugin had a simple setting in the Admin CP that the admin could use to change the contents.

    To create a setting - go to the "Settings" tab in the developer center for your plugin and add a setting - for the key, use "globalMessage_content", and set the default value to whatever you like. It's important to make sure that your plugin starts working straight after it installs so the admin knows it's working properly, so don't leave the default value blank.

    Creating the setting here allocates space for your setting in the database, but you still need to create a form where the admin can edit it. To do this, again look in the directory for your plugin on the filesystem - you'll see a file called "settings.rename.php", first rename this to "settings.php" then open it up. It already contains example code to get you going. Change the first line of code (the  $form->add(...)  call) to this:
    $form->add( new \IPS\Helpers\Form\Editor( 'globalMessage_content', \IPS\Settings::i()->globalMessage_content, FALSE, array( 'app' => 'core', 'key' => 'Admin', 'autoSaveKey' => 'globalMessage_content' ) ) ); This code is using the  form helper .

    Now when you go to the Plugins area of the Admin CP, you'll see a new "edit" button next to your plugin, when clicked, this brings up a form with a place where users can fill in a message.

    There is however, a problem with your form. The label for the setting just says "globalMessage_content" - obviously this needs to be changed to something more useful, for which you'll need a language string. To create one, look in the directory for your plugin again and open up the dev/lang.php file. It will contain just an empty array - add an element to this array like so:
    $lang = array( 'globalMessage_content' => "Message", ); The label will now say "Message".

    Finally, you need to actually make the user-defined message show. To do this, open up the globalMessage.phtml file you created in step 3 and change it's contents to:
    <ips:template parameters="" /> {{if settings.globalMessage_content}} <div class="ipsMessage ipsMessage_information"> {setting="globalMessage_content"} </div> {{endif}} That adds some  template logic  which detects if there is a value for our setting and only displays the message if there is, and another  template tag  which gets the value of our setting.

    If you wanted to add your own CSS, you can create CSS files in the dev/css folder of your plugin directory - any files you create there will be included automatically.

    Step 5: Making changes to the database

    If we wanted to take this plugin even further, you could add a "close" button to the message allowing users to dismiss the message once they have read it. Whether or not any given user has dismissed the message is information can be stored in the database.

    Open up the dev/setup folder of your plugin directory. In here you'll find a file called "install.php" - this file is ran when your plugin is installed. If you need to make subsequent database changes in future versions of your plugin, you can create new versions in the developer center, and a file will be created for each version you create - you need to make sure you add whatever changes you need both to the install.php file and the appropriate upgrade file.

    Open up install.php and add this code inside the step1 method:
    \IPS\Db::i()->addColumn( 'core_members', array( 'name' => 'globalMessage_dismissed', 'type' => 'BIT', 'length' => 1, 'null' => FALSE, 'default' => 0, 'comment' => 'If 1, the user has dismissed the global message' ) ); return TRUE; This code adds a new column to the core_members table in the database, which is the table which contains information on all the members. The column created is a BIT(1) column (which means it can store only 1 or 0) - 0 will indicate the user has not dismissed the message (so it should show) and 1 will indicate they have - the default value is set to 0.

    Though this code will make sure when your plugin gets installed the column will be added, you'll need to run a query on your local database so that  you  have it during development. Run this SQL query using whatever your preferred method is for managing your database:
    ALTER TABLE core_members ADD COLUMN globalMessage_dismissed BIT(1) NOT NULL DEFAULT 0 COMMENT 'If 1, the user has dismissed the global message'; After the user dismisses the message, their preference will need to be reset if the admin changes the contents of the message. To handle this, add this line to your settings.php file in your plugin directory, just after the  $form->saveAsSettings();  call.
    \IPS\Db::i()->update( 'core_members', array( 'globalMessage_dismissed' => 0 ) ); Step 6: Creating a Code Hook

    Now you've allocated space in the database where the flag will be stored, you need to write code to actually set that value, and link it up with the template.

    You'll need to add a method to a controller which will handle the user's click. A generic-use controller intended for this sort of thing is available -  \IPS\core\modules\front\system\plugins  - though theoretically, you could add the method to any controller.

    In the developer center, create a code hook on that class and open it up (either in the Admin CP or by opening the file that has been created in the hooks directory of your plugin directory) and add the following code inside the class:
    public function dismissGlobalMessage() { \IPS\Session::i()->csrfCheck(); if ( \IPS\Member::loggedIn()->member_id ) { \IPS\Member::loggedIn()->globalMessage_dismissed = TRUE; \IPS\Member::loggedIn()->save(); } else { \IPS\Request::i()->setCookie( 'globalMessage_dismissed', TRUE ); } \IPS\Output::i()->redirect( isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : \IPS\Http\Url::internal( '' ) ); } It's important to understand thoroughly what this function is doing:
    It first performs a  CSRF  check. Because this is a controller method, it is executed automatically on accessing the appropriate URL. Because it does something which affects the user (it modifies a preference) it is essential that it have a CSRF check. If the check fails, execution will be halted automatically. It checks if the current user is a registered member.  \IPS\Member::loggedIn()  returns an  \IPS\Member  object for the current user - if the user is a guest (not logged in) the  member_id  property will be 0. If the user is logged in, it sets the  globalMessage_dismissed  property to  TRUE  and saves the member. Since \IPS\Member is an  Active Record , this will edit the column you created in step 5 for the appropriate row in the database. If the user is not logged in, it sets a cookie so that even users who are not logged in can dismiss the message. It then redirects the user back to the page they were viewing, or if the server cannot provide the HTTP Referer, the home page. Now that the action has been created, you need to adjust the template to honour the preference, and add a button that links to the dismiss action.

    Modify the template you created in step 3 to:
    <ips:template parameters="" /> {{if settings.globalMessage_content and !member.globalMessage_dismissed and !cookie.globalMessage_dismissed}} <div class="ipsMessage ipsMessage_information"> <a href="{url="app=core&module=system&section=plugins&do=dismissGlobalMessage" csrf="1"}" class="ipsMessage_code ipsType_blendlinks"><i class="icon-remove"></i></a></span> {setting="globalMessage_content"} </div> {{endif}} Step 7: JavaScript

    You now have a completely functional plugin which displays a message to all users that they can dismiss. For a little bit of added flair, you can make it so when the user dismisses the message, rather than reloading the page, it performs the action with an AJAX request and then the message fades out. It's really important that you only think about JavaScript enhancements  after  the core functionality has been written, so that users who don't have JavaScript enabled can still use your plugin, and search engines can access any content your plugin provides.

    To do this, you need to create a JavaScript controller. In the dev/js directory of your plugin directory, create a file named "globalMessageDismiss.js" with the following contents:
    ;( function($, _, undefined){ "use strict"; ips.controller.register('plugins.globalMessageDismiss', { initialize: function () { this.on( document, 'click', '[data-action="dismiss"]', this.dismiss ); }, dismiss: function (e) { e.preventDefault(); var url = $( e.currentTarget ).attr('href'); var message = $(this.scope); ips.getAjax()(url).done(function(){ ips.utils.anim.go( 'fadeOut', message ); }).fail(function(){ window.location = url; }); } }); }(jQuery, _)); To explain what this is doing:
    Everything other than the contents of the initialize and dismiss functions is required code for JavaScript controllers. The  ips.controller.register  line specifies the name of the controller. When an element with a JavaScript controller attached to it is loaded (you'll attach it to the global message in a moment) the initialize function is ran - the best practice is to only set up event handlers here and handle events in other functions. An event is being set up here to fire when any elements matching the selector  [data-action="dismiss"]  (you'll add that attribute to the close button in a moment) are clicked, and it calls the dismiss function when this happens. This dismiss function first prevents the event from performing it's default action (as a click will of course take the user to the target URL), then sets the variables needed (the URL that the button points to and the message box. It then sends an AJAX request to the URL that the button points to - if it succeeds, it fades out the box, and if it fails, it redirects the user to it just as if there was no JavaScript functionality. To make it actually work, you need to specify that you want your message to use this controller. In your template, add  data-controller="plugins.globalMessageDismiss"  to the  div  element, and  data-action="dismiss"  to the  a  element.

    For completeness, we should also adjust our action a little so that is aware of AJAX requests - while this is not strictly necessary, if it is not done, it is possible that if the redirect has to redirect to a screen which will display an error message, that the AJAX request will see  this  response and assume the request failed. Open up the action.php file you created in step 6 and change the redirect line to:
    if ( \IPS\Request::i()->isAjax() ) { \IPS\Output::i()->sendOutput( NULL, 200 ); } else { \IPS\Output::i()->redirect( isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : \IPS\Http\Url::internal( '' ) ); } Once this is done, closing your message will now fade out without a page reload.

    Step 8: Downloading

    Congratulations, you've just created your first plugin! You can download so that you can install it on other sites or distribute it through the IPS Marketplace from the developer center.

    For reference, a downloaded version of the hook, as well as a zip of the plugin directory for development are attached:

      Global Message.xml
    • 0 replies
  13. Mark added a article in Development   


    Plugins provide a way for developers to modify or extend the behaviour of applications within the IPS Community Suite.

    This guide will cover in detail the different features available within Plugins. Many of the features describes are accessed from the Plugin Developer Center - to access this, first place IPS Community Suite  in developer mode . Once developer mode is enabled, a "Create Plugin" button will appear in the Admin CP under System --> Site Features --> Plugin. Use this tool to create your plugin, after which you'll be taken into the Plugin Developer Center.

    Useful links for further reading:
    Plugins: An Example - A step-by-step guide to creating your first plugin. The Autoloader - An overview of how to locate classes. Plugin Features

    Code Hooks

    Code Hooks allow you to  extend  any class within the IPS Community Suite or an application installed. You can create Code Hooks in the Plugin Developer Center.

    You'll notice that throughout the code, classes are defined beginning with an underscore, but called without. For example, in /system/Member/Member.php, the declaration for is:
    namespace IPS; class _Member extends \IPS\Patterns\ActiveRecord Yet throughout the code,  \IPS\Member  is called rather than  \IPS\_Member . This is a technicality in how code hooks are facilitated. When creating your code hook, specify the class you want to extend  without  the underscore. When you're actually editing the code, it will say "extends _HOOK_CLASS_" - this is because the actual class it will extend will vary if more than one hook is overloading the same class.

    You can edit code hooks either in the Plugin Developer Center or by manually editing the file which will have been created in /plugins/<your plugin>/hooks. Using the Plugin Developer Center has the benefit that all the properties and methods of the class you're extending (and classes that class extends) are shown in the sidebar, and clicking on any one will insert the declaration with the doc-comment into the editor.

    Important things to remember when creating code hooks:
    The first line in the class that is created is  //<?php  - this is so if you want to edit the file in your own editor that syntax highlighting works properly, however the  <?php  tag should not be used uncommented. Your hook will specify that it extends _HOOK_CLASS_ - though this is syntaxually wrong, this should not be changed. The system will automatically change it to the correct class which may vary if there is more than one hook on a single class. If your code causes a PHP error or throws an exception, your hook will be ignored and the system will revert to the default class. If you need to deliberately throw an exception, throw an instance of  \IPS\ExpectedException  rather than  \Exception . When overriding a method, you  MUST  call the parent method so that ultimately you are inserting your code at the beginning or end of the method. This is necessary for allowing hooks on the same method to work. When overriding a method, you  MUST NOT  copy code from the original method into your hook. This is necessary to ensure that your hook does not interfere with any bug fixes or changes made to the original class in future versions. Also, it is against the terms of the IPS Community Suite license to distribute any code within it. Theme Hooks

    Theme hooks allow you to modify the HTML templates. The HTML templates within the IPS Community Suite are structured into  groups ,  locations  and  applications . Each application will have at least 3 locations:
    front  (templates used for the front-end) admin  (templates used for the Admin CP) global  (templates which are used for both) Though some applications may have more for specific purposes (for example, the core application has another location for installer/upgrader called  setup ). Each location can contain any number of groups - groups are generic collections of templates, for example, the core application has one group (in the  front  location) called  messaging  which contains all the templates for composing and viewing personal conversations.

    One Theme Hook acts on one template group.

    You can create Theme Hooks in the Plugin Developer Center. Once created there are two ways to use Theme Hooks - each of which work differently and so will be appropriate for different circumstances:

    CSS-Selector Mode

    Editing your Theme Hook in the Plugin Developer Center will display a panel showing all of the templates in that group:

    Selecting any template will bring up a tabbed interface showing the modifications your Theme Hook is making to that template, and allow to create more:

    To make a modification to the selected template, you will provide a CSS selector - you can  use any of the selectors supported by jQuery  (it's worth mentioning that jQuery isn't involved in making your Theme Hook work, it is simply that the supported selectors are the same). The easiest way to choose a selector is with the interactive "Select Element" feature, which will launch a model displaying the template allowing you to simply click on the element you want to use:

    It is important to note that when using the "Select Element" feature, it will use the most specific CSS selector it can for the element selected, however, that selector may also match other elements so you may need to adjust it.

    When providing the content, in addition to the variables that are available to the template (which are shown next to the editor) you can also use any variables available at that point in the template (for example, if you're inserting code within a foreach loop, you can use the variables created by it). You can also use  template logic  and  template tags .

    PHP Mode

    Under the hood, each template group is compiled into a PHP class, with a method for each template. You can extend this class by manually editing the file which will have been created in /plugins/<your plugin>/hooks.

    It is important to note that when using this mode, you are overloading the  compiled  template group so the return value will be the HTML that will be displayed, without any template logic or template tags.]

    All of the same considerations as for Code Hooks (see "Important things to remember when creating code hooks") also apply here.


    Allocating space in IPS\Settings

    IPS\Settings is a repository for scalar key/value data pairs - it is shared amongst all applications and plugins and is accessible from anywhere as a Singleton, there are also shortcuts to access it's contents within templates. It's primary intention is for storing settings the administrator provides in the Admin CP.

    In order to use this, you must enter the keys you want to be allocated to your plugin and the default values in the Plugin Developer Center. The keys are not namespaced, so it is recommended that you choose keys that are unlikely to be used by other applications or plugins.

    Creating a settings page

    Your plugin is allowed to have one settings page which will be displayed with the administrator clicks the "edit" button next to your plugin. In most cases, this will be just a regular form using the  form helper , however, though the form helper is very flexible (it can handle tabs, sidebars and a wide variety of input types), you are not restricted to this and can display your settings however you like.

    In the /plugins/<your plugin> directory, you'll find a file called "settings.rename.php" - rename this to "settings.php" and this will be the code that runs when the user accesses your settings page. The example file is written with the form helper in mind, and to use that, all you need to do is add additional input fields. If you want to use a custom interface though, you just need to return either a string (with the content to display) or TRUE to dismiss the settings page. Your code will be eval'd and so you should not include an opening  <?php  tag - one is included in the example file so that syntax highlighting will work in most code editors, however it is commented out.


    Tasks are scripts which run at specific intervals. They are useful for performing any maintenance that must be ran on a regular basis. It is important to note however, that they cannot be relied on - though IPS Community Suite provides a way for administrators to run tasks using a cron, this may not be enabled, in which case tasks can only be triggered when users are active on the site - if the site is inactive at the time your task is scheduled to run, it will run at the soonest opportunity after.

    You can create tasks in the Plugin Developer Center. When you create a task, a file will automatically be created in the /plugins/<your plugin>/tasks directory for your task. The doc-comments in this file explain how to implement the task.

    Database changes (Versions)


    To run code such as database changes when your plugin is installed, open the /plugins/<your plugin>/dev/setup/install.php file and follow the instructions given by the doc-comments.


    Plugins support version management. Whenever you release a new version of your plugin you should add a new version in the Plugin Developer Center. When you add a version, you must specify an ID number to represent it (e.g. "10000", "10001", etc.) as well as a human-readable string (e.g. "1.0.0", "1.0.1", etc.). There is no particular standard to how the ID number should be formatted other than it must increase the newer the version. The human-readable string should be in the format "x.y.z".

    For each version, a file will be added to the /plugins/<your plugin>/dev/setup folder where you can specify code that will be ran when upgrading to that version (see the doc-comments within them for more details).

    It is important to note that at install, only install will be ran, so when you make a change you will usually need to add it both to your installer, and the appropriate upgrade file for that version.

    HTML, CSS, JavaScript and Images

    You can place HTML, CSS, JavaScript and image files in the appropriate folders in /plugins/<your plugin directory>/dev.


    HTML files will become templates within the  core  application >  global  location >  plugins  group. In other words, you will get the content of the HTML files you create using this code in controllers:
    \IPS\Theme::i()->getTemplate( 'plugins', 'core', 'global' )->FILENAME( ... ); Or the following code in other templates:
    {template="FILENAME" group="plugins" location="global" app="core"} You should create your files using the extension ".phtml". The first line of the file should be the following tag:
    <ips:template parameters="$example1, $example2" /> Replacing  $example1, $example2  with the names of whatever variables you intend to pass in. It is fine to have no parameters.

    After that opening line, the remainder of the file should just be what you want the template contents to be. You can use  template logic  and  template tags .

    CSS and JavaScript

    Any CSS and JavaScript files you create will be compiled with the rest of the CSS and JavaScript automatically, so you do not need to do anything other than create the files.


    Images will be placed in the  core  application >  global  location, in a folder called  plugins . In other words, you will render the images you place in the "img" directory by using this code in templates:
    <img src='{image="plugins/example.jpg" app="core" location="global"}' /> You must put all images directly in the "img" directory, do not create subdirectories.

    Language Strings

    IPS Community Suite supports multiple languages and translation tools such as the Visual Language Editor. Any text your plugin uses should be abstracted to language strings so that your plugin also supports these features.

    Language strings are defined as key/value pairs. To add a language string, just open the /plugins/<your plugin>/dev/lang.php file and add an element to the array. Then to use the language string, use this in controllers:
    \IPS\Member::loggedIn()->language()->get( 'KEY' ); Or this in templates:
    {lang="KEY"} Distribution

    You can download your plugin from the Plugin Developer Center which will download a single XML file. This will automatically handle building all of the above features.
    • 0 replies
  14. Mark added a article in Development   

    Developer Mode
    Developer Mode is required to develop Applications and Plugins in IPS Community Suite.

    Developer Mode will cause the software to run much slower than usual and may introduce security vulnerabilities. You should only enable Developer Mode if you are a PHP developer intending to develop Applications and Plugins and should only do so on a local installation that is not web-accessible

    To enable Developer Mode:
    Download the  Developer Tools , making sure you download the correct version for the version of IPS Community Suite you are using. Developer Tools for pre-release versions may be available, so you may need to download an older version from the "Previous Versions" section. Extract the developer tools and move them to where IPS Community Suite is installed, merging with the existing files. There is a root "dev" folder, and "dev" folders for each application. If you do not have every IPS Community Suite application installed, you should delete the folders you don't need from the Developer Tools folder before copying. The presence of Developer Tools for uninstalled applications may cause errors. If you do not already have a constants.php file in the root folder of your installation, create one. Add the following line to your constants.php file: define( 'IN_DEV', TRUE );  
    For more information on how to use the tools which become available when Developer Mode is enabled, and for more information on developing for the IPS Community Suite, see the  developer documentation .

    Note that when you upgrade your installation, you will need to download the updated Developer Tools.
    • 0 replies
  15. Mark added a post in a topic: Phrase "on"   

    Yes it would be wonderful if you did that

About Me

Contributes To

  1. News and Announcements    By IPS

    • 500
    • 13794
    • 4815662