Merge branch 'testing' of gitorious.org:statusnet/mainline into testing

* 'testing' of gitorious.org:statusnet/mainline:
  init_conversation.php script to copy old notice conversations into the conversation table
  cache results of webfinger lookups
  Parse an hcard for hints, if available
  use new hcard method for webfinger
  add hkit for hCard parsing
  use new dedicated hcard method for Webfinger profile
  Add an hcard action
This commit is contained in:
Zach Copley 2010-02-25 18:13:34 -08:00
commit c49fbb63c5
8 changed files with 881 additions and 23 deletions

120
actions/hcard.php Normal file
View File

@ -0,0 +1,120 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show the user's hcard
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Personal
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* User profile page
*
* @category Personal
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
* @link http://status.net/
*/
class HcardAction extends Action
{
var $user;
var $profile;
function prepare($args)
{
parent::prepare($args);
$nickname_arg = $this->arg('nickname');
$nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname);
common_redirect(common_local_url('hcard', $args), 301);
return false;
}
$this->user = User::staticGet('nickname', $nickname);
if (!$this->user) {
$this->clientError(_('No such user.'), 404);
return false;
}
$this->profile = $this->user->getProfile();
if (!$this->profile) {
$this->serverError(_('User has no profile.'));
return false;
}
return true;
}
function handle($args)
{
parent::handle($args);
$this->showPage();
}
function title()
{
return $this->profile->getBestName();
}
function showContent()
{
$up = new ShortUserProfile($this, $this->user, $this->profile);
$up->show();
}
function showHeader()
{
return;
}
function showAside()
{
return;
}
function showSecondaryNav()
{
return;
}
}
class ShortUserProfile extends UserProfile
{
function showEntityActions()
{
return;
}
}

View File

