[ 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 * Group external PHPunit tests 19 * 20 * @package core_group 21 * @category external 22 * @copyright 2012 Jerome Mouneyrac 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 * @since Moodle 2.4 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 global $CFG; 30 31 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 32 require_once($CFG->dirroot . '/group/externallib.php'); 33 require_once($CFG->dirroot . '/group/lib.php'); 34 35 class core_group_externallib_testcase extends externallib_advanced_testcase { 36 37 /** 38 * Test create_groups 39 * 40 * @expectedException required_capability_exception 41 */ 42 public function test_create_groups() { 43 global $DB; 44 45 $this->resetAfterTest(true); 46 47 $course = self::getDataGenerator()->create_course(); 48 49 $group1 = array(); 50 $group1['courseid'] = $course->id; 51 $group1['name'] = 'Group Test 1'; 52 $group1['description'] = 'Group Test 1 description'; 53 $group1['descriptionformat'] = FORMAT_MOODLE; 54 $group1['enrolmentkey'] = 'Test group enrol secret phrase'; 55 $group1['idnumber'] = 'TEST1'; 56 $group2 = array(); 57 $group2['courseid'] = $course->id; 58 $group2['name'] = 'Group Test 2'; 59 $group2['description'] = 'Group Test 2 description'; 60 $group3 = array(); 61 $group3['courseid'] = $course->id; 62 $group3['name'] = 'Group Test 3'; 63 $group3['description'] = 'Group Test 3 description'; 64 $group3['idnumber'] = 'TEST1'; 65 $group4 = array(); 66 $group4['courseid'] = $course->id; 67 $group4['name'] = 'Group Test 4'; 68 $group4['description'] = 'Group Test 4 description'; 69 70 // Set the required capabilities by the external function 71 $context = context_course::instance($course->id); 72 $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id); 73 $this->assignUserCapability('moodle/course:view', $context->id, $roleid); 74 75 // Call the external function. 76 $groups = core_group_external::create_groups(array($group1, $group2)); 77 78 // We need to execute the return values cleaning process to simulate the web service server. 79 $groups = external_api::clean_returnvalue(core_group_external::create_groups_returns(), $groups); 80 81 // Checks against DB values 82 $this->assertEquals(2, count($groups)); 83 foreach ($groups as $group) { 84 $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST); 85 switch ($dbgroup->name) { 86 case $group1['name']: 87 $groupdescription = $group1['description']; 88 $groupcourseid = $group1['courseid']; 89 $this->assertEquals($dbgroup->descriptionformat, $group1['descriptionformat']); 90 $this->assertEquals($dbgroup->enrolmentkey, $group1['enrolmentkey']); 91 $this->assertEquals($dbgroup->idnumber, $group1['idnumber']); 92 break; 93 case $group2['name']: 94 $groupdescription = $group2['description']; 95 $groupcourseid = $group2['courseid']; 96 break; 97 default: 98 throw new moodle_exception('unknowgroupname'); 99 break; 100 } 101 $this->assertEquals($dbgroup->description, $groupdescription); 102 $this->assertEquals($dbgroup->courseid, $groupcourseid); 103 } 104 105 try { 106 $froups = core_group_external::create_groups(array($group3)); 107 $this->fail('Exception expected due to already existing idnumber.'); 108 } catch (moodle_exception $e) { 109 $this->assertInstanceOf('invalid_parameter_exception', $e); 110 $this->assertEquals('Invalid parameter value detected (Group with the same idnumber already exists)', 111 $e->getMessage()); 112 } 113 114 // Call without required capability 115 $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid); 116 $froups = core_group_external::create_groups(array($group4)); 117 } 118 119 /** 120 * Test get_groups 121 * 122 * @expectedException required_capability_exception 123 */ 124 public function test_get_groups() { 125 global $DB; 126 127 $this->resetAfterTest(true); 128 129 $course = self::getDataGenerator()->create_course(); 130 $group1data = array(); 131 $group1data['courseid'] = $course->id; 132 $group1data['name'] = 'Group Test 1'; 133 $group1data['description'] = 'Group Test 1 description'; 134 $group1data['descriptionformat'] = FORMAT_MOODLE; 135 $group1data['enrolmentkey'] = 'Test group enrol secret phrase'; 136 $group1data['idnumber'] = 'TEST1'; 137 $group2data = array(); 138 $group2data['courseid'] = $course->id; 139 $group2data['name'] = 'Group Test 2'; 140 $group2data['description'] = 'Group Test 2 description'; 141 $group1 = self::getDataGenerator()->create_group($group1data); 142 $group2 = self::getDataGenerator()->create_group($group2data); 143 144 // Set the required capabilities by the external function 145 $context = context_course::instance($course->id); 146 $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id); 147 $this->assignUserCapability('moodle/course:view', $context->id, $roleid); 148 149 // Call the external function. 150 $groups = core_group_external::get_groups(array($group1->id, $group2->id)); 151 152 // We need to execute the return values cleaning process to simulate the web service server. 153 $groups = external_api::clean_returnvalue(core_group_external::get_groups_returns(), $groups); 154 155 // Checks against DB values 156 $this->assertEquals(2, count($groups)); 157 foreach ($groups as $group) { 158 $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST); 159 switch ($dbgroup->name) { 160 case $group1->name: 161 $groupdescription = $group1->description; 162 $groupcourseid = $group1->courseid; 163 $this->assertEquals($dbgroup->descriptionformat, $group1->descriptionformat); 164 $this->assertEquals($dbgroup->enrolmentkey, $group1->enrolmentkey); 165 $this->assertEquals($dbgroup->idnumber, $group1->idnumber); 166 break; 167 case $group2->name: 168 $groupdescription = $group2->description; 169 $groupcourseid = $group2->courseid; 170 break; 171 default: 172 throw new moodle_exception('unknowgroupname'); 173 break; 174 } 175 $this->assertEquals($dbgroup->description, $groupdescription); 176 $this->assertEquals($dbgroup->courseid, $groupcourseid); 177 } 178 179 // Call without required capability 180 $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid); 181 $groups = core_group_external::get_groups(array($group1->id, $group2->id)); 182 } 183 184 /** 185 * Test delete_groups 186 * 187 * @expectedException required_capability_exception 188 */ 189 public function test_delete_groups() { 190 global $DB; 191 192 $this->resetAfterTest(true); 193 194 $course = self::getDataGenerator()->create_course(); 195 $group1data = array(); 196 $group1data['courseid'] = $course->id; 197 $group1data['name'] = 'Group Test 1'; 198 $group1data['description'] = 'Group Test 1 description'; 199 $group1data['descriptionformat'] = FORMAT_MOODLE; 200 $group1data['enrolmentkey'] = 'Test group enrol secret phrase'; 201 $group2data = array(); 202 $group2data['courseid'] = $course->id; 203 $group2data['name'] = 'Group Test 2'; 204 $group2data['description'] = 'Group Test 2 description'; 205 $group3data['courseid'] = $course->id; 206 $group3data['name'] = 'Group Test 3'; 207 $group3data['description'] = 'Group Test 3 description'; 208 $group1 = self::getDataGenerator()->create_group($group1data); 209 $group2 = self::getDataGenerator()->create_group($group2data); 210 $group3 = self::getDataGenerator()->create_group($group3data); 211 212 // Set the required capabilities by the external function 213 $context = context_course::instance($course->id); 214 $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id); 215 $this->assignUserCapability('moodle/course:view', $context->id, $roleid); 216 217 // Checks against DB values 218 $groupstotal = $DB->count_records('groups', array()); 219 $this->assertEquals(3, $groupstotal); 220 221 // Call the external function. 222 core_group_external::delete_groups(array($group1->id, $group2->id)); 223 224 // Checks against DB values 225 $groupstotal = $DB->count_records('groups', array()); 226 $this->assertEquals(1, $groupstotal); 227 228 // Call without required capability 229 $this->unassignUserCapability('moodle/course:managegroups', $context->id, $roleid); 230 $froups = core_group_external::delete_groups(array($group3->id)); 231 } 232 233 /** 234 * Test create and update groupings. 235 * @return void 236 */ 237 public function test_create_update_groupings() { 238 global $DB; 239 240 $this->resetAfterTest(true); 241 242 $this->setAdminUser(); 243 244 $course = self::getDataGenerator()->create_course(); 245 246 $grouping1data = array(); 247 $grouping1data['courseid'] = $course->id; 248 $grouping1data['name'] = 'Grouping 1 Test'; 249 $grouping1data['description'] = 'Grouping 1 Test description'; 250 $grouping1data['descriptionformat'] = FORMAT_MOODLE; 251 $grouping1data['idnumber'] = 'TEST'; 252 253 $grouping1 = self::getDataGenerator()->create_grouping($grouping1data); 254 255 $grouping1data['name'] = 'Another group'; 256 257 try { 258 $groupings = core_group_external::create_groupings(array($grouping1data)); 259 $this->fail('Exception expected due to already existing idnumber.'); 260 } catch (moodle_exception $e) { 261 $this->assertInstanceOf('invalid_parameter_exception', $e); 262 $this->assertEquals('Invalid parameter value detected (Grouping with the same idnumber already exists)', 263 $e->getMessage()); 264 } 265 266 // No exception should be triggered. 267 $grouping1data['id'] = $grouping1->id; 268 $grouping1data['idnumber'] = 'CHANGED'; 269 unset($grouping1data['courseid']); 270 core_group_external::update_groupings(array($grouping1data)); 271 272 $grouping2data = array(); 273 $grouping2data['courseid'] = $course->id; 274 $grouping2data['name'] = 'Grouping 2 Test'; 275 $grouping2data['description'] = 'Grouping 2 Test description'; 276 $grouping2data['descriptionformat'] = FORMAT_MOODLE; 277 $grouping2data['idnumber'] = 'TEST'; 278 279 $grouping2 = self::getDataGenerator()->create_grouping($grouping2data); 280 281 $grouping2data['id'] = $grouping2->id; 282 $grouping2data['idnumber'] = 'CHANGED'; 283 unset($grouping2data['courseid']); 284 try { 285 $groupings = core_group_external::update_groupings(array($grouping2data)); 286 $this->fail('Exception expected due to already existing idnumber.'); 287 } catch (moodle_exception $e) { 288 $this->assertInstanceOf('invalid_parameter_exception', $e); 289 $this->assertEquals('Invalid parameter value detected (A different grouping with the same idnumber already exists)', 290 $e->getMessage()); 291 } 292 } 293 294 /** 295 * Test get_groupings 296 */ 297 public function test_get_groupings() { 298 global $DB; 299 300 $this->resetAfterTest(true); 301 302 $course = self::getDataGenerator()->create_course(); 303 304 $groupingdata = array(); 305 $groupingdata['courseid'] = $course->id; 306 $groupingdata['name'] = 'Grouping Test'; 307 $groupingdata['description'] = 'Grouping Test description'; 308 $groupingdata['descriptionformat'] = FORMAT_MOODLE; 309 310 $grouping = self::getDataGenerator()->create_grouping($groupingdata); 311 312 // Set the required capabilities by the external function. 313 $context = context_course::instance($course->id); 314 $roleid = $this->assignUserCapability('moodle/course:managegroups', $context->id); 315 $this->assignUserCapability('moodle/course:view', $context->id, $roleid); 316 317 // Call the external function without specifying the optional parameter. 318 $groupings = core_group_external::get_groupings(array($grouping->id)); 319 // We need to execute the return values cleaning process to simulate the web service server. 320 $groupings = external_api::clean_returnvalue(core_group_external::get_groupings_returns(), $groupings); 321 322 $this->assertEquals(1, count($groupings)); 323 324 $group1data = array(); 325 $group1data['courseid'] = $course->id; 326 $group1data['name'] = 'Group Test 1'; 327 $group1data['description'] = 'Group Test 1 description'; 328 $group1data['descriptionformat'] = FORMAT_MOODLE; 329 $group2data = array(); 330 $group2data['courseid'] = $course->id; 331 $group2data['name'] = 'Group Test 2'; 332 $group2data['description'] = 'Group Test 2 description'; 333 $group2data['descriptionformat'] = FORMAT_MOODLE; 334 335 $group1 = self::getDataGenerator()->create_group($group1data); 336 $group2 = self::getDataGenerator()->create_group($group2data); 337 338 groups_assign_grouping($grouping->id, $group1->id); 339 groups_assign_grouping($grouping->id, $group2->id); 340 341 // Call the external function specifying that groups are returned. 342 $groupings = core_group_external::get_groupings(array($grouping->id), true); 343 // We need to execute the return values cleaning process to simulate the web service server. 344 $groupings = external_api::clean_returnvalue(core_group_external::get_groupings_returns(), $groupings); 345 $this->assertEquals(1, count($groupings)); 346 $this->assertEquals(2, count($groupings[0]['groups'])); 347 foreach ($groupings[0]['groups'] as $group) { 348 $dbgroup = $DB->get_record('groups', array('id' => $group['id']), '*', MUST_EXIST); 349 $dbgroupinggroups = $DB->get_record('groupings_groups', 350 array('groupingid' => $groupings[0]['id'], 351 'groupid' => $group['id']), 352 '*', MUST_EXIST); 353 switch ($dbgroup->name) { 354 case $group1->name: 355 $groupdescription = $group1->description; 356 $groupcourseid = $group1->courseid; 357 break; 358 case $group2->name: 359 $groupdescription = $group2->description; 360 $groupcourseid = $group2->courseid; 361 break; 362 default: 363 throw new moodle_exception('unknowgroupname'); 364 break; 365 } 366 $this->assertEquals($dbgroup->description, $groupdescription); 367 $this->assertEquals($dbgroup->courseid, $groupcourseid); 368 } 369 } 370 371 /** 372 * Test get_groups 373 */ 374 public function test_get_course_user_groups() { 375 global $DB; 376 377 $this->resetAfterTest(true); 378 379 $student1 = self::getDataGenerator()->create_user(); 380 $student2 = self::getDataGenerator()->create_user(); 381 $teacher = self::getDataGenerator()->create_user(); 382 383 $course = self::getDataGenerator()->create_course(); 384 $emptycourse = self::getDataGenerator()->create_course(); 385 386 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 387 $this->getDataGenerator()->enrol_user($student1->id, $course->id, $studentrole->id); 388 $this->getDataGenerator()->enrol_user($student2->id, $course->id, $studentrole->id); 389 390 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); 391 $this->getDataGenerator()->enrol_user($teacher->id, $course->id, $teacherrole->id); 392 $this->getDataGenerator()->enrol_user($teacher->id, $emptycourse->id, $teacherrole->id); 393 394 $group1data = array(); 395 $group1data['courseid'] = $course->id; 396 $group1data['name'] = 'Group Test 1'; 397 $group1data['description'] = 'Group Test 1 description'; 398 $group1data['idnumber'] = 'TEST1'; 399 $group2data = array(); 400 $group2data['courseid'] = $course->id; 401 $group2data['name'] = 'Group Test 2'; 402 $group2data['description'] = 'Group Test 2 description'; 403 $group1 = self::getDataGenerator()->create_group($group1data); 404 $group2 = self::getDataGenerator()->create_group($group2data); 405 406 groups_add_member($group1->id, $student1->id); 407 groups_add_member($group1->id, $student2->id); 408 groups_add_member($group2->id, $student1->id); 409 410 $this->setUser($student1); 411 412 $groups = core_group_external::get_course_user_groups($course->id, $student1->id); 413 $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups); 414 // Check that I see my groups. 415 $this->assertCount(2, $groups['groups']); 416 417 $this->setUser($student2); 418 $groups = core_group_external::get_course_user_groups($course->id, $student2->id); 419 $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups); 420 // Check that I see my groups. 421 $this->assertCount(1, $groups['groups']); 422 423 $this->assertEquals($group1data['name'], $groups['groups'][0]['name']); 424 $this->assertEquals($group1data['description'], $groups['groups'][0]['description']); 425 $this->assertEquals($group1data['idnumber'], $groups['groups'][0]['idnumber']); 426 427 $this->setUser($teacher); 428 $groups = core_group_external::get_course_user_groups($course->id, $student1->id); 429 $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups); 430 // Check that a teacher can see student groups. 431 $this->assertCount(2, $groups['groups']); 432 433 $groups = core_group_external::get_course_user_groups($course->id, $student2->id); 434 $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups); 435 // Check that a teacher can see student groups. 436 $this->assertCount(1, $groups['groups']); 437 438 // Check permissions. 439 $this->setUser($student1); 440 try { 441 $groups = core_group_external::get_course_user_groups($course->id, $student2->id); 442 } catch (moodle_exception $e) { 443 $this->assertEquals('accessdenied', $e->errorcode); 444 } 445 446 try { 447 $groups = core_group_external::get_course_user_groups($emptycourse->id, $student2->id); 448 } catch (moodle_exception $e) { 449 $this->assertEquals('requireloginerror', $e->errorcode); 450 } 451 452 $this->setUser($teacher); 453 // Check warnings. 454 $groups = core_group_external::get_course_user_groups($emptycourse->id, $student1->id); 455 $groups = external_api::clean_returnvalue(core_group_external::get_course_user_groups_returns(), $groups); 456 $this->assertCount(1, $groups['warnings']); 457 458 } 459 460 /** 461 * Test get_activity_allowed_groups 462 */ 463 public function test_get_activity_allowed_groups() { 464 global $DB; 465 466 $this->resetAfterTest(true); 467 468 $generator = self::getDataGenerator(); 469 470 $student = $generator->create_user(); 471 $otherstudent = $generator->create_user(); 472 $teacher = $generator->create_user(); 473 $course = $generator->create_course(); 474 $othercourse = $generator->create_course(); 475 476 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 477 $teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); 478 $generator->enrol_user($student->id, $course->id, $studentrole->id); 479 $generator->enrol_user($otherstudent->id, $othercourse->id, $studentrole->id); 480 $generator->enrol_user($teacher->id, $course->id, $teacherrole->id); 481 482 $forum1 = $generator->create_module("forum", array('course' => $course->id), array('groupmode' => VISIBLEGROUPS)); 483 $forum2 = $generator->create_module("forum", array('course' => $othercourse->id)); 484 $forum3 = $generator->create_module("forum", array('course' => $course->id), array('visible' => 0)); 485 486 // Request data for tests. 487 $cm1 = get_coursemodule_from_instance("forum", $forum1->id); 488 $cm2 = get_coursemodule_from_instance("forum", $forum2->id); 489 $cm3 = get_coursemodule_from_instance("forum", $forum3->id); 490 491 $group1data = array(); 492 $group1data['courseid'] = $course->id; 493 $group1data['name'] = 'Group Test 1'; 494 $group1data['description'] = 'Group Test 1 description'; 495 $group1data['idnumber'] = 'TEST1'; 496 $group2data = array(); 497 $group2data['courseid'] = $course->id; 498 $group2data['name'] = 'Group Test 2'; 499 $group2data['description'] = 'Group Test 2 description'; 500 $group2data['idnumber'] = 'TEST2'; 501 $group1 = $generator->create_group($group1data); 502 $group2 = $generator->create_group($group2data); 503 504 groups_add_member($group1->id, $student->id); 505 groups_add_member($group2->id, $student->id); 506 507 $this->setUser($student); 508 509 // First try possible errors. 510 try { 511 $data = core_group_external::get_activity_allowed_groups($cm2->id); 512 } catch (moodle_exception $e) { 513 $this->assertEquals('requireloginerror', $e->errorcode); 514 } 515 516 try { 517 $data = core_group_external::get_activity_allowed_groups($cm3->id); 518 } catch (moodle_exception $e) { 519 $this->assertEquals('requireloginerror', $e->errorcode); 520 } 521 522 // Retrieve my groups. 523 $groups = core_group_external::get_activity_allowed_groups($cm1->id); 524 $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups); 525 $this->assertCount(2, $groups['groups']); 526 527 foreach ($groups['groups'] as $group) { 528 if ($group['name'] == $group1data['name']) { 529 $this->assertEquals($group1data['description'], $group['description']); 530 $this->assertEquals($group1data['idnumber'], $group['idnumber']); 531 } else { 532 $this->assertEquals($group2data['description'], $group['description']); 533 $this->assertEquals($group2data['idnumber'], $group['idnumber']); 534 } 535 } 536 537 $this->setUser($teacher); 538 // Retrieve other users groups. 539 $groups = core_group_external::get_activity_allowed_groups($cm1->id, $student->id); 540 $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups); 541 $this->assertCount(2, $groups['groups']); 542 543 // Check warnings. Trying to get groups for a user not enrolled in course. 544 $groups = core_group_external::get_activity_allowed_groups($cm1->id, $otherstudent->id); 545 $groups = external_api::clean_returnvalue(core_group_external::get_activity_allowed_groups_returns(), $groups); 546 $this->assertCount(1, $groups['warnings']); 547 548 } 549 550 /** 551 * Test get_activity_groupmode 552 */ 553 public function test_get_activity_groupmode() { 554 global $DB; 555 556 $this->resetAfterTest(true); 557 558 $generator = self::getDataGenerator(); 559 560 $student = $generator->create_user(); 561 $course = $generator->create_course(); 562 $othercourse = $generator->create_course(); 563 564 $studentrole = $DB->get_record('role', array('shortname' => 'student')); 565 $generator->enrol_user($student->id, $course->id, $studentrole->id); 566 567 $forum1 = $generator->create_module("forum", array('course' => $course->id), array('groupmode' => VISIBLEGROUPS)); 568 $forum2 = $generator->create_module("forum", array('course' => $othercourse->id)); 569 $forum3 = $generator->create_module("forum", array('course' => $course->id), array('visible' => 0)); 570 571 // Request data for tests. 572 $cm1 = get_coursemodule_from_instance("forum", $forum1->id); 573 $cm2 = get_coursemodule_from_instance("forum", $forum2->id); 574 $cm3 = get_coursemodule_from_instance("forum", $forum3->id); 575 576 $this->setUser($student); 577 578 $data = core_group_external::get_activity_groupmode($cm1->id); 579 $data = external_api::clean_returnvalue(core_group_external::get_activity_groupmode_returns(), $data); 580 $this->assertEquals(VISIBLEGROUPS, $data['groupmode']); 581 582 try { 583 $data = core_group_external::get_activity_groupmode($cm2->id); 584 } catch (moodle_exception $e) { 585 $this->assertEquals('requireloginerror', $e->errorcode); 586 } 587 588 try { 589 $data = core_group_external::get_activity_groupmode($cm3->id); 590 } catch (moodle_exception $e) { 591 $this->assertEquals('requireloginerror', $e->errorcode); 592 } 593 594 } 595 }
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 |