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 {
23 static function fromXRD(XML_XRD $xrd)
27 foreach ($xrd->links as $link) {
29 case WebFingerResource_Profile::PROFILEPAGE:
30 $hints['profileurl'] = $link->href;
32 case Salmon::REL_SALMON:
33 case Salmon::NS_MENTIONS: // XXX: deprecated, remove in the future
34 case Salmon::NS_REPLIES: // XXX: deprecated, remove in the future
35 $hints['salmon'] = $link->href;
37 case Discovery::UPDATESFROM:
38 if (empty($link->type) || $link->type == 'application/atom+xml') {
39 $hints['feedurl'] = $link->href;
42 case Discovery::HCARD:
43 $hints['hcardurl'] = $link->href;
53 static function fromHcardUrl($url)
55 $client = new HTTPClient();
56 $client->setHeader('Accept', 'text/html,application/xhtml+xml');
57 $response = $client->get($url);
59 if (!$response->isOk()) {
63 return self::hcardHints($response->getBody(),
67 static function hcardHints($body, $url)
69 $hcard = self::_hcard($body, $url);
77 // XXX: don't copy stuff into an array and then copy it again
79 if (array_key_exists('nickname', $hcard)) {
80 $hints['nickname'] = $hcard['nickname'];
83 if (array_key_exists('fn', $hcard)) {
84 $hints['fullname'] = $hcard['fn'];
85 } else if (array_key_exists('n', $hcard)) {
86 $hints['fullname'] = implode(' ', $hcard['n']);
89 if (array_key_exists('photo', $hcard) && count($hcard['photo'])) {
90 $hints['avatar'] = $hcard['photo'][0];
93 if (array_key_exists('note', $hcard)) {
94 $hints['bio'] = $hcard['note'];
97 if (array_key_exists('adr', $hcard)) {
98 if (is_string($hcard['adr'])) {
99 $hints['location'] = $hcard['adr'];
100 } else if (is_array($hcard['adr'])) {
101 $hints['location'] = implode(' ', $hcard['adr']);
105 if (array_key_exists('url', $hcard)) {
106 if (is_string($hcard['url'])) {
107 $hints['homepage'] = $hcard['url'];
108 } else if (is_array($hcard['url']) && !empty($hcard['url'])) {
109 // HACK get the last one; that's how our hcards look
110 $hints['homepage'] = $hcard['url'][count($hcard['url'])-1];
117 static function _hcard($body, $url)
119 // DOMDocument::loadHTML may throw warnings on unrecognized elements,
120 // and notices on unrecognized namespaces.
122 $old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE));
124 $doc = new DOMDocument();
125 $doc->loadHTML($body);
127 error_reporting($old);
129 $xp = new DOMXPath($doc);
131 $hcardNodes = self::_getChildrenByClass($doc->documentElement, 'vcard', $xp);
135 for ($i = 0; $i < $hcardNodes->length; $i++) {
137 $hcardNode = $hcardNodes->item($i);
139 $hcard = self::_hcardFromNode($hcardNode, $xp, $url);
146 foreach ($hcards as $hcard) {
147 if (in_array($url, $hcard['url'])) {
153 if (!is_null($repr)) {
155 } else if (count($hcards) > 0) {
162 function _getChildrenByClass($el, $cls, $xp)
164 // borrowed from hkit. Thanks dudes!
166 $qry = ".//*[contains(concat(' ',normalize-space(@class),' '),' $cls ')]";
168 $nodes = $xp->query($qry, $el);
173 function _hcardFromNode($hcardNode, $xp, $base)
177 $hcard['url'] = array();
179 $urlNodes = self::_getChildrenByClass($hcardNode, 'url', $xp);
181 for ($j = 0; $j < $urlNodes->length; $j++) {
183 $urlNode = $urlNodes->item($j);
185 if ($urlNode->hasAttribute('href')) {
186 $url = $urlNode->getAttribute('href');
188 $url = $urlNode->textContent;
191 $hcard['url'][] = self::_rel2abs($url, $base);
194 $hcard['photo'] = array();
196 $photoNodes = self::_getChildrenByClass($hcardNode, 'photo', $xp);
198 for ($j = 0; $j < $photoNodes->length; $j++) {
199 $photoNode = $photoNodes->item($j);
200 if ($photoNode->hasAttribute('src')) {
201 $url = $photoNode->getAttribute('src');
202 } else if ($photoNode->hasAttribute('href')) {
203 $url = $photoNode->getAttribute('href');
205 $url = $photoNode->textContent;
207 $hcard['photo'][] = self::_rel2abs($url, $base);
210 $singles = array('nickname', 'note', 'fn', 'n', 'adr');
212 foreach ($singles as $single) {
214 $nodes = self::_getChildrenByClass($hcardNode, $single, $xp);
216 if ($nodes->length > 0) {
217 $node = $nodes->item(0);
218 $hcard[$single] = $node->textContent;
225 // XXX: this is a first pass; we probably need
226 // to handle things like ../ and ./ and so on
228 static function _rel2abs($rel, $wrt)
230 $parts = parse_url($rel);
232 if ($parts === false) {
236 // If it's got a scheme, use it
238 if (!empty($parts['scheme'])) {
242 $w = parse_url($wrt);
244 $base = $w['scheme'].'://'.$w['host'];
246 if ($rel[0] == '/') {
250 $wp = explode('/', $w['path']);
254 return $base.implode('/', $wp).'/'.$rel;