File Coverage

blib/lib/Email/ExactTarget.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             =encoding utf8
2              
3             =cut
4              
5             package Email::ExactTarget;
6              
7 15     15   808851 use strict;
  15         40  
  15         616  
8 15     15   86 use warnings;
  15         33  
  15         533  
9              
10 15     15   18945 use HTTP::Request;
  15         429016  
  15         584  
11 15     15   20103 use LWP::UserAgent;
  15         413758  
  15         620  
12 15     15   14433 use HTML::Entities qw();
  15         96661  
  15         763  
13 15     15   8011 use Data::Dumper;
  15         47730  
  15         1120  
14 15     15   114 use Carp;
  15         30  
  15         943  
15 15     15   26010 use SOAP::Lite 0.71; #+trace => [qw (debug)];
  0            
  0            
16              
17             use Email::ExactTarget::SubscriberOperations;
18              
19              
20             =head1 NAME
21              
22             Email::ExactTarget - Interface to ExactTarget's API.
23              
24              
25             =head1 VERSION
26              
27             Version 1.6.2
28              
29             =cut
30              
31             our $VERSION = '1.6.2';
32              
33             our $ENDPOINT_LIVE = 'https://webservice.exacttarget.com/Service.asmx';
34              
35             our $ENDPOINT_TEST = 'https://webservice.test.exacttarget.com/Service.asmx';
36              
37             our $NAMESPACE = 'http://exacttarget.com/wsdl/partnerAPI';
38              
39              
40             =head1 SYNOPSIS
41              
42             This module allows you to interact with Exact Target, an Email Service
43             Provider. It encapsulates all the communications with the API provided by Exact
44             Target to offer a Perl interface for managing lists and subscribers amongst
45             other features.
46              
47             Please note that you will need to register with Exact Target first in order to
48             obtain an API key and password, as well as agree with the Terms and Conditions
49             for using the API.
50              
51             use Email::ExactTarget;
52              
53             # Create an object to communicate with Exact Target
54             my $exact_target = Email::ExactTarget->new(
55             'username' => 'dummyusername',
56             'password' => 'dummypassword',
57             'verbose' => 1,
58             'unaccent' => 1,
59             );
60              
61              
62             =head1 METHODS
63              
64             =head2 new()
65              
66             Create a new Exact Target object that will be used as the interface with Exact
67             Target's API.
68              
69             my $exact_target = Email::ExactTarget->new(
70             'username' => 'dummyusername',
71             'password' => 'dummypassword',
72             'verbose' => 2,
73             'unaccent' => 1,
74             );
75              
76             Creates a new object to communicate with Exact Target.
77              
78             'username' and 'password' are mandatory.
79              
80             The verbose parameter is optional and defaults to not verbose.
81              
82             The 'unaccent' parameter is optional and defaults to 0. See the documentation
83             for unaccent() for more information.
84              
85             =cut
86              
87             sub new
88             {
89             my ( $class, %args ) = @_;
90              
91             # Check for deprecated parameters.
92             carp "'all_subscribers_list_id' is not used anymore by Email::ExactTarget, please drop it from the list of arguments passed to Email::ExactTarget->new()"
93             if exists( $args{'all_subscribers_list_id'} );
94              
95             # Check for mandatory parameters
96             foreach my $arg ( qw( username password ) )
97             {
98             croak "Argument '$arg' is needed to create the Email::ExactTarget object"
99             if !defined( $args{$arg} ) || ( $args{$arg} eq '' );
100             }
101              
102             #Defaults.
103             $args{'unaccent'} = 0
104             unless defined( $args{'unaccent'} ) && ( $args{'unaccent'} eq '1' );
105             $args{'use_test_environment'} = 0
106             unless defined( $args{'use_test_environment'} ) && ( $args{'use_test_environment'} eq '1' );
107              
108             # Create the object
109             my $self = bless(
110             {
111             'username' => $args{'username'},
112             'password' => $args{'password'},
113             'use_test_environment' => $args{'use_test_environment'},
114             },
115             $class,
116             );
117              
118             # Set properties for which we have a setter.
119             $self->unaccent( $args{'unaccent'} );
120             $self->verbose( $args{'verbose'} );
121              
122             return $self;
123             }
124              
125              
126             =head2 subscriber_operations()
127              
128             Create a new Email::ExactTarget::SubscriberOperations object, which
129             will allow interacting with collections of
130             Email::ExactTarget::Subscriber objects.
131              
132             my $subscriber_operations = $exact_target->subscriber_operations();
133              
134             =cut
135              
136             sub subscriber_operations
137             {
138             my ( $self, %args ) = @_;
139              
140             return Email::ExactTarget::SubscriberOperations->new( $self, %args );
141             }
142              
143              
144             =head1 GETTERS / SETTERS
145              
146             =head2 unaccent()
147              
148             Exact Target charges a fee to allow accentuated characters to be passed through
149             their API, and otherwise turns them into question marks (for example,
150             "Jérôme" would become "J?r?me"). The alternative is to preemptively transform
151             accentuated characters from the messages sent to Exact Target into their
152             unaccentuated version("Jérôme" would thus become "Jerome"), which is free and
153             degrades in an nicer way. To enable that automatic conversion to unaccentuated
154             characters, set this to 1.
155              
156             $exact_target->unaccent( 1 );
157              
158             if ( $exact_target->unaccent() )
159             {
160             # [...]
161             }
162              
163             =cut
164              
165             sub unaccent
166             {
167             my ( $self, $unaccent ) = @_;
168              
169             $self->{'unaccent'} = ( $unaccent || 0 )
170             if defined( $unaccent );
171              
172             return $self->{'unaccent'};
173             }
174              
175              
176             =head2 verbose()
177              
178             Control the verbosity of the warnings in the code.
179              
180             $exact_target->verbose( 1 ); # turn on verbose information
181              
182             $exact_target->verbose( 0 ); # quiet now!
183              
184             warn 'Verbose' if $exact_target->verbose(); # getter-style
185              
186             =cut
187              
188             sub verbose
189             {
190             my ( $self, $verbose ) = @_;
191              
192             $self->{'verbose'} = ( $verbose || 0 )
193             if defined( $verbose );
194              
195             return $self->{'verbose'};
196             }
197              
198              
199             =head2 get_all_subscribers_list_id()
200              
201             Discontinued, this method will be removed soon.
202              
203             =cut
204              
205             sub get_all_subscribers_list_id
206             {
207             carp 'get_all_subscribers_list_id() is deprecated!';
208              
209             return undef;
210             }
211              
212              
213             =head2 use_test_environment()
214              
215             Return a boolean indicating whether the test environment is used in requests.
216              
217             my $use_test_environment = $exact_target->use_test_environment();
218              
219             =cut
220              
221             sub use_test_environment
222             {
223             my ( $self ) = @_;
224              
225             return $self->{'use_test_environment'} ? 1 : 0;
226             }
227              
228              
229             =head1 GENERAL WEBSERVICE INFORMATION
230              
231             =head2 version_info()
232              
233             Deprecated.
234              
235             =cut
236              
237             sub version_info
238             {
239             my ( $self ) = @_;
240              
241             my $soap_args =
242             [
243             SOAP::Data->name(
244             IncludeVersionHistory => 'true'
245             )->type('boolean')
246             ];
247              
248             my $soap_response = $self->soap_call(
249             'action' => 'VersionInfo',
250             'method' => 'VersionInfoRequestMsg',
251             'arguments' => $soap_args,
252             );
253              
254             croak $soap_response->fault()
255             if defined( $soap_response->fault() );
256              
257             return $soap_response->result();
258             }
259              
260              
261             =head2 get_system_status()
262              
263             See L
264              
265             Returns the system status information given by the webservice.
266              
267             Return example:
268              
269             {
270             'StatusCode' => 'OK',
271             'SystemStatus' => 'OK',
272             'StatusMessage' => 'System Status Retrieved',
273             };
274              
275             =cut
276              
277             sub get_system_status
278             {
279             my ( $self ) = @_;
280              
281             my $soap_response = $self->soap_call(
282             'action' => 'GetSystemStatus',
283             'method' => 'GetSystemStatusRequestMsg',
284             'arguments' => [],
285             );
286             my $soap_results = $soap_response->result();
287              
288             # Check for errors.
289             croak $soap_response->fault()
290             if defined( $soap_response->fault() );
291             croak 'No results found.'
292             unless defined( $soap_results->{'Result'} );
293              
294             return $soap_results->{'Result'};
295             }
296              
297              
298             =head1 INTERNAL METHODS
299              
300             =head2 soap_call()
301              
302             Internal, formats the SOAP call with the arguments provided and checks the
303             reply.
304              
305             my ( $error, $response_data ) = $exact_target->soap_call(
306             'action' => $method,
307             'arguments' => $arguments,
308             );
309              
310             =cut
311              
312             sub soap_call
313             {
314             my ( $self, %args ) = @_;
315             my $verbose = $self->verbose();
316             my $use_test_environment = $self->use_test_environment();
317             my $endpoint = $use_test_environment
318             ? $ENDPOINT_TEST
319             : $ENDPOINT_LIVE;
320              
321             # Check the parameters.
322             confess 'You must define a SOAP action'
323             if !defined( $args{'action'} ) || ( $args{'action'} eq '' );
324             confess 'You must define a SOAP method'
325             if !defined( $args{'method'} ) || ( $args{'method'} eq '' );
326             $args{'arguments'} ||= [];
327              
328             # Do not forget to specify the soapaction (on_action), you will find it in the
329             # wsdl.
330             # - uri is the target namespace in the wsdl
331             # - proxy is the endpoint address
332             my $soap = SOAP::Lite
333             ->uri( $NAMESPACE )
334             ->on_action( sub { return '"' . $args{'action'} . '"' } )
335             ->proxy( $endpoint )
336             ->readable( ( $verbose ? 1 : 0 ) );
337              
338             # You must define the namespace used in the wsdl, as an attribute to the
339             # method without namespace prefix for compatibility with .NET
340             # (document/literal).
341             my $method = SOAP::Data->name( $args{'method'} )
342             ->attr( { xmlns => $NAMESPACE } );
343              
344             # SOAP envelope headers. SOAP API requires addressing, security extensions.
345             #
346             #
347             #
348             # username
349             # password
350             #
351             #
352             my @header = (
353             SOAP::Header
354             ->name( Action => $args{'action'} )
355             ->uri( 'http://schemas.xmlsoap.org/ws/2004/08/addressing' )
356             ->prefix( 'wsa' ),
357             SOAP::Header
358             ->name( To => $endpoint )
359             ->uri( 'http://schemas.xmlsoap.org/ws/2004/08/addressing' )
360             ->prefix( 'wsa' ),
361             SOAP::Header
362             ->name(
363             Security => \SOAP::Data->value(
364             SOAP::Data->name(
365             UsernameToken => \SOAP::Data->value(
366             SOAP::Data->name( Username => $self->{'username'} )->prefix( 'wsse' ),
367             SOAP::Data->name( Password => $self->{'password'} )->prefix( 'wsse' )
368             )
369             )->prefix('wsse')
370             )
371             )
372             ->uri( 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' )
373             ->prefix( 'wsse' )
374             );
375              
376             # Make the call to the webservice.
377             my $soap_response = $soap->call(
378             @header,
379             $method,
380             @{ $args{'arguments'} }
381             );
382              
383             # Print some debugging information if requested.
384             if ( $verbose )
385             {
386             carp 'Fault: ' . Dumper( $soap_response->fault() )
387             if defined( $soap_response->fault() );
388              
389             carp 'Result: ' . Dumper( [ $soap_response->result() ] )
390             if defined( $soap_response->result() );
391              
392             carp 'Params out: ' . Dumper( $soap_response->paramsout() )
393             if defined( $soap_response->paramsout() );
394             }
395              
396             return $soap_response;
397             }
398              
399              
400             =head1 RUNNING TESTS
401              
402             By default, only basic tests that do not require a connection to ExactTarget's
403             platform are run in t/.
404              
405             To run the developer tests, you will need to do the following:
406              
407             =over 4
408              
409             =item *
410              
411             Request access to the test environment from ExactTarget (recommended) unless
412             you want to run the tests in your production environment (definitely NOT
413             recommended).
414              
415             =item *
416              
417             Ask ExactTarget to enable the webservice access for you, if not already set up.
418             It appears to be a customer-level property only ExactTarget can change.
419              
420             =item *
421              
422             In ExactTarget's interface, you will need to log in as an admin, then go to the
423             "Admin" tab, "Account Settings > My Users". Then create a user with "API User"
424             set to "Yes".
425              
426             =item *
427              
428             Go to the "Subscribers" tab, then "My Subscribers". If you look at the
429             properties for the list named "All Subscribers", you will see a field named
430             "ID". This is your "All Subscribers List ID", make a note of it.
431              
432             =item *
433              
434             Back to "My Subscribers", create at least two new lists and make a note of their
435             IDs.
436              
437             =back
438              
439             You can now create a file named ExactTargetConfig.pm in your own directory, with
440             the following content:
441              
442             package ExactTargetConfig;
443              
444             # The arguments that will be passed to Email::ExactTarget->new() when
445             # instantiating new objects during testing.
446             sub new
447             {
448             return
449             {
450             username => 'username', # The username of the test account you created.
451             password => 'password', # The password of the test account you created.
452             verbose => 0,
453             unaccent => 1,
454             use_test_environment => 1,
455             };
456             }
457              
458             # 'All Subscribers' is a special list in ExactTarget. If a user is
459             # subscribed to a list but not the 'All Subscribers' list, the user
460             # won't get any email.
461             sub get_all_subscribers_list_id
462             {
463             # The ID of the 'All Subscribers' list that exists by default
464             # in ExactTarget.
465             return 00000;
466             }
467              
468             # Tests cover adding/removing users from lists, this is an arrayref of
469             # list IDs to use during those tests. Two list IDs are required.
470             sub get_test_list_ids
471             {
472             return
473             [
474             # The IDs of the test lists you created.
475             000000,
476             000000,
477             ];
478             }
479              
480             1;
481              
482             You will then be able to run all the tests included in this distribution, after
483             adding the path to ExactTargetConfig.pm to your library paths.
484              
485              
486             =head1 BUGS
487              
488             Please report any bugs or feature requests through the web interface at
489             L.
490             I will be notified, and then you'll automatically be notified of progress on
491             your bug as I make changes.
492              
493              
494             =head1 SUPPORT
495              
496             You can find documentation for this module with the perldoc command.
497              
498             perldoc Email::ExactTarget
499              
500              
501             You can also look for information at:
502              
503             =over 4
504              
505             =item * GitHub's request tracker
506              
507             L
508              
509             =item * AnnoCPAN: Annotated CPAN documentation
510              
511             L
512              
513             =item * CPAN Ratings
514              
515             L
516              
517             =item * MetaCPAN
518              
519             L
520              
521             =back
522              
523              
524             =head1 AUTHOR
525              
526             L,
527             C<< >>.
528              
529              
530             =head1 ACKNOWLEDGEMENTS
531              
532             I originally developed this project for ThinkGeek
533             (L). Thanks for allowing me to open-source it!
534              
535              
536             =head1 COPYRIGHT & LICENSE
537              
538             Copyright 2009-2014 Guillaume Aubert.
539              
540             This program is free software: you can redistribute it and/or modify it under
541             the terms of the GNU General Public License version 3 as published by the Free
542             Software Foundation.
543              
544             This program is distributed in the hope that it will be useful, but WITHOUT ANY
545             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
546             PARTICULAR PURPOSE. See the GNU General Public License for more details.
547              
548             You should have received a copy of the GNU General Public License along with
549             this program. If not, see http://www.gnu.org/licenses/
550              
551             =cut
552              
553             1;