diff options
-rw-r--r-- | actions/shownotice.php | 8 | ||||
-rw-r--r-- | actions/twitapistatuses.php | 10 | ||||
-rwxr-xr-x | classes/Deleted_notice.php | 44 | ||||
-rw-r--r-- | classes/Notice.php | 17 | ||||
-rw-r--r-- | classes/Notice_inbox.php | 41 | ||||
-rwxr-xr-x[-rw-r--r--] | classes/laconica.ini | 11 | ||||
-rw-r--r-- | db/laconica.sql | 14 | ||||
-rw-r--r-- | extlib/facebook/facebook.php | 6 | ||||
-rwxr-xr-x | extlib/facebook/facebookapi_php5_restlib.php | 354 | ||||
-rw-r--r-- | plugins/FBConnect/FBConnectPlugin.php | 14 | ||||
-rw-r--r-- | plugins/FBConnect/README | 77 | ||||
-rw-r--r-- | scripts/triminboxes.php | 40 |
12 files changed, 516 insertions, 120 deletions
diff --git a/actions/shownotice.php b/actions/shownotice.php index 1ec38a76b..fba1468aa 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -84,7 +84,13 @@ class ShownoticeAction extends OwnerDesignAction $this->notice = Notice::staticGet($id); if (empty($this->notice)) { - $this->clientError(_('No such notice.'), 404); + // Did we used to have it, and it got deleted? + $deleted = Deleted_notice::staticGet($id); + if (!empty($deleted)) { + $this->clientError(_('Notice deleted.'), 410); + } else { + $this->clientError(_('No such notice.'), 404); + } return false; } diff --git a/actions/twitapistatuses.php b/actions/twitapistatuses.php index c9943698d..e3d366ecc 100644 --- a/actions/twitapistatuses.php +++ b/actions/twitapistatuses.php @@ -396,8 +396,14 @@ class TwitapistatusesAction extends TwitterapiAction } else { // XXX: Twitter just sets a 404 header and doens't bother // to return an err msg - $this->clientError(_('No status with that ID found.'), - 404, $apidata['content-type']); + $deleted = Deleted_notice::staticGet($notice_id); + if (!empty($deleted)) { + $this->clientError(_('Status deleted.'), + 410, $apidata['content-type']); + } else { + $this->clientError(_('No status with that ID found.'), + 404, $apidata['content-type']); + } } } diff --git a/classes/Deleted_notice.php b/classes/Deleted_notice.php new file mode 100755 index 000000000..474d0b6f4 --- /dev/null +++ b/classes/Deleted_notice.php @@ -0,0 +1,44 @@ +<?php +/* + * Laconica - a distributed open-source microblogging tool + * Copyright (C) 2008, 2009, Control Yourself, 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('LACONICA')) { exit(1); } + +/** + * Table Definition for notice + */ +require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; + +class Deleted_notice extends Memcached_DataObject +{ + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'deleted_notice'; // table name + public $id; // int(4) primary_key not_null + public $profile_id; // int(4) not_null + public $uri; // varchar(255) unique_key + public $created; // datetime() not_null + public $deleted; // datetime() not_null + + /* Static get */ + function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Deleted_notice',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE +} diff --git a/classes/Notice.php b/classes/Notice.php index 08125cf7b..c6ad7d6c4 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -74,7 +74,21 @@ class Notice extends Memcached_DataObject $this->blowFavesCache(true); $this->blowSubsCache(true); + // For auditing purposes, save a record that the notice + // was deleted. + + $deleted = new Deleted_notice(); + + $deleted->id = $this->id; + $deleted->profile_id = $this->profile_id; + $deleted->uri = $this->uri; + $deleted->created = $this->created; + $deleted->deleted = common_sql_now(); + $this->query('BEGIN'); + + $deleted->insert(); + //Null any notices that are replies to this notice $this->query(sprintf("UPDATE notice set reply_to = null WHERE reply_to = %d", $this->id)); $related = array('Reply', @@ -875,6 +889,9 @@ class Notice extends Memcached_DataObject } $qry .= '('.$id.', '.$this->id.', '.$source.', "'.$this->created.'") '; $cnt++; + if (rand() % NOTICE_INBOX_SOFT_LIMIT == 0) { + Notice_inbox::gc($id); + } if ($cnt >= MAX_BOXCARS) { $inbox = new Notice_inbox(); $inbox->query($qry); diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php index 940381f84..2af34b1a4 100644 --- a/classes/Notice_inbox.php +++ b/classes/Notice_inbox.php @@ -24,6 +24,10 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; // We keep 5 pages of inbox notices in memcache, +1 for pagination check define('INBOX_CACHE_WINDOW', 101); +define('NOTICE_INBOX_GC_BOXCAR', 128); +define('NOTICE_INBOX_GC_MAX', 12800); +define('NOTICE_INBOX_LIMIT', 1000); +define('NOTICE_INBOX_SOFT_LIMIT', 1000); define('NOTICE_INBOX_SOURCE_SUB', 1); define('NOTICE_INBOX_SOURCE_GROUP', 2); @@ -100,4 +104,41 @@ class Notice_inbox extends Memcached_DataObject { return Memcached_DataObject::pkeyGet('Notice_inbox', $kv); } + + static function gc($user_id) + { + $entry = new Notice_inbox(); + $entry->user_id = $user_id; + $entry->orderBy('created DESC'); + $entry->limit(NOTICE_INBOX_LIMIT - 1, NOTICE_INBOX_GC_MAX); + + $total = $entry->find(); + + if ($total > 0) { + $notices = array(); + $cnt = 0; + while ($entry->fetch()) { + $notices[] = $entry->notice_id; + $cnt++; + if ($cnt >= NOTICE_INBOX_GC_BOXCAR) { + self::deleteMatching($user_id, $notices); + $notices = array(); + $cnt = 0; + } + } + + if ($cnt > 0) { + self::deleteMatching($user_id, $notices); + $notices = array(); + } + } + } + + static function deleteMatching($user_id, $notices) + { + $entry = new Notice_inbox(); + return $entry->query('DELETE FROM notice_inbox '. + 'WHERE user_id = ' . $user_id . ' ' . + 'AND notice_id in ('.implode(',', $notices).')'); + } } diff --git a/classes/laconica.ini b/classes/laconica.ini index 766bed75d..f8d4eebd3 100644..100755 --- a/classes/laconica.ini +++ b/classes/laconica.ini @@ -38,6 +38,17 @@ modified = 384 [consumer__keys] consumer_key = K +[deleted_notice] +id = 129 +profile_id = 129 +uri = 2 +created = 142 +deleted = 142 + +[deleted_notice__keys] +id = K +uri = U + [design] id = 129 backgroundcolor = 1 diff --git a/db/laconica.sql b/db/laconica.sql index 2c04f680a..f01107176 100644 --- a/db/laconica.sql +++ b/db/laconica.sql @@ -534,4 +534,16 @@ create table session ( index session_modified_idx (modified) -) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
\ No newline at end of file +) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; + +create table deleted_notice ( + + id integer primary key comment 'identity of notice', + profile_id integer not null comment 'author of the notice', + uri varchar(255) unique key comment 'universally unique identifier, usually a tag URI', + created datetime not null comment 'date the notice record was created', + deleted datetime not null comment 'date the notice record was created', + + index deleted_notice_profile_id_idx (profile_id) + +) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; diff --git a/extlib/facebook/facebook.php b/extlib/facebook/facebook.php index fee1dd086..016e8e8e0 100644 --- a/extlib/facebook/facebook.php +++ b/extlib/facebook/facebook.php @@ -107,13 +107,13 @@ class Facebook { * @param bool resolve_auth_token convert an auth token into a session */ public function validate_fb_params($resolve_auth_token=true) { - $this->fb_params = $this->get_valid_fb_params($_POST, 48*3600, 'fb_sig'); + $this->fb_params = $this->get_valid_fb_params($_POST, 48 * 3600, 'fb_sig'); // note that with preload FQL, it's possible to receive POST params in // addition to GET, so use a different prefix to differentiate them if (!$this->fb_params) { - $fb_params = $this->get_valid_fb_params($_GET, 48*3600, 'fb_sig'); - $fb_post_params = $this->get_valid_fb_params($_POST, 48*3600, 'fb_post_sig'); + $fb_params = $this->get_valid_fb_params($_GET, 48 * 3600, 'fb_sig'); + $fb_post_params = $this->get_valid_fb_params($_POST, 48 * 3600, 'fb_post_sig'); $this->fb_params = array_merge($fb_params, $fb_post_params); } diff --git a/extlib/facebook/facebookapi_php5_restlib.php b/extlib/facebook/facebookapi_php5_restlib.php index 3fec06e8a..55cb7fb86 100755 --- a/extlib/facebook/facebookapi_php5_restlib.php +++ b/extlib/facebook/facebookapi_php5_restlib.php @@ -55,6 +55,7 @@ class FacebookRestClient { private $pending_batch; private $call_as_apikey; private $use_curl_if_available; + private $format = null; const BATCH_MODE_DEFAULT = 0; const BATCH_MODE_SERVER_PARALLEL = 0; @@ -178,39 +179,32 @@ function toggleDisplay(id, type) { private function execute_server_side_batch() { $item_count = count($this->batch_queue); $method_feed = array(); - foreach($this->batch_queue as $batch_item) { + foreach ($this->batch_queue as $batch_item) { $method = $batch_item['m']; $params = $batch_item['p']; - $this->finalize_params($method, $params); - $method_feed[] = $this->create_post_string($method, $params); + list($get, $post) = $this->finalize_params($method, $params); + $method_feed[] = $this->create_url_string(array_merge($post, $get)); } - $method_feed_json = json_encode($method_feed); - $serial_only = ($this->batch_mode == FacebookRestClient::BATCH_MODE_SERIAL_ONLY); - $params = array('method_feed' => $method_feed_json, - 'serial_only' => $serial_only); - if ($this->call_as_apikey) { - $params['call_as_apikey'] = $this->call_as_apikey; - } - - $xml = $this->post_request('batch.run', $params); - - $result = $this->convert_xml_to_result($xml, 'batch.run', $params); + $params = array('method_feed' => json_encode($method_feed), + 'serial_only' => $serial_only, + 'format' => $this->format); + $result = $this->call_method('facebook.batch.run', $params); if (is_array($result) && isset($result['error_code'])) { throw new FacebookRestClientException($result['error_msg'], $result['error_code']); } - for($i = 0; $i < $item_count; $i++) { + for ($i = 0; $i < $item_count; $i++) { $batch_item = $this->batch_queue[$i]; - $batch_item_result_xml = $result[$i]; - $batch_item_result = $this->convert_xml_to_result($batch_item_result_xml, - $batch_item['m'], - $batch_item['p']); + $batch_item['p']['format'] = $this->format; + $batch_item_result = $this->convert_result($result[$i], + $batch_item['m'], + $batch_item['p']); if (is_array($batch_item_result) && isset($batch_item_result['error_code'])) { @@ -516,12 +510,20 @@ function toggleDisplay(id, type) { * behalf of app. Successful creation guarantees app will be admin. * * @param assoc array $event_info json encoded event information + * @param string $file (Optional) filename of picture to set * * @return int event id */ - public function &events_create($event_info) { - return $this->call_method('facebook.events.create', + public function events_create($event_info, $file = null) { + if ($file) { + return $this->call_upload_method('facebook.events.create', + array('event_info' => $event_info), + $file, + Facebook::get_facebook_url('api-photo') . '/restserver.php'); + } else { + return $this->call_method('facebook.events.create', array('event_info' => $event_info)); + } } /** @@ -529,13 +531,21 @@ function toggleDisplay(id, type) { * * @param int $eid event id * @param assoc array $event_info json encoded event information + * @param string $file (Optional) filename of new picture to set * * @return bool true if successful */ - public function &events_edit($eid, $event_info) { - return $this->call_method('facebook.events.edit', + public function events_edit($eid, $event_info, $file = null) { + if ($file) { + return $this->call_upload_method('facebook.events.edit', + array('eid' => $eid, 'event_info' => $event_info), + $file, + Facebook::get_facebook_url('api-photo') . '/restserver.php'); + } else { + return $this->call_method('facebook.events.edit', array('eid' => $eid, - 'event_info' => $event_info)); + 'event_info' => $event_info)); + } } /** @@ -935,7 +945,7 @@ function toggleDisplay(id, type) { /** * Makes an FQL query. This is a generalized way of accessing all the data * in the API, as an alternative to most of the other method calls. More - * info at http://developers.facebook.com/documentation.php?v=1.0&doc=fql + * info at http://wiki.developers.facebook.com/index.php/FQL * * @param string $query the query to evaluate * @@ -947,6 +957,21 @@ function toggleDisplay(id, type) { } /** + * Makes a set of FQL queries in parallel. This method takes a dictionary + * of FQL queries where the keys are names for the queries. Results from + * one query can be used within another query to fetch additional data. More + * info about FQL queries at http://wiki.developers.facebook.com/index.php/FQL + * + * @param string $queries JSON-encoded dictionary of queries to evaluate + * + * @return array generalized array representing the results + */ + public function &fql_multiquery($queries) { + return $this->call_method('facebook.fql.multiquery', + array('queries' => $queries)); + } + + /** * Returns whether or not pairs of users are friends. * Note that the Facebook friend relationship is symmetric. * @@ -995,6 +1020,23 @@ function toggleDisplay(id, type) { } /** + * Returns the mutual friends between the target uid and a source uid or + * the current session user. + * + * @param int $target_uid Target uid for which mutual friends will be found. + * @param int $source_uid (optional) Source uid for which mutual friends will + * be found. If no source_uid is specified, + * source_id will default to the session + * user. + * @return array An array of friend uids + */ + public function &friends_getMutualFriends($target_uid, $source_uid = null) { + return $this->call_method('facebook.friends.getMutualFriends', + array("target_uid" => $target_uid, + "source_uid" => $source_uid)); + } + + /** * Returns the set of friend lists for the current session user. * * @return array An array of friend list objects @@ -1169,6 +1211,44 @@ function toggleDisplay(id, type) { } /** + * Payments Order API + */ + + /** + * Set Payments properties for an app. + * + * @param properties a map from property names to values + * @return true on success + */ + public function payments_setProperties($properties) { + return $this->call_method ('facebook.payments.setProperties', + array('properties' => json_encode($properties))); + } + + public function payments_getOrderDetails($order_id) { + return json_decode($this->call_method( + 'facebook.payments.getOrderDetails', + array('order_id' => $order_id)), true); + } + + public function payments_updateOrder($order_id, $status, + $params) { + return $this->call_method('facebook.payments.updateOrder', + array('order_id' => $order_id, + 'status' => $status, + 'params' => json_encode($params))); + } + + public function payments_getOrders($status, $start_time, + $end_time, $test_mode=false) { + return json_decode($this->call_method('facebook.payments.getOrders', + array('status' => $status, + 'start_time' => $start_time, + 'end_time' => $end_time, + 'test_mode' => $test_mode)), true); + } + + /** * Creates a note with the specified title and content. * * @param string $title Title of the note. @@ -1233,7 +1313,6 @@ function toggleDisplay(id, type) { * notes. */ public function ¬es_get($uid, $note_ids = null) { - return $this->call_method('notes.get', array('uid' => $uid, 'note_ids' => $note_ids)); @@ -1632,6 +1711,63 @@ function toggleDisplay(id, type) { } /** + * Gets the comments for a particular xid. This is essentially a wrapper + * around the comment FQL table. + * + * @param string $xid external id associated with the comments + * + * @return array of comment objects + */ + public function &comments_get($xid) { + $args = array('xid' => $xid); + return $this->call_method('facebook.comments.get', $args); + } + + /** + * Add a comment to a particular xid on behalf of a user. If called + * without an app_secret (with session secret), this will only work + * for the session user. + * + * @param string $xid external id associated with the comments + * @param string $text text of the comment + * @param int $uid user adding the comment (def: session user) + * @param string $title optional title for the stream story + * @param string $url optional url for the stream story + * @param bool $publish_to_stream publish a feed story about this comment? + * a link will be generated to title/url in the story + * + * @return string comment_id associated with the comment + */ + public function &comments_add($xid, $text, $uid=0, $title='', $url='', + $publish_to_stream=false) { + $args = array( + 'xid' => $xid, + 'uid' => $this->get_uid($uid), + 'text' => $text, + 'title' => $title, + 'url' => $url, + 'publish_to_stream' => $publish_to_stream); + + return $this->call_method('facebook.comments.add', $args); + } + + /** + * Remove a particular comment. + * + * @param string $xid the external id associated with the comments + * @param string $comment_id id of the comment to remove (returned by + * comments.add and comments.get) + * + * @return boolean + */ + public function &comments_remove($xid, $comment_id) { + $args = array( + 'xid' => $xid, + 'comment_id' => $comment_id); + return $this->call_method('facebook.comments.remove', $args); + } + + /** * Gets the stream on behalf of a user using a set of users. This * call will return the latest $limit queries between $start_time * and $end_time. @@ -1642,11 +1778,16 @@ function toggleDisplay(id, type) { * @param int $end_time end time to look for stories (def: now) * @param int $limit number of stories to attempt to fetch (def: 30) * @param string $filter_key key returned by stream.getFilters to fetch + * @param array $metadata metadata to include with the return, allows + * requested metadata to be returned, such as + * profiles, albums, photo_tags * * @return array( - * 'posts' => array of posts, - * 'profiles' => array of profile metadata of users/pages in posts - * 'albums' => array of album metadata in posts + * 'posts' => array of posts, + * // if requested, the following data may be returned + * 'profiles' => array of profile metadata of users/pages in posts + * 'albums' => array of album metadata in posts + * 'photo_tags' => array of photo_tags for photos in posts * ) */ public function &stream_get($viewer_id = null, @@ -2849,6 +2990,7 @@ function toggleDisplay(id, type) { array('uids' => $uids ? json_encode($uids) : null)); } + /* UTILITY FUNCTIONS */ /** @@ -2862,18 +3004,15 @@ function toggleDisplay(id, type) { * See: http://wiki.developers.facebook.com/index.php/Using_batching_API */ public function &call_method($method, $params = array()) { + if ($this->format) { + $params['format'] = $this->format; + } if (!$this->pending_batch()) { if ($this->call_as_apikey) { $params['call_as_apikey'] = $this->call_as_apikey; } $data = $this->post_request($method, $params); - if (empty($params['format']) || strtolower($params['format']) != 'json') { - $result = $this->convert_xml_to_result($data, $method, $params); - } - else { - $result = json_decode($data, true); - } - + $result = $this->convert_result($data, $method, $params); if (is_array($result) && isset($result['error_code'])) { throw new FacebookRestClientException($result['error_msg'], $result['error_code']); @@ -2888,6 +3027,32 @@ function toggleDisplay(id, type) { return $result; } + protected function convert_result($data, $method, $params) { + $is_xml = (empty($params['format']) || + strtolower($params['format']) != 'json'); + return ($is_xml) ? $this->convert_xml_to_result($data, $method, $params) + : json_decode($data, true); + } + + /** + * Change the response format + * + * @param string $format The response format (json, xml) + */ + public function setFormat($format) { + $this->format = $format; + return $this; + } + + /** + * get the current response serialization format + * + * @return string 'xml', 'json', or null (which means 'xml') + */ + public function getFormat() { + return $this->format; + } + /** * Calls the specified file-upload POST method with the specified parameters * @@ -2906,8 +3071,14 @@ function toggleDisplay(id, type) { throw new FacebookRestClientException($description, $code); } - $xml = $this->post_upload_request($method, $params, $file, $server_addr); - $result = $this->convert_xml_to_result($xml, $method, $params); + if ($this->format) { + $params['format'] = $this->format; + } + $data = $this->post_upload_request($method, + $params, + $file, + $server_addr); + $result = $this->convert_result($data, $method, $params); if (is_array($result) && isset($result['error_code'])) { throw new FacebookRestClientException($result['error_msg'], @@ -2946,11 +3117,13 @@ function toggleDisplay(id, type) { return $result; } - private function finalize_params($method, &$params) { - $this->add_standard_params($method, $params); + protected function finalize_params($method, $params) { + list($get, $post) = $this->add_standard_params($method, $params); // we need to do this before signing the params - $this->convert_array_values_to_json($params); - $params['sig'] = Facebook::generate_sig($params, $this->secret); + $this->convert_array_values_to_json($post); + $post['sig'] = Facebook::generate_sig(array_merge($get, $post), + $this->secret); + return array($get, $post); } private function convert_array_values_to_json(&$params) { @@ -2961,28 +3134,38 @@ function toggleDisplay(id, type) { } } - private function add_standard_params($method, &$params) { + /** + * Add the generally required params to our request. + * Params method, api_key, and v should be sent over as get. + */ + private function add_standard_params($method, $params) { + $post = $params; + $get = array(); if ($this->call_as_apikey) { - $params['call_as_apikey'] = $this->call_as_apikey; + $get['call_as_apikey'] = $this->call_as_apikey; } - $params['method'] = $method; - $params['session_key'] = $this->session_key; - $params['api_key'] = $this->api_key; - $params['call_id'] = microtime(true); - if ($params['call_id'] <= $this->last_call_id) { - $params['call_id'] = $this->last_call_id + 0.001; + $get['method'] = $method; + $get['session_key'] = $this->session_key; + $get['api_key'] = $this->api_key; + $post['call_id'] = microtime(true); + if ($post['call_id'] <= $this->last_call_id) { + $post['call_id'] = $this->last_call_id + 0.001; } - $this->last_call_id = $params['call_id']; - if (!isset($params['v'])) { - $params['v'] = '1.0'; + $this->last_call_id = $post['call_id']; + if (isset($post['v'])) { + $get['v'] = $post['v']; + unset($post['v']); + } else { + $get['v'] = '1.0'; } if (isset($this->use_ssl_resources) && $this->use_ssl_resources) { - $params['return_ssl_resources'] = true; + $post['return_ssl_resources'] = true; } + return array($get, $post); } - private function create_post_string($method, $params) { + private function create_url_string($params) { $post_params = array(); foreach ($params as $key => &$val) { $post_params[] = $key.'='.urlencode($val); @@ -3022,48 +3205,64 @@ function toggleDisplay(id, type) { } public function post_request($method, $params) { - $this->finalize_params($method, $params); - $post_string = $this->create_post_string($method, $params); + list($get, $post) = $this->finalize_params($method, $params); + $post_string = $this->create_url_string($post); + $get_string = $this->create_url_string($get); + $url_with_get = $this->server_addr . '?' . $get_string; if ($this->use_curl_if_available && function_exists('curl_init')) { $useragent = 'Facebook API PHP5 Client 1.1 (curl) ' . phpversion(); $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $this->server_addr); + curl_setopt($ch, CURLOPT_URL, $url_with_get); curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERAGENT, $useragent); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_TIMEOUT, 30); - $result = curl_exec($ch); + $result = $this->curl_exec($ch); curl_close($ch); } else { $content_type = 'application/x-www-form-urlencoded'; $content = $post_string; $result = $this->run_http_post_transaction($content_type, $content, - $this->server_addr); + $url_with_get); } return $result; } + /** + * execute a curl transaction -- this exists mostly so subclasses can add + * extra options and/or process the response, if they wish. + * + * @param resource $ch a curl handle + */ + protected function curl_exec($ch) { + $result = curl_exec($ch); + return $result; + } + private function post_upload_request($method, $params, $file, $server_addr = null) { $server_addr = $server_addr ? $server_addr : $this->server_addr; - $this->finalize_params($method, $params); + list($get, $post) = $this->finalize_params($method, $params); + $get_string = $this->create_url_string($get); + $url_with_get = $server_addr . '?' . $get_string; if ($this->use_curl_if_available && function_exists('curl_init')) { // prepending '@' causes cURL to upload the file; the key is ignored. - $params['_file'] = '@' . $file; + $post['_file'] = '@' . $file; $useragent = 'Facebook API PHP5 Client 1.1 (curl) ' . phpversion(); $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $server_addr); + curl_setopt($ch, CURLOPT_URL, $url_with_get); // this has to come before the POSTFIELDS set! - curl_setopt($ch, CURLOPT_POST, 1 ); + curl_setopt($ch, CURLOPT_POST, 1); // passing an array gets curl to use the multipart/form-data content type - curl_setopt($ch, CURLOPT_POSTFIELDS, $params); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERAGENT, $useragent); - $result = curl_exec($ch); + $result = $this->curl_exec($ch); curl_close($ch); } else { - $result = $this->run_multipart_http_transaction($method, $params, $file, $server_addr); + $result = $this->run_multipart_http_transaction($method, $post, + $file, $url_with_get); } return $result; } @@ -3110,7 +3309,7 @@ function toggleDisplay(id, type) { } } - private function get_uid($uid) { + protected function get_uid($uid) { return $uid ? $uid : $this->user; } } @@ -3145,6 +3344,7 @@ class FacebookAPIErrorCodes { const API_EC_DEPRECATED = 11; const API_EC_VERSION = 12; const API_EC_INTERNAL_FQL_ERROR = 13; + const API_EC_HOST_PUP = 14; /* * PARAMETER ERRORS @@ -3179,6 +3379,7 @@ class FacebookAPIErrorCodes { const API_EC_PERMISSION = 200; const API_EC_PERMISSION_USER = 210; const API_EC_PERMISSION_NO_DEVELOPERS = 211; + const API_EC_PERMISSION_OFFLINE_ACCESS = 212; const API_EC_PERMISSION_ALBUM = 220; const API_EC_PERMISSION_PHOTO = 221; const API_EC_PERMISSION_MESSAGE = 230; @@ -3267,6 +3468,7 @@ class FacebookAPIErrorCodes { const FQL_EC_DEPRECATED_TABLE = 611; const FQL_EC_EXTENDED_PERMISSION = 612; const FQL_EC_RATE_LIMIT_EXCEEDED = 613; + const FQL_EC_UNRESOLVED_DEPENDENCY = 614; const API_EC_REF_SET_FAILED = 700; @@ -3319,6 +3521,21 @@ class FacebookAPIErrorCodes { const API_EC_LIVEMESSAGE_MESSAGE_TOO_LONG = 1102; /* + * PAYMENTS API ERRORS + */ + const API_EC_PAYMENTS_UNKNOWN = 1150; + const API_EC_PAYMENTS_APP_INVALID = 1151; + const API_EC_PAYMENTS_DATABASE = 1152; + const API_EC_PAYMENTS_PERMISSION_DENIED = 1153; + const API_EC_PAYMENTS_APP_NO_RESPONSE = 1154; + const API_EC_PAYMENTS_APP_ERROR_RESPONSE = 1155; + const API_EC_PAYMENTS_INVALID_ORDER = 1156; + const API_EC_PAYMENTS_INVALID_PARAM = 1157; + const API_EC_PAYMENTS_INVALID_OPERATION = 1158; + const API_EC_PAYMENTS_PAYMENT_FAILED = 1159; + const API_EC_PAYMENTS_DISABLED = 1160; + + /* * CONNECT SESSION ERRORS */ const API_EC_CONNECT_FEED_DISABLED = 1300; @@ -3347,6 +3564,7 @@ class FacebookAPIErrorCodes { const API_EC_COMMENTS_INVALID_XID = 1703; const API_EC_COMMENTS_INVALID_UID = 1704; const API_EC_COMMENTS_INVALID_POST = 1705; + const API_EC_COMMENTS_INVALID_REMOVE = 1706; /** * This array is no longer maintained; to view the description of an error diff --git a/plugins/FBConnect/FBConnectPlugin.php b/plugins/FBConnect/FBConnectPlugin.php index 65870a187..2e32ad198 100644 --- a/plugins/FBConnect/FBConnectPlugin.php +++ b/plugins/FBConnect/FBConnectPlugin.php @@ -122,7 +122,9 @@ class FBConnectPlugin extends Plugin FB_RequireFeatures( ["XFBML"], function() { - FB.Facebook.init("%s", "../xd_receiver.html"); + FB.init("%s", "../xd_receiver.html", + {"doNotUseCachedConnectState":true }); + } ); } @@ -220,11 +222,11 @@ class FBConnectPlugin extends Plugin try { $facebook = getFacebook(); - $fbuid = getFacebook()->get_loggedin_user(); + $fbuid = $facebook->api_client->users_getLoggedInUser(); } catch (Exception $e) { common_log(LOG_WARNING, - 'Problem getting Facebook client: ' . + 'Problem getting Facebook user: ' . $e->getMessage()); } @@ -297,9 +299,9 @@ class FBConnectPlugin extends Plugin $title = _('Logout from the site'); $text = _('Logout'); - $html = sprintf('<li id="nav_logout"><a href="%s" title="%s" ' . - 'onclick="FB.Connect.logout(function() { goto_logout() })">%s</a></li>', - $logout_url, $title, $text); + $html = sprintf('<li id="nav_logout"><a href="#" title="%s" ' . + 'onclick="FB.Connect.logoutAndRedirect(\'%s\');">%s</a></li>', + $title, $logout_url, $text); $action->raw($html); diff --git a/plugins/FBConnect/README b/plugins/FBConnect/README new file mode 100644 index 000000000..914b774cb --- /dev/null +++ b/plugins/FBConnect/README @@ -0,0 +1,77 @@ +This plugin allows you to utilize Facebook Connect with Laconica. +Supported Facebook Connect features: + +- Authenticate (register/login/logout -- works similar to OpenID) +- Associate an existing Laconica account with a Facebook account +- Disconnect a Facebook account from a Laconica account + +Future planned functionality: + +- Invite Facebook friends to use your Laconica installation +- Auto-subscribe Facebook friends already using Laconica +- Share Laconica favorite notices to your Facebook stream + +To use the plugin you will need to configure a Facebook application +to point to your Laconica installation (see the Installation section +below). + +Installation +============ + +If you don't already have the built-in Facebook application configured, +you'll need to log into Facebook and create/configure a new application. +Please follow the instructions in the section titled, "Setting Up Your +Application and Getting an API Key," on the following page of the +Facebook developer wiki: + + http://wiki.developers.facebook.com/index.php/Connect/Setting_Up_Your_Site + +If you already are using the build-in Laconica Facebook application, +you can modify your existing application's configuration using the +Facebook Developer Application on Facebook. Use it to edit your +application settings, and under the 'Connect' tab, change the 'Connect +URL' to be the main URL for your Laconica site. E.g.: + + http://SITE/PATH_TO_LACONICA/ + +After you application is created and configured, you'll need to add its +API key and secret to your Laconica config.php file: + + $config['facebook']['apikey'] = 'APIKEY'; + $config['facebook']['secret'] = 'SECRET'; + +Finally, to enable the plugin, add the following stanza to your +config.php: + + require_once(INSTALLDIR.'/plugins/FBConnect/FBConnectPlugin.php'); + $fbc = new FBConnectPlugin(); + +To try out the plugin, fire up your browser and connect to: + + http://SITE/PATH_TO_LACONICA/main/facebooklogin + +or, if you do not have fancy URLs turned on: + + http://SITE/PATH_TO_LACONICA/index.php/main/facebooklogin + +You should see a page with a blue button that says: "Connect with +Facebook". + +Connect/Disconnect existing account +=================================== + +If the Facebook Connect plugin is enabled, there will be a new Facebook +Connect Settings tab under each user's Connect menu. Users can connect +and disconnect to their Facebook accounts from it. Note: Before a user +can disconnect from Facebook, she must set a normal Laconica password. +Otherwise, she might not be able to login in to her account in the +future. This is usually only required for users who have used Facebook +Connect to register their Laconica account, and therefore haven't +already set a local password. + +Helpful links +============= + +Facebook Connect Homepage: +http://developers.facebook.com/connect.php + diff --git a/scripts/triminboxes.php b/scripts/triminboxes.php index b2135d682..27e200fef 100644 --- a/scripts/triminboxes.php +++ b/scripts/triminboxes.php @@ -52,43 +52,5 @@ if (!empty($id)) { $cnt = $user->find(); while ($user->fetch()) { - - $inbox_entry = new Notice_inbox(); - $inbox_entry->user_id = $user->id; - $inbox_entry->orderBy('created DESC'); - $inbox_entry->limit(1000, 1); - - $id = null; - - if ($inbox_entry->find(true)) { - $id = $inbox_entry->notice_id; - } - - $inbox_entry->free(); - unset($inbox_entry); - - if (is_null($id)) { - continue; - } - - $start = microtime(true); - - $old_inbox = new Notice_inbox(); - $cnt = $old_inbox->query('DELETE from notice_inbox WHERE user_id = ' . $user->id . ' AND notice_id < ' . $id); - $old_inbox->free(); - unset($old_inbox); - - print "Deleted $cnt notices for $user->nickname ($user->id).\n"; - - $finish = microtime(true); - - $delay = 3.0 * ($finish - $start); - - print "Delaying $delay seconds..."; - - // Wait to let slaves catch up - - usleep($delay * 1000000); - - print "DONE.\n"; + Notice_inbox::gc($user->id); } |