├── media ├── images │ ├── dm.png │ ├── repeat.png │ ├── reply.png │ ├── stars.gif │ ├── system.gif │ ├── user.jpg │ ├── host_big.gif │ ├── host_small.gif │ ├── book-default-small.gif │ ├── book-default-medium.gif │ ├── event-default-medium.jpg │ ├── event-default-small.jpg │ ├── movie-default-medium.gif │ ├── movie-default-small.gif │ ├── music-default-medium.gif │ ├── music-default-small.gif │ ├── list.php │ └── list.txt └── douban │ └── console │ ├── images │ └── douban_console.gif │ ├── css │ ├── screen.css │ ├── ie7.css │ └── ie6.css │ └── js │ └── console.js ├── classes ├── douban.php ├── controller │ └── douban │ │ ├── console │ │ ├── book.php │ │ ├── music.php │ │ ├── movie.php │ │ ├── broadcast.php │ │ ├── doumail.php │ │ ├── people.php │ │ ├── collection.php │ │ └── event.php │ │ ├── demo │ │ ├── album.php │ │ ├── music.php │ │ ├── book.php │ │ ├── movie.php │ │ ├── people.php │ │ ├── doumail.php │ │ ├── collection.php │ │ ├── broadcast.php │ │ ├── review.php │ │ └── recommendation.php │ │ ├── demo.php │ │ └── console.php └── douban │ ├── api │ ├── album.php │ ├── music.php │ ├── book.php │ ├── movie.php │ ├── people.php │ ├── note.php │ ├── collection.php │ ├── doumail.php │ ├── recommendation.php │ └── broadcast.php │ ├── response.php │ ├── request.php │ ├── oauth.php │ └── core.php ├── LICENSE ├── IMAGE_SIZE.markdown ├── config ├── douban.php ├── douban_console.php └── user_agents.php ├── CHANGELOG.markdown ├── init.php ├── README.markdown └── views └── douban └── console └── home.php /media/images/dm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icyleaf/kohana-douban/HEAD/media/images/dm.png -------------------------------------------------------------------------------- /media/images/repeat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icyleaf/kohana-douban/HEAD/media/images/repeat.png -------------------------------------------------------------------------------- /media/images/reply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icyleaf/kohana-douban/HEAD/media/images/reply.png -------------------------------------------------------------------------------- /media/images/stars.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icyleaf/kohana-douban/HEAD/media/images/stars.gif -------------------------------------------------------------------------------- /media/images/system.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icyleaf/kohana-douban/HEAD/media/images/system.gif -------------------------------------------------------------------------------- /media/images/user.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icyleaf/kohana-douban/HEAD/media/images/user.jpg -------------------------------------------------------------------------------- /media/images/host_big.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icyleaf/kohana-douban/HEAD/media/images/host_big.gif -------------------------------------------------------------------------------- /media/images/host_small.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icyleaf/kohana-douban/HEAD/media/images/host_small.gif -------------------------------------------------------------------------------- /classes/douban.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. -------------------------------------------------------------------------------- /IMAGE_SIZE.markdown: -------------------------------------------------------------------------------- 1 | # Douban Images URL 2 | 3 | ## Book/Moive/Music: 4 | 5 | [small] http://t.douban.com/spic/s1492563.jpg 6 | [medium] http://t.douban.com/mpic/s1492563.jpg 7 | [large] http://t.douban.com/lpic/s1492563.jpg 8 | 9 | ## Event: 10 | 11 | [b?] http://t.douban.com/bpic/e170924.jpg 12 | [t?] http://t.douban.com/tpic/e170924.jpg 13 | [small] http://t.douban.com/spic/e170924.jpg 14 | [medium] http://t.douban.com/mpic/e170924.jpg 15 | [large] http://t.douban.com/lpic/e170924.jpg 16 | 17 | ## People: 18 | 19 | [icon] http://t.douban.com/icon/u1353793-4.jpg 20 | [original] http://t.douban.com/icon/ul1353793-4.jpg -------------------------------------------------------------------------------- /config/douban.php: -------------------------------------------------------------------------------- 1 | '0816a2ebb89b35331843e8c329d19f99', 15 | 'api_secret' => 'a0e0012eeda56b40', 16 | 17 | /** 18 | * Configuration 19 | */ 20 | 'lifetime' => 3600 * 30 * 30, // Store for 30 days 21 | 'session_key' => array( 22 | 'oauth_token' => 'douban_oauth_token', // Saved access token 23 | 'oauth_user' => 'douban_oauth_token', // Saved current user 24 | ), 25 | ); 26 | 27 | -------------------------------------------------------------------------------- /media/images/list.php: -------------------------------------------------------------------------------- 1 | http://t.douban.com/icon/user.jpg 2 | 3 | http://t.douban.com/pics/book-default-small.gif 4 | http://t.douban.com/pics/music-default-small.gif 5 | http://t.douban.com/pics/movie-default-small.gif 6 | 7 | http://t.douban.com/pics/book-default-medium.gif 8 | http://t.douban.com/pics/music-default-medium.gif 9 | http://t.douban.com/pics/movie-default-medium.gif 10 | 11 | http://t.douban.com/pics/icon/host_small.gif 12 | http://t.douban.com/pics/icon/host_big.gif 13 | 14 | http://t.douban.com/pics/icon/system_9px.gif => system.gif 15 | 16 | http://t.douban.com/pics/event/mpic/event_dft.jpg => event-default-medium.jpg 17 | http://t.douban.com/pics/event/bpic/event_dft.jpg => event-default-small.jpg 18 | 19 | http://t.douban.com/pics/stars.gif 20 | 21 | =========== 22 | repeat.png, reply.png, dm.png picked from dabr. 23 | =========== -------------------------------------------------------------------------------- /media/images/list.txt: -------------------------------------------------------------------------------- 1 | http://t.douban.com/icon/user.jpg 2 | 3 | http://t.douban.com/pics/book-default-small.gif 4 | http://t.douban.com/pics/music-default-small.gif 5 | http://t.douban.com/pics/movie-default-small.gif 6 | 7 | http://t.douban.com/pics/book-default-medium.gif 8 | http://t.douban.com/pics/music-default-medium.gif 9 | http://t.douban.com/pics/movie-default-medium.gif 10 | 11 | http://t.douban.com/pics/icon/host_small.gif 12 | http://t.douban.com/pics/icon/host_big.gif 13 | 14 | http://t.douban.com/pics/icon/system_9px.gif => system.gif 15 | 16 | http://t.douban.com/pics/event/mpic/event_dft.jpg => event-default-medium.jpg 17 | http://t.douban.com/pics/event/bpic/event_dft.jpg => event-default-small.jpg 18 | 19 | http://t.douban.com/pics/stars.gif 20 | 21 | =========== 22 | repeat.png, reply.png, dm.png picked from dabr. 23 | =========== -------------------------------------------------------------------------------- /classes/controller/douban/console/book.php: -------------------------------------------------------------------------------- 1 | auto_render = FALSE; 13 | 14 | if ( ! $_POST) 15 | { 16 | die('必须采用 POST 方法请求'); 17 | } 18 | 19 | $this->book = $this->douban->book(); 20 | $this->book->alt = Arr::get($_POST, 'alt', 'json'); 21 | $this->book->format = FALSE; 22 | } 23 | 24 | public function action_get() 25 | { 26 | $id = Arr::get($_POST, 'id'); 27 | 28 | $this->result = $this->book->get($id); 29 | } 30 | 31 | public function action_search() 32 | { 33 | $query = Arr::get($_POST, 'query'); 34 | $index = Arr::get($_POST, 'index', 1); 35 | $max = Arr::get($_POST, 'max', 10); 36 | 37 | $this->result = $this->book->search($query, $index, $max); 38 | } 39 | 40 | public function after() 41 | { 42 | if ( ! empty($this->result)) 43 | { 44 | parent::render($this->result, $this->book->alt); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /classes/controller/douban/console/music.php: -------------------------------------------------------------------------------- 1 | auto_render = FALSE; 13 | 14 | if ( ! $_POST) 15 | { 16 | die('必须采用 POST 方法请求'); 17 | } 18 | 19 | $this->music = $this->douban->music(); 20 | $this->music->alt = Arr::get($_POST, 'alt', 'json'); 21 | $this->music->format = FALSE; 22 | } 23 | 24 | public function action_get() 25 | { 26 | $id = Arr::get($_POST, 'id'); 27 | 28 | $this->result = $this->music->get($id); 29 | } 30 | 31 | public function action_search() 32 | { 33 | $query = Arr::get($_POST, 'query'); 34 | $index = Arr::get($_POST, 'index', 1); 35 | $max = Arr::get($_POST, 'max', 10); 36 | 37 | $this->result = $this->music->search($query, $index, $max); 38 | } 39 | 40 | public function after() 41 | { 42 | if ( ! empty($this->result)) 43 | { 44 | parent::render($this->result, $this->music->alt); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /classes/controller/douban/console/movie.php: -------------------------------------------------------------------------------- 1 | auto_render = FALSE; 13 | 14 | if ( ! $_POST) 15 | { 16 | die('必须采用 POST 方法请求'); 17 | } 18 | 19 | $this->movie = $this->douban->movie(); 20 | $this->movie->alt = Arr::get($_POST, 'alt', 'json'); 21 | $this->movie->format = FALSE; 22 | } 23 | 24 | public function action_get($id = NULL) 25 | { 26 | $id = Arr::get($_POST, 'id'); 27 | 28 | $this->result = $this->movie->get($id); 29 | } 30 | 31 | public function action_search() 32 | { 33 | $query = Arr::get($_POST, 'query'); 34 | $index = Arr::get($_POST, 'index', 1); 35 | $max = Arr::get($_POST, 'max', 10); 36 | 37 | $this->result = $this->movie->search($query, $index, $max); 38 | } 39 | 40 | public function after() 41 | { 42 | if ( ! empty($this->result)) 43 | { 44 | parent::render($this->result, $this->movie->alt); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CHANGELOG.markdown: -------------------------------------------------------------------------------- 1 | # Douban API Package ChangeLog 2 | 3 | > [A] Added 4 | > [D] Deleted 5 | > [E] Edited 6 | > [F] Bugfixed 7 | 8 | ## 2.0 9 | 10 | [E] ONLY support Kohana v3.2.x 11 | 12 | ## 1.1.1 13 | 14 | [F] Always throw 'undefined "opensearch:xxx' exception for book/music 15 | 16 | [F] Fixed broken uri of douban demo 17 | 18 | ## 1.1 19 | 20 | [F] Always throw 'undefined "opensearch:xxx' exception 21 | 22 | [A] Added Alubm API (it founded in attribute of event api) 23 | 24 | ## 1.0.3 25 | 26 | [A] GEO for Event information 27 | 28 | [A] Signture for People information 29 | 30 | ## 1.0.2 31 | 32 | [A] Complated markdown all doumail type (normal/system/host) 33 | 34 | [F] Added 'total' attribute to format_comments in broadcast api 35 | 36 | [F] Added throws exception: 'undefined index in formated json array' included 'openSearch:index, openSearch:max, openSearch:total' 37 | 38 | [F] Removed debug code 39 | 40 | ## 1.0.1 41 | 42 | [F] Removed surplus class called load function: auto_load 43 | 44 | [F] Added missing total property in Douban_API_Doumail 45 | 46 | [F] Added missing some attributes for book/movie/music 47 | 48 | [F] Fixed sometime the author attritube is not exist 49 | 50 | [F] Updated typo code 51 | 52 | -------------------------------------------------------------------------------- /init.php: -------------------------------------------------------------------------------- 1 | /(/)', array( 6 | 'controller' => '\w+', 7 | 'action' => '\w+', 8 | 'index' => '\d+', 9 | 'max' => '\d+', 10 | )) 11 | ->defaults(array( 12 | 'directory' => 'douban/console', 13 | )); 14 | 15 | /** 16 | * douban api console 17 | */ 18 | Route::set('douban/console', 'douban_console(/(/))', array( 19 | 'action' => '\w+', 20 | 'id' => '\w+', 21 | )) 22 | ->defaults(array( 23 | 'directory' => 'douban', 24 | 'controller' => 'console', 25 | 'action' => 'index', 26 | )); 27 | 28 | /** 29 | * douban api demo 30 | */ 31 | Route::set('douban/demo', 'douban_demo(/(/))', array( 32 | 'action' => '\w+', 33 | 'id' => '\w+', 34 | )) 35 | ->defaults(array( 36 | 'directory' => 'douban', 37 | 'controller' => 'demo', 38 | 'action' => 'index', 39 | )); 40 | 41 | 42 | /** 43 | * Remap media folder 44 | */ 45 | Route::set('media', 'media(/)', array( 46 | 'file' => '.+' 47 | )) 48 | ->defaults(array( 49 | 'directory' => 'douban', 50 | 'controller' => 'console', 51 | 'action' => 'media', 52 | 'file' => NULL, 53 | )); -------------------------------------------------------------------------------- /classes/controller/douban/console/broadcast.php: -------------------------------------------------------------------------------- 1 | auto_render = FALSE; 13 | 14 | if ( ! $_POST) 15 | { 16 | die('必须采用 POST 方法请求'); 17 | } 18 | 19 | $this->broadcast = $this->douban->broadcast(); 20 | $this->broadcast->alt = Arr::get($_POST, 'alt', 'json'); 21 | $this->broadcast->format = FALSE; 22 | } 23 | 24 | public function action_get() 25 | { 26 | $id = Arr::get($_POST, 'id'); 27 | 28 | $this->result = $this->broadcast->get($id); 29 | } 30 | 31 | public function action_getContacts() 32 | { 33 | $id = Arr::get($_POST, 'id', '@me'); 34 | $index = Arr::get($_POST, 'index', 1); 35 | $max = Arr::get($_POST, 'max', 10); 36 | 37 | $this->result = $this->broadcast->get_contacts($id, $index, $max); 38 | } 39 | 40 | public function action_getMine() 41 | { 42 | $id = Arr::get($_POST, 'id', '@me'); 43 | $index = Arr::get($_POST, 'index', 1); 44 | $max = Arr::get($_POST, 'max', 10); 45 | 46 | $this->result = $this->broadcast->get_mine($id, $index, $max); 47 | } 48 | 49 | public function action_post() 50 | { 51 | $message = Arr::get($_POST, 'message'); 52 | 53 | $this->result = $this->broadcast->create($message); 54 | } 55 | 56 | public function after() 57 | { 58 | if ( ! empty($this->result)) 59 | { 60 | parent::render($this->result, $this->broadcast->alt); 61 | } 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /classes/controller/douban/console/doumail.php: -------------------------------------------------------------------------------- 1 | auto_render = FALSE; 13 | 14 | // if ( ! $_POST) 15 | // { 16 | // die('必须采用 POST 方法请求'); 17 | // } 18 | 19 | $this->doumail = $this->douban->doumail(); 20 | $this->doumail->alt = Arr::get($_POST, 'alt', 'json'); 21 | $this->doumail->format = FALSE; 22 | } 23 | 24 | public function action_get() 25 | { 26 | $id = Arr::get($_POST, 'id'); 27 | 28 | $this->result = $this->doumail->get($id); 29 | } 30 | 31 | public function action_getInbox() 32 | { 33 | $index = Arr::get($_POST, 'index', 1); 34 | $max = Arr::get($_POST, 'max', 10); 35 | 36 | $this->result = $this->doumail->get_inbox($index, $max); 37 | } 38 | 39 | public function action_getUnread() 40 | { 41 | $index = Arr::get($_POST, 'index', 1); 42 | $max = Arr::get($_POST, 'max', 10); 43 | 44 | $this->result = $this->doumail->get_unread($index, $max); 45 | } 46 | 47 | public function action_getOutbox() 48 | { 49 | $index = Arr::get($_POST, 'index', 1); 50 | $max = Arr::get($_POST, 'max', 10); 51 | 52 | $this->result = $this->doumail->get_outbox($index, $max); 53 | } 54 | 55 | public function action_send() 56 | { 57 | $this->result = $this->doumail->send($_POST); 58 | } 59 | 60 | public function after() 61 | { 62 | if ( ! empty($this->result)) 63 | { 64 | parent::render($this->result, $this->doumail->alt); 65 | } 66 | } 67 | 68 | } 69 | 70 | -------------------------------------------------------------------------------- /classes/controller/douban/console/people.php: -------------------------------------------------------------------------------- 1 | auto_render = FALSE; 13 | 14 | if ( ! $_POST) 15 | { 16 | die('必须采用 POST 方法请求'); 17 | } 18 | 19 | $this->people = $this->douban->people(); 20 | $this->people->alt = Arr::get($_POST, 'alt', 'json'); 21 | $this->people->format = FALSE; 22 | } 23 | 24 | public function action_get() 25 | { 26 | $id = Arr::get($_POST, 'id', '@me'); 27 | 28 | $this->result = $this->people->get($id); 29 | } 30 | 31 | public function action_getFriends() 32 | { 33 | $id = Arr::get($_POST, 'id', '@me'); 34 | $index = Arr::get($_POST, 'index', 1); 35 | $max = Arr::get($_POST, 'max', 10); 36 | 37 | $this->result = $this->people->get_friends($id, $index, $max); 38 | } 39 | 40 | public function action_getContacts() 41 | { 42 | $id = Arr::get($_POST, 'id', '@me'); 43 | $index = Arr::get($_POST, 'index', 1); 44 | $max = Arr::get($_POST, 'max', 10); 45 | 46 | $this->result = $this->people->get_contacts($id, $index, $max); 47 | } 48 | 49 | public function action_search() 50 | { 51 | $query = Arr::get($_POST, 'query'); 52 | $index = Arr::get($_POST, 'index', 1); 53 | $max = Arr::get($_POST, 'max', 10); 54 | 55 | $this->result = $this->people->search($query, $index, $max); 56 | } 57 | 58 | public function after() 59 | { 60 | if ( ! empty($this->result)) 61 | { 62 | parent::render($this->result, $this->people->alt); 63 | } 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /classes/douban/api/album.php: -------------------------------------------------------------------------------- 1 | 7 | * @link http://icyleaf.com 8 | * @copyright (c) 2009-2010 icyleaf 9 | * @license http://www.apache.org/licenses/LICENSE-2.0 10 | */ 11 | class Douban_API_Album extends Douban_Core { 12 | 13 | /** 14 | * Get album 15 | * @param int $album_id 16 | * @return object 17 | */ 18 | public function get($album_id) 19 | { 20 | $url = Douban_Core::ALBUM_URL.$album_id; 21 | $post_data = array 22 | ( 23 | 'alt' => $this->alt 24 | ); 25 | $result = $this->_client->get($url, $post_data); 26 | 27 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 28 | { 29 | $result = $this->_format($result->to_json()); 30 | } 31 | 32 | return $result; 33 | } 34 | 35 | /** 36 | * Format album 37 | * @param string $album 38 | * @return object 39 | */ 40 | public function _format($album) 41 | { 42 | $result = new stdClass; 43 | // id 44 | $result->id = substr($album['id']['$t'], strlen(Douban_Core::ALBUM_URL)); 45 | // title 46 | $result->title = $album['title']['$t']; 47 | // author 48 | if (isset($album['author'])) 49 | { 50 | $result->author = Douban_API_Event::format($album['author']); 51 | } 52 | // published 53 | $result->published = strtotime($album['published']['$t']); 54 | // updated 55 | $result->updated = strtotime($album['updated']['$t']); 56 | // link 57 | foreach ($album['link'] as $link) 58 | { 59 | $result->link[$link['@rel']] = $link['@href']; 60 | } 61 | // attributes 62 | foreach ($album['db:attribute'] as $att) 63 | { 64 | $result->attribute[$att['@name']] = $att['$t']; 65 | } 66 | 67 | return $result; 68 | } 69 | } 70 | ?> 71 | -------------------------------------------------------------------------------- /classes/controller/douban/demo/album.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 12 | if ($this->_config->api_key AND $this->_config->api_secret) 13 | { 14 | $this->_douban = Douban::instance(); 15 | } 16 | else 17 | { 18 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 19 | } 20 | 21 | // base url 22 | $this->_base = $this->request->uri(); 23 | } 24 | 25 | public function action_index() 26 | { 27 | // get methods 28 | $methods = new ArrayIterator(get_class_methods($this)); 29 | // methods to ignore 30 | $ignore = array 31 | ( 32 | '__construct', 33 | '__call', 34 | 'action_index', 35 | 'before', 36 | 'after' 37 | ); 38 | 39 | $output = '

Douban Album Demo

'; 40 | if ($people = $this->_douban->get_user()) 41 | { 42 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 43 | } 44 | else 45 | { 46 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.html('demo_douban/verity_oauth', '验证'); 47 | } 48 | $output .= '

    '; 49 | 50 | while ($methods->valid()) 51 | { 52 | $action = $methods->current(); 53 | if ( ! in_array($action, $ignore)) 54 | { 55 | $action = str_replace('action_', '', $action); 56 | $output .= '
  1. '.html::anchor($this->_base.'/'.$action, $action).'
  2. '; 57 | } 58 | $methods->next(); 59 | } 60 | $output .= '
