#8680: Allow OpenDocument library to replace markers
authorAlexander Stintzing <a.stintzing@metaways.de>
Fri, 26 Jul 2013 12:55:07 +0000 (14:55 +0200)
committerPhilipp Schüle <p.schuele@metaways.de>
Mon, 29 Jul 2013 14:44:56 +0000 (16:44 +0200)
Markers like <{MARKER_NAME}> should be easily replaceable.

https://forge.tine20.org/mantisbt/view.php?id=8680

Change-Id: I505d08b4e6bc03107ed54f4b83f1b7811834c9e3
Reviewed-on: https://gerrit.tine20.org/tine20/2196
Tested-by: jenkins user
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
15 files changed:
tests/tine20/AllTests.php
tests/tine20/TestHelper.php
tests/tine20/Tinebase/AllTests.php
tests/tine20/library/OpenDocument/AllTests.php [new file with mode: 0644]
tests/tine20/library/OpenDocument/DocumentTests.php [new file with mode: 0644]
tine20/Tinebase/Export.php
tine20/Tinebase/Export/Spreadsheet/Ods.php
tine20/library/OpenDocument/Document.php
tine20/library/OpenDocument/Matrix.php [new file with mode: 0644]
tine20/library/OpenDocument/Matrix/List.php [new file with mode: 0644]
tine20/library/OpenDocument/Shared/SimpleXML.php [new file with mode: 0644]
tine20/library/OpenDocument/SpreadSheet.php
tine20/library/OpenDocument/SpreadSheet/Cell.php
tine20/library/OpenDocument/SpreadSheet/Row.php
tine20/library/OpenDocument/SpreadSheet/Table.php

index ae017ba..5e9a1d8 100644 (file)
@@ -47,7 +47,7 @@ class AllTests
         $suite->addTest(Inventory_AllTests::suite());
         $suite->addTest(Sipgate_AllTests::suite());
         $suite->addTest(Zend_AllTests::suite());
-        
+        $suite->addTest(OpenDocument_AllTests::suite());
         return $suite;
     }
 }
index 437c646..dd909ad 100644 (file)
@@ -42,6 +42,7 @@ if (version_compare($phpUnitVersion[1], "3.6.0") >= 0) {
 $path = array(
     PATH_TO_REAL_DIR,
     PATH_TO_TEST_DIR,
+    PATH_TO_TEST_DIR . PATH_SEPARATOR . 'library',
     PATH_TO_TINE_LIBRARY,
     get_include_path(),
 );
index 15d889f..abcd45a 100644 (file)
@@ -73,7 +73,7 @@ class Tinebase_AllTests
         $suite->addTest(Tinebase_Tree_AllTests::suite());
         $suite->addTest(Tinebase_Scheduler_AllTests::suite());
         $suite->addTest(Tinebase_WebDav_AllTests::suite());
-        
+        $suite->addTest(OpenDocument_AllTests::suite());
         return $suite;
     }
 }
diff --git a/tests/tine20/library/OpenDocument/AllTests.php b/tests/tine20/library/OpenDocument/AllTests.php
new file mode 100644 (file)
index 0000000..b346a5f
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     OpenDocument
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2012-2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Alexander Stintzing <a.stintzing@metaways.de>
+ */
+
+/**
+ * Test helper
+ */
+require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
+
+class OpenDocument_AllTests
+{
+    public static function main()
+    {
+        PHPUnit_TextUI_TestRunner::run(self::suite());
+    }
+    
+    public static function suite ()
+    {
+        $suite = new PHPUnit_Framework_TestSuite('Tine 2.0 OpenDocument All Tests');
+        $suite->addTestSuite('OpenDocument_DocumentTests');
+        return $suite;
+    }
+}
diff --git a/tests/tine20/library/OpenDocument/DocumentTests.php b/tests/tine20/library/OpenDocument/DocumentTests.php
new file mode 100644 (file)
index 0000000..bc1d2fb
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+/**
+ * Tine 2.0 - http://www.tine20.org
+ * 
+ * @package     Tests
+ * @license     http://www.gnu.org/licenses/agpl.html
+ * @copyright   Copyright (c) 2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @author      Alexander Stintzing <a.stintzing@metaways.de>
+ */
+
+
+class OpenDocument_DocumentTests extends PHPUnit_Framework_TestCase
+{
+    /**
+     * Sets up the fixture.
+     * This method is called before a test is executed.
+     *
+     * @access protected
+     */
+    protected function setUp()
+    {
+        Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
+    }
+    /**
+     * Tears down the fixture
+     * This method is called after a test is executed.
+     *
+     * @access protected
+     */
+    protected function tearDown()
+    {
+        Tinebase_TransactionManager::getInstance()->rollBack();
+    }
+    
+    /**
+     * tests the correct replacement of markers with different contents
+     */
+    public function testMarkerReplacement()
+    {
+        $doc = new OpenDocument_Document(OpenDocument_Document::SPREADSHEET);
+        $table = $doc->getBody()->appendTable('UNITTEST');
+        
+        $titleText = 'Hello unittest!';
+        
+        $row  = $table->appendRow();
+        $cell = $row->appendCell($titleText);
+        
+        $row  = $table->appendRow();
+        
+        $row  = $table->appendRow();
+        $cell = $row->appendCell('<{MATRIX}>');
+        
+        $row  = $table->appendRow();
+        
+        $row  = $table->appendRow();
+        $cell = $row->appendCell('<{MARKER}>');
+        
+        $filename = Tinebase_Config::getInstance()->get('tmpdir') . DIRECTORY_SEPARATOR . Tinebase_Record_Abstract::generateUID(4) . '-ods-unittest.ods';
+        
+        $ccc = Sales_Controller_CostCenter::getInstance();
+        
+        $uid = Tinebase_Record_Abstract::generateUID(4);
+        $cc1 = $ccc->create(new Sales_Model_CostCenter(array('number' => $uid, 'remark' => 'unittest-' . $uid)));
+        
+        $uid = Tinebase_Record_Abstract::generateUID(4);
+        $cc2 = $ccc->create(new Sales_Model_CostCenter(array('number' => $uid, 'remark' => 'unittest-' . $uid)));
+        
+        $colInfo = array();
+        $colInfo[$cc1->getId()] = $cc1->number;
+        $colInfo[$cc2->getId()] = $cc2->number;
+        
+        $matrixArray = array(
+            $cc1->getId() => array($cc2->getId() => '100'),
+            $cc2->getId() => array($cc1->getId() => '200')
+        );
+        
+        $matrix = new OpenDocument_Matrix($matrixArray, $colInfo, $colInfo, OpenDocument_Matrix::TYPE_FLOAT);
+        
+        $matrix->setColumnLegendDescription('Cat');
+        $matrix->setRowLegendDescription('Dog');
+        
+        $markerText = 'unittest-marker';
+        $doc->replaceMarker('marker', $markerText)->replaceMatrix('matrix', $matrix);
+        $doc->getDocument($filename);
+        
+        $contentXml = file_get_contents('zip://' . $filename . '#content.xml');
+        $xml = simplexml_load_string($contentXml);
+        
+        unlink($filename);
+        
+        $spreadSheets = $xml->xpath('//office:body/office:spreadsheet');
+        $spreadSheet  = $spreadSheets[0];
+        
+        $results = $spreadSheet->xpath("//text()[contains(., '$markerText')]");
+        $this->assertEquals(1, count($results));
+        
+        $results = $spreadSheet->xpath("//text()[contains(., '$titleText')]");
+        $this->assertEquals(1, count($results));
+        
+        $results = $spreadSheet->xpath("//text()[contains(., '$cc1->number')]");
+        $this->assertEquals(2, count($results));
+        
+        $results = $spreadSheet->xpath("//text()[contains(., '$cc2->number')]");
+        $this->assertEquals(2, count($results));
+        
+        $results = $spreadSheet->xpath("//text()[contains(., 'Sum')]");
+        $this->assertEquals(2, count($results));
+        
+        $results = $spreadSheet->xpath("//text()[contains(., 'Cat')]");
+        $this->assertEquals(1, count($results));
+        
+        $results = $spreadSheet->xpath("//text()[contains(., 'Dog')]");
+        $this->assertEquals(1, count($results));
+        
+        $results = $spreadSheet->xpath("//text()[contains(., '100')]");
+        $this->assertEquals(3, count($results));
+        
+        $results = $spreadSheet->xpath("//text()[contains(., '200')]");
+        $this->assertEquals(3, count($results));
+        
+        $results = $spreadSheet->xpath("//text()[contains(., '300')]");
+        $this->assertEquals(1, count($results));
+    }
+}
\ No newline at end of file
index a6a1d0a..35e8693 100644 (file)
@@ -36,7 +36,7 @@ class Tinebase_Export
             $_options = array(
                 'format' => $_options
             );