@ -671,7 +671,7 @@ class Router
foreach (array('subscriptions', 'subscribers', foreach (array('subscriptions', 'subscribers',
'all', 'foaf', 'xrds', 'all', 'foaf', 'xrds',
'replies', 'microsummary') as $a) { 'replies', 'microsummary', 'hcard') as $a) {
$m->connect($a, $m->connect($a,
array('action' => $a, array('action' => $a,
'nickname' => $nickname)); 'nickname' => $nickname));
@ -737,7 +737,7 @@ class Router
foreach (array('subscriptions', 'subscribers', foreach (array('subscriptions', 'subscribers',
'nudge', 'all', 'foaf', 'xrds', 'nudge', 'all', 'foaf', 'xrds',
'replies', 'inbox', 'outbox', 'microsummary') as $a) { 'replies', 'inbox', 'outbox', 'microsummary', 'hcard') as $a) {
$m->connect(':nickname/'.$a, $m->connect(':nickname/'.$a,
array('action' => $a), array('action' => $a),
array('nickname' => '[a-zA-Z0-9]{1,64}')); array('nickname' => '[a-zA-Z0-9]{1,64}'));

View File

@ -66,9 +66,9 @@ class WebfingerAction extends Action
'type' => 'application/atom+xml'); 'type' => 'application/atom+xml');
// hCard // hCard
$xrd->links[] = array('rel' => 'http://microformats.org/profile/hcard', $xrd->links[] = array('rel' => Webfinger::HCARD,
'type' => 'text/html', 'type' => 'text/html',
'href' => common_profile_url($nick)); 'href' => common_local_url('hcard', array('nickname' => $nick)));
// XFN // XFN
$xrd->links[] = array('rel' => 'http://gmpg.org/xfn/11', $xrd->links[] = array('rel' => 'http://gmpg.org/xfn/11',

View File

@ -645,7 +645,6 @@ class Ostatus_profile extends Memcached_DataObject
'groups' => array(), 'groups' => array(),
'tags' => array()); 'tags' => array());
// Check for optional attributes... // Check for optional attributes...
if (!empty($activity->time)) { if (!empty($activity->time)) {
@ -1156,7 +1155,13 @@ class Ostatus_profile extends Memcached_DataObject
$orig = clone($profile); $orig = clone($profile);
$profile->nickname = self::getActivityObjectNickname($object, $hints); $profile->nickname = self::getActivityObjectNickname($object, $hints);
$profile->fullname = $object->title;
if (!empty($object->title)) {
$profile->fullname = $object->title;
} else if (array_key_exists('fullname', $hints)) {
$profile->fullname = $hints['fullname'];
}
if (!empty($object->link)) { if (!empty($object->link)) {
$profile->profileurl = $object->link; $profile->profileurl = $object->link;
} else if (array_key_exists('profileurl', $hints)) { } else if (array_key_exists('profileurl', $hints)) {
@ -1231,12 +1236,16 @@ class Ostatus_profile extends Memcached_DataObject
{ {
$location = null; $location = null;
if (!empty($object->poco)) { if (!empty($object->poco) &&
if (isset($object->poco->address->formatted)) { isset($object->poco->address->formatted)) {
$location = $object->poco->address->formatted; $location = $object->poco->address->formatted;
if (mb_strlen($location) > 255) { } else if (array_key_exists('location', $hints)) {
$location = mb_substr($note, 0, 255 - 3) . ' … '; $location = $hints['location'];
} }
if (!empty($location)) {
if (mb_strlen($location) > 255) {
$location = mb_substr($note, 0, 255 - 3) . ' … ';
} }
} }
@ -1251,13 +1260,16 @@ class Ostatus_profile extends Memcached_DataObject
if (!empty($object->poco)) { if (!empty($object->poco)) {
$note = $object->poco->note; $note = $object->poco->note;
if (!empty($note)) { } else if (array_key_exists('bio', $hints)) {
if (mb_strlen($note) > Profile::maxBio()) { $note = $hints['bio'];
// XXX: truncate ok? }
$bio = mb_substr($note, 0, Profile::maxBio() - 3) . ' … ';
} else { if (!empty($note)) {
$bio = $note; if (Profile::bioTooLong($note)) {
} // XXX: truncate ok?
$bio = mb_substr($note, 0, Profile::maxBio() - 3) . ' … ';
} else {
$bio = $note;
} }
} }
@ -1273,10 +1285,15 @@ class Ostatus_profile extends Memcached_DataObject
return common_nicknamize($object->poco->preferredUsername); return common_nicknamize($object->poco->preferredUsername);
} }
} }
if (!empty($object->nickname)) { if (!empty($object->nickname)) {
return common_nicknamize($object->nickname); return common_nicknamize($object->nickname);
} }
if (array_key_exists('nickname', $hints)) {
return $hints['nickname'];
}
// Try the definitive ID // Try the definitive ID
$nickname = self::nicknameFromURI($object->id); $nickname = self::nicknameFromURI($object->id);
@ -1321,11 +1338,26 @@ class Ostatus_profile extends Memcached_DataObject
public static function ensureWebfinger($addr) public static function ensureWebfinger($addr)
{ {
// First, try the cache
$uri = self::cacheGet(sprintf('ostatus_profile:webfinger:%s', $addr));
if ($uri !== false) {
if (is_null($uri)) {
return null;
}
$oprofile = Ostatus_profile::staticGet('uri', $uri);
if (!empty($oprofile)) {
return $oprofile;
}
}
// First, look it up // First, look it up
$oprofile = Ostatus_profile::staticGet('uri', 'acct:'.$addr); $oprofile = Ostatus_profile::staticGet('uri', 'acct:'.$addr);
if (!empty($oprofile)) { if (!empty($oprofile)) {
self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri);
return $oprofile; return $oprofile;
} }
@ -1336,6 +1368,7 @@ class Ostatus_profile extends Memcached_DataObject
$result = $wf->lookup($addr); $result = $wf->lookup($addr);
if (!$result) { if (!$result) {
self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), null);
return null; return null;
} }
@ -1350,6 +1383,9 @@ class Ostatus_profile extends Memcached_DataObject
case Webfinger::UPDATESFROM: case Webfinger::UPDATESFROM:
$feedUrl = $link['href']; $feedUrl = $link['href'];
break; break;
case Webfinger::HCARD:
$hcardUrl = $link['href'];
break;
default: default:
common_log(LOG_NOTICE, "Don't know what to do with rel = '{$link['rel']}'"); common_log(LOG_NOTICE, "Don't know what to do with rel = '{$link['rel']}'");
break; break;
@ -1361,11 +1397,18 @@ class Ostatus_profile extends Memcached_DataObject
'feedurl' => $feedUrl, 'feedurl' => $feedUrl,
'salmon' => $salmonEndpoint); 'salmon' => $salmonEndpoint);
if (isset($hcardUrl)) {
$hcardHints = self::slurpHcard($hcardUrl);
// Note: Webfinger > hcard
$hints = array_merge($hcardHints, $hints);
}
// If we got a feed URL, try that // If we got a feed URL, try that
if (isset($feedUrl)) { if (isset($feedUrl)) {
try { try {
$oprofile = self::ensureProfile($feedUrl, $hints); $oprofile = self::ensureProfile($feedUrl, $hints);
self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri);
return $oprofile; return $oprofile;
} catch (Exception $e) { } catch (Exception $e) {
common_log(LOG_WARNING, "Failed creating profile from feed URL '$feedUrl': " . $e->getMessage()); common_log(LOG_WARNING, "Failed creating profile from feed URL '$feedUrl': " . $e->getMessage());
@ -1378,6 +1421,7 @@ class Ostatus_profile extends Memcached_DataObject
if (isset($profileUrl)) { if (isset($profileUrl)) {
try { try {
$oprofile = self::ensureProfile($profileUrl, $hints); $oprofile = self::ensureProfile($profileUrl, $hints);
self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri);
return $oprofile; return $oprofile;
} catch (Exception $e) { } catch (Exception $e) {
common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage()); common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage());
@ -1429,6 +1473,7 @@ class Ostatus_profile extends Memcached_DataObject
throw new Exception("Couldn't save ostatus_profile for '$addr'"); throw new Exception("Couldn't save ostatus_profile for '$addr'");
} }
self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri);
return $oprofile; return $oprofile;
} }
@ -1467,4 +1512,67 @@ class Ostatus_profile extends Memcached_DataObject
return $file; return $file;
} }
protected static function slurpHcard($url)
{
set_include_path(get_include_path() . PATH_SEPARATOR . INSTALLDIR . '/plugins/OStatus/extlib/hkit/');
require_once('hkit.class.php');
$h = new hKit;
// Google Buzz hcards need to be tidied. Probably others too.
$h->tidy_mode = 'proxy'; // 'proxy', 'exec', 'php' or 'none'
// Get by URL
$hcards = $h->getByURL('hcard', $url);
if (empty($hcards)) {
return array();
}
// @fixme more intelligent guess on multi-hcard pages
$hcard = $hcards[0];
$hints = array();
$hints['profileurl'] = $url;
if (array_key_exists('nickname', $hcard)) {
$hints['nickname'] = $hcard['nickname'];
}
if (array_key_exists('fn', $hcard)) {
$hints['fullname'] = $hcard['fn'];
} else if (array_key_exists('n', $hcard)) {
$hints['fullname'] = implode(' ', $hcard['n']);
}
if (array_key_exists('photo', $hcard)) {
$hints['avatar'] = $hcard['photo'];
}
if (array_key_exists('note', $hcard)) {
$hints['bio'] = $hcard['note'];
}
if (array_key_exists('adr', $hcard)) {
if (is_string($hcard['adr'])) {
$hints['location'] = $hcard['adr'];
} else if (is_array($hcard['adr'])) {
$hints['location'] = implode(' ', $hcard['adr']);
}
}
if (array_key_exists('url', $hcard)) {
if (is_string($hcard['url'])) {
$hints['homepage'] = $hcard['url'];
} else if (is_array($hcard['adr'])) {
// HACK get the last one; that's how our hcards look
$hints['homepage'] = $hcard['url'][count($hcard['url'])-1];
}
}
return $hints;
}
} }