'; 61 | // render 62 | $this->response->body($output); 63 | } 64 | 65 | public function action_album_information() 66 | { 67 | if ( ! $_GET) 68 | { 69 | echo '通过 album id 获得相册信息:(比如,"27752561")
'; 70 | echo '
'; 71 | } 72 | else 73 | { 74 | $id = trim($_GET['id']); 75 | 76 | echo Debug::dump($this->_douban->album()->get($id)); 77 | } 78 | } 79 | 80 | } 81 | 82 | -------------------------------------------------------------------------------- /config/douban_console.php: -------------------------------------------------------------------------------- 1 | '', 15 | 'api_secret' => '', 16 | 17 | 'methods' => array( 18 | 'people' => array( 19 | 'get' => array('id'), 20 | 'getFriends' => array('id', 'index', 'max'), 21 | 'getContacts' => array('id', 'index', 'max'), 22 | 'search' => array('query', 'index', 'max'), 23 | ), 24 | 'broadcast' => array( 25 | 'get' => array('id'), 26 | 'getContacts' => array('id', 'index', 'max'), 27 | 'getMine' => array('id', 'index', 'max'), 28 | 'post' => array('message'), 29 | ), 30 | 'collection' => array( 31 | 'getByPeople' => array('id', 'index', 'max'), 32 | 'getBookByPeople' => array('id', 'index', 'max'), 33 | 'getMusicByPeople' => array('id', 'index', 'max'), 34 | 'getMovieByPeople' => array('id', 'index', 'max'), 35 | ), 36 | 'doumail' => array( 37 | 'get' => array('id'), 38 | 'getUnread' => array('index', 'max'), 39 | 'getInbox' => array('index', 'max'), 40 | 'getOutbox' => array('index', 'max'), 41 | 'send' => array('people_id', 'title', 'content', 'captcha_token', 'captcha_string'), 42 | ), 43 | 'book' => array( 44 | 'get' => array('id'), 45 | 'search' => array('query', 'index', 'max'), 46 | ), 47 | 'movie' => array( 48 | 'get' => array('id'), 49 | 'search' => array('query', 'index', 'max'), 50 | ), 51 | 'music' => array( 52 | 'get' => array('id'), 53 | 'search' => array('query', 'index', 'max'), 54 | ), 55 | 'event' => array( 56 | 'get' => array('id'), 57 | 'getByPeople' => array('id', 'index', 'max'), 58 | 'getByLocation' => array('location', 'index', 'max'), 59 | 'getParticipants' => array('id', 'index', 'max'), 60 | 'getWishers' => array('id', 'index', 'max'), 61 | 'search' => array('query', 'index', 'max'), 62 | ), 63 | ), 64 | ); 65 | 66 | -------------------------------------------------------------------------------- /classes/controller/douban/console/collection.php: -------------------------------------------------------------------------------- 1 | auto_render = FALSE; 13 | 14 | if ( ! $_POST) 15 | { 16 | die('必须采用 POST 方法请求'); 17 | } 18 | 19 | $this->collection = $this->douban->collection(); 20 | $this->collection->alt = Arr::get($_POST, 'alt', 'json'); 21 | $this->collection->format = FALSE; 22 | } 23 | 24 | public function action_getByPeople() 25 | { 26 | $id = $this->request->param('id', '@me'); 27 | $index = $this->request->param('index', 1); 28 | $max = $this->request->param('max', 10); 29 | 30 | $this->result = $this->collection->get_by_people($id, $index, $max); 31 | } 32 | 33 | public function action_getBookByPeople() 34 | { 35 | $id = Arr::get($_POST, 'id', '@me'); 36 | $index = Arr::get($_POST, 'index', 1); 37 | $max = Arr::get($_POST, 'max', 10); 38 | 39 | $data = array 40 | ( 41 | 'cat' => 'book', 42 | ); 43 | $this->result = $this->collection->get_by_people($id, $index, $max, $data); 44 | } 45 | 46 | public function action_getMusicByPeople() 47 | { 48 | $id = Arr::get($_POST, 'id', '@me'); 49 | $index = Arr::get($_POST, 'index', 1); 50 | $max = Arr::get($_POST, 'max', 10); 51 | $data = array 52 | ( 53 | 'cat' => 'music', 54 | ); 55 | $this->result = $this->collection->get_by_people($id, $index, $max, $data); 56 | } 57 | 58 | public function action_getMovieByPeople() 59 | { 60 | $id = Arr::get($_POST, 'id', '@me'); 61 | $index = Arr::get($_POST, 'index', 1); 62 | $max = Arr::get($_POST, 'max', 10); 63 | $data = array 64 | ( 65 | 'cat' => 'movie', 66 | ); 67 | $this->result = $this->collection->get_by_people($id, $index, $max, $data); 68 | } 69 | 70 | public function after() 71 | { 72 | if ( ! empty($this->result)) 73 | { 74 | parent::render($this->result, $this->collection->alt); 75 | } 76 | } 77 | 78 | } 79 | 80 | -------------------------------------------------------------------------------- /classes/douban/response.php: -------------------------------------------------------------------------------- 1 | 7 | * @link http://icyleaf.com 8 | * @copyright (c) 2009-2010 icyleaf 9 | * @license http://www.apache.org/licenses/LICENSE-2.0 10 | */ 11 | class Douban_Response { 12 | 13 | private $_url; 14 | private $_method; 15 | private $_content; 16 | private $_status; 17 | 18 | public function __construct($response) 19 | { 20 | $this->_url = $response['url']; 21 | $this->_method = $response['method']; 22 | $this->_content = $response['content']; 23 | $this->_status = $response['status']; 24 | } 25 | 26 | /** 27 | * Orgin format content 28 | * 29 | * @return object 30 | */ 31 | public function to_normal() 32 | { 33 | return $this->_content; 34 | } 35 | 36 | /** 37 | * Format String(url) to Array 38 | * 39 | * @param string $url 40 | * @return array 41 | */ 42 | public function to_array() 43 | { 44 | $content = preg_replace('/&(?!(?:#\d++|[a-z]++);)/ui', '&', $this->_content); 45 | if ( ! preg_match('/[=|&]/', $content)) 46 | { 47 | return $content; 48 | } 49 | 50 | $array = array(); 51 | $expression = explode('&', $content); 52 | for ($i = 0; $i < count($expression); $i++) 53 | { 54 | $attribute = explode('=', $expression[$i]); 55 | $array[$attribute[0]] = $attribute[1]; 56 | } 57 | 58 | return $array; 59 | } 60 | 61 | /** 62 | * Format to xml 63 | * 64 | * @return object 65 | */ 66 | public function to_xml() 67 | { 68 | return new SimpleXMLElement($this->_content); 69 | } 70 | 71 | /** 72 | * Format to json 73 | * 74 | * @param string $assoc 75 | * @return object 76 | */ 77 | public function to_json($assoc = TRUE) 78 | { 79 | return json_decode($this->_content, $assoc); 80 | } 81 | 82 | public function __call($name, $arguments) 83 | { 84 | if ( empty($arguments) ) 85 | { 86 | if ($name == 'status') 87 | { 88 | return (int) $this->_status; 89 | } 90 | elseif ($name == 'url' OR $name == 'method') 91 | { 92 | return $this->{'_'.$name}; 93 | } 94 | } 95 | else 96 | { 97 | return NULL; 98 | } 99 | } 100 | 101 | /** 102 | * Get response http status code 103 | * 104 | * @return int 105 | */ 106 | public function status() 107 | { 108 | return (int) $this->_status; 109 | } 110 | 111 | /** 112 | * Default render string 113 | * 114 | * @return string 115 | */ 116 | public function __toString() 117 | { 118 | return $this->to_normal(); 119 | } 120 | 121 | } -------------------------------------------------------------------------------- /classes/controller/douban/console/event.php: -------------------------------------------------------------------------------- 1 | auto_render = FALSE; 13 | 14 | if ( ! $_POST) 15 | { 16 | die('必须采用 POST 方法请求'); 17 | } 18 | 19 | $this->event = $this->douban->event(); 20 | $this->event->alt = Arr::get($_POST, 'alt', 'json'); 21 | $this->event->format = FALSE; 22 | } 23 | 24 | public function action_get() 25 | { 26 | $id = Arr::get($_POST, 'id'); 27 | 28 | $this->result = $this->event->get($id); 29 | } 30 | 31 | public function action_getByPeople() 32 | { 33 | $id = Arr::get($_POST, 'id', '@me'); 34 | $index = Arr::get($_POST, 'index', 1); 35 | $max = Arr::get($_POST, 'max', 10); 36 | 37 | $this->result = $this->event->get_by_people($id, $index, $max); 38 | } 39 | 40 | public function action_getByLocation() 41 | { 42 | $location = Arr::get($_POST, 'location', 'beijing'); 43 | $index = Arr::get($_POST, 'index', 1); 44 | $max = Arr::get($_POST, 'max', 10); 45 | $type = 'all'; 46 | 47 | $this->result = $this->event->get_by_location($location, $index, $max, $type); 48 | } 49 | 50 | public function action_getParticipants() 51 | { 52 | $id = Arr::get($_POST, 'id'); 53 | $index = Arr::get($_POST, 'index', 1); 54 | $max = Arr::get($_POST, 'max', 10); 55 | 56 | $this->result = $this->event->get_participants($id, $index, $max); 57 | } 58 | 59 | public function action_getWishers() 60 | { 61 | $id = Arr::get($_POST, 'id'); 62 | $index = Arr::get($_POST, 'index', 1); 63 | $max = Arr::get($_POST, 'max', 10); 64 | 65 | $this->result = $this->event->get_wishers($id, $index, $max); 66 | } 67 | 68 | public function action_participate() 69 | { 70 | $id = Arr::get($_POST, 'id'); 71 | 72 | $this->result = $this->event->participate($id); 73 | } 74 | 75 | public function action_wisher() 76 | { 77 | $id = Arr::get($_POST, 'id'); 78 | 79 | $this->result = $this->event->wisher($id); 80 | } 81 | 82 | public function action_search() 83 | { 84 | $query = Arr::get($_POST, 'query'); 85 | $index = Arr::get($_POST, 'index', 1); 86 | $max = Arr::get($_POST, 'max', 10); 87 | 88 | $this->result = $this->event->search($query, 'all', $index, $max); 89 | } 90 | 91 | public function after() 92 | { 93 | if ( ! empty($this->result)) 94 | { 95 | parent::render($this->result, $this->event->alt); 96 | } 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /media/douban/console/css/screen.css: -------------------------------------------------------------------------------- 1 | /* HTTP Host: static.ak.fbcdn.net */ 2 | /* Generated: June 25th 2009 4:19:59 PM PDT */ 3 | /* Machine: 10.16.139.101 */ 4 | /* Source: Local Cache */ 5 | /* Location: rsrc:4vfy9xj1:en_US:/html/css/8jlnn6i0f8kkks4k.pkg.css:141 */ 6 | /* Locale: en_US */ 7 | /* Path: css/8jlnn6i0f8kkks4k.pkg.css */ 8 | 9 | 10 | html{height:100%;} 11 | html.auto_resize_iframe{height:auto;} 12 | body{height:100%;} 13 | body.auto_resize_iframe{height:auto;} 14 | .use_scroll_wrapper #scroll_wrapper{width:100%;height:100%;overflow:auto;position:relative;} 15 | #nonfooter{padding:0px;margin:0px;min-height:100%;} 16 | #page_height{padding:0px 0px 100px 0px;margin:0px;} 17 | body{background:#fff;font-family:"lucida grande",tahoma,verdana,arial,sans-serif;font-size:11px;margin:0px;padding:0px;text-align:left;direction:ltr;unicode-bidi:embed;} 18 | h1, h2, h3, h4, h5{font-size:13px;color:#333;margin:0px;padding:0px;} 19 | h1{font-size:14px;} 20 | h4, h5{font-size:11px;} 21 | p{font-family:"lucida grande", tahoma, verdana, arial, sans-serif;font-size:11px;text-align:left;} 22 | a{cursor:pointer;color:#3b5998;-moz-outline-style:none;text-decoration:none;} 23 | a:hover{text-decoration:underline;} 24 | img{border:0px;} 25 | select{border:1px solid #BDC7D8;font-family:"lucida grande",tahoma,verdana,arial,sans-serif;font-size:11px;padding:2px;} 26 | td, 27 | td.label{font-size:11px;text-align:left;} 28 | .wbr{display:block;float:left;} 29 | .standard_message{padding:10px;} 30 | .standard_message.no_padding{padding:0;} 31 | .standard_message.shorten{padding-bottom:0;} 32 | .standard_message.has_padding{padding:10px;} 33 | .standard_message .error, 34 | .standard_message .status, 35 | .standard_message .explanation_note{margin:0;} 36 | .error, 37 | .status, 38 | .explanation_note{margin:0 0px 10px 0px;padding:10px;border:solid 1px #dd3c10;background:#ffebe8;} 39 | .created{margin:0 20px 20px;} 40 | .error p, 41 | .status p, 42 | .explanation_note p{margin:4px 0px 0px;line-height:14px;font-weight:normal;} 43 | .error a, 44 | .status a, 45 | .explanation_note a{text-decoration:none;color:#dd3c10;} 46 | .status{border-color:#e2c822;background:#fff9d7;} 47 | .status a{color:#3b5998;text-decoration:none;} 48 | .page_top_notice{color:#444;border-bottom:solid 1px #e2c822;padding:5px 20px 4px;background:#fff9d7;} 49 | .explanation_note{border-color:#bdc7d8;background:#fff;} 50 | .explanation_note h1{font-size:11px;} 51 | .pipe{color:gray;padding:0px 3px;} 52 | .column{float:left;} 53 | .center{margin-left:auto;margin-right:auto;} 54 | .editor_title{background:#f7f7f7;padding-bottom:5px;} 55 | .page_title{border-bottom:1px solid #ccc;padding:10px 10px 5px;margin:0px 10px;} 56 | 57 | -------------------------------------------------------------------------------- /classes/controller/douban/demo/music.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 12 | if ($this->_config->api_key AND $this->_config->api_secret) 13 | { 14 | $this->_douban = Douban::instance(); 15 | } 16 | else 17 | { 18 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 19 | } 20 | 21 | // base url 22 | $this->_base = $this->request->uri(); 23 | } 24 | 25 | public function action_index() 26 | { 27 | // get methods 28 | $methods = new ArrayIterator(get_class_methods($this)); 29 | // methods to ignore 30 | $ignore = array 31 | ( 32 | '__construct', 33 | '__call', 34 | 'action_index', 35 | 'before', 36 | 'after' 37 | ); 38 | 39 | $output = '

Douban Music Demo

'; 40 | if ($people = $this->_douban->get_user()) 41 | { 42 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 43 | } 44 | else 45 | { 46 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.html('demo_douban/verity_oauth', '验证'); 47 | } 48 | $output .= '

    '; 49 | 50 | while ($methods->valid()) 51 | { 52 | $action = $methods->current(); 53 | if ( ! in_array($action, $ignore)) 54 | { 55 | $action = str_replace('action_', '', $action); 56 | $output .= '
  1. '.html::anchor($this->_base.'/'.$action, $action).'
  2. '; 57 | } 58 | $methods->next(); 59 | } 60 | $output .= '
'; 61 | // render 62 | $this->response->body($output); 63 | } 64 | 65 | public function action_music_information() 66 | { 67 | if ( ! $_GET) 68 | { 69 | echo '通过 music id 获得音乐信息:(比如,"3040149")

'; 70 | echo '
'; 71 | } 72 | else 73 | { 74 | $id = trim($_GET['id']); 75 | echo Debug::dump($this->_douban->music()->get($id)); 76 | } 77 | } 78 | 79 | public function action_music_tags() 80 | { 81 | if ( ! $_GET) 82 | { 83 | echo '通过 music id 获得音乐标签:(比如,"3040149")

'; 84 | echo '
'; 85 | } 86 | else 87 | { 88 | $id = trim($_GET['id']); 89 | echo Debug::dump($this->_douban->music()->tags($id)); 90 | } 91 | } 92 | 93 | public function action_search_music() 94 | { 95 | if ( ! $_GET) 96 | { 97 | echo '搜索音乐:(比如,"Viva La Vida")

'; 98 | echo '
'; 99 | } 100 | else 101 | { 102 | $query = trim($_GET['query']); 103 | echo Debug::dump($this->_douban->music()->search($query)); 104 | } 105 | } 106 | 107 | } 108 | 109 | -------------------------------------------------------------------------------- /classes/controller/douban/demo/book.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 12 | if ($this->_config->api_key AND $this->_config->api_secret) 13 | { 14 | $this->_douban = Douban::instance(); 15 | } 16 | else 17 | { 18 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 19 | } 20 | 21 | // base url 22 | $this->_base = $this->request->uri(); 23 | } 24 | 25 | public function action_index() 26 | { 27 | // get methods 28 | $methods = new ArrayIterator(get_class_methods($this)); 29 | // methods to ignore 30 | $ignore = array 31 | ( 32 | '__construct', 33 | '__call', 34 | 'action_index', 35 | 'before', 36 | 'after' 37 | ); 38 | 39 | $output = '

Douban Book Demo

'; 40 | if ($people = $this->_douban->get_user()) 41 | { 42 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 43 | } 44 | else 45 | { 46 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.html('demo_douban/verity_oauth', '验证'); 47 | } 48 | $output .= '

    '; 49 | 50 | while ($methods->valid()) 51 | { 52 | $action = $methods->current(); 53 | if ( ! in_array($action, $ignore)) 54 | { 55 | $action = str_replace('action_', '', $action); 56 | $output .= '
  1. '.html::anchor($this->_base.'/'.$action, $action).'
  2. '; 57 | } 58 | $methods->next(); 59 | } 60 | $output .= '
'; 61 | // render 62 | $this->response->body($output); 63 | } 64 | 65 | public function action_book_information() 66 | { 67 | if ( ! $_GET) 68 | { 69 | echo '通过 book id 获得图书信息:(比如,"1082387")
'; 70 | echo '
'; 71 | } 72 | else 73 | { 74 | $id = trim($_GET['id']); 75 | 76 | echo Debug::dump($this->_douban->book()->get($id)); 77 | } 78 | } 79 | 80 | public function action_book_tags() 81 | { 82 | if ( ! $_GET) 83 | { 84 | echo '通过 book id 获得图书标签:(比如,"1082387")
'; 85 | echo '
'; 86 | } 87 | else 88 | { 89 | $id = trim($_GET['id']); 90 | echo Debug::dump($this->_douban->book()->tags($id)); 91 | } 92 | } 93 | 94 | public function action_book_information_by_ibsn() 95 | { 96 | if ( ! $_GET) 97 | { 98 | echo '通过 IBSN 获得图书信息:(比如,"9787532729357")
'; 99 | echo '
'; 100 | } 101 | else 102 | { 103 | $isbn = trim($_GET['isbn']); 104 | echo Debug::dump($this->_douban->book()->isbn($isbn)); 105 | } 106 | } 107 | 108 | public function action_search_book() 109 | { 110 | if ( ! $_GET) 111 | { 112 | echo '搜索图书:(比如,"1984")
'; 113 | echo '
'; 114 | } 115 | else 116 | { 117 | $query = trim($_GET['query']); 118 | echo Debug::dump($this->_douban->book()->search($query)); 119 | } 120 | } 121 | 122 | } 123 | 124 | -------------------------------------------------------------------------------- /classes/controller/douban/demo/movie.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 12 | if ($this->_config->api_key AND $this->_config->api_secret) 13 | { 14 | $this->_douban = Douban::instance(); 15 | } 16 | else 17 | { 18 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 19 | } 20 | 21 | // base url 22 | $this->_base = $this->request->uri(); 23 | } 24 | 25 | public function action_index() 26 | { 27 | // get methods 28 | $methods = new ArrayIterator(get_class_methods($this)); 29 | // methods to ignore 30 | $ignore = array 31 | ( 32 | '__construct', 33 | '__call', 34 | 'action_index', 35 | 'before', 36 | 'after' 37 | ); 38 | 39 | $output = '

Douban Movie Demo

'; 40 | if ($people = $this->_douban->get_user()) 41 | { 42 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 43 | } 44 | else 45 | { 46 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.html('demo_douban/verity_oauth', '验证'); 47 | } 48 | $output .= '

    '; 49 | 50 | while ($methods->valid()) 51 | { 52 | $action = $methods->current(); 53 | if ( ! in_array($action, $ignore)) 54 | { 55 | $action = str_replace('action_', '', $action); 56 | $output .= '
  1. '.html::anchor($this->_base.'/'.$action, $action).'
  2. '; 57 | } 58 | $methods->next(); 59 | } 60 | $output .= '
'; 61 | // render 62 | $this->response->body($output); 63 | } 64 | 65 | public function action_movie_information() 66 | { 67 | if ( ! $_GET) 68 | { 69 | echo '通过 movie id 获得电影信息:(比如,"1652587")

'; 70 | echo '
'; 71 | } 72 | else 73 | { 74 | $id = trim($_GET['id']); 75 | echo Debug::dump($this->_douban->movie()->get($id)); 76 | } 77 | } 78 | 79 | public function action_movie_tags() 80 | { 81 | if ( ! $_GET) 82 | { 83 | echo '通过 movie id 获取电影标签:(比如,"1652587")

'; 84 | echo '
'; 85 | } 86 | else 87 | { 88 | $id = trim($_GET['id']); 89 | echo Debug::dump($this->_douban->movie()->tags($id)); 90 | } 91 | } 92 | 93 | public function action_movie_information_by_imdb() 94 | { 95 | if ( ! $_GET) 96 | { 97 | echo '通过 IMDB 获取电影信息:(比如,"tt0499549")

'; 98 | echo '
'; 99 | } 100 | else 101 | { 102 | $isbn = trim($_GET['isbn']); 103 | echo Debug::dump($this->_douban->movie()->imdb($isbn)); 104 | } 105 | } 106 | 107 | public function action_search_movie() 108 | { 109 | if ( ! $_GET) 110 | { 111 | echo '搜索电影:(比如, "avatar")

'; 112 | echo '
'; 113 | } 114 | else 115 | { 116 | $query = trim($_GET['query']); 117 | echo Debug::dump($this->_douban->movie()->search($query)); 118 | } 119 | } 120 | 121 | } 122 | 123 | -------------------------------------------------------------------------------- /classes/controller/douban/demo/people.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 12 | if ($this->_config->api_key AND $this->_config->api_secret) 13 | { 14 | $this->_douban = Douban::instance(); 15 | } 16 | else 17 | { 18 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 19 | } 20 | 21 | // base url 22 | $this->_base = $this->request->uri(); 23 | } 24 | 25 | public function action_index() 26 | { 27 | // get methods 28 | $methods = new ArrayIterator(get_class_methods($this)); 29 | // methods to ignore 30 | $ignore = array 31 | ( 32 | '__construct', 33 | '__call', 34 | 'action_index', 35 | 'before', 36 | 'after' 37 | ); 38 | 39 | $output = '

Douban People Demo

'; 40 | if ($people = $this->_douban->get_user()) 41 | { 42 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 43 | } 44 | else 45 | { 46 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.html('demo_douban/verity_oauth', '验证'); 47 | } 48 | $output .= '

    '; 49 | 50 | while ($methods->valid()) 51 | { 52 | $action = $methods->current(); 53 | if ( ! in_array($action, $ignore)) 54 | { 55 | $action = str_replace('action_', '', $action); 56 | $output .= '
  1. '.html::anchor($this->_base.'/'.$action, $action).'
  2. '; 57 | } 58 | $methods->next(); 59 | } 60 | $output .= '
'; 61 | // render 62 | $this->response->body($output); 63 | } 64 | 65 | public function action_my_profile() 66 | { 67 | if ($this->_douban->logged_in()) 68 | { 69 | echo Debug::dump($this->_douban->people()->get('me')); 70 | } 71 | else 72 | { 73 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 74 | } 75 | } 76 | 77 | public function action_my_friends() 78 | { 79 | if ($this->_douban->logged_in()) 80 | { 81 | echo Debug::dump($this->_douban->people()->get_friends('me')); 82 | } 83 | else 84 | { 85 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 86 | } 87 | } 88 | 89 | public function action_my_contacts() 90 | { 91 | if ($this->_douban->logged_in()) 92 | { 93 | echo Debug::dump($this->_douban->people()->get_contacts('me')); 94 | } 95 | else 96 | { 97 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 98 | } 99 | } 100 | 101 | public function action_people_profile() 102 | { 103 | if ( ! $_GET) 104 | { 105 | echo '通过 people id 获取用户资料: (比如,"icyleaf" or "1353793")

'; 106 | echo '
'; 107 | } 108 | else 109 | { 110 | $id = trim($_GET['id']); 111 | echo Debug::dump($this->_douban->people()->get($id)); 112 | } 113 | } 114 | 115 | public function action_search_people() 116 | { 117 | if ( ! $_GET) 118 | { 119 | echo '搜索用户: (比如,"icyleaf")

'; 120 | echo '
'; 121 | } 122 | else 123 | { 124 | $query = trim($_GET['query']); 125 | echo Debug::dump($this->_douban->people()->search($query)); 126 | } 127 | } 128 | 129 | } 130 | 131 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Douban API Package Module (ONLY for Kohana v3.2) 2 | 3 | > 项目**不再**维护开发。欢迎 PR,我会尽快合并发布新版本。 4 | 5 | Douban API Package 是一个使用 PHP 开发且基于 Kohana v3 开发的一个扩展(Module)。 6 | 7 | 它对[豆瓣](http://www.douban.com/) [API](http://www.douban.com/service/apidoc/) 进行重新封包。 8 | 它使用起来非常方便,可以快速开发一个 web 应用。 9 | 10 | ## 为什么要另外封包? 11 | 12 | 对,豆瓣官方提供了一种 PHP 的解决方案,可是需要庞大的支持库: 13 | Zend GData(20M左右)以及可能无法修改的环境配置(租用的空间主机会遇到这样的问题)。 14 | 虽说 Douban API Package 也是基于框架开发,不过 Kohana 是一个纯 PHP5 模式且体积小巧,优秀迅捷的框架。 15 | **最重要的是**,Douban API Package 提供了目前官方所有功能的支持。 16 | 并有线上网站:[豆瓣 API 控制台](http://ews.im/douban_console)长期运营。 17 | 18 | > 虽然它是基于 Kohana 框架开发,但是并没有使用太多框架的依赖,可在独立环境把报错地方替换从而轻松分离出来(通过邮件沟通得知成功案例) 19 | 20 | ## 支持 API 列表 21 | 22 | 目前 Douban API Package 支持豆瓣网提供 API 的全部功能,列表如下: 23 | 24 | * People - 获取用户信息,搜索用户 25 | * Book - 获取图书信息(subject id,ibsn)和标签,搜索图书 26 | * Movie - 获取电影信息(subject id,imdb)和标签,搜索电影 27 | * Music - 获取音乐信息和标签,搜索音乐 28 | * Broadcast - 获取广播信息(自己或友邻),发布广播,回复广播,删除广播 29 | * Collection - 获取收藏信息(条目或用户),创建收藏,删除收藏 30 | * Doumail - 获取豆邮信息(未读邮件,收件箱,发件箱),发送豆邮,删除豆邮 31 | * Event - 获取同城活动信息(通过用户或城市),获取同城详情(活动信息,GEO信息,参加人数详情,感兴趣人数详情),同城活动操作(参加/感兴趣/不参加),发布同城活动,更新同城活动,删除同城活动 32 | * Note - 获取用户日记信息,发布日记,更新日记,删除日记 33 | * Recommendation - 获取用户推荐信息,发布推荐,回应推荐,删除推荐,删除回应 34 | * Review - 获取条目评论信息(书影音),获取用户评论信息,发布评论,更新评论,删除评论 35 | * Album - 获取相册信息(目前只能获取同城活动的相册,偶然在 Event 的 API 字段中发现此接口) 36 | 37 | ## 已发现 API (受限) 38 | 39 | * Host - `http://api.douban.com/host/{id}` 访问会提示 `access page limit`. 40 | 41 | 42 | ## 安装需求 43 | 44 | * PHP 5.2.8+ with curl module 45 | * [Kohana v3](http://github.com/kohana/kohana) - 一款纯 PHP 5 框架,它的特点就是**高安全性**,**轻量级代码**,**容易使用**。 46 | 47 | ## 安装步骤 48 | 49 | 步骤 0: 部署 Kohana 50 | 51 | 下载并安装 Kohana 的过程,请大家参考此教程:[使用 Git 部署 Kohana 系统](http://kohanaframework.org/3.2/guide/kohana/tutorials/git) 52 | 53 | 步骤 1: 下载本扩展! 54 | 55 | 你可以在部署完毕的 Kohana 系统的根目录执行下面操作: 56 | 57 | $ git submodule add git://github.com/icyleaf/douban.git modules/douban 58 | 59 | 完成! 60 | 61 | 或者你也可以从本 [github 项目](http://github.com/icyleaf/douban)下载压缩包放置在 modules 文件夹下面。 62 | 63 | 步骤 2: 在 `bootstrap.php` 文件中启用该模块(默认情况下,存储在 `application' 文件夹) 64 | 65 | /** 66 | * Enable modules. Modules are referenced by a relative or absolute path. 67 | */ 68 | Kohana::modules(array( 69 | 'douban' => MODPATH.'douban', // Douban API Module 70 | // 'database' => MODPATH.'database', // Database access 71 | // 'image' => MODPATH.'image', // Image manipulation 72 | // 'orm' => MODPATH.'orm', // Object Relationship Mapping (not complete) 73 | // 'pagination' => MODPATH.'pagination', // Paging of results 74 | // 'paypal' => MODPATH.'paypal', // PayPal integration (not complete) 75 | // 'todoist' => MODPATH.'todoist', // Todoist integration 76 | // 'unittest' => MODPATH.'unittest', // Unit testing 77 | // 'codebench' => MODPATH.'codebench', // Benchmarking tool 78 | )); 79 | 80 | ## 目录结构 81 | 82 | douban 83 | +--- classes 84 | +--- controller # 豆瓣样例 85 | +--- douban 86 | +--- api # Douban API 87 | +--- book.php 88 | +--- . 89 | +--- . 90 | +--- . 91 | +--- core.php # Douban Core 92 | +--- oauth.php # Douban OAuth 93 | +--- request.php # Douban Requset 94 | +--- response.php # Douban Response 95 | +--- douban.php # Douban API Class 96 | +--- config 97 | +--- douban.php # 豆瓣 API 配置文件 98 | +--- user_agents.php # 奉送给大家一些手机 User agent 配置文件 99 | +--- media 100 | +--- images # 奉送给大家豆瓣 API 用到的图像 101 | +--- vendor 102 | +--- OAuth.php # OAuth 官方推荐 PHP 库 103 | +--- LICENSE 104 | +--- README.markdown 105 | 106 | ## 快速上手 107 | 108 | * Douban API Package 提供一些演示用例,系统部署完毕后可以通过 `http://host/douban_demo` 或者 `http://host/douban_console` 访问。 109 | 110 | ## 祝你开发顺利! 111 | 112 | 如果任何疑问或者 Bugs 反馈,即可以在本项目中提交 Issue。 113 | 114 | * Twitter: @icyleaf 115 | * 新浪微博: @icyleaf 116 | 117 | 04/22/2012 118 | -------------------------------------------------------------------------------- /config/user_agents.php: -------------------------------------------------------------------------------- 1 | array( 6 | 'windows nt 6.1' => 'Windows 7', 7 | 'windows NT 6.1' => 'Windows 7', 8 | 'windows nt 6.0' => 'Windows Vista', 9 | 'windows nt 5.2' => 'Windows 2003', 10 | 'windows nt 5.1' => 'Windows XP', 11 | 'windows nt 5.0' => 'Windows 2000', 12 | 'windows nt 4.0' => 'Windows NT', 13 | 'winnt4.0' => 'Windows NT', 14 | 'winnt 4.0' => 'Windows NT', 15 | 'winnt' => 'Windows NT', 16 | 'windows 98' => 'Windows 98', 17 | 'win98' => 'Windows 98', 18 | 'windows 95' => 'Windows 95', 19 | 'win95' => 'Windows 95', 20 | 'windows' => 'Unknown Windows OS', 21 | 'os x' => 'Mac OS X', 22 | 'intel mac' => 'Intel Mac', 23 | 'ppc mac' => 'PowerPC Mac', 24 | 'powerpc' => 'PowerPC', 25 | 'ppc' => 'PowerPC', 26 | 'cygwin' => 'Cygwin', 27 | 'linux' => 'Linux', 28 | 'debian' => 'Debian', 29 | 'openvms' => 'OpenVMS', 30 | 'sunos' => 'Sun Solaris', 31 | 'amiga' => 'Amiga', 32 | 'beos' => 'BeOS', 33 | 'apachebench' => 'ApacheBench', 34 | 'freebsd' => 'FreeBSD', 35 | 'netbsd' => 'NetBSD', 36 | 'bsdi' => 'BSDi', 37 | 'openbsd' => 'OpenBSD', 38 | 'os/2' => 'OS/2', 39 | 'warp' => 'OS/2', 40 | 'aix' => 'AIX', 41 | 'irix' => 'Irix', 42 | 'osf' => 'DEC OSF', 43 | 'hp-ux' => 'HP-UX', 44 | 'hurd' => 'GNU/Hurd', 45 | 'unix' => 'Unknown Unix OS', 46 | ), 47 | 48 | 'browser' => array( 49 | 'Opera' => 'Opera', 50 | 'MSIE' => 'Internet Explorer', 51 | 'Internet Explorer' => 'Internet Explorer', 52 | 'Shiira' => 'Shiira', 53 | 'Firefox' => 'Firefox', 54 | 'Chimera' => 'Chimera', 55 | 'Phoenix' => 'Phoenix', 56 | 'Firebird' => 'Firebird', 57 | 'Camino' => 'Camino', 58 | 'Navigator' => 'Netscape', 59 | 'Netscape' => 'Netscape', 60 | 'OmniWeb' => 'OmniWeb', 61 | 'Chrome' => 'Chrome', 62 | 'Safari' => 'Safari', 63 | 'Konqueror' => 'Konqueror', 64 | 'Epiphany' => 'Epiphany', 65 | 'Galeon' => 'Galeon', 66 | 'Mozilla' => 'Mozilla', 67 | 'icab' => 'iCab', 68 | 'lynx' => 'Lynx', 69 | 'links' => 'Links', 70 | 'hotjava' => 'HotJava', 71 | 'amaya' => 'Amaya', 72 | 'IBrowse' => 'IBrowse', 73 | 74 | // Mobile default browser 75 | 'NetFront' => 'NetFront', 76 | // China browers 77 | 'MAXTHON' => 'Maxthon', 78 | 'TheWorld' => 'TheWorld', 79 | '360SE' => '360SE', 80 | 'Ucweb' => 'Ucweb', 81 | ), 82 | 83 | 'mobile' => array( 84 | 'mobileexplorer' => 'Mobile Explorer', 85 | 'openwave' => 'Open Wave', 86 | 'opera mini' => 'Opera Mini', 87 | 'operamini' => 'Opera Mini', 88 | 'elaine' => 'Palm', 89 | 'palmsource' => 'Palm', 90 | 'digital paths' => 'Palm', 91 | 'avantgo' => 'Avantgo', 92 | 'xiino' => 'Xiino', 93 | 'palmscape' => 'Palmscape', 94 | 'nokia' => 'Nokia', 95 | 'ericsson' => 'Ericsson', 96 | 'blackBerry' => 'BlackBerry', 97 | 'motorola' => 'Motorola', 98 | 'iphone' => 'iPhone', 99 | 'android' => 'Android', 100 | 101 | // New added 102 | 'RIM' => 'BlackBerry', 103 | 'SAMSUNG' => 'Samsung', 104 | 'dopod' => 'Dopod', 105 | 'MOT-Q8_CMCC' => 'Motorola', 106 | 'ZTE-Me' => 'ZTE', 107 | 'iPod' => 'iPod', 108 | 109 | // Smart Phone 110 | 'Windows CE' => 'Unkown Smartphone', 111 | 'Smartphone' => 'Unkown Smartphone', 112 | 'HPiPAQ' => 'HP iPAQ', 113 | 114 | // Unkown cellphone 115 | 'UNTRUSTED' => 'Unkown Cellphone', 116 | 'Ucweb' => 'Ucweb', 117 | ), 118 | 119 | 'robot' => array( 120 | 'googlebot' => 'Googlebot', 121 | 'msnbot' => 'MSNBot', 122 | 'slurp' => 'Inktomi Slurp', 123 | 'yahoo' => 'Yahoo', 124 | 'askjeeves' => 'AskJeeves', 125 | 'fastcrawler' => 'FastCrawler', 126 | 'infoseek' => 'InfoSeek Robot 1.0', 127 | 'lycos' => 'Lycos', 128 | 129 | 'Baiduspider' => 'Baidu', 130 | 'Sogou' => 'Sogou', 131 | 'YodaoBot' => 'Youdao', 132 | 'Sosospider' => 'Soso', 133 | ), 134 | ); 135 | -------------------------------------------------------------------------------- /media/douban/console/js/console.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Douban API Console 3 | * 4 | * @author icyleaf 5 | * @version 0.2 6 | */ 7 | var db_api_console = { 8 | json_url: null, 9 | 10 | /** 11 | * Init 12 | */ 13 | init: function(logged_in, uid, json_url) 14 | { 15 | this.json_url = json_url; 16 | 17 | if (logged_in == 0) 18 | { 19 | // not logged in 20 | $('#user_id').attr('disabled', true).val('尚未登录'); 21 | $('#format').attr('disabled', true); 22 | $('#method').attr('disabled', true); 23 | $('#method_submit').attr('disabled', true); 24 | } 25 | else 26 | { 27 | // logged in 28 | $('#user_id').val(uid); 29 | } 30 | 31 | // Load api methods 32 | db_api_console.load_methods(); 33 | // Add method change linstener 34 | db_api_console.methods_listener(); 35 | // Added call api linstener 36 | db_api_console.call_api_listener(); 37 | }, 38 | 39 | /** 40 | * Load all method of douban api from json file 41 | */ 42 | load_methods: function() 43 | { 44 | $.getJSON(this.json_url, function(json){ 45 | var output = ''; 46 | // console.log(json); 47 | $.each(json, function(className, methods){ 48 | // console.log(methods); 49 | $.each(methods, function(method, value){ 50 | // console.log(method, args); 51 | output += ''; 53 | }); 54 | }); 55 | 56 | $('#method').empty().html(output); 57 | }); 58 | }, 59 | 60 | /** 61 | * Added Calling Douban API Listener 62 | */ 63 | call_api_listener: function() 64 | { 65 | $('#method_submit').bind('click', function(){ 66 | var value = $('select#method').find('option:selected').val(); 67 | if (value == 'none') 68 | { 69 | alert('请选择你要调用的 API 接口'); 70 | return; 71 | } 72 | 73 | var method = $('select#method').find('option:selected').text().split('.'); 74 | var args = $('select#method').find('option:selected').attr('label').split(','); 75 | var className = method[0]; 76 | var methodName = method[1]; 77 | var alt = $('select#format').find('option:selected').val(); 78 | var url = '/douban_console_'+className+'/'+methodName+'/'; 79 | var data = ''; 80 | 81 | if (value > 0) 82 | { 83 | for (var i = 0;i < value; i++) 84 | { 85 | var param_name = $('#label_' + i).html(); 86 | var param_value = $('#val_' + i).val(); 87 | 88 | if (param_value == '') 89 | continue; 90 | 91 | data += param_name + '=' + param_value + '&'; 92 | } 93 | } 94 | 95 | data += 'alt=' + alt; 96 | 97 | $('#method_submit').addClass('calling').attr('disabled', true).val("调用中..."); 98 | $.ajax({ 99 | type: 'POST', 100 | url: url, 101 | data: data, 102 | success: function(result){ 103 | result = $.trim(result); 104 | content = result.split('[-icyleaf-]'); 105 | 106 | $('#method_submit').removeClass('calling').attr('disabled', false).val("调用此方法"); 107 | output = '
'+content[1]+'
'; 108 | var id = ($('#label_0').text() == 'id' || $('#label_0').text()=='city') 109 | ? $('#val_0').val() : ''; 110 | 111 | $('#query_url').html(content[0].replace('%40', '@')); 112 | $('#trace').html(output); 113 | }, 114 | error: function(result){ 115 | var code = result.status; 116 | var message = result.statusText; 117 | 118 | output = '

'+message+' ('+code+')

'; 119 | $('#trace').html(output); 120 | $('#method_submit').removeClass('calling').attr('disabled', false).val("调用此方法"); 121 | } 122 | }); 123 | }); 124 | }, 125 | 126 | /** 127 | * Added Douban API Methods Listener 128 | */ 129 | methods_listener: function() 130 | { 131 | $('#method').bind('change', function(){ 132 | db_api_console.clear_args(); 133 | var value = $('select#method').find("option:selected").val(); 134 | if (value == 'none') 135 | { 136 | return; 137 | } 138 | 139 | var method = $('select#method').find("option:selected").text().split('.'); 140 | var args = $('select#method').find("option:selected").attr('ref').split(','); 141 | var className = method[0]; 142 | var methodName = method[1]; 143 | 144 | if (value > 0) 145 | { 146 | for (var i = 0; i < value; i++) 147 | { 148 | $('#label_'+i).text(args[i]); 149 | $('#arg_'+i).css('display', 'block'); 150 | } 151 | } 152 | }); 153 | }, 154 | 155 | /** 156 | * Clear hidden args on HTML codes 157 | */ 158 | clear_args: function() 159 | { 160 | for(var i = 0; i <= 8; i++) 161 | { 162 | $('#arg_'+i).css('display', 'none'); 163 | $('#val_'+i).val(''); 164 | } 165 | } 166 | }; -------------------------------------------------------------------------------- /media/douban/console/css/ie7.css: -------------------------------------------------------------------------------- 1 | /* HTTP Host: static.ak.fbcdn.net */ 2 | /* Generated: June 15th 2009 9:09:16 PM PDT */ 3 | /* Machine: 10.16.140.110 */ 4 | /* Source: Global Cache */ 5 | /* Location: css/ie7.css h:qn4w7u7o */ 6 | /* Locale: en_US */ 7 | /* Path: css/ie7.css */ 8 | 9 | 10 | .inputbutton, 11 | .inputsubmit{width:1;overflow:visible;padding:2px 15px;} 12 | #profilenarrowcolumn{margin-right:9px!important;} 13 | .profile .right_column_container .profile_sidebar_ads{margin-right:0px;} 14 | .box_head{padding-bottom:0px;} 15 | .box_head h2{padding-bottom:0px;} 16 | #pagefooter li{white-space:nowrap;} 17 | .fb_menu_dropdown{width:200px;} 18 | .profile .account_info{margin:0px -21px 2px -229px;padding:17px 21px 8px 237px;} 19 | #profileActions a:hover .unselected .text{padding-top:2px;} 20 | .app_icon_row{position:static;} 21 | .editaccount .settings_panel .email_option{padding-bottom:0px;} 22 | .editaccount #email .buttons{margin-top:0px;margin-left:0px;padding-left:22px;} 23 | .dropdown-container{direction:ltr;margin-left:2px;margin-top:1px;z-index:1000 !important;} 24 | .ubersearch .filters fieldset legend{margin:-5px -10px 0px -7px;padding-bottom:4px;width:133px } 25 | .filters #label_c2, .filters #label_c1{display:block;} 26 | .composer_fields label{padding-top:7px;} 27 | #compose_message .inputsubmit{padding-left:15px;padding-right:15px;} 28 | .inbox #megaboxx .subject_wrap .subject_text{display:inline-block;} 29 | .notifications .submission small{line-height:12px;} 30 | .notification_checkboxes{margin-top:-8px;} 31 | .notifications label{display:inline-block;} 32 | .confirminvite .inputsubmit, 33 | .confirminvite .inputbutton{padding:1px 3px;} 34 | .invite .left #summaryandpager h4{height:16px;} 35 | .explanation .square_bullets{margin-left:0;} 36 | .feedback .mixer_panel .saved_notification{display:inline-block;} 37 | .feedback .mixer_panel .left_buttons, .feedback .mixer_panel .right_buttons{margin-top:1px;} 38 | .feedback .mixer_panel .mixer_bar{margin-top:5px;margin-bottom:1px;} 39 | .vslider{padding-bottom:19px;} 40 | #shareitems .share_and_hide{width:60px;} 41 | #shareitems .x_to_hide{float:right;} 42 | .share_pop_dialog .status h2{padding-top:1px;padding-bottom:1px;} 43 | .video_permalink #add_comment{padding-top:15px;} 44 | .video_upload_page .separators{position:relative;margin-bottom:-1.1em;} 45 | .video_upload_page .separators a{display:inline-block;} 46 | .editor.showrows .hiddenrow{display:block;} 47 | .products .side_column{float:right;width:156px;} 48 | .products .app_browser li a, 49 | .products .app_browser li.app_type a{margin-left:1px;} 50 | .products .app_browser li.selected a{margin-left:0;border-left:1px solid #fff;} 51 | #content .thewall{margin-bottom:15px;} 52 | #inline_wall_post dd{margin-left:4px;} 53 | .attachment_compact .wall_video_thumb .play{margin-top:-22px;padding-bottom:3px;} 54 | #fb_multi_friend_selector .navigator_menu ul{height:220px;width:140px;} 55 | #fb_multi_friend_selector #friends .square{position:static;} 56 | #fb_multi_friend_selector #friends .square span{position:static;} 57 | #fb_multi_friend_selector #fs_filter_links a{display:block;float:left;} 58 | #fb_multi_friend_selector .navigator_menu{top:32px;} 59 | #fb_multi_friend_selector .cols_squeeze .navigator_menu{top:30px;} 60 | #fb_multi_friend_selector #fs_limit_by_list_menu{right:96px;} 61 | .num_cols_2 #fb_multi_friend_selector_wrapper .buttons{clear:both;} 62 | .num_cols_2 #fb_multi_friend_selector_wrapper #finder .hide{left:320px;top:44px;} 63 | .num_cols_3 #fb_multi_friend_selector_wrapper #finder .hide{left:351px;top:44px;} 64 | .editor #start_calendar, .editor #end_calendar{margin-top:18px;margin-left:-35px;} 65 | .calendar .month_title span{margin-left:-80px;} 66 | #fb_multi_friend_selector #skip_top{margin-top:12px;margin-right:0px;} 67 | .req_confirmation #req_msg_add{padding-top:6px;} 68 | input.request_form_submit{background:#3B5998 url(/images/icons/request_button_icon.gif) no-repeat 8px 5px;padding:3px 6px 3px 30px;overflow:visible;} 69 | .targeting_table td{padding:2px 1px;} 70 | .minifeed .photobox{padding-bottom:1px;} 71 | .privacy_widget .advanced .body .friends .advanced_friends_radio_item .radio_left{padding-left:0px;padding-top:0px;padding-left:3px;margin-top:-2px;} 72 | .privacy_widget .privacy_profile_advanced .checks label{padding-left:2px;} 73 | #presence #chat_tab_bar .chat_window_wrapper{margin-left:-219px;} 74 | .presence_popout #presence #chat_tab_bar .chat_window_wrapper{margin-left:0px;} 75 | #presence #presence_bar_buttons .titletip strong{margin:0;} 76 | #buddy_list .compact ul li{margin-top:-2px;} 77 | #buddy_list ul li.subheader{margin-top:-6px;} 78 | .presence_popout #presence #chat_tab_bar .chat_window .chat_conv p{margin-left:52px;padding-left:0px;} 79 | .in_videochat #presence #chat_tab_bar .chat_window .chat_conv p{margin-left:7px;} 80 | #presence .emote_text, 81 | #presence .emote_img{margin-bottom:-3px;} 82 | .translations_admin #all_translations .show_all tr.hidden_elem{display:block;} 83 | .profile_pic_suggest_top_buttons{padding:0 0 0px 0;} 84 | .profile_pic_suggest_top_buttons .toggle_tabs{position:relative;top:1px;} 85 | 86 | 87 | #bootloader_css_ie7_css { height: 42px; } -------------------------------------------------------------------------------- /classes/controller/douban/demo/doumail.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 13 | if ($this->_config->api_key AND $this->_config->api_secret) 14 | { 15 | $this->_douban = Douban::instance(); 16 | } 17 | else 18 | { 19 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 20 | } 21 | 22 | // base url 23 | $this->_base = $this->request->uri(); 24 | // Douban doumail class 25 | $this->_doumail = $this->_douban->doumail(); 26 | } 27 | 28 | public function action_index() 29 | { 30 | // get methods 31 | $methods = new ArrayIterator(get_class_methods($this)); 32 | // methods to ignore 33 | $ignore = array 34 | ( 35 | '__construct', 36 | '__call', 37 | 'action_index', 38 | 'before', 39 | 'after' 40 | ); 41 | 42 | $output = '

Douban doumail Demo

'; 43 | if ($people = $this->_douban->get_user()) 44 | { 45 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 46 | } 47 | else 48 | { 49 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.html('demo_douban/verity_oauth', '验证'); 50 | } 51 | $output .= '

    '; 52 | 53 | while ($methods->valid()) 54 | { 55 | $action = $methods->current(); 56 | if ( ! in_array($action, $ignore)) 57 | { 58 | $action = str_replace('action_', '', $action); 59 | $output .= '
  1. '.html::anchor($this->_base.'/'.$action, $action).'
  2. '; 60 | } 61 | $methods->next(); 62 | } 63 | $output .= '
'; 64 | // render 65 | $this->response->body($output); 66 | } 67 | 68 | public function action_doumail_information() 69 | { 70 | if ( ! $_GET) 71 | { 72 | echo '通过 doumail id 获得豆邮信息:(你可以从下面操作中查询结果获得)

'; 73 | echo '
'; 74 | } 75 | else 76 | { 77 | $id = trim($_GET['id']); 78 | echo Debug::dump($this->_doumail->get($id)); 79 | } 80 | } 81 | 82 | public function action_list_unread_box() 83 | { 84 | if ($this->_douban->logged_in()) 85 | { 86 | echo Debug::dump($this->_doumail->get_unread()); 87 | } 88 | else 89 | { 90 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 91 | } 92 | } 93 | 94 | public function action_list_inbox() 95 | { 96 | if ($this->_douban->logged_in()) 97 | { 98 | echo Debug::dump($this->_doumail->get_inbox()); 99 | } 100 | else 101 | { 102 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 103 | } 104 | } 105 | 106 | public function action_list_outbox() 107 | { 108 | if ($this->_douban->logged_in()) 109 | { 110 | echo Debug::dump($this->_doumail->get_outbox()); 111 | } 112 | else 113 | { 114 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 115 | } 116 | } 117 | 118 | public function action_send_doumail() 119 | { 120 | if ($this->_douban->logged_in()) 121 | { 122 | if ($_POST) 123 | { 124 | foreach ($_POST as $key => $value) 125 | { 126 | if (empty($_POST[$key])) 127 | { 128 | echo '['.$key.'] 不能为空!'; 129 | return; 130 | } 131 | } 132 | 133 | $result = $this->_doumail->send($_POST); 134 | if ($result) 135 | { 136 | if (is_bool($result) AND $result) 137 | { 138 | echo '发送成功!'; 139 | return; 140 | } 141 | } 142 | else 143 | { 144 | echo '发送失败:'; 145 | echo Debug::dump($this->_doumail->errors()); 146 | return; 147 | } 148 | } 149 | 150 | if (isset($result)) 151 | echo Debug::dump($result); 152 | 153 | echo '

发送豆邮

'; 154 | echo '
people id:
'; 155 | echo '
'; 156 | echo '标题:

'; 157 | echo '内容:

'; 158 | if (isset($result) AND is_array($result)) 159 | { 160 | echo ''; 161 | } 162 | if (isset($result) AND is_array($result)) 163 | { 164 | echo '
'; 165 | echo '验证码:
'; 166 | } 167 | 168 | echo '
'; 169 | } 170 | else 171 | { 172 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 173 | } 174 | } 175 | 176 | public function action_delete_doumail() 177 | { 178 | if ($this->_douban->logged_in()) 179 | { 180 | 181 | if ( ! $_GET) 182 | { 183 | echo '通过 doumail id 删除豆邮信息:(你可以从下面操作中查询结果获得)

'; 184 | echo '
'; 185 | } 186 | else 187 | { 188 | $id = trim($_GET['id']); 189 | if ($this->_doumail->delete($id)) 190 | { 191 | echo '删除成功!'; 192 | } 193 | else 194 | { 195 | echo '删除失败:'; 196 | echo Debug::dump($this->_doumail->errors()); 197 | } 198 | } 199 | } 200 | else 201 | { 202 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 203 | } 204 | } 205 | 206 | } 207 | 208 | -------------------------------------------------------------------------------- /classes/douban/api/music.php: -------------------------------------------------------------------------------- 1 | 13 | * @link http://icyleaf.com 14 | * @copyright (c) 2009-2010 icyleaf 15 | * @license http://www.apache.org/licenses/LICENSE-2.0 16 | */ 17 | class Douban_API_Music extends Douban_Core { 18 | 19 | /** 20 | * Get music information 21 | * 22 | * @param int $music_id 23 | * @return mixed 24 | */ 25 | public function get($music_id) 26 | { 27 | $url = Douban_Core::MUSIC_URL.$music_id; 28 | $post_data = array 29 | ( 30 | 'alt' => $this->alt 31 | ); 32 | $result = $this->_client->get($url, $post_data); 33 | 34 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 35 | { 36 | $result = Douban_API_MUSIC::format($result->to_json()); 37 | } 38 | 39 | return $result; 40 | } 41 | 42 | 43 | /** 44 | * Get tags form a music 45 | * 46 | * @param int $music_id 47 | * @return mixed 48 | */ 49 | public function tags($music_id) 50 | { 51 | $url = Douban_Core::MUSIC_URL.$music_id.'/tags'; 52 | $post_data = array 53 | ( 54 | 'alt' => $this->alt 55 | ); 56 | $result = $this->_client->get($url, $post_data); 57 | 58 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 59 | { 60 | $music = $result->to_json(); 61 | 62 | $result = new stdClass; 63 | $result->title = $music['title']['$t']; 64 | // search 65 | $result->index = $music['openSearch:startIndex']['$t']; 66 | $result->max = $music['openSearch:itemsPerPage']['$t']; 67 | $result->total = $music['openSearch:totalResults']['$t']; 68 | if ($result->total > 0) 69 | { 70 | // tags 71 | foreach ($music['entry'] as $tag) 72 | { 73 | $result->entry[] = array 74 | ( 75 | 'title' => $tag['title']['$t'], 76 | 'count' => $tag['db:count']['$t'], 77 | 'url' => $tag['id']['$t'], 78 | ); 79 | } 80 | } 81 | } 82 | 83 | return $result; 84 | } 85 | 86 | /** 87 | * Search music result 88 | * 89 | * @param string $query 90 | * @param int $index 91 | * @param int $max 92 | * @return mixed 93 | */ 94 | public function search($query, $index = 1, $max = 10) 95 | { 96 | $url = substr(Douban_Core::MUSIC_URL, 0, strlen(Douban_Core::MUSIC_URL) - 1).'s'; 97 | $post_data = array 98 | ( 99 | 'q' => $query, 100 | 'start-index' => $index, 101 | 'max-results' => $max, 102 | 'alt' => $this->alt 103 | ); 104 | 105 | return $this->_get_musics($url, $post_data); 106 | } 107 | 108 | /** 109 | * Format music 110 | * 111 | * @param array $music 112 | * @return object 113 | */ 114 | public static function format($music) 115 | { 116 | $result = new stdClass; 117 | // id 118 | $result->id = substr($music['id']['$t'], strlen(Douban_Core::MUSIC_URL)); 119 | // title 120 | $result->title = $music['title']['$t']; 121 | // category 122 | $result->category = substr($music['category']['@term'], strlen(Douban_Core::CATEGORY_URL)); 123 | // authors 124 | if (isset($music['author'])) 125 | { 126 | foreach ($music['author'] as $author) 127 | { 128 | $result->author[] = $author['name']['$t']; 129 | } 130 | } 131 | // attribute 132 | foreach ($music['db:attribute'] as $att) 133 | { 134 | $result->attribute[$att['@name']] = $att['$t']; 135 | } 136 | // link 137 | foreach ($music['link'] as $link) 138 | { 139 | $result->link[$link['@rel']] = $link['@href']; 140 | } 141 | // Set the default music image 142 | if ( ! isset($result->link['image'])) 143 | { 144 | $result->link['image'] = Douban_Core::DEFAULT_MUSIC_IMAGE_URL; 145 | } 146 | // rating 147 | if (isset($music['gd:rating'])) 148 | { 149 | foreach ($music['gd:rating'] as $key => $value) 150 | { 151 | $result->rating[substr($key, 1)] = $value; 152 | } 153 | } 154 | // tags 155 | if (isset($music['db:tag'])) 156 | { 157 | foreach ($music['db:tag'] as $tag) 158 | { 159 | $result->tags[$tag['@name']] = $tag['@count']; 160 | } 161 | } 162 | // cummary 163 | $result->summary = isset($music['summary']['$t']) ? $music['summary']['$t'] : ''; 164 | 165 | return $result; 166 | } 167 | 168 | /** 169 | * Get musics 170 | * 171 | * @param string $url 172 | * @param array $post_data 173 | * @return mixed 174 | */ 175 | private function _get_musics($url, $post_data = array()) 176 | { 177 | $result = $this->_client->get($url, $post_data); 178 | 179 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 180 | { 181 | $musics = $result->to_json(); 182 | 183 | $result = new stdClass; 184 | $result->title = $musics['title']['$t']; 185 | // search 186 | $result->index = $musics['opensearch:startIndex']['$t']; 187 | $result->max = $musics['opensearch:itemsPerPage']['$t']; 188 | $result->total = $musics['opensearch:totalResults']['$t']; 189 | if ($result->total > 0) 190 | { 191 | // musics 192 | foreach ($musics['entry'] as $music) 193 | { 194 | $result->entry[] = Douban_API_MUSIC::format($music); 195 | } 196 | } 197 | } 198 | 199 | return $result; 200 | } 201 | 202 | } 203 | 204 | -------------------------------------------------------------------------------- /classes/controller/douban/demo.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 12 | if ($this->_config->api_key AND $this->_config->api_secret) 13 | { 14 | $this->_douban = Douban::instance(); 15 | } 16 | else 17 | { 18 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 19 | } 20 | 21 | // base url 22 | $this->_base = $this->request->uri(); 23 | } 24 | 25 | /** 26 | * Douban Entry 27 | * 28 | * @link http://www.douban.com/service/apidoc/ 29 | */ 30 | public function action_index() 31 | { 32 | // get methods 33 | $methods = new ArrayIterator(get_class_methods($this)); 34 | // methods to ignore 35 | $ignore = array 36 | ( 37 | '__construct', 38 | '__call', 39 | 'action_index', 40 | 'action_check', 41 | 'before', 42 | 'after' 43 | ); 44 | 45 | $apis = array( 46 | 'people', 'book', 'movie', 'music', 'broadcast', 47 | 'doumail', 'collection', 'review', 'recommendation', 48 | 'album' 49 | ); 50 | 51 | $output = '

豆瓣 API 演示用例

'; 52 | if ($people = $this->_douban->get_user()) 53 | { 54 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 55 | $ignore[] = 'action_verity_oauth'; 56 | } 57 | else 58 | { 59 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.HTML::anchor('douban_demo/verity_oauth', '验证'); 60 | } 61 | $output .= '


    '; 62 | while ($methods->valid()) 63 | { 64 | $action = $methods->current(); 65 | if ( ! in_array($action, $ignore)) 66 | { 67 | $action = str_replace('action_', '', $action); 68 | $output .= '
  1. '.HTML::anchor($this->_base.'/'.$action, $action).'
  2. '; 69 | } 70 | $methods->next(); 71 | } 72 | $output .= '
'; 73 | $output .= '

其他API

    '; 74 | foreach ($apis as $api) 75 | { 76 | $output .= '
  1. '.HTML::anchor('douban_demo'.$api, $api).'
  2. '; 77 | } 78 | $output .= '
'; 79 | // render 80 | $this->response->body($output); 81 | } 82 | 83 | /** 84 | * Douban OAuth 85 | */ 86 | public function action_verity_oauth() 87 | { 88 | $callback_url = url::site('demo_douban/check'); 89 | if ($auth_url = $this->_douban->verify($callback_url)) 90 | { 91 | $this->request->redirect($auth_url); 92 | } 93 | else 94 | { 95 | echo Debug::dump($this->_douban->errors()); 96 | } 97 | } 98 | 99 | /** 100 | * Check OAuth Token 101 | */ 102 | public function action_check() 103 | { 104 | if ($_GET) 105 | { 106 | $result = $this->_douban->login(); 107 | 108 | if ($result) 109 | { 110 | $this->request->redirect('demo_douban'); 111 | } 112 | } 113 | } 114 | 115 | public function action_my_profile() 116 | { 117 | if ($this->_douban->logged_in()) 118 | { 119 | echo Debug::dump($this->_douban->people()->get('me')); 120 | } 121 | else 122 | { 123 | echo HTML::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 124 | } 125 | } 126 | 127 | 128 | public function action_my_friends() 129 | { 130 | if ($this->_douban->logged_in()) 131 | { 132 | echo Debug::dump($this->_douban->people()->get_friends('me')); 133 | } 134 | else 135 | { 136 | echo HTML::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 137 | } 138 | } 139 | 140 | public function action_my_contacts() 141 | { 142 | if ($this->_douban->logged_in()) 143 | { 144 | echo Debug::dump($this->_douban->people()->get_contacts('me')); 145 | } 146 | else 147 | { 148 | echo HTML::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 149 | } 150 | } 151 | 152 | public function action_my_broadcasts() 153 | { 154 | if ($this->_douban->logged_in()) 155 | { 156 | echo Debug::dump($this->_douban->broadcast()->get_mine('me')); 157 | } 158 | else 159 | { 160 | echo HTML::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 161 | } 162 | } 163 | 164 | public function action_my_notes() 165 | { 166 | if ($this->_douban->logged_in()) 167 | { 168 | echo Debug::dump($this->_douban->note()->get_by_people('me')); 169 | } 170 | else 171 | { 172 | echo HTML::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 173 | } 174 | } 175 | 176 | public function action_my_collections() 177 | { 178 | if ($this->_douban->logged_in()) 179 | { 180 | echo Debug::dump($this->_douban->collection()->get_by_people('me')); 181 | } 182 | else 183 | { 184 | echo HTML::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 185 | } 186 | } 187 | 188 | public function action_my_recommendations() 189 | { 190 | if ($this->_douban->logged_in()) 191 | { 192 | echo Debug::dump($this->_douban->recommendation()->get_by_people('me')); 193 | } 194 | else 195 | { 196 | echo HTML::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 197 | } 198 | } 199 | 200 | public function action_my_reviews() 201 | { 202 | if ($this->_douban->logged_in()) 203 | { 204 | echo Debug::dump($this->_douban->review()->get_by_people('me')); 205 | } 206 | else 207 | { 208 | echo HTML::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 209 | } 210 | } 211 | 212 | public function action_logged_in() 213 | { 214 | $status = '状态: '; 215 | if ($this->_douban->logged_in()) 216 | { 217 | $status .= '已经登录'; 218 | } 219 | else 220 | { 221 | $status .= '没有登录'; 222 | } 223 | 224 | $this->response->body($status); 225 | } 226 | 227 | public function action_loggout() 228 | { 229 | $this->_douban->logout(); 230 | 231 | $this->response->body('成功退出'); 232 | } 233 | 234 | } 235 | 236 | -------------------------------------------------------------------------------- /classes/controller/douban/demo/collection.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 13 | if ($this->_config->api_key AND $this->_config->api_secret) 14 | { 15 | $this->_douban = Douban::instance(); 16 | } 17 | else 18 | { 19 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 20 | } 21 | 22 | // base url 23 | $this->_base = $this->request->uri(); 24 | // Douban collection class 25 | $this->_collection = $this->_douban->collection(); 26 | } 27 | 28 | public function action_index() 29 | { 30 | // get methods 31 | $methods = new ArrayIterator(get_class_methods($this)); 32 | // methods to ignore 33 | $ignore = array 34 | ( 35 | '__construct', 36 | '__call', 37 | 'action_index', 38 | 'before', 39 | 'after' 40 | ); 41 | 42 | $output = '

Douban collection Demo

'; 43 | if ($people = $this->_douban->get_user()) 44 | { 45 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 46 | } 47 | else 48 | { 49 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.html('demo_douban/verity_oauth', '验证'); 50 | } 51 | $output .= '

    '; 52 | 53 | while ($methods->valid()) 54 | { 55 | $action = $methods->current(); 56 | if ( ! in_array($action, $ignore)) 57 | { 58 | $action = str_replace('action_', '', $action); 59 | $output .= '
  1. '.html::anchor($this->_base.'/'.$action, $action).'
  2. '; 60 | } 61 | $methods->next(); 62 | } 63 | $output .= '
'; 64 | // render 65 | $this->response->body($output); 66 | } 67 | 68 | public function action_collection_information() 69 | { 70 | if ( ! $_GET) 71 | { 72 | echo '通过 collection id 获得收藏信息:(比如,"197872797")
'; 73 | echo '
'; 74 | } 75 | else 76 | { 77 | $id = trim($_GET['id']); 78 | echo Debug::dump($this->_collection->get($id)); 79 | } 80 | } 81 | 82 | public function action_get_collections_by_people() 83 | { 84 | if ( ! $_GET) 85 | { 86 | echo '获得用户的收藏信息:(比如,"icyleaf" 或 "1353793")
'; 87 | echo '
'; 88 | } 89 | else 90 | { 91 | $id = trim($_GET['id']); 92 | echo Debug::dump($this->_collection->get_by_people($id)); 93 | } 94 | } 95 | 96 | public function action_create_collection() 97 | { 98 | if ($this->_douban->logged_in()) 99 | { 100 | if ( ! $_POST) 101 | { 102 | echo '

创建或更新收藏

'; 103 | echo '
Subject URL (book/music/movie)
'; 104 | echo '比如:http://api.douban.com/movie/subject/1652587

'; 105 | echo '状态:

'; 106 | echo '评分 (可选, 0-5) :

'; 107 | echo '简评 (可选):

'; 108 | echo '标签 (可选,使用逗号分隔):

'; 109 | echo '隐私 :公开私有
'; 110 | echo '
'; 111 | echo '

状态

'; 112 | echo '

book(图书):wish(想读), readin(在读), read(读过)

'; 113 | echo '

music(音乐):wish(想听), listening(在听), listened(听过)

'; 114 | echo '

movie(电影):wish(想看), watching(在看), watched(看过)

'; 115 | } 116 | else 117 | { 118 | foreach ($_POST as $key => $value) 119 | { 120 | if ($_POST[$key] == NULL OR $_POST[$key] == '') 121 | { 122 | echo '['.$key.'] 不能为空!'; 123 | return; 124 | } 125 | else 126 | { 127 | if ($key == 'tags' AND ! empty($value)) 128 | { 129 | $_POST[$key] = array(); 130 | 131 | $tags = explode(',', $value); 132 | $tag_count = count($tags); 133 | 134 | if ($tag_count > 0) 135 | { 136 | for ($i = 0; $i < count($tags); $i++) 137 | { 138 | $_POST[$key][$i] = trim($tags[$i]); 139 | } 140 | } 141 | } 142 | else 143 | { 144 | $_POST[$key] = trim($value); 145 | } 146 | } 147 | } 148 | $result = $this->_collection->create($_POST); 149 | if ($result) 150 | { 151 | echo '创建成功!'; 152 | echo html::anchor('demo_douban_collection/collection_information?id='.$result, '查看'); 153 | echo ' 或 '; 154 | echo html::anchor('demo_douban_collection/delete_collection?id='.$result, '删除'); 155 | } 156 | else 157 | { 158 | echo Debug::dump($this->_collection->errors()); 159 | } 160 | } 161 | } 162 | else 163 | { 164 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 165 | } 166 | } 167 | 168 | public function action_delete_collection() 169 | { 170 | if ($this->_douban->logged_in()) 171 | { 172 | if ( ! $_GET) 173 | { 174 | echo '通过 collection id 删除收藏信息:
'; 175 | echo '
'; 176 | } 177 | else 178 | { 179 | $id = trim($_GET['id']); 180 | if ($this->_collection->delete($id)) 181 | { 182 | echo '删除成功!'; 183 | } 184 | else 185 | { 186 | echo '删除失败: '; 187 | echo Debug::dump($this->_collection->errors()); 188 | } 189 | } 190 | } 191 | else 192 | { 193 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 194 | } 195 | } 196 | 197 | } 198 | 199 | -------------------------------------------------------------------------------- /classes/douban/api/book.php: -------------------------------------------------------------------------------- 1 | 13 | * @link http://icyleaf.com 14 | * @copyright (c) 2009 icyleaf 15 | * @license http://www.apache.org/licenses/LICENSE-2.0 16 | */ 17 | class Douban_API_Book extends Douban_Core { 18 | 19 | /** 20 | * Get book information 21 | * 22 | * @param int $book_id 23 | * @return mixed 24 | */ 25 | public function get($book_id) 26 | { 27 | $url = Douban_Core::BOOK_URL.$book_id; 28 | $post_data = array 29 | ( 30 | 'alt' => $this->alt 31 | ); 32 | $result = $this->_client->get($url, $post_data); 33 | 34 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 35 | { 36 | $result = Douban_API_Book::format($result->to_json()); 37 | } 38 | 39 | return $result; 40 | } 41 | 42 | /** 43 | * Get book access isbn number 44 | * 45 | * @param int $number 46 | * @return mixed 47 | */ 48 | public function isbn($number) 49 | { 50 | $url = Douban_Core::BOOK_URL.'isbn/'.$number; 51 | $post_data = array 52 | ( 53 | 'alt' => $this->alt 54 | ); 55 | $result = $this->_client->get($url, $post_data); 56 | 57 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 58 | { 59 | $result = Douban_API_Book::format($result->to_json()); 60 | } 61 | 62 | return $result; 63 | } 64 | 65 | /** 66 | * Get tags form a book 67 | * 68 | * @param int $book_id 69 | * @return mixed 70 | */ 71 | public function tags($book_id) 72 | { 73 | $url = Douban_Core::BOOK_URL.$book_id.'/tags'; 74 | $post_data = array 75 | ( 76 | 'alt' => $this->alt 77 | ); 78 | $result = $this->_client->get($url, $post_data); 79 | 80 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 81 | { 82 | $book = $result->to_json(); 83 | 84 | $result = new stdClass; 85 | $result->title = $book['title']['$t']; 86 | // search 87 | $result->index = $book['openSearch:startIndex']['$t']; 88 | $result->max = $book['openSearch:itemsPerPage']['$t']; 89 | $result->total = $book['openSearch:totalResults']['$t']; 90 | if ($result->total > 0) 91 | { 92 | // tags 93 | foreach ($book['entry'] as $tag) 94 | { 95 | $result->entry[] = array 96 | ( 97 | 'title' => $tag['title']['$t'], 98 | 'count' => $tag['db:count']['$t'], 99 | 'url' => $tag['id']['$t'], 100 | ); 101 | } 102 | } 103 | } 104 | 105 | return $result; 106 | } 107 | 108 | /** 109 | * Search book result 110 | * 111 | * @param string $query 112 | * @param int $index 113 | * @param int $max 114 | * @return mixed 115 | */ 116 | public function search($query, $index = 1, $max = 10) 117 | { 118 | $url = substr(Douban_Core::BOOK_URL, 0, strlen(Douban_Core::BOOK_URL) - 1).'s'; 119 | $post_data = array 120 | ( 121 | 'q' => $query, 122 | 'start-index' => $index, 123 | 'max-results' => $max, 124 | 'alt' => $this->alt 125 | ); 126 | 127 | return $this->_get_books($url, $post_data); 128 | } 129 | 130 | /** 131 | * Format book 132 | * 133 | * @param array $book 134 | * @return object 135 | */ 136 | public static function format($book) 137 | { 138 | $result = new stdClass; 139 | // id 140 | $result->id = substr($book['id']['$t'], strlen(Douban_Core::BOOK_URL)); 141 | // title 142 | $result->title = $book['title']['$t']; 143 | // category 144 | $result->category = substr($book['category']['@term'], strlen(Douban_Core::CATEGORY_URL)); 145 | // authors 146 | if (isset($book['author'])) 147 | { 148 | foreach ($book['author'] as $author) 149 | { 150 | $result->author[] = $author['name']['$t']; 151 | } 152 | } 153 | // attribute 154 | foreach ($book['db:attribute'] as $att ) 155 | { 156 | $result->attribute[$att['@name']] = $att['$t']; 157 | } 158 | // link 159 | foreach ($book['link'] as $link) 160 | { 161 | $result->link[$link['@rel']] = $link['@href']; 162 | } 163 | // Set the default book image 164 | if ( ! isset($result->link['image'])) 165 | { 166 | $result->link['image'] = Douban_Core::DEFAULT_BOOK_IMAGE_URL; 167 | } 168 | // rating 169 | if (isset($book['gd:rating'])) 170 | { 171 | foreach ($book['gd:rating'] as $key => $value) 172 | { 173 | $result->rating[substr($key, 1)] = $value; 174 | } 175 | } 176 | // tags 177 | if (isset($book['db:tag'])) 178 | { 179 | foreach ($book['db:tag'] as $tag) 180 | { 181 | $result->tags[$tag['@name']] = $tag['@count']; 182 | } 183 | } 184 | // cummary 185 | $result->summary = isset($book['summary']['$t']) ? $book['summary']['$t'] : ''; 186 | 187 | return $result; 188 | } 189 | 190 | /** 191 | * Get books 192 | * 193 | * @param string $url 194 | * @param array $post_data 195 | * @return mixed 196 | */ 197 | private function _get_books($url, $post_data = array()) 198 | { 199 | $result = $this->_client->get($url, $post_data); 200 | 201 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 202 | { 203 | $books = $result->to_json(); 204 | 205 | $result = new stdClass; 206 | $result->title = $books['title']['$t']; 207 | // search 208 | $result->index = $books['opensearch:startIndex']['$t']; 209 | $result->max = $books['opensearch:itemsPerPage']['$t']; 210 | $result->total = $books['opensearch:totalResults']['$t']; 211 | if ($result->total > 0) 212 | { 213 | // books 214 | foreach ($books['entry'] as $book) 215 | { 216 | $result->entry[] = Douban_API_Book::format($book); 217 | } 218 | } 219 | } 220 | 221 | return $result; 222 | } 223 | 224 | } 225 | 226 | -------------------------------------------------------------------------------- /classes/controller/douban/demo/broadcast.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 13 | if ($this->_config->api_key AND $this->_config->api_secret) 14 | { 15 | $this->_douban = Douban::instance(); 16 | } 17 | else 18 | { 19 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 20 | } 21 | 22 | // base url 23 | $this->_base = $this->request->uri(); 24 | // Douban broadcast class 25 | $this->_broadcast = $this->_douban->broadcast(); 26 | } 27 | 28 | public function action_index() 29 | { 30 | // get methods 31 | $methods = new ArrayIterator(get_class_methods($this)); 32 | // methods to ignore 33 | $ignore = array 34 | ( 35 | '__construct', 36 | '__call', 37 | 'action_index', 38 | 'before', 39 | 'after' 40 | ); 41 | 42 | $output = '

Douban broadcast Demo

'; 43 | if ($people = $this->_douban->get_user()) 44 | { 45 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 46 | } 47 | else 48 | { 49 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.html('demo_douban/verity_oauth', '验证'); 50 | } 51 | $output .= '

    '; 52 | 53 | while ($methods->valid()) 54 | { 55 | $action = $methods->current(); 56 | if ( ! in_array($action, $ignore)) 57 | { 58 | $action = str_replace('action_', '', $action); 59 | $output .= '
  1. '.html::anchor($this->_base.'/'.$action, $action).'
  2. '; 60 | } 61 | $methods->next(); 62 | } 63 | $output .= '
'; 64 | // render 65 | $this->response->body($output); 66 | } 67 | 68 | public function action_broadcast_information() 69 | { 70 | if ( ! $_GET) 71 | { 72 | echo '通过 broadcast id 获得广播信息:(比如,"255649446")
'; 73 | echo '
'; 74 | } 75 | else 76 | { 77 | $id = trim($_GET['id']); 78 | echo Debug::dump($this->_broadcast->get($id)); 79 | } 80 | } 81 | 82 | public function action_list_broadcast_replies() 83 | { 84 | if ( ! $_GET) 85 | { 86 | echo '通过 broadcast id 获得广播回复:(比如,"255633574")
'; 87 | echo '
'; 88 | } 89 | else 90 | { 91 | $id = trim($_GET['id']); 92 | echo Debug::dump($this->_broadcast->get_comments($id)); 93 | } 94 | } 95 | 96 | public function action_my_broadcasts() 97 | { 98 | if ($this->_douban->logged_in()) 99 | { 100 | echo Debug::dump($this->_broadcast->get_mine()); 101 | } 102 | else 103 | { 104 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 105 | } 106 | } 107 | 108 | public function action_contacts_broadcasts() 109 | { 110 | if ($this->_douban->logged_in()) 111 | { 112 | echo Debug::dump($this->_broadcast->get_contacts()); 113 | } 114 | else 115 | { 116 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 117 | } 118 | } 119 | 120 | public function action_create_broadcast() 121 | { 122 | if ($this->_douban->logged_in()) 123 | { 124 | if ( ! $_POST) 125 | { 126 | echo '

发布一个广播

'; 127 | echo '
消息:
'; 128 | echo '
'; 129 | echo '
'; 130 | } 131 | else 132 | { 133 | $message = trim($_POST['message']); 134 | $result = $this->_broadcast->create($message); 135 | if ($result) 136 | { 137 | echo '创建成功!'; 138 | echo html::anchor('demo_douban_broadcast/broadcast_information?id='.$result, '查看', array('target' => '_blank')); 139 | echo ' 或 '; 140 | echo html::anchor('demo_douban_broadcast/reply_broadcast?id='.$result, '回复', array('target' => '_blank')); 141 | echo ' 或 '; 142 | echo html::anchor('demo_douban_broadcast/delete_broadcast?id='.$result, '删除', array('target' => '_blank')); 143 | echo '.'; 144 | } 145 | else 146 | { 147 | echo Debug::dump($this->_broadcast->errors()); 148 | } 149 | } 150 | } 151 | else 152 | { 153 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 154 | } 155 | } 156 | 157 | public function action_delete_broadcast() 158 | { 159 | if ($this->_douban->logged_in()) 160 | { 161 | 162 | if ( ! $_GET) 163 | { 164 | echo '通过 broadcast id 删除广播回复:
'; 165 | echo '
'; 166 | } 167 | else 168 | { 169 | $id = trim($_GET['id']); 170 | if ($this->_broadcast->delete($id)) 171 | { 172 | echo '删除成功!'; 173 | } 174 | else 175 | { 176 | echo '删除失败:'; 177 | echo Debug::dump($this->_broadcast->errors()); 178 | } 179 | } 180 | } 181 | else 182 | { 183 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 184 | } 185 | } 186 | 187 | public function action_reply_broadcast() 188 | { 189 | if ($this->_douban->logged_in()) 190 | { 191 | $id = isset($_GET['id'])?$_GET['id']:''; 192 | if ( ! $_POST) 193 | { 194 | echo '

回应广播

'; 195 | echo '
broadcast id:
'; 196 | echo '
'; 197 | echo '消息:

'; 198 | echo '
'; 199 | } 200 | else 201 | { 202 | $miniblog_id = trim($_POST['miniblog_id']); 203 | $message = trim($_POST['message']); 204 | $result = $this->_broadcast->reply($miniblog_id, $message); 205 | if ($result) 206 | { 207 | echo '回应成功!'; 208 | } 209 | else 210 | { 211 | echo '回应失败:'; 212 | echo Debug::dump($this->_broadcast->errors()); 213 | } 214 | } 215 | } 216 | else 217 | { 218 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 219 | } 220 | } 221 | 222 | } 223 | 224 | -------------------------------------------------------------------------------- /classes/controller/douban/demo/review.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 13 | if ($this->_config->api_key AND $this->_config->api_secret) 14 | { 15 | $this->_douban = Douban::instance(); 16 | } 17 | else 18 | { 19 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 20 | } 21 | 22 | // base url 23 | $this->_base = $this->request->uri(); 24 | // Douban Review class 25 | $this->_review = $this->_douban->review(); 26 | } 27 | 28 | public function action_index() 29 | { 30 | // get methods 31 | $methods = new ArrayIterator(get_class_methods($this)); 32 | // methods to ignore 33 | $ignore = array 34 | ( 35 | '__construct', 36 | '__call', 37 | 'action_index', 38 | 'before', 39 | 'after' 40 | ); 41 | 42 | $output = '

Douban review Demo

'; 43 | if ($people = $this->_douban->get_user()) 44 | { 45 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 46 | } 47 | else 48 | { 49 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.html('demo_douban/verity_oauth', '验证'); 50 | } 51 | $output .= '

    '; 52 | 53 | while ($methods->valid()) 54 | { 55 | $action = $methods->current(); 56 | if ( ! in_array($action, $ignore)) 57 | { 58 | $action = str_replace('action_', '', $action); 59 | $output .= '
  1. '.html::anchor($this->_base.'/'.$action, $action).'
  2. '; 60 | } 61 | $methods->next(); 62 | } 63 | $output .= '
'; 64 | // render 65 | $this->response->body($output); 66 | } 67 | 68 | public function action_review_information() 69 | { 70 | if ( ! $_GET) 71 | { 72 | echo '通过 review id 获得评论信息: (比如, "1579992")

'; 73 | echo '
'; 74 | } 75 | else 76 | { 77 | $id = trim($_GET['id']); 78 | echo Debug::dump($this->_review->get($id)); 79 | } 80 | } 81 | 82 | public function action_get_reviews_by_people() 83 | { 84 | if ( ! $_GET) 85 | { 86 | echo '获取用户评论:(比如,"icyleaf" or "1353793")

'; 87 | echo '
'; 88 | } 89 | else 90 | { 91 | $id = trim($_GET['id']); 92 | echo Debug::dump($this->_review->get_by_people($id)); 93 | } 94 | } 95 | 96 | public function action_get_reviews_by_book() 97 | { 98 | if ( ! $_GET) 99 | { 100 | echo '通过 book id 获取某图书的评论:(比如, "1082387")

'; 101 | echo '
'; 102 | } 103 | else 104 | { 105 | $id = trim($_GET['id']); 106 | echo Debug::dump($this->_review->get_by_book($id)); 107 | } 108 | } 109 | 110 | public function action_get_reviews_by_movie() 111 | { 112 | if ( ! $_GET) 113 | { 114 | echo '通过 movie id 获取某电影的评论:(比如,"1652587")

'; 115 | echo '
'; 116 | } 117 | else 118 | { 119 | $id = trim($_GET['id']); 120 | echo Debug::dump($this->_review->get_by_movie($id)); 121 | } 122 | } 123 | 124 | public function action_get_reviews_by_music() 125 | { 126 | if ( ! $_GET) 127 | { 128 | echo '通过 music id 获取某音乐的评论:(比如,"3040149")

'; 129 | echo '
'; 130 | } 131 | else 132 | { 133 | $id = trim($_GET['id']); 134 | echo Debug::dump($this->_review->get_by_music($id)); 135 | } 136 | } 137 | 138 | public function action_get_reviews_by_ibsn() 139 | { 140 | if ( ! $_GET) 141 | { 142 | echo '通过 IBSN 获取某图书的评论:(比如,"9787532729357")

'; 143 | echo '
'; 144 | } 145 | else 146 | { 147 | $ibsn = trim($_GET['ibsn']); 148 | echo Debug::dump($this->_review->get_by_isbn($ibsn)); 149 | } 150 | } 151 | 152 | public function action_get_reviews_by_imdb() 153 | { 154 | if ( ! $_GET) 155 | { 156 | echo '通过 IMDB 获取某电影的评论:(比如,"tt0499549")

'; 157 | echo '
'; 158 | } 159 | else 160 | { 161 | $imdb = trim($_GET['imdb']); 162 | echo Debug::dump($this->_review->get_by_imdb($imdb)); 163 | } 164 | } 165 | 166 | public function action_create_review() 167 | { 168 | if ($this->_douban->logged_in()) 169 | { 170 | if ( ! $_POST) 171 | { 172 | echo '

发布评论

'; 173 | echo '
Subject URL (book/music/movie)
'; 174 | echo '比如,http://api.douban.com/movie/subject/1652587

'; 175 | echo '标题:

'; 176 | echo '评价 (0-5):

'; 177 | echo '评语 (大于 150 个字):

'; 178 | echo '
'; 179 | } 180 | else 181 | { 182 | foreach ($_POST as $key => $value) 183 | { 184 | if ($_POST[$key] == NULL OR $_POST[$key] == '') 185 | { 186 | echo '['.$key.'] 不能为空!'; 187 | return; 188 | } 189 | else 190 | { 191 | $_POST[$key] = trim($value); 192 | } 193 | } 194 | 195 | $result = $this->_review->create($_POST); 196 | if ($result) 197 | { 198 | echo '创建成功!'; 199 | echo html::anchor('demo_douban_review/review_information?id='.$result, '查看'); 200 | echo ' 或 '; 201 | echo html::anchor('demo_douban_review/delete_review?id='.$result, '删除'); 202 | } 203 | else 204 | { 205 | echo Debug::dump($this->_review->errors()); 206 | } 207 | } 208 | } 209 | else 210 | { 211 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 212 | } 213 | } 214 | 215 | public function action_delete_review() 216 | { 217 | if ($this->_douban->logged_in()) 218 | { 219 | 220 | if ( ! $_GET) 221 | { 222 | echo '通过 review id 删除评论信息:
'; 223 | echo '
'; 224 | } 225 | else 226 | { 227 | $id = trim($_GET['id']); 228 | if ($this->_review->delete($id)) 229 | { 230 | echo '删除成功!'; 231 | } 232 | else 233 | { 234 | echo '删除失败'; 235 | echo Debug::dump($this->_review->errors()); 236 | } 237 | } 238 | } 239 | else 240 | { 241 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 242 | } 243 | } 244 | 245 | } 246 | 247 | -------------------------------------------------------------------------------- /classes/douban/api/movie.php: -------------------------------------------------------------------------------- 1 | 13 | * @link http://icyleaf.com 14 | * @copyright (c) 2009-2010 icyleaf 15 | * @license http://www.apache.org/licenses/LICENSE-2.0 16 | */ 17 | class Douban_API_Movie extends Douban_Core { 18 | 19 | /** 20 | * Get movie information 21 | * 22 | * @param int $movie_id 23 | * @return mixed 24 | */ 25 | public function get($movie_id) 26 | { 27 | $url = Douban_Core::MOVIE_URL.$movie_id; 28 | $post_data = array 29 | ( 30 | 'alt' => $this->alt 31 | ); 32 | $result = $this->_client->get($url, $post_data); 33 | 34 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 35 | { 36 | $result = Douban_API_Movie::format($result->to_json()); 37 | } 38 | 39 | return $result; 40 | } 41 | 42 | /** 43 | * Get movie access imdb number 44 | * 45 | * @param int $number 46 | * @return mixed 47 | */ 48 | public function imdb($number) 49 | { 50 | $url = Douban_Core::MOVIE_URL.'imdb/'.$number; 51 | $post_data = array 52 | ( 53 | 'alt' => $this->alt 54 | ); 55 | $result = $this->_client->get($url, $post_data); 56 | 57 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 58 | { 59 | $result = Douban_API_Movie::format($result->to_json()); 60 | } 61 | 62 | return $result; 63 | } 64 | 65 | /** 66 | * Get tags form a movie 67 | * 68 | * @param int $movie_id 69 | * @return mixed 70 | */ 71 | public function tags($movie_id) 72 | { 73 | $url = Douban_Core::MOVIE_URL.$movie_id.'/tags'; 74 | $post_data = array 75 | ( 76 | 'alt' => $this->alt 77 | ); 78 | $result = $this->_client->get($url, $post_data); 79 | 80 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 81 | { 82 | $movie = $result->to_json(); 83 | 84 | $result = new stdClass; 85 | $result->title = $movie['title']['$t']; 86 | // search 87 | $result->index = $movie['openSearch:startIndex']['$t']; 88 | $result->max = $movie['openSearch:itemsPerPage']['$t']; 89 | $result->total = $movie['openSearch:totalResults']['$t']; 90 | if ($result->total > 0) 91 | { 92 | // tags 93 | foreach ($movie['entry'] as $tag) 94 | { 95 | $result->entry[] = array 96 | ( 97 | 'title' => $tag['title']['$t'], 98 | 'count' => $tag['db:count']['$t'], 99 | 'url' => $tag['id']['$t'], 100 | ); 101 | } 102 | } 103 | } 104 | 105 | return $result; 106 | } 107 | 108 | /** 109 | * Search movie result 110 | * 111 | * @param string $query 112 | * @param int $index 113 | * @param int $max 114 | * @return mixed 115 | */ 116 | public function search($query, $index = 1, $max = 10) 117 | { 118 | $url = substr(Douban_Core::MOVIE_URL, 0, strlen(Douban_Core::MOVIE_URL) - 1).'s'; 119 | $post_data = array 120 | ( 121 | 'q' => $query, 122 | 'start-index' => $index, 123 | 'max-results' => $max, 124 | 'alt' => $this->alt 125 | ); 126 | 127 | return $this->_get_movies($url, $post_data); 128 | } 129 | 130 | /** 131 | * Format movie 132 | * 133 | * @param array $movie 134 | * @return object 135 | */ 136 | public static function format($movie) 137 | { 138 | $result = new stdClass; 139 | // id 140 | $result->id = substr($movie['id']['$t'], strlen(Douban_Core::MOVIE_URL)); 141 | // title 142 | $result->title = $movie['title']['$t']; 143 | // category 144 | $result->category = substr($movie['category']['@term'], strlen(Douban_Core::CATEGORY_URL)); 145 | // authors 146 | if (isset($movie['author'])) 147 | { 148 | foreach ($movie['author'] as $author) 149 | { 150 | $result->author[] = $author['name']['$t']; 151 | } 152 | } 153 | // attribute 154 | foreach ($movie['db:attribute'] as $att) 155 | { 156 | if (isset($result->attribute[$att['@name']])) 157 | { 158 | if (is_array($result->attribute[$att['@name']])) 159 | { 160 | $result->attribute[$att['@name']][] = $att['$t']; 161 | } 162 | else 163 | { 164 | $temp = $result->attribute[$att['@name']]; 165 | $result->attribute[$att['@name']] = array($temp, $att['$t']); 166 | } 167 | } 168 | else 169 | { 170 | $result->attribute[$att['@name']] = $att['$t']; 171 | } 172 | } 173 | 174 | if (isset($result->attribute['imdb'])) 175 | { 176 | $imdb = $result->attribute['imdb']; 177 | $imdb_url = 'http://www.imdb.com/title/'; 178 | $result->attribute['imdb'] = substr(substr($imdb, strlen($imdb_url)), 0, -1); 179 | unset($imdb, $imdb_url); 180 | } 181 | 182 | // link 183 | foreach ($movie['link'] as $link) 184 | { 185 | $result->link[$link['@rel']] = $link['@href']; 186 | } 187 | // Set the default music image 188 | if ( ! isset($result->link['image'])) 189 | { 190 | $result->link['image'] = Douban_Core::DEFAULT_MUSIC_IMAGE_URL; 191 | } 192 | // rating 193 | if (isset($movie['gd:rating'])) 194 | { 195 | foreach ($movie['gd:rating'] as $key => $value) 196 | { 197 | $result->rating[substr($key, 1)] = $value; 198 | } 199 | } 200 | // tags 201 | if (isset($movie['db:tag'])) 202 | { 203 | foreach ($movie['db:tag'] as $tag) 204 | { 205 | $result->tags[$tag['@name']][] = $tag['@count']; 206 | } 207 | } 208 | // cummary 209 | $result->summary = isset($movie['summary']['$t']) ? $movie['summary']['$t'] : ''; 210 | 211 | return $result; 212 | } 213 | 214 | /** 215 | * Get movies 216 | * 217 | * @param string $url 218 | * @param array $post_data 219 | * @return mixed 220 | */ 221 | private function _get_movies($url, $post_data = array()) 222 | { 223 | $result = $this->_client->get($url, $post_data); 224 | 225 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 226 | { 227 | $movies = $result->to_json(); 228 | 229 | $result = new stdClass; 230 | $result->title = $movies['title']['$t']; 231 | // search 232 | $result->index = $movies['opensearch:startIndex']['$t']; 233 | $result->max = $movies['opensearch:itemsPerPage']['$t']; 234 | $result->total = $movies['opensearch:totalResults']['$t']; 235 | if ($result->total > 0) 236 | { 237 | // musics 238 | foreach ($movies['entry'] as $movie) 239 | { 240 | $result->entry[] = Douban_API_Movie::format($movie); 241 | } 242 | } 243 | } 244 | 245 | return $result; 246 | } 247 | 248 | } 249 | 250 | -------------------------------------------------------------------------------- /classes/douban/api/people.php: -------------------------------------------------------------------------------- 1 | 13 | * @link http://icyleaf.com 14 | * @copyright (c) 2009-2010 icyleaf 15 | * @license http://www.apache.org/licenses/LICENSE-2.0 16 | */ 17 | class Douban_API_People extends Douban_Core { 18 | 19 | /** 20 | * Get people information 21 | * 22 | * @param mixed $people_id 23 | * @return mixed 24 | */ 25 | public function get($people_id = NULL) 26 | { 27 | $people_id = (empty($people_id) OR $people_id == 'me') ? '@me' : $people_id; 28 | $url = Douban_Core::PEOPLE_URL.urlencode($people_id); 29 | $post_data = array 30 | ( 31 | 'alt' => $this->alt 32 | ); 33 | 34 | $result = $this->_client->get($url, $post_data); 35 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 36 | { 37 | $result = Douban_API_People::format($result->to_json()); 38 | } 39 | 40 | return $result; 41 | } 42 | 43 | /** 44 | * Get someone's friends 45 | * 46 | * @param mixed $people_id 47 | * @param int $index 48 | * @param int $max 49 | * @return mixed 50 | */ 51 | public function get_friends($people_id = NULL, $index = 1, $max = 10) 52 | { 53 | $people_id = (empty($people_id) OR $people_id == 'me') ? '@me' : $people_id; 54 | $url = Douban_Core::PEOPLE_URL.urlencode($people_id).'/friends'; 55 | $post_data = array 56 | ( 57 | 'start-index' => $index, 58 | 'max-results' => $max, 59 | 'alt' => $this->alt, 60 | ); 61 | $result = $this->_client->get($url, $post_data); 62 | 63 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 64 | { 65 | $result = Douban_API_People::get_peoples($result->to_json()); 66 | } 67 | 68 | return $result; 69 | } 70 | 71 | /** 72 | * Get someone's contacts 73 | * 74 | * @param mixed $people_id 75 | * @param int $index 76 | * @param int $max 77 | * @return mixed 78 | */ 79 | public function get_contacts($people_id = NULL, $index = 1, $max = 10) 80 | { 81 | $people_id = (empty($people_id) OR $people_id == 'me') ? '@me' : $people_id; 82 | $url = Douban_Core::PEOPLE_URL.urlencode($people_id).'/contacts'; 83 | $post_data = array 84 | ( 85 | 'start-index' => $index, 86 | 'max-results' => $max, 87 | 'alt' => $this->alt, 88 | ); 89 | $result = $this->_client->get($url, $post_data); 90 | 91 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 92 | { 93 | $result = Douban_API_People::get_peoples($result->to_json()); 94 | } 95 | 96 | return $result; 97 | } 98 | 99 | /** 100 | * Search peoples 101 | * 102 | * @param string $query 103 | * @param int $index 104 | * @param int $max 105 | * @return mixed 106 | */ 107 | public function search($query, $index = 1, $max = 10) 108 | { 109 | $url = substr(Douban_Core::PEOPLE_URL, 0, strlen(Douban_Core::PEOPLE_URL) - 1); 110 | $post_data = array 111 | ( 112 | 'q' => $query, 113 | 'start-index' => $index, 114 | 'max-results' => $max, 115 | 'alt' => $this->alt 116 | ); 117 | $result = $this->_client->get($url, $post_data); 118 | 119 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 120 | { 121 | $result = Douban_API_People::get_peoples($result->to_json()); 122 | } 123 | 124 | return $result; 125 | } 126 | 127 | /** 128 | * Get peoples 129 | * 130 | * @param array $peoples 131 | * @return mixed 132 | */ 133 | public static function get_peoples($peoples) 134 | { 135 | $result = new stdClass; 136 | $result->title = $peoples['title']['$t']; 137 | // author 138 | if (isset($peoples['author'])) 139 | { 140 | $result->author = Douban_API_People::format($peoples['author']); 141 | } 142 | // search 143 | $result->index = $peoples['openSearch:startIndex']['$t']; 144 | $result->max = $peoples['openSearch:itemsPerPage']['$t']; 145 | $result->total = $peoples['openSearch:totalResults']['$t']; 146 | if ($result->total > 0) 147 | { 148 | // peoples 149 | foreach ($peoples['entry'] as $people) 150 | { 151 | $result->entry[] = Douban_API_People::format($people); 152 | } 153 | } 154 | else 155 | { 156 | $result->entry = array(); 157 | } 158 | 159 | return $result; 160 | } 161 | 162 | /** 163 | * Format eople 164 | * 165 | * @param array $people 166 | * @return object 167 | */ 168 | public static function format($people) 169 | { 170 | $result = new stdClass; 171 | // id 172 | if (isset($people['uri'])) 173 | { 174 | if (isset($people['uri'][0])) 175 | { 176 | if (strpos($people['uri'][0]['$t'], Douban_Core::PEOPLE_URL) !== FALSE) 177 | { 178 | $people_url = Douban_Core::PEOPLE_URL; 179 | $result->type = 'people'; 180 | } 181 | else 182 | { 183 | $people_url = Douban_Core::HOST_URL; 184 | $result->type = 'host'; 185 | } 186 | $result->id = substr($people['uri'][0]['$t'], strlen($people_url)); 187 | } 188 | else 189 | { 190 | if (strpos($people['uri']['$t'], Douban_Core::PEOPLE_URL) !== FALSE) 191 | { 192 | $people_url = Douban_Core::PEOPLE_URL; 193 | $result->type = 'people'; 194 | } 195 | else 196 | { 197 | $people_url = Douban_Core::HOST_URL; 198 | $result->type = 'host'; 199 | } 200 | $result->id = substr($people['uri']['$t'], strlen($people_url)); 201 | } 202 | 203 | } 204 | elseif (isset($people['id'])) 205 | { 206 | $result->type = 'people'; 207 | $result->id = substr($people['id']['$t'], strlen(Douban_Core::PEOPLE_URL)); 208 | } 209 | // name 210 | if (isset($people['title'])) 211 | { 212 | $result->name = $people['title']['$t']; 213 | } 214 | elseif (isset($people['name'])) 215 | { 216 | $result->name = $people['name']['$t']; 217 | } 218 | // nick 219 | if (isset($people['db:uid'])) 220 | { 221 | $result->nick = $people['db:uid']['$t']; 222 | } 223 | // location 224 | if (isset($people['db:location'])) 225 | { 226 | $result->location = array 227 | ( 228 | 'id' => $people['db:location']['@id'], 229 | 'title' => $people['db:location']['$t'], 230 | ); 231 | } 232 | // signature 233 | if (isset($people['db:signature'])) 234 | { 235 | $result->signature = $people['db:signature']['$t']; 236 | } 237 | // links 238 | if (isset($people['link'])) 239 | { 240 | foreach ($people['link'] as $link) 241 | { 242 | $result->link[$link['@rel']] = $link['@href']; 243 | } 244 | } 245 | // Set the default people avatar 246 | if ( ! isset($result->link['icon'])) 247 | { 248 | $result->link['icon'] = Douban_Core::DEFAULT_PEOPLE_AVATAR_URL; 249 | } 250 | // content 251 | if (isset($people['content'])) 252 | { 253 | $result->content = $people['content']['$t']; 254 | } 255 | 256 | return $result; 257 | } 258 | 259 | } 260 | 261 | -------------------------------------------------------------------------------- /classes/douban/request.php: -------------------------------------------------------------------------------- 1 | 7 | * @link http://icyleaf.com 8 | * @copyright (c) 2009-2010 icyleaf 9 | * @license http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Base on Curl Library by Matt Wells (www.ninjapenguin.co.uk) 12 | */ 13 | class Douban_Request { 14 | 15 | private $_config = array(); 16 | private $_resource = NULL; 17 | private static $_status = NULL; 18 | 19 | /** 20 | * Factory Method 21 | * 22 | * @param string $data 23 | * @chainable 24 | */ 25 | public static function factory($data = array()) 26 | { 27 | return new Douban_Request($data); 28 | } 29 | 30 | /** 31 | * Constructor 32 | * 33 | * @param array curl data 34 | */ 35 | public function __construct($data = array()) 36 | { 37 | if( ! function_exists('curl_init')) 38 | { 39 | throw new Exception('A cURL error occurred. It appears you do not have cURL installed!'); 40 | } 41 | 42 | $config = array 43 | ( 44 | CURLOPT_HEADER => FALSE 45 | ); 46 | 47 | //Apply any passed configuration 48 | $data += $config; 49 | $this->_config = $data; 50 | 51 | $this->_resource = curl_init(); 52 | 53 | //Apply configuration settings 54 | foreach ($this->_config as $key => $value) 55 | { 56 | $this->set_opt($key, $value); 57 | } 58 | } 59 | 60 | /** 61 | * Set option 62 | * 63 | * @param string Curl option to set 64 | * @param string Value for option 65 | * @chainable 66 | */ 67 | public function set_opt($key, $value) 68 | { 69 | curl_setopt($this->_resource, $key, $value); 70 | 71 | return $this; 72 | } 73 | 74 | /** 75 | * Execute the curl request and return the response 76 | * 77 | * @return string Returned output from the requested resource 78 | * @throws Kohana_User_Exception 79 | */ 80 | public function exec() 81 | { 82 | $ret = curl_exec($this->_resource); 83 | 84 | //Wrap the error reporting in an exception 85 | if($ret === FALSE) 86 | { 87 | throw new Exception("Curl Error: ".curl_error($this->_resource)); 88 | } 89 | else 90 | { 91 | Douban_Request::$_status = curl_getinfo($this->_resource, CURLINFO_HTTP_CODE); 92 | return $ret; 93 | } 94 | } 95 | 96 | /** 97 | * Get curl Error 98 | * 99 | * @return string any current error for the curl request 100 | */ 101 | public function get_error() 102 | { 103 | return curl_error($this->_resource); 104 | } 105 | 106 | /** 107 | * Destructor 108 | */ 109 | function __destruct() 110 | { 111 | curl_close($this->_resource); 112 | } 113 | 114 | /** 115 | * Request GET method 116 | * Execute an HTTP GET request using curl 117 | * 118 | * @param string url to request 119 | * @param array additional headers to send in the request 120 | * @param boolean flag to return only the headers 121 | * @param array Additional curl options to instantiate curl with 122 | */ 123 | public static function get($url, Array $headers = array(), $headers_only = FALSE, Array $curl_options = array()) 124 | { 125 | return Douban_Request::request('GET', $url, NULL, $headers, $headers_only, $curl_options); 126 | } 127 | 128 | /** 129 | * Request POST method 130 | * Execute an HTTP POST request using curl 131 | * 132 | * @param string url to request 133 | * @param mixed past data to post to $url 134 | * @param array additional headers to send in the request 135 | * @param boolean flag to return only the headers 136 | * @param array Additional curl options to instantiate curl with 137 | */ 138 | public static function post($url, $data = '', Array $headers = array(), $headers_only = FALSE, Array $curl_options = array()) 139 | { 140 | return Douban_Request::request('POST', $url, $data, $headers, $headers_only, $curl_options); 141 | } 142 | 143 | /** 144 | * Request PUT method 145 | * Execute an HTTP PUT request using curl 146 | * 147 | * @param string url to request 148 | * @param array additional headers to send in the request 149 | * @param boolean flag to return only the headers 150 | * @param array Additional curl options to instantiate curl with 151 | */ 152 | public static function put($url, $data = '', Array $headers = array(), $headers_only = FALSE, Array $curl_options = array()) 153 | { 154 | return Douban_Request::request('PUT', $url, $data, $headers, $headers_only, $curl_options); 155 | } 156 | 157 | /** 158 | * Request DELETE method 159 | * Execute an HTTP DELETE request using curl 160 | * 161 | * @param string url to request 162 | * @param array additional headers to send in the request 163 | * @param boolean flag to return only the headers 164 | * @param array Additional curl options to instantiate curl with 165 | */ 166 | public static function delete($url, $data = '', Array $headers = array(), $headers_only = FALSE, Array $curl_options = array()) 167 | { 168 | return Douban_Request::request('DELETE', $url, $data, $headers, $headers_only, $curl_options); 169 | } 170 | 171 | /** 172 | * Execute an HTTP request 173 | * 174 | * @param string request method 175 | * @param string url to request 176 | * @param string additional headers to send in the request 177 | * @param boolean flag to return only the headers 178 | * @param array flag to return only the headers 179 | */ 180 | private static function request($method, $url, $data = NULL, Array $headers = array(), $headers_only = FALSE, Array $curl_options = array()) 181 | { 182 | $ch = Douban_Request::factory($curl_options); 183 | $method = strtoupper($method); 184 | 185 | $ch->set_opt(CURLOPT_URL, $url) 186 | ->set_opt(CURLOPT_RETURNTRANSFER, TRUE) 187 | ->set_opt(CURLOPT_NOBODY, $headers_only); 188 | 189 | // Available methods: GET, POST, PUT, DELETE 190 | switch( $method ) 191 | { 192 | default: 193 | case 'GET': 194 | break; 195 | case 'POST': 196 | $request = ''; 197 | if (is_array($data)) 198 | { 199 | foreach ($data as $key => $value) 200 | { 201 | $request .= $key.'='.$value.'&'; 202 | } 203 | } 204 | else 205 | { 206 | $request = $data; 207 | } 208 | 209 | $ch->set_opt(CURLOPT_POST, TRUE)->set_opt(CURLOPT_POSTFIELDS, $request); 210 | break; 211 | case 'PUT': 212 | $request = ''; 213 | if (is_array($data)) 214 | { 215 | foreach ($data as $key => $value) 216 | { 217 | $request .= $key.'='.$value.'&'; 218 | } 219 | } 220 | else 221 | { 222 | $request = $data; 223 | } 224 | $ch->set_opt(CURLOPT_CUSTOMREQUEST, 'PUT')->set_opt(CURLOPT_POSTFIELDS, $request); 225 | break; 226 | case 'DELETE': 227 | $ch->set_opt(CURLOPT_CUSTOMREQUEST, 'DELETE'); 228 | break; 229 | } 230 | 231 | //Set any additional headers 232 | if( ! empty($headers)) 233 | { 234 | $ch->set_opt(CURLOPT_HTTPHEADER, $headers); 235 | } 236 | 237 | $response = array 238 | ( 239 | 'url' => $url, 240 | 'method' => $method, 241 | 'content' => $ch->exec(), 242 | 'status' => Douban_Request::$_status, 243 | ); 244 | 245 | return new Douban_Response($response); 246 | } 247 | } -------------------------------------------------------------------------------- /classes/controller/douban/demo/recommendation.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban'); 13 | if ($this->_config->api_key AND $this->_config->api_secret) 14 | { 15 | $this->_douban = Douban::instance(); 16 | } 17 | else 18 | { 19 | throw new Kohana_Exception('豆瓣 API Key 或 Secrect 是空的!'); 20 | } 21 | 22 | // base url 23 | $this->_base = $this->request->uri(); 24 | // Douban recommendation class 25 | $this->_recommendation = $this->_douban->recommendation(); 26 | } 27 | 28 | public function action_index() 29 | { 30 | // get methods 31 | $methods = new ArrayIterator(get_class_methods($this)); 32 | // methods to ignore 33 | $ignore = array 34 | ( 35 | '__construct', 36 | '__call', 37 | 'action_index', 38 | 'before', 39 | 'after' 40 | ); 41 | 42 | $output = '

Douban recommendation Demo

'; 43 | if ($people = $this->_douban->get_user()) 44 | { 45 | $output .= '你好,'.$people->name.'。你已经通过 OAuth 验证,你可以尝试下面操作:'; 46 | } 47 | else 48 | { 49 | $output .= '你好,请在执行下面操作前通过豆瓣 OAuth 的'.html('demo_douban/verity_oauth', '验证'); 50 | } 51 | $output .= '

    '; 52 | 53 | while ($methods->valid()) 54 | { 55 | $action = $methods->current(); 56 | if ( ! in_array($action, $ignore)) 57 | { 58 | $action = str_replace('action_', '', $action); 59 | $output .= '
  1. '.html::anchor($this->_base.'/'.$action, $action).'
  2. '; 60 | } 61 | $methods->next(); 62 | } 63 | $output .= '
'; 64 | // render 65 | $this->response->body($output); 66 | } 67 | 68 | public function action_recommendation_information() 69 | { 70 | if ( ! $_GET) 71 | { 72 | echo '通过 recommendation id 获得推荐信息:(比如,"15064757")

'; 73 | echo '
'; 74 | } 75 | else 76 | { 77 | $id = trim($_GET['id']); 78 | echo Debug::dump($this->_recommendation->get($id)); 79 | } 80 | } 81 | 82 | public function action_get_recommendations_by_people() 83 | { 84 | if ( ! $_GET) 85 | { 86 | echo '获得用户的推荐信息:(比如,"icyleaf" or "1353793")

'; 87 | echo '
'; 88 | } 89 | else 90 | { 91 | $id = trim($_GET['id']); 92 | echo Debug::dump($this->_recommendation->get_by_people($id)); 93 | } 94 | } 95 | 96 | public function action_get_recommendation_comment() 97 | { 98 | if ( ! $_GET) 99 | { 100 | echo '通过 recommendation id 获得推荐的回应:(比如,"15064757")

'; 101 | echo '
'; 102 | } 103 | else 104 | { 105 | $id = trim($_GET['id']); 106 | echo Debug::dump($this->_recommendation->get_comments($id)); 107 | } 108 | } 109 | 110 | public function action_create_recommendation() 111 | { 112 | if ($this->_douban->logged_in()) 113 | { 114 | if ( ! $_POST) 115 | { 116 | echo '

创建推荐

'; 117 | echo '
Subject URL (book/music/movie)
'; 118 | echo '比如,http://api.douban.com/movie/subject/1652587
'; 119 | echo '
'; 120 | echo '推荐语:
'; 121 | echo '
'; 122 | echo '
'; 123 | } 124 | else 125 | { 126 | foreach ($_POST as $key => $value) 127 | { 128 | if ($_POST[$key] == NULL OR $_POST[$key] == '') 129 | { 130 | echo '['.$key.'] is empty!'; 131 | return; 132 | } 133 | else 134 | { 135 | $_POST[$key] = trim($value); 136 | } 137 | } 138 | 139 | $result = $this->_recommendation->create($_POST['subject_url'], $_POST['comment']); 140 | if ($result) 141 | { 142 | echo '成功创建! '; 143 | echo html::anchor('demo_douban_recommendation/recommendation_information?id='.$result, '查看'); 144 | echo ' 或 '; 145 | echo html::anchor('demo_douban_recommendation/reply_recommendation?id='.$result, '回应', array('target' => '_blank')); 146 | echo ' 或 '; 147 | echo html::anchor('demo_douban_recommendation/delete_recommendation?id='.$result, '删除'); 148 | } 149 | else 150 | { 151 | echo Debug::dump($this->_recommendation->errors()); 152 | } 153 | } 154 | } 155 | else 156 | { 157 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 158 | } 159 | } 160 | 161 | public function action_delete_recommendation() 162 | { 163 | if ($this->_douban->logged_in()) 164 | { 165 | 166 | if ( ! $_GET) 167 | { 168 | echo '通过 recommendation id 删除推荐信息:

'; 169 | echo '
'; 170 | } 171 | else 172 | { 173 | $id = trim($_GET['id']); 174 | if ($this->_recommendation->delete_recommendation($id)) 175 | { 176 | echo '删除成功!'; 177 | } 178 | else 179 | { 180 | echo '删除失败:'; 181 | echo Debug::dump($this->_recommendation->errors()); 182 | } 183 | } 184 | } 185 | else 186 | { 187 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 188 | } 189 | } 190 | 191 | public function action_reply_recommendation() 192 | { 193 | if ($this->_douban->logged_in()) 194 | { 195 | $id = isset($_GET['id'])?$_GET['id']:''; 196 | if ( ! $_POST) 197 | { 198 | echo '

回应推荐

'; 199 | echo '
recommendation id:
'; 200 | echo '
'; 201 | echo '内容:

'; 202 | echo '
'; 203 | } 204 | else 205 | { 206 | $miniblog_id = trim($_POST['miniblog_id']); 207 | $content = trim($_POST['content']); 208 | $result = $this->_recommendation->reply($miniblog_id, $content); 209 | if ($result) 210 | { 211 | echo '创建成功!'; 212 | echo html::anchor('delete_recommendation_comment?id='.$result, '删除'); 213 | } 214 | else 215 | { 216 | echo Debug::dump($this->_recommendation->errors()); 217 | } 218 | } 219 | } 220 | else 221 | { 222 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 223 | } 224 | } 225 | 226 | public function action_delete_recommendation_comment() 227 | { 228 | if ($this->_douban->logged_in()) 229 | { 230 | 231 | if ( ! $_GET) 232 | { 233 | echo '通过 recommendation url 删除推荐信息:(比如,http://api.douban.com/recommendation/3671284/comment/178651)

'; 234 | echo '
'; 235 | } 236 | else 237 | { 238 | $url = trim($_GET['url']); 239 | if ($this->_recommendation->delete_comment($url)) 240 | { 241 | echo 'Delete successful!'; 242 | } 243 | else 244 | { 245 | echo 'Delete failed: '; 246 | echo Debug::dump($this->_recommendation->errors()); 247 | } 248 | } 249 | } 250 | else 251 | { 252 | echo html::anchor('demo_douban/verity_oauth', 'OAuth 验证'); 253 | } 254 | } 255 | 256 | } 257 | 258 | -------------------------------------------------------------------------------- /classes/douban/api/note.php: -------------------------------------------------------------------------------- 1 | 7 | * @link http://icyleaf.com 8 | * @copyright (c) 2009-2010 icyleaf 9 | * @license http://www.apache.org/licenses/LICENSE-2.0 10 | */ 11 | class Douban_API_Note extends Douban_Core { 12 | 13 | /** 14 | * Get note 15 | * 16 | * @param int $note_id 17 | * @return mixed 18 | */ 19 | public function get($note_id) 20 | { 21 | $url = Douban_Core::NOTE_URL.$note_id; 22 | $post_data = array 23 | ( 24 | 'alt' => $this->alt 25 | ); 26 | $result = $this->_client->get($url, $post_data); 27 | 28 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 29 | { 30 | $result = $this->_format($result->to_json()); 31 | } 32 | 33 | return $result; 34 | } 35 | 36 | /** 37 | * Get notes by people 38 | * 39 | * @param mixed $people_id 40 | * @param int $index 41 | * @param int $max 42 | * @return mixed 43 | */ 44 | public function get_by_people($people_id, $index = 1, $max = 10) 45 | { 46 | $people_id = (empty($people_id) OR $people_id == 'me') ? '@me' : $people_id; 47 | $url = Douban_Core::PEOPLE_URL.urlencode($people_id).'/notes'; 48 | $post_data = array 49 | ( 50 | 'start-index' => $index, 51 | 'max-results' => $max, 52 | 'alt' => $this->alt 53 | ); 54 | 55 | return $this->_get_notes($url, $post_data); 56 | } 57 | 58 | /** 59 | * Create a new note 60 | * 61 | * $data = array 62 | * ( 63 | * 'title' => 'My secrets', 64 | * 'content' => 'note content', 65 | * 'privacy' => 'private', 66 | * 'reply' => 'no', 67 | * ); 68 | * 69 | * @param araray $data 70 | * @return mixed 71 | */ 72 | public function create($data = array()) 73 | { 74 | $url = substr(Douban_Core::NOTE_URL, 0, strlen(Douban_Core::NOTE_URL) - 1) . 's'; 75 | $parameters = array 76 | ( 77 | 'title' => '', 78 | 'content' => '', 79 | 'privacy' => 'public', // private or public 80 | 'reply' => 'yes', // yes or no 81 | ); 82 | $data = array_merge($parameters, $data); 83 | 84 | if ($data['privazy'] != 'public') 85 | { 86 | $data['privazy'] = 'private'; 87 | } 88 | if ($data['reply'] != 'yes') 89 | { 90 | $data['reply'] = 'no'; 91 | } 92 | 93 | $header = array('Content-Type: application/atom+xml'); 94 | $post_data = ''. 95 | ''. 97 | ''.$data['title'].''. 98 | ''.$data['content'].''. 99 | ''.$data['privazy'].''. 100 | ''.$data['reply'].''. 101 | ''; 102 | 103 | $result = $this->_client->post($url, $post_data, $header); 104 | 105 | if ($this->format) 106 | { 107 | if ($result->status() == 201) 108 | { 109 | $result = TRUE; 110 | } 111 | else 112 | { 113 | $this->_errors = $result; 114 | 115 | $result = FALSE; 116 | } 117 | } 118 | 119 | return $result; 120 | } 121 | 122 | /** 123 | * Update a note 124 | * 125 | * @param int $note_id 126 | * @param array $data 127 | * @return mixed 128 | */ 129 | public function update($note_id, $data = array()) 130 | { 131 | $url = Douban_Core::NOTE_URL.$note_id; 132 | $parameters = array 133 | ( 134 | 'title' => '', 135 | 'content' => '', 136 | 'privazy' => 'public', 137 | 'reply' => 'yes', 138 | 139 | ); 140 | $data = array_merge($parameters, $data); 141 | 142 | if ($data['privazy'] != 'public') 143 | { 144 | $data['privazy'] = 'private'; 145 | } 146 | if ($data['reply'] != 'yes') 147 | { 148 | $data['reply'] = 'no'; 149 | } 150 | 151 | $header = array('Content-Type: application/atom+xml'); 152 | $post_data = ''. 153 | ''. 155 | ''.$data['title'].''. 156 | ''.$data['content'].''. 157 | ''.$data['privazy'].''. 158 | ''.$data['reply'].''. 159 | ''; 160 | $result = $this->_client->put($url, $post_data, $header); 161 | 162 | if ($this->format) 163 | { 164 | if ($result->status() == 202) 165 | { 166 | $result = TRUE; 167 | } 168 | else 169 | { 170 | $this->_errors = $result; 171 | 172 | $result = FALSE; 173 | } 174 | } 175 | 176 | return $result; 177 | } 178 | 179 | /** 180 | * Delete a note 181 | * 182 | * @param int $note_id 183 | * @return mixed 184 | */ 185 | public function delete($note_id) 186 | { 187 | $url = Douban_Core::NOTE_URL.$note_id; 188 | $result = $this->_client->delete($url); 189 | if ($this->format) 190 | { 191 | if ($result->status() == 200) 192 | { 193 | $result = TRUE; 194 | } 195 | else 196 | { 197 | $this->_errors = $result; 198 | 199 | $result = FALSE; 200 | } 201 | } 202 | 203 | return $result; 204 | } 205 | 206 | /** 207 | * Get notes 208 | * 209 | * @param string $url 210 | * @param array $post_data 211 | * @return mixed 212 | */ 213 | private function _get_notes($url, $post_data = array()) 214 | { 215 | $result = $this->_client->get($url, $post_data); 216 | 217 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 218 | { 219 | $notes = $result->to_json(); 220 | 221 | $result = new stdClass; 222 | $result->title = $notes['title']['$t']; 223 | // author 224 | $result->author = Douban_API_People::format($notes['author']); 225 | // links 226 | foreach ($notes['link'] as $link) 227 | { 228 | $result->link[$link['@rel']] = $link['@href']; 229 | } 230 | // search 231 | $result->index = $notes['openSearch:startIndex']['$t']; 232 | $result->max = $notes['openSearch:itemsPerPage']['$t']; 233 | if ($notes['entry'] > 0) 234 | { 235 | // notes 236 | foreach ($notes['entry'] as $note) 237 | { 238 | $result->entry[] = $this->_format($note); 239 | } 240 | } 241 | else 242 | { 243 | $result->entry = array(); 244 | } 245 | } 246 | 247 | return $result; 248 | } 249 | 250 | /** 251 | * Format note 252 | * 253 | * @param array $note 254 | * @return mixed 255 | */ 256 | private function _format($note) 257 | { 258 | $result = new stdClass; 259 | // id 260 | $result->id = substr($note['id']['$t'], strlen(Douban_Core::NOTE_URL)); 261 | // title 262 | $result->title = $note['title']['$t']; 263 | // authors 264 | if (isset($note['author'])) 265 | { 266 | $result->author = Douban_API_People::format($note['author']); 267 | } 268 | // attribute 269 | foreach ($note['db:attribute'] as $att) 270 | { 271 | $result->attribute[$att['@name']] = $att['$t']; 272 | } 273 | // link 274 | foreach ($note['link'] as $link) 275 | { 276 | $result->link[$link['@rel']] = $link['@href']; 277 | } 278 | // summary 279 | $result->summary = $note['summary']['$t']; 280 | // content 281 | $result->content = $note['content']['$t']; 282 | // published 283 | $result->published = strtotime($note['published']['$t']); 284 | // updated 285 | $result->updated = strtotime($note['updated']['$t']); 286 | 287 | return $result; 288 | } 289 | 290 | } 291 | 292 | -------------------------------------------------------------------------------- /classes/douban/api/collection.php: -------------------------------------------------------------------------------- 1 | 8 | * @link http://icyleaf.com 9 | * @copyright (c) 2009-2010 icyleaf 10 | * @license http://www.apache.org/licenses/LICENSE-2.0 11 | */ 12 | class Douban_API_Collection extends Douban_Core { 13 | 14 | /** 15 | * Get collection 16 | * 17 | * @param int $collection_id 18 | * @return mixed 19 | */ 20 | public function get($collection_id) 21 | { 22 | $url = Douban_Core::COLLECTION_URL.$collection_id; 23 | $post_data = array 24 | ( 25 | 'alt' => $this->alt 26 | ); 27 | $result = $this->_client->get($url, $post_data); 28 | 29 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 30 | { 31 | $result = $this->_format($result->to_json()); 32 | } 33 | 34 | return $result; 35 | } 36 | 37 | /** 38 | * Get collections by people 39 | * 40 | * @param mixed $people_id 41 | * @param int $index 42 | * @param int $max 43 | * @param mixed $data 44 | * @return mixed 45 | */ 46 | public function get_by_people($people_id, $index = 1, $max = 10, $data = NULL) 47 | { 48 | $people_id = (empty($people_id) OR $people_id == 'me') ? '@me' : $people_id; 49 | $url = Douban_Core::PEOPLE_URL.urlencode($people_id).'/collection'; 50 | $post_data = array 51 | ( 52 | 'cat' => '', 53 | 'tag' => '', 54 | 'status' => '', 55 | 'start-index' => $index, 56 | 'max-results' => $max, 57 | 'alt' => $this->alt 58 | ); 59 | if ( ! empty($data) AND is_array($data) AND count($data) > 0) 60 | { 61 | $post_data = array_merge($post_data, $data); 62 | } 63 | 64 | return $this->_get_collections($url, $post_data); 65 | } 66 | 67 | /** 68 | * Create a new collection 69 | * 70 | * $data = array 71 | * ( 72 | * 'subject_url' => 'http://api.douban.com/movie/subject/1652587', 73 | * 'status' => 'watched', 74 | * 'rating' => 5, 75 | * 'content' => 'left some words about this collection', 76 | * 'privacy' => 'public', 77 | * 'tags' => array('avatar', '3D', '2009', 'IMAX'), 78 | * ); 79 | * 80 | * status: 81 | * book wish, reading, read 82 | * music wish, listening, listened 83 | * movie wish, watching, watched 84 | * 85 | * @param array $data 86 | * @return mixed 87 | */ 88 | public function create($data = array()) 89 | { 90 | $url = substr(Douban_Core::COLLECTION_URL, 0, (strlen(Douban_Core::COLLECTION_URL) - 1)); 91 | $parameters = array 92 | ( 93 | 'subject_url' => '', 94 | 'status' => '', 95 | 'content' => '', 96 | 'rating' => 0, // 0-5 97 | 'privacy' => 'public', // private or public 98 | 'tags' => array(), // array 99 | ); 100 | $data = array_merge($parameters, $data); 101 | 102 | if (is_array($data['tags']) AND count($data['tags']) > 0) 103 | { 104 | $tags =''; 105 | foreach($data['tags'] as $tag) 106 | { 107 | $tags .= ''; 108 | } 109 | } 110 | else 111 | { 112 | $tags = ''; 113 | } 114 | 115 | $header = array('Content-Type: application/atom+xml'); 116 | $post_data = ''. 117 | ''. 119 | ''.$data['status'].''. 120 | $tags. 121 | ''. 122 | ''.$data['content'].''. 123 | ''. 124 | ''.$data['subject_url'].''. 125 | ''. 126 | ''.$data['privacy'].''. 127 | ''; 128 | $result = $this->_client->post($url, $post_data, $header); 129 | 130 | if ($this->format) 131 | { 132 | if ($result->status() == 201) 133 | { 134 | $collection = $result->to_xml(); 135 | // get created review id and return 136 | $result = substr($collection->id, strlen(Douban_Core::COLLECTION_URL)); 137 | } 138 | else 139 | { 140 | $this->_errors = $result; 141 | 142 | $result = FALSE; 143 | } 144 | } 145 | 146 | return $result; 147 | } 148 | 149 | /** 150 | * Delete a collection 151 | * 152 | * @param int $collection_id 153 | * @return mixed 154 | */ 155 | public function delete($collection_id) 156 | { 157 | $url = Douban_Core::COLLECTION_URL.$collection_id; 158 | $result = $this->_client->delete($url); 159 | 160 | if ($result->status() == 200) 161 | { 162 | return TRUE; 163 | } 164 | else 165 | { 166 | $this->_errors = $result; 167 | 168 | return FALSE; 169 | } 170 | } 171 | 172 | /** 173 | * Get collections 174 | * 175 | * @param string $url 176 | * @param array $post_data 177 | * @return mixed 178 | */ 179 | private function _get_collections($url, $post_data = array()) 180 | { 181 | $result = $this->_client->get($url, $post_data); 182 | 183 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 184 | { 185 | $collections = $result->to_json(); 186 | 187 | $result = new stdClass; 188 | $result->title = $collections['title']['$t']; 189 | // author 190 | if (isset($collections['author'])) 191 | { 192 | $result->author = Douban_API_People::format($collections['author']); 193 | } 194 | // search 195 | $result->index = $collections['opensearch:startIndex']['$t']; 196 | $result->max = $collections['opensearch:itemsPerPage']['$t']; 197 | $result->total = $collections['opensearch:totalResults']['$t']; 198 | if ($result->total > 0) 199 | { 200 | // collections 201 | foreach ($collections['entry'] as $collection) 202 | { 203 | $result->entry[] = $this->_format($collection); 204 | } 205 | } 206 | } 207 | 208 | return $result; 209 | } 210 | 211 | /** 212 | * Format collection 213 | * 214 | * @param array $collection 215 | * @return mixed 216 | */ 217 | private function _format($collection) 218 | { 219 | $result = new stdClass; 220 | // id 221 | $result->id = substr($collection['id']['$t'], strlen(Douban_Core::COLLECTION_URL)); 222 | // title 223 | $result->title = $collection['title']['$t']; 224 | // author 225 | if (isset($collection['author'])) 226 | { 227 | $result->author = Douban_API_People::format($collection['author']); 228 | } 229 | // status 230 | $result->status = $collection['db:status']['$t']; 231 | // subject 232 | $category = substr($collection['db:subject']['category']['@term'], strlen(Douban_Core::CATEGORY_URL)); 233 | $result->subject = call_user_func(array('Douban_API_'.ucfirst($category), 'format'), $collection['db:subject']); 234 | // summary 235 | if (isset($collection['summary'])) 236 | { 237 | $result->summary = $collection['summary']['$t']; 238 | } 239 | // rating 240 | if (isset($collection['gd:rating'])) 241 | { 242 | foreach ($collection['gd:rating'] as $key => $value) 243 | { 244 | $result->rating[substr($key, 1)] = $value; 245 | } 246 | } 247 | // tags 248 | if (isset($collection['db:tag'])) 249 | { 250 | foreach ($collection['db:tag'] as $tag) 251 | { 252 | $result->tags[] = $tag['@name']; 253 | } 254 | } 255 | // updated 256 | $result->updated = strtotime($collection['updated']['$t']); 257 | 258 | unset($category, $class); 259 | 260 | return $result; 261 | } 262 | } 263 | 264 | -------------------------------------------------------------------------------- /classes/controller/douban/console.php: -------------------------------------------------------------------------------- 1 | _config = Kohana::$config->load('douban_console'); 16 | $this->session = Session::instance(); 17 | 18 | $this->douban = Douban::instance($this->_config->api_key, $this->_config->api_secret); 19 | $people = $this->douban->get_user(); 20 | if ($people instanceof Douban_Response) 21 | { 22 | unset($people); 23 | } 24 | 25 | $this->header = Head::instance(); 26 | $this->header->title->set('豆瓣 API 测试控制台'); 27 | $this->header->css->append_file('media/douban/console/css/screen.css', 'date', 'all'); 28 | $this->header->css->append_file('media/douban/console/css/layout.css', 'date'); 29 | $this->header->css->brower_hacks('media/douban/console/css/ie6.css', 'ie', '6'); 30 | $this->header->css->brower_hacks('media/douban/console/css/ie7.css', 'ie', '7'); 31 | $this->header->javascript->append_file('media/douban/console/js/jquery-1.4.2.min.js'); 32 | $this->header->javascript->append_file('media/douban/console/js/console.js', 'date'); 33 | 34 | $this->template = View::factory($this->template) 35 | ->set('header', $this->header) 36 | ->bind('people', $people); 37 | } 38 | 39 | public function action_index() 40 | { 41 | $this->template->action = 'home'; 42 | } 43 | 44 | public function action_about() 45 | { 46 | $this->header->title->prepend('关于'); 47 | $this->template->action = 'about'; 48 | } 49 | 50 | public function action_json() 51 | { 52 | $this->auto_render = FALSE; 53 | 54 | $type = $this->request->param('id'); 55 | $this->request->headers = array( 56 | 'Content-Type' => 'application/json; charset=utf-8' 57 | ); 58 | $this->response->body(json_encode($this->_config->$type)); 59 | } 60 | 61 | public function action_auth() 62 | { 63 | $auth_url = $request_token = $this->douban->verify(URL::site('douban_console/check')); 64 | 65 | $this->request->redirect($auth_url); 66 | } 67 | 68 | public function action_check() 69 | { 70 | if ($this->douban->login()) 71 | { 72 | $this->request->redirect('douban_console'); 73 | } 74 | else 75 | { 76 | $this->template->content = 'OAuth 验证失败,'.HTML::anchor('douban_console/auth', '重试'); 77 | } 78 | } 79 | 80 | public function action_media() 81 | { 82 | // Get the file path from the request 83 | $file = $this->request->param('file'); 84 | 85 | // Find the file extension 86 | $ext = pathinfo($file, PATHINFO_EXTENSION); 87 | 88 | // Remove the extension from the filename 89 | $file = substr($file, 0, -(strlen($ext) + 1)); 90 | 91 | if ($file = Kohana::find_file('media', $file, $ext)) 92 | { 93 | // Check if the browser sent an "if-none-match: " header, and tell if the file hasn't changed 94 | $this->response->check_cache(sha1($this->request->uri()).filemtime($file), $this->request); 95 | 96 | // Send the file content as the response 97 | $this->response->body(file_get_contents($file)); 98 | 99 | // Set the proper headers to allow caching 100 | $this->response->headers('content-type', File::mime_by_ext($ext)); 101 | $this->response->headers('last-modified', date('r', filemtime($file))); 102 | } 103 | else 104 | { 105 | // Return a 404 status 106 | $this->response->status(404); 107 | } 108 | } 109 | 110 | protected static function render($result = NULL, $alt = 'atom') 111 | { 112 | $url = $result->url(); 113 | $url_array = parse_url($url); 114 | if (isset($url_array['query'])) 115 | { 116 | $query_array = explode('&', $url_array['query']); 117 | $removed_query = array( 118 | 'oauth_version', 'oauth_nonce', 'oauth_timestamp', 119 | 'oauth_consumer_key', 'oauth_token', 'oauth_signature_method', 120 | 'oauth_signature' 121 | ); 122 | 123 | $post_data = array(); 124 | $i = 0; 125 | foreach ($query_array as $item) 126 | { 127 | list($key, $value) = explode('=', $item); 128 | if (in_array($key, $removed_query)) 129 | { 130 | unset($query_array[$i]); 131 | } 132 | else 133 | { 134 | $post_data[$key] = $value; 135 | } 136 | $i++; 137 | } 138 | $post_url = $url_array['scheme'].'://'.$url_array['host']. 139 | $url_array['path'].'?'.http_build_query($post_data, '&'); 140 | } 141 | else 142 | { 143 | $post_url = $url; 144 | } 145 | 146 | $response = $result->to_normal(); 147 | $status = $result->status(); 148 | $success_array = array(200, 201, 202); 149 | if ( ! in_array($status, $success_array)) 150 | { 151 | echo $post_url.'[-icyleaf-]('.$status.') '.$response; 152 | } 153 | else 154 | { 155 | if ($alt == 'json') 156 | { 157 | echo $post_url.'[-icyleaf-]'.htmlspecialchars(self::format_json($response)); 158 | } 159 | else 160 | { 161 | echo $post_url.'[-icyleaf-]'.htmlspecialchars($response); 162 | } 163 | } 164 | } 165 | 166 | private function login() 167 | { 168 | $status = FALSE; 169 | if ($token = $this->session->get('md_token')) 170 | { 171 | $this->douban->login($token['oauth_token'], $token['oauth_token_secret']); 172 | $status = TRUE; 173 | } 174 | else 175 | { 176 | $key = cookie::get('oauth_token'); 177 | $secret = cookie::get('oauth_token_secret'); 178 | if ( ! empty($key) AND ! empty($secret)) 179 | { 180 | $status = TRUE; 181 | $this->douban->login($key, $secret); 182 | 183 | $this->session->set( 184 | 'md_token', 185 | array( 186 | 'oauth_token'=> $key, 187 | 'oauth_token_secret' => $secret 188 | ) 189 | ); 190 | $this->session->set('auth_user', $this->douban->people('me')); 191 | } 192 | } 193 | 194 | return $status; 195 | } 196 | 197 | private static function format_json($json) 198 | { 199 | $tab = ' '; 200 | $new_json = ''; 201 | $indent_level = 0; 202 | $in_string = false; 203 | 204 | $json_obj = json_decode($json); 205 | 206 | if ( ! $json_obj) 207 | { 208 | return $new_json; 209 | } 210 | 211 | $len = strlen($json); 212 | for ($c = 0; $c < $len; $c++) 213 | { 214 | $char = $json[$c]; 215 | switch($char) 216 | { 217 | case '{': 218 | case '[': 219 | if ( ! $in_string) 220 | { 221 | $new_json .= $char."\n".str_repeat($tab, $indent_level+1); 222 | $indent_level++; 223 | } 224 | else 225 | { 226 | $new_json .= $char; 227 | } 228 | break; 229 | case '}': 230 | case ']': 231 | if (!$in_string) 232 | { 233 | $indent_level--; 234 | $new_json .= "\n".str_repeat($tab, $indent_level).$char; 235 | } 236 | else 237 | { 238 | $new_json .= $char; 239 | } 240 | break; 241 | case ',': 242 | if (!$in_string) 243 | { 244 | $new_json .= ",\n".str_repeat($tab, $indent_level); 245 | } 246 | else 247 | { 248 | $new_json .= $char; 249 | } 250 | break; 251 | case ':': 252 | if (!$in_string) 253 | { 254 | $new_json .= ': '; 255 | } 256 | else 257 | { 258 | $new_json .= $char; 259 | } 260 | break; 261 | case '"': 262 | $in_string = !$in_string; 263 | default: 264 | $new_json .= $char; 265 | break; 266 | } 267 | } 268 | 269 | return $new_json; 270 | } 271 | } 272 | 273 | -------------------------------------------------------------------------------- /views/douban/console/home.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 15 | 16 |
17 |
18 |
19 | 47 |
48 |
49 |
50 |
51 |
52 | 53 |

豆瓣 API 测试控制台

54 |
55 |
56 |

57 | 这是一个在线的豆瓣 API 测试控制台,你可以在下列左侧选择函和返回格式, 58 | 点击“调用此方法”。在右侧就会显示请求的地址及参数信息和返回的结果。 59 |

60 | 61 |
62 |

63 | 64 |
用户必须登录验证后才能使用控制台。 65 | 66 |
67 |
68 |

69 |
70 | 71 |
72 |
73 |
74 | 75 | 77 |
78 | 79 | 83 |
84 |
85 | 86 | 89 |
90 | 95 | 99 | 103 | 107 | 111 | 115 | 119 | 123 | 127 |
128 | 131 |
132 | 133 |
134 | 如果长时间处于“调用中”,可能是由于程序问题,请刷新后重试。

135 | 如果还再出现此问题,请联系我
136 | Twitter: @icyleaf
137 | 新浪微博: @icyleaf 138 |
139 |
140 |
请求的地址及参数在这里显示。
141 |
返回的结果在这里显示。
142 |
143 |
144 |
145 | 146 |

关于

147 |
148 |
149 |

150 | 豆瓣 API 测试控制台的创作来源于 Facebook Developers Tools 中的 Facebook API Console, 151 | 由于自己在使用 PHP 重新封装 Douban API,每次想查看某个 API 接口返回的数据都要写一个 test 跑一下。 152 | 麻烦不说,还容易出错,于是,使用自己封装的 Douban API,复制 Facebook API Console 153 | 的界面和类似的功能显示,终于小有所成,其中为了实现此控制台,封包的库类经过两次大改,虽然改的很辛苦, 154 | 但从中学到了很多开发经验。 155 |

156 | 157 |

158 | 本工具经测试可用于大多数主流浏览器(Firefox/IE/Chrome)。 159 |

160 | 161 |

162 | 最后更新: 12/04/2011. 163 |

164 |
165 |
166 | 167 |

错误

168 |
169 |
170 |

171 | 抱歉,目前由于访问者请求次数过多,超出豆瓣每分钟请求限制,请过 30 分钟后重新尝试。 172 |

173 |
174 |
175 | 176 |
177 | 187 | 197 | 198 | 202 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /classes/douban/api/doumail.php: -------------------------------------------------------------------------------- 1 | 7 | * @link http://icyleaf.com 8 | * @copyright (c) 2009-2010 icyleaf 9 | * @license http://www.apache.org/licenses/LICENSE-2.0 10 | */ 11 | class Douban_API_Doumail extends Douban_Core { 12 | 13 | /** 14 | * get a doumail 15 | * 16 | * @param int $mail_id 17 | * @return mixed 18 | */ 19 | public function get($mail_id) 20 | { 21 | $url = Douban_Core::DOUMAIL_URL.$mail_id; 22 | $post_data = array 23 | ( 24 | 'alt' => $this->alt 25 | ); 26 | $result = $this->_client->get($url, $post_data); 27 | 28 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 29 | { 30 | $result = $this->_format($result->to_json()); 31 | } 32 | 33 | return $result; 34 | } 35 | 36 | /** 37 | * get inbox 38 | * 39 | * @param int $index 40 | * @param int $max 41 | * @return mixed 42 | */ 43 | public function get_inbox($index = 1, $max = 10) 44 | { 45 | $url = Douban_Core::DOUMAIL_URL.'inbox'; 46 | $post_data = array 47 | ( 48 | 'start-index' => $index, 49 | 'max-results' => $max, 50 | 'alt' => $this->alt 51 | ); 52 | 53 | return $this->_get_doumails($url, $post_data); 54 | } 55 | 56 | /** 57 | * get unread doumails 58 | * 59 | * @param int $index 60 | * @param int $max 61 | * @return mixed 62 | */ 63 | public function get_unread($index = 1, $max = 10) 64 | { 65 | $url = Douban_Core::DOUMAIL_URL.'inbox/unread'; 66 | $post_data = array 67 | ( 68 | 'start-index' => $index, 69 | 'max-results' => $max, 70 | 'alt' => $this->alt 71 | ); 72 | 73 | return $this->_get_doumails($url, $post_data); 74 | } 75 | 76 | /** 77 | * get outbox 78 | * 79 | * @param int $index 80 | * @param int $max 81 | * @return mixed 82 | */ 83 | public function get_outbox($index = 1, $max = 10) 84 | { 85 | $url = Douban_Core::DOUMAIL_URL.'outbox'; 86 | $post_data = array 87 | ( 88 | 'start-index' => $index, 89 | 'max-results' => $max, 90 | 'alt' => $this->alt 91 | ); 92 | 93 | return $this->_get_doumails($url, $post_data); 94 | } 95 | 96 | /** 97 | * Send a new doumail 98 | * 99 | * $data = array 100 | * ( 101 | * 'people_id' => 'icyleaf', 102 | * 'title' => 'Hello world', 103 | * 'content' => 'The world is in your hand!', 104 | * 'captcha_token' => '1234567890', 105 | * 'captcha_string' => 'hello', 106 | * ); 107 | * 108 | * @param array $data 109 | * @return mixed 110 | */ 111 | public function send($data = array()) 112 | { 113 | $url = substr(Douban_Core::DOUMAIL_URL, 0, strlen(Douban_Core::DOUMAIL_URL) - 1) . 's'; 114 | $parameters = array 115 | ( 116 | 'people_id' => '', 117 | 'title' => '', 118 | 'content' => '', 119 | 'captcha_token' => '', 120 | 'captcha_string' => '', 121 | ); 122 | $data = array_merge($parameters, $data); 123 | 124 | if ( ! empty($data['captcha_token']) AND ! empty($data['captcha_string'])) 125 | { 126 | $captcha = ''.$data['captcha_token'].' 127 | '.$data['captcha_string'].''; 128 | } 129 | else 130 | { 131 | $captcha = ''; 132 | } 133 | 134 | $header = array('Content-Type: application/atom+xml'); 135 | $post_data = ''. 136 | ''. 140 | ''. 141 | 'http://api.douban.com/people/'.$data['people_id'].''. 142 | ''. 143 | ''.$data['title'].''. 144 | ''.$data['content'].''. 145 | $captcha. 146 | ''; 147 | $result = $this->_client->post($url, $post_data, $header); 148 | 149 | if ($this->format) 150 | { 151 | if ($result->status() == 201) 152 | { 153 | $result = TRUE; 154 | } 155 | else if ($result->status() == 403) 156 | { 157 | $array = $result->to_array(); 158 | $array['captcha_url'] = $array['captcha_url'].'='.$array['captcha_token']; 159 | $array['captcha_small_url'] = $array['captcha_url'].'&size=s'; 160 | unset($array['size']); 161 | $result = $array; 162 | } 163 | else 164 | { 165 | $this->_errors = $result; 166 | 167 | $result = FALSE; 168 | } 169 | } 170 | 171 | return $result; 172 | } 173 | 174 | /** 175 | * Delete a doumail 176 | * 177 | * @param int $mail_id 178 | * @return mixed 179 | */ 180 | public function delete($mail_id) 181 | { 182 | $url = Douban_Core::DOUMAIL_URL.$mail_id; 183 | $result = $this->_client->delete($url); 184 | if ($this->format) 185 | { 186 | if ($result->status() == 200) 187 | { 188 | $result = TRUE; 189 | } 190 | else 191 | { 192 | $this->_errors = $result; 193 | 194 | $result = FALSE; 195 | } 196 | } 197 | 198 | return $result; 199 | } 200 | 201 | /** 202 | * Get doumails 203 | * 204 | * @param string $url 205 | * @param array $post_data 206 | * @return mixed 207 | */ 208 | private function _get_doumails($url, $post_data = array()) 209 | { 210 | $result = $this->_client->get($url, $post_data); 211 | 212 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 213 | { 214 | $doumails = $result->to_json(); 215 | 216 | $result = new stdClass; 217 | $result->title = $doumails['title']['$t']; 218 | // search 219 | $result->index = $doumails['openSearch:startIndex']['$t']; 220 | $result->max = $doumails['openSearch:itemsPerPage']['$t']; 221 | if (isset($doumails['openSearch:totalResults'])) 222 | { 223 | $result->total = $doumails['openSearch:totalResults']['$t']; 224 | } 225 | if ($doumails['entry'] > 0) 226 | { 227 | // doumails 228 | foreach ($doumails['entry'] as $doumail) 229 | { 230 | $result->entry[] = $this->_format($doumail); 231 | } 232 | } 233 | else 234 | { 235 | $result->entry = array(); 236 | } 237 | } 238 | 239 | return $result; 240 | } 241 | 242 | /** 243 | * Format doumail 244 | * 245 | * @param array $doumail 246 | * @return mixed 247 | */ 248 | private function _format($doumail) 249 | { 250 | //echo Debug::dump($doumail); 251 | $result = new stdClass; 252 | // id 253 | $result->id = substr($doumail['id']['$t'], strlen(Douban_Core::DOUMAIL_URL)); 254 | // title 255 | $result->title = $doumail['title']['$t']; 256 | // published 257 | $result->published = strtotime($doumail['published']['$t']); 258 | // authors 259 | if (isset($doumail['author'])) 260 | { 261 | $result->author = Douban_API_People::format($doumail['author']); 262 | $result->receiver = FALSE; 263 | } 264 | else if (isset($doumail['db:entity'])) 265 | { 266 | $result->author = Douban_API_People::format($doumail['db:entity']); 267 | $result->receiver = TRUE; 268 | } 269 | 270 | // type 271 | if (isset($result->author->id)) 272 | { 273 | if ( ! $result->author->id) 274 | { 275 | $result->type = 'system'; 276 | } 277 | else 278 | { 279 | $result->type = 'normal'; 280 | } 281 | } 282 | else 283 | { 284 | $result->type = 'host'; 285 | } 286 | // attribute 287 | if (isset($doumail['db:attribute'])) 288 | { 289 | foreach ($doumail['db:attribute'] as $att) 290 | { 291 | if ($att['@name'] == 'unread') 292 | { 293 | if (strtolower($att['$t']) == 'true') 294 | { 295 | $result->attribute[$att['@name']] = TRUE; 296 | } 297 | else 298 | { 299 | $result->attribute[$att['@name']] = FALSE; 300 | } 301 | } 302 | else 303 | { 304 | $result->attribute[$att['@name']] = $att['$t']; 305 | } 306 | 307 | } 308 | } 309 | // link 310 | foreach ($doumail['link'] as $link) 311 | { 312 | $result->link[$link['@rel']] = $link['@href']; 313 | } 314 | // content 315 | if (isset($doumail['content'])) 316 | { 317 | $result->content = $doumail['content']['$t']; 318 | } 319 | 320 | return $result; 321 | } 322 | 323 | } 324 | 325 | -------------------------------------------------------------------------------- /classes/douban/api/recommendation.php: -------------------------------------------------------------------------------- 1 | 7 | * @link http://icyleaf.com 8 | * @copyright (c) 2009-2010 icyleaf 9 | * @license http://www.apache.org/licenses/LICENSE-2.0 10 | */ 11 | class Douban_API_Recommendation extends Douban_Core { 12 | 13 | /** 14 | * Get recommendation 15 | * 16 | * @param int $recommendation_id 17 | * @return mixed 18 | */ 19 | public function get($recommendation_id) 20 | { 21 | $url = Douban_Core::RECOMMENDATION_URL.$recommendation_id; 22 | $post_data = array 23 | ( 24 | 'alt' => $this->alt 25 | ); 26 | $result = $this->_client->get($url, $post_data); 27 | 28 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 29 | { 30 | $result = $this->_format_recommendation($result->to_json()); 31 | } 32 | 33 | return $result; 34 | } 35 | 36 | /** 37 | * Get recommendations by people 38 | * 39 | * @param mixed $people_id 40 | * @param int $index 41 | * @param int $max 42 | * @return mixed 43 | */ 44 | public function get_by_people($people_id, $index = 1, $max = 10) 45 | { 46 | $people_id = (empty($people_id) OR $people_id == 'me') ? '@me' : $people_id; 47 | $url = Douban_Core::PEOPLE_URL.urlencode($people_id).'/recommendations'; 48 | $post_data = array 49 | ( 50 | 'start-index' => $index, 51 | 'max-results' => $max, 52 | 'alt' => $this->alt 53 | ); 54 | 55 | return $this->_get_recommendations($url, $post_data); 56 | } 57 | 58 | /** 59 | * Create a new recommendation 60 | * 61 | * @param int $subject_url 62 | * @param string $comment 63 | * @return mixed 64 | */ 65 | public function create($subject_url, $comment) 66 | { 67 | $url = substr(Douban_Core::RECOMMENDATION_URL, 0, strlen(Douban_Core::RECOMMENDATION_URL) - 1) . 's'; 68 | $header = array('Content-Type: application/atom+xml'); 69 | $post_data = ''. 70 | ''. 74 | ''.rand(1, 100).''. 75 | ''.$comment.''. 76 | ''. 77 | ''; 78 | $result = $this->_client->post($url, $post_data, $header); 79 | 80 | if ($this->format) 81 | { 82 | if ($result->status() == 201) 83 | { 84 | $recommendation = $result->to_xml(); 85 | // get created review id and return 86 | $result = substr($recommendation->id, strlen(Douban_Core::RECOMMENDATION_URL)); 87 | } 88 | else 89 | { 90 | $this->_errors = $result; 91 | 92 | $result = FALSE; 93 | } 94 | } 95 | 96 | return $result; 97 | } 98 | 99 | /** 100 | * Reply a recommendation 101 | * 102 | * @param int $recommendation_id 103 | * @param string $content 104 | * @return mixed 105 | */ 106 | public function reply($recommendation_id, $content) 107 | { 108 | $url = Douban_Core::RECOMMENDATION_URL.$recommendation_id.'/comments'; 109 | $header = array('Content-Type: application/atom+xml'); 110 | $post_data = ''. 111 | ''.$content.''; 112 | $result = $this->_client->post($url, $post_data, $header); 113 | 114 | if ($this->format) 115 | { 116 | if ($result->status() == 200) 117 | { 118 | $comment = $result->to_xml(); 119 | // get created review id and return 120 | $result = $comment->id; 121 | } 122 | else 123 | { 124 | $this->_errors = $result; 125 | 126 | $result = FALSE; 127 | } 128 | } 129 | 130 | return $result; 131 | } 132 | 133 | /** 134 | * Delete recommendation 135 | * 136 | * @param int $recommendation_id 137 | * @return mixed 138 | */ 139 | public function delete_recommendation($recommendation_id) 140 | { 141 | $url = Douban_Core::RECOMMENDATION_URL.$recommendation_id; 142 | $result = $this->_client->delete($url); 143 | if ($this->format) 144 | { 145 | if ($result->status() == 200) 146 | { 147 | $result = TRUE; 148 | } 149 | else 150 | { 151 | $this->_errors = $result; 152 | 153 | $result = FALSE; 154 | } 155 | } 156 | 157 | return $result; 158 | } 159 | 160 | /** 161 | * Delete recommendation's comment 162 | * 163 | * @param string $comment_url 164 | * @return mixed 165 | */ 166 | public function delete_comment($comment_url) 167 | { 168 | $result = $this->_client->delete($comment_url); 169 | if ($this->format) 170 | { 171 | if ($result->status() == 200) 172 | { 173 | $result = TRUE; 174 | } 175 | else 176 | { 177 | $this->_errors = $result; 178 | 179 | $result = FALSE; 180 | } 181 | } 182 | 183 | return $result; 184 | } 185 | 186 | /** 187 | * Get comments 188 | * 189 | * @param int $recommendation_id 190 | * @return mixed 191 | */ 192 | public function get_comments($recommendation_id) 193 | { 194 | $url = Douban_Core::RECOMMENDATION_URL.$recommendation_id.'/comments'; 195 | $post_data = array 196 | ( 197 | 'alt' => $this->alt 198 | ); 199 | $result = $this->_client->get($url, $post_data); 200 | 201 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 202 | { 203 | $comments = $result->to_json(); 204 | 205 | $result = new stdClass; 206 | $result->title = $comments['title']['$t']; 207 | // author 208 | $result->author = Douban_API_People::format($comments['author']); 209 | // search 210 | $result->index = $comments['openSearch:startIndex']['$t']; 211 | $result->max = $comments['openSearch:itemsPerPage']['$t']; 212 | $result->total = $comments['openSearch:totalResults']['$t']; 213 | if ($result->total > 0) 214 | { 215 | // recommendations 216 | foreach ($comments['entry'] as $comment) 217 | { 218 | $result->entry[] = $this->_format_comment($comment, $url); 219 | } 220 | } 221 | } 222 | 223 | return $result; 224 | } 225 | 226 | /** 227 | * Get recommendations 228 | * 229 | * @param string $url 230 | * @param array $post_data 231 | * @return mixed 232 | */ 233 | private function _get_recommendations($url, $post_data = array()) 234 | { 235 | $result = $this->_client->get($url, $post_data); 236 | 237 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 238 | { 239 | $recommendations = $result->to_json(); 240 | 241 | $result = new stdClass; 242 | $result->title = $recommendations['title']['$t']; 243 | // author 244 | if ( isset($recommendations['author']) ) 245 | { 246 | $result->author = Douban_API_People::format($recommendations['author']); 247 | } 248 | // search 249 | $result->index = $recommendations['openSearch:startIndex']['$t']; 250 | $result->max = $recommendations['openSearch:itemsPerPage']['$t']; 251 | if (count($recommendations['entry']) > 0) 252 | { 253 | // recommendations 254 | foreach ($recommendations['entry'] as $recommendation) 255 | { 256 | $result->entry[] = $this->_format_recommendation($recommendation); 257 | } 258 | } 259 | } 260 | 261 | return $result; 262 | } 263 | 264 | /** 265 | * Format recommendation 266 | * 267 | * @param array $recommendation 268 | * @return mixed 269 | */ 270 | private function _format_recommendation($recommendation) 271 | { 272 | $result = new stdClass; 273 | // id 274 | $result->id = substr($recommendation['id']['$t'], strlen(Douban_Core::RECOMMENDATION_URL)); 275 | // title 276 | $result->title = $recommendation['title']['$t']; 277 | // attribute 278 | foreach ($recommendation['db:attribute'] as $att ) 279 | { 280 | $result->attribute[$att['@name']] = $att['$t']; 281 | } 282 | // link 283 | if (count($recommendation['link']) > 0) 284 | { 285 | foreach ($recommendation['link'] as $link) 286 | { 287 | $result->link[$link['@rel']] = $link['@href']; 288 | } 289 | } 290 | // content 291 | $result->content = $recommendation['content']['$t']; 292 | // published 293 | $result->published = strtotime($recommendation['published']['$t']); 294 | 295 | return $result; 296 | } 297 | 298 | /** 299 | * Format recommendation's comment 300 | * 301 | * @param array $comment 302 | * @param string $url 303 | * @return mixed 304 | */ 305 | private function _format_comment($comment, $url) 306 | { 307 | $result = new stdClass; 308 | // id 309 | $result->id = substr($comment['id']['$t'], strlen($url) + 1); 310 | // authors 311 | $result->author = Douban_API_People::format($comment['author']); 312 | // content 313 | $result->content = $comment['content']['$t']; 314 | // published 315 | $result->published = strtotime($comment['published']['$t']); 316 | 317 | return $result; 318 | } 319 | 320 | } 321 | 322 | -------------------------------------------------------------------------------- /media/douban/console/css/ie6.css: -------------------------------------------------------------------------------- 1 | /* HTTP Host: b.static.ak.fbcdn.net */ 2 | /* Generated: June 9th 2009 9:30:25 AM PDT */ 3 | /* Machine: 10.16.140.105 */ 4 | /* Source: Global Cache */ 5 | /* Location: css/ie6.css h:8ylg4nn7 */ 6 | /* Locale: en_US */ 7 | /* Path: css/ie6.css */ 8 | 9 | 10 | .inputbutton, 11 | .inputsubmit{width:1;overflow:visible;padding:2px 15px;} 12 | #tabs{margin-top:22px } 13 | #announce p{word-wrap:break-word } 14 | #suggest div{cursor:hand } 15 | .fb_menu_dropdown{width:200px;} 16 | #group.profile .related{word-wrap:break-word;} 17 | #pagefooter li{white-space:nowrap;} 18 | .profile .account_info{position:relative;width:388px;margin-left:-231px;} 19 | .obj_media_header .media_gray_bg, 20 | .media_header .media_gray_bg{position:relative;} 21 | .highlight, .inserted{line-height:17px;} 22 | .composer_fields label{padding-top:7px;} 23 | #compose_message .inputsubmit{padding-left:15px;padding-right:15px;} 24 | .message .share_media.photo .photo_thumb img, 25 | .message .share_media.share_ext_photo .share_thumb img{width:360px;} 26 | .inbox #megaboxx .subject_wrap .subject_text{display:inline-block;} 27 | .dropdown-container{margin-left:1px;} 28 | #goodFrame{position:absolute;z-index:0;border:0px;filter:progid:DXImageTransform.Microsoft.Alpha(style=0,opacity=0);} 29 | .ubersearch .uber_bar #label_q{} 30 | .ubersearch .uber_bar #n_dropdown{margin-top:2px;} 31 | .filters legend{cursor:hand } 32 | .ubersearch .filters fieldset legend{margin:-5px -10px 0px -7px;padding-bottom:4px;width:133px } 33 | .filters #label_c2, .filters #label_c1{display:block;} 34 | .ubersearch .filters .checkbox{padding-bottom:0px;} 35 | .ubersearch .filters .checkbox input{margin:0px;} 36 | .ubersearch .filters .checkbox label{margin:0px;} 37 | .ubersearch #filters_sx label{padding:0px } 38 | .ubersearch #filters_sx .filter_controls{padding-bottom:2px } 39 | .mailbox .dropdown-container{margin-top:1px;margin-left:2px;} 40 | .profile .discussions .board_topic{width:368px } 41 | .profile .profileTable .data .datawrap{overflow:hidden } 42 | .grouplist .infotable .datawrap{display:block;width:300px } 43 | #groupslist .info{width:420px } 44 | .confirmed .people_table td{width:56px } 45 | .confirmed .people_table table td.name div{width:54px } 46 | .guestlist .people_table td{width:60px } 47 | .guestlist .people_table table td.name div{width:58px } 48 | .editaccount .action_box .buttons input{width:140px } 49 | .editaccount .settings_panel .email_option{padding-bottom:0px;} 50 | .editaccount #email .buttons{margin-top:0px;margin-left:0px;padding-left:20px;} 51 | .subrow td table td{border-left:2px solid;border-top:3px solid;} 52 | .editor_panel .subrow td table td{border-left:2px solid #f7f7f7;border-top:3px solid #f7f7f7;} 53 | .subrow td table td input{margin-top:1px } 54 | .school select{margin-bottom:1px } 55 | .highschool select{margin-top:2px } 56 | .editprofile .noneditable_value{width:170px } 57 | .editprofile .editor_actions{float:left;padding-left:5px;} 58 | .set{padding-left:1px;} 59 | #create_note{padding-left:0;margin-left:1px;} 60 | .editnote .side_column{margin:0 0 0 8px;} 61 | #blog_url_field .inputtext{width:285px;} 62 | .side_column #tag_this_note .inputtext{margin:0px;width:141px;} 63 | .editnote .main_column{width:408px;} 64 | .editnote #upload_iframe{height:48px;} 65 | .home #home_main{display:inline;} 66 | .sidebar_item_header .option{margin-top:5px;} 67 | .confirminvite .inputsubmit, 68 | .confirminvite .inputbutton{padding:1px 3px;} 69 | .invite .left #summaryandpager h4{height:16px;} 70 | .minifeed{padding-top:0;} 71 | .minifeed .photobox{padding-bottom:1px;} 72 | .minifeedwall .wall_story .story_table{width:400px;} 73 | .wallattachment{width:376px;} 74 | .wall_gift{text-align:right;} 75 | .my_sharebox .share_header .share_and_hide{width:7.0em;} 76 | .share_media .external .video_thumb a img.decoy{display:none;} 77 | .share_media .external .video_thumb a span.thumb{position:relative;} 78 | .share_media .external .video_thumb a span.thumb img{visibility:hidden;} 79 | .share_media .external .video_thumb a span.play{background-image:url(/images/video_play_overlay.gif);display:none;position:relative;} 80 | .share_pop_dialog .status h2{padding-top:1px;padding-bottom:1px;} 81 | .notifications .submission small{line-height:12px;} 82 | .notification_checkboxes{margin-top:-8px;} 83 | .notifications label{display:inline-block;} 84 | .help_column img{margin-top:0px;} 85 | .help_column .highlight img{margin-top:0px;} 86 | .explanation .square_bullets{margin-left:0;} 87 | .feedback .mixer_panel .saved_notification{display:inline-block;} 88 | .feedback .mixer_panel .left_buttons, .feedback .mixer_panel .right_buttons{margin-top:1px;} 89 | .feedback .mixer_panel .mixer_bar{margin-top:5px;margin-bottom:1px;} 90 | .vslider{padding-bottom:19px;} 91 | .listing #header{padding-bottom:10px;} 92 | .video_dashboard #video_banner{border-left:0px;border-right:0px;margin:0px;} 93 | .video_upload_page .separators{position:relative;margin-bottom:-1.1em;} 94 | .video_upload_page .separators a{display:inline-block;} 95 | .editor.showrows .hiddenrow{display:block;} 96 | .products .side_column{float:right;width:156px;} 97 | .products .app_browser li a, 98 | .products .app_browser li.app_type a{margin-left:1px;} 99 | .products .app_browser li.selected a{margin-left:0;border-left:1px solid #fff;} 100 | .products .app_browser li a{height:1px;} 101 | #inline_wall_post dd{margin-left:4px;} 102 | .attachment_compact .wall_video_thumb .play .wrap{padding-bottom:2px;} 103 | .wallpost .attached_item_info{height:19px;} 104 | .wall_attachment .share_media .share_thumb img{width:100px;} 105 | .wall_attachment .share_media.photo .fb_photo .photo_thumb img, 106 | .wall_attachment .share_media.share_ext_photo .share_thumb img{width:316px;} 107 | .attachment_stage{height:50px;} 108 | #fb_multi_friend_selector #friends .square{position:static;} 109 | #fb_multi_friend_selector #friends .square span{position:static;} 110 | #fb_multi_friend_selector #fs_filter_links a{display:block;float:left;} 111 | #fb_multi_friend_selector .navigator_menu{position:absolute;top:32px;margin-right:-10px;} 112 | #fb_multi_friend_selector .cols_squeeze .navigator_menu{top:30px;} 113 | #fb_multi_friend_selector #fs_limit_by_list_menu{margin-right:4px;} 114 | #fb_multi_friend_selector .navigator_menu ul{height:220px;width:140px;} 115 | #fb_multi_friend_selector .cols_squeeze #fs_filter_links{position:static;} 116 | .num_cols_2 #fb_multi_friend_selector_wrapper .buttons{clear:both;} 117 | .num_cols_2 #fb_multi_friend_selector_wrapper #finder .hide{left:320px;top:44px;} 118 | .num_cols_3 #fb_multi_friend_selector_wrapper #finder .hide{top:44px;} 119 | .editor #start_calendar, .editor #end_calendar{margin-top:18px;margin-left:-35px;} 120 | .calendar .month_title span{margin-left:-80px;} 121 | #fb_multi_friend_selector #fs_limit_by_network_menu{right:22px;} 122 | .num_cols_2 #fb_multi_friend_selector_wrapper .buttons{clear:both;} 123 | .req_confirmation #req_msg_add{padding-top:6px;} 124 | .req_confirmation .req_recipients .token span{display:inline;} 125 | .req_confirmation .req_preview{height:150px;} 126 | input.request_form_submit{background:#3B5998 url(/images/icons/request_button_icon.gif) no-repeat 8px 5px;padding:3px 6px 3px 30px;} 127 | .targeting_table td{padding:2px 1px;} 128 | .dev_app_infos{margin:0 -1px -1px 0;} 129 | .dev_app_list li.selected .border_hack{z-index:100;height:100%;padding:6px 0;background-color:#3b5998;width:1px;display:block;right:-1px;top:0;position:absolute;border:0;margin:0;text-indent:-9999px;} 130 | .privacy_widget .advanced .body .friends .advanced_friends_radio_item .radio_left{padding-left:0px;padding-top:0px;padding-left:3px;margin-top:-2px;} 131 | .privacy_widget .privacy_profile_advanced .checks label{padding-left:2px;} 132 | #profilenarrowcolumn .box_subhead{margin-right:1px;overflow:hidden;width:184px;} 133 | .fbpage_profile #profilenarrowcolumn .box_subhead{margin:auto;overflow:visible;width:auto;} 134 | .profile .right_column_container .profile_sidebar_ads{margin-right:0px;} 135 | #no_profile_picture #profileimage img{width:180px;} 136 | #presence #presence_preload{position:absolute;} 137 | .presence_popout #presence #buddy_list ul{padding-left:1px;} 138 | #buddy_list .compact ul li{margin-top:-2px;} 139 | .presence_popout #buddy_list ul li.subheader{margin-top:-8px;} 140 | .presence_popout #presence #chat_tab_bar .chat_shadow_input{position:absolute;} 141 | .presence_popout #presence #chat_tab_bar .chat_window .chat_conv p{margin-left:52px;padding-left:0px;} 142 | #presence .emote_text, 143 | #presence .emote_img{margin-bottom:-3px;} 144 | .transparent_png{background-image:none;display:inline;zoom:1;} 145 | .invisible_png{visibility:hidden;} 146 | html #nonfooter{height:100%;} 147 | .profile_pic_suggest_top_buttons{padding:0 0 0px 0;} 148 | .profile_pic_suggest_top_buttons .toggle_tabs{position:relative;top:1px;} 149 | .UIActionMenu_Menu{width:160px;} 150 | 151 | 152 | #bootloader_css_ie6_css { height: 42px; } -------------------------------------------------------------------------------- /classes/douban/api/broadcast.php: -------------------------------------------------------------------------------- 1 | 7 | * @link http://icyleaf.com 8 | * @copyright (c) 2009-2010 icyleaf 9 | * @license http://www.apache.org/licenses/LICENSE-2.0 10 | */ 11 | class Douban_API_Broadcast extends Douban_Core { 12 | 13 | /** 14 | * Get broadcast information 15 | * 16 | * @param int $broadcat_id 17 | * @return mixed 18 | */ 19 | public function get($broadcat_id) 20 | { 21 | $url = Douban_Core::MINIBLOG_URL.$broadcat_id; 22 | $post_data = array 23 | ( 24 | 'alt' => $this->alt 25 | ); 26 | $result = $this->_client->get($url, $post_data); 27 | 28 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 29 | { 30 | $result = $this->_format_broadcast($result->to_json()); 31 | } 32 | 33 | return $result; 34 | } 35 | 36 | /** 37 | * Get someone's broadcasts 38 | * 39 | * @param mixed $people_id 40 | * @param int $index 41 | * @param int $max 42 | * @return mixed 43 | */ 44 | public function get_mine($people_id = NULL, $index = 1, $max = 10) 45 | { 46 | $people_id = (empty($people_id) OR $people_id == 'me') ? '@me' : $people_id; 47 | $url = Douban_Core::PEOPLE_URL.urlencode($people_id).'/miniblog'; 48 | $post_data = array 49 | ( 50 | 'start-index' => $index, 51 | 'max-results' => $max, 52 | 'alt' => $this->alt 53 | ); 54 | 55 | return $this->_get_broadcasts($url, $post_data); 56 | } 57 | 58 | /** 59 | * Get someone's contacts 60 | * 61 | * @param mixed $people_id 62 | * @param int $index 63 | * @param int $max 64 | * @return mixed 65 | */ 66 | public function get_contacts($people_id = NULL, $index = 1, $max = 10) 67 | { 68 | $people_id = (empty($people_id) OR $people_id == 'me') ? '@me' : $people_id; 69 | $url = Douban_Core::PEOPLE_URL.urlencode($people_id).'/miniblog/contacts'; 70 | $post_data = array 71 | ( 72 | 'start-index' => $index, 73 | 'max-results' => $max, 74 | 'alt' => $this->alt 75 | ); 76 | 77 | return $this->_get_broadcasts($url, $post_data); 78 | } 79 | 80 | /** 81 | * Get a miniblog's comments 82 | * 83 | * @param mixed $miniblog_id 84 | * @param int $index 85 | * @param int $max 86 | * @return mixed 87 | */ 88 | public function get_comments($miniblog_id, $index = 1, $max = 10) 89 | { 90 | $url = Douban_Core::MINIBLOG_URL.urlencode($miniblog_id).'/comments'; 91 | $post_data = array 92 | ( 93 | 'start-index' => $index, 94 | 'max-results' => $max, 95 | 'alt' => $this->alt 96 | ); 97 | 98 | return $this->_get_comments($url, $post_data); 99 | } 100 | 101 | /** 102 | * Create a new broadcast 103 | * 104 | * @param string $message 105 | * @return mixed 106 | */ 107 | public function create($message) 108 | { 109 | $url = Douban_Core::MINIBLOG_URL.'saying'; 110 | $header = array('Content-Type: application/atom+xml'); 111 | $post_data = ''. 112 | ''. 114 | ''.$message.''. 115 | ''; 116 | $result = $this->_client->post($url, $post_data, $header); 117 | 118 | if ($this->format) 119 | { 120 | if ($result->status() == 201) 121 | { 122 | $broadcast = $result->to_xml(); 123 | // get created review id and return 124 | $result = substr($broadcast->id, strlen(Douban_Core::MINIBLOG_URL)); 125 | } 126 | else 127 | { 128 | $this->_errors = $result; 129 | 130 | $result = FALSE; 131 | } 132 | } 133 | 134 | return $result; 135 | } 136 | 137 | /** 138 | * Post a new comment to the specified miniblog 139 | * 140 | * @param int $miniblog_id 141 | * @param string $message 142 | * @return mixed 143 | */ 144 | public function reply($miniblog_id, $message) 145 | { 146 | $url = Douban_Core::MINIBLOG_URL.urlencode($miniblog_id).'/comments'; 147 | $header = array('Content-Type: application/atom+xml'); 148 | $post_data = ''. 149 | ''.$message.''; 150 | $result = $this->_client->post($url, $post_data, $header); 151 | 152 | if ($this->format) 153 | { 154 | if ($result->status() == 201) 155 | { 156 | $result = TRUE; 157 | } 158 | else 159 | { 160 | $this->_errors = $result; 161 | 162 | $result = FALSE; 163 | } 164 | } 165 | 166 | return $result; 167 | } 168 | 169 | /** 170 | * Delete a broadcast 171 | * 172 | * @param int $broadcast_id 173 | * @return mixed 174 | */ 175 | public function delete($broadcast_id) 176 | { 177 | $url = Douban_Core::MINIBLOG_URL.$broadcast_id; 178 | $result = $this->_client->delete($url); 179 | 180 | if ($result->status() == 200) 181 | { 182 | return TRUE; 183 | } 184 | else 185 | { 186 | $this->_errors = $result; 187 | 188 | return FALSE; 189 | } 190 | } 191 | 192 | /** 193 | * Get broadcasts 194 | * 195 | * @param string $url 196 | * @param int $index 197 | * @param int $max 198 | * @return mixed 199 | */ 200 | private function _get_broadcasts($url, $post_data = array()) 201 | { 202 | $result = $this->_client->get($url, $post_data); 203 | 204 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 205 | { 206 | $broadcasts = $result->to_json(); 207 | 208 | $result = new stdClass; 209 | $result->title = $broadcasts['title']['$t']; 210 | // author 211 | if (isset($broadcasts['author'])) 212 | { 213 | $result->author = Douban_API_People::format($broadcasts['author']); 214 | } 215 | // search 216 | $result->index = $broadcasts['openSearch:startIndex']['$t']; 217 | $result->max = $broadcasts['openSearch:itemsPerPage']['$t']; 218 | if (count($broadcasts['entry']) > 0) 219 | { 220 | // broadcasts 221 | foreach ($broadcasts['entry'] as $broadcast) 222 | { 223 | $result->entry[] = $this->_format_broadcast($broadcast); 224 | } 225 | } 226 | else 227 | { 228 | $result->entry = array(); 229 | } 230 | } 231 | 232 | return $result; 233 | } 234 | 235 | /** 236 | * Format broadcast 237 | * 238 | * @param array $broadcast 239 | * @return object 240 | */ 241 | private function _format_broadcast($broadcast) 242 | { 243 | // echo Debug::dump($broadcast); 244 | $result = new stdClass; 245 | // id 246 | $result->id = substr($broadcast['id']['$t'], strlen(Douban_Core::MINIBLOG_URL)); 247 | // title 248 | $result->title = $broadcast['title']['$t']; 249 | // author 250 | if (isset($broadcast['author'])) 251 | { 252 | $result->author = Douban_API_People::format($broadcast['author']); 253 | } 254 | // link 255 | if (isset($broadcast['link'])) 256 | { 257 | foreach ($broadcast['link'] as $link) 258 | { 259 | $result->link[$link['@rel']] = $link['@href']; 260 | } 261 | } 262 | // category 263 | if (isset($broadcast['category'])) 264 | { 265 | $category_url = Douban_Core::CATEGORY_URL.'miniblog.'; 266 | $result->category = substr($broadcast['category']['@term'], strlen($category_url)); 267 | } 268 | // attributes 269 | if (isset($broadcast['db:attribute'])) 270 | { 271 | foreach ($broadcast['db:attribute'] as $attribute) 272 | { 273 | $result->attribute[$attribute['@name']] = $attribute['$t']; 274 | } 275 | } 276 | // content 277 | $result->content = isset($broadcast['content']) ? $broadcast['content']['$t'] : ''; 278 | // published date 279 | $result->published = strtotime($broadcast['published']['$t']); 280 | 281 | unset($category_url); 282 | 283 | return $result; 284 | } 285 | 286 | /** 287 | * Get comments 288 | * 289 | * @param string $url 290 | * @param int $index 291 | * @param int $max 292 | * @return mixed 293 | */ 294 | private function _get_comments($url, $post_data = array()) 295 | { 296 | $result = $this->_client->get($url, $post_data); 297 | 298 | if ($this->alt == 'json' AND $this->format AND $result->status() == 200) 299 | { 300 | $comments = $result->to_json(); 301 | 302 | $result = new stdClass; 303 | $result->title = $comments['title']['$t']; 304 | // author 305 | if (isset($comments['author'])) 306 | { 307 | $result->author = Douban_API_People::format($comments['author']); 308 | } 309 | // search 310 | $result->index = $comments['openSearch:startIndex']['$t']; 311 | $result->max = $comments['openSearch:itemsPerPage']['$t']; 312 | $result->total = $comments['openSearch:totalResults']['$t']; 313 | if (count($comments['entry']) > 0) 314 | { 315 | // comments 316 | foreach ($comments['entry'] as $comment) 317 | { 318 | $result->entry[] = $this->_format_comment($comment); 319 | } 320 | } 321 | else 322 | { 323 | $result->entry = array(); 324 | } 325 | } 326 | 327 | return $result; 328 | } 329 | 330 | /** 331 | * Format comment 332 | * 333 | * @param array $comment 334 | * @return object 335 | */ 336 | private function _format_comment($comment) 337 | { 338 | $result = new stdClass; 339 | // author 340 | if (isset($comment['author'])) 341 | { 342 | $result->author = Douban_API_People::format($comment['author']); 343 | } 344 | // content 345 | $result->content = $comment['content']['$t']; 346 | // published 347 | $result->published = strtotime($comment['published']['$t']); 348 | 349 | return $result; 350 | } 351 | 352 | } 353 | 354 | -------------------------------------------------------------------------------- /classes/douban/oauth.php: -------------------------------------------------------------------------------- 1 | 7 | * @link http://icyleaf.com 8 | * @version 0.6 9 | * @copyright (c) 2009-2010 icyleaf 10 | * @license http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * REQUEST: OAuth official library for PHP (http://oauth.net/code) 13 | * which is store in 'vender' folder 14 | */ 15 | class Douban_OAuth { 16 | 17 | public $key = NULL; // API key 18 | public $secret = NULL; // API secret 19 | 20 | private $_config = NULL; // douban configurations 21 | private static $_instance; 22 | 23 | public static function instance($key = NULL, $secret = NULL) 24 | { 25 | empty(Douban_OAuth::$_instance) AND Douban_OAuth::$_instance = new Douban_OAuth($key, $secret); 26 | 27 | return Douban_OAuth::$_instance; 28 | } 29 | 30 | /** 31 | * Construct function 32 | * 33 | * @param string $key - API key 34 | * @param string $secret - API secret 35 | */ 36 | public function __construct($key = NULL, $secret = NULL) 37 | { 38 | // include OAuth classes 39 | include_once Kohana::find_file('vendor', 'OAuth'); 40 | 41 | $this->key = $key; 42 | $this->secret = $secret; 43 | $this->_config = Kohana::$config->load('douban'); 44 | } 45 | 46 | /** 47 | * Get Requset token 48 | * 49 | * @param string $url - requset token url 50 | * @param string $method - http sending method 51 | * @param array $parameters - sending parameters 52 | * @return string 53 | */ 54 | public function request_token($url, $method = 'POST', $parameters = array()) 55 | { 56 | $request = $this->_prepare_request(NULL, $method, $url, $parameters); 57 | if ($method == 'POST') 58 | { 59 | return Douban_Request::post($url, $request->to_postdata()); 60 | } 61 | else 62 | { 63 | return Douban_Request::get($request->to_url()); 64 | } 65 | } 66 | 67 | /** 68 | * Ger access token 69 | * 70 | * @param string $url - access token url 71 | * @param object $token - request token 72 | * @param string $method - http sending method 73 | * @param array $parameters - sending parameters 74 | * @return string 75 | */ 76 | public function access_token($url, $token, $method = 'POST', $parameters = array()) 77 | { 78 | $request = $this->_prepare_request($token, $method, $url, $parameters); 79 | if ($method == 'POST') 80 | { 81 | return Douban_Request::post($url, $request->to_postdata()); 82 | } 83 | else 84 | { 85 | return Douban_Request::get($request->to_url()); 86 | } 87 | } 88 | 89 | /** 90 | * OAuth validate method 91 | * 92 | * @param string $type - activate in HMAC_SHA1, RSA_SHA1, PLAINTEXT 93 | * @return object 94 | */ 95 | public function sign_method($type = 'HMAC_SHA1') 96 | { 97 | switch (strtoupper($type)) 98 | { 99 | default: 100 | case 'HMAC_SHA1': 101 | return new OAuthSignatureMethod_HMAC_SHA1(); 102 | case 'RSA_SHA1': 103 | return new OAuthSignatureMethod_RSA_SHA1(); 104 | case 'PLAINTEXT': 105 | return new OAuthSignatureMethod_PLAINTEXT(); 106 | } 107 | } 108 | 109 | /** 110 | * Get request/access token 111 | * 112 | * @param mixed $token - request/access token 113 | * @param string $secret - request/access secret 114 | * @return object 115 | */ 116 | public function oauth_token($token, $secret = NULL) 117 | { 118 | if (is_array($token)) 119 | { 120 | return new OAuthToken($token['oauth_token'], $token['oauth_token_secret']); 121 | } 122 | elseif($token AND $secret) 123 | { 124 | return new OAuthToken($token, $secret); 125 | } 126 | else 127 | { 128 | return FALSE; 129 | } 130 | } 131 | 132 | /** 133 | * Get request url 134 | * 135 | * @param string $method - http sending method 136 | * @param string $url - http sending url 137 | * @param array $parameters - http sending parameters 138 | * @return object 139 | */ 140 | public function get_request($method, $url, $parameters) 141 | { 142 | return new OAuthRequest($method, $url, $parameters); 143 | } 144 | 145 | /** 146 | * Get request url from Consumer 147 | * 148 | * @param string $consumer - OAuth consumer 149 | * @param mixed $token - request token 150 | * @param string $method - http sending url 151 | * @param string $url - http sending method 152 | * @param array $parameters - http sending parameters 153 | * @return object 154 | */ 155 | public function consumer_request($consumer = NULL, $token = NULL, $method = NULL, $url = NULL, $parameters = NULL) 156 | { 157 | return OAuthRequest::from_consumer_and_token($consumer, $token, $method, $url, $parameters); 158 | } 159 | 160 | /** 161 | * Create OAuth token 162 | * 163 | * @param array $response - requset token 164 | * @return object 165 | */ 166 | protected function create_oauth_token($response) 167 | { 168 | if (isset($response['oauth_token']) AND isset($response['oauth_token_secret'])) { 169 | return $this->oauth_token($response['oauth_token'], $response['oauth_token_secret']); 170 | } 171 | 172 | return NULL; 173 | } 174 | 175 | /** 176 | * Create OAuth Consumer 177 | * 178 | * @return object 179 | */ 180 | private function consumer() 181 | { 182 | return new OAuthConsumer($this->key, $this->secret); 183 | } 184 | 185 | /** 186 | * Call API with a GET request 187 | * 188 | * @param string $url 189 | * @param mixed $data 190 | * @param mixed $header 191 | * @param boolean $headers_only 192 | * @return object 193 | */ 194 | public function get($url, $data = NULL, $header = array(), $headers_only = FALSE) 195 | { 196 | if ($token = $this->_get_oauth_token()) 197 | { 198 | // get data with oauth token 199 | $accessToken = $this->oauth_token($token); 200 | $request = $this->_prepare_request($accessToken, 'GET', $url, $data); 201 | $header = array 202 | ( 203 | $request->to_header() 204 | ); 205 | 206 | $result = Douban_Request::get($request->to_url(), $header, $headers_only); 207 | } 208 | else 209 | { 210 | // get data with api key 211 | $data['apikey'] = $this->key; 212 | $url = $this->_build_url($url, $data); 213 | $result = Douban_Request::get($url, $header, $headers_only); 214 | } 215 | 216 | return $result; 217 | } 218 | 219 | /** 220 | * Call API with a POST request 221 | * 222 | * @param string $url 223 | * @param mixed $data 224 | * @param mixed $header 225 | * @param boolean $headers_only 226 | * @return object 227 | */ 228 | public function post($url, $data = NULL, $header = NULL, $headers_only = FALSE) 229 | { 230 | if ($token = $this->_get_oauth_token()) 231 | { 232 | @$header OR $header = array(); 233 | $accessToken = $this->oauth_token($token); 234 | $request = $this->_prepare_request($accessToken, 'POST', $url); 235 | $header = array_merge(array($request->to_header()), $header); 236 | 237 | return Douban_Request::post($url, $data, $header, $headers_only); 238 | } 239 | else 240 | { 241 | return FALSE; 242 | } 243 | } 244 | 245 | /** 246 | * Call API with a PUT request 247 | * 248 | * @param string $url 249 | * @param mixed $data 250 | * @param mixed $header 251 | * @param boolean $headers_only 252 | * @return object 253 | */ 254 | public function put($url, $data = NULL, $header = NULL, $headers_only = FALSE) 255 | { 256 | if ($token = $this->_get_oauth_token()) 257 | { 258 | @$header OR $header = array(); 259 | $accessToken = $this->oauth_token($token); 260 | $request = $this->_prepare_request($accessToken, 'PUT', $url); 261 | $header = array_merge(array($request->to_header()), $header); 262 | 263 | return Douban_Request::put($url, $data, $header, $headers_only); 264 | } 265 | else 266 | { 267 | return FALSE; 268 | } 269 | } 270 | 271 | /** 272 | * Call API with a DELETE request 273 | * 274 | * @param string $url 275 | * @param mixed $data 276 | * @param mixed $header 277 | * @param boolean $headers_only 278 | * @return object 279 | */ 280 | public function delete($url, $data = NULL, $header = NULL, $headers_only = FALSE) 281 | { 282 | if ($token = $this->_get_oauth_token()) 283 | { 284 | @$header OR $header = array(); 285 | $accessToken = $this->oauth_token($token); 286 | $request = $this->_prepare_request($accessToken, 'DELETE', $url); 287 | $header = array_merge(array($request->to_header()), $header); 288 | 289 | return Douban_Request::delete($url, $data, $header, $headers_only); 290 | } 291 | else 292 | { 293 | return FALSE; 294 | } 295 | } 296 | 297 | /** 298 | * Prepere Request 299 | * 300 | * @param object $token 301 | * @param string $httpMethod 302 | * @param string $url 303 | * @param string $parameters 304 | * @return void 305 | */ 306 | private function _prepare_request($token, $httpMethod, $url, $parameters = NULL) 307 | { 308 | $consumer = $this->consumer(); 309 | $request = $this->consumer_request($consumer, $token, $httpMethod, $url, $parameters); 310 | $request->sign_request($this->sign_method('HMAC_SHA1'), $consumer, $token); 311 | 312 | return $request; 313 | } 314 | 315 | /** 316 | * Get oauth access token 317 | * 318 | * @return mixed 319 | */ 320 | protected function _get_oauth_token() 321 | { 322 | if ($token = Cookie::get($this->_config->session_key['oauth_token'])) 323 | { 324 | return unserialize($token); 325 | } 326 | else if ($token = Session::instance()->get($this->_config->session_key['oauth_token'])) 327 | { 328 | return $token; 329 | } 330 | else 331 | { 332 | return FALSE; 333 | } 334 | } 335 | 336 | /** 337 | * Build url with GET parameters 338 | * 339 | * @param string $url 340 | * @param array $parameters 341 | * @return string 342 | */ 343 | private function _build_url($url, $parameters) 344 | { 345 | $total = array(); 346 | foreach ($parameters as $key => $value) 347 | { 348 | $total[] = $key . "=" . $value; 349 | } 350 | 351 | $url = $url . '?' . implode("&", $total); 352 | 353 | return $url; 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /classes/douban/core.php: -------------------------------------------------------------------------------- 1 | 7 | * @link http://icyleaf.com 8 | * @copyright (c) 2009-2010 icyleaf 9 | * @license http://www.apache.org/licenses/LICENSE-2.0 10 | */ 11 | class Douban_Core { 12 | 13 | // Release version 14 | const VERSION = '2.0'; 15 | 16 | // Douban OAuth URL 17 | const SERVER_URL = 'http://api.douban.com/'; 18 | const REQUEST_TOKEN_URL = 'http://www.douban.com/service/auth/request_token'; 19 | const AUTHORIZATION_URL = 'http://www.douban.com/service/auth/authorize'; 20 | const ACCESS_TOKEN_URL = 'http://www.douban.com/service/auth/access_token'; 21 | const VERITY_TOKEN_URL = 'http://api.douban.com/access_token/'; 22 | // Douban API URL 23 | const PEOPLE_URL = 'http://api.douban.com/people/'; 24 | const MINIBLOG_URL = 'http://api.douban.com/miniblog/'; 25 | const BOOK_URL = 'http://api.douban.com/book/subject/'; 26 | const MUSIC_URL = 'http://api.douban.com/music/subject/'; 27 | const MOVIE_URL = 'http://api.douban.com/movie/subject/'; 28 | const EVENT_URL = 'http://api.douban.com/event/'; 29 | const DOUMAIL_URL = 'http://api.douban.com/doumail/'; 30 | const COLLECTION_URL = 'http://api.douban.com/collection/'; 31 | const NOTE_URL = 'http://api.douban.com/note/'; 32 | const REVIEW_URL = 'http://api.douban.com/review/'; 33 | const RECOMMENDATION_URL = 'http://api.douban.com/recommendation/'; 34 | const ALBUM_URL = 'http://api.douban.com/album/'; 35 | const HOST_URL = 'http://api.douban.com/host/'; 36 | // Douban Category URL 37 | const CATEGORY_URL = 'http://www.douban.com/2007#'; 38 | // The Default Image URL 39 | const DEFAULT_PEOPLE_AVATAR_URL = 'http://t.douban.com/icon/user.jpg'; 40 | const DEFAULT_BOOK_IMAGE_URL = 'http://t.douban.com/pics/book-default-small.gif'; 41 | const DEFAULT_MUSIC_IMAGE_URL = 'http://t.douban.com/pics/music-default-small.gif'; 42 | const DEFAULT_MOVIEE_IMAG_URL = 'http://t.douban.com/pics/movie-default-small.gif'; 43 | const DEFAULT_EVENT_IMAGE_URL = 'http://t.douban.com/pics/event/bpic/event_dft.jpg'; 44 | 45 | public $alt = 'json'; // Return format type from Douban API. Available: json, atom(xml) 46 | public $format = TRUE; // Set 'TRUE', it will return 'Douban->alt' format type. 47 | public $session = NULL; 48 | 49 | protected $_config = NULL; // Douban configuration 50 | protected $_client = NULL; // OAuth 51 | protected $_method = NULL; // OAurh method 52 | protected $_errors = NULL; // Throw Errors 53 | 54 | private static $_instance; 55 | 56 | /** 57 | * Instance Douban 58 | * 59 | * @param string $api_key - Douban API key 60 | * @param string $api_secret - Douban API secret 61 | * @return object 62 | */ 63 | public static function instance($api_key = NULL, $api_secret = NULL) 64 | { 65 | empty(Douban_Core::$_instance) AND Douban_Core::$_instance = new Douban_Core($api_key, $api_secret); 66 | 67 | return Douban_Core::$_instance; 68 | } 69 | 70 | /** 71 | * Construct Douban function 72 | * 73 | * @param string $api_key - Douban API key 74 | * @param string $api_secret - Douban API secret 75 | */ 76 | public function __construct($api_key = NULL, $api_secret = NULL) 77 | { 78 | $this->_config = Kohana::$config->load('douban'); 79 | $api_key = empty($api_key) ? $this->_config->api_key : $api_key; 80 | $api_secret = empty($api_secret) ? $this->_config->api_secret : $api_secret; 81 | 82 | if ($api_key AND $api_secret) 83 | { 84 | $this->_client = Douban_OAuth::instance($api_key, $api_secret); 85 | $this->_method = $this->_client->sign_method('HMAC_SHA1'); 86 | 87 | $this->session = Session::instance(); 88 | } 89 | else 90 | { 91 | throw new Exception('豆瓣 API Key 或 Secrect 是空的!'); 92 | } 93 | } 94 | 95 | /** 96 | * Get request token 97 | * 98 | * @return mixed 99 | */ 100 | public function request_token() 101 | { 102 | return $this->_client->request_token(Douban_Core::REQUEST_TOKEN_URL); 103 | } 104 | 105 | /** 106 | * Get Authorization URL 107 | * 108 | * @param string $key - request token key 109 | * @param string $secret - request token secret 110 | * @param string $callback - callback url after authorization 111 | * @return string 112 | */ 113 | public function auth_url($request_token = array(), $callback = NULL) 114 | { 115 | $parameters = array 116 | ( 117 | 'oauth_token' => $request_token['oauth_token'] 118 | ); 119 | 120 | if ($callback) 121 | { 122 | // set the callback url 123 | $parameters['oauth_callback'] = $callback; 124 | } 125 | 126 | $oauth_request = $this->_client->get_request('GET', Douban_Core::AUTHORIZATION_URL, $parameters); 127 | 128 | return $oauth_request->to_url(); 129 | } 130 | 131 | /** 132 | * Get access token 133 | * 134 | * @param mixed $token - request token 135 | * @param string $secret - request token secret 136 | * @param string $returnURL - return type, default is array 137 | * @return mixed 138 | */ 139 | public function access_token($token, $secret = NULL) 140 | { 141 | if (is_array($token)) 142 | { 143 | $request_token = $this->_client->oauth_token($token['oauth_token'], $token['oauth_token_secret']); 144 | } 145 | elseif ($token AND $secret) 146 | { 147 | $request_token = $this->_client->oauth_token($token, $secret); 148 | } 149 | else 150 | { 151 | return FALSE; 152 | } 153 | 154 | $oauth_request = $this->_client->consumer_request($this->_client, $request_token, 'GET', Douban_Core::ACCESS_TOKEN_URL); 155 | $oauth_request->sign_request($this->_method, $this->_client, $request_token); 156 | $access_token = Douban_Request::get($oauth_request); 157 | 158 | return $access_token; 159 | } 160 | 161 | /** 162 | * Verify api and general auth url 163 | * 164 | * @param string $callback_url 165 | * @return mixed 166 | */ 167 | public function verify($callback_url) 168 | { 169 | $request_token = $this->request_token(); 170 | 171 | if ($request_token->status() == 200) 172 | { 173 | $token = $request_token->to_array(); 174 | $this->session->set('request_token', $token); 175 | 176 | // get Authorization URL from douban.com 177 | return $this->auth_url($token, $callback_url); 178 | } 179 | else 180 | { 181 | $this->_errors = $request_token; 182 | 183 | return FALSE; 184 | } 185 | } 186 | 187 | /** 188 | * OAuth login 189 | * 190 | * @return boolean 191 | */ 192 | public function login() 193 | { 194 | // Request token 195 | $request_token = $this->session->get('request_token'); 196 | $this->session->delete('request_token'); 197 | 198 | // Get Access token 199 | $access_token = $this->access_token($request_token); 200 | if ($access_token->status() == 200) 201 | { 202 | $access_token = $access_token->to_array(); 203 | // Save the access token into both cookie and session. 204 | Cookie::set($this->_config->session_key['oauth_token'], serialize($access_token), $this->_config->lifetime); 205 | $this->session->set($this->_config->session_key['oauth_token'], $access_token); 206 | // Get oauth user information 207 | $people = $this->people()->get('me'); 208 | $this->session->set($this->_config->session_key['oauth_user'], $people); 209 | 210 | return TRUE; 211 | } 212 | else 213 | { 214 | $this->_errors = $request_token; 215 | 216 | return FALSE; 217 | } 218 | } 219 | 220 | /** 221 | * Get Current User profile 222 | * 223 | * @return mixed 224 | */ 225 | public function get_user() 226 | { 227 | if ($user = $this->session->get($this->_config->session_key['oauth_user'])) 228 | { 229 | return $user; 230 | } 231 | else 232 | { 233 | if ($token = $this->_get_oauth_token()) 234 | { 235 | // Save the access token into both cookie and session. 236 | Cookie::set($this->_config->session_key['oauth_token'], serialize($token), $this->_config->lifetime); 237 | $this->session->set($this->_config->session_key['oauth_token'], $token); 238 | // Get oauth user information 239 | $people = $this->people()->get('me'); 240 | $this->session->set($this->_config->session_key['oauth_user'], $people); 241 | 242 | return $people; 243 | } 244 | else 245 | { 246 | return FALSE; 247 | } 248 | } 249 | } 250 | 251 | /** 252 | * Check user if logged in 253 | * 254 | * @return boolean 255 | */ 256 | public function logged_in() 257 | { 258 | $access_token = $this->_get_oauth_token(); 259 | if (is_array($access_token)) 260 | { 261 | $url = Douban_Core::VERITY_TOKEN_URL.$access_token['oauth_token']; 262 | $request = $this->_client->get($url); 263 | if ($request->status() == 200) 264 | { 265 | return TRUE; 266 | } 267 | } 268 | 269 | return FALSE; 270 | } 271 | 272 | /** 273 | * Destory oauth token 274 | */ 275 | public function logout() 276 | { 277 | $this->session->delete($this->_config->session_key['oauth_token']); 278 | $this->session->destroy(); 279 | 280 | Cookie::delete('oauth_token'); 281 | Cookie::delete('oauth_token_secret'); 282 | } 283 | 284 | /** 285 | * Default call Douban API by $name without $arguments 286 | * 287 | * @param string $name 288 | * @param string $arguments 289 | */ 290 | public function __call($name, $arguments) 291 | { 292 | if (empty($arguments)) 293 | { 294 | if ($name == 'errors') 295 | { 296 | return $this->_errors; 297 | } 298 | else 299 | { 300 | // Create the Douban API instance 301 | $interface = 'Douban_API_' . ucfirst($name); 302 | 303 | // Load douban api interface 304 | return new $interface(); 305 | } 306 | } 307 | else 308 | { 309 | return NULL; 310 | } 311 | } 312 | 313 | /** 314 | * Get oauth access token 315 | * 316 | * @return mixed 317 | */ 318 | protected function _get_oauth_token() 319 | { 320 | if ($token = Cookie::get($this->_config->session_key['oauth_token'])) 321 | { 322 | return unserialize($token); 323 | } 324 | else if ($token = $this->session->get($this->_config->session_key['oauth_token'])) 325 | { 326 | return $token; 327 | } 328 | else 329 | { 330 | return FALSE; 331 | } 332 | } 333 | 334 | protected function to_array($string) 335 | { 336 | $string = preg_replace('/&(?!(?:#\d++|[a-z]++);)/ui', '&', $string); 337 | if ( ! preg_match('/[=|&]/', $string)) 338 | { 339 | return $string; 340 | } 341 | 342 | $array = array(); 343 | $expression = explode('&', $string); 344 | for ($i = 0; $i < count($expression); $i++) 345 | { 346 | $attribute = explode('=', $expression[$i]); 347 | $array[$attribute[0]] = $attribute[1]; 348 | } 349 | 350 | return $array; 351 | } 352 | 353 | } 354 | 355 | --------------------------------------------------------------------------------