<?php
/**
 * FIT TableEditor
 * 
 * $Id$
 * 
 * @author Daniel Jahnke <daniel.jahnke@web.de>
 * @package FIT
 * @license LGPL http://www.gnu.org/copyleft/lesser.html
 */
 
/**
 * FIT TableEditor
 * 
 * @version 0.4.2
 * @package FIT
 * @subpackage Parser
 */

/** 
 * path to user's fixtures
 */
if( !defined'PHPFIT_FIXTURE_DIR' ) ) {
    
define'PHPFIT_FIXTURE_DIR''.' );
}

/**
 * load Parser
 */
include_once 'PHPFIT/Parser.php';

/**
 * load Fixture
 */
include_once 'PHPFIT/Fixture.php';

class 
PHPFIT_TableEditor
{
   
/**
    * parser object
    * @var object 
    */
    
private $_parser;

   
/**
    * file, contain the html-file
    * @var string
    */
    
private $_file;

   
/**
    * finalFile, file-string to save the modified table into original file
    * @var string
    */
    
private $_finalFile;

   
/**
    * action
    * @var string
    */
    
private $_action;

   
/**
    * tables, contain the tables of html-doc and cell-content
    * tables[ table_number ][ cell_number ]
    * @var array
    */
    
private $_tables;

   
/**
    * checkBox
    * @var array
    */
    
private $_checkBox = array();

   
/**
    * work file
    * file to edited
    * @var string
    */
    
private $_workFile;

   
/**
    * state
    * contain the state for the table method
    * tableEdit or tableSave or tableDelete
    * needed to display the button over the tables at function getHtml()
    * @var string
    */
    
private $_state;

   
/**
    * configuration array for table design edit/delete buttons
    *
    * self
    * - the to-be-called script
    * fixture
    * - kind of fixture
    * edit/delete/checkBox
    * - on | off ( if edit/delete-button should display )
    * cacheDir
    * - cache directory
    * dataDir
    * - source directory
    * css
    * - button      style of buttons
    * - inputField  style of input field
    * - column      style of append column
    */
    
private $_config = array(
                        
'self'              =>  null,
                        
'edit'              => 'on',
                        
'delete'            => 'on',
                        
'checkBox'          => 'on',
                        
'cacheDir'          => 'cache',
                        
'dataDir'           => 'data',
                        
'css-button'        => 'button',
                        
'css-inputField'    => 'inputField',
                        
'css-column'        => 'column',
                            );

   
/**
    * row
    * row which will be edited
    * @var int 
    */
    
private $_row;

   
/**
    * constructor
    *
    * creates a parser and fixture object
    * decide wether function will be called
    * creates a copy form file and work with it
    * @param string $file the html file, that will be modified
    * @param array  $options contain the setting for tableEditor
    */
    
public function __construct$file$options null )
    {
        
// create parser
        
$this->_parser = new PHPFIT_Parser();

        if( 
$options != null ) {
            foreach( 
$options as $key => $value ) {
                
$this->_config[$key] = $value;
            }
        }

        
/*
        * creates a temp-file and work with it, if finalSave the temp-file-content
        * will be written into original file an unlink the temp file
        */
        
$this->_finalFile $this->_config['dataDir'] . $file;
        if( 
file_exists$this->_config['cacheDir'] . '/' .  md5$this->_finalFile ) ) ) {
            
$this->_file $this->_config['cacheDir'] . '/' md5$this->_finalFile );
        }
        else {
            
$this->_file $this->_config['dataDir'] . $file;
        }

        
$content file_get_contents$this->_file );
        
$this->_parser->parse$content );
        
$html $this->_parser->serialize();

        
$this->_workFile $file;
    }

   
/**
    * get fixture information
    * default fixture = columnfixture
    *
    * @param int $table     table number
    * @param bool $func     true retunrs the fixture functions, else return the fixture kind
    * @return               kind of fixture or return the fixture functions
    */
    
private function _getFixtureInformation$table$dry false  )
    {
        
$includeFixtures = array(
                        
'columnfixture' => 'Column',
                        
'actionfixture' => 'Action',
                        
'summary'       => 'Summary',
                        
//'rowfixture' => 'Row.php',
                                
);

        
$fixtureName    $this->_parser->getNodeValue'cData'$table0);
        
