00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042 include_once( "lib/ezxml/classes/ezxml.php" );
00043
00044 if ( !class_exists( 'eZXMLSchema' ) )
00045 include_once( 'kernel/classes/datatypes/ezxmltext/ezxmlschema.php' );
00046
00047
00048 define( 'EZ_XMLINPUTPARSER_SHOW_NO_ERRORS', 0 );
00049 define( 'EZ_XMLINPUTPARSER_SHOW_SCHEMA_ERRORS', 1 );
00050 define( 'EZ_XMLINPUTPARSER_SHOW_ALL_ERRORS', 2 );
00051
00052
00053 define( 'EZ_XMLINPUTPARSER_ERROR_NONE', 0 );
00054 define( 'EZ_XMLINPUTPARSER_ERROR_SYNTAX', 4 );
00055 define( 'EZ_XMLINPUTPARSER_ERROR_SCHEMA', 8 );
00056 define( 'EZ_XMLINPUTPARSER_ERROR_DATA', 16 );
00057 define( 'EZ_XMLINPUTPARSER_ERROR_ALL', 28 );
00058
00059 class eZXMLInputParser
00060 {
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090 var $InputTags = array();
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126 var $OutputTags = array();
00127
00128 var $Namespaces = array( 'image' => 'http://ez.no/namespaces/ezpublish3/image/',
00129 'xhtml' => 'http://ez.no/namespaces/ezpublish3/xhtml/',
00130 'custom' => 'http://ez.no/namespaces/ezpublish3/custom/',
00131 'tmp' => 'http://ez.no/namespaces/ezpublish3/temporary/' );
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144 function eZXMLInputParser( $validateErrorLevel = EZ_XMLINPUTPARSER_ERROR_NONE, $detectErrorLevel = EZ_XMLINPUTPARSER_ERROR_NONE, $parseLineBreaks = false,
00145 $removeDefaultAttrs = false )
00146 {
00147
00148 if ( $detectErrorLevel === EZ_XMLINPUTPARSER_SHOW_SCHEMA_ERRORS )
00149 $detectErrorLevel = EZ_XMLINPUTPARSER_ERROR_SCHEMA;
00150 elseif ( $detectErrorLevel === EZ_XMLINPUTPARSER_SHOW_ALL_ERRORS )
00151 $detectErrorLevel = EZ_XMLINPUTPARSER_ERROR_ALL;
00152
00153 if ( $validateErrorLevel === false )
00154 $validateErrorLevel = EZ_XMLINPUTPARSER_ERROR_NONE;
00155 elseif ( $validateErrorLevel === true )
00156 $validateErrorLevel = $detectErrorLevel;
00157
00158 $this->ValidateErrorLevel = $validateErrorLevel;
00159 $this->DetectErrorLevel = $detectErrorLevel;
00160
00161 $this->RemoveDefaultAttrs = $removeDefaultAttrs;
00162 $this->ParseLineBreaks = $parseLineBreaks;
00163
00164 $this->XMLSchema =& eZXMLSchema::instance();
00165
00166 include_once( 'lib/version.php' );
00167 $this->eZPublishVersion = eZPublishSDK::majorVersion() + eZPublishSDK::minorVersion() * 0.1;
00168
00169 $ini =& eZINI::instance( 'ezxml.ini' );
00170 if ( $this->eZPublishVersion >= 3.8 )
00171 {
00172 if ( $ini->hasVariable( 'InputSettings', 'TrimSpaces' ) )
00173 {
00174 $trimSpaces = $ini->variable( 'InputSettings', 'TrimSpaces' );
00175 $this->TrimSpaces = $trimSpaces == 'true' ? true : false;
00176 }
00177
00178 if ( $ini->hasVariable( 'InputSettings', 'AllowMultipleSpaces' ) )
00179 {
00180 $allowMultipleSpaces = $ini->variable( 'InputSettings', 'AllowMultipleSpaces' );
00181 $this->AllowMultipleSpaces = $allowMultipleSpaces == 'true' ? true : false;
00182 }
00183 }
00184 else
00185 {
00186 $this->TrimSpaces = true;
00187 $this->AllowMultipleSpaces = false;
00188 }
00189
00190 if ( $this->eZPublishVersion >= 3.9 )
00191 {
00192 if ( $ini->hasVariable( 'InputSettings', 'AllowNumericEntities' ) )
00193 {
00194 $allowNumericEntities = $ini->variable( 'InputSettings', 'AllowNumericEntities' );
00195 $this->AllowNumericEntities = $allowNumericEntities == 'true' ? true : false;
00196 }
00197 }
00198 else
00199 {
00200 $this->AllowNumericEntities = false;
00201 }
00202
00203 $contentIni =& eZINI::instance( 'content.ini' );
00204 $useStrictHeaderRule = $contentIni->variable( 'header', 'UseStrictHeaderRule' );
00205 $this->StrictHeaders = $useStrictHeaderRule == 'true' ? true : false;
00206 }
00207
00208
00209 function setDOMDocumentClass( $DOMDocumentClass )
00210 {
00211 $this->DOMDocumentClass = $DOMDocumentClass;
00212 }
00213
00214
00215 function setParseLineBreaks( $value )
00216 {
00217 $this->ParseLineBreaks = $value;
00218 }
00219
00220
00221 function setRemoveDefaultAttrs( $value )
00222 {
00223 $this->RemoveDefaultAttrs = $value;
00224 }
00225
00226
00227 function createRootNode()
00228 {
00229 if ( !$this->Document )
00230 $this->Document = new $this->DOMDocumentClass( '', true );
00231
00232
00233 $mainSection =& $this->Document->createElement( 'section' );
00234 $this->Document->appendChild( $mainSection );
00235 foreach( array( 'image', 'xhtml', 'custom' ) as $prefix )
00236 {
00237 $mainSection->setAttributeNS( 'http://www.w3.org/2000/xmlns/', 'xmlns:' . $prefix, $this->Namespaces[$prefix] );
00238 }
00239 return $this->Document;
00240 }
00241
00242
00243
00244
00245
00246 function process( $text, $createRootNode = true )
00247 {
00248 $text = str_replace( "\r", '', $text);
00249 $text = str_replace( "\t", ' ', $text);
00250 if ( !$this->ParseLineBreaks )
00251 {
00252 $text = str_replace( "\n", '', $text);
00253 }
00254
00255 $this->Document = new $this->DOMDocumentClass( '', true );
00256
00257 if ( $createRootNode )
00258 $this->createRootNode();
00259
00260
00261
00262 $this->performPass1( $text );
00263
00264 if ( $this->QuitProcess )
00265 return false;
00266
00267
00268 $this->performPass2();
00269
00270 if ( $this->QuitProcess )
00271 return false;
00272
00273 return $this->Document;
00274 }
00275
00276
00277
00278
00279
00280
00281 function performPass1( &$data )
00282 {
00283 $ret = true;
00284 $pos = 0;
00285
00286 if ( $this->Document->Root )
00287 {
00288 do
00289 {
00290 $this->parseTag( $data, $pos, $this->Document->Root );
00291 if ( $this->QuitProcess )
00292 {
00293 $ret = false;
00294 break;
00295 }
00296
00297 }
00298 while( $pos < strlen( $data ) );
00299 }
00300 else
00301 {
00302 $tmp = null;
00303 $this->parseTag( $data, $pos, $tmp );
00304 if ( $this->QuitProcess )
00305 {
00306 $ret = false;
00307 }
00308 }
00309 return $ret;
00310 }
00311
00312
00313
00314 function parseTag( &$data, &$pos, &$parent )
00315 {
00316
00317 $initialPos = $pos;
00318
00319
00320
00321
00322
00323
00324 if ( $pos >= strlen( $data ) )
00325 {
00326 return true;
00327 }
00328 $tagBeginPos = strpos( $data, '<', $pos );
00329
00330 if ( $this->ParseLineBreaks )
00331 {
00332
00333 $lineBreakPos = strpos( $data, "\n", $pos );
00334 if ( $lineBreakPos !== false )
00335 {
00336 if ( $tagBeginPos === false )
00337 $tagBeginPos = $lineBreakPos;
00338 else
00339 $tagBeginPos = min( $tagBeginPos, $lineBreakPos );
00340 }
00341 }
00342
00343 $tagName = '';
00344 $attributes = null;
00345
00346 if ( $tagBeginPos != $pos || $tagBeginPos === false )
00347 {
00348 $pos = $initialPos;
00349 $tagName = $newTagName = '#text';
00350 $noChildren = true;
00351
00352 if ( !$tagBeginPos )
00353 $tagBeginPos = strlen( $data );
00354
00355 $textContent = substr( $data, $pos, $tagBeginPos - $pos );
00356
00357 $textContent = $this->washText( $textContent );
00358
00359 $pos = $tagBeginPos;
00360 if ( $textContent === '' )
00361 return false;
00362 }
00363
00364 elseif ( $data[$tagBeginPos] == '<' && $tagBeginPos + 1 < strlen( $data ) &&
00365 $data[$tagBeginPos + 1] == '/' )
00366 {
00367 $tagEndPos = strpos( $data, '>', $tagBeginPos + 1 );
00368 if ( $tagEndPos === false )
00369 {
00370 $pos = $tagBeginPos + 1;
00371
00372 $this->handleError( EZ_XMLINPUTPARSER_ERROR_SYNTAX, 'Wrong closing tag' );
00373 return false;
00374 }
00375
00376 $pos = $tagEndPos + 1;
00377 $closedTagName = strtolower( trim( substr( $data, $tagBeginPos + 2, $tagEndPos - $tagBeginPos - 2 ) ) );
00378
00379
00380 $firstLoop = true;
00381 for( $i = count( $this->ParentStack ) - 1; $i >= 0; $i-- )
00382 {
00383 $parentNames = $this->ParentStack[$i];
00384 if ( $parentNames[0] == $closedTagName )
00385 {
00386 array_pop( $this->ParentStack );
00387 if ( !$firstLoop )
00388 {
00389 $pos = $tagBeginPos;
00390 return true;
00391 }
00392
00393 elseif ( $parentNames[1] !== '' )
00394 return true;
00395 else
00396 return false;
00397 }
00398 $firstLoop = false;
00399 }
00400
00401 $this->handleError( EZ_XMLINPUTPARSER_ERROR_SYNTAX, 'Wrong closing tag : </%1>.', array( $closedTagName ) );
00402
00403 return false;
00404 }
00405
00406 elseif ( $this->ParseLineBreaks && $data[$tagBeginPos] == "\n" )
00407 {
00408 $newTagName = 'br';
00409 $noChildren = true;
00410 $pos = $tagBeginPos + 1;
00411 }
00412
00413 else
00414 {
00415 $tagEndPos = strpos( $data, '>', $tagBeginPos );
00416 if ( $tagEndPos === false )
00417 {
00418 $pos = $tagBeginPos + 1;
00419
00420 $this->handleError( EZ_XMLINPUTPARSER_ERROR_SYNTAX, 'Wrong opening tag' );
00421 return false;
00422 }
00423
00424 $pos = $tagEndPos + 1;
00425 $tagString = substr( $data, $tagBeginPos + 1, $tagEndPos - $tagBeginPos - 1 );
00426
00427 $noChildren = substr( $tagString, -1, 1 ) == '/' ? true : false;
00428
00429 $tagString = preg_replace( "/\s*\/$/", "", $tagString );
00430
00431 $firstSpacePos = strpos( $tagString, ' ' );
00432 if ( $firstSpacePos === false )
00433 {
00434 $tagName = strtolower( trim( $tagString ) );
00435 $attributeString = '';
00436 }
00437 else
00438 {
00439 $tagName = strtolower( substr( $tagString, 0, $firstSpacePos ) );
00440 $attributeString = substr( $tagString, $firstSpacePos + 1 );
00441 $attributeString = trim( $attributeString );
00442
00443 if ( $attributeString )
00444 $attributes = $this->parseAttributes( $attributeString );
00445 }
00446
00447
00448 if ( isset( $this->InputTags[$tagName] ) )
00449 {
00450 $thisInputTag =& $this->InputTags[$tagName];
00451
00452 if ( isset( $thisInputTag['name'] ) )
00453 $newTagName = $thisInputTag['name'];
00454 else
00455 $newTagName =& $this->callInputHandler( 'nameHandler', $tagName, $attributes );
00456 }
00457 else
00458 {
00459 if ( $this->XMLSchema->exists( $tagName ) )
00460 {
00461 $newTagName = $tagName;
00462 }
00463 else
00464 {
00465 $this->handleError( EZ_XMLINPUTPARSER_ERROR_SYNTAX, 'Unknown tag: <%1>.', array( $tagName ) );
00466 return false;
00467 }
00468 }
00469
00470
00471 if ( isset( $thisInputTag['noChildren'] ) )
00472 $noChildren = true;
00473
00474 $thisOutputTag =& $this->OutputTags[$newTagName];
00475
00476
00477 if ( isset( $thisOutputTag['autoCloseOn'] ) &&
00478 $parent &&
00479 $parent->parentNode &&
00480 in_array( $parent->nodeName, $thisOutputTag['autoCloseOn'] ) )
00481 {
00482
00483 array_pop( $this->ParentStack );
00484 $pos = $tagBeginPos;
00485 return true;
00486 }
00487
00488
00489 if ( !$noChildren && $newTagName !== false )
00490 {
00491 $this->ParentStack[] = array( $tagName, $newTagName, $attributeString );
00492 }
00493
00494 if ( !$newTagName )
00495 {
00496
00497 if ( $newTagName === false )
00498 $this->handleError( EZ_XMLINPUTPARSER_ERROR_SYNTAX, "Can't convert tag's name: <%1>.", array( $tagName ) );
00499
00500 return false;
00501 }
00502
00503
00504 if ( $attributeString )
00505 {
00506 $this->wordMatchSupport( $newTagName, $attributes, $attributeString );
00507 }
00508 }
00509
00510
00511 if ( $newTagName == '#text' )
00512 $element = $this->Document->createTextNode( $textContent );
00513 else
00514 $element =& $this->Document->createElement( $newTagName );
00515
00516 if ( $attributes )
00517 {
00518 $this->setAttributes( $element, $attributes );
00519 }
00520
00521
00522 if ( $parent )
00523 $parent->appendChild( $element );
00524 else
00525 $this->Document->appendChild( $element );
00526
00527
00528 $params = array();
00529 $params[] =& $data;
00530 $params[] =& $pos;
00531 $params[] =& $tagBeginPos;
00532 $result =& $this->callOutputHandler( 'parsingHandler', $element, $params );
00533
00534 if ( $result === false )
00535 {
00536
00537 if ( !$noChildren )
00538 array_pop( $this->ParentStack );
00539 return false;
00540 }
00541
00542 if ( $this->QuitProcess )
00543 return false;
00544
00545
00546 if ( !$noChildren )
00547 {
00548 do
00549 {
00550 $parseResult = $this->parseTag( $data, $pos, $element );
00551 if ( $this->QuitProcess )
00552 return false;
00553 }
00554 while( $parseResult !== true );
00555 }
00556
00557 return false;
00558 }
00559
00560
00561
00562
00563
00564 function parseAttributes( $attributeString )
00565 {
00566
00567 $attributeString = preg_replace( "/ +([a-zA-Z0-9:-_#\-]+) *\='(.*?)'/e", "' \\1'.'=\"'.'\\2'.'\"'", ' ' . $attributeString );
00568
00569
00570 $attributeString = preg_replace( "/ +([a-zA-Z0-9:-_#\-]+) *\= *([^\s'\"]+)/e", "' \\1'.'=\"'.'\\2'.'\" '", $attributeString );
00571
00572
00573 $attributeArray = preg_split( "#(?<=\") +#", $attributeString );
00574
00575 $attributes = array();
00576 foreach( $attributeArray as $attrStr )
00577 {
00578 if ( !$attrStr || strlen( $attrStr ) < 4 )
00579 continue;
00580
00581 list( $attrName, $attrValue ) = split( '="', $attrStr );
00582
00583 $attrName = strtolower( trim( $attrName ) );
00584 if ( !$attrName )
00585 continue;
00586
00587 $attrValue = substr( $attrValue, 0, -1 );
00588 if ( $attrValue === '' || $attrValue === false )
00589 continue;
00590
00591 $attributes[$attrName] = $attrValue;
00592 }
00593
00594 return $attributes;
00595 }
00596
00597 function setAttributes( &$element, $attributes )
00598 {
00599 $thisOutputTag =& $this->OutputTags[$element->nodeName];
00600
00601 foreach( $attributes as $key => $value )
00602 {
00603
00604 if ( isset( $thisOutputTag['attributes'] ) &&
00605 isset( $thisOutputTag['attributes'][$key] ) )
00606 {
00607 $qualifiedName = $thisOutputTag['attributes'][$key];
00608 }
00609 else
00610 $qualifiedName = $key;
00611
00612
00613 if ( $qualifiedName == 'class' )
00614 {
00615 $classesList = $this->XMLSchema->getClassesList( $element->nodeName );
00616 if ( !in_array( $value, $classesList ) )
00617 {
00618 $this->handleError( EZ_XMLINPUTPARSER_ERROR_DATA,
00619 "Class '%1' is not allowed for element <%2> (check content.ini).",
00620 array( $value, $element->nodeName ) );
00621 continue;
00622 }
00623 }
00624
00625
00626 if ( $qualifiedName )
00627 {
00628 if ( strpos( $qualifiedName, ':' ) )
00629 {
00630 list( $prefix, $name ) = explode( ':', $qualifiedName );
00631 if ( isset( $this->Namespaces[$prefix] ) )
00632 {
00633 $URI = $this->Namespaces[$prefix];
00634 $element->setAttributeNS( $URI, $qualifiedName, $value );
00635 }
00636 else
00637 eZDebug::writeWarning( "No namespace defined for prefix '$prefix'.", 'eZXML input parser' );
00638 }
00639 else
00640 {
00641 $element->setAttribute( $qualifiedName, $value );
00642 }
00643 }
00644 }
00645
00646
00647 if ( isset( $this->OutputTags[$element->nodeName]['requiredInputAttributes'] ) )
00648 {
00649 foreach( $this->OutputTags[$element->nodeName]['requiredInputAttributes'] as $reqAttrName )
00650 {
00651 $presented = false;
00652 foreach( $attributes as $key => $value )
00653 {
00654 if ( $key == $reqAttrName )
00655 {
00656 $presented = true;
00657 break;
00658 }
00659 }
00660 if ( !$presented )
00661 {
00662 $this->handleError( EZ_XMLINPUTPARSER_ERROR_SCHEMA,
00663 "Required attribute '%1' is not presented in tag <%2>.",
00664 array( $reqAttrName, $element->nodeName ) );
00665 }
00666 }
00667 }
00668 }
00669
00670 function washText( $textContent )
00671 {
00672 $textContent = $this->entitiesDecode( $textContent );
00673
00674 if ( !$this->AllowNumericEntities )
00675 $textContent = $this->convertNumericEntities( $textContent );
00676
00677 if ( !$this->AllowMultipleSpaces )
00678 $textContent = preg_replace( "/ {2,}/", " ", $textContent );
00679
00680 return $textContent;
00681 }
00682
00683 function entitiesDecode( $text )
00684 {
00685
00686 $text = str_replace( "'", "'", $text );
00687
00688 $text = str_replace( ">", ">", $text );
00689 $text = str_replace( "<", "<", $text );
00690 $text = str_replace( "'", "'", $text );
00691 $text = str_replace( """, '"', $text );
00692 $text = str_replace( "&", "&", $text );
00693 $text = str_replace( " ", " ", $text );
00694 return $text;
00695 }
00696
00697 function convertNumericEntities( $text )
00698 {
00699 if ( strlen( $text ) < 4 )
00700 {
00701 return $text;
00702 }
00703
00704 include_once( 'lib/ezi18n/classes/eztextcodec.php' );
00705 $codec = eZTextCodec::instance( 'unicode', false );
00706 $pos = 0;
00707 $domString = "";
00708 while ( $pos < strlen( $text ) - 1 )
00709 {
00710 $startPos = $pos;
00711 while( !( $text[$pos] == '&' && $text[$pos + 1] == '#' ) && $pos < strlen( $text ) - 1 )
00712 $pos++;
00713
00714 $domString .= substr( $text, $startPos, $pos - $startPos );
00715
00716 if ( $pos < strlen( $text ) - 1 )
00717 {
00718 $endPos = strpos( $text, ";", $pos + 2 );
00719 if ( $endPos === false )
00720 {
00721 $convertedText .= '&#';
00722 $pos += 2;
00723 continue;
00724 }
00725
00726 $code = substr( $text, $pos + 2, $endPos - ( $pos + 2 ) );
00727 $char = $codec->convertString( array( $code ) );
00728
00729 $pos = $endPos + 1;
00730 $domString .= $char;
00731 }
00732 else
00733 {
00734 $domString .= substr( $text, $pos, 2 );
00735 }
00736 }
00737 return $domString;
00738 }
00739
00740 function wordMatchSupport( $newTagName, &$attributes, $attributeString )
00741 {
00742 $ini =& eZINI::instance( 'wordmatch.ini' );
00743 if ( $ini->hasVariable( $newTagName, 'MatchString' ) )
00744 {
00745 $matchArray = $ini->variable( $newTagName, 'MatchString' );
00746 if ( $matchArray )
00747 {
00748 foreach ( array_keys( $matchArray ) as $key )
00749 {
00750 $matchString = $matchArray[$key];
00751 if ( preg_match( "/$matchString/i", $attributeString ) )
00752 {
00753 $attributes['class'] = $key;
00754 unset( $attributes['style'] );
00755 }
00756 }
00757 }
00758 }
00759 }
00760
00761
00762
00763
00764
00765
00766
00767 function performPass2()
00768 {
00769 $tmp = null;
00770
00771
00772 $this->processSubtree( $this->Document->Root, $tmp );
00773 }
00774
00775
00776
00777 function &processSubtree( &$element, &$lastHandlerResult )
00778 {
00779 $ret = null;
00780 $tmp = null;
00781
00782
00783
00784
00785
00786 $this->callOutputHandler( 'initHandler', $element, $tmp );
00787
00788
00789 if ( $element->hasChildNodes() )
00790 {
00791
00792
00793 $childrenCount = count( $element->Children );
00794 $children = array();
00795 foreach( array_keys( $element->Children ) as $child_key )
00796 {
00797 $children[] =& $element->Children[$child_key];
00798 }
00799 $lastResult = null;
00800 $newElements = array();
00801 for( $i = 0; $i < $childrenCount; $i++ )
00802 {
00803 $childReturn =& $this->processSubtree( $children[$i], $lastResult );
00804
00805 if ( isset( $childReturn['result'] ) )
00806 {
00807 unset( $lastResult );
00808 $lastResult =& $childReturn['result'];
00809 }
00810 else
00811 unset( $lastResult );
00812
00813 if ( isset( $childReturn['new_elements'] ) )
00814 $newElements = array_merge( $newElements, $childReturn['new_elements'] );
00815
00816 unset( $childReturn );
00817
00818 if ( $this->QuitProcess )
00819 return $ret;
00820 }
00821
00822
00823 $this->processNewElements( $newElements );
00824 }
00825
00826
00827 $ret =& $this->callOutputHandler( 'structHandler', $element, $lastHandlerResult );
00828
00829
00830 if ( !$this->processBySchemaPresence( $element ) )
00831 return $ret;
00832
00833
00834 if ( !$this->processBySchemaTree( $element ) )
00835 return $ret;
00836
00837 $tmp = null;
00838
00839 $this->callOutputHandler( 'publishHandler', $element, $tmp );
00840
00841
00842 if( $element->hasAttributes() )
00843 {
00844 if ( !$this->XMLSchema->hasAttributes( $element ) )
00845 {
00846 $element->removeAttributes();
00847 }
00848 else
00849 {
00850 $this->processAttributesBySchema( $element );
00851 }
00852 }
00853 return $ret;
00854 }
00855
00856
00857
00858
00859
00860 function processBySchemaPresence( &$element )
00861 {
00862 $parent =& $element->parentNode;
00863 if ( $parent )
00864 {
00865
00866 if ( !$this->XMLSchema->exists( $element ) )
00867 {
00868 if ( $element->nodeName == 'custom' )
00869 {
00870 $this->handleError( EZ_XMLINPUTPARSER_ERROR_SCHEMA, "Custom tag '%1' is not allowed.",
00871 array( $element->getAttribute( 'name' ) ) );
00872 }
00873 $parent->removeChild( $element );
00874 return false;
00875 }
00876
00877
00878
00879 if ( ( $this->XMLSchema->childrenRequired( $element ) || $element->getAttribute( 'children_required' ) )
00880 && !$element->hasChildNodes() )
00881 {
00882 $parent->removeChild( $element );
00883 if ( !$element->getAttributeNS( 'http://ez.no/namespaces/ezpublish3/temporary/', 'new-element' ) )
00884 {
00885 $this->handleError( EZ_XMLINPUTPARSER_ERROR_SCHEMA, "<%1> tag can't be empty.",
00886 array( $element->nodeName ) );
00887 return false;
00888 }
00889 }
00890 }
00891
00892
00893 elseif ( $element->nodeName != 'section' )
00894 {
00895 return false;
00896 }
00897 return true;
00898 }
00899
00900
00901 function processBySchemaTree( &$element )
00902 {
00903 $parent =& $element->parentNode;
00904 if ( $parent )
00905 {
00906 $schemaCheckResult = $this->XMLSchema->check( $parent, $element );
00907 if ( !$schemaCheckResult )
00908 {
00909 if ( $schemaCheckResult === false )
00910 {
00911
00912 if ( $element->Type == EZ_XML_NODE_TEXT && !trim( $element->content() ) )
00913 {
00914 $parent->removeChild( $element );
00915 return false;
00916 }
00917
00918 $elementName = $element->nodeName == '#text' ? $element->nodeName : '<' . $element->nodeName . '>';
00919 $this->handleError( EZ_XMLINPUTPARSER_ERROR_SCHEMA, "%1 is not allowed to be a child of <%2>.",
00920 array( $elementName, $parent->nodeName ) );
00921 }
00922 $this->fixSubtree( $element, $element );
00923 return false;
00924 }
00925 }
00926
00927
00928 elseif ( $element->nodeName != 'section' )
00929 {
00930 return false;
00931 }
00932 return true;
00933 }
00934
00935
00936 function fixSubtree( &$element, &$mainChild )
00937 {
00938 $parent =& $element->parentNode;
00939 $mainParent =& $mainChild->parentNode;
00940 if ( $element->hasChildNodes() )
00941 {
00942 foreach( array_keys( $element->Children ) as $child_key )
00943 {
00944 $child =& $element->Children[$child_key];
00945
00946 $element->removeChild( $child );
00947
00948 $mainParent->insertBefore( $child, $mainChild );
00949
00950 if ( !$this->XMLSchema->check( $mainParent, $child ) )
00951 $this->fixSubtree( $child, $mainChild );
00952 }
00953 }
00954 $parent->removeChild( $element );
00955 }
00956
00957 function processAttributesBySchema( &$element )
00958 {
00959
00960 $schemaAttributes = $this->XMLSchema->attributes( $element );
00961 if ( $this->eZPublishVersion >= 3.9 )
00962 {
00963 $schemaCustomAttributes = $this->XMLSchema->customAttributes( $element );
00964 }
00965
00966 $attributes = $element->attributes();
00967
00968 foreach( $attributes as $attr )
00969 {
00970 if ( $attr->Prefix == 'tmp' )
00971 {
00972 $element->removeAttributeNS( $attr->NamespaceURI, $attr->LocalName );
00973 continue;
00974 }
00975
00976 $allowed = false;
00977 $removeAttr = false;
00978
00979
00980 if ( $attr->Prefix )
00981 $fullName = $attr->Prefix . ':' . $attr->LocalName;
00982 else
00983 $fullName = $attr->LocalName;
00984
00985 if ( $this->eZPublishVersion >= 3.9 )
00986 {
00987
00988 if ( $attr->Prefix == 'custom' && in_array( $attr->LocalName, $schemaCustomAttributes ) )
00989 {
00990 $allowed = true;
00991 }
00992 else
00993 {
00994 if ( in_array( $fullName, $schemaAttributes ) )
00995 {
00996 $allowed = true;
00997 }
00998 elseif ( in_array( $fullName, $schemaCustomAttributes ) )
00999 {
01000
01001 $allowed = true;
01002 $removeAttr = true;
01003 $element->setAttributeNS( $this->Namespaces['custom'], 'custom:' . $fullName, $attr->content() );
01004 }
01005 }
01006 }
01007 else
01008 {
01009 if ( $attr->Prefix == 'custom' ||
01010 in_array( $fullName, $schemaAttributes ) )
01011 {
01012 $allowed = true;
01013 }
01014 }
01015
01016 if ( !$allowed )
01017 {
01018 $removeAttr = true;
01019 $this->handleError( EZ_XMLINPUTPARSER_ERROR_SCHEMA,
01020 "Attribute '%1' is not allowed in <%2> element.",
01021 array( $fullName, $element->nodeName ) );
01022 }
01023 elseif ( $this->RemoveDefaultAttrs )
01024 {
01025
01026 $default = $this->XMLSchema->attrDefaultValue( $element->nodeName, $fullName );
01027 if ( $attr->Content == $default )
01028 {
01029 $removeAttr = true;
01030 }
01031 }
01032
01033 if ( $removeAttr )
01034 {
01035 if ( $attr->Prefix )
01036 $element->removeAttributeNS( $attr->NamespaceURI, $attr->LocalName );
01037 else
01038 $element->removeAttribute( $attr->nodeName );
01039 }
01040 }
01041 }
01042
01043 function &callInputHandler( $handlerName, $tagName, &$attributes )
01044 {
01045 $result = null;
01046 $thisInputTag =& $this->InputTags[$tagName];
01047 if ( isset( $thisInputTag[$handlerName] ) )
01048 {
01049 if ( is_callable( array( $this, $thisInputTag[$handlerName] ) ) )
01050 eval( '$result =& $this->' . $thisInputTag[$handlerName] . '( $tagName, $attributes );' );
01051 else
01052 eZDebug::writeWarning( "'$handlerName' input handler for tag <$tagName> doesn't exist: '" . $thisInputTag[$handlerName] . "'.", 'eZXML input parser' );
01053 }
01054 return $result;
01055 }
01056
01057 function &callOutputHandler( $handlerName, &$element, &$params )
01058 {
01059 $result = null;
01060 $thisOutputTag =& $this->OutputTags[$element->nodeName];
01061 if ( isset( $thisOutputTag[$handlerName] ) )
01062 {
01063 if ( is_callable( array( $this, $thisOutputTag[$handlerName] ) ) )
01064 eval( '$result =& $this->' . $thisOutputTag[$handlerName] . '( $element, $params );' );
01065 else
01066 eZDebug::writeWarning( "'$handlerName' output handler for tag <$element->nodeName> doesn't exist: '" . $thisOutputTag[$handlerName] . "'.", 'eZXML input parser' );
01067 }
01068
01069 return $result;
01070 }
01071
01072
01073
01074
01075 function &createAndPublishElement( $elementName, &$ret )
01076 {
01077 $element =& $this->Document->createElement( $elementName );
01078 $element->setAttributeNS( 'http://ez.no/namespaces/ezpublish3/temporary/', 'tmp:new-element', 'true' );
01079
01080 if ( !isset( $ret['new_elements'] ) )
01081 $ret['new_elements'] = array();
01082
01083 $ret['new_elements'][] =& $element;
01084 return $element;
01085 }
01086
01087 function processNewElements( &$createdElements )
01088 {
01089
01090 foreach( array_keys( $createdElements ) as $key )
01091 {
01092 $element =& $createdElements[$key];
01093
01094 $tmp = null;
01095
01096 if ( !$this->processBySchemaPresence( $element ) )
01097 continue;
01098
01099
01100 $this->callOutputHandler( 'structHandler', $element, $tmp );
01101
01102 if ( !$this->processBySchemaTree( $element ) )
01103 continue;
01104
01105 $tmp2 = null;
01106
01107 $this->callOutputHandler( 'publishHandler', $element, $tmp2 );
01108
01109
01110 if( $element->hasAttributes() )
01111 {
01112 if ( !$this->XMLSchema->hasAttributes( $element ) )
01113 {
01114 $element->removeAttributes();
01115 }
01116 else
01117 {
01118 $this->processAttributesBySchema( $element );
01119 }
01120 }
01121 }
01122 }
01123
01124
01125 function getMessages()
01126 {
01127 return $this->Messages;
01128 }
01129
01130
01131 function isValid()
01132 {
01133 return $this->IsInputValid;
01134 }
01135
01136 function handleError( $type, $message, $params = false )
01137 {
01138 if ( $type & $this->DetectErrorLevel )
01139 {
01140 $this->IsInputValid = false;
01141 if ( $message )
01142 $this->Messages[] = ezi18n( 'kernel/classes/datatypes/ezxmltext', $message,
01143 false, $params );
01144 }
01145
01146 if ( $type & $this->ValidateErrorLevel )
01147 {
01148 $this->IsInputValid = false;
01149 $this->QuitProcess = true;
01150 }
01151 }
01152
01153 var $DOMDocumentClass = 'eZDOMDocument';
01154
01155 var $XMLSchema;
01156 var $Document = null;
01157 var $Messages = array();
01158 var $eZPublishVersion;
01159
01160 var $ParentStack = array();
01161
01162 var $ValidateErrorLevel;
01163 var $DetectErrorLevel;
01164
01165 var $IsInputValid = true;
01166 var $QuitProcess = false;
01167
01168
01169 var $TrimSpaces = true;
01170 var $AllowMultipleSpaces = false;
01171 var $AllowNumericEntities = false;
01172 var $StrictHeaders = false;
01173
01174
01175 var $parseLineBreaks = false;
01176 var $removeDefaultAttrs = false;
01177
01178 var $createdElements = array();
01179 }
01180 ?>