Documentation TYPO3 par Ameos |
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 1999-2005 Kasper Skaarhoj (kasperYYYY@typo3.com) 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * A copy is found in the textfile GPL.txt and important notices to the license 00017 * from the author is found in LICENSE.txt distributed with these scripts. 00018 * 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00062 $BACK_PATH=''; 00063 require ('init.php'); 00064 require ('template.php'); 00065 $LANG->includeLLFile('EXT:lang/locallang_wizards.xml'); 00066 00067 00068 00069 00070 00071 00072 00073 00074 00075 00076 00084 class SC_wizard_table { 00085 00086 // Internal, dynamic: 00087 var $doc; // Document template object 00088 var $content; // Content accumulation for the module. 00089 var $include_once=array(); // List of files to include. 00090 var $inputStyle=0; // True, then <input> fields are shown, not textareas. 00091 00092 00093 // Internal, static: 00094 var $xmlStorage=0; // If set, the string version of the content is interpreted/written as XML instead of the original linebased kind. This variable still needs binding to the wizard parameters - but support is ready! 00095 var $numNewRows=1; // Number of new rows to add in bottom of wizard 00096 var $colsFieldName='cols'; // Name of field in parent record which MAY contain the number of columns for the table - here hardcoded to the value of tt_content. Should be set by TCEform parameters (from P) 00097 00098 00099 // Internal, static: GPvars 00100 var $P; // Wizard parameters, coming from TCEforms linking to the wizard. 00101 var $TABLECFG; // The array which is constantly submitted by the multidimensional form of this wizard. 00102 00103 00104 00105 00106 00107 00113 function init() { 00114 global $BACK_PATH; 00115 00116 // GPvars: 00117 $this->P = t3lib_div::_GP('P'); 00118 $this->TABLECFG = t3lib_div::_GP('TABLE'); 00119 00120 // Setting options: 00121 $this->xmlStorage = $this->P['params']['xmlOutput']; 00122 $this->numNewRows = t3lib_div::intInRange($this->P['params']['numNewRows'],1,50,5); 00123 00124 // Textareas or input fields: 00125 $this->inputStyle=isset($this->TABLECFG['textFields']) ? $this->TABLECFG['textFields'] : 1; 00126 00127 // Document template object: 00128 $this->doc = t3lib_div::makeInstance('mediumDoc'); 00129 $this->doc->docType = 'xhtml_trans'; 00130 $this->doc->backPath = $BACK_PATH; 00131 $this->doc->JScode=$this->doc->wrapScriptTags(' 00132 function jumpToUrl(URL,formEl) { // 00133 document.location = URL; 00134 } 00135 '); 00136 00137 // Setting form tag: 00138 list($rUri) = explode('#',t3lib_div::getIndpEnv('REQUEST_URI')); 00139 $this->doc->form ='<form action="'.htmlspecialchars($rUri).'" method="post" name="wizardForm">'; 00140 00141 // Start page: 00142 $this->content.=$this->doc->startPage('Table'); 00143 00144 // If save command found, include tcemain: 00145 if ($_POST['savedok_x'] || $_POST['saveandclosedok_x']) { 00146 $this->include_once[]=PATH_t3lib.'class.t3lib_tcemain.php'; 00147 } 00148 } 00149 00155 function main() { 00156 global $LANG; 00157 00158 if ($this->P['table'] && $this->P['field'] && $this->P['uid']) { 00159 $this->content.=$this->doc->section($LANG->getLL('table_title'),$this->tableWizard(),0,1); 00160 } else { 00161 $this->content.=$this->doc->section($LANG->getLL('table_title'),'<span class="typo3-red">'.$LANG->getLL('table_noData',1).'</span>',0,1); 00162 } 00163 $this->content.=$this->doc->endPage(); 00164 } 00165 00171 function printContent() { 00172 echo $this->content; 00173 } 00174 00180 function tableWizard() { 00181 00182 // First, check the references by selecting the record: 00183 $row=t3lib_BEfunc::getRecord($this->P['table'],$this->P['uid']); 00184 if (!is_array($row)) { 00185 t3lib_BEfunc::typo3PrintError ('Wizard Error','No reference to record',0); 00186 exit; 00187 } 00188 00189 // This will get the content of the form configuration code field to us - possibly cleaned up, saved to database etc. if the form has been submitted in the meantime. 00190 $tableCfgArray = $this->getConfigCode($row); 00191 00192 // Generation of the Table Wizards HTML code: 00193 $content = $this->getTableHTML($tableCfgArray,$row); 00194 00195 // Return content: 00196 return $content; 00197 } 00198 00199 00200 00201 00202 00203 00204 00205 /*************************** 00206 * 00207 * Helper functions 00208 * 00209 ***************************/ 00210 00219 function getConfigCode($row) { 00220 00221 // If some data has been submitted, then construct 00222 if (isset($this->TABLECFG['c'])) { 00223 00224 // Process incoming: 00225 $this->changeFunc(); 00226 00227 00228 // Convert to string (either line based or XML): 00229 if ($this->xmlStorage) { 00230 // Convert the input array to XML: 00231 $bodyText = t3lib_div::array2xml($this->TABLECFG['c'],'',0,'T3TableWizard'); 00232 00233 // Setting cfgArr directly from the input: 00234 $cfgArr = $this->TABLECFG['c']; 00235 } else { 00236 // Convert the input array to a string of configuration code: 00237 $bodyText = $this->cfgArray2CfgString($this->TABLECFG['c']); 00238 00239 // Create cfgArr from the string based configuration - that way it is cleaned up and any incompatibilities will be removed! 00240 $cfgArr = $this->cfgString2CfgArray($bodyText,$row[$this->colsFieldName]); 00241 } 00242 00243 // If a save button has been pressed, then save the new field content: 00244 if ($_POST['savedok_x'] || $_POST['saveandclosedok_x']) { 00245 00246 // Make TCEmain object: 00247 $tce = t3lib_div::makeInstance('t3lib_TCEmain'); 00248 $tce->stripslashes_values=0; 00249 00250 // Put content into the data array: 00251 $data=array(); 00252 $data[$this->P['table']][$this->P['uid']][$this->P['field']]=$bodyText; 00253 00254 // Perform the update: 00255 $tce->start($data,array()); 00256 $tce->process_datamap(); 00257 00258 // If the save/close button was pressed, then redirect the screen: 00259 if ($_POST['saveandclosedok_x']) { 00260 header('Location: '.t3lib_div::locationHeaderUrl($this->P['returnUrl'])); 00261 exit; 00262 } 00263 } 00264 } else { // If nothing has been submitted, load the $bodyText variable from the selected database row: 00265 if ($this->xmlStorage) { 00266 $cfgArr = t3lib_div::xml2array($row[$this->P['field']]); 00267 } else { // Regular linebased table configuration: 00268 $cfgArr = $this->cfgString2CfgArray($row[$this->P['field']],$row[$this->colsFieldName]); 00269 } 00270 $cfgArr = is_array($cfgArr) ? $cfgArr : array(); 00271 } 00272 00273 return $cfgArr; 00274 } 00275 00284 function getTableHTML($cfgArr,$row) { 00285 global $LANG; 00286 00287 // Traverse the rows: 00288 $tRows=array(); 00289 $k=0; 00290 foreach($cfgArr as $cellArr) { 00291 if (is_array($cellArr)) { 00292 // Initialize: 00293 $cells=array(); 00294 $a=0; 00295 00296 // Traverse the columns: 00297 foreach($cellArr as $cellContent) { 00298 if ($this->inputStyle) { 00299 $cells[]='<input type="text"'.$this->doc->formWidth(20).' name="TABLE[c]['.(($k+1)*2).']['.(($a+1)*2).']" value="'.htmlspecialchars($cellContent).'" />'; 00300 } else { 00301 $cellContent=eregi_replace('<br[ ]?[\/]?>',chr(10),$cellContent); 00302 $cells[]='<textarea '.$this->doc->formWidth(20).' rows="5" name="TABLE[c]['.(($k+1)*2).']['.(($a+1)*2).']">'.t3lib_div::formatForTextarea($cellContent).'</textarea>'; 00303 } 00304 00305 // Increment counter: 00306 $a++; 00307 } 00308 00309 // CTRL panel for a table row (move up/down/around): 00310 $onClick="document.wizardForm.action+='#ANC_".(($k+1)*2-2)."';"; 00311 $onClick=' onclick="'.htmlspecialchars($onClick).'"'; 00312 $ctrl=''; 00313 00314 $brTag=$this->inputStyle?'':'<br />'; 00315 if ($k!=0) { 00316 $ctrl.='<input type="image" name="TABLE[row_up]['.(($k+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/pil2up.gif','').$onClick.' title="'.$LANG->getLL('table_up',1).'" />'.$brTag; 00317 } else { 00318 $ctrl.='<input type="image" name="TABLE[row_bottom]['.(($k+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/turn_up.gif','').$onClick.' title="'.$LANG->getLL('table_bottom',1).'" />'.$brTag; 00319 } 00320 $ctrl.='<input type="image" name="TABLE[row_remove]['.(($k+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/garbage.gif','').$onClick.' title="'.$LANG->getLL('table_removeRow',1).'" />'.$brTag; 00321 00322 if (($k+1)!=count($tLines)) { 00323 $ctrl.='<input type="image" name="TABLE[row_down]['.(($k+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/pil2down.gif','').$onClick.' title="'.$LANG->getLL('table_down',1).'" />'.$brTag; 00324 } else { 00325 $ctrl.='<input type="image" name="TABLE[row_top]['.(($k+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/turn_down.gif','').$onClick.' title="'.$LANG->getLL('table_top',1).'" />'.$brTag; 00326 } 00327 $ctrl.='<input type="image" name="TABLE[row_add]['.(($k+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/add.gif','').$onClick.' title="'.$LANG->getLL('table_addRow',1).'" />'.$brTag; 00328 00329 $tRows[]=' 00330 <tr class="bgColor4"> 00331 <td class="bgColor5"><a name="ANC_'.(($k+1)*2).'"></a><span class="c-wizButtonsV">'.$ctrl.'</span></td> 00332 <td>'.implode('</td> 00333 <td>',$cells).'</td> 00334 </tr>'; 00335 00336 // Increment counter: 00337 $k++; 00338 } 00339 } 00340 00341 // CTRL panel for a table column (move left/right/around/delete) 00342 $cells=array(); 00343 $cells[]=''; 00344 // Finding first row: 00345 reset($cfgArr); 00346 $firstRow=current($cfgArr); 00347 if (is_array($firstRow)) { 00348 00349 // Init: 00350 $a=0; 00351 $cols=count($firstRow); 00352 00353 // Traverse first row: 00354 foreach($firstRow as $temp) { 00355 $ctrl=''; 00356 if ($a!=0) { 00357 $ctrl.='<input type="image" name="TABLE[col_left]['.(($a+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/pil2left.gif','').' title="'.$LANG->getLL('table_left',1).'" />'; 00358 } else { 00359 $ctrl.='<input type="image" name="TABLE[col_end]['.(($a+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/turn_left.gif','').' title="'.$LANG->getLL('table_end',1).'" />'; 00360 } 00361 $ctrl.='<input type="image" name="TABLE[col_remove]['.(($a+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/garbage.gif','').' title="'.$LANG->getLL('table_removeColumn',1).'" />'; 00362 if (($a+1)!=$cols) { 00363 $ctrl.='<input type="image" name="TABLE[col_right]['.(($a+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/pil2right.gif','').' title="'.$LANG->getLL('table_right',1).'" />'; 00364 } else { 00365 $ctrl.='<input type="image" name="TABLE[col_start]['.(($a+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/turn_right.gif','').' title="'.$LANG->getLL('table_start',1).'" />'; 00366 } 00367 $ctrl.='<input type="image" name="TABLE[col_add]['.(($a+1)*2).']"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/add.gif','').' title="'.$LANG->getLL('table_addColumn',1).'" />'; 00368 $cells[]='<span class="c-wizButtonsH">'.$ctrl.'</span>'; 00369 00370 // Incr. counter: 00371 $a++; 00372 } 00373 $tRows[]=' 00374 <tr class="bgColor5"> 00375 <td align="center">'.implode('</td> 00376 <td align="center">',$cells).'</td> 00377 </tr>'; 00378 } 00379 00380 $content = ''; 00381 00382 // Add CSH: 00383 $content.= t3lib_BEfunc::cshItem('xMOD_csh_corebe', 'wizard_table_wiz', $GLOBALS['BACK_PATH'],''); 00384 00385 // Implode all table rows into a string, wrapped in table tags. 00386 $content.= ' 00387 00388 00389 <!-- 00390 Table wizard 00391 --> 00392 <table border="0" cellpadding="0" cellspacing="1" id="typo3-tablewizard"> 00393 '.implode('',$tRows).' 00394 </table>'; 00395 00396 // Add saving buttons in the bottom: 00397 $content.= ' 00398 00399 <!-- 00400 Save buttons: 00401 --> 00402 <div id="c-saveButtonPanel">'; 00403 $content.= '<input type="image" class="c-inputButton" name="savedok"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/savedok.gif','').' title="'.$LANG->sL('LLL:EXT:lang/locallang_core.php:rm.saveDoc',1).'" />'; 00404 $content.= '<input type="image" class="c-inputButton" name="saveandclosedok"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/saveandclosedok.gif','').' title="'.$LANG->sL('LLL:EXT:lang/locallang_core.php:rm.saveCloseDoc',1).'" />'; 00405 $content.= '<a href="#" onclick="'.htmlspecialchars('jumpToUrl(unescape(\''.rawurlencode($this->P['returnUrl']).'\')); return false;').'">'. 00406 '<img'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/closedok.gif','width="21" height="16"').' class="c-inputButton" title="'.$LANG->sL('LLL:EXT:lang/locallang_core.php:rm.closeDoc',1).'" alt="" />'. 00407 '</a>'; 00408 $content.= '<input type="image" class="c-inputButton" name="_refresh"'.t3lib_iconWorks::skinImg($this->doc->backPath,'gfx/refresh_n.gif','').' title="'.$LANG->getLL('forms_refresh',1).'" />'; 00409 $content.= t3lib_BEfunc::cshItem('xMOD_csh_corebe', 'wizard_table_wiz_buttons', $GLOBALS['BACK_PATH'],''); 00410 $content.= ' 00411 </div> 00412 '; 00413 00414 // Input type checkbox: 00415 $content.= ' 00416 00417 <!-- 00418 Input mode check box: 00419 --> 00420 <div id="c-inputMode"> 00421 '. 00422 '<input type="hidden" name="TABLE[textFields]" value="0" />'. 00423 '<input type="checkbox" name="TABLE[textFields]" value="1"'.($this->inputStyle?' checked="checked"':'').' /> '. 00424 $LANG->getLL('table_smallFields').' 00425 </div> 00426 00427 <br /><br /> 00428 '; 00429 00430 // Return content: 00431 return $content; 00432 } 00433 00440 function changeFunc() { 00441 if ($this->TABLECFG['col_remove']) { 00442 $kk = key($this->TABLECFG['col_remove']); 00443 $cmd='col_remove'; 00444 } elseif ($this->TABLECFG['col_add']) { 00445 $kk = key($this->TABLECFG['col_add']); 00446 $cmd='col_add'; 00447 } elseif ($this->TABLECFG['col_start']) { 00448 $kk = key($this->TABLECFG['col_start']); 00449 $cmd='col_start'; 00450 } elseif ($this->TABLECFG['col_end']) { 00451 $kk = key($this->TABLECFG['col_end']); 00452 $cmd='col_end'; 00453 } elseif ($this->TABLECFG['col_left']) { 00454 $kk = key($this->TABLECFG['col_left']); 00455 $cmd='col_left'; 00456 } elseif ($this->TABLECFG['col_right']) { 00457 $kk = key($this->TABLECFG['col_right']); 00458 $cmd='col_right'; 00459 } elseif ($this->TABLECFG['row_remove']) { 00460 $kk = key($this->TABLECFG['row_remove']); 00461 $cmd='row_remove'; 00462 } elseif ($this->TABLECFG['row_add']) { 00463 $kk = key($this->TABLECFG['row_add']); 00464 $cmd='row_add'; 00465 } elseif ($this->TABLECFG['row_top']) { 00466 $kk = key($this->TABLECFG['row_top']); 00467 $cmd='row_top'; 00468 } elseif ($this->TABLECFG['row_bottom']) { 00469 $kk = key($this->TABLECFG['row_bottom']); 00470 $cmd='row_bottom'; 00471 } elseif ($this->TABLECFG['row_up']) { 00472 $kk = key($this->TABLECFG['row_up']); 00473 $cmd='row_up'; 00474 } elseif ($this->TABLECFG['row_down']) { 00475 $kk = key($this->TABLECFG['row_down']); 00476 $cmd='row_down'; 00477 } 00478 00479 if ($cmd && t3lib_div::testInt($kk)) { 00480 if (substr($cmd,0,4)=='row_') { 00481 switch($cmd) { 00482 case 'row_remove': 00483 unset($this->TABLECFG['c'][$kk]); 00484 break; 00485 case 'row_add': 00486 for($a=1;$a<=$this->numNewRows;$a++) { 00487 if (!isset($this->TABLECFG['c'][$kk+$a])) { // Checking if set: The point is that any new row inbetween existing rows will be true after one row is added while if rows are added in the bottom of the table there will be no existing rows to stop the addition of new rows which means it will add up to $this->numNewRows rows then. 00488 $this->TABLECFG['c'][$kk+$a] = array(); 00489 } else { 00490 break; 00491 } 00492 } 00493 break; 00494 case 'row_top': 00495 $this->TABLECFG['c'][1]=$this->TABLECFG['c'][$kk]; 00496 unset($this->TABLECFG['c'][$kk]); 00497 break; 00498 case 'row_bottom': 00499 $this->TABLECFG['c'][10000000]=$this->TABLECFG['c'][$kk]; 00500 unset($this->TABLECFG['c'][$kk]); 00501 break; 00502 case 'row_up': 00503 $this->TABLECFG['c'][$kk-3]=$this->TABLECFG['c'][$kk]; 00504 unset($this->TABLECFG['c'][$kk]); 00505 break; 00506 case 'row_down': 00507 $this->TABLECFG['c'][$kk+3]=$this->TABLECFG['c'][$kk]; 00508 unset($this->TABLECFG['c'][$kk]); 00509 break; 00510 } 00511 ksort($this->TABLECFG['c']); 00512 } 00513 if (substr($cmd,0,4)=='col_') { 00514 reset($this->TABLECFG['c']); 00515 while(list($cAK)=each($this->TABLECFG['c'])) { 00516 switch($cmd) { 00517 case 'col_remove': 00518 unset($this->TABLECFG['c'][$cAK][$kk]); 00519 break; 00520 case 'col_add': 00521 $this->TABLECFG['c'][$cAK][$kk+1]=''; 00522 break; 00523 case 'col_start': 00524 $this->TABLECFG['c'][$cAK][1]=$this->TABLECFG['c'][$cAK][$kk]; 00525 unset($this->TABLECFG['c'][$cAK][$kk]); 00526 break; 00527 case 'col_end': 00528 $this->TABLECFG['c'][$cAK][1000000]=$this->TABLECFG['c'][$cAK][$kk]; 00529 unset($this->TABLECFG['c'][$cAK][$kk]); 00530 break; 00531 case 'col_left': 00532 $this->TABLECFG['c'][$cAK][$kk-3]=$this->TABLECFG['c'][$cAK][$kk]; 00533 unset($this->TABLECFG['c'][$cAK][$kk]); 00534 break; 00535 case 'col_right': 00536 $this->TABLECFG['c'][$cAK][$kk+3]=$this->TABLECFG['c'][$cAK][$kk]; 00537 unset($this->TABLECFG['c'][$cAK][$kk]); 00538 break; 00539 } 00540 ksort($this->TABLECFG['c'][$cAK]); 00541 } 00542 } 00543 } 00544 00545 // Convert line breaks to <br /> tags: 00546 reset($this->TABLECFG['c']); 00547 while(list($a)=each($this->TABLECFG['c'])) { 00548 reset($this->TABLECFG['c'][$a]); 00549 while(list($b)=each($this->TABLECFG['c'][$a])) { 00550 $this->TABLECFG['c'][$a][$b] = str_replace(chr(10),'<br />',str_replace(chr(13),'',$this->TABLECFG['c'][$a][$b])); 00551 } 00552 } 00553 } 00554 00562 function cfgArray2CfgString($cfgArr) { 00563 00564 // Initialize: 00565 $inLines=array(); 00566 00567 // Traverse the elements of the table wizard and transform the settings into configuration code. 00568 reset($this->TABLECFG['c']); 00569 while(list($a)=each($this->TABLECFG['c'])) { 00570 $thisLine=array(); 00571 reset($this->TABLECFG['c'][$a]); 00572 while(list($b)=each($this->TABLECFG['c'][$a])) { 00573 $thisLine[]=str_replace('|','',$this->TABLECFG['c'][$a][$b]); 00574 } 00575 $inLines[]=implode('|',$thisLine); 00576 } 00577 00578 // Finally, implode the lines into a string: 00579 $bodyText = implode(chr(10),$inLines); 00580 00581 // Return the configuration code: 00582 return $bodyText; 00583 } 00584 00593 function cfgString2CfgArray($cfgStr,$cols) { 00594 00595 // Explode lines in the configuration code - each line is a table row. 00596 $tLines=explode(chr(10),$cfgStr); 00597 00598 // Setting number of columns 00599 if (!$cols && trim($tLines[0])) { // auto... 00600 $cols = count(explode('|',$tLines[0])); 00601 } 00602 $cols=$cols?$cols:4; 00603 00604 // Traverse the number of table elements: 00605 $cfgArr=array(); 00606 foreach($tLines as $k => $v) { 00607 00608 // Initialize: 00609 $vParts = explode('|',$v); 00610 00611 // Traverse columns: 00612 for ($a=0;$a<$cols;$a++) { 00613 $cfgArr[$k][$a]=$vParts[$a]; 00614 } 00615 } 00616 00617 // Return configuration array: 00618 return $cfgArr; 00619 } 00620 } 00621 00622 // Include extension? 00623 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/wizard_table.php']) { 00624 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/wizard_table.php']); 00625 } 00626 00627 00628 00629 00630 00631 00632 00633 00634 00635 00636 00637 00638 // Make instance: 00639 $SOBE = t3lib_div::makeInstance('SC_wizard_table'); 00640 $SOBE->init(); 00641 00642 // Include files? 00643 foreach($SOBE->include_once as $INC_FILE) include_once($INC_FILE); 00644 00645 $SOBE->main(); 00646 $SOBE->printContent(); 00647 ?>