| 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. |