File Coverage

blib/lib/Dancer/Plugin/Piwik.pm
Criterion Covered Total %
statement 84 88 95.4
branch 25 32 78.1
condition 7 17 41.1
subroutine 13 13 100.0
pod n/a
total 129 150 86.0


line stmt bran cond sub pod time code
1             package Dancer::Plugin::Piwik;
2              
3 3     3   533929 use 5.010001;
  3         11  
4 3     3   13 use strict;
  3         5  
  3         76  
5 3     3   12 use warnings FATAL => 'all';
  3         15  
  3         143  
6 3     3   1533 use Dancer qw/:syntax/;
  3         536297  
  3         20  
7 3     3   3462 use Dancer::Plugin;
  3         5817  
  3         4608  
8              
9             =head1 NAME
10              
11             Dancer::Plugin::Piwik - Generate JS code for Piwik
12              
13             =head1 VERSION
14              
15             Version 0.05
16              
17             =cut
18              
19             our $VERSION = '0.05';
20              
21             # on loading, spam the message if disabled
22              
23             if (plugin_setting->{url} && plugin_setting->{id}) {
24             info "Loading Piwiki plugin";
25             }
26             else {
27             warning "Missing url and id for Piwiki, plugin is disabled";
28             }
29              
30             =head1 SYNOPSIS
31              
32             In your configuration:
33              
34             plugins:
35             Piwik:
36             id: "your-id"
37             url: "your-url"
38             username_session_key: 'piwik_username'
39              
40             The username_session_key is optional. If set, the plugin will look
41             into the session value for the given key and set that as the tracked
42             username. You probably want to that in an hook.
43              
44              
45             In your module
46              
47             use Dancer ':syntax';
48             use Dancer::Plugin::Piwik;
49              
50             # if use'ing Dancer::Plugin::Auth::Extensible;
51             hook before => sub {
52             if (my $user = logged_in_user) {
53             session(piwik_username => $user->username);
54             }
55             else {
56             session(piwik_username => undef);
57             }
58             }
59              
60             =head1 CONFIGURATION
61              
62             Two keys are required:
63              
64             =head2 id
65              
66             The numeric id of the tracked site (you find it in the Piwik admin).
67              
68             =head2 url
69              
70             The url of the tracking site, B and B
71             trailing slash>. E.g.
72              
73             mysite.org/stats
74              
75             =head1 EXPORTED KEYWORDS
76              
77             All the following keywords support the boolean argument C.
78             Instead of the javascript, a perl structure will be returned.
79              
80             =head2 piwik
81              
82             Return generic code for page view tracking. No argument required.
83              
84             =head2 piwik_category(category => "name")
85              
86             Generate js for category pages. Requires a named argument C
87             with the name of the category to track.
88              
89             =head2 piwik_view(product => { sku => $sku, description => $desc, categories => \@categories, price => $price })
90              
91             Generate js for flypages. Expects a named argument product, paired
92             with an hashref with the product data, having the following keys
93              
94             =over 4
95              
96             =item sku
97              
98             =item description
99              
100             =item categories
101              
102             (an arrayref with the names of the categories). An empty arrayref can
103             do as well).
104              
105             =item price
106              
107             The price of the item
108              
109             =back
110              
111             =head2 piwik_cart(cart => \@items, subtotal => $subtotal)
112              
113             Generate js for cart view. Requires two named arguments. The C
114             argument must point to an arrayref of hashref products, with the same
115             keys of C, and an additional key C.
116              
117             =cut
118              
119             sub _piwik {
120 6     6   2393 my %args = @_;
121 6         37 return _generate_js($args{ajax});
122             }
123              
124             sub _piwik_category {
125 7     7   26 my %args = @_;
126 7         11 my $category = $args{category};
127 7 100       18 unless ($category) {
128 1         4 return _generate_js($args{ajax});
129             }
130 6         31 return _generate_js($args{ajax}, [ setEcommerceView => \0, \0, $category ]);
131             }
132              
133             =head2 piwik_search(search => { query => 'query', category => 'category', matches => 10 }};
134              
135             Generate js for search results. Requires a C argument with the
136             data to pass to piwik. The only mandatory value is C.
137              
138             =cut
139              
140             sub _piwik_search {
141 5     5   384 my %args = @_;
142 5         9 my $search = $args{search};
143 5 50 33     39 unless ($search and (ref($search) eq 'HASH') and $search->{query}) {
      33        
144 0         0 return _generate_js($args{ajax});
145             }
146 5   50     13 my $category = $search->{category} || \0;
147 5         6 my $query = $search->{query};
148 5 50       11 my $matches = defined($search->{matches}) ? $search->{matches} : \0;
149 5         15 return _generate_js($args{ajax}, [ trackSiteSearch => $query, $category, $matches ]);
150             }
151              
152              
153             sub _piwik_view {
154 5     5   229 my %args = @_;
155 5         9 my $product = $args{product};
156 5 50       26 unless ($product) {
157 0         0 return _generate_js($args{ajax});
158             }
159             my $arg = [
160             setEcommerceView => $product->{sku},
161             $product->{description},
162 5         19 [ @{ $product->{categories} } ],
163 5         8 $product->{price} + 0,
164             ];
165 5         14 return _generate_js($args{ajax}, $arg);
166             }
167              
168             sub _piwik_cart {
169 5     5   13 my %args = @_;
170 5         7 my $subtotal = $args{subtotal};
171 5         5 my $cart = $args{cart};
172 5 50 33     39 unless ($cart && defined($subtotal)) {
173 0         0 return _generate_js($args{ajax});
174             }
175 5         9 my @addendum = _unroll_cart($cart);
176 5         10 push @addendum, [ trackEcommerceCartUpdate => $subtotal + 0 ];
177 5         13 return _generate_js($args{ajax}, @addendum);
178             }
179              
180             sub _unroll_cart {
181 13     13   14 my $cart = shift;
182 13         14 my @addendum;
183 13         18 foreach my $item (@$cart) {
184             push @addendum, [
185             addEcommerceItem => $item->{sku},
186             $item->{description},
187 11         65 [ @{ $item->{categories} } ],
188             $item->{price} + 0,
189 11         18 $item->{quantity} + 0,
190             ];
191             }
192 13         26 return @addendum;
193             }
194              
195             =head2 piwik_order(cart => \@items, order => { order_number => $id, total_cost => $total, subtotal => $subtotal, taxes => $taxes, shipping => $shipping, discount => $discount }
196              
197             Generate js for the receipt. Two required arguments: C is the
198             same as C, while order is an hashref with the following keys:
199              
200             =over 4
201              
202             =item order_number (required)
203              
204             =item total_cost (required)
205              
206             =item subtotal (optional)
207              
208             =item taxes (optional)
209              
210             =item shipping (optional)
211              
212             =item discount (optional)
213              
214             =back
215              
216             =cut
217              
218             sub _piwik_order {
219 8     8   664 my %args = @_;
220 8         10 my $cart = $args{cart};
221 8         10 my $order = $args{order};
222 8 50 33     39 unless ($cart && $order) {
223 0         0 return _generate_js($args{ajax});
224             }
225 8         23 my @addendum = _unroll_cart($cart);
226 8         6 my @missing;
227 8         10 foreach my $i (qw/total_cost order_number/) {
228 16 100       35 push @missing, $i unless defined $order->{$i};
229             }
230 8 100       26 if (@missing) {
231 1         8 warning "Missing order keys: " . join(' ', @missing);
232 1         74 return _generate_js($args{ajax});
233             }
234              
235             # avoid touching the original
236 7         23 $order = { %$order };
237              
238 7         12 foreach my $i (qw/subtotal taxes shipping discount/) {
239 28 100       39 if (defined $order->{$i}) {
240             # coerce it to a number
241 12         38 $order->{$i} += 0;
242             }
243             else {
244             # set it to false
245 16         23 $order->{$i} = \0;
246             }
247             }
248             push @addendum, [trackEcommerceOrder => $order->{order_number} .'',
249             $order->{total_cost} + 0,
250             $order->{subtotal},
251             $order->{taxes},
252             $order->{shipping},
253             $order->{discount},
254 7         23 ];
255 7         17 return _generate_js($args{ajax}, @addendum);
256             }
257              
258             sub _generate_js {
259 36     36   79 my ($ajax, @args) = @_;
260 36         83 my $piwik_url = plugin_setting->{url};
261 36         511 my $piwik_id = plugin_setting->{id};
262 36 100 66     395 unless ($piwik_url && defined($piwik_id)) {
263 10 100       77 $ajax ? return {} : return '';
264             }
265 26 100       40 if (my $session_key = plugin_setting->{username_session_key}) {
266 7 50       68 if (my $username = session($session_key)) {
267 7         838 push @args, [ setUserId => $username ];
268             }
269             }
270 26 50       253 unless (scalar(grep { ref($_) eq 'ARRAY' and $_->[0] eq 'trackSiteSearch' } @args)) {
  34 100       183  
271 21         37 push @args, ['trackPageView'];
272             }
273 26         38 push @args, ['enableLinkTracking'];
274 26 100       42 if ($ajax) {
275             return {
276 2         21 piwik_url => $piwik_url,
277             piwik_id => $piwik_id,
278             elements => \@args,
279             };
280             }
281 24         26 my $addendum = '';
282 24         32 foreach my $arg (@args) {
283 75         9808 $addendum .= '_paq.push(' . to_json($arg) . ");\n";
284             }
285              
286 24         4261 my $js = <<"JAVASCRIPT";
287            
298            
299             JAVASCRIPT
300 24         309 return $js;
301             }
302              
303             register piwik => \&_piwik;
304             register piwik_category => \&_piwik_category;
305             register piwik_view => \&_piwik_view;
306             register piwik_cart => \&_piwik_cart;
307             register piwik_order => \&_piwik_order;
308             register piwik_search => \&_piwik_search;
309              
310             register_plugin;
311              
312              
313             =head1 AUTHOR
314              
315             Stefan Hornburg (Racke), C<< >>
316              
317             =head1 BUGS
318              
319             Please report any bugs or feature requests to C, or through
320             the web interface at L. I will be notified, and then you'll
321             automatically be notified of progress on your bug as I make changes.
322              
323              
324              
325              
326             =head1 SUPPORT
327              
328             You can find documentation for this module with the perldoc command.
329              
330             perldoc Dancer::Plugin::Piwik
331              
332              
333             You can also look for information at:
334              
335             =over 4
336              
337             =item * RT: CPAN's request tracker (report bugs here)
338              
339             L
340              
341             =item * AnnoCPAN: Annotated CPAN documentation
342              
343             L
344              
345             =item * CPAN Ratings
346              
347             L
348              
349             =item * Search CPAN
350              
351             L
352              
353             =back
354              
355              
356             =head1 ACKNOWLEDGEMENTS
357              
358              
359             =head1 LICENSE AND COPYRIGHT
360              
361             Copyright 2014 Stefan Hornburg (Racke).
362              
363             This program is free software; you can redistribute it and/or modify it
364             under the terms of the the Artistic License (2.0). You may obtain a
365             copy of the full license at:
366              
367             L
368              
369             Any use, modification, and distribution of the Standard or Modified
370             Versions is governed by this Artistic License. By using, modifying or
371             distributing the Package, you accept this license. Do not use, modify,
372             or distribute the Package, if you do not accept this license.
373              
374             If your Modified Version has been derived from a Modified Version made
375             by someone other than you, you are nevertheless required to ensure that
376             your Modified Version complies with the requirements of this license.
377              
378             This license does not grant you the right to use any trademark, service
379             mark, tradename, or logo of the Copyright Holder.
380              
381             This license includes the non-exclusive, worldwide, free-of-charge
382             patent license to make, have made, use, offer to sell, sell, import and
383             otherwise transfer the Package with respect to any patent claims
384             licensable by the Copyright Holder that are necessarily infringed by the
385             Package. If you institute patent litigation (including a cross-claim or
386             counterclaim) against any party alleging that the Package constitutes
387             direct or contributory patent infringement, then this Artistic License
388             to you shall terminate on the date that such litigation is filed.
389              
390             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
391             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
392             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
393             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
394             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
395             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
396             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
397             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
398              
399              
400             =cut
401              
402             1; # End of Dancer::Plugin::Piwik