| [ Index ] |
|
Code source de b2evolution 2.1.0-beta |
1 <?php 2 /** 3 * XML-RPC APIs 4 * 5 * This file implements the following XML-RPC remote procedures, to be called by remote clients: 6 * - the B2 API for b2evo (this is used by w.bloggar for example...) 7 * - the BLOGGER API for b2evo, see {@link http://www.blogger.com/developers/api/1_docs/} 8 * - Metaweblog API 9 * - Movable Type API (partial) 10 * 11 * b2evolution - {@link http://b2evolution.net/} 12 * Released under GNU GPL License - {@link http://b2evolution.net/about/license.html} 13 * @copyright (c)2003-2007 by Francois PLANQUE - {@link http://fplanque.net/} 14 * 15 * @package xmlsrv 16 * 17 * @version $Id: xmlrpc.php,v 1.139 2007/06/27 02:23:32 fplanque Exp $ 18 * 19 * @modified wendall911 at users dot sourceforge.net 20 */ 21 22 /** 23 * Initialize everything: 24 */ 25 26 // Disable Cookies 27 $_COOKIE = array(); 28 29 if( ! isset($HTTP_RAW_POST_DATA) ) 30 { 31 $HTTP_RAW_POST_DATA = implode("\r\n", file('php://input')); 32 } 33 // Trim requests (used by XML-RPC library); fix for mozBlog and other cases where '<?xml' isn't on the very first line 34 $HTTP_RAW_POST_DATA = trim( $HTTP_RAW_POST_DATA ); 35 36 37 /** 38 * Set to TRUE to do HTML sanity checking as in the browser interface, set to 39 * FALSE if you trust the editing tool to do this (more features than the 40 * browser interface) 41 * @todo fp> have a global setting with 3 options: check|nocheck|userdef => each then has his own setting to define if his tool does the checking or not. 42 * fp> Also, there should be a permission to say if members of a given group can or cannot post insecure content. If they cannot, then they cannot disable the sanity check 43 * fp> note: if allowed unsecure posting, disabling the sanity cjecker should also be allowed in the html backoffice 44 */ 45 $xmlrpc_htmlchecking = true; 46 require_once dirname(__FILE__).'/../conf/_config.php'; 47 require_once $inc_path.'_main.inc.php'; 48 load_funcs('_ext/xmlrpc/_xmlrpc.php'); 49 load_class('items/model/_itemlist.class.php'); 50 51 if( CANUSEXMLRPC !== TRUE ) 52 { // We cannot use XML-RPC: send a error response ( "1 Unknown method" ). 53 //this should be structured as an xml response 54 $errResponse = new xmlrpcresp( 0, 1, 'Cannot use XML-RPC. Probably the server is missing the XML extension. Error: '.CANUSEXMLRPC ); 55 die( $errResponse->serialize() ); 56 } 57 58 59 // Handle "Really Simple Discovery": 60 if ( isset( $_GET['rsd'] ) ) { // http://archipelago.phrasewise.com/rsd 61 header('Content-type: text/xml; charset=' . $evo_charset, true); 62 63 ?> 64 <?php echo '<?xml version="1.0" encoding="'.$evo_charset.'"?'.'>'; ?> 65 <rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd"> 66 <service> 67 <engineName>b2evolution</engineName> 68 <engineLink>http://b2evolution.net/</engineLink> 69 <homePageLink><?php echo $baseurl ?></homePageLink> 70 <apis> 71 <api name="Movable Type" preferred="false" apiLink="<?php echo $xmlsrv_url; ?>xmlrpc.php" /> 72 <api name="MetaWeblog" preferred="true" apiLink="<?php echo $xmlsrv_url; ?>xmlrpc.php" /> 73 <api name="Blogger" preferred="false" apiLink="<?php echo $xmlsrv_url; ?>xmlrpc.php" /> 74 </apis> 75 </service> 76 </rsd> 77 <?php 78 exit; 79 } 80 81 // We can't display standard error messages. We must return XMLRPC responses. 82 $DB->halt_on_error = false; 83 $DB->show_errors = false; 84 85 $post_default_title = ''; // posts submitted via the xmlrpc interface get that title 86 87 /** 88 * Used for logging, only if {@link $debug_xmlrpc_logging} is true 89 * 90 * @return boolean Have we logged? 91 */ 92 function logIO($io,$msg) 93 { 94 global $debug_xmlrpc_logging; 95 96 if( ! $debug_xmlrpc_logging ) 97 { 98 return false; 99 } 100 101 $date = date('Y-m-d H:i:s '); 102 $iot = ($io == 'I') ? ' Input: ' : ' Output: '; 103 104 $fp = fopen( dirname(__FILE__).'/xmlrpc.log', 'a+' ); 105 fwrite($fp, $date.$iot.$msg."\n"); 106 fclose($fp); 107 108 return true; 109 } 110 111 112 /** 113 * Returns a string replaced by stars, for passwords. 114 * 115 * @param string the source string 116 * @return string same length, but only stars 117 */ 118 function starify( $string ) 119 { 120 return str_repeat( '*', strlen( $string ) ); 121 } 122 123 $b2newpost_doc='Adds a post, blogger-api like, +title +category +postdate'; 124 $b2newpost_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcBoolean, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 125 /** 126 * b2.newPost. Adds a post, blogger-api like, +title +category +postdate. 127 * 128 * b2 API 129 * 130 * @param xmlrpcmsg XML-RPC Message 131 * 0 ? 132 * 1 ? 133 * 2 username (string): Login for a Blogger user who is member of the blog. 134 * 3 password (string): Password for said username. 135 * 4 content (string): The content of the post. 136 * 5 publish (boolean): If set to true, the post will be published immediately. 137 * 6 title (string): The title of the post. 138 * 7 category (string): The internal name of the category you want to post the post into. 139 * 8 date (string): This is the date that will be shown in the post, give "" for current date. 140 * @return xmlrpcresp XML-RPC Response 141 */ 142 function b2newpost($m) 143 { 144 global $xmlrpcerruser; // import user errcode value 145 global $DB; 146 global $Settings, $Messages; 147 148 $username = $m->getParam(2); 149 $username = $username->scalarval(); 150 151 $password = $m->getParam(3); 152 $password = $password->scalarval(); 153 154 $content = $m->getParam(4); 155 $content = $content->scalarval(); 156 157 $post_title = $m->getParam(6); 158 $post_title = $post_title->scalarval(); 159 160 $category = $m->getParam(7); 161 $category = $category->scalarval(); 162 163 $postdate = $m->getParam(8); 164 $postdate = $postdate->scalarval(); 165 166 if( ! user_pass_ok($username,$password) ) 167 { 168 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 169 'Wrong username/password combination '.$username.' / '.starify($password)); 170 } 171 172 $UserCache = & get_Cache( 'UserCache' ); 173 $current_User = & $UserCache->get_by_login( $username ); 174 175 // Check if category exists 176 if( get_the_category_by_ID( $category, false ) === false ) 177 { // Cat does not exist: 178 return new xmlrpcresp(0, $xmlrpcerruser+5, 'Requested category does not exist.'); // user error 5 179 } 180 181 $blog_ID = get_catblog($category); 182 183 // Check permission: 184 if( ! $current_User->check_perm( 'blog_post_statuses', 'published', false, $blog_ID ) ) 185 { 186 return new xmlrpcresp(0, $xmlrpcerruser+2, 'Permission denied.'); // user error 2 187 } 188 189 if( $postdate != '' ) 190 { 191 $now = $postdate; 192 } 193 else 194 { 195 $now = date('Y-m-d H:i:s', (time() + $Settings->get('time_difference'))); 196 } 197 198 // CHECK and FORMAT content 199 $post_title = format_to_post($post_title, 0, 0); 200 $content = format_to_post($content, 0, 0); 201 202 if( $errstring = $Messages->get_string( 'Cannot post, please correct these errors:', '' ) ) 203 { 204 return new xmlrpcresp(0, $xmlrpcerruser+6, $errstring ); // user error 6 205 } 206 207 // INSERT NEW POST INTO DB: 208 $edited_Item = & new Item(); 209 $post_ID = $edited_Item->insert( $current_User->ID, $post_title, $content, $now, $category, array(), 'published', $current_User->locale ); 210 if( $DB->error ) 211 { // DB error 212 return new xmlrpcresp(0, $xmlrpcerruser+9, 'DB error: '.$DB->last_error ); // user error 9 213 } 214 215 logIO( 'O', 'Handling notifications...' ); 216 // Execute or schedule notifications & pings: 217 $edited_Item->handle_post_processing(); 218 219 return new xmlrpcresp(new xmlrpcval($post_ID)); 220 } 221 222 223 224 225 $b2getcategories_doc='given a blogID, gives a struct that list categories in that blog, using categoryID and categoryName. categoryName is there so the user would choose a category name from the client, rather than just a number. however, when using b2.newPost, only the category ID number should be sent.'; 226 $b2getcategories_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 227 /** 228 * b2.getCategories 229 * 230 * B2 API 231 * 232 * Gets also used for mt.getCategoryList. Is this correct? 233 * 234 * @param xmlrpcmsg XML-RPC Message 235 * 0 blogid (string): Unique identifier of the blog to query 236 * 1 username (string): Login for a Blogger user who is member of the blog. 237 * 2 password (string): Password for said username. 238 * @return xmlrpcresp XML-RPC Response 239 */ 240 function b2getcategories( $m ) 241 { 242 return _b2_or_mt_get_categories('b2', $m); 243 } 244 245 246 247 248 $b2_getPostURL_doc = 'Given a blog ID, username, password, and a post ID, returns the URL to that post.'; 249 $b2_getPostURL_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 250 /** 251 * b2.getPostURL 252 * 253 * B2 API 254 * 255 * @param xmlrpcmsg XML-RPC Message 256 * 0 blogid (string): Unique identifier of the blog to query 257 * 1 ? 258 * 2 username (string): Login for a Blogger user who is member of the blog. 259 * 3 password (string): Password for said username. 260 * 4 post_ID (string): Post to query 261 * @return xmlrpcresp XML-RPC Response 262 */ 263 function b2_getPostURL($m) 264 { 265 global $xmlrpcerruser; 266 global $siteurl; 267 268 $blog_ID = $m->getParam(0); 269 $blog_ID = $blog_ID->scalarval(); 270 271 $username = $m->getParam(2); 272 $username = $username->scalarval(); 273 274 $password = $m->getParam(3); 275 $password = $password->scalarval(); 276 277 $post_ID = $m->getParam(4); 278 $post_ID = intval($post_ID->scalarval()); 279 280 if( ! user_pass_ok($username, $password) ) 281 { 282 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 283 'Wrong username/password combination '.$username.' / '.starify($password)); 284 } 285 286 $UserCache = & get_Cache( 'UserCache' ); 287 $current_User = & $UserCache->get_by_login( $username ); 288 289 // Check permission: 290 if( ! $current_User->check_perm( 'blog_ismember', 1, false, $blog_ID ) ) 291 { 292 return new xmlrpcresp(0, $xmlrpcerruser+2, // user error 2 293 'Permission denied.' ); 294 } 295 296 $ItemCache = & get_Cache( 'ItemCache' ); 297 if( ( $Item = & $ItemCache->get_by_ID( $post_ID ) ) === false ) 298 { // Post does not exist 299 return new xmlrpcresp(0, $xmlrpcerruser+7, 300 'This post ID ('.$post_ID.') does not correspond to any post here.' ); 301 } 302 303 return new xmlrpcresp( new xmlrpcval( $Item->get_permanent_url() ) ); 304 } 305 306 307 308 309 $bloggernewpost_doc = 'Adds a post, blogger-api like'; 310 $bloggernewpost_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcBoolean)); 311 /** 312 * blogger.newPost makes a new post to a designated blog. 313 * 314 * BLOGGER API 315 * 316 * Optionally, will publish the blog after making the post. (In b2evo, this means the 317 * new post will be in 'published' state). 318 * On success, it returns the unique ID of the new post (usually a seven-digit number 319 * at this time). 320 * On error, it will return some error message. 321 * 322 * @see http://www.blogger.com/developers/api/1_docs/xmlrpc_newPost.html 323 * 324 * @param xmlrpcmsg XML-RPC Message 325 * 0 appkey (string): Unique identifier/passcode of the application sending the post. 326 * (See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .) 327 * 1 blogid (string): Unique identifier of the blog the post will be added to. 328 * Currently ignored in b2evo, in favor of the category. 329 * 2 username (string): Login for a Blogger user who has permission to post to the blog. 330 * 3 password (string): Password for said username. 331 * 4 content (string): Contents of the post. 332 * 5 publish (boolean): If true, the blog will be published immediately after the 333 * post is made. (In b2evo,this means, the new post will be in 'published' state, 334 * otherwise it would be in draft state). 335 * @return xmlrpcresp XML-RPC Response 336 */ 337 function bloggernewpost( $m ) 338 { 339 global $xmlrpcerruser; // import user errcode value 340 global $DB; 341 global $Settings, $Messages; 342 343 logIO('I','Called function: blogger.newPost'); 344 345 $username = $m->getParam(2); 346 $password = $m->getParam(3); 347 $content = $m->getParam(4); 348 $publish = $m->getParam(5); 349 350 $username = $username->scalarval(); 351 $password = $password->scalarval(); 352 $content = $content->scalarval(); 353 $publish = $publish->scalarval(); 354 $status = $publish ? 'published' : 'draft'; 355 logIO('I',"Publish: $publish -> Status: $status"); 356 357 if( !user_pass_ok($username,$password) ) 358 { 359 logIO('O', "Wrong username/password combination <strong>$username / $password</strong>"); 360 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 361 'Wrong username/password combination '.$username.' / '.starify($password)); 362 } 363 364 $UserCache = & get_Cache( 'UserCache' ); 365 $current_User = & $UserCache->get_by_login( $username ); 366 367 $post_categories = xmlrpc_getpostcategories($content); 368 369 if( ! $post_categories ) 370 { // There were no categories passed in the content: 371 return new xmlrpcresp(0, $xmlrpcerruser+5, 'No category given.'); // user error 5 372 } 373 374 $main_cat = array_shift($post_categories); 375 logIO('I', 'Main cat: '.$main_cat); 376 377 // Check if category exists 378 if( get_the_category_by_ID( $main_cat, false ) === false ) 379 { // Cat does not exist: 380 // fp> TODO use $Blog->get_default_cat_ID(); 381 return new xmlrpcresp(0, $xmlrpcerruser+5, 'Requested category does not exist.'); // user error 5 382 } 383 384 $blog_ID = get_catblog($main_cat); 385 386 // Check permission: 387 if( ! $current_User->check_perm( 'blog_post_statuses', $status, false, $blog_ID ) ) 388 { 389 return new xmlrpcresp(0, $xmlrpcerruser+2, 'Permission denied.'); // user error 2 390 } 391 392 // Extract <title> from content 393 $post_title = xmlrpc_getposttitle( $content ); 394 395 // cleanup content from extra tags like <category> and <title>: 396 $content = xmlrpc_removepostdata( $content ); 397 398 $now = date('Y-m-d H:i:s', (time() + $Settings->get('time_difference'))); 399 400 // CHECK and FORMAT content 401 $post_title = format_to_post($post_title,0,0); 402 $content = format_to_post($content,0,0); 403 404 if( $errstring = $Messages->get_string( 'Cannot post, please correct these errors:', '' ) ) 405 { 406 return new xmlrpcresp(0, $xmlrpcerruser+6, $errstring ); // user error 6 407 } 408 409 // INSERT NEW POST INTO DB: 410 $edited_Item = & new Item(); 411 $edited_Item->set('title', $post_title); 412 $edited_Item->set('content', $content); 413 $edited_Item->set('datestart', $now); 414 $edited_Item->set('main_cat_ID', $main_cat); 415 $edited_Item->set('extra_cat_IDs', $post_categories); 416 $edited_Item->set('status', $status); 417 $edited_Item->set('locale', $current_User->locale ); 418 $edited_Item->set_creator_User($current_User); 419 $edited_Item->dbinsert(); 420 421 if( ! $edited_Item->ID ) 422 { // DB error 423 return new xmlrpcresp(0, $xmlrpcerruser+9, 'Error while inserting item: '.$DB->last_error ); // user error 9 424 } 425 426 logIO('O', "Posted ! ID: $edited_Item->ID"); 427 428 logIO( 'O', 'Handling notifications...' ); 429 // Execute or schedule notifications & pings: 430 $edited_Item->handle_post_processing(); 431 432 logIO("O","All done."); 433 434 return new xmlrpcresp(new xmlrpcval($edited_Item->ID)); 435 } 436 437 438 $bloggereditpost_doc='Edits a post, blogger-api like'; 439 $bloggereditpost_sig=array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcBoolean)); 440 /** 441 * blogger.editPost changes the contents of a given post. 442 * 443 * Optionally, will publish the blog the post belongs to after changing the post. 444 * (In b2evo, this means the changed post will be moved to published state). 445 * On success, it returns a boolean true value. 446 * On error, it will return a fault with an error message. 447 * 448 * @see http://www.blogger.com/developers/api/1_docs/xmlrpc_editPost.html 449 * 450 * @param xmlrpcmsg XML-RPC Message 451 * 0 appkey (string): Unique identifier/passcode of the application sending the post. 452 * (See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .) 453 * 1 postid (string): Unique identifier of the post to be changed. 454 * 2 username (string): Login for a Blogger user who has permission to edit the given 455 * post (either the user who originally created it or an admin of the blog). 456 * 3 password (string): Password for said username. 457 * 4 content (string): New content of the post. 458 * 5 publish (boolean): If true, the blog will be published immediately after the 459 * post is made. (In b2evo,this means, the new post will be in 'published' state, 460 * otherwise it would be in draft state). 461 * @return xmlrpcresp XML-RPC Response 462 * 463 * @todo check current status and permission on it 464 */ 465 function bloggereditpost($m) 466 { 467 global $xmlrpcerruser; // import user errcode value 468 global $DB; 469 global $Messages; 470 471 logIO('I','Called function: blogger.editPost'); 472 473 // return new xmlrpcresp(0, $xmlrpcerruser+50, 'bloggereditpost' ); 474 475 $post_ID = $m->getParam(1); 476 $post_ID = $post_ID->scalarval(); 477 478 $username = $m->getParam(2); 479 $username = $username->scalarval(); 480 481 $password = $m->getParam(3); 482 $password = $password->scalarval(); 483 484 if( !user_pass_ok($username, $password) ) 485 { 486 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 487 'Wrong username/password combination '.$username.' / '.starify($password)); 488 } 489 490 $ItemCache = & get_Cache( 'ItemCache' ); 491 if( ! ($edited_Item = & $ItemCache->get_by_ID( $post_ID ) ) ) 492 { 493 return new xmlrpcresp(0, $xmlrpcerruser+7, "No such post (#$post_ID)."); // user error 7 494 } 495 496 $content = $m->getParam(4); 497 $content = $content->scalarval(); 498 $content = str_replace("\n",'',$content); // Tor - kludge to fix bug in xmlrpc libraries 499 // WARNING: the following debug MAY produce a non valid response (XML comment containing emebedded <!-- more -->) 500 // xmlrpc_debugmsg( 'New content: '.$content ); 501 502 $publish = $m->getParam(5); 503 $publish = $publish->scalarval(); 504 $status = $publish ? 'published' : 'draft'; 505 logIO('I',"Publish: $publish -> Status: $status"); 506 507 $UserCache = & get_Cache( 'UserCache' ); 508 $current_User = & $UserCache->get_by_login( $username ); 509 510 $post_categories = xmlrpc_getpostcategories($content); 511 if( $post_categories ) 512 { 513 $main_cat = array_shift($post_categories); 514 515 if( get_the_category_by_ID( $main_cat, false ) === false ) 516 { // requested Cat does not exist: 517 return new xmlrpcresp(0, $xmlrpcerruser+5, 'Requested main category does not exist.'); // user error 5 518 } 519 } 520 else 521 { 522 $main_cat = $edited_Item->main_cat_ID; 523 } 524 525 $blog_ID = get_catblog($main_cat); 526 527 // Check permission: 528 if( ! $current_User->check_perm( 'blog_post_statuses', $status, false, $blog_ID ) ) 529 { 530 return new xmlrpcresp(0, $xmlrpcerruser+2, // user error 2 531 'Permission denied.' ); 532 } 533 534 $post_title = xmlrpc_getposttitle($content); 535 $content = xmlrpc_removepostdata($content); 536 537 // CHECK and FORMAT content 538 $post_title = format_to_post($post_title,0,0); 539 $content = format_to_post($content,0,0); 540 541 if( $errstring = $Messages->get_string( 'Cannot update post, please correct these errors:', '' ) ) 542 { 543 return new xmlrpcresp(0, $xmlrpcerruser+6, $errstring ); // user error 6 544 } 545 546 // UPDATE POST IN DB: 547 $edited_Item->set( 'title', $post_title ); 548 $edited_Item->set( 'content', $content ); 549 if( $post_categories ) 550 { // update cats, if given: 551 $edited_Item->set( 'main_cat_ID', $main_cat ); 552 $edited_Item->set( 'extra_cat_IDs', array($post_categories) ); 553 } 554 $edited_Item->set( 'status', $status ); 555 $edited_Item->dbupdate(); 556 557 if( $DB->error ) 558 { // DB error 559 return new xmlrpcresp(0, $xmlrpcerruser+9, 'DB error: '.$DB->last_error ); // user error 9 560 } 561 562 logIO( 'O', 'Handling notifications...' ); 563 // Execute or schedule notifications & pings: 564 $edited_Item->handle_post_processing(); 565 566 return new xmlrpcresp(new xmlrpcval("1", "boolean")); 567 } 568 569 570 571 572 $bloggerdeletepost_doc = 'Deletes a post, blogger-api like'; 573 $bloggerdeletepost_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 574 /** 575 * blogger.editPost deletes a given post. 576 * 577 * This API call is not documented on 578 * {@link http://www.blogger.com/developers/api/1_docs/} 579 * 580 * @param xmlrpcmsg XML-RPC Message 581 * 0 appkey (string): Unique identifier/passcode of the application sending the post. 582 * (See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .) 583 * 1 postid (string): Unique identifier of the post to be deleted. 584 * 2 username (string): Login for a Blogger user who has permission to edit the given 585 * post (either the user who originally created it or an admin of the blog). 586 * 3 password (string): Password for said username. 587 * @return xmlrpcresp XML-RPC Response 588 */ 589 function bloggerdeletepost($m) 590 { 591 global $xmlrpcerruser; // import user errcode value 592 global $DB; 593 594 $post_ID = $m->getParam(1); 595 $post_ID = $post_ID->scalarval(); 596 // logIO("O","finished getting post_id ...".$post_ID); 597 598 $username = $m->getParam(2); 599 $username = $username->scalarval(); 600 601 $password = $m->getParam(3); 602 $password = $password->scalarval(); 603 604 if( ! user_pass_ok( $username, $password ) ) 605 { 606 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 607 'Wrong username/password combination '.$username.' / '.starify($password)); 608 } 609 610 $ItemCache = & get_Cache( 'ItemCache' ); 611 if( ! ($edited_Item = & $ItemCache->get_by_ID( $post_ID, false ) ) ) 612 { 613 return new xmlrpcresp(0, $xmlrpcerruser+7, 'No such post.'); // user error 7 614 } 615 616 $UserCache = & get_Cache( 'UserCache' ); 617 $current_User = & $UserCache->get_by_login( $username ); 618 619 // Check permission: 620 if( ! $current_User->check_perm( 'blog_del_post', 'any', false, $edited_Item->blog_ID ) ) 621 { 622 return new xmlrpcresp(0, $xmlrpcerruser+2, // user error 2 623 'Permission denied.'); 624 } 625 626 // DELETE POST FROM DB: 627 $edited_Item->dbdelete(); 628 if( $DB->error ) 629 { // DB error 630 return new xmlrpcresp(0, $xmlrpcerruser+9, 'DB error: '.$DB->last_error ); // user error 9 631 } 632 633 return new xmlrpcresp(new xmlrpcval(1)); 634 } 635 636 637 638 $bloggergetusersblogs_doc='returns the user\'s blogs - this is a dummy function, just so that BlogBuddy and other blogs-retrieving apps work'; 639 $bloggergetusersblogs_sig=array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 640 /** 641 * blogger.getUsersBlogs returns information about all the blogs a given user is a member of. 642 * 643 * Data is returned as an array of <struct>'s containing the ID (blogid), name (blogName), 644 * and URL (url) of each blog. 645 * 646 * Non official: Also return a boolean stating wether or not the user can edit th eblog templates 647 * (isAdmin). 648 * 649 * see {@link http://www.blogger.com/developers/api/1_docs/xmlrpc_getUsersBlogs.html} 650 * 651 * @param xmlrpcmsg XML-RPC Message 652 * 0 appkey (string): Unique identifier/passcode of the application sending the post. 653 * (See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .) 654 * 1 username (string): Login for the Blogger user who's blogs will be retrieved. 655 * 2 password (string): Password for said username. 656 * (currently not required by b2evo) 657 * @return xmlrpcresp XML-RPC Response, an array of <struct>'s containing for each blog: 658 * - ID (blogid), 659 * - name (blogName), 660 * - URL (url), 661 * - bool: can user edit template? (isAdmin). 662 */ 663 function bloggergetusersblogs($m) 664 { 665 global $xmlrpcerruser; 666 global $baseurl; 667 668 $username = $m->getParam(1); 669 $username = $username->scalarval(); 670 671 $password = $m->getParam(2); 672 $password = $password->scalarval(); 673 logIO("O","entered bloggergetusersblogs."); 674 675 676 if( ! user_pass_ok($username,$password) ) 677 { 678 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 679 'Wrong username/password combination '.$username.' / '.starify($password)); 680 } 681 logIO("O","user approved."); 682 683 684 $UserCache = & get_Cache( 'UserCache' ); 685 $current_User = & $UserCache->get_by_login( $username ); 686 logIO("O","Got Current user (ID ".$current_User->ID.')'); 687 688 689 $resp_array = array(); 690 691 $BlogCache = & get_Cache( 'BlogCache' ); 692 693 $blog_array = $BlogCache->load_user_blogs( 'blog_ismember', 'view', $current_User->ID, 'ID' ); 694 695 foreach( $blog_array as $l_blog_ID ) 696 { // Loop through all blogs that match the requested permission: 697 698 /** 699 * @var Blog 700 */ 701 $l_Blog = & $BlogCache->get_by_ID( $l_blog_ID ); 702 703 logIO("O","Current user IS a member of this blog.".$l_blog_ID); 704 705 $resp_array[] = new xmlrpcval( array( 706 "blogid" => new xmlrpcval( $l_blog_ID ), 707 "blogName" => new xmlrpcval( $l_Blog->get('shortname') ), 708 "url" => new xmlrpcval( $l_Blog->gen_blogurl() ), 709 "isAdmin" => new xmlrpcval( $current_User->check_perm( 'templates', 'any' ), 'boolean') 710 ), 'struct'); 711 } 712 713 $resp = new xmlrpcval($resp_array, 'array'); 714 715 return new xmlrpcresp($resp); 716 } 717 718 719 720 721 $bloggergetuserinfo_doc='gives the info about a user'; 722 $bloggergetuserinfo_sig=array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 723 /** 724 * blogger.getUserInfo returns returns a struct containing user info. 725 * 726 * Data returned: userid, firstname, lastname, nickname, email, and url. 727 * 728 * see {@link http://www.blogger.com/developers/api/1_docs/xmlrpc_getUserInfo.html} 729 * 730 * @param xmlrpcmsg XML-RPC Message 731 * 0 appkey (string): Unique identifier/passcode of the application sending the post. 732 * (See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .) 733 * 1 username (string): Login for the Blogger user who's blogs will be retrieved. 734 * 2 password (string): Password for said username. 735 * (currently not required by b2evo) 736 * @return xmlrpcresp XML-RPC Response, a <struct> containing: 737 * - userid, 738 * - firstname, 739 * - lastname, 740 * - nickname, 741 * - email, 742 * - url 743 */ 744 function bloggergetuserinfo($m) 745 { 746 global $xmlrpcerruser; 747 748 $username = $m->getParam(1); 749 $username = $username->scalarval(); 750 751 $password = $m->getParam(2); 752 $password = $password->scalarval(); 753 754 $UserCache = & get_Cache( 'UserCache' ); 755 $User = & $UserCache->get_by_login( $username ); 756 757 if( user_pass_ok( $username, $password) ) 758 { 759 $struct = new xmlrpcval( array( 760 'nickname' => new xmlrpcval( $User->get('nickname') ), 761 'userid' => new xmlrpcval( $User->ID ), 762 'url' => new xmlrpcval( $User->get('url') ), 763 'email' => new xmlrpcval( $User->get('email') ), 764 'lastname' => new xmlrpcval( $User->get('lastname') ), 765 'firstname' => new xmlrpcval( $User->get('firstname') ) 766 ), 'struct' ); 767 $resp = $struct; 768 return new xmlrpcresp($resp); 769 } 770 else 771 { 772 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 773 'Wrong username/password combination '.$username.' / '.starify($password)); 774 } 775 } 776 777 778 779 780 $bloggergetpost_doc = 'fetches a post, blogger-api like'; 781 $bloggergetpost_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 782 /** 783 * blogger.getPost retieves a given post. 784 * 785 * This API call is not documented on 786 * {@link http://www.blogger.com/developers/api/1_docs/} 787 * 788 * @param xmlrpcmsg XML-RPC Message 789 * 0 appkey (string): Unique identifier/passcode of the application sending the post. 790 * (See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .) 791 * 1 postid (string): Unique identifier of the post to be deleted. 792 * 2 username (string): Login for a Blogger user who has permission to edit the given 793 * post (either the user who originally created it or an admin of the blog). 794 * 3 password (string): Password for said username. 795 * @return xmlrpcresp XML-RPC Response 796 */ 797 function bloggergetpost($m) 798 { 799 global $xmlrpcerruser; 800 801 $post_ID = $m->getParam(1); 802 $post_ID = $post_ID->scalarval(); 803 804 $username = $m->getParam(2); 805 $username = $username->scalarval(); 806 807 $password = $m->getParam(3); 808 $password = $password->scalarval(); 809 810 if( user_pass_ok($username,$password) ) 811 { 812 $postdata = get_postdata($post_ID); 813 814 if( $postdata['Date'] != '' ) 815 { 816 $post_date = mysql2date("U", $postdata["Date"]); 817 $post_date = gmdate("Ymd", $post_date)."T".gmdate("H:i:s", $post_date); 818 819 $content = "<title>".$postdata["Title"]."</title>"; 820 $content .= "<category>".$postdata["Category"]."</category>"; 821 $content .= $postdata["Content"]; 822 823 $struct = new xmlrpcval(array("userid" => new xmlrpcval($postdata["Author_ID"]), 824 "dateCreated" => new xmlrpcval($post_date,"dateTime.iso8601"), 825 "content" => new xmlrpcval($content), 826 "postid" => new xmlrpcval($postdata["ID"]) 827 ),"struct"); 828 829 $resp = $struct; 830 return new xmlrpcresp($resp); 831 } 832 else 833 { 834 return new xmlrpcresp(0, $xmlrpcerruser+7, // user error 7 835 "No such post #$post_ID"); 836 } 837 } 838 else 839 { 840 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 841 'Wrong username/password combination '.$username.' / '.starify($password)); 842 } 843 } 844 845 846 847 848 $bloggergetrecentposts_doc = 'fetches X most recent posts, blogger-api like'; 849 $bloggergetrecentposts_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcInt)); 850 /** 851 * blogger.getRecentPosts retieves X most recent posts. 852 * 853 * This API call is not documented on 854 * {@link http://www.blogger.com/developers/api/1_docs/} 855 * 856 * @param xmlrpcmsg XML-RPC Message 857 * 0 appkey (string): Unique identifier/passcode of the application sending the post. 858 * (See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .) 859 * 1 blogid (string): Unique identifier of the blog the post will be added to. 860 * Currently ignored in b2evo, in favor of the category. 861 * 2 username (string): Login for a Blogger user who has permission to edit the given 862 * post (either the user who originally created it or an admin of the blog). 863 * 3 password (string): Password for said username. 864 * 4 numposts (integer): number of posts to retrieve. 865 * @return xmlrpcresp XML-RPC Response 866 */ 867 function bloggergetrecentposts( $m ) 868 { 869 global $xmlrpcerruser, $DB; 870 871 $blog_ID = $m->getParam(1); 872 $blog_ID = $blog_ID->scalarval(); 873 874 $username = $m->getParam(2); 875 $username = $username->scalarval(); 876 877 $password = $m->getParam(3); 878 $password = $password->scalarval(); 879 880 $numposts = $m->getParam(4); 881 $numposts = $numposts->scalarval(); 882 883 if( ! user_pass_ok($username, $password) ) 884 { 885 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 886 'Wrong username/password combination '.$username.' / '.starify($password)); 887 } 888 889 $UserCache = & get_Cache( 'UserCache' ); 890 $current_User = & $UserCache->get_by_login( $username ); 891 892 // Check permission: 893 if( ! $current_User->check_perm( 'blog_ismember', 1, false, $blog_ID ) ) 894 { 895 return new xmlrpcresp(0, $xmlrpcerruser+2, 'Permission denied.' ); // user error 2 896 } 897 898 899 $BlogCache = & get_Cache( 'BlogCache' ); 900 $Blog = & $BlogCache->get_by_ID( $blog_ID ); 901 902 // Get the posts to display: 903 $MainList = & new ItemList2( $Blog, NULL, NULL, $numposts ); 904 905 $MainList->set_filters( array( 906 'visibility_array' => array( 'published', 'protected', 'private', 'draft', 'deprecated', 'redirected' ), 907 'order' => 'DESC', 908 'unit' => 'posts', 909 ) ); 910 911 // Run the query: 912 $MainList->query(); 913 914 915 xmlrpc_debugmsg( 'Items:'.$MainList->result_num_rows ); 916 917 $data = array(); 918 while( $Item = & $MainList->get_item() ) 919 { 920 xmlrpc_debugmsg( 'Item:'.$Item->title. 921 ' - Issued: '.$Item->issue_date. 922 ' - Modified: '.$Item->mod_date ); 923 924 $post_date = mysql2date("U", $Item->issue_date); 925 $post_date = gmdate("Ymd", $post_date)."T".gmdate("H:i:s", $post_date); 926 927 $content = '<title>'.$Item->title.'</title>'; 928 $content .= '<category>'.$Item->main_cat_ID.'</category>'; 929 $content .= $Item->content; 930 931 // Load Item's creator User: 932 $Item->get_creator_User(); 933 $authorname = $Item->creator_User->get('preferredname'); 934 935 $data[] = new xmlrpcval(array( 936 "authorName" => new xmlrpcval($authorname), 937 "userid" => new xmlrpcval($Item->creator_user_ID), 938 "dateCreated" => new xmlrpcval($post_date,"dateTime.iso8601"), 939 "content" => new xmlrpcval($content), 940 "postid" => new xmlrpcval($Item->ID) 941 ),"struct"); 942 } 943 944 $resp = new xmlrpcval($data, "array"); 945 946 return new xmlrpcresp($resp); 947 948 } 949 950 951 952 953 954 $bloggergettemplate_doc = 'returns the default template file\'s code'; 955 $bloggergettemplate_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 956 /** 957 * blogger.getTemplate returns text of the main or archive index template for a given blog. 958 * 959 * Currently, in b2evo, this will return the templates of the 'custom' skin. 960 * 961 * see {@link http://www.blogger.com/developers/api/1_docs/xmlrpc_getTemplate.html} 962 * 963 * @param xmlrpcmsg XML-RPC Message 964 * 0 appkey (string): Unique identifier/passcode of the application sending the post. 965 * (See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .) 966 * 1 blogid (string): Unique identifier of the blog who's template is to be returned. 967 * 2 username (string): Login for a Blogger who has admin permission on given blog. 968 * 3 password (string): Password for said username. 969 * 4 templateType (string): Determines which of the blog's templates will be returned. 970 * Currently, either "main" or "archiveIndex". 971 * @return xmlrpcresp XML-RPC Response 972 */ 973 function bloggergettemplate($m) 974 { 975 global $xmlrpcerruser; 976 977 $blog_ID = $m->getParam(1); 978 $blog_ID = $blog_ID->scalarval(); 979 980 $username = $m->getParam(2); 981 $username = $username->scalarval(); 982 983 $password = $m->getParam(3); 984 $password = $password->scalarval(); 985 986 $templateType = $m->getParam(4); 987 $templateType = $templateType->scalarval(); 988 989 if( !user_pass_ok($username, $password) ) 990 { 991 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 992 'Wrong username/password combination '.$username.' / '.starify($password)); 993 } 994 995 $UserCache = & get_Cache( 'UserCache' ); 996 $current_User = & $UserCache->get_by_login( $username ); 997 998 // Check permission: 999 if( ! $current_User->check_perm( 'templates' ) ) 1000 { 1001 return new xmlrpcresp(0, $xmlrpcerruser+2, // user error 2 1002 'Permission denied.'); 1003 } 1004 1005 // Determine the edit folder: 1006 $edit_folder = $skins_path.'custom/'; 1007 1008 if ($templateType == "main") 1009 { 1010 $file = $edit_folder.'index.main.php'; 1011 } 1012 elseif ($templateType == "archiveIndex") 1013 { 1014 $file = $edit_folder.'_arcdir.php'; 1015 } 1016 else return; // TODO: handle this cleanly 1017 1018 $f = fopen($file,"r"); 1019 $content = fread($f,filesize($file)); 1020 fclose($file); 1021 1022 $content = str_replace("\n","\r\n",$content); // so it is actually editable with a windows/mac client, instead of being returned as a looooooooooong line of code 1023 1024 return new xmlrpcresp(new xmlrpcval("$content")); 1025 1026 } 1027 1028 1029 1030 $bloggersettemplate_doc = 'saves the default template file\'s code'; 1031 $bloggersettemplate_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 1032 /** 1033 * blogger.setTemplate changes the template for a given blog. 1034 * 1035 * Can change either main or archive index template. 1036 * 1037 * Currently, in b2evo, this will change the templates of the 'custom' skin. 1038 * 1039 * see {@link http://www.blogger.com/developers/api/1_docs/xmlrpc_getTemplate.html} 1040 * 1041 * @param xmlrpcmsg XML-RPC Message 1042 * 0 appkey (string): Unique identifier/passcode of the application sending the post. 1043 * (See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .) 1044 * 1 blogid (string): Unique identifier of the blog who's template is to be returned. 1045 * 2 username (string): Login for a Blogger who has admin permission on given blog. 1046 * 3 password (string): Password for said username. 1047 * 4 template (string): The text for the new template (usually mostly HTML). 1048 * 5 templateType (string): Determines which of the blog's templates will be returned. 1049 * Currently, either "main" or "archiveIndex". 1050 * @return xmlrpcresp XML-RPC Response 1051 */ 1052 function bloggersettemplate( $m ) 1053 { 1054 global $xmlrpcerruser, $blogfilename; 1055 1056 $blog_ID = $m->getParam(1); 1057 $blog_ID = $blog_ID->scalarval(); 1058 1059 $username = $m->getParam(2); 1060 $username = $username->scalarval(); 1061 1062 $password = $m->getParam(3); 1063 $password = $password->scalarval(); 1064 1065 $template = $m->getParam(4); 1066 $template = $template->scalarval(); 1067 1068 $templateType = $m->getParam(5); 1069 $templateType = $templateType->scalarval(); 1070 1071 if( !user_pass_ok($username, $password) ) 1072 { 1073 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1074 'Wrong username/password combination '.$username.' / '.starify($password)); 1075 } 1076 1077 $UserCache = & get_Cache( 'UserCache' ); 1078 $current_User = & $UserCache->get_by_login( $username ); 1079 1080 // Check permission: 1081 if( ! $current_User->check_perm( 'templates' ) ) 1082 { 1083 return new xmlrpcresp(0, $xmlrpcerruser+2, // user error 2 1084 'Permission denied.'); 1085 } 1086 1087 // Determine the edit folder: 1088 $edit_folder = $skins_path.'custom/'; 1089 1090 if( $templateType == 'main' ) 1091 { 1092 $file = $edit_folder.'index.main.php'; 1093 } 1094 elseif ($templateType == "archiveIndex") 1095 { 1096 $file = $edit_folder.'_arcdir.php'; 1097 } 1098 else return; // TODO: handle this cleanly 1099 1100 $f = fopen($file,"w+"); 1101 fwrite($f, $template); 1102 fclose($file); 1103 1104 return new xmlrpcresp(new xmlrpcval("1", "boolean")); 1105 } 1106 1107 1108 1109 //---------- Tor Jan 2005 Metaweblog API ---------------- 1110 // 1111 // Tor Dec 2004 1112 // 1113 // image upload 1114 // image is supplied coded in the info struct as bits 1115 // 1116 // To do - do not overwrite existing pics with same name 1117 // - security, password etc. 1118 // 1119 // 1120 function mwnewMediaObject($m) { 1121 global $xmlrpcerruser; // import user errcode value 1122 global $Settings, $baseurl,$fileupload_allowedtypes; 1123 1124 logIO("O","start of _newmediaobject..."); 1125 $blogid = $m->getParam(0); 1126 $blogid = $blogid->scalarval(); 1127 $username = $m->getParam(1); 1128 $username = $username->scalarval(); 1129 $password = $m->getParam(2); 1130 $password = $password->scalarval(); 1131 if( !user_pass_ok($username, $password) ) 1132 { 1133 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1134 'Wrong username/password combination '.$username.' / '.starify($password)); 1135 } 1136 if( ! $Settings->get('upload_enabled') ) 1137 { 1138 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1139 'Object upload not allowed '); 1140 } 1141 $BlogCache = & get_Cache('BlogCache'); 1142 $Blog = & $BlogCache->get_by_ID($blogid); 1143 1144 // Get the main data - and decode it properly for the image - sorry, binary object 1145 $xcontent = $m->getParam(3); 1146 $contentstruct = xmlrpc_decode($xcontent); 1147 logIO("O", 'Got first contentstruct!'."\n"); 1148 1149 // This call seems to go wrong from Marsedit under certain circumstances - Tor 04012005 1150 $data = $contentstruct['bits']; // decoding was done transparantly by xmlrpclibs xmlrpc_decode 1151 logIO("O", 'Have decoded data data?'."\n"); 1152 1153 // TODO: check filesize 1154 $filename = $contentstruct['name']; 1155 logIO("O", 'Found filename ->'. $filename ."\n"); 1156 $type = $contentstruct['type']; 1157 logIO("O", 'Done type ->'. $type ."\n"); 1158 $data = $contentstruct['bits']; 1159 logIO("O", 'Done bits ' ."\n"); 1160 1161 // Split into path + name: 1162 $filepath = dirname($filename); 1163 $filename = basename($filename); 1164 1165 $filepath_parts = explode('/', $filepath); 1166 if( in_array('..', $filepath_parts) ) 1167 { // invalid relative path: 1168 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1169 'Invalid relative part in file path: '.$filepath); 1170 } 1171 1172 // Check valid filename/extension: 1173 load_funcs('files/model/_file.funcs.php'); 1174 if( $error_filename = validate_filename($filename) ) 1175 { 1176 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1177 'Invalid objecttype for upload ('.$filename.'): '.$error_filename); 1178 } 1179 1180 $fileupload_path = $Blog->get_media_dir(); 1181 if( ! $fileupload_path ) 1182 { 1183 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1184 'Error accessing Blog media directory.'); 1185 } 1186 1187 // Handle subdirs, if any: 1188 if( strlen($filepath) && $filepath != '.' ) 1189 { 1190 $fileupload_path .= $filepath; 1191 if( ! mkdir_r(dirname($fileupload_path)) ) 1192 { 1193 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1194 'Error creating sub directories: '.rel_path_to_base($fileupload_path)); 1195 } 1196 } 1197 1198 logIO("O", 'fileupload_path ->'. $fileupload_path ."\n"); 1199 $fh = @fopen($fileupload_path.$filename, 'wb'); 1200 logIO("O", 'Managed to open file ->'. $filename ."\n"); 1201 if (!$fh) 1202 { 1203 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1204 'Error opening file for writing.'); 1205 } 1206 1207 logIO("O", 'Managed to open file for writing ->'. $fileupload_path.$filename."\n"); 1208 $ok = @fwrite($fh, $data); 1209 @fclose($fh); 1210 1211 if (!$ok) 1212 { 1213 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1214 'Error while writing to file.'); 1215 } 1216 1217 // chmod uploaded file: 1218 $oldumask = umask(0000); 1219 $chmod = $Settings->get('fm_default_chmod_file'); 1220 @chmod($fileupload_path.$filename, octdec( $chmod )); 1221 umask($oldumask); 1222 1223 $url = $Blog->get_media_url().$filepath.$filename; 1224 logIO("O", 'Full returned filename ->'. $fileupload_path . '/' . $filename ."\n"); 1225 logIO("O", 'Full returned url ->'. $url ."\n"); 1226 1227 // - return URL as XML 1228 $urlstruct = new xmlrpcval(array( 1229 'url' => new xmlrpcval($url, 'string') 1230 ), 'struct'); 1231 return new xmlrpcresp($urlstruct); 1232 } 1233 1234 1235 1236 // metaWeblog.newMediaObject 1237 $mwnewMediaObject_sig = array(array( 1238 // return type 1239 $xmlrpcStruct, // "url" element 1240 // params 1241 $xmlrpcString, // blogid 1242 $xmlrpcString, // username 1243 $xmlrpcString, // password 1244 $xmlrpcStruct // 'name', 'type' and 'bits' 1245 )); 1246 $mwnewMediaObject_doc = 'Uploads a file to the media library of the blog'; 1247 1248 1249 1250 $mwnewpost_doc='Adds a post, blogger-api like, +title +category +postdate'; 1251 $mwnewpost_sig = array(array($xmlrpcString,$xmlrpcString,$xmlrpcString,$xmlrpcString,$xmlrpcStruct,$xmlrpcBoolean)); 1252 1253 /** 1254 * mw.newPost 1255 * 1256 * mw API 1257 * Tor 2004 1258 * 1259 * NB! (Tor Feb 2005) status in metaweblog API speak dictates whether static html files are generated or not, so fairly misleading 1260 */ 1261 function mwnewpost($m) 1262 { 1263 global $xmlrpcerruser; // import user errcode value 1264 global $DB; 1265 global $Settings; 1266 1267 logIO("O","start of mwnewpost..."); 1268 1269 $blog_ID = $m->getParam(0); 1270 $blog_ID = $blog_ID->scalarval(); 1271 1272 $username = $m->getParam(1); 1273 $username = $username->scalarval(); 1274 logIO("O","finished getting username ..."); 1275 $password = $m->getParam(2); 1276 $password = $password->scalarval(); 1277 logIO("O","finished getting password ...".starify($password)); 1278 1279 if( ! user_pass_ok($username,$password) ) 1280 { 1281 logIO("O","error during checking password ..."); 1282 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1283 'Wrong username/password combination '.$username.' / '.starify($password)); 1284 } 1285 logIO("O","finished checking password ..."); 1286 1287 1288 $xcontent = $m->getParam(3); 1289 // $xcontent = $xcontent->scalarval(); 1290 logIO("O","finished getting xcontent ..."); 1291 xmlrpc_debugmsg( 'Getting xcontent' ); 1292 1293 // getParam(4) should now be a flag for publish or draft 1294 $xstatus = $m->getParam(4); 1295 $xstatus = $xstatus->scalarval(); 1296 $status = $xstatus ? 'published' : 'draft'; 1297 logIO('I',"Publish: $xstatus -> Status: $status"); 1298 logIO("O","finished getting xstatus ->". $xstatus); 1299 1300 $contentstruct = xmlrpc_decode($xcontent); //this does not work properly.... need better decoding 1301 logIO("O","finished getting contentstruct ..."); 1302 // $content = format_to_post($contentstruct['description']); 1303 $post_title = $contentstruct['title']; 1304 $content = $contentstruct['description']; 1305 logIO("O","finished getting title ...".$post_title); 1306 1307 1308 // Categories: 1309 $cat_IDs = _mw_get_cat_IDs($contentstruct, $blog_ID); 1310 1311 if( ! is_array($cat_IDs) ) 1312 { // error: 1313 return $cat_IDs; 1314 } 1315 1316 1317 if( empty($contentstruct['dateCreated']) ) 1318 { 1319 $postdate = date('Y-m-d H:i:s', (time() + $Settings->get('time_difference'))); 1320 logIO("O","no contentstruct dateCreated, using now...".$postdate); 1321 } 1322 else 1323 { 1324 $postdate = $contentstruct['dateCreated']; 1325 logIO("O","finished getting contentstruct dateCreated...".$postdate); 1326 } 1327 1328 // Check permission: 1329 $UserCache = & get_Cache( 'UserCache' ); 1330 $current_User = & $UserCache->get_by_login( $username ); 1331 logIO("O","currentuser ...". $current_User->ID); 1332 1333 if( ! $current_User->check_perm( 'blog_post_statuses', 'published', false, $blog_ID ) ) 1334 { 1335 logIO("O","user error 9 ..."); 1336 return new xmlrpcresp(0, $xmlrpcerruser+2, 'Permission denied.'); // user error 2 1337 } 1338 logIO("O","finished checking permissions ..."); 1339 1340 // CHECK and FORMAT content - error occur after this line 1341 //$post_title = format_to_post($post_title, 0, 0); 1342 //logIO("O","finished converting post_title ...",$post_title); 1343 1344 //$content = format_to_post($content, 0, 0); // 25122004 tag - security !!! 1345 //logIO("O","finished converting content ...".$content); // error occurs before this line 1346 1347 // if( $errstring = $Messages->get_string( 'Cannot post, please correct these errors:', '' ) ) 1348 // { 1349 // return new xmlrpcresp(0, $xmlrpcerruser+6, $errstring ); // user error 6 1350 // } 1351 //logIO("O","finished checking if errors exists, ready to insert into DB ..."); 1352 1353 // INSERT NEW POST INTO DB: 1354 // Tor - comment this out to stop inserts into database 1355 $edited_Item = & new Item(); 1356 $post_ID = $edited_Item->insert( $current_User->ID, $post_title, $content, $postdate, $cat_IDs[0], $cat_IDs, $status, $current_User->locale ); 1357 1358 if( $DB->error ) 1359 { // DB error 1360 logIO("O","user error 9 ..."); 1361 return new xmlrpcresp(0, $xmlrpcerruser+9, 'DB error: '.$DB->last_error ); // user error 9 1362 } 1363 1364 logIO( 'O', 'Handling notifications...' ); 1365 // Execute or schedule notifications & pings: 1366 $edited_Item->handle_post_processing(); 1367 1368 return new xmlrpcresp(new xmlrpcval($post_ID)); 1369 } 1370 1371 1372 // Movable Type API: {{{ 1373 1374 $mt_setPostCategories_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcArray)); 1375 $mt_setPostCategories_doc = "Sets the categories for a post."; 1376 1377 function mt_setPostCategories($m) { 1378 global $xmlrpcerruser,$Settings; 1379 global $DB, $Messages; 1380 1381 $post_ID = $m->getParam(0); 1382 $post_ID = $post_ID->scalarval(); 1383 $username = $m->getParam(1); 1384 $username = $username->scalarval(); 1385 $password = $m->getParam(2); 1386 $password = $password->scalarval(); 1387 1388 if( ! user_pass_ok($username,$password) ) 1389 { 1390 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1391 'Wrong username/password combination '.$username.' / '.starify($password)); 1392 } 1393 1394 // ok - pick up new category from call 1395 $xcontent = $m->getParam(3); // This is now a array of structs 1396 $iSize = $xcontent->arraysize(); // The number of objects ie categories 1397 logIO("O","finished getting - iSize ...".$iSize); // number of categories entry has set 1398 1399 logIO("O","finished getting contentstruct ..."); 1400 $categories = array(); 1401 if ($iSize > 0) 1402 { 1403 for ($i=0;$i<$iSize;$i++) 1404 { 1405 logIO("O","finished getting - i ...>".$i); // works! 1406 $struct = $xcontent->arraymem($i); // get a struct object from array 1407 $tempcat = $struct->structmem('categoryId'); 1408 $tempcat = $tempcat->scalarval(); 1409 $tempPrimary = $struct->structmem('isPrimary');// Start finding the primary category 1410 $tempPrimary = $tempPrimary->scalarval(); 1411 if ($tempPrimary != 0) 1412 { 1413 logIO("O","got primary category and there should only be one...".$tempcat); 1414 $category = $tempcat; 1415 } 1416 logIO("O","finished getting - tempcat ...".$tempcat); // works! 1417 $categories[$i] = $tempcat; 1418 logIO("O","finished getting - categories ...".$categories[$i]); 1419 } 1420 } 1421 1422 // UPDATE POST CATEGORIES IN DB: 1423 logIO("O","bpost_update - category ...".$category); // works! 1424 $ItemCache = & get_Cache( 'ItemCache' ); 1425 if( ! ($edited_Item = & $ItemCache->get_by_ID( $post_ID ) ) ) 1426 { 1427 return new xmlrpcresp(0, $xmlrpcerruser+7, "No such post (#$post_ID)."); // user error 7 1428 } 1429 $edited_Item->set( 'main_cat_ID', $category ); 1430 $edited_Item->set( 'extra_cat_IDs', $categories ); 1431 1432 $edited_Item->dbupdate(); 1433 1434 return new xmlrpcresp(new xmlrpcval(1)); 1435 } 1436 1437 1438 $mt_getPostCategories_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 1439 $mt_getPostCategories_doc = "Returns a list of all categories to which the post is assigned."; 1440 1441 function mt_getPostCategories($m) { 1442 global $xmlrpcerruser; 1443 global $DB; 1444 1445 $post_ID = $m->getParam(0); 1446 $post_ID = $post_ID->scalarval(); 1447 logIO("O","mt_getPostCategories postID ...".$post_ID); 1448 $username = $m->getParam(1); 1449 $username = $username->scalarval(); 1450 $password = $m->getParam(2); 1451 $password = $password->scalarval(); 1452 if( user_pass_ok($username,$password) ) 1453 { 1454 // First get the primary category in postdata 1455 $postdata = get_postdata($post_ID); 1456 $dato = $postdata["Date"]; 1457 logIO("O","mt_getPostCategories get_postdata argument postID ...".$post_ID); 1458 logIO("O","mt_getPostCategories postdata argument date ...".$dato); 1459 $Category = $postdata["Category"];// Primary category - nb also present in separate table so will not be used 1460 logIO("O","mt_getPostCategories postdata argument Category ...".$Category); 1461 $categories = postcats_get_byID( $post_ID ); // Secondary categories 1462 logIO("O","mt_getPostCategories postcats_get_byID ...".$categories); 1463 $iSize = count($categories); // The number of objects ie categories 1464 logIO("O","mt_getgategorylist no of categories...".$iSize);// works 1465 $struct = array(); 1466 for ($i=0;$i<$iSize;$i++) 1467 { 1468 logIO("O","mt_getPostCategories categories ...".$categories[$i]); 1469 // In database cat_ID and cat_name from tablecategories 1470 $sql = "SELECT * FROM T_categories WHERE cat_ID = $categories[$i] "; 1471 logIO("O","mt_getgategorylist sql...".$sql); 1472 $rows = $DB->get_results( $sql ); 1473 foreach( $rows as $row ) 1474 { 1475 $Categoryname = $row->cat_name; 1476 logIO("O","mt_getPostCategories Categoryname ...".$Categoryname); 1477 } 1478 if( $postdata['Date'] != '' ) 1479 { 1480 logIO("O","mt_getPostCategories date ok ..."); 1481 if ($i > 0) { 1482 logIO("O","mt_getPostCategories found secondary ...".$categories[$i]); 1483 $isPrimary = "0"; 1484 } 1485 else 1486 { 1487 logIO("O","mt_getPostCategories found primary ...".$categories[$i]); 1488 $isPrimary = "1"; 1489 } 1490 $struct[$i] = new xmlrpcval(array("categoryId" => new xmlrpcval($categories[$i]), // Look up name from ID separately 1491 "categoryName" => new xmlrpcval($Categoryname), 1492 "isPrimary" => new xmlrpcval($isPrimary) 1493 ),"struct"); 1494 } 1495 } 1496 return new xmlrpcresp(new xmlrpcval($struct, "array") ); 1497 // else 1498 // { 1499 // return new xmlrpcresp(0, $xmlrpcerruser+7, // user error 7 1500 // "No such post #$post_ID"); 1501 // } 1502 } 1503 else 1504 { 1505 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1506 'Wrong username/password combination '.$username.' / '.starify($password)); 1507 } 1508 } 1509 1510 1511 $mt_getCategoryList_sig = array(array($xmlrpcArray,$xmlrpcString,$xmlrpcString,$xmlrpcString)); 1512 $mt_getCategoryList_doc = 'Get category list'; 1513 /** 1514 * Provides mt.getCategoryList 1515 * @see http://www.sixapart.com/developers/xmlrpc/movable_type_api/mtgetcategorylist.html 1516 */ 1517 function mt_getCategoryList($m) { 1518 logIO("O","mt_getCategoryList start"); 1519 return _b2_or_mt_get_categories('mt', $m); 1520 } 1521 1522 // }}} 1523 1524 1525 $mweditpost_doc='Edits a post, blogger-api like, +title +category +postdate'; 1526 $mweditpost_sig = array(array($xmlrpcString,$xmlrpcString,$xmlrpcString,$xmlrpcString,$xmlrpcStruct,$xmlrpcBoolean)); 1527 /** 1528 * mw.EditPost (metaWeblog.editPost) 1529 * 1530 * mw API 1531 * 1532 * Tor - TODO 1533 * - Sort out sql select with blog ID 1534 * - screws up posts with multiple categories 1535 * partly due to the fact that Movable Type calls to this API are different to Metaweblog API calls when handling categories. 1536 */ 1537 1538 function mweditpost($m) 1539 { 1540 global $xmlrpcerruser; // import user errcode value 1541 global $DB; 1542 global $Settings; 1543 global $Messages; 1544 global $xmlrpc_htmlchecking; 1545 1546 logIO("O","start of mweditpost..."); 1547 $post_ID = $m->getParam(0); 1548 $post_ID = $post_ID->scalarval(); 1549 logIO("O","finished getting post_ID ...".$post_ID); 1550 1551 // Username/Password 1552 $username = $m->getParam(1); 1553 $username = $username->scalarval(); 1554 logIO("O","finished getting username ..."); 1555 $password = $m->getParam(2); 1556 $password = $password->scalarval(); 1557 logIO("O","finished getting password ...".$password); 1558 if( ! user_pass_ok($username,$password) ) 1559 { 1560 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1561 'Wrong username/password combination '.$username.' / '.starify($password)); 1562 } 1563 logIO("O","finished checking password ..."); 1564 1565 // getParam(4) should now be a flag for publish or draft 1566 $xstatus = $m->getParam(4); 1567 $xstatus = $xstatus->scalarval(); 1568 $status = $xstatus ? 'published' : 'draft'; 1569 logIO('I',"Publish: $xstatus -> Status: $status"); 1570 logIO("O","finished getting xstatus ->". $xstatus); 1571 1572 1573 // Get Item: 1574 $ItemCache = & get_Cache( 'ItemCache' ); 1575 if( ! ($edited_Item = & $ItemCache->get_by_ID( $post_ID ) ) ) 1576 { 1577 return new xmlrpcresp(0, $xmlrpcerruser+7, "No such post (#$post_ID)."); // user error 7 1578 } 1579 1580 // Check permission: 1581 $UserCache = & get_Cache( 'UserCache' ); 1582 $User = & $UserCache->get_by_login( $username ); 1583 logIO('O','User ID ...'.$User->ID); 1584 if( ! $User->check_perm( 'blog_post_statuses', $status, false, $edited_Item->blog_ID ) ) 1585 { 1586 return new xmlrpcresp(0, $xmlrpcerruser+2, 'Permission denied.'); // user error 2 1587 } 1588 logIO("O","finished checking permissions ..."); 1589 1590 1591 $xcontent = $m->getParam(3); 1592 // $xcontent = $xcontent->scalarval(); 1593 logIO("O","finished getting xcontent ..."); 1594 $contentstruct = xmlrpc_decode($xcontent); //this does not work properly.... need better decoding 1595 logIO("O","finished getting contentstruct ..."); 1596 1597 1598 // Categories: 1599 $cat_IDs = _mw_get_cat_IDs($contentstruct, $blog_ID, true /* empty is ok */); 1600 1601 if( ! is_array($cat_IDs) ) 1602 { // error: 1603 return $cat_IDs; 1604 } 1605 1606 1607 $post_title = $contentstruct['title']; 1608 $content = $contentstruct['description']; 1609 logIO("O","finished getting title ...".$post_title); 1610 1611 $postdate = $contentstruct['dateCreated']; 1612 logIO("O","finished getting contentstruct dateCreated...".$postdate); 1613 1614 1615 if( ! empty($xmlrpc_htmlchecking) ) 1616 { // CHECK and FORMAT content 1617 $post_title = format_to_post($post_title, 0, 0); 1618 } 1619 logIO("O","finished converting post_title ...->".$post_title); 1620 if( ! empty($xmlrpc_htmlchecking) ) 1621 { 1622 $content = format_to_post($content, 0, 0); // 25122004 tag - security issue - need to sort !!! 1623 } 1624 logIO("O","finished converting content ...".$content); 1625 if( $errstring = $Messages->get_string( 'Cannot post, please correct these errors:', '' ) ) 1626 { 1627 return new xmlrpcresp(0, $xmlrpcerruser+6, $errstring ); // user error 6 1628 } 1629 logIO("O","finished checking if errors exists, ready to insert into DB ..."); 1630 xmlrpc_debugmsg( 'post_ID: '.$post_ID ); 1631 1632 // UPDATE POST IN DB: 1633 $edited_Item->set( 'title', $post_title ); 1634 $edited_Item->set( 'content', $content ); 1635 $edited_Item->set( 'status', $status ); 1636 if( ! empty($postdate) ) 1637 { 1638 $edited_Item->set( 'datestart', $postdate ); 1639 } 1640 if( ! empty($cat_IDs) ) 1641 { // Update cats: 1642 $edited_Item->set('main_cat_ID', $cat_IDs[0]); 1643 1644 if( count($cat_IDs) > 1 ) 1645 { // Extra-Cats: 1646 $edited_Item->set('extra_cat_IDs', $cat_IDs); 1647 } 1648 } 1649 $edited_Item->dbupdate(); 1650 if( $DB->error ) 1651 { // DB error 1652 return new xmlrpcresp(0, $xmlrpcerruser+9, 'DB error: '.$DB->last_error ); // user error 9 1653 } 1654 1655 // Time to perform trackbacks NB NOT WORKING YET 1656 // 1657 // NB Requires a change to the _trackback library 1658 // 1659 // function trackbacks( $post_trackbacks, $content, $post_title, $post_ID ) 1660 1661 // first extract these from posting as post_trackbacks array, then rest is easy 1662 // <member> 1663 // <name>mt_tb_ping_urls</name> 1664 // <value><array><data> 1665 // <value><string>http://archive.scripting.com/2005/04/17</string></value> 1666 // </data></array></value> 1667 // </member> 1668 // First check that trackbacks are allowed - mt_allow_pings 1669 $trackback_ok = 0; 1670 $trackbacks = array(); 1671 $trackback_ok = $contentstruct['mt_allow_pings']; 1672 logIO("O","Trackback OK ...".$trackback_ok); 1673 if ($trackback_ok == 1) 1674 { 1675 $trackbacks = $contentstruct['mt_tb_ping_urls']; 1676 logIO("O","Trackback url 0 ...".$trackbacks[0]); 1677 $no_of_trackbacks = count($trackbacks); 1678 logIO("O","Number of Trackbacks ...".$no_of_trackbacks); 1679 if ($no_of_trackbacks > 0) 1680 { 1681 logIO("O","Calling Trackbacks ..."); 1682 load_funcs('comments/_trackback.funcs.php'); 1683 $result = trackbacks( $trackbacks, $content, $post_title, $post_ID ); 1684 logIO("O","Returned from Trackbacks ..."); 1685 } 1686 1687 } 1688 return new xmlrpcresp(new xmlrpcval($post_ID)); 1689 } 1690 1691 1692 1693 $mwgetcats_sig = array(array($xmlrpcArray,$xmlrpcString,$xmlrpcString,$xmlrpcString)); 1694 $mwgetcats_doc = 'Get categories of a post, MetaWeblog API-style'; 1695 /** 1696 * metaWeblog.getCategories 1697 * @see http://www.xmlrpc.com/metaWeblogApi#metawebloggetcategories 1698 */ 1699 function mwgetcats( $m ) 1700 { 1701 global $xmlrpcerruser, $DB; 1702 1703 logIO('O','Start of mwgetcats'); 1704 $blogid = $m->getParam(0); 1705 $blogid = $blogid->scalarval(); 1706 $username = $m->getParam(1); 1707 $username = $username->scalarval(); 1708 $password = $m->getParam(2); 1709 $password = $password->scalarval(); 1710 logIO('O','Got params 0, 1 , 2'); 1711 if( ! user_pass_ok($username,$password) ) 1712 { 1713 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1714 'Wrong username/password combination '.$username.' / '.starify($password)); 1715 } 1716 $sql = "SELECT cat_ID, cat_name 1717 FROM T_categories "; 1718 1719 $BlogCache = & get_Cache('BlogCache'); 1720 $current_Blog = $BlogCache->get_by_ID( $blog ); 1721 $aggregate_coll_IDs = $current_Blog->get_setting('aggregate_coll_IDs'); 1722 if( empty( $aggregate_coll_IDs ) ) 1723 { // We only want posts from the current blog: 1724 $sql .= 'WHERE cat_blog_ID ='.$current_Blog->ID; 1725 } 1726 else 1727 { // We are aggregating posts from several blogs: 1728 $sql .= 'WHERE cat_blog_ID IN ('.$aggregate_coll_IDs.')'; 1729 } 1730 1731 $sql .= " ORDER BY cat_name ASC"; 1732 $rows = $DB->get_results( $sql ); 1733 if( $DB->error ) 1734 { // DB error 1735 return new xmlrpcresp(0, $xmlrpcerruser+9, 'DB error: '.$DB->last_error ); // user error 9 1736 } 1737 xmlrpc_debugmsg( 'Categories:'.count($rows) ); 1738 1739 $ChapterCache = & get_Cache('ChapterCache'); 1740 $data = array(); 1741 foreach( $rows as $row ) 1742 { 1743 $Chapter = & $ChapterCache->get_by_ID($row->cat_ID); 1744 if( ! $Chapter ) 1745 { 1746 continue; 1747 } 1748 $data[] = new xmlrpcval( array( 1749 'categoryId' => new xmlrpcval( $row->cat_ID ), // not in RFC (http://www.xmlrpc.com/metaWeblogApi) 1750 'description' => new xmlrpcval( $row->cat_name ), // not in RFC (http://www.xmlrpc.com/metaWeblogApi) 1751 'categoryName' => new xmlrpcval( $row->cat_name ), 1752 'htmlUrl' => new xmlrpcval( $Chapter->get_permanent_url() ), 1753 'rssUrl' => new xmlrpcval( url_add_param($Chapter->get_permanent_url(), 'tempskin=_rss2') ) 1754 // mb_convert_encoding( $row->cat_name, "utf-8", "iso-8859-1") ) 1755 ),"struct"); 1756 } 1757 return new xmlrpcresp( new xmlrpcval($data, "array") ); 1758 } 1759 1760 1761 1762 1763 1764 $metawebloggetrecentposts_doc = 'fetches X most recent posts, blogger-api like'; 1765 $metawebloggetrecentposts_sig = array(array($xmlrpcArray,$xmlrpcString,$xmlrpcString,$xmlrpcString,$xmlrpcInt)); 1766 1767 function metawebloggetrecentposts( $m ) 1768 { 1769 global $xmlrpcerruser, $DB; 1770 global $blog; 1771 1772 $blog_ID = $m->getParam(0); 1773 logIO("O","In metawebloggetrecentposts, current blog_id is ...". $blog_ID); 1774 $blog_ID = $blog_ID->scalarval(); 1775 logIO("O","In metawebloggetrecentposts, current blog_id is ...". $blog_ID); 1776 $username = $m->getParam(1); 1777 logIO("O","In metawebloggetrecentposts, current username is ...". $username); 1778 $username = $username->scalarval(); 1779 logIO("O","In metawebloggetrecentposts, current username is ...". $username); 1780 $password = $m->getParam(2); 1781 $password = $password->scalarval(); 1782 $numposts = $m->getParam(3); 1783 logIO("O","In metawebloggetrecentposts, current numposts is ...". $numposts); 1784 $numposts = $numposts->scalarval(); 1785 logIO("O","In metawebloggetrecentposts, current numposts is ...". $numposts); 1786 1787 if( ! user_pass_ok($username, $password) ) 1788 { 1789 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1790 'Wrong username/password combination '.$username.' / '.starify($password)); 1791 } 1792 logIO("O","In metawebloggetrecentposts, user and pass ok..."); 1793 $UserCache = & get_Cache( 'UserCache' ); 1794 $current_User = & $UserCache->get_by_login( $username ); 1795 logIO( 'O', 'In metawebloggetrecentposts, current user is ...'.$current_User->ID ); 1796 // Check permission: 1797 if( ! $current_User->check_perm( 'blog_ismember', 1, false, $blog_ID ) ) 1798 { 1799 return new xmlrpcresp(0, $xmlrpcerruser+2, 'Permission denied.' ); // user error 2 1800 } 1801 logIO("O","In metawebloggetrecentposts, permissions ok..."); 1802 logIO("O","In metawebloggetrecentposts, current blog is ...". $blog_ID); 1803 1804 $BlogCache = & get_Cache( 'BlogCache' ); 1805 $Blog = & $BlogCache->get_by_ID( $blog_ID ); 1806 1807 // Get the posts to display: 1808 $MainList = & new ItemList2( $Blog, NULL, NULL, $numposts ); 1809 1810 $MainList->set_filters( array( 1811 'visibility_array' => array( 'published', 'protected', 'private', 'draft', 'deprecated', 'redirected' ), 1812 'order' => 'DESC', 1813 'unit' => 'posts', 1814 ) ); 1815 1816 // Run the query: 1817 $MainList->query(); 1818 1819 xmlrpc_debugmsg( 'Items:'.$MainList->result_num_rows ); 1820 1821 $data = array(); 1822 while( $Item = & $MainList->get_item() ) 1823 { 1824 xmlrpc_debugmsg( 'Item:'.$Item->title. 1825 ' - Issued: '.$Item->issue_date. 1826 ' - Modified: '.$Item->mod_date ); 1827 $post_date = mysql2date("U", $Item->issue_date); 1828 $post_date = gmdate("Ymd", $post_date)."T".gmdate("H:i:s", $post_date); 1829 $content = $Item->content; 1830 $content = str_replace("\n",'',$content); // Tor - kludge to fix bug in xmlrpc libraries 1831 // Load Item's creator User: 1832 $Item->get_creator_User(); 1833 $authorname = $Item->creator_User->get('preferredname'); 1834 // need a loop here to extract all categoy names 1835 // $extra_cat_IDs is the variable for the rest of the IDs 1836 $hope_cat_name = get_the_category_by_ID($Item->main_cat_ID); 1837 $test = $Item->extra_cat_IDs[0]; 1838 xmlrpc_debugmsg( 'postcats:'.$hope_cat_name["cat_name"]); 1839 xmlrpc_debugmsg( 'test:'.$test); 1840 $data[] = new xmlrpcval(array( 1841 "dateCreated" => new xmlrpcval($post_date,"dateTime.iso8601"), 1842 "userid" => new xmlrpcval($Item->creator_user_ID), 1843 "postid" => new xmlrpcval($Item->ID), 1844 "categories" => new xmlrpcval(array(new xmlrpcval($hope_cat_name["cat_name"])),'array'), 1845 "title" => new xmlrpcval($Item->title), 1846 "description" => new xmlrpcval($content), 1847 "link" => new xmlrpcval($Item->url), 1848 "permalink" => new xmlrpcval($Item->urltitle), 1849 "mt_excerpt" => new xmlrpcval($content), 1850 "mt_allow_comments" => new xmlrpcval('1'), 1851 "mt_allow_pings" => new xmlrpcval('1'), 1852 "mt_text_more" => new xmlrpcval('') 1853 ),"struct"); 1854 } 1855 $resp = new xmlrpcval($data, "array"); 1856 return new xmlrpcresp($resp); 1857 } 1858 1859 1860 1861 1862 1863 1864 $mwgetpost_doc = 'fetches a post, blogger-api like'; 1865 $mwgetpost_sig = array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); 1866 /** 1867 * metaweblog.getPost retieves a given post. 1868 * 1869 * This API call is not documented on 1870 * {@link http://www.blogger.com/developers/api/1_docs/} 1871 * 1872 * @param xmlrpcmsg XML-RPC Message 1873 * 0 appkey (string): Unique identifier/passcode of the application sending the post. 1874 * (See access info {@link http://www.blogger.com/developers/api/1_docs/#access} .) 1875 * 1 postid (string): Unique identifier of the post to be deleted. 1876 * 2 username (string): Login for a Blogger user who has permission to edit the given 1877 * post (either the user who originally created it or an admin of the blog). 1878 * 3 password (string): Password for said username. 1879 * @return xmlrpcresp XML-RPC Response 1880 */ 1881 1882 function mwgetpost($m) 1883 { 1884 1885 global $xmlrpcerruser; 1886 1887 1888 1889 $post_ID = $m->getParam(0); 1890 $post_ID = $post_ID->scalarval(); 1891 $username = $m->getParam(1); 1892 $username = $username->scalarval(); 1893 $password = $m->getParam(2); 1894 $password = $password->scalarval(); 1895 if( user_pass_ok($username,$password) ) 1896 { 1897 $postdata = get_postdata($post_ID); 1898 if( $postdata['Date'] != '' ) 1899 { 1900 $post_date = mysql2date("U", $postdata["Date"]); 1901 $post_date = gmdate("Ymd", $post_date)."T".gmdate("H:i:s", $post_date); 1902 $content = $postdata["Content"]; 1903 // Kludge to fix library problem str_replace(#10,'',$content) 1904 $content = str_replace("\n",'',$content); // Tor - kludge to fix bug in xmlrpc libraries 1905 $struct = new xmlrpcval(array("link" => new xmlrpcval(''), 1906 "title" => new xmlrpcval($postdata["Title"]), 1907 "description" => new xmlrpcval($content), 1908 "dateCreated" => new xmlrpcval($post_date,"dateTime.iso8601"), 1909 "userid" => new xmlrpcval(""), 1910 "postid" => new xmlrpcval($post_ID), 1911 "content" => new xmlrpcval($content), 1912 "permalink" => new xmlrpcval(""), 1913 "categories" => new xmlrpcval($postdata["Category"]), 1914 "mt_excerpt" => new xmlrpcval($content), 1915 "mt_allow_comments" => new xmlrpcval("",'int'), 1916 "mt_allow_pings" => new xmlrpcval("",'int'), 1917 "mt_text_more" => new xmlrpcval("") 1918 ),"struct"); 1919 $resp = $struct; 1920 return new xmlrpcresp($resp); 1921 } 1922 else 1923 { 1924 return new xmlrpcresp(0, $xmlrpcerruser+7, // user error 7 1925 "No such post #$post_ID"); 1926 } 1927 } 1928 else 1929 { 1930 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1931 'Wrong username/password combination '.$username.' / '.starify($password)); 1932 } 1933 } 1934 1935 1936 /** 1937 * Helper for {@link b2getcategories()} and {@link mt_getPostCategories()}, because they differ 1938 * only in the "categoryId" case ("categoryId" (b2) vs "categoryID" (MT)) 1939 * 1940 * @param string Type, either "b2" or "mt" 1941 * @param xmlrpcmsg XML-RPC Message 1942 * 0 blogid (string): Unique identifier of the blog to query 1943 * 1 username (string): Login for a Blogger user who is member of the blog. 1944 * 2 password (string): Password for said username. 1945 * @return xmlrpcresp XML-RPC Response 1946 */ 1947 function _b2_or_mt_get_categories( $type, $m ) 1948 { 1949 global $xmlrpcerruser, $DB; 1950 1951 $blogid = $m->getParam(0); 1952 $blogid = $blogid->scalarval(); 1953 1954 $username = $m->getParam(1); 1955 $username = $username->scalarval(); 1956 1957 $password = $m->getParam(2); 1958 $password = $password->scalarval(); 1959 1960 if( ! user_pass_ok($username,$password) ) 1961 { 1962 return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 1963 'Wrong username/password combination '.$username.' / '.starify($password)); 1964 } 1965 1966 $sql = 'SELECT * 1967 FROM T_categories '; 1968 1969 $BlogCache = & get_Cache('BlogCache'); 1970 $current_Blog = $BlogCache->get_by_ID( $blog ); 1971 $aggregate_coll_IDs = $current_Blog->get_setting('aggregate_coll_IDs'); 1972 if( empty( $aggregate_coll_IDs ) ) 1973 { // We only want posts from the current blog: 1974 $sql .= 'WHERE cat_blog_ID ='.$current_Blog->ID; 1975 } 1976 else 1977 { // We are aggregating posts from several blogs: 1978 $sql .= 'WHERE cat_blog_ID IN ('.$aggregate_coll_IDs.')'; 1979 } 1980 1981 $sql .= " ORDER BY cat_name ASC"; 1982 1983 $rows = $DB->get_results( $sql ); 1984 if( $DB->error ) 1985 { // DB error 1986 return new xmlrpcresp(0, $xmlrpcerruser+9, 'DB error: '.$DB->last_error ); // user error 9 1987 } 1988 1989 xmlrpc_debugmsg( 'Categories:'.count($rows) ); 1990 1991 $categoryIdName = ( $type == 'b2' ? 'categoryID' : 'categoryId' ); 1992 $data = array(); 1993 foreach( $rows as $row ) 1994 { 1995 $data[] = new xmlrpcval( array( 1996 $categoryIdName => new xmlrpcval($row->cat_ID), 1997 'categoryName' => new xmlrpcval( $row->cat_name ) 1998 // mb_convert_encoding( $row->cat_name, "utf-8", "iso-8859-1") ) 1999 ), 'struct' ); 2000 } 2001 2002 return new xmlrpcresp( new xmlrpcval($data, "array") ); 2003 } 2004 2005 2006 /** 2007 * 2008 * @param array struct 2009 * @param integer blog ID 2010 * @param boolean Return empty array (instead of error), if no cats given in struct? 2011 * @return array|xmlrpcresp A list of category IDs or xmlrpcresp in case of error. 2012 */ 2013 function _mw_get_cat_IDs($contentstruct, $blog_ID, $empty_struct_ok = false) 2014 { 2015 global $DB, $xmlrpcerruser; 2016 2017 if( isset($contentstruct['categories']) ) 2018 { 2019 $categories = $contentstruct['categories']; 2020 } 2021 else 2022 { 2023 $categories = array(); 2024 } 2025 logIO("O","finished getting contentstruct categories...".implode( ', ', $categories ) ); 2026 2027 if( $empty_struct_ok && empty($categories) ) 2028 { 2029 return $categories; 2030 } 2031 2032 xmlrpc_debugmsg( 'Categories: '.implode( ', ', $categories ) ); 2033 2034 // for cross-blog-entries, the cat_blog_ID WHERE clause should be removed (but cats are given by name!) 2035 if( ! empty($categories) ) 2036 { 2037 $sql = " 2038 SELECT cat_ID FROM T_categories 2039 WHERE cat_blog_ID = $blog_ID 2040 AND cat_name IN ( "; 2041 foreach( $categories as $l_cat ) 2042 { 2043 $sql .= $DB->quote($l_cat).', '; 2044 } 2045 if( ! empty($categories) ) 2046 { 2047 $sql = substr($sql, 0, -2); // remove ', ' 2048 } 2049 $sql .= ' )'; 2050 logIO("O","sql for finding IDs ...".$sql); 2051 2052 $cat_IDs = $DB->get_col( $sql ); 2053 if( $DB->error ) 2054 { // DB error 2055 logIO("O","user error finding categories info ..."); 2056 } 2057 } 2058 else 2059 { 2060 $cat_IDs = array(); 2061 } 2062 2063 if( ! empty($cat_IDs) ) 2064 { // categories requested to be set: 2065 2066 // Check if category exists 2067 if( get_the_category_by_ID( $cat_IDs[0], false ) === false ) 2068 { // Main cat does not exist: 2069 logIO("O","usererror 5 ..."); 2070 return new xmlrpcresp(0, $xmlrpcerruser+5, 'Requested category does not exist.'); // user error 5 2071 } 2072 logIO("O","finished checking if main category exists ...".$cat_IDs[0]); 2073 } 2074 else 2075 { // No category given/valid - use the first for the blog: 2076 logIO("O","No category for post given ..."); 2077 2078 $first_cat = $DB->get_var( ' 2079 SELECT cat_ID 2080 FROM T_categories 2081 WHERE cat_blog_ID = '.$blog_ID.' 2082 ORDER BY cat_name 2083 LIMIT 1' ); 2084 if( empty($first_cat) ) 2085 { 2086 logIO("O", 'No categories for this blog...'); 2087 return new xmlrpcresp(0, $xmlrpcerruser+5, 'No categories for this blog.'); // user error 5 2088 } 2089 else 2090 { 2091 $cat_IDs = array($first_cat); 2092 } 2093 } 2094 2095 return $cat_IDs; 2096 } 2097 2098 2099 2100 /**** SERVER FUNCTIONS ARRAY ****/ 2101 // dh> TODO: Plugin hook here, so that Plugins can provide own callbacks?! 2102 // fp> The current implementation of this file is not optimal (file is way too large) 2103 // fp> xmlrpc.php should actually only be a switcher and it should load the function to execute once it has been identified 2104 // fp> maybe it would make sense to register xmlrpc apis/functions in a DB table 2105 // fp> it would probably make sense to have *all* xmlrpc methods implemented as plugins (maybe 1 plugin per API; it should be possible to add a single func to an API with an additional plugin) 2106 // dh> NOTE: some tools may use different API entry points, e.g. for extended methods.. (But I'm not sure..) 2107 // fp> from a security standpoint it would make a lot of sense to disable any rpc that is not needed 2108 2109 load_funcs('_ext/xmlrpc/_xmlrpcs.php'); // This will add generic remote calls 2110 2111 $s = new xmlrpc_server( 2112 array( 2113 "metaWeblog.newMediaObject" => 2114 array( 2115 "function" => "mwnewMediaObject", 2116 "signature" => $mwnewMediaObject_sig, 2117 "docstring" => $mwnewMediaObject_doc), 2118 2119 "metaWeblog.newPost" => 2120 array( 2121 "function" => "mwnewpost", 2122 "signature" => $mwnewpost_sig, 2123 "docstring" => $mwnewpost_doc), 2124 2125 "metaWeblog.editPost" => 2126 array( 2127 "function" => "mweditpost", 2128 "signature" => $mweditpost_sig, 2129 "docstring" => $mweditpost_doc), 2130 2131 "metaWeblog.getPost" => 2132 array( 2133 "function" => "mwgetpost", 2134 "signature" => $mwgetpost_sig, 2135 "docstring" => $mwgetpost_doc), 2136 2137 "metaWeblog.getCategories" => 2138 array( 2139 "function" => "mwgetcats", 2140 "signature" => $mwgetcats_sig, 2141 "docstring" => $mwgetcats_doc), 2142 2143 "metaWeblog.getRecentPosts" => 2144 array( 2145 "function" => "metawebloggetrecentposts", 2146 "signature" => $metawebloggetrecentposts_sig, 2147 "docstring" => $metawebloggetrecentposts_doc), 2148 2149 "b2.newPost" => 2150 array( 2151 "function" => "b2newpost", 2152 "signature" => $b2newpost_sig, 2153 "docstring" => $b2newpost_doc), 2154 2155 "b2.getCategories" => 2156 array( 2157 "function" => "b2getcategories", 2158 "signature" => $b2getcategories_sig, 2159 "docstring" => $b2getcategories_doc), 2160 2161 "b2.getPostURL" => 2162 array( 2163 "function" => "b2_getPostURL", 2164 "signature" => $b2_getPostURL_sig, 2165 "docstring" => $b2_getPostURL_doc), 2166 2167 "blogger.newPost" => 2168 array( 2169 "function" => "bloggernewpost", 2170 "signature" => $bloggernewpost_sig, 2171 "docstring" => $bloggernewpost_doc), 2172 2173 "blogger.editPost" => 2174 array( 2175 "function" => "bloggereditpost", 2176 "signature" => $bloggereditpost_sig, 2177 "docstring" => $bloggereditpost_doc), 2178 2179 "blogger.deletePost" => 2180 array( 2181 "function" => "bloggerdeletepost", 2182 "signature" => $bloggerdeletepost_sig, 2183 "docstring" => $bloggerdeletepost_doc), 2184 2185 "blogger.getUsersBlogs" => 2186 array( 2187 "function" => "bloggergetusersblogs", 2188 "signature" => $bloggergetusersblogs_sig, 2189 "docstring" => $bloggergetusersblogs_doc), 2190 2191 "blogger.getUserInfo" => 2192 array( 2193 "function" => "bloggergetuserinfo", 2194 "signature" => $bloggergetuserinfo_sig, 2195 "docstring" => $bloggergetuserinfo_doc), 2196 2197 "blogger.getPost" => 2198 array( 2199 "function" => "bloggergetpost", 2200 "signature" => $bloggergetpost_sig, 2201 "docstring" => $bloggergetpost_doc), 2202 2203 "blogger.getRecentPosts" => 2204 array( 2205 "function" => "bloggergetrecentposts", 2206 "signature" => $bloggergetrecentposts_sig, 2207 "docstring" => $bloggergetrecentposts_doc), 2208 2209 "blogger.getTemplate" => 2210 array( 2211 "function" => "bloggergettemplate", 2212 "signature" => $bloggergettemplate_sig, 2213 "docstring" => $bloggergettemplate_doc), 2214 2215 "blogger.setTemplate" => 2216 array( 2217 "function" => "bloggersettemplate", 2218 "signature" => $bloggersettemplate_sig, 2219 "docstring" => $bloggersettemplate_doc), 2220 2221 "mt.getPostCategories" => 2222 array( 2223 "function" => "mt_getPostCategories", 2224 "signature" => $mt_getPostCategories_sig, 2225 "docstring" => $mt_getPostCategories_doc), 2226 2227 "mt.getCategoryList" => 2228 array( 2229 "function" => "mt_getCategoryList", 2230 "signature" => $mt_getCategoryList_sig, 2231 "docstring" => $mt_getCategoryList_doc), 2232 2233 "mt.setPostCategories" => 2234 array( 2235 "function" => "mt_setPostCategories", 2236 "signature" => $mt_setPostCategories_sig, 2237 "docstring" => $mt_setPostCategories_doc), 2238 ) 2239 ); 2240 2241 /* 2242 * $Log: xmlrpc.php,v $ 2243 * Revision 1.139 2007/06/27 02:23:32 fplanque 2244 * new default template for skins named index.main.php 2245 * 2246 * Revision 1.138 2007/06/25 11:02:42 fplanque 2247 * MODULES (refactored MVC) 2248 * 2249 * Revision 1.137 2007/05/21 19:40:15 blueyed 2250 * Fixed usage of $HTTP_RAW_POST_DATA global by using php://input stream, if $HTTP_RAW_POST_DATA is not set. 2251 * 2252 * Revision 1.136 2007/05/09 01:00:25 fplanque 2253 * optimized querying for blog lists 2254 * 2255 * Revision 1.135 2007/05/08 18:50:47 fplanque 2256 * minor fixes 2257 * 2258 * Revision 1.134 2007/04/26 00:11:14 fplanque 2259 * (c) 2007 2260 * 2261 * Revision 1.133 2007/03/20 07:39:08 fplanque 2262 * filemanager fixes, including the chmod octal stuff 2263 * 2264 * Revision 1.132 2007/03/11 23:57:07 fplanque 2265 * item editing: allow setting to 'redirected' status 2266 * 2267 * Revision 1.131 2006/12/23 23:37:35 fplanque 2268 * refactoring / Blog::get_default_cat_ID() 2269 * 2270 * Revision 1.130 2006/12/17 23:42:40 fplanque 2271 * Removed special behavior of blog #1. Any blog can now aggregate any other combination of blogs. 2272 * Look into Advanced Settings for the aggregating blog. 2273 * There may be side effects and new bugs created by this. Please report them :] 2274 * 2275 * Revision 1.129 2006/12/12 02:53:57 fplanque 2276 * Activated new item/comments controllers + new editing navigation 2277 * Some things are unfinished yet. Other things may need more testing. 2278 * 2279 * Revision 1.128 2006/12/06 17:49:11 blueyed 2280 * Moved strip_all_but_one_link() to obsolete2.php; doc (pingback removed) 2281 * 2282 * Revision 1.127 2006/12/05 07:23:22 blueyed 2283 * Fixed categories handling also for blogger.editPost; doc 2284 * 2285 * Revision 1.126 2006/12/05 06:31:41 blueyed 2286 * Nuked $default_category from XMLRPC 2287 * 2288 * Revision 1.125 2006/12/05 06:22:25 blueyed 2289 * Fixed blogger.newPost to accept a list of categories, as given by w.bloggar 2290 * 2291 * Revision 1.124 2006/12/03 18:22:58 blueyed 2292 * Nuked deprecated fileupload globals 2293 * 2294 * Revision 1.122 2006/12/03 01:24:38 blueyed 2295 * "htmlUrl" and "rssUrl" for metaWeblog.getCategories 2296 * 2297 * Revision 1.121 2006/12/02 19:51:08 blueyed 2298 * "categoryId" case fixes; see http://forums.b2evolution.net/viewtopic.php?p=47650#47650 2299 * 2300 * Revision 1.120 2006/11/16 19:14:10 fplanque 2301 * minor 2302 * 2303 * Revision 1.119 2006/11/13 20:49:53 fplanque 2304 * doc/cleanup :/ 2305 * 2306 * Revision 1.118 2006/11/05 20:13:57 fplanque 2307 * minor 2308 * 2309 * Revision 1.117 2006/10/10 19:23:51 blueyed 2310 * Use set()/dbupdate() instead of update() in bloggereditpost() 2311 * 2312 * Revision 1.116 2006/10/01 20:08:39 blueyed 2313 * Removed DEBUG_XMLRPC_LOGGING constant again. It gave a notice when using the xmlrpcclient e.g. for sending pings and is not as flexible as a global. 2314 * 2315 * Revision 1.115 2006/09/22 21:27:53 blueyed 2316 * Minor fixes for RSD, formatting, whitespace. 2317 * 2318 * Revision 1.114 2006/09/22 19:11:20 wendall911 2319 * Added rsd support, restored 0.9.x functionality 2320 * 2321 * Revision 1.113 2006/09/06 21:39:23 fplanque 2322 * ItemList2 fixes 2323 * 2324 * Revision 1.112 2006/09/06 20:45:34 fplanque 2325 * ItemList2 fixes 2326 * 2327 * Revision 1.111 2006/09/06 18:34:07 fplanque 2328 * Finally killed the old stinkin' ItemList(1) class which is deprecated by ItemList2 2329 * 2330 * Revision 1.110 2006/08/29 00:26:12 fplanque 2331 * Massive changes rolling in ItemList2. 2332 * This is somehow the meat of version 2.0. 2333 * This branch has gone officially unstable at this point! :> 2334 * 2335 * Revision 1.109 2006/08/21 16:07:45 fplanque 2336 * refactoring 2337 * 2338 * Revision 1.108 2006/08/21 00:03:13 fplanque 2339 * obsoleted some dirty old thing 2340 * 2341 * Revision 1.107 2006/08/19 07:56:32 fplanque 2342 * Moved a lot of stuff out of the automatic instanciation in _main.inc 2343 * 2344 * Revision 1.106 2006/08/19 02:15:09 fplanque 2345 * Half kille dthe pingbacks 2346 * Still supported in DB in case someone wants to write a plugin. 2347 * 2348 * Revision 1.105 2006/08/01 23:32:45 blueyed 2349 * Moved $xmlrpc_logging into /conf/_advanced.php (and renamed it to $debug_xmlrpc_logging), so it can get overridden easily. 2350 * 2351 * Revision 1.104 2006/08/01 22:56:38 blueyed 2352 * Fixed "perm denied" 2353 * 2354 * Revision 1.103 2006/08/01 22:53:31 blueyed 2355 * Fix 2356 * 2357 * Revision 1.102 2006/08/01 21:53:02 blueyed 2358 * Fixes 2359 * 2360 * Revision 1.101 2006/08/01 19:23:55 blueyed 2361 * fixes 2362 * 2363 * Revision 1.100 2006/08/01 18:43:27 blueyed 2364 * Removed last deprecated $table.. occurences. 2365 * 2366 * Revision 1.99 2006/08/01 18:24:10 blueyed 2367 * Fixes: 2368 * - metaWeblog.editPost: 2369 * - respect publish/status param 2370 * - check perms 2371 * - only update relevant parts of item 2372 * - mt.setPostCategories: set only categories (keep status etc) 2373 * Features: 2374 * - support categories for metaWeblog.editPost/newPost 2375 * Code cleanup! 2376 * Completely untested.. :/ 2377 * 2378 * Revision 1.97 2006/07/24 00:05:46 fplanque 2379 * cleaned up skins 2380 * 2381 * Revision 1.96 2006/07/06 19:56:31 fplanque 2382 * no message 2383 * 2384 * Revision 1.95 2006/07/02 21:53:31 blueyed 2385 * time difference as seconds instead of hours; validate user#1 on upgrade; bumped new_db_version to 9300. 2386 * 2387 * Revision 1.94 2006/05/30 20:32:57 blueyed 2388 * Lazy-instantiate "expensive" properties of Comment and Item. 2389 * 2390 * Revision 1.93 2006/03/24 01:04:33 blueyed 2391 * Fix for mt.getCategoryList? At least E_NOTICE fixed. 2392 * 2393 * Revision 1.91 2006/03/18 19:17:54 blueyed 2394 * Removed remaining use of $img_url 2395 * 2396 * Revision 1.90 2006/03/12 23:09:31 fplanque 2397 * doc cleanup 2398 * 2399 * Revision 1.89 2006/03/09 22:30:02 fplanque 2400 * cleaned up permanent urls 2401 * 2402 * Revision 1.88 2006/03/09 20:40:41 fplanque 2403 * cleanup 2404 * 2405 * Revision 1.87 2006/02/23 21:12:54 fplanque 2406 * File reorganization to MVC (Model View Controller) architecture. 2407 * See index.hml files in folders. 2408 * (Sorry for all the remaining bugs induced by the reorg... :/) 2409 * 2410 * Revision 1.86 2006/01/06 16:42:41 fplanque 2411 * bugfix 2412 * 2413 * Revision 1.80 2005/10/29 19:46:45 tor_gisvold 2414 * Bug fix for all blogger API routines - all of these errored due to lack of global cache definition 2415 * I also hope that I have fixed the pesky double line spacing done by my CVS frontend - if not I apologise and will fix. 2416 * 2417 * Revision 1.79 2005/10/23 18:14:24 tor_gisvold 2418 * Metaweblog API and Movable Type API first cut for new version of b2evolution 2419 * Tor 23102005 2420 */ 2421 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Thu Nov 29 23:58:50 2007 | par Balluche grâce à PHPXref 0.7 |
|