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
+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(
|
||||
|
||||
Reference in new issue
Block a user