$fixture        PHPFIT_FIXTURE_DIR '/' str_replace'.''/'$fixtureName );

        
$fix '';
        try{
            
$fix     PHPFIT_Fixture::loadFixture$fixtureName );
        }
        catch ( 
Exception $e ) {
            
PHPFIT_ParserException::addMsg'no fixure found for this table! ' $e->getMessage(), 10$e->getFile(), $e->getLine() );
        }

        foreach( 
$includeFixtures as $key => $fixture ) {
            if( !include_once 
PHPFIT_FIXTURE_DIR '/Fixture/' $fixture '.php' ) {
                
PHPFIT_ParserException::addMsg'Could not load Fixture: PHPFIT_Fixture_' $fixture2__FILE__ __LINE__ );
            }

            
$fixStr 'PHPFIT_Fixture_' $fixture;

            if( 
$fix instanceof $fixStr  ) {
                return 
$key;
            }
        }
        
//default columnfixture
        
return 'columnfixture';
    }

   
/**
    * edit row
    *
    * set to all cells in the editRow html-input-fields
    * @param int $table     table of the tables array
    * @param int $editRow   which row of selected table
    * @return bool true on success
    */
    
public function editRow$table$editRow )
    {
        
$row $this->_parser->countChildNodes$table );
        
$col $this->_parser->countChildNodes$table$row );

        
$fixture $this->_getFixtureInformation$table );
        if( 
$fixture == 'columnfixture' ) {
            
$startRow 1;
        }
        if( 
$fixture == 'rowfixture' ) {
            
$startRow 1;
        }
        if( 
$fixture == 'actionfixture' ) {
            
$startRow 0;
        }

        for( 
$i 0$i $row $i++ ) {
            if( 
$i == || $i == $startRow || $i != $editRow) {
                continue;
            }

            for( 
$column 0$column $col$column++ ) {
                
$value $this->_parser->getNodeValue'cData'$table$editRow$column );
                if( 
$value == " " ) {
                    
$value '&nbsp;';
                }
                
$value '<input class="' $this->_config['css-inputField'] . '" type="text" value="' $value '" name="table[' $table '][' $editRow '][' $column ']" />';
                if( 
$this->_parser->setNodeValue'cData'$value$table$editRow$column ) == false ) {
                    return 
false;
                }
                
$html $this->_parser->serialize();
            }
        }
        return 
true;
    }

   
/**
    * save row
    *
    * save modified row
    * @param int $table     table of the tables array
    * @param int saveRow    the modified row
    * @param array $cells   values of the row
    * @return string        modified table
    */
    
public function saveRow$table$cells$saveRow )
    {
        
$row $this->_parser->countChildNodes$table );
        
$col $this->_parser->countChildNodes$table$row );

        for( 
$column 0$column $col$column++ ) {
               if( 
$cells[$saveRow][$column] == " " ) {
                
$cells[$saveRow][$column] = '&nbsp;';
            }
            
$this->_parser->setNodeValue'cData'$cells[$saveRow][$column], $table$saveRow$column );
        }
        return 
$this->_parser->serialize();
    }

   
/**
    * save table
    *
    * if final = false
    * - the table will be saved at the cache - directory
    * if final = true
    * - the table will overwrite the original file at the data - directory
    * @param string $html   contain the html-table
    * @param bool $final    if true overwrite the original file, if false save to cache directory as md5
    */
    
public function saveTable$html$final false )
    {
        if( 
$final == true ) {
            if( 
file_put_contents$this->_finalFile$html ) ) {
                if( 
file_exists$this->_config['cacheDir'] . '/' md5$this->_finalFile ) ) ) {
                    
unlink$this->_config['cacheDir'] . '/' md5$this->_finalFile ) );
                    return 
true;
                }
            }
        }
        if( 
file_put_contents$this->_config['cacheDir'] . '/' md5$this->_finalFile ), $html ) ) {
            return 
true;
        }

        return 
false;
    }

   
/**
    * display
    *
    * getHtml table with form-tags
    * and print it
    */
    
public function getHtml()
    {
        
$html'';

        
$summarySet false;
        
// prepare tables
        
for( $table 0$table <  $this->_parser->countChildNodes(); $table++ ) {

            
$fixture $this->_getFixtureInformation$table );
            if( ( 
$table+$this->_parser->countChildNodes() ) && ( $this->_getFixtureInformation$table+) == 'summary' ) ) {
                
$summarySet true;
            }

            
// do not create modify-button for summay table
            
if( $fixture == 'summary' ) {
                continue;
            }

            
$startRow 0;
            if( 
$fixture == 'columnfixture' ) {
                
$startRow 2;
            }
            if( 
$fixture == 'rowfixture' ) {
                
$startRow 2;
            }
            if( 
$fixture == 'actionfixture' ) {
                
$startRow 1;
            }

            
