3 * StatusNet - the distributed open-source microblogging tool
4 * Copyright (C) 2010, StatusNet, Inc.
6 * Some utilities for generating hint data
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 class DiscoveryHints {
24 static function fromXRD($xrd)
28 foreach ($xrd->links as $link) {
29 switch ($link['rel']) {
30 case Discovery::PROFILEPAGE:
31 $hints['profileurl'] = $link['href'];
33 case Salmon::NS_REPLIES:
34 $hints['salmon'] = $link['href'];
36 case Discovery::UPDATESFROM:
37 $hints['feedurl'] = $link['href'];
39 case Discovery::HCARD:
40 $hints['hcardurl'] = $link['href'];
50 static function fromHcardUrl($url)
52 $client = new HTTPClient();
53 $client->setHeader('Accept', 'text/html,application/xhtml+xml');
54 $response = $client->get($url);
56 if (!$response->isOk()) {
60 return self::hcardHints($response->getBody(),
64 static function hcardHints($body, $url)
66 common_debug("starting tidy");
68 $body = self::_tidy($body, $url);
70 common_debug("done with tidy");
72 set_include_path(get_include_path() . PATH_SEPARATOR . INSTALLDIR . '/plugins/OStatus/extlib/hkit/');
73 require_once('hkit.class.php');
75 // hKit code is not clean for notices and warnings
76 $old = error_reporting();
77 error_reporting($old & ~E_NOTICE & ~E_WARNING);
80 $hcards = $h->getByString('hcard', $body);
82 error_reporting($old);
88 if (count($hcards) == 1) {
91 foreach ($hcards as $try) {
92 if (array_key_exists('url', $try)) {
93 if (is_string($try['url']) && $try['url'] == $url) {
96 } else if (is_array($try['url'])) {
97 foreach ($try['url'] as $tryurl) {
98 if ($tryurl == $url) {
106 // last chance; grab the first one
114 if (array_key_exists('nickname', $hcard)) {
115 $hints['nickname'] = $hcard['nickname'];
118 if (array_key_exists('fn', $hcard)) {
119 $hints['fullname'] = $hcard['fn'];
120 } else if (array_key_exists('n', $hcard)) {
121 $hints['fullname'] = implode(' ', $hcard['n']);
124 if (array_key_exists('photo', $hcard)) {
125 $hints['avatar'] = $hcard['photo'];
128 if (array_key_exists('note', $hcard)) {
129 $hints['bio'] = $hcard['note'];
132 if (array_key_exists('adr', $hcard)) {
133 if (is_string($hcard['adr'])) {
134 $hints['location'] = $hcard['adr'];
135 } else if (is_array($hcard['adr'])) {
136 $hints['location'] = implode(' ', $hcard['adr']);
140 if (array_key_exists('url', $hcard)) {
141 if (is_string($hcard['url'])) {
142 $hints['homepage'] = $hcard['url'];
143 } else if (is_array($hcard['url'])) {
144 // HACK get the last one; that's how our hcards look
145 $hints['homepage'] = $hcard['url'][count($hcard['url'])-1];
153 * hKit needs well-formed XML for its parsing.
154 * We'll take the HTML body here and normalize it to XML.
156 * @param string $body HTML document source, possibly not-well-formed
157 * @param string $url source URL
158 * @return string well-formed XML document source
159 * @throws Exception if HTML parsing failed.
161 private static function _tidy($body, $url)
164 throw new Exception("Empty HTML could not be parsed.");
166 $dom = new DOMDocument();
168 // Some HTML errors will trigger warnings, but still work.
169 $old = error_reporting();
170 error_reporting($old & ~E_WARNING);
172 $ok = $dom->loadHTML($body);
174 error_reporting($old);
177 // hKit doesn't give us a chance to pass the source URL for
178 // resolving relative links, such as the avatar photo on a
179 // Google profile. We'll slip it into a <base> tag if there's
180 // not already one present.
181 $bases = $dom->getElementsByTagName('base');
182 if ($bases && $bases->length >= 1) {
183 $base = $bases->item(0);
184 if ($base->hasAttribute('href')) {
185 $base->setAttribute('href', $url);
188 $base = $dom->createElement('base');
189 $base->setAttribute('href', $url);
190 $heads = $dom->getElementsByTagName('head');
191 if ($heads || $heads->length) {
192 $head = $heads->item(0);
194 $head = $dom->createElement('head');
195 $root = $dom->documentRoot;
196 if ($root->firstChild) {
197 $root->insertBefore($head, $root->firstChild);
199 $root->appendChild($head);
202 $head->appendChild($base);
204 return $dom->saveXML();
206 throw new Exception("Invalid HTML could not be parsed.");