Version 4.6.0

This commit is contained in:
Neo committed 2025-12-19 05:55:31 -08:00
1 parent f79dcf067a
commit 517a5e1f70
2036 files changed
+110041 -26162

No files matched your search

+75 -65
View File
@@ -22,22 +22,6 @@ if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
*/
class _Application extends \IPS\Node\Model
{
/**
* @brief IPS Applications
* @deprecated - see \IPS\IPS::$ipsApps
*/
public static $ipsApps = array(
'blog',
'calendar',
'cms',
'core',
'downloads',
'forums',
'gallery',
'nexus',
'convert'
);
/**
* @brief [ActiveRecord] Multiton Store
*/
@@ -127,12 +111,11 @@ class _Application extends \IPS\Node\Model
{
if( $application->default )
{
$defaultApplication = $directory;
static::removeMetaPrefix( $application );
break;
}
}
static::removeMetaPrefix( \IPS\Application::load( $defaultApplication ) );
static::addMetaPrefix( $this );
/* Actually update the database */
@@ -1036,7 +1019,7 @@ EOF;
);
/* Upgrade */
if( !$this->protected AND !\IPS\DEMO_MODE AND $this->marketplace_id === NULL )
if( !$this->protected AND !\IPS\DEMO_MODE AND $this->marketplace_id === NULL AND \IPS\IPS::canManageResources() )
{
$buttons['upgrade'] = array(
'icon' => 'upload',
@@ -1071,26 +1054,29 @@ EOF;
{
$buttons['edit'] = $edit;
}
$buttons['compilejs'] = array(
'icon' => 'cog',
'title' => 'app_compile_js',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$this->_id}&do=compilejs" )->csrf()
);
$buttons['build'] = array(
'icon' => 'cog',
'title' => 'app_build',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$this->_id}&do=build" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('app_build') )
);
$buttons['export'] = array(
'icon' => 'download',
'title' => 'download',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$this->_id}&do=download" ),
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('download'), 'ipsDialog-remoteVerify' => 'false' )
);
if( !$this->marketplace_id )
{
$buttons['compilejs'] = array(
'icon' => 'cog',
'title' => 'app_compile_js',
'link' => \IPS\Http\Url::internal( "app=core&module=applications&controller=applications&appKey={$this->_id}&do=compilejs" )->csrf()
);
$buttons['build'] = array(
'icon' => 'cog',
'title' => 'app_build',
'link' => \IPS\Http\Url::internal("app=core&module=applications&controller=applications&appKey={$this->_id}&do=build"),
'data' => array('ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('app_build'))
);
$buttons['export'] = array(
'icon' => 'download',
'title' => 'download',
'link' => \IPS\Http\Url::internal("app=core&module=applications&controller=applications&appKey={$this->_id}&do=download"),
'data' => array('ipsDialog' => '', 'ipsDialog-title' => \IPS\Member::loggedIn()->language()->addToStack('download'), 'ipsDialog-remoteVerify' => 'false')
);
}
$buttons['developer'] = array(
'icon' => 'cogs',
@@ -1312,6 +1298,7 @@ EOF;
$form->add( new \IPS\Helpers\Form\Url( 'app_website', $this->website ) );
$form->add( new \IPS\Helpers\Form\Url( 'app_update_check', $this->update_check ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'app_protected', $this->protected, FALSE ) );
$form->add( new \IPS\Helpers\Form\YesNo( 'app_hide_tab', !$this->hide_tab, FALSE ) );
}
/**
@@ -1396,6 +1383,7 @@ EOF;
'app_protected' => $values['app_protected'],
'app_website' => $values['app_website'],
'app_update_check' => $values['app_update_check'],
'app_hide_tab' => $values['app_hide_tab']
) ) );
}
}
@@ -1405,6 +1393,8 @@ EOF;
$values['app_added'] = time();
}
$values['app_hide_tab'] = !$values['app_hide_tab'];
if( isset( $values['app_title'] ) )
{
unset( $values['app_title'] );
@@ -2069,8 +2059,7 @@ EOF;
if( file_exists( $this->getApplicationPath() . "/data/lang.xml" ) )
{
/* Open XML file */
$xml = new \IPS\Xml\XMLReader;
$xml->open( $this->getApplicationPath() . "/data/lang.xml" );
$xml = \IPS\Xml\XMLReader::safeOpen( $this->getApplicationPath() . "/data/lang.xml" );
$xml->read();
/* Get the version */
@@ -2184,8 +2173,7 @@ EOF;
\IPS\Db::i()->delete( 'core_email_templates', array( 'template_app=? AND template_parent=0', $this->directory ) );
/* Open XML file */
$xml = new \IPS\Xml\XMLReader;
$xml->open( $this->getApplicationPath() . "/data/emails.xml" );
$xml = \IPS\Xml\XMLReader::safeOpen( $this->getApplicationPath() . "/data/emails.xml" );
$xml->read();
/* Start looping through each word */
@@ -2247,7 +2235,7 @@ EOF;
$parent = \IPS\Db::i()->select( '*', 'core_email_templates', array( 'template_app=? and template_name=? and template_parent=0', $template['template_app'], $template['template_name'] ) )->first();
/* And now update this template */
\IPS\Db::i()->update( 'core_email_templates', array( 'template_parent' => $parent['template_id'] ), array( 'template_id=?', $template['template_id'] ) );
\IPS\Db::i()->update( 'core_email_templates', array( 'template_parent' => $parent['template_id'], 'template_data' => $parent['template_data'] ), array( 'template_id=?', $template['template_id'] ) );
\IPS\Db::i()->update( 'core_email_templates', array( 'template_edited' => 1 ), array( 'template_id=?', $parent['template_id'] ) );
}
catch( \UnderflowException $ex ) { }
@@ -2500,8 +2488,7 @@ EOF;
}
/* Open XML file */
$xml = new \IPS\Xml\XMLReader;
$xml->open( $this->getApplicationPath() . "/data/theme.xml" );
$xml = \IPS\Xml\XMLReader::safeOpen( $this->getApplicationPath() . "/data/theme.xml" );
$xml->read();
while( $xml->read() )
@@ -2870,6 +2857,12 @@ EOF;
}
}
/* Remove attempted changes back to empty string when INT */
if( !empty( $differences['default'] ) AND $differences['default']['is'] == 0 AND $differences['default']['shouldBe'] == '' )
{
unset( $differences['default'] );
}
/* If this is a decimal column, ignore unsigned attribute */
if( $compareDefinition['columns'][ $columnName ]['type'] == 'DECIMAL' AND isset( $differences['unsigned'] ) )
{
@@ -2899,7 +2892,7 @@ EOF;
/* If the column we are about to adjust is included in this index, see if it needs adjusting */
if( $indexColumn == $columnName AND !\in_array( $indexName, $dropped ) )
{
$thisIndex = $db->updateDefinitionIndexLengths( $compareDefinition );
$thisIndex = $db->updateDefinitionIndexLengths( $tableDefinition );
if( !isset( $thisIndex['indexes'][ $indexName ] ) )
{
@@ -3162,6 +3155,7 @@ EOF;
'app_protected' => $this->protected,
'app_website' => $this->website,
'app_update_check' => $this->update_check,
'app_hide_tab' => $this->hide_tab
);
\IPS\Application::writeJson( \IPS\ROOT_PATH . '/applications/' . $this->directory . '/data/application.json', $applicationData );
@@ -3454,7 +3448,7 @@ EOF;
public function buildNewFeatures()
{
/* Assign any that are flagged is "current" to this version */
\IPS\Db::i()->update( 'core_members_features', array( 'feature_version_added' => $this->long_version ), array( "feature_version_added=? AND feature_app=?", 0, $this->directory ) );
\IPS\Db::i()->update( 'core_members_features', array( 'feature_version_added' => $this->long_version ), array( "feature_version_added=? AND feature_app=?", -1, $this->directory ) );
/* Write the JSON file */
\IPS\Application\Feature::writeJsonFile( $this->directory, $this->long_version );
@@ -3476,19 +3470,9 @@ EOF;
if ( file_exists( \IPS\ROOT_PATH . "/applications/{$appKey}/setup/upg_{$versionId}/whatsnew.json" ) )
{
if ( $data = @json_decode( \file_get_contents( $this->getApplicationPath() . "/setup/upg_{$versionId}/whatsnew.json" ), TRUE ) )
if ( $data = @json_decode( \file_get_contents( \IPS\Application::load( $appKey )->getApplicationPath() . "/setup/upg_{$versionId}/whatsnew.json" ), TRUE ) )
{
$insert = array();
foreach( $data AS $uid => $row )
{
$filename = explode( '/', $row['feature_image'] );
$filename = array_pop( $filename );
\IPS\File::create( 'core_Feature', $filename, base64_decode( $row['_image_data'], 'features', TRUE, NULL, FALSE ) );
unset( $row['_image_data'] );
$insert[] = $row;
}
\IPS\Db::i()->insert( 'core_members_features', $insert );
\IPS\Db::i()->insert( 'core_members_features', $data );
}
}
}
@@ -4756,14 +4740,12 @@ EOF;
}
}
/* Call onOtherAppUninstall so that other applications may perform any necessary cleanup */
/* @todo This is deprecated - we need to migrate to onOtherUninstall() but I'm leaving this call as-is to prevent breaking
backwards-compatibility until a major release */
/* Call onOtherUninstall so that other applications may perform any necessary cleanup */
foreach( static::allExtensions( 'core', 'Uninstall', FALSE ) as $extension )
{
if( method_exists( $extension, 'onOtherAppUninstall' ) )
if( method_exists( $extension, 'onOtherUninstall' ) )
{
$extension->onOtherAppUninstall( $this->directory );
$extension->onOtherUninstall( $this->directory );
}
}
@@ -4855,6 +4837,18 @@ EOF;
/* Delete ratings from this app */
\IPS\Db::i()->delete( 'core_ratings', \IPS\Db::i()->in( 'class', $classes ) );
/* Delete merge redirects */
\IPS\Db::i()->delete( 'core_item_redirect', \IPS\Db::i()->in( 'redirect_class', $classes ) );
/* Delete member map */
\IPS\Db::i()->delete( 'core_item_member_map', \IPS\Db::i()->in( 'map_class', $classes ) );
/* Delete RSS Imports */
foreach( new \IPS\Patterns\ActiveRecordIterator( \IPS\Db::i()->select( '*', 'core_rss_import', \IPS\Db::i()->in( 'rss_import_class', $classes ) ), 'IPS\core\Rss\Import' ) as $import )
{
$import->delete();
}
/* Delete Soft Deletion Log data */
$softDeleteKeys = array();
foreach ( $classes as $class )
@@ -4872,6 +4866,9 @@ EOF;
/* Delete PBR Data */
\IPS\Db::i()->delete( 'core_post_before_registering', \IPS\Db::i()->in( 'class', $classes ) );
/* Delete Anonymous Data */
\IPS\Db::i()->delete( 'core_anonymous_posts', \IPS\Db::i()->in( 'anonymous_object_class', $classes ) );
}
/* Delete attachment maps - if the attachment is unused, the regular cleanup task will remove the file later */
@@ -4954,6 +4951,19 @@ EOF;
catch( \Exception $e ){}
}
/* Delete any upload settings */
foreach( $this->uploadSettings() as $setting )
{
if( \IPS\Settings::i()->$setting )
{
try
{
\IPS\File::get( 'core_Theme', \IPS\Settings::i()->$setting )->delete();
}
catch( \Exception $e ){}
}
}
$notificationTypes = array();
foreach( $this->extensions( 'core', 'Notifications' ) as $key => $class )
{
+20 -6
View File
@@ -155,7 +155,16 @@ class _Feature extends \IPS\Patterns\ActiveRecord implements \JsonSerializable
return $tree;
}
/**
* Developer Management Table row
*
* @param string|int $long Long version
* @param bool $root Is this a root row
* @param \IPS\Application $application The application this feature belongs to
* @param \IPS\Http\Url $url Base URL
* @return string
*/
public static function _devTableRow( $long, $root, $application, $url )
{
$file = \IPS\ROOT_PATH . "/applications/{$application->directory}/setup/upg_" . ( $long == -1 ? 'working' : $long ) . "/whatsnew.json";
@@ -208,7 +217,7 @@ class _Feature extends \IPS\Patterns\ActiveRecord implements \JsonSerializable
* @param NULL|\IPS\Application\Feature $current Currently editing, or NULL for new.
* @return void
*/
public static function form( &$form, \IPS\Application $application, ?\IPS\Application\Feature $current, ?int $version ): void
public static function form( &$form, \IPS\Application $application, ?\IPS\Application\Feature $current, ?int $version )
{
$form->hiddenValues['feature_app'] = $application->directory;
$form->hiddenValues['feature_version_added'] = ( $current ) ? $current->version_added : $version;
@@ -272,7 +281,7 @@ class _Feature extends \IPS\Patterns\ActiveRecord implements \JsonSerializable
* @param array $values Form Values
* @return void
*/
public function formatFormValues( array $values ): void
public function formatFormValues( array $values )
{
$this->title = $values['feature_title'];
$this->text = $values['feature_text'];
@@ -321,9 +330,14 @@ class _Feature extends \IPS\Patterns\ActiveRecord implements \JsonSerializable
* @param \IPS\Member\Feature $features Variable length amount of features to mark as read.
* @return void
*/
public static function markRead( ?\IPS\Member $member, \IPS\Application\Feature ...$features ): void
public static function markRead( ?\IPS\Member $member, \IPS\Application\Feature ...$features )
{
$member = $member ?: \IPS\Member::loggedIn();
if( !$member->member_id )
{
return;
}
$insert = array();
foreach( $features AS $feature )
@@ -333,7 +347,7 @@ class _Feature extends \IPS\Patterns\ActiveRecord implements \JsonSerializable
if ( \count( $insert ) )
{
\IPS\Db::i()->insert( 'core_members_feature_seen', $insert );
\IPS\Db::i()->insert( 'core_members_feature_seen', $insert, TRUE );
}
}
@@ -362,7 +376,7 @@ class _Feature extends \IPS\Patterns\ActiveRecord implements \JsonSerializable
* @param int $versionId Long version
* @return void
*/
public static function writeJsonFile( string $appKey, int $versionId ): void
public static function writeJsonFile( string $appKey, int $versionId )
{
$path = \IPS\ROOT_PATH . "/applications/{$appKey}/setup/upg_" . ( $versionId == -1 ? 'working' : $versionId ) . "/whatsnew.json";
@unlink( $path );