$rows $this->_parser->countChildNodes$table );

            
$editCol    $this->_parser->countChildNodes$table$rows );
            
$delCol     $editCol;
            
$boxCol     $editCol;

            
// edit ON | OFF
            
if( $this->_config['edit'] == 'on' && $this->_state != 'tableEdit' ) {

                
$this->_parser->appendColumn$table );

                
// set the edit/save-button
                
for( $i $startRow$i $rows$i++ ) {
                    if( 
$this->_row == $i ) {
                        
$this->_parser->setNodeValue'cData''<input type="submit" value="Save" " name="action[save_' $table '_' $i ']" > '$table$i$editCol );
                        
// css
                        
$this->_parser->setNodeValue'attributes', array( 'class' => $this->_config['css-column']  ), $table$i$editCol );
                    }
                    else {
                        
$this->_parser->setNodeValue'cData''<input type="submit" value="Edit" name="action[edit_' $table '_' $i ']" > '$table$i$editCol );
                        
// css
                        
$this->_parser->setNodeValue'attributes', array( 'class' => $this->_config['css-column']  ), $table$i$editCol );
                    }
                }
                
$delCol += 1;
                ++
$boxCol;
            }

            
// delete ON | OFF
            
if( $this->_config['delete'] == 'on' && $this->_state != 'tableEdit' ) {
                
$this->_parser->appendColumn$table );

                
// set the delete-button
                
for( $i $startRow$i $rows$i++ ) {
                    
$this->_parser->setNodeValue'cData''<input type="submit" value="Delete" name="action[delete_' $table '_' $i ']" >'$table$i$delCol );
                    
// css
                    
$this->_parser->setNodeValue'attributes', array( 'class' => $this->_config['css-column']  ), $table$i$delCol );
                }
                ++
$boxCol;
            }

            
// checkbox ON | OFF
            
if( $this->_config['checkBox'] == 'on' ) {
                
$this->_parser->appendColumn$table );
                if( 
$this->_state == 'tableEdit' ) {
                    for( 
$k 0$k $rows$k++ ) {
                        if( 
in_array$k$this->_checkBox[0] ) ) {
                            
$this->_parser->setNodeValue'cData''<input type="checkbox" name="tableAction[checkBox_' $table '_' $k ']" checked="true" >' $this->_checkBox[1], $k$boxCol );
                            
// css
                            
$this->_parser->setNodeValue'attributes', array( 'class' => $this->_config['css-column']  ), $this->_checkBox[1], $k$boxCol );
                        }
                    }
                }
                else {
                    
// set the checkboxes
                    
for( $i $startRow$i $rows$i++ ) {
                        
$this->_parser->setNodeValue'cData''<input type="checkbox" name="tableAction[checkBox_' $table '_' $i ']" >'$table$i$boxCol );
                        
// css
                        
$this->_parser->setNodeValue'attributes', array( 'class' => $this->_config['css-column']  ), $table$i$boxCol );
                    }
                }
            }

            
$addRowButton $this->_parser->getNodeValue'before'$table ) . "\n";

            
// css 
            
$addRowButton .= '<div class="' $this->_config['css-button'] . '">';

            
// append | remove summary table button 
/*
            if( $summarySet ) {
                $addRowButton .= '<input type="submit" value="Remove summary" name="action[tableEdit_' . $table . '_removeSummary]" >&nbsp';
                $summarySet = false;
            }
            else {
                $addRowButton .= '<input type="submit" value="Add summary" name="action[tableEdit_' . $table . '_addSummary]" >&nbsp';
            }
*/
            // do not show Append row-button if no option is set
            
if( $this->_config['checkBox'] == 'off' && $this->_config['delete'] == 'off' && $this->_config['edit'] == 'off' ) {
                
$addRowButton .= '&nbsp;';
            }
            else {
                
// add Append row-button
                
$addRowButton .= '<input type="submit" value="Append row" name="action[newRow_' $table '_append]" >&nbsp';
            }
            
$addRowButton .= '<input type="hidden" value="' $this->_workFile '" name="file" >';

            if( 
$this->_config['checkBox'] == 'on' ) {
                
// show Insert before row-button only if checkBox is on
                
$addRowButton .= '<input type="submit" value="Insert before row" name="action[tableEdit_' $table '_newRow]" >&nbsp;&nbsp;&nbsp;&nbsp;';
                if( 
$this->_state == 'tableEdit' ) {
                    
$addRowButton .= '<input type="submit" value="Save"   name="action[tableEdit_' $table '_save]" >&nbsp;';
                }
                else {
                    
$addRowButton .= '<input type="submit" value="Edit"   name="action[tableEdit_' $table '_edit]" >&nbsp;';
                    
$addRowButton .= '<input type="submit" value="Delete" name="action[tableEdit_' $table '_delete]" >&nbsp;';
                }
            }

            
