825 lines
25 KiB
PHP
825 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* @brief dataLayer
|
|
* @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 08 Feb 2022
|
|
*/
|
|
|
|
namespace IPS\core\modules\admin\settings;
|
|
|
|
/* To prevent PHP errors (extending class does not exist) revealing path */
|
|
|
|
use InvalidArgumentException;
|
|
use IPS\Application;
|
|
use IPS\core\DataLayer as DataLayerClass;
|
|
use IPS\core\DataLayer\Handler;
|
|
use IPS\Data\Store;
|
|
use IPS\Db;
|
|
use IPS\Dispatcher;
|
|
use IPS\Dispatcher\Controller;
|
|
use IPS\Helpers\Form;
|
|
use IPS\Helpers\Form\CheckboxSet;
|
|
use IPS\Helpers\Form\Node;
|
|
use IPS\Helpers\Form\Text;
|
|
use IPS\Helpers\Form\TextArea;
|
|
use IPS\Helpers\Form\YesNo;
|
|
use IPS\Helpers\Table\Custom;
|
|
use IPS\Http\Url;
|
|
use IPS\Login\Handler as HandlerClass;
|
|
use IPS\Member;
|
|
use IPS\Output;
|
|
use IPS\Request;
|
|
use IPS\Session;
|
|
use IPS\Settings;
|
|
use IPS\Theme;
|
|
use OutOfRangeException;
|
|
use function call_user_func_array;
|
|
use function count;
|
|
use function defined;
|
|
use function mb_substr;
|
|
use function strlen;
|
|
use function strtolower;
|
|
use function substr;
|
|
use function trim;
|
|
|
|
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
|
{
|
|
header( ( $_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.0' ) . ' 403 Forbidden' );
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* dataLayer
|
|
*/
|
|
class dataLayer extends Controller
|
|
{
|
|
/**
|
|
* @breif the tabs
|
|
*/
|
|
protected static array $tabs = array(
|
|
'main' => 'datalayer_main',
|
|
'pageContext' => 'datalayer_pageContext',
|
|
'properties' => 'datalayer_properties',
|
|
'events' => 'datalayer_events',
|
|
);
|
|
|
|
/**
|
|
* @breif Makes this controller work
|
|
*/
|
|
public static bool $csrfProtected = true;
|
|
|
|
/**
|
|
* @brief used to actually handle events and properties in the browser
|
|
*/
|
|
public static string $handlerClass = '\IPS\core\DataLayer\Handler';
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct()
|
|
{
|
|
if ( Application::appIsEnabled('cloud') and DataLayerClass::i() instanceof \IPS\cloud\DataLayer\Handler )
|
|
{
|
|
static::$handlerClass = 'IPS\cloud\DataLayer\Handler';
|
|
}
|
|
|
|
return parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* Execute
|
|
*
|
|
* @return void
|
|
*/
|
|
public function execute() : void
|
|
{
|
|
Dispatcher::i()->checkAcpPermission( 'dataLayer_manage' );
|
|
Output::i()->title = Member::loggedIn()->language()->addToStack( 'menu__core_settings_dataLayer' );
|
|
|
|
/* Can we use custom handlers? Only show the tab on sites that have full analytics */
|
|
$class = static::$handlerClass;
|
|
if (
|
|
class_exists( $class ) AND
|
|
class_exists( 'IPS\cloud\Application' ) AND
|
|
method_exists( $class, 'handlerForm' ) AND
|
|
method_exists( $class, 'loadWhere' ) AND
|
|
Member::loggedIn()->hasAcpRestriction( 'dataLayer_handlers_view' ) AND
|
|
\IPS\cloud\Application::featureIsEnabled('analytics_full')
|
|
)
|
|
{
|
|
static::$tabs = array(
|
|
'main' => 'datalayer_main',
|
|
'pageContext' => 'datalayer_pageContext',
|
|
'handlers' => 'datalayer_handlers',
|
|
'properties' => 'datalayer_properties',
|
|
'events' => 'datalayer_events',
|
|
);
|
|
}
|
|
|
|
Output::i()->cssFiles = array_merge( Output::i()->cssFiles, Theme::i()->css( 'settings/general.css', 'core', 'admin' ) );
|
|
parent::execute();
|
|
}
|
|
|
|
/**
|
|
* Call Magic Method, seamless way to integrate cloud features if there are any
|
|
*
|
|
* @return string|void
|
|
*/
|
|
public function __call( $name, $arguments )
|
|
{
|
|
if ( method_exists( $this, $name ) )
|
|
{
|
|
return call_user_func_array( $this->$name, $arguments );
|
|
}
|
|
elseif ( method_exists( DataLayerClass::i(), $name ) )
|
|
{
|
|
$method = array( DataLayerClass::i(), $name );
|
|
return call_user_func_array( $method, $arguments );
|
|
}
|
|
|
|
return "You should Upgrade!";
|
|
}
|
|
|
|
/**
|
|
* ...
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function manage() : void
|
|
{
|
|
$tab = Request::i()->tab;
|
|
$tab = empty( static::$tabs[ $tab ] ) ? 'main' : $tab;
|
|
$output = $this->$tab();
|
|
$output = "<div id='dataLayerContent'>$output</div>" ;
|
|
|
|
if ( isset( static::$tabs['handlers'] ) AND Member::loggedIn()->hasAcpRestriction( 'dataLayer_handlers_edit' ) )
|
|
{
|
|
Output::i()->sidebar['actions']['addHandler'] = array(
|
|
'title' => Member::loggedIn()->language()->addToStack( 'datalayer_handler_form' ),
|
|
'icon' => 'plus',
|
|
'link' => Url::internal( 'app=core&module=settings&controller=dataLayer&do=addHandler' ),
|
|
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack( 'datalayer_handler_form' ) )
|
|
);
|
|
}
|
|
|
|
|
|
$addPropertyUrl = Url::internal( 'app=core&module=settings&controller=dataLayer&do=addProperty' );
|
|
Output::i()->sidebar['actions']['addProperty'] = array(
|
|
'title' => Member::loggedIn()->language()->addToStack( 'datalayer_add_property' ),
|
|
'icon' => 'plus',
|
|
'link' => $addPropertyUrl,
|
|
'data' => array( 'ipsDialog' => '', 'ipsDialog-title' => Member::loggedIn()->language()->addToStack( 'datalayer_add_property' ) )
|
|
);
|
|
|
|
if ( Request::i()->isAjax() )
|
|
{
|
|
Output::i()->output = $output;
|
|
}
|
|
else
|
|
{
|
|
if ( isset( $_SESSION['deleted_datalayer_property'] ) )
|
|
{
|
|
Output::i()->inlineMessage = 'Deleted Property';
|
|
unset( $_SESSION['deleted_datalayer_property'] );
|
|
}
|
|
|
|
Output::i()->output = Theme::i()->getTemplate( 'global', 'core', 'admin' )->tabs(
|
|
static::$tabs,
|
|
$tab,
|
|
$output,
|
|
Url::internal( 'app=core&module=settings&controller=dataLayer', 'admin' )
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Gets the general settings form
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function main() : string
|
|
{
|
|
$form = new Form( 'datalayer_general' );
|
|
|
|
$form->addHeader('Settings');
|
|
$form->add( new YesNo( 'core_datalayer_enabled', Settings::i()->core_datalayer_enabled, false ) );
|
|
$form->add( new YesNo( 'core_datalayer_include_pii', Settings::i()->core_datalayer_include_pii, false, array( 'togglesOn' => [ 'datalayer_general_core_datalayer_member_pii_choice' ] ) ) );
|
|
$form->add( new YesNo( 'core_datalayer_member_pii_choice', Settings::i()->core_datalayer_member_pii_choice, false ) );
|
|
|
|
try
|
|
{
|
|
$default = HandlerClass::load( Settings::i()->core_datalayer_replace_with_sso );
|
|
}
|
|
catch ( OutOfRangeException $e )
|
|
{
|
|
$default = 0;
|
|
}
|
|
|
|
$form->add( new Node( 'core_datalayer_replace_with_sso', $default, false, array(
|
|
'class' => '\IPS\Login\Handler',
|
|
'zeroVal' => 'Use internal Member ID (default)',
|
|
'where' => array(
|
|
['login_enabled=?', 1],
|
|
['login_classname LIKE ?', '%Login_Handler%'],
|
|
['login_classname NOT LIKE ?', '%Login_Handler_Standard']
|
|
),
|
|
|
|
) ) );
|
|
|
|
$form->addHeader('enhancements__core_GoogleTagManager');
|
|
$form->addMessage( 'use_gtm_for_installation', 'ipsMessage ipsMessage--info i-margin_3' );
|
|
$form->add( new YesNo( 'core_datalayer_use_gtm', Settings::i()->core_datalayer_use_gtm, false ) );
|
|
$gtmkey = explode( '.', Settings::i()->core_datalayer_gtmkey );
|
|
$form->add( new Text(
|
|
'core_datalayer_gtmkey',
|
|
array_pop( $gtmkey ),
|
|
false,
|
|
array(),
|
|
function( $_value ) {
|
|
/* Check for invalid characters */
|
|
if ( preg_match( '/[^a-zA-Z0-9_]/', $_value ) )
|
|
{
|
|
throw new InvalidArgumentException( 'The variable name can only contain alphanumeric characters and underscores.' );
|
|
}
|
|
elseif ( preg_match( '/[0-9]/', mb_substr( $_value, 0, 1 ) ) )
|
|
{
|
|
throw new InvalidArgumentException( 'The variable name cannot start with a number.' );
|
|
}
|
|
|
|
/* Is a custom handler using this? Note that, in JS, window.variable and variable are nearly always the same */
|
|
$where = [
|
|
[ 'enabled=?', 1 ],
|
|
[ 'use_js=?', 1 ],
|
|
Db::i()->in( 'datalayer_key', [ $_value, "window.$_value" ] ),
|
|
];
|
|
|
|
if (
|
|
class_exists( 'IPS\cloud\DataLayer' ) AND
|
|
DataLayerClass::i() instanceof \IPS\cloud\DataLayer AND
|
|
count( Db::i()->select( 'datalayer_key', \IPS\cloud\DataLayer\Handler::$databaseTable, $where, null, 1 ) )
|
|
)
|
|
{
|
|
throw new InvalidArgumentException( "$_value or window.$_value is in use by a custom handler." );
|
|
}
|
|
},
|
|
'<span class="i-font-family_monospace">window.</span>'
|
|
));
|
|
|
|
if ( $values = $form->values() )
|
|
{
|
|
Session::i()->csrfCheck();
|
|
|
|
if ( isset( $values['core_datalayer_replace_with_sso'] ) )
|
|
{
|
|
$values['core_datalayer_replace_with_sso'] = $values['core_datalayer_replace_with_sso'] ? $values['core_datalayer_replace_with_sso']->_id : 0;
|
|
}
|
|
|
|
/* Prefix the gtmkey with window. */
|
|
if ( isset( $values['core_datalayer_gtmkey'] ) )
|
|
{
|
|
$values['core_datalayer_gtmkey'] = "window.{$values['core_datalayer_gtmkey']}";
|
|
}
|
|
|
|
/* Since GTM is a handler, cachebust the handler for template if it was updated */
|
|
foreach( ['core_datalayer_use_gtm', 'core_datalayer_gtmkey', 'googletag_head_code', 'googletag_noscript_code'] as $input )
|
|
{
|
|
if ( isset( $values[$input] ) )
|
|
{
|
|
$key = Handler::$handlerCacheKey;
|
|
unset( Store::i()->$key );
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Clear the cached configuration since we're changing it */
|
|
DataLayerClass::i()->clearCachedConfiguration([ '_jsConfig', '_eventProperties', '_propertyEvents' ]);
|
|
|
|
Settings::i()->changeValues( $values );
|
|
|
|
Session::i()->log( 'acplog__datalayer_settings_edited' );
|
|
|
|
/* Redirect to avoid csrfKey errors */
|
|
Output::i()->redirect( Url::internal( 'app=core&module=settings&controller=dataLayer&tab=main' ) );
|
|
}
|
|
|
|
return "<br>$form";
|
|
}
|
|
|
|
/**
|
|
* Property settings and configuration
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function pageContext() : string
|
|
{
|
|
/* Get the page level properties */
|
|
$_properties = DataLayerClass::i()->propertiesConfiguration;
|
|
foreach ( $_properties as $k => $property )
|
|
{
|
|
if ( $property['page_level'] AND $property['enabled'] )
|
|
{
|
|
$properties[$k] = array(
|
|
'name' => $property['formatted_name'],
|
|
'type' => $property['type'],
|
|
'pii' => $property['pii'],
|
|
'custom' => $property['custom'] ?? 0,
|
|
'short' => $property['short'],
|
|
);
|
|
}
|
|
}
|
|
|
|
$propertyBlock = new Custom(
|
|
$properties,
|
|
Url::internal( 'app=core&module=settings&controller=dataLayer&tab=pageContext' )
|
|
);
|
|
$propertyBlock->classes[] = 'ipsPadding:none';
|
|
$propertyBlock->exclude = array('custom', 'description');
|
|
$propertyBlock->langPrefix = 'datalayer_';
|
|
|
|
if ( !Request::i()->sortby )
|
|
{
|
|
$propertyBlock->sortBy = 'name';
|
|
}
|
|
|
|
if ( !Request::i()->sortdirection )
|
|
{
|
|
$propertyBlock->sortDirection = 'asc';
|
|
}
|
|
|
|
$propertyBlock->parsers = array(
|
|
'pii' => array( $this, '_yesNo' ),
|
|
'name' => function ( $val, $row ) {
|
|
$url = Url::internal('app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $val);
|
|
$title = $row['short'];
|
|
return "<span class='i-font-family_monospace'><a href='$url' data-ipstooltip title='$title'>$val</a></span>";
|
|
},
|
|
);
|
|
|
|
return Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerContext( $propertyBlock );
|
|
}
|
|
|
|
/**
|
|
* Property settings and configuration
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function properties() : string
|
|
{
|
|
/* Load our properties */
|
|
$properties = DataLayerClass::i()->propertiesConfiguration;
|
|
$property_key = Request::i()->property_key ?: array_keys( $properties )[0];
|
|
|
|
/* Do we know the requested property? */
|
|
if ( !isset( $properties[$property_key] ) )
|
|
{
|
|
Output::i()->redirect( Request::i()->url()->stripQueryString([ 'property_key' ]) );
|
|
}
|
|
$property = $properties[$property_key];
|
|
|
|
/* Sidebar */
|
|
$propertySelector = Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerSelector(
|
|
$properties,
|
|
'properties',
|
|
'property_key',
|
|
'Data Layer Properties',
|
|
$property_key,
|
|
true,
|
|
array( $this, '_truncate' )
|
|
);
|
|
|
|
/* Form */
|
|
$form = new Form( 'datalayer_properties_' . $property_key, 'save', Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $property_key ) );
|
|
$form->class = 'ipsForm--vertical ipsForm--datalayer-properties';
|
|
|
|
$form->add( new YesNo( 'datalayer_property_enabled', $property['enabled'], false ) );
|
|
$form->add( new Text( 'datalayer_property_formatted_name', $property['formatted_name'], false, array(), $this->formattedNameValid('properties', $property_key) ) );
|
|
|
|
if ( $property['custom'] ?? 0 )
|
|
{
|
|
$form->add( new Text( 'datalayer_property_value', $property['value'], true ) );
|
|
$form->add( new YesNo( 'datalayer_property_page_level', $property['page_level'], true ) );
|
|
$form->add( new Text( 'datalayer_property_short', $property['short'], false, array( 'maxLength' => 50 ) ) );
|
|
|
|
$events = DataLayerClass::i()->eventConfiguration;
|
|
$options = array();
|
|
foreach ( $events as $key => $data )
|
|
{
|
|
$options[$key] = $data['formatted_name'];
|
|
}
|
|
|
|
$form->add( new CheckboxSet( 'datalayer_property_event_keys', $property['event_keys'], false, array('options' => $options, 'multiple' => 1) ) );
|
|
}
|
|
|
|
/* Update the values as long as the property_key was specified in the request */
|
|
if ( ( $values = $form->values() ) AND Request::i()->property_key === $property_key )
|
|
{
|
|
/* Get our submitted values */
|
|
$_values = array();
|
|
if ( isset( $values['datalayer_property_enabled'] ) )
|
|
{
|
|
$_values['enabled'] = (bool) $values['datalayer_property_enabled'];
|
|
}
|
|
|
|
if ( isset( $values['datalayer_property_formatted_name'] ) )
|
|
{
|
|
$_values['formatted_name'] = $values['datalayer_property_formatted_name'];
|
|
}
|
|
|
|
if ( $property['custom'] ?? 0 )
|
|
{
|
|
foreach ( $values as $field => $value )
|
|
{
|
|
if ( isset( $_values[$field] ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
$field = str_replace( 'datalayer_property_', '', $field );
|
|
$_values[$field] = $value;
|
|
}
|
|
}
|
|
|
|
/* Save the property */
|
|
if ( !empty( $_values ) )
|
|
{
|
|
try
|
|
{
|
|
DataLayerClass::i()->savePropertyConfiguration( $property_key, $_values );
|
|
}
|
|
catch ( InvalidArgumentException $e ) {}
|
|
|
|
Session::i()->log( 'acplog__data_layer_property_edited', array( $property_key => FALSE ) );
|
|
}
|
|
|
|
Output::i()->redirect( Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $property_key ) );
|
|
}
|
|
|
|
/* Load our events */
|
|
$_events = DataLayerClass::i()->getPropertyEvents( $property_key );
|
|
$events = array();
|
|
foreach( $_events as $key => $event )
|
|
{
|
|
$events[$key] = array(
|
|
'name' => $event['formatted_name'],
|
|
'key' => $key,
|
|
'enabled' => $event['enabled'],
|
|
'short' => $event['short'],
|
|
);
|
|
}
|
|
|
|
/* Property's events table */
|
|
if ( empty( $events ) )
|
|
{
|
|
$eventTable = "";
|
|
}
|
|
else
|
|
{
|
|
$eventTable = new Custom(
|
|
$events,
|
|
Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $property_key )
|
|
);
|
|
|
|
|
|
|
|
if ( !Request::i()->sortby )
|
|
{
|
|
$eventTable->sortBy = 'name';
|
|
}
|
|
if ( !Request::i()->sortdirection )
|
|
{
|
|
$eventTable->sortDirection = 'asc';
|
|
}
|
|
|
|
$eventTable->langPrefix = 'datalayer_';
|
|
$eventTable->exclude = array( 'description', 'key' );
|
|
$eventTable->parsers = array(
|
|
'name' => function ( $val, $row )
|
|
{
|
|
$url = Url::internal('app=core&module=settings&controller=dataLayer&tab=events&event_key=' . $row['key']);
|
|
$title = $row['short'];
|
|
return "<span class='i-font-family_monospace'><a href='$url' data-ipstooltip title='$title'>$val</a></span>";
|
|
},
|
|
'enabled' => array( $this, '_enabledDisabled' ),
|
|
);
|
|
}
|
|
|
|
/* Render Content */
|
|
$content = Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerTitleContent(
|
|
$property,
|
|
$property_key,
|
|
'property',
|
|
$form,
|
|
(string) $eventTable
|
|
);
|
|
|
|
return Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerTab( $propertySelector, $content );
|
|
}
|
|
|
|
/**
|
|
* Property settings and configuration
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function addProperty() : void
|
|
{
|
|
Output::i()->title = 'Add property';
|
|
$form = new Form( 'datalayer_add_property' );
|
|
$form->add( new Text( 'datalayer_property_formatted_name', null, true, array(), $this->formattedNameValid() ) );
|
|
$form->add( new Text( 'datalayer_property_value', null, true ) );
|
|
$form->add( new TextArea( 'datalayer_property_description', null, true ) );
|
|
$form->add( new Text( 'datalayer_property_short', null, false, array( 'maxLength' => 50 ) ) );
|
|
$form->add( new YesNo( 'datalayer_property_page_level', 1, true ) );
|
|
|
|
$events = DataLayerClass::i()->eventConfiguration;
|
|
$options = array();
|
|
foreach ( $events as $key => $data )
|
|
{
|
|
$options[$key] = $data['formatted_name'];
|
|
}
|
|
|
|
$form->add( new CheckboxSet( 'datalayer_property_event_keys', array(), false, array('options' => $options, 'multiple' => 1) ) );
|
|
|
|
if ( $values = $form->values() )
|
|
{
|
|
Session::i()->csrfCheck();
|
|
$_values = array();
|
|
foreach ( $values as $field => $value )
|
|
{
|
|
$field = str_replace( 'datalayer_property_', '', $field );
|
|
$_values[$field] = $value;
|
|
}
|
|
$values = $_values;
|
|
|
|
$values['enabled'] = true;
|
|
$key = $values['formatted_name'];
|
|
|
|
DataLayerClass::i()->savePropertyConfiguration( $key, $values, true );
|
|
|
|
Session::i()->log( 'acplog__data_layer_property_added', array( $key => FALSE ) );
|
|
|
|
Output::i()->redirect( Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $key ) );
|
|
}
|
|
|
|
Output::i()->output = (string) $form;
|
|
}
|
|
|
|
/**
|
|
* Delete a property
|
|
*/
|
|
protected function deleteProperty() : void
|
|
{
|
|
Session::i()->csrfCheck();
|
|
|
|
$key = Request::i()->property_key;
|
|
$propertiesUrl = Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties' );
|
|
if ( empty( $key ) )
|
|
{
|
|
Output::i()->redirect( $propertiesUrl );
|
|
}
|
|
|
|
$properties = DataLayerClass::i()->propertiesConfiguration;
|
|
if ( isset( $properties[$key] ) )
|
|
{
|
|
if ( $properties[$key]['custom'] ?? 0 )
|
|
{
|
|
$setting = json_decode( Settings::i()->core_datalayer_properties, true ) ?: array();
|
|
unset( $setting[$key] );
|
|
Settings::i()->changeValues([ 'core_datalayer_properties' => json_encode( $setting ) ]);
|
|
DataLayerClass::i()->clearCachedConfiguration();
|
|
Session::i()->log( 'acplog__data_layer_property_deleted', array( $key => FALSE ) );
|
|
$_SESSION['deleted_datalayer_property'] = 1;
|
|
}
|
|
else
|
|
{
|
|
$propertiesUrl = $propertiesUrl->setQueryString([ 'property_key' => $key ]);
|
|
}
|
|
}
|
|
Output::i()->redirect( $propertiesUrl );
|
|
}
|
|
|
|
/**
|
|
* Event settings and configuration
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function events() : string
|
|
{
|
|
/* Load our events */
|
|
$events = DataLayerClass::i()->eventConfiguration;
|
|
$event_key = Request::i()->event_key ?: 'content_create';
|
|
|
|
/* Do we know the recognized one? */
|
|
if ( !isset( $events[$event_key] ) )
|
|
{
|
|
Output::i()->redirect( Request::i()->url()->stripQueryString([ 'event_key' ]) );
|
|
}
|
|
$event = $events[$event_key];
|
|
|
|
foreach ( array_keys( $events ) as $_eventKey )
|
|
{
|
|
$events[$_eventKey]['description'] = 'Fires when ' . $events[$_eventKey]['description'];
|
|
}
|
|
|
|
/* Create our sidebar */
|
|
$eventSelector = Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerSelector(
|
|
$events,
|
|
'events',
|
|
'event_key',
|
|
'Data Layer Events',
|
|
$event_key,
|
|
true,
|
|
array( $this, '_truncate' )
|
|
);
|
|
|
|
/* Form */
|
|
$form = new Form( 'datalayer_events_' . $event_key, 'save', Url::internal( 'app=core&module=settings&controller=dataLayer&tab=events&event_key=' . $event_key ) );
|
|
$form->class = 'ipsForm--vertical ipsForm--datalayer-events';
|
|
|
|
$form->add( new YesNo( 'datalayer_event_enabled', $event['enabled'], false ) );
|
|
$form->add( new Text( 'datalayer_event_formatted_name', $event['formatted_name'], false, array(), $this->formattedNameValid( 'events', $event_key ) ) );
|
|
|
|
/* We only want to change values when a real event is specified in the request */
|
|
if ( ( $values = $form->values() ) AND Request::i()->event_key === $event_key )
|
|
{
|
|
/* Pull out the values to use */
|
|
$_values = array();
|
|
if ( isset( $values['datalayer_event_enabled'] ) )
|
|
{
|
|
$_values['enabled'] = (bool) $values['datalayer_event_enabled'];
|
|
}
|
|
|
|
if ( isset( $values['datalayer_event_formatted_name'] ) )
|
|
{
|
|
$_values['formatted_name'] = $values['datalayer_event_formatted_name'];
|
|
}
|
|
|
|
/* Save the event */
|
|
if ( !empty( $_values ) )
|
|
{
|
|
try
|
|
{
|
|
DataLayerClass::i()->saveEventConfiguration( $event_key, $_values );
|
|
}
|
|
catch ( InvalidArgumentException $e ) {}
|
|
}
|
|
|
|
Session::i()->log( 'acplog__data_layer_event_edited', array( $event_key => FALSE ) );
|
|
|
|
Output::i()->redirect( Url::internal( 'app=core&module=settings&controller=dataLayer&tab=events&event_key=' . $event_key ) );
|
|
}
|
|
|
|
|
|
/* Load our properties */
|
|
$_properties = DataLayerClass::i()->getEventProperties( $event_key );
|
|
$properties = array();
|
|
foreach( $_properties as $key => $property )
|
|
{
|
|
$properties[$key] = array(
|
|
'name' => $property['formatted_name'],
|
|
'key' => $key,
|
|
'type' => $property['type'],
|
|
'enabled' => $property['enabled'],
|
|
'pii' => $property['pii'],
|
|
'custom' => $property['custom'] ?? 0,
|
|
'short' => $property['short'],
|
|
);
|
|
}
|
|
|
|
/* Create our neat properties table */
|
|
if ( empty( $properties ) )
|
|
{
|
|
$propertyTable = "";
|
|
}
|
|
else
|
|
{
|
|
$propertyTable = new Custom(
|
|
$properties,
|
|
Url::internal( 'app=core&module=settings&controller=dataLayer&tab=events&event_key=' . $event_key )
|
|
);
|
|
|
|
if ( !Request::i()->sortby )
|
|
{
|
|
$propertyTable->sortBy = 'name';
|
|
}
|
|
|
|
if ( !Request::i()->sortdirection )
|
|
{
|
|
$propertyTable->sortDirection = 'asc';
|
|
}
|
|
|
|
$propertyTable->langPrefix = 'datalayer_';
|
|
$propertyTable->exclude = array( 'custom', 'short', 'key' );
|
|
$propertyTable->parsers = array(
|
|
'enabled' => array( $this, '_enabledDisabled' ),
|
|
'pii' => array( $this, '_yesNo' ),
|
|
'name' => function ( $val, $row )
|
|
{
|
|
$url = Url::internal( 'app=core&module=settings&controller=dataLayer&tab=properties&property_key=' . $row['key'] );
|
|
$title = $row['short'];
|
|
return "<pre><a href='$url' data-ipstooltip title='$title' >$val</a></pre>";
|
|
},
|
|
);
|
|
}
|
|
|
|
/* Render Content */
|
|
$event['description'] = "Fires when {$event['description']}";
|
|
$content = Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerTitleContent(
|
|
$event,
|
|
$event_key,
|
|
'event',
|
|
(string) $form,
|
|
(string) $propertyTable
|
|
);
|
|
|
|
return Theme::i()->getTemplate( 'settings', 'core', 'admin' )->dataLayerTab( $eventSelector, $content );
|
|
}
|
|
|
|
/**
|
|
* Is the value a valid property or event formatted name?
|
|
*
|
|
* @param string $group Either properties or events; the datalayer collection to test the key against
|
|
* @param ?string $current The key of the property/event that currently has this value set as its formatted name
|
|
*
|
|
* @return callable
|
|
*/
|
|
public function formattedNameValid( string $group='properties', ?string $current=null ) : callable
|
|
{
|
|
return function( $value ) use ( $group, $current )
|
|
{
|
|
if ( $group === 'properties' AND strtolower( $value ) === 'event' )
|
|
{
|
|
throw new InvalidArgumentException( "You cannot name a property '$value' because that key is reserved" );
|
|
}
|
|
|
|
if ( preg_match( '/[^a-zA-Z_]/', $value ) )
|
|
{
|
|
throw new InvalidArgumentException( 'The name can only contain letters and underscores.' );
|
|
}
|
|
|
|
$collection = ( $group === 'events' ) ? DataLayerClass::i()->eventConfiguration : DataLayerClass::i()->propertiesConfiguration;
|
|
foreach ( $collection as $key => $data )
|
|
{
|
|
if ( $key === $current )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( isset( $data['formatted_name'] ) AND $data['formatted_name'] === $value )
|
|
{
|
|
throw new InvalidArgumentException( 'This Data Layer name is already in use' );
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Formatter method to make code cleaner
|
|
*
|
|
* @param mixed $val
|
|
* @param array|null $row *
|
|
* @return string
|
|
*/
|
|
public function _enabledDisabled( mixed $val, ?array $row=null ) : string
|
|
{
|
|
return $val ? 'Enabled' : 'Disabled';
|
|
}
|
|
|
|
/**
|
|
* Formatter method to make code cleaner
|
|
*
|
|
* @param mixed $val
|
|
* @param array|null $row
|
|
* @return string
|
|
*/
|
|
public function _yesNo( mixed $val, ?array $row=null ) : string
|
|
{
|
|
return $val ? 'Yes' : 'No';
|
|
}
|
|
|
|
/**
|
|
* Truncates the string and removes tags. Ends in ... if the string is longer than the limit
|
|
*
|
|
* @param string $_string The string to truncate
|
|
* @param int $length The desired length
|
|
*
|
|
* @return string
|
|
*/
|
|
public function _truncate( string $_string, int $length=100 ) : string
|
|
{
|
|
$_string = strip_tags( $_string );
|
|
$string = substr( $_string, 0, $length );
|
|
$changedLength = ( strlen( $_string ) !== strlen( $string ) );
|
|
$string = trim( $string, ". \n\r\t\v\x00" );
|
|
if ( $changedLength )
|
|
{
|
|
$string = trim( substr( $string, 0, $length - 3 ), ". \n\r\t\v\x00" ) . '...';
|
|
}
|
|
return $string;
|
|
}
|
|
|
|
} |