line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
1
|
|
|
1
|
|
14621
|
use 5.20.0; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
29
|
|
2
|
1
|
|
|
1
|
|
4
|
use strict; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
25
|
|
3
|
1
|
|
|
1
|
|
3
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
49
|
|
4
|
|
|
|
|
|
|
package App::Addex::AddressBook::AppleScript; |
5
|
|
|
|
|
|
|
$App::Addex::AddressBook::AppleScript::VERSION = '0.008'; |
6
|
1
|
|
|
1
|
|
411
|
use parent qw(App::Addex::AddressBook); |
|
1
|
|
|
|
|
232
|
|
|
1
|
|
|
|
|
3
|
|
7
|
|
|
|
|
|
|
use experimental 'postderef'; |
8
|
|
|
|
|
|
|
# ABSTRACT: Mac::Glue-less Addex adapter for Apple Address Book and Addex |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
#pod =head1 SYNOPSIS |
11
|
|
|
|
|
|
|
#pod |
12
|
|
|
|
|
|
|
#pod This module implements the L<App::Addex::AddressBook> interface for Mac OS X's |
13
|
|
|
|
|
|
|
#pod Address Book application, using I<a horrible hack> to get entries from the |
14
|
|
|
|
|
|
|
#pod address book. |
15
|
|
|
|
|
|
|
#pod |
16
|
|
|
|
|
|
|
#pod A much cleaner interface would be to use L<App::Addex::AddressBook::Apple>, |
17
|
|
|
|
|
|
|
#pod which uses L<Mac::Glue> to access the address book. Unfortunately, Mac::Glue |
18
|
|
|
|
|
|
|
#pod does not work in many recent builds of Perl, and will cease to work as the |
19
|
|
|
|
|
|
|
#pod Carbon API is killed off. |
20
|
|
|
|
|
|
|
#pod |
21
|
|
|
|
|
|
|
#pod The AppleScript adapter builds an AppleScript program that prints out a dump of |
22
|
|
|
|
|
|
|
#pod relevant address book entries, then runs it, then parses its output. The |
23
|
|
|
|
|
|
|
#pod format of the intermediate form may change for all kinds of crazy reasons. |
24
|
|
|
|
|
|
|
#pod |
25
|
|
|
|
|
|
|
#pod =cut |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
use App::Addex::Entry; |
28
|
|
|
|
|
|
|
use App::Addex::Entry::EmailAddress; |
29
|
|
|
|
|
|
|
use Encode (); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
use File::Temp (); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub _produce_applescript { |
34
|
|
|
|
|
|
|
my @fields = ( |
35
|
|
|
|
|
|
|
'company', # true / false |
36
|
|
|
|
|
|
|
'organization', |
37
|
|
|
|
|
|
|
'first name', |
38
|
|
|
|
|
|
|
'middle name', |
39
|
|
|
|
|
|
|
'last name', |
40
|
|
|
|
|
|
|
'nickname', |
41
|
|
|
|
|
|
|
'suffix', |
42
|
|
|
|
|
|
|
'note', |
43
|
|
|
|
|
|
|
); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
my $dumper = ''; |
46
|
|
|
|
|
|
|
for my $field (@fields) { |
47
|
|
|
|
|
|
|
$dumper .= <<"END_FIELD_DUMPER"; |
48
|
|
|
|
|
|
|
set _this to get $field of _person |
49
|
|
|
|
|
|
|
if $field of _person is not missing value then |
50
|
|
|
|
|
|
|
set _answer to _answer & "- BEGIN $field\n" |
51
|
|
|
|
|
|
|
set _answer to _answer & ($field of _person) & "\n" |
52
|
|
|
|
|
|
|
set _answer to _answer & "- END $field\n" |
53
|
|
|
|
|
|
|
end if |
54
|
|
|
|
|
|
|
END_FIELD_DUMPER |
55
|
|
|
|
|
|
|
} |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
my $osascript = <<'END_APPLESCRIPT'; |
58
|
|
|
|
|
|
|
tell application "Address Book" |
59
|
|
|
|
|
|
|
set _people to (get people) |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
set _answer to "" |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
repeat with _person in _people |
64
|
|
|
|
|
|
|
repeat 1 times |
65
|
|
|
|
|
|
|
if count of email of _person = 0 then |
66
|
|
|
|
|
|
|
exit repeat |
67
|
|
|
|
|
|
|
end if |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
set _answer to _answer & "--- BEGIN " & id of _person & "\n" |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
$dumper |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
set _answer to _answer & "- BEGIN email\n" |
74
|
|
|
|
|
|
|
repeat with _email in (get email of _person) |
75
|
|
|
|
|
|
|
set _answer to _answer & (label of _email) & "\n" |
76
|
|
|
|
|
|
|
set _answer to _answer & (value of _email) & "\n" |
77
|
|
|
|
|
|
|
end repeat |
78
|
|
|
|
|
|
|
set _answer to _answer & "- END email\n" |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
set _answer to _answer & "--- END " & id of _person & "\n\n" |
81
|
|
|
|
|
|
|
end repeat |
82
|
|
|
|
|
|
|
end repeat |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
_answer |
85
|
|
|
|
|
|
|
end tell |
86
|
|
|
|
|
|
|
END_APPLESCRIPT |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
$osascript =~ s/\$dumper/$dumper/; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
return $osascript; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub _produce_scriptfile { |
94
|
|
|
|
|
|
|
my ($self) = @_; |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
my $osascript = $self->_produce_applescript; |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
my ($fh, $filename) = File::Temp::tempfile(UNLINK => 1); |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
$fh->print($osascript); |
101
|
|
|
|
|
|
|
$fh->close or die "can't close $filename: $!"; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
return $filename; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub entries { |
107
|
|
|
|
|
|
|
my ($self) = @_; |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
my $script = $self->_produce_scriptfile; |
110
|
|
|
|
|
|
|
my @output = `/usr/bin/osascript $script`; |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
@output = map {; Encode::decode('utf-8', $_) } @output; |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
my @people; |
115
|
|
|
|
|
|
|
my $this; |
116
|
|
|
|
|
|
|
LINE: while (my $line = shift @output) { |
117
|
|
|
|
|
|
|
unless ($this) { |
118
|
|
|
|
|
|
|
next LINE unless $line =~ /\A--- BEGIN (.+)\Z/; |
119
|
|
|
|
|
|
|
$this = { id => $1 }; |
120
|
|
|
|
|
|
|
push @people, $this; |
121
|
|
|
|
|
|
|
next LINE; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
my @input; |
125
|
|
|
|
|
|
|
if ($line =~ /\A- BEGIN (.+)\Z/) { |
126
|
|
|
|
|
|
|
my $field = $1; |
127
|
|
|
|
|
|
|
push @input, shift @output until @input and $input[-1] =~ /\A- END $1\Z/; |
128
|
|
|
|
|
|
|
pop @input; |
129
|
|
|
|
|
|
|
$this->{ $field } = join q{}, @input; |
130
|
|
|
|
|
|
|
chomp $this->{ $field }; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
if ($field eq 'email') { |
133
|
|
|
|
|
|
|
$this->{emails} = [ split /\n/, delete $this->{email} ]; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
if ($line =~ /\A--- END \Q$this->{id}\E\Z/) { |
138
|
|
|
|
|
|
|
undef $this; |
139
|
|
|
|
|
|
|
next LINE; |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
} |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
my @entries = map {; $self->_entrify($_) } @people; |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
return @entries; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
sub _entrify { |
149
|
|
|
|
|
|
|
my ($self, $person) = @_; |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
my %fields; |
152
|
|
|
|
|
|
|
if (my $note = $person->{note} // '') { |
153
|
|
|
|
|
|
|
my @lines = grep { length } split /\R/, $note; |
154
|
|
|
|
|
|
|
for my $line (@lines) { |
155
|
|
|
|
|
|
|
next if $line =~ /^--/; # comment |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
my $tmpname |
158
|
|
|
|
|
|
|
= join q{ }, grep $_, |
159
|
|
|
|
|
|
|
$person->@{'first name', 'middle name', 'last name', 'suffix'}; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
warn("bogus line in notes on $tmpname: $line\n"), next |
162
|
|
|
|
|
|
|
unless $line =~ /\A([^:]+):\s*(.+?)\Z/; |
163
|
|
|
|
|
|
|
$fields{$1} = $2; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
my $fname = $person->{'first name'} // ''; |
168
|
|
|
|
|
|
|
my $mname = $person->{'middle name'} // ''; |
169
|
|
|
|
|
|
|
my $lname = $person->{'last name'} // ''; |
170
|
|
|
|
|
|
|
my $suffix = $person->{suffix} // ''; |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
$mname = '' unless $fields{'use middle'} // 1; |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
my $name; |
175
|
|
|
|
|
|
|
if ($person->{company} eq 'true') { |
176
|
|
|
|
|
|
|
$name = $person->{organization}; |
177
|
|
|
|
|
|
|
} else { |
178
|
|
|
|
|
|
|
$name = $fname |
179
|
|
|
|
|
|
|
. (length $mname ? " $mname" : '') |
180
|
|
|
|
|
|
|
. (length $lname ? " $lname" : '') |
181
|
|
|
|
|
|
|
. (length $suffix ? " $suffix" : ''); |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
unless (length $name) { |
185
|
|
|
|
|
|
|
warn "couldn't figure out a name for this entry\n"; |
186
|
|
|
|
|
|
|
return; |
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
my @emails; |
190
|
|
|
|
|
|
|
my @kv = @{ $person->{emails} }; |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
for (my $i = 0; $i < $#kv; $i += 2) { |
193
|
|
|
|
|
|
|
push @emails, App::Addex::Entry::EmailAddress->new({ |
194
|
|
|
|
|
|
|
address => $kv[ $i + 1 ], |
195
|
|
|
|
|
|
|
label => $kv[ $i ], |
196
|
|
|
|
|
|
|
}); |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
CHECK_DEFAULT: { |
200
|
|
|
|
|
|
|
if (@emails > 1 and my $default = $fields{default_email}) { |
201
|
|
|
|
|
|
|
my $check; |
202
|
|
|
|
|
|
|
if ($default =~ m{\A/(.+)/\z}) { |
203
|
|
|
|
|
|
|
$default = qr/$1/; |
204
|
|
|
|
|
|
|
$check = sub { $_[0]->address =~ $default }; |
205
|
|
|
|
|
|
|
} else { |
206
|
|
|
|
|
|
|
$check = sub { $_[0]->label eq $default }; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
for my $i (0 .. $#emails) { |
210
|
|
|
|
|
|
|
if ($check->($emails[$i])) { |
211
|
|
|
|
|
|
|
unshift @emails, splice @emails, $i, 1 if $i != 0; |
212
|
|
|
|
|
|
|
last CHECK_DEFAULT; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
warn "no email found for $name matching $fields{default_email}\n"; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
} |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
my $arg = { |
221
|
|
|
|
|
|
|
name => $name, |
222
|
|
|
|
|
|
|
nick => $person->{nickname}, |
223
|
|
|
|
|
|
|
emails => \@emails, |
224
|
|
|
|
|
|
|
fields => \%fields, |
225
|
|
|
|
|
|
|
}; |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
return App::Addex::Entry->new($arg); |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
1; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
__END__ |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=pod |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=encoding UTF-8 |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=head1 NAME |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
App::Addex::AddressBook::AppleScript - Mac::Glue-less Addex adapter for Apple Address Book and Addex |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=head1 VERSION |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
version 0.008 |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
=head1 SYNOPSIS |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
This module implements the L<App::Addex::AddressBook> interface for Mac OS X's |
249
|
|
|
|
|
|
|
Address Book application, using I<a horrible hack> to get entries from the |
250
|
|
|
|
|
|
|
address book. |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
A much cleaner interface would be to use L<App::Addex::AddressBook::Apple>, |
253
|
|
|
|
|
|
|
which uses L<Mac::Glue> to access the address book. Unfortunately, Mac::Glue |
254
|
|
|
|
|
|
|
does not work in many recent builds of Perl, and will cease to work as the |
255
|
|
|
|
|
|
|
Carbon API is killed off. |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
The AppleScript adapter builds an AppleScript program that prints out a dump of |
258
|
|
|
|
|
|
|
relevant address book entries, then runs it, then parses its output. The |
259
|
|
|
|
|
|
|
format of the intermediate form may change for all kinds of crazy reasons. |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
=head1 AUTHOR |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
Ricardo Signes <rjbs@cpan.org> |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
This software is copyright (c) 2010 by Ricardo Signes. |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
270
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
=cut |