Merge branch '0.9.x' into 1.0.x
Conflicts: classes/Memcached_DataObject.php
This commit is contained in:
commit
d8a3a88ec8
12
EVENTS.txt
12
EVENTS.txt
@ -999,7 +999,7 @@ StartXrdActionAliases: About to set aliases for the XRD object for a user
|
|||||||
EndXrdActionAliases: Done with aliases for the XRD object for a user
|
EndXrdActionAliases: Done with aliases for the XRD object for a user
|
||||||
- &$xrd: XRD object being shown
|
- &$xrd: XRD object being shown
|
||||||
- $user: User being shown
|
- $user: User being shown
|
||||||
|
|
||||||
StartXrdActionLinks: About to set links for the XRD object for a user
|
StartXrdActionLinks: About to set links for the XRD object for a user
|
||||||
- &$xrd: XRD object being shown
|
- &$xrd: XRD object being shown
|
||||||
- $user: User being shown
|
- $user: User being shown
|
||||||
@ -1007,3 +1007,13 @@ StartXrdActionLinks: About to set links for the XRD object for a user
|
|||||||
EndXrdActionLinks: Done with links for the XRD object for a user
|
EndXrdActionLinks: Done with links for the XRD object for a user
|
||||||
- &$xrd: XRD object being shown
|
- &$xrd: XRD object being shown
|
||||||
- $user: User being shown
|
- $user: User being shown
|
||||||
|
|
||||||
|
AdminPanelCheck: When checking whether the current user can access a given admin panel
|
||||||
|
- $name: Name of the admin panel
|
||||||
|
- &$isOK: Boolean whether the user is allowed to use the panel
|
||||||
|
|
||||||
|
StartAdminPanelNav: Before displaying the first item in the list of admin panels
|
||||||
|
- $nav The AdminPanelNav widget
|
||||||
|
|
||||||
|
EndAdminPanelNav: After displaying the last item in the list of admin panels
|
||||||
|
- $nav The AdminPanelNav widget
|
||||||
|
135
actions/apiuserprofileimage.php
Normal file
135
actions/apiuserprofileimage.php
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* StatusNet, the distributed open-source microblogging tool
|
||||||
|
*
|
||||||
|
* Return a user's avatar image
|
||||||
|
*
|
||||||
|
* 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 API
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Brion Vibber <brion@status.net>
|
||||||
|
* @copyright 2010 StatusNet, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once INSTALLDIR . '/lib/apiprivateauth.php';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ouputs avatar URL for a user, specified by screen name.
|
||||||
|
* Unlike most API endpoints, this returns an HTTP redirect rather than direct data.
|
||||||
|
*
|
||||||
|
* @category API
|
||||||
|
* @package StatusNet
|
||||||
|
* @author Brion Vibber <brion@status.net>
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://status.net/
|
||||||
|
*/
|
||||||
|
class ApiUserProfileImageAction extends ApiPrivateAuthAction
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Take arguments for running
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST args
|
||||||
|
*
|
||||||
|
* @return boolean success flag
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function prepare($args)
|
||||||
|
{
|
||||||
|
parent::prepare($args);
|
||||||
|
$this->user = User::staticGet('nickname', $this->arg('screen_name'));
|
||||||
|
$this->size = $this->arg('size');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the request
|
||||||
|
*
|
||||||
|
* Check the format and show the user info
|
||||||
|
*
|
||||||
|
* @param array $args $_REQUEST data (unused)
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
function handle($args)
|
||||||
|
{
|
||||||
|
parent::handle($args);
|
||||||
|
|
||||||
|
if (empty($this->user)) {
|
||||||
|
// TRANS: Client error displayed when requesting user information for a non-existing user.
|
||||||
|
$this->clientError(_('User not found.'), 404, $this->format);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$profile = $this->user->getProfile();
|
||||||
|
|
||||||
|
if (empty($profile)) {
|
||||||
|
// TRANS: Client error displayed when requesting user information for a user without a profile.
|
||||||
|
$this->clientError(_('User has no profile.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$size = $this->avatarSize();
|
||||||
|
$avatar = $profile->getAvatar($size);
|
||||||
|
if ($avatar) {
|
||||||
|
$url = $avatar->displayUrl();
|
||||||
|
} else {
|
||||||
|
$url = Avatar::defaultImage($size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't actually output JSON or XML data -- redirect!
|
||||||
|
common_redirect($url, 302);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the appropriate pixel size for an avatar based on the request...
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private function avatarSize()
|
||||||
|
{
|
||||||
|
switch ($this->size) {
|
||||||
|
case 'mini':
|
||||||
|
return AVATAR_MINI_SIZE; // 24x24
|
||||||
|
case 'bigger':
|
||||||
|
return AVATAR_PROFILE_SIZE; // Twitter does 73x73, but we do 96x96
|
||||||
|
case 'normal': // fall through
|
||||||
|
default:
|
||||||
|
return AVATAR_STREAM_SIZE; // 48x48
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if read only.
|
||||||
|
*
|
||||||
|
* MAY override
|
||||||
|
*
|
||||||
|
* @param array $args other arguments
|
||||||
|
*
|
||||||
|
* @return boolean is read only action?
|
||||||
|
*/
|
||||||
|
function isReadOnly($args)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -311,7 +311,7 @@ class ProfileNoticeListItem extends DoFollowListItem
|
|||||||
'class' => 'url');
|
'class' => 'url');
|
||||||
|
|
||||||
if (!empty($this->profile->fullname)) {
|
if (!empty($this->profile->fullname)) {
|
||||||
$attrs['title'] = $this->getFancyName();
|
$attrs['title'] = $this->profile->getFancyName();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->out->elementStart('span', 'repeat');
|
$this->out->elementStart('span', 'repeat');
|
||||||
|
@ -85,6 +85,19 @@ class Fave extends Memcached_DataObject
|
|||||||
return $ids;
|
return $ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note that the sorting for this is by order of *fave* not order of *notice*.
|
||||||
|
*
|
||||||
|
* @fixme add since_id, max_id support?
|
||||||
|
*
|
||||||
|
* @param <type> $user_id
|
||||||
|
* @param <type> $own
|
||||||
|
* @param <type> $offset
|
||||||
|
* @param <type> $limit
|
||||||
|
* @param <type> $since_id
|
||||||
|
* @param <type> $max_id
|
||||||
|
* @return <type>
|
||||||
|
*/
|
||||||
function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id)
|
function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id)
|
||||||
{
|
{
|
||||||
$fav = new Fave();
|
$fav = new Fave();
|
||||||
|
@ -339,10 +339,14 @@ class Memcached_DataObject extends Safe_DataObject
|
|||||||
|
|
||||||
$start = microtime(true);
|
$start = microtime(true);
|
||||||
$fail = false;
|
$fail = false;
|
||||||
try {
|
$result = null;
|
||||||
$result = parent::_query($string);
|
if (Event::handle('StartDBQuery', array($this, $string, &$result))) {
|
||||||
} catch (Exception $e) {
|
try {
|
||||||
$fail = $e;
|
$result = parent::_query($string);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$fail = $e;
|
||||||
|
}
|
||||||
|
Event::handle('EndDBQuery', array($this, $string, &$result));
|
||||||
}
|
}
|
||||||
$delta = microtime(true) - $start;
|
$delta = microtime(true) - $start;
|
||||||
|
|
||||||
|
@ -654,7 +654,7 @@ class Notice extends Memcached_DataObject
|
|||||||
$notice->selectAdd(); // clears it
|
$notice->selectAdd(); // clears it
|
||||||
$notice->selectAdd('id');
|
$notice->selectAdd('id');
|
||||||
|
|
||||||
$notice->orderBy('id DESC');
|
$notice->orderBy('created DESC, id DESC');
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($offset)) {
|
||||||
$notice->limit($offset, $limit);
|
$notice->limit($offset, $limit);
|
||||||
@ -668,13 +668,8 @@ class Notice extends Memcached_DataObject
|
|||||||
$notice->whereAdd('is_local !='. Notice::GATEWAY);
|
$notice->whereAdd('is_local !='. Notice::GATEWAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($since_id != 0) {
|
Notice::addWhereSinceId($notice, $since_id);
|
||||||
$notice->whereAdd('id > ' . $since_id);
|
Notice::addWhereMaxId($notice, $max_id);
|
||||||
}
|
|
||||||
|
|
||||||
if ($max_id != 0) {
|
|
||||||
$notice->whereAdd('id <= ' . $max_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$ids = array();
|
$ids = array();
|
||||||
|
|
||||||
@ -709,19 +704,14 @@ class Notice extends Memcached_DataObject
|
|||||||
|
|
||||||
$notice->conversation = $id;
|
$notice->conversation = $id;
|
||||||
|
|
||||||
$notice->orderBy('id DESC');
|
$notice->orderBy('created DESC, id DESC');
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($offset)) {
|
||||||
$notice->limit($offset, $limit);
|
$notice->limit($offset, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($since_id != 0) {
|
Notice::addWhereSinceId($notice, $since_id);
|
||||||
$notice->whereAdd('id > ' . $since_id);
|
Notice::addWhereMaxId($notice, $max_id);
|
||||||
}
|
|
||||||
|
|
||||||
if ($max_id != 0) {
|
|
||||||
$notice->whereAdd('id <= ' . $max_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$ids = array();
|
$ids = array();
|
||||||
|
|
||||||
@ -1695,10 +1685,10 @@ class Notice extends Memcached_DataObject
|
|||||||
|
|
||||||
$notice->repeat_of = $this->id;
|
$notice->repeat_of = $this->id;
|
||||||
|
|
||||||
$notice->orderBy('created'); // NB: asc!
|
$notice->orderBy('created, id'); // NB: asc!
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($limit)) {
|
||||||
$notice->limit($offset, $limit);
|
$notice->limit(0, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
$ids = array();
|
$ids = array();
|
||||||
@ -1978,4 +1968,108 @@ class Notice extends Memcached_DataObject
|
|||||||
$d = new DateTime($dateStr, new DateTimeZone('UTC'));
|
$d = new DateTime($dateStr, new DateTimeZone('UTC'));
|
||||||
return $d->format(DATE_W3C);
|
return $d->format(DATE_W3C);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up the creation timestamp for a given notice ID, even
|
||||||
|
* if it's been deleted.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @return mixed string recorded creation timestamp, or false if can't be found
|
||||||
|
*/
|
||||||
|
public static function getAsTimestamp($id)
|
||||||
|
{
|
||||||
|
if (!$id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$notice = Notice::staticGet('id', $id);
|
||||||
|
if ($notice) {
|
||||||
|
return $notice->created;
|
||||||
|
}
|
||||||
|
|
||||||
|
$deleted = Deleted_notice::staticGet('id', $id);
|
||||||
|
if ($deleted) {
|
||||||
|
return $deleted->created;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an SQL 'where' fragment for timestamp-based sorting from a since_id
|
||||||
|
* parameter, matching notices posted after the given one (exclusive).
|
||||||
|
*
|
||||||
|
* If the referenced notice can't be found, will return false.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @param string $idField
|
||||||
|
* @param string $createdField
|
||||||
|
* @return mixed string or false if no match
|
||||||
|
*/
|
||||||
|
public static function whereSinceId($id, $idField='id', $createdField='created')
|
||||||
|
{
|
||||||
|
$since = Notice::getAsTimestamp($id);
|
||||||
|
if ($since) {
|
||||||
|
return sprintf("($createdField = '%s' and $idField > %d) or ($createdField > '%s')", $since, $id, $since);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an SQL 'where' fragment for timestamp-based sorting from a since_id
|
||||||
|
* parameter, matching notices posted after the given one (exclusive), and
|
||||||
|
* if necessary add it to the data object's query.
|
||||||
|
*
|
||||||
|
* @param DB_DataObject $obj
|
||||||
|
* @param int $id
|
||||||
|
* @param string $idField
|
||||||
|
* @param string $createdField
|
||||||
|
* @return mixed string or false if no match
|
||||||
|
*/
|
||||||
|
public static function addWhereSinceId(DB_DataObject $obj, $id, $idField='id', $createdField='created')
|
||||||
|
{
|
||||||
|
$since = self::whereSinceId($id);
|
||||||
|
if ($since) {
|
||||||
|
$obj->whereAdd($since);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an SQL 'where' fragment for timestamp-based sorting from a max_id
|
||||||
|
* parameter, matching notices posted before the given one (inclusive).
|
||||||
|
*
|
||||||
|
* If the referenced notice can't be found, will return false.
|
||||||
|
*
|
||||||
|
* @param int $id
|
||||||
|
* @param string $idField
|
||||||
|
* @param string $createdField
|
||||||
|
* @return mixed string or false if no match
|
||||||
|
*/
|
||||||
|
public static function whereMaxId($id, $idField='id', $createdField='created')
|
||||||
|
{
|
||||||
|
$max = Notice::getAsTimestamp($id);
|
||||||
|
if ($max) {
|
||||||
|
return sprintf("($createdField < '%s') or ($createdField = '%s' and $idField <= %d)", $max, $max, $id);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build an SQL 'where' fragment for timestamp-based sorting from a max_id
|
||||||
|
* parameter, matching notices posted before the given one (inclusive), and
|
||||||
|
* if necessary add it to the data object's query.
|
||||||
|
*
|
||||||
|
* @param DB_DataObject $obj
|
||||||
|
* @param int $id
|
||||||
|
* @param string $idField
|
||||||
|
* @param string $createdField
|
||||||
|
* @return mixed string or false if no match
|
||||||
|
*/
|
||||||
|
public static function addWhereMaxId(DB_DataObject $obj, $id, $idField='id', $createdField='created')
|
||||||
|
{
|
||||||
|
$max = self::whereMaxId($id);
|
||||||
|
if ($max) {
|
||||||
|
$obj->whereAdd($max);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,15 +55,10 @@ class Notice_tag extends Memcached_DataObject
|
|||||||
$nt->selectAdd();
|
$nt->selectAdd();
|
||||||
$nt->selectAdd('notice_id');
|
$nt->selectAdd('notice_id');
|
||||||
|
|
||||||
if ($since_id != 0) {
|
Notice::addWhereSinceId($nt, $since_id, 'notice_id');
|
||||||
$nt->whereAdd('notice_id > ' . $since_id);
|
Notice::addWhereMaxId($nt, $max_id, 'notice_id');
|
||||||
}
|
|
||||||
|
|
||||||
if ($max_id != 0) {
|
$nt->orderBy('created DESC, notice_id DESC');
|
||||||
$nt->whereAdd('notice_id <= ' . $max_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$nt->orderBy('notice_id DESC');
|
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($offset)) {
|
||||||
$nt->limit($offset, $limit);
|
$nt->limit($offset, $limit);
|
||||||
|
@ -215,26 +215,29 @@ class Profile extends Memcached_DataObject
|
|||||||
function _streamTaggedDirect($tag, $offset, $limit, $since_id, $max_id)
|
function _streamTaggedDirect($tag, $offset, $limit, $since_id, $max_id)
|
||||||
{
|
{
|
||||||
// XXX It would be nice to do this without a join
|
// XXX It would be nice to do this without a join
|
||||||
|
// (necessary to do it efficiently on accounts with long history)
|
||||||
|
|
||||||
$notice = new Notice();
|
$notice = new Notice();
|
||||||
|
|
||||||
$query =
|
$query =
|
||||||
"select id from notice join notice_tag on id=notice_id where tag='".
|
"select id from notice join notice_tag on id=notice_id where tag='".
|
||||||
$notice->escape($tag) .
|
$notice->escape($tag) .
|
||||||
"' and profile_id=" . $notice->escape($this->id);
|
"' and profile_id=" . intval($this->id);
|
||||||
|
|
||||||
if ($since_id != 0) {
|
$since = Notice::whereSinceId($since_id, 'id', 'notice.created');
|
||||||
$query .= " and id > $since_id";
|
if ($since) {
|
||||||
|
$query .= " and ($since)";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($max_id != 0) {
|
$max = Notice::whereMaxId($max_id, 'id', 'notice.created');
|
||||||
$query .= " and id <= $max_id";
|
if ($max) {
|
||||||
|
$query .= " and ($max)";
|
||||||
}
|
}
|
||||||
|
|
||||||
$query .= ' order by id DESC';
|
$query .= ' order by notice.created DESC, id DESC';
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($offset)) {
|
||||||
$query .= " LIMIT $limit OFFSET $offset";
|
$query .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
$notice->query($query);
|
$notice->query($query);
|
||||||
@ -252,58 +255,22 @@ class Profile extends Memcached_DataObject
|
|||||||
{
|
{
|
||||||
$notice = new Notice();
|
$notice = new Notice();
|
||||||
|
|
||||||
// Temporary hack until notice_profile_id_idx is updated
|
$notice->profile_id = $this->id;
|
||||||
// to (profile_id, id) instead of (profile_id, created, id).
|
|
||||||
// It's been falling back to PRIMARY instead, which is really
|
|
||||||
// very inefficient for a profile that hasn't posted in a few
|
|
||||||
// months. Even though forcing the index will cause a filesort,
|
|
||||||
// it's usually going to be better.
|
|
||||||
if (common_config('db', 'type') == 'mysql') {
|
|
||||||
$index = '';
|
|
||||||
$query =
|
|
||||||
"select id from notice force index (notice_profile_id_idx) ".
|
|
||||||
"where profile_id=" . $notice->escape($this->id);
|
|
||||||
|
|
||||||
if ($since_id != 0) {
|
$notice->selectAdd();
|
||||||
$query .= " and id > $since_id";
|
$notice->selectAdd('id');
|
||||||
}
|
|
||||||
|
|
||||||
if ($max_id != 0) {
|
Notice::addWhereSinceId($notice, $since_id);
|
||||||
$query .= " and id <= $max_id";
|
Notice::addWhereMaxId($notice, $max_id);
|
||||||
}
|
|
||||||
|
|
||||||
$query .= ' order by id DESC';
|
$notice->orderBy('created DESC, id DESC');
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($offset)) {
|
||||||
$query .= " LIMIT $limit OFFSET $offset";
|
$notice->limit($offset, $limit);
|
||||||
}
|
|
||||||
|
|
||||||
$notice->query($query);
|
|
||||||
} else {
|
|
||||||
$index = '';
|
|
||||||
|
|
||||||
$notice->profile_id = $this->id;
|
|
||||||
|
|
||||||
$notice->selectAdd();
|
|
||||||
$notice->selectAdd('id');
|
|
||||||
|
|
||||||
if ($since_id != 0) {
|
|
||||||
$notice->whereAdd('id > ' . $since_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($max_id != 0) {
|
|
||||||
$notice->whereAdd('id <= ' . $max_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$notice->orderBy('id DESC');
|
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
|
||||||
$notice->limit($offset, $limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
$notice->find();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$notice->find();
|
||||||
|
|
||||||
$ids = array();
|
$ids = array();
|
||||||
|
|
||||||
while ($notice->fetch()) {
|
while ($notice->fetch()) {
|
||||||
|
@ -50,15 +50,10 @@ class Reply extends Memcached_DataObject
|
|||||||
$reply = new Reply();
|
$reply = new Reply();
|
||||||
$reply->profile_id = $user_id;
|
$reply->profile_id = $user_id;
|
||||||
|
|
||||||
if ($since_id != 0) {
|
Notice::addWhereSinceId($reply, $since_id, 'notice_id', 'modified');
|
||||||
$reply->whereAdd('notice_id > ' . $since_id);
|
Notice::addWhereMaxId($reply, $max_id, 'notice_id', 'modified');
|
||||||
}
|
|
||||||
|
|
||||||
if ($max_id != 0) {
|
$reply->orderBy('modified DESC, notice_id DESC');
|
||||||
$reply->whereAdd('notice_id <= ' . $max_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$reply->orderBy('notice_id DESC');
|
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($offset)) {
|
||||||
$reply->limit($offset, $limit);
|
$reply->limit($offset, $limit);
|
||||||
|
@ -750,19 +750,14 @@ class User extends Memcached_DataObject
|
|||||||
$notice->profile_id = $this->id;
|
$notice->profile_id = $this->id;
|
||||||
$notice->whereAdd('repeat_of IS NOT NULL');
|
$notice->whereAdd('repeat_of IS NOT NULL');
|
||||||
|
|
||||||
$notice->orderBy('id DESC');
|
$notice->orderBy('created DESC, id DESC');
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($offset)) {
|
||||||
$notice->limit($offset, $limit);
|
$notice->limit($offset, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($since_id != 0) {
|
Notice::addWhereSinceId($notice, $since_id);
|
||||||
$notice->whereAdd('id > ' . $since_id);
|
Notice::addWhereMaxId($notice, $max_id);
|
||||||
}
|
|
||||||
|
|
||||||
if ($max_id != 0) {
|
|
||||||
$notice->whereAdd('id <= ' . $max_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$ids = array();
|
$ids = array();
|
||||||
|
|
||||||
@ -795,17 +790,17 @@ class User extends Memcached_DataObject
|
|||||||
'FROM notice original JOIN notice rept ON original.id = rept.repeat_of ' .
|
'FROM notice original JOIN notice rept ON original.id = rept.repeat_of ' .
|
||||||
'WHERE original.profile_id = ' . $this->id . ' ';
|
'WHERE original.profile_id = ' . $this->id . ' ';
|
||||||
|
|
||||||
if ($since_id != 0) {
|
$since = Notice::whereSinceId($since_id, 'original.id', 'original.created');
|
||||||
$qry .= 'AND original.id > ' . $since_id . ' ';
|
if ($since) {
|
||||||
|
$qry .= "AND ($since) ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($max_id != 0) {
|
$max = Notice::whereMaxId($max_id, 'original.id', 'original.created');
|
||||||
$qry .= 'AND original.id <= ' . $max_id . ' ';
|
if ($max) {
|
||||||
|
$qry .= "AND ($max) ";
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: we sort by fave time, not by notice time!
|
$qry .= 'ORDER BY original.created, original.id DESC ';
|
||||||
|
|
||||||
$qry .= 'ORDER BY original.id DESC ';
|
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($offset)) {
|
||||||
$qry .= "LIMIT $limit OFFSET $offset";
|
$qry .= "LIMIT $limit OFFSET $offset";
|
||||||
|
@ -100,15 +100,10 @@ class User_group extends Memcached_DataObject
|
|||||||
$inbox->selectAdd();
|
$inbox->selectAdd();
|
||||||
$inbox->selectAdd('notice_id');
|
$inbox->selectAdd('notice_id');
|
||||||
|
|
||||||
if ($since_id != 0) {
|
Notice::addWhereSinceId($inbox, $since_id, 'notice_id');
|
||||||
$inbox->whereAdd('notice_id > ' . $since_id);
|
Notice::addWhereMaxId($inbox, $max_id, 'notice_id');
|
||||||
}
|
|
||||||
|
|
||||||
if ($max_id != 0) {
|
$inbox->orderBy('created DESC, notice_id DESC');
|
||||||
$inbox->whereAdd('notice_id <= ' . $max_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$inbox->orderBy('notice_id DESC');
|
|
||||||
|
|
||||||
if (!is_null($offset)) {
|
if (!is_null($offset)) {
|
||||||
$inbox->limit($offset, $limit);
|
$inbox->limit($offset, $limit);
|
||||||
|
26
db/096to097.sql
Normal file
26
db/096to097.sql
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
-- Add indexes for sorting changes in 0.9.7
|
||||||
|
|
||||||
|
-- Allows sorting public timeline, api/statuses/repeats, and conversations by timestamp efficiently
|
||||||
|
alter table notice
|
||||||
|
add index notice_created_id_is_local_idx (created,id,is_local),
|
||||||
|
|
||||||
|
add index notice_repeat_of_created_id_idx (repeat_of, created, id),
|
||||||
|
drop index notice_repeatof_idx,
|
||||||
|
|
||||||
|
add index notice_conversation_created_id_idx (conversation, created, id),
|
||||||
|
drop index notice_conversation_idx;
|
||||||
|
|
||||||
|
-- Allows sorting tag-filtered public timeline by timestamp efficiently
|
||||||
|
alter table notice_tag add index notice_tag_tag_created_notice_id_idx (tag, created, notice_id);
|
||||||
|
|
||||||
|
-- Needed for sorting reply/mentions timelines
|
||||||
|
alter table reply add index reply_profile_id_modified_notice_id_idx (profile_id, modified, notice_id);
|
||||||
|
|
||||||
|
-- Needed for sorting group messages by timestamp
|
||||||
|
alter table group_inbox add index group_inbox_group_id_created_notice_id_idx (group_id, created, notice_id);
|
||||||
|
|
||||||
|
-- Helps make some reverse role lookups more efficient if there's a lot of assigned accounts
|
||||||
|
alter table profile_role add index profile_role_role_created_profile_id_idx (role, created, profile_id);
|
||||||
|
|
||||||
|
-- Fix for sorting a user's group memberships by order joined
|
||||||
|
alter table group_member add index group_member_profile_id_created_idx (profile_id, created);
|
@ -126,11 +126,21 @@ create table notice (
|
|||||||
location_ns integer comment 'namespace for location',
|
location_ns integer comment 'namespace for location',
|
||||||
repeat_of integer comment 'notice this is a repeat of' references notice (id),
|
repeat_of integer comment 'notice this is a repeat of' references notice (id),
|
||||||
|
|
||||||
|
-- For public timeline...
|
||||||
|
index notice_created_id_is_local_idx (created,id,is_local),
|
||||||
|
|
||||||
|
-- For profile timelines...
|
||||||
index notice_profile_id_idx (profile_id,created,id),
|
index notice_profile_id_idx (profile_id,created,id),
|
||||||
index notice_conversation_idx (conversation),
|
|
||||||
index notice_created_idx (created),
|
-- For api/statuses/repeats...
|
||||||
|
index notice_repeat_of_created_id_idx (repeat_of, created, id),
|
||||||
|
|
||||||
|
-- For conversation views
|
||||||
|
index notice_conversation_created_id_idx (conversation, created, id),
|
||||||
|
|
||||||
|
-- Are these needed/used?
|
||||||
index notice_replyto_idx (reply_to),
|
index notice_replyto_idx (reply_to),
|
||||||
index notice_repeatof_idx (repeat_of),
|
|
||||||
FULLTEXT(content)
|
FULLTEXT(content)
|
||||||
) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
|
) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci;
|
||||||
|
|
||||||
@ -151,7 +161,10 @@ create table reply (
|
|||||||
constraint primary key (notice_id, profile_id),
|
constraint primary key (notice_id, profile_id),
|
||||||
index reply_notice_id_idx (notice_id),
|
index reply_notice_id_idx (notice_id),
|
||||||
index reply_profile_id_idx (profile_id),
|
index reply_profile_id_idx (profile_id),
|
||||||
index reply_replied_id_idx (replied_id)
|
index reply_replied_id_idx (replied_id),
|
||||||
|
|
||||||
|
-- Needed for sorting reply/mentions timelines
|
||||||
|
index reply_profile_id_modified_notice_id_idx (profile_id, modified, notice_id)
|
||||||
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
||||||
|
|
||||||
@ -296,7 +309,10 @@ create table notice_tag (
|
|||||||
|
|
||||||
constraint primary key (tag, notice_id),
|
constraint primary key (tag, notice_id),
|
||||||
index notice_tag_created_idx (created),
|
index notice_tag_created_idx (created),
|
||||||
index notice_tag_notice_id_idx (notice_id)
|
index notice_tag_notice_id_idx (notice_id),
|
||||||
|
|
||||||
|
-- For sorting tag-filtered public timeline
|
||||||
|
index notice_tag_tag_created_notice_id_idx (tag, created, notice_id)
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
||||||
|
|
||||||
/* Synching with foreign services */
|
/* Synching with foreign services */
|
||||||
@ -442,7 +458,10 @@ create table group_member (
|
|||||||
|
|
||||||
constraint primary key (group_id, profile_id),
|
constraint primary key (group_id, profile_id),
|
||||||
index group_member_profile_id_idx (profile_id),
|
index group_member_profile_id_idx (profile_id),
|
||||||
index group_member_created_idx (created)
|
index group_member_created_idx (created),
|
||||||
|
|
||||||
|
-- To pull up a list of someone's groups in order joined
|
||||||
|
index group_member_profile_id_created_idx (profile_id, created)
|
||||||
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
||||||
|
|
||||||
@ -463,7 +482,10 @@ create table group_inbox (
|
|||||||
|
|
||||||
constraint primary key (group_id, notice_id),
|
constraint primary key (group_id, notice_id),
|
||||||
index group_inbox_created_idx (created),
|
index group_inbox_created_idx (created),
|
||||||
index group_inbox_notice_id_idx (notice_id)
|
index group_inbox_notice_id_idx (notice_id),
|
||||||
|
|
||||||
|
-- Needed for sorting group messages by timestamp
|
||||||
|
index group_inbox_group_id_created_notice_id_idx (group_id, created, notice_id)
|
||||||
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
||||||
|
|
||||||
@ -603,7 +625,8 @@ create table profile_role (
|
|||||||
role varchar(32) not null comment 'string representing the role',
|
role varchar(32) not null comment 'string representing the role',
|
||||||
created datetime not null comment 'date the role was granted',
|
created datetime not null comment 'date the role was granted',
|
||||||
|
|
||||||
constraint primary key (profile_id, role)
|
constraint primary key (profile_id, role),
|
||||||
|
index profile_role_role_created_profile_id_idx (role, created, profile_id)
|
||||||
|
|
||||||
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
|
||||||
|
|
||||||
|
@ -529,6 +529,11 @@ class Router
|
|||||||
'id' => Nickname::INPUT_FMT,
|
'id' => Nickname::INPUT_FMT,
|
||||||
'format' => '(xml|json)'));
|
'format' => '(xml|json)'));
|
||||||
|
|
||||||
|
$m->connect('api/users/profile_image/:screen_name.:format',
|
||||||
|
array('action' => 'ApiUserProfileImage',
|
||||||
|
'screen_name' => Nickname::DISPLAY_FMT,
|
||||||
|
'format' => '(xml|json)'));
|
||||||
|
|
||||||
// direct messages
|
// direct messages
|
||||||
|
|
||||||
$m->connect('api/direct_messages.:format',
|
$m->connect('api/direct_messages.:format',
|
||||||
|
@ -140,7 +140,7 @@ class LdapCommon
|
|||||||
|
|
||||||
function checkPassword($username, $password)
|
function checkPassword($username, $password)
|
||||||
{
|
{
|
||||||
$entry = $this->get_user($username);
|
$entry = $this->get_user($username,array('dn' => 'dn'));
|
||||||
if(!$entry){
|
if(!$entry){
|
||||||
return false;
|
return false;
|
||||||
}else{
|
}else{
|
||||||
@ -168,7 +168,7 @@ class LdapCommon
|
|||||||
//throw new Exception(_('Sorry, changing LDAP passwords is not supported at this time'));
|
//throw new Exception(_('Sorry, changing LDAP passwords is not supported at this time'));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$entry = $this->get_user($username);
|
$entry = $this->get_user($username,array('dn' => 'dn'));
|
||||||
if(!$entry){
|
if(!$entry){
|
||||||
return false;
|
return false;
|
||||||
}else{
|
}else{
|
||||||
|
66
plugins/SQLProfile/SQLProfilePlugin.php
Normal file
66
plugins/SQLProfile/SQLProfilePlugin.php
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* StatusNet - the distributed open-source microblogging tool
|
||||||
|
* Copyright (C) 2010, 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!defined('STATUSNET')) {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check DB queries for filesorts and such and log em.
|
||||||
|
*
|
||||||
|
* @package SQLProfilePlugin
|
||||||
|
* @maintainer Brion Vibber <brion@status.net>
|
||||||
|
*/
|
||||||
|
class SQLProfilePlugin extends Plugin
|
||||||
|
{
|
||||||
|
private $recursionGuard = false;
|
||||||
|
|
||||||
|
function onPluginVersion(&$versions)
|
||||||
|
{
|
||||||
|
$versions[] = array('name' => 'SQLProfile',
|
||||||
|
'version' => STATUSNET_VERSION,
|
||||||
|
'author' => 'Brion Vibber',
|
||||||
|
'homepage' => 'http://status.net/wiki/Plugin:SQLProfile',
|
||||||
|
'rawdescription' =>
|
||||||
|
_m('Debug tool to watch for poorly indexed DB queries'));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStartDBQuery($obj, $query, &$result)
|
||||||
|
{
|
||||||
|
if (!$this->recursionGuard && preg_match('/\bselect\b/i', $query)) {
|
||||||
|
$this->recursionGuard = true;
|
||||||
|
$xobj = clone($obj);
|
||||||
|
$explain = $xobj->query('EXPLAIN ' . $query);
|
||||||
|
$this->recursionGuard = false;
|
||||||
|
|
||||||
|
while ($xobj->fetch()) {
|
||||||
|
$extra = $xobj->Extra;
|
||||||
|
$evil = (strpos($extra, 'Using filesort') !== false) ||
|
||||||
|
(strpos($extra, 'Using temporary') !== false);
|
||||||
|
if ($evil) {
|
||||||
|
$xquery = $xobj->sanitizeQuery($query);
|
||||||
|
common_log(LOG_DEBUG, "$extra | $xquery");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -44,7 +44,7 @@ $n->query('SELECT notice.id, notice.uri ' .
|
|||||||
'AND notice_to_status.status_id IS NULL');
|
'AND notice_to_status.status_id IS NULL');
|
||||||
|
|
||||||
while ($n->fetch()) {
|
while ($n->fetch()) {
|
||||||
if (preg_match('#^http://twitter.com/[\w_.]+/status/(\d+)$#', $n->uri, $match)) {
|
if (preg_match('/^http://twitter.com(/#!)?/[\w_.]+/status/(\d+)$/', $n->uri, $match)) {
|
||||||
$status_id = $match[1];
|
$status_id = $match[1];
|
||||||
Notice_to_status::saveNew($n->id, $status_id);
|
Notice_to_status::saveNew($n->id, $status_id);
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ function add_twitter_user($twitter_id, $screen_name)
|
|||||||
$fuser = new Foreign_user();
|
$fuser = new Foreign_user();
|
||||||
|
|
||||||
$fuser->nickname = $screen_name;
|
$fuser->nickname = $screen_name;
|
||||||
$fuser->uri = 'http://twitter.com/' . $screen_name;
|
$fuser->uri = 'http://twitter.com/#!/' . $screen_name;
|
||||||
$fuser->id = $twitter_id;
|
$fuser->id = $twitter_id;
|
||||||
$fuser->service = TWITTER_SERVICE;
|
$fuser->service = TWITTER_SERVICE;
|
||||||
$fuser->created = common_sql_now();
|
$fuser->created = common_sql_now();
|
||||||
|
@ -207,7 +207,7 @@ class TwitterImport
|
|||||||
*/
|
*/
|
||||||
function makeStatusURI($username, $id)
|
function makeStatusURI($username, $id)
|
||||||
{
|
{
|
||||||
return 'http://twitter.com/'
|
return 'http://twitter.com/#!/'
|
||||||
. $username
|
. $username
|
||||||
. '/status/'
|
. '/status/'
|
||||||
. $id;
|
. $id;
|
||||||
@ -264,7 +264,7 @@ class TwitterImport
|
|||||||
function ensureProfile($user)
|
function ensureProfile($user)
|
||||||
{
|
{
|
||||||
// check to see if there's already a profile for this user
|
// check to see if there's already a profile for this user
|
||||||
$profileurl = 'http://twitter.com/' . $user->screen_name;
|
$profileurl = 'http://twitter.com/#!/' . $user->screen_name;
|
||||||
$profile = $this->getProfileByUrl($user->screen_name, $profileurl);
|
$profile = $this->getProfileByUrl($user->screen_name, $profileurl);
|
||||||
|
|
||||||
if (!empty($profile)) {
|
if (!empty($profile)) {
|
||||||
@ -618,15 +618,15 @@ class TwitterImport
|
|||||||
|
|
||||||
static function tagLink($tag)
|
static function tagLink($tag)
|
||||||
{
|
{
|
||||||
return "<a href='https://twitter.com/search?q=%23{$tag}' class='hashtag'>{$tag}</a>";
|
return "<a href='https://search.twitter.com/search?q=%23{$tag}' class='hashtag'>{$tag}</a>";
|
||||||
}
|
}
|
||||||
|
|
||||||
static function atLink($screenName, $fullName=null)
|
static function atLink($screenName, $fullName=null)
|
||||||
{
|
{
|
||||||
if (!empty($fullName)) {
|
if (!empty($fullName)) {
|
||||||
return "<a href='http://twitter.com/{$screenName}' title='{$fullName}'>{$screenName}</a>";
|
return "<a href='http://twitter.com/#!/{$screenName}' title='{$fullName}'>{$screenName}</a>";
|
||||||
} else {
|
} else {
|
||||||
return "<a href='http://twitter.com/{$screenName}'>{$screenName}</a>";
|
return "<a href='http://twitter.com/#!/{$screenName}'>{$screenName}</a>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,10 +43,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
|
|||||||
*/
|
*/
|
||||||
class TwitterOAuthClient extends OAuthClient
|
class TwitterOAuthClient extends OAuthClient
|
||||||
{
|
{
|
||||||
public static $requestTokenURL = 'https://twitter.com/oauth/request_token';
|
public static $requestTokenURL = 'https://api.twitter.com/oauth/request_token';
|
||||||
public static $authorizeURL = 'https://twitter.com/oauth/authorize';
|
public static $authorizeURL = 'https://api.twitter.com/oauth/authorize';
|
||||||
public static $signinUrl = 'https://twitter.com/oauth/authenticate';
|
public static $signinUrl = 'https://api.twitter.com/oauth/authenticate';
|
||||||
public static $accessTokenURL = 'https://twitter.com/oauth/access_token';
|
public static $accessTokenURL = 'https://api.twitter.com/oauth/access_token';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -157,7 +157,7 @@ class TwitterOAuthClient extends OAuthClient
|
|||||||
*/
|
*/
|
||||||
function verifyCredentials()
|
function verifyCredentials()
|
||||||
{
|
{
|
||||||
$url = 'https://twitter.com/account/verify_credentials.json';
|
$url = 'https://api.twitter.com/1/account/verify_credentials.json';
|
||||||
$response = $this->oAuthGet($url);
|
$response = $this->oAuthGet($url);
|
||||||
$twitter_user = json_decode($response);
|
$twitter_user = json_decode($response);
|
||||||
return $twitter_user;
|
return $twitter_user;
|
||||||
@ -175,7 +175,7 @@ class TwitterOAuthClient extends OAuthClient
|
|||||||
*/
|
*/
|
||||||
function statusesUpdate($status, $params=array())
|
function statusesUpdate($status, $params=array())
|
||||||
{
|
{
|
||||||
$url = 'https://twitter.com/statuses/update.json';
|
$url = 'https://api.twitter.com/1/statuses/update.json';
|
||||||
if (is_numeric($params)) {
|
if (is_numeric($params)) {
|
||||||
$params = array('in_reply_to_status_id' => intval($params));
|
$params = array('in_reply_to_status_id' => intval($params));
|
||||||
}
|
}
|
||||||
@ -200,7 +200,7 @@ class TwitterOAuthClient extends OAuthClient
|
|||||||
function statusesHomeTimeline($since_id = null, $max_id = null,
|
function statusesHomeTimeline($since_id = null, $max_id = null,
|
||||||
$cnt = null, $page = null)
|
$cnt = null, $page = null)
|
||||||
{
|
{
|
||||||
$url = 'https://twitter.com/statuses/home_timeline.json';
|
$url = 'https://api.twitter.com/1/statuses/home_timeline.json';
|
||||||
|
|
||||||
$params = array('include_entities' => 'true');
|
$params = array('include_entities' => 'true');
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ class TwitterOAuthClient extends OAuthClient
|
|||||||
function statusesFriends($id = null, $user_id = null, $screen_name = null,
|
function statusesFriends($id = null, $user_id = null, $screen_name = null,
|
||||||
$page = null)
|
$page = null)
|
||||||
{
|
{
|
||||||
$url = "https://twitter.com/statuses/friends.json";
|
$url = "https://api.twitter.com/1/statuses/friends.json";
|
||||||
|
|
||||||
$params = array();
|
$params = array();
|
||||||
|
|
||||||
@ -273,7 +273,7 @@ class TwitterOAuthClient extends OAuthClient
|
|||||||
function friendsIds($id = null, $user_id = null, $screen_name = null,
|
function friendsIds($id = null, $user_id = null, $screen_name = null,
|
||||||
$page = null)
|
$page = null)
|
||||||
{
|
{
|
||||||
$url = "https://twitter.com/friends/ids.json";
|
$url = "https://api.twitter.com/1/friends/ids.json";
|
||||||
|
|
||||||
$params = array();
|
$params = array();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user