Version 4.0.13.1

This commit is contained in:
Neo committed 2025-12-19 04:57:54 -08:00
1 parent f9e857d255
commit c6a86f0d9b
7517 files changed
+597400 -668041

No files matched your search

+240
View File
@@ -0,0 +1,240 @@
<?php
/**
* @brief Address input class 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 11 Jul 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;
}
/**
* Address input class for Form Builder
*/
class _Address extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'minimize' => FALSE, // Minimize the address field until the user focuses?
);
* @endcode
*/
protected $defaultOptions = array(
'minimize' => FALSE
);
/**
* Get HTML
*
* @return string
*/
public function html()
{
/* If we don't have a value, set their country based on the HTTP headers */
if ( !$this->value OR ( $this->value instanceof \IPS\GeoLocation AND !$this->value->country ) )
{
$this->value = ( $this->value instanceof \IPS\GeoLocation ) ? $this->value : new \IPS\GeoLocation;
if ( $defaultCountry = $this->_calculateDefaultCountry() )
{
$this->value->country = $defaultCountry;
}
}
/* Display */
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->address( $this->name, $this->value, \IPS\Settings::i()->googleplacesautocomplete, $this->options['minimize'] );
}
/**
* Calculate default country
*
* @return string|NULL
*/
protected function _calculateDefaultCountry()
{
if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) )
{
if( mb_strpos( $_SERVER['HTTP_ACCEPT_LANGUAGE'], ',' ) )
{
$exploded = explode( ',', $_SERVER['HTTP_ACCEPT_LANGUAGE'] );
$exploded = $exploded[0];
}
else
{
$exploded = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
}
$exploded = explode( '-', $exploded );
if ( isset( $exploded[1] ) )
{
$country = mb_strtoupper( $exploded[1] );
if ( in_array( $country, \IPS\GeoLocation::$countries ) )
{
return $country;
}
}
}
return NULL;
}
/**
* Get Value
*
* @return mixed
*/
public function getValue()
{
/* Create the object */
$input = parent::getValue();
$value = new \IPS\GeoLocation;
$value->addressLines = array_filter( $input['address'] );
if ( empty( $value->addressLines ) )
{
$value->addressLines = array( NULL );
}
if( isset( $input['city'] ) )
{
$value->city = $input['city'];
}
if( isset( $input['region'] ) )
{
$value->region = $input['region'];
}
if( isset( $input['postalCode'] ) )
{
$value->postalCode = $input['postalCode'];
}
if( isset( $input['country'] ) )
{
$value->country = $input['country'];
}
/* Work out what parts are filled in */
$partiallyCompleted = FALSE;
$fullyCompleted = TRUE;
$addresslines = array_filter( $value->addressLines );
if ( empty( $addresslines ) )
{
$fullyCompleted = FALSE;
}
else
{
$partiallyCompleted = TRUE;
}
if ( $value->city )
{
$partiallyCompleted = TRUE;
}
else
{
$fullyCompleted = FALSE;
}
if ( $value->postalCode )
{
$partiallyCompleted = TRUE;
}
else
{
$fullyCompleted = FALSE;
}
if ( $value->country )
{
if ( $value->country != $this->_calculateDefaultCountry() )
{
$partiallyCompleted = TRUE;
}
if ( array_key_exists( $value->country, \IPS\GeoLocation::$states ) )
{
if ( !$value->region )
{
$fullyCompleted = FALSE;
}
}
}
else
{
$fullyCompleted = FALSE;
}
if ( trim( $value->region ) )
{
$states = ( isset( \IPS\GeoLocation::$states[ $value->country ] ) ) ? \IPS\GeoLocation::$states[ $value->country ] : array();
if ( !array_key_exists( $value->country, \IPS\GeoLocation::$states ) or $value->region != array_shift( $states ) )
{
$partiallyCompleted = TRUE;
}
}
/* Validate, return NULL if we have nothing */
if ( !$fullyCompleted )
{
if ( $partiallyCompleted )
{
if ( $this->required )
{
throw new \InvalidArgumentException('form_partial_address_req');
}
else
{
throw new \InvalidArgumentException('form_partial_address_opt');
}
}
else
{
return NULL;
}
}
/* Add in latitude and longitude if we can */
try
{
$value->getLatLong();
}
catch( \BadFunctionCallException $e ){}
/* Return */
return $value;
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
if( $this->value === NULL and $this->required )
{
throw new \InvalidArgumentException('form_required');
}
return parent::validate();
}
/**
* String Value
*
* @param mixed $value The value
* @return string
*/
public static function stringValue( $value )
{
return json_encode( $value );
}
}
+138
View File
@@ -0,0 +1,138 @@
<?php
/**
* @brief Captcha class 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 18 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;
}
/**
* Captcha class for Form Builder
*/
class _Captcha extends FormAbstract
{
/**
* CAPTCHA Class
*/
protected $captcha = NULL;
/**
* Constructor
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
*/
public function __construct()
{
$params = func_get_args();
if ( !isset( $params[0] ) )
{
$params[0] = 'captcha_field';
}
if ( \IPS\Settings::i()->bot_antispam_type != 'none' )
{
$class = '\IPS\Helpers\Form\Captcha\\' . ucfirst( \IPS\Settings::i()->bot_antispam_type );
if ( !class_exists( $class ) )
{
\IPS\Output::i()->error( 'unexpected_captcha', '4S262/1', 500, 'unexpected_captcha_admin' );
}
$this->captcha = new $class;
}
call_user_func_array( 'parent::__construct', $params );
$this->required = TRUE;
}
/**
* Get HTML
*
* @return string
*/
public function __toString()
{
if ( $this->captcha === NULL )
{
return '';
}
return parent::__toString();
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
if ( $this->captcha === NULL )
{
return '';
}
return $this->captcha->getHtml();
}
/**
* Get Value
*
* @return bool|null TRUE/FALSE indicate if the test passed or not. NULL indicates the test failed, but the captcha system will display an error so we don't have to.
*/
public function getValue()
{
if ( $this->captcha === NULL )
{
return TRUE;
}
else
{
/* 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 ) )
{
return TRUE;
}
/* Otherwise, check with service */
else
{
/* Check */
$return = $this->captcha->verify();
/* If it's valid and we're doing an AJAX validate, save that in the session so the next request doesn't check again */
if ( $return and \IPS\Request::i()->isAjax() )
{
$_SESSION[ 'captcha-val-' . $this->name ] = time();
}
/* Return */
return $return;
}
}
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
if ( $this->value !== TRUE )
{
throw new \InvalidArgumentException( 'form_bad_captcha' );
}
return TRUE;
}
}
@@ -0,0 +1,79 @@
<?php
/**
* @brief keyCAPTCHA
* @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 18 Apr 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Helpers\Form\Captcha;
/* 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;
}
/**
* keyCAPTCHA
*/
class _Keycaptcha
{
/**
* @brief Error
*/
protected $error;
/**
* Display
*
* @return string
*/
public function getHtml()
{
$explodedKey = explode( '0', \IPS\Settings::i()->keycaptcha_privatekey, 2 );
$uniq = md5( uniqid() );
$sign = md5( $uniq . \IPS\Request::i()->ipAddress . $explodedKey[0] );
$sign2 = md5( $uniq . $explodedKey[0] );
return <<<HTML
<input type='hidden' id='capcode' name='keycaptcha'>
<script type="text/javascript">
// required
var s_s_c_user_id = '{$explodedKey[1]}';
var s_s_c_session_id = '{$uniq}';
var s_s_c_captcha_field_id = 'capcode';
var s_s_c_submit_button_id ='sbutton-#-r';
var s_s_c_web_server_sign = '{$sign}';
var s_s_c_web_server_sign2 = '{$sign2}';
</script>
<div data-ipsCaptcha data-ipsCaptcha-service='keycaptcha' id='div_for_keycaptcha'></div>
HTML;
}
/**
* Verify
*
* @return bool|null TRUE/FALSE indicate if the test passed or not. NULL indicates the test failed, but the captcha system will display an error so we don't have to.
*/
public function verify()
{
$explodedResponse = explode( '|', \IPS\Request::i()->keycaptcha );
$explodedKey = explode( '0', \IPS\Settings::i()->keycaptcha_privatekey );
if( $explodedResponse[0] === md5( 'accept' . $explodedResponse[1] . $explodedKey[0] . $explodedResponse[2] ) )
{
if( (string) \IPS\Http\Url::external( $explodedResponse[2] )->request()->get() === '1' )
{
return TRUE;
}
}
return FALSE;
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
/**
* @brief reCAPTCHA
* @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 18 Apr 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Helpers\Form\Captcha;
/* 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;
}
/**
* reCAPTCHA
*/
class _Recaptcha
{
/**
* @brief Error
*/
protected $error;
/**
* Display
*
* @return string
*/
public function getHtml()
{
$publicKey = \IPS\Settings::i()->recaptcha_public_key;
$theme = \IPS\Settings::i()->recaptcha_theme;
$lang = preg_replace( '/^(.+?)\..*$/', '$1', \IPS\Member::loggedIn()->language()->short );
return <<<HTML
<div data-ipsCaptcha data-ipsCaptcha-service='recaptcha' data-ipsCaptcha-key="{$publicKey}" data-ipsCaptcha-lang="{$lang}" data-ipsCaptcha-theme="{$theme}">
<noscript>
<iframe src="//www.google.com/recaptcha/api/noscript?k={$publicKey}&hl={$lang}&error={$this->error}"
height="300" width="500" frameborder="0"></iframe><br>
<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
<input type="hidden" name="recaptcha_response_field" value="manual_challenge">
</noscript>
</div>
HTML;
}
/**
* Verify
*
* @return bool|null TRUE/FALSE indicate if the test passed or not. NULL indicates the test failed, but the captcha system will display an error so we don't have to.
*/
public function verify()
{
try
{
$response = \IPS\Http\Url::external( 'http://www.google.com/recaptcha/api/verify' )->request()->post( array(
'privatekey' => \IPS\Settings::i()->recaptcha_private_key,
'remoteip' => \IPS\Request::i()->ipAddress(),
'challenge' => \IPS\Request::i()->recaptcha_challenge_field,
'response' => \IPS\Request::i()->recaptcha_response_field
) );
}
catch ( \IPS\Http\Request\Exception $e )
{
return FALSE;
}
$response = explode( "\n", $response );
if ( $response[0] === 'true' )
{
return true;
}
else
{
$this->error = $response[1];
return null;
}
}
}
@@ -0,0 +1,92 @@
<?php
/**
* @brief reCAPTCHA
* @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 5 Dec 2014
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Helpers\Form\Captcha;
/* 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;
}
/**
* reCAPTCHA
*/
class _Recaptcha2
{
/**
* @brief Error
*/
protected $error;
/**
* Display
*
* @return string
*/
public function getHtml()
{
$publicKey = \IPS\Settings::i()->recaptcha2_public_key;
$theme = \IPS\Settings::i()->recaptcha2_theme;
$lang = preg_replace( '/^(.+?)\..*$/', '$1', \IPS\Member::loggedIn()->language()->short );
return <<<HTML
<div data-ipsCaptcha data-ipsCaptcha-service='recaptcha2' data-ipsCaptcha-key="{$publicKey}" data-ipsCaptcha-lang="{$lang}" data-ipsCaptcha-theme="{$theme}">
<noscript>
<div style="width: 302px; height: 352px;">
<div style="width: 302px; height: 352px; position: relative;">
<div style="width: 302px; height: 352px; position: absolute;">
<iframe src="https://www.google.com/recaptcha/api/fallback?k={$publicKey}" frameborder="0" scrolling="no" style="width: 302px; height:352px; border-style: none;">
</iframe>
</div>
<div style="width: 250px; height: 80px; position: absolute; border-style: none; bottom: 21px; left: 25px; margin: 0px; padding: 0px; right: 25px;">
<textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px; height: 80px; border: 1px solid #c1c1c1; margin: 0px; padding: 0px; resize: none;" value="">
</textarea>
</div>
</div>
</div>
</noscript>
</div>
HTML;
}
/**
* Verify
*
* @return bool|null TRUE/FALSE indicate if the test passed or not. NULL indicates the test failed, but the captcha system will display an error so we don't have to.
*/
public function verify()
{
try
{
$response = \IPS\Http\Url::external( 'https://www.google.com/recaptcha/api/siteverify' )->request()->post( array(
'secret' => \IPS\Settings::i()->recaptcha2_private_key,
'response' => trim( \IPS\Request::i()->__get('g-recaptcha-response') ),
'remoteip' => \IPS\Request::i()->ipAddress(),
) )->decodeJson( TRUE );
return (bool) $response['success'];
}
catch( \RuntimeException $e )
{
if( $e->getMessage() == 'BAD_JSON' )
{
return FALSE;
}
else
{
throw $e;
}
}
}
}
Whitespace-only changes.
+98
View File
@@ -0,0 +1,98 @@
<?php
/**
* @brief Checkbox class 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 26 Feb 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;
}
/**
* Checkbox class for Form Builder
*/
class _Checkbox extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'disabled' => FALSE, // Disabled?
'togglesOn' => array( ... ), // IDs of elements to show when checked
'togglesOff' => array( ... ), // IDs of elements to show when unchecked
'label' => '', // Label language key
);
* @endcode
*/
protected $defaultOptions = array(
'disabled' => FALSE,
'togglesOn' => array(),
'togglesOff' => array(),
'label' => '',
);
/**
* Get HTML
*
* @return string
*/
public function html()
{
if ( !$this->htmlId )
{
$this->htmlId = md5( uniqid() );
}
$checkboxName = preg_replace( '/^(.+?\[?.+?)(\])?$/', '$1_checkbox$2', $this->name );
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->checkbox( $checkboxName, $this->value, $this->options['disabled'], $this->options['togglesOn'], $this->options['togglesOff'], $this->options['label'], $this->name, $this->htmlId );
}
/**
* Format Value
*
* @return bool
*/
public function formatValue()
{
return (bool) $this->value;
}
/**
* Get Value
*
* @return bool
*/
public function getValue()
{
$checkboxName = preg_replace( '/^(.+?\[?.+?)(\])?$/', '$1_checkbox$2', $this->name );
if ( mb_strpos( $checkboxName, '[' ) === FALSE )
{
return \IPS\Request::i()->$checkboxName;
}
else
{
return \IPS\Request::i()->valueFromArray( $checkboxName );
}
}
/**
* String Value
*
* @param mixed $value The value
* @return string
*/
public static function stringValue( $value )
{
return intval( $value );
}
}
+112
View File
@@ -0,0 +1,112 @@
<?php
/**
* @brief Checkbox Set class 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 19 Jul 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;
}
/**
* Checkbox Set class for Form Builder
*/
class _CheckboxSet extends Select
{
/**
* Constructor
*
* @return void
*/
public function __construct()
{
$args = func_get_args();
$args[3]['multiple'] = TRUE;
call_user_func_array( 'parent::__construct', $args );
if ( !isset( $this->options['descriptions'] ) )
{
$this->options['descriptions'] = array();
}
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
/* Get descriptions */
$descriptions = $this->options['descriptions'];
if ( $this->options['parse'] === 'lang' )
{
foreach ( $this->options['options'] as $k => $v )
{
$key = "{$v}_desc";
if ( \IPS\Member::loggedIn()->language()->checkKeyExists( $key ) )
{
$descriptions[ $k ] = \IPS\Member::loggedIn()->language()->addToStack( $key );
}
}
}
/* Translate labels back to keys? */
if ( $this->options['returnLabels'] )
{
$value = array();
if ( !is_array( $this->value ) )
{
$this->value = explode( ',', $this->value );
}
foreach ( $this->value as $v )
{
$value[] = array_search( $v, $this->options['options'] );
}
}
else
{
$value = $this->value;
}
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->checkboxset( $this->name, $value, $this->required, $this->parseOptions(), $this->options['multiple'], $this->options['class'], $this->options['disabled'], $this->options['toggles'], NULL, $this->options['unlimited'], $this->options['unlimitedLang'], $this->options['unlimitedToggles'], $this->options['unlimitedToggleOn'], $descriptions );
}
/**
* Get value
*
* @return array
*/
public function getValue()
{
$value = array_keys( parent::getValue() );
if ( $this->options['returnLabels'] )
{
if ( is_array( $value ) )
{
$return = array();
foreach ( $value as $k => $v )
{
$return[ $k ] = $this->options['options'][ $v ];
}
return $return;
}
else
{
return $this->options['options'][ $value ];
}
}
return $value;
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
/**
* @brief Codemirror class 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 8 Jul 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;
}
/**
* Codemirror class for Form Builder
*/
class _Codemirror extends TextArea
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'minLength' => 1, // Minimum number of characters. NULL is no minimum. Default is NULL.
'maxLength' => 255, // Maximum number of characters. NULL is no maximum. Default is NULL.
'disabled' => FALSE, // Disables input. Default is FALSE.
'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.
);
* @endcode
*/
protected $defaultOptions = array(
'minLength' => NULL,
'maxLength' => NULL,
'disabled' => FALSE,
'placeholder' => NULL,
'tags' => array(),
'mode' => 'htmlmixed',
'nullLang' => NULL,
);
/**
* Constructor
* Sets that the field is required if there is a minimum length and vice-versa
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
*/
public function __construct()
{
/* Call parent constructor */
call_user_func_array( 'parent::__construct', func_get_args() );
/* We don't support this feature */
$this->options['nullLang'] = NULL;
/* Append our necessary JS/CSS */
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'codemirror/diff_match_patch.js', 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'codemirror/codemirror.js', 'core', 'interface' ) );
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'codemirror/codemirror.css', 'core', 'interface' ) );
}
/**
* Get HTML
*
* @return string
*/
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 );
}
}
+105
View File
@@ -0,0 +1,105 @@
<?php
/**
* @brief Color input class 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 11 Mar 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;
}
/**
* Color input class for Form Builder
*/
class _Color extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'disabled' => FALSE, // Disables input. Default is FALSE.
);
* @endcode
*/
protected $defaultOptions = array(
'disabled' => FALSE,
);
/**
* Constructor
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
*/
public function __construct()
{
/* Call parent constructor */
call_user_func_array( 'parent::__construct', func_get_args() );
/* Add the fallback for browsers that don't support the HTML5 color input type */
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jscolor/jscolor.js', 'core', 'interface' ) );
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->color( $this->name, $this->value, $this->required, $this->options['disabled'] );
}
/**
* Format Value
*
* @return string
*/
public function formatValue()
{
$manualName = $this->name . '_manual';
/* If a manual value has been supplied, use that instead */
if ( isset( \IPS\Request::i()->$manualName ) )
{
$value = \IPS\Request::i()->$manualName;
}
else
{
$value = $this->value;
}
if ( mb_substr( $value, 0, 1 ) !== '#' )
{
$value = '#' . $value;
}
return mb_strtolower( $value );
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
parent::validate();
if ( !preg_match( '/^(?:#)?(([a-f0-9]{3})|([a-f0-9]{6}))$/i', $this->value ) )
{
throw new \InvalidArgumentException('form_color_bad');
}
}
}
+98
View File
@@ -0,0 +1,98 @@
<?php
/**
* @brief Custom input class 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 18 Feb 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;
}
/**
* Custom input class for Form Builder
*/
class _Custom extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'getHtml' => function(){...} // Function to get output
'formatValue' => function(){...} // Function to format value
'validate' => function(){...} // Function to validate
);
* @endcode
*/
protected $defaultOptions = array(
'getHtml' => NULL,
'formatValue' => NULL,
'validate' => NULL,
);
/**
* Get HTML
*
* @return string
*/
public function html()
{
return call_user_func( $this->options['getHtml'], $this );
}
/**
* Get HTML
*
* @return string
*/
public function rowHtml( $form=NULL )
{
if ( isset( $this->options['rowHtml'] ) )
{
return call_user_func( $this->options['rowHtml'], $this, $form );
}
return parent::rowHtml( $form );
}
/**
* Format Value
*
* @return mixed
*/
public function formatValue()
{
if ( $this->options['formatValue'] !== NULL )
{
return call_user_func( $this->options['formatValue'], $this );
}
else
{
return parent::formatValue();
}
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
parent::validate();
if ( $this->options['validate'] )
{
call_user_func( $this->options['validate'], $this );
}
}
}
+294
View File
@@ -0,0 +1,294 @@
<?php
/**
* @brief Date input class 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 8 Mar 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;
}
/**
* Date input class for Form Builder
*/
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.
'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.
'unlimitedLang' => 'unlimited', // Language string to use for unlimited checkbox label
'unlimitedToggles' => array(...), // Names of other input fields that should show/hide when the "Unlimited" checkbox is toggled.
'unlimitedToggleOn' => TRUE, // Whether the toggles should show on unlimited TRUE or FALSE. Default is TRUE
'timezone' => NULL, // The timezone (DateTimeZone object) the submitted date/time is in. If NULL is provided, the user's timezone will be used
);
* @endcode
*/
protected $defaultOptions = array(
'min' => NULL,
'max' => NULL,
'disabled' => FALSE,
'time' => FALSE,
'unlimited' => NULL,
'unlimitedLang' => 'indefinite',
'unlimitedToggles' => array(),
'unlimitedToggleOn' => TRUE,
'timezone' => NULL,
);
/**
* Input name for time field
*/
protected $timeName = NULL;
/**
* Input name for unlimited checkbox
*/
protected $unlimitedName = NULL;
/**
* Constructor
* Formats the value appropriately
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @param string $name Name
* @param mixed $defaultValue Default value
* @param bool $required Required?
* @param array $options Type-specific options
* @return void
*/
public function __construct( $name, $defaultValue=NULL, $required=FALSE, $options=array() )
{
/* Work out the key for the time input and unlimited checkboxes */
if ( mb_strpos( $name, '[' ) !== FALSE )
{
$this->timeName = preg_replace( '/\[(.+?)\]/', '[$1_time]', $name, 1 );
$this->unlimitedName = preg_replace( '/\[(.+?)\]/', '[$1_unlimited]', $name, 1 );
}
else
{
$this->timeName = "{$name}_time";
$this->unlimitedName = "{$name}_unlimited";
}
/* Call parent constructor */
call_user_func_array( 'parent::__construct', func_get_args() );
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery-ui.js', 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery-touchpunch.js', 'core', 'interface' ) );
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->date(
$this->name,
$this->value,
$this->required,
( $this->options['min'] ? $this->options['min']->format( 'Y-m-d' ) : NULL ),
( $this->options['max'] ? $this->options['max']->format( 'Y-m-d' ) : NULL ),
$this->options['disabled'],
$this->options['time'] ? $this->timeName : NULL,
$this->options['unlimited'],
$this->options['unlimitedLang'],
$this->unlimitedName,
$this->options['unlimitedToggles'],
$this->options['unlimitedToggleOn']
);
}
/**
* Get Value
*
* @return mixed
*/
public function getValue()
{
/* Unlimited? */
if ( $this->options['unlimited'] !== NULL )
{
$unlimitedName = $this->unlimitedName;
if ( mb_strpos( $unlimitedName, '[' ) === FALSE )
{
if ( isset( \IPS\Request::i()->$unlimitedName ) )
{
return $this->options['unlimited'];
}
}
elseif ( \IPS\Request::i()->valueFromArray( $unlimitedName ) !== NULL )
{
return $this->options['unlimited'];
}
}
/* Get value */
return parent::getValue();
}
/**
* Format Value
*
* @return \IPS\DateTime|null
*/
public function formatValue()
{
$v = $this->value;
$timezone = $this->options['timezone'] ?: ( \IPS\Member::loggedIn()->timezone ? new \DateTimeZone( \IPS\Member::loggedIn()->timezone ) : NULL );
if ( $this->options['unlimited'] !== NULL and $v === $this->options['unlimited'] )
{
return $v;
}
elseif ( $v )
{
if ( is_int( $v ) or ( $v and is_string( $v ) and preg_match( '/^[0-9]+$/', $v ) ) )
{
return \IPS\DateTime::ts( $v );
}
else if( is_array( $v ) )
{
/* When using pagination and date range, data may come as an array of the datetime object, e.g. field['start']['date']=(date)&field['start']['timezone']=(timezone) */
try
{
$timeKey = $this->timeName;
$time = $this->options['time'] ? ( mb_strpos( $timeKey, '[' ) === FALSE ? \IPS\Request::i()->$timeKey : \IPS\Request::i()->valueFromArray( $timeKey ) ) : '';
return new \IPS\DateTime( static::_convertDateFormat( $v['date'] ) . ' ' . $time, new \DateTimeZone( $v['timezone'] ) );
}
catch ( \Exception $e )
{
return $v;
}
}
else
{
try
{
$timeKey = $this->timeName;
$time = $this->options['time'] ? ( mb_strpos( $timeKey, '[' ) === FALSE ? \IPS\Request::i()->$timeKey : \IPS\Request::i()->valueFromArray( $timeKey ) ) : '';
if( $time )
{
return new \IPS\DateTime( static::_convertDateFormat( $v ) . ' ' . $time, $timezone );
}
else if( $v instanceof \IPS\DateTime )
{
return $v;
}
else
{
return new \IPS\DateTime( static::_convertDateFormat( $v ), $timezone );
}
}
catch ( \Exception $e )
{
return $v;
}
}
}
return NULL;
}
/**
* Convert date to expected format
*
* @param string $date User supplied date
* @param \IPS\Member|NULL $member The user that supplied it (NULL For currently logged in member)
* @return string
*/
public static function _convertDateFormat( $date, \IPS\Member $member = NULL )
{
$member = $member ?: \IPS\Member::loggedIn();
$format = $member->language()->preferredDateFormat();
$count = 0;
$bits = array();
foreach ( array( 'dd', 'mm', 'yy' ) as $k )
{
$regex = str_replace( $k, '(.+?)', preg_quote( $format, '/' ) );
foreach ( array( 'dd', 'mm', 'yy' ) as $j )
{
if ( $k != $j )
{
$regex = str_replace( $j, '.+?', $regex );
}
}
$_count = 0;
$bits[ $k ] = preg_replace( "/^{$regex}$/", '$1', trim( $date ), 1, $_count );
$count += $_count;
}
return ( $count == 3 ) ? "{$bits['yy']}-{$bits['mm']}-{$bits['dd']}" : $date;
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @throws \LengthException
* @return TRUE
*/
public function validate()
{
if( $this->value === NULL and $this->required )
{
throw new \InvalidArgumentException('form_required');
}
parent::validate();
/* Unlimited is fine */
if ( $this->options['unlimited'] !== NULL and $this->value === $this->options['unlimited'] )
{
return;
}
/* Check it's valid */
if ( !( $this->value instanceof \IPS\DateTime ) and $this->value !== NULL )
{
throw new \InvalidArgumentException( 'form_date_bad' );
}
/* Check minimum */
if ( $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 )
{
throw new \LengthException( \IPS\Member::loggedIn()->language()->addToStack('form_date_max', FALSE, array( 'sprintf' => array( $this->options['max']->localeDate() ) ) ) );
}
return TRUE;
}
/**
* String Value
*
* @param mixed $value The value
* @return string
*/
public static function stringValue( $value )
{
return $value instanceof \IPS\DateTime ? $value->getTimestamp() : $value;
}
}
+137
View File
@@ -0,0 +1,137 @@
<?php
/**
* @brief Date range input class 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 18 Feb 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;
}
/**
* Date range input class for Form Builder
*/
class _DateRange extends FormAbstract
{
/**
* @brief Default Options
* @see \IPS\Helpers\Form\Date::$defaultOptions
* @code
$defaultOptions = array(
'start' => array( ... ),
'end' => array( ... ),
);
* @endcode
*/
protected $defaultOptions = array(
'start' => array(
'min' => NULL,
'max' => NULL,
'disabled' => FALSE,
'time' => FALSE,
'unlimited' => NULL,
'unlimitedLang' => 'indefinite',
'unlimitedToggles' => array(),
'unlimitedToggleOn' => TRUE,
),
'end' => array(
'min' => NULL,
'max' => NULL,
'disabled' => FALSE,
'time' => FALSE,
'unlimited' => NULL,
'unlimitedLang' => 'indefinite',
'unlimitedToggles' => array(),
'unlimitedToggleOn' => TRUE,
),
);
/**
* @brief Start Date Object
*/
public $start = NULL;
/**
* @brief End Date Object
*/
public $end = NULL;
/**
* Constructor
* Creates the two date objects
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @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() );
call_user_func_array( 'parent::__construct', func_get_args() );
}
/**
* Format Value
*
* @return array
*/
public function formatValue()
{
/* The end date needs to be 23:59:59 rather than 00:00:00 as we need to go right up to the end of the day */
$end = $this->end->formatValue();
if ( $end instanceof \IPS\DateTime and $this->options['end']['time'] === FALSE )
{
$end->setTime( 23, 59, 59 );
}
/* Return */
return array(
'start' => $this->start->formatValue(),
'end' => $end
);
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->dateRange( $this->start->html(), $this->end->html() );
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @throws \LengthException
* @return TRUE
*/
public function validate()
{
$this->start->validate();
$this->end->validate();
if ( $this->required and $this->value['start'] === NULL and $this->value['end'] === NULL )
{
throw new \InvalidArgumentException('form_required');
}
if( $this->customValidationCode !== NULL )
{
call_user_func( $this->customValidationCode, $this->value );
}
}
}
+591
View File
@@ -0,0 +1,591 @@
<?php
/**
* @brief Editor class 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 5 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;
}
/**
* Editor class for Form Builder
*/
class _Editor extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'app' => 'core', // The application that owns this type of editor (as defined by an extension)
'key' => 'Example', // The key for this type of editor (as defined by an extension)
'autoSaveKey' => 'abc', // Pass 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' => array( // ID numbers to idenfity content for attachments if the content has been saved - the first two must be int or null, the third must be string or null. If content has not been saved yet, you must claim attachments after saving.
1,
2,
'foo'
),
'minimize' => 'clickme', // Language string to use for mimimized view. NULL will mean editor is not minimized.
'minimizeIcon' => 'flag-us', // Icon to use for minimized view
'allButtons' => FALSE, // Only used for the customisation ACP page. Do not use.
'tags' => array(), // An array of extra insertable tags in key => value pair with key being what is inserted and value serving as a description
'autoGrow' => FALSE, // Used to specify if editor should grow in size as content is added. Defaults to TRUE.
);
* @endcode
*/
protected $defaultOptions = array(
'app' => NULL,
'key' => NULL,
'autoSaveKey' => NULL,
'attachIds' => NULL,
'minimize' => NULL,
'minimizeIcon' => 'fa fa-comment-o',
'allButtons' => FALSE,
'tags' => array(),
'autoGrow' => TRUE,
);
/**
* @brief The extension that owns this type of editor
*/
protected $extension;
/**
* @brief The uploader helper
*/
protected $uploader = NULL;
/**
* @brief Editor identifier
*/
protected $postKey;
/**
* Constructor
* Sets that the field is required if there is a minimum length and vice-versa
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
* @throws \OutOfBoundsException
*/
public function __construct()
{
$args = func_get_args();
$this->postKey = md5( $args[3]['autoSaveKey'] . ':' . session_id() );
/* Get our extension */
if ( !isset( $args[3]['allButtons'] ) or !$args[3]['allButtons'] )
{
$extensions = \IPS\Application::load( $args[3]['app'] )->extensions( 'core', 'EditorLocations' );
if ( !isset( $extensions[ $args[3]['key'] ] ) )
{
throw new \OutOfBoundsException( $args[3]['key'] );
}
$this->extension = $extensions[ $args[3]['key'] ];
}
/* Don't mimize if we have a value */
if ( $args[1] or \IPS\Request::i()->$args[0] or ( isset( \IPS\Request::i()->cookie['vle_editor'] ) and \IPS\Request::i()->cookie['vle_editor'] ) )
{
$args[3]['minimize'] = NULL;
}
/* Create the upload helper - if the form has been submitted, this has to be done before parent::__construct() as we need the uploader present for getValue(), but for views, we won't load until the editor is clicked */
$this->options = array_merge( $this->defaultOptions, $args[3] );
if ( $this->canAttach() )
{
if ( isset( \IPS\Request::i()->getUploader ) and \IPS\Request::i()->getUploader === $args[0] or ( isset( \IPS\Request::i()->postKey ) and \IPS\Request::i()->postKey === $this->postKey and isset( \IPS\Request::i()->deleteFile ) ) )
{
if ( $uploader = $this->getUploader( $args[0]) )
{
\IPS\Output::i()->sendOutput( $uploader->html() );
}
else
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->editorAttachmentsPlaceholder( $args[0], $this->postKey ) );
}
}
elseif( mb_strtolower( $_SERVER['REQUEST_METHOD'] ) == 'post' or !$this->options['minimize'] )
{
$this->uploader = $this->getUploader( $args[0] );
}
else
{
$this->uploader = FALSE;
}
}
/* Go */
call_user_func_array( 'parent::__construct', $args );
/* Include editor JS - but not if loaded via ajax. JS loader will handle that on demand */
if( !\IPS\Request::i()->isAjax() )
{
if ( \IPS\IN_DEV )
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, array( (string) \IPS\Http\Url::internal( 'applications/core/dev/ckeditor/ckeditor.js', 'none', NULL, array(), \IPS\Http\Url::PROTOCOL_RELATIVE ) ) );
}
else
{
\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;
}
/**
* Get HTML
*
* @param bool $raw If TRUE, will return without HTML any chrome
* @return string
*/
public function html( $raw=FALSE )
{
/* What buttons should we show? */
$allowed = NULL;
if ( !$this->options['allButtons'] )
{
$toolbars = json_decode( \IPS\Settings::i()->ckeditor_toolbars, TRUE );
$allowed = array();
foreach ( $toolbars as $device => $rows )
{
foreach ( $rows as $rowId => $data )
{
if ( is_array( $data ) )
{
$allowed[ $device ][ $rowId ]['name'] = $data['name'];
foreach ( $data['items'] as $k => $v )
{
if ( \IPS\Text\Parser::canUse( \IPS\Member::loggedIn(), $v, "{$this->options['app']}_{$this->options['key']}" ) )
{
$allowed[ $device ][ $rowId ]['items'][] = $v;
}
}
}
else
{
$allowed[ $device ][ $rowId ] = $data;
}
}
}
/* Can we use HTML? */
if ( $this->canUseHtml() === TRUE )
{
array_unshift( $allowed['desktop'][0]['items'], 'Source' );
array_unshift( $allowed['tablet'][0]['items'], 'Source' );
array_unshift( $allowed['phone'][0]['items'], 'Source' );
}
}
/* Clean resources in ACP */
$value = $this->value;
\IPS\Output::i()->parseFileObjectUrls( $value );
if( \IPS\Dispatcher::hasInstance() AND \IPS\Dispatcher::i()->controllerLocation == 'admin' )
{
$value = \IPS\Text\Parser::mungeResources( $value );
}
/* Show full uploader */
if ( $this->uploader )
{
$attachmentArea = $this->uploader->html();
}
/* Or show a loading icon where the uploader will go if the editor is minimized */
elseif ( $this->uploader === FALSE )
{
$attachmentArea = \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->editorAttachmentsMinimized( $this->name );
/* We still need to include plupload otherwise it won't work when they click in */
if ( \IPS\IN_DEV )
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'plupload/moxie.js', 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'plupload/plupload.dev.js', 'core', 'interface' ) );
}
else
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'plupload/plupload.full.min.js', 'core', 'interface' ) );
}
}
/* Or if the user can't attach, just show a bar */
else
{
$attachmentArea = \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->editorAttachmentsPlaceholder( $this->name, $this->postKey );
}
/* Display */
$template = $raw ? 'editorRaw' : 'editor';
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->$template( $this->name, $value, $this->options, $allowed, md5( $this->options['autoSaveKey'] . ':' . session_id() ), $attachmentArea, json_encode( static::getEmoticons() ), $this->options['tags'] );
}
/**
* Convert the value to something that will look okay for the no-JS fallback
*
* @param string $value Valie
* @return string
*/
public static function valueForNoJsFallback( $value )
{
$value = preg_replace( "/\<br(\s*)?\/?\>(\s*)?/i", "\n", html_entity_decode( $value ) );
$value = trim( $value );
$value = preg_replace( '/<\/p>\s*<p>/', "\n\n", $value );
if ( mb_substr( $value, 0, 3 ) === '<p>' )
{
$value = mb_substr( $value, 3 );
}
if ( mb_substr( $value, -4 ) === '</p>' )
{
$value = mb_substr( $value, 0, -4 );
}
return $value;
}
/**
* Get Value
*
* @return string
*/
public function getValue()
{
$error = NULL;
$value = parent::getValue();
/* If it was made without JS, convert linebreaks to <br>s */
$noJsKey = $this->name . '_noscript';
if ( isset( \IPS\Request::i()->$noJsKey ) AND !$value )
{
$value = nl2br( \IPS\Request::i()->$noJsKey, FALSE );
}
/* Or remove any invisible spaces used by the editor JS */
else
{
$value = preg_replace( '/[\x{200B}\x{2063}]/u', '', $value );
}
/* Parse value */
if ( $value )
{
$parser = $this->_getParser();
$parsedValue = $parser->parse( $value );
if( count( $parser->errors ) )
{
$error = new \DomainException( implode( '; ', array_unique( $parser->errors ) ) );
}
$value = $parsedValue;
}
/* Add any attachments that weren't inserted in the content */
if ( $this->uploader )
{
$inserts = array();
foreach ( static::getAttachments( NULL, $this->postKey ) as $attachment )
{
if ( !isset( $parser ) or !in_array( $attachment['attach_id'], $parser->mappedAttachments ) )
{
if ( $attachment['attach_is_image'] )
{
$value .= \IPS\Theme::i()->getTemplate( 'editor', 'core', 'global' )->attachedImage( $attachment['attach_location'], $attachment['attach_thumb_location'] ? $attachment['attach_thumb_location'] : $attachment['attach_location'], $attachment['attach_file'], $attachment['attach_id'] );
}
else
{
$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( count( $inserts ) )
{
\IPS\Db::i()->insert( 'core_attachments_map', $inserts, TRUE );
}
}
/* Remove abandoned attachments */
foreach ( \IPS\Db::i()->select( '*', 'core_attachments', array( array( 'attach_id NOT IN(?)', \IPS\Db::i()->select( 'DISTINCT attachment_id', 'core_attachments_map' ) ), array( 'attach_member_id=? AND attach_date<?', \IPS\Member::loggedIn()->member_id, time() - 86400 ) ) ) as $attachment )
{
try
{
\IPS\Db::i()->delete( 'core_attachments', array( 'attach_id=?', $attachment['attach_id'] ) );
\IPS\File::get( 'core_Attachment', $attachment['attach_location'] )->delete();
if ( $attachment['attach_thumb_location'] )
{
\IPS\File::get( 'core_Attachment', $attachment['attach_thumb_location'] )->delete();
}
}
catch ( \Exception $e ) { }
}
/* Throw any errors */
if ( $error )
{
$this->value = $value;
throw $error;
}
/* Return */
return $value;
}
/**
* Get parser object
*
* @return \IPS\Text\Parser
*/
protected function _getParser()
{
return new \IPS\Text\Parser( TRUE, ( $this->options['attachIds'] === NULL ? md5( $this->options['autoSaveKey'] ) : $this->options['attachIds'] ), NULL, "{$this->options['app']}_{$this->options['key']}", TRUE, !$this->canUseHtml(), method_exists( $this->extension, 'htmlPurifierConfig' ) ? array( $this->extension, 'htmlPurifierConfig' ) : NULL );
}
/**
* Can use HTML?
*
* @return bool
*/
protected function canUseHtml()
{
$canUseHtml = (bool) \IPS\Member::loggedIn()->group['g_dohtml'];
if ( $this->extension )
{
$extensionCanUseHtml = $this->extension->canUseHtml( \IPS\Member::loggedIn(), $this );
if ( $extensionCanUseHtml !== NULL )
{
$canUseHtml = $extensionCanUseHtml;
}
}
return $canUseHtml;
}
/**
* Can Attach?
*
* @return bool
*/
protected function canAttach()
{
$canAttach = ( \IPS\Member::loggedIn()->group['g_attach_max'] == '0' ) ? FALSE : TRUE;
if ( $this->extension )
{
$extensionCanAttach = $this->extension->canAttach( \IPS\Member::loggedIn(), $this );
if ( $extensionCanAttach !== NULL )
{
$canAttach = $extensionCanAttach;
}
}
return $canAttach;
}
/**
* Get uploader
*
* @param string $name Form name
* @return \IPS\Helpers\Form\Uploader
*/
protected function getUploader( $name )
{
/* Attachments enabled? */
if ( \IPS\Settings::i()->attach_allowed_types == 'none' )
{
return NULL;
}
/* 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 )
{
try
{
$file = \IPS\File::get( 'core_Attachment', $attachment['attach_location'] );
$file->attachmentThumbnailUrl = $attachment['attach_thumb_location'] ? \IPS\File::get( 'core_Attachment', $attachment['attach_thumb_location'] )->url : $file->url;
/* Reset the original filename based on the attachment record as filenames can be renamed if they contain special characters as AWS being the lowest common denominator cannot
handle special characters. @link https://community.invisionpower.com/4bugtrack/active-reports/408-wrong-name-for-uploaded-files-in-editor-r6519/ */
$file->originalFilename = $attachment['attach_file'];
$existing[ $attachment['attach_id'] ] = $file;
}
catch ( \Exception $e ) { }
}
/* Can we upload more? */
$maxTotalSize = array();
if ( \IPS\Member::loggedIn()->group['g_attach_max'] > 0 and \IPS\Member::loggedIn()->member_id )
{
$maxTotalSize[] = ( ( \IPS\Member::loggedIn()->group['g_attach_max'] * 1024 ) - \IPS\Db::i()->select( 'SUM(attach_filesize)', 'core_attachments', array( 'attach_member_id=?', \IPS\Member::loggedIn()->member_id ) )->first() ) / 1048576;
}
if ( \IPS\Member::loggedIn()->group['g_attach_per_post'] )
{
$maxTotalSize[] = \IPS\Member::loggedIn()->group['g_attach_per_post'] / 1024;
}
$maxTotalSize = count( $maxTotalSize ) ? min( $maxTotalSize ) : NULL;
/* Create the uploader */
if ( $maxTotalSize === NULL or $maxTotalSize > 0 )
{
$maxTotalSize = ( !is_null( $maxTotalSize ) ) ? round( $maxTotalSize, 2 ) : NULL;
$postKey = $this->postKey;
$options = array(
'template' => 'core.attachments.fileItem',
'multiple' => TRUE,
'postKey' => $this->postKey,
'storageExtension' => 'core_Attachment',
'retainDeleted' => TRUE,
'totalMaxSize' => $maxTotalSize,
'callback' => function( $file ) use ( $postKey )
{
\IPS\Db::i()->delete( 'core_files_temp', array( 'contents=?', (string) $file ) );
$attachment = $file->makeAttachment( $postKey );
return $attachment['attach_id'];
}
);
if ( \IPS\Settings::i()->attach_allowed_types == 'all' and \IPS\Settings::i()->attach_allowed_extensions )
{
$options['allowedFileTypes'] = explode( ',', \IPS\Settings::i()->attach_allowed_extensions );
}
if ( \IPS\Settings::i()->attachment_resample_size )
{
$maxImageSizes = explode( 'x', \IPS\Settings::i()->attachment_resample_size );
if ( $maxImageSizes[0] and $maxImageSizes[1] )
{
$options['image'] = array( 'maxWidth' => $maxImageSizes[0], 'maxHeight' => $maxImageSizes[1] );
if ( \IPS\Settings::i()->attach_allowed_types != 'images' )
{
$options['image']['optional'] = TRUE;
}
}
elseif ( \IPS\Settings::i()->attach_allowed_types == 'images' )
{
$options['image'] = TRUE;
}
}
elseif ( \IPS\Settings::i()->attach_allowed_types == 'images' )
{
$options['image'] = TRUE;
}
$uploader = new \IPS\Helpers\Form\Upload( str_replace( array( '[', ']' ), '_', $name ) . '_upload', $existing, FALSE, $options );
$uploader->template = array( \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' ), 'editorAttachments' );
/* Handle delete calls */
if ( isset( \IPS\Request::i()->postKey ) and \IPS\Request::i()->postKey == $this->postKey and isset( \IPS\Request::i()->deleteFile ) )
{
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'] ) ) ) );
$otherMaps = \IPS\Db::i()->select( 'COUNT(*)', 'core_attachments_map', array( 'attachment_id=?', $attachment['attach_id'] ) )->first();
if ( !$otherMaps )
{
\IPS\Db::i()->delete( 'core_attachments', array( 'attach_id=?', $attachment['attach_id'] ) );
\IPS\File::get( 'core_Attachment', $attachment['attach_location'] )->delete();
if ( $attachment['attach_thumb_location'] )
{
\IPS\File::get( 'core_Attachment', $attachment['attach_thumb_location'] )->delete();
}
}
}
catch ( \Exception $e ) { }
\IPS\Output::i()->json( 'OK' );
}
}
else
{
$uploader = NULL;
}
/* Return */
return $uploader;
}
/**
* Fetch the emoticons - abstracted to a static method for caching
*
* @return array
*/
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') );
foreach( $emoticons as $key => $emoticon )
{
$emoticons[ $key ] = (string) \IPS\File::get( "core_Emoticons", $emoticon )->url;
}
\IPS\Data\Store::i()->emoticons = $emoticons;
}
return \IPS\Data\Store::i()->emoticons;
}
/**
* Fetch existing attachments - abstracted to a static method for caching
*
* @param string $existing Existing attachments limiter
* @param string $postKey Post key
* @return array
*/
public static function getAttachments( $existing, $postKey )
{
static $attachments = array();
$_key = md5( $existing . $postKey );
if( isset( $attachments[ $_key ] ) )
{
return $attachments[ $_key ];
}
$attachments[ $_key ] = \IPS\Db::i()->select( '*', 'core_attachments', array( $existing . 'attach_post_key=?', $postKey ) );
return $attachments[ $_key ];
}
}
+84
View File
@@ -0,0 +1,84 @@
<?php
/**
* @brief Email input class 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 11 Mar 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;
}
/**
* Email input class for Form Builder
*/
class _Email extends Text
{
/**
* @brief Default Options
* @code
$childDefaultOptions = array(
'accountEmail' => TRUE, // If TRUE, additional checks will be performed to ensure provided email address is acceptable for use on a user's account. If an \IPS\Member object, that member will be excluded
);
* @endcode
*/
public $childDefaultOptions = array(
'accountEmail' => FALSE,
);
/**
* Validate
*
* @throws \InvalidArgumentException
* @throws \DomainException
* @return TRUE
*/
public function validate()
{
parent::validate();
/* Check it's generally an acceptable email */
if ( $this->value !== '' and filter_var( $this->value, FILTER_VALIDATE_EMAIL ) === FALSE )
{
throw new \InvalidArgumentException('form_email_bad');
}
/* If it's for a user account, do additional checks */
if ( $this->options['accountEmail'] )
{
/* Check if it exists */
foreach ( \IPS\Login::handlers( TRUE ) as $k => $handler )
{
if ( $handler->emailIsInUse( $this->value, ( $this->options['accountEmail'] instanceof \IPS\Member ) ? $this->options['accountEmail'] : NULL ) === TRUE )
{
if ( \IPS\Member::loggedIn()->isAdmin() )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('member_email_exists_admin', FALSE, array( 'sprintf' => array( $k ) ) ) );
}
else
{
throw new \DomainException( 'member_email_exists' );
}
}
}
/* Check it's not banned */
foreach ( \IPS\Db::i()->select( 'ban_content', 'core_banfilters', array( "ban_type=?", 'email' ) ) as $bannedEmail )
{
if ( preg_match( '/^' . str_replace( '\*', '.*', preg_quote( $bannedEmail, '/' ) ) . '$/i', $this->value ) )
{
throw new \DomainException( 'form_email_banned' );
}
}
}
}
}
+566
View File
@@ -0,0 +1,566 @@
<?php
/**
* @brief 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 18 Feb 2013
* @version SVN_VERSION_NUMBER
*/
namespace IPS\Helpers;
/* 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;
}
/**
* Form Builder
*/
class _Form
{
/**
* @brief Form ID
*/
public $id = '';
/**
* @brief Action URL
*/
public $action = '';
/**
* @brief Input Elements HTML
*/
public $elements = array();
/**
* @brief Tabs
*/
protected $tabs = array();
/**
* @brief Current Tab we're adding elements to
*/
protected $currentTab = '';
/**
* @brief Active tab
*/
public $activeTab = NULL;
/**
* @brief Additional class for tables
*/
protected $tabClasses = array();
/**
* @brief Sidebar
*/
public $sidebar = array();
/**
* @brief CSS Class(es)
*/
public $class = 'ipsForm_horizontal';
/**
* @brief Generic Form Error
*/
public $error = '';
/**
* @brief Hidden Values
*/
public $hiddenValues = array();
/**
* @brief Extra attributes for <form> tag
*/
public $attributes = array();
/**
* @brief Action Buttons
*/
public $actionButtons = array();
/**
* @brief If form has upload field, the maximum size (Needed to add enctype="multipart/form-data")
* @ntoe Only actually affects no-JS uploads, Plupload does it's own thing
*/
protected $uploadField = FALSE;
/**
* @brief If enabled, and this form is submitted in a modal popup window, the next screen will be shown within the modal popup
*/
public $ajaxOutput = FALSE;
/**
* @brief Is the form using tabs with icons
*/
protected $iconTabs = FALSE;
/**
* @brief Copy Button URL
*/
public $copyButton = NULL;
/**
* Constructor
*
* @param string $id Form ID
* @param string $submitLang Language string for submit button
* @param \IPS\Http\Url|NULL $action Action URL
* @param array $attributes Extra attributes for <form> tag
* @return void
*/
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->attributes = $attributes;
if( $submitLang )
{
$this->actionButtons[] = \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->button( $submitLang, 'submit', null, 'ipsButton ipsButton_primary', array( 'accesskey' => 's' ) );
}
$this->hiddenValues['csrfKey'] = \IPS\Session::i()->csrfKey;
$potentialMaxUploadValues = array();
if( (float) ini_get('upload_max_filesize') > 0 )
{
$potentialMaxUploadValues[] = (float) ini_get('upload_max_filesize');
}
if( (float) ini_get('post_max_size') > 0 )
{
$potentialMaxUploadValues[] = (float) ini_get('post_max_size');
}
if( (float) ini_get('memory_limit') > 0 )
{
$potentialMaxUploadValues[] = (float) ini_get('memory_limit');
}
$this->uploadField = min( $potentialMaxUploadValues ) * 1048576;
}
/**
* Add Tab
*
* @param string $lang Language key
* @return void
*/
public function addTab( $lang, $icon=NULL, $blurbLang=NULL, $css=NULL )
{
$this->tabs[$lang]['title'] = $lang;
$this->currentTab = $lang;
if ( $this->activeTab === NULL )
{
$this->activeTab = $lang;
}
if ( $icon )
{
$this->tabs[$lang]['icon'] = $icon;
$this->iconTabs = TRUE;
}
if ( $blurbLang )
{
$this->elements[ $this->currentTab ][] = \IPS\Theme::i()->getTemplate( 'forms', 'core' )->blurb( $blurbLang );
}
if ( $css )
{
$this->tabClasses[ $this->currentTab ] = $css;
}
}
/**
* Add Header
*
* @param string $lang Language key
* @return void
*/
public function addHeader( $lang )
{
$this->elements[ $this->currentTab ][] = \IPS\Theme::i()->getTemplate( 'forms', 'core' )->header( $lang, "{$this->id}_header_{$lang}" );
}
/**
* Add Seperator
*
* @return void
*/
public function addSeparator()
{
$this->elements[ $this->currentTab ][] = \IPS\Theme::i()->getTemplate( 'forms', 'core', 'front' )->seperator();
}
/**
* Add Message Row
*
* @param string $lang Language key or formatted string to display
* @param string $css Custom CSS class(es) to apply
* @param bool $parse Set this to false if the language string passed is already formatted for display
* @param string $_id HTML ID
* @return void
*/
public function addMessage( $lang, $css='', $parse=TRUE, $_id=NULL )
{
if ( !$_id )
{
$_id = "{$this->id}_header_" . md5( $lang );
if( $parse === FALSE )
{
$_id = preg_replace( "/[^a-zA-Z0-9_]/", '_', $_id );
}
}
$this->elements[ $this->currentTab ][] = \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->message( $lang, $_id, $css, $parse );
}
/**
* Add Html
*
* @param string $html HTML to add
* @return void
*/
public function addHtml( $html )
{
$this->elements[ $this->currentTab ][] = $html;
}
/**
* Add Sidebar
*
* @param string $contents Contents
* @return void
*/
public function addSidebar( $contents )
{
$this->sidebar[ $this->currentTab ] = $contents;
}
/**
* Add Matrix
*
* @param mixed $name Name to identify matrix
* @param \IPS\Helpers\Form\Matrix $matrix The Matrix
* @return void
*/
public function addMatrix( $name, $matrix )
{
//$matrix->manageable = FALSE;
$this->tabClasses[ $this->currentTab ] = 'ipsMatrix';
$this->elements[ $this->currentTab ][ $name ] = $matrix;
}
/**
* Add Button
*
* @param string $lang Language key
* @param string $type 'link', 'button' or 'submit'
* @param string $href If type is 'link', the target
* @param string $class CSS class(es) to applys
* @param array $attributes Attributes to apply
* @return void
*/
public function addButton( $lang, $type, $href=NULL, $class='', $attributes=array() )
{
$this->actionButtons[] = ' ' . \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->button( $lang, $type, $href, $class, $attributes );
}
/**
* Add Dummy Row
*
* @param string $langKey Language key
* @param string $value Value
* @param string $desc Field description
* @param string $warning Field warning
* @param string $id Element ID
* @return void
*/
public function addDummy( $langKey, $value, $desc='', $warning='', $id='' )
{
$this->elements[ $this->currentTab ][] = \IPS\Theme::i()->getTemplate( 'forms', 'core' )->row( \IPS\Member::loggedIn()->language()->addToStack( $langKey ), $value, $desc, $warning, FALSE, NULL, NULL, NULL, $id );
}
/**
* Add Input
*
* @param \IPS\Helpers\Form\Abstract $input Form element to add
* @param string|NULL $after The key of element to insert after
* @param string|NULL $tab The tab to insert onto
* @return void
*/
public function add( $input, $after=NULL, $tab=NULL )
{
$tab = $tab ?: $this->currentTab;
if ( $after )
{
$elements = array();
foreach ( $this->elements[ $tab ] as $key => $element )
{
$elements[ $key ] = $element;
if ( $key === $after )
{
$elements[ $input->name ] = $input;
}
}
$this->elements[ $tab ] = $elements;
}
else
{
$this->elements[ $tab ][ $input->name ] = $input;
}
/* If it's a captcha field, we need to add a hidden value */
if ( $input instanceof Form\Captcha )
{
$this->hiddenValues[ $input->name ] = TRUE;
}
}
/**
* Get HTML
*
* @return string
*/
public function __toString()
{
try
{
$html = array();
$errorTabs = array();
foreach ( $this->elements as $tab => $elements )
{
$html[ $tab ] = '';
foreach ( $elements as $k => $element )
{
if ( $element instanceof Form\Matrix )
{
$html[ $tab ] .= \IPS\Theme::i()->getTemplate( 'forms', 'core' )->emptyRow( $element->nested(), $k );
continue;
}
if ( !is_string( $element ) and $element->error )
{
$errorTabs[] = $tab;
}
$html[ $tab ] .= ( $element instanceof \IPS\Helpers\Form\FormAbstract ) ? $element->rowHtml( $this ) : (string) $element;
}
}
return \IPS\Theme::i()->getTemplate( 'forms', 'core' )->template( $this->id, $this->action, $html, $this->activeTab, $this->error, $errorTabs, $this->hiddenValues, $this->actionButtons, $this->uploadField, $this->sidebar, $this->tabClasses, $this->class, $this->attributes, $this->tabs, $this->iconTabs );
}
catch ( \Exception $e )
{
\IPS\IPS::exceptionHandler( $e );
}
}
/**
* Get HTML using custom template
*
* @param callback $template The template to use
* @return string
*/
public function customTemplate( $template )
{
$args = func_get_args();
if ( count( $args ) > 1 )
{
array_shift( $args );
}
else
{
$args = array();
}
$errorTabs = array();
foreach ( $this->elements as $tab => $elements )
{
$html[ $tab ] = '';
foreach ( $elements as $k => $element )
{
if ( $element instanceof Form\Matrix )
{
$html[ $tab ] .= \IPS\Theme::i()->getTemplate( 'forms', 'core' )->emptyRow( $element->nested(), $k );
continue;
}
if ( !is_string( $element ) and $element->error )
{
$errorTabs[] = $tab;
}
$html[ $tab ] .= ( $element instanceof \IPS\Helpers\Form\FormAbstract ) ? $element->rowHtml( $this ) : (string) $element;
}
}
return call_user_func_array( $template, array_merge( $args, array( $this->id, $this->action, $this->elements, $this->hiddenValues, $this->actionButtons, $this->uploadField, $this->class, $this->attributes, $this->sidebar, $this, $errorTabs ) ) );
}
/**
* Get submitted values
*
* @param bool $stringValues If true, all values will be returned as strings
* @return array|FALSE Array of field values or FALSE if the form has not been submitted or if there were validation errors
*/
public function values( $stringValues=FALSE )
{
$values = array();
$name = "{$this->id}_submitted";
$uploadFieldNames = array();
/* Did we submit the form? */
if( isset( \IPS\Request::i()->$name ) and \IPS\Request::i()->csrfKey === \IPS\Session::i()->csrfKey )
{
/* Loop elements */
foreach ( $this->elements as $elements )
{
foreach ( $elements as $_name => $element )
{
/* If it's a matrix, populate the values from it */
if ( ( $element instanceof Form\Matrix ) )
{
$values[ $_name ] = $element->values( TRUE );
continue;
}
/* If it's not a form element, skip */
if ( !( $element instanceof Form\FormAbstract ) )
{
continue;
}
/* Make sure we have a value (someone might try to be sneaky and remove the HTML from the form before submitting) */
if ( !$element->valueSet )
{
$element->setValue( FALSE, TRUE );
}
/* If it's an upload field, we'll need to remember the name */
if ( ( $element instanceof Form\Upload ) )
{
$uploadFieldNames[] = $element->name;
}
/* If it has an error, set it and return */
if( !empty( $element->error ) )
{
\IPS\Output::i()->httpHeaders['X-IPS-FormError'] = "true";
return FALSE;
}
/* If it's a poll, save it */
if ( $element instanceof Form\Poll and $element->value !== NULL )
{
$element->value->save();
}
/* If it's a social group, save it */
if ( $element instanceof \IPS\Helpers\Form\SocialGroup )
{
$element->saveValue();
}
/* If the element has requested the form doesn't submit, return */
if ( $element->reloadForm === TRUE )
{
\IPS\Output::i()->httpHeaders['X-IPS-FormNoSubmit'] = "true";
return FALSE;
}
/* Still here? Then add the value */
$values[ $element->name ] = $stringValues ? $element::stringValue( $element->value ) : $element->value;
}
}
foreach ( $this->hiddenValues as $key => $value )
{
if( $key != 'csrfKey' )
{
$values[$key] = $value;
}
}
/* If we've reached this point, all fields have acceptable values. If we're just checking that, return that it's okay */
if ( \IPS\Request::i()->isAjax() and \IPS\Request::i()->ajaxValidate )
{
if ( $this->ajaxOutput === TRUE )
{
\IPS\Output::i()->httpHeaders['X-IPS-FormNoSubmit'] = "true";
}
else
{
\IPS\Output::i()->json( array( 'validate' => true ) );
}
}
/* At this point we are about to return the values. Any uploaded files are now the responsibility of the controller, so release the hold on them */
foreach ( $uploadFieldNames as $name )
{
\IPS\Db::i()->delete( 'core_files_temp', array( 'upload_key=?', md5( $name . session_id() ) ) );
}
/* And return */
return $values;
}
/* Nope, return FALSE */
else
{
return FALSE;
}
}
/**
* Save values to settings table
*
* @return bool
*/
public function saveAsSettings( $values=NULL )
{
if ( !$values )
{
$values = $this->values( TRUE );
}
if ( $values )
{
foreach ( $values as $k => $v )
{
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => $v ), array( 'conf_key=?', $k ) );
\IPS\Settings::i()->$k = $v;
}
unset( \IPS\Data\Store::i()->settings );
return TRUE;
}
return FALSE;
}
/**
* Flood Check
*
* @return void
*/
public static function floodCheck()
{
if ( \IPS\Settings::i()->flood_control and !\IPS\Member::loggedIn()->group['g_avoid_flood'] )
{
if ( time() - \IPS\Member::loggedIn()->member_last_post < \IPS\Settings::i()->flood_control )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('error_flood_control', FALSE, array( 'sprintf' => array( time() - \IPS\Member::loggedIn()->member_last_post ) ) ) );
}
}
}
}
+401
View File
@@ -0,0 +1,401 @@
<?php
/**
* @brief Abstract Class for input types 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 18 Feb 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;
}
/**
* Abstract Class for input types for Form Builder
*/
abstract class _FormAbstract
{
/**
* @brief Name
*/
protected $_name = '';
/**
* @brief Label
*/
public $label = NULL;
/**
* @brief Default Value
*/
public $defaultValue = NULL;
/**
* @brief Value
*/
public $value = NULL;
/**
* @brief Unformatted Value
*/
public $unformatted = NULL;
/**
* @brief Required?
*/
public $required = FALSE;
/**
* @brief Appears Required?
*/
public $appearRequired = FALSE;
/**
* @brief Type-Specific Options
*/
public $options = array();
/**
* @brief Default Options
*/
protected $defaultOptions = array(
'disabled' => FALSE,
);
/**
* @brief Custom Validation Code
*/
protected $customValidationCode;
/**
* @brief Prefix (HTML that displays before the input box)
*/
public $prefix;
/**
* @brief Suffix (HTML that displays after the input box)
*/
public $suffix;
/**
* @brief HTML ID
*/
public $htmlId = NULL;
/**
* @brief Validation Error
*/
public $error = NULL;
/**
* @brief Reload form flag (Can be used by JS disabled fall backs to alter form content on submit)
*/
public $reloadForm = FALSE;
/**
* @brief Warning
*/
public $warningBox = NULL;
/**
* @brief Value has been set?
*/
public $valueSet = FALSE;
/**
* 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 )
{
$this->_name = $name;
$this->required = is_null( $required ) ? FALSE : $required;
$this->appearRequired = is_null( $required ) ? TRUE : $required;
$this->options = array_merge( $this->defaultOptions, $options );
$this->customValidationCode = $customValidationCode;
$this->prefix = $prefix;
$this->suffix = $suffix;
$this->defaultValue = $defaultValue;
$this->htmlId = preg_replace( "/[^a-zA-Z0-9\-_]/", "_", $id );
$this->setValue( TRUE );
}
/**
* Set the value of the element
*
* @param bool $initial Whether this is the initial call or not. Do not reset default values on subsequent calls.
* @param bool $force Set the value even if one was not submitted (done on the final validation when getting values)?
* @return void
*/
public function setValue( $initial=FALSE, $force=FALSE )
{
$name = $this->name;
$unlimitedKey = "{$name}_unlimited";
$nullKey = "{$name}_null";
if( $force or ( mb_substr( $name, 0, 8 ) !== '_new_[x]' and ( mb_strpos( $name, '[' ) ? \IPS\Request::i()->valueFromArray( $name ) !== NULL : ( isset( \IPS\Request::i()->$name ) OR isset( \IPS\Request::i()->$unlimitedKey ) OR isset( \IPS\Request::i()->$nullKey ) ) ) ) )
{
try
{
$this->value = $this->getValue();
$this->unformatted = $this->value;
$this->value = $this->formatValue();
$this->validate();
$this->valueSet = TRUE;
}
catch ( \LogicException $e )
{
$this->error = $e->getMessage();
}
}
else
{
if( $initial )
{
$this->value = $this->defaultValue;
try
{
$this->value = $this->formatValue();
}
catch ( \LogicException $e )
{
$this->error = $e->getMessage();
}
}
}
}
/**
* Magic get method
*
* @param string $property Property requested
* @return mixed
*/
public function __get( $property )
{
if( $property === 'name' )
{
return $this->_name;
}
return NULL;
}
/**
* Magic set method
*
* @param string $property Property requested
* @param mixed $value Value to set
* @return void
* @note We are operating on the 'name' property so that if an element's name is reset after the element is initialized we can reinitialize the value
*/
public function __set( $property, $value )
{
if( $property === 'name' )
{
$this->_name = $value;
$this->setValue();
}
}
/**
* Get HTML
*
* @return string
*/
public function __toString()
{
return $this->rowHtml();
}
/**
* Get HTML
*
* @return string
*/
public function rowHtml( $form=NULL )
{
try
{
if ( $this->label )
{
$label = $this->label;
}
else
{
$label = $this->name;
if ( isset( $this->options['labelSprintf'] ) )
{
$label = \IPS\Member::loggedIn()->language()->addToStack( $label, FALSE, array( 'sprintf' => $this->options['labelSprintf'] ) );
}
else if ( isset( $this->options['labelHtmlSprintf'] ) )
{
$label = \IPS\Member::loggedIn()->language()->addToStack( $label, FALSE, array( 'htmlsprintf' => $this->options['labelHtmlSprintf'] ) );
}
else
{
$label = \IPS\Member::loggedIn()->language()->addToStack( $label );
}
}
$desc = $this->name . '_desc';
$template = \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->rowDesc( $label, $this->html(), $this->appearRequired, $this->error, $this->prefix, $this->suffix, $this->htmlId ?: ( $form ? "{$form->id}_{$this->name}" : NULL ), $this, $form );
$desc = \IPS\Member::loggedIn()->language()->addToStack( $desc, FALSE, array( 'returnBlank' => TRUE, 'htmlsprintf' => (string) $template, 'flipsprintf' => true ) );
if ( $this->warningBox )
{
$warning = $this->warningBox;
}
else
{
$warning = $this->name . '_warning';
$template = \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->rowWarning( $label, $this->html(), $this->appearRequired, $this->error, $this->prefix, $this->suffix, $this->htmlId ?: ( $form ? "{$form->id}_{$this->name}" : NULL ), $this, $form );
$warning = \IPS\Member::loggedIn()->language()->addToStack( $warning, FALSE, array( 'returnBlank' => TRUE, 'htmlsprintf' => (string) $template, 'flipsprintf' => true ) );
}
/* Some form helpers might want to allow nested and outer suffixes */
$html = $this->html();
if( array_key_exists( 'endSuffix', $this->options ) )
{
$this->suffix = $this->options['endSuffix'];
}
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 )
{
if ( \IPS\IN_DEV )
{
echo '<pre>';
print_r( $e );
exit;
}
throw $e;
}
}
/**
* Get Value
*
* @return mixed
*/
public function getValue()
{
$name = $this->name;
$value = ( mb_strpos( $name, '[' ) OR ( isset( $this->options['multiple'] ) AND $this->options['multiple'] === TRUE ) ) ? \IPS\Request::i()->valueFromArray( $name ) : \IPS\Request::i()->$name;
if( isset( $this->options['disabled'] ) AND $this->options['disabled'] === TRUE AND $value === NULL )
{
$value = $this->defaultValue;
}
return $value;
}
/**
* Format Value
*
* @return mixed
*/
public function formatValue()
{
return $this->value;
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
if( $this->value === '' and $this->required )
{
throw new \InvalidArgumentException('form_required');
}
if ( \IPS\Settings::i()->getFromConfGlobal('sql_utf8mb4') !== TRUE )
{
if ( !static::utf8mb4Check( $this->value ) )
{
throw new \DomainException( 'form_multibyte_unicode' );
}
}
if( $this->customValidationCode !== NULL )
{
call_user_func( $this->customValidationCode, $this->value );
}
return TRUE;
}
/**
* Check if a value is okay to be stored in a non-utf8mb4 database
*
* @param mixed $value The value
* @return bool
*/
protected static function utf8mb4Check( $value )
{
if ( is_array( $value ) )
{
foreach ( $value as $_value )
{
if ( !static::utf8mb4Check( $_value ) )
{
return FALSE;
}
}
}
elseif ( is_string( $value ) )
{
return (bool) !preg_match( '/[\x{10000}-\x{10FFFF}]/u', $value );
}
return TRUE;
}
/**
* String Value
*
* @param mixed $value The value
* @return string
*/
public static function stringValue( $value )
{
if ( is_array( $value ) )
{
return implode( ',', array_map( function( $v )
{
if ( is_object( $v ) )
{
return (string) $v;
}
return $v;
}, $value ) );
}
return (string) $value;
}
}
+100
View File
@@ -0,0 +1,100 @@
<?php
/**
* @brief FTP Details input class 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
* @subpackage Nexus
* @since 17 Apr 2014
* @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;
}
/**
* FTP input class for Form Builder
*/
class _Ftp extends \IPS\Helpers\Form\FormAbstract
{
/**
* @brief Default Options
* @code
'validate' => TRUE // Should details be validated?
);
* @endcode
*/
protected $defaultOptions = array(
'validate' => TRUE,
);
/**
* Get HTML
*
* @return string
*/
public function html()
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'global_forms.js', 'nexus', 'global' ) );
$value = is_array( $this->value ) ? $this->value : json_decode( \IPS\Text\Encrypt::fromTag( $this->value )->decrypt(), TRUE );
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->ftp( $this->name, $value );
}
/**
* Validate
*
* @return string
*/
public function validate()
{
if ( $this->value['server'] or $this->value['un'] or $this->value['pw'] )
{
if ( $this->options['validate'] )
{
try
{
if ( $this->value['protocol'] == 'sftp' )
{
$ftp = new \IPS\Ftp\Sftp( $this->value['server'], $this->value['un'], $this->value['pw'], $this->value['port'] );
}
else
{
$ftp = new \IPS\Ftp( $this->value['server'], $this->value['un'], $this->value['pw'], $this->value['port'], ( $this->value['protocol'] == 'ssl_ftp' ) );
}
$ftp->chdir( $this->value['path'] );
}
catch ( \IPS\Ftp\Exception $e )
{
throw new \DomainException( 'ftp_err-' . $e->getMessage() );
}
catch ( \BadMethodCallException $e )
{
// This means we tried an SFTP connection, but the server doesn't support it. We'll have to assume it's correct
}
}
}
elseif ( $this->required )
{
throw new \DomainException( 'form_required' );
}
}
/**
* String Value
*
* @param mixed $value The value
* @return string
*/
public static function stringValue( $value )
{
return \IPS\Text\Encrypt::fromPlaintext( json_encode( $value ) )->tag();
}
}
+130
View File
@@ -0,0 +1,130 @@
<?php
/**
* @brief Key/Value input class 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 18 Feb 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;
}
/**
* Key/Value input class for Form Builder
*/
class _KeyValue extends FormAbstract
{
/**
* @brief Default Options
* @see \IPS\Helpers\Form\Date::$defaultOptions
* @code
$defaultOptions = array(
'start' => array( ... ),
'end' => array( ... ),
);
* @endcode
*/
protected $defaultOptions = array(
'key' => array(
'minLength' => NULL,
'maxLength' => NULL,
'size' => 20,
'disabled' => FALSE,
'autocomplete' => NULL,
'placeholder' => NULL,
'regex' => NULL,
'nullLang' => NULL,
'accountUsername' => FALSE,
'trim' => TRUE,
),
'value' => array(
'minLength' => NULL,
'maxLength' => NULL,
'size' => NULL,
'disabled' => FALSE,
'autocomplete' => NULL,
'placeholder' => NULL,
'regex' => NULL,
'nullLang' => NULL,
'accountUsername' => FALSE,
'trim' => TRUE,
),
);
/**
* @brief Key Object
*/
public $keyField = NULL;
/**
* @brief Value Object
*/
public $valueField = NULL;
/**
* Constructor
* Creates the two date objects
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
*/
public function __construct( $name, $defaultValue=NULL, $required=FALSE, $options=array() )
{
$options = array_merge( $this->defaultOptions, $options );
$this->keyField = new \IPS\Helpers\Form\Text( "{$name}[key]", isset( $defaultValue['key'] ) ? $defaultValue['key'] : NULL, FALSE, isset( $options['key'] ) ? $options['key'] : array() );
$this->valueField = new \IPS\Helpers\Form\Text( "{$name}[value]", isset( $defaultValue['value'] ) ? $defaultValue['value'] : NULL, FALSE, isset( $options['value'] ) ? $options['value'] : array() );
call_user_func_array( 'parent::__construct', func_get_args() );
}
/**
* Format Value
*
* @return array
*/
public function formatValue()
{
return array(
'key' => $this->keyField->formatValue(),
'value' => $this->valueField->formatValue()
);
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->keyValue( $this->keyField->html(), $this->valueField->html() );
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @throws \LengthException
* @return TRUE
*/
public function validate()
{
$this->keyField->validate();
$this->valueField->validate();
if( $this->customValidationCode !== NULL )
{
call_user_func( $this->customValidationCode, $this->value );
}
}
}
+344
View File
@@ -0,0 +1,344 @@
<?php
/**
* @brief Matrix 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 26 Feb 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;
}
/**
* Matrix Builder
* @code
$matrix = new \IPS\Helpers\Form\Matrix;
$matrix->columns = array(
'foo' => function( $key, $value, $data )
{
return new \IPS\Helpers\Form\Text( $key, $value );
},
//...
);
$matrix->rows = array(
0 => array(
'foo' => TRUE,
// ...
),
// ...
)
if ( $values = $matrix->values() )
{
}
\IPS\Output::i()->output = $matrix;
* @endcode
*/
class _Matrix extends \IPS\Helpers\Form
{
/**
* @brief Input Elements array
*/
public $elements = NULL;
/**
* @brief Columns array
*/
public $columns = array();
/**
* @brief Widths
*/
public $widths = array();
/**
* @brief Columns to have "check all" checkboxes
*/
public $checkAlls = array();
/**
* @brief Should rows have all/none toggles?
*/
public $checkAllRows = FALSE;
/**
* @brief Rows array
*/
public $rows = array();
/**
* @brief Manageable? (Rows can be added and deleted)
*/
public $manageable = TRUE;
/**
* @brief Added rows
* @see \IPS\Helpers\Form\Matrix::values
*/
public $addedRows = array();
/**
* @brief Changed rows
* @see \IPS\Helpers\Form\Matrix::values
*/
public $changedRows = array();
/**
* @brief Removed rows
* @see \IPS\Helpers\Form\Matrix::elements
*/
public $removedRows = array();
/**
* @brief Prefix to add to the language keys used for column headers
*/
public $langPrefix = '';
/**
* @brief Classnames to add to the table within the matrix
*/
public $classes = array();
/**
* @brief Show tooltips in each cell?
*/
public $showTooltips = FALSE;
/**
* Get HTML
*
* @return string
*/
public function __toString()
{
try
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->matrix( $this->id, array_keys( $this->columns ), $this->elements(), $this->action, $this->hiddenValues, $this->actionButtons, $this->langPrefix, $this->calculateWidths(), $this->manageable, $this->checkAlls, $this->checkAllRows, $this->classes, $this->showTooltips );
}
catch ( \Exception $e )
{
\IPS\IPS::exceptionHandler( $e );
}
}
/**
* Get Nested HTML
*
* @return string
*/
public function nested()
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->matrixNested( $this->id, array_keys( $this->columns ), $this->elements(), $this->action, $this->hiddenValues, $this->actionButtons, $this->langPrefix, $this->calculateWidths(), $this->manageable, $this->checkAlls, $this->checkAllRows, $this->classes, $this->showTooltips );
}
/**
* Calculate widths
*
* @return array
*/
protected function calculateWidths()
{
$widths = $this->widths;
$width = ( ( 100 - array_sum( $this->widths ) ) / count( $this->columns ) );
foreach ( array_keys( $this->columns ) as $c )
{
if ( !isset( $widths[ $c ] ) )
{
$widths[ $c ] = $width;
}
}
return $widths;
}
/**
* Get elements
*
* @param $bool Get values?
* @return array
*/
public function elements( $getValues=TRUE )
{
if ( $this->elements === NULL )
{
/* Stuff about our form */
$name = "{$this->id}_submitted";
$matrixName = "{$this->id}_matrixRows";
$matrixValues = \IPS\Request::i()->$matrixName;
/* Loop our defined rows */
$this->elements = array();
foreach ( $this->rows as $rowId => $data )
{
/* Have we deleted this row? */
$deleteKey = "{$rowId}_delete";
if ( $this->manageable and isset( \IPS\Request::i()->$name ) and ( isset( \IPS\Request::i()->$deleteKey ) or !isset( $matrixValues[ $rowId ] ) or !$matrixValues[ $rowId ] ) )
{
$this->removedRows[] = $rowId;
continue;
}
/* Build the row */
$this->elements[ $rowId ] = $this->buildRow( $rowId, $data );
}
/* Create blank row */
if ( $this->manageable )
{
$blankRow = $this->buildRow( "_new_[x]", NULL );
}
/* Look for added ones */
if ( isset( \IPS\Request::i()->_new_ ) )
{
$i = 1;
foreach ( \IPS\Request::i()->_new_ as $newId => $data )
{
$added = TRUE;
if ( $newId === 'x_unlimited' )
{
continue;
}
elseif ( $newId === 'x' )
{
$added = FALSE;
foreach ( $data as $k => $v )
{
if ( isset( $blankRow[$k] ) and $v and $v !== $blankRow[$k]->value )
{
$added = TRUE;
$blankRow[$k] = $blankRow[$k]->getValue();
}
}
}
if ( $added )
{
$this->elements["_new_[{$i}]"] = $this->buildRow( "_new_[{$i}]", $data );
$this->addedRows[] = "_new_[{$i}]";
$i++;
}
}
}
/* Add blank one */
if ( $this->manageable and $getValues )
{
$this->elements["_new_[x]"] = $blankRow;
}
}
return $this->elements;
}
/**
* Build Row
*
* @param mixed $rowId Row identifier
* @param array $data Values
* @return array
*/
protected function buildRow( $rowId, $data )
{
$row = array();
if ( is_string( $data ) )
{
return $data;
}
foreach ( $this->columns as $columnName => $columnData )
{
$inputName = "{$rowId}[{$columnName}]";
/* Create using array */
if ( is_array( $columnData ) )
{
$classname = '\IPS\Helpers\Form\\' . $columnData[0];
$row[ $columnName ] = new $classname(
$inputName,
isset( $data[ $columnName ] ) ? $data[ $columnName ] : ( isset( $columnData[1] ) ? $columnData[1] : NULL ),
isset( $columnData[2] ) ? $columnData[2] : FALSE,
isset( $columnData[3] ) ? $columnData[3] : array()
);
}
/* Create using callback function */
else
{
$row[ $columnName ] = call_user_func( $columnData, $inputName, isset( $data[ $columnName ] ) ? $data[ $columnName ] : NULL, $data );
}
}
if ( isset( $data['_level'] ) )
{
$row['_level'] = $data['_level'];
}
return $row;
}
/**
* Get submitted values
*
* @param bool $force If true, wil not check if form was submitted (used for nested matrixes)
* @return array|FALSE Array of field values or FALSE if the form has not been submitted or if there were validation errors
*/
public function values( $force=FALSE )
{
$values = array();
$name = "{$this->id}_submitted";
if( $force or ( isset( \IPS\Request::i()->$name ) and \IPS\Request::i()->csrfKey === \IPS\Session::i()->csrfKey ) )
{
foreach ( $this->elements( FALSE ) as $rowId => $columns )
{
if ( is_array( $columns ) )
{
foreach ( $columns as $columnName => $element )
{
/* If this was a ehader or something, skip it */
if ( !is_object( $element ) )
{
continue;
}
/* Return FALSE on error */
if( $element->error !== NULL )
{
return FALSE;
}
/* If the "check all" box was checked, set it to TRUE */
if ( $element instanceof Checkbox and isset( \IPS\Request::i()->__all[ $columnName ] ) )
{
$element->value = TRUE;
}
/* Set value */
$values[ $rowId ][ $columnName ] = $element->value;
/* Not if it's changed */
if ( $element->value !== $element->defaultValue and mb_substr( $rowId, 0, 5 ) !== '_new_' )
{
$this->changedRows[ $rowId ] = $rowId;
}
}
}
}
return $values;
}
return FALSE;
}
}
+168
View File
@@ -0,0 +1,168 @@
<?php
/**
* @brief Member input class 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 11 Mar 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;
}
/**
* Member input class for Form Builder
*/
class _Member extends Text
{
/**
* @brief Default Options
* @code
$childDefaultOptions = array(
'multiple' => 1, // Maximum number of members. NULL for any. Default is 1.
);
* @endcode
*/
public $childDefaultOptions = array(
'multiple' => 1,
);
/**
* Constructor
* Adds 'confirm' to the available options
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
*/
public function __construct()
{
$args = func_get_args();
$this->defaultOptions['autocomplete'] = array(
'source' => 'app=core&module=system&controller=ajax&do=findMember',
'resultItemTemplate' => 'core.autocomplete.memberItem',
'unique' => true,
'minAjaxLength' => 3
);
if( isset( $args[3] ) and array_key_exists( 'multiple', $args[3] ) and $args[3]['multiple'] > 0 )
{
$this->defaultOptions['autocomplete']['maxItems'] = $args[3]['multiple'];
}
elseif ( !isset( $args[3] ) or !array_key_exists( 'multiple', $args[3] ) )
{
$this->defaultOptions['autocomplete']['maxItems'] = $this->childDefaultOptions['multiple'];
}
call_user_func_array( 'parent::__construct', $args );
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
$value = $this->value;
if ( is_array( $this->value ) )
{
$value = array();
foreach ( $this->value as $v )
{
$value[] = ( $v instanceof \IPS\Member ) ? $v->name : $v;
}
$value = implode( ',', $value );
}
elseif ( $value instanceof \IPS\Member )
{
$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 );
}
/**
* Format Value
*
* @return \IPS\Member|NULL|FALSE
*/
public function formatValue()
{
if ( $this->value and !( $this->value instanceof \IPS\Member ) )
{
$return = array();
foreach ( is_array( $this->value ) ? $this->value : explode( ',', $this->value ) as $v )
{
if ( $v instanceof \IPS\Member )
{
$return[ $v->member_id ] = $v;
}
elseif( $v )
{
$member = \IPS\Member::load( $v, 'name' );
if ( $member->member_id )
{
if ( $this->options['multiple'] === 1 )
{
return $member;
}
$return[ $member->member_id ] = $member;
}
}
}
if ( !empty( $return ) )
{
return ( $this->options['multiple'] === NULL or $this->options['multiple'] == 0 ) ? $return : array_slice( $return, 0, $this->options['multiple'] );
}
}
return $this->value;
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
parent::validate();
if ( $this->value !== '' and !( $this->value instanceof \IPS\Member ) and !is_array( $this->value ) )
{
throw new \InvalidArgumentException('form_member_bad');
}
}
/**
* String Value
*
* @param mixed $value The value
* @return string
*/
public static function stringValue( $value )
{
if ( is_array( $value ) )
{
return implode( ',', array_map( function( $v )
{
return $v->member_id;
}, $value ) );
}
return $value ? (string) $value->member_id : NULL;
}
}
+409
View File
@@ -0,0 +1,409 @@
<?php
/**
* @brief Number input class 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;
}
/**
* Text input class for Form Builder
*/
class _Node extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'url' => \IPS\Http\Url::internal(...) // The URL that this form element will be displayed on
'class' => '\IPS\core\Foo', // The node class
'permissionCheck' => 'add', // If a permission key is provided, only nodes that the member has that permission for will be available. Alternatively, can be a callback to return if node can be selected.
'zeroVal' => 'none', // If provided, a checkbox allowing you to receive a value of 0 will be shown with the label given. Default is NULL.
'zeroValTogglesOn' => array( ... ), // Element IDs to toggle on when the zero val checkbox is checked
'zeroValTogglesOff' => array( ... ), // Element IDs to toggle on when the zero val checkbox is unchecked
'multiple' => TRUE, // If multiple values are supported. Defaults to FALSE
'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
'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
);
* @endcode
*/
protected $defaultOptions = array(
'url' => NULL,
'class' => NULL,
'permissionCheck' => NULL,
'zeroVal' => NULL,
'multiple' => FALSE,
'subnodes' => TRUE,
'togglePerm' => NULL,
'toggleIds' => array(),
'zeroValTogglesOn' => array(),
'zeroValTogglesOff' => array(),
'forceOwner' => NULL,
'where' => array(),
'disabledIds' => array(),
'noParentNodes' => NULL
);
/**
* Constructor
*
* @return void
*/
public function __construct()
{
call_user_func_array( 'parent::__construct', func_get_args() );
if ( !$this->options['url'] )
{
$this->options['url'] = \IPS\Request::i()->url();
}
$this->options['url'] = $this->options['url']->setQueryString( '_nodeSelectName', $this->name );
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
$nodeClass = $this->options['class'];
$permCheck = is_string( $this->options['permissionCheck'] ) ? $this->options['permissionCheck'] : NULL;
$disabledCallback = is_callable( $this->options['permissionCheck'] ) ? $this->options['permissionCheck'] : NULL;
/* Are we getting some AJAX stuff? */
if ( isset( \IPS\Request::i()->_nodeSelectName ) and \IPS\Request::i()->_nodeSelectName === $this->name )
{
$disabled = null;
if ( isset( \IPS\Request::i()->_disabled ) )
{
$disabled = json_decode( \IPS\Request::i()->_disabled, true );
$disabled = ( $disabled === FALSE ) ? array() : $disabled;
}
switch ( \IPS\Request::i()->_nodeSelect )
{
case 'children':
try
{
$node = $nodeClass::load( \IPS\Request::i()->_nodeId );
$children = $node->children( NULL, NULL, $this->options['subnodes'], $disabled, $this->options['where'] );
}
catch ( \Exception $e )
{
\IPS\Output::i()->json( NULL, 404 );
}
\IPS\Output::i()->json( array( 'viewing' => $node->_id, 'title' => $node->_title, 'output' => \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->nodeCascade( $children, FALSE, $permCheck, $this->options['subnodes'], $this->options['togglePerm'], $this->options['toggleIds'], $disabledCallback, FALSE, NULL, $this->options['class'], $this->options['where'], $this->options['disabledIds'] ) ) );
break;
case 'parent':
try
{
$node = $nodeClass::load( \IPS\Request::i()->_nodeId );
$parent = $node->parent();
$children = $parent ? $parent->children( NULL, NULL, $this->options['subnodes'], $disabled, $this->options['where'] ) : $nodeClass::roots( NULL, NULL, $this->options['where'] );
}
catch ( \Exception $e )
{
\IPS\Output::i()->json( $e->getMessage(), 404 );
}
\IPS\Output::i()->json( array( 'viewing' => $parent ? $parent->_id : 0, 'title' => $parent ? $parent->_title : \IPS\Member::loggedIn()->language()->addToStack( $nodeClass::$nodeTitle ), 'output' => \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->nodeCascade( $children, FALSE, $permCheck, $this->options['subnodes'], $this->options['togglePerm'], $this->options['toggleIds'], $disabledCallback, FALSE, NULL, $this->options['class'], $this->options['where'], $this->options['disabledIds'] ) ) );
break;
case 'search':
$results = array();
$_results = $nodeClass::search( '_title', \IPS\Request::i()->q, '_title' );
foreach ( $_results as $node )
{
if ( ( !$permCheck or $node->can( $permCheck ) ) AND ! in_array( $node->_id, $disabled ) )
{
$id = ( $node instanceof $nodeClass ? $node->_id : "s.{$node->_id}" );
$results[ $id ] = $node;
}
}
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->nodeCascade( $results, TRUE, $permCheck, $this->options['subnodes'], $this->options['togglePerm'], $this->options['toggleIds'], $disabledCallback, FALSE, NULL, $this->options['class'], $this->options['where'], $this->options['disabled'] ) );
break;
}
}
/* Get initial nodes */
$nodes = array();
$children = array();
$noParentNodes = array();
if( isset( $nodeClass::$ownerTypes ) and $nodeClass::$ownerTypes !== NULL )
{
$nodes = $nodeClass::loadByOwner( $this->options['forceOwner'] ?: \IPS\Member::loggedIn(), $this->options['where'] );
}
else
{
/* Recurse three levels in to allow for discoverability */
foreach( $nodeClass::roots( NULL, NULL, $this->options['where'] ) AS $node )
{
$this->_populate( $nodes, $disabled, 0, $node, $children, 3 );
}
if ( $this->options['noParentNodes'] )
{
$subnodeClass = $nodeClass::$subnodeClass;
$noParentNodes = iterator_to_array( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', $subnodeClass::$databaseTable, $subnodeClass::$databasePrefix . $subnodeClass::$parentNodeColumnId . '=0' )->setKeyField( $subnodeClass::$databasePrefix . $subnodeClass::$databaseColumnId ), $subnodeClass ) );
}
}
if ( isset( $this->options['disabled'] ) and is_array( $this->options['disabled'] ) )
{
$this->options['url'] = $this->options['url']->setQueryString( '_disabled', json_encode( $this->options['disabled'] ) );
foreach( $this->options['disabled'] as $id )
{
if ( isset( $nodes[ $id ] ) )
{
unset( $nodes[ $id ] );
}
}
}
/* What is selected? */
if ( $this->options['zeroVal'] !== NULL and $this->value === 0 )
{
$selected = 0;
}
else
{
$selected = array();
if ( is_array( $this->value ) )
{
foreach ( $this->value as $node )
{
$title = isset( $node::$titleLangPrefix ) ? \IPS\Member::loggedIn()->language()->addToStack( $node::$titleLangPrefix . $node->_id, FALSE, array( 'json' => TRUE, 'escape' => TRUE ) ) : $node->_title;
$selected[ !( $node instanceof $nodeClass ) ? "{$node->_id}.s" : $node->_id ] = array( 'title' => $title, 'parents' => array_values( array_map( function( $val ){
return isset( $val::$titleLangPrefix ) ? \IPS\Member::loggedIn()->language()->addToStack( $val::$titleLangPrefix . $val->_id, FALSE, array( 'json' => TRUE, 'escape' => TRUE ) ) : $val->_title;
}, iterator_to_array( $node->parents() ) ) ) );
}
}
elseif ( $this->value instanceof \IPS\Node\Model )
{
$class = get_class( $this->value );
$title = isset( $nodeClass::$titleLangPrefix ) ? \IPS\Member::loggedIn()->language()->addToStack( $class::$titleLangPrefix . $this->value->_id, FALSE, array( 'json' => TRUE, 'escape' => TRUE ) ) : $this->value->_title;
$selected[ !( $this->value instanceof $nodeClass ) ? "{$this->value->_id}.s" : $this->value->_id ] = array( 'title' => $title, 'parents' => array_values( array_map( function( $val ){
return isset( $val::$titleLangPrefix ) ? \IPS\Member::loggedIn()->language()->addToStack( $val::$titleLangPrefix . $val->_id, FALSE, array( 'json' => TRUE, 'escape' => TRUE ) ) : $val->_title;
}, iterator_to_array( $this->value->parents() ) ) ) );
}
$selected = json_encode( $selected );
}
/* Do we need the no-JS fallback? We only do this if we know JS is disabled as it's intensive and may cause memory exhaustion if there are lots of nodes - it's a last resort */
$noJS = NULL;
if ( isset( \IPS\Request::i()->_noJs ) )
{
$options = array();
$_children = array();
$disabled = ( isset( $this->options['disabled'] ) and is_array( $this->options['disabled'] ) ) ? $this->options['disabled'] : array();
foreach ( $nodeClass::roots( 'view', NULL, $this->options['where'] ) as $root )
{
$this->_populate( $options, $disabled, 0, $root, $_children, NULL, NULL, TRUE );
}
$value = NULL;
if ( $this->value !== 0 )
{
if ( $this->options['multiple'] )
{
$value = array();
if ( is_array( $this->value ) )
{
$value = array_keys( $this->value );
}
elseif ( is_object( $this->value ) )
{
$value = array( $this->value->_id );
}
}
else
{
if ( is_numeric( $this->value ) )
{
$value = $this->value;
}
elseif ( is_object( $this->value ) )
{
$value = array( $this->value->_id );
}
}
}
$noJS = \IPS\Theme::i()->getTemplate( 'forms', 'core' )->select( $this->name . ( $this->options['multiple'] ? '[]' : '' ), $value, $this->required, $options, $this->options['multiple'], '', $disabled );
}
/* Display */
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->node( $this->name, $selected, $this->options['multiple'], $this->options['url'], $nodeClass::$nodeTitle, $nodes, $this->options['zeroVal'], $noJS, $permCheck, $this->options['subnodes'], $this->options['togglePerm'], $this->options['toggleIds'], $disabledCallback, $this->options['zeroValTogglesOn'], $this->options['zeroValTogglesOff'], TRUE, $children, $this->options['class'], $this->options['where'], is_array( $this->options['disabledIds'] ) ? $this->options['disabledIds'] : array(), $this->options['noParentNodes'], $noParentNodes );
}
/**
* Populate array options
*
* @param array $options The list of nodes
* @param array $disabled Disabled options
* @param int $depth The current depth
* @param \IPS\Node\Model $node The node
* @param int|NULL $depthLimit How deep the recursion should go
* @param \IPS\Node\Model|NULL $parent If we are recursing on a child, this should be the parent node. Otherwise NULL if in the root.
* @return void
*/
protected function _populate( &$nodes, &$disabled, $depth, $node, &$children, $depthLimit = NULL, $parent = NULL, $noJS = FALSE )
{
if ( !$this->options['permissionCheck'] OR $node->can('view') )
{
if ( $noJS )
{
$nodes[ $node->_id . ( !( $node instanceof $this->options['class'] ) ? '.s' : '' ) ] = str_repeat( '- ', $depth ) . $node->_title;
}
else
{
if ( $parent === NULL )
{
$nodes[ $node->_id ] = $node;
}
else
{
$children[ $parent->_id ][ $node->_id ] = $node;
}
}
if ( $this->options['permissionCheck'] )
{
if ( is_string( $this->options['permissionCheck'] ) )
{
if ( !$node->can( $this->options['permissionCheck'] ) )
{
$disabled[] = $node->_id;
}
}
elseif ( is_callable( $this->options['permissionCheck'] ) )
{
if ( !call_user_func( $this->options['permissionCheck'], $node ) )
{
$disabled[] = $node->_id;
}
}
}
if ( $depthLimit === NULL OR $depth < (int) $depthLimit )
{
foreach( $node->children( 'view', NULL, $this->options['subnodes'], NULL, $this->options['where'] ) AS $child )
{
$this->_populate( $nodes, $disabled, $depth + 1, $child, $children, $depthLimit, $node, $noJS );
}
}
}
}
/**
* Get Value
*
* @return string|int
*/
public function getValue()
{
$zeroValName = "{$this->name}-zeroVal";
if ( $this->options['zeroVal'] !== NULL and isset( \IPS\Request::i()->$zeroValName ) )
{
return 0;
}
else
{
return parent::getValue();
}
}
/**
* Format Value
*
* @return \IPS\Node\Model|array|NULL
*/
public function formatValue()
{
$nodeClass = $this->options['class'];
$permCheck = $this->options['permissionCheck'];
if ( $this->value and !( $this->value instanceof \IPS\Node\Model ) )
{
$return = array();
foreach ( is_array( $this->value ) ? $this->value : explode( ',', $this->value ) as $v )
{
if ( $v instanceof \IPS\Node\Model )
{
$return[ !( $v instanceof $nodeClass ) ? "s{$v->_id}" : $v->_id ] = $v;
}
elseif( $v )
{
try
{
$exploded = explode( '.', $v );
$node = call_user_func( array( ( isset( $exploded[1] ) and $exploded[1] === 's' ) ? $nodeClass::$subnodeClass : $nodeClass, 'load' ), $exploded[0] );
if ( !$permCheck or ( is_string( $permCheck ) and $node->can( $permCheck ) ) or ( is_callable( $permCheck ) and call_user_func( $permCheck, $node ) ) )
{
$return[ isset( $exploded[1] ) ? "s{$node->_id}" : $node->_id ] = $node;
}
}
catch ( \Exception $e ) {}
}
}
if ( !empty( $return ) )
{
return $this->options['multiple'] ? $return : array_pop( $return );
}
else
{
return NULL;
}
}
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;
}
}
+220
View File
@@ -0,0 +1,220 @@
<?php
/**
* @brief Number input class 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 18 Feb 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;
}
/**
* Text input class for Form Builder
*/
class _Number extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'min' => 0, // Minimum number. NULL is no minimum. Default is 0.
'max' => 100, // Maximum number. NULL is no maximum. Default is NULL.
'unlimited' => -1, // If any value other than NULL is provided, an "Unlimited" checkbox will be displayed. If checked, the value specified will be sent.
'unlimitedLang' => 'unlimited', // Language string to use for unlimited checkbox label
'unlimitedToggles' => array(...), // Names of other input fields that should show/hide when the "Unlimited" checkbox is toggled.
'unlimitedToggleOn' => TRUE, // Whether the toggles should show on unlimited checked (TRUE) or unchecked(FALSE). Default is TRUE
'valueToggles' => array(...), // Names of other input fields that should show/hide if the value is/isn't 0.
'decimals' => 2, // Number of decimal places (or TRUE to allow any number)
'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
);
* @endcode
*/
protected $defaultOptions = array(
'min' => 0,
'max' => NULL,
'unlimited' => NULL,
'unlimitedLang' => 'unlimited',
'unlimitedToggles' => array(),
'unlimitedToggleOn' => TRUE,
'valueToggles' => array(),
'decimals' => 0,
'step' => NULL,
'range' => FALSE,
'disabled' => FALSE,
'endSuffix' => NULL,
);
/**
* Constructor
* Formats the value appropriately
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @param string $name Name
* @param mixed $defaultValue Default value
* @param bool $required Required?
* @param array $options Type-specific options
* @return void
*/
public function __construct( $name, $defaultValue=NULL, $required=FALSE, $options=array() )
{
/* Specify there's a value if the unlimited element contains a value */
$unlimitedName = "{$name}_unlimited";
if( isset( $options['unlimited'] ) and $options['unlimited'] !== NULL and isset( \IPS\Request::i()->$unlimitedName ) )
{
\IPS\Request::i()->$name = $options['unlimited'];
}
/* Work out the step */
$args = func_get_args();
if ( !isset( $options['base'] ) and isset( $options['decimals'] ) )
{
if ( $options['decimals'] === TRUE )
{
$args[3]['step'] = 'any';
}
else
{
$args[3]['step'] = 1 / ( pow( 10, $options['decimals'] ) );
}
}
/* Call parent constructor */
call_user_func_array( 'parent::__construct', $args );
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
/* Display */
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->number( $this->name, $this->value, $this->required, $this->options['unlimited'], $this->options['range'], $this->options['min'], $this->options['max'], $this->options['step'], $this->options['decimals'], $this->options['unlimitedLang'], $this->options['disabled'], $this->suffix, $this->options['unlimitedToggles'], $this->options['unlimitedToggleOn'], $this->options['valueToggles'] );
}
/**
* Get Value
*
* @return mixed
*/
public function getValue()
{
$name = $this->name;
if ( $pos = mb_strpos( $name, '[' ) )
{
$_name = mb_substr( preg_replace( '/\[(.+?)\]/', '[$1_unlimited]', $name, 1 ), 0, $pos );
$val = \IPS\Request::i()->$_name;
preg_match_all( '/\[(.+?)\]/', $name, $matches );
foreach ( $matches[1] as $_name )
{
if ( isset( $val[ $_name ] ) )
{
$val = $val[ $_name ];
}
else
{
$val = FALSE;
break;
}
}
return $val;
}
else
{
$unlimitedName = "{$name}_unlimited";
$unlimitedChecked = isset( \IPS\Request::i()->$unlimitedName );
}
/* Unlimited? */
if ( $this->options['unlimited'] !== NULL and isset( \IPS\Request::i()->$unlimitedName ) )
{
return $this->options['unlimited'];
}
/* Get value */
return \IPS\Request::i()->$name;
}
/**
* Format Value
*
* @return mixed
*/
public function formatValue()
{
/* Get value */
$value = $this->value;
/* If this is the "unlimited" value, we don't need to format it */
if ( $this->options['unlimited'] !== NULL and $value === $this->options['unlimited'] )
{
return $value;
}
/* Convert decimal point and thousand separators to a way PHP understand */
$value = str_replace( trim( \IPS\Member::loggedIn()->language()->locale['thousands_sep'] ), '', $value );
$value = str_replace( trim( \IPS\Member::loggedIn()->language()->locale['decimal_point'] ), '.', $value );
/* If it's not numeric, throw an exception */
if ( ( !is_numeric( $value ) and $value !== '' and $this->required === FALSE ) or ( !is_numeric( $value ) and $this->required === TRUE ) )
{
throw new \InvalidArgumentException( 'form_number_bad' );
}
/* Convert to int/float */
if ( $this->options['decimals'] )
{
$value = floatval( $value );
if ( is_int( $this->options['decimals'] ) )
{
$value = round( $value, $this->options['decimals'] );
}
}
else
{
$value = intval( $value );
}
/* Return */
return $value;
}
/**
* Validate
*
* @throws \LengthException
* @return TRUE
*/
public function validate()
{
parent::validate();
if ( $this->options['unlimited'] === NULL or $this->value !== $this->options['unlimited'] )
{
if ( $this->options['min'] !== NULL and $this->value < $this->options['min'] )
{
throw new \LengthException( \IPS\Member::loggedIn()->language()->addToStack('form_number_min', FALSE, array( 'sprintf' => array( $this->options['min'] ) ) ) );
}
if ( $this->options['max'] !== NULL and $this->value > $this->options['max'] )
{
throw new \LengthException( \IPS\Member::loggedIn()->language()->addToStack('form_number_max', FALSE, array( 'sprintf' => array( $this->options['max'] ) ) ) );
}
}
}
}
+128
View File
@@ -0,0 +1,128 @@
<?php
/**
* @brief Number range input class 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 11 Jul 2014
* @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;
}
/**
* Number range input class for Form Builder
*/
class _NumberRange extends FormAbstract
{
/**
* @brief Default Options
* @see \IPS\Helpers\Form\Number::$defaultOptions
* @code
$defaultOptions = array(
'start' => array( ... ),
'end' => array( ... ),
);
* @endcode
*/
protected $defaultOptions = array(
'start' => array(
'min' => NULL,
'max' => NULL,
'disabled' => FALSE,
'unlimited' => NULL,
'unlimitedLang' => 'any',
'unlimitedToggles' => array(),
'unlimitedToggleOn' => TRUE,
),
'end' => array(
'min' => NULL,
'max' => NULL,
'disabled' => FALSE,
'unlimited' => NULL,
'unlimitedLang' => 'any',
'unlimitedToggles' => array(),
'unlimitedToggleOn' => TRUE,
),
);
/**
* @brief Start Number Object
*/
public $start = NULL;
/**
* @brief End Number Object
*/
public $end = NULL;
/**
* Constructor
* Creates the two number objects
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
*/
public function __construct( $name, $defaultValue=NULL, $required=FALSE, $options=array() )
{
$this->start = new \IPS\Helpers\Form\Number( "{$name}[start]", isset( $defaultValue['start'] ) ? $defaultValue['start'] : NULL, FALSE, isset( $options['start'] ) ? $options['start'] : array() );
$this->end = new \IPS\Helpers\Form\Number( "{$name}[end]", isset( $defaultValue['end'] ) ? $defaultValue['end'] : NULL, FALSE, isset( $options['end'] ) ? $options['end'] : array() );
call_user_func_array( 'parent::__construct', func_get_args() );
}
/**
* Format Value
*
* @return array
*/
public function formatValue()
{
/* Return */
return array(
'start' => $this->start->formatValue(),
'end' => $this->end->formatValue(),
);
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->numberRange( $this->start->html(), $this->end->html() );
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @throws \LengthException
* @return TRUE
*/
public function validate()
{
$this->start->validate();
$this->end->validate();
if ( $this->required and $this->value['start'] === NULL and $this->value['end'] === NULL )
{
throw new \InvalidArgumentException('form_required');
}
if( $this->customValidationCode !== NULL )
{
call_user_func( $this->customValidationCode, $this->value );
}
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
/**
* @brief Password input class 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 11 Mar 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;
}
/**
* Password input class for Form Builder
*/
class _Password extends Text
{
/**
* @brief Default Options
* @code
$childDefaultOptions = array(
'validateFor' => \IPS\Member::loggedIn(), // If an \IPS\Member object is provided, the password will be checked if it is valid for that account. Default is NULL.
'confirm' => 'password1', // If the name of another element in the form is provided, will check is the values match. Default is NULL.
);
* @endcode
*/
public $childDefaultOptions = array(
'validateFor' => NULL,
'confirm' => NULL
);
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
parent::validate();
/* Password length */
if ( mb_strlen( $this->value ) < 3 AND ( $this->required OR $this->value ) )
{
throw new \InvalidArgumentException( 'err_password_length' );
}
/* Is valid for member? */
if ( $this->options['validateFor'] !== NULL )
{
$valid = FALSE;
foreach( \IPS\Login::handlers( TRUE ) as $k => $handler )
{
if ( $handler->authTypes & \IPS\Login::AUTH_TYPE_USERNAME )
{
try
{
$handler->authenticate( array( 'auth' => $this->options['validateFor']->name, 'password' => $this->value ) );
$valid = TRUE;
}
catch ( \Exception $e ) {}
}
if ( $handler->authTypes & \IPS\Login::AUTH_TYPE_EMAIL )
{
try
{
$handler->authenticate( array( 'auth' => $this->options['validateFor']->email, 'password' => $this->value ) );
$valid = TRUE;
}
catch ( \Exception $e ) {}
}
}
if ( !$valid )
{
throw new \InvalidArgumentException( 'login_err_bad_password' );
}
}
/* Matches the other one? */
if ( $this->options['confirm'] !== NULL )
{
$confirmKey = $this->options['confirm'];
if ( $this->value !== \IPS\Request::i()->$confirmKey )
{
throw new \InvalidArgumentException( 'form_password_confirm' );
}
}
}
}
+200
View File
@@ -0,0 +1,200 @@
<?php
/**
* @brief Poll input class 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 10 Jan 2014
* @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;
}
/**
* Poll input class for Form Builder
*/
class _Poll extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'allowPollOnly' => TRUE, // Controls if "Allow poll only" option is allowed (if the admin has enabled it)
);
* @endcode
*/
protected $defaultOptions = array(
'allowPollOnly' => FALSE,
);
/**
* @brief Have we output the js yet?
*/
protected $jsSent = FALSE;
/**
* Get HTML
*
* @return string
*/
public function html()
{
$json = array();
for( $i = 1; $i <= \IPS\Settings::i()->max_poll_questions; $i++ )
{
$thisQuestion = array();
if( $this->value AND isset( $this->value->choices[ $i ] ) )
{
$thisQuestion['title'] = $this->value->choices[ $i ]['question'];
$thisQuestion['multiChoice'] = (bool) $this->value->choices[ $i ]['multi'];
$thisQuestion['choices'] = array();
for( $j = 1; $j <= \IPS\Settings::i()->max_poll_choices; $j++ )
{
$thisChoice = array();
if( $this->value AND isset( $this->value->choices[ $i ]['choice'][ $j ] ) )
{
$thisChoice['title'] = $this->value->choices[ $i ]['choice'][ $j ];
$thisChoice['count'] = $this->value->choices[ $i ]['votes'][ $j ];
$thisQuestion['choices'][] = $thisChoice;
}
}
$json[] = $thisQuestion;
}
}
$json = json_encode( $json );
if( !$this->jsSent )
{
\IPS\Output::i()->endBodyCode .= <<<EOF
<script type='text/javascript'>
ips.setSetting('pollData', {$json} );
</script>
EOF;
$this->jsSent = TRUE;
}
/* Ensure poll JS is loaded */
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'front_core.js', 'core' ) );
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->poll( $this->name, $this->value, $json, $this->options['allowPollOnly'] );
}
/**
* Get Value
*
* @return mixed
*/
public function getValue()
{
/* Check we provided a value */
$formValues = parent::getValue();
/* Init object */
if ( $this->defaultValue instanceof \IPS\Poll )
{
$poll = $this->defaultValue;
}
else
{
$poll = new \IPS\Poll;
$poll->starter_id = \IPS\Member::loggedIn()->member_id;
}
/* Set values */
$data = parent::getValue();
if ( !isset( $data['questions'] ) )
{
return NULL;
}
$poll->setDataFromForm( $data, $this->options['allowPollOnly'] );
if ( !$poll->poll_question )
{
if ( $poll->pid )
{
$poll->delete();
}
return NULL;
}
/* Return */
return $poll;
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
/* Required? */
if ( $this->value === NULL )
{
if ( $this->required )
{
throw new \InvalidArgumentException('form_required');
}
else
{
return TRUE;
}
}
if( mb_strlen( $this->value->poll_question ) > 255 )
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('form_maxlength', FALSE, array( 'pluralize' => array( 255 ) ) ) );
}
/* Do we have at least one question? */
if ( !count( $this->value->choices ) )
{
throw new \InvalidArgumentException('form_poll_no_questions');
}
if ( count( $this->value->choices ) > \IPS\Settings::i()->max_poll_questions )
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('form_poll_too_many_questions', FALSE, array( 'pluralize' => array( \IPS\Settings::i()->max_poll_questions ) ) ) );
}
/* Do all the questions have at least 2 options? */
foreach ( $this->value->choices as $question )
{
if ( count( $question['choice'] ) < 2 )
{
throw new \InvalidArgumentException('form_poll_too_few_answers');
}
if ( count( $question['choice'] ) > \IPS\Settings::i()->max_poll_choices )
{
throw new \InvalidArgumentException( \IPS\Member::loggedIn()->language()->addToStack('form_poll_too_many_answers', FALSE, array( 'sprintf' => array( \IPS\Settings::i()->max_poll_choices ) ) ) );
}
}
return TRUE;
}
/**
* String Value
*
* @param mixed $value The value
* @return string
*/
public static function stringValue( $value )
{
return $value ? $value->pid : NULL;
}
}
+136
View File
@@ -0,0 +1,136 @@
<?php
/**
* @brief Radio Switch class 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 18 Feb 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;
}
/**
* Radio Switch class for Form Builder
*/
class _Radio extends Select
{
/**
* Constructor
*
* @return void
*/
public function __construct()
{
call_user_func_array( 'parent::__construct', func_get_args() );
if ( !isset( $this->options['descriptions'] ) )
{
$this->options['descriptions'] = array();
}
/* If you haven't selected any radio options, then there is no input and the required flag is never checked (validate() is never called) */
$_key = "radio_" . $this->name . "__empty";
if( isset( \IPS\Request::i()->$_key ) )
{
try
{
$this->value = $this->getValue();
$this->unformatted = $this->value;
$this->value = $this->formatValue();
$this->validate();
}
catch ( \LogicException $e )
{
$this->error = $e->getMessage();
}
}
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
$descriptions = $this->options['descriptions'];
if ( $this->options['parse'] === 'lang' )
{
foreach ( $this->options['options'] as $k => $v )
{
$key = "{$v}_desc";
if ( \IPS\Member::loggedIn()->language()->checkKeyExists( $key ) )
{
$descriptions[ $k ] = \IPS\Member::loggedIn()->language()->addToStack( $key );
}
}
}
/* Translate label back to key? */
if ( $this->options['returnLabels'] )
{
$value = array_search( $this->value, $this->options['options'] );
}
else
{
$value = $this->value;
}
if ( $this->options['parse'] === 'image' )
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->radioImages( $this->name, $value, $this->required, $this->options['options'], $this->options['disabled'], $this->options['toggles'], $descriptions, $this->options['userSuppliedInput'], $this->options['unlimited'], $this->options['unlimitedLang'], $this->htmlId );
}
else
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->radio( $this->name, $value, $this->required, $this->parseOptions(), $this->options['disabled'], $this->options['toggles'], $descriptions, $this->options['userSuppliedInput'], $this->options['unlimited'], $this->options['unlimitedLang'], $this->htmlId );
}
}
/**
* Validate
*
* @throws \OutOfRangeException
* @return TRUE
*/
public function validate()
{
if( $this->value === null and $this->required )
{
throw new \InvalidArgumentException('form_required');
}
/* Field is not required and value was not supplied */
else if( $this->value === null )
{
return true;
}
return parent::validate();
}
/**
* Get value
*
* @return array
*/
public function getValue()
{
$value = parent::getValue();
/* Disabled radio fields do not submit a value to the server */
if( $this->options['disabled'] === TRUE or ( is_array( $this->options['disabled'] ) and in_array( $value, $this->options['disabled'] ) ) )
{
return $this->defaultValue;
}
return $value;
}
}
+73
View File
@@ -0,0 +1,73 @@
<?php
/**
* @brief Rating input 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 3 Oct 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;
}
/**
* Rating input class for Form Builder
*/
class _Rating extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'max' => 5, // Maximum number of stars
);
* @endcode
*/
protected $defaultOptions = array(
'max' => 5,
);
/**
* Get HTML
*
* @return string
*/
public function html()
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->rating( $this->name, $this->value, $this->required, $this->options['max'] );
}
/**
* Format Value
*
* @return int|NULL
*/
public function formatValue()
{
return $this->value ? intval( $this->value ) : NULL;
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
if( $this->value === NULL and $this->required )
{
throw new \InvalidArgumentException('form_required');
}
return parent::validate();
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/**
* @brief Search input class 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 11 Mar 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;
}
/**
* Search input class for Form Builder
*/
class _Search extends Text
{
}
+297
View File
@@ -0,0 +1,297 @@
<?php
/**
* @brief Select-box class 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 18 Feb 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;
}
/**
* Select-box class for Form Builder
*/
class _Select extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'options' => array( 'key' => 'val', 'key' => 'val', ... ) // Options for select box
'toggles' => array( 'key' => array( ... ) ) // IDs that each option should show/hide when toggled
'multiple' => TRUE // Sets the select box to allow multiple values to be selected. Default is FALSE.
'class' => '', // CSS class
'disabled' => FALSE, // Disables input. Default is FALSE.
'parse' => 'lang', // Sets how the values for options should be parsed. Acceptable values are "lang" (language keys), "normal" (htmlentities), "raw" (no parsing) or "image" (image URLs, only for Radio fields). Default is "lang".
'unlimited' => -1, // If any value other than NULL is provided, an "Unlimited" checkbox will be displayed. If checked, the value specified will be sent.
'unlimitedLang' => 'unlimited', // Language string to use for unlimited checkbox label
'unlimitedToggles' => array(...), // Names of other input fields that should show/hide when the "Unlimited" checkbox is toggled.
'unlimitedToggleOn' => TRUE, // Whether the toggles should show on unlimited checked (TRUE) or unchecked(FALSE). Default is TRUE
'userSuppliedInput' => '' // If this option is selected (it must be a valid option passed to 'options'), a text input field will display, allowing the user to enter their own value
'noDefault' => TRUE // For radios, you can set this to TRUE if you do not want any radio option selected by default (useful for things like polls)
'returnLabels' => FALSE // If TRUE, will return the labels rather than the keys as the value
'sort' => FALSE, // If TRUE, options will be sorted by JavaScript. Useful where the options are language-dependant but you still want them to be in alphabetical order
);
* @endcode
*/
protected $defaultOptions = array(
'options' => array(),
'toggles' => array(),
'multiple' => FALSE,
'class' => '',
'disabled' => FALSE,
'parse' => 'lang',
'unlimited' => NULL,
'unlimitedLang' => 'all',
'unlimitedToggles' => array(),
'unlimitedToggleOn' => TRUE,
'userSuppliedInput' => '',
'noDefault' => FALSE,
'returnLabels' => FALSE,
'sort' => FALSE,
);
/**
* Constructor
*
* @param string $name Name
* @param mixed $defaultValue Default value
* @param bool $required Required?
* @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 )
{
/* Set the default value to the first option if one isn't provided */
if ( $defaultValue === NULL and ( !isset( $options['noDefault'] ) OR !$options['noDefault'] ) and is_array( $options['options'] ) )
{
foreach ( $options['options'] as $k => $v )
{
if ( is_array( $v ) )
{
foreach ( $v as $_k => $_v )
{
if ( !isset( $options['disabled'] ) OR ( is_array( $options['disabled'] ) AND !in_array( $_k, $options['disabled'] ) ) )
{
$defaultValue = $_k;
break;
}
}
}
else
{
if ( !isset( $options['disabled'] ) OR ( is_array( $options['disabled'] ) AND !in_array( $k, $options['disabled'] ) ) )
{
$defaultValue = $k;
break;
}
}
}
if ( isset( $options['multiple'] ) and $options['multiple'] === TRUE )
{
$defaultValue = array( $defaultValue );
}
}
/* Call parent constructor */
call_user_func( 'parent::__construct', $name, $defaultValue, $required, $options, $customValidationCode, $prefix, $suffix, $id );
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
/* Add [] to the name if this is a multi-select */
$name = $this->name;
if ( $this->options['multiple'] )
{
$name .= '[]';
}
/* Translate labels back to keys? */
if ( $this->options['returnLabels'] )
{
$value = array();
if ( is_array( $this->value ) )
{
foreach ( $this->value as $v )
{
$value[] = array_search( $v, $this->options['options'] );
}
}
else
{
$value = array_search( $this->value, $this->options['options'] );
}
}
else
{
$value = $this->value;
}
return \IPS\Theme::i()->getTemplate( 'forms', 'core' )->select( $name, $value, $this->required, $this->parseOptions(), $this->options['multiple'], $this->options['class'], $this->options['disabled'], $this->options['toggles'], $this->htmlId, $this->options['unlimited'], $this->options['unlimitedLang'], $this->options['unlimitedToggles'], $this->options['unlimitedToggleOn'], $this->options['userSuppliedInput'], $this->options['sort'], $this->options['parse'] );
}
/**
* Parse Values
*
* @return array
*/
protected function parseOptions()
{
$options = $this->options['options'];
switch ( $this->options['parse'] )
{
case 'lang':
foreach ( $this->options['options'] as $k => $v )
{
if ( is_array( $v ) )
{
foreach ( $v as $x => $y )
{
$options[ $k ][ $x ] = \IPS\Member::loggedIn()->language()->addToStack( $y );
}
}
else
{
$options[ $k ] = \IPS\Member::loggedIn()->language()->addToStack( $v );
}
}
break;
case 'normal':
foreach ( $this->options['options'] as $k => $v )
{
if ( is_array( $v ) )
{
foreach ( $v as $x => $y )
{
$options[ $k ][ $x ] = htmlentities( $y, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
}
else
{
$options[ $k ] = htmlentities( $v, \IPS\HTMLENTITIES, 'UTF-8', FALSE );
}
}
break;
}
return $options;
}
/**
* Get value
*
* @return array
*/
public function getValue()
{
/* Unlimited? */
$unlimitedName = "{$this->name}_unlimited";
if ( $this->options['unlimited'] !== NULL and isset( \IPS\Request::i()->$unlimitedName ) )
{
return $this->options['unlimited'];
}
/* Get value */
$value = parent::getValue();
if( isset( $this->options['userSuppliedInput'] ) )
{
if( $value == $this->options['userSuppliedInput'] )
{
$name = $this->options['userSuppliedInput'] . '_' . $this->name;
$value = mb_strpos( $name, '[' ) ? \IPS\Request::i()->valueFromArray( $name ) : \IPS\Request::i()->$name;
}
}
if ( isset( $value ) AND is_array( $value ) AND $this->options['multiple'] )
{
unset( $value[ array_search( '__EMPTY', $value ) ] );
}
if ( $this->options['returnLabels'] )
{
if ( is_array( $value ) )
{
$return = array();
foreach ( $value as $k => $v )
{
$return[ $k ] = $this->options['options'][ $v ];
}
return $return;
}
else
{
return $this->options['options'][ $value ];
}
}
return $value;
}
/**
* Validate
*
* @throws \OutOfRangeException
* @return TRUE
*/
public function validate()
{
if( $this->options['userSuppliedInput'] )
{
return TRUE;
}
parent::validate();
$acceptableValues = array();
if ( $this->options['unlimited'] !== NULL )
{
$acceptableValues[] = $this->options['unlimited'];
}
foreach ( $this->options['options'] as $k => $v )
{
if ( is_array( $v ) )
{
$acceptableValues = array_merge( $acceptableValues, $this->options['returnLabels'] ? $v : array_keys( $v ) );
}
else
{
$acceptableValues[] = $this->options['returnLabels'] ? $v : $k;
}
}
$diff = array_diff( ( is_array( $this->value ) ? $this->value : array( $this->value ) ), $acceptableValues );
if ( !empty( $diff ) )
{
throw new \OutOfRangeException( 'form_bad_value' );
}
if ( $this->options['multiple'] and $this->required and empty( $this->value ) )
{
throw new \DomainException( 'form_required' );
}
return TRUE;
}
}
+116
View File
@@ -0,0 +1,116 @@
<?php
/**
* @brief Form builder helper to allow user-created "groups" of other users
* @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 10 Mar 2014
* @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;
}
/**
* Form builder helper to allow user-created "groups" of other users
*/
class _SocialGroup extends Member
{
/**
* @brief Default Options
* @code
$childDefaultOptions = array(
'owner' => 1, // \IPS\Member object or member ID who "owns" this group
'multiple' => 1, // Maximum number of members. NULL for any. Default is 1.
);
* @endcode
*/
public $childDefaultOptions = array(
'multiple' => NULL,
'owner' => NULL,
);
/**
* @brief Group ID, if already set
*/
public $groupId = NULL;
/**
* Constructor
* Sets that the field is required if there is a minimum length and vice-versa
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
*/
public function __construct()
{
/* Call parent constructor */
call_user_func_array( 'parent::__construct', func_get_args() );
/* If we are editing, the value will be a group ID we need to load */
if( is_int( $this->value ) )
{
$this->groupId = $this->value;
$values = array();
foreach( \IPS\Db::i()->select( '*', 'core_sys_social_group_members', array( 'group_id=?', $this->value ) )->setKeyField('member_id')->setValueField('member_id') as $k => $v )
{
$values[ $k ] = \IPS\Member::load( $v );
}
$this->value = $values;
}
/* Make sure we have an owner...fall back to logged in member */
if( !$this->options['owner'] )
{
$this->options['owner'] = \IPS\Member::loggedIn();
}
else if( !$this->options['owner'] instanceof \IPS\Member AND is_int( $this->options['owner'] ) )
{
$this->options['owner'] = \IPS\Member::load( $this->options['owner'] );
}
}
/**
* Save the social group
*
* @return void
*/
public function saveValue()
{
/* Delete any existing entries */
if( $this->groupId )
{
\IPS\Db::i()->delete( 'core_sys_social_group_members', array( 'group_id=?', $this->groupId ) );
}
else if( $this->value )
{
$this->groupId = \IPS\Db::i()->insert( 'core_sys_social_groups', array( 'owner_id' => $this->options['owner']->member_id ) );
}
if( $this->value )
{
$inserts = array();
foreach( $this->value as $member )
{
$inserts[] = array( 'group_id' => $this->groupId, 'member_id' => $member->member_id );
}
if( count( $inserts ) )
{
\IPS\Db::i()->insert( 'core_sys_social_group_members', $inserts );
}
}
$this->value = $this->groupId;
}
}
+164
View File
@@ -0,0 +1,164 @@
<?php
/**
* @brief Stack input class 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 11 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;
}
/**
* Stack input class for Form Builder
*/
class _Stack extends FormAbstract
{
/**
* @brief Default Options
*/
protected $defaultOptions = array(
'stackFieldType' => 'Text',
'removeEmptyValues' => TRUE,
);
/**
* Constructor: Store the form element to stack
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @param string $name Name
* @param mixed $defaultValue Default value
* @param bool $required Required?
* @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 )
{
call_user_func_array( 'parent::__construct', func_get_args() );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery-ui.js', 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery-touchpunch.js', 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery.menuaim.js', 'core', 'interface' ) );
/* Test for javascript disabled add stack */
if ( $_SERVER['REQUEST_METHOD'] === 'POST' )
{
if ( ( \IPS\Request::i()->valueFromArray('form_remove_stack') !== NULL OR isset( \IPS\Request::i()->form_add_stack ) ) and \IPS\Request::i()->csrfKey === \IPS\Session::i()->csrfKey )
{
$this->reloadForm = true;
}
}
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
$fields = array();
$classType = mb_strpos( $this->options['stackFieldType'], '\\' ) === FALSE ? ( "\\IPS\\Helpers\\Form\\" . $this->options['stackFieldType'] ) : $this->options['stackFieldType'];
$remove = \IPS\Request::i()->valueFromArray('form_remove_stack');
/* The JS fallback needs a unique key for the field to remove */
$remove = ( is_array( $remove ) ) ? key( $remove ) : null;
if( count($this->value) )
{
foreach( $this->value as $k => $v )
{
$class = ( new $classType( $this->name . '[' . count($fields) . ']', $v, FALSE, $this->options, $this->customValidationCode, $this->prefix, $this->suffix, $this->htmlId . '_' . count($fields) ) );
$html = $class->html();
if ( $remove !== md5( $html ) )
{
$fields[] = $html;
}
}
}
else
{
$class = ( new $classType( $this->name . '[0]', NULL, FALSE, $this->options, $this->customValidationCode, $this->prefix, $this->suffix, $this->htmlId . '_' . count($fields) ) );
$html = $class->html();
if ( $remove !== md5( $html ) )
{
$fields[] = $html;
}
}
/* We hit the add stack button with JS disabled */
if ( $this->reloadForm === TRUE AND $remove === NULL )
{
$class = ( new $classType( $this->name . '[]', NULL, FALSE, $this->options, $this->customValidationCode, $this->prefix, $this->suffix, $this->htmlId . '_' . count($fields) ) );
$html = $class->html();
$fields[] = $html;
}
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->stack( $this->name, $fields );
}
/**
* Format Value
*
* @return mixed
*/
public function formatValue()
{
$values = array();
$classType = mb_strpos( $this->options['stackFieldType'], '\\' ) === FALSE ? ( "\\IPS\\Helpers\\Form\\" . $this->options['stackFieldType'] ) : $this->options['stackFieldType'];
$name = $this->name;
if( mb_substr( $name, 0, 8 ) !== '_new_[x]' and isset( \IPS\Request::i()->$name ) )
{
if( is_array( \IPS\Request::i()->$name ) AND count( \IPS\Request::i()->$name ) )
{
foreach( \IPS\Request::i()->$name as $k => $v )
{
$class = ( new $classType( $this->name . '[]', $v, FALSE, $this->options, $this->customValidationCode, $this->prefix, $this->suffix, $this->htmlId ) );
$values[] = $class->value;
}
}
return ( $this->options['removeEmptyValues'] ) ? array_filter( $values ) : $values;
}
else if ( is_array( $this->value ) AND count( $this->value ) )
{
foreach( $this->value as $k => $v )
{
$class = ( new $classType( $this->name . '[]', $v, FALSE, $this->options, $this->customValidationCode, $this->prefix, $this->suffix, $this->htmlId ) );
$values[] = $class->value;
}
return ( $this->options['removeEmptyValues'] ) ? array_filter( $values ) : $values;
}
else
{
return array();
}
}
public function validate()
{
if ( empty( $this->value ) and $this->required )
{
throw new \InvalidArgumentException('form_required');
}
parent::validate();
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
/**
* @brief Telephone input class 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 11 Mar 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;
}
/**
* Telephone input class for Form Builder
*/
class _Tel extends Text
{
}
+398
View File
@@ -0,0 +1,398 @@
<?php
/**
* @brief Text input class 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 18 Feb 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;
}
/**
* Text input class for Form Builder
*/
class _Text extends TextArea
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'minLength' => 1, // Minimum number of characters. NULL is no minimum. Default is NULL.
'maxLength' => 255, // Maximum number of characters. NULL is no maximum. Default is NULL.
'size' => 20, // Text input size. NULL will use default size. Default is NULL.
'disabled' => FALSE, // Disables input. Default is FALSE.
'autocomplete' => array( // An array of options for autocomplete.
'source' => array(), // An array of values, or a URI which will be passed an 'input' parameter and return a JSON array of autocomplete values,
'freeChoice' => TRUE, // If FALSE, users will only be able to choose from autocomplete values
'maxItems' => 5, // Maximum number of items (if it should be unlimited, do not specify this element)
'minItems' => 2, // Minimum number of items (if it should be unlimited, do not specify this element) - if field is not required, 0 items will be allowed
'unique' => TRUE, // Specifies if the values must be unique
'forceLower' => TRUE, // If TRUE, all values will be converted to lowercase
'minLength' => 5, // The minimum length of each tag (characters) - if not specified, will be unlimited
'maxLength' => 10, // The minimum length of each tag (characters) - if not specified, will be unlimited
'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
),
'placeholder' => 'e.g. ...', // A placeholder (NB: Will only work on compatible browsers)
'regex' => '/[A-Z]+/i', // RegEx of acceptable value
'nullLang' => 'no_value', // If provided, an "or X" checkbox will appear with X being the value of this language key. When checked, NULL will be returned as the value.
'accountUsername' => TRUE, // If TRUE or an \IPS\Member, additional checks will be performed to ensure provided value is acceptable for use as a username. Pass an \IPS\Member object to exclude that member from the duplicate checks
'trim' => TRUE, // If TRUE (which is the default), whitespace will be stripped from the start and end of the value
);
* @endcode
*/
protected $defaultOptions = array(
'minLength' => NULL,
'maxLength' => NULL,
'size' => NULL,
'disabled' => FALSE,
'autocomplete' => NULL,
'placeholder' => NULL,
'regex' => NULL,
'nullLang' => NULL,
'accountUsername' => FALSE,
'trim' => TRUE,
);
/**
* @brief Child default Options
*/
protected $childDefaultOptions = array();
/**
* @brief Form type
*/
public $formType = 'text';
/**
* Constructor
* Sets that the field is required if there is a minimum length and vice-versa
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
*/
public function __construct()
{
/* Pull in default options from child class */
$this->defaultOptions = array_merge( $this->defaultOptions, $this->childDefaultOptions );
/* Set username regex */
$args = func_get_args();
if ( isset( $args[3]['accountUsername'] ) and $args[3]['accountUsername'] === TRUE )
{
$args[3]['maxLength'] = \IPS\Settings::i()->max_user_name_length;
if ( \IPS\Settings::i()->username_characters )
{
$args[3]['regex'] = '/^[' . str_replace( '\-', '-', preg_quote( \IPS\Settings::i()->username_characters, '/' ) ) . ']*$/i';
}
}
/* Call parent constructor */
call_user_func_array( 'parent::__construct', $args );
/* Add JS */
if ( isset( $this->options['autocomplete']['prefix'] ) and $this->options['autocomplete']['prefix'] )
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'global_core.js', 'core', 'global' ) );
}
/* Set the form type */
$this->formType = mb_strtolower( mb_substr( get_called_class(), mb_strrpos( get_called_class(), '\\' ) + 1 ) );
}
/**
* Get HTML
*
* @return string
* @note We cannot pass the regex to the HTML5 'pattern' attribute for two reasons:
* @li PCRE and ECMAScript regex are not 100% compatible (though the instances this present a problem are admittedly rare)
* @li You cannot specify modifiers with the pattern attribute, which we need to support on the PHP side
*/
public function html()
{
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 );
}
/**
* Get Value
*
* @return mixed
*/
public function getValue()
{
$name = $this->name . '_noscript';
if ( isset( \IPS\Request::i()->$name ) )
{
$return = \IPS\Request::i()->$name;
}
else
{
$return = parent::getValue();
}
if ( $this->options['trim'] )
{
if ( is_array( $return ) )
{
$return = array_map( 'trim', $return );
}
else
{
$return = trim( $return );
}
}
/* Remove all invisible characters */
$return = preg_replace( '/\p{C}+/u', '', $return );
if ( isset( $this->options['autocomplete']['prefix'] ) and $this->options['autocomplete']['prefix'] )
{
if ( !is_array( $return ) )
{
$return = array_filter( explode( ',', $return ) );
}
$firstAsPrefix = $this->name . '_first_as_prefix';
$freechoicePrefixCheckbox = $this->name . '_freechoice_prefix';
$freechoicePrefix = $this->name . '_prefix';
$noscriptPrefix = $this->name . '_noscript_prefix';
if ( isset( \IPS\Request::i()->$noscriptPrefix ) and \IPS\Request::i()->$noscriptPrefix )
{
$currentIndex = array_search( \IPS\Request::i()->$noscriptPrefix, $return );
if ( $currentIndex !== FALSE )
{
unset( $return[ $currentIndex ] );
}
$return['prefix'] = \IPS\Request::i()->$noscriptPrefix;
}
elseif ( isset( \IPS\Request::i()->$freechoicePrefixCheckbox ) and \IPS\Request::i()->$freechoicePrefixCheckbox and isset( \IPS\Request::i()->$freechoicePrefix ) and \IPS\Request::i()->$freechoicePrefix )
{
$currentIndex = array_search( \IPS\Request::i()->$freechoicePrefix, $return );
if ( $currentIndex !== FALSE )
{
unset( $return[ $currentIndex ] );
}
$return['prefix'] = \IPS\Request::i()->$freechoicePrefix;
}
elseif ( isset( \IPS\Request::i()->$firstAsPrefix ) and \IPS\Request::i()->$firstAsPrefix )
{
$return = array_merge( array( 'prefix' => array_shift( $return ) ), $return );
}
}
return $return;
}
/**
* Format Value
*
* @return mixed
*/
public function formatValue()
{
if ( $this->options['autocomplete'] !== NULL and ( !isset( $this->options['autocomplete']['maxItems'] ) or $this->options['autocomplete']['maxItems'] != 1 ) and !is_array( $this->value ) and $this->value !== NULL )
{
return array_filter( array_map( 'trim', explode( ',', $this->value ) ) );
}
return $this->value;
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @throws \DomainException
* @return TRUE
*/
public function validate()
{
parent::validate();
if( \IPS\Dispatcher::i() instanceof \IPS\Dispatcher\Front and !\IPS\Member::loggedIn()->group['g_bypass_badwords'] )
{
/* Set up profanity filters */
$exactProfanity = iterator_to_array( \IPS\Db::i()->select( '*', 'core_profanity_filters', array('m_exact=?', TRUE) )->setKeyField( 'type' )->setValueField( 'swop' ) );
$looseProfanity = iterator_to_array( \IPS\Db::i()->select( '*', 'core_profanity_filters', array('m_exact=?', FALSE) )->setKeyField( 'type' )->setValueField( 'swop' ) );
foreach( $exactProfanity as $key => $value )
{
$exactProfanity[mb_strtolower( $key )] = $value;
}
/* Construct break points */
$breakPoints = array();
if( count( $exactProfanity ) )
{
$array = array();
foreach( array_keys( $exactProfanity ) as $entry )
{
$array[] = preg_quote( $entry, '/' );
}
/* @note: We use \b because of http://community.invisionpower.com/resources/bugs.html/_/4-0-0/anacronym-doesnt-work-in-middle-of-a-phrase-r47612 */
$breakPoints[] = '((?=<^|\b)(?:' . implode( '|', $array ) . ')(?=\b|$))';
}
}
/* Regex */
if ( $this->options['regex'] !== NULL and $this->value and !preg_match( $this->options['regex'], $this->value ) )
{
throw new \InvalidArgumentException( 'form_bad_value' );
}
/* Username */
if ( $this->options['accountUsername'] )
{
/* Check if it exists */
if ( !( $this->options['accountUsername'] instanceof \IPS\Member ) or mb_strtolower( $this->options['accountUsername']->name ) !== mb_strtolower( $this->value ) )
{
foreach( \IPS\Login::handlers( TRUE ) as $k => $handler )
{
if( $handler->usernameIsInUse( $this->value, $this->options['accountUsername'] ) === TRUE )
{
if( \IPS\Member::loggedIn()->isAdmin() )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'member_name_exists_admin', FALSE, array('sprintf' => array($k)) ) );
}
else
{
throw new \DomainException( 'member_name_exists' );
}
}
}
/* Check it's not banned */
foreach( \IPS\Db::i()->select( 'ban_content', 'core_banfilters', array("ban_type=?", 'name') ) as $bannedName )
{
if( preg_match( '/^' . str_replace( '\*', '.*', preg_quote( $bannedName, '/' ) ) . '$/i', $this->value ) )
{
throw new \DomainException( 'form_name_banned' );
}
}
}
}
/* Tags */
if ( $this->options['autocomplete'] !== NULL )
{
if( $this->value )
{
$values = ( is_array( $this->value ) ) ? $this->value : array( $this->value );
}
else
{
$values = array();
}
if ( isset( $this->options['autocomplete']['maxItems'] ) and count( $values ) > $this->options['autocomplete']['maxItems'] )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_tags_max', FALSE, array( 'pluralize' => $this->options['autocomplete']['maxItems'] ) ) );
}
if ( isset( $this->options['autocomplete']['minItems'] ) and ( $this->required or count( $values ) > 0 ) and count( $values ) < $this->options['autocomplete']['minItems'] )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_tags_min', FALSE, array( 'pluralize' => $this->options['autocomplete']['minItems'] ) ) );
}
if ( isset( $this->options['autocomplete']['minLength'] ) )
{
foreach ( $values as $v )
{
if ( mb_strlen( $v ) < $this->options['autocomplete']['minLength'] )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_tags_length_min', FALSE, array( 'pluralize' => array( $this->options['autocomplete']['minLength'] ) ) ) );
}
}
}
if ( isset( $this->options['autocomplete']['maxLength'] ) )
{
foreach ( $values as $v )
{
if ( mb_strlen( $v ) > $this->options['autocomplete']['maxLength'] )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'form_tags_length_max', FALSE, array( 'pluralize' => $this->options['autocomplete']['maxLength'] ) ) );
}
}
}
if ( isset( $this->options['autocomplete']['filterProfanity'] ) AND is_array( $this->value ) AND count( $this->value ) and \IPS\Dispatcher::i() instanceof \IPS\Dispatcher\Front and !\IPS\Member::loggedIn()->group['g_bypass_badwords'] )
{
foreach ( $values as $k => $v )
{
if ( array_key_exists( $v, $exactProfanity ) )
{
$this->value[ $k ] = $exactProfanity[ $v ];
}
else
{
$this->value[ $k ] = str_replace( array_keys( $looseProfanity ), array_values( $looseProfanity ), $v );
}
}
}
if ( isset( $this->options['autocomplete']['unique'] ) AND $this->options['autocomplete']['unique'] AND is_array( $this->value ) AND count( $this->value ) )
{
foreach ( $this->value as $v )
{
if ( is_scalar( $v ) )
{
$this->value = array_unique( $this->value );
}
break;
}
}
if ( is_array ( $this->value ) AND isset( $this->options['autocomplete']['source'] ) AND is_array( $this->options['autocomplete']['source'] ) AND !$this->options['autocomplete']['freeChoice'] )
{
$this->value = array_uintersect( $this->value, $this->options['autocomplete']['source'], function( $a, $b ){
return ( mb_strtolower( $a ) === mb_strtolower( $b ) );
} );
}
}
/* Split on profanity */
if( is_string( $this->value ) and \IPS\Dispatcher::i() instanceof \IPS\Dispatcher\Front and !\IPS\Member::loggedIn()->group['g_bypass_badwords'] )
{
$newVal = NULL;
if ( count( $breakPoints ) )
{
/* preg_split can return boolean false*/
$split = preg_split( '/' . implode( '|', $breakPoints ) . '/iu', $this->value, null, PREG_SPLIT_DELIM_CAPTURE );
if( is_array( $split ) )
{
foreach ( $split as $section )
{
/* Replace loose profanity */
$section = str_ireplace( array_keys( $looseProfanity ), array_values( $looseProfanity ), $section );
if ( isset( $exactProfanity[ mb_strtolower( $section ) ] ) )
{
$newVal .= $exactProfanity[ mb_strtolower( $section ) ];
}
else
{
$newVal .= $section;
}
}
}
}
$this->value = $newVal ?: $this->value;
}
return TRUE;
}
}
+133
View File
@@ -0,0 +1,133 @@
<?php
/**
* @brief Text input class 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 18 Feb 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;
}
/**
* Text input class for Form Builder
*/
class _TextArea extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'minLength' => 1, // Minimum number of characters. NULL is no minimum. Default is NULL.
'maxLength' => 255, // Maximum number of characters. NULL is no maximum. Default is NULL.
'disabled' => FALSE, // Disables input. Default is FALSE.
'placeholder' => 'e.g. ...', // A placeholder (NB: Will only work on compatible browsers)
'nullLang' => 'no_value', // If provided, an "or X" checkbox will appear with X being the value of this language key. When checked, NULL will be returned as the value.
'tags' => array(), // An array of extra insertable tags in key => value pair with key being what is inserted and value serving as a description
'class' => 'ipsField_codeInput', // Additional CSS class
);
* @endcode
*/
protected $defaultOptions = array(
'minLength' => NULL,
'maxLength' => NULL,
'disabled' => FALSE,
'placeholder' => NULL,
'nullLang' => NULL,
'tags' => array(),
'rows' => NULL,
'class' => '',
);
/**
* Constructor
* Sets that the field is required if there is a minimum length and vice-versa
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
*/
public function __construct()
{
/* Call parent constructor */
call_user_func_array( 'parent::__construct', func_get_args() );
/* If we have a minimum length, the field is required */
if( $this->options['minLength'] >= 1 )
{
$this->required = TRUE;
}
elseif( !$this->options['minLength'] and $this->required )
{
$this->options['minLength'] = 1;
}
/* Append needed javascript if appropriate */
if( !empty( $this->options['tags'] ) )
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'jquery/jquery.rangyinputs.js', 'core', 'interface' ) );
}
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->textarea( $this->name, $this->value, $this->required, $this->options['maxLength'], $this->options['disabled'], $this->options['class'], $this->options['placeholder'], $this->options['nullLang'], $this->options['tags'], $this->options['rows'] );
}
/**
* Get value
*
* @return array
*/
public function getValue()
{
$nullName = "{$this->name}_null";
if ( $this->options['nullLang'] !== NULL and isset( \IPS\Request::i()->$nullName ) )
{
return NULL;
}
return parent::getValue();
}
/**
* Validate
*
* @throws \LengthException
* @return TRUE
*/
public function validate()
{
parent::validate();
/* Tags are stored as an array so we can't do things like mb_strlen() against them */
if( is_array( $this->value ) )
{
return TRUE;
}
if( $this->options['minLength'] !== NULL and mb_strlen( $this->value ) < $this->options['minLength'] )
{
throw new \LengthException( \IPS\Member::loggedIn()->language()->addToStack( 'form_minlength', FALSE, array( 'pluralize' => array( $this->options['minLength'] ) ) ) );
}
if( $this->options['maxLength'] !== NULL and mb_strlen( $this->value ) > $this->options['maxLength'] )
{
throw new \LengthException( \IPS\Member::loggedIn()->language()->addToStack( 'form_maxlength', FALSE, array( 'pluralize' => array( $this->options['maxLength'] ) ) ) );
}
return TRUE;
}
}
+134
View File
@@ -0,0 +1,134 @@
<?php
/**
* @brief Translatable text input class 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 25 Mar 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;
}
/**
* Translatable text input class for Form Builder
*/
class _Translatable extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'key' => 'foo', // Language key
'editor' => array(...), // If this needs to be an editor rather than a textbox, all the options of \IPS\Helpers\Form\Editor are available here
'textArea' => FALSE, // Makes a textarea rather than a textbox
'placeholder' => 'Example', // Placeholder
);
* @endcode
*/
protected $defaultOptions = array(
'key' => NULL,
'editor' => NULL,
'textArea' => FALSE,
'placeholder' => NULL
);
/**
* @brief Editors
*/
protected $editors = array();
/**
* Constructor
* Sets that the field is required if there is a minimum length and vice-versa
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @return void
*/
public function __construct()
{
/* Call parent constructor */
call_user_func_array( 'parent::__construct', func_get_args() );
/* Get current values */
if ( $this->value === NULL )
{
$values = array();
if ( $this->options['key'] )
{
foreach( \IPS\Db::i()->select( '*', 'core_sys_lang_words', array( 'word_key=?', $this->options['key'] ) )->setKeyField('lang_id') as $k => $v )
{
$v = $v['word_custom'] ?: $v['word_default'];
if ( $v or !isset( $values[ $k ] ) )
{
$values[ $k ] = $v;
}
}
}
$this->value = $values;
}
elseif ( is_string( $this->value ) )
{
$values = array();
foreach ( \IPS\Lang::getEnabledLanguages() as $lang )
{
$values[ $lang->id ] = $this->value;
}
$this->value = $values;
}
/* Init editors */
if ( isset( $this->options['editor'] ) )
{
foreach ( \IPS\Lang::getEnabledLanguages() as $lang )
{
$options = $this->options['editor'];
$options['autoSaveKey'] .= $lang->id;
$this->editors[ $lang->id ] = new Editor( "{$this->name}[{$lang->id}]", isset( $this->value[ $lang->id ] ) ? $this->value[ $lang->id ] : NULL, $this->required, $options );
}
}
/* Add flags.css */
\IPS\Output::i()->cssFiles = array_merge( \IPS\Output::i()->cssFiles, \IPS\Theme::i()->css( 'flags.css', 'core', 'global' ) );
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->translatable( $this->name, \IPS\Lang::getEnabledLanguages(), $this->value, $this->editors, $this->options['placeholder'], $this->options['textArea'], $this->required );
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
parent::validate();
if ( $this->required )
{
if ( ! trim( $this->value[ \IPS\Lang::defaultLanguage() ] ) )
{
throw new \InvalidArgumentException('form_required');
}
}
return TRUE;
}
}
+699
View File
@@ -0,0 +1,699 @@
<?php
/**
* @brief Upload class 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 18 Feb 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;
}
/**
* Upload class for Form Builder
*/
class _Upload extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'storageExtension' => 'Profile', // The file storage extension to use. This is required if postKey is NULL and temporary is FALSE.
'storageContainer' => NULL, // The file storage container to use.
'multiple' => TRUE, // Specifies if the field should allow multiple file uploads. Default is FALSE.
'image' => array( 'maxWidth' => 100, 'maxHeight' => 100 ), // If the upload must be an image, can pass TRUE or an array with max width and height (in which case, image will be resized appropriately). Default is NULL. Max width/height cannot be used in conjunction with temporary uploads. If it can be, but doesn't have to an image, and you still want to specify max width and height, add an "optional" property set to true
'allowedFileTypes' => array( 'pdf', 'txt' ), // Allowed file extensions. NULL allows any. Default is NULL.
'maxFileSize' => 100, // Maximum file size in megabytes. NULL is no limit. Default is NULL. Note that there *may* be server limitations regardless of this value which are calculated automatically.
'totalMaxSize' => 100, // If this is a "multiple" upload field, the maximum storage space allowed in total in megabytes.
'postKey' => 'abc', // If provided, uploads will be treated as post attachments using the given post key
'temporary' => TRUE, // If TRUE, the image will not be moved and the filename returned, rather than an \IPS\File object. This should ONLY be used for files which are genuinely
temporary (e.g. importing skins, languages) as the file will be deleted after the script finished executing. Default is FALSE.
'callback' => function() { ... }, // A callback function to run against submitted files
'minimize' => TRUE, // Default is minimized. Pass FALSE to show the maximized field. Cannot be used in conjunction with temporary uploads
'retainDeleted' => FALSE, // By default, if you specify a default value and the user deletes the files specified, the files will be physically deleted. This option overrides this behaviour.
'template' => 'core.attachments.fileItem' // The javascript template key to use when rendering uploaded items
'obscure' => TRUE, // Controls if an md5 hash should be added to the filename. *Must* be TRUE unless the uploaded files are public to all users (like emoticons)
);
* @endcode
*/
protected $defaultOptions = array(
'multiple' => FALSE,
'image' => NULL,
'allowedFileTypes' => NULL,
'maxFileSize' => NULL,
'totalMaxSize' => NULL,
'postKey' => NULL,
'storageExtension' => NULL,
'storageContainer' => NULL,
'temporary' => FALSE,
'callback' => NULL,
'minimize' => TRUE,
'retainDeleted' => FALSE,
'template' => 'core.attachments.fileItem',
'default' => NULL,
'obscure' => TRUE
);
/**
* @brief Max chunk size (in MB)
*/
protected $maxChunkSize;
/**
* @brief Template
*/
public $template;
/**
* Constructor
* Sets that the max file size based on PHP's limits as well as the specified one
*
* @see \IPS\Helpers\Form\Abstract::__construct
* @param string $name Name
* @return void
*/
public function __construct( $name )
{
/* What's PHP's upload limit */
$potentialValues = array();
if( (float) ini_get('upload_max_filesize') > 0 )
{
$potentialValues[] = (float) ini_get('upload_max_filesize');
}
if( (float) ini_get('post_max_size') > 0 )
{
$potentialValues[] = (float) ini_get('post_max_size');
}
if( (float) ini_get('memory_limit') > 0 )
{
$potentialValues[] = (float) ini_get('memory_limit');
}
$this->maxChunkSize = min( $potentialValues );
/* Call parent constructor */
call_user_func_array( 'parent::__construct', func_get_args() );
/* Work out storage extension */
if ( $this->options['postKey'] and !$this->options['storageExtension'] )
{
$this->options['storageExtension'] = 'core_Attachment';
}
if ( !$this->options['storageExtension'] and !$this->options['temporary'] )
{
throw new \InvalidArgumentException;
}
/* Does the storage extension support chunking? */
if ( $this->options['storageExtension'] )
{
$storageClass = \IPS\File::getClass( $this->options['storageExtension'] );
if( !$storageClass::$supportsChunking and ( $this->options['maxFileSize'] === NULL or $this->maxChunkSize < $this->options['maxFileSize'] ) )
{
$this->options['maxFileSize'] = $this->maxChunkSize;
}
}
if( $this->options['maxFileSize'] !== NULL AND $this->options['maxFileSize'] <= 0 )
{
throw new \InvalidArgumentException;
}
/* If this has to be an image, set the allowed file types */
if ( $this->options['image'] !== NULL and !isset( $this->options['image']['optional'] ) and $this->options['allowedFileTypes'] === NULL )
{
$this->options['allowedFileTypes'] = \IPS\Image::$imageExtensions;
}
/* Add JS */
if ( \IPS\IN_DEV )
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'plupload/moxie.js', 'core', 'interface' ) );
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'plupload/plupload.dev.js', 'core', 'interface' ) );
}
else
{
\IPS\Output::i()->jsFiles = array_merge( \IPS\Output::i()->jsFiles, \IPS\Output::i()->js( 'plupload/plupload.full.min.js', 'core', 'interface' ) );
}
if ( \IPS\Settings::i()->ipb_url_filter_option != 'none' )
{
$links = \IPS\Settings::i()->ipb_url_filter_option == "black" ? \IPS\Settings::i()->ipb_url_blacklist : \IPS\Settings::i()->ipb_url_whitelist;
if( $links )
{
$linkValues = array();
$linkValues = explode( "," , $links );
if( \IPS\Settings::i()->ipb_url_filter_option == 'white' )
{
$listValues[] = "http://" . parse_url( \IPS\Settings::i()->base_url, PHP_URL_HOST ) . "/*";
}
if ( !empty( $linkValues ) )
{
\IPS\Output::i()->headJs = \IPS\Output::i()->headJs . "ips.setSetting( '" . \IPS\Settings::i()->ipb_url_filter_option . "list', " . json_encode( $linkValues ) . " );";
}
}
}
/* Are we processing an AJAX upload? */
if( isset( $_SERVER['HTTP_X_PLUPLOAD'] ) AND ( $_SERVER['HTTP_X_PLUPLOAD'] == md5( $this->name . session_id() ) ) )
{
try
{
if ( $storageClass::$supportsChunking and isset( \IPS\Request::i()->chunks ) and \IPS\Request::i()->chunks > 1 )
{
$chunkName = 'chunk-' . md5( \IPS\Request::i()->name ) . '-' . $this->name . '-' . session_id() . '.txt';
$lastChunk = ( \IPS\Request::i()->chunk == \IPS\Request::i()->chunks - 1 );
if ( \IPS\Request::i()->chunk == 0 )
{
$url = \IPS\File::create( $this->options['storageExtension'], $chunkName, NULL, $this->options['storageContainer'], FALSE, $_FILES[ $this->name ]['tmp_name'], FALSE );
}
else
{
$url = $storageClass->chunkAppend( $chunkName, $_FILES[ $this->name ]['tmp_name'], $lastChunk ? \IPS\Request::i()->name : NULL, $this->options['storageContainer'] );
}
if ( $lastChunk )
{
$fileObj = \IPS\File::get( $this->options['storageExtension'], $url );
$fileArray = array(
'error' => NULL,
'_skipUploadCheck' => TRUE,
'size' => $fileObj->filesize(),
'name' => $fileObj->originalFilename,
'tmp_name' => $_FILES[ $this->name ]['tmp_name']
);
/* If there is an error, an exception will be thrown and will be caught below like normal */
\IPS\File::validateUpload( $fileArray, $this->options['allowedFileTypes'], $this->options['maxFileSize'] );
if ( is_array( $this->options['image'] ) and $fileObj->isImage() )
{
$options = $this->options;
try
{
$image = \IPS\Image::create( $fileObj->contents() );
$image->resizeToMax( $options['image']['maxWidth'] ?: NULL, $options['image']['maxHeight'] ?: NULL );
$fileObj->replace( (string) $image );
}
catch ( \Exception $e ) { }
}
$insertId = \IPS\Db::i()->insert( 'core_files_temp', array(
'upload_key' => md5( $this->name . session_id() ),
'filename' => $fileObj->originalFilename,
'mime' => \IPS\File::getMimeType( $fileObj->originalFilename ),
'contents' => (string) $fileObj,
'time' => time()
) );
if ( $this->options['callback'] )
{
$r = call_user_func( $this->options['callback'], $fileObj );
if ( $r !== NULL )
{
$insertId = $r;
}
}
\IPS\Output::i()->json( array(
'id' => $insertId,
'key' => $_SERVER['HTTP_X_PLUPLOAD'],
'imagesrc' => $fileObj->isImage() ? (string) $fileObj->url : NULL,
'thumbnail' => ( $fileObj->isImage() AND $fileObj->attachmentThumbnailUrl !== NULL and is_string( $fileObj->attachmentThumbnailUrl ) ) ? \IPS\File::get( $this->options['storageExtension'], $fileObj->attachmentThumbnailUrl )->url : NULL,
) );
}
else
{
\IPS\Output::i()->json( array( 'chunk' => 'OK' ) );
}
}
else
{
foreach ( $this->processUploads() as $insertId => $fileObj )
{
\IPS\Output::i()->json( array(
'id' => $insertId,
'key' => $_SERVER['HTTP_X_PLUPLOAD'],
'imagesrc' => $fileObj->isImage() ? (string) $fileObj->url : NULL,
'thumbnail' => ( $fileObj->isImage() AND $fileObj->attachmentThumbnailUrl !== NULL and is_string( $fileObj->attachmentThumbnailUrl ) ) ? \IPS\File::get( $this->options['storageExtension'], $fileObj->attachmentThumbnailUrl )->url : NULL,
) );
}
}
}
catch ( \Exception $e )
{
$message = $e->getMessage();
if ( \IPS\Member::loggedIn()->isAdmin() and \IPS\Member::loggedIn()->language()->checkKeyExists("uploaderr_{$e->getMessage()}_admin") )
{
$message = \IPS\Member::loggedIn()->language()->addToStack("uploaderr_{$e->getMessage()}_admin");
}
elseif ( \IPS\Member::loggedIn()->language()->checkKeyExists("uploaderr_{$e->getMessage()}") )
{
$message = \IPS\Member::loggedIn()->language()->addToStack("uploaderr_{$e->getMessage()}");
}
\IPS\Output::i()->json( array(
'error' => $message,
'extra' => $e->getCode()
) );
}
}
/* Set the template */
$this->template = array( \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' ), 'upload' );
}
/**
* @brief Build the HTML once or we lose the custom properties sent
*/
protected $builtHtml = NULL;
/**
* Get HTML without row template
*
* @return string
*/
public function html()
{
if( $this->builtHtml !== NULL )
{
return $this->builtHtml;
}
$uploadKey = md5( $this->name . session_id() );
/* Put the value in an array if we need to */
if ( $this->value !== NULL and ! empty( $this->value ) )
{
if ( $this->options['multiple'] !== TRUE and !is_array( $this->value ) )
{
$this->value = array( $this->value );
}
}
/* Build JSON version of the existing value, which allows the widget to build the interface */
$existing = array();
if( $this->value and count( $this->value ) )
{
foreach( $this->value as $id => $file )
{
/* If this was attachment data, expand */
if( is_array( $file ) )
{
$attachment = $file;
$file = $file['fileurl'];
unset( $attachment['fileurl'] );
}
/* If last loop was an array, we need to unset it this loop otherwise variable persists */
else
{
unset( $attachment );
}
/* Set existing files */
if ( $this->options['temporary'] )
{
$existing[] = array(
'id' => $id,
'fileurl' => (string) $file,
'insertable' => (bool) $this->options['postKey'],
'hasThumb' => false,
'originalFileName' => $file,
'size' => \IPS\Output\Plugin\Filesize::humanReadableFilesize( $file, FALSE, TRUE ),
'custom' => NULL
);
}
else
{
try
{
$existing[] = array(
'id' => $id,
'fileurl' => (string) $file,
'insertable' => (bool) $this->options['postKey'],
'hasThumb' => ( isset( $attachment ) AND isset( $attachment['attach_is_image'] ) ) ? ( $attachment['attach_is_image'] AND $attachment['attach_thumb_location'] ) : $file->isImage(),
'originalFileName' => $file->originalFilename,
'thumbnail' => ( isset( $attachment ) AND isset( $attachment['attach_is_image'] ) AND $attachment['attach_is_image'] AND $attachment['attach_thumb_location'] ) ? ( (string) \IPS\File::get( $this->options['storageExtension'], $attachment['attach_thumb_location'] )->url ) : ( $file->isImage() ? (string) $file->url : NULL ),
'size' => \IPS\Output\Plugin\Filesize::humanReadableFilesize( $file->filesize(), FALSE, TRUE ),
'default' => ( isset( $attachment ) AND $attachment['default'] ) ? $attachment['default'] : NULL,
);
}
catch( \UnderflowException $e ){}
}
}
foreach( $this->value as $id => $file )
{
$this->value[ $id ] = ( is_array( $file ) ) ? $file['fileurl'] : $file;
}
}
/* We want this to use decimals even if locale wants commas for decimal separator, i.e. for uploader */
$maxFileSize = $this->options['maxFileSize'] ? number_format( $this->options['maxFileSize'], 4, '.', '' ) : $this->options['maxFileSize'];
/* The html() method is called more than once, however $this->value is wiped out so if it was an attachments array all of the array properties are lost */
$this->builtHtml = call_user_func( $this->template, $this->name, $this->value, $this->options['minimize'], $maxFileSize, $this->maxChunkSize, $this->options['totalMaxSize'], $this->options['allowedFileTypes'], $uploadKey, $this->options['multiple'], $this->options['postKey'], $this->options['temporary'] or ( isset( \IPS\Request::i()->cookie['vle_editor'] ) and \IPS\Request::i()->cookie['vle_editor'] ), $this->options['template'], $existing, $this->options['default'] );
return $this->builtHtml;
}
/**
* Get Value
*
* @return \IPS\File|array|NULL
* @throws \LogicException
* @throws \DomainException
* @throws \RuntimeException
*/
public function getValue()
{
/* 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') );
if ( $this->options['storageExtension'] )
{
$existingName = $this->name . '_existing';
$keepName = $this->name . '_keep';
$keep = \IPS\Request::i()->$keepName;
if ( isset( \IPS\Request::i()->$existingName ) and is_array( \IPS\Request::i()->$existingName ) )
{
foreach ( \IPS\Request::i()->$existingName as $id => $tempId )
{
if ( isset( $keep[ $id ] ) )
{
if ( $tempId and isset( $tempFiles[ $tempId ] ) )
{
$file = \IPS\File::get( $this->options['storageExtension'], $tempFiles[ $tempId ] );
$file->tempId = $tempId;
$return[ $id ] = $file;
}
else
{
if ( $this->options['multiple'] )
{
if ( isset( $this->defaultValue[ $id ] ) )
{
$return[ $id ] = $this->defaultValue[ $id ];
}
}
elseif ( $id == 0 )
{
$return[ $id ] = $this->defaultValue;
}
}
}
elseif ( !$this->options['retainDeleted'] )
{
if ( $tempId and isset( $tempFiles[ $tempId ] ) )
{
\IPS\Db::i()->delete( 'core_files_temp', array( 'id=?', $tempId ) );
try
{
\IPS\File::get( $this->options['storageExtension'], $tempFiles[ $tempId ] )->delete();
}
catch ( \Exception $e ) { }
}
else
{
if ( $this->options['multiple'] )
{
if ( isset( $this->defaultValue[ $id ] ) )
{
/* Don't delete file if new file upload has same name */
$okToDelete = TRUE;
foreach( $tempFiles as $tid => $path )
{
if ( (string) $this->defaultValue[ $id ] == $path )
{
$okToDelete = FALSE;
}
}
if ( $okToDelete )
{
try
{
$this->defaultValue[ $id ]->delete();
}
catch ( \Exception $e ) { }
}
}
}
else
{
if ( $this->defaultValue )
{
try
{
if ( is_array( $this->defaultValue ) )
{
foreach( $this->defaultValue as $file )
{
/* Don't delete file if new file upload has same name */
$okToDelete = TRUE;
foreach( $tempFiles as $id => $path )
{
if ( (string) $file == $path )
{
$okToDelete = FALSE;
}
}
if ( $okToDelete )
{
$file->delete();
}
}
}
else
{
/* Don't delete file if new file upload has same name */
$okToDelete = TRUE;
foreach( $tempFiles as $id => $path )
{
if ( (string) $this->defaultValue == $path )
{
$okToDelete = FALSE;
}
}
if ( $okToDelete )
{
$this->defaultValue->delete();
}
}
}
catch ( \Exception $e ) { }
}
}
}
}
}
}
}
/* Process files from noscript fallback - If this is just an AJAX validate, don't do anything so we still have the files when we actually submit */
if ( !\IPS\Request::i()->ajaxValidate )
{
try
{
/* We used to use array_merge but this reindexes the array - we want to retain the keys */
$return = $return + $this->processUploads( "{$this->name}_noscript" );
}
catch ( \DomainException $e )
{
/* If there is no file and field is not required, then that's fine */
if( $e->getCode() !== 1 or $this->required )
{
/* We have to format the message because there are variables to swap out. */
if ( \IPS\Member::loggedIn()->language()->checkKeyExists( 'pluploaderr_' . $e->getMessage() ) )
{
$message = \IPS\Member::loggedIn()->language()->get( 'pluploaderr_' . $e->getMessage() );
$message = str_replace( '{{max_file_size}}', $this->maxChunkSize, $message );
if ( is_array( $this->options['allowedFileTypes'] ) )
{
$message = str_replace( '{{allowed_extensions}}', implode( ', ', $this->options['allowedFileTypes'] ), $message );
}
$message = str_replace( '{{server_error_code}}', $e->getCode(), $message );
throw new \DomainException( $message, $e->getCode() );
}
else
{
throw $e;
}
}
}
catch ( \RuntimeException $e )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('upload_error_generic', FALSE, array( 'sprintf' => array( $e->getMessage() ) ) ), $e->getCode() );
}
}
/* Check we haven't exceeded the maximum total size */
if ( $this->options['totalMaxSize'] !== NULL )
{
$total = 0;
foreach ( $return as $file )
{
$total += $file->filesize();
}
if ( $total > $this->options['totalMaxSize'] * 1048576 )
{
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack('uploaderr_total_size', FALSE, array( 'sprintf' => array( $this->options['totalMaxSize'] ) ) ) );
}
}
/* Return */
if ( !$this->options['multiple'] )
{
return array_pop( $return );
}
return $return;
}
/**
* Process Uploads
*
* @param string $fieldName The field name to look for
* @return array
* @throws \DomainException
*/
protected function processUploads( $fieldName=NULL )
{
$return = array();
/* Temporary - just process uploads and return paths */
if ( $this->options['temporary'] )
{
foreach( \IPS\File::normalizeFilesArray( $fieldName ) as $file )
{
\IPS\File::validateUpload( $file, $this->options['allowedFileTypes'], $this->options['maxFileSize'] );
$ext = mb_substr( $file['name'], ( mb_strrpos( $file['name'], '.' ) + 1 ) );
/* Don't allow "XSS" in images */
if( in_array( $ext, \IPS\File::$safeFileExtensions ) AND in_array( $ext, \IPS\Image::$imageExtensions ) )
{
if( \IPS\File::checkXssInFile( file_get_contents( $file['tmp_name'] ) ) )
{
throw new \DomainException( "SECURITY_EXCEPTION_RAISED", 99 );
}
}
$return[] = $file['tmp_name'];
}
return $return;
}
/* Normal - send to storage extension */
else
{
if ( is_array( $this->options['image'] ) )
{
$options = $this->options;
$fileObjects = \IPS\File::createFromUploads( $this->options['storageExtension'], $fieldName, $this->options['allowedFileTypes'], $this->options['maxFileSize'], $this->options['totalMaxSize'], 0, function( $contents ) use ( $options )
{
try
{
$image = \IPS\Image::create( $contents );
$image->resizeToMax( $options['image']['maxWidth'] ?: NULL, $options['image']['maxHeight'] ?: NULL );
return (string) $image;
}
catch ( \Exception $e ) {}
return $contents;
}, $this->options['storageContainer'], $this->options['obscure'] );
}
else
{
$fileObjects = \IPS\File::createFromUploads( $this->options['storageExtension'], $fieldName, $this->options['allowedFileTypes'], $this->options['maxFileSize'], $this->options['totalMaxSize'], 0, NULL, $this->options['storageContainer'], $this->options['obscure'] );
}
foreach ( $fileObjects as $fileObj )
{
$insertId = \IPS\Db::i()->insert( 'core_files_temp', array(
'upload_key' => md5( $this->name . session_id() ),
'filename' => $fileObj->originalFilename,
'mime' => \IPS\File::getMimeType( $fileObj->originalFilename ),
'contents' => (string) $fileObj,
'time' => time()
) );
if ( $this->options['callback'] )
{
$r = call_user_func( $this->options['callback'], $fileObj );
if ( $r !== NULL )
{
$insertId = $r;
}
}
$return[ $insertId ] = $fileObj;
}
return $return;
}
}
/**
* Validate
*
* @throws \InvalidArgumentException
* @return TRUE
*/
public function validate()
{
parent::validate();
if ( $this->required and empty( $this->value ) and ( !\IPS\Request::i()->ajaxValidate or !$this->options['temporary'] ) )
{
throw new \InvalidArgumentException('form_required');
}
}
/**
* String Value
*
* @param mixed $value The value
* @return string
*/
public static function stringValue( $value )
{
if ( is_array( $value ) )
{
return implode( ',', array_map( function( $v )
{
if ( is_object( $v ) )
{
return (string) $v->url;
}
}, $value ) );
}
return ( $value ) ? (string) $value->url : NULL;
}
}
+167
View File
@@ -0,0 +1,167 @@
<?php
/**
* @brief URL input class 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 11 Mar 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;
}
/**
* URL input class for Form Builder
*/
class _Url extends Text
{
/**
* @brief Default Options
* @code
$childDefaultOptions = array(
'allowedProtocols' => array( 'http', 'https' ), // Allowed protocols. Default is http and https. Be careful changing this not to introduce security issues.
'allowedMimes' => 'image/*', // Sets the allowed mimetype(s). Can be string or array. * is a wildcard. Default is NULL, which allows any mimetypes.
'file' => 'Profile', // If provided, the contents of the URL will be fetched and written as a file, an \IPS\File object will then be returned rather than a string. Provide the extension name which specifies the storage location to use.
'maxFileSize' => NULL, // If provided along with 'file', the resulting file that is written to disk cannot be greater than this size in megabytes. NULL for no limit (default is NULL).
);
* @endcode
*/
public $childDefaultOptions = array(
'allowedProtocols' => array( 'http', 'https' ),
'allowedMimes' => NULL,
'file' => FALSE,
'maxFileSize' => NULL,
);
/**
* Validate
*
* @throws \InvalidArgumentException
* @throws \DomainException
* @return TRUE
*/
public function validate()
{
parent::validate();
if ( $this->value )
{
$value = $this->formatValue();
/* Check the URL is valid */
if ( !( $value instanceof \IPS\Http\Url ) )
{
throw new \InvalidArgumentException('form_url_bad');
}
/* And that it's an allowed protocol */
if ( !in_array( mb_strtolower( $value->data['scheme'] ), $this->options['allowedProtocols'] ) )
{
throw new \DomainException('form_url_bad_protocol');
}
/* Try to fetch it, if necessary */
if ( $this->options['file'] or $this->options['allowedMimes'] )
{
try
{
$response = $value->request()->get();
/* Check MIME */
if ( $this->options['allowedMimes'] )
{
$match = FALSE;
$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 ) )
{
$match = TRUE;
break;
}
}
}
if ( !$match )
{
throw new \DomainException( 'form_url_bad_mime' );
}
}
/* Write file if necessary */
if ( $this->options['file'] )
{
$filename = preg_replace( "/(.+?)(\?|$)/", "$1", mb_substr( $value, mb_strrpos( $value, '/' ) + 1 ) );
$response = (string) $response;
if( $this->options['maxFileSize'] !== NULL )
{
$maxFileSize = $this->options['maxFileSize'] * 1048576;
if( \strlen( $response ) > $maxFileSize )
{
unset( $response );
throw new \DomainException( \IPS\Member::loggedIn()->language()->addToStack( 'upload_too_big', TRUE, array( 'sprintf' => $this->options['maxFileSize'] ) ), 2 );
}
}
$this->value = \IPS\File::create( $this->options['file'], $filename, $response );
}
}
catch ( \IPS\Http\Request\Exception $e )
{
throw new \DomainException( 'form_url_error' );
}
}
}
}
/**
* Get Value
*
* @return string
*/
public function getValue()
{
$val = str_replace( 'feed://', 'http://', parent::getValue() );
if ( $val and !mb_strpos( $val, '://' ) )
{
$val = "http://{$val}";
}
return $val;
}
/**
* Format Value
*
* @return \IPS\Http\Url|string
*/
public function formatValue()
{
if ( $this->value and !( $this->value instanceof \IPS\Http\Url ) )
{
try
{
return new \IPS\Http\Url( $this->value );
}
catch ( \InvalidArgumentException $e )
{
return $this->value;
}
}
return $this->value;
}
}
+91
View File
@@ -0,0 +1,91 @@
<?php
/**
* @brief Width/Height input class 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 28 Mar 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;
}
/**
* Width/Height input class for Form Builder
*/
class _WidthHeight extends FormAbstract
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'unlimited' => array( 0, 0 ), // If any value other than NULL is provided, an "Unlimited" checkbox will be displayed. If checked, the values specified will be sent.
'image' => NULL, // If an \IPS\File object is provided, the image will be shown for resizing rather than a div
'resizableDiv' => FALSE, // If set to false, the resizable div will not be displayed (useful if you expect dimensions to be large)
);
* @endcode
*/
protected $defaultOptions = array(
'unlimited' => NULL,
'image' => NULL,
'resizableDiv' => TRUE,
);
/**
* Constructor
* Sets an appropriate default value
*
* @return void
*/
public function __construct()
{
call_user_func_array( 'parent::__construct', func_get_args() );
if ( $this->value === NULL )
{
if ( $this->options['image'] !== NULL )
{
$image = \IPS\Image::create( $this->options['image']->contents() );
$this->value = array( $image->width, $image->height );
}
else
{
$this->value = array( 100, 100 );
}
}
}
/**
* Get HTML
*
* @return string
*/
public function html()
{
return \IPS\Theme::i()->getTemplate( 'forms', 'core' )->widthheight( $this->name, $this->value[0], $this->value[1], $this->options['unlimited'], $this->options['image'] ? $this->options['image'] : NULL, $this->options['resizableDiv'] );
}
/**
* Get Value
*
* @return mixed
*/
public function getValue()
{
$name = $this->name;
$value = \IPS\Request::i()->$name;
if ( $this->options['unlimited'] !== NULL and isset( $value['unlimited'] ) )
{
return $this->options['unlimited'];
}
return \IPS\Request::i()->$name;
}
}
+55
View File
@@ -0,0 +1,55 @@
<?php
/**
* @brief Yes/No Radio Switches class 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 8 Mar 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;
}
/**
* Yes/No Radio Switches class for Form Builder
*/
class _YesNo extends Checkbox
{
/**
* @brief Default Options
* @code
$defaultOptions = array(
'disabled' => FALSE, // Disabled?
'togglesOn' => array( ... ), // IDs of elements to show when checked
'togglesOff' => array( ... ), // IDs of elements to show when unchecked
'label' => '', // Label language key
);
* @endcode
*/
protected $defaultOptions = array(
'disabled' => FALSE,
'togglesOn' => array(),
'togglesOff' => array(),
'label' => '',
'tooltip' => NULL,
);
/**
* Get HTML
*
* @return string
*/
public function html()
{
$checkboxName = preg_replace( '/^(.+?\[?.+?)(\])?$/', '$1_checkbox$2', $this->name );
return \IPS\Theme::i()->getTemplate( 'forms', 'core', 'global' )->checkbox( $checkboxName, $this->value, $this->options['disabled'], $this->options['togglesOn'], $this->options['togglesOff'], $this->options['label'], $this->name, $this->htmlId ?: preg_replace( "/[^a-zA-Z0-9\-_]/", "_", $this->name ), TRUE, $this->options['tooltip'] );
}
}
View File
Whitespace-only changes.