View File

@ -0,0 +1,105 @@
<?php
// hcard profile for hkit
$this->root_class = 'vcard';
$this->classes = array(
'fn', array('honorific-prefix', 'given-name', 'additional-name', 'family-name', 'honorific-suffix'),
'n', array('honorific-prefix', 'given-name', 'additional-name', 'family-name', 'honorific-suffix'),
'adr', array('post-office-box', 'extended-address', 'street-address', 'postal-code', 'country-name', 'type', 'region', 'locality'),
'label', 'bday', 'agent', 'nickname', 'photo', 'class',
'email', array('type', 'value'),
'category', 'key', 'logo', 'mailer', 'note',
'org', array('organization-name', 'organization-unit'),
'tel', array('type', 'value'),
'geo', array('latitude', 'longitude'),
'tz', 'uid', 'url', 'rev', 'role', 'sort-string', 'sound', 'title'
);
// classes that must only appear once per card
$this->singles = array(
'fn'
);
// classes that are required (not strictly enforced - give at least one!)
$this->required = array(
'fn'
);
$this->att_map = array(
'fn' => array('IMG|alt'),
'url' => array('A|href', 'IMG|src', 'AREA|href'),
'photo' => array('IMG|src'),
'bday' => array('ABBR|title'),
'logo' => array('IMG|src'),
'email' => array('A|href'),
'geo' => array('ABBR|title')
);
$this->callbacks = array(
'url' => array($this, 'resolvePath'),
'photo' => array($this, 'resolvePath'),
'logo' => array($this, 'resolvePath'),
'email' => array($this, 'resolveEmail')
);
function hKit_hcard_post($a)
{
foreach ($a as &$vcard){
hKit_implied_n_optimization($vcard);
hKit_implied_n_from_fn($vcard);
}
return $a;
}
function hKit_implied_n_optimization(&$vcard)
{
if (array_key_exists('fn', $vcard) && !is_array($vcard['fn']) &&
!array_key_exists('n', $vcard) && (!array_key_exists('org', $vcard) || $vcard['fn'] != $vcard['org'])){
if (sizeof(explode(' ', $vcard['fn'])) == 2){
$patterns = array();
$patterns[] = array('/^(\S+),\s*(\S{1})$/', 2, 1); // Lastname, Initial
$patterns[] = array('/^(\S+)\s*(\S{1})\.*$/', 2, 1); // Lastname Initial(.)
$patterns[] = array('/^(\S+),\s*(\S+)$/', 2, 1); // Lastname, Firstname
$patterns[] = array('/^(\S+)\s*(\S+)$/', 1, 2); // Firstname Lastname
foreach ($patterns as $pattern){
if (preg_match($pattern[0], $vcard['fn'], $matches) === 1){
$n = array();
$n['given-name'] = $matches[$pattern[1]];
$n['family-name'] = $matches[$pattern[2]];
$vcard['n'] = $n;
break;
}
}
}
}
}
function hKit_implied_n_from_fn(&$vcard)
{
if (array_key_exists('fn', $vcard) && is_array($vcard['fn'])
&& !array_key_exists('n', $vcard) && (!array_key_exists('org', $vcard) || $vcard['fn'] != $vcard['org'])){
$vcard['n'] = $vcard['fn'];
}
if (array_key_exists('fn', $vcard) && is_array($vcard['fn'])){
$vcard['fn'] = $vcard['fn']['text'];
}
}
?>

