Skip to content
Snippets Groups Projects
Commit c7ead084 authored by Joshua Sedler's avatar Joshua Sedler :cartwheel_tone2:
Browse files

Issue #3501722 by grevil: Implement alter hook and temporarily handle anonymous user

parent 829a00b8
No related branches found
Tags 8.x-1.11
1 merge request!13Issue #3501722: Implement alter hook and temporarily handle anonymous user
Pipeline #407422 failed
Showing
with 208 additions and 121 deletions
host: "https://eu.i.posthog.com"
api_key: ""
distinct_id: 'none'
distinct_id: 'no_identification'
user_properties: []
......@@ -9,13 +9,14 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\commerce_cart\Event\CartEntityAddEvent;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelFactory;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
/**
* The order subscriber.
*/
class CartSubscriber implements EventSubscriberInterface {
public const PH_CART_ADD_EVENT = 'Product Added';
public const POSTHOG_CART_ADD_EVENT = 'Product Added';
/**
* Constructs a new order subscriber object.
......@@ -24,7 +25,7 @@ class CartSubscriber implements EventSubscriberInterface {
* The PostHog PHP SDK.
* @param \Drupal\posthog\UserAttributesProvider $userAttributesProvider
* The user attributes provider.
* @param \Drupal\Core\Logger\LoggerChannelFactory $loggerChannelFactory
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $loggerChannelFactory
* The logger channel factory.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The config factory.
......@@ -32,7 +33,7 @@ class CartSubscriber implements EventSubscriberInterface {
public function __construct(
protected readonly SdkInterface $posthogPhpSdk,
protected readonly UserAttributesProvider $userAttributesProvider,
protected readonly LoggerChannelFactory $loggerChannelFactory,
protected readonly LoggerChannelFactoryInterface $loggerChannelFactory,
protected readonly ConfigFactoryInterface $configFactory,
) {
}
......@@ -61,7 +62,10 @@ class CartSubscriber implements EventSubscriberInterface {
$cart = $event->getCart();
$purchasableEntity = $event->getEntity();
$distinctUserId = $this->userAttributesProvider->getUserDistinctId($orderItem->getOrder()?->getCustomer());
$order = $orderItem->getOrder();
$customer = $order->getCustomer();
$distinctUserId = $this->userAttributesProvider->getUserDistinctId($customer);
$properties = [
'cart_id' => $cart->id(),
'product_id' => $purchasableEntity->id(),
......@@ -70,13 +74,16 @@ class CartSubscriber implements EventSubscriberInterface {
'price' => $purchasableEntity?->getPrice()?->getNumber(),
'quantity' => $quantity,
];
$this->posthogPhpSdk->captureUserEvent($distinctUserId, self::PH_CART_ADD_EVENT, $properties);
$this->posthogPhpSdk->captureUserEvent($distinctUserId, self::POSTHOG_CART_ADD_EVENT, $properties, [
'commerce_order' => $order,
'user' => $customer,
]);
}
catch (\Exception $e) {
if ($this->configFactory->get('posthog_php.settings')->get('enable_logging')) {
$logger = $this->loggerChannelFactory->get('posthog_commerce');
$logger->notice('Something went wrong, while capturing the posthog @event. Error message: @error_message', [
'@event' => self::PH_CART_ADD_EVENT,
'@event' => self::POSTHOG_CART_ADD_EVENT,
'@error_message' => $e->getMessage(),
]);
}
......
......@@ -5,7 +5,7 @@ namespace Drupal\posthog_commerce\EventSubscriber;
use Drupal\commerce_order\Event\OrderEvent;
use Drupal\commerce_order\Event\OrderEvents;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelFactory;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\posthog\UserAttributesProvider;
use Drupal\posthog_php\SdkInterface;
use Drupal\state_machine\Event\WorkflowTransitionEvent;
......@@ -16,7 +16,7 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface;
*/
class OrderSubscriber implements EventSubscriberInterface {
public const PH_ORDER_COMPLETED_EVENT = 'Order Completed';
public const POSTHOG_ORDER_COMPLETED_EVENT = 'Order Completed';
/**
* Constructs a new order subscriber object.
......@@ -25,7 +25,7 @@ class OrderSubscriber implements EventSubscriberInterface {
* The PostHog PHP SDK.
* @param \Drupal\posthog\UserAttributesProvider $userAttributesProvider
* The user attributes provider.
* @param \Drupal\Core\Logger\LoggerChannelFactory $loggerChannelFactory
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $loggerChannelFactory
* The logger channel factory.
* @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
* The config factory.
......@@ -33,7 +33,7 @@ class OrderSubscriber implements EventSubscriberInterface {
public function __construct(
protected readonly SdkInterface $posthogPhpSdk,
protected readonly UserAttributesProvider $userAttributesProvider,
protected readonly LoggerChannelFactory $loggerChannelFactory,
protected readonly LoggerChannelFactoryInterface $loggerChannelFactory,
protected readonly ConfigFactoryInterface $configFactory,
) {
}
......@@ -72,24 +72,28 @@ class OrderSubscriber implements EventSubscriberInterface {
/** @var \Drupal\commerce_order\Entity\OrderInterface $order */
$order = $event->getEntity();
if (!empty($order)) {
$distinctUserId = $this->userAttributesProvider->getUserDistinctId($order->getCustomer());
$customer = $order->getCustomer();
$distinctUserId = $this->userAttributesProvider->getUserDistinctId($customer);
$properties = [
'checkout_id' => $order->getOrderNumber(),
'order_id' => $order->id(),
'checkout_id' => $order->id(),
'order_id' => $order->getOrderNumber(),
'total' => $order->getTotalPrice()->getNumber(),
'currency' => $order->getTotalPrice()->getCurrencyCode(),
'coupon' => !empty($order->collectAdjustments(['promotion'])) ? $order->collectAdjustments(['promotion'])[0]->getSourceId() : NULL,
// @todo Get the products from the order:
'products' => [],
// 'products' => [],
];
$this->posthogPhpSdk->captureUserEvent($distinctUserId, self::PH_ORDER_COMPLETED_EVENT, $properties);
$this->posthogPhpSdk->captureUserEvent($distinctUserId, self::POSTHOG_ORDER_COMPLETED_EVENT, $properties, [
'commerce_order' => $order,
'user' => $customer,
]);
}
}
catch (\Exception $e) {
if ($this->configFactory->get('posthog_php.settings')->get('enable_logging')) {
$logger = $this->loggerChannelFactory->get('posthog_commerce');
$logger->notice('Something went wrong, while capturing the posthog @event. Error message: @error_message', [
'@event' => self::PH_ORDER_COMPLETED_EVENT,
'@event' => self::POSTHOG_ORDER_COMPLETED_EVENT,
'@error_message' => $e->getMessage(),
]);
}
......
......@@ -41,19 +41,22 @@
// Expose posthog to the global scope:
Drupal.posthog = window.posthog;
// Identify the user if the feature is enabled:
if (posthogJsSettings.enable_identifying_users) {
// If the user is anonymous, he could have logged out. Reset any
// potential identify process:
if (posthogJsSettings.isAnonymous) {
Drupal.posthog.reset();
// Identify the user if the feature is enabled and the distinct id is not
// null:
if (
posthogJsSettings.enable_identifying_users &&
posthogJsSettings.distinct_id
) {
// If he isn't anonymous, identify the user:
} else {
Drupal.posthog.identify(
posthogJsSettings.distinct_id,
posthogJsSettings.user_properties,
);
}
Drupal.posthog.identify(
posthogJsSettings.distinct_id,
posthogJsSettings.user_properties,
);
// If the user is anonymous (logged out), no user identification is set or
// enable identifying users is disabled, reset any potential
// identification:
} else {
Drupal.posthog.reset();
}
},
};
......
......@@ -42,7 +42,6 @@ function posthog_js_page_attachments(array &$attachments) {
'init_config_json' => $initConfigJson,
'cdn' => $posthogJsSettings->get('cdn'),
'enable_identifying_users' => $posthogJsSettings->get('enable_identifying_users'),
'isAnonymous' => \Drupal::currentUser()->isAnonymous(),
'distinct_id' => $posthogPropertyProvider->getCurrentUserDistinctId(),
'user_properties' => $posthogPropertyProvider->getCurrentUserProperties(),
];
......
......@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Drupal\posthog_js\Form;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Condition\ConditionInterface;
use Drupal\Core\Condition\ConditionManager;
use Drupal\Core\Condition\ConditionPluginCollection;
......@@ -12,7 +13,6 @@ use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -27,10 +27,17 @@ final class SettingsForm extends ConfigFormBase {
* The factory for configuration objects.
* @param \Drupal\Component\Plugin\PluginManagerInterface $conditionManager
* The condition manager.
* @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
* The cache backend.
* @param \Drupal\Core\Config\TypedConfigManagerInterface $typedConfigManager
* The typed config manager.
*/
public function __construct(ConfigFactoryInterface $config_factory, protected ConditionManager $conditionManager, ?TypedConfigManagerInterface $typedConfigManager = NULL) {
public function __construct(
ConfigFactoryInterface $config_factory,
protected ConditionManager $conditionManager,
protected CacheBackendInterface $cacheBackend,
?TypedConfigManagerInterface $typedConfigManager = NULL,
) {
parent::__construct($config_factory, $typedConfigManager);
}
......@@ -41,6 +48,7 @@ final class SettingsForm extends ConfigFormBase {
return new self(
$container->get('config.factory'),
$container->get('plugin.manager.condition'),
$container->get('cache.discovery'),
$container->get('config.typed'),
);
}
......@@ -89,7 +97,7 @@ final class SettingsForm extends ConfigFormBase {
$form['init_config_json'] = [
'#type' => 'textarea',
'#title' => $this->t('Initialization configuration (JSON)'),
'#description' => $this->t('Optional additional Posthog initialization JSON code. Checkout the <a href=":link1" target="_blank">help page</a> for further help on this. For a full list of all available options, see <a href=":link2" target="_blank">here</a>. Leave empty to use the defaults.', [
'#description' => $this->t('Optional additional Posthog initialization JSON code. See the <a href=":link1" target="_blank">help page</a> for further help on this. For a full list of all available options, see <a href=":link2" target="_blank">here</a>. Leave empty to use the defaults.', [
':link1' => '/admin/help/posthog',
':link2' => 'https://posthog.com/docs/configure/initialization-configuration',
]) . $accessJsSettingsWarning,
......@@ -107,12 +115,12 @@ final class SettingsForm extends ConfigFormBase {
'#type' => 'checkbox',
'#title' => $this->t('Enable identifying users'),
'#description' => $this->t('Enables identifying users client-side.
This means, that all <strong>client-side</strong> events will become identified events.
Without this option enabled, client-side events are anonymous by default. Checkout <a href=:link1>Anonymous vs identified events</a> and
<a href=:link2>Events</a> for more information.<br><strong>NOTE</strong>, that identified events can cost up to <strong>x4</strong> more than anonymous events!', [
':link1' => 'https://posthog.com/docs/data/anonymous-vs-identified-events',
':link2' => 'https://posthog.com/events',
]),
This means, that all <strong>client-side</strong> events will become identified events.
See <a href=:link1>Anonymous vs identified events</a> and
<a href=:link2>Events</a> for more information. Make sure "Enhanced User identification" is setup as required.<br><strong>NOTE</strong>, that identified events can cost up to <strong>x4</strong> more than anonymous events!', [
':link1' => 'https://posthog.com/docs/data/anonymous-vs-identified-events',
':link2' => 'https://posthog.com/events',
]),
'#default_value' => $config->get('enable_identifying_users'),
'#states' => [
'invisible' => [
......@@ -276,6 +284,8 @@ final class SettingsForm extends ConfigFormBase {
->set('enable_identifying_users', $form_state->getValue('enable_identifying_users'))
->set('conditions', $this->getConditionsConfigurationValue($form, $form_state))
->save();
// Clear the discovery cache to make sure the new settings are applied:
$this->cacheBackend->invalidateAll();
parent::submitForm($form, $form_state);
}
......
enable_identifying_users: false
identify_users_on_login: false
process_person_profile: false
enable_logging: false
......@@ -3,9 +3,12 @@ posthog_php.settings:
type: config_object
label: 'Posthog PHP settings'
mapping:
enable_identifying_users:
identify_users_on_login:
type: bool
label: 'Enable identifying users'
process_person_profile:
type: bool
label: 'Process person profile'
enable_logging:
type: bool
label: 'Enable logging'
<?php
/**
* @file
* Example hook implementations for the posthog_php module.
*/
/**
* @addtogroup hooks
* @{
*/
/**
* Alters the posthog php capture "message" array.
*
* @param array $message
* The message array to be sent to PostHogs "capture" call.
* Structure: [
* 'distinctId' => 'distinct_id',
* 'event' => 'event_name',
* 'properties' => [
* 'property_key' => 'property_value',
* ],
* ].
* @param mixed $context
* Context variables related to where the capture event was emitted.
* For example, the user (user) of the user who triggered the event.
* Commerce events also have the order (commerce_order), as a context
* variable.
*
* @see https://posthog.com/docs/libraries/php
* @see https://posthog.com/docs/product-analytics/capture-events?tab=PHP
*/
function hook_posthog_capture_alter(array &$message, array $context = []) {
// Example: Conditionally add a custom property to the message array:
if ($message['event'] === 'user_login') {
$message['properties']['login_time'] = time();
if (isset($context['user'])) {
$message['properties']['uid'] = $context['user']->id();
}
}
}
......@@ -12,16 +12,22 @@ use Drupal\user\UserInterface;
*/
function posthog_php_user_login(UserInterface $user) {
// Only identify the logged in user, if the setting is enabled:
if (!\Drupal::config('posthog_php.settings')->get('enable_identifying_users')) {
if (!\Drupal::config('posthog_php.settings')->get('identify_users_on_login')) {
return;
}
/** @var \Drupal\posthog_php\SdkInterface $posthogSdk */
$posthogSdk = \Drupal::service('posthog_php.sdk');
/** @var \Drupal\posthog\UserAttributesProvider $userAttributesProvider */
$userAttributesProvider = \Drupal::service('posthog.user_attributes_provider');
// If the user doesn't have a distinct id, we can't identify them. Return
// early:
$userDistinctId = $userAttributesProvider->getUserDistinctId($user);
if ($userDistinctId === NULL) {
return;
}
/** @var \Drupal\posthog_php\SdkInterface $posthogSdk */
$posthogSdk = \Drupal::service('posthog_php.sdk');
// Identify the user in posthog:
$posthogSdk->identify(
$userAttributesProvider->getUserDistinctId($user),
$userDistinctId,
$userAttributesProvider->getUserProperties($user),
);
}
services:
posthog_php.sdk:
class: Drupal\posthog_php\Sdk
arguments: ['@config.factory', '@logger.factory']
arguments: ['@config.factory', '@logger.factory', '@module_handler']
......@@ -32,17 +32,22 @@ final class SettingsForm extends ConfigFormBase {
public function buildForm(array $form, FormStateInterface $form_state): array {
$config = $this->config('posthog_php.settings');
$form['enable_identifying_users'] = [
$form['identify_users_on_login'] = [
'#type' => 'checkbox',
'#title' => $this->t('Enable identifying users'),
'#description' => $this->t('Enables identifying users server-side.
This means, that users will get identified on login and all <strong>server-side</strong> events will become identified events.
Without this option enabled, server-side events are anonymous by default. Checkout <a href=:link1>Anonymous vs identified events</a> and
<a href=:link2>Events</a> for more information.<br><strong>NOTE</strong>, that identified events can cost up to <strong>x4</strong> more than anonymous events!', [
':link1' => 'https://posthog.com/docs/data/anonymous-vs-identified-events',
':link2' => 'https://posthog.com/events',
]),
'#default_value' => $config->get('enable_identifying_users'),
'#title' => $this->t('Identify users on login'),
'#description' => $this->t('Enables identifying users on login, usind the users distinct id + the enabled user properties. Note, that if "Distinct ID" is set to "None", this setting will not have any effects.'),
'#default_value' => $config->get('identify_users_on_login'),
];
$form['process_person_profiles'] = [
'#type' => 'checkbox',
'#title' => $this->t('Process person profiles'),
'#description' => $this->t('Whether to process person profiles
Without this option enabled, posthog will NOT create person profiles and server-side events are anonymous by default. See <a href=:link1>Anonymous vs identified events</a> and
<a href=:link2>Events</a> for more information. Anonymous users always send anonymous events.<br><strong>NOTE</strong>, that identified events can cost up to <strong>x4</strong> more than anonymous events!', [
':link1' => 'https://posthog.com/docs/data/anonymous-vs-identified-events',
':link2' => 'https://posthog.com/events',
]),
'#default_value' => $config->get('process_person_profiles'),
];
$form['enable_logging'] = [
'#type' => 'checkbox',
......@@ -59,7 +64,8 @@ final class SettingsForm extends ConfigFormBase {
*/
public function submitForm(array &$form, FormStateInterface $form_state): void {
$this->config('posthog_php.settings')
->set('enable_identifying_users', $form_state->getValue('enable_identifying_users'))
->set('identify_users_on_login', $form_state->getValue('identify_users_on_login'))
->set('process_person_profiles', $form_state->getValue('process_person_profiles'))
->set('enable_logging', $form_state->getValue('enable_logging'))
->save();
parent::submitForm($form, $form_state);
......
......@@ -5,9 +5,8 @@ declare(strict_types=1);
namespace Drupal\posthog_php;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use PostHog\PostHog;
/**
......@@ -15,30 +14,15 @@ use PostHog\PostHog;
*/
class Sdk implements SdkInterface {
/**
* The logger channel.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected LoggerChannelInterface $logger;
/**
* The posthog_php configuration.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected ImmutableConfig $config;
/**
* Constructs a Sdk object.
*/
public function __construct(
readonly ConfigFactoryInterface $configFactory,
readonly LoggerChannelFactoryInterface $loggerChannelFactory,
readonly ModuleHandlerInterface $moduleHandler,
protected $initalized = FALSE,
) {
$this->logger = $loggerChannelFactory->get('posthog_php');
$this->config = $this->configFactory->get('posthog.settings');
$this->init();
}
......@@ -56,8 +40,9 @@ class Sdk implements SdkInterface {
if ($this->initalized) {
return;
}
$apiKey = $this->config->get('api_key');
$host = $this->config->get('host');
$config = $this->configFactory->get('posthog.settings');
$apiKey = $config->get('api_key');
$host = $config->get('host');
PostHog::init($apiKey,
['host' => $host]
);
......@@ -80,8 +65,8 @@ class Sdk implements SdkInterface {
$success = PostHog::identify($message);
if (!$success && (bool) $this->config->get('enable_logging')) {
$this->logger->error('Failed to identify user with distinct id: %did.', [
if (!$success && (bool) $this->configFactory->get('posthog.settings')->get('enable_logging')) {
$this->loggerChannelFactory->get('posthog_php')->error('Failed to identify user with distinct id: %did.', [
'%did' => $distinctId,
]);
}
......@@ -104,8 +89,8 @@ class Sdk implements SdkInterface {
}
$success = PostHog::groupIdentify($message);
if (!$success && (bool) $this->config->get('enable_logging')) {
$this->logger->error('Failed to group identify group with group key: %gKey.', [
if (!$success && (bool) $this->configFactory->get('posthog.settings')->get('enable_logging')) {
$this->loggerChannelFactory->get('posthog_php')->error('Failed to group identify group with group key: %gKey.', [
'%gKey' => $groupKey,
]);
}
......@@ -165,8 +150,8 @@ class Sdk implements SdkInterface {
];
$success = PostHog::alias($message);
if (!$success && (bool) $this->config->get('enable_logging')) {
$this->logger->error('Failed to alias user with distinct id: %did.', [
if (!$success && (bool) $this->configFactory->get('posthog.settings')->get('enable_logging')) {
$this->loggerChannelFactory->get('posthog_php')->error('Failed to alias user with distinct id: %did.', [
'%did' => $distinctId,
]);
}
......@@ -207,23 +192,29 @@ class Sdk implements SdkInterface {
/**
* {@inheritDoc}
*/
public function capture(array $message): bool {
public function capture(array $message, array $context = []): bool {
if (!$this->initalized) {
$this->init();
}
$config = $this->configFactory->get('posthog_php.settings');
// If the distinct id is NULL, we want to generate our own id:
if ($message['distinctId'] === NULL) {
$message['distinctId'] = uniqid('anonymous-');
}
// If identifying users is disabled, pass the "$process_person_profile"
// property. This will cause the event to be an anonymous event and
// therefore will cost less:
if (!$this->config->get('enable_identifying_users')) {
// IF the distinct id is NULL, or the process_person_profile is disabled,
// we won't process person profiles and therefore handle this as an
// anonymous event:
if (!$config->get('process_person_profile') || $message['distinctId'] === NULL) {
$message['properties']['$process_person_profile'] = FALSE;
}
// @todo add $context param and alter hook.
$this->moduleHandler->alter('posthog_capture', $message, $context);
$success = PostHog::capture($message);
if (!$success && (bool) $this->config->get('enable_logging')) {
$this->logger->error('Failed to capture user event for user with distinct id: %did.', [
if (!$success && (bool) $config->get('enable_logging')) {
$this->loggerChannelFactory->get('posthog_php')->error('Failed to capture user event for user with distinct id: %did.', [
'%did' => $message['distinctId'],
]);
}
......@@ -234,7 +225,7 @@ class Sdk implements SdkInterface {
/**
* {@inheritDoc}
*/
public function captureUserEvent(string $distinctId, string $event, array $properties = []): bool {
public function captureUserEvent(?string $distinctId, string $event, array $properties = [], array $context = []): bool {
$capture = [
'distinctId' => $distinctId,
'event' => $event,
......@@ -244,14 +235,14 @@ class Sdk implements SdkInterface {
$capture['properties'] = $properties;
}
return $this->capture($capture);
return $this->capture($capture, $context);
}
/**
* {@inheritDoc}
*/
public function captureUserPageView(string $distinctId, string $currentUrl): bool {
return $this->captureUserEvent($distinctId, SdkInterface::CAPTURE_EVENT_PAGEVIEW, ['$current_url' => $currentUrl]);
public function captureUserPageView(?string $distinctId, string $currentUrl, array $context = []): bool {
return $this->captureUserEvent($distinctId, SdkInterface::CAPTURE_EVENT_PAGEVIEW, ['$current_url' => $currentUrl], $context);
}
}
......@@ -54,7 +54,7 @@ interface SdkInterface {
*
* @see https://posthog.com/docs/libraries/php#capturing-events
*/
public function capture(array $message): bool;
public function capture(array $message, array $context = []): bool;
/**
* Helper to capture an event on top of ::capture().
......@@ -79,7 +79,7 @@ interface SdkInterface {
*
* @see https://posthog.com/docs/libraries/php#capturing-events
*/
public function captureUserEvent(string $distinctId, string $event, array $properties = []): bool;
public function captureUserEvent(string $distinctId, string $event, array $properties = [], array $context = []): bool;
/**
* Helper to capture page view events on top of ::capture().
......@@ -92,7 +92,7 @@ interface SdkInterface {
* @return bool
* Whether the capture call succeeded
*/
public function captureUserPageView(string $distinctId, string $currentUrl): bool;
public function captureUserPageView(string $distinctId, string $currentUrl, array $context = []): bool;
/**
* Tags properties about the user.
......
......@@ -24,13 +24,17 @@ function posthog_php_events_user_login(AccountInterface $user) {
$trackDrupalUserLogin = \Drupal::config('posthog_php_events.settings')->get('track_drupal_user_login');
if ($trackDrupalUserLogin) {
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User login');
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User login', [
'user' => $user,
]);
}
$trackDrupalUserFirstTimeLogin = \Drupal::config('posthog_php_events.settings')->get('track_drupal_user_first_time_login');
if ($trackDrupalUserFirstTimeLogin) {
if (!$user->getLastAccessedTime()) {
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User first time login');
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User first time login', [
'user' => $user,
]);
}
}
}
......@@ -45,7 +49,9 @@ function posthog_php_events_user_logout(AccountInterface $user) {
$posthogSdk = \Drupal::service('posthog_php.sdk');
/** @var \Drupal\posthog\UserAttributesProvider $userAttributesProvider */
$userAttributesProvider = \Drupal::service('posthog.user_attributes_provider');
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User logout');
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User logout', [
'user' => $user,
]);
}
}
......@@ -61,7 +67,9 @@ function posthog_php_events_user_insert(AccountInterface $user) {
$trackRegistration = $config->get('track_drupal_user_registration');
if ($trackRegistration && (\Drupal::currentUser()->isAnonymous() || \Drupal::currentUser()->id() == $user->id())) {
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User registration');
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User registration', [
'user' => $user,
]);
}
}
......@@ -77,14 +85,18 @@ function posthog_php_events_user_update(AccountInterface $user) {
$trackDrupalUserBlock = \Drupal::config('posthog_php_events.settings')->get('track_drupal_user_block');
if ($trackDrupalUserBlock) {
if ($user->original->status->value != $user->status->value && (bool) $user->status->value == FALSE) {
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User blocked');
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User blocked', [
'user' => $user,
]);
}
}
$trackDrupalUserUnblock = \Drupal::config('posthog_php_events.settings')->get('track_drupal_user_unblock');
if ($trackDrupalUserUnblock) {
if ($user->original->status->value != $user->status->value && (bool) $user->status->value == TRUE) {
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User unblocked');
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User unblocked', [
'user' => $user,
]);
}
}
}
......@@ -99,6 +111,8 @@ function posthog_php_events_user_delete(AccountInterface $user) {
$posthogSdk = \Drupal::service('posthog_php.sdk');
/** @var \Drupal\posthog\UserAttributesProvider $userAttributesProvider */
$userAttributesProvider = \Drupal::service('posthog.user_attributes_provider');
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User deleted');
$posthogSdk->captureUserEvent($userAttributesProvider->getUserDistinctId($user), 'User deleted', [
'user' => $user,
]);
}
}
......@@ -6,14 +6,12 @@ namespace Drupal\posthog\Form;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
/**
* Configure Posthog settings for this site.
*/
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Symfony\Component\Routing\RouterInterface;
/**
* The posthog settings form.
......@@ -25,12 +23,9 @@ final class SettingsForm extends ConfigFormBase {
*
* @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
* The cache backend.
* @param \Drupal\Core\Routing\RouteProviderInterface $routeProvider
* The route provider.
*/
public function __construct(
protected CacheBackendInterface $cacheBackend,
protected RouterInterface $router,
) {}
/**
......@@ -39,7 +34,6 @@ final class SettingsForm extends ConfigFormBase {
public static function create(ContainerInterface $container) {
return new static(
$container->get('cache.discovery'),
$container->get('router'),
);
}
......@@ -85,28 +79,29 @@ final class SettingsForm extends ConfigFormBase {
$form['user_identifying'] = [
'#type' => 'details',
'#title' => $this->t('User identifying'),
'#description' => $this->t('<strong>NOTE</strong>, that if you are using the PosthogJS / PosthogPHP module you additionally need to activate the
"Enable identifying users" option inside the <a href=":link1">Posthog JS settings</a> and / or the <a href=":link2">Posthog PHP settings</a>, to use the defined user identifying attributes.', [
':link1' => $this->router->getRouteCollection()->get('posthog_js.settings') ? Url::fromRoute('posthog_js.settings')->toString() : '',
':link2' => $this->router->getRouteCollection()->get('posthog_php.settings') ? Url::fromRoute('posthog_php.settings')->toString() : '',
]),
'#open' => TRUE,
];
$form['user_identifying']['distinct_id'] = [
'#type' => 'radios',
'#title' => $this->t('Enhanced User identification'),
'#options' => [
'none' => $this->t('None'),
'uid' => $this->t('Drupal user ID'),
'mail' => $this->t('Drupal user email'),
'name' => $this->t('Drupal user name'),
'no_identification' => $this->t('No identification'),
'uid' => $this->t('Identify by Drupal user ID'),
'mail' => $this->t('Identify by Drupal user email'),
'name' => $this->t('Identify by Drupal user name'),
],
'#default_value' => $config->get('distinct_id'),
'#description' => $this->t('Select, which unique identifier should be used as the posthog "distinct_id" to identify user events.'),
'#description' => $this->t('Select, which unique identifier should be used as the posthog "distinct_id" to identify user events.<br>"No identification" will indirectly make all emmited events "anonymous" and use an auto generated ID ("anonymous-xxxxx") as a user\'s "distinctId". You can adjust the identification / profile processing setting individually in the respective Posthog PHP / Posthog JS submodule settings. Note, that there never is user identification for anonymous users.'),
];
$form['user_identifying']['user_properties'] = [
'#type' => 'checkboxes',
'#title' => $this->t('User properties'),
'#default_value' => $config->get('user_properties'),
'#states' => [
'invisible' => [
':input[name="distinct_id"]' => ['value' => 'no_identification'],
],
],
'#options' => [
'uid' => $this->t('Drupal user ID'),
'email' => $this->t('Drupal user email'),
......
......@@ -34,6 +34,11 @@ final class UserAttributesProvider {
* Returns a user's distinct id based on the configuration.
*/
public function getUserDistinctId(AccountInterface $user): ?string {
// Anonymous users, can not be identified by their user id, email or name,
// so we simply return NULL:
if ($user->isAnonymous()) {
return NULL;
}
switch ($this->posthogSettings->get('distinct_id')) {
case 'uid':
return (string) $user->id();
......@@ -45,6 +50,7 @@ final class UserAttributesProvider {
return $user->getAccountName();
default:
case 'no_identification':
return NULL;
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment