Пример #1
0
 /**
  * Parse and evaluate the content of a cell
  *
  * @since 1.0.0
  *
  * @param string $content Content of a cell
  * @param array $parents List of cells that depend on this cell (to prevent circle references)
  * @return string Result of the parsing/evaluation
  */
 protected function _evaluate_cell($content, array $parents = array())
 {
     if ('' == $content || '=' == $content || '=' != $content[0]) {
         return $content;
     }
     $content = substr($content, 1);
     // Support putting formulas in strings, like =Total: {A3+A4}
     $expressions = array();
     if (preg_match_all('#{(.+?)}#', $content, $expressions, PREG_SET_ORDER)) {
         $formula_in_string = true;
     } else {
         $formula_in_string = false;
         $expressions[] = array($content, $content);
         // fill array so that it has the same structure as if it came from preg_match_all()
     }
     foreach ($expressions as $expression) {
         $orig_expression = $expression[0];
         $expression = $expression[1];
         $replaced_references = $replaced_ranges = array();
         // remove all whitespace characters
         $expression = preg_replace('#[\\r\\n\\t ]#', '', $expression);
         // expand cell ranges (like A3:A6) to a list of single cells (like A3,A4,A5,A6)
         if (preg_match_all('#([A-Z]+)([0-9]+):([A-Z]+)([0-9]+)#', $expression, $referenced_cell_ranges, PREG_SET_ORDER)) {
             foreach ($referenced_cell_ranges as $cell_range) {
                 if (in_array($cell_range[0], $replaced_ranges, true)) {
                     continue;
                 }
                 $replaced_ranges[] = $cell_range[0];
                 if (isset($this->known_ranges[$cell_range[0]])) {
                     $expression = preg_replace('#(?<![A-Z])' . preg_quote($cell_range[0], '#') . '(?![0-9])#', $this->known_ranges[$cell_range[0]], $expression);
                     continue;
                 }
                 // no -1 necessary for this transformation, as we don't actually access the table
                 $first_col = TablePress::letter_to_number($cell_range[1]);
                 $first_row = $cell_range[2];
                 $last_col = TablePress::letter_to_number($cell_range[3]);
                 $last_row = $cell_range[4];
                 $col_start = min($first_col, $last_col);
                 $col_end = max($first_col, $last_col) + 1;
                 // +1 for loop below
                 $row_start = min($first_row, $last_row);
                 $row_end = max($first_row, $last_row) + 1;
                 // +1 for loop below
                 $cell_list = array();
                 for ($col = $col_start; $col < $col_end; $col++) {
                     for ($row = $row_start; $row < $row_end; $row++) {
                         $column = TablePress::number_to_letter($col);
                         $cell_list[] = "{$column}{$row}";
                     }
                 }
                 $cell_list = implode(',', $cell_list);
                 $expression = preg_replace('#(?<![A-Z])' . preg_quote($cell_range[0], '#') . '(?![0-9])#', $cell_list, $expression);
                 $this->known_ranges[$cell_range[0]] = $cell_list;
             }
         }
         // parse and evaluate single cell references (like A3 or XY312), while prohibiting circle references
         if (preg_match_all('#([A-Z]+)([0-9]+)#', $expression, $referenced_cells, PREG_SET_ORDER)) {
             foreach ($referenced_cells as $cell_reference) {
                 if (in_array($cell_reference[0], $parents, true)) {
                     return '!ERROR! Circle Reference';
                 }
                 if (in_array($cell_reference[0], $replaced_references, true)) {
                     continue;
                 }
                 $replaced_references[] = $cell_reference[0];
                 $ref_col = TablePress::letter_to_number($cell_reference[1]) - 1;
                 $ref_row = $cell_reference[2] - 1;
                 if (!isset($this->table['data'][$ref_row]) || !isset($this->table['data'][$ref_row][$ref_col])) {
                     return "!ERROR! Cell {$cell_reference[0]} does not exist";
                 }
                 $ref_parents = $parents;
                 $ref_parents[] = $cell_reference[0];
                 $result = $this->table['data'][$ref_row][$ref_col] = $this->_evaluate_cell($this->table['data'][$ref_row][$ref_col], $ref_parents);
                 // Bail if there was an error already
                 if (false !== strpos($result, '!ERROR!')) {
                     return $result;
                 }
                 // remove all whitespace characters
                 $result = preg_replace('#[\\r\\n\\t ]#', '', $result);
                 // Treat empty cells as 0
                 if ('' == $result) {
                     $result = 0;
                 }
                 // Bail if the cell does not result in a number (meaning it was a number or expression before being evaluated)
                 if (!is_numeric($result)) {
                     return "!ERROR! {$cell_reference[0]} does not contain a number or expression";
                 }
                 $expression = preg_replace('#(?<![A-Z])' . $cell_reference[0] . '(?![0-9])#', $result, $expression);
             }
         }
         $result = $this->_evaluate_math_expression($expression);
         // Support putting formulas in strings, like =Total: {A3+A4}
         if ($formula_in_string) {
             $content = str_replace($orig_expression, $result, $content);
         } else {
             $content = $result;
         }
     }
     return $content;
 }