-        }  
+        }
         
         if (array_key_exists('definitionId', $_options)) {
             $definition = Tinebase_ImportExportDefinition::getInstance()->get($_options['definitionId']);
index bab9fce..2a5cffc 100644 (file)
@@ -117,7 +117,7 @@ class Tinebase_Export_Spreadsheet_Ods extends Tinebase_Export_Spreadsheet_Abstra
         $spreadSheet = $this->_openDocumentObject->getBody();
         
         // append / use existing table
-        if($spreadSheet->tableExists($this->_getDataTableName()) === true) {
+        if ($spreadSheet->tableExists($this->_getDataTableName()) === true) {
             $this->_activeTable = $spreadSheet->getTable($this->_getDataTableName());
         } else {
             $this->_activeTable = $spreadSheet->appendTable($this->_getDataTableName());
@@ -154,7 +154,7 @@ class Tinebase_Export_Spreadsheet_Ods extends Tinebase_Export_Spreadsheet_Abstra
     {
         // check for template file
         $templateFile = $this->_getTemplateFilename();
-                        
+        
         $this->_openDocumentObject = new OpenDocument_Document(OpenDocument_Document::SPREADSHEET, $templateFile, Tinebase_Core::getTempDir(), $this->_userStyles);
     }
     
index 5800af1..f00c604 100644 (file)
@@ -5,7 +5,7 @@
  * @package     OpenDocument
  * @subpackage  OpenDocument
  * @license     http://framework.zend.com/license/new-bsd     New BSD License
- * @copyright   Copyright (c) 2009-2011 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
  */
 
@@ -32,8 +32,18 @@ class OpenDocument_Document
     
     protected $_cellStyles = array();
     
+    /**
+     * the given template file
+     * 
+     * @var string
+     */
     protected $_templateFile;
     
+    /**
+     * the content.xml in an simplexml element
+     * 
+     * @var SimpleXMLElement 
+     */
     protected $_document;
         
     /**
@@ -43,6 +53,17 @@ class OpenDocument_Document
      */
     protected $_body;
     
+    /**
+     * 
+     * @var string
+     */
+    protected $_type;
+    
+    /**
+     * the content.xml as text
+     *
+     * @var string
+     */
     protected $_content = '<?xml version="1.0" encoding="UTF-8"?>
         <office:document-content 
             xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
@@ -154,7 +175,7 @@ class OpenDocument_Document
      */
     public function __construct($_type, $_fileName = null, $_tmpdir = NULL, $_userStyles = array())
     {
-        if($_fileName !== null) {
+        if ($_fileName !== null) {
             if (! file_exists($_fileName)) {
                 error_log('Template file not found: ' . $_fileName);
             } else {
@@ -195,7 +216,9 @@ class OpenDocument_Document
                 throw new Exception('unsupported documenttype: ' . $_type);
                 break;
         }
-    }    
+        
+        $this->_type = $_type;
+    }
     
     /**
      * get the body
@@ -207,97 +230,195 @@ class OpenDocument_Document
         return $this->_body;
     }
     
+    /**
+     * replaces a marker with a matrix
+     * 
+     * @param string $marker
+     * @param OpenDocument_Matrix $matrix
+     * @param bool $showLegend
+     * @return OpenDocument_Document
+     */
+    public function replaceMatrix($marker, $matrix, $showLegend = TRUE, $showLegendDescription = TRUE, $showSums = TRUE)
+    {
+        if ($xml = $this->_findMarkers($marker)) {
+            switch ($this->_type) {
+                case self::SPREADSHEET:
+                    foreach($xml as $xmlElement) {
+                        $this->_body->replaceMatrix($xmlElement, $matrix, $showLegend, $showLegendDescription, $showSums);
+                    }
+                    break;
+                default:
+                    throw new Exception('unsupported documenttype for matrix replace: ' . $this->_type);
+            }
+        }
+        return $this;
+    }
+    
+    /**
+     * 
+     * @param unknown $marker
+     * @param unknown $list
+     * @throws Exception
+     * @return OpenDocument_Document
+     */
+    public function replaceList($marker, $list, $type = OpenDocument_SpreadSheet_Cell::TYPE_STRING, $direction = 'horizontal')
+    {
+        if ($xml = $this->_findMarkers($marker)) {
+            switch ($this->_type) {
+                case self::SPREADSHEET:
+                    foreach($xml as $xmlElement) {
+                        $this->_body->replaceList($xmlElement, $list, $type, $direction);
+                    }
+                    break;
+                default:
+                    throw new Exception('unsupported documenttype for matrix replace: ' . $this->_type);
+            }
+        }
+        return $this;
+    }
+    
+    /**
+     * 
+     * @param string $marker
+     * @return array
+     */
+    protected function _findMarkers($marker)
+    {
+        return $this->_document->xpath("//text()[contains(., '<{".strtoupper($marker)."}>')]");
+    }
+    
+    /**
+     * replaces a marker
+     * 
+     * @param SimpleXMLElement $xml
+     * @param string $value
+     */
+    public function replaceMarker($marker, $value)
+    {
+        if ($xml = $this->_findMarkers($marker)) {
+            foreach($xml as $xmlElement) {
+                $xmlElement[0] = $value;
+            }
+        }
+        
+        return $this;
+    }
+    
+    /**
+     * returns this _document (content.xml) as string
+     * 
+     * @return mixed
+     */
     public function asXML()
     {
         return $this->_document->asXML();
     }
     
+    /**
+     * adds additional user styles
+     * 
+     * @param string|array $_style
+     * @return OpenDocument_Document
+     */
     public function addStyle($_style)
     {
-        $this->_userStyles[] = $_style; 
+        if (! is_array($_style)) {
+            $_style = array($_style);
+        }
+        foreach($_style as $s) {
+            $this->_userStyles[] = $s;
+        }
+        return $this;
     }
     
+    /**
+     * creates the document
+     * 
+     * @param string $_filename
+     * @throws Exception
+     * @return string
+     */
     public function getDocument($_filename = null)
     {
         $this->_addStyles();
 
         $filename =  $_filename !== null ? $_filename : tempnam($this->_tmpdir, 'OpenDocument');
-        $tempDir = $this->_tmpdir . DIRECTORY_SEPARATOR . 'od_' . md5(uniqid(rand(), true));
         
-        if (file_exists($tempDir)) {
-            throw new Exception('Directory already exists.');
-        }
-        mkdir($tempDir);
+        if($this->_templateFile === null) {
+            
+            $tempDir = $this->_tmpdir . DIRECTORY_SEPARATOR . 'od_' . md5(uniqid(rand(), true));
         
-        if($this->_templateFile !== null) {
-            $templateZip = new ZipArchive();
-            if ($templateZip->open($this->_templateFile) === TRUE) {
-                $templateZip->extractTo($tempDir);
-                $templateZip->close();
+            if (file_exists($tempDir)) {
+                throw new Exception('Directory already exists.');
             }
-        }
-        
-        if($this->_templateFile === null) {
+            
+            mkdir($tempDir);
+            
             mkdir($tempDir . DIRECTORY_SEPARATOR . 'META-INF');
             file_put_contents($tempDir . DIRECTORY_SEPARATOR . 'mimetype', $this->_body->getContentType());
             file_put_contents($tempDir . DIRECTORY_SEPARATOR . 'meta.xml', $this->_meta);
             file_put_contents($tempDir . DIRECTORY_SEPARATOR . 'settings.xml', $this->_settings);
             file_put_contents($tempDir . DIRECTORY_SEPARATOR . 'META-INF/manifest.xml', $this->_manifest);
-        }
-        
-        // ObjectReplacements
-//        $images = $this->_document->xpath('//draw:image');
-//        foreach($images as $image) {
-//            $image_sxe = dom_import_simplexml($image);
-//            $image_sxe->parentNode->removeChild($image_sxe);
-//        }
-//        
-//        $images = $this->_document->xpath('//draw:image');
-//        Tinebase_Core::getLogger()->err(print_r($images, TRUE));
-        
-        file_put_contents($tempDir . DIRECTORY_SEPARATOR . 'content.xml', $this->_document->saveXML());
-        file_put_contents($tempDir . DIRECTORY_SEPARATOR . 'styles.xml', $this->_styles);
-        
-        $zip = new ZipArchive();
-        $opened = $zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE);
-        if( $opened !== true ) {
-            throw new Exception('could not open zip file');
-        }
-
-        $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tempDir));
-        
-        foreach ($iterator as $fullFilename => $cur) {
-            // the second parameter of the addFile function needs always the unix directory separator
-            $localname = str_replace('\\', '/', substr($fullFilename, strlen($tempDir)+1));
-            if ($localname !== '.' && $localname !== '..') {
-                $zip->addFile($fullFilename, $localname);
+            
+            file_put_contents($tempDir . DIRECTORY_SEPARATOR . 'content.xml', $this->_document->saveXML());
+            file_put_contents($tempDir . DIRECTORY_SEPARATOR . 'styles.xml', $this->_styles);
+            
+            $zip = new ZipArchive();
+            $opened = $zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE);
+            
+            if( $opened !== true ) {
+                throw new Exception('could not open zip file');
+            }
+            
+            $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tempDir));
+            
+            foreach ($iterator as $fullFilename => $cur) {
+                // the second parameter of the addFile function needs always the unix directory separator
+                $localname = str_replace('\\', '/', substr($fullFilename, strlen($tempDir)+1));
+                if ($localname !== 'mimetype' && $localname !== '.' && $localname !== '..') {
+                    $zip->addFile($fullFilename, $localname);
+                }
+            }
+    
+            $zip->close();
+            
+            // delete files / remove dir
+            removeDir($tempDir);
+            
+            
+        } else {
+            $templateZip = new ZipArchive();
+            if ($templateZip->open($this->_templateFile) === TRUE) {
+                $templateZip->close();
+                
+                copy($this->_templateFile, $filename);
+                
+                $templateZip->open($filename);
+                $templateZip->addFromString('content.xml', $this->_document->saveXML());
+                $templateZip->close();
             }
         }
-
-        $zip->close();
-        
-        // delete files / remove dir
-        removeDir($tempDir);
         
         return $filename;
     }
     
+    /**
+     * add styles to this document
+     */
     protected function _addStyles()
     {
         $styles = $this->_document->xpath('//office:automatic-styles');
         $domStyles = dom_import_simplexml($styles[0]);
 
         foreach($this->_userStyles as $userStyle) {
-            if($userStyle instanceof SimpleXMLElement) {
+            if ($userStyle instanceof SimpleXMLElement) {
                 $newChild = $userStyle;
             } else {
                 $newChild = new SimpleXMLElement($userStyle);
             }
             $dom_sxe = dom_import_simplexml($newChild);
             $newStyle = $domStyles->ownerDocument->importNode($dom_sxe, true);
-            $domStyles->appendChild($newStyle);        
+            $domStyles->appendChild($newStyle);
         }
-        
-        #if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . $styles[0]->generateXML());
     }
 }
\ No newline at end of file
diff --git a/tine20/library/OpenDocument/Matrix.php b/tine20/library/OpenDocument/Matrix.php
new file mode 100644 (file)
index 0000000..f3ef27a
--- /dev/null
@@ -0,0 +1,290 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     OpenDocument
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Alexander Stintzing <a.stintzing@metaways.de>
+ * @copyright   Copyright (c) 2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+
+
+/**
+ * class for a matrix used by OpenDocument_Document->replaceMarkers()
+ *
+ * @package    OpenDocument
+ * @subpackage Matrix
+ */
+class OpenDocument_Matrix implements IteratorAggregate, Countable
+{
+
+    /**
+     * info to resolve the X legend
+     *
+     * @var array
+     */
+    protected $_colInfo = NULL;
+
+    /**
+     * info to resolve the Y legend
+     *
+     * @var array
+     */
+    protected $_rowInfo = NULL;
+    
+    protected $_indexIdMappingCols = NULL;
+    
+    /**
+     * holds the reversed state of the matrix
+     * 
+     * @var bool
+     */
+    protected $_reversed = FALSE;
+    
+    /**
+     * 
+     * @var string
+     */
+    protected $_colLegendDescription = 'Cols'; // _('Cols')
+    
+    /**
+     *
+     * @var string
+     */
+    protected $_rowLegendDescription = 'Rows'; // _('Rows')
+    
+    
+
+    protected $_matrix = NULL;
+
+    /**
+     * the value type for the cells
+     * currently string, float and function are supported
+     * defaults to 'string'
+     *
+     * @var string
+     */
+    protected $_valueType = NULL;
+    
+    /**
+     * if this is set to true, the legend will be returned
+     *
+     * @var bool
+     */
+    protected $_returnLegend = TRUE;
+
+    const TYPE_STRING     = 'string';
+    const TYPE_FLOAT      = 'float';
+    const TYPE_FUNCTION   = 'function';
+    
+    /**
+     * creates a rectangular matrix from a 2 dimensional array by the given record sets and fills it with the given data.
+     * if the second record set is not given, the first will be used as second one, also.
+     * 
+     *
+     * @param array $data
+     * @param array $colInfo
+     * @param array $rowInfo
+     * @param string $valueType
+     */
+    public function __construct(array $data, $colInfo = NULL, $rowInfo = NULL, $valueType = self::TYPE_STRING)
+    {
+        $this->_colInfo   = $colInfo;
+        $this->_rowInfo   = $rowInfo ? $rowInfo : $colInfo;
+        $this->_valueType = $valueType;
+        $this->_matrix    = array();
+
+        if ($colInfo !== NULL) {
+            foreach ($this->_rowInfo as $id => $title) {
+                if (array_key_exists($id, $data)) {
+                    $d = $data[$id];
+                } else {
+                    $d = array_flip($this->_colInfo);
+                    foreach($d as $key => &$val) {
+                        $val = 0;
+                    }
+                }
+                
+                $this->_matrix[$id] = new OpenDocument_Matrix_List($d, $this->_colInfo, $this->_valueType);
+            }
+        }
+    }
+    
+    /**
+     * returns the column legend
+     * 
+     * @return array
+     */
+    public function getColumnLegend()
+    {
+        $legend = array();
+        foreach($this->_colInfo as $id => $title) {
+            $legend[$id] = $title;
+        }
+        return $legend;
+    }
+    
+    /**
+     * returns the row legend
+     * 
+     * @return array
+     */
+    public function getRowLegend()
+    {
+        $legend = array();
+        
+        foreach ($this->_rowInfo as $id => $title) {
+            $legend[$id] = $title;
+        }
+        
+        if ($this->_reversed) {
+            $legend = array_reverse($legend, TRUE);
+        }
+        
+        return $legend;
+    }
+
+    /**
+     * returns the column legend description
+     *
+     * @return string
+     */
+     
+    public function getColumnLegendDescription()
+    {
+        return $this->_colLegendDescription;
+    }
+    
+    /**
+     * returns the row legend description
+     * 
+     * @return string
+     */
+    public function getRowLegendDescription()
+    {
+        return $this->_rowLegendDescription;
+    }
+    
+    /**
+     * sets the column legend description
+     * 
+     * @param string $text
+     */
+     
+    public function setColumnLegendDescription($text)
+    {
+        $this->_colLegendDescription = $text;
+    }
+    
+    /**
+     * sets the row legend description
+     * 
+     * @param string $text
+     */
+    public function setRowLegendDescription($text)
+    {
+        $this->_rowLegendDescription = $text;
+    }
+    
+    /**
+     * counts items in this->_matrix
+     * 
+     * @return number
+     */
+    public function count()
+    {
+        return count($this->_matrix);
+    }
+    
+    /**
+     * returns the iterator
+     *
+     * @return ArrayIterator
+    */
+    public function getIterator() {
+        return new ArrayIterator($this->_matrix);
+    }
+    
+    /**
+     * returns the iteratable as array
+     * 
+     * @return array
+     */
+    public function toArray()
+    {
+        $ret = array();
+        foreach($this->_matrix as $key => $list) {
+            $ret[$key] = $list->toArray();
+        }
+        return $ret;
+    }
+    
+    /**
+     * summarizes the whole matrix
+     * 
+     * @return number
+     */
+    public function sum()
+    {
+        $sum = 0;
+        foreach($this->_matrix as $list) {
+            $sum += $list->sum();
+        }
+        return $sum;
+    }
+    
+    /**
+     * summarizes the column given by the index or record id (index must be int, otherwise string)
+     * 
+     * @param integer|string $index
+     * @return number
+     */
+    public function sumColumn($index)
+    {
+        $sum = 0;
+        
+        if (is_int($index)) {
+            foreach ($this->_matrix as $list) {
+                $sum = $sum + (int) $list->getAt($index);
+            }
+        } elseif (strlen($index) == 40) {
+            foreach($this->_matrix as $list) {
+                $sum = $sum + (int) $list->getById($index);
+            }
+        } else {
+            throw new Tinebase_Exception_InvalidArgument('Param not supported!');
+        }
+        
+        return $sum;
+    }
+    
+    /**
+     * 
+     * @return array
+     */
+    public function getRowInfo()
+    {
+        return $this->_rowInfo;
+    }
+    
+    /**
+     * 
+     * @return array
+     */
+    public function getColInfo()
+    {
+        return $this->_colInfo;
+    }
+    
+    /**
+     * reverses the order of the matrix
+     * 
+     * @return OpenDocument_Matrix
+     */
+    public function reverse()
+    {
+        $this->_matrix = array_reverse($this->_matrix, true);
+        $this->_reversed = ! $this->_reversed;
+        return $this;
+    }
+}
\ No newline at end of file
diff --git a/tine20/library/OpenDocument/Matrix/List.php b/tine20/library/OpenDocument/Matrix/List.php
new file mode 100644 (file)
index 0000000..db3ce72
--- /dev/null
@@ -0,0 +1,161 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     OpenDocument
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Alexander Stintzing <a.stintzing@metaways.de>
+ * @copyright   Copyright (c) 2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+
+
+/**
+ * class for a list OpenDocument_Matrix
+ *
+ * @package    OpenDocument
+ * @subpackage Matrix_List
+ */
+class OpenDocument_Matrix_List implements IteratorAggregate, Countable
+{
+    
+    /*
+     * possible value types to create
+     */
+    const TYPE_STRING     = 'string';
+    const TYPE_FLOAT      = 'float';
+    const TYPE_FUNCTION   = 'function';
+    
+    /**
+     * records to resolve the list
+     * 
+     * @var OpenDocument_Record_RecordSet
+     */
+    protected $_records = NULL;
+    
+    protected $_reversed = FALSE;
+    
+    /**
+     * maps index to ids
+     * 
+     * @var array
+     */
+    protected $_indexIdMapping = NULL;
+    
+    /**
+     * holds all lines
+     * 
+     * @var array
+     */
+    protected $_list = NULL;
+    
+    /**
+     * the value type for the cells
+     * currently string and float are supported
+     * defaults to 'string'
+     * 
+     * @var string
+     */
+    protected $_valueType = NULL;
+    
+    /**
+     * the constructor
+     * 
+     * @param array $data
+     * @param array $info
+     * @param string $valueType
+     */
+    public function __construct($data, $info = NULL, $valueType = self::TYPE_FLOAT)
+    {
+        $this->_info      = $info;
+        $this->_valueType = $valueType;
+        
+        if ($info !== NULL) {
+            foreach ($this->_info as $id => $title) {
+                $_this->_indexIdMapping[] = $id;
+                $this->_list[$id] = (isset($data[$id]) ? $data[$id] : ($this->_valueType == self::TYPE_FLOAT ? 0 : ''));
+            }
+        } else {
+            $this->_list = $data;
+        }
+    }
+    
+    /**
+     * reverses the direction
+     */
+    public function reverse()
+    {
+        $this->_list = array_reverse($this->_list, TRUE);
+        $this->_reversed = ! $this->_reversed;
+    }
+    
+    /**
+     * returns value from a specific row
+     * 
+     * @param integer $index
+     * @return integer
+     */
+    public function getAt($index)
+    {
+        return $this->_list[$this->_indexIdMappingRows[$index]];
+    }
+    
+    /**
+     * returns an item by its id
+     * 
+     * @param string $id
+     * @return multitype:
+     */
+    public function getById($id)
+    {
+        return $this->_list[$id];
+    }
+    
+    /**
+     * counts items in this->_list
+     *
+     * @return number
+     */
+    public function count()
+    {
+        return count($this->_list);
+    }
+    
+    /**
+     * returns the iterator
+     * 
+     * @return ArrayIterator
+     */
+    public function getIterator()
+    {
+        return new ArrayIterator($this->_list);
+    }
+    
+    /**
+     * returns the value type
+     * 
+     * @return string
+     */
+    public function getValueType()
+    {
+        return $this->_valueType;
+    }
+    
+    /**
+     * returns the iteratable as array
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return $this->_list;
+    }
+    
+    /**
+     *
+     * @return number
+     */
+    public function sum()
+    {
+        return array_sum($this->_list);
+    }
+}
\ No newline at end of file
diff --git a/tine20/library/OpenDocument/Shared/SimpleXML.php b/tine20/library/OpenDocument/Shared/SimpleXML.php
new file mode 100644 (file)
index 0000000..bec355a
--- /dev/null
@@ -0,0 +1,58 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     OpenDocument
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Alexander Stintzing <a.stintzing@metaways.de>
+ * @copyright   Copyright (c) 2013 Metaways Infosystems GmbH (http://www.metaways.de)
+ */
+
+
+/**
+ * helper class for simplexml
+ *
+ * @package    OpenDocument
+ * @subpackage Shared_SimpleXML
+ */
+class OpenDocument_Shared_SimpleXML {
+    
+    /**
+     * inserts a simpleaml element after another one
+     * 
+     * @param SimpleXMLElement $insert the element to insert
+     * @param array $target
+     * @param number $targetIndex
+     * @return SimpleXMLElement
+     */
+    public static function simplexml_insert_after(SimpleXMLElement $insert, array $target, $targetIndex = 0)
+    {
+        $dt = dom_import_simplexml($target[$targetIndex]);
+        $di = $dt->ownerDocument->importNode(dom_import_simplexml($insert), true);
+        
+        if ($dt->nextSibling) {
+            return $target[$targetIndex] = simplexml_import_dom($dt->parentNode->insertBefore($di, $dt->nextSibling));
+        } else {
+            return $target[$targetIndex] = simplexml_import_dom($dt->parentNode->appendChild($di));
+        }
+    }
+    
+    /**
+     * 
+     * @param SimpleXMLElement $insert
+     * @param array $target
+     * @param number $targetIndex
+     * @return SimpleXMLElement
+     */
+    public static function simplexml_insert_before(SimpleXMLElement $insert, array $target, $targetIndex = 0)
+    {
+        $dt = dom_import_simplexml($target[$targetIndex]);
+        $di = $dt->ownerDocument->importNode(dom_import_simplexml($insert), true);
+        
+        if ($dt->previousSibling) {
+            return $target[$targetIndex] = simplexml_import_dom($dt->parentNode->insertBefore($di, $dt->previousSibling->nextSibling));
+        } else {
+            return $target[$targetIndex] = simplexml_import_dom($dt->parentNode->insertBefore($di, $dt));
+        }
+    }
+}
\ No newline at end of file
index ad598d9..a543d7e 100644 (file)
@@ -5,11 +5,8 @@
  * @package     OpenDocument
  * @subpackage  OpenDocument
  * @license     http://framework.zend.com/license/new-bsd     New BSD License
- * @copyright   Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
- * @version     $Id$
- * 
- * @todo        parse existing tables/rows/columns/cells when using template file
  */
 
 /**
@@ -33,48 +30,327 @@ class OpenDocument_SpreadSheet implements Iterator, Countable
      * @var SimpleXMLElement
      */
     protected $_spreadSheet;
-        
+    
+    /**
+     * returns the content type
+     * 
+     * @return string
+     */
     public function getContentType()
     {
         return self::CONTENT_TYPE;
     }
     
+    /**
+     * no idea what this is for
+     * 
+     * @param SimpleXMLElement $_parentNode
+     */
     public function __construct(SimpleXMLElement $_parentNode)
     {
         $this->_spreadSheet = $_parentNode;
     }
     
+    /**
+     * returns the body as simplexml
+     * 
+     * @var SimpleXMLElement
+     */
+    public function getBody()
+    {
+        return $this->_spreadSheet;
+    }
+    
+    /**
+     * replaces a marker with a matrix
+     * 
+     * @param SimpleXMLElement $xml
+     * @param OpenDocument_Matrix $matrix
+     * @param bool $showLegend
+     * @param bool $showLegendDescription
+     * @param bool $showSums
+     */
+    public function replaceMatrix($xml, $matrix, $showLegend = TRUE, $showLegendDescription = TRUE, $showSums = TRUE)
+    {
+        $table  = $this->_findParentTable($xml);
+        $sp     = $this->_getStartingPoint($xml);
+        $reference      = $sp['reference'];
+        $referenceIndex = $sp['referenceIndex'];
+        
+        $colLegend = $matrix->getColumnLegend();
+        $rowLegend = $matrix->getRowLegend();
+        $columnLegendDescription = $matrix->getColumnLegendDescription();
+        $rowLegendDescription = $matrix->getRowLegendDescription();
+        
+        $mcount = $matrix->count();
+        
+        // show vertical sum legend
+        if ($showSums && $showLegend) {
+            $row = OpenDocument_SpreadSheet_Row::createRow($table, null, $reference, $referenceIndex, 'after');
+            $row->appendCell('', OpenDocument_SpreadSheet_Cell::TYPE_STRING);
+            $row->appendCell(_('Sum'), OpenDocument_SpreadSheet_Cell::TYPE_STRING, array('table:style-name' => "cell-header"));
+            
+            $index = 0;
+            foreach ($matrix->getRowInfo() as $id => $title) {
+                $row->appendCell($matrix->sumColumn($id), OpenDocument_SpreadSheet_Cell::TYPE_FLOAT);
+                $index++;
+            }
+            $row->appendCell('', OpenDocument_SpreadSheet_Cell::TYPE_STRING);
+            $row->appendCell($matrix->sum(), OpenDocument_SpreadSheet_Cell::TYPE_FLOAT);
+            $row = OpenDocument_SpreadSheet_Row::createRow($table, null, $reference, $referenceIndex, 'after');
+        }
+        
+        // insertion is always after the line before the marker has been, so reversion is needed
+        $matrix->reverse();
+        $countColumns = count($matrix);
+        $i = 1;
+        $rowIndex = $countColumns - 1;
+        $colIndex = 0;
+        
+        foreach ($matrix as $key => $list) {
+            $row = OpenDocument_SpreadSheet_Row::createRow($table, null, $reference, $referenceIndex, 'after');
+            
+            if ($showLegendDescription) {
+                if ($i < $countColumns) {
+                    $row->appendCoveredCell();
+                } else {
+                    $row->appendCell(
+                        $columnLegendDescription,
+                        OpenDocument_SpreadSheet_Cell::TYPE_STRING,
+                        array(
+                            'table:number-rows-spanned' => $mcount,
+                            'table:style-name' => "cell-center-middle"
+                        )
+                    );
+                }
+                $i++;
+            }
+            
+            if ($showLegend) {
+                $val = isset($colLegend[$key]) ? $colLegend[$key] : '';
+                $row->appendCell(
+                    $val,
+                    OpenDocument_SpreadSheet_Cell::TYPE_STRING,
+                    array('table:style-name' => "cell-header")
+                );
+            }
+            
+            // process values of the matrix
+            foreach ($list as $cellValue) {
+                $row->appendCell(
+                    $cellValue,
+                    $list->getValueType(),
+                    ($colIndex === $rowIndex ? array('table:style-name' => "cell-color-grey") : array('table:style-name' => 'value-cell-default'))
+                );
+                $colIndex++;
+            }
+            $colIndex = 0;
+            $rowIndex--;
+            
+            if ($showSums) {
+                $row->appendCell('', OpenDocument_SpreadSheet_Cell::TYPE_STRING);
+                $row->appendCell($list->sum(), OpenDocument_SpreadSheet_Cell:: TYPE_FLOAT, array('table:style-name' => "cell-header"));
+            }
+        }
+        
+        // add legend at top
+        if ($showLegend || $showLegendDescription) {
+            $row = OpenDocument_SpreadSheet_Row::createRow($table, null, $reference, $referenceIndex, 'after');
+            $row->appendCell('', OpenDocument_SpreadSheet_Cell::TYPE_STRING);
+        }
+        
+        if ($showLegend && $showLegendDescription) {
+            $row->appendCell('', OpenDocument_SpreadSheet_Cell::TYPE_STRING);
+        }
+        
+        if ($showLegend) {
+            foreach ($rowLegend as $title) {
+                $row->appendCell($title, OpenDocument_SpreadSheet_Cell::TYPE_STRING, array('table:style-name' => "cell-header"));
+            }
+            // show vertical sums
+            if ($showSums) {
+                $row->appendCell('', OpenDocument_SpreadSheet_Cell::TYPE_STRING);
+                $row->appendCell(_('Sum'), OpenDocument_SpreadSheet_Cell::TYPE_STRING, array('table:style-name' => "cell-header"));
+            }
+        }
+        
+        if ($showLegendDescription) {
+            
+            $row = OpenDocument_SpreadSheet_Row::createRow($table, null, $reference, $referenceIndex, 'after');
+            $row->appendCell('', OpenDocument_SpreadSheet_Cell::TYPE_STRING);
+            
+            if ($showLegend) {
+                $row->appendCell('', OpenDocument_SpreadSheet_Cell::TYPE_STRING);
+                $row->appendCell(
+                    $rowLegendDescription,
+                    OpenDocument_SpreadSheet_Cell::TYPE_STRING,
+                    array(
+                        'table:number-columns-spanned' => count($list),
+                        'table:style-name' => "cell-center-middle"
+                    )
+                );
+            }
+        }
+    }
+    
+    /**
+     * 
+     * @param SimpleXMLElement $xmlElement
+     * @param OpenDocument_Matrix_List $list
+     * @param string $type
+     * @param string $direction
+     */
+    public function replaceList($xml, $list, $type = OpenDocument_SpreadSheet_Cell::TYPE_STRING, $direction = 'horizontal')
+    {
+        $table          = $this->_findParentTable($xml);
+        $sp             = $this->_getStartingPoint($xml, $rows);
+        $reference      = $sp['reference'];
+        $referenceIndex = $sp['referenceIndex'];
+        
+        if ($direction == 'horizontal') {
+            $row = OpenDocument_SpreadSheet_Row::createRow($table, null, $reference, $referenceIndex, 'after');
+            foreach($list as $item) {
+                $row->appendCell($item, $type);
+            }
+        } elseif ($direction == 'vertical') {
+            $list->reverse();
+            foreach($list as $item) {
+                $row = OpenDocument_SpreadSheet_Row::createRow(
+                    $table, null, $reference, $referenceIndex, 'after'
+                );
+                $row->appendCell($item);
+            }
+        } else {
+            throw new Exception('direction ' . $direction . ' is unsupported!');
+        }
+    }
+    
+    /**
+     * returns the starting point for replacing, deletes the marker itself
+     * 
+     * @param SimpleXMLElement $xml
+     * @return array
+     */
+    protected function _getStartingPoint($xml)
+    {
+        $rows  = $this->_findParentRow($xml, TRUE);
+        $prev = $rows[0]->xpath('preceding-sibling::*');
+        
+        // find reference row, if no preceding sibling gets found, the next one will be used
+        if (count($prev) > 0) {
+            $reference = $prev;
+            $referenceIndex = count($prev) - 1;
+        } else {
+            $next = $rows[0]->xpath('following-sibling::*');
+            $referenceIndex = 0;
+            $reference = isset($next[0]) ? $next : NULL;
+        }
+        
+        // delete row node where the marker resides
+        unset($rows[0]);
+        
+        return array('reference' => $reference, 'referenceIndex' => $referenceIndex);
+    }
+    
+    /**
+     * finds the parent table of a node
+     *
+     * @param array|SimpleXMLElement $xml
+     * @throws Exception
+     * @return SimpleXMLElement
+     */
+    protected function _findParentTable($xml)
+    {
+        if (! $xml) {
+            throw new Exception('The table of the element could not be found!');
+        }
+    
+        if (is_array($xml) && count($xml) == 1) {
+            $xml = $xml[0];
+        }
+    
+        if ($xml->getName() != 'table') {
+            return $this->_findParentTable($xml->xpath('parent::*'));
+        }
+    
+        return $xml;
+    }
+    
+    /**
+     * finds the parent row of a node (where a marker may sit)
+     * 
+     * @param array|SimpleXMLElement $xml
+     * @param bool $asRef
+     * @throws Exception
+     * @return SimpleXMLElement
+     */
+    protected function _findParentRow($xml, $asRef = FALSE)
+    {
+        if (! $xml) {
+            throw new Exception('The row of the element could not be found!');
+        }
+        
+        if (is_array($xml) && count($xml) == 1) {
+            $el = $xml[0];
+        } else {
+            $el = $xml;
+        }
+        
+        if ($el->getName() != 'table-row') {
+            return $this->_findParentRow($el->xpath('parent::*'));
+        }
+    
+        return $asRef ? $xml : $el;
+    }
+
+    /**
+     * returns all tables of the spreadsheet
+     * 
+     * @return array
+     */
     public function getTables()
     {
         $tables = $this->_spreadSheet->xpath('//office:body/office:spreadsheet/table:table');
         
         $result = array();
         
-        foreach($tables as $table) {
-            $attributes = $table->attributes(self::TABLE_URN);
+        foreach ($tables as $table) {
+            $attributes = $table->attributes(OpenDocument_Document::NS_TABLE);
             $result[(string)$attributes['name']] = new OpenDocument_SpreadSheet_Table($table);
         }
         
         return $result;
     }
     
+    /**
+     * checks if a table exists (by name)
+     * 
+     * @param string $_tableName
+     * @return bool
+     */
     public function tableExists($_tableName)
     {
         $table = $this->_spreadSheet->xpath("//office:body/office:spreadsheet/table:table[@table:name='$_tableName']");
         
-        if(count($table) === 0) {
-            return false;
+        if (count($table) === 0) {
+            return FALSE;
         }
         
         return true;
     }
     
+    /**
+     * returns a table
+     * 
+     * @param string $_tableName
+     * @return boolean|OpenDocument_SpreadSheet_Table
+     */
     public function getTable($_tableName)
     {
         $table = $this->_spreadSheet->xpath("//office:body/office:spreadsheet/table:table[@table:name='$_tableName']");
         
-        if(count($table) === 0) {
-            return false;
+        if (count($table) === 0) {
+            return FALSE;
         }
         
         return new OpenDocument_SpreadSheet_Table($table[0]);
index cbb14c3..7586ff3 100644 (file)
@@ -25,6 +25,7 @@ class OpenDocument_SpreadSheet_Cell
     const TYPE_FLOAT      = 'float';
     const TYPE_PERCENTAGE = 'percentage';
     const TYPE_STRING     = 'string';
+    const TYPE_FUNCTION   = 'function';
     
     /**
      * 
@@ -37,13 +38,56 @@ class OpenDocument_SpreadSheet_Cell
         $this->_cell = $_cell;
     }
     
-    static public function createCell($_parent, $_value, $_type = null)
+    /**
+     * creates a covered table cell
+     * 
+     * @param OpenDocument_SpreadSheet_Row|SimpleXMLElement $parent
+     */
+    static public function createCoveredCell($parent)
     {
+        if ($parent instanceof OpenDocument_SpreadSheet_Row) {
+            $parent = $parent->getBody();
+        }
+        
+        $parent->addChild('covered-table-cell', null, OpenDocument_Document::NS_TABLE);
+    }
+    
+    
+    /**
+     * 
+     * @param SimpleXMLElement|OpenDocument_SpreadSheet_Row $_parent
+     * @param scalar $_value
+     * @param string $_type
+     * @param array $additionalAttributes
+     * 
+     * @return OpenDocument_SpreadSheet_Cell
+     */
+    static public function createCell($_parent, $_value, $_type = null, $additionalAttributes = array())
+    {
+        if ($_parent instanceof OpenDocument_SpreadSheet_Row) {
+            $_parent = $_parent->getBody();
+        }
+        
         $cellElement = $_parent->addChild('table-cell', null, OpenDocument_Document::NS_TABLE);
         
         if($_value !== null) {
             if($_type !== null) {
-                $cellElement->addAttribute('office:value-type', $_type, OpenDocument_Document::NS_OFFICE);
+                if ($_type == self::TYPE_FUNCTION) {
+                    $cellElement->addAttribute('office:value-type', self::TYPE_FLOAT, OpenDocument_Document::NS_OFFICE);
+                    $cellElement->addAttribute('table:formula', self::_encodeValue($_value), OpenDocument_Document::NS_TABLE);
+                } else {
+                    $cellElement->addAttribute('office:value-type', $_type, OpenDocument_Document::NS_OFFICE);
+                }
+            }
+            
+            foreach ($additionalAttributes as $attName => $attValue) {
+                $ns = NULL;
+                if (strstr($attName, ':')) {
+                    $ex = explode(':', $attName);
+                    $cellElement->addAttribute($attName, self::_encodeValue($attValue), $ex[0]);
+                } else {
+                    $cellElement->addAttribute($attName, self::_encodeValue($attValue), $ns);
+                }
             }
             
             switch($_type) {
@@ -72,14 +116,15 @@ class OpenDocument_SpreadSheet_Cell
                     } else {
                         list($value, $currency) = explode(' ', $_value);
                     }
-                    if(isset($currency) && ! empty($$currency)) {                 
+                    if(isset($currency) && ! empty($$currency)) {
                         $cellElement->addAttribute('office:currency', self::_encodeValue($currency), OpenDocument_Document::NS_OFFICE);
-                    }                 
+                    }
                     $cellElement->addAttribute('office:value', self::_encodeValue($value), OpenDocument_Document::NS_OFFICE);
                     break;
             }
-            
-            if($_type != self::TYPE_CURRENCY && $_type != self::TYPE_PERCENTAGE) {
+            if ($_type == self::TYPE_FUNCTION) {
+                
+            } elseif ($_type != self::TYPE_CURRENCY && $_type != self::TYPE_PERCENTAGE) {
                 $cellElement->addChild('p', self::_encodeValue($_value), OpenDocument_Document::NS_TEXT);
             }
         }
index 9a619a6..dcf33d2 100644 (file)
@@ -5,9 +5,8 @@
  * @package     OpenDocument
  * @subpackage  OpenDocument
  * @license     http://framework.zend.com/license/new-bsd     New BSD License
- * @copyright   Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
- * @version     $Id$
  */
 
 /**
@@ -38,33 +37,64 @@ class OpenDocument_SpreadSheet_Row implements Iterator, Countable
         $this->_row = $_row;
     }
     
+    public function getBody()
+    {
+        return $this->_row;
+    }
+    
     /**
-     * add new row and return reference
+     * add new cell and return reference
      *
      * @param string|optional $_tableName
      * @return OpenDocument_SpreadSheet_Cell
      */
-    public function appendCell($_value, $_type = null)
+    public function appendCell($_value, $_type = null, $additionalAttributes = array())
     {
-        $cell = OpenDocument_SpreadSheet_Cell::createCell($this->_row, $_value, $_type);
-                
+        $cell = OpenDocument_SpreadSheet_Cell::createCell($this->_row, $_value, $_type, $additionalAttributes);
         return $cell;
     }
-        
+    
+    public function appendCoveredCell()
+    {
+        $cell = OpenDocument_SpreadSheet_Cell::createCoveredCell($this->_row);
+        return $cell;
+    }
+    
     public function setStyle($_styleName)
     {
         $this->_attributes['table:style-name'] = $_styleName;
     }
 
-    static public function createRow($_parent, $_styleName = null)
+    /**
+     * 
+     * @param SimpleXMLElement $_parent
+     * @param string $_styleName
+     * @param SimpleXMLElement $_referenceRow
+     * @param string $_position
+     * 
+     * @return OpenDocument_SpreadSheet_Row
+     */
+    static public function createRow($_parent, $_styleName = NULL, $_reference = NULL, $_refIndex = 0, $_position = 'after')
     {
-        $rowElement = $_parent->addChild('table-row', null, OpenDocument_Document::NS_TABLE);
-        
-        if($_styleName !== null) {
-            $rowElement->addAttribute('table:style-name', $_styleName, OpenDocument_Document::NS_TABLE);
+        if ($_reference == NULL) {
+            $rowElement = $_parent->addChild('table-row', NULL, OpenDocument_Document::NS_TABLE);
+            
+            if ($_styleName !== NULL) {
+                $rowElement->addAttribute('table:style-name', $_styleName, OpenDocument_Document::NS_TABLE);
+            }
+        } else {
+            
+            $rowElement = $_parent->addChild('table:table-row', NULL, OpenDocument_Document::NS_TABLE);
+            
+            if ($_position == 'after') {
+                $rowElement = OpenDocument_Shared_SimpleXML::simplexml_insert_after($rowElement, $_reference, $_refIndex);
+            } else {
+                $rowElement = OpenDocument_Shared_SimpleXML::simplexml_insert_before($rowElement, $_reference, $_refIndex);
+            }
+            
         }
         
-        $row = new OpenDocument_SpreadSheet_Row($rowElement);
+        $row = new self($rowElement);
         
         return $row;
     }
@@ -81,11 +111,6 @@ class OpenDocument_SpreadSheet_Row implements Iterator, Countable
         }
     }
     
-//    public function addStyle($_key, $_value)
-//    {
-//        $this->_styles[$_key] = $_value;
-//    }
-    
     function rewind() {
         $this->_position = 0;
     }
index 8204ba5..2efd0c1 100644 (file)
@@ -5,9 +5,8 @@
  * @package     OpenDocument
  * @subpackage  OpenDocument
  * @license     http://framework.zend.com/license/new-bsd     New BSD License
- * @copyright   Copyright (c) 2009 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2009-2013 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Lars Kneschke <l.kneschke@metaways.de>
- * @version     $Id$
  */
 
 /**
@@ -25,6 +24,11 @@ class OpenDocument_SpreadSheet_Table implements Iterator, Countable
     
     protected $_position = 0;
     
+    /**
+     * holds the representing xml
+     * 
+     * @var SimpleXMLElement
+     */
     protected $_table;
     
     public function __construct(SimpleXMLElement $_table)
@@ -32,6 +36,11 @@ class OpenDocument_SpreadSheet_Table implements Iterator, Countable
         $this->_table = $_table;
     }
     
+    public function getBody()
+    {
+        return $this->_table;
+    }
+    
     /**
      * add new row and return reference
      *
@@ -45,12 +54,43 @@ class OpenDocument_SpreadSheet_Table implements Iterator, Countable
         return $row;
     }
     
+    /**
+     * inserts new row and return reference
+     *
+     * @param string|optional $_tableName
+     * @return OpenDocument_SpreadSheet_Row
+     */
+    public function insertRow($referenceRow, $position = 'after', $styleName = null)
+    {
+        $row = OpenDocument_SpreadSheet_Row::createRow($this->_table, $styleName, $referenceRow, $position);
+        
+        return $row;
+    }
+    
+    /**
+     * sets the title of the table
+     * 
+     * @param string $tile
+     */
+    public function setTitle($title)
+    {
+        $this->_table->attributes(OpenDocument_Document::NS_TABLE)->name = $title;
+    }
+    
+    /**
+     * creates a table
+     * 
+     * @param SimpleXMLElement $_parent
+     * @param string $_tableName
+     * @param string $_styleName
+     * @return OpenDocument_SpreadSheet_Table
+     */
     static public function createTable(SimpleXMLElement $_parent, $_tableName, $_styleName = null)
     {
         $tableElement = $_parent->addChild('table', null, OpenDocument_Document::NS_TABLE);
         $tableElement->addAttribute('table:name', $_tableName, OpenDocument_Document::NS_TABLE);
         
-        if($_styleName !== null) {
+        if ($_styleName !== null) {
             $tableElement->addAttribute('table:style-name', $_styleName, OpenDocument_Document::NS_TABLE);
         }