config = & $config; $this->def = $config->getHTMLDefinition (); $ret .= $this->start ( 'div', array ( 'class' => 'HTMLPurifier_Printer' ) ); $ret .= $this->renderDoctype (); $ret .= $this->renderEnvironment (); $ret .= $this->renderContentSets (); $ret .= $this->renderInfo (); $ret .= $this->end ( 'div' ); return $ret; } /** * Renders the Doctype table * * @return string */ protected function renderDoctype() { $doctype = $this->def->doctype; $ret = ''; $ret .= $this->start ( 'table' ); $ret .= $this->element ( 'caption', 'Doctype' ); $ret .= $this->row ( 'Name', $doctype->name ); $ret .= $this->row ( 'XML', $doctype->xml ? 'Yes' : 'No' ); $ret .= $this->row ( 'Default Modules', implode ( $doctype->modules, ', ' ) ); $ret .= $this->row ( 'Default Tidy Modules', implode ( $doctype->tidyModules, ', ' ) ); $ret .= $this->end ( 'table' ); return $ret; } /** * Renders environment table, which is miscellaneous info * * @return string */ protected function renderEnvironment() { $def = $this->def; $ret = ''; $ret .= $this->start ( 'table' ); $ret .= $this->element ( 'caption', 'Environment' ); $ret .= $this->row ( 'Parent of fragment', $def->info_parent ); $ret .= $this->renderChildren ( $def->info_parent_def->child ); $ret .= $this->row ( 'Block wrap name', $def->info_block_wrapper ); $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', 'Global attributes' ); $ret .= $this->element ( 'td', $this->listifyAttr ( $def->info_global_attr ), null, 0 ); $ret .= $this->end ( 'tr' ); $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', 'Tag transforms' ); $list = array (); foreach ( $def->info_tag_transform as $old => $new ) { $new = $this->getClass ( $new, 'TagTransform_' ); $list [] = "<$old> with $new"; } $ret .= $this->element ( 'td', $this->listify ( $list ) ); $ret .= $this->end ( 'tr' ); $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', 'Pre-AttrTransform' ); $ret .= $this->element ( 'td', $this->listifyObjectList ( $def->info_attr_transform_pre ) ); $ret .= $this->end ( 'tr' ); $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', 'Post-AttrTransform' ); $ret .= $this->element ( 'td', $this->listifyObjectList ( $def->info_attr_transform_post ) ); $ret .= $this->end ( 'tr' ); $ret .= $this->end ( 'table' ); return $ret; } /** * Renders the Content Sets table * * @return string */ protected function renderContentSets() { $ret = ''; $ret .= $this->start ( 'table' ); $ret .= $this->element ( 'caption', 'Content Sets' ); foreach ( $this->def->info_content_sets as $name => $lookup ) { $ret .= $this->heavyHeader ( $name ); $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'td', $this->listifyTagLookup ( $lookup ) ); $ret .= $this->end ( 'tr' ); } $ret .= $this->end ( 'table' ); return $ret; } /** * Renders the Elements ($info) table * * @return string */ protected function renderInfo() { $ret = ''; $ret .= $this->start ( 'table' ); $ret .= $this->element ( 'caption', 'Elements ($info)' ); ksort ( $this->def->info ); $ret .= $this->heavyHeader ( 'Allowed tags', 2 ); $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'td', $this->listifyTagLookup ( $this->def->info ), array ( 'colspan' => 2 ) ); $ret .= $this->end ( 'tr' ); foreach ( $this->def->info as $name => $def ) { $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', "<$name>", array ( 'class' => 'heavy', 'colspan' => 2 ) ); $ret .= $this->end ( 'tr' ); $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', 'Inline content' ); $ret .= $this->element ( 'td', $def->descendants_are_inline ? 'Yes' : 'No' ); $ret .= $this->end ( 'tr' ); if (! empty ( $def->excludes )) { $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', 'Excludes' ); $ret .= $this->element ( 'td', $this->listifyTagLookup ( $def->excludes ) ); $ret .= $this->end ( 'tr' ); } if (! empty ( $def->attr_transform_pre )) { $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', 'Pre-AttrTransform' ); $ret .= $this->element ( 'td', $this->listifyObjectList ( $def->attr_transform_pre ) ); $ret .= $this->end ( 'tr' ); } if (! empty ( $def->attr_transform_post )) { $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', 'Post-AttrTransform' ); $ret .= $this->element ( 'td', $this->listifyObjectList ( $def->attr_transform_post ) ); $ret .= $this->end ( 'tr' ); } if (! empty ( $def->auto_close )) { $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', 'Auto closed by' ); $ret .= $this->element ( 'td', $this->listifyTagLookup ( $def->auto_close ) ); $ret .= $this->end ( 'tr' ); } $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', 'Allowed attributes' ); $ret .= $this->element ( 'td', $this->listifyAttr ( $def->attr ), array (), 0 ); $ret .= $this->end ( 'tr' ); if (! empty ( $def->required_attr )) { $ret .= $this->row ( 'Required attributes', $this->listify ( $def->required_attr ) ); } $ret .= $this->renderChildren ( $def->child ); } $ret .= $this->end ( 'table' ); return $ret; } /** * Renders a row describing the allowed children of an element * * @param HTMLPurifier_ChildDef $def * HTMLPurifier_ChildDef of pertinent element * @return string */ protected function renderChildren($def) { $context = new HTMLPurifier_Context (); $ret = ''; $ret .= $this->start ( 'tr' ); $elements = array (); $attr = array (); if (isset ( $def->elements )) { if ($def->type == 'strictblockquote') { $def->validateChildren ( array (), $this->config, $context ); } $elements = $def->elements; } if ($def->type == 'chameleon') { $attr ['rowspan'] = 2; } elseif ($def->type == 'empty') { $elements = array (); } elseif ($def->type == 'table') { $elements = array_flip ( array ( 'col', 'caption', 'colgroup', 'thead', 'tfoot', 'tbody', 'tr' ) ); } $ret .= $this->element ( 'th', 'Allowed children', $attr ); if ($def->type == 'chameleon') { $ret .= $this->element ( 'td', 'Block: ' . $this->escape ( $this->listifyTagLookup ( $def->block->elements ) ), null, 0 ); $ret .= $this->end ( 'tr' ); $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'td', 'Inline: ' . $this->escape ( $this->listifyTagLookup ( $def->inline->elements ) ), null, 0 ); } elseif ($def->type == 'custom') { $ret .= $this->element ( 'td', '' . ucfirst ( $def->type ) . ': ' . $def->dtd_regex ); } else { $ret .= $this->element ( 'td', '' . ucfirst ( $def->type ) . ': ' . $this->escape ( $this->listifyTagLookup ( $elements ) ), null, 0 ); } $ret .= $this->end ( 'tr' ); return $ret; } /** * Listifies a tag lookup table. * * @param array $array * Tag lookup array in form of array('tagname' => true) * @return string */ protected function listifyTagLookup($array) { ksort ( $array ); $list = array (); foreach ( $array as $name => $discard ) { if ($name !== '#PCDATA' && ! isset ( $this->def->info [$name] )) { continue; } $list [] = $name; } return $this->listify ( $list ); } /** * Listifies a list of objects by retrieving class names and internal state * * @param array $array * List of objects * @return string * @todo Also add information about internal state */ protected function listifyObjectList($array) { ksort ( $array ); $list = array (); foreach ( $array as $obj ) { $list [] = $this->getClass ( $obj, 'AttrTransform_' ); } return $this->listify ( $list ); } /** * Listifies a hash of attributes to AttrDef classes * * @param array $array * Array hash in form of array('attrname' => HTMLPurifier_AttrDef) * @return string */ protected function listifyAttr($array) { ksort ( $array ); $list = array (); foreach ( $array as $name => $obj ) { if ($obj === false) { continue; } $list [] = "$name = " . $this->getClass ( $obj, 'AttrDef_' ) . ''; } return $this->listify ( $list ); } /** * Creates a heavy header row * * @param string $text * @param int $num * @return string */ protected function heavyHeader($text, $num = 1) { $ret = ''; $ret .= $this->start ( 'tr' ); $ret .= $this->element ( 'th', $text, array ( 'colspan' => $num, 'class' => 'heavy' ) ); $ret .= $this->end ( 'tr' ); return $ret; } } // vim: et sw=4 sts=4