Skip to content

Commit

Permalink
Merge pull request #339 from Automattic/master
Browse files Browse the repository at this point in the history
Alpha release Dec 08
  • Loading branch information
adekbadek authored Dec 8, 2020
2 parents 45a713a + 14120a8 commit 7e71118
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 109 deletions.
14 changes: 12 additions & 2 deletions api/classes/class-lightweight-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public function get_transient( $name ) {
return null;
} elseif ( false === $value ) {
$this->debug['read_query_count'] += 1;
$value = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $name ) ); // phpcs:ignore
$value = $this->get_option( $name );
if ( $value ) {
wp_cache_set( $name, $value, 'newspack-popups' );
} else {
Expand All @@ -153,7 +153,7 @@ public function get_transient( $name ) {
/**
* Upsert transient.
*
* @param string $name THe transient's name.
* @param string $name The transient's name.
* @param string $value THe transient's value.
*/
public function set_transient( $name, $value ) {
Expand Down Expand Up @@ -309,4 +309,14 @@ public function get_post_payload() {
}
return $payload;
}

/**
* Get option.
*
* @param string $name Option name.
*/
public function get_option( $name ) {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", $name ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
}
}
99 changes: 99 additions & 0 deletions api/segmentation/class-segmentation-custom-ga-config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php
/**
* Newspack Campaigns custom GA config.
*
* @package Newspack
*/

/**
* Extend the base Lightweight_API class.
*/
require_once dirname( __FILE__ ) . '/../classes/class-lightweight-api.php';
require_once dirname( __FILE__ ) . '/../segmentation/class-segmentation.php';
require_once dirname( __FILE__ ) . '/../campaigns/class-campaign-data-utils.php';

/**
* GET endpoint to create custom GA Config for AMP Analytics.
*/
class Segmentation_Custom_GA_Config extends Lightweight_API {
/**
* Constructor.
*
* @codeCoverageIgnore
*/
public function __construct() {
parent::__construct();
$this->response = $this->get_custom_analytics_configuration( $_REQUEST ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$this->respond();
}

/**
* Get custom Analytics config, with segmentation-related custom dimensions assigned.
* The pageviews will be reported using this configuration, so it's important
* to include the custom dimensions set up by the Newspack Plugin, too.
*
* @param Request $request Request object.
*/
public function get_custom_analytics_configuration( $request ) {
$client_id = $request['client_id'];
$ga_settings = maybe_unserialize( $this->get_option( 'googlesitekit_analytics_settings' ) );
if ( ! $client_id || ! $ga_settings || ! isset( $ga_settings['propertyID'] ) ) {
return [];
}

$custom_dimensions = json_decode( $request['custom_dimensions'] );

// Tracking ID from Site Kit.
$gtag_id = $ga_settings['propertyID'];

$custom_dimensions_values = [];

$api = new Lightweight_API();
$client_data = $api->get_client_data( $client_id );

foreach ( $custom_dimensions as $custom_dimension ) {
// Strip the `ga:` prefix from gaID.
$dimension_id = substr( $custom_dimension->gaID, 3 ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
switch ( $custom_dimension->role ) {
case Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_READER_FREQUENCY:
$read_count = count( $client_data['posts_read'] );
// Tiers mimick NCI's – https://news-consumer-insights.appspot.com.
$read_count_tier = 'casual';
if ( $read_count > 1 && $read_count <= 14 ) {
$read_count_tier = 'loyal';
} elseif ( $read_count > 14 ) {
$read_count_tier = 'brand_lover';
}
$custom_dimensions_values[ $dimension_id ] = $read_count_tier;
break;
case Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_IS_SUBSCRIBER:
$custom_dimensions_values[ $dimension_id ] = Campaign_Data_Utils::is_subscriber( $client_data, wp_get_referer() );
break;
case Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_IS_DONOR:
$custom_dimensions_values[ $dimension_id ] = Campaign_Data_Utils::is_donor( $client_data );
break;
}
}

$custom_dimensions_existing_values = (array) json_decode( $request['custom_dimensions_existing_values'] );

// This is an AMP Analytics-compliant configuration, which on non-AMP pages will be
// processed by this plugin's amp-analytics polyfill (src/view).
return [
'vars' => [
'gtag_id' => $gtag_id,
'config' => [
$gtag_id => array_merge(
[
'groups' => 'default',
],
$custom_dimensions_values,
$custom_dimensions_existing_values
),
],
],
'optoutElementId' => '__gaOptOutExtension',
];
}
}
new Segmentation_Custom_GA_Config();
2 changes: 1 addition & 1 deletion api/segmentation/class-segmentation-report.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public static function log_single_visit( $payload ) {
[
'post_read',
$payload['clientId'],
isset( $payload['date'] ) ? $payload['date'] : gmdate( 'Y-m-d', time() ),
isset( $payload['date'] ) ? $payload['date'] : gmdate( 'Y-m-d H:i:s', time() ),
$payload['post_id'],
$payload['categories'],
]
Expand Down
7 changes: 7 additions & 0 deletions api/segmentation/class-segmentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
* Manages Segmentation.
*/
class Segmentation {
/**
* Names of custom dimensions options.
*/
const CUSTOM_DIMENSIONS_OPTION_NAME_READER_FREQUENCY = 'newspack_popups_cd_reader_frequency';
const CUSTOM_DIMENSIONS_OPTION_NAME_IS_SUBSCRIBER = 'newspack_popups_cd_is_subscriber';
const CUSTOM_DIMENSIONS_OPTION_NAME_IS_DONOR = 'newspack_popups_cd_is_donor';

/**
* Get log file path.
*/
Expand Down
3 changes: 3 additions & 0 deletions api/segmentation/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
require_once '../setup.php';

switch ( $_SERVER['REQUEST_METHOD'] ) { //phpcs:ignore
case 'GET':
include './class-segmentation-custom-ga-config.php';
break;
case 'POST':
include './class-segmentation-client-data.php';
break;
Expand Down
86 changes: 0 additions & 86 deletions includes/class-newspack-popups-api.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,92 +69,6 @@ public function register_api_endpoints() {
],
]
);
\register_rest_route(
'newspack-popups/v1',
'analytics-config',
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ $this, 'get_custom_analytics_configuration' ],
'permission_callback' => '__return_true',
]
);
}

/**
* Get custom Analytics config, with segmentation-related custom dimensions assigned.
* The pageviews will be reported using this configuration, so it's important
* to include the custom dimensions set up by the Newspack Plugin, too.
*
* @param WP_REST_Request $request Request object.
*/
public static function get_custom_analytics_configuration( $request ) {
$client_id = $request['client_id'];
$ga_settings = get_option( 'googlesitekit_analytics_settings' );
if ( ! $client_id || ! $ga_settings || ! isset( $ga_settings['propertyID'] ) ) {
return [];
}

$custom_dimensions = [];
if ( class_exists( 'Newspack\Analytics_Wizard' ) ) {
$custom_dimensions = Newspack\Analytics_Wizard::list_configured_custom_dimensions();
}

// Tracking ID from Site Kit.
$gtag_id = $ga_settings['propertyID'];

$custom_dimensions_values = [];

require_once dirname( __FILE__ ) . '/../api/classes/class-lightweight-api.php';
require_once dirname( __FILE__ ) . '/../api/campaigns/class-campaign-data-utils.php';
$api = new Lightweight_API();
$client_data = $api->get_client_data( $client_id );

foreach ( $custom_dimensions as $custom_dimension ) {
// Strip the `ga:` prefix from gaID.
$dimension_id = substr( $custom_dimension['gaID'], 3 );
switch ( $custom_dimension['role'] ) {
case Newspack_Popups_Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_READER_FREQUENCY:
$read_count = count( $client_data['posts_read'] );
// Tiers mimick NCI's – https://news-consumer-insights.appspot.com.
$read_count_tier = 'casual';
if ( $read_count > 1 && $read_count <= 14 ) {
$read_count_tier = 'loyal';
} elseif ( $read_count > 14 ) {
$read_count_tier = 'brand_lover';
}
$custom_dimensions_values[ $dimension_id ] = $read_count_tier;
break;
case Newspack_Popups_Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_IS_SUBSCRIBER:
$custom_dimensions_values[ $dimension_id ] = Campaign_Data_Utils::is_subscriber( $client_data, wp_get_referer() );
break;
case Newspack_Popups_Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_IS_DONOR:
$custom_dimensions_values[ $dimension_id ] = Campaign_Data_Utils::is_donor( $client_data );
break;
}
}

$custom_dimensions_existing_values = [];
if ( class_exists( 'Newspack\Analytics' ) ) {
$custom_dimensions_existing_values = Newspack\Analytics::get_custom_dimensions_values( $request['post_id'] );
}

// This is an AMP Analytics-compliant configuration, which on non-AMP pages will be
// processed by this plugin's amp-analytics polyfill (src/view).
return [
'vars' => [
'gtag_id' => $gtag_id,
'config' => [
$gtag_id => array_merge(
[
'groups' => 'default',
],
$custom_dimensions_values,
$custom_dimensions_existing_values
),
],
],
'optoutElementId' => '__gaOptOutExtension',
];
}

/**
Expand Down
42 changes: 25 additions & 17 deletions includes/class-newspack-popups-segmentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,6 @@ final class Newspack_Popups_Segmentation {
*/
const SEGMENTS_OPTION_NAME = 'newspack_popups_segments';

/**
* Names of custom dimensions options.
*/
const CUSTOM_DIMENSIONS_OPTION_NAME_READER_FREQUENCY = 'newspack_popups_cd_reader_frequency';
const CUSTOM_DIMENSIONS_OPTION_NAME_IS_SUBSCRIBER = 'newspack_popups_cd_is_subscriber';
const CUSTOM_DIMENSIONS_OPTION_NAME_IS_DONOR = 'newspack_popups_cd_is_donor';

/**
* Main Newspack Segmentation Plugin Instance.
* Ensures only one instance of Newspack Segmentation Plugin Instance is loaded or can be loaded.
Expand Down Expand Up @@ -109,23 +102,23 @@ public static function register_custom_dimensions( $default_dimensions ) {
$default_dimensions,
[
[
'role' => self::CUSTOM_DIMENSIONS_OPTION_NAME_READER_FREQUENCY,
'role' => Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_READER_FREQUENCY,
'option' => [
'value' => self::CUSTOM_DIMENSIONS_OPTION_NAME_READER_FREQUENCY,
'value' => Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_READER_FREQUENCY,
'label' => __( 'Reader frequency', 'newspack' ),
],
],
[
'role' => self::CUSTOM_DIMENSIONS_OPTION_NAME_IS_SUBSCRIBER,
'role' => Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_IS_SUBSCRIBER,
'option' => [
'value' => self::CUSTOM_DIMENSIONS_OPTION_NAME_IS_SUBSCRIBER,
'value' => Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_IS_SUBSCRIBER,
'label' => __( 'Is a subcriber', 'newspack' ),
],
],
[
'role' => self::CUSTOM_DIMENSIONS_OPTION_NAME_IS_DONOR,
'role' => Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_IS_DONOR,
'option' => [
'value' => self::CUSTOM_DIMENSIONS_OPTION_NAME_IS_DONOR,
'value' => Segmentation::CUSTOM_DIMENSIONS_OPTION_NAME_IS_DONOR,
'label' => __( 'Is a donor', 'newspack' ),
],
],
Expand All @@ -134,6 +127,7 @@ public static function register_custom_dimensions( $default_dimensions ) {
return $default_dimensions;
}


/**
* Get GA property ID from Site Kit's options.
*/
Expand All @@ -148,12 +142,24 @@ public static function get_ga_property_id() {
* Inset GTAG amp-analytics with a remote config, which will insert segmentation-related custom dimensions.
*/
public static function insert_gtag_amp_analytics() {

$custom_dimensions = [];
if ( class_exists( 'Newspack\Analytics_Wizard' ) ) {
$custom_dimensions = Newspack\Analytics_Wizard::list_configured_custom_dimensions();
}
$custom_dimensions_existing_values = [];
if ( class_exists( 'Newspack\Analytics' ) ) {
$custom_dimensions_existing_values = Newspack\Analytics::get_custom_dimensions_values( get_the_ID() );
}

$remote_config_url = add_query_arg(
[
'client_id' => 'CLIENT_ID(' . esc_attr( self::NEWSPACK_SEGMENTATION_CID_NAME ) . ')',
'post_id' => esc_attr( get_the_ID() ),
'client_id' => 'CLIENT_ID(' . esc_attr( self::NEWSPACK_SEGMENTATION_CID_NAME ) . ')',
'post_id' => esc_attr( get_the_ID() ),
'custom_dimensions' => wp_json_encode( $custom_dimensions ),
'custom_dimensions_existing_values' => wp_json_encode( $custom_dimensions_existing_values ),
],
get_rest_url( null, 'newspack-popups/v1/analytics-config' )
plugins_url( '../api/segmentation/index.php', __FILE__ )
);

?>
Expand Down Expand Up @@ -258,7 +264,7 @@ public static function create_database_table() {

$sql = "CREATE TABLE $events_table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
created_at date NOT NULL,
created_at datetime NOT NULL,
-- type of event
type varchar(20) NOT NULL,
-- Unique id of a device/browser pair
Expand All @@ -274,6 +280,8 @@ public static function create_database_table() {

require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta( $sql ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.dbDelta_dbdelta
} elseif ( 'date' === $wpdb->get_var( $wpdb->prepare( "SHOW COLUMNS FROM {$events_table_name} LIKE %s", 'created_at' ), 1 ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
$wpdb->query( "ALTER TABLE {$events_table_name} CHANGE `created_at` `created_at` DATETIME NOT NULL" ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
}
}

Expand Down
4 changes: 2 additions & 2 deletions includes/class-newspack-popups.php
Original file line number Diff line number Diff line change
Expand Up @@ -454,9 +454,9 @@ public static function popup_default_fields( $post_id, $post, $update ) {
* Create the config file for the API, unless it exists.
*/
public static function create_lightweight_api_config() {
// Don't create a config file on Newspack's Atomic platform, or if there is a file already.
// Don't create a config file if not on Newspack's Atomic platform, or if there is a file already.
if (
defined( 'ATOMIC_SITE_ID' ) ||
! ( defined( 'ATOMIC_SITE_ID' ) && ATOMIC_SITE_ID ) ||
( file_exists( self::LIGHTWEIGHT_API_CONFIG_FILE_PATH_LEGACY ) || file_exists( self::LIGHTWEIGHT_API_CONFIG_FILE_PATH ) )
) {
return;
Expand Down
2 changes: 1 addition & 1 deletion tests/test-segmentation.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function test_log_visit() {
[
'post_read',
self::$post_read_payload['clientId'],
gmdate( 'Y-m-d', time() ),
gmdate( 'Y-m-d H:i:s', time() ),
self::$post_read_payload['post_id'],
self::$post_read_payload['categories'],
]
Expand Down

0 comments on commit 7e71118

Please sign in to comment.