// css
            
$addRowButton .= '</div>';

            
$this->_parser->setNodeValue'before'$addRowButton$table );

            
$html $this->_parser->serialize();
            
$this->_stripHtml( &$html );
        }

        
//$self = $_SERVER["PHP_SELF"] . '?file=' . $this->_workFile;
        
$self   $this->_config['self'] . '&amp;file=' urlencode$this->_workFile );
        
        
// prepaire table
        
$editor = array();
        
array_push$editor'<form action="'$self .'" method="post">' );
        
array_push$editor$html );
        
// css
        
array_push$editor'<div class="' $this->_config['css-button'] . '">' );
        
array_push$editor'<input type="submit" value="Save Tables" name="action[saveFinal_' 'write' '_original]" >&nbsp;&nbsp;' );
        
array_push$editor'<input type="button" value="Cancel" onclick="confirmation(\'' $self '\')">' );
        
// css
        
array_push$editor'</div>' );
        
array_push$editor'</form>' );
        return 
$editor;
    }

   
/**
    * strip Html
    * extract the body from html-string
    * @param string $html   html-string
    */
    
private function _stripHtml( &$html )
    {
        
// search array for available tags
        
$stripArr   = array(
                        array( 
"start" => "<body""end" => "</body>" ),
                        array( 
"start" => "<head""end" => "</html>" ),
                        array( 
"start" => "<html""end" => "</html>" ),
                           );

        
$startStr "";
        
$endStr "";
        
// walk through the html-string and search after special html-tags
        
foreach( $stripArr as $key => $tag ) {
            if( 
is_intstrpos$html$tag['start'] ) ) && is_intstrpos$html$tag['end'] ) ) ) {
                
$startStr   $tag['start'];
                
$endStr     $tag['end'];
                break;
            }
        }

        
// if no special html-tags (see stripArr) were found, return
        
if( empty( $startStr )  && empty( $endStr ) ) {
            return;
        }

        
$start      stripos$html$startStr );
        
$end        stripos$html$endStr );

        
// ignore the attributes of opening special-tags
        
if( is_int$start ) && is_int$end ) ) {
           while( 
$html[$start++] != '>' );
        }

        
// cut the essetial part of html
        
$html mb_strcut$html$start$end $start );
    }

   
/**
    * edit
    * call the editRow function
    * @param int $table table number
    * @param int $row   row number
    * @return bool              true on success
    */
    
public function edit$table$row )
    {
        
$this->editRow$table$row );
        
// to set the edit-button  to save-button
        
$this->_row $row;
        return 
true;
    }

   
/**
    * save
    * call the saveRow function and save the complete table to temp-file
    * @param int $table     table number
    * @param array $cells   cells content
    * @param int $row       row number
    * @return bool              true on success
    */
    
public function save$table$cells$row )
    {
        
$html $this->saveRow$table$cells$row );
        
$this->saveTable$html );
        return 
true;
    }

   
/**
    * saveFinal
    * call the saveTable function and set the final flag TRUE   
    * the original file will be overwritten!
    * @return bool              true on success
    */
    
public function saveFinal()
    {
        
$html $this->_parser->serialize();
        
$this->saveTable$htmltrue );
        return 
true;
    }

   
/**
    * delete
    * call the deleteRow function 
    * and save the table content to temp-file
    * @param int $table table number
    * @param int $row   row number
    * @return bool              true on success
    */
    
public function delete$table$row )
    {
        
$this->_parser->deleteRow$table$row );

        
$html $this->_parser->serialize();
        
$this->saveTable$html );
        return 
true;
    }

   
/**
    * newRow
    * call the function appendRow from parser
    * and save the table content to temp-file
    * @param int $table         table number
    * @return bool              true on success
    */
    
public function newRow$table )
    {
        
$this->_parser->appendRow$table );
        
$html $this->_parser->serialize();
        
$this->saveTable$html );

        
$row  $this->_parser->countChildNodes$table ) - 1;
        
$this->edit$table$row );
        return 
true;
    }

   
/**
    * cancel
    * deletes the temp-file from cacheDir
    * @return bool              true on success, else false
    */
    
