[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Unit tests for blog 19 * 20 * @package core_blog 21 * @category phpunit 22 * @copyright 2009 Nicolas Connault 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 global $CFG; 27 require_once($CFG->dirroot . '/blog/locallib.php'); 28 require_once($CFG->dirroot . '/blog/lib.php'); 29 30 /** 31 * Test functions that rely on the DB tables 32 */ 33 class core_blog_lib_testcase extends advanced_testcase { 34 35 private $courseid; 36 private $cmid; 37 private $groupid; 38 private $userid; 39 private $tagid; 40 private $postid; 41 42 protected function setUp() { 43 global $DB; 44 parent::setUp(); 45 46 $this->resetAfterTest(); 47 48 // Create default course. 49 $course = $this->getDataGenerator()->create_course(array('category' => 1, 'shortname' => 'ANON')); 50 $this->assertNotEmpty($course); 51 $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id)); 52 $this->assertNotEmpty($page); 53 54 // Create default group. 55 $group = new stdClass(); 56 $group->courseid = $course->id; 57 $group->name = 'ANON'; 58 $group->id = $DB->insert_record('groups', $group); 59 60 // Create default user. 61 $user = $this->getDataGenerator()->create_user(array( 62 'username' => 'testuser', 63 'firstname' => 'Jimmy', 64 'lastname' => 'Kinnon' 65 )); 66 67 // Create default tag. 68 $tag = $this->getDataGenerator()->create_tag(array('userid' => $user->id, 69 'rawname' => 'Testtagname', 'isstandard' => 1)); 70 71 // Create default post. 72 $post = new stdClass(); 73 $post->userid = $user->id; 74 $post->groupid = $group->id; 75 $post->content = 'test post content text'; 76 $post->module = 'blog'; 77 $post->id = $DB->insert_record('post', $post); 78 79 // Grab important ids. 80 $this->courseid = $course->id; 81 $this->cmid = $page->cmid; 82 $this->groupid = $group->id; 83 $this->userid = $user->id; 84 $this->tagid = $tag->id; 85 $this->postid = $post->id; 86 } 87 88 89 public function test_overrides() { 90 global $SITE; 91 92 // Try all the filters at once: Only the entry filter is active. 93 $filters = array('site' => $SITE->id, 'course' => $this->courseid, 'module' => $this->cmid, 94 'group' => $this->groupid, 'user' => $this->userid, 'tag' => $this->tagid, 'entry' => $this->postid); 95 $bloglisting = new blog_listing($filters); 96 $this->assertFalse(array_key_exists('site', $bloglisting->filters)); 97 $this->assertFalse(array_key_exists('course', $bloglisting->filters)); 98 $this->assertFalse(array_key_exists('module', $bloglisting->filters)); 99 $this->assertFalse(array_key_exists('group', $bloglisting->filters)); 100 $this->assertFalse(array_key_exists('user', $bloglisting->filters)); 101 $this->assertFalse(array_key_exists('tag', $bloglisting->filters)); 102 $this->assertTrue(array_key_exists('entry', $bloglisting->filters)); 103 104 // Again, but without the entry filter: This time, the tag, user and module filters are active. 105 $filters = array('site' => $SITE->id, 'course' => $this->courseid, 'module' => $this->cmid, 106 'group' => $this->groupid, 'user' => $this->userid, 'tag' => $this->postid); 107 $bloglisting = new blog_listing($filters); 108 $this->assertFalse(array_key_exists('site', $bloglisting->filters)); 109 $this->assertFalse(array_key_exists('course', $bloglisting->filters)); 110 $this->assertFalse(array_key_exists('group', $bloglisting->filters)); 111 $this->assertTrue(array_key_exists('module', $bloglisting->filters)); 112 $this->assertTrue(array_key_exists('user', $bloglisting->filters)); 113 $this->assertTrue(array_key_exists('tag', $bloglisting->filters)); 114 115 // We should get the same result by removing the 3 inactive filters: site, course and group. 116 $filters = array('module' => $this->cmid, 'user' => $this->userid, 'tag' => $this->tagid); 117 $bloglisting = new blog_listing($filters); 118 $this->assertFalse(array_key_exists('site', $bloglisting->filters)); 119 $this->assertFalse(array_key_exists('course', $bloglisting->filters)); 120 $this->assertFalse(array_key_exists('group', $bloglisting->filters)); 121 $this->assertTrue(array_key_exists('module', $bloglisting->filters)); 122 $this->assertTrue(array_key_exists('user', $bloglisting->filters)); 123 $this->assertTrue(array_key_exists('tag', $bloglisting->filters)); 124 125 } 126 127 // The following series of 'test_blog..' functions correspond to the blog_get_headers() function within blog/lib.php. 128 // Some cases are omitted due to the optional_param variables used. 129 130 public function test_blog_get_headers_case_1() { 131 global $CFG, $PAGE, $OUTPUT; 132 $blogheaders = blog_get_headers(); 133 $this->assertEquals($blogheaders['heading'], get_string('siteblogheading', 'blog')); 134 } 135 136 public function test_blog_get_headers_case_6() { 137 global $CFG, $PAGE, $OUTPUT; 138 $blogheaders = blog_get_headers($this->courseid, null, $this->userid); 139 $this->assertNotEquals($blogheaders['heading'], ''); 140 } 141 142 public function test_blog_get_headers_case_7() { 143 global $CFG, $PAGE, $OUTPUT; 144 $blogheaders = blog_get_headers(null, $this->groupid); 145 $this->assertNotEquals($blogheaders['heading'], ''); 146 } 147 148 public function test_blog_get_headers_case_10() { 149 global $CFG, $PAGE, $OUTPUT; 150 $blogheaders = blog_get_headers($this->courseid); 151 $this->assertNotEquals($blogheaders['heading'], ''); 152 } 153 154 /** 155 * Test various blog related events. 156 */ 157 public function test_blog_entry_created_event() { 158 global $USER; 159 160 $this->setAdminUser(); 161 $this->resetAfterTest(); 162 163 // Create a blog entry for another user as Admin. 164 $sink = $this->redirectEvents(); 165 $blog = new blog_entry(); 166 $blog->subject = "Subject of blog"; 167 $blog->userid = $this->userid; 168 $states = blog_entry::get_applicable_publish_states(); 169 $blog->publishstate = reset($states); 170 $blog->add(); 171 $events = $sink->get_events(); 172 $sink->close(); 173 $event = reset($events); 174 $sitecontext = context_system::instance(); 175 176 // Validate event data. 177 $this->assertInstanceOf('\core\event\blog_entry_created', $event); 178 $url = new moodle_url('/blog/index.php', array('entryid' => $event->objectid)); 179 $this->assertEquals($url, $event->get_url()); 180 $this->assertEquals($sitecontext->id, $event->contextid); 181 $this->assertEquals($blog->id, $event->objectid); 182 $this->assertEquals($USER->id, $event->userid); 183 $this->assertEquals($this->userid, $event->relateduserid); 184 $this->assertEquals("post", $event->objecttable); 185 $arr = array(SITEID, 'blog', 'add', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id, $blog->subject); 186 $this->assertEventLegacyLogData($arr, $event); 187 $this->assertEquals("blog_entry_added", $event->get_legacy_eventname()); 188 $this->assertEventLegacyData($blog, $event); 189 $this->assertEventContextNotUsed($event); 190 } 191 192 /** 193 * Tests for event blog_entry_updated. 194 */ 195 public function test_blog_entry_updated_event() { 196 global $USER; 197 198 $this->setAdminUser(); 199 $this->resetAfterTest(); 200 $sitecontext = context_system::instance(); 201 202 // Edit a blog entry as Admin. 203 $blog = new blog_entry($this->postid); 204 $sink = $this->redirectEvents(); 205 $blog->summary_editor = array('text' => 'Something', 'format' => FORMAT_MOODLE); 206 $blog->edit(array(), null, array(), array()); 207 $events = $sink->get_events(); 208 $event = array_pop($events); 209 $sink->close(); 210 211 // Validate event data. 212 $this->assertInstanceOf('\core\event\blog_entry_updated', $event); 213 $url = new moodle_url('/blog/index.php', array('entryid' => $event->objectid)); 214 $this->assertEquals($url, $event->get_url()); 215 $this->assertEquals($sitecontext->id, $event->contextid); 216 $this->assertEquals($blog->id, $event->objectid); 217 $this->assertEquals($USER->id, $event->userid); 218 $this->assertEquals($this->userid, $event->relateduserid); 219 $this->assertEquals("post", $event->objecttable); 220 $this->assertEquals("blog_entry_edited", $event->get_legacy_eventname()); 221 $this->assertEventLegacyData($blog, $event); 222 $arr = array (SITEID, 'blog', 'update', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id, $blog->subject); 223 $this->assertEventLegacyLogData($arr, $event); 224 $this->assertEventContextNotUsed($event); 225 } 226 227 /** 228 * Tests for event blog_entry_deleted. 229 */ 230 public function test_blog_entry_deleted_event() { 231 global $USER, $DB; 232 233 $this->setAdminUser(); 234 $this->resetAfterTest(); 235 $sitecontext = context_system::instance(); 236 237 // Delete a user blog entry as Admin. 238 $blog = new blog_entry($this->postid); 239 $sink = $this->redirectEvents(); 240 $record = $DB->get_record('post', array('id' => $blog->id)); 241 $blog->delete(); 242 $events = $sink->get_events(); 243 $event = array_pop($events); 244 $sink->close(); 245 246 // Validate event data. 247 $this->assertInstanceOf('\core\event\blog_entry_deleted', $event); 248 $this->assertEquals(null, $event->get_url()); 249 $this->assertEquals($sitecontext->id, $event->contextid); 250 $this->assertEquals($blog->id, $event->objectid); 251 $this->assertEquals($USER->id, $event->userid); 252 $this->assertEquals($this->userid, $event->relateduserid); 253 $this->assertEquals("post", $event->objecttable); 254 $this->assertEquals($record, $event->get_record_snapshot("post", $blog->id)); 255 $this->assertSame('blog_entry_deleted', $event->get_legacy_eventname()); 256 $arr = array(SITEID, 'blog', 'delete', 'index.php?userid=' . $blog->userid, 'deleted blog entry with entry id# ' . 257 $blog->id); 258 $this->assertEventLegacyLogData($arr, $event); 259 $this->assertEventLegacyData($blog, $event); 260 $this->assertEventContextNotUsed($event); 261 } 262 263 264 /** 265 * Tests for event blog_association_created. 266 */ 267 public function test_blog_association_created_event() { 268 global $USER; 269 270 $this->setAdminUser(); 271 $this->resetAfterTest(); 272 $sitecontext = context_system::instance(); 273 $coursecontext = context_course::instance($this->courseid); 274 $contextmodule = context_module::instance($this->cmid); 275 276 // Add blog associations with a course. 277 $blog = new blog_entry($this->postid); 278 $sink = $this->redirectEvents(); 279 $blog->add_association($coursecontext->id); 280 $events = $sink->get_events(); 281 $event = reset($events); 282 $sink->close(); 283 284 // Validate event data. 285 $this->assertInstanceOf('\core\event\blog_association_created', $event); 286 $this->assertEquals($sitecontext->id, $event->contextid); 287 $url = new moodle_url('/blog/index.php', array('entryid' => $event->other['blogid'])); 288 $this->assertEquals($url, $event->get_url()); 289 $this->assertEquals($blog->id, $event->other['blogid']); 290 $this->assertEquals($this->courseid, $event->other['associateid']); 291 $this->assertEquals('course', $event->other['associatetype']); 292 $this->assertEquals($blog->subject, $event->other['subject']); 293 $this->assertEquals($USER->id, $event->userid); 294 $this->assertEquals($this->userid, $event->relateduserid); 295 $this->assertEquals('blog_association', $event->objecttable); 296 $arr = array(SITEID, 'blog', 'add association', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id, 297 $blog->subject, 0, $this->userid); 298 $this->assertEventLegacyLogData($arr, $event); 299 300 // Add blog associations with a module. 301 $blog = new blog_entry($this->postid); 302 $sink = $this->redirectEvents(); 303 $blog->add_association($contextmodule->id); 304 $events = $sink->get_events(); 305 $event = reset($events); 306 $sink->close(); 307 308 // Validate event data. 309 $this->assertEquals($blog->id, $event->other['blogid']); 310 $this->assertEquals($this->cmid, $event->other['associateid']); 311 $this->assertEquals('coursemodule', $event->other['associatetype']); 312 $arr = array(SITEID, 'blog', 'add association', 'index.php?userid=' . $this->userid . '&entryid=' . $blog->id, 313 $blog->subject, $this->cmid, $this->userid); 314 $this->assertEventLegacyLogData($arr, $event); 315 $this->assertEventContextNotUsed($event); 316 } 317 318 /** 319 * Tests for event blog_association_created validations. 320 */ 321 public function test_blog_association_created_event_validations() { 322 323 $this->resetAfterTest(); 324 325 // Make sure associatetype validations work. 326 try { 327 \core\event\blog_association_created::create(array( 328 'contextid' => 1, 329 'objectid' => 3, 330 'relateduserid' => 2, 331 'other' => array('associateid' => 2 , 'blogid' => 3, 'subject' => 'blog subject'))); 332 } catch (coding_exception $e) { 333 $this->assertContains('The \'associatetype\' value must be set in other and be a valid type.', $e->getMessage()); 334 } 335 try { 336 \core\event\blog_association_created::create(array( 337 'contextid' => 1, 338 'objectid' => 3, 339 'relateduserid' => 2, 340 'other' => array('associateid' => 2 , 'blogid' => 3, 'associatetype' => 'random', 'subject' => 'blog subject'))); 341 } catch (coding_exception $e) { 342 $this->assertContains('The \'associatetype\' value must be set in other and be a valid type.', $e->getMessage()); 343 } 344 // Make sure associateid validations work. 345 try { 346 \core\event\blog_association_created::create(array( 347 'contextid' => 1, 348 'objectid' => 3, 349 'relateduserid' => 2, 350 'other' => array('blogid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject'))); 351 } catch (coding_exception $e) { 352 $this->assertContains('The \'associateid\' value must be set in other.', $e->getMessage()); 353 } 354 // Make sure blogid validations work. 355 try { 356 \core\event\blog_association_created::create(array( 357 'contextid' => 1, 358 'objectid' => 3, 359 'relateduserid' => 2, 360 'other' => array('associateid' => 3, 'associatetype' => 'course', 'subject' => 'blog subject'))); 361 } catch (coding_exception $e) { 362 $this->assertContains('The \'blogid\' value must be set in other.', $e->getMessage()); 363 } 364 // Make sure blogid validations work. 365 try { 366 \core\event\blog_association_created::create(array( 367 'contextid' => 1, 368 'objectid' => 3, 369 'relateduserid' => 2, 370 'other' => array('blogid' => 3, 'associateid' => 3, 'associatetype' => 'course'))); 371 } catch (coding_exception $e) { 372 $this->assertContains('The \'subject\' value must be set in other.', $e->getMessage()); 373 } 374 } 375 376 /** 377 * Tests for event blog_entries_viewed. 378 */ 379 public function test_blog_entries_viewed_event() { 380 381 $this->setAdminUser(); 382 383 $other = array('entryid' => $this->postid, 'tagid' => $this->tagid, 'userid' => $this->userid, 'modid' => $this->cmid, 384 'groupid' => $this->groupid, 'courseid' => $this->courseid, 'search' => 'search', 'fromstart' => 2); 385 386 // Trigger event. 387 $sink = $this->redirectEvents(); 388 $eventparams = array('other' => $other); 389 $eventinst = \core\event\blog_entries_viewed::create($eventparams); 390 $eventinst->trigger(); 391 $events = $sink->get_events(); 392 $event = reset($events); 393 $sink->close(); 394 395 // Validate event data. 396 $url = new moodle_url('/blog/index.php', $other); 397 $url2 = new moodle_url('index.php', $other); 398 $this->assertEquals($url, $event->get_url()); 399 $arr = array(SITEID, 'blog', 'view', $url2->out(), 'view blog entry'); 400 $this->assertEventLegacyLogData($arr, $event); 401 $this->assertEventContextNotUsed($event); 402 } 403 404 /** 405 * Test comment_created event. 406 */ 407 public function test_blog_comment_created_event() { 408 global $USER, $CFG; 409 410 $this->setAdminUser(); 411 412 require_once($CFG->dirroot . '/comment/lib.php'); 413 $context = context_user::instance($USER->id); 414 415 $cmt = new stdClass(); 416 $cmt->context = $context; 417 $cmt->courseid = $this->courseid; 418 $cmt->area = 'format_blog'; 419 $cmt->itemid = $this->postid; 420 $cmt->showcount = 1; 421 $cmt->component = 'blog'; 422 $manager = new comment($cmt); 423 424 // Triggering and capturing the event. 425 $sink = $this->redirectEvents(); 426 $manager->add("New comment"); 427 $events = $sink->get_events(); 428 $this->assertCount(1, $events); 429 $event = reset($events); 430 431 // Checking that the event contains the expected values. 432 $this->assertInstanceOf('\core\event\blog_comment_created', $event); 433 $this->assertEquals($context, $event->get_context()); 434 $this->assertEquals($this->postid, $event->other['itemid']); 435 $url = new moodle_url('/blog/index.php', array('entryid' => $this->postid)); 436 $this->assertEquals($url, $event->get_url()); 437 $this->assertEventContextNotUsed($event); 438 } 439 440 /** 441 * Test comment_deleted event. 442 */ 443 public function test_blog_comment_deleted_event() { 444 global $USER, $CFG; 445 446 $this->setAdminUser(); 447 448 require_once($CFG->dirroot . '/comment/lib.php'); 449 $context = context_user::instance($USER->id); 450 451 $cmt = new stdClass(); 452 $cmt->context = $context; 453 $cmt->courseid = $this->courseid; 454 $cmt->area = 'format_blog'; 455 $cmt->itemid = $this->postid; 456 $cmt->showcount = 1; 457 $cmt->component = 'blog'; 458 $manager = new comment($cmt); 459 $newcomment = $manager->add("New comment"); 460 461 // Triggering and capturing the event. 462 $sink = $this->redirectEvents(); 463 $manager->delete($newcomment->id); 464 $events = $sink->get_events(); 465 $this->assertCount(1, $events); 466 $event = reset($events); 467 468 // Checking that the event contains the expected values. 469 $this->assertInstanceOf('\core\event\blog_comment_deleted', $event); 470 $this->assertEquals($context, $event->get_context()); 471 $this->assertEquals($this->postid, $event->other['itemid']); 472 $url = new moodle_url('/blog/index.php', array('entryid' => $this->postid)); 473 $this->assertEquals($url, $event->get_url()); 474 $this->assertEventContextNotUsed($event); 475 } 476 477 /** 478 * Tests the core_blog_myprofile_navigation() function. 479 */ 480 public function test_core_blog_myprofile_navigation() { 481 global $USER; 482 483 // Set up the test. 484 $tree = new \core_user\output\myprofile\tree(); 485 $this->setAdminUser(); 486 $iscurrentuser = true; 487 $course = null; 488 489 // Enable blogs. 490 set_config('enableblogs', true); 491 492 // Check the node tree is correct. 493 core_blog_myprofile_navigation($tree, $USER, $iscurrentuser, $course); 494 $reflector = new ReflectionObject($tree); 495 $nodes = $reflector->getProperty('nodes'); 496 $nodes->setAccessible(true); 497 $this->assertArrayHasKey('blogs', $nodes->getValue($tree)); 498 } 499 500 /** 501 * Tests the core_blog_myprofile_navigation() function as a guest. 502 */ 503 public function test_core_blog_myprofile_navigation_as_guest() { 504 global $USER; 505 506 // Set up the test. 507 $tree = new \core_user\output\myprofile\tree(); 508 $iscurrentuser = false; 509 $course = null; 510 511 // Set user as guest. 512 $this->setGuestUser(); 513 514 // Check the node tree is correct. 515 core_blog_myprofile_navigation($tree, $USER, $iscurrentuser, $course); 516 $reflector = new ReflectionObject($tree); 517 $nodes = $reflector->getProperty('nodes'); 518 $nodes->setAccessible(true); 519 $this->assertArrayNotHasKey('blogs', $nodes->getValue($tree)); 520 } 521 522 /** 523 * Tests the core_blog_myprofile_navigation() function when blogs are disabled. 524 */ 525 public function test_core_blog_myprofile_navigation_blogs_disabled() { 526 global $USER; 527 528 // Set up the test. 529 $tree = new \core_user\output\myprofile\tree(); 530 $this->setAdminUser(); 531 $iscurrentuser = false; 532 $course = null; 533 534 // Disable blogs. 535 set_config('enableblogs', false); 536 537 // Check the node tree is correct. 538 core_blog_myprofile_navigation($tree, $USER, $iscurrentuser, $course); 539 $reflector = new ReflectionObject($tree); 540 $nodes = $reflector->getProperty('nodes'); 541 $nodes->setAccessible(true); 542 $this->assertArrayNotHasKey('blogs', $nodes->getValue($tree)); 543 } 544 545 public function test_blog_get_listing_course() { 546 $this->setAdminUser(); 547 $coursecontext = context_course::instance($this->courseid); 548 $anothercourse = $this->getDataGenerator()->create_course(); 549 550 // Add blog associations with a course. 551 $blog = new blog_entry($this->postid); 552 $blog->add_association($coursecontext->id); 553 554 // There is one entry associated with a course. 555 $bloglisting = new blog_listing(array('course' => $this->courseid)); 556 $this->assertCount(1, $bloglisting->get_entries()); 557 558 // There is no entry associated with a wrong course. 559 $bloglisting = new blog_listing(array('course' => $anothercourse->id)); 560 $this->assertCount(0, $bloglisting->get_entries()); 561 562 // There is no entry associated with a module. 563 $bloglisting = new blog_listing(array('module' => $this->cmid)); 564 $this->assertCount(0, $bloglisting->get_entries()); 565 566 // There is one entry associated with a site (id is ignored). 567 $bloglisting = new blog_listing(array('site' => 12345)); 568 $this->assertCount(1, $bloglisting->get_entries()); 569 570 // There is one entry associated with course context. 571 $bloglisting = new blog_listing(array('context' => $coursecontext->id)); 572 $this->assertCount(1, $bloglisting->get_entries()); 573 } 574 575 public function test_blog_get_listing_module() { 576 $this->setAdminUser(); 577 $coursecontext = context_course::instance($this->courseid); 578 $contextmodule = context_module::instance($this->cmid); 579 $anothermodule = $this->getDataGenerator()->create_module('page', array('course' => $this->courseid)); 580 581 // Add blog associations with a course. 582 $blog = new blog_entry($this->postid); 583 $blog->add_association($contextmodule->id); 584 585 // There is no entry associated with a course. 586 $bloglisting = new blog_listing(array('course' => $this->courseid)); 587 $this->assertCount(0, $bloglisting->get_entries()); 588 589 // There is one entry associated with a module. 590 $bloglisting = new blog_listing(array('module' => $this->cmid)); 591 $this->assertCount(1, $bloglisting->get_entries()); 592 593 // There is no entry associated with a wrong module. 594 $bloglisting = new blog_listing(array('module' => $anothermodule->cmid)); 595 $this->assertCount(0, $bloglisting->get_entries()); 596 597 // There is one entry associated with a site (id is ignored). 598 $bloglisting = new blog_listing(array('site' => 12345)); 599 $this->assertCount(1, $bloglisting->get_entries()); 600 601 // There is one entry associated with course context (module is a subcontext of a course). 602 $bloglisting = new blog_listing(array('context' => $coursecontext->id)); 603 $this->assertCount(1, $bloglisting->get_entries()); 604 } 605 } 606
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |