| [ Index ] |
PHP Cross Reference of Joomla 1.5.26 DE |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * @version $Id: blogger.php 14401 2010-01-26 14:10:00Z louis $ 4 * @package Joomla 5 * @copyright Copyright (C) 2005 - 2010 Open Source Matters. All rights reserved. 6 * @license GNU/GPL, see LICENSE.php 7 * Joomla! is free software. This version may have been modified pursuant 8 * to the GNU General Public License, and as distributed it includes or 9 * is derivative of works licensed under the GNU General Public License or 10 * other free or open source software licenses. 11 * See COPYRIGHT.php for copyright notices and details. 12 */ 13 14 // no direct access 15 defined( '_JEXEC' ) or die( 'Restricted access' ); 16 17 jimport( 'joomla.plugin.plugin' ); 18 19 class plgXMLRPCBlogger extends JPlugin 20 { 21 function plgXMLRPCBlogger(&$subject, $config) 22 { 23 parent::__construct($subject, $config); 24 $this->loadLanguage( '', JPATH_ADMINISTRATOR ); 25 } 26 27 /** 28 * @return array An array of associative arrays defining the available methods 29 */ 30 function onGetWebServices() 31 { 32 global $xmlrpcI4, $xmlrpcInt, $xmlrpcBoolean, $xmlrpcDouble, $xmlrpcString, $xmlrpcDateTime, $xmlrpcBase64, $xmlrpcArray, $xmlrpcStruct, $xmlrpcValue; 33 34 return array 35 ( 36 'blogger.getUsersBlogs' => array( 37 'function' => 'plgXMLRPCBloggerServices::getUserBlogs', 38 'docstring' => JText::_('Returns a list of weblogs to which an author has posting privileges.'), 39 'signature' => array(array($xmlrpcArray, $xmlrpcString, $xmlrpcString, $xmlrpcString )) 40 ), 41 'blogger.getUserInfo' => array( 42 'function' => 'plgXMLRPCBloggerServices::getUserInfo', 43 'docstring' => JText::_('Returns information about an author in the system.'), 44 'signature' => array(array($xmlrpcStruct, $xmlrpcString, $xmlrpcString, $xmlrpcString)) 45 ), 46 'blogger.getPost' => array( 47 'function' => 'plgXMLRPCBloggerServices::getPost', 48 'docstring' => JText::_('Returns information about a specific post.'), 49 'signature' => array(array($xmlrpcStruct, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)) 50 ), 51 'blogger.getRecentPosts' => array( 52 'function' => 'plgXMLRPCBloggerServices::getRecentPosts', 53 'docstring' => JText::_('Returns a list of the most recent posts in the system.'), 54 'signature' => array(array($xmlrpcArray, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcInt)) 55 ), 56 'blogger.getTemplate' => array( 57 'function' => 'plgXMLRPCBloggerServices::getTemplate', 58 'docstring' => '', 59 'signature' => array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)) 60 ), 61 'blogger.setTemplate' => array( 62 'function' => 'plgXMLRPCBloggerServices::setTemplate', 63 'docstring' => '', 64 'signature' => array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)) 65 ), 66 'blogger.newPost' => array( 67 'function' => 'plgXMLRPCBloggerServices::newPost', 68 'docstring' => JText::_('Creates a new post, and optionally publishes it.'), 69 'signature' => array(array($xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcBoolean)) 70 ), 71 'blogger.deletePost' => array( 72 'function' => 'plgXMLRPCBloggerServices::deletePost', 73 'docstring' => JText::_('Deletes a post.'), 74 'signature' => array(array($xmlrpcBoolean, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcBoolean)) 75 ), 76 'blogger.editPost' => array( 77 'function' => 'plgXMLRPCBloggerServices::editPost', 78 'docstring' => JText::_('Updates the information about an existing post.'), 79 'signature' => array(array($xmlrpcBoolean, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcBoolean)) 80 ) 81 ); 82 } 83 } 84 85 class plgXMLRPCBloggerServices 86 { 87 /* 88 * Note : blogger.getUsersBlogs will make more sense once we support multiple blogs 89 */ 90 function getUserBlogs($appkey, $username, $password) 91 { 92 global $mainframe, $xmlrpcerruser, $xmlrpcI4, $xmlrpcInt, $xmlrpcBoolean, $xmlrpcDouble, $xmlrpcString, $xmlrpcDateTime, $xmlrpcBase64, $xmlrpcArray, $xmlrpcStruct, $xmlrpcValue; 93 94 if(!plgXMLRPCBloggerHelper::authenticateUser($username, $password)) { 95 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_("Login Failed")); 96 } 97 98 $user =& JFactory::getUser($username); 99 plgXMLRPCBloggerHelper::getUserAid( $user ); 100 101 // Handle the access permissions part of the main database query 102 if ($user->authorize('com_content', 'edit', 'content', 'all')) { 103 $xwhere = ''; 104 } else { 105 $xwhere = ' AND a.published = 1 AND b.published = 1'; 106 } 107 $gid = $user->get('aid', 0); 108 $access_check = ' AND a.access <= '.(int) $gid . 109 ' AND b.access <= '.(int) $gid; 110 // Query of categories within section 111 $query = 'SELECT a.id, a.title, a.section, ' . 112 ' CONCAT_WS(\'/\', a.title, b.title) AS catName' . 113 ' FROM #__categories AS a' . 114 ' LEFT JOIN #__sections AS b ON a.section = b.id' . 115 $xwhere. 116 $access_check; 117 $db = &JFactory::getDBO(); 118 $db->setQuery( $query ); 119 $categories = $db->loadObjectList(); 120 $structarray = array(); 121 122 foreach( $categories AS $category ) { 123 if (intval($category->section) > 0) { 124 $blog = new xmlrpcval(array( 125 'url' => new xmlrpcval(JURI::base(), $xmlrpcString), 126 'blogid' => new xmlrpcval($category->id, $xmlrpcString), 127 'blogName' => new xmlrpcval($category->catName, $xmlrpcString) 128 ), 'struct'); 129 array_push($structarray, $blog); 130 } 131 } 132 return new xmlrpcresp(new xmlrpcval( $structarray , $xmlrpcArray)); 133 } 134 135 function getUserInfo($appkey, $username, $password) 136 { 137 global $xmlrpcerruser, $xmlrpcStruct; 138 139 if(!plgXMLRPCBloggerHelper::authenticateUser($username, $password)) { 140 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_("Login Failed")); 141 } 142 143 $user =& JFactory::getUser($username); 144 plgXMLRPCBloggerHelper::getUserAid( $user ); 145 146 $struct = new xmlrpcval( 147 array( 148 'nickname' => new xmlrpcval($user->get('username')), 149 'userid' => new xmlrpcval($user->get('id')), 150 'url' => new xmlrpcval(''), 151 'email' => new xmlrpcval($user->get('email')), 152 'lastname' => new xmlrpcval($user->get('name')), 153 'firstname' => new xmlrpcval($user->get('name')) 154 ), $xmlrpcStruct); 155 156 return new xmlrpcresp($struct); 157 } 158 159 function getPost($appkey, $postid, $username, $password) 160 { 161 global $xmlrpcerruser, $xmlrpcI4, $xmlrpcInt, $xmlrpcBoolean, $xmlrpcDouble, $xmlrpcString, $xmlrpcDateTime, $xmlrpcBase64, $xmlrpcArray, $xmlrpcStruct, $xmlrpcValue; 162 163 if(!plgXMLRPCBloggerHelper::authenticateUser($username, $password)) { 164 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_("Login Failed")); 165 } 166 167 $user =& JFactory::getUser($username); 168 plgXMLRPCBloggerHelper::getUserAid( $user ); 169 170 $db = &JFactory::getDBO(); 171 172 $where = 'a.id = ' . (int) $postid; 173 174 $canReadUnpublished = $user->authorize('com_content', 'edit', 'content', 'all'); 175 if ($canReadUnpublished) { 176 $publishedWhere = ''; 177 } else { 178 $publishedWhere = ' AND u.published = 1 AND b.published = 1'; 179 } 180 181 $nullDate = $db->getNullDate(); 182 $date =& JFactory::getDate(); 183 $now = $date->toMySQL(); 184 185 $query = 'SELECT a.title AS title,' 186 . ' a.created AS created,' 187 . ' a.introtext AS introtext,' 188 . ' a.fulltext AS ftext,' 189 . ' a.id AS id,' 190 . ' a.created_by AS created_by' 191 . ' FROM #__content AS a' 192 . ' INNER JOIN #__categories AS b ON b.id=a.catid' 193 . ' INNER JOIN #__sections AS u ON u.id = a.sectionid' 194 . ' WHERE '.$where 195 . $publishedWhere 196 . ' AND a.access <= '.(int) $user->get( 'aid' ) 197 . ' AND b.access <= '.(int) $user->get( 'aid' ) 198 . ' AND u.access <= '.(int) $user->get( 'aid' ) 199 . ' AND ( a.publish_up = '.$db->Quote($nullDate).' OR a.publish_up <= '.$db->Quote($now).' )' 200 . ' AND ( a.publish_down = '.$db->Quote($nullDate).' OR a.publish_down >= '.$db->Quote($now).' )' 201 ; 202 203 $db->setQuery( $query ); 204 $item = $db->loadObject(); 205 206 if ($item === null) { 207 return new xmlrpcresp(0, $xmlrpcerruser+2, JText::_("Access Denied")); 208 } 209 210 $content = '<title>'.$item->title.'</title>'; 211 $content .= $item->introtext.'<more_text>'.$item->ftext.'</more_text>'; 212 213 $struct = new xmlrpcval( 214 array( 215 'userid' => new xmlrpcval($item->created_by), 216 'dateCreated' => new xmlrpcval($item->created), 217 'content' => new xmlrpcval($content), 218 'postid' => new xmlrpcval($item->id) 219 ), $xmlrpcStruct); 220 221 return new xmlrpcresp($struct); 222 } 223 224 function newPost($appkey, $blogid, $username, $password, $content, $publish) 225 { 226 global $xmlrpcerruser, $xmlrpcI4, $xmlrpcInt, $xmlrpcBoolean, $xmlrpcDouble, $xmlrpcString, $xmlrpcDateTime, $xmlrpcBase64, $xmlrpcArray, $xmlrpcStruct, $xmlrpcValue; 227 228 if(!plgXMLRPCBloggerHelper::authenticateUser($username, $password)) { 229 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_("Login Failed")); 230 } 231 232 $user =& JFactory::getUser($username); 233 plgXMLRPCBloggerHelper::getUserAid( $user ); 234 235 if ($user->get('gid') < 19) { 236 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('ALERTNOTAUTH')); 237 } 238 239 // Create a user access object for the user 240 $access = new stdClass(); 241 $access->canEdit = $user->authorize('com_content', 'edit', 'content', 'all'); 242 $access->canEditOwn = $user->authorize('com_content', 'edit', 'content', 'own'); 243 $access->canPublish = $user->authorize('com_content', 'publish', 'content', 'all'); 244 245 if (!($access->canEdit || $access->canEditOwn)) { 246 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('ALERTNOTAUTH')); 247 } 248 249 $db =& JFactory::getDBO(); 250 251 // load plugin params info 252 $plugin =& JPluginHelper::getPlugin('xmlrpc','blogger'); 253 $params = new JParameter( $plugin->params ); 254 255 $blogid = (int) $blogid; 256 257 // load the category 258 $cat =& JTable::getInstance('category'); 259 $cat->load($blogid); 260 261 // create a new content item 262 $item =& JTable::getInstance('content'); 263 264 $item->title = plgXMLRPCBloggerHelper::getPostTitle($content); 265 $item->introtext = plgXMLRPCBloggerHelper::getPostIntroText($content); 266 $item->fulltext = plgXMLRPCBloggerHelper::getPostFullText($content); 267 268 $item->catid = $blogid; 269 $item->sectionid = $cat->section; 270 271 $date =& JFactory::getDate(); 272 273 $item->created = $date->toMySQL(); 274 $item->created_by = $user->get('id'); 275 276 $item->publish_up = $date->toMySQL(); 277 $item->publish_down = $db->getNullDate(); 278 279 $item->state = ($publish && $access->canPublish) ? 1 : 0; 280 281 if (!$item->check()) { 282 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Post check failed') ); 283 } 284 285 $item->version++; 286 287 if (!$item->store()) { 288 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Post store failed') ); 289 } 290 291 return new xmlrpcresp(new xmlrpcval($item->id, $xmlrpcString)); 292 } 293 294 function editPost($appkey, $postid, $username, $password, $content, $publish) 295 { 296 global $xmlrpcerruser, $xmlrpcI4, $xmlrpcInt, $xmlrpcBoolean, $xmlrpcDouble, $xmlrpcString, $xmlrpcDateTime, $xmlrpcBase64, $xmlrpcArray, $xmlrpcStruct, $xmlrpcValue; 297 298 if(!plgXMLRPCBloggerHelper::authenticateUser($username, $password)) { 299 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_("Login Failed")); 300 } 301 302 $user =& JFactory::getUser($username); 303 plgXMLRPCBloggerHelper::getUserAid( $user ); 304 305 // Create a user access object for the user 306 $access = new stdClass(); 307 $access->canEdit = $user->authorize('com_content', 'edit', 'content', 'all'); 308 $access->canEditOwn = $user->authorize('com_content', 'edit', 'content', 'own'); 309 $access->canPublish = $user->authorize('com_content', 'publish', 'content', 'all'); 310 311 if (!($access->canEdit || $access->canEditOwn)) { 312 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('ALERTNOTAUTH')); 313 } 314 315 // load the row from the db table 316 $item =& JTable::getInstance('content'); 317 if(!$item->load( $postid )) { 318 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Sorry, no such post') ); 319 } 320 321 if($item->isCheckedOut($user->get('id'))) { 322 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Sorry, post is already being edited') ); 323 } 324 325 //lock the item 326 $item->checkout($user->id); 327 328 $item->title = plgXMLRPCBloggerHelper::getPostTitle($content); 329 $item->introtext = plgXMLRPCBloggerHelper::getPostIntroText($content); 330 $item->fulltext = plgXMLRPCBloggerHelper::getPostFullText($content); 331 332 if (!$item->check()) { 333 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Post check failed') ); 334 } 335 336 $item->version++; 337 338 if (!$item->store()) { 339 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Post store failed') ); 340 } 341 342 $item->state = ($publish && $access->canPublish) ? 1 : 0; 343 344 //lock the item 345 $item->checkout(); 346 347 return new xmlrpcresp(new xmlrpcval('true', $xmlrpcBoolean)); 348 } 349 350 function deletePost($appkey, $postid, $username, $password, $publish) 351 { 352 global $xmlrpcerruser, $xmlrpcI4, $xmlrpcInt, $xmlrpcBoolean, $xmlrpcDouble, $xmlrpcString, $xmlrpcDateTime, $xmlrpcBase64, $xmlrpcArray, $xmlrpcStruct, $xmlrpcValue; 353 354 if(!plgXMLRPCBloggerHelper::authenticateUser($username, $password)) { 355 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_("Login Failed")); 356 } 357 358 $user =& JFactory::getUser($username); 359 plgXMLRPCBloggerHelper::getUserAid( $user ); 360 361 if ($user->get('gid') < 23) { 362 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('ALERTNOTAUTH')); 363 } 364 365 // load the row from the db table 366 $item =& JTable::getInstance('content'); 367 if(!$item->load( $postid )) { 368 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Sorry, no such post') ); 369 } 370 371 if($item->isCheckedOut($user->get('id'))) { 372 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Sorry, post is already being edited') ); 373 } 374 375 //lock the item 376 $item->checkout(); 377 378 $item->state = -2; 379 $item->ordering = 0; 380 381 if (!$item->store()) { 382 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Post delete failed') ); 383 } 384 385 return new xmlrpcresp(new xmlrpcval('true', $xmlrpcBoolean)); 386 } 387 388 389 /** 390 * Blogger API - blogger.getRecentPosts 391 * 392 * @param xmlrpcmessage XML-RPC message passed to the method 393 * @return xmlrpcresp XML-RPC response 394 */ 395 function getRecentPosts($appkey, $blogid, $username, $password, $numposts) 396 { 397 global $xmlrpcerruser, $xmlrpcI4, $xmlrpcInt, $xmlrpcBoolean, $xmlrpcDouble, $xmlrpcString, $xmlrpcDateTime, $xmlrpcBase64, $xmlrpcArray, $xmlrpcStruct, $xmlrpcValue; 398 399 if(!plgXMLRPCBloggerHelper::authenticateUser($username, $password)) { 400 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_("Login Failed")); 401 } 402 403 $user =& JFactory::getUser($username); 404 plgXMLRPCBloggerHelper::getUserAid( $user ); 405 406 // load plugin params info 407 $plugin =& JPluginHelper::getPlugin('xmlrpc','blogger'); 408 $params = new JParameter( $plugin->params ); 409 410 $db =& JFactory::getDBO(); 411 412 $nullDate = $db->getNullDate(); 413 $date =& JFactory::getDate(); 414 $now = $date->toMySQL(); 415 416 $blogid = (int) $blogid; 417 418 $canReadUnpublished = $user->authorize('com_content', 'edit', 'content', 'all'); 419 if ($canReadUnpublished) { 420 $publishedWhere = ''; 421 $publishTimeWhere = ''; 422 } else { 423 $publishedWhere = ' AND u.published = 1 AND b.published = 1'; 424 $publishTimeWhere = ' AND ( a.publish_up = '.$db->Quote($nullDate).' OR a.publish_up <= '.$db->Quote($now).' )' 425 . ' AND ( a.publish_down = '.$db->Quote($nullDate).' OR a.publish_down >= '.$db->Quote($now).' )'; 426 } 427 428 $query = 'SELECT a.title AS title,' 429 . ' a.created AS created,' 430 . ' a.introtext AS introtext,' 431 . ' a.fulltext AS ftext,' 432 . ' a.id AS id,' 433 . ' a.created_by AS created_by' 434 . ' FROM #__content AS a' 435 . ' INNER JOIN #__categories AS b ON b.id=a.catid' 436 . ' INNER JOIN #__sections AS u ON u.id = a.sectionid' 437 . ' WHERE a.catid = '. $blogid 438 . $publishedWhere 439 . ' AND a.access <= '.(int) $user->get( 'aid' ) 440 . ' AND b.access <= '.(int) $user->get( 'aid' ) 441 . ' AND u.access <= '.(int) $user->get( 'aid' ) 442 . $publishTimeWhere 443 ; 444 445 $db->setQuery($query, 0, $numposts); 446 $items = $db->loadObjectList(); 447 448 if ($items === null) { 449 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('No posts available, or an error has occured.') ); 450 } 451 452 $structArray = array(); 453 foreach ($items as $item) 454 { 455 $content = '<title>'.$item->title.'</title>'; 456 $content .= $item->introtext.'<more_text>'.$item->ftext.'</more_text>'; 457 458 $structArray[] = new xmlrpcval(array( 459 'userid' => new xmlrpcval($item->created_by), 460 'dateCreated' => new xmlrpcval($item->created), 461 'content' => new xmlrpcval($content), 462 'postid' => new xmlrpcval($item->id) 463 ), 'struct'); 464 } 465 466 return new xmlrpcresp(new xmlrpcval( $structArray , $xmlrpcArray)); 467 } 468 469 function getTemplate($appkey, $blogid, $username, $password, $templateType) 470 { 471 global $xmlrpcerruser; 472 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Method not implemented') ); 473 } 474 475 function setTemplate($appkey, $blogid, $username, $password, $template, $templateType) 476 { 477 global $xmlrpcerruser; 478 return new xmlrpcresp(0, $xmlrpcerruser+1, JText::_('Method not implemented') ); 479 } 480 } 481 482 class plgXMLRPCBloggerHelper 483 { 484 function getUserAid( &$user ) { 485 486 $acl = &JFactory::getACL(); 487 488 //Get the user group from the ACL 489 $grp = $acl->getAroGroup($user->get('id')); 490 491 // Mark the user as logged in 492 $user->set('guest', 0); 493 $user->set('aid', 1); 494 495 // Fudge Authors, Editors, Publishers and Super Administrators into the special access group 496 if ($acl->is_group_child_of($grp->name, 'Registered') || 497 $acl->is_group_child_of($grp->name, 'Public Backend')) { 498 $user->set('aid', 2); 499 } 500 } 501 502 function authenticateUser($username, $password) 503 { 504 // Get the global JAuthentication object 505 jimport( 'joomla.user.authentication'); 506 $auth = & JAuthentication::getInstance(); 507 $credentials = array( 'username' => $username, 'password' => $password ); 508 $options = array(); 509 $response = $auth->authenticate($credentials, $options); 510 return $response->status === JAUTHENTICATE_STATUS_SUCCESS; 511 } 512 513 function getPostTitle($content) 514 { 515 $title = ''; 516 if ( preg_match('/<title>(.+?)<\/title>/is', $content, $matchtitle) ) 517 { 518 $title = $matchtitle[0]; 519 $title = preg_replace('/<title>/si', '', $title); 520 $title = preg_replace('/<\/title>/si', '', $title); 521 } 522 if (empty( $title )) { 523 $title = substr( $content, 0, 20 ); 524 } 525 return $title; 526 } 527 528 function getPostCategory($content) 529 { 530 $category = 0; 531 532 $match = array(); 533 if ( preg_match('/<category>(.+?)<\/category>/is', $content, $match) ) 534 { 535 $category = trim($match[1], ','); 536 $category = explode(',', $category); 537 } 538 539 return $category; 540 } 541 542 function getPostIntroText($content) 543 { 544 return plgXMLRPCBloggerHelper::removePostData($content); //substr($string, 0, strpos($string, '<more_text>')); 545 } 546 547 function getPostFullText($content) 548 { 549 $match = array(); 550 if ( preg_match('/<more_text>(.+?)<\/more_text>/is', $content, $match) ) 551 { 552 $fulltext = $match[0]; 553 $fulltext = preg_replace('/<more_text>/si', '', $fulltext); 554 $fulltext = preg_replace('/<\/more_text>/si', '', $fulltext); 555 } 556 557 return $fulltext; 558 } 559 560 function removePostData($content) 561 { 562 $content = preg_replace('/<title>(.+?)<\/title>/si', '', $content); 563 $content = preg_replace('/<category>(.+?)<\/category>/si', '', $content); 564 $content = preg_replace('/<more_text>(.+?)<\/more_text>/si', '', $content); 565 $content = trim($content); 566 return $content; 567 } 568 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Mar 28 15:54:07 2012 | Cross-referenced by PHPXref 0.7.1 |