public function cancel()
    {
        if( 
file_exists$this->_config['cacheDir'] . '/' md5$this->_finalFile ) ) ) {
            if( 
unlink$this->_config['cacheDir'] . '/' md5$this->_finalFile ) ) ) {
                return 
true;
            }
            return 
false;
        }
        return 
true;
    }

   
/**
    * tableEdit
    * to edit the selected rows form $table
    * $this->_state is necessary to display the correct button to edit the table
    * checkBoxes-array contains all rows which will be edited
    * @param int $table         tablenumber
    * @param array $checkBoxes  list of all rows which will be edited
    * @return bool              true on success
    */
    
public function tableEdit$table$checkBoxes )
    {
        
// if no rows are selected, do nothing
        
if( empty( $checkBoxes ) ) {
            return 
true;
        }

        
$this->_state 'tableEdit';

        
$box = array();
        foreach( 
$checkBoxes as $value ) {
            
array_push$box$value[2] );
        }

        
array_push$this->_checkBox$box );
        
array_push$this->_checkBox$table );

        for( 
$i 0$i count$checkBoxes ); $i++ ) {
            
$this->editRow$table$checkBoxes[$i][2] );
        }
        return 
true;
    }

   
/**
    * tableDelete
    * to delete the selected rows form $table
    * $this->_state is necessary to display the correct button to delete the table
    * checkBoxes-array contains all rows which will be deleted
    * @param int $table         tablenumber
    * @param array $checkBoxes  list of all rows which will be edited
    * @return bool              true on success
    */
    
public function tableDelete$table$checkBoxes )
    {
        
$this->_state 'tableDelete';

        
$box = array();
        foreach( 
$checkBoxes as $value ) {
            
array_push$box$value[2] );
        }

        
$value count$checkBoxes ) - 1;
        for( 
$i 0$value >= $i$value-- ) {
            
// check deleting in correct table
            
if( $checkBoxes[$value][1] == $table ) {
                
$this->_parser->deleteRow$table$checkBoxes[$value][2] );
            }
        }
        
$html $this->_parser->serialize();

        
$this->saveTable$html );
        return 
true;
    }

   
/**
    * table new Row
    * insert new row before the selected row 
    * checkBoxes-array contains all rows which will be deleted
    * @param int $table         tablenumber
    * @param array $checkBoxes  list of all rows which will be edited
    * @return bool              true on success
    * @todo after insert new row make rows editable
    */
    
public function tableNewRow$table$checkBoxes )
    {
        
$box = array();
        foreach( 
$checkBoxes as $value ) {
            
array_push$box$value[2] );
        }

        
$value count$checkBoxes ) - 1;
        for( 
$i 0$value >= $i$value-- ) {
            
// check deleting in correct table
            
if( $checkBoxes[$value][1] == $table ) {
                
$this->_parser->insertBeforeRow$table$checkBoxes[$value][2] );
            }
        }
        
$html $this->_parser->serialize();

        
$this->saveTable$html );
        return 
true;
    }

   
/**
    * tableSave
    * to save the selected rows form $table
    * $this->_state is necessary to display the correct button to save the table
    * checkBoxes-array contains all rows which will be saved
    * @param int $table         tablenumber
    * @param array $checkBoxes  list of all rows which will be edited
    * @return bool              true on success
    */
    
public function tableSave$table$checkBoxes$cells )
    {
        
$this->_state 'tableSave';

        
$saveRows = array();
        foreach( 
$checkBoxes as $value ) {
            
array_push$saveRows$value[2] );
        }

        for( 
$i 0$i count$checkBoxes ); $i++ ) {
            
$this->saveRow$table$cells[$table], $saveRows[$i] );
        }

        
$html $this->_parser->serialize();
        
$this->saveTable$html );

        return 
true;
    }

   
/**
    * add summary table
    * to append fit.Summary table
    * @param int $table         tablenumber
    * @return bool              true on success
    */
    
public function addSummaryTable$table )
    {
        
$content '<br><table border="1"><tr><td>fit.Summary</td></tr></table>';
        
$this->_parser->appendTable$table$content );

        
$html $this->_parser->serialize();
        
$this->saveTable$html );

        return 
true;
    }

   
/**
    * remove summary table
    * remove a fit.Summary table
    * @param int $table         tablenumber
    * @return bool              true on success
    */
    
public function removeSummaryTable$table )
    {
        
$this->_parser->removeTable$table );

        
$html $this->_parser->serialize();
        
$this->saveTable$html );

        return 
true;
    }
}

?>