View source
<?php
require_once drupal_get_path('module', 'sheetnode') . '/socialcalc.inc';
define('SHEETNODE_EXPANSION_VERTICAL', 0);
define('SHEETNODE_EXPANSION_HORIZONTAL', 1);
class sheetnode_plugin_style extends views_plugin_style {
function option_definition() {
$options = parent::option_definition();
$options['expansion'] = array(
'default' => SHEETNODE_EXPANSION_VERTICAL,
);
$options['template'] = array(
'default' => FALSE,
);
$options['sheetsave'] = array(
'default' => '',
);
return $options;
}
function options_form(&$form, &$form_values) {
parent::options_form($form, $form_values);
$form['expansion'] = array(
'#type' => 'radios',
'#title' => t('Expansion of results'),
'#description' => t('You can specify whether view results should be expanded horizontally or vertically.'),
'#options' => array(
SHEETNODE_EXPANSION_VERTICAL => t('Consecutive rows'),
SHEETNODE_EXPANSION_HORIZONTAL => t('Consecutive columns'),
),
'#default_value' => $this->options['expansion'],
'#weight' => 97,
);
$form['template'] = array(
'#type' => 'checkbox',
'#title' => t('Use template'),
'#description' => t('Check this box to use the spreadsheet below as template for your view.
To place view results in the template, use the following syntax in the cells:
<ul>
<li><code>${field_label}</code> to indicate that this cell and subsequent ones should be filled with values of this field.</li>
<li><code>${FUNCTION(@field_label@, $field_label$, @cell_reference@, ...)}</code> to indicate that this cell and subsequent ones should be filled with a calculation.
<code>@field_label@</code> are replaced with the corresponding cell references, and <code>$field_label$</code> are replaced with actual field values.</li>
<li><code>$[FUNCTION(@field_label@, @cell_reference@, ...)]</code> to indicate that a calculation should be placed in this cell.
<code>@field_label@</code> are replaced with the corresponding cell ranges.</li>
</ul>'),
'#default_value' => $this->options['template'],
'#weight' => 98,
);
$form['sheetview'] = array(
'#value' => $this
->render_ajax($this->options['sheetsave'], 'edit-style-options-sheetsave'),
'#weight' => 99,
);
$form['sheetsave'] = array(
'#type' => 'hidden',
);
}
function render_sheet() {
$tangent = $this->options['expansion'];
$normal = 1 - $tangent;
if ($this->options['template']) {
$socialcalc = socialcalc_parse($this->options['sheetsave']);
$lastpos = array(
0,
0,
);
if (!empty($socialcalc['sheet']['cells'])) {
foreach ($socialcalc['sheet']['cells'] as $coord => $cell) {
$matches = array();
if (isset($cell['datavalue']) && $cell['datatype'] == 't') {
if (preg_match('/^\\${(.*?)\\}$/', $cell['datavalue'], $matches)) {
$field_name = $this
->find_field($matches[1]);
if ($field_name && !$this->view->field[$field_name]->options['exclude']) {
$fields[$field_name] = array(
'coord' => $coord,
'cell' => $cell,
'info' => $this->view->field[$field_name],
);
}
else {
$formula_expansions[] = array(
'coord' => $coord,
'cell' => $cell,
'expression' => $matches[1],
);
}
}
else {
if (preg_match('/^\\$\\[(.*?)]$/', $cell['datavalue'], $matches)) {
$formulas[] = array(
'coord' => $coord,
'cell' => $cell,
'expression' => $matches[1],
);
}
}
}
$lastpos = array(
max($lastpos[0], $cell['pos'][0]),
max($lastpos[1], $cell['pos'][1]),
);
}
}
$tangent_increment = array(
SHEETNODE_EXPANSION_VERTICAL => 0,
SHEETNODE_EXPANSION_HORIZONTAL => 1,
);
if (!empty($fields)) {
foreach ($fields as $field_name => $field) {
unset($socialcalc['sheet']['cells'][$field['coord']]);
$newcell = $field['cell'];
$pos = $maxpos = $newcell['pos'];
$this->view->row_index = 0;
foreach ($this->view->result as $j => $result) {
$this->view->row_index = $j;
$value = $field['info']
->theme($result);
if (is_array($value)) {
$itempos = $pos;
foreach ($value as $item) {
$itemcell = $newcell;
$itemcell['pos'] = $itempos;
$itemcell['datavalue'] = $item;
$itemcell['datatype'] = is_numeric($item) ? 'v' : 't';
$itemcell['valuetype'] = is_numeric($item) ? 'n' : 'th';
$newcells[socialcalc_cr_to_coord($itempos[0], $itempos[1])] = $itemcell;
$itempos[$tangent]++;
$maxpos[$tangent] = max($maxpos[$tangent], $itempos[$tangent]);
}
}
else {
$newcell['pos'] = $pos;
$newcell['datavalue'] = $value;
$newcell['datatype'] = is_numeric($value) ? 'v' : 't';
$newcell['valuetype'] = is_numeric($value) ? 'n' : 'th';
$newcells[socialcalc_cr_to_coord($pos[0], $pos[1])] = $newcell;
}
$pos[$normal]++;
}
$maxpos[$normal] = $pos[$normal];
$fields[$field_name]['endpos'] = array(
$maxpos[0] - $tangent_increment[$tangent],
$maxpos[1] - $tangent_increment[$normal],
);
$lastpos = array(
max($lastpos[0], $maxpos[0]),
max($lastpos[1], $maxpos[1]),
);
}
}
if (!empty($formula_expansions) && !empty($fields)) {
foreach ($formula_expansions as $fe) {
unset($socialcalc['sheet']['cells'][$fe['coord']]);
$newcell = $fe['cell'];
$pos = $newcell['pos'];
$matches = array();
$count = preg_match_all('/@(.*?)@|\\$(.*?)\\$/', $fe['expression'], $matches);
$this->view->row_index = 0;
foreach ($this->view->result as $j => $result) {
$this->view->row_index = $j;
$expression = $fe['expression'];
$newcell['pos'] = $pos;
for ($i = 0; $i < $count; $i++) {
$replace = NULL;
$match = !empty($matches[1][$i]) ? $matches[1][$i] : $matches[2][$i];
$field_name = $this
->find_field($match);
if (!$field_name) {
if (preg_match('/^\\$?\\w\\w?\\$?\\d+$/', $match)) {
$refpos = socialcalc_coord_to_cr($match);
foreach ($fields as $field) {
if ($refpos == $field['cell']['pos']) {
$replace = socialcalc_cr_to_coord($field['cell']['pos'][0] + $j * $tangent_increment[$tangent], $field['cell']['pos'][1] + $j * $tangent_increment[$normal]);
break;
}
}
}
}
else {
if ($matches[0][$i][0] == '@' && isset($fields[$field_name])) {
$replace = socialcalc_cr_to_coord($fields[$field_name]['cell']['pos'][0] + $j * $tangent_increment[$tangent], $fields[$field_name]['cell']['pos'][1] + $j * $tangent_increment[$normal]);
}
else {
if ($matches[0][$i][0] == '$') {
if (!isset($result->{$this->view->field[$field_name]->field_alias})) {
$replace = NULL;
}
else {
$replace = $result->{$this->view->field[$field_name]->field_alias};
}
}
}
}
if (!is_null($replace)) {
$expression = str_replace($matches[0][$i], $replace, $expression);
}
else {
$expression = NULL;
}
}
if (!is_null($expression)) {
$newcell['formula'] = $expression;
$newcell['datatype'] = 'f';
$newcell['valuetype'] = 'n';
$newcell['datavalue'] = 0;
$newcells[socialcalc_cr_to_coord($pos[0], $pos[1])] = $newcell;
}
$pos[$normal]++;
}
$fields[] = array(
'cell' => $fe['cell'],
'coord' => $fe['coord'],
'endpos' => array(
$pos[0] - $tangent_increment[$tangent],
$pos[1] - $tangent_increment[$normal],
),
);
$lastpos = array(
max($lastpos[0], $pos[0]),
max($lastpos[1], $pos[1]),
);
}
}
if (!empty($formulas)) {
foreach ($formulas as $formula) {
$newcell = $formula['cell'];
$expression = $formula['expression'];
$matches = array();
$count = (int) preg_match_all('/@(.*?)@/', $expression, $matches);
for ($i = 0; $i < $count; $i++) {
$field_name = $this
->find_field($matches[1][$i]);
if (!$field_name || !isset($fields[$field_name])) {
if (preg_match('/^\\$?\\w\\w?\\$?\\d+$/', $matches[1][$i])) {
$replace = '@' . $matches[1][$i] . '@';
$references[$formula['coord']][] = socialcalc_coord_to_cr(str_replace('$', '', $matches[1][$i]));
}
else {
$replace = NULL;
}
}
else {
$field = $fields[$field_name];
$replace = socialcalc_cr_to_coord($field['cell']['pos'][0], $field['cell']['pos'][1]) . ':' . socialcalc_cr_to_coord($field['endpos'][0], $field['endpos'][1]);
}
if (!is_null($replace)) {
$expression = str_replace($matches[0][$i], $replace, $expression);
}
else {
$expression = NULL;
}
}
if (!is_null($expression)) {
$newcell['formula'] = $expression;
$newcell['datatype'] = 'f';
$newcell['valuetype'] = 'n';
$newcell['datavalue'] = 0;
$socialcalc['sheet']['cells'][$formula['coord']] = $newcell;
}
}
}
if (!empty($fields)) {
foreach ($socialcalc['sheet']['cells'] as $coord => $cell) {
$pos = $cell['pos'];
foreach ($fields as $field) {
if ($pos[$tangent] == $field['cell']['pos'][$tangent] && $pos[$normal] > $field['cell']['pos'][$normal]) {
$pos[$normal] += $field['endpos'][$normal] - $field['cell']['pos'][$normal];
unset($socialcalc['sheet']['cells'][$coord]);
$cell['pos'] = $pos;
$newcoord = socialcalc_cr_to_coord($pos[0], $pos[1]);
$socialcalc['sheet']['cells'][$newcoord] = $cell;
if (isset($references[$coord])) {
$references[$newcoord] = $references[$coord];
unset($references[$coord]);
}
$lastpos = array(
max($lastpos[0], $pos[0]),
max($lastpos[1], $pos[1]),
);
break;
}
}
}
}
if (!empty($fields) && !empty($references)) {
foreach ($references as $coord => $positions) {
$cell = $socialcalc['sheet']['cells'][$coord];
$expression = $cell['formula'];
$matches = array();
$count = (int) preg_match_all('/@\\$?\\w\\w?\\$?\\d+@/', $expression, $matches);
for ($i = 0; $i < $count; $i++) {
$pos = $positions[$i];
foreach ($fields as $field) {
if ($pos[$tangent] == $field['cell']['pos'][$tangent]) {
if ($pos[$normal] == $field['cell']['pos'][$normal]) {
$refrange = socialcalc_cr_to_coord($field['cell']['pos'][0], $field['cell']['pos'][1]) . ':' . socialcalc_cr_to_coord($field['endpos'][0], $field['endpos'][1]);
$expression = str_replace($matches[0][$i], $refrange, $expression);
break;
}
else {
if ($pos[$normal] > $field['cell']['pos'][$normal]) {
$pos[$normal] += $field['endpos'][$normal] - $field['cell']['pos'][$normal];
$refcoord = socialcalc_cr_to_coord($pos[0], $pos[1], TRUE);
$expression = preg_replace('/@(\\$?)\\w\\w?(\\$?)\\d+@/', '${1}' . $refcoord[0] . '${2}' . $refcoord[1], $expression, 1);
break;
}
}
}
}
}
$cell['formula'] = $expression;
$socialcalc['sheet']['cells'][$coord] = $cell;
}
}
if (!empty($newcells)) {
$socialcalc['sheet']['cells'] += $newcells;
}
$socialcalc['sheet']['attribs']['lastcol'] = $lastpos[0];
$socialcalc['sheet']['attribs']['lastrow'] = $lastpos[1];
$pos = $socialcalc['edit']['ecell']['pos'];
if (!empty($fields)) {
foreach ($fields as $field) {
if ($pos[$tangent] == $field['cell']['pos'][$tangent] && $pos[$normal] > $field['cell']['pos'][$normal]) {
$pos[$normal] += $field['endpos'][$normal] - $field['cell']['pos'][$normal];
$ecell_changed = TRUE;
break;
}
}
}
$socialcalc['edit']['ecell']['pos'] = $pos;
$socialcalc['edit']['ecell']['coord'] = socialcalc_cr_to_coord($pos[0], $pos[1]);
$panes = $tangent == SHEETNODE_EXPANSION_VERTICAL ? 'rowpanes' : 'colpanes';
foreach ($socialcalc['edit'][$panes] as $i => $pane) {
$delta = 0;
if (!empty($fields)) {
foreach ($fields as $field) {
if ($pane['last'] > $field['cell']['pos'][$normal]) {
$delta = max($delta, $field['endpos'][$normal] - $field['cell']['pos'][$normal]);
}
}
}
if (!empty($ecell_changed)) {
$socialcalc['edit'][$panes][$i]['first'] += $delta;
$socialcalc['edit'][$panes][$i]['last'] += $delta;
}
}
}
else {
$pos = array(
1,
1,
);
foreach ($this->view->field as $field => $info) {
if ($info->options['exclude']) {
continue;
}
$cell['pos'] = $pos;
$cell['datavalue'] = $info
->label();
$cell['datatype'] = 't';
$cell['valuetype'] = 't';
$sheet['cells'][socialcalc_cr_to_coord($pos[0], $pos[1])] = $cell;
$pos[$tangent]++;
}
$pos[$normal] = 2;
$this->view->row_index = 0;
foreach ($this->view->result as $j => $result) {
$this->view->row_index = $j;
$pos[$tangent] = 1;
foreach ($this->view->field as $field => $info) {
if ($info->options['exclude']) {
continue;
}
$value = $info
->theme($result);
if (is_array($value)) {
foreach ($value as $item) {
$cell['pos'] = $pos;
$cell['datavalue'] = $item;
$cell['datatype'] = is_numeric($item) ? 'v' : 't';
$cell['valuetype'] = is_numeric($item) ? 'n' : 'th';
$sheet['cells'][socialcalc_cr_to_coord($pos[0], $pos[1])] = $cell;
$pos[$tangent]++;
}
}
else {
$cell['pos'] = $pos;
$cell['datavalue'] = $value;
$cell['datatype'] = is_numeric($value) ? 'v' : 't';
$cell['valuetype'] = is_numeric($value) ? 'n' : 'th';
$sheet['cells'][socialcalc_cr_to_coord($pos[0], $pos[1])] = $cell;
$pos[$tangent]++;
}
}
$pos[$normal]++;
}
$sheet['attribs']['lastcol'] = $pos[0] - 1;
$sheet['attribs']['lastrow'] = $pos[1] - 1;
if ($tangent == SHEETNODE_EXPANSION_VERTICAL) {
$edit['rowpanes'] = array(
0 => array(
'first' => 1,
'last' => 1,
),
1 => array(
'first' => 2,
'last' => $sheet['attribs']['lastrow'],
),
);
$edit['colpanes'] = array(
0 => array(
'first' => 1,
'last' => $sheet['attribs']['lastcol'],
),
);
}
else {
$edit['colpanes'] = array(
0 => array(
'first' => 1,
'last' => 1,
),
1 => array(
'first' => 2,
'last' => $sheet['attribs']['lastcol'],
),
);
$edit['rowpanes'] = array(
0 => array(
'first' => 1,
'last' => $sheet['attribs']['lastrow'],
),
);
}
$edit['ecell'] = array(
'coord' => 'A1',
);
$socialcalc = array(
'sheet' => $sheet,
'edit' => $edit,
'audit' => socialcalc_default_audit($sheet),
);
}
unset($this->view->row_index);
return $socialcalc;
}
function render_ajax($value, $save_element) {
$sheet_id = 'sheetview-' . str_replace('_', '-', $this->view->name);
if (!empty($this->view->live_preview)) {
$sheet_id .= '-preview';
}
$settings = drupal_to_js(array(
'sheetnode' => array(
$sheet_id => array(
'aliases' => array(
$this->view->name,
),
'value' => $value,
'imagePrefix' => base_path() . drupal_get_path('module', 'sheetnode') . '/socialcalc/images/sc-',
'containerElement' => $sheet_id,
'saveElement' => $save_element,
'viewMode' => variable_get('sheetnode_view_mode', SHEETNODE_VIEW_FIDDLE),
'showToolbar' => variable_get('sheetnode_view_toolbar', FALSE),
'permissions' => array(
'edit sheetnode settings' => user_access('edit sheetnode settings'),
),
'context' => array(
'entity-type' => 'view',
'oid' => $this->view->name,
),
),
),
));
return <<<EOS
<div class="sheetview" id="{<span class="php-variable">$sheet_id</span>}">
<script language="javascript" type="text/javascript">
Drupal.behaviors.sheetview = function(context) {
jQuery.extend(Drupal.settings, {<span class="php-variable">$settings</span>});
Drupal.behaviors.sheetnode(context);
}
</script>
</div>
EOS;
}
function render() {
if (!empty($this->view->live_preview)) {
return $this
->render_ajax(socialcalc_save($this
->render_sheet()), NULL);
}
$path = drupal_get_path('module', 'sheetnode');
drupal_add_js($path . '/socialcalc/socialcalcconstants.js');
drupal_add_js($path . '/socialcalc/socialcalc-3.js');
drupal_add_js($path . '/socialcalc/socialcalctableeditor.js');
drupal_add_js($path . '/socialcalc/formatnumber2.js');
drupal_add_js($path . '/socialcalc/formula1.js');
drupal_add_js($path . '/socialcalc/socialcalcpopup.js');
drupal_add_js($path . '/socialcalc/socialcalcspreadsheetcontrol.js');
drupal_add_js($path . '/socialcalc/socialcalcviewer.js');
drupal_add_js($path . '/sheetnode.js');
drupal_add_css($path . '/socialcalc/socialcalc.css');
drupal_add_css($path . '/sheetnode.css');
$value = socialcalc_save($this
->render_sheet());
$context = array(
'entity-type' => 'view',
'oid' => $this->view->name,
);
module_invoke_all('sheetnode_plugins', $value, FALSE, $context);
$sheet_id = 'sheetview-' . str_replace('_', '-', $this->view->name);
drupal_add_js(array(
'sheetnode' => array(
$sheet_id => array(
'aliases' => array(
$this->view->name,
),
'value' => $value,
'imagePrefix' => base_path() . $path . '/socialcalc/images/sc-',
'containerElement' => $sheet_id,
'saveElement' => FALSE,
'viewMode' => variable_get('sheetnode_view_mode', SHEETNODE_VIEW_FIDDLE),
'showToolbar' => variable_get('sheetnode_view_toolbar', FALSE),
'permissions' => array(
'edit sheetnode settings' => user_access('edit sheetnode settings'),
),
'context' => $context,
),
),
), 'setting');
return '<div class="sheetview" id="' . $sheet_id . '"></div>';
}
function find_field($field_name) {
if (isset($this->view->field[$field_name])) {
return $field_name;
}
foreach ($this->view->field as $field => $info) {
if ($info->field_alias == $field_name) {
return $field;
}
if (strcasecmp($info
->label(), $field_name) == 0) {
return $field;
}
if (method_exists($info, 'ui_name') && strcasecmp($info
->ui_name(), $field_name) == 0) {
return $field;
}
}
return FALSE;
}
function attach_to($display_id, $path, $title) {
$url_options = array(
'html' => TRUE,
);
$input = $this->view
->get_exposed_input();
if ($input) {
$url_options['query'] = $input;
}
$image = theme('image', $this->feed_image);
$this->view->feed_icon .= l($image, $this->view
->get_url(NULL, $path), $url_options);
}
}
class sheetnode_feed_plugin_style extends sheetnode_plugin_style {
function option_definition() {
$options = parent::option_definition();
$options['inherit'] = array(
'default' => TRUE,
);
return $options;
}
function options_form(&$form, &$form_values) {
parent::options_form($form, $form_values);
$form['inherit'] = array(
'#type' => 'checkbox',
'#title' => t('Inherit spreadsheet settings'),
'#default_value' => $this->options['inherit'],
'#description' => t('If this feed is attached to a display that has a Spreadsheet style,
this option lets you inherit the settings of that style, including the spreadsheet formatting.
The settings below are then ignored.'),
);
}
function inherit_options() {
if ($this->options['inherit']) {
$displays = array_filter($this->display->handler
->get_option('displays'));
if (!empty($displays)) {
foreach ($displays as $display) {
$handler = $this->view->display[$display]->handler;
if ($handler
->get_option('style_plugin') == 'sheet') {
$options = $handler
->get_option('style_options');
break;
}
}
}
if (isset($options)) {
foreach ($options as $key => $option) {
$this->options[$key] = $option;
}
}
}
}
}