line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Text::vCard::Precisely::V4; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.26'; |
4
|
|
|
|
|
|
|
|
5
|
14
|
|
|
14
|
|
1402092
|
use Moose; |
|
14
|
|
|
|
|
6086640
|
|
|
14
|
|
|
|
|
103
|
|
6
|
14
|
|
|
14
|
|
105788
|
use Moose::Util::TypeConstraints; |
|
14
|
|
|
|
|
33
|
|
|
14
|
|
|
|
|
140
|
|
7
|
14
|
|
|
14
|
|
40164
|
use MooseX::Types::DateTime qw(TimeZone); |
|
14
|
|
|
|
|
7867195
|
|
|
14
|
|
|
|
|
118
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
extends 'Text::vCard::Precisely::V3'; |
10
|
|
|
|
|
|
|
|
11
|
14
|
|
|
14
|
|
28869
|
use Carp; |
|
14
|
|
|
|
|
34
|
|
|
14
|
|
|
|
|
1120
|
|
12
|
14
|
|
|
14
|
|
9886
|
use Encode; |
|
14
|
|
|
|
|
137194
|
|
|
14
|
|
|
|
|
1236
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=encoding utf8 |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 NAME |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Text::vCard::Precisely::V4 - Read, Write and Edit B<vCards 4.0> |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 SYNOPSIS |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
You can unlock types that will be available in vCard4.0 |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my $vc = Text::vCard::Precisely->new( version => '4.0' ); |
25
|
|
|
|
|
|
|
# Or you can write like below: |
26
|
|
|
|
|
|
|
my $vc4 = Text::vCard::Precisely::V4->new(); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
The Usage is same with L<Text::vCard::Precisely::V3> |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 DESCRIPTION |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
This module is an additional version for reading/writing for vCard4.0. it's just a wrapper of L<Text::vCard::Precisely::V3|https://metacpan.org/pod/Text::vCard::Precisely::V3> |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
B<Caution!> It's NOT be recommended because some reasons below: |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=over |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=item |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
Mac OS X and iOS can't parse vCard4.0 with UTF-8 precisely. |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=item |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
Android 4.4.x can't parse vCard4.0. |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=back |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
Note that the vCard RFC requires C<FN> type. |
49
|
|
|
|
|
|
|
And this module does not check or warn if these conditions have not been met. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=cut |
52
|
|
|
|
|
|
|
|
53
|
14
|
|
|
14
|
|
6616
|
use Text::vCard::Precisely::V4::Node; |
|
14
|
|
|
|
|
62
|
|
|
14
|
|
|
|
|
698
|
|
54
|
14
|
|
|
14
|
|
8602
|
use Text::vCard::Precisely::V4::Node::N; |
|
14
|
|
|
|
|
58
|
|
|
14
|
|
|
|
|
1075
|
|
55
|
14
|
|
|
14
|
|
8454
|
use Text::vCard::Precisely::V4::Node::Address; |
|
14
|
|
|
|
|
337
|
|
|
14
|
|
|
|
|
766
|
|
56
|
14
|
|
|
14
|
|
8556
|
use Text::vCard::Precisely::V4::Node::Tel; |
|
14
|
|
|
|
|
63
|
|
|
14
|
|
|
|
|
725
|
|
57
|
14
|
|
|
14
|
|
8411
|
use Text::vCard::Precisely::V4::Node::Related; |
|
14
|
|
|
|
|
63
|
|
|
14
|
|
|
|
|
730
|
|
58
|
14
|
|
|
14
|
|
8956
|
use Text::vCard::Precisely::V4::Node::Member; |
|
14
|
|
|
|
|
62
|
|
|
14
|
|
|
|
|
693
|
|
59
|
14
|
|
|
14
|
|
8463
|
use Text::vCard::Precisely::V4::Node::Image; |
|
14
|
|
|
|
|
58
|
|
|
14
|
|
|
|
|
36425
|
|
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
has version => ( is => 'ro', isa => 'Str', default => '4.0' ); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=head1 Constructors |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head2 load_hashref($HashRef) |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
SAME as 3.0 |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head2 loadI<file($file>name) |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
SAME as 3.0 |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head2 load_string($vCard) |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
SAME as 3.0 |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=cut |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
override '_parse_param' => sub { |
80
|
|
|
|
|
|
|
my ( $self, $content ) = @_; |
81
|
|
|
|
|
|
|
my $ref = super(); |
82
|
|
|
|
|
|
|
$ref->{'media_type'} = $content->{'param'}{'MEDIATYPE'} if $content->{'param'}{'MEDIATYPE'}; |
83
|
|
|
|
|
|
|
return $ref; |
84
|
|
|
|
|
|
|
}; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
=head1 METHODS |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
=head2 as_string() |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
Returns the vCard as a string. |
91
|
|
|
|
|
|
|
You HAVE TO use C<Encode::encode_utf8()> if your vCard is written in utf8 |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=cut |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
my $cr = "\x0D\x0A"; |
96
|
|
|
|
|
|
|
my @types = qw( |
97
|
|
|
|
|
|
|
FN N NICKNAME |
98
|
|
|
|
|
|
|
ADR TEL EMAIL IMPP LANG GEO |
99
|
|
|
|
|
|
|
ORG TITLE ROLE CATEGORIES RELATED |
100
|
|
|
|
|
|
|
NOTE SOUND URL FBURL CALADRURI CALURI |
101
|
|
|
|
|
|
|
XML KEY SOCIALPROFILE PHOTO LOGO SOURCE |
102
|
|
|
|
|
|
|
); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# ToDo: to accept SORT-AS param in FN,N,ORG |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub as_string { |
107
|
35
|
|
|
35
|
1
|
5802
|
my ($self) = @_; |
108
|
35
|
|
|
|
|
253
|
my $str = $self->_header(); |
109
|
35
|
|
|
|
|
233
|
$str .= $self->_make_types(@types); |
110
|
|
|
|
|
|
|
|
111
|
35
|
100
|
|
|
|
1020
|
$str .= 'KIND:' . $self->kind() . $cr if $self->kind(); |
112
|
35
|
100
|
|
|
|
1056
|
$str .= 'BDAY:' . $self->bday() . $cr if $self->bday(); |
113
|
35
|
100
|
|
|
|
1028
|
$str .= 'ANNIVERSARY:' . $self->anniversary() . $cr if $self->anniversary(); |
114
|
35
|
100
|
|
|
|
1037
|
$str .= 'GENDER:' . $self->gender() . $cr if $self->gender(); |
115
|
35
|
100
|
|
|
|
1041
|
$str .= 'UID:' . $self->uid() . $cr if $self->uid(); |
116
|
35
|
100
|
|
|
|
1050
|
$str .= join '', @{ $self->member() } if $self->member(); |
|
1
|
|
|
|
|
30
|
|
117
|
35
|
100
|
|
|
|
1106
|
map { $str .= "CLIENTPIDMAP:$_" . $cr } @{ $self->clientpidmap() } if $self->clientpidmap(); |
|
3
|
|
|
|
|
17
|
|
|
2
|
|
|
|
|
94
|
|
118
|
|
|
|
|
|
|
|
119
|
35
|
|
|
|
|
183
|
$str .= $self->_footer(); |
120
|
35
|
|
|
|
|
205
|
$str = $self->_fold($str); |
121
|
35
|
50
|
|
|
|
1292
|
return decode( $self->encoding_out(), $str ) unless $self->encoding_out() eq 'none'; |
122
|
0
|
|
|
|
|
0
|
return $str; |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=head2 as_file($filename) |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Write data in vCard format to $filename. |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
Dies if not successful. |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=head1 SIMPLE GETTERS/SETTERS |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
These methods accept and return strings. |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=head2 version() |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
Returns Version number of the vcard. Defaults to B<'3.0'> |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
It is B<READONLY> method. So you can NOT downgrade it to 3.0 |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=head2 rev() |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
To specify revision information about the current vCard |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
The format in as_string() is B<different from 3.0>, but the interface is SAME |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=head1 COMPLEX GETTERS/SETTERS |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
They are based on Moose with coercion |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
So these methods accept not only ArrayRef[HashRef] but also ArrayRef[Str], |
152
|
|
|
|
|
|
|
single HashRef or single Str |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
Read source if you were confused |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=head2 n() |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
The format is SAME as 3.0 |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=cut |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
subtype 'v4N' => as 'Text::vCard::Precisely::V4::Node::N'; |
163
|
|
|
|
|
|
|
coerce 'v4N', from 'HashRef[Maybe[Ref]|Maybe[Str]]', via { |
164
|
|
|
|
|
|
|
my %param; |
165
|
|
|
|
|
|
|
while ( my ( $key, $value ) = each %$_ ) { |
166
|
|
|
|
|
|
|
$param{$key} = $value if $value; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
return Text::vCard::Precisely::V4::Node::N->new( \%param ); |
169
|
|
|
|
|
|
|
}, |
170
|
|
|
|
|
|
|
from 'HashRef[Maybe[Str]]', |
171
|
|
|
|
|
|
|
via { Text::vCard::Precisely::V4::Node::N->new( { content => $_ } ) }, |
172
|
|
|
|
|
|
|
from 'ArrayRef[Maybe[Str]]', via { |
173
|
|
|
|
|
|
|
Text::vCard::Precisely::V4::Node::N->new( |
174
|
|
|
|
|
|
|
{ content => { |
175
|
|
|
|
|
|
|
family => $_->[0] || '', |
176
|
|
|
|
|
|
|
given => $_->[1] || '', |
177
|
|
|
|
|
|
|
additional => $_->[2] || '', |
178
|
|
|
|
|
|
|
prefixes => $_->[3] || '', |
179
|
|
|
|
|
|
|
suffixes => $_->[4] || '', |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
) |
183
|
|
|
|
|
|
|
}, |
184
|
|
|
|
|
|
|
from 'Str', |
185
|
|
|
|
|
|
|
via { Text::vCard::Precisely::V4::Node::N->new( { content => [ split /(?<!\\);/, $_ ] } ) }; |
186
|
|
|
|
|
|
|
has n => ( is => 'rw', isa => 'v4N', coerce => 1 ); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=head2 tel() |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
The format in as_string() is B<different from 3.0>, but the interface is SAME |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=cut |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
subtype 'v4Tels' => as 'ArrayRef[Text::vCard::Precisely::V4::Node::Tel]'; |
195
|
|
|
|
|
|
|
coerce 'v4Tels', |
196
|
|
|
|
|
|
|
from 'Str', |
197
|
|
|
|
|
|
|
via { [ Text::vCard::Precisely::V4::Node::Tel->new( { content => $_ } ) ] }, |
198
|
|
|
|
|
|
|
from 'HashRef', via { |
199
|
|
|
|
|
|
|
my $types = ref( $_->{'types'} ) eq 'ARRAY' ? $_->{'types'} : [ $_->{'types'} ]; |
200
|
|
|
|
|
|
|
[ Text::vCard::Precisely::V4::Node::Tel->new( { %$_, types => $types } ) ] |
201
|
|
|
|
|
|
|
}, from 'ArrayRef[HashRef]', via { |
202
|
|
|
|
|
|
|
[ map { |
203
|
|
|
|
|
|
|
my $types = ref( $_->{'types'} ) eq 'ARRAY' ? $_->{'types'} : [ $_->{'types'} ]; |
204
|
|
|
|
|
|
|
Text::vCard::Precisely::V4::Node::Tel->new( { %$_, types => $types } ) |
205
|
|
|
|
|
|
|
} @$_ |
206
|
|
|
|
|
|
|
] |
207
|
|
|
|
|
|
|
}; |
208
|
|
|
|
|
|
|
has tel => ( is => 'rw', isa => 'v4Tels', coerce => 1 ); |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=head2 adr(), address() |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
Both are same method with Alias |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
LABEL param and GEO param are now available |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
=cut |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
subtype 'v4Address' => as 'ArrayRef[Text::vCard::Precisely::V4::Node::Address]'; |
219
|
|
|
|
|
|
|
coerce 'v4Address', |
220
|
|
|
|
|
|
|
from 'HashRef', |
221
|
|
|
|
|
|
|
via { [ Text::vCard::Precisely::V4::Node::Address->new($_) ] }, from 'ArrayRef[HashRef]', via { |
222
|
|
|
|
|
|
|
[ map { Text::vCard::Precisely::V4::Node::Address->new($_) } @$_ ] |
223
|
|
|
|
|
|
|
}; |
224
|
|
|
|
|
|
|
has adr => ( is => 'rw', isa => 'v4Address', coerce => 1 ); |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=head2 email() |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
The format is SAME as 3.0 |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
=head2 url() |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
The format is SAME as 3.0 |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=head2 photo(), logo() |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
The format is SAME as 3.0 |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=cut |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
subtype 'v4Photos' => as 'ArrayRef[Text::vCard::Precisely::V4::Node::Image]'; |
241
|
|
|
|
|
|
|
coerce 'v4Photos', from 'HashRef', via { |
242
|
|
|
|
|
|
|
my $name = uc [ split( /::/, [ caller(2) ]->[3] ) ]->[-1]; |
243
|
|
|
|
|
|
|
return [ |
244
|
|
|
|
|
|
|
Text::vCard::Precisely::V4::Node::Image->new( |
245
|
|
|
|
|
|
|
{ name => $name, |
246
|
|
|
|
|
|
|
media_type => $_->{media_type} || $_->{type}, |
247
|
|
|
|
|
|
|
content => $_->{content}, |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
) |
250
|
|
|
|
|
|
|
] |
251
|
|
|
|
|
|
|
}, from 'ArrayRef[HashRef]', via { |
252
|
|
|
|
|
|
|
[ map { |
253
|
|
|
|
|
|
|
if ( ref $_->{types} eq 'ARRAY' ) { |
254
|
|
|
|
|
|
|
( $_->{media_type} ) = @{ $_->{types} }; |
255
|
|
|
|
|
|
|
delete $_->{types}; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
Text::vCard::Precisely::V4::Node::Image->new($_) |
258
|
|
|
|
|
|
|
} @$_ |
259
|
|
|
|
|
|
|
] |
260
|
|
|
|
|
|
|
}, from 'Str', # when parse BASE64 encoded strings |
261
|
|
|
|
|
|
|
via { |
262
|
|
|
|
|
|
|
my $name = uc [ split( /::/, [ caller(2) ]->[3] ) ]->[-1]; |
263
|
|
|
|
|
|
|
return [ |
264
|
|
|
|
|
|
|
Text::vCard::Precisely::V4::Node::Image->new( |
265
|
|
|
|
|
|
|
{ name => $name, |
266
|
|
|
|
|
|
|
content => $_, |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
) |
269
|
|
|
|
|
|
|
] |
270
|
|
|
|
|
|
|
}, from 'ArrayRef[Str]', # when parse BASE64 encoded strings |
271
|
|
|
|
|
|
|
via { |
272
|
|
|
|
|
|
|
my $name = uc [ split( /::/, [ caller(2) ]->[3] ) ]->[-1]; |
273
|
|
|
|
|
|
|
return [ |
274
|
|
|
|
|
|
|
map { Text::vCard::Precisely::V4::Node::Image->new( { name => $name, content => $_, } ) } |
275
|
|
|
|
|
|
|
@$_ ] |
276
|
|
|
|
|
|
|
}, from 'Object', # when URI.pm is used |
277
|
|
|
|
|
|
|
via { [ Text::vCard::Precisely::V4::Node::Image->new( { content => $_->as_string() } ) ] }; |
278
|
|
|
|
|
|
|
has [qw| photo logo |] => ( is => 'rw', isa => 'v4Photos', coerce => 1 ); |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
=head2 note() |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
The format is SAME as 3.0 |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
=head2 org(), title(), role(), categories() |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
The format is SAME as 3.0 |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=head2 fn(), full_name(), fullname() |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
They are same method at all with Alias |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
The format is SAME as 3.0 |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=head2 nickname() |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
The format is SAME as 3.0 |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=head2 lang() |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
To specify the language(s) that may be used for contacting the entity associated with the vCard |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
It's the B<new method from 4.0> |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=head2 impp(), xml() |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
I don't think they are so popular paramater, but here are the methods! |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
They are the B<new method from 4.0> |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=head2 geo(), key() |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
The format is SAME as 3.0 |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
=cut |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
subtype 'v4Node' => as 'ArrayRef[Text::vCard::Precisely::V4::Node]'; |
317
|
|
|
|
|
|
|
coerce 'v4Node', from 'Str', via { |
318
|
|
|
|
|
|
|
my $name = uc [ split( /::/, [ caller(2) ]->[3] ) ]->[-1]; |
319
|
|
|
|
|
|
|
return [ Text::vCard::Precisely::V4::Node->new( { name => $name, content => $_ } ) ] |
320
|
|
|
|
|
|
|
}, from 'HashRef', via { |
321
|
|
|
|
|
|
|
my $name = uc [ split( /::/, [ caller(2) ]->[3] ) ]->[-1]; |
322
|
|
|
|
|
|
|
return [ |
323
|
|
|
|
|
|
|
Text::vCard::Precisely::V4::Node->new( |
324
|
|
|
|
|
|
|
{ name => $_->{'name'} || $name, |
325
|
|
|
|
|
|
|
types => $_->{'types'} || [], |
326
|
|
|
|
|
|
|
sort_as => $_->{'sort_as'}, |
327
|
|
|
|
|
|
|
content => $_->{'content'} || croak "No value in HashRef!", |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
) |
330
|
|
|
|
|
|
|
] |
331
|
|
|
|
|
|
|
}, from 'ArrayRef[HashRef]', via { |
332
|
|
|
|
|
|
|
my $name = uc [ split( /::/, [ caller(2) ]->[3] ) ]->[-1]; |
333
|
|
|
|
|
|
|
return [ |
334
|
|
|
|
|
|
|
map { |
335
|
|
|
|
|
|
|
Text::vCard::Precisely::V4::Node->new( |
336
|
|
|
|
|
|
|
{ name => $_->{'name'} || $name, |
337
|
|
|
|
|
|
|
types => $_->{'types'} || [], |
338
|
|
|
|
|
|
|
sort_as => $_->{'sort_as'}, |
339
|
|
|
|
|
|
|
content => $_->{'content'} || croak "No value in HashRef!", |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
) |
342
|
|
|
|
|
|
|
} @$_ |
343
|
|
|
|
|
|
|
] |
344
|
|
|
|
|
|
|
}; |
345
|
|
|
|
|
|
|
has [qw|note org title role categories fn nickname lang impp xml geo key|] => |
346
|
|
|
|
|
|
|
( is => 'rw', isa => 'v4Node', coerce => 1 ); |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=head2 source(), sound() |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
The formats are SAME as 3.0 |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=head2 fburl(), caladruri(), caluri() |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
I don't think they are so popular types, but here are the methods! |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
They are the B<new method from 4.0> |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=cut |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
has [qw|source sound fburl caladruri caluri|] => ( is => 'rw', isa => 'URLs', coerce => 1 ); |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
subtype 'Related' => as 'ArrayRef[Text::vCard::Precisely::V4::Node::Related]'; |
363
|
|
|
|
|
|
|
coerce 'Related', |
364
|
|
|
|
|
|
|
from 'HashRef', |
365
|
|
|
|
|
|
|
via { [ Text::vCard::Precisely::V4::Node::Related->new($_) ] }, from 'ArrayRef[HashRef]', via { |
366
|
|
|
|
|
|
|
[ map { Text::vCard::Precisely::V4::Node::Related->new($_) } @$_ ] |
367
|
|
|
|
|
|
|
}; |
368
|
|
|
|
|
|
|
has related => ( is => 'rw', isa => 'Related', coerce => 1 ); |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=head2 kind() |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
To specify the kind of object the vCard represents |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
It's the B<new method from 4.0> |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=cut |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
subtype 'KIND' => as 'Str' => |
379
|
|
|
|
|
|
|
where {m/^(?:individual|group|org|location|[a-z0-9\-]+|X-[a-z0-9\-]+)$/s} |
380
|
|
|
|
|
|
|
=> message {"The KIND you provided, $_, was not supported"}; |
381
|
|
|
|
|
|
|
has kind => ( is => 'rw', isa => 'KIND' ); |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
subtype 'v4TimeStamp' => as 'Str' => where {m/^\d{8}T\d{6}(?:Z(?:-\d{2}(?:\d{2})?)?)?$/is} |
384
|
|
|
|
|
|
|
=> message {"The TimeStamp you provided, $_, was not correct"}; |
385
|
|
|
|
|
|
|
coerce 'v4TimeStamp', from 'Str', via { |
386
|
|
|
|
|
|
|
m/^(\d{4})-?(\d{2})-?(\d{2})(?:T(\d{2}):?(\d{2}):?(\d{2})Z)?$/is; |
387
|
|
|
|
|
|
|
return sprintf '%4d%02d%02dT%02d%02d%02dZ', $1, $2, $3, $4, $5, $6 |
388
|
|
|
|
|
|
|
}, from 'Int', via { |
389
|
|
|
|
|
|
|
my ( $s, $m, $h, $d, $M, $y ) = gmtime($_); |
390
|
|
|
|
|
|
|
return sprintf '%4d%02d%02dT%02d%02d%02dZ', $y + 1900, $M + 1, $d, $h, $m, $s |
391
|
|
|
|
|
|
|
}, from 'ArrayRef[HashRef]', via { $_->[0]{content} }; |
392
|
|
|
|
|
|
|
has rev => ( is => 'rw', isa => 'v4TimeStamp', coerce => 1 ); |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=head2 member(), clientpidmap() |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
I don't think they are so popular types, but here are the methods! |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
It's the B<new method from 4.0> |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
=cut |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
subtype 'MEMBER' => as 'ArrayRef[Text::vCard::Precisely::V4::Node::Member]'; |
403
|
|
|
|
|
|
|
coerce 'MEMBER', |
404
|
|
|
|
|
|
|
from 'UID', |
405
|
|
|
|
|
|
|
via { [ Text::vCard::Precisely::V4::Node::Member->new($_) ] }, from 'ArrayRef[UID]', via { |
406
|
|
|
|
|
|
|
[ map { Text::vCard::Precisely::V4::Node::Member->new( { content => $_ } ) } @$_ ] |
407
|
|
|
|
|
|
|
}; |
408
|
|
|
|
|
|
|
has member => ( is => 'rw', isa => 'MEMBER', coerce => 1 ); |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
subtype 'CLIENTPIDMAP' => as 'Str' => |
411
|
|
|
|
|
|
|
where {m/^\d+;urn:uuid:[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}$/is} |
412
|
|
|
|
|
|
|
=> message {"The CLIENTPIDMAP you provided, $_, was not correct"}; |
413
|
|
|
|
|
|
|
subtype 'CLIENTPIDMAPs' => as 'ArrayRef[CLIENTPIDMAP]'; |
414
|
|
|
|
|
|
|
coerce 'CLIENTPIDMAPs', from 'Str', via { [$_] }; |
415
|
|
|
|
|
|
|
has clientpidmap => ( is => 'rw', isa => 'CLIENTPIDMAPs', coerce => 1 ); |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
=head2 tz(), timezone() |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
Both are same method with Alias |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
The format is SAME as 3.0 |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=head2 bday(), birthday() |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
Both are same method with Alias |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
The format is SAME as 3.0 |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
=head2 anniversary() |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
The date of marriage, or equivalent, of the object the vCard represents |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
It's the B<new method from 4.0> |
434
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
=head2 gender() |
436
|
|
|
|
|
|
|
|
437
|
|
|
|
|
|
|
To specify the components of the sex and gender identity of the object the vCard represents |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
It's the B<new method from 4.0> |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=head2 prodid() |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
The format is SAME as 3.0 |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
=cut |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
has [qw|bday anniversary gender prodid|] => ( is => 'rw', isa => 'Str' ); |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
__PACKAGE__->meta->make_immutable; |
450
|
14
|
|
|
14
|
|
166
|
no Moose; |
|
14
|
|
|
|
|
68
|
|
|
14
|
|
|
|
|
133
|
|
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
=head1 DEPRECATED Methods |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
B<They're DEPRECATED in 4.0> |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
=head2 sort_string() |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
Use C<SORT-AS> param instead of it |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
=cut |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
sub sort_string { |
463
|
2
|
|
|
2
|
1
|
1217
|
my $self = shift; |
464
|
2
|
|
|
|
|
351
|
croak "'SORT-STRING' type is DEPRECATED! Use 'SORT-AS' param instead of it."; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=head2 label() |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Use C<LABEL> param in C<ADR> instead of it |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=cut |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
sub label { |
474
|
1
|
|
|
1
|
1
|
506
|
my $self = shift; |
475
|
1
|
|
|
|
|
97
|
croak "'LABEL' Type is DEPRECATED in vCard4.0!"; |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
=head2 class(), name(), profile(), mailer() |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
There is no method for these, just warn if you use them |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=cut |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
sub class { |
485
|
1
|
|
|
1
|
1
|
444
|
my $self = shift; |
486
|
1
|
|
|
|
|
96
|
croak "'CLASS' Type is DEPRECATED from vCard4.0!"; |
487
|
|
|
|
|
|
|
} |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
sub name { |
490
|
1
|
|
|
1
|
1
|
12
|
my $self = shift; |
491
|
1
|
|
|
|
|
282
|
croak "'NAME' Type is DEPRECATED from vCard4.0!"; |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
sub profile { |
495
|
1
|
|
|
1
|
1
|
896
|
my $self = shift; |
496
|
1
|
|
|
|
|
101
|
croak "'PROFILE' Type is DEPRECATED from vCard4.0!"; |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
sub mailer { |
500
|
1
|
|
|
1
|
1
|
424
|
my $self = shift; |
501
|
1
|
|
|
|
|
96
|
croak "'MAILER' Type is DEPRECATED from vCard4.0!"; |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=head2 agent() |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
Use C<AGENT> param in C<RELATED> instead of it |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
=cut |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
sub agent { |
511
|
1
|
|
|
1
|
1
|
461
|
my $self = shift; |
512
|
1
|
|
|
|
|
96
|
croak "'AGENT' Type is DEPRECATED from vCard4.0! Use AGENT param in RELATED instead of it"; |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
1; |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
=head1 aroud UTF-8 |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
If you want to send precisely the vCard with UTF-8 characters to |
520
|
|
|
|
|
|
|
the B<ALMOST> of smartphones, Use 3.0 |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
It seems to be TOO EARLY to use 4.0 |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=head1 for under perl-5.12.5 |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
This module uses C<\P{ascii}> in regexp so You have to use 5.12.5 and later |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
=head1 SEE ALSO |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=over |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
=item |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
L<RFC 6350|https://tools.ietf.org/html/rfc6350> |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=item |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
L<Text::vCard::Precisely::V3> |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=item |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
L<vCard on Wikipedia|https://en.wikipedia.org/wiki/VCard> |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
=back |
545
|
|
|
|
|
|
|
|
546
|
|
|
|
|
|
|
=head1 AUTHOR |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
Yuki Yoshida(L<worthmine|https://github.com/worthmine>) |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=head1 LICENSE |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under the same terms as Perl. |