diff --git a/EVENTS.txt b/EVENTS.txt index 9f98637bc6..57f2bf6bcc 100644 --- a/EVENTS.txt +++ b/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 - &$xrd: XRD object being shown - $user: User being shown - + StartXrdActionLinks: About to set links for the XRD object for a user - &$xrd: XRD object 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 - &$xrd: XRD object 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 diff --git a/actions/apiuserprofileimage.php b/actions/apiuserprofileimage.php new file mode 100644 index 0000000000..d2cf9a3e57 --- /dev/null +++ b/actions/apiuserprofileimage.php @@ -0,0 +1,135 @@ +. + * + * @category API + * @package StatusNet + * @author Brion Vibber + * @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 + * @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; + } +} diff --git a/actions/showstream.php b/actions/showstream.php index 702edd7327..14f629b6a8 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -311,7 +311,7 @@ class ProfileNoticeListItem extends DoFollowListItem 'class' => 'url'); if (!empty($this->profile->fullname)) { - $attrs['title'] = $this->getFancyName(); + $attrs['title'] = $this->profile->getFancyName(); } $this->out->elementStart('span', 'repeat'); diff --git a/classes/Fave.php b/classes/Fave.php index 3aa23e7b4e..4a9cfaae06 100644 --- a/classes/Fave.php +++ b/classes/Fave.php @@ -85,6 +85,19 @@ class Fave extends Memcached_DataObject 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 $user_id + * @param $own + * @param $offset + * @param $limit + * @param $since_id + * @param $max_id + * @return + */ function _streamDirect($user_id, $own, $offset, $limit, $since_id, $max_id) { $fav = new Fave(); diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 27bb5d3c9d..f71bfd3da0 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -339,10 +339,14 @@ class Memcached_DataObject extends Safe_DataObject $start = microtime(true); $fail = false; - try { - $result = parent::_query($string); - } catch (Exception $e) { - $fail = $e; + $result = null; + if (Event::handle('StartDBQuery', array($this, $string, &$result))) { + try { + $result = parent::_query($string); + } catch (Exception $e) { + $fail = $e; + } + Event::handle('EndDBQuery', array($this, $string, &$result)); } $delta = microtime(true) - $start; diff --git a/classes/Notice.php b/classes/Notice.php index c1150626b3..6acf0c7c1a 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -654,7 +654,7 @@ class Notice extends Memcached_DataObject $notice->selectAdd(); // clears it $notice->selectAdd('id'); - $notice->orderBy('id DESC'); + $notice->orderBy('created DESC, id DESC'); if (!is_null($offset)) { $notice->limit($offset, $limit); @@ -668,13 +668,8 @@ class Notice extends Memcached_DataObject $notice->whereAdd('is_local !='. Notice::GATEWAY); } - if ($since_id != 0) { - $notice->whereAdd('id > ' . $since_id); - } - - if ($max_id != 0) { - $notice->whereAdd('id <= ' . $max_id); - } + Notice::addWhereSinceId($notice, $since_id); + Notice::addWhereMaxId($notice, $max_id); $ids = array(); @@ -709,19 +704,14 @@ class Notice extends Memcached_DataObject $notice->conversation = $id; - $notice->orderBy('id DESC'); + $notice->orderBy('created DESC, id DESC'); if (!is_null($offset)) { $notice->limit($offset, $limit); } - if ($since_id != 0) { - $notice->whereAdd('id > ' . $since_id); - } - - if ($max_id != 0) { - $notice->whereAdd('id <= ' . $max_id); - } + Notice::addWhereSinceId($notice, $since_id); + Notice::addWhereMaxId($notice, $max_id); $ids = array(); @@ -1695,10 +1685,10 @@ class Notice extends Memcached_DataObject $notice->repeat_of = $this->id; - $notice->orderBy('created'); // NB: asc! + $notice->orderBy('created, id'); // NB: asc! - if (!is_null($offset)) { - $notice->limit($offset, $limit); + if (!is_null($limit)) { + $notice->limit(0, $limit); } $ids = array(); @@ -1978,4 +1968,108 @@ class Notice extends Memcached_DataObject $d = new DateTime($dateStr, new DateTimeZone('UTC')); 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); + } + } } diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php index ea38bb3502..c8031ddf39 100644 --- a/classes/Notice_tag.php +++ b/classes/Notice_tag.php @@ -55,15 +55,10 @@ class Notice_tag extends Memcached_DataObject $nt->selectAdd(); $nt->selectAdd('notice_id'); - if ($since_id != 0) { - $nt->whereAdd('notice_id > ' . $since_id); - } + Notice::addWhereSinceId($nt, $since_id, 'notice_id'); + Notice::addWhereMaxId($nt, $max_id, 'notice_id'); - if ($max_id != 0) { - $nt->whereAdd('notice_id <= ' . $max_id); - } - - $nt->orderBy('notice_id DESC'); + $nt->orderBy('created DESC, notice_id DESC'); if (!is_null($offset)) { $nt->limit($offset, $limit); diff --git a/classes/Profile.php b/classes/Profile.php index 1f959eb0d9..977947b6f7 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -215,26 +215,29 @@ class Profile extends Memcached_DataObject function _streamTaggedDirect($tag, $offset, $limit, $since_id, $max_id) { // XXX It would be nice to do this without a join + // (necessary to do it efficiently on accounts with long history) $notice = new Notice(); $query = "select id from notice join notice_tag on id=notice_id where tag='". $notice->escape($tag) . - "' and profile_id=" . $notice->escape($this->id); + "' and profile_id=" . intval($this->id); - if ($since_id != 0) { - $query .= " and id > $since_id"; + $since = Notice::whereSinceId($since_id, 'id', 'notice.created'); + if ($since) { + $query .= " and ($since)"; } - if ($max_id != 0) { - $query .= " and id <= $max_id"; + $max = Notice::whereMaxId($max_id, 'id', 'notice.created'); + if ($max) { + $query .= " and ($max)"; } - $query .= ' order by id DESC'; + $query .= ' order by notice.created DESC, id DESC'; if (!is_null($offset)) { - $query .= " LIMIT $limit OFFSET $offset"; + $query .= " LIMIT " . intval($limit) . " OFFSET " . intval($offset); } $notice->query($query); @@ -252,58 +255,22 @@ class Profile extends Memcached_DataObject { $notice = new Notice(); - // Temporary hack until notice_profile_id_idx is updated - // 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); + $notice->profile_id = $this->id; - if ($since_id != 0) { - $query .= " and id > $since_id"; - } + $notice->selectAdd(); + $notice->selectAdd('id'); - if ($max_id != 0) { - $query .= " and id <= $max_id"; - } + Notice::addWhereSinceId($notice, $since_id); + Notice::addWhereMaxId($notice, $max_id); - $query .= ' order by id DESC'; + $notice->orderBy('created DESC, id DESC'); - if (!is_null($offset)) { - $query .= " LIMIT $limit OFFSET $offset"; - } - - $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(); + if (!is_null($offset)) { + $notice->limit($offset, $limit); } + $notice->find(); + $ids = array(); while ($notice->fetch()) { diff --git a/classes/Reply.php b/classes/Reply.php index da8a4f685b..371c16cf48 100644 --- a/classes/Reply.php +++ b/classes/Reply.php @@ -50,15 +50,10 @@ class Reply extends Memcached_DataObject $reply = new Reply(); $reply->profile_id = $user_id; - if ($since_id != 0) { - $reply->whereAdd('notice_id > ' . $since_id); - } + Notice::addWhereSinceId($reply, $since_id, 'notice_id', 'modified'); + Notice::addWhereMaxId($reply, $max_id, 'notice_id', 'modified'); - if ($max_id != 0) { - $reply->whereAdd('notice_id <= ' . $max_id); - } - - $reply->orderBy('notice_id DESC'); + $reply->orderBy('modified DESC, notice_id DESC'); if (!is_null($offset)) { $reply->limit($offset, $limit); diff --git a/classes/User.php b/classes/User.php index 4baa6d1b33..04b9b4cc68 100644 --- a/classes/User.php +++ b/classes/User.php @@ -750,19 +750,14 @@ class User extends Memcached_DataObject $notice->profile_id = $this->id; $notice->whereAdd('repeat_of IS NOT NULL'); - $notice->orderBy('id DESC'); + $notice->orderBy('created DESC, id DESC'); if (!is_null($offset)) { $notice->limit($offset, $limit); } - if ($since_id != 0) { - $notice->whereAdd('id > ' . $since_id); - } - - if ($max_id != 0) { - $notice->whereAdd('id <= ' . $max_id); - } + Notice::addWhereSinceId($notice, $since_id); + Notice::addWhereMaxId($notice, $max_id); $ids = array(); @@ -795,17 +790,17 @@ class User extends Memcached_DataObject 'FROM notice original JOIN notice rept ON original.id = rept.repeat_of ' . 'WHERE original.profile_id = ' . $this->id . ' '; - if ($since_id != 0) { - $qry .= 'AND original.id > ' . $since_id . ' '; + $since = Notice::whereSinceId($since_id, 'original.id', 'original.created'); + if ($since) { + $qry .= "AND ($since) "; } - if ($max_id != 0) { - $qry .= 'AND original.id <= ' . $max_id . ' '; + $max = Notice::whereMaxId($max_id, 'original.id', 'original.created'); + if ($max) { + $qry .= "AND ($max) "; } - // NOTE: we sort by fave time, not by notice time! - - $qry .= 'ORDER BY original.id DESC '; + $qry .= 'ORDER BY original.created, original.id DESC '; if (!is_null($offset)) { $qry .= "LIMIT $limit OFFSET $offset"; diff --git a/classes/User_group.php b/classes/User_group.php index 60217e960e..cffc786458 100644 --- a/classes/User_group.php +++ b/classes/User_group.php @@ -100,15 +100,10 @@ class User_group extends Memcached_DataObject $inbox->selectAdd(); $inbox->selectAdd('notice_id'); - if ($since_id != 0) { - $inbox->whereAdd('notice_id > ' . $since_id); - } + Notice::addWhereSinceId($inbox, $since_id, 'notice_id'); + Notice::addWhereMaxId($inbox, $max_id, 'notice_id'); - if ($max_id != 0) { - $inbox->whereAdd('notice_id <= ' . $max_id); - } - - $inbox->orderBy('notice_id DESC'); + $inbox->orderBy('created DESC, notice_id DESC'); if (!is_null($offset)) { $inbox->limit($offset, $limit); diff --git a/db/096to097.sql b/db/096to097.sql new file mode 100644 index 0000000000..209a3a8811 --- /dev/null +++ b/db/096to097.sql @@ -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); diff --git a/db/statusnet.sql b/db/statusnet.sql index 5426ace92b..debe6f095c 100644 --- a/db/statusnet.sql +++ b/db/statusnet.sql @@ -126,11 +126,21 @@ create table notice ( location_ns integer comment 'namespace for location', 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_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_repeatof_idx (repeat_of), + FULLTEXT(content) ) ENGINE=MyISAM CHARACTER SET utf8 COLLATE utf8_general_ci; @@ -151,7 +161,10 @@ create table reply ( constraint primary key (notice_id, profile_id), index reply_notice_id_idx (notice_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; @@ -296,7 +309,10 @@ create table notice_tag ( constraint primary key (tag, notice_id), 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; /* Synching with foreign services */ @@ -442,7 +458,10 @@ create table group_member ( constraint primary key (group_id, 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; @@ -463,7 +482,10 @@ create table group_inbox ( constraint primary key (group_id, notice_id), 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; @@ -603,7 +625,8 @@ create table profile_role ( role varchar(32) not null comment 'string representing the role', 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; diff --git a/lib/router.php b/lib/router.php index e40eb517f9..b7159e6130 100644 --- a/lib/router.php +++ b/lib/router.php @@ -529,6 +529,11 @@ class Router 'id' => Nickname::INPUT_FMT, '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 $m->connect('api/direct_messages.:format', diff --git a/plugins/LdapCommon/LdapCommon.php b/plugins/LdapCommon/LdapCommon.php index 579fe4b64b..3afcd824f9 100644 --- a/plugins/LdapCommon/LdapCommon.php +++ b/plugins/LdapCommon/LdapCommon.php @@ -140,7 +140,7 @@ class LdapCommon function checkPassword($username, $password) { - $entry = $this->get_user($username); + $entry = $this->get_user($username,array('dn' => 'dn')); if(!$entry){ return false; }else{ @@ -168,7 +168,7 @@ class LdapCommon //throw new Exception(_('Sorry, changing LDAP passwords is not supported at this time')); return false; } - $entry = $this->get_user($username); + $entry = $this->get_user($username,array('dn' => 'dn')); if(!$entry){ return false; }else{ diff --git a/plugins/SQLProfile/SQLProfilePlugin.php b/plugins/SQLProfile/SQLProfilePlugin.php new file mode 100644 index 0000000000..1e49d9005e --- /dev/null +++ b/plugins/SQLProfile/SQLProfilePlugin.php @@ -0,0 +1,66 @@ +. + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Check DB queries for filesorts and such and log em. + * + * @package SQLProfilePlugin + * @maintainer Brion Vibber + */ +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; + } +} diff --git a/plugins/TwitterBridge/scripts/initialize_notice_to_status.php b/plugins/TwitterBridge/scripts/initialize_notice_to_status.php index d1acfd53fa..f4a60c479d 100644 --- a/plugins/TwitterBridge/scripts/initialize_notice_to_status.php +++ b/plugins/TwitterBridge/scripts/initialize_notice_to_status.php @@ -44,7 +44,7 @@ $n->query('SELECT notice.id, notice.uri ' . 'AND notice_to_status.status_id IS NULL'); 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]; Notice_to_status::saveNew($n->id, $status_id); } diff --git a/plugins/TwitterBridge/twitter.php b/plugins/TwitterBridge/twitter.php index e8d11f3b6a..a993f8ff8f 100644 --- a/plugins/TwitterBridge/twitter.php +++ b/plugins/TwitterBridge/twitter.php @@ -45,7 +45,7 @@ function add_twitter_user($twitter_id, $screen_name) $fuser = new Foreign_user(); $fuser->nickname = $screen_name; - $fuser->uri = 'http://twitter.com/' . $screen_name; + $fuser->uri = 'http://twitter.com/#!/' . $screen_name; $fuser->id = $twitter_id; $fuser->service = TWITTER_SERVICE; $fuser->created = common_sql_now(); diff --git a/plugins/TwitterBridge/twitterimport.php b/plugins/TwitterBridge/twitterimport.php index 143543d8ef..3a4c0c43c0 100644 --- a/plugins/TwitterBridge/twitterimport.php +++ b/plugins/TwitterBridge/twitterimport.php @@ -207,7 +207,7 @@ class TwitterImport */ function makeStatusURI($username, $id) { - return 'http://twitter.com/' + return 'http://twitter.com/#!/' . $username . '/status/' . $id; @@ -264,7 +264,7 @@ class TwitterImport function ensureProfile($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); if (!empty($profile)) { @@ -618,15 +618,15 @@ class TwitterImport static function tagLink($tag) { - return "{$tag}"; + return "{$tag}"; } static function atLink($screenName, $fullName=null) { if (!empty($fullName)) { - return "{$screenName}"; + return "{$screenName}"; } else { - return "{$screenName}"; + return "{$screenName}"; } } diff --git a/plugins/TwitterBridge/twitteroauthclient.php b/plugins/TwitterBridge/twitteroauthclient.php index 345510a0d0..a17911b03e 100644 --- a/plugins/TwitterBridge/twitteroauthclient.php +++ b/plugins/TwitterBridge/twitteroauthclient.php @@ -43,10 +43,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { */ class TwitterOAuthClient extends OAuthClient { - public static $requestTokenURL = 'https://twitter.com/oauth/request_token'; - public static $authorizeURL = 'https://twitter.com/oauth/authorize'; - public static $signinUrl = 'https://twitter.com/oauth/authenticate'; - public static $accessTokenURL = 'https://twitter.com/oauth/access_token'; + public static $requestTokenURL = 'https://api.twitter.com/oauth/request_token'; + public static $authorizeURL = 'https://api.twitter.com/oauth/authorize'; + public static $signinUrl = 'https://api.twitter.com/oauth/authenticate'; + public static $accessTokenURL = 'https://api.twitter.com/oauth/access_token'; /** * Constructor @@ -157,7 +157,7 @@ class TwitterOAuthClient extends OAuthClient */ function verifyCredentials() { - $url = 'https://twitter.com/account/verify_credentials.json'; + $url = 'https://api.twitter.com/1/account/verify_credentials.json'; $response = $this->oAuthGet($url); $twitter_user = json_decode($response); return $twitter_user; @@ -175,7 +175,7 @@ class TwitterOAuthClient extends OAuthClient */ 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)) { $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, $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'); @@ -235,7 +235,7 @@ class TwitterOAuthClient extends OAuthClient function statusesFriends($id = null, $user_id = null, $screen_name = null, $page = null) { - $url = "https://twitter.com/statuses/friends.json"; + $url = "https://api.twitter.com/1/statuses/friends.json"; $params = array(); @@ -273,7 +273,7 @@ class TwitterOAuthClient extends OAuthClient function friendsIds($id = null, $user_id = null, $screen_name = null, $page = null) { - $url = "https://twitter.com/friends/ids.json"; + $url = "https://api.twitter.com/1/friends/ids.json"; $params = array();