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:
commit
c49fbb63c5
120
actions/hcard.php
Normal file
120
actions/hcard.php
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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}'));
|
||||||
|
@ -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',
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
105
plugins/OStatus/extlib/hkit/hcard.profile.php
Normal file
105
plugins/OStatus/extlib/hkit/hcard.profile.php
Normal 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'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
475
plugins/OStatus/extlib/hkit/hkit.class.php
Normal file
475
plugins/OStatus/extlib/hkit/hkit.class.php
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
@ -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
49
scripts/init_conversation.php
Executable 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";
|
Loading…
Reference in New Issue
Block a user