Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Reopen] Implemented campaigns and tests in style of EventInfo tests #72

Merged
merged 4 commits into from
May 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/matomo_tracker.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
library matomo_tracker;

export 'src/campaign.dart';
export 'src/event_info.dart';
export 'src/exceptions.dart';
export 'src/local_storage/local_storage.dart';
Expand Down
119 changes: 119 additions & 0 deletions lib/src/campaign.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import 'package:matomo_tracker/src/assert.dart';

/// Describes a campaign.
///
/// Read more about [Campaign Tracking](https://matomo.org/faq/reports/what-is-campaign-tracking-and-why-it-is-important/).
class Campaign {
/// Creates a campaign description.
///
/// Note: Strings filled with whitespace will be considered as (invalid) empty
/// values.
factory Campaign({
required String name,
String? keyword,
String? source,
String? medium,
String? content,
String? id,
String? group,
String? placement,
}) {
assertStringIsFilled(value: name, name: 'name');
assertStringIsFilled(value: keyword, name: 'keyword');
assertStringIsFilled(value: source, name: 'source');
assertStringIsFilled(value: medium, name: 'medium');
assertStringIsFilled(value: content, name: 'content');
assertStringIsFilled(value: id, name: 'id');
assertStringIsFilled(value: group, name: 'group');
assertStringIsFilled(value: placement, name: 'placement');

return Campaign._(
name: name,
keyword: keyword,
source: source,
medium: medium,
content: content,
id: id,
group: group,
placement: placement,
);
}

const Campaign._({
required this.name,
this.keyword,
this.source,
this.medium,
this.content,
this.id,
this.group,
this.placement,
});

/// A descriptive name for the campaign, e.g. a blog post title or email campaign name.
///
/// Corresponds with `mtm_campaign`.
final String name;

/// The specific keyword that someone searched for, or category of interest.
///
/// Corresponds with `mtm_keyword`.
final String? keyword;

/// The actual source of the traffic, e.g. newsletter, twitter, ebay, etc.
///
/// Requires Matomo Cloud or Marketing Campaigns Reporting Plugin.
/// Corresponds with `mtm_source`.
final String? source;

/// The type of marketing channel, e.g. email, social, paid, etc.
///
/// Requires Matomo Cloud or Marketing Campaigns Reporting Plugin.
/// Corresponds with `mtm_medium`.
final String? medium;

/// This is a specific link or content that somebody clicked. e.g. banner, big-green-button.
///
/// Requires Matomo Cloud or Marketing Campaigns Reporting Plugin.
/// Corresponds with `mtm_content`.
final String? content;

/// A unique identifier for your specific ad. This parameter is often used with the numeric IDs automatically generated by advertising platforms.
///
/// Requires Matomo Cloud or Marketing Campaigns Reporting Plugin.
/// Corresponds with `mtm_cid`.
final String? id;

/// The audience your campaign is targeting e.g. customers, retargeting, etc.
///
/// Requires Matomo Cloud or Matomo 4 or above with Marketing Campaigns Reporting Plugin.
/// Corresponds with `mtm_group`.
final String? group;

///The placement on an advertising network e.g. newsfeed, sidebar, home-banner, etc.
///
/// Requires Matomo Cloud or Matomo 4 or above with Marketing Campaigns Reporting Plugin.
/// Corresponds with `mtm_placement`.
final String? placement;

Map<String, String> toMap() {
final mtmKeyword = keyword;
final mtmSource = source;
final mtmMedium = medium;
final mtmContent = content;
final mtmCid = id;
final mtmGroup = group;
final mtmPlacement = placement;

return {
'mtm_campaign': name,
if (mtmKeyword != null) 'mtm_keyword': mtmKeyword,
if (mtmSource != null) 'mtm_source': mtmSource,
if (mtmMedium != null) 'mtm_medium': mtmMedium,
if (mtmContent != null) 'mtm_content': mtmContent,
if (mtmCid != null) 'mtm_cid': mtmCid,
if (mtmGroup != null) 'mtm_group': mtmGroup,
if (mtmPlacement != null) 'mtm_placement': mtmPlacement,
};
}
}
9 changes: 9 additions & 0 deletions lib/src/matomo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:clock/clock.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:matomo_tracker/src/campaign.dart';
import 'package:matomo_tracker/src/event_info.dart';
import 'package:matomo_tracker/src/exceptions.dart';
import 'package:matomo_tracker/src/local_storage/cookieless_storage.dart';
Expand Down Expand Up @@ -329,11 +330,14 @@ class MatomoTracker {
/// `null`, it will be combined to [contentBase] to create a URL. This combination
/// corresponds with `url`.
///
/// - `campaign`: The campaign that lead to this page view.
///
/// For remarks on [dimensions] see [trackDimensions].
void trackScreen(
BuildContext context, {
String? pvId,
String? path,
Campaign? campaign,
Map<String, String>? dimensions,
}) {
final actionName = context.widget.toStringShort();
Expand All @@ -342,6 +346,7 @@ class MatomoTracker {
actionName: actionName,
pvId: pvId,
path: path,
campaign: campaign,
dimensions: dimensions,
);
}
Expand All @@ -360,11 +365,14 @@ class MatomoTracker {
/// `null`, it will be combined to [contentBase] to create a URL. This
/// combination corresponds with `url`.
///
/// - `campaign`: The campaign that lead to this page view.
///
/// For remarks on [dimensions] see [trackDimensions].
void trackScreenWithName({
required String actionName,
String? pvId,
String? path,
Campaign? campaign,
Map<String, String>? dimensions,
}) {
_initializationCheck();
Expand All @@ -383,6 +391,7 @@ class MatomoTracker {
tracker: this,
action: actionName,
path: path,
campaign: campaign,
dimensions: dimensions,
screenId: pvId ?? randomAlphaNumeric(6),
),
Expand Down
24 changes: 20 additions & 4 deletions lib/src/matomo_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class MatomoEvent {
this.searchCategory,
this.searchCount,
this.link,
this.campaign,
this.dimensions,
}) :
// we use clock.now instead of DateTime.now to make testing easier
Expand Down Expand Up @@ -73,6 +74,8 @@ class MatomoEvent {

final String? link;

final Campaign? campaign;

// The dimensions associated with the event
final Map<String, String>? dimensions;

Expand All @@ -81,8 +84,17 @@ class MatomoEvent {
final uid = tracker.visitor.uid;
final pvId = screenId;
final actionName = action;
final url =
path != null ? '${tracker.contentBase}/$path' : tracker.contentBase;
final camp = campaign;
final campKeyword = camp?.keyword;
final uri = Uri.parse(
path != null ? '${tracker.contentBase}/$path' : tracker.contentBase,
);
final url = uri.replace(
queryParameters: {
if (camp != null) ...camp.toMap(),
...uri.queryParameters,
},
).toString();
final idgoal = goalId;
final aRevenue = revenue;
final event = eventInfo;
Expand All @@ -93,9 +105,9 @@ class MatomoEvent {
final ecSh = shippingCost;
final ecDt = discountAmount;
final ua = tracker.userAgent;
final dims = dimensions;
final locale = PlatformDispatcher.instance.locale;
final country = locale.countryCode;
final dims = dimensions ?? {};

return {
// Required parameters
Expand All @@ -105,6 +117,8 @@ class MatomoEvent {
// Recommended parameters
if (actionName != null) 'action_name': actionName,
'url': url,
if (camp != null) '_rcn': camp.name,
if (campKeyword != null) '_rck': campKeyword,
if (id != null) '_id': id,
'rand': '${Random().nextInt(1000000000)}',
'apiv': '1',
Expand Down Expand Up @@ -150,6 +164,8 @@ class MatomoEvent {

// Other parameters (require authentication via `token_auth`)
'cdt': _date.toIso8601String(),
}..addAll(dims);

if (dims != null) ...dims,
};
}
}
7 changes: 7 additions & 0 deletions lib/src/traceable_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class TraceableWidget extends StatefulWidget {
this.pvId,
this.path,
this.dimensions,
this.campaign,
this.tracker,
});

Expand All @@ -23,6 +24,9 @@ class TraceableWidget extends StatefulWidget {
/// {@macro traceableClientMixin.path}
final String? path;

/// {@macro traceableClientMixin.campaign}
final Campaign? campaign;

/// {@macro traceableClientMixin.dimensions}
final Map<String, String>? dimensions;

Expand Down Expand Up @@ -51,6 +55,9 @@ class _TraceableWidgetState extends State<TraceableWidget>
@override
String? get path => widget.path;

@override
Campaign? get campaign => widget.campaign;

@override
Map<String, String>? get dimensions => widget.dimensions;

Expand Down
8 changes: 8 additions & 0 deletions lib/src/traceable_widget_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ mixin TraceableClientMixin<T extends StatefulWidget> on State<T>
@protected
String? path;

/// {@template traceableClientMixin.campaign}
/// The campaign that lead to this interaction or `null` for a
/// default entry.
/// {@endtemplate}
@protected
Campaign? campaign;

/// {@template traceableClientMixin.dimensions}
/// A Custom Dimension value for a specific Custom Dimension ID.
///
Expand Down Expand Up @@ -82,6 +89,7 @@ mixin TraceableClientMixin<T extends StatefulWidget> on State<T>
actionName: actionName,
pvId: pvId,
path: path,
campaign: campaign,
dimensions: dimensions,
);
}
Expand Down
28 changes: 27 additions & 1 deletion test/ressources/mock/data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ const trackingOrderItemPrice = 1.0;
const trackingOrderItemQuantity = 1;

// MatomoEvent
const matomoCampaignName = 'name';
const matomoCampaignKeyword = 'keyword';
const matomoCampaignSource = 'source';
const matomoCampaignMedium = 'medium';
const matomoCampaignContent = 'content';
const matomoCampaignId = 'id';
const matomoCampaignGroup = 'group';
const matomoCampaignPlacement = 'placement';
const matomoEventPath = 'path';
const matomoEventAction = 'action';
const matomoEventCategory = 'eventCategory';
Expand All @@ -43,7 +51,10 @@ Map<String, String> getWantedEventMap(DateTime now, {String? userAgent}) => {
"idsite": "1",
"rec": "1",
"action_name": "action",
"url": "contentBase/path",
"url":
"contentBase/path?mtm_campaign=name&mtm_keyword=keyword&mtm_source=source&mtm_medium=medium&mtm_content=content&mtm_cid=id&mtm_group=group&mtm_placement=placement",
"_rcn": "name",
"_rck": "keyword",
"_id": "visitorId",
"apiv": "1",
"_idvc": "1",
Expand Down Expand Up @@ -133,3 +144,18 @@ final wantedEventMapFull = <String, String>{
'e_n': matomoEventName,
'e_v': matomoEventValue.toString(),
};

// Campaign
final wantedCampaignMap = <String, String>{
'mtm_campaign': matomoCampaignName,
};
final wantedCampaignMapFull = <String, String>{
'mtm_campaign': matomoCampaignName,
'mtm_keyword': matomoCampaignKeyword,
'mtm_source': matomoCampaignSource,
'mtm_medium': matomoCampaignMedium,
'mtm_content': matomoCampaignContent,
'mtm_cid': matomoCampaignId,
'mtm_group': matomoCampaignGroup,
'mtm_placement': matomoCampaignPlacement,
};
Loading