| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Web::Mention::Author; |
|
2
|
|
|
|
|
|
|
|
|
3
|
8
|
|
|
8
|
|
1199
|
use Moo; |
|
|
8
|
|
|
|
|
9479
|
|
|
|
8
|
|
|
|
|
89
|
|
|
4
|
8
|
|
|
8
|
|
4528
|
use MooX::ClassAttribute; |
|
|
8
|
|
|
|
|
14709
|
|
|
|
8
|
|
|
|
|
72
|
|
|
5
|
8
|
|
|
8
|
|
1572
|
use Types::Standard qw(InstanceOf Str Maybe); |
|
|
8
|
|
|
|
|
58778
|
|
|
|
8
|
|
|
|
|
108
|
|
|
6
|
8
|
|
|
8
|
|
7592
|
use Try::Tiny; |
|
|
8
|
|
|
|
|
1000
|
|
|
|
8
|
|
|
|
|
553
|
|
|
7
|
8
|
|
|
8
|
|
683
|
use LWP::UserAgent; |
|
|
8
|
|
|
|
|
33400
|
|
|
|
8
|
|
|
|
|
354
|
|
|
8
|
8
|
|
|
8
|
|
60
|
use List::Util qw(first); |
|
|
8
|
|
|
|
|
15
|
|
|
|
8
|
|
|
|
|
597
|
|
|
9
|
8
|
|
|
8
|
|
55
|
use Scalar::Util qw(blessed); |
|
|
8
|
|
|
|
|
30
|
|
|
|
8
|
|
|
|
|
527
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
8
|
|
|
8
|
|
541
|
use Web::Microformats2::Parser; |
|
|
8
|
|
|
|
|
665939
|
|
|
|
8
|
|
|
|
|
7206
|
|
|
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
|
122
|
my $class = shift; |
|
38
|
12
|
|
|
|
|
29
|
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
|
|
|
|
|
43
|
my $author; |
|
49
|
|
|
|
|
|
|
my $author_page; |
|
50
|
|
|
|
|
|
|
|
|
51
|
12
|
|
|
|
|
69
|
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
|
|
|
|
2038
|
unless ( $h_entry ) { |
|
55
|
4
|
|
|
|
|
76
|
return $class->new; |
|
56
|
|
|
|
|
|
|
} |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# "If the h-entry has an author property, use that." |
|
59
|
8
|
|
|
|
|
28
|
$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
|
|
|
157
|
if ( |
|
|
|
|
66
|
|
|
|
|
|
64
|
|
|
|
|
|
|
not ( $author ) |
|
65
|
|
|
|
|
|
|
&& $h_entry->parent |
|
66
|
|
|
|
|
|
|
&& ( $h_entry->parent->has_type ('h-feed') ) |
|
67
|
|
|
|
|
|
|
) { |
|
68
|
2
|
|
|
|
|
121
|
$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
|
|
|
77
|
if ( |
|
|
|
|
66
|
|
|
|
|
|
75
|
|
|
|
|
|
|
defined $author |
|
76
|
|
|
|
|
|
|
&& blessed( $author ) |
|
77
|
|
|
|
|
|
|
&& ( $author->has_type( 'h-card' ) ) |
|
78
|
|
|
|
|
|
|
) { |
|
79
|
3
|
|
|
|
|
192
|
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
|
|
|
|
13
|
if ( defined $author ) { |
|
85
|
|
|
|
|
|
|
try { |
|
86
|
2
|
|
|
2
|
|
66
|
$author_page = URI->new( $author ); |
|
87
|
2
|
0
|
|
|
|
139
|
unless ( $author_page->schema =~ /^http/ ) { |
|
88
|
0
|
|
|
|
|
0
|
undef $author_page; |
|
89
|
|
|
|
|
|
|
} |
|
90
|
2
|
|
|
|
|
32
|
}; |
|
91
|
|
|
|
|
|
|
} |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# "Otherwise use the author property as the author name, exit." |
|
94
|
5
|
50
|
66
|
|
|
50
|
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
|
|
|
|
21
|
if ( $author_page ) { |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# "Get the author-page from that URL and parse it for Microformats-2." |
|
102
|
2
|
|
|
|
|
28
|
my $ua = LWP::UserAgent->new; |
|
103
|
2
|
|
|
|
|
2861
|
my $response = $ua->get( $author_page ); |
|
104
|
2
|
|
|
|
|
499540
|
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
|
|
|
|
|
2289
|
my @h_cards = grep{ $_->has_type( 'h-card' ) } $doc->all_items; |
|
|
5
|
|
|
|
|
349
|
|
|
109
|
2
|
|
|
|
|
157
|
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
|
|
|
|
|
68
|
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
|
|
|
|
|
82
|
return $class->new; |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
} |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
sub new_from_html { |
|
140
|
8
|
|
|
8
|
0
|
13556
|
my $class = shift; |
|
141
|
8
|
|
|
|
|
19
|
my ($html) = @_; |
|
142
|
|
|
|
|
|
|
|
|
143
|
8
|
|
|
|
|
166
|
my $doc = $class->parser->parse( $html ); |
|
144
|
|
|
|
|
|
|
|
|
145
|
8
|
|
|
|
|
102428
|
return $class->new_from_mf2_document( $doc ); |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub _new_with_h_card { |
|
150
|
3
|
|
|
3
|
|
10
|
my ( $class, $h_card ) = @_; |
|
151
|
|
|
|
|
|
|
|
|
152
|
3
|
|
|
|
|
7
|
my %constructor_args; |
|
153
|
|
|
|
|
|
|
|
|
154
|
3
|
|
|
|
|
8
|
foreach ( qw (name url photo ) ) { |
|
155
|
9
|
|
|
|
|
20
|
my $value = $h_card->get_properties( $_ ); |
|
156
|
9
|
50
|
33
|
|
|
69
|
if ( defined $value && defined $value->[0] ) { |
|
157
|
9
|
|
|
|
|
22
|
$constructor_args{ $_ } = $value->[0]; |
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
|
|
161
|
3
|
|
|
|
|
43
|
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 |