1<?php
2/**
3* Part of the Services_Blogging package.
4*
5* PHP version 5
6*
7* @category Services
8* @package  Services_Blogging
9* @author   Anant Narayanan <anant@php.net>
10* @author   Christian Weiske <cweiske@php.net>
11* @license  http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
12* @version  CVS: $Id$
13* @link     http://pear.php.net/package/Services_Blogging
14*/
15
16require_once 'Services/Blogging/Driver/Exception.php';
17require_once 'Services/Blogging/ExtendedDriver.php';
18require_once 'Services/Blogging/Post.php';
19require_once 'Services/Blogging/XmlRpc.php';
20require_once 'XML/RPC.php';
21
22/**
23* metaWeblog API implementation.
24* http://www.xmlrpc.com/metaWeblogApi
25* http://www.movabletype.org/mt-static/docs/mtmanual_programmatic.html
26*
27* @category Services
28* @package  Services_Blogging
29* @author   Anant Narayanan <anant@php.net>
30* @author   Christian Weiske <cweiske@php.net>
31* @license  http://www.gnu.org/copyleft/lesser.html  LGPL License 2.1
32* @link     http://pear.php.net/package/Services_Blogging
33*/
34class Services_Blogging_Driver_MetaWeblog extends Services_Blogging_ExtendedDriver
35{
36
37    /**
38    * Internal list with user data.
39    * @var array
40    */
41    protected $userdata = array();
42
43    protected $arSupportedPostProperties = array(
44        Services_Blogging_Post::TITLE,
45        Services_Blogging_Post::CONTENT,
46        Services_Blogging_Post::DATE,
47        Services_Blogging_Post::URL,
48        Services_Blogging_Post::CATEGORIES,
49    );
50
51
52
53    /**
54    * Constructor for the metaWeblog driver class.
55    *
56    * @param string $user   The username of the blog account.
57    * @param string $pass   The password of the blog account.
58    * @param string $server The URI of the server to connect to.
59    * @param string $path   The path to the XML-RPC server script.
60    *
61    * @throws Services_Blogging_Exception  If authentication fails
62    */
63    public function __construct($user, $pass, $server, $path)
64    {
65        $this->userdata = array(
66            'user'      => $user,
67            'pass'      => $pass,
68            'server'    => $server,
69            'path'      => $path,
70            'rpc_user'  => new XML_RPC_Value($user, 'string'),
71            'rpc_pass'  => new XML_RPC_Value($pass, 'string'),
72            'rpc_blogid'=> new XML_RPC_Value($user, 'string'),
73        );
74
75        $this->rpc_client = new XML_RPC_Client(
76            $this->userdata['path'],
77            $this->userdata['server']
78        );
79        //$this->rpc_client->setDebug(true);
80    }//public function __construct($userid, $pass, $server, $path)
81
82
83
84    /**
85    * Save a new post into the blog.
86    *
87    * @param Services_Blogging_Post $post Post object to put online
88    *
89    * @return void
90    *
91    * @throws Services_Blogging_Exception If an error occured
92    */
93    public function savePost(Services_Blogging_Post $post)
94    {
95        if ($post->id === null) {
96            //post is new and has no Id => create new one
97            $request = new XML_RPC_Message('metaWeblog.newPost',
98                array(
99                    $this->userdata['rpc_blogid'],
100                    $this->userdata['rpc_user'],
101                    $this->userdata['rpc_pass'],
102                    self::convertPostToStruct($post),
103                    new XML_RPC_Value(true, 'boolean')
104                )
105            );
106            $nPostId = Services_Blogging_XmlRpc::sendRequest(
107                $request, $this->rpc_client
108            );
109            $post->setId($nPostId);
110        } else {
111            //edit the post; it already exists
112            $request = new XML_RPC_Message('metaWeblog.editPost',
113                array(
114                    new XML_RPC_Value($post->id, 'string'),
115                    $this->userdata['rpc_user'],
116                    $this->userdata['rpc_pass'],
117                    self::convertPostToStruct($post),
118                    new XML_RPC_Value(true, 'boolean')
119                )
120            );
121            Services_Blogging_XmlRpc::sendRequest($request, $this->rpc_client);
122        }
123    }//public function savePost(Services_Blogging_Post $post)
124
125
126
127    /**
128    * The getPost method is intended to retrive a given post as an object of
129    * the Services_Blogging_Post class; given the unique post id which is passed
130    * as a parameter to the function.
131    *
132    * @param string $id The PostID of the post to be retrieved. (As
133    *                    returned by newPost() defined in
134    *                    Services_Blogging_driver).
135    *
136    * @return Services_Blogging_Post The elements of the post returned as an
137    *                                object of the Services_Blogging_Post class.
138    *
139    * @throws Services_Blogging_Exception If the post does not exist
140    */
141    public function getPost($id)
142    {
143        $request = new XML_RPC_Message('metaWeblog.getPost',
144            array(
145                new XML_RPC_Value($id, 'int'),
146                $this->userdata['rpc_user'],
147                $this->userdata['rpc_pass'],
148            )
149        );
150
151        $arData = Services_Blogging_XmlRpc::sendRequest(
152            $request, $this->rpc_client
153        );
154        return $this->convertStructToPost($arData);
155    }//public function getPost($id)
156
157
158
159    /**
160    * Delete a given post.
161    * The deletePost method in metaWeblog is just
162    *  an alias to the deletePost blogger method
163    *
164    * @param mixed $post Services_Blogging_Post object to delete,
165    *                     or post id (integer) to delete
166    *
167    * @return boolean True if deleted, false if not.
168    */
169    public function deletePost($post)
170    {
171        if (!($post instanceof Services_Blogging_Post)) {
172            $nPostId = $post;
173            $post    = new Services_Blogging_Post();
174            $post->setId($nPostId);
175        }
176
177        $request = new XML_RPC_Message('metaWeblog.deletePost',
178            array(
179                //dummy API key
180                new XML_RPC_Value('0123456789ABCDEF', 'string'),
181                new XML_RPC_Value($post->id, 'int'),
182                $this->userdata['rpc_user'],
183                $this->userdata['rpc_pass'],
184                new XML_RPC_Value(true, 'boolean')
185            )
186        );
187        Services_Blogging_XmlRpc::sendRequest($request, $this->rpc_client);
188    }//public function deletePost($post)
189
190
191
192    /**
193    * Returns an array of recent posts as Services_Blogging_Post objects
194    *
195    * @param int $number The number of posts to be retrieved.
196    *                     Defaults to 15
197    *
198    * @return Array An array of objects of the Services_Blogging_Post class that
199    *                correspond to the number of posts requested.
200    */
201    public function getRecentPosts($number = 15)
202    {
203        $request = new XML_RPC_Message('metaWeblog.getRecentPosts',
204            array(
205                $this->userdata['rpc_blogid'],
206                $this->userdata['rpc_user'],
207                $this->userdata['rpc_pass'],
208                new XML_RPC_Value($number, 'int')
209            )
210        );
211
212        $arData = Services_Blogging_XmlRpc::sendRequest(
213            $request, $this->rpc_client
214        );
215
216        $arPosts = array();
217        foreach ($arData as $data) {
218            $post               = $this->convertStructToPost($data);
219            $arPosts[$post->id] = $post;
220        }
221        return $arPosts;
222    }//public function getRecentPosts($number = 15)
223
224
225
226    /**
227    * The getRecentPostTitles method is intended to retrieve the given number of
228    * posts titles from a blog.
229    * The posts themselves can be retrieved with getPost() or getRecentPosts().
230    *
231    * There is no direct getRecentPostTitles method in metaWeblog. So
232    * we internally call getRecentPosts() and strip out ids and titles of
233    * the post. So this method is slow here, because all post data needs
234    * to be transmitted.
235    *
236    * @param int $number The number of posts to be retrieved.
237    *
238    * @return Array An array of int => strings representing the
239    *                post ids (key) and their title (value).
240    */
241    public function getRecentPostTitles($number = 15)
242    {
243        $arPosts  = $this->getRecentPosts($number);
244        $arTitles = array();
245        foreach ($arPosts as $post) {
246            $arTitles[$post->id] = $post->{Services_Blogging_Post::TITLE};
247        }
248        return $arTitles;
249    }//public function getRecentPostTitles($number = 15)
250
251
252
253    /**
254    * Returns an array of strings thay define
255    * the properties that a post to this blog may
256    * have.
257    *
258    * @param string $strPostType Type of post to create.
259    *
260    * @return array Array of strings
261    *
262    * @see getSupportedPostTypes()
263    */
264    public function getSupportedPostProperties($strPostType = 'post')
265    {
266        return $this->arSupportedPostProperties;
267    }//public function getSupportedPostProperties(..)
268
269
270
271    /**
272    * Checks if the given property name/id is supported
273    * for this driver.
274    *
275    * @param string $strProperty Property name/id to check
276    * @param string $strPostType Type of post to create.
277    *
278    * @return boolean If the property is supported
279    *
280    * @see getSupportedPostTypes()
281    */
282    public function isPostPropertySupported($strProperty, $strPostType = 'post')
283    {
284        return in_array($strProperty, $this->arSupportedPostProperties);
285    }//public function isPostPropertySupported(..)
286
287
288
289    /**
290    * Converts a struct returned by the webservice to
291    * a Services_Blogging_Post object
292    *
293    * @param array $arStruct Struct to convert
294    *
295    * @return Services_Blogging_Post Converted post
296    */
297    protected function convertStructToPost($arStruct)
298    {
299        $post = new Services_Blogging_Post($this);
300
301        $post->{Services_Blogging_Post::CONTENT} = $arStruct['description'];
302        $post->{Services_Blogging_Post::TITLE}   = $arStruct['title'];
303        //0123456789012345678
304        //20060514T09:19:33
305        $post->{Services_Blogging_Post::DATE} = mktime(
306            substr($arStruct['dateCreated'],  9, 2), //hour
307            substr($arStruct['dateCreated'], 12, 2), //minute
308            substr($arStruct['dateCreated'], 15, 2), //second
309            substr($arStruct['dateCreated'],  4, 2), //month
310            substr($arStruct['dateCreated'],  6, 2), //day
311            substr($arStruct['dateCreated'],  0, 4)  //year
312        );
313
314        $post->{Services_Blogging_Post::URL} = $arStruct['link'];
315
316        if (!isset($arStruct['categories'])) {
317            $arStruct['categories'] = array();
318        }
319        $post->{Services_Blogging_Post::CATEGORIES} = $arStruct['categories'];
320        $post->setId($arStruct['postid']);
321
322        return $post;
323    }//protected function convertStructToPost($arStruct)
324
325
326
327    /**
328    * Converts Services_Blogging_Post object to
329    * an XML-RPC struct that can be sent to the server.
330    *
331    * @param Services_Blogging_Post $post Post object to convert
332    *
333    * @return void
334    */
335    protected function convertPostToStruct($post)
336    {
337        $time = $post->{Services_Blogging_Post::DATE};
338        if ($time == ''  || $time == 0) {
339            $time = time();
340        }
341        $categories = $post->{Services_Blogging_Post::CATEGORIES};
342        if (!is_array($categories)) {
343            $categories = array();
344        } else {
345            $catstr     = $categories;
346            $categories = array();
347            foreach ($catstr as $cat) {
348                $categories[] = new XML_RPC_Value($cat, 'string');
349            }
350        }
351
352        return new XML_RPC_Value(
353            array(
354                'categories'  => new XML_RPC_Value($categories, 'array'),
355                'dateCreated' => new XML_RPC_Value(
356                    date('Ymd\\TH:i:s', $time), 'dateTime.iso8601'
357                ),
358                'description' => new XML_RPC_Value(
359                    $post->{Services_Blogging_Post::CONTENT}, 'string'
360                ),
361                'title'       => new XML_RPC_Value(
362                    $post->{Services_Blogging_Post::TITLE}, 'string'
363                )
364            ),
365            'struct'
366        );
367    }//protected function convertPostToStruct($post)
368
369}//class Services_Blogging_Driver_MetaWeblog extends Services_Blogging_ExtendedDriver
370?>