Пример #2
0
    /**
     * Print the content of the "Table Content" post meta box
     *
     * @since 1.0.0
     */
    public function postbox_table_data($data, $box)
    {
        $table = $data['table']['data'];
        $options = $data['table']['options'];
        $visibility = $data['table']['visibility'];
        $rows = count($table);
        $columns = count($table[0]);
        $head_row_idx = $foot_row_idx = -1;
        // determine row index of the table head row, by excluding all hidden rows from the beginning
        if ($options['table_head']) {
            for ($row_idx = 0; $row_idx < $rows; $row_idx++) {
                if (1 === $visibility['rows'][$row_idx]) {
                    $head_row_idx = $row_idx;
                    break;
                }
            }
        }
        // determine row index of the table foot row, by excluding all hidden rows from the end
        if ($options['table_foot']) {
            for ($row_idx = $rows - 1; $row_idx > -1; $row_idx--) {
                if (1 === $visibility['rows'][$row_idx]) {
                    $foot_row_idx = $row_idx;
                    break;
                }
            }
        }
        ?>
<table id="edit-form" class="tablepress-edit-screen-id-<?php 
        echo esc_attr($data['table']['id']);
        ?>
">
	<thead>
		<tr id="edit-form-head">
			<th></th>
			<th></th>
<?php 
        for ($col_idx = 0; $col_idx < $columns; $col_idx++) {
            $column_class = '';
            if (0 === $visibility['columns'][$col_idx]) {
                $column_class = ' column-hidden';
            }
            $column = TablePress::number_to_letter($col_idx + 1);
            echo "\t\t\t<th class=\"head{$column_class}\"><span class=\"sort-control sort-desc hide-if-no-js\" title=\"" . esc_attr__('Sort descending', 'tablepress') . "\"><span class=\"sorting-indicator\"></span></span><span class=\"sort-control sort-asc hide-if-no-js\" title=\"" . esc_attr__('Sort ascending', 'tablepress') . "\"><span class=\"sorting-indicator\"></span></span><span class=\"move-handle\">{$column}</span></th>\n";
        }
        ?>
			<th></th>
		</tr>
	</thead>
	<tfoot>
		<tr id="edit-form-foot">
			<th></th>
			<th></th>
<?php 
        for ($col_idx = 0; $col_idx < $columns; $col_idx++) {
            $column_class = '';
            if (0 === $visibility['columns'][$col_idx]) {
                $column_class = ' class="column-hidden"';
            }
            echo "\t\t\t<th{$column_class}><input type=\"checkbox\" class=\"hide-if-no-js\" />";
            echo "<input type=\"hidden\" class=\"visibility\" name=\"table[visibility][columns][]\" value=\"{$visibility['columns'][$col_idx]}\" /></th>\n";
        }
        ?>
			<th></th>
		</tr>
	</tfoot>
	<tbody id="edit-form-body">
<?php 
        foreach ($table as $row_idx => $row_data) {
            $row = $row_idx + 1;
            $classes = array();
            if ($row_idx % 2 == 0) {
                $classes[] = 'odd';
            }
            if ($head_row_idx == $row_idx) {
                $classes[] = 'head-row';
            } elseif ($foot_row_idx == $row_idx) {
                $classes[] = 'foot-row';
            }
            if (0 === $visibility['rows'][$row_idx]) {
                $classes[] = 'row-hidden';
            }
            $row_class = !empty($classes) ? ' class="' . implode(' ', $classes) . '"' : '';
            echo "\t\t<tr{$row_class}>\n";
            echo "\t\t\t<td><span class=\"move-handle\">{$row}</span></td>";
            echo "<td><input type=\"checkbox\" class=\"hide-if-no-js\" /><input type=\"hidden\" class=\"visibility\" name=\"table[visibility][rows][]\" value=\"{$visibility['rows'][$row_idx]}\" /></td>";
            foreach ($row_data as $col_idx => $cell) {
                $column = TablePress::number_to_letter($col_idx + 1);
                $column_class = '';
                if (0 === $visibility['columns'][$col_idx]) {
                    $column_class = ' class="column-hidden"';
                }
                $cell = esc_textarea($cell);
                // sanitize, so that HTML is possible in table cells
                echo "<td{$column_class}><textarea name=\"table[data][{$row_idx}][{$col_idx}]\" id=\"cell-{$column}{$row}\" rows=\"1\">{$cell}</textarea></td>";
            }
            echo "<td><span class=\"move-handle\">{$row}</span></td>\n";
            echo "\t\t</tr>\n";
        }
        ?>
	</tbody>
</table>
<input type="hidden" id="number-rows" name="table[number][rows]" value="<?php 
        echo $rows;
        ?>
" />
<input type="hidden" id="number-columns" name="table[number][columns]" value="<?php 
        echo $columns;
        ?>
" />
<?php 
    }
Пример #3
0
 /**
  * Test that columns positions (numbers) are converted to their names (letters) properly.
  *
  * @dataProvider data_number_to_letter
  *
  * @since 1.1.0
  *
  * @param int    $number Number to convert.
  * @param string $letter Conversion result letter.
  */
 public function test_number_to_letter($number, $letter)
 {
     $this->assertSame($letter, TablePress::number_to_letter($number));
 }