View File

@ -0,0 +1,475 @@
<?php
/*
hKit Library for PHP5 - a generic library for parsing Microformats
Copyright (C) 2006 Drew McLellan
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Author
Drew McLellan - http://allinthehead.com/
Contributors:
Scott Reynen - http://www.randomchaos.com/
Version 0.5, 22-Jul-2006
fixed by-ref issue cropping up in PHP 5.0.5
fixed a bug with a@title
added support for new fn=n optimisation
added support for new a.include include-pattern
Version 0.4, 23-Jun-2006
prevented nested includes from causing infinite loops
returns false if URL can't be fetched
added pre-flight check for base support level
added deduping of once-only classnames
prevented accumulation of multiple 'value' values
tuned whitespace handling and treatment of DEL elements
Version 0.3, 21-Jun-2006
added post-processor callback method into profiles
fixed minor problems raised by hcard testsuite
added support for include-pattern
added support for td@headers pattern
added implied-n optimization into default hcard profile
Version 0.2, 20-Jun-2006
added class callback mechanism
added resolvePath & resolveEmail
added basic BASE support
Version 0.1.1, 19-Jun-2006 (different timezone, no time machine)
added external Tidy option
Version 0.1, 20-Jun-2006
initial release
*/
class hKit
{
public $tidy_mode = 'proxy'; // 'proxy', 'exec', 'php' or 'none'
public $tidy_proxy = 'http://cgi.w3.org/cgi-bin/tidy?forceXML=on&docAddr='; // required only for tidy_mode=proxy
public $tmp_dir = '/path/to/writable/dir/'; // required only for tidy_mode=exec
private $root_class = '';
private $classes = '';
private $singles = '';
private $required = '';
private $att_map = '';
private $callbacks = '';
private $processor = '';
private $url = '';
private $base = '';
private $doc = '';
public function hKit()
{
// pre-flight checks
$pass = true;
$required = array('dom_import_simplexml', 'file_get_contents', 'simplexml_load_string');
$missing = array();
foreach ($required as $f){
if (!function_exists($f)){
$pass = false;
$missing[] = $f . '()';
}
}
if (!$pass)
die('hKit error: these required functions are not available: <strong>' . implode(', ', $missing) . '</strong>');
}
public function getByURL($profile='', $url='')
{
if ($profile=='' || $url == '') return false;
$this->loadProfile($profile);
$source = $this->loadURL($url);
if ($source){
$tidy_xhtml = $this->tidyThis($source);
$fragment = false;
if (strrchr($url, '#'))
$fragment = array_pop(explode('#', $url));
$doc = $this->loadDoc($tidy_xhtml, $fragment);
$s = $this->processNodes($doc, $this->classes);
$s = $this->postProcess($profile, $s);
return $s;
}else{
return false;
}
}
public function getByString($profile='', $input_xml='')
{
if ($profile=='' || $input_xml == '') return false;
$this->loadProfile($profile);
$doc = $this->loadDoc($input_xml);
$s = $this->processNodes($doc, $this->classes);
$s = $this->postProcess($profile, $s);
return $s;
}
private function processNodes($items, $classes, $allow_includes=true){
$out = array();
foreach($items as $item){
$data = array();
for ($i=0; $i<sizeof($classes); $i++){
if (!is_array($classes[$i])){
$xpath = ".//*[contains(concat(' ',normalize-space(@class),' '),' " . $classes[$i] . " ')]";
$results = $item->xpath($xpath);
if ($results){
foreach ($results as $result){
if (isset($classes[$i+1]) && is_array($classes[$i+1])){
$nodes = $this->processNodes($results, $classes[$i+1]);
if (sizeof($nodes) > 0){
$nodes = array_merge(array('text'=>$this->getNodeValue($result, $classes[$i])), $nodes);
$data[$classes[$i]] = $nodes;
}else{
$data[$classes[$i]] = $this->getNodeValue($result, $classes[$i]);
}
}else{
if (isset($data[$classes[$i]])){
if (is_array($data[$classes[$i]])){
// is already an array - append
$data[$classes[$i]][] = $this->getNodeValue($result, $classes[$i]);
}else{
// make it an array
if ($classes[$i] == 'value'){ // unless it's the 'value' of a type/value pattern
$data[$classes[$i]] .= $this->getNodeValue($result, $classes[$i]);
}else{
$old_val = $data[$classes[$i]];
$data[$classes[$i]] = array($old_val, $this->getNodeValue($result, $classes[$i]));
$old_val = false;
}
}
}else{
// set as normal value
$data[$classes[$i]] = $this->getNodeValue($result, $classes[$i]);
}
}
// td@headers pattern
if (strtoupper(dom_import_simplexml($result)->tagName)== "TD" && $result['headers']){
$include_ids = explode(' ', $result['headers']);
$doc = $this->doc;
foreach ($include_ids as $id){
$xpath = "//*[@id='$id']/..";
$includes = $doc->xpath($xpath);
foreach ($includes as $include){
$tmp = $this->processNodes($include, $this->classes);
if (is_array($tmp)) $data = array_merge($data, $tmp);
}
}
}
}
}
}
$result = false;
}
// include-pattern
if ($allow_includes){
$xpath = ".//*[contains(concat(' ',normalize-space(@class),' '),' include ')]";
$results = $item->xpath($xpath);
if ($results){
foreach ($results as $result){
$tagName = strtoupper(dom_import_simplexml($result)->tagName);
if ((($tagName == "OBJECT" && $result['data']) || ($tagName == "A" && $result['href']))
&& preg_match('/\binclude\b/', $result['class'])){
$att = ($tagName == "OBJECT" ? 'data' : 'href');
$id = str_replace('#', '', $result[$att]);
$doc = $this->doc;
$xpath = "//*[@id='$id']";
$includes = $doc->xpath($xpath);
foreach ($includes as $include){
$include = simplexml_load_string('<root1><root2>'.$include->asXML().'</root2></root1>'); // don't ask.
$tmp = $this->processNodes($include, $this->classes, false);
if (is_array($tmp)) $data = array_merge($data, $tmp);
}
}
}
}
}
$out[] = $data;
}
if (sizeof($out) > 1){
return $out;
}else if (isset($data)){
return $data;
}else{
return array();
}
}
private function getNodeValue($node, $className)
{
$tag_name = strtoupper(dom_import_simplexml($node)->tagName);
$s = false;
// ignore DEL tags
if ($tag_name == 'DEL') return $s;
// look up att map values
if (array_key_exists($className, $this->att_map)){
foreach ($this->att_map[$className] as $map){
if (preg_match("/$tag_name\|/", $map)){
$s = ''.$node[array_pop($foo = explode('|', $map))];
}
}
}
// if nothing and OBJ, try data.
if (!$s && $tag_name=='OBJECT' && $node['data']) $s = ''.$node['data'];
// if nothing and IMG, try alt.
if (!$s && $tag_name=='IMG' && $node['alt']) $s = ''.$node['alt'];
// if nothing and AREA, try alt.
if (!$s && $tag_name=='AREA' && $node['alt']) $s = ''.$node['alt'];
//if nothing and not A, try title.
if (!$s && $tag_name!='A' && $node['title']) $s = ''.$node['title'];
// if nothing found, go with node text
$s = ($s ? $s : implode(array_filter($node->xpath('child::node()'), array(&$this, "filterBlankValues")), ' '));
// callbacks
if (array_key_exists($className, $this->callbacks)){
$s = preg_replace_callback('/.*/', $this->callbacks[$className], $s, 1);
}
// trim and remove line breaks
if ($tag_name != 'PRE'){
$s = trim(preg_replace('/[\r\n\t]+/', '', $s));
$s = trim(preg_replace('/(\s{2})+/', ' ', $s));
}
return $s;
}
private function filterBlankValues($s){
return preg_match("/\w+/", $s);
}
private function tidyThis($source)
{
switch ( $this->tidy_mode )
{
case 'exec':
$tmp_file = $this->tmp_dir.md5($source).'.txt';
file_put_contents($tmp_file, $source);
exec("tidy -utf8 -indent -asxhtml -numeric -bare -quiet $tmp_file", $tidy);
unlink($tmp_file);
return implode("\n", $tidy);
break;
case 'php':
$tidy = tidy_parse_string($source);
return tidy_clean_repair($tidy);
break;
default:
return $source;
break;
}
}
private function loadProfile($profile)
{
require_once("$profile.profile.php");
}
private function loadDoc($input_xml, $fragment=false)
{
$xml = simplexml_load_string($input_xml);
$this->doc = $xml;
if ($fragment){
$doc = $xml->xpath("//*[@id='$fragment']");
$xml = simplexml_load_string($doc[0]->asXML());
$doc = null;
}
// base tag
if ($xml->head->base['href']) $this->base = $xml->head->base['href'];
// xml:base attribute - PITA with SimpleXML
preg_match('/xml:base="(.*)"/', $xml->asXML(), $matches);
if (is_array($matches) && sizeof($matches)>1) $this->base = $matches[1];
return $xml->xpath("//*[contains(concat(' ',normalize-space(@class),' '),' $this->root_class ')]");
}
private function loadURL($url)
{
$this->url = $url;
if ($this->tidy_mode == 'proxy' && $this->tidy_proxy != ''){
$url = $this->tidy_proxy . $url;
}
return @file_get_contents($url);
}
private function postProcess($profile, $s)
{
$required = $this->required;
if (is_array($s) && array_key_exists($required[0], $s)){
$s = array($s);
}
$s = $this->dedupeSingles($s);
if (function_exists('hKit_'.$profile.'_post')){
$s = call_user_func('hKit_'.$profile.'_post', $s);
}
$s = $this->removeTextVals($s);
return $s;
}
private function resolvePath($filepath)
{ // ugly code ahoy: needs a serious tidy up
$filepath = $filepath[0];
$base = $this->base;
$url = $this->url;
if ($base != '' && strpos($base, '://') !== false)
$url = $base;
$r = parse_url($url);
$domain = $r['scheme'] . '://' . $r['host'];
if (!isset($r['path'])) $r['path'] = '/';
$path = explode('/', $r['path']);
$file = explode('/', $filepath);
$new = array('');
if (strpos($filepath, '://') !== false || strpos($filepath, 'data:') !== false){
return $filepath;
}
if ($file[0] == ''){
// absolute path
return ''.$domain . implode('/', $file);
}else{
// relative path
if ($path[sizeof($path)-1] == '') array_pop($path);
if (strpos($path[sizeof($path)-1], '.') !== false) array_pop($path);
foreach ($file as $segment){
if ($segment == '..'){
array_pop($path);
}else{
$new[] = $segment;
}
}
return ''.$domain . implode('/', $path) . implode('/', $new);
}
}
private function resolveEmail($v)
{
$parts = parse_url($v[0]);
return ($parts['path']);
}
private function dedupeSingles($s)
{
$singles = $this->singles;
foreach ($s as &$item){
foreach ($singles as $classname){
if (array_key_exists($classname, $item) && is_array($item[$classname])){
if (isset($item[$classname][0])) $item[$classname] = $item[$classname][0];
}
}
}
return $s;
}
private function removeTextVals($s)
{
foreach ($s as $key => &$val){
if ($key){
$k = $key;
}else{
$k = '';
}
if (is_array($val)){
$val = $this->removeTextVals($val);
}else{
if ($k == 'text'){
$val = '';
}
}
}
return array_filter($s);
}
}
?>

