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