line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Web::Mention::Author; |
2
|
|
|
|
|
|
|
|
3
|
8
|
|
|
8
|
|
1400
|
use Moo; |
|
8
|
|
|
|
|
11466
|
|
|
8
|
|
|
|
|
62
|
|
4
|
8
|
|
|
8
|
|
4574
|
use MooX::ClassAttribute; |
|
8
|
|
|
|
|
17514
|
|
|
8
|
|
|
|
|
76
|
|
5
|
8
|
|
|
8
|
|
1741
|
use Types::Standard qw(InstanceOf Str Maybe); |
|
8
|
|
|
|
|
71098
|
|
|
8
|
|
|
|
|
78
|
|
6
|
8
|
|
|
8
|
|
7518
|
use Try::Tiny; |
|
8
|
|
|
|
|
1274
|
|
|
8
|
|
|
|
|
532
|
|
7
|
8
|
|
|
8
|
|
781
|
use LWP::UserAgent; |
|
8
|
|
|
|
|
41692
|
|
|
8
|
|
|
|
|
318
|
|
8
|
8
|
|
|
8
|
|
61
|
use List::Util qw(first); |
|
8
|
|
|
|
|
17
|
|
|
8
|
|
|
|
|
609
|
|
9
|
8
|
|
|
8
|
|
59
|
use Scalar::Util qw(blessed); |
|
8
|
|
|
|
|
19
|
|
|
8
|
|
|
|
|
425
|
|
10
|
|
|
|
|
|
|
|
11
|
8
|
|
|
8
|
|
635
|
use Web::Microformats2::Parser; |
|
8
|
|
|
|
|
771844
|
|
|
8
|
|
|
|
|
7072
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
has 'name' => ( |
14
|
|
|
|
|
|
|
is => 'ro', |
15
|
|
|
|
|
|
|
isa => Maybe[Str], |
16
|
|
|
|
|
|
|
); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
has 'url' => ( |
19
|
|
|
|
|
|
|
is => 'ro', |
20
|
|
|
|
|
|
|
isa => Maybe[InstanceOf['URI']], |
21
|
|
|
|
|
|
|
coerce => sub { URI->new($_[0]) }, |
22
|
|
|
|
|
|
|
); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
has 'photo' => ( |
25
|
|
|
|
|
|
|
is => 'ro', |
26
|
|
|
|
|
|
|
isa => Maybe[InstanceOf['URI']], |
27
|
|
|
|
|
|
|
coerce => sub { URI->new($_[0]) }, |
28
|
|
|
|
|
|
|
); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
class_has 'parser' => ( |
31
|
|
|
|
|
|
|
is => 'ro', |
32
|
|
|
|
|
|
|
isa => InstanceOf['Web::Microformats2::Parser'], |
33
|
|
|
|
|
|
|
default => sub { Web::Microformats2::Parser->new }, |
34
|
|
|
|
|
|
|
); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub new_from_mf2_document { |
37
|
12
|
|
|
12
|
0
|
67
|
my $class = shift; |
38
|
12
|
|
|
|
|
39
|
my ($doc) = @_; |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
# This method implements the Indieweb Authorship Algorithm. |
41
|
|
|
|
|
|
|
# https://indieweb.org/authorship#How_to_determine |
42
|
|
|
|
|
|
|
# The quoted comments below are direct quotes from that page |
43
|
|
|
|
|
|
|
# (as of spring 2018). |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
# "Start with a particular h-entry to determine authorship for, |
46
|
|
|
|
|
|
|
# and no author." |
47
|
|
|
|
|
|
|
|
48
|
12
|
|
|
|
|
26
|
my $author; |
49
|
|
|
|
|
|
|
my $author_page; |
50
|
|
|
|
|
|
|
|
51
|
12
|
|
|
|
|
67
|
my $h_entry = $doc->get_first( 'h-entry' ); |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# "If no h-entry, then there's no post to find authorship for, abort." |
54
|
12
|
100
|
|
|
|
2278
|
unless ( $h_entry ) { |
55
|
4
|
|
|
|
|
77
|
return $class->new; |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# "If the h-entry has an author property, use that." |
59
|
8
|
|
|
|
|
33
|
$author = $h_entry->get_property( 'author' ); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# "Otherwise if the h-entry has a parent h-feed with author property, |
62
|
|
|
|
|
|
|
# use that." |
63
|
8
|
100
|
100
|
|
|
215
|
if ( |
|
|
|
66
|
|
|
|
|
64
|
|
|
|
|
|
|
not ( $author ) |
65
|
|
|
|
|
|
|
&& $h_entry->parent |
66
|
|
|
|
|
|
|
&& ( $h_entry->parent->has_type ('h-feed') ) |
67
|
|
|
|
|
|
|
) { |
68
|
2
|
|
|
|
|
149
|
$author = $h_entry->parent->get_property( 'author' ); |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# "If an author property was found:" |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# "If it has an h-card, use it, exit." |
74
|
8
|
100
|
100
|
|
|
94
|
if ( |
|
|
|
66
|
|
|
|
|
75
|
|
|
|
|
|
|
defined $author |
76
|
|
|
|
|
|
|
&& blessed( $author ) |
77
|
|
|
|
|
|
|
&& ( $author->has_type( 'h-card' ) ) |
78
|
|
|
|
|
|
|
) { |
79
|
3
|
|
|
|
|
248
|
return $class->_new_with_h_card( $author ); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# "Otherwise if author property is an http(s) URL, |
83
|
|
|
|
|
|
|
# let the author-page have that URL." |
84
|
5
|
100
|
|
|
|
16
|
if ( defined $author ) { |
85
|
|
|
|
|
|
|
try { |
86
|
2
|
|
|
2
|
|
88
|
$author_page = URI->new( $author ); |
87
|
2
|
0
|
|
|
|
182
|
unless ( $author_page->schema =~ /^http/ ) { |
88
|
0
|
|
|
|
|
0
|
undef $author_page; |
89
|
|
|
|
|
|
|
} |
90
|
2
|
|
|
|
|
58
|
}; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# "Otherwise use the author property as the author name, exit." |
94
|
5
|
50
|
66
|
|
|
59
|
if ( $author and !$author_page ) { |
95
|
0
|
|
|
|
|
0
|
return $class->new( name => $author ); |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# "If there is an author-page URL:" |
99
|
5
|
100
|
|
|
|
27
|
if ( $author_page ) { |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# "Get the author-page from that URL and parse it for Microformats-2." |
102
|
2
|
|
|
|
|
32
|
my $ua = LWP::UserAgent->new; |
103
|
2
|
|
|
|
|
3600
|
my $response = $ua->get( $author_page ); |
104
|
2
|
|
|
|
|
433663
|
my $author_doc = $class->parser->parse( $response ); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# "If author-page has 1+ h-card with url == uid == author-page's URL, |
107
|
|
|
|
|
|
|
# then use first such h-card, exit." |
108
|
2
|
|
|
|
|
3705
|
my @h_cards = grep{ $_->has_type( 'h-card' ) } $doc->all_items; |
|
5
|
|
|
|
|
598
|
|
109
|
2
|
|
|
|
|
328
|
for my $h_card ( @h_cards ) { |
110
|
0
|
|
|
|
|
0
|
my $urls_ref = $h_card->get_properties( 'url' ); |
111
|
0
|
|
|
|
|
0
|
my $uids_ref = $h_card->get_properties( 'uid' ); |
112
|
0
|
0
|
0
|
|
|
0
|
if ( |
113
|
0
|
|
|
0
|
|
0
|
first { $_ eq $author_page->as_string } @$urls_ref |
114
|
0
|
|
|
0
|
|
0
|
&& first { $_ eq $author_page->as_string } @$uids_ref |
115
|
|
|
|
|
|
|
) { |
116
|
0
|
|
|
|
|
0
|
return $class->_new_with_h_card( $h_card ); |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# XXX Skipping the "rel-me"-based test. |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# "if the h-entry's page has 1+ h-card with url == author-page URL, |
123
|
|
|
|
|
|
|
# use first such h-card, exit." |
124
|
2
|
|
|
|
|
96
|
for my $h_card ( @h_cards ) { |
125
|
0
|
|
|
|
|
0
|
my $urls_ref = $h_card->get_properties( 'url' ); |
126
|
0
|
0
|
|
|
|
0
|
if ( |
127
|
0
|
|
|
0
|
|
0
|
first { $_ eq $author_page->as_string } @$urls_ref |
128
|
|
|
|
|
|
|
) { |
129
|
0
|
|
|
|
|
0
|
return $class->_new_with_h_card( $h_card ); |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
5
|
|
|
|
|
98
|
return $class->new; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub new_from_html { |
140
|
8
|
|
|
8
|
0
|
18182
|
my $class = shift; |
141
|
8
|
|
|
|
|
27
|
my ($html) = @_; |
142
|
|
|
|
|
|
|
|
143
|
8
|
|
|
|
|
244
|
my $doc = $class->parser->parse( $html ); |
144
|
|
|
|
|
|
|
|
145
|
8
|
|
|
|
|
127813
|
return $class->new_from_mf2_document( $doc ); |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub _new_with_h_card { |
150
|
3
|
|
|
3
|
|
12
|
my ( $class, $h_card ) = @_; |
151
|
|
|
|
|
|
|
|
152
|
3
|
|
|
|
|
9
|
my %constructor_args; |
153
|
|
|
|
|
|
|
|
154
|
3
|
|
|
|
|
9
|
foreach ( qw (name url photo ) ) { |
155
|
9
|
|
|
|
|
23
|
my $value = $h_card->get_properties( $_ ); |
156
|
9
|
50
|
33
|
|
|
82
|
if ( defined $value && defined $value->[0] ) { |
157
|
9
|
|
|
|
|
26
|
$constructor_args{ $_ } = $value->[0]; |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
3
|
|
|
|
|
51
|
return $class->new( %constructor_args ); |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
1; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=pod |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=head1 NAME |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
Web::Mention::Author - The author of a webmention's source document |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=head1 DESCRIPTION |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
An object of this class represents the author of a webmention -- or, |
175
|
|
|
|
|
|
|
more specifically, the author of the document that a given webmention |
176
|
|
|
|
|
|
|
points to as its source. |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
It implements the IndieWeb I<authorship protocol>, as defined here: |
179
|
|
|
|
|
|
|
L<https://indieweb.org/authorship#How_to_determine> |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
It is not expected that you'll build objects of this class yourself. |
182
|
|
|
|
|
|
|
Rather, you'll receive and query them by way of the C<author()> method |
183
|
|
|
|
|
|
|
of Web::Mention objects. |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=head1 METHODS |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 Object Methods |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
=head3 name |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
$name = $author->name; |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
Returns the author's name. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=head3 url |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
$author_url = $author->url; |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
Returns the author's URL as a L<URI> object, or undef. |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=head3 photo |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
$photo_url = $author->photo; |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Returns the author's photo (avatar) as a L<URI> object, or undef. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=head1 NOTES AND BUGS |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
This software is B<alpha>; its author is still determining how it wants |
210
|
|
|
|
|
|
|
to work, and its interface might change dramatically. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
(Honestly, the Web::Mention namespace might not even be the best place |
213
|
|
|
|
|
|
|
for it!) |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
Its implementation of the authorship algorithm is I<very> incomplete. |
216
|
|
|
|
|
|
|
The author only got as far as being able to parse typical output from |
217
|
|
|
|
|
|
|
L<http://brid.gy> and then stopped. Tsk tsk. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head1 AUTHOR |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Jason McIntosh (jmac@jmac.org) |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
This software is Copyright (c) 2018 by Jason McIntosh. |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
This is free software, licensed under: |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
The MIT (X11) License |