forena-8.x-1.x-dev/src/FrxPlugin/Renderer/RendererBase.php
src/FrxPlugin/Renderer/RendererBase.php
<?php /** * @file FrxRenderer.php * Base class for FrxAPI custom Renderer * @author davidmetzler * */ namespace Drupal\forena\FrxPlugin\Renderer; use Drupal\forena\AppService; use DOMXPath; use DOMElement; use Drupal\forena\Context\DataContext; use Drupal\forena\FrxAPI; use Drupal\forena\FrxPlugin\Document\DocumentInterface; use Drupal\forena\Report; use DOMNode; /** * Crosstab Renderer * * @FrxRenderer(id = "RendererBase") */ class RendererBase implements RendererInterface { use FrxAPI; public $report; // The report object being used. public $reportDomNode; public $reportNode; public $frxAttributes; // FrxAPI Attributes of the node we are rendering. public $htmlAttributes; // Html attributes of the node that we are rendering public $name; public $id; public $columns; public $numeric_columns; public $xmlns = 'urn:FrxReports'; public $xpathQuery; public $input_format = 'full_html'; public $doc_types = array(); // Specify the required document types to use this format. protected $document; public function __construct(Report $report, DocumentInterface $doc = NULL) { $this->report = $report; if ($doc) { $this->document = $doc; } else { $this->document = $this->getDocument(); } } public function write($text) { $this->document->write($text); } /** * This function is called to give the renderer the current conetxt in report rendering. * It makes sure the renderer has the current DOM nodes dom documnent, and other attributes. * @param DOMElement $domNode * @param Report $frxReport */ public function initReportNode(DOMNode $domNode) { $this->reportNode = simplexml_import_dom($domNode); $this->reportDomNode = $domNode; $skin = $this->getDataContext('skin'); $this->settings = isset($skin['Report']) ? $skin['Report'] : array(); $this->htmlAttributes = $this->reportNode->attributes(); $this->id = (string)$this->htmlAttributes['id']; $this->frxAttributes = $this->reportNode->attributes(Report::FRX_NS); unset($this->xpathQuery); $this->xpathQuery = new DOMXPath($this->report->dom); } /** * A helper function to allow replacement of tokens from inside a renderer wihout * needing to understand the object * @param string $text * Text containing tokens to replace. * @param bool $raw_mode * TRUE implies that token data should not formatted for human consumption. * @return string * Replaced text. */ public function replaceTokens($text, $raw_mode=FALSE) { if (is_array($text)) { foreach($text as $k => $v) { $text[$k] = $this->replaceTokens($v, $raw_mode); } return $text; } elseif (is_object($text)) { foreach($text as $k => $v) { $text->$k = $this->replaceTokens($v, $raw_mode); } return $text; } else { return $this->report->replace($text, $raw_mode); } } /** * Recursive report renderer * Walks the nodes rendering the report. */ public function renderDomNode(DOMNode $dom_node, &$o) { // Write out buffer if we've gotten too big $continue = TRUE; $is_data_block = FALSE; //$o = ''; $node_type = $dom_node->nodeType; $settings = $this->settings; $context = $this->currentDataContextArray(); // Shortcut process a text node if ($node_type == XML_TEXT_NODE|| $node_type == XML_ENTITY_REF_NODE || $node_type == XML_ENTITY_NODE) { $text = $dom_node->textContent; if (!empty($settings['stripWhiteSpace'])) { $this->write( trim( $this->report->replace(htmlspecialchars($text, ENT_NOQUOTES)))); } else { $this->write($this->report->replace(htmlspecialchars($text, ENT_NOQUOTES))); } return NULL; } //Handle comment nodes if ($node_type == XML_COMMENT_NODE) { if (!empty($dom_node->length) && !empty($dom_node->data)) { $text = $dom_node->data; // strip empty comments if configured to if (!empty($settings['stripEmptyComments'])) { $comment_text = trim($this->report->replace($text)); if ($comment_text === '') { return ''; } } // comment markup is stripped so need to add it back in $cmt = '<!--' . $this->report->replace($text) . '-->'; $this->write($cmt); return NULL; } else { return NULL; } } // Continue processing non text nodes $node = simplexml_import_dom($dom_node); // Special catch to make sure we don't process bad nodes if (!is_object($node)) { return NULL; } $frx = $node->attributes(Report::FRX_NS); $include_root = !isset($frx['skip_root']) || !$frx['skip_root']; $elements = $dom_node->childNodes->length; // Check for invalid link processing. if (@(string)$frx['invalid_link']) { $old_link_mode = $this->link_mode; $this->report->link_mode = (string)$frx['invalid_link']; } // Test to see if we have any nodes that contain data url $attrs = $node->attributes(); $id = (string)$attrs['id']; $tag = $node->getName(); $has_children = TRUE; // Preprocessing for detecting blank nodes if (@$settings['stripEmptyElements']) { $has_children = count($node->children()) > 0; $has_attributes = FALSE; foreach($attrs as $attr) { if (trim($this->report->replace((string)$attr))) { $has_attributes = TRUE; } } if (!$has_children && !$has_attributes) { $has_text = trim($this->report->replace((string)$dom_node->textContent)) !== ''; if (!$has_text) { return NULL; } else { $has_children = TRUE; } } } else { $has_children = count($node->children()) > 0 || trim($dom_node->textContent) !== ''; } if ((string)$frx['block']) { // Determine the context $this->blockName = (string)$frx['block']; $this->blockParms = $context; // Now get the block $is_data_block = TRUE; $xml = $this->report->getData((string)$frx['block'], (string)$frx['parameters']); if ($xml) { $this->pushData($xml, $id); } else { if ($id) $this->setDataContext($id, $xml); return NULL; } } //Implment if then logic if ((string)$frx['if']) { $cond = (string)$frx['if']; if (!$this->report->test($cond)) return NULL; } // Preserve plain attributes $attr_text=''; $tmp_attrs = array(); if ($attrs) foreach ($attrs as $key => $value) { $attr_text .= ' ' . $key . '="' . (string)$value . '"'; $tmp_attrs[$key] = (string)$value; } // Preserve other namespaced attributes $ns_attrs = ''; foreach ($node->getNamespaces() as $ns_key => $ns_uri) { if ($ns_key && $ns_key != 'frx') { foreach ($node->attributes($ns_uri) as $key => $value) { $ns_attrs .= ' ' . $ns_key . ':' . $key . '="' . (string)$value . '"'; } } } // Check for include syntax $include_file = (string)$frx['include']; if ($include_file) { $parms = $this->dmSvc->dataSvc->currentContextArray(); forena_report_include($include_file, $parms); return NULL; } // Determine if we have a custom renderer $renderer = (string)$frx['renderer']; // if we have a foreach in this node, we need to iterate the children if ((string)$frx['foreach'] ) { // Get proper XML for current data context. $path = $this->report->replace((string)$frx['foreach'], TRUE); if ($path && strpos($path, '.')) { @list($context, $path) = explode('.', $path, 2); $data = $this->getDataContext($context); } else { $data = $this->currentDataContext(); } if (is_object($data) || $path != '*') { if ((method_exists($data, 'xpath') || is_array($data))) { if (is_array($data)) $data = DataContext::arrayToXml($data); $nodes = $data->xpath($path); } else { $nodes = $data; } } else { $nodes = (array)$data; } // Sort values $sort = @(string)$frx['sort']; if ($sort) { $compare_type = @(string)$frx['compare']; $this->report->sort($data, $sort, $compare_type); } // values $group = @(string)$frx['group']; if ($group) { $opt = $this->mergedAttributes($node); $sums = (array)@$opt['sum']; $nodes = $this->report->group($nodes, $group, $sums); } $i=0; //$tmp_attrs = (array)$attrs; if ($nodes) foreach ($nodes as $x) { if ($group) { $this->setDataContext('group', $x[0]); } $this->pushData($x, $id); $i++; $odd = $i & 1; $row_class = $odd ? 'odd' : 'even'; $r_attr_text = ''; if (trim($id)) { if (strpos($attrs['id'],'{')!== FALSE) { $id_attr = $this->report->replace($attrs['id']); } else { if (!empty($settings['numericFrxForeachID'])) { $id_attr = $i; } else { $id_attr = $attrs['id'] . '-' . $i; } } $tmp_attrs['id'] = $id_attr; } if (@!$settings['noHelperClasses']) { $tmp_attrs['class'] = trim($attrs['class'] . ' ' . $row_class); } foreach ($tmp_attrs as $key => $value) { $r_attr_text .= ' ' . $key . '="' . (string)$value . '"'; } if ($include_root) $this->write($this->report->replace('<' . $tag . $r_attr_text . $ns_attrs . '>', TRUE)); foreach ($dom_node->childNodes as $child) { $this->renderDomNode($child, $o); } if ($include_root) { $close_tag = '</' . $tag . '>'; $this->write($close_tag); } $this->popData(); } } elseif ($continue) { if ($renderer) { // Implement custom renderer. /** @var \Drupal\forena\FrxPlugin\Renderer\RendererInterface $co */ $co = $this->report->getRenderer($renderer); if ($co) { $co->initReportNode($dom_node); $output = $co->render(); $this->write($output); } } else { if ($has_children) { if ($include_root) $this->write($this->report->replace('<' . $tag . $attr_text . $ns_attrs . '>', TRUE)); // None found, so render children foreach ($dom_node->childNodes as $child) { $this->renderDomNode($child, $o); } if ($include_root) $this->write('</' . $tag . '>'); } else { $this->write($this->report->replace('<' . $tag . $attr_text . $ns_attrs . '/>', TRUE)); } } } if ($is_data_block && $continue) { $this->popData(); } // Restore link processing. if (@(string)$frx['invalid_link']) { $this->report->link_mode = $old_link_mode; } return NULL; } public function renderChildren(DOMNode $domNode, &$o) { foreach ($domNode->childNodes as $node) { $this->renderDomNode($node, $o); } } /** * Default Render action, which simply does normal forena rendering. * You can use renderDomNode at any time to generate the default forena * rendering methods. * @return string * text from the renderer. */ public function render() { //$o = ''; if ($this->reportDomNode) $this->renderChildren($this->reportDomNode, $o); } /** * Helper function for convergint methods to a standard associated array. * @param array $attributes * @param string $key * @param mixed $value */ public static function addAttributes(&$attributes, $key, $value) { $parts = explode('_', $key); $suff = ''; if (count($parts) > 1) { $suff=array_pop($parts); $part = implode('_', $parts); } // If we have _0 _1 _2 attributes convert them into arrays. if ((int)$suff || $suff === '0') { $attributes[$part][] = (string)$value; } else { $attributes[$key] = (string)$value; } } /** * Starting at the current report node, this function removes all child nodes. It aso * removes any FRX attributes on the current as well. * @return \SimpleXMLElement * Report xml created. */ public function resetTemplate() { $node = $this->reportDocDomNode; $this->removeChildren($node); $tag = $node->tagName; $new_node = $this->report->dom->createElement($tag); $this->frxAttributes = array(); $parent = $node->parentNode; $parent->replaceChild($new_node, $node); $this->reportDocDomNode = $new_node; $this->initReportNode($new_node); return $node; } /** * Set FRX attributes. * @param DOMNode $node * @param array $attributes * @param array $frxattributes */ public function setAttributes(DOMElement $node, $attributes, $frx_attributes) { if ($attributes) foreach ($attributes as $key => $value) { $node->setAttribute($key, $value); } // Iterate the value if ($frx_attributes) foreach ($frx_attributes as $key => $value) { // If the value is an array create multiple attributes // that are of the form key_1, key_2 .... etc. if (is_array($value)) { $i=0; $done=FALSE; while(!$done) { $v = ''; if ($value) $v = array_shift($value); $i++; $k = $key . '_' . trim((string)$i); $node->setAttribute($k,$v); if (!$v) { $done = TRUE; } } } // A normal value. else { if ($value) $node->setAttributeNS($this->xmlns, $key, $value); } } } /** * Standard php array containing merged attributes * Enter description here ... */ public function mergedAttributes($node = NULL) { if ($node) { $frx_attributes = $node->attributes($this->xmlns); $html_attributes = $node->attributes(); } else { $frx_attributes = $this->frxAttributes; $html_attributes = $this->htmlAttributes; } $attributes = array(); if ($frx_attributes) foreach ($frx_attributes as $key => $data) { RendererBase::addAttributes($attributes, $key, $data); } if ($html_attributes) foreach ($html_attributes as $key => $data) { RendererBase::addAttributes($attributes, $key, $data); } $skin_data = $this->getDataContext('skin'); $class = get_class($this); if (isset($skin_data[$class])) { $attributes = array_merge($skin_data[$class], $attributes); } $classes = class_parents($this); array_pop($classes); if ($classes) foreach ($classes as $class) { if (isset($skin_data[$class])) { $attributes = array_merge($attributes, $skin_data[$class]); } } return $attributes; } /** * Gives the token replaced attributes of a node. * @return array * Key value pair of attributes */ public function replacedAttributes() { $attributes = array(); if (isset($this->frxAttributes)) foreach ($this->frxAttributes as $key => $data) { $attributes[$key] = $this->report->replace((string)$data, TRUE); } if (isset($this->htmlAttributes)) foreach ($this->htmlAttributes as $key => $data) { $attributes[$key] = $this->report->replace((string)$data, TRUE); } return $attributes; } /** * Render a drupal form in a forena template * @param $form array * @return string * Rendered elements. */ public function drupalRender($form) { return AppService::instance()->drupalRender($elements); } /** * Default configuration validator. Simply validates header and footer attributes. * @param array $config * Array containing template configuration information * @return bool * Indicates whether configuration is valid. */ public function configValidate(&$config) { return $this->validateTextFormats($config, array('header', 'footer')); } /** * Helper function for validating text_format type controls. * @param array $config * Configuration to valiate * @param array $elements * Form elements to validate * @return array * key value pair containing lement names and any error messages related * to those elements. */ public function validateTextFormats(&$config, $elements) { $temp_dom = FrxAPI::tempDOM(); $errors = array(); foreach ($elements as $element) if (isset($config[$element]['value'])) { if ($config[$element]['value']) { $body_xml = '<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY nbsp " "> ]><html xmlns:frx="' . $this->xmlns . '"><body>' . $config[$element]['value'] . '</body></html>'; @$temp_dom->loadXML($body_xml); if (!$temp_dom->documentElement) { $errors[$element] = t('Invalid XHTML in %s', array('%s' => $element)); } } } return $errors; } /** * Default method for extracting configuration information from the template. * This just scrapes teh current child html as the template. */ public function scrapeConfig() { $content = array(); $this->extractTemplateHTML($this->reportDocDomNode, $content); return $content; } /** * Generate ajax configuration attributes for use in template configurtion forms. * @param string $event * @return array * an #ajax element attribute. */ public function configAjax($event='') { $ajax = array( 'callback' => 'forena_template_callback', 'wrapper' => 'forena-template-wrapper', ); if ($event) $ajax['event'] = $event; return $ajax; } /** * Add a node to the existing dom element with attributes * @param $cur_node DOMNode Parent node * @param $indent Integer Text indentation. * @param $tag String Tag name * @param $value String text value of the element * @param $attributes array Html attributes to add * @param $frx_attributes array FRX attributes. * @return \SimpleXMLElement * The node added. */ function addNode($cur_node, $indent, $tag='div', $value='', $attributes=array(), $frx_attributes=array()) { $dom = $this->report->dom; if (!$cur_node) { return NULL; } if ($indent) { $tnode = $dom->createTextNode("\n" . str_repeat(' ', $indent)); $cur_node->appendChild($tnode); } $node = $this->report->dom->createElement($tag, $value); $cur_node->appendChild($node); $this->setAttributes($node, $attributes, $frx_attributes); $cur_node->appendChild($this->report->dom->createTextNode("")); return $node; } /** * Append a textual XHTML fragment to the dom. * We do not use the DOMDocumentFragment optioin because they don't properly import namespaces. . * @param DOMNode $node * Node of DOM to add element too. * @param string $xml_string * String containing an XML fragment to add * @param string $ctl_name */ function addFragment(DOMNode $node, $xml_string, $ctl_name = 'Header') { if (is_array($xml_string) && isset($xml_string['value'])) { $xml_string = $xml_string['value']; } if ($xml_string && !is_array($xml_string)) { $temp_dom = FrxAPI::tempDOM(); $body_xml = '<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE root [ <!ENTITY nbsp " "> ]><html xmlns:frx="' . $this->xmlns . '"><body>' . $xml_string . '</body></html>'; try { $temp_dom->loadXML($body_xml); } catch (\Exception $e) { $this->error('Malformed report body', '<pre>' . $e->getMessage() . $e->getTraceAsString() . '</pre>'); } $body = $temp_dom->getElementsByTagName('body')->item(0); foreach($body->childNodes as $sub) { $new_node = $this->report->dom->importNode($sub, TRUE); $node->appendChild($new_node); } if ($node->nodeType == XML_ELEMENT_NODE) { $xmlnode = simplexml_import_dom($node); $frx_nodes = $xmlnode->xpath('//*[@frx:*]'); if (!$frx_nodes) { $this->frxReportsave_attributes_by_id(); } } } } /** * Extract a list of columns from the data context. * @param $xml \SimpleXMLElement The xml data * @param string $path * @return array * columns or fields contained in the XML. */ public function columns($xml, $path='/*/*') { //create an array of columns if (!is_object($xml)) return array(); // Use xpath if possible otherwise iterate. if (method_exists($xml, 'xpath')) { $rows = $xml->xpath($path); } else { $rows = $xml; } $column_array = array(); $numeric_columns = array(); foreach ($rows as $columns) { foreach ($columns as $name => $value) { $label = str_replace('_', ' ', $name); $column_array[$name] = $label; if (is_numeric((string)$value)) { $numeric_columns[$name] = $label; } else { if (isset($numeric_columns[$name])) unset($numeric_columns[$name]); } } if (is_object($xml) && method_exists($xml, 'attributes')) { foreach ($xml->attributes() as $name => $value) { $column_array['@' . $name] = '@' . $name; } } } $this->columns = $column_array; $this->numeric_columns = $numeric_columns; return $column_array; } /** * Add a text node to the current dom node. * @param DOMNode $cur_node * Dom node to append text to. * @param string $text * Text to add * @return DOMNode * Added text node. */ function addText($cur_node, $text) { $dom = $this->report->dom; $tnode = $dom->createTextNode($text); $cur_node->appendChild($tnode); return $tnode; } /** * * Extract a configuration var removing it from the array * @param string $key attribute key for the data being extracted. * @param array $config * @return string * Value of setting. */ public function extract($key, &$config) { $value = ''; if (isset($config[$key])) { $value = $config[$key]; unset($config[$key]); } return $value; } /** * * Generate generic div tag. * @param array $config * @param string $text */ public function blockDiv(&$config, $text='') { $node = $this->reportDocDomNode; $heading = $this->extract('heading', $config); $descr = $this->extract('description', $config); $include = $this->extract('include', $config); $block = $this->extract('block', $config); $foreach = $this->extract('foreach', $config); $id = $this->extract('id', $config); if (!$id) { $id = $this->idFromBlock($block); } $class = $this->extract('class', $config); if (!$class) $class = get_class($this); $frx_attributes = array( 'block' => $block, ); if ($foreach) $frx_attributes['foreach'] = $foreach; $attributes = array( 'id' => $id, 'class' => $class, ); $this->setAttributes($node, $attributes, $frx_attributes); if ($heading) { $this->addNode($node, 4, 'h2', $heading); } if ($descr) { $this->addNode($node, 4, 'p', $descr); } if ($include) { $src = 'reports/' . str_replace('/', '.', $include); $this->addNode($node, 4, 'div', NULL, NULL, array('renderer' => get_class($this), 'src' => $src)); } return $node; } /** * Generate the template from the configuration. * @param string $data_block * @param \SimpleXMLElement $xml * @param array $config */ public function generate($xml , &$config) { if (!@$config['foreach']) $config['foreach']='*'; $columns = $this->columns($xml); $text = ''; if ($columns) foreach ($columns as $col => $label) { $text .= ' {' . $col . '}'; } $this->blockDiv($config, $text); } /** * Simple function to get id from node. * @param string $block * @return string */ public function idFromBlock($block) { $parts = explode('/', $block); $id = str_replace('.', '_', array_pop($parts)); return $id; } /** * Sets the first child element to a node and returns it. * IF the node * @param DOMNode $node * Dom node on which to operate * @param string $tag * The tag to add * @param int $indent * How much space we need to indent the text by. * @param string $value * Contents of the new node * @param array attributes * html attributes to add * @param array * frx: attributes to add. * @return Node just set */ public function setFirstNode(DOMElement $parent_node, $indent=0, $tag='div', $value='', $attributes=array(), $frx_attributes=array()) { $dom = $this->report->dom; if (!$parent_node) { return NULL; } $nodes = $parent_node->getElementsByTagName($tag); if ($nodes->length) { $node = $nodes->item(0); $this->setAttributes($node, $attributes, $frx_attributes); } else { $node = $this->addNode($parent_node, $indent, $tag, $value, $attributes, $frx_attributes); } return $node; } /** * Rmove all the children of a dom node in the current report. * @param DOMNode $node */ public function removeChildren(DOMNode $node) { while (isset($node->firstChild) && $node->firstChild->nodeType < 9) { $this->removeChildren($node->firstChild); $node->removeChild($node->firstChild); } } /** * Convert XML to key value pairs. * This is used in support of graping to get specific key/value pairs in * an array format suitable for passing off to php libraries. * @param string $path * xpath expression to use to convert * @param string $data_path * path to configuration data. * @param string $label_path * xpath to label values * @param bool $pairs * * @return array * data values from xml. */ public function xmlToValues($path, $data_path, $label_path='', $pairs = FALSE) { $do = $this->dmSvc->dataSvc; $data = $do->currentContext(); $values = array(); if (is_object($data)) { $nodes = $data->xpath($path); if ($nodes) foreach ($nodes as $i => $node) { $do->push($node, $this->id); $val = $this->report->replace($data_path, TRUE); if ($label_path) { $key = strip_tags($this->report->replace($label_path, FALSE)); } else { $key = $i; } if ($pairs && $label_path) { $values[] = array(floatval($key), floatval($val)); } else { $values[$key] = $val; } $do->pop(); } } return $values; } /** * Removes all chidren from the dome node expect those with a tagname specified by the * the $tags argurment * @param DomNode $node * Parent node to remove from * @param array $tags * Tags to eingore */ public function removeChildrenExcept(DOMNode $node, $tags = array('table')) { foreach ($node->childNodes as $child) { if ($child->nodeType != XML_ELEMENT_NODE || array_search($child->tagName, $tags)===FALSE) { $this->removeChildren($child); $node->removeChild($child); } } } /** * Get the textual representations of html for the configuration engine. */ public function extractSource(DOMNode $node) { $content = ''; switch ($node->nodeType) { case XML_ELEMENT_NODE: $content = $this->report->dom->saveXML($node); break; case XML_TEXT_NODE: case XML_ENTITY_REF_NODE: case XML_ENTITY_NODE: case XML_ATTRIBUTE_NODE: $content = $node->textContent; break; case XML_COMMENT_NODE: $content = '<!--' . $node->data . '-->'; break; } return $content; } /** * Get the textual representations of html for the configuration engine. */ public function extractChildSource(DOMNode $node) { $content = ''; foreach ($node->childNodes as $child) { switch ($child->nodeType) { case XML_ELEMENT_NODE: $content .= $this->report->dom->saveXML($child); break; case XML_TEXT_NODE: case XML_ENTITY_REF_NODE: case XML_ENTITY_NODE: $content .= $child->textContent; break; case XML_COMMENT_NODE: $content .= '<!--' . $child->data . '-->'; break; } } return $content; } /** * Get the textual representations of html for the configuration engine. */ public function extractTemplateHTML(DOMNode $node, &$content, $tags = array()) { $this->report->get_attributes_by_id(); $cur_section = 'header'; if (!$content) $content = array('header' => '', 'content' => '', 'footer' => ''); if (!$tags) $cur_section = 'content'; foreach ($node->childNodes as $child) { switch ($child->nodeType) { case XML_ELEMENT_NODE: if (array_search($child->tagName, $tags)!==FALSE) { $cur_section = 'content'; } elseif ($tags && $cur_section == 'content') { $cur_section = 'footer'; } @$content[$cur_section]['value'] .= $this->report->dom->saveXML($child); break; case XML_TEXT_NODE: case XML_ENTITY_REF_NODE: case XML_ENTITY_NODE: @$content[$cur_section]['value'] .= $child->textContent; break; case XML_COMMENT_NODE: @$content[$cur_section]['value'] .= '<!--' . $child->data . '-->'; break; } } } /** * Extracts the inner html of all nodes that match a particular xpath expression. * @param $query string xpath query expression * @param DOMNode $context Dom node to use as source * @param $concat boolean Set to false to return an array with the source for each element matching the path. * @return String XHTML source */ public function extractXPathInnerHTML($query, DOMNode $context, $concat = TRUE) { $result = $this->xpathQuery->query($query, $context); $length = $result->length; $content = array(); for ($i=0; $i<$length; $i++) { $content[] = $this->extractChildSource($result->item($i)); } if ($concat) $content = implode('', $content); return $content; } /** * Extracts the inner html of all nodes that match a particular xpath expression. * @param $query string xpath query expression * @param DOMNode $context Dom node to use as source * @param $concat boolean Set to false to return an array with the source for each element matching the path. * @return String XHTML source */ public function extractXPath($query, DOMNode $context, $concat = TRUE) { $result = $this->xpathQuery->query($query, $context); $length = $result->length; $content = array(); for ($i=0; $i<$length; $i++) { $content[] = $this->extractSource($result->item($i)); } if ($concat) $content = implode('', $content); return $content; } /** * Puts attributes back in array format prior to rendering. * @param array $attributes * @return array * Attributes of the node. */ public function arrayAttributes($attributes) { $remove_attrs = array(); foreach ($attributes as $key => $value) { if (is_array($value)) { $i=0; foreach($value as $idx => $v) { $i++; $new_key = $key . '_' . trim($i); $attributes[$new_key] = (string)$v; } $remove_attrs [] = $key; } } foreach ($remove_attrs as $key) { unset($attributes[$key]); } return $attributes; } // Helper sort functoin for sorting config by weight. public static function weight_sort_comp($a, $b) { if ($a['weight'] == $b['weight']) return 0; return $a['weight'] < $b['weight'] ? -1 : 1; } /** * Sort a column list by weight. * @param array $entries * Entries to sort. */ public function weight_sort(&$entries) { if ($entries) uasort($entries, 'FrxRenderer::weight_sort_comp'); } }