Version 4.3.0

This commit is contained in:
Neo committed 2025-12-19 05:47:12 -08:00
1 parent 96997ddd8e
commit fe1acf984a
1349 files changed
+116159 -67711

No files matched your search

+87 -62
View File
@@ -12,7 +12,6 @@
/**
*
* @todo: Shared media field type
* @todo: Relational field type
*
*/
namespace IPS\cms;
@@ -178,7 +177,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
public static $cannotBeTitleFields = array( 'Member', 'Editor', 'CheckboxSet', 'YesNo', 'Radio', 'Item' );
/**
* @brief Fields that cannot be title fields
* @brief Fields that cannot be content fields
*/
public static $cannotBeContentFields = array();
@@ -229,7 +228,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
foreach( static::$cache[ static::$customDatabaseId ][ $permissionCheck ] as $id => $obj )
{
if ( ! array_key_exists( $obj->type, static::$additionalFieldTypes ) AND ( ! class_exists( '\IPS\Helpers\Form\\' . ucfirst( $obj->type ) ) AND ! class_exists( '\IPS\cms\Fields\\' . ucfirst( $obj->type ) ) ) )
if ( ! array_key_exists( $obj->type, static::$additionalFieldTypes ) AND ( ! class_exists( '\IPS\Helpers\Form\\' . mb_ucfirst( $obj->type ) ) AND ! class_exists( '\IPS\cms\Fields\\' . mb_ucfirst( $obj->type ) ) ) )
{
unset( static::$cache[ static::$customDatabaseId ][ $permissionCheck ][ $id ] );
continue;
@@ -630,24 +629,33 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
{
if ( isset( $this->extra['thumbsize'] ) )
{
$thumbs = iterator_to_array( \IPS\Db::i()->select( '*', 'cms_database_fields_thumbnails', array( array( 'thumb_field_id=?', $this->id ) ) )->setKeyField('thumb_original_location')->setValueField('thumb_location') );
$images = array();
if ( $this->is_multiple )
{
$thumbs = iterator_to_array( \IPS\Db::i()->select( '*', 'cms_database_fields_thumbnails', array( array( 'thumb_field_id=? AND thumb_record_id=?', $this->id, $record->_id ) ) )->setKeyField('thumb_original_location')->setValueField('thumb_location') );
$images = array();
foreach( $thumbs as $orig => $thumb )
foreach( $thumbs as $orig => $thumb )
{
try
{
$images[] = \IPS\File::get( static::$uploadStorageExtension, $thumb )->url;
}
catch( \Exception $e ) { }
}
return $images;
}
else
{
try
{
$images[] = \IPS\File::get( static::$uploadStorageExtension, $thumb )->url;
return (string) \IPS\File::get( static::$uploadStorageExtension, \IPS\Db::i()->select( 'thumb_location', 'cms_database_fields_thumbnails', array( array( 'thumb_field_id=? AND thumb_record_id=?', $this->id, $record->_id ) ) )->first() )->url;
}
catch( \Exception $e ) { }
}
return $images;
}
else
{
return $this->formatForDisplay( $value, $formValue, 'raw', $record );
}
return $this->formatForDisplay( $value, $formValue, 'raw', $record );
}
$options = $this->display_json;
@@ -732,7 +740,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
$class .= ' ' . 'ipsPos_right';
}
if ( $this->type === 'Address' and $formValue and isset( $options[ $type ]['map'] ) AND $options[ $type ]['map'] AND \IPS\Settings::i()->google_maps_api_key )
if ( $this->type === 'Address' and $formValue and isset( $options[ $type ]['map'] ) AND $options[ $type ]['map'] AND \IPS\GeoLocation::enabled() )
{
$value .= \IPS\GeoLocation::buildFromJson( $formValue )->map()->render( $options[ $type ]['mapDims'][0], $options[ $type ]['mapDims'][1] );
}
@@ -775,7 +783,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
}
else
{
if ( $this->type === 'Address' and $formValue and isset( $options[ $type ]['map'] ) AND $options[ $type ]['map'] AND \IPS\Settings::i()->google_maps_api_key )
if ( $this->type === 'Address' and $formValue and isset( $options[ $type ]['map'] ) AND $options[ $type ]['map'] AND \IPS\GeoLocation::enabled() )
{
$value .= \IPS\GeoLocation::buildFromJson( $formValue )->map()->render( $options[ $type ]['mapDims'][0], $options[ $type ]['mapDims'][1] );
}
@@ -826,7 +834,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
if ( $formValue and $this->type === 'Address' )
{
$functionName .= '_' . uniqid();
$functionName .= '_' . mt_rand();
$template = str_replace( '{map}' , \IPS\GeoLocation::buildFromJson( $formValue )->map()->render( $options[ $type ]['mapDims'][0], $options[ $type ]['mapDims'][1] ), $template );
$template = str_replace( '{address}', \IPS\GeoLocation::parseForOutput( $formValue ), $template );
@@ -1186,11 +1194,11 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
*/
protected function get__icon()
{
if ( class_exists( '\IPS\Helpers\Form\\' . ucfirst( $this->type ) ) )
if ( class_exists( '\IPS\Helpers\Form\\' . mb_ucfirst( $this->type ) ) )
{
return NULL;
}
else if ( class_exists( '\IPS\cms\Fields\\' . ucfirst( $this->type ) ) )
else if ( class_exists( '\IPS\cms\Fields\\' . mb_ucfirst( $this->type ) ) )
{
return NULL;
}
@@ -1213,7 +1221,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
return $text;
}
switch( ucfirst( $this->type ) )
switch( mb_ucfirst( $this->type ) )
{
default:
// No truncate
@@ -1245,10 +1253,10 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
{
$database = \IPS\cms\Databases::load( static::$customDatabaseId );
if ( class_exists( '\IPS\cms\Fields\\' . ucfirst( $this->type ) ) )
if ( class_exists( '\IPS\cms\Fields\\' . mb_ucfirst( $this->type ) ) )
{
/* Is special! */
$class = '\IPS\cms\Fields\\' . ucfirst( $this->type );
$class = '\IPS\cms\Fields\\' . mb_ucfirst( $this->type );
if ( method_exists( $class, 'displayValue' ) )
{
@@ -1256,7 +1264,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
}
}
switch( ucfirst( $this->type ) )
switch( mb_ucfirst( $this->type ) )
{
case 'Upload':
return \IPS\File::get( 'cms_Records', $value )->url;
@@ -1306,14 +1314,14 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
$return = array();
foreach( $value as $key )
{
$return[] = isset( $this->extra[ $key ] ) ? htmlspecialchars( $this->extra[ $key ], \IPS\HTMLENTITIES | ENT_QUOTES, 'UTF-8', FALSE ) : htmlspecialchars( $key, \IPS\HTMLENTITIES | ENT_QUOTES, 'UTF-8', FALSE );
$return[] = isset( $this->extra[ $key ] ) ? htmlspecialchars( $this->extra[ $key ], ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) : htmlspecialchars( $key, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE );
}
return implode( ', ', $return );
}
else
{
return ( isset( $this->extra[ $value ] ) ? htmlspecialchars( $this->extra[ $value ], \IPS\HTMLENTITIES | ENT_QUOTES, 'UTF-8', FALSE ) : htmlspecialchars( $value, \IPS\HTMLENTITIES | ENT_QUOTES, 'UTF-8', FALSE ) );
return ( isset( $this->extra[ $value ] ) ? htmlspecialchars( $this->extra[ $value ], ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) : htmlspecialchars( $value, ENT_DISALLOWED | ENT_QUOTES, 'UTF-8', FALSE ) );
}
break;
case 'Member':
@@ -1332,7 +1340,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
case 'Url':
if ( \IPS\Dispatcher::hasInstance() AND class_exists( '\IPS\Dispatcher', FALSE ) and \IPS\Dispatcher::i()->controllerLocation === 'front' )
{
return ( $value ) ? \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $value, TRUE, NULL, FALSE ) : NULL;
return ( $value ) ? \IPS\Theme::i()->getTemplate( 'global', 'core', 'global' )->basicUrl( $value, TRUE, NULL, FALSE ) : NULL;
}
break;
case 'Date':
@@ -1377,7 +1385,11 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
$time = ( is_integer( $value[ $t ] ) ) ? \IPS\DateTime::ts( $value[ $t ], TRUE ) : $value[ $t ];
if ( isset( $this->extra['timezone'] ) and $this->extra['timezone'] )
{
$time->setTimezone( new \DateTimeZone( $this->extra['timezone'] ) );
try
{
$time->setTimezone( new \DateTimeZone( $this->extra['timezone'] ) );
}
catch( \Exception $e ){}
}
$$t = $time->localeDate();
@@ -1547,7 +1559,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
*/
public function canBeTitleField()
{
if ( $this->is_multiple or in_array( ucfirst( $this->type ), static::$cannotBeTitleFields ) )
if ( $this->is_multiple or in_array( mb_ucfirst( $this->type ), static::$cannotBeTitleFields ) )
{
return FALSE;
}
@@ -1564,7 +1576,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
{
$no = array();
if ( $this->is_multiple or in_array( ucfirst( $this->type ), static::$cannotBeContentFields ) )
if ( $this->is_multiple or in_array( mb_ucfirst( $this->type ), static::$cannotBeContentFields ) )
{
return FALSE;
}
@@ -1613,11 +1625,11 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
if ( $this->type )
{
$ok = FALSE;
if ( class_exists( '\IPS\Helpers\Form\\' . ucfirst( $this->type ) ) )
if ( class_exists( '\IPS\Helpers\Form\\' . mb_ucfirst( $this->type ) ) )
{
$ok = TRUE;
}
else if ( class_exists( '\IPS\cms\Fields\\' . ucfirst( $this->type ) ) )
else if ( class_exists( '\IPS\cms\Fields\\' . mb_ucfirst( $this->type ) ) )
{
$ok = TRUE;
}
@@ -1697,12 +1709,16 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
}
/* Title or content? */
$isTitleField = FALSE;
if ( $this->id )
{
$database = \IPS\cms\Databases::load( static::$customDatabaseId );
if ( $this->id == $database->field_title )
{
$isTitleField = TRUE;
foreach( static::$cannotBeTitleFields as $type )
{
unset( $options[ $type ] );
@@ -1734,34 +1750,37 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
}
}
$form->add( new \IPS\Helpers\Form\Select( 'field_type', $this->id ? \ucfirst( $this->type ) : 'Text', TRUE, array(
$form->add( new \IPS\Helpers\Form\Select( 'field_type', $this->id ? mb_ucfirst( $this->type ) : 'Text', TRUE, array(
'options' => $options,
'toggles' => $toggles
) ) );
/* Relational specific */
$databases = array();
$disabled = array();
foreach( \IPS\cms\Databases::databases() as $db )
if( !$isTitleField )
{
if ( $db->page_id )
$databases = array();
$disabled = array();
foreach( \IPS\cms\Databases::databases() as $db )
{
$databases[ $db->id ] = $db->_title;
if ( $db->page_id )
{
$databases[ $db->id ] = $db->_title;
}
else
{
$disabled[] = $db->id;
$databases[ $db->id ] = \IPS\Member::loggedIn()->language()->addToStack( 'cms_db_relational_with_name_disabled', NULL, array( 'sprintf' => array( $db->_title ) ) );
}
}
else
if ( ! count( $databases ) )
{
$disabled[] = $db->id;
$databases[ $db->id ] = \IPS\Member::loggedIn()->language()->addToStack( 'cms_db_relational_with_name_disabled', NULL, array( 'sprintf' => array( $db->_title ) ) );
$databases[0] = \IPS\Member::loggedIn()->language()->addToStack('cms_relational_field_no_dbs');
$disabled[] = 0;
}
}
if ( ! count( $databases ) )
{
$databases[0] = \IPS\Member::loggedIn()->language()->addToStack('cms_relational_field_no_dbs');
$disabled[] = 0;
}
$form->add( new \IPS\Helpers\Form\Select( 'field_relational_db', ( isset( $this->extra['database'] ) ? $this->extra['database'] : NULL ), FALSE, array( 'options' => $databases, 'disabled' => $disabled ), NULL, NULL, NULL, 'field_relational_db' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'field_crosslink', $this->id ? ( ( isset( $this->extra['crosslink'] ) and $this->extra['crosslink'] ) ? TRUE : FALSE ) : FALSE, FALSE, array(), NULL, NULL, NULL, 'field_crosslink' ) );
$form->add( new \IPS\Helpers\Form\Select( 'field_relational_db', ( isset( $this->extra['database'] ) ? $this->extra['database'] : NULL ), FALSE, array( 'options' => $databases, 'disabled' => $disabled ), NULL, NULL, NULL, 'field_relational_db' ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'field_crosslink', $this->id ? ( ( isset( $this->extra['crosslink'] ) and $this->extra['crosslink'] ) ? TRUE : FALSE ) : FALSE, FALSE, array(), NULL, NULL, NULL, 'field_crosslink' ) );
}
/* Number specific */
$form->add( new \IPS\Helpers\Form\YesNo( 'field_number_decimals_on', $this->id ? ( ( isset( $this->extra['on'] ) and $this->extra['on'] ) ? TRUE : FALSE ) : FALSE, FALSE, array( 'togglesOn' => array( 'field_number_decimals' ) ), NULL, NULL, NULL, 'field_number_decimals_on' ) );
@@ -1804,7 +1823,10 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
), NULL, NULL, NULL, 'field_allowed_extensions' ) );
/* Editor Specific */
$form->add( new \IPS\Helpers\Form\YesNo( 'field_allow_attachments', $this->id ? $this->allow_attachments : 1, FALSE, array( ), NULL, NULL, NULL, 'field_allow_attachments' ) );
if( !$isTitleField )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'field_allow_attachments', $this->id ? $this->allow_attachments : 1, FALSE, array( ), NULL, NULL, NULL, 'field_allow_attachments' ) );
}
/* Date specific */
$tzValue = 'UTC';
@@ -1832,7 +1854,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
), NULL, NULL, NULL, 'field_date_time_override' ) );
$timezones = array();
foreach ( \DateTimeZone::listIdentifiers() as $tz )
foreach ( \IPS\DateTime::getTimezoneIdentifiers() as $tz )
{
if ( $pos = mb_strpos( $tz, '/' ) )
{
@@ -1915,7 +1937,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
}
}
if ( ! ( $this->id AND $this->id === \IPS\cms\Databases::load( static::$customDatabaseId )->field_title ) )
if ( !$isTitleField )
{
$form->add( new \IPS\Helpers\Form\YesNo( 'field_required', $this->id ? $this->required : TRUE, FALSE ) );
}
@@ -2237,7 +2259,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
if ( !$this->id )
{
/* field key cannot be null, so we assign a temporary key here which is overwritten below */
$this->key = md5( uniqid() );
$this->key = md5( mt_rand() );
$this->database_id = static::$customDatabaseId;
$values['database_id'] = $this->database_id;
@@ -2407,7 +2429,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
/* It's taken... */
if ( $this->id != $testField->id )
{
$this->key .= '_' . uniqid();
$this->key .= '_' . mt_rand();
}
}
catch( \OutOfRangeException $ex )
@@ -2570,8 +2592,11 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
{
throw new \LogicException( \IPS\Member::loggedIn()->language()->addToStack('cms_relational_field_no_db_selected') );
}
$values['extra'] = array( 'database' => $values['field_relational_db'] );
if ( isset( $values['field_relational_db'] ) )
{
$values['extra'] = array( 'database' => $values['field_relational_db'] );
}
if ( array_key_exists( 'field_crosslink', $values ) and ! empty( $values['field_crosslink'] ) )
{
@@ -2747,14 +2772,14 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
*/
public function buildHelper( $value=NULL, $customValidationCode=NULL, \IPS\cms\Records $record = NULL, $flags=0 )
{
if ( class_exists( '\IPS\cms\Fields\\' . ucfirst( $this->type ) ) )
if ( class_exists( '\IPS\cms\Fields\\' . mb_ucfirst( $this->type ) ) )
{
/* Is special! */
$class = '\IPS\cms\Fields\\' . ucfirst( $this->type );
$class = '\IPS\cms\Fields\\' . mb_ucfirst( $this->type );
}
else if ( class_exists( '\IPS\Helpers\Form\\' . ucfirst( $this->type ) ) )
else if ( class_exists( '\IPS\Helpers\Form\\' . mb_ucfirst( $this->type ) ) )
{
$class = '\IPS\Helpers\Form\\' . ucfirst( $this->type );
$class = '\IPS\Helpers\Form\\' . mb_ucfirst( $this->type );
if ( !is_array( $this->extra ) )
{
@@ -2776,11 +2801,11 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
}
$options = array();
switch ( ucfirst( $this->type ) )
switch ( mb_ucfirst( $this->type ) )
{
case 'Editor':
$options['app'] = 'cms';
$options['key'] = 'Records';
$options['key'] = 'Records' . static::$customDatabaseId;
$options['allowAttachments'] = $this->allow_attachments;
$options['autoSaveKey'] = 'RecordField_' . ( $record === NULL ? 'new' : $record->_id ) . '_' . $this->id;
$options['attachIds'] = ( $record === NULL ) ? NULL : array( $record->_id, $this->id, static::$customDatabaseId );
@@ -2851,7 +2876,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
break;
case 'Select':
case 'CheckboxSet':
$options['multiple'] = ( ucfirst( $this->type ) == 'CheckboxSet' ) ? TRUE : $this->is_multiple;
$options['multiple'] = ( mb_ucfirst( $this->type ) == 'CheckboxSet' ) ? TRUE : $this->is_multiple;
if ( $flags & self::FIELD_DISPLAY_FILTERS or ( ! $this->default_value and ! $this->required ) )
{
@@ -2967,7 +2992,7 @@ class _Fields extends \IPS\CustomField implements \IPS\Node\Permissions
if ( $this->validator AND $this->validator_custom )
{
switch( ucfirst( $this->type ) )
switch( mb_ucfirst( $this->type ) )
{
case 'Text':
case 'TextArea':