00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00031 class XFormsInterfaceBuilder extends SObject {
00032
00033
00034
00035
00036
00037
00038
00039
00040 protected $parent;
00041 protected $namePrefix = "shodor_xforms_";
00042 protected $validator;
00043 protected $selectCount;
00044
00045 protected $inputWidth = 50;
00046 protected $textAreaWidth = 49;
00047 protected $tableBorder = 0;
00048 protected $tableWidth = 700;
00049 protected $tablePadding = 1;
00050 protected $tableSpacing = 5;
00051
00052 protected $xformCache = null;
00053
00054
00055
00064 public function __construct ( $parent )
00065 {
00066 $this->parent = $parent;
00067 $this->validator = new StandardValidator();
00068 $this->selectCount = array();
00069 }
00070
00071
00072
00073
00074
00075
00083 public function getIdPrefix() {
00084 return $this->namePrefix;
00085 }
00086
00087
00088
00089
00099 public function getPresetValues ( $node, $xml )
00100 {
00101
00102 $nodes = $this->findXMLNode($node, $xml);
00103 $result = array();
00104 if (!is_array($nodes)){
00105 $this->setError('Could not look up preset values for non-existent node "' . $node . '"');
00106 return array();
00107 }
00108 foreach($nodes as $n) {
00109
00110
00111
00112
00113
00114 $val = SXMLHelper::contentFromXML((string)$n);
00115
00116 if ($val != '') {
00117 array_push($result, $val);
00118 }
00119 }
00120
00121
00122
00123
00124
00125 $preset = $this->parent->getPreset($node);
00126 if($preset && count($nodes) > 0){
00127
00128
00129
00130 if (!is_array($preset)) {
00131 $this->setPrettyError('getPresetValues', 'Preset for node ' . $node . ' was a '
00132 . 'string. Expecting array.');
00133 return false;
00134 }
00135
00136
00137
00138 $result = array();
00139 foreach ($preset as $pkey => $pval) {
00140 $result[$pkey] = SXMLHelper::contentFromXML($pval);
00141
00142 }
00143 $result = $preset;
00144
00145 }
00146 return $result;
00147 }
00148
00158 public function findXMLNode($node, $xml)
00159 {
00160 if (!isset($xml->model->instance)) {
00161 $this->setPrettyError('findXMLNode', 'model->instance node not found');
00162 return false;
00163 }
00164
00165 $instance = $xml->model->instance;
00166 $result = array();
00167
00168
00169 if (count($instance->xpath($node)) > 0 ){
00170 $result = $instance->xpath($node);
00171
00172
00173 } else if (count($instance->xpath("//instance/" . $node)) > 0) {
00174 $result = $instance->xpath("//instance/" . $node);
00175
00176
00177 } else if (count($instance->xpath("//model/" . $node)) > 0) {
00178 $result = $instance->xpath("//model/" . $node);
00179
00180
00181
00182 } else if (count($instance->xpath("//" . $node)) > 0) {
00183 $result = $instance->xpath("//" . $node);
00184
00185
00186 } else {
00187 $this->setError('Field "' . $node . '" appears in controls section but not in model section.');
00188
00189 return false;
00190 }
00191 return $result;
00192 }
00193
00204 public function getValuesFromPOST ( $node, $xml, $count = 0 )
00205 {
00206 # generate the field name
00207 $fieldName = $this->namePrefix . $node;
00208
00209 # if the field name is set directly, return that
00210 if (isset($_POST[$fieldName])) {
00211 $postData = $_POST[$fieldName];
00212 return array($postData);
00213 }
00214
00215 # return array for multiple-select lists that have numbered items
00216 $eltIndex = $fieldName . "_" . $count;
00217
00218 # if the element is set, return it (decoded); otherwise return empty array
00219 return (isset($_POST[$eltIndex])) ?
00220 array($_POST[$eltIndex])
00221
00222 : array();
00223 }
00224
00235 public function getValueForInstanceNode ( $node, $xml, $count = 0) {
00236 return ($this->parent->isSubmitted()) ?
00237 $this->getValuesFromPOST($node, $xml, $count)
00238 : $this->getPresetValues($node, $xml);
00239 }
00240
00251 public function getFirstValueForInstanceNode ($node, $xml, $count = 0) {
00252 $array = $this->getValueForInstanceNode($node, $xml, $count);
00253 $return = (count($array) > 0) ? $array[0] : $array;
00254 return $return;
00255 }
00256
00265 public function incrementSelectCount ($node) {
00266 if (!isset($this->selectCount["$node"])) { $this->selectCount["$node"] = 0; }
00267 else { $this->selectCount["$node"]++; }
00268 return $this->selectCount["$node"];
00269 }
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00288 public function formatXFormsInterface ( $xform )
00289 {
00290 global $TRANSACTION;
00291
00292 $infoArray = array();
00293 $htmlOutput = "";
00294
00295 $this->xformCache = $xform;
00296
00297
00298 $hasSubmission = isset($xform->model->submission);
00299 if ($hasSubmission){
00300 $id = $xform->model->submission['id'];
00301 $action = $xform->model->submission['action'];
00302 if($this->parent->getOption('formAction') != "") {
00303 $action = $this->parent->getOption('formAction');
00304 }
00305 if ($action == "SELF") {
00306 $action = $_SERVER['PHP_SELF'];
00307 $action = $_SERVER['REQUEST_URI'];
00308 }
00309 if (SConfig::getOption('xforms.action') != '') {
00310 $action = SConfig::getOption('xforms.action');
00311 }
00312 $method = $xform->model->submission['method'];
00313
00314
00315 $transId = ($TRANSACTION == null) ? "" :
00316 $TRANSACTION->getTransaction();
00317 $transInput = "";
00318 $tokenInput = "";
00319 if ($transId != "") {
00320
00321 $transInput =<<<ENDTRANS
00322 <input type="hidden" name="transaction" value="$transId" />
00323 ENDTRANS;
00324 }
00325 if ($this->parent->getOption('token')) {
00326 $token = sha1(session_id() . 'hasdfhv894h3gpvub');
00327 $tokenInput =<<<ENDTOKEN
00328 <input type="hidden" name="xformtoken" value="$token" />
00329 ENDTOKEN;
00330 }
00331
00332 $method = strtolower($method);
00333
00334 $htmlOutput .=<<<END_OUTPUT
00335 <div class="shodorxformWrapper">
00336 <form class="shodorxform" action="$action" id="$id" method="$method" enctype="multipart/form-data" accept-charset="utf-8">
00337 $transInput
00338 $tokenInput
00339 END_OUTPUT;
00340 }
00341
00342
00343 if ($this->parent->getErrorCount() > 0)
00344 {
00345 $errorCount = $this->parent->getErrorCount();
00346 $htmlOutput .= <<<END_OUTPUT
00347 <div class="errorHeader" align="center">↓ Your submission was not complete. Please see the $errorCount errors marked below: ↓</div>
00348 END_OUTPUT;
00349 }
00350
00351
00352 $formErr = $this->getParent()->getErrorCode('FORM');
00353 if ($formErr != '') {
00354 $formErrText = $this->getValidator()->getErrorCode($formErr);
00355 $htmlOutput .= <<<END_OUTPUT
00356 <div class="errorHeader" align="center">$formErrText</div>
00357 END_OUTPUT;
00358 }
00359
00360
00361
00362
00363 $lastChild = null; # will hold the child
00364 $curGroup = null; # will hold the current group; stored at start of next iteration
00365 $curGroupItemList = array(); #holds the list of items of the first control of the cur group
00366 $groups = array(); # the list to which groups are added after each iteration
00367
00368
00369
00370 foreach($xform->children() as $tag => $child)
00371 {
00372
00373
00374
00375
00376
00377 $control = null;
00378 switch($tag) {
00379 case 'select1':
00380 $control = new XFormsSelect1Control();
00381 $control->addItemsFromXMLTree($child);
00382 break;
00383 case 'select':
00384 $control = new XFormsSelectControl();
00385 $control->addItemsFromXMLTree($child);
00386 break;
00387 case 'input':
00388 $control = new XFormsInputControl();
00389 break;
00390 case 'password':
00391 $control = new XFormsSecretControl();
00392 break;
00393 case 'upload':
00394 $control = new XFormsUploadControl();
00395 break;
00396 case 'textarea':
00397 $control = new XFormsTextareaControl();
00398 break;
00399 case 'textbox':
00400 case 'submit':
00401 $control = new XFormsSubmitControl();
00402 break;
00403 case 'text':
00404 $control = new XFormsTitleControl();
00405 break;
00406 case 'model':
00407 break;
00408 default:
00409 $this->setPrettyError('formatXFormsInterface', 'Unknown tag found: ' . $tag);
00410 return false;
00411 break;
00412 }
00413
00414 # if this control is null, skip it
00415 if ($control == null) continue;
00416
00417
00418
00419
00420
00421
00422 # set the parent attribute (the link to THIS InterfaceBuilder object)
00423 $control->setParent($this);
00424
00425 # Get the description from the element
00426 if (isset($child->description)) {
00427 $xml = $child->description->asXML();
00428 $xml = str_replace("<description>", "", $xml);
00429 $xml = str_replace("</description>", "", $xml);
00430 $xml = SXMLHelper::contentFromXML($xml);
00431 $control->setDescription($xml);
00432 }
00433
00434 # Set the appearance
00435 if ( isset($child['appearance'])) {
00436 $control->setAppearance($child['appearance']);
00437 }
00438
00439 # set the ref-- binding to the instance XML
00440 if (isset($child['ref'])) {
00441 $control->setRef($child['ref']);
00442 $control->setRequired($this->isRequired($control->getRef(), $xform));
00443 }
00444
00445 # set the location
00446 if (isset($child['location'])) {
00447 $control->setLocation($child['location']);
00448 }
00449
00450 $control->searchForInputValues();
00451
00452
00453
00454
00455 $curItemValues = $control->getItemValues();
00456
00457 if ($curGroup != null
00458 && (count($curItemValues) > 0)
00459 && (SConfig::getOption('xforms.collapseIdentical') == true)
00460 && (count(array_diff($curItemValues, $curGroupItemList)) == 0 &&
00461 count(array_diff($curGroupItemList, $curItemValues)) == 0)) {
00462
00463 # USE THE EXISTING QUESTION GROUP
00464 # (if they are the same, just use the old group - don't make a new one)
00465
00466 # set the question group's display mode to grid
00467 $curGroup->setDisplayMode('grid');
00468
00469 # Transfer the label from the XML to be the group label
00470 $labelHTML = $this->getHTMLFromLabelXML($child);
00471 if ($labelHTML != '') {
00472 $control->setLabel($labelHTML);
00473 }
00474
00475 } else {
00476
00477 # MAKE A NEW QUESTION GROUP
00478 # If they are not the same, or if we're not collapsing questions,
00479 # we need to make a new question group
00480
00481 # store the old group
00482 if ($curGroup != null){
00483 array_push($groups, $curGroup);
00484 }
00485
00486 # Decide which type of group we are making based on the first element
00487 # to go into that group
00488 switch($tag) {
00489 case 'text':
00490 $curGroup = new XFormsTextGroup();
00491 break;
00492 case 'submit':
00493 $curGroup = new XFormsSubmitGroup();
00494 break;
00495 default:
00496 $curGroup = new XFormsControlGroup();
00497 break;
00498 }
00499
00500
00501 $curGroupItemList = ($control->getItemValues());
00502
00503 # Transfer the label from the XML to be the group label
00504 $labelHTML = $this->getHTMLFromLabelXML($child);
00505 if ($labelHTML != '') {
00506 $curGroup->setLabel($labelHTML);
00507 $control->setLabel($labelHTML);
00508 }
00509
00510 }
00511
00512 # LINK THE GROUP INTO THE CONTROL
00513 # set the group attribute (what group is the control in on the page)
00514 $control->setGroup($curGroup);
00515
00516 # ADD THE CONTROL TO THE GROUP
00517 $curGroup->addControl($control);
00518 }
00519
00520 # push the last group onto the array
00521 if ($curGroup != null) {
00522 array_push($groups, $curGroup);
00523 }
00524
00525
00526
00527 foreach($groups as $g) {
00528 $htmlOutput .= $g->render();
00529 }
00530
00531
00532 $xfe = $this->parent->getValidator()->getErrorCode(
00533 $this->parent->getErrorCode('token'));
00534 if ($xfe != '') {
00535 $errLabel = new TKLabel('↑ ' . $xfe);
00536 $errLabel->class = array('errorMsg');
00537 $box = new TKVBox();
00538 $box->add($errLabel);
00539 $htmlOutput .= $box->render();
00540 }
00541
00542 if ($hasSubmission){
00543 $htmlOutput .= "</form></div>";
00544 }
00545
00546 return $htmlOutput;
00547 }
00548
00549
00550
00551
00552
00553
00562 protected function getHTMLFromLabelXML($child) {
00563 if (isset($child->label))
00564 {
00565 # get the label, preserving the formatting inside the XML
00566 $xml = $child->label->asXML();
00567 $xml = str_replace("<label>", "", $xml);
00568 $xml = str_replace("</label>", "", $xml);
00569 $xml = SXMLHelper::contentFromXML($xml);
00570 return $xml;
00571 }
00572 return '';
00573 }
00574
00584 public function isRequired ($ref, $xform)
00585 {
00586 $node = $this->findXMLNode($ref, $xform);
00587 return ((count($node) > 0) && (isset($node[0]['required'])) && ($node[0]['required'] == "true()"));
00588 }
00589
00598 public function getItemCount ($tag)
00599 {
00600 return (isset($this->itemCount["$tag"])) ? $this->itemCount["$tag"] : 0;
00601 }
00602
00610 public function getNamePrefix ()
00611 {
00612 return $this->namePrefix;
00613 }
00614
00622 public function getValidator ()
00623 {
00624 return $this->validator;
00625 }
00626
00634 public function getParent() {
00635 return $this->parent;
00636 }
00637
00645 public function getXFormCache() {
00646 return $this->xformCache;
00647 }
00648
00649 }
00650
00651 ?>