output($this->renderTable($this->table)); } protected function renderTable($table) { if($table instanceof Piwik_DataTable_Array) { $str = $header = ''; $prefixColumns = $table->getKeyName() . $this->separator; foreach($table->getArray() as $currentLinePrefix => $dataTable) { $returned = explode("\n",$this->renderTable($dataTable)); // get the columns names if(empty($header)) { $header = $returned[0]; } $returned = array_slice($returned,1); // case empty datatable we dont print anything in the CSV export // when in xml we would output if(!empty($returned)) { foreach($returned as &$row) { $row = $currentLinePrefix . $this->separator . $row; } $str .= "\n" . implode("\n", $returned); } } if(!empty($header)) { $str = $prefixColumns . $header . $str; } } else { $str = $this->renderDataTable($table); } return $str; } protected function renderDataTable( $table ) { if($table instanceof Piwik_DataTable_Simple) { $row = $table->getFirstRow(); if($row !== false) { $columnNameToValue = $row->getColumns(); if(count($columnNameToValue) == 1) { $value = array_values($columnNameToValue); $str = 'value' . $this->lineEnd . $this->formatValue($value[0]); return $str; } } } $csv = $allColumns = array(); foreach($table->getRows() as $row) { $csvRow = array(); $columns = $row->getColumns(); foreach($columns as $name => $value) { //goals => array( 'idgoal=1' =>array(..), 'idgoal=2' => array(..)) if(is_array($value)) { foreach($value as $key => $subValues) { if(is_array($subValues)) { foreach($subValues as $subKey => $subValue) { // goals_idgoal=1 $columnName = $name . "_" . $key . "_" . $subKey; $allColumns[$columnName] = true; $csvRow[$columnName] = $subValue; } } } } else { $allColumns[$name] = true; $csvRow[$name] = $value; } } if($this->exportMetadata) { $metadata = $row->getMetadata(); foreach($metadata as $name => $value) { //if a metadata and a column have the same name make sure they dont overwrite $name = 'metadata_'.$name; $allColumns[$name] = true; $csvRow[$name] = $value; } } if($this->exportIdSubtable) { $idsubdatatable = $row->getIdSubDataTable(); if($idsubdatatable !== false) { $csvRow['idsubdatatable'] = $idsubdatatable; } } $csv[] = $csvRow; } // now we make sure that all the rows in the CSV array have all the columns foreach($csv as &$row) { foreach($allColumns as $columnName => $true) { if(!isset($row[$columnName])) { $row[$columnName] = ''; } } } $str = ''; // specific case, we have only one column and this column wasn't named properly (indexed by a number) // we don't print anything in the CSV file => an empty line if(sizeof($allColumns) == 1 && reset($allColumns) && !is_string(key($allColumns))) { $str .= ''; } else { $keys = array_keys($allColumns); $str .= implode($this->separator, $keys); $str .= $this->lineEnd; } // we render the CSV foreach($csv as $theRow) { $rowStr = ''; foreach($allColumns as $columnName => $true) { $rowStr .= $this->formatValue($theRow[$columnName]) . $this->separator; } // remove the last separator $rowStr = substr_replace($rowStr,"",-strlen($this->separator)); $str .= $rowStr . $this->lineEnd; } $str = substr($str, 0, -strlen($this->lineEnd)); return $str; } protected function formatValue($value) { if(is_string($value) && !is_numeric($value)) { $value = html_entity_decode($value, ENT_COMPAT, 'UTF-8'); } elseif($value === false) { $value = 0; } if(strpos($value, '"') !== false || strpos($value, ',') !== false ) { $value = '"'. str_replace('"', '""', $value). '"'; } return $value; } protected function output( $str ) { if(empty($str)) { return 'No data available'; } // silent fail otherwise unit tests fail @header("Content-type: application/vnd.ms-excel"); @header("Content-Disposition: attachment; filename=piwik-report-export.csv"); if($this->convertToUnicode && function_exists('mb_convert_encoding')) { $str = chr(255) . chr(254) . mb_convert_encoding($str, 'UTF-16LE', 'UTF-8'); } return $str; } }