00001 <?php
00002
00013 class SnapContentLessonPlan extends SnapContent {
00014 protected $NEED_TRANSLATION = TRUE;
00015 protected $lessonTitle = "";
00016 protected $gradeLevel = "";
00017 protected $lessonAbstract = "";
00018 protected $standardsAddressed = "";
00019 protected $objectives = "";
00020 protected $prereqs = "";
00021 protected $errors = array();
00022 protected $currentXML = false;
00023
00024 public static $PROTOTYPES = array(
00025 'introduction' => array(
00026 'name' => 'Introduction',
00027 'required' => true,
00028 'note' => false
00029 ),
00030 'exploration' => array(
00031 'name' => 'Exploration',
00032 'required' => true,
00033 'note' => false
00034 ),
00035 'phys_modeling' => array(
00036 'name' => 'Physical Modeling and Measurement',
00037 'required' => true,
00038 'note' => false
00039 ),
00040 'comp_modeling' => array(
00041 'name' => 'Computational Modeling',
00042 'required' => true,
00043 'note' => false
00044 ),
00045 'conclusion' => array(
00046 'name' => 'Conclusion',
00047 'required' => true,
00048 'note' => false
00049 ),
00050 'follow_up' => array(
00051 'name' => 'Follow Up',
00052 'required' => false,
00053 'note' => false
00054 ),
00055 'alternate' => array(
00056 'name' => 'Alternate Outline',
00057 'required' => false,
00058 'note' => false
00059 ),
00060 'guidedpractice' => array(
00061 'name' => 'Guided Practice',
00062 'required' => false,
00063 'note' => false
00064 ),
00065 'indeppractice' => array(
00066 'name' => 'Independent Practice',
00067 'required' => false,
00068 'note' => false
00069 )
00070 );
00071
00072 public static $OUTLINES = array(
00073 'Custom' => array(),
00074 'Default Lesson Plan Template' => array('introduction', 'exploration', 'phys_modeling', 'comp_modeling',
00075 'conclusion', 'follow_up', 'alternate')
00076 );
00077
00081 public static $NICE_NAMES = array(
00082 'lessonTitle' => 'Title of Lesson',
00083 'gradeLevel' => 'Grade Level',
00084 'lessonAbstract' => 'Lesson Abstract',
00085 'standardsAddress' => 'Standards Addressed',
00086 'objectives' => 'Objectives',
00087 'prereqs' => 'Prequisite Knowledge',
00088 'teacherPrep' => 'Teacher Preparation',
00089 'requiredMaterials' => 'Required Materials',
00090 'mediaUsed' => 'Media Used',
00091 'requiredEquipment' => 'Required Equipment',
00092 'safety' => 'Safety Reminders',
00093 'keyterms' => 'Key Terms'
00094 );
00095
00130 public function setStandardFields($contentArray) {
00131 $keys = array('lessonTitle', 'gradeLevel', 'lessonAbstract', 'standardsAddressed', 'objectives',
00132 'prereqs', 'teacherPrep', 'requiredMaterials', 'mediaUsed', 'requiredEquipment',
00133 'safety', 'keyterms');
00134
00135 foreach ($keys as $key) {
00136 if(($errors = $this->isKeyValid($key, $contentArray)) !== true) {
00137 foreach($errors as $e)
00138 $this->errors[] = $e;
00139 }
00140 }
00141
00142 if(count($this->errors) > 0)
00143 return false;
00144
00145 $old = $this->getAll();
00146
00147 $this->set('lessonTitle', $contentArray['lessonTitle']);
00148 $this->set('gradeLevel', $contentArray['gradeLevel']);
00149 $this->set('lessonAbstract', $contentArray['lessonAbstract']);
00150 $this->set('standardsAddressed', $contentArray['standardsAddressed']);
00151 $this->set('objectives', $contentArray['objectives']);
00152 $this->set('prereqs', $contentArray['prereqs']);
00153 $this->set('teacherPrep', $contentArray['teacherPrep']);
00154 $this->set('requiredMaterials', $contentArray['requiredMaterials']);
00155 $this->set('mediaUsed', $contentArray['mediaUsed']);
00156 $this->set('requiredEquipment', $contentArray['requiredEquipment']);
00157 $this->set('safety', $contentArray['safety']);
00158 $this->set('keyterms', $contentArray['keyterms']);
00159
00160 $errors = $this->generateXML();
00161 if($errors !== true) {
00162 $this->setAll($old);
00163 $this->errors += $errors;
00164 return false;
00165 }
00166 return true;
00167 }
00168
00177 public function setOutlineType($type) {
00178 $oldOutline = $this->get('outlineType');
00179 if(!isset(self::$OUTLINES[$type])) {
00180 $this->errors[] = 'No such outline type "' . $type . '"';
00181 return false;
00182 }
00183 if($oldOutline && $oldOutline != $type) {
00184
00185
00186 $events = $this->get('events');
00187 $newEvents = array();
00188 foreach($events as $evt) {
00189
00190 if(!is_string($evt['proto']))
00191 continue;
00192 if(in_array($evt['proto'], self::$OUTLINES[$type]))
00193 $newEvents[] = $evt;
00194 }
00195 $this->set('events', $newEvents);
00196 }
00197 $this->set('outlineType', $type);
00198 return true;
00199 }
00200
00211 public function setOutlineField($sname, $time, $content) {
00212 $outlineType = $this->get('outlineType');
00213 if(!$outlineType) {
00214 $this->errors[] = 'INTERNAL: You must select an outline type first before setting fields';
00215 return false;
00216 }
00217 if(is_array($sname)) {
00218 if($outlineType != 'Custom') {
00219 $this->errors[] = 'INTERNAL: You cannot have custom prototypes unless the outline type is "Custom"';
00220 return false;
00221 }
00222 if(!isset($sname['name']) || !isset($sname['required']) || !isset($sname['note'])
00223 || !is_string($sname['name']) || !is_bool($sname['required']) || !is_bool($sname['note'])
00224 || $sname['name'] == '')
00225 {
00226 $this->errors[] = 'INTERNAL: Custom prototype is incomplete: ' . print_r($sname, true);
00227 return false;
00228 }
00229 $name = $sname['name'];
00230 }
00231 else {
00232 if(!isset(self::$PROTOTYPES[$sname])) {
00233 $this->errors[] = 'INTERNAL: No such prototype: "' . $sname . '"';
00234 return false;
00235 }
00236 if($outlineType != 'Custom' && !in_array($sname, self::$OUTLINES[$outlineType])) {
00237 $this->errors[] = 'INTERNAL: Prototype "' . $sname . '" is not available in the selected template "'
00238 . $outlineType . '"';
00239 return false;
00240 }
00241 $name = self::$PROTOTYPES[$sname]['name'];
00242 }
00243 $time = htmlspecialchars($time);
00244 $tmp = $this->prepareXML($content, $name);
00245 if(is_array($tmp)) {
00246 foreach($tmp as $err)
00247 $this->errors[] = 'In field <b>' . $name . '</b> of Outline: ' . $err;
00248 return false;
00249 }
00250 $ret = SXMLHelper::validateXML('<XMLResource><section><p>' . $tmp . '</p></section></XMLResource>', true);
00251 if($ret !== true) {
00252 foreach($ret as $err)
00253 $this->errors[] = 'In field <b>' . $name . '</b> of Outline: ' . $err;
00254 return false;
00255 }
00256 $events = $this->get('events');
00257 $events[] = array(
00258 'proto' => $sname,
00259 'time' => $time,
00260 'content' => $content
00261 );
00262 $this->set('events', $events);
00263 return true;
00264 }
00265
00273 public function getErrors() {
00274 return $this->errors;
00275 }
00276
00284 public function clearErrors() {
00285 $this->errors = array();
00286 }
00287
00296 public function cctHelper($matches) {
00297 return '<p><pre><![CDATA[' . base64_encode(htmlspecialchars($matches[1])) . ']]></pre></p>';
00298 }
00299
00308 public function litHelper($matches) {
00309 return '<span><![CDATA[' . base64_encode(htmlspecialchars($matches[1])) . ']]></span>';
00310 }
00311
00321 private function prepareXML($str, $field) {
00322
00323 $str = strtr($str, array('<' => '&lt;', '>' => '&gt;', '&' => '&amp;'));
00324
00325
00326 $str = preg_replace_callback('/(?![\\\\])<code>(.*?)<\/code>/ms', array($this, 'cctHelper'), $str);
00327
00328
00329 $str = EscapeParser::parseString($str, 'In field <b>' . $field . '</b>: ');
00330 if(is_array($str))
00331 return $str;
00332
00333
00334 $str = preg_replace("/\n[\t ]*\n/", "</p><p>", $str);
00335 $str = preg_replace("/\r[\t ]*\r/", "</p><p>", $str);
00336 $str = preg_replace("/\r\n[\t ]*\r\n/", "</p><p>", $str);
00337
00338 return $str;
00339 }
00340
00349 public function fixPreBlocks($matches) {
00350 return '<pre>' . htmlspecialchars(base64_decode($matches[1])) . '</pre>';
00351 }
00352
00361 public function fixLitBlocks($matches) {
00362 $str = htmlspecialchars(base64_decode($matches[1]));
00363
00364
00365 $str = preg_replace("/\n[\t ]*\n/", "</p><p>", $str);
00366 $str = preg_replace("/\r[\t ]*\r/", "</p><p>", $str);
00367 $str = preg_replace("/\r\n[\t ]*\r\n/", "</p><p>", $str);
00368
00369 return $str;
00370 }
00371
00381 protected function isKeyValid($key, &$array) {
00382 if(!isset($array[$key]))
00383 return array('Missing field <b>' . $key . '</b>! This is an internal error; please report it.');
00384
00385 $errors = array();
00386
00387 switch($key) {
00388 case 'lessonTitle':
00389 case 'gradeLevel':
00390 case 'lessonAbstract':
00391 case 'prereqs':
00392 case 'teacherPrep':
00393 case 'safety':
00394 if(!is_string($array[$key]) or $array[$key] == '') {
00395 $errors[] = 'In field <b>' . self::$NICE_NAMES[$key] . '</b>: field is empty, or not a string';
00396 break;
00397 }
00398 $tmp = $this->prepareXML($array[$key], self::$NICE_NAMES[$key]);
00399 if(is_array($tmp)) {
00400 $errors += $tmp;
00401 continue;
00402 }
00403 $ret = SXMLHelper::validateXML('<XMLResource><section><p>' . $tmp . '</p></section></XMLResource>', true);
00404 if($ret !== true) {
00405 foreach($ret as $r)
00406 $errors[] = 'In field <b>' . self::$NICE_NAMES[$key] . '</b>: ' . $r;
00407 }
00408
00409
00410 break;
00411 case 'requiredMaterials':
00412 case 'standardsAddressed':
00413 case 'objectives':
00414 case 'requiredEquipment':
00415 case 'keyterms':
00416 if (!is_array($array[$key])) {
00417 $errors[] = 'In field <b>' . self::$NICE_NAMES[$key]
00418 . '</b>: field must be an array (internal error: please report it';
00419 break;
00420 }
00421 foreach($array[$key] as $i => $re) {
00422
00423 $ret = SXMLHelper::validateXML('<XMLResource><section><p>' . $re . '</p></section></XMLResource>', true);
00424 if($ret !== true) {
00425 foreach($ret as $err)
00426 $errors[] = 'In field <b>' . self::$NICE_NAMES[$key] . '</b>: ' . $err;
00427 }
00428 else
00429 $array[$key][$i] = $re;
00430 }
00431 break;
00432 }
00433
00434 if(count($errors) > 0)
00435 return $errors;
00436 else
00437 return true;
00438 }
00439
00447 public function getOutlineType() {
00448 return $this->get('outlineType');
00449 }
00450
00458 public function getOutlineFieldNames() {
00459 return self::$OUTLINES[$this->get('outlineType')];
00460 }
00461
00469 public function getOutlineFields() {
00470 return $this->get('events');
00471 }
00472
00482 public function getWhichOutline() {
00483 return $this->get('whichOutline');
00484 }
00485
00494 public function getXML() {
00495 if(!$this->currentXML) {
00496 if(!$this->generateXML(true))
00497 return false;
00498 }
00499 return $this->currentXML;
00500 }
00501
00510 private function generateXML($save = false) {
00511 $lessonTitle = $this->prepareXML($this->get('lessonTitle'), self::$NICE_NAMES['lessonTitle']);
00512 $gradeLevel = $this->prepareXML($this->get('gradeLevel'), self::$NICE_NAMES['gradeLevel']);
00513 $lessonAbstract = $this->prepareXML($this->get('lessonAbstract'), self::$NICE_NAMES['lessonAbstract']);
00514
00515 $standardsArray = (is_array($this->get('standardsAddressed'))) ? $this->get('standardsAddressed') : array();
00516 $objectivesArray = (is_array($this->get('objectives'))) ? $this->get('objectives') : array();
00517 $materialsArray = (is_array($this->get('requiredMaterials'))) ? $this->get('requiredMaterials') : array();
00518 $mediaArray = (is_array($this->get('mediaUsed'))) ? $this->get('mediaUsed') : array();
00519 $equipmentArray = (is_array($this->get('requiredEquipment'))) ? $this->get('requiredEquipment') : array();
00520 $eventsArray = (is_array($this->get('events'))) ? $this->get('events') : array();
00521 $keytermsArray = (is_array($this->get('keyterms'))) ? $this->get('keyterms') : array();
00522 if(count($standardsArray) > 0)
00523 $standards = '<ul><li>' . implode('</li><li>', $standardsArray) . '</li></ul>';
00524 else
00525 $standards = '<i>none</i>';
00526 if(count($objectivesArray) > 0)
00527 $objectives = '<ul><li>' . implode('</li><li>', $objectivesArray) . '</li></ul>';
00528 else
00529 $objectives = '<i>none</i>';
00530 if(count($materialsArray) > 0)
00531 $materials = '<ul><li>' . implode('</li><li>', $materialsArray) . '</li></ul>';
00532 else
00533 $materials = '<i>none</i>';
00534 if(count($mediaArray) > 0)
00535 $media = '<ul><li>' . implode('</li><li>', $mediaArray) . '</li></ul>';
00536 else
00537 $media = '<i>none</i>';
00538 if(count($equipmentArray) > 0)
00539 $equipment = '<ul><li>' . implode('</li><li>', $equipmentArray) . '</li></ul>';
00540 else
00541 $equipment = '<i>none</i>';
00542 if(count($keytermsArray) > 0)
00543 $keyterms = '<ul><li>' . implode('</li><li>', $keytermsArray) . '</li></ul>';
00544 else
00545 $keyterms = '<i>none</i>';
00546 $prereqs = $this->prepareXML($this->get('prereqs'), self::$NICE_NAMES['prereqs']);
00547 $events = '';
00548
00549 $errors = array();
00550 foreach($eventsArray as $thisEvent) {
00551 $events .= '<section mapping="event">';
00552 if(!isset($thisEvent['proto']) && isset($thisEvent['name'])) {
00553 $proto = array('name' => $thisEvent['name']);
00554 $this->setWarning('Please upgrade this lesson plan (edit and save)!');
00555 }
00556 else
00557 $proto = $thisEvent['proto'];
00558 if(is_string($proto))
00559 $proto = self::$PROTOTYPES[$proto];
00560 $events .= '<section mapping="eventName"><p>' . $proto['name'] . '</p></section>';
00561 $events .= '<section mapping="eventTime"><p>' . $thisEvent['time'] . "</p></section>";
00562 $xml = $this->prepareXML($thisEvent['content'], $proto['name']);
00563 if(is_array($xml))
00564 $errors += $xml;
00565 else
00566 $events .= '<section mapping="eventContent"><p>' . $xml . "</p></section>";
00567 $events .= "</section>";
00568 }
00569
00570 $teacherPrep = $this->prepareXML($this->get('teacherPrep'), self::$NICE_NAMES['teacherPrep']);
00571 $safety = $this->prepareXML($this->get('safety'), self::$NICE_NAMES['safety']);
00572
00573 if(is_array($lessonTitle)) $errors += $lessonTitle;
00574 if(is_array($gradeLevel)) $errors += $gradeLevel;
00575 if(is_array($lessonAbstract)) $errors += $lessonAbstract;
00576 if(is_array($prereqs)) $errors += $prereqs;
00577 if(is_array($teacherPrep)) $errors += $teacherPrep;
00578 if(is_array($safety)) $errors += $safety;
00579
00580 if(count($errors) > 0)
00581 return $errors;
00582
00583 $xmlString = <<<END_XML
00584 <XMLResource>
00585 <section mapping="moduleInfo">
00586 <section mapping="moduleName"><p>$lessonTitle</p></section>
00587 <section mapping="moduleLevel"><p>$gradeLevel</p></section>
00588 </section>
00589 <section mapping="concepts">
00590 <section mapping="abstract">
00591 <p>$lessonAbstract</p>
00592 </section>
00593 <section mapping="standards">
00594 <p>$standards</p>
00595 </section>
00596 <section mapping="objectives">
00597 <p>$objectives</p>
00598 </section>
00599 <section mapping="keyterms">
00600 <p>$keyterms</p>
00601 </section>
00602 </section>
00603 <section mapping="prereq">
00604 <p>$prereqs</p>
00605 </section>
00606 <section mapping="teacherPrep">
00607 <p>$teacherPrep</p>
00608 </section>
00609 <section mapping="required">
00610 <section mapping="materials">
00611 <p>$materials</p>
00612 </section>
00613 <section mapping="media">
00614 <p>$media</p>
00615 </section>
00616 <section mapping="equipment">
00617 <p>$equipment</p>
00618 </section>
00619 </section>
00620 <section mapping="safety">
00621 <p>$safety</p>
00622 </section>
00623 <section mapping="outline">
00624 $events
00625 </section>
00626 </XMLResource>
00627 END_XML;
00628
00629 $xmlString = preg_replace_callback('/<p><pre><!\[CDATA\[([a-zA-Z0-9+_\/=-]*)\]\]><\/pre><\/p>/',
00630 array($this, 'fixPreBlocks'), $xmlString);
00631 $xmlString = preg_replace_callback('/<span><!\[CDATA\[([a-zA-Z0-9+_\/=-]*)\]\]><\/span>/',
00632 array($this, 'fixLitBlocks'), $xmlString);
00633
00634 $errors = SXMLHelper::validateXML($xmlString, true);
00635
00636 if($errors !== true) {
00637 $this->currentXML = false;
00638 $errors[] = '<b>The above errors indicate that Snap2 had an internal error. Please report those errors.</b>';
00639 return $errors;
00640 }
00641
00642 if($save)
00643 $this->currentXML = $xmlString;
00644 return true;
00645 }
00646
00651 public function getLessonTitle() {
00652 return $this->get('lessonTitle');
00653 }
00654
00660 public function getGradeLevel() {
00661 return $this->get('gradeLevel');
00662 }
00663
00668 public function getLessonAbstract() {
00669 return $this->get('lessonAbstract');
00670 }
00671
00676 public function getStandardsAddressed() {
00677 return $this->get('standardsAddressed');
00678 }
00679
00684 public function getObjectives() {
00685 return $this->get('objectives');
00686 }
00687
00692 public function getPrereqs() {
00693 return $this->get('prereqs');
00694 }
00699 public function getTeacherPrep() {
00700 return $this->get('teacherPrep');
00701 }
00702
00707 public function getRequiredMaterials() {
00708 return $this->get('requiredMaterials');
00709 }
00710
00715 public function getMediaUsed() {
00716 return $this->get('mediaUsed');
00717 }
00718
00723 public function getRequiredEquipment() {
00724 return $this->get('requiredEquipment');
00725 }
00726
00731 public function getSafety() {
00732 return $this->get('safety');
00733 }
00734
00742 public function getKeyTerms() {
00743 return $this->get('keyterms');
00744 }
00745
00751 public function getEvent($whichEvent) {
00752 $event = $this->getEvents();
00753 return $event[$whichEvent];
00754 }
00755
00760 public function getEvents() {
00761 return $this->get('events');
00762 }
00763
00769 public function getEventName($whichEvent) {
00770 $event = $this->getEvent($whichEvent);
00771 return $event['name'];
00772 }
00773
00779 public function getEventTime($whichEvent) {
00780 $event = $this->getEvent($whichEvent);
00781 return $event['time'];
00782 }
00783
00789 public function getEventContent($whichEvent) {
00790 $event = $this->getEvent($whichEvent);
00791 return $event['content'];
00792 }
00793
00801 protected function validate() {
00802 return true;
00803 }
00804
00813 protected function doGetHTML($params) {
00814 return $this->getAll();
00815 }
00816
00824 public function checkMedia() {
00825 return true;
00826 }
00827
00835 public function updateFileList() {
00836 return true;
00837 }
00838 }
00839
00840 ?>