Version 4.2.0
This commit is contained in:
1 parent
0dc2aee5ed
commit
96997ddd8e
2074 files changed
+125454
-55780
No files matched your search
@@ -1,12 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Application Class
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @author <a href='https://www.invisioncommunity.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) Invision Power Services, Inc.
|
||||
* @license https://www.invisioncommunity.com/legal/standards/
|
||||
* @package Invision Community
|
||||
* @since 18 Feb 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS;
|
||||
@@ -249,12 +248,12 @@ class _Application extends \IPS\Node\Model
|
||||
/**
|
||||
* Get all extensions
|
||||
*
|
||||
* @param \IPS\Application|string $app The app key of the application which owns the extension
|
||||
* @param string $extension Extension Type
|
||||
* @param \IPS\Member|bool $checkAccess Check access permission for application against supplied member (or logged in member, if TRUE) before including extension
|
||||
* @param string|NULL $firstApp If specified, the application with this key will be returned first
|
||||
* @param string|NULL $firstExtensionKey If specified, the extension with this key will be returned first
|
||||
* @param bool $construct Should an object be returned? (If false, just the classname will be returned)
|
||||
* @param \IPS\Application|string $app The app key of the application which owns the extension
|
||||
* @param string $extension Extension Type
|
||||
* @param \IPS\Member|\IPS\Member\Group|bool $checkAccess Check access permission for application against supplied member/group (or logged in member, if TRUE) before including extension
|
||||
* @param string|NULL $firstApp If specified, the application with this key will be returned first
|
||||
* @param string|NULL $firstExtensionKey If specified, the extension with this key will be returned first
|
||||
* @param bool $construct Should an object be returned? (If false, just the classname will be returned)
|
||||
* @return array
|
||||
*/
|
||||
public static function allExtensions( $app, $extension, $checkAccess=TRUE, $firstApp=NULL, $firstExtensionKey=NULL, $construct=TRUE )
|
||||
@@ -512,58 +511,35 @@ class _Application extends \IPS\Node\Model
|
||||
$correctMd5s = \IPS\Http\Url::ips( 'md5' )->request()->get()->decodeJson();
|
||||
|
||||
/* And return whichever ones don't match */
|
||||
return static::_md5sumCheckerIterator( \IPS\ROOT_PATH, $correctMd5s, 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* MD5 check directory
|
||||
*
|
||||
* @param string $directory Directory to look through
|
||||
* @param array $correctMd5s The correct md5 hashes
|
||||
* @param int $depth How deep into the folder structure we are
|
||||
* @return array
|
||||
*/
|
||||
public static function _md5sumCheckerIterator( $directory, $correctMd5s, $depth = 1 )
|
||||
{
|
||||
$return = array();
|
||||
foreach( new \DirectoryIterator( $directory ) as $file )
|
||||
foreach ( $correctMd5s as $file => $md5Hash )
|
||||
{
|
||||
if ( mb_substr( $file, 0, 1 ) === '.' or mb_substr( $file, 0, 1 ) === '_' or $file == 'index.html' )
|
||||
/* Fix the admin directory */
|
||||
$file = preg_replace( '/^\/admin\//', '/' . \IPS\CP_DIRECTORY . '/', $file );
|
||||
|
||||
/* If this is an application directory but the application doesn't exist or has been disabled then we shouldn't check it */
|
||||
preg_match( '/^\/applications\/(.+?)\//', $file, $matches );
|
||||
if ( $matches )
|
||||
{
|
||||
if( in_array( $matches[1], static::$ipsApps ) AND !static::appIsEnabled( $matches[1] ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ignore init.php (which always changes on build) and conf_global.dist.php (which doesn't exist after install) */
|
||||
if ( in_array( $file, array( '/init.php', '/conf_global.dist.php' ) ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $file->isDir() )
|
||||
/* MD5 check */
|
||||
if ( !file_exists( \IPS\ROOT_PATH . $file ) or !\IPS\Login::compareHashes( md5( preg_replace( '#\s#', '', utf8_decode( file_get_contents( \IPS\ROOT_PATH . $file ) ) ) ), $md5Hash ) ) // We strip whitespace since FTP in ASCII mode will change the whitespace characters
|
||||
{
|
||||
/* We only want to check directories which are ours (not other random directories they have on the server) and we can ignore datastore, plugins and uploads */
|
||||
if ( $depth === 1 and !in_array( $file->getFilename(), array( \IPS\CP_DIRECTORY, 'api', 'applications', 'system' ) ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If this is an application directory but the application has been disabled then we shouldn't check it */
|
||||
if( mb_strpos( $file->getPathname(), \IPS\ROOT_PATH . DIRECTORY_SEPARATOR . 'applications' ) !== FALSE AND $file->getFilename() != 'applications' )
|
||||
{
|
||||
/* If the directory name is a valid application key and the application is not enabled, don't check it */
|
||||
if( in_array( $file->getFilename(), static::$ipsApps ) AND !static::appIsEnabled( $file->getFilename() ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$return = array_merge( $return, static::_md5sumCheckerIterator( $file->getPathname(), $correctMd5s, $depth + 1 ) );
|
||||
}
|
||||
elseif ( mb_substr( $file, -4 ) === '.php' )
|
||||
{
|
||||
$fullPath = $file->getPathname();
|
||||
$shortPath = str_replace( '\\', '/', mb_substr( $fullPath, mb_strlen( \IPS\ROOT_PATH ) ) );
|
||||
|
||||
if ( $shortPath != '/init.php' and isset( $correctMd5s[ $shortPath ] ) and !\IPS\Login::compareHashes( md5( preg_replace( '#\s#', '', utf8_decode( file_get_contents( $fullPath ) ) ) ), $correctMd5s[ $shortPath ] ) ) // We strip whitespace since FTP in ASCII mode will change the whitespace characters
|
||||
{
|
||||
$return[] = $fullPath;
|
||||
}
|
||||
$return[] = \IPS\ROOT_PATH . $file;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
@@ -727,10 +703,10 @@ EOF;
|
||||
/**
|
||||
* Get Extensions
|
||||
*
|
||||
* @param \IPS\Application|string $app The app key of the application which owns the extension
|
||||
* @param string $extension Extension Type
|
||||
* @param bool $construct Should an object be returned? (If false, just the classname will be returned)
|
||||
* @param \IPS\Member|bool $checkAccess Check access permission for extension against supplied member (or logged in member, if TRUE)
|
||||
* @param \IPS\Application|string $app The app key of the application which owns the extension
|
||||
* @param string $extension Extension Type
|
||||
* @param bool $construct Should an object be returned? (If false, just the classname will be returned)
|
||||
* @param \IPS\Member|\IPS\Member\Group|bool $checkAccess Check access permission for extension against supplied member/group (or logged in member, if TRUE)
|
||||
* @return array
|
||||
*/
|
||||
public function extensions( $app, $extension, $construct=TRUE, $checkAccess=FALSE )
|
||||
@@ -750,6 +726,19 @@ EOF;
|
||||
if ( !$file->isDir() and !$file->isDot() and mb_substr( $file, -4 ) === '.php' AND mb_substr( $file, 0, 2 ) != '._' )
|
||||
{
|
||||
$classname = 'IPS\\' . $this->directory . '\extensions\\' . $app . '\\' . $extension . '\\' . mb_substr( $file, 0, -4 );
|
||||
|
||||
/* Check if class exists - sometimes we have to use blank files to wipe out old extensions */
|
||||
try
|
||||
{
|
||||
if( !class_exists( $classname ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch( \ErrorException $e )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( method_exists( $classname, 'generate' ) )
|
||||
{
|
||||
@@ -1510,7 +1499,7 @@ EOF;
|
||||
}
|
||||
catch( \IPS\Db\Exception $e )
|
||||
{
|
||||
\IPS\Log::log( "Error: " . $instruction['params']['method'] . ' ' . json_encode( $instruction['params']['params'] ), 'upgrade_error' );
|
||||
\IPS\Log::log( "Error (" . $e->getCode() . ") " . $e->getMessage() . ": " . $instruction['params']['method'] . ' ' . json_encode( $instruction['params']['params'] ), 'upgrade_error' );
|
||||
|
||||
/* If the issue is with a create table other than exists, we should just throw it */
|
||||
if ( $instruction['params']['method'] == 'createTable' and ! in_array( $e->getCode(), array( 1007, 1050 ) ) )
|
||||
@@ -2603,14 +2592,21 @@ EOF;
|
||||
$tableChanges = array();
|
||||
$needIgnore = false;
|
||||
$innoDbFullTextIndexes = array();
|
||||
|
||||
|
||||
/* Get our local definition of this table */
|
||||
try
|
||||
{
|
||||
$localDefinition = \IPS\Db::i()->getTableDefinition( $tableName );
|
||||
$originalDefinition = $localDefinition; #Store this before it is normalised and engine stripped
|
||||
$localDefinition = \IPS\Db::i()->normalizeDefinition( $localDefinition );
|
||||
$compareDefinition = \IPS\Db::i()->normalizeDefinition( $tableDefinition );
|
||||
$tableDefinition = \IPS\Db::i()->updateDefinitionIndexLengths( $tableDefinition );
|
||||
|
||||
/* Ensure that we use the proper engine, not whatever is in the schema.json as this will confuse index sub_part lengths */
|
||||
if ( isset( $originalDefinition['engine'] ) )
|
||||
{
|
||||
$tableDefinition['engine'] = $originalDefinition['engine'];
|
||||
}
|
||||
|
||||
if ( $compareDefinition != $localDefinition )
|
||||
{
|
||||
@@ -2787,6 +2783,17 @@ EOF;
|
||||
{
|
||||
if ( $indexName != 'PRIMARY' and !isset( $compareDefinition['indexes'][ $indexName ] ) )
|
||||
{
|
||||
/* If the index is on a column which we don't recognise (which may happen on tables which we add columns to like the ones that
|
||||
store custom fields, or very naughty third parties adding columns on tables they don't own), don't drop it */
|
||||
foreach ( $indexData['columns'] as $indexedColumn )
|
||||
{
|
||||
if ( !isset( $compareDefinition['columns'][ $indexedColumn ] ) )
|
||||
{
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Still here? Go ahead */
|
||||
$dropIndexQuery = "DROP INDEX `{$db->escape_string( $indexName )}`";
|
||||
if ( !in_array( $dropIndexQuery, $tableChanges ) ) // We skip the primary key as it can cause errors related to auto-increment
|
||||
{
|
||||
@@ -2892,24 +2899,32 @@ EOF;
|
||||
static::writeJson( $queriesJsonFile, $write );
|
||||
}
|
||||
|
||||
/* Add the actual version number in upgrade.php */
|
||||
$upgradeFile = $workingDir . "/upgrade.php";
|
||||
if ( file_exists( $upgradeFile ) )
|
||||
/* Add the actual version number in upgrade.php & options.php */
|
||||
$versionReplacement = function( $file ) use ( $human, $long )
|
||||
{
|
||||
$contents = file_get_contents( $upgradeFile );
|
||||
$contents = str_replace(
|
||||
array(
|
||||
'{version_human}',
|
||||
'upg_working'
|
||||
),
|
||||
array(
|
||||
$human,
|
||||
"upg_{$long}"
|
||||
),
|
||||
$contents
|
||||
);
|
||||
\file_put_contents( $upgradeFile, $contents );
|
||||
}
|
||||
if ( file_exists( $file ) )
|
||||
{
|
||||
$contents = file_get_contents( $file );
|
||||
$contents = str_replace(
|
||||
array(
|
||||
'{version_human}',
|
||||
'upg_working',
|
||||
'{version_long}'
|
||||
),
|
||||
array(
|
||||
$human,
|
||||
"upg_{$long}",
|
||||
$long
|
||||
),
|
||||
$contents
|
||||
);
|
||||
\file_put_contents( $file, $contents );
|
||||
}
|
||||
};
|
||||
|
||||
/* Make the replacement */
|
||||
$versionReplacement( $workingDir . "/upgrade.php" );
|
||||
$versionReplacement( $workingDir . "/options.php" );
|
||||
|
||||
/* Rename the directory */
|
||||
rename( $workingDir, $setupDir . "/upg_{$long}" );
|
||||
@@ -3013,7 +3028,7 @@ EOF;
|
||||
$xml->text( "Invision Power Services, Inc" );
|
||||
$xml->endAttribute();
|
||||
$xml->startAttribute('author_url');
|
||||
$xml->text( "http://www.invisionpower.com" );
|
||||
$xml->text( "https://www.invisioncommunity.com" );
|
||||
$xml->endAttribute();
|
||||
|
||||
/* Skin settings */
|
||||
@@ -3612,10 +3627,10 @@ EOF;
|
||||
/**
|
||||
* Can the user access this application?
|
||||
*
|
||||
* @param \IPS\Member|NULL $member Member we are checking against or NULL for currently logged on user
|
||||
* @param \IPS\Member|\IPS\Member\Group|NULL $memberOrGroup Member/group we are checking against or NULL for currently logged on user
|
||||
* @return bool
|
||||
*/
|
||||
public function canAccess( $member=NULL )
|
||||
public function canAccess( $memberOrGroup=NULL )
|
||||
{
|
||||
/* If it's not enabled, we can't */
|
||||
if( !$this->enabled )
|
||||
@@ -3636,8 +3651,15 @@ EOF;
|
||||
}
|
||||
|
||||
/* Check member */
|
||||
$member = ( $member === NULL ) ? \IPS\Member::loggedIn() : $member;
|
||||
$memberGroups = array_merge( array( $member->member_group_id ), array_filter( explode( ',', $member->mgroup_others ) ) );
|
||||
if ( $memberOrGroup instanceof \IPS\Member\Group )
|
||||
{
|
||||
$memberGroups = array( $memberOrGroup->g_id );
|
||||
}
|
||||
else
|
||||
{
|
||||
$member = ( $memberGroups === NULL ) ? \IPS\Member::loggedIn() : $memberGroups;
|
||||
$memberGroups = array_merge( array( $member->member_group_id ), array_filter( explode( ',', $member->mgroup_others ) ) );
|
||||
}
|
||||
$accessGroups = explode( ',', $this->disabled_groups );
|
||||
|
||||
/* Are we in an allowed group? */
|
||||
@@ -3672,6 +3694,18 @@ EOF;
|
||||
public function save( $skipMember=FALSE )
|
||||
{
|
||||
parent::save();
|
||||
static::postToggleEnable( $skipMember );
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleanup after saving
|
||||
*
|
||||
* @param bool $skipMember Skip clearing member cache clearing
|
||||
* @return void
|
||||
* @note This is abstracted so it can be called externally, i.e. by the support tool
|
||||
*/
|
||||
public static function postToggleEnable( $skipMember=FALSE )
|
||||
{
|
||||
unset( \IPS\Data\Store::i()->applications );
|
||||
unset( \IPS\Data\Store::i()->frontNavigation );
|
||||
|
||||
@@ -3724,9 +3758,15 @@ EOF;
|
||||
$templatesToRecompile[ $class ] = $class;
|
||||
}
|
||||
|
||||
/* Delete profile steps */
|
||||
\IPS\Member\ProfileStep::deleteByApplication( $this );
|
||||
|
||||
/* Delete menu items */
|
||||
\IPS\core\FrontNavigation::deleteByApplication( $this );
|
||||
|
||||
/* Delete club node maps */
|
||||
\IPS\Member\Club::deleteByApplication( $this );
|
||||
|
||||
/* Delete data from shared tables */
|
||||
\IPS\Content\Search\Index::i()->removeApplicationContent( $this );
|
||||
\IPS\Db::i()->delete( 'core_permission_index', array( 'app=? AND perm_type=? AND perm_type_id IN(?)', 'core', 'module', \IPS\Db::i()->select( 'sys_module_id', 'core_modules', array( 'sys_module_application=?', $this->directory ) ) ) );
|
||||
@@ -3742,6 +3782,7 @@ EOF;
|
||||
\IPS\Db::i()->delete( 'core_queue', array( 'app=?', $this->directory ) );
|
||||
\IPS\Db::i()->delete( 'core_follow', array( 'follow_app=?', $this->directory ) );
|
||||
\IPS\Db::i()->delete( 'core_view_updates', array( "classname LIKE CONCAT( ?, '%' )", "IPS\\\\{$this->directory}" ) );
|
||||
\IPS\Db::i()->delete( 'core_moderator_logs', array( 'appcomponent=?', $this->directory ) );
|
||||
|
||||
$classes = array();
|
||||
foreach( $this->extensions( 'core', 'ContentRouter' ) AS $contentRouter )
|
||||
@@ -3778,6 +3819,9 @@ EOF;
|
||||
}
|
||||
|
||||
\IPS\Db::i()->delete( 'core_notifications', \IPS\Db::i()->in( 'item_class', $classes ) );
|
||||
|
||||
/* Delete Deletion Log Records */
|
||||
\IPS\Db::i()->delete( 'core_deletion_log', \IPS\Db::i()->in( 'dellog_content_class', $classes ) );
|
||||
}
|
||||
|
||||
unset( \IPS\Data\Store::i()->settings );
|
||||
|
||||
Reference in new issue
Block a user