Version 5.0.0 beta 1
This commit is contained in:
1 parent
25ddeb65d6
commit
15c7beabc5
6736 files changed
+627902
-497943
No files matched your search
@@ -11,16 +11,20 @@
|
||||
namespace IPS\Http\Url;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use DomainException;
|
||||
use function defined;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL Exception
|
||||
*/
|
||||
class _Exception extends \DomainException
|
||||
class Exception extends DomainException
|
||||
{
|
||||
|
||||
}
|
||||
+240
-113
@@ -11,51 +11,69 @@
|
||||
namespace IPS\Http\Url;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use IPS\Application;
|
||||
use IPS\cms\Pages\Page;
|
||||
use IPS\Data\Store;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Settings;
|
||||
use IPS\Db;
|
||||
use OutOfRangeException;
|
||||
use UnderflowException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function file_get_contents;
|
||||
use function intval;
|
||||
use function is_array;
|
||||
use function is_null;
|
||||
use function is_string;
|
||||
use const IPS\DEV_USE_FURL_CACHE;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Friendly URL
|
||||
*/
|
||||
class _Friendly extends \IPS\Http\Url\Internal
|
||||
class Friendly extends Internal
|
||||
{
|
||||
/**
|
||||
* @brief Base
|
||||
*/
|
||||
public $base = 'front';
|
||||
public ?string $base = 'front';
|
||||
|
||||
/**
|
||||
* @brief SEO Template
|
||||
*/
|
||||
public $seoTemplate;
|
||||
public ?string $seoTemplate = NULL;
|
||||
|
||||
/**
|
||||
* @brief SEO Titles (unencoded)
|
||||
*/
|
||||
public $seoTitles = array();
|
||||
public array $seoTitles = array();
|
||||
|
||||
/**
|
||||
* @brief The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
||||
*/
|
||||
public $friendlyUrlComponent;
|
||||
public ?string $friendlyUrlComponent;
|
||||
|
||||
/* !Factory methods */
|
||||
|
||||
/**
|
||||
* Create a friendly URL from a query string
|
||||
*
|
||||
* @param int $protocol Protocol (one of the PROTOCOL_* constants)
|
||||
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
||||
* @param array $queryString Additional query string data
|
||||
* @param int $protocol Protocol (one of the PROTOCOL_* constants)
|
||||
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
||||
* @param array|string $queryString Additional query string data
|
||||
* @return static
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function friendlyUrlFromComponent( $protocol, $friendlyUrlComponent, $queryString )
|
||||
public static function friendlyUrlFromComponent( int $protocol, string $friendlyUrlComponent, array|string $queryString ): Url
|
||||
{
|
||||
if ( \IPS\Settings::i()->htaccess_mod_rewrite )
|
||||
if ( Settings::i()->htaccess_mod_rewrite )
|
||||
{
|
||||
return static::createInternalFromComponents(
|
||||
'front',
|
||||
@@ -66,11 +84,20 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
}
|
||||
else
|
||||
{
|
||||
if( is_string( $queryString ) )
|
||||
{
|
||||
parse_str( $queryString, $queryStringArray );
|
||||
}
|
||||
else
|
||||
{
|
||||
$queryStringArray = $queryString;
|
||||
}
|
||||
|
||||
return static::createInternalFromComponents(
|
||||
'front',
|
||||
$protocol,
|
||||
'index.php',
|
||||
( $friendlyUrlComponent ? array( "/{$friendlyUrlComponent}/" => NULL ) : array() ) + $queryString
|
||||
( $friendlyUrlComponent ? array( "/{$friendlyUrlComponent}/" => NULL ) : array() ) + $queryStringArray
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -78,11 +105,11 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* Create a friendly URL from a full URL, working out friendly URL data
|
||||
*
|
||||
* @param array $components An array of components as returned by componentsFromUrlString()
|
||||
* @param string $potentialFurl The potential FURL slug (e.g. "topic/1-test")
|
||||
* @param array $components An array of components as returned by componentsFromUrlString()
|
||||
* @param string $potentialFurl The potential FURL slug (e.g. "topic/1-test")
|
||||
* @return static|NULL
|
||||
*/
|
||||
public static function createFriendlyUrlFromComponents( $components, $potentialFurl )
|
||||
public static function createFriendlyUrlFromComponents( array $components, string $potentialFurl ): ?static
|
||||
{
|
||||
/* Loop each of our friendly URL definitions */
|
||||
$matchedFurlDefinition = NULL;
|
||||
@@ -92,7 +119,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
if ( $returnedMatches = static::getMatchedParamsFromFriendlyUrlComponent( $furlDefinition, $potentialFurl ) )
|
||||
{
|
||||
$matchedFurlDefinition = $furlDefinition;
|
||||
list( $matchedParams, $seoTitles ) = $returnedMatches;
|
||||
[ $matchedParams, $seoTitles ] = $returnedMatches;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -100,35 +127,100 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/* If we didn't get one return NULL */
|
||||
if ( !$matchedFurlDefinition )
|
||||
{
|
||||
return NULL;
|
||||
$return = NULL;
|
||||
}
|
||||
|
||||
/* If we did, create our object... */
|
||||
return static::createFromComponents( $components[ static::COMPONENT_HOST ], $components[ static::COMPONENT_SCHEME ], $components[ static::COMPONENT_PATH ], $components[ static::COMPONENT_QUERY ], $components[ static::COMPONENT_PORT ], $components[ static::COMPONENT_USERNAME ], $components[ static::COMPONENT_PASSWORD ], $components[ static::COMPONENT_FRAGMENT ] )
|
||||
->setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams, $potentialFurl );
|
||||
else if( $return = static::createFromComponents( $components[ static::COMPONENT_HOST ], $components[ static::COMPONENT_SCHEME ], $components[ static::COMPONENT_PATH ], $components[ static::COMPONENT_QUERY ], $components[ static::COMPONENT_PORT ], $components[ static::COMPONENT_USERNAME ], $components[ static::COMPONENT_PASSWORD ], $components[ static::COMPONENT_FRAGMENT ] )
|
||||
->setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams, $potentialFurl ) or !$potentialFurl )
|
||||
{
|
||||
if ( !Application::appIsEnabled('cms') or !Application::load('cms')->default OR $potentialFurl )
|
||||
{
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
if( Application::appIsEnabled('cms') )
|
||||
{
|
||||
/* Try to find a page */
|
||||
try
|
||||
{
|
||||
[ $pagePath, $pageNumber ] = Page::getStrippedPagePath( $potentialFurl );
|
||||
|
||||
/* Is this from the static folders? */
|
||||
if ( mb_substr( $pagePath, 0, 7 ) === 'static/' )
|
||||
{
|
||||
/* Yes, so just return to avoid looking it up in pages when it won't be there */
|
||||
return $return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$page = Page::loadFromPath( $pagePath );
|
||||
}
|
||||
catch( \Exception $e )
|
||||
{
|
||||
/* Try from furl */
|
||||
try
|
||||
{
|
||||
$page = Page::load( Db::i()->select( 'store_current_id', 'cms_url_store', array( 'store_type=? and store_path=?', 'page', $pagePath ) )->first() );
|
||||
}
|
||||
catch( UnderflowException $e )
|
||||
{
|
||||
throw new OutOfRangeException;
|
||||
}
|
||||
}
|
||||
|
||||
return static::createFromComponents( $components[ static::COMPONENT_HOST ], $components[ static::COMPONENT_SCHEME ], $components[ static::COMPONENT_PATH ], $components[ static::COMPONENT_QUERY ], $components[ static::COMPONENT_PORT ], $components[ static::COMPONENT_USERNAME ], $components[ static::COMPONENT_PASSWORD ], $components[ static::COMPONENT_FRAGMENT ] )
|
||||
->setFriendlyUrlData( 'content_page_path', array( $potentialFurl ), array( 'path' => $potentialFurl ), $potentialFurl );
|
||||
}
|
||||
/* Couldn't find one? Don't accept responsibility */
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
return $return;
|
||||
}
|
||||
/* The table may not yet exist if we're using the parser in an upgrade */
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
if( $e->getCode() == 1146 )
|
||||
{
|
||||
return $return;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a friendly URL from a query string with known friendly URL data
|
||||
*
|
||||
* @param string $queryString The query string
|
||||
* @param string $seoTemplate The key for making this a friendly URL
|
||||
* @param string|array $seoTitles The title(s) needed for the friendly URL
|
||||
* @param int $protocol Protocol (one of the PROTOCOL_* constants)
|
||||
* @param string $queryString The query string
|
||||
* @param string $seoTemplate The key for making this a friendly URL
|
||||
* @param array|string $seoTitles The title(s) needed for the friendly URL
|
||||
* @param int $protocol Protocol (one of the PROTOCOL_* constants)
|
||||
* @return static
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
* @throws Exception
|
||||
* @todo Currently this will silently return a non-friendly URL if $seoTemplate is not valid, for backwards compatibility. Remove this in a major update.
|
||||
*/
|
||||
public static function friendlyUrlFromQueryString( $queryString, $seoTemplate, $seoTitles, $protocol )
|
||||
public static function friendlyUrlFromQueryString( string $queryString, string $seoTemplate, array|string $seoTitles, int $protocol ): Url
|
||||
{
|
||||
$_originalQueryString = $queryString;
|
||||
|
||||
if ( $seoTemplate === 'content_page_path' )
|
||||
{
|
||||
/* Get the friendly URL component */
|
||||
$friendlyUrlComponent = static::buildFriendlyUrlComponentFromData( $queryString, $seoTemplate, $seoTitles );
|
||||
|
||||
return static::friendlyUrlFromComponent( $protocol, $friendlyUrlComponent, $queryString )->setFriendlyUrlData( $seoTemplate, $seoTitles, array( 'path' => $friendlyUrlComponent ), $friendlyUrlComponent );
|
||||
}
|
||||
|
||||
/* Get the friendly URL component */
|
||||
try
|
||||
{
|
||||
$friendlyUrlComponent = static::buildFriendlyUrlComponentFromData( $queryString, $seoTemplate, $seoTitles );
|
||||
}
|
||||
catch ( \IPS\Http\Url\Exception $e )
|
||||
catch (Exception $e )
|
||||
{
|
||||
if ( $e->getMessage() === 'INVALID_SEO_TEMPLATE' and !\IPS\IN_DEV )
|
||||
{
|
||||
@@ -139,23 +231,23 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Extract the hidden query string parameters inside it */
|
||||
$matchedParams = array();
|
||||
$furlDefinition = static::furlDefinition();
|
||||
if ( !isset( $furlDefinition[ $seoTemplate ] ) )
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
||||
throw new Exception( 'INVALID_SEO_TEMPLATE' );
|
||||
}
|
||||
if ( $returnedMatches = static::getMatchedParamsFromFriendlyUrlComponent( $furlDefinition[ $seoTemplate ], $friendlyUrlComponent ) )
|
||||
{
|
||||
list( $matchedParams, $_seoTitles ) = $returnedMatches;
|
||||
[ $matchedParams, $_seoTitles ] = $returnedMatches;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( \IPS\IN_DEV )
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'SEO_TEMPLATE_IS_NOT_VALID_FOR_URL: ' . $_originalQueryString . ' - ' . $seoTemplate );
|
||||
throw new Exception( 'SEO_TEMPLATE_IS_NOT_VALID_FOR_URL: ' . $_originalQueryString . ' - ' . $seoTemplate );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,27 +258,42 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* Set friendly URL data
|
||||
*
|
||||
* @param string $seoTemplate The key for making this a friendly URL
|
||||
* @param string|array $seoTitles The title(s) needed for the friendly URL
|
||||
* @param array $matchedParams The values for hidden query string properties
|
||||
* @param string $seoTemplate The key for making this a friendly URL
|
||||
* @param array|string $seoTitles The title(s) needed for the friendly URL
|
||||
* @param array $matchedParams The values for hidden query string properties
|
||||
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
||||
* @return static
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams=array(), string $friendlyUrlComponent = '' )
|
||||
protected function setFriendlyUrlData( string $seoTemplate, array|string $seoTitles, array $matchedParams=array(), string $friendlyUrlComponent = '' ): static
|
||||
{
|
||||
if ( $seoTemplate === 'content_page_path' )
|
||||
{
|
||||
/* Set basic properties */
|
||||
$this->seoTemplate = 'content_page_path';
|
||||
$this->seoTitles = is_string( $seoTitles ) ? array( $seoTitles ) : $seoTitles;
|
||||
$this->friendlyUrlComponent = $friendlyUrlComponent;
|
||||
$this->seoPagination = true;
|
||||
|
||||
/* Set hidden query string */
|
||||
$this->hiddenQueryString = array( 'app' => 'cms', 'module' => 'pages', 'controller' => 'page' ) + $matchedParams;
|
||||
|
||||
/* Return */
|
||||
return $this;
|
||||
}
|
||||
|
||||
/* Get the definition */
|
||||
$furlDefinition = static::furlDefinition();
|
||||
if ( !isset( $furlDefinition[ $seoTemplate ] ) )
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
||||
throw new Exception( 'INVALID_SEO_TEMPLATE' );
|
||||
}
|
||||
|
||||
/* Set basic properties */
|
||||
$this->seoTemplate = $seoTemplate;
|
||||
$this->seoTitles = \is_string( $seoTitles ) ? array( $seoTitles ) : $seoTitles;
|
||||
$this->seoTitles = is_string( $seoTitles ) ? array( $seoTitles ) : $seoTitles;
|
||||
$this->friendlyUrlComponent = $friendlyUrlComponent;
|
||||
$this->seoPagination = ! empty( $furlDefinition[ $seoTemplate ]['seoPagination'] ) ? true : false;
|
||||
$this->seoPagination = ! empty( $furlDefinition[ $seoTemplate ]['seoPagination'] );
|
||||
|
||||
/* Set hidden query string */
|
||||
parse_str( $furlDefinition[ $seoTemplate ]['real'], $hiddenQueryString );
|
||||
@@ -201,7 +308,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
*
|
||||
* @return mixed The correct URL if incorrect, TRUE if correct, or NULL if unknown
|
||||
*/
|
||||
public function correctFriendlyUrl()
|
||||
public function correctFriendlyUrl(): mixed
|
||||
{
|
||||
/* Get the definition */
|
||||
$furlDefinition = static::furlDefinition();
|
||||
@@ -262,7 +369,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
return NULL;
|
||||
}
|
||||
/* It doesn't exist */
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -271,9 +378,9 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* Normalise the URL for comparing if the "correct" URL is different to the accessed URL
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
* @return Url
|
||||
*/
|
||||
public function normaliseForEqualityCheck()
|
||||
public function normaliseForEqualityCheck(): Url
|
||||
{
|
||||
/* Strip the query string except the FURL component */
|
||||
$return = $this->stripQueryString();
|
||||
@@ -284,9 +391,6 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/* Remove pagination attributes */
|
||||
$return = $return->setPage();
|
||||
|
||||
/* now remove any trailing / from the path */
|
||||
$return = $return->setPath( rtrim( $return->data['path'], '/' ) );
|
||||
|
||||
/* Return */
|
||||
return $return;
|
||||
}
|
||||
@@ -295,12 +399,12 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
* Strip Query String
|
||||
* Overrides main method so that the FURL slug isn't lost
|
||||
*
|
||||
* @param string|array $keys The key(s) to strip - if omitted, entire query string is wiped
|
||||
* @return \IPS\Http\Url
|
||||
* @param array|string|null $keys The key(s) to strip - if omitted, entire query string is wiped
|
||||
* @return Url
|
||||
*/
|
||||
public function stripQueryString( $keys=NULL )
|
||||
public function stripQueryString( array|string $keys=NULL ): Url
|
||||
{
|
||||
if ( $keys === NULL and !\IPS\Settings::i()->htaccess_mod_rewrite )
|
||||
if ( $keys === NULL and !Settings::i()->htaccess_mod_rewrite )
|
||||
{
|
||||
$keys = $this->queryString;
|
||||
unset( $keys[ "/{$this->friendlyUrlComponent}/" ] );
|
||||
@@ -313,11 +417,11 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* Adds the page parameter to the URL
|
||||
*
|
||||
* @param string $param The page key, default is 'page'
|
||||
* @param int|NULL $number The page number to use
|
||||
* @return \IPS\Http\Url
|
||||
* @param string $param The page key, default is 'page'
|
||||
* @param int|null $number The page number to use
|
||||
* @return Url
|
||||
*/
|
||||
public function setPage( $param='page', $number=1 )
|
||||
public function setPage( string $param='page', ?int $number=1 ): Url
|
||||
{
|
||||
$number = (int) $number;
|
||||
if ( $param === 'page' )
|
||||
@@ -344,9 +448,9 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
}
|
||||
|
||||
/* If rewrite furls is disabled, or specifically if the request has index.php in the URL (it may be a cached URL from before the setting was toggled) */
|
||||
if ( !\IPS\Settings::i()->htaccess_mod_rewrite OR static::fixComponentPath( $components[ static::COMPONENT_PATH ] ) === 'index.php' )
|
||||
if ( !Settings::i()->htaccess_mod_rewrite OR static::fixComponentPath( $components[ static::COMPONENT_PATH ] ) === 'index.php' )
|
||||
{
|
||||
if ( \is_array( $components[ static::COMPONENT_QUERY ] ) and \count( $components[ static::COMPONENT_QUERY ] ) )
|
||||
if ( is_array( $components[ static::COMPONENT_QUERY ] ) and count( $components[ static::COMPONENT_QUERY ] ) )
|
||||
{
|
||||
$new = NULL;
|
||||
foreach( $components[ static::COMPONENT_QUERY ] as $k => $v )
|
||||
@@ -377,10 +481,10 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
}
|
||||
|
||||
/* Add on the page itself */
|
||||
if ( \intval( $number ) > 1 )
|
||||
if ( $number > 1 )
|
||||
{
|
||||
$potentialFurl = NULL;
|
||||
if ( ! \IPS\Settings::i()->htaccess_mod_rewrite OR static::fixComponentPath( $components[ static::COMPONENT_PATH ] ) === 'index.php' )
|
||||
if ( ! Settings::i()->htaccess_mod_rewrite OR static::fixComponentPath( $components[ static::COMPONENT_PATH ] ) === 'index.php' )
|
||||
{
|
||||
foreach( $components[ static::COMPONENT_QUERY ] as $k => $v )
|
||||
{
|
||||
@@ -421,7 +525,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
*
|
||||
* @return string|NULL The furl part of the URL, eg /topic/1-foo/
|
||||
*/
|
||||
protected function getFriendlyComponent()
|
||||
protected function getFriendlyComponent(): ?string
|
||||
{
|
||||
/* Are we lucky? */
|
||||
if ( ! empty( $this->friendlyUrlComponent ) )
|
||||
@@ -430,9 +534,9 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
}
|
||||
|
||||
/* Of course not... */
|
||||
if ( ! \IPS\Settings::i()->htaccess_mod_rewrite )
|
||||
if ( ! Settings::i()->htaccess_mod_rewrite )
|
||||
{
|
||||
if ( \is_array( $this->queryString ) and \count( $this->queryString ) )
|
||||
if ( is_array( $this->queryString ) and count( $this->queryString ) )
|
||||
{
|
||||
foreach( $this->queryString as $k => $v )
|
||||
{
|
||||
@@ -459,7 +563,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
* @param string $path The path from ->data['path'] eg forums/topic/1-foo
|
||||
* @return string The furl part of the path, eg topic/1-foo
|
||||
*/
|
||||
public static function fixComponentPath( $path )
|
||||
public static function fixComponentPath( string $path ): string
|
||||
{
|
||||
$baseUrl = parse_url( static::baseUrl() );
|
||||
|
||||
@@ -478,10 +582,10 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* Returns the matching FURL definition data for the FURL component eg /topic/1-foo/
|
||||
*
|
||||
* @param string $friendlyUrlComponent The furl part of the path, eg /topic/1-foo/
|
||||
* @param string $friendlyUrlComponent The furl part of the path, eg /topic/1-foo/
|
||||
* @return array|NULL The FURL definition
|
||||
*/
|
||||
protected static function getFurlDefinitionFromPath( $friendlyUrlComponent )
|
||||
protected static function getFurlDefinitionFromPath( string $friendlyUrlComponent ): ?array
|
||||
{
|
||||
foreach ( static::furlDefinition() as $seoTemplate => $furlDefinition )
|
||||
{
|
||||
@@ -500,11 +604,11 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* Removes any inline page component from the FURL component eg /topic/1-foo/page/2/
|
||||
*
|
||||
* @param string $friendlyUrlComponent The furl part of the path, eg /topic/1-foo/
|
||||
* @param string $param The /page/N/ key
|
||||
* @param string $friendlyUrlComponent The furl part of the path, eg /topic/1-foo/
|
||||
* @param string $param The /page/N/ key
|
||||
* @return string The cleaned FURL eg /topic/1-foo/
|
||||
*/
|
||||
public static function stripPageComponent( $friendlyUrlComponent, $param='page' )
|
||||
public static function stripPageComponent( string $friendlyUrlComponent, string $param='page' ): string
|
||||
{
|
||||
return preg_replace( '#/' . $param . '/(\d+?)$#', '', rtrim( $friendlyUrlComponent, '/' ) ) . '/';
|
||||
}
|
||||
@@ -512,39 +616,55 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* Get friendly URL component (e.g. "topic/1-test") from a query string and SEO template
|
||||
*
|
||||
* @param string $queryString The query string - is passed by reference and any parts used are removed, which can be used to detect extraneous parts
|
||||
* @param string $seoTemplate The key for making this a friendly URL
|
||||
* @param string|array $seoTitles The title(s) needed for the friendly URL
|
||||
* @param string $queryString The query string - is passed by reference and any parts used are removed, which can be used to detect extraneous parts
|
||||
* @param string $seoTemplate The key for making this a friendly URL
|
||||
* @param array|string|null $seoTitles The title(s) needed for the friendly URL
|
||||
* @return string
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function buildFriendlyUrlComponentFromData( &$queryString, $seoTemplate, $seoTitles )
|
||||
public static function buildFriendlyUrlComponentFromData( string &$queryString, string $seoTemplate, array|string|null $seoTitles ): string
|
||||
{
|
||||
if ( $seoTemplate === 'content_page_path' )
|
||||
{
|
||||
parse_str( $queryString, $queryStringParts );
|
||||
unset( $queryStringParts['app'] );
|
||||
unset( $queryStringParts['module'] );
|
||||
unset( $queryStringParts['controller'] );
|
||||
unset( $queryStringParts['page'] );
|
||||
|
||||
$return = $queryStringParts['path'];
|
||||
unset( $queryStringParts['path'] );
|
||||
|
||||
$queryString = http_build_query( $queryStringParts );
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/* Get the definition */
|
||||
$furlDefinition = static::furlDefinition();
|
||||
if ( !isset( $furlDefinition[ $seoTemplate ] ) )
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
||||
throw new Exception( 'INVALID_SEO_TEMPLATE ' . $seoTemplate );
|
||||
}
|
||||
$component = $furlDefinition[ $seoTemplate ]['friendly'];
|
||||
|
||||
/* Parse the query string into an array */
|
||||
parse_str( $queryString, $queryString );
|
||||
|
||||
parse_str( $queryString, $queryStringParts );
|
||||
|
||||
/* For each query string component, replace it out */
|
||||
foreach ( $queryString as $k => $v )
|
||||
foreach ( $queryStringParts as $k => $v )
|
||||
{
|
||||
if ( mb_strpos( $component, "{#{$k}}" ) !== FALSE or mb_strpos( $component, "{@{$k}}" ) !== FALSE )
|
||||
{
|
||||
$component = str_replace( "{#{$k}}", \intval( $v ), $component );
|
||||
$component = str_replace( "{#{$k}}", intval( $v ), $component );
|
||||
$component = str_replace( "{@{$k}}", $v, $component );
|
||||
unset( $queryString[ $k ] );
|
||||
unset( $queryStringParts[ $k ] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse out the titles */
|
||||
$seoTitles = \is_null( $seoTitles ) ? array() : ( \is_string( $seoTitles ) ? array( $seoTitles ) : array_values( $seoTitles ) );
|
||||
$seoTitlesCount = \count( $seoTitles );
|
||||
$seoTitles = is_null( $seoTitles ) ? array() : ( is_string( $seoTitles ) ? array( $seoTitles ) : array_values( $seoTitles ) );
|
||||
$seoTitlesCount = count( $seoTitles );
|
||||
for ( $i = 0; $i < $seoTitlesCount; $i++ )
|
||||
{
|
||||
if ( $i === 0 )
|
||||
@@ -558,11 +678,13 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
parse_str( $furlDefinition[ $seoTemplate ]['real'], $realQueryString );
|
||||
foreach ( $realQueryString as $k => $v )
|
||||
{
|
||||
if ( isset( $queryString[ $k ] ) and $queryString[ $k ] == $v )
|
||||
if ( isset( $queryStringParts[ $k ] ) and $queryStringParts[ $k ] == $v )
|
||||
{
|
||||
unset( $queryString[ $k ] );
|
||||
unset( $queryStringParts[ $k ] );
|
||||
}
|
||||
}
|
||||
|
||||
$queryString = http_build_query( $queryStringParts );
|
||||
|
||||
/* Return */
|
||||
return $component;
|
||||
@@ -575,7 +697,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
* @return string
|
||||
* @note Many places require an SEO title, so we always need to return something, so when no valid title is available we return a dash
|
||||
*/
|
||||
public static function seoTitle( $value )
|
||||
public static function seoTitle( string $value ): string
|
||||
{
|
||||
/* Ensure there are no HTML tags */
|
||||
$value = strip_tags( $value );
|
||||
@@ -604,11 +726,11 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* Get the matched parameters and SEO titles from a friendly URL component for a particular
|
||||
*
|
||||
* @param array $furlDefinition The FURL definition from the furl.json file
|
||||
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
||||
* @param array $furlDefinition The FURL definition from the furl.json file
|
||||
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
||||
* @return array|NULL array( $matchedParams, $seoTitles ) if matched, NULL if the $friendlyUrlComponent does not match this $furlDefinition
|
||||
*/
|
||||
protected static function getMatchedParamsFromFriendlyUrlComponent( $furlDefinition, $friendlyUrlComponent )
|
||||
protected static function getMatchedParamsFromFriendlyUrlComponent( array $furlDefinition, string $friendlyUrlComponent ): ?array
|
||||
{
|
||||
/* See if it matches */
|
||||
$seoTitles = array();
|
||||
@@ -625,13 +747,13 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
{
|
||||
foreach( $matches as $k => $v )
|
||||
{
|
||||
if ( $k and mb_strstr( $matches[ $k ], '/page/' ) )
|
||||
if ( $k and mb_strstr( $v, '/page/' ) )
|
||||
{
|
||||
list( $x, $page ) = explode( '/', trim( $matches[ $k ], '/' ) );
|
||||
[ $x, $page ] = explode( '/', trim( $v, '/' ) );
|
||||
|
||||
if ( $page )
|
||||
{
|
||||
$matchedParams['page'] = \intval( $page );
|
||||
$matchedParams['page'] = intval( $page );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -660,7 +782,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* @brief FURL Definition
|
||||
*/
|
||||
protected static $furlDefinition = NULL;
|
||||
protected static ?array $furlDefinition = NULL;
|
||||
|
||||
/**
|
||||
* Get FURL Definition
|
||||
@@ -668,21 +790,26 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
* @param bool $revert If TRUE, ignores all customisations and reloads from json
|
||||
* @return array
|
||||
*/
|
||||
public static function furlDefinition( $revert=FALSE )
|
||||
public static function furlDefinition( bool $revert=FALSE ): array
|
||||
{
|
||||
if ( static::$furlDefinition === NULL or $revert )
|
||||
{
|
||||
$furlCustomizations = ( \IPS\Settings::i()->furl_configuration AND !$revert and ! \IPS\IN_DEV ) ? json_decode( \IPS\Settings::i()->furl_configuration, TRUE ) : array();
|
||||
$furlConfiguration = ( isset( \IPS\Data\Store::i()->furl_configuration ) AND \IPS\Data\Store::i()->furl_configuration ) ? json_decode( \IPS\Data\Store::i()->furl_configuration, TRUE ) : array();
|
||||
$furlCustomizations = ( Settings::i()->furl_configuration AND !$revert and ! \IPS\IN_DEV ) ? json_decode( Settings::i()->furl_configuration, TRUE ) : array();
|
||||
$furlConfiguration = ( isset( Store::i()->furl_configuration ) AND Store::i()->furl_configuration ) ? json_decode( Store::i()->furl_configuration, TRUE ) : array();
|
||||
|
||||
if ( ( \IPS\IN_DEV and !\IPS\DEV_USE_FURL_CACHE ) or !\count( $furlConfiguration ) or $revert )
|
||||
if ( ( \IPS\IN_DEV and !DEV_USE_FURL_CACHE ) or !count( $furlConfiguration ) or $revert )
|
||||
{
|
||||
$furlConfiguration = static::buildFurlConfiguation();
|
||||
}
|
||||
|
||||
static::$furlDefinition = array_merge( $furlConfiguration, $furlCustomizations );
|
||||
|
||||
if( Application::appIsEnabled('cms') )
|
||||
{
|
||||
static::$furlDefinition= array_merge( static::$furlDefinition, array( 'content_page_path' => static::buildFurlDefinition( 'app=cms&module=pages&controller=page', 'app=cms&module=pages&controller=page', NULL, FALSE, NULL, FALSE, 'IPS\cms\Pages\Router' ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return static::$furlDefinition;
|
||||
}
|
||||
|
||||
@@ -691,13 +818,13 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function buildFurlConfiguation()
|
||||
protected static function buildFurlConfiguation(): array
|
||||
{
|
||||
/* Init */
|
||||
$furlConfiguration = array();
|
||||
|
||||
/* Load apps, prioritising the default (otherwise if two apps both have "/category/..." the app which is higher in the list may steal from the default app) */
|
||||
$applications = \IPS\Application::applications();
|
||||
$applications = Application::applications();
|
||||
foreach ( $applications as $k => $app )
|
||||
{
|
||||
if ( $app->default )
|
||||
@@ -715,14 +842,14 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
if( $app->enabled and file_exists( $app->getApplicationPath() . "/data/furl.json" ) )
|
||||
{
|
||||
/* Open it up */
|
||||
$data = json_decode( preg_replace( '/\/\*.+?\*\//s', '', \file_get_contents( $app->getApplicationPath() . "/data/furl.json" ) ), TRUE );
|
||||
$data = json_decode( preg_replace( '/\/\*.+?\*\//s', '', file_get_contents( $app->getApplicationPath() . "/data/furl.json" ) ), TRUE );
|
||||
$topLevel = $data['topLevel'];
|
||||
|
||||
/* Process them */
|
||||
$definitions = array();
|
||||
foreach ( $data['pages'] as $k => $page )
|
||||
{
|
||||
$definitions[ $k ] = static::buildFurlDefinition( $page['friendly'], $page['real'], $topLevel, $app->default, isset( $page['alias'] ) ? $page['alias'] : NULL, FALSE, isset( $page['verify'] ) ? $page['verify'] : NULL, isset( $page['seoTitles'] ) ? $page['seoTitles'] : NULL, isset( $page['seoPagination'] ) ? $page['seoPagination'] : NULL );
|
||||
$definitions[ $k ] = static::buildFurlDefinition($page['friendly'], $page['real'], $topLevel, $app->default, $page['alias'] ?? NULL, FALSE, $page['verify'] ?? NULL, $page['seoTitles'] ?? NULL, $page['seoPagination'] ?? NULL );
|
||||
}
|
||||
|
||||
/* Add it in */
|
||||
@@ -731,7 +858,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
}
|
||||
|
||||
/* Store */
|
||||
\IPS\Data\Store::i()->furl_configuration = json_encode( $furlConfiguration );
|
||||
Store::i()->furl_configuration = json_encode( $furlConfiguration );
|
||||
|
||||
/* Return */
|
||||
return $furlConfiguration;
|
||||
@@ -740,18 +867,18 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* Build the friendly URL definition
|
||||
*
|
||||
* @param string $friendly Friendly URL pattern
|
||||
* @param string $real Non-friendly URL pattern
|
||||
* @param NULL|string $appTopLevel FURL slug if the app is not the default app
|
||||
* @param bool $appIsDefault Flag to indicate if the app is default or not
|
||||
* @param NULL|string $alias Friendly URL alias
|
||||
* @param bool $custom Flag to indicate if this is a custom FURL definition
|
||||
* @param string $verify The name of a class that contains a loadFromUrl() and an url() method for verifying the friendly URL is correct
|
||||
* @param array $seoTitles The class, query param and property to load from to rebuild seo titles
|
||||
* @param bool|null $seoPagination Whether to use SEO-friendly pagination (e.g. /page/2/) or not
|
||||
* @param string $friendly Friendly URL pattern
|
||||
* @param string $real Non-friendly URL pattern
|
||||
* @param string|null $appTopLevel FURL slug if the app is not the default app
|
||||
* @param bool $appIsDefault Flag to indicate if the app is default or not
|
||||
* @param string|null $alias Friendly URL alias
|
||||
* @param bool $custom Flag to indicate if this is a custom FURL definition
|
||||
* @param string|null $verify The name of a class that contains a loadFromUrl() and an url() method for verifying the friendly URL is correct
|
||||
* @param array|null $seoTitles The class, query param and property to load from to rebuild seo titles
|
||||
* @param bool|null $seoPagination Whether to use SEO-friendly pagination (e.g. /page/2/) or not
|
||||
* @return array
|
||||
*/
|
||||
public static function buildFurlDefinition( $friendly, $real, $appTopLevel = NULL, $appIsDefault = FALSE, $alias = NULL, $custom = FALSE, $verify = NULL, $seoTitles = NULL, $seoPagination = NULL )
|
||||
public static function buildFurlDefinition( string $friendly, string $real, string $appTopLevel = NULL, bool $appIsDefault=FALSE, string $alias = NULL, bool $custom=FALSE, string $verify = NULL, array $seoTitles = NULL, bool $seoPagination = NULL ): array
|
||||
{
|
||||
/* Init */
|
||||
$return = array(
|
||||
|
||||
@@ -11,30 +11,46 @@
|
||||
namespace IPS\Http\Url;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
|
||||
use Exception;
|
||||
use IPS\Content;
|
||||
use IPS\Http\Url;
|
||||
use IPS\Member\Club;
|
||||
use IPS\Node\Model;
|
||||
use IPS\cms\Pages\Router;
|
||||
use IPS\Session;
|
||||
use IPS\Application;
|
||||
use OutOfRangeException;
|
||||
use function count;
|
||||
use function defined;
|
||||
use function intval;
|
||||
use function is_numeric;
|
||||
use function is_string;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal URL
|
||||
*/
|
||||
class _Internal extends \IPS\Http\Url
|
||||
class Internal extends Url
|
||||
{
|
||||
/**
|
||||
* Create internal URL from components - all arguments should be UNENCODED
|
||||
*
|
||||
* @param string $base Key for the URL base
|
||||
* @param int|string|null $protocol Protocol (one of the PROTOCOL_* constants) or an actual scheme
|
||||
* @param string $path Path, relative to site's base URL
|
||||
* @param string|array|NULL $query Query
|
||||
* @param string|NULL $fragment Fragment
|
||||
* @return \IPS\Http\Url
|
||||
* @param string $base Key for the URL base
|
||||
* @param int|string|null $protocol Protocol (one of the PROTOCOL_* constants) or an actual scheme
|
||||
* @param string $path Path, relative to site's base URL
|
||||
* @param array|string|null $query Query
|
||||
* @param string|null $fragment Fragment
|
||||
* @return Url
|
||||
*/
|
||||
protected static function createInternalFromComponents( $base, $protocol, $path, $query = NULL, $fragment = NULL )
|
||||
protected static function createInternalFromComponents( string $base, int|string|null $protocol, string $path, array|string $query = NULL, string $fragment = NULL ): Url
|
||||
{
|
||||
$baseUrlData = parse_url( static::baseUrl( \is_numeric( $protocol ) ? $protocol : NULL ) );
|
||||
$baseUrlData = parse_url( static::baseUrl( is_numeric( $protocol ) ? $protocol : NULL ) );
|
||||
|
||||
$pathToUse = '';
|
||||
if ( isset( $baseUrlData['path'] ) and $baseUrlData['path'] )
|
||||
@@ -52,12 +68,12 @@ class _Internal extends \IPS\Http\Url
|
||||
|
||||
$return = static::createFromComponents(
|
||||
$baseUrlData['host'],
|
||||
( \is_string( $protocol ) and !\is_numeric( $protocol ) ) ? $protocol : ( ( isset( $baseUrlData['scheme'] ) AND $protocol !== NULL ) ? $baseUrlData['scheme'] : NULL ),
|
||||
( is_string( $protocol ) and !is_numeric( $protocol ) ) ? $protocol : ( ( isset( $baseUrlData['scheme'] ) AND $protocol !== NULL ) ? $baseUrlData['scheme'] : NULL ),
|
||||
$pathToUse ? "/{$pathToUse}" : '',
|
||||
$query,
|
||||
isset( $baseUrlData['port'] ) ? $baseUrlData['port'] : NULL,
|
||||
isset( $baseUrlData['user'] ) ? $baseUrlData['user'] : NULL,
|
||||
isset( $baseUrlData['pass'] ) ? $baseUrlData['pass'] : NULL,
|
||||
$baseUrlData['port'] ?? NULL,
|
||||
$baseUrlData['user'] ?? NULL,
|
||||
$baseUrlData['pass'] ?? NULL,
|
||||
$fragment
|
||||
);
|
||||
|
||||
@@ -68,21 +84,23 @@ class _Internal extends \IPS\Http\Url
|
||||
/**
|
||||
* @brief Does this URL have the SEO pagination (page/n/)?
|
||||
*/
|
||||
public $seoPagination = NULL;
|
||||
public ?bool $seoPagination = NULL;
|
||||
|
||||
/**
|
||||
* @brief Base
|
||||
*/
|
||||
public $base;
|
||||
public ?string $base = NULL;
|
||||
|
||||
/**
|
||||
* Get the friendly URL for this URL if there is one
|
||||
*
|
||||
* @return mixed The friendly URL if there is one, TRUE if there isn't, or NULL if not sure
|
||||
*/
|
||||
public function correctFriendlyUrl()
|
||||
public function correctFriendlyUrl(): mixed
|
||||
{
|
||||
foreach ( \IPS\Http\Url\Friendly::furlDefinition() as $seoTemplate => $furlDefinition )
|
||||
$return = FALSE;
|
||||
|
||||
foreach (Friendly::furlDefinition() as $seoTemplate => $furlDefinition )
|
||||
{
|
||||
if( mb_stripos( $this->data[ static::COMPONENT_QUERY ], $furlDefinition['real'] ) !== FALSE )
|
||||
{
|
||||
@@ -95,9 +113,6 @@ class _Internal extends \IPS\Http\Url
|
||||
switch ( mb_substr( $tag, 0, 1 ) )
|
||||
{
|
||||
case '#':
|
||||
$params[] = mb_substr( $tag, 1 );
|
||||
break;
|
||||
|
||||
case '@':
|
||||
$params[] = mb_substr( $tag, 1 );
|
||||
break;
|
||||
@@ -105,7 +120,7 @@ class _Internal extends \IPS\Http\Url
|
||||
}
|
||||
|
||||
/* If this definition requires a parameter, see if we have it. If not, skip to next definition to check. */
|
||||
if( \count( array_diff( $params, array_keys( $this->queryString ) ) ) )
|
||||
if( count( array_diff( $params, array_keys( $this->queryString ) ) ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -119,16 +134,15 @@ class _Internal extends \IPS\Http\Url
|
||||
/* Load it */
|
||||
try
|
||||
{
|
||||
if ( $return = $this->correctUrlFromVerifyClass( $furlDefinition['verify'] ) )
|
||||
if ( ! $return = $this->correctUrlFromVerifyClass( $furlDefinition['verify'] ) )
|
||||
{
|
||||
return $return;
|
||||
$return = NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/* It doesn't exist */
|
||||
catch ( \OutOfRangeException $e )
|
||||
catch ( OutOfRangeException $e )
|
||||
{
|
||||
return NULL;
|
||||
$return = NULL;
|
||||
}
|
||||
}
|
||||
/* If not, we have to build it ourself */
|
||||
@@ -147,27 +161,62 @@ class _Internal extends \IPS\Http\Url
|
||||
|
||||
$seoTitles[] = $class::load( $this->queryString[ $queryParam ] )->$property;
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) {}
|
||||
catch ( OutOfRangeException $e ) {}
|
||||
}
|
||||
}
|
||||
|
||||
return \IPS\Http\Url::internal( $this->data['query'], 'front', $seoTemplate, $seoTitles );
|
||||
$return = Url::internal( $this->data['query'], 'front', $seoTemplate, $seoTitles );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) {}
|
||||
catch ( Exception $e ) {}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
/* If it it thinks it belongs to "pages", we might be able to be more accurate */
|
||||
if ( Application::appIsEnabled( 'cms' ) and ( $return instanceof Friendly and $return->seoTemplate === 'content_page_path' and isset( $this->queryString['path'] ) ) )
|
||||
{
|
||||
/* Try to find a page */
|
||||
try
|
||||
{
|
||||
/* Create it */
|
||||
$correctUrl = Router::loadFromUrl( $this )->url();
|
||||
|
||||
/* Set extra stuff in our query string */
|
||||
$paramsToSet = array();
|
||||
foreach ( $this->queryString as $k => $v )
|
||||
{
|
||||
if ( !array_key_exists( $k, $correctUrl->queryString ) and !array_key_exists( $k, $correctUrl->hiddenQueryString ) )
|
||||
{
|
||||
$paramsToSet[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
if ( count( $paramsToSet ) )
|
||||
{
|
||||
$correctUrl = $correctUrl->setQueryString( $paramsToSet );
|
||||
}
|
||||
|
||||
/* Return */
|
||||
$return = $correctUrl;
|
||||
}
|
||||
/* Couldn't find one? Don't accept responsibility */
|
||||
catch (OutOfRangeException $e ){}
|
||||
}
|
||||
else if( $return !== NULL )
|
||||
{
|
||||
$return = TRUE;
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct URL using a "verify" property from the FURL definition
|
||||
*
|
||||
* @param object $verify Class containing `preCorrectUrlFromVerifyClass()` method to check URL against
|
||||
* @return NULL|\IPS\Http\Url
|
||||
* @throws \OutOfRangeException
|
||||
* @param mixed $verify Class containing `preCorrectUrlFromVerifyClass()` method to check URL against
|
||||
* @return NULL|Url
|
||||
* @throws OutOfRangeException
|
||||
*/
|
||||
protected function correctUrlFromVerifyClass( $verify )
|
||||
protected function correctUrlFromVerifyClass( mixed $verify ): ?Url
|
||||
{
|
||||
/* Load it */
|
||||
if ( method_exists( $verify, 'preCorrectUrlFromVerifyClass' ) )
|
||||
@@ -179,11 +228,11 @@ class _Internal extends \IPS\Http\Url
|
||||
/* Check the currently logged in user can view it, as we don't want to reveal the correct
|
||||
URL to something the user has no permission to view */
|
||||
$canView = TRUE;
|
||||
if ( $contentObject instanceof \IPS\Content or $contentObject instanceof \IPS\Member\Club )
|
||||
if ( $contentObject instanceof Content or $contentObject instanceof Club )
|
||||
{
|
||||
$canView = $contentObject->canView();
|
||||
}
|
||||
elseif ( $contentObject instanceof \IPS\Node\Model )
|
||||
elseif ( $contentObject instanceof Model )
|
||||
{
|
||||
$canView = $contentObject->can('view');
|
||||
}
|
||||
@@ -204,7 +253,7 @@ class _Internal extends \IPS\Http\Url
|
||||
$paramsToSet[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
if ( \count( $paramsToSet ) )
|
||||
if ( count( $paramsToSet ) )
|
||||
{
|
||||
$correctUrl = $correctUrl->setQueryString( $paramsToSet );
|
||||
}
|
||||
@@ -216,26 +265,26 @@ class _Internal extends \IPS\Http\Url
|
||||
/**
|
||||
* Add CSRF check to query string
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
* @return Url
|
||||
*/
|
||||
public function csrf()
|
||||
public function csrf(): Url
|
||||
{
|
||||
return $this->setQueryString( 'csrfKey', \IPS\Session::i()->csrfKey );
|
||||
return $this->setQueryString( 'csrfKey', Session::i()->csrfKey );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the page parameter to the URL
|
||||
*
|
||||
* @param string $param The page key, default is 'page'
|
||||
* @param int|NULL $number The page number to use
|
||||
* @return \IPS\Http\Url
|
||||
* @param string $param The page key, default is 'page'
|
||||
* @param int|null $number The page number to use
|
||||
* @return Url
|
||||
*/
|
||||
public function setPage( $param='page', $number=1 )
|
||||
public function setPage( string $param='page', ?int $number=1 ): Url
|
||||
{
|
||||
/* Remove any existing page param */
|
||||
$url = $this->stripQueryString( $param );
|
||||
|
||||
if ( \intval( $number ) > 1 )
|
||||
if ( intval( $number ) > 1 )
|
||||
{
|
||||
$url = $this->setQueryString( $param, $number );
|
||||
}
|
||||
@@ -248,7 +297,7 @@ class _Internal extends \IPS\Http\Url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function acpQueryString()
|
||||
public function acpQueryString(): string
|
||||
{
|
||||
$queryString = $this->queryString;
|
||||
unset( $queryString['csrf'] );
|
||||
@@ -261,7 +310,7 @@ class _Internal extends \IPS\Http\Url
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function openRedirect()
|
||||
public function openRedirect(): bool
|
||||
{
|
||||
return isset( $this->queryString['app'] ) and $this->queryString['app'] === 'core' and isset( $this->queryString['module'] ) and $this->queryString['module'] === 'system' and isset( $this->queryString['controller'] ) and $this->queryString['controller'] === 'redirect';
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Raw (data) URL
|
||||
* @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 October 2023
|
||||
*/
|
||||
|
||||
namespace IPS\Http\Url;
|
||||
|
||||
use IPS\Http\Url;
|
||||
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
class Raw extends Url
|
||||
{
|
||||
const TYPE_PLAIN = 'text/plain';
|
||||
const TYPE_SVG = 'image/svg+xml';
|
||||
const TYPE_PNG = 'image/png';
|
||||
const TYPE_JPG = 'image/jpeg';
|
||||
const TYPE_HTML = 'text/html';
|
||||
const TYPE_CSV = 'text/csv';
|
||||
const TYPE_GIF = 'image/gif';
|
||||
|
||||
|
||||
protected string $mimeType = Raw::TYPE_PLAIN;
|
||||
protected string $rawData = '';
|
||||
|
||||
/**
|
||||
* Creates a raw URL (e.g. a data URL)
|
||||
* @param string $data
|
||||
* @param string $mimeType
|
||||
*/
|
||||
public function __construct( string $data, string $mimeType=Raw::TYPE_PLAIN )
|
||||
{
|
||||
$this->setData( $data, $mimeType );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the url as a string when accessing the `url` property. This is because the raw url is often a replacement for a file attahcment, for which the url property is referenced
|
||||
* @param string $key
|
||||
* @return string|void
|
||||
*/
|
||||
public function __get( string $key )
|
||||
{
|
||||
if ($key === 'url') {
|
||||
return (string) $this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the data of this url
|
||||
*
|
||||
* @param string $data
|
||||
* @param ?string $mimeType The mime type. Pass NULL to not override the currently in use mime type
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setData( string $data, ?string $mimeType=null ) : void
|
||||
{
|
||||
$this->rawData = $data;
|
||||
$this->mimeType = $mimeType ?? $this->mimeType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the data
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getData() : string
|
||||
{
|
||||
return $this->rawData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() : string
|
||||
{
|
||||
$encoded = rawurlencode( $this->rawData );
|
||||
return "data:{$this->mimeType},{$encoded}";
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user