View File

@ -37,6 +37,7 @@ class Webfinger
{ {
const PROFILEPAGE = 'http://webfinger.net/rel/profile-page'; const PROFILEPAGE = 'http://webfinger.net/rel/profile-page';
const UPDATESFROM = 'http://schemas.google.com/g/2010#updates-from'; const UPDATESFROM = 'http://schemas.google.com/g/2010#updates-from';
const HCARD = 'http://microformats.org/profile/hcard';
/** /**
* Perform a webfinger lookup given an account. * Perform a webfinger lookup given an account.

49
scripts/init_conversation.php Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
require_once INSTALLDIR.'/scripts/commandline.inc';
common_log(LOG_INFO, 'Initializing conversation table...');
$notice = new Notice();
$notice->query('select distinct conversation from notice');
while ($notice->fetch()) {
$id = $notice->conversation;
if ($id) {
$uri = common_local_url('conversation', array('id' => $id));
// @fixme db_dataobject won't save our value for an autoincrement
// so we're bypassing the insert wrappers
$conv = new Conversation();
$sql = "insert into conversation (id,uri,created) values(%d,'%s','%s')";
$sql = sprintf($sql,
$id,
$conv->escape($uri),
$conv->escape(common_sql_now()));
echo "$id ";
$conv->query($sql);
print "... ";
}
}
print "done.\n";