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