Version 4.1.2

This commit is contained in:
Neo committed 2025-12-19 05:32:19 -08:00
1 parent 3ba79295ee
commit 28bd025b35
1131 files changed
+63658 -20579

No files matched your search

+4 -4
View File
@@ -96,7 +96,7 @@ class _Dynamic extends \IPS\Helpers\Chart
/**
* @brief Available Types
*/
public $availableTypes = array( 'LineChart', 'ColumnChart', 'BarChart', 'PieChart' );
public $availableTypes = array( 'LineChart', 'ColumnChart', 'BarChart', 'PieChart', 'Table' );
/**
* @brief Available Filters
@@ -162,7 +162,7 @@ class _Dynamic extends \IPS\Helpers\Chart
$this->start = $defaultTimes['start'];
$this->end = $defaultTimes['end'];
$this->identifier = \substr( md5( $table . $dateField ), 0, 6 ) . $identifier;
if ( isset( \IPS\Request::i()->type[ $this->identifier ] ) and in_array( \IPS\Request::i()->type[ $this->identifier ], $this->availableTypes ) )
{
$this->type = \IPS\Request::i()->type[ $this->identifier ];
@@ -407,7 +407,7 @@ class _Dynamic extends \IPS\Helpers\Chart
$key = array_pop( $keys );
$stmt = \IPS\Db::i()->select(
"{$key}, {$this->groupBy}",
"{$key}" . ( $this->groupBy ? ", {$this->groupBy}" : '' ),
$this->table,
$where,
"{$this->dateField} ASC",
@@ -556,7 +556,7 @@ class _Dynamic extends \IPS\Helpers\Chart
{
$this->options['domainAxis']['type'] = 'category';
}
$output = $this->render( $this->type, $this->options, $this->format );
}
}
+2 -1
View File
@@ -47,8 +47,9 @@ abstract class _Controller extends \IPS\Dispatcher\Controller
}
catch ( \Exception $e ) { }
$returnURL = new \IPS\Http\Url( $_SERVER['HTTP_REFERER'] );
$this->_coverPhotoSet( new \IPS\Helpers\CoverPhoto( $values['cover_photo'], 0 ) );
\IPS\Output::i()->redirect( $_SERVER['HTTP_REFERER'] );
\IPS\Output::i()->redirect( $returnURL->setQueryString( array( '_position' => 1 ) ) );
}
\IPS\Output::i()->output = $form->customTemplate( array( call_user_func_array( array( \IPS\Theme::i(), 'getTemplate' ), array( 'forms', 'core' ) ), 'popupTemplate' ) );
+22 -8
View File
@@ -27,12 +27,14 @@ class _Address extends FormAbstract
* @brief Default Options
* @code
$defaultOptions = array(
'minimize' => FALSE, // Minimize the address field until the user focuses?
'minimize' => FALSE, // Minimize the address field until the user focuses?
'requireFullAddress'=> TRUE, // Does this have to be a full address? Default is TRUE, may set to FALSE if a more generic location is acceptable
);
* @endcode
*/
protected $defaultOptions = array(
'minimize' => FALSE
'minimize' => FALSE,
'requireFullAddress' => TRUE,
);
/**
@@ -181,7 +183,7 @@ class _Address extends FormAbstract
}
/* Validate, return NULL if we have nothing */
if ( !$fullyCompleted )
if ( !$fullyCompleted and $this->options['requireFullAddress'] )
{
if ( $partiallyCompleted )
{
@@ -199,13 +201,25 @@ class _Address extends FormAbstract
return NULL;
}
}
/* Add in latitude and longitude if we can */
try
elseif( !$fullyCompleted )
{
$value->getLatLong();
/* If we don't have any country, region city AND postal code, return NULL, otherwise everywhere ends up storing empty
location arrays which makes it difficult to confirm if a location was actually set or not */
if( !$value->country AND !$value->region AND !$value->city AND !$value->postalCode )
{
return NULL;
}
}
/* Add in latitude and longitude if we can */
if ( $fullyCompleted )
{
try
{
$value->getLatLong();
}
catch( \BadFunctionCallException $e ){}
}
catch( \BadFunctionCallException $e ){}
/* Return */
return $value;
+1
View File
@@ -100,6 +100,7 @@ class _Captcha extends FormAbstract
/* If we previously did an AJAX validate which is still valid, return true */
if ( isset( $_SESSION[ 'captcha-val-' . $this->name ] ) and $_SESSION[ 'captcha-val-' . $this->name ] > ( time() - 60 ) )
{
unset( $_SESSION[ 'captcha-val-' . $this->name ] );
return TRUE;
}
/* Otherwise, check with service */
+1 -1
View File
@@ -90,7 +90,7 @@ class _CheckboxSet extends Select
*/
public function getValue()
{
$value = array_keys( parent::getValue() );
$value = is_array( parent::getValue() ) ? array_keys( parent::getValue() ) : array();
if ( $this->options['returnLabels'] )
{
if ( is_array( $value ) )
+8 -1
View File
@@ -33,6 +33,7 @@ class _Codemirror extends TextArea
'placeholder' => 'e.g. ...', // A placeholder (NB: Will only work on compatible browsers)
'tags' => array(), // An array of extra insertable tags in key => value pair with key being what is inserted and value serving as a description
'mode' => 'php' // Formatting mode. Default is htmlmixed.
'height' => 300 // Height of code mirror editor
);
* @endcode
*/
@@ -44,6 +45,7 @@ class _Codemirror extends TextArea
'tags' => array(),
'mode' => 'htmlmixed',
'nullLang' => NULL,
'height' => 300
);
/**
@@ -74,6 +76,11 @@ class _Codemirror extends TextArea
*/
public function html()
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->codemirror( $this->name, $this->value, $this->required, $this->options['maxLength'], $this->options['disabled'], '', $this->options['placeholder'], $this->options['tags'], $this->options['mode'], $this->htmlId ? "{$this->htmlId}-input" : NULL );
if ( $this->options['height'] )
{
$this->options['height'] = is_numeric( $this->options['height'] ) ? $this->options['height'] . 'px' : $this->options['height'];
}
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->codemirror( $this->name, $this->value, $this->required, $this->options['maxLength'], $this->options['disabled'], '', $this->options['placeholder'], $this->options['tags'], $this->options['mode'], $this->htmlId ? "{$this->htmlId}-input" : NULL, $this->options['height'] );
}
}
+15 -5
View File
@@ -27,8 +27,8 @@ class _Date extends FormAbstract
* @brief Default Options
* @code
$defaultOptions = array(
'min' => new \IPS\DateTime('1970-01-01'), // Minimum date. NULL will mean no minimum (though note hard limits if it will be stored as a UNIX timestamp). Default is null.
'max' => new \IPS\DateTime('2038-01-19'), // Maximum date. NULL will mean no maximum (though note hard limits if it will be stored as a UNIX timestamp). Default is null.
'min' => new \IPS\DateTime('1970-01-01'), // Minimum date. NULL will mean 1901-12-13 20:45:52 (the minimum 32-bit timestamp). Default is null.
'max' => new \IPS\DateTime('2038-01-19'), // Maximum date. NULL will mean 2038-01-19 03:14:07 (the maximum 32-bit timestamp). Default is null.
'disabled' => FALSE, // Disables input. Default is FALSE.
'time' => FALSE, // Also allow time input?
'unlimited' => -1, // If any value other than NULL is provided, an "Unlimited" checkbox will be displayed. If checked, the value specified will be sent.
@@ -85,9 +85,19 @@ class _Date extends FormAbstract
$this->timeName = "{$name}_time";
$this->unlimitedName = "{$name}_unlimited";
}
/* Call parent constructor */
call_user_func_array( 'parent::__construct', func_get_args() );
/* Set min/max - hardcoded to 32bit values rather than using PHP_INT_MAX because the MySQL server may be 32-bit even if the web server is 64 */
if ( !isset( $this->options['min'] ) or $this->options['min']->getTimestamp() < 2147483648 )
{
$this->options['min'] = \IPS\DateTime::ts( -2147483648 );
}
if ( !isset( $this->options['max'] ) or $this->options['max']->getTimestamp() > 2147483647 )
{
$this->options['max'] = \IPS\DateTime::ts( 2147483647 );
}
}
/**
@@ -267,13 +277,13 @@ class _Date extends FormAbstract
}
/* Check minimum */
if ( $this->options['min'] !== NULL and $this->options['min'] > $this->value )
if ( $this->value and $this->options['min'] !== NULL and $this->options['min'] > $this->value )
{
throw new \LengthException( \IPS\Member::loggedIn()->language()->addToStack('form_date_min', FALSE, array( 'sprintf' => array( $this->options['min']->localeDate() ) ) ) );
}
/* Check maximum */
if ( $this->options['max'] !== NULL and $this->options['max'] < $this->value )
if ( $this->value and $this->options['max'] !== NULL and $this->options['max'] < $this->value )
{
throw new \LengthException( \IPS\Member::loggedIn()->language()->addToStack('form_date_max', FALSE, array( 'sprintf' => array( $this->options['max']->localeDate() ) ) ) );
}
+1 -1
View File
@@ -74,7 +74,7 @@ class _DateRange extends FormAbstract
* @return void
*/
public function __construct( $name, $defaultValue=NULL, $required=FALSE, $options=array() )
{
{
$this->start = new \IPS\Helpers\Form\Date( "{$name}[start]", isset( $defaultValue['start'] ) ? $defaultValue['start'] : NULL, FALSE, isset( $options['start'] ) ? $options['start'] : array() );
$this->end = new \IPS\Helpers\Form\Date( "{$name}[end]", isset( $defaultValue['end'] ) ? $defaultValue['end'] : NULL, FALSE, isset( $options['end'] ) ? $options['end'] : array() );
+70 -50
View File
@@ -141,7 +141,6 @@ class _Editor extends FormAbstract
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, array( \IPS\Http\Url::internal( 'applications/core/interface/ckeditor/ckeditor/ckeditor.js', 'none', NULL, array(), \IPS\Http\Url::PROTOCOL_RELATIVE ) ) );
}
}
\IPS\Output::i()->jsVars['strip_quotes'] = \IPS\Settings::i()->strip_quotes;
}
/**
@@ -269,7 +268,7 @@ class _Editor extends FormAbstract
/* If it was made without JS, convert linebreaks to <br>s */
$noJsKey = $this->name . '_noscript';
if ( isset( \IPS\Request::i()->$noJsKey ) AND !$value )
if ( isset( \IPS\Request::i()->$noJsKey ) )
{
$value = nl2br( \IPS\Request::i()->$noJsKey, FALSE );
}
@@ -297,9 +296,9 @@ class _Editor extends FormAbstract
if ( $this->uploader )
{
$inserts = array();
foreach ( static::getAttachments( NULL, $this->postKey ) as $attachment )
foreach ( $this->getAttachments() as $attachment )
{
if ( !isset( $parser ) or !in_array( $attachment['attach_id'], $parser->mappedAttachments ) )
if ( !isset( $parser ) or !in_array( $attachment['attach_id'], $parser->mappedAttachments ) or array_key_exists( $attachment['attach_id'], $parser->existingAttachments ) )
{
if ( $attachment['attach_is_image'] )
{
@@ -309,21 +308,30 @@ class _Editor extends FormAbstract
{
$value .= \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->attachedFile( \IPS\Settings::i()->base_url . "applications/core/interface/file/attachment.php?id=" . $attachment['attach_id'], $attachment['attach_file'] );
}
$inserts[] = array(
'attachment_id' => $attachment['attach_id'],
'location_key' => "{$this->options['app']}_{$this->options['key']}",
'id1' => ( is_array( $this->options['attachIds'] ) and isset( $this->options['attachIds'][0] ) ) ? $this->options['attachIds'][0] : NULL,
'id2' => ( is_array( $this->options['attachIds'] ) and isset( $this->options['attachIds'][1] ) ) ? $this->options['attachIds'][1] : NULL,
'id3' => ( is_array( $this->options['attachIds'] ) and isset( $this->options['attachIds'][2] ) ) ? $this->options['attachIds'][2] : NULL,
'temp' => is_string( $this->options['attachIds'] ) ? $this->options['attachIds'] : ( $this->options['attachIds'] === NULL ? md5( $this->options['autoSaveKey'] ) : $this->options['attachIds'] )
);
if ( !isset( $parser ) or !in_array( $attachment['attach_id'], $parser->mappedAttachments ) )
{
$inserts[] = array(
'attachment_id' => $attachment['attach_id'],
'location_key' => "{$this->options['app']}_{$this->options['key']}",
'id1' => ( is_array( $this->options['attachIds'] ) and isset( $this->options['attachIds'][0] ) ) ? $this->options['attachIds'][0] : NULL,
'id2' => ( is_array( $this->options['attachIds'] ) and isset( $this->options['attachIds'][1] ) ) ? $this->options['attachIds'][1] : NULL,
'id3' => ( is_array( $this->options['attachIds'] ) and isset( $this->options['attachIds'][2] ) ) ? $this->options['attachIds'][2] : NULL,
'temp' => is_string( $this->options['attachIds'] ) ? $this->options['attachIds'] : ( $this->options['attachIds'] === NULL ? md5( $this->options['autoSaveKey'] ) : $this->options['attachIds'] )
);
}
}
}
if( count( $inserts ) )
{
\IPS\Db::i()->insert( 'core_attachments_map', $inserts, TRUE );
}
/* Clear out the post key for attachments that are claimed automatically */
if ( $this->options['attachIds'] )
{
\IPS\Db::i()->update( 'core_attachments', array( 'attach_post_key' => '' ), array( 'attach_post_key=?', $this->postKey ) );
}
}
/* Remove abandoned attachments */
@@ -416,26 +424,8 @@ class _Editor extends FormAbstract
}
/* Load existing attachments */
$existingAttachments = '';
$counts = array();
$where = array();
if ( isset( $this->options['attachIds'] ) and is_array( $this->options['attachIds'] ) and count( $this->options['attachIds'] ) )
{
$where = array( array( 'location_key=?', "{$this->options['app']}_{$this->options['key']}" ) );
$i = 1;
foreach ( $this->options['attachIds'] as $id )
{
$where[] = array( "id{$i}=?", $id );
$i++;
}
$_existingAttachments = iterator_to_array( \IPS\Db::i()->select( 'attachment_id', 'core_attachments_map', $where ) );
if ( !empty( $_existingAttachments ) )
{
$existingAttachments = \IPS\Db::i()->in( 'attach_id', $_existingAttachments ) . ' OR ';
}
}
$existing = array();
foreach ( static::getAttachments( $existingAttachments, $this->postKey ) as $attachment )
foreach ( $this->getAttachments() as $attachment )
{
try
{
@@ -519,7 +509,7 @@ class _Editor extends FormAbstract
try
{
$attachment = \IPS\Db::i()->select( '*', 'core_attachments', array( 'attach_id=?', \IPS\Request::i()->deleteFile ) )->first();
\IPS\Db::i()->delete( 'core_attachments_map', array_merge( $where, array( array( 'attachment_id=?', $attachment['attach_id'] ) ) ) );
\IPS\Db::i()->delete( 'core_attachments_map', array( array( 'location_key=?', "{$this->options['app']}_{$this->options['key']}" ), array( 'attachment_id=?', $attachment['attach_id'] ) ) );
$otherMaps = \IPS\Db::i()->select( 'COUNT(*)', 'core_attachments_map', array( 'attachment_id=?', $attachment['attach_id'] ) )->first();
if ( !$otherMaps )
{
@@ -551,13 +541,17 @@ class _Editor extends FormAbstract
*/
public static function getEmoticons()
{
if( !isset( \IPS\Data\Store::i()->emoticons ) )
{
$emoticons = iterator_to_array( \IPS\Db::i()->select( '*', 'core_emoticons', array( 'typed<>?', '' ) )->setKeyField('typed')->setValueField('image') );
$emoticons = iterator_to_array( \IPS\Db::i()->select( '*', 'core_emoticons', array( 'typed<>?', '' ) )->setKeyField('typed') );
foreach( $emoticons as $key => $emoticon )
{
$emoticons[ $key ] = (string) \IPS\File::get( "core_Emoticons", $emoticon )->url;
$emoticons[ $key ] = array(
'image' => (string) \IPS\File::get( "core_Emoticons", $emoticon['image'] )->url,
'image_2x' => $emoticon['image_2x'] ? (string) \IPS\File::get( "core_Emoticons", $emoticon['image_2x'] )->url . ' 2x' : FALSE,
'height' => $emoticon['height'] ?: FALSE,
'width' => $emoticon['width'] ?: FALSE,
);
}
\IPS\Data\Store::i()->emoticons = $emoticons;
@@ -565,27 +559,53 @@ class _Editor extends FormAbstract
return \IPS\Data\Store::i()->emoticons;
}
/**
* Attachments
*/
protected $attachments;
/**
* Fetch existing attachments - abstracted to a static method for caching
* Fetch existing attachments
*
* @param string $existing Existing attachments limiter
* @param string $postKey Post key
* @return array
*/
public static function getAttachments( $existing, $postKey )
protected function getAttachments()
{
static $attachments = array();
$_key = md5( $existing . $postKey );
if( isset( $attachments[ $_key ] ) )
if ( $this->attachments === NULL )
{
return $attachments[ $_key ];
$existingAttachments = '';
$where = array();
if ( isset( $this->options['attachIds'] ) and is_array( $this->options['attachIds'] ) and count( $this->options['attachIds'] ) )
{
$where = array( array( 'location_key=?', "{$this->options['app']}_{$this->options['key']}" ) );
$i = 1;
foreach ( $this->options['attachIds'] as $id )
{
$where[] = array( "id{$i}=?", $id );
$i++;
}
$_existingAttachments = iterator_to_array( \IPS\Db::i()->select( 'attachment_id', 'core_attachments_map', $where ) );
if ( !empty( $_existingAttachments ) )
{
$existingAttachments = \IPS\Db::i()->in( 'attach_id', $_existingAttachments ) . ' OR ';
}
}
$this->attachments = \IPS\Db::i()->select( '*', 'core_attachments', array( $existingAttachments . 'attach_post_key=?', $this->postKey ) );
}
$attachments[ $_key ] = \IPS\Db::i()->select( '*', 'core_attachments', array( $existing . 'attach_post_key=?', $postKey ) );
return $attachments[ $_key ];
return $this->attachments;
}
/**
* Get CKEditor Version
*
* @return string
*/
public static function ckeditorVersion()
{
preg_match( '/CKEditor\s+Included version: (.+?)\s/', file_get_contents( \IPS\ROOT_PATH . '/Credits.txt' ), $matches );
return $matches[1];
}
}
+7 -6
View File
@@ -121,8 +121,7 @@ class _Form
public function __construct( $id='form', $submitLang='save', $action=NULL, $attributes=array() )
{
$this->id = $id;
$this->action = $action ?: \IPS\Request::i()->url();
$this->action = $this->action->stripQueryString('csrfKey');
$this->action = $action ?: \IPS\Request::i()->url()->stripQueryString( array( 'csrfKey', 'ajaxValidate' ) );
$this->attributes = $attributes;
@@ -136,17 +135,18 @@ class _Form
$potentialMaxUploadValues = array();
if( (float) ini_get('upload_max_filesize') > 0 )
{
$potentialMaxUploadValues[] = (float) ini_get('upload_max_filesize');
$potentialMaxUploadValues[] = \IPS\File::returnBytes( ini_get('upload_max_filesize') );
}
if( (float) ini_get('post_max_size') > 0 )
{
$potentialMaxUploadValues[] = (float) ini_get('post_max_size');
/* We need to reduce post_max_size lower because it includes the ENTIRE post and other data, such as the number of chunks, will also be sent with the request */
$potentialMaxUploadValues[] = \IPS\File::returnBytes( ini_get('post_max_size') ) - 1048576;
}
if( (float) ini_get('memory_limit') > 0 )
{
$potentialMaxUploadValues[] = (float) ini_get('memory_limit');
$potentialMaxUploadValues[] = \IPS\File::returnBytes( ini_get('memory_limit') );
}
$this->uploadField = min( $potentialMaxUploadValues ) * 1048576;
$this->uploadField = min( $potentialMaxUploadValues );
}
/**
@@ -257,6 +257,7 @@ class _Form
public function addMatrix( $name, $matrix )
{
//$matrix->manageable = FALSE;
$matrix->id = $this->id;
$this->tabClasses[ $this->currentTab ] = 'ipsMatrix';
$this->elements[ $this->currentTab ][ $name ] = $matrix;
}
+9 -1
View File
@@ -274,11 +274,19 @@ abstract class _FormAbstract
/* Some form helpers might want to allow nested and outer suffixes */
$html = $this->html();
if( array_key_exists( 'endSuffix', $this->options ) )
if( array_key_exists( 'endSuffix', $this->options ) and ! empty( $this->options['endSuffix'] ) )
{
$this->suffix = $this->options['endSuffix'];
}
/* Some elements support an array for suffix, such as Number which supports preUnlimited and postUnlimited. We need to wipe out
the suffix here before calling the row() template, however, which only supports a string and throws an Array to string conversion error.
By this point, the element template has already ran and used the suffix if designed to */
if( is_array( $this->suffix ) )
{
$this->suffix = '';
}
return \IPS\Theme::i()->getTemplate( 'forms', 'core' )->row( $label, $html, $desc, $warning, $this->appearRequired, $this->error, $this->prefix, $this->suffix, $this->htmlId ?: ( $form ? "{$form->id}_{$this->name}" : NULL ), $this, $form );
}
catch ( \Exception $e )
+190
View File
@@ -0,0 +1,190 @@
<?php
/**
* @brief Item selector for Form Builder
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
* @license http://www.invisionpower.com/legal/standards/
* @package IPS Social Suite
* @since 12 Apr 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Helpers\Form;
/* To prevent PHP errors (extending class does not exist) revealing path */
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
{
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
exit;
}
/* @todo limit by container IDs */
/**
* Content Item selector
*/
class _Item extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'class' => '\IPS\core\Foo', // The \Content\Item class
'permissionCheck' => 'read', // If a permission key is provided, only content items that the member has that permission for will be available.
'maxItems' => NULL, // Maximum items allowed to select, or NULL for unlimited
'orderResults' => NULL|array // NULL or array( field, 'asc' ) where field is a mappable content item field (date, title, etc)
'itemTemplate' => NULL // NULL or array of template group and template name array( \IPS\Theme::i()->getTemplate( 'group', 'app', 'location'), 'name' )
'containerIds' => array // Array of container IDs to limit the search
'minAjaxLength' => int // Number of characters to type before selector appears
* 'where' => array // Array of additional where array( array( 'foo=?', 1 ) )
* );
* @encode
*/
protected $defaultOptions = array(
'class' => NULL,
'permissionCheck' => 'read',
'maxItems' => NULL,
'orderResults' => NULL,
'itemTemplate' => NULL,
'containerIds' => array(),
'minAjaxLength' => 3,
'where' => array()
);
/**
* Constructor
*
* @return void
*/
public function __construct()
{
call_user_func_array( 'parent::__construct', func_get_args() );
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
/* Display */
$url = \IPS\Request::i()->url()->setQueryString( '_itemSelectName', $this->name );
$template = NULL;
if ( $this->options['itemTemplate'] === NULL )
{
$template = array( \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' ), 'itemResult' );
}
/* Are we getting some AJAX stuff? */
if ( isset( \IPS\Request::i()->_itemSelectName ) and \IPS\Request::i()->_itemSelectName === $this->name )
{
$results = array();
$class = $this->options['class'];
$field = $class::$databasePrefix . $class::$databaseColumnMap['title'];
$where = array( array( $field . " LIKE CONCAT('%', ?, '%')", \IPS\Request::i()->q ) );
$idField = $class::$databaseColumnId;
if ( isset( $class::$databaseColumnMap['container'] ) and is_array( $this->options['containerIds'] ) and count( $this->options['containerIds'] ) )
{
$where[] = array( \IPS\Db::i()->in( $class::$databasePrefix . $class::$databaseColumnMap['container'], $this->options['containerIds'] ) );
}
if ( isset( $this->options['where'] ) and count( $this->options['where'] ) )
{
$where = array_merge( $where, $this->options['where'] );
}
foreach( $class::getItemsWithPermission( $where, 'LENGTH(' . $field . ') ASC', array( 0, 20 ), $this->options['permissionCheck'] ) as $item )
{
$results[] = array(
'id' => $item->$idField,
'html' => call_user_func( $template, $item )
);
}
\IPS\Output::i()->json( $results );
}
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->item( $this->name, $this->value, $this->options['maxItems'], $this->options['minAjaxLength'], $url, $template );
}
/**
* Get Value
*
* @return mixed
*/
public function getValue()
{
$name = $this->name . '_values';
if ( isset( \IPS\Request::i()->$name ) )
{
return explode( ',', \IPS\Request::i()->$name );
}
else
{
return array();
}
}
/**
* Format Value
*
* @return \IPS\Node\Model|array|NULL
*/
public function formatValue()
{
$itemClass = $this->options['class'];
$order = NULL;
$items = array();
if ( ! $this->value )
{
$this->value = array();
}
else
{
$this->value = ( is_string( $this->value ) ) ? explode( ',', $this->value ) : $this->value;
}
if ( count( $this->value ) )
{
if ( is_array( $this->options['orderResults'] ) )
{
if ( isset( $itemClass::$databaseColumnMap[ $this->options['orderResults'][0] ] ) )
{
$order = $itemClass::$databaseColumnMap[ $this->options['orderResults'][0] ] . ' ' . $this->options['orderResults'][1];
}
}
$where = array( \IPS\Db::i()->in( $itemClass::$databaseColumnId, $this->value ) );
foreach( $itemClass::getItemsWithPermission( array( $where ), $order, NULL, $this->options['permissionCheck'] ) as $item )
{
$items[ $item->_id ] = $item;
}
}
$this->value = $items;
return $this->value;
}
/**
* String Value
*
* @param mixed $value The value
* @return string
*/
public static function stringValue( $value )
{
if ( is_array( $value ) )
{
return implode( ',', array_keys( $value ) );
}
elseif ( is_object( $value ) )
{
return $value->_id;
}
return (string) $value;
}
}
+5 -2
View File
@@ -51,7 +51,8 @@ class _Member extends Text
'source' => 'app=core&module=system&controller=ajax&do=findMember',
'resultItemTemplate' => 'core.autocomplete.memberItem',
'unique' => true,
'minAjaxLength' => 3
'minAjaxLength' => 3,
'disallowedCharacters' => array()
);
if( isset( $args[3] ) and array_key_exists( 'multiple', $args[3] ) and $args[3]['multiple'] > 0 )
{
@@ -87,7 +88,9 @@ class _Member extends Text
$value = $value->name;
}
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->text( $this->name, 'text', $value, $this->required, $this->options['maxLength'], $this->options['size'], $this->options['disabled'], $this->options['autocomplete'], $this->options['placeholder'], NULL, NULL );
/* 10/19/15 - adding htmlspecialchars around value if autocomplete is enabled so that html tag characters can be used (e.g. for members) */
/* This value is decoded by the JS widget before use. See https://community.invisionpower.com/4bugtrack/active-reports/cannot-pm-certain-members-r6181/ */
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->text( $this->name, 'text', ( $this->options['autocomplete'] ? htmlspecialchars( $value, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE ) : $value ), $this->required, $this->options['maxLength'], $this->options['size'], $this->options['disabled'], $this->options['autocomplete'], $this->options['placeholder'], NULL, NULL );
}
/**
+2 -2
View File
@@ -37,7 +37,7 @@ class _Node extends FormAbstract
'subnodes' => TRUE, // Controls if subnodes should be included. Defaults to TRUE
'togglePerm' => 'edit', // If a permission key is provided, nodes that have this permission will toggle the element IDs in toggleIds on/off
'toggleIds' => array(), // Element IDs to toggle on/off when togglePerm is reached
'forceOwner' => \IPS\Member::load(), // If nodes are 'owned' you can force a specific owner
'forceOwner' => \IPS\Member::load(), // If nodes are 'owned', the owner. NULL for currently logged in member, FALSE to not limit by owner
'where' => array(), // where clause to control which results to display
'disabledIds' => array(), // Array of disabled IDs
'noParentNodes' => 'Custom' // If a value is provided, subnodes of this class which have no parent node will be added into a pseudo-group with the title provided. e.g. custom packages in Nexus do not belong to any package group
@@ -154,7 +154,7 @@ class _Node extends FormAbstract
$nodes = array();
$children = array();
$noParentNodes = array();
if( isset( $nodeClass::$ownerTypes ) and $nodeClass::$ownerTypes !== NULL )
if( isset( $nodeClass::$ownerTypes ) and $nodeClass::$ownerTypes !== NULL and $this->options['forceOwner'] !== FALSE )
{
$nodes = $nodeClass::loadByOwner( $this->options['forceOwner'] ?: \IPS\Member::loggedIn(), $this->options['where'] );
}
+1 -1
View File
@@ -38,7 +38,7 @@ class _Number extends FormAbstract
'step' => NULL, // Increase step for supported browsers. NULL will cause the value to be based off "decimals". Default is NULL.
'range' => TRUE, // Use a range selection for supported browsers? Default is FALSE.
'disabled' => FALSE, // Disables input. Default is FALSE.
'suffixAfterNumber' => TRUE, // A suffix to go after the unlimited checkbox
'endSuffix' => '...', // A suffix to go after the unlimited checkbox
);
* @endcode
*/
+19 -2
View File
@@ -184,12 +184,12 @@ class _Select extends FormAbstract
{
foreach ( $v as $x => $y )
{
$options[ $k ][ $x ] = htmlentities( $y, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
$options[ $k ][ $x ] = htmlspecialchars( $y, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
}
else
{
$options[ $k ] = htmlentities( $v, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
$options[ $k ] = htmlspecialchars( $v, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
}
break;
@@ -261,6 +261,12 @@ class _Select extends FormAbstract
{
return TRUE;
}
/* If it is not required and there is no value (pagination from table advanced search form then pass validation otherwise it throws an error) */
if ( $this->value === NULL and ! $this->required )
{
return TRUE;
}
parent::validate();
@@ -294,4 +300,15 @@ class _Select extends FormAbstract
return TRUE;
}
/**
* String Value
*
* @param mixed $value The value
* @return string
*/
public static function stringValue( $value )
{
return parent::stringValue( $value );
}
}
+17 -1
View File
@@ -43,6 +43,7 @@ class _Text extends TextArea
'prefix' => TRUE, // If TRUE, user will have option to specify one tag as a prefix
'resultItemTemplate'=> 'core.foo.bar', // Can be used to specify a custom JavaScript template to use for the result
'minAjaxLength' => 3, // Minimum length of value before sending AJAX lookup call
'disallowedCharacters' => array() // An array of disallowed characters (default < > ' ")
),
'placeholder' => 'e.g. ...', // A placeholder (NB: Will only work on compatible browsers)
'regex' => '/[A-Z]+/i', // RegEx of acceptable value
@@ -89,7 +90,7 @@ class _Text extends TextArea
/* Set username regex */
$args = func_get_args();
if ( isset( $args[3]['accountUsername'] ) and $args[3]['accountUsername'] === TRUE )
if ( isset( $args[3]['accountUsername'] ) and $args[3]['accountUsername'] !== FALSE )
{
$args[3]['maxLength'] = \IPS\Settings::i()->max_user_name_length;
@@ -122,6 +123,16 @@ class _Text extends TextArea
*/
public function html()
{
/* 10/19/15 - adding htmlspecialchars around value if autocomplete is enabled so that html tag characters can be used (e.g. for members) */
/* This value is decoded by the JS widget before use. See https://community.invisionpower.com/4bugtrack/active-reports/cannot-pm-certain-members-r6181/ */
if( $this->options['autocomplete'] and !empty( $this->value ) )
{
foreach( $this->value as $key => $value )
{
$this->value[ $key ] = htmlspecialchars( $value, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
}
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->text( $this->name, $this->formType, $this->value, $this->required, $this->options['maxLength'], $this->options['size'], $this->options['disabled'], $this->options['autocomplete'], $this->options['placeholder'], NULL, $this->options['nullLang'], $this->htmlId );
}
@@ -154,6 +165,11 @@ class _Text extends TextArea
}
}
if ( isset( $this->options['autocomplete'] ) )
{
$return = htmlspecialchars_decode( $return, ENT_QUOTES );
}
/* Remove all invisible characters */
$return = preg_replace( '/\p{C}+/u', '', $return );
+10 -7
View File
@@ -88,20 +88,21 @@ class _Upload extends FormAbstract
if( (float) ini_get('upload_max_filesize') > 0 )
{
$potentialValues[] = (float) ini_get('upload_max_filesize');
$potentialValues[] = \IPS\File::returnBytes( ini_get('upload_max_filesize') );
}
if( (float) ini_get('post_max_size') > 0 )
{
$potentialValues[] = (float) ini_get('post_max_size');
$potentialValues[] = \IPS\File::returnBytes( ini_get('post_max_size') ) - 1048576;
}
if( (float) ini_get('memory_limit') > 0 )
{
$potentialValues[] = (float) ini_get('memory_limit');
$potentialValues[] = \IPS\File::returnBytes( ini_get('memory_limit') );
}
$this->maxChunkSize = min( $potentialValues );
/* We want the value in MB */
$this->maxChunkSize = min( $potentialValues ) / 1048576;
/* Call parent constructor */
call_user_func_array( 'parent::__construct', func_get_args() );
@@ -388,7 +389,7 @@ class _Upload extends FormAbstract
{
/* Get the files we had already */
$return = array();
$tempFiles = iterator_to_array( \IPS\Db::i()->select( 'id,contents', 'core_files_temp', array( 'upload_key=?', md5( $this->name . session_id() ) ) )->setKeyField('id')->setValueField('contents') );
$tempFiles = iterator_to_array( \IPS\Db::i()->select( 'id,contents,filename', 'core_files_temp', array( 'upload_key=?', md5( $this->name . session_id() ) ) )->setKeyField('id') );
if ( $this->options['storageExtension'] )
{
$existingName = $this->name . '_existing';
@@ -402,8 +403,10 @@ class _Upload extends FormAbstract
{
if ( $tempId and isset( $tempFiles[ $tempId ] ) )
{
$file = \IPS\File::get( $this->options['storageExtension'], $tempFiles[ $tempId ] );
$file = \IPS\File::get( $this->options['storageExtension'], $tempFiles[ $tempId ]['contents'] );
$file->tempId = $tempId;
/* Reset the original filename to the real file name as $this->originalFilename is generated from the now AWS-safe $this->filename */
$file->originalFilename = $tempFiles[ $tempId ]['filename'];
$return[ $id ] = $file;
}
else
@@ -429,7 +432,7 @@ class _Upload extends FormAbstract
try
{
\IPS\File::get( $this->options['storageExtension'], $tempFiles[ $tempId ] )->delete();
\IPS\File::get( $this->options['storageExtension'], $tempFiles[ $tempId ]['contents'] )->delete();
}
catch ( \Exception $e ) { }
}
+8 -2
View File
@@ -82,7 +82,6 @@ class _Url extends Text
$contentType = ( isset( $response->httpHeaders['Content-Type'] ) ) ? $response->httpHeaders['Content-Type'] : ( ( isset( $response->httpHeaders['content-type'] ) ) ? $response->httpHeaders['content-type'] : NULL );
if( $contentType )
{
foreach ( is_array( $this->options['allowedMimes'] ) ? $this->options['allowedMimes'] : array( $this->options['allowedMimes'] ) as $mime )
{
if ( preg_match( '/^' . str_replace( '~~', '.+', preg_quote( str_replace( '*', '~~', $mime ), '/' ) ) . '$/i', $contentType ) )
@@ -116,7 +115,14 @@ class _Url extends Text
}
}
$this->value = \IPS\File::create( $this->options['file'], $filename, $response );
try
{
$this->value = \IPS\File::create( $this->options['file'], $filename, $response );
}
catch( \InvalidArgumentException $e )
{
throw new \DomainException( 'form_url_error' );
}
}
}
catch ( \IPS\Http\Request\Exception $e )
+3 -3
View File
@@ -54,7 +54,7 @@ class _Content extends Table
public $sortOptions = array();
/**
* @brief Honor the pinned flag for sorting. Default exclusions include vnc and activity streams
* @brief Honor the pinned flag for sorting. Will be set to false in stream-like views
*/
protected $honorPinned = TRUE;
@@ -247,8 +247,8 @@ class _Content extends Table
}
/* Get results */
$it = $class::getItemsWithPermission( $where, $sortBy, array( ( $this->limit * ( $this->page - 1 ) ), $this->limit ), $this->permCheck, $this->includeHiddenContent, \IPS\Db::SELECT_SQL_CALC_FOUND_ROWS, NULL, $this->joinContainer, $this->joinComments, $this->joinReviews, FALSE, $this->joins );
$this->count = $it->count( TRUE );
$this->count = $class::getItemsWithPermission( $where, $sortBy, NULL, $this->permCheck, $this->includeHiddenContent, 0, NULL, $this->joinContainer, $this->joinComments, $this->joinReviews, TRUE, $this->joins );
$it = $class::getItemsWithPermission( $where, $sortBy, array( ( $this->limit * ( $this->page - 1 ) ), $this->limit ), $this->permCheck, $this->includeHiddenContent, 0, NULL, $this->joinContainer, $this->joinComments, $this->joinReviews, FALSE, $this->joins );
$this->pages = ceil( $this->count / $this->limit );
$rows = iterator_to_array( $it );
+49 -20
View File
@@ -27,7 +27,17 @@ class _Custom extends Table
* @brief Data
*/
protected $dataSource;
/**
* @brief Parse Source
*/
public $parseSource = TRUE;
/**
* Number of results
*/
public $count = 0;
/**
* Constructor
*
@@ -50,6 +60,10 @@ class _Custom extends Table
*/
public function getRows( $advancedSearchValues )
{
/* Sort out paging */
$this->count = count( $this->dataSource );
$this->pages = ceil( $this->count / $this->limit );
/* Get them */
$rows = array();
foreach ( $this->dataSource as $i => $data )
@@ -64,29 +78,13 @@ class _Custom extends Table
{
continue;
}
/* Parse if necessary */
if( isset( $this->parsers[ $k ] ) )
{
$v = call_user_func( $this->parsers[ $k ], $v, $data );
}
else
{
$v = htmlentities( $v, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
/* Add to array */
$row[ $k ] = $v;
}
/* Add in some buttons if necessary */
if( $this->rowButtons !== NULL )
{
$row['_buttons'] = call_user_func( $this->rowButtons, $row, $i );
}
/* Add it to the array */
$rows[] = $row;
$rows[ $i ] = $row;
}
/* Quicksearch */
@@ -95,12 +93,18 @@ class _Custom extends Table
$quickSearchColumn = $this->quickSearch;
$rows = array_filter( $rows, is_callable( $this->quickSearch ) ? $this->quickSearch : function( $row ) use ( $quickSearchColumn )
{
return mb_strpos( $row[ $quickSearchColumn ], \IPS\Request::i()->quicksearch ) !== FALSE;
return mb_strpos( $row[ $quickSearchColumn ], trim( \IPS\Request::i()->quicksearch ) ) !== FALSE;
} );
}
/* Limit */
if ( $this->limit and $this->count > $this->limit )
{
$rows = array_slice( $rows, ( $this->limit * ( $this->page - 1 ) ), $this->limit );
}
/* Do we need to sort? */
if( !empty( $rows ) and $this->sortBy and isset( $rows[0][ $this->sortBy ] ) )
if( !empty( $rows ) and $this->sortBy and isset( $rows[ key( $rows ) ][ $this->sortBy ] ) )
{
$sortBy = $this->sortBy;
$sortDirection = $this->sortDirection;
@@ -116,6 +120,31 @@ class _Custom extends Table
}
});
}
/* Apply parsers */
foreach( $rows as $i => $row )
{
foreach( $row as $k => $v )
{
/* Parse if necessary */
if( isset( $this->parsers[ $k ] ) )
{
$v = call_user_func( $this->parsers[ $k ], $v, $this->dataSource[ $i ] );
}
else
{
$v = htmlspecialchars( $v, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
$rows[ $i ][ $k ] = $v;
}
/* Add in some buttons if necessary */
if( $this->rowButtons !== NULL )
{
$rows[ $i ]['_buttons'] = call_user_func( $this->rowButtons, $row, $i );
}
}
return $rows;
}
+14 -7
View File
@@ -99,7 +99,7 @@ class _Db extends Table
{
if ( is_callable( $this->quickSearch ) )
{
$where[] = call_user_func( $this->quickSearch, \IPS\Request::i()->quicksearch );
$where[] = call_user_func( $this->quickSearch, trim( \IPS\Request::i()->quicksearch ) );
}
else
{
@@ -112,7 +112,7 @@ class _Db extends Table
$_where[] = "LOWER(`{$c}`) LIKE CONCAT( '%', ?, '%' )";
}
$where[] = array_merge( array( '(' . implode( ' OR ', $_where ) . ')' ), array_fill( 0, count( $_where ), mb_strtolower( \IPS\Request::i()->quicksearch ) ) );
$where[] = array_merge( array( '(' . implode( ' OR ', $_where ) . ')' ), array_fill( 0, count( $_where ), mb_strtolower( trim( \IPS\Request::i()->quicksearch ) ) ) );
}
}
@@ -164,7 +164,14 @@ class _Db extends Table
break;
case SEARCH_SELECT:
$where[] = array( "{$k}=?", $v );
if ( isset( $options['multiple'] ) AND $options['multiple'] === TRUE )
{
$where[] = array( \IPS\Db::i()->in( $k, $v ) );
}
else
{
$where[] = array( "{$k}=?", $v );
}
break;
case SEARCH_MEMBER:
@@ -262,7 +269,7 @@ class _Db extends Table
{
foreach( $this->joins as $join )
{
$query->join( $join['from'], ( isset( $join['where'] ) ? $join['where'] : null ), 'LEFT' );
$query->join( $join['from'], ( isset( $join['where'] ) ? $join['where'] : null ), ( isset( $join['type'] ) ) ? $join['type'] : 'LEFT' );
}
}
@@ -337,7 +344,7 @@ class _Db extends Table
{
$select->setKeyField( $this->keyField );
}
foreach ( $select as $rowId => $row )
{
/* Add in any 'custom' fields */
@@ -349,7 +356,7 @@ class _Db extends Table
{
$row[ $k ] = isset( $_row[ $k ] ) ? $_row[ $k ] : NULL;
}
if( !empty( $advancedSearchValues ) )
{
foreach ( $advancedSearchValues as $k => $v )
@@ -369,7 +376,7 @@ class _Db extends Table
}
else
{
$v = htmlentities( $v, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE );
$v = htmlspecialchars( $v, ENT_QUOTES | \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
/* Are we including this one? */
+30 -4
View File
@@ -56,7 +56,7 @@ class _Tree
/**
* @brief Searchable?
*/
protected $searchable = FALSE;
public $searchable = FALSE;
/**
* @brief If true, will prevent any item from being moved out of its current parent, only allowing them to be reordered within their current parent
@@ -67,6 +67,16 @@ class _Tree
* @brief If true, root cannot be turned into sub-items, and other items cannot be turned into roots
*/
protected $protectRoots = FALSE;
/**
* @brief Number of roots to show per page (NULL for unlimited). In most cases this doesn't make sense, since it makes re-ordering impossible. But for trees which are not orderable and which may contain a lot of roots, you can set this value
*/
public $rootsPerPage = NULL;
/**
* @brief If using $rootsPerPage, a callback function that returns the total number of roots
*/
public $getTotalRoots = NULL;
/**
* Constructor
@@ -80,9 +90,11 @@ class _Tree
* @param callback $searchable Show the search bar?
* @param bool $lockParents If true, will prevent any item from being moved out of its current parent, only allowing them to be reordered within their current parent
* @param bool $protectRoots If true, root cannot be turned into sub-items, and other items cannot be turned into roots
* @param int|null $rootsPerPage Number of roots to show per page (NULL for unlimited). In most cases this doesn't make sense, since it makes re-ordering impossible. But for trees which are not orderable and which may contain a lot of roots, you can set this value
* @param callback $getTotalRoots If using $rootsPerPage, a callback function that returns the total number of roots
* @return void
*/
public function __construct( $url, $title, $getRoots, $getRow, $getRowParentId, $getChildren, $getRootButtons=NULL, $searchable=FALSE, $lockParents=FALSE, $protectRoots=FALSE )
public function __construct( $url, $title, $getRoots, $getRow, $getRowParentId, $getChildren, $getRootButtons=NULL, $searchable=FALSE, $lockParents=FALSE, $protectRoots=FALSE, $rootsPerPage = NULL, $getTotalRoots = NULL )
{
$this->url = $url;
$this->title = $title;
@@ -94,6 +106,8 @@ class _Tree
$this->searchable = $searchable;
$this->lockParents = $lockParents;
$this->protectRoots = $protectRoots;
$this->rootsPerPage = $rootsPerPage;
$this->getTotalRoots = $getTotalRoots;
}
/**
@@ -106,11 +120,12 @@ class _Tree
try
{
/* Get rows */
$page = isset( \IPS\Request::i()->page ) ? intval( \IPS\Request::i()->page ) : 1;
$root = NULL;
$rootParent = NULL;
if( !\IPS\Request::i()->root )
{
$rows = call_user_func( $this->getRoots );
$rows = call_user_func( $this->getRoots, $this->rootsPerPage ? array( ( $page - 1 ) * $this->rootsPerPage, $this->rootsPerPage ) : NULL );
}
else
{
@@ -124,9 +139,20 @@ class _Tree
$root = call_user_func( $this->getRow, \IPS\Request::i()->root, TRUE );
$rootParent = call_user_func( $this->getRowParentId, \IPS\Request::i()->root );
}
/* Pagination? */
$pagination = '';
if ( $this->rootsPerPage )
{
$totalNumber = call_user_func( $this->getTotalRoots );
if ( $totalNumber )
{
$pagination = \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->pagination( $this->url, ceil( $totalNumber / $this->rootsPerPage ), $page, $this->rootsPerPage );
}
}
/* Display */
return \IPS\Theme::i()->getTemplate( 'trees', 'core' )->template( $this->url, $this->title, $root, $rootParent, $rows, call_user_func( $this->getRootButtons ), $this->lockParents, $this->protectRoots, $this->searchable );
return \IPS\Theme::i()->getTemplate( 'trees', 'core' )->template( $this->url, $this->title, $root, $rootParent, $rows, call_user_func( $this->getRootButtons ), $this->lockParents, $this->protectRoots, $this->searchable, $pagination );
}
catch ( \Exception $e )
{
+16 -7
View File
@@ -29,7 +29,7 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
$form = new \IPS\Helpers\Form;
$form->add( ... );
if ( $values = $form->values )
if ( $values = $form->values() )
{
return $values;
}
@@ -41,7 +41,7 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
// $data contains the form values from the previous step
}
),
\IPS\Http\Url::internal( 'app=example&module=example&controller=example&do=wizard' );
\IPS\Http\Url::internal( 'app=example&module=example&controller=example&do=wizard' )
);
* @endcode
*/
@@ -75,11 +75,12 @@ class _Wizard
/**
* Constructor
*
* @param array $steps An array of callback functions. Each function should return either a string to output or (if the step is done) an array (which can be blank) of arbitrary data to retain between steps (which will be passed to each callback function). The keys should be langauge keys for the title of the step.
* @param \IPS\Http\Url $baseUrl The base URL (used when moving between steps)
* @param array $steps An array of callback functions. Each function should return either a string to output or (if the step is done) an array (which can be blank) of arbitrary data to retain between steps (which will be passed to each callback function). The keys should be langauge keys for the title of the step.
* @param \IPS\Http\Url $baseUrl The base URL (used when moving between steps)
* @param array $initialData The initial data
* @return void
*/
public function __construct( $steps, $baseUrl, $showSteps=TRUE )
public function __construct( $steps, $baseUrl, $showSteps=TRUE, $initialData=NULL )
{
$this->steps = $steps;
$this->baseUrl = $baseUrl;
@@ -90,8 +91,16 @@ class _Wizard
{
unset( $_SESSION[ 'wizard-' . md5( $this->baseUrl ) . '-step' ] );
unset( $_SESSION[ 'wizard-' . md5( $this->baseUrl ) . '-data' ] );
\IPS\Output::i()->redirect( $baseUrl );
if ( !is_null( $initialData ) )
{
$_SESSION[ 'wizard-' . md5( $this->baseUrl ) . '-data' ] = $initialData;
}
if ( !\IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->redirect( $baseUrl );
}
}
}