Home | Gifts

TWiki Sheet Plugin

screenshot.png
Add TWiki Sheet spreadsheet functionality to TWiki tables

Introduction

The TWiki Sheet Plugin turns a TWiki table into a spreadsheet that can be modified right in the browser. TWiki Sheet looks and feels like Excel and Google Sheets. Functions with a familiar syntax such as =SUM(A2:A6) can be added to cells. A cell range can be selected, copied and pasted between TWiki Sheet, Excel and Google Sheets. Changes to cells are saved automatically in the background, e.g. there is no explicit save step. Concurrent editing can be enabled, e.g. changes by others will magically show up in TWiki Sheet.

Related Plugins

1. The pre-installed SpreadSheetPlugin also offers spreadsheet functions for TWiki tables, such as %CALC{$SUM($ABOVE())}%. The syntax is TWiki specific, and the function are evaluated server side. We recommend using the SpreadSheetPlugin for TWiki applications that deliver ready-made tables.

2. The pre-installed EditTablePlugin offers table editing for TWiki tables using a conventional edit, modify, save sequence. This is done with HTML forms, e.g. has the look and feel of a web application. The EditTablePlugin does not offer spreadsheet functions, however, it is possible to add SpreadSheetPlugin functions.

We recommend using TWiki Sheet if you want to easily work with spreadsheets in TWiki, as you would in Excel and Google Sheets.

Description

%TWIKISHEET{ }%
|  | 16Q1 | 16Q2 | 16Q3 |
| East: | 163 | 182 | 208 |
| Center: | 82 | 97 | 126 |
| West: | 217 | 231 | 294 |
| Total: | =SUM(B2:B4) | =SUM(C2:C4) | =SUM(D2:D4) |
Add %TWIKISHEET{ }% before a TWiki table to turn that table into a TWiki Sheet, as can be seen to the right. Multiple tables in a TWiki topic can be changed into TWiki Sheets.

A TWiki Sheet can be in edit mode by default (as in Excel or Google Sheets), or can be set to toggle between view mode and edit mode.

The user interface of TWiki Sheet is done client side with a Javascript widget; for details see the technical detail section below.

TWIKISHEET Variable

The %TWIKISHEET{ }% variable supports the following parameters:

Parameter Description Example Default
mode Mode of operation:
mode="classic" - regular TWiki table and an edit button; once pressed, the table switches into spreadsheet edit mode.
mode="toggle" - spreadsheet in read-only mode and an edit button; once pressed, the table switches into spreadsheet edit mode.
mode="toggle-edit" - like "toggle" but initial state is spreadsheet edit mode.
mode="edit" - the table is always in spreadsheet edit mode.
mode="edit" {Plugins}
{TWikiSheetPlugin}
{Mode}

configure
setting ("toggle")
concurrent Concurrent editing. If enabled, multiple people can edit TWiki Sheet and see each other's changes. This includes editing cells, pasting a range of cells, drag-filling cells, adding/removing rows, adding/removing columns, and undo.
concurrent="0" - disable concurrent editing; changes by others will be shown after a page reload.
concurrent="1" - enable concurrent editing; changes by others will show up while editing TWiki Sheet.
concurrent="1" {Plugins}
{TWikiSheetPlugin}
{ConcurrentEdit}

configure
setting ("0")
save Optionally disable save; useful to demo the plugin save="0" save="1"
width Table width in pixels; a horizontal scrollbar is added if needed width="500" (full window width)
height Table height in pixels; a vertical scrollbar is added if needed height="300" (full table height)
colHeaders Set to "false" to disable the default column headers (A, B, C); set to a function for custom headers, such as:
colHeaders="function(index) { return String.fromCharCode(65+index) + ' (' + (index + 1) + ')'; }",
which will show headers A (1), B (2), C (3), ...
colHeaders="false" colHeaders="true"
rowHeaders Set to "false" to disable the default row headers (1, 2, 3) rowHeaders="false" rowHeaders="true"
contextMenu Defines the right-click context menu; set to "false" to disable; set to array of available strings, such as:
contextMenu="['row_above', 'row_below', 'col_left', 'col_right', 'remove_row', 'remove_col', '---------', 'undo', 'redo']"
contextMenu="false" contextMenu="true"
fixedRowsTop Fixed number of rows shown on top; takes effect if height parameter is used fixedRowsTop="1" (none)
fixedColumnsLeft Fixed number of columns shown on the left; takes effect if width parameter is used fixedColumnsLeft="2" (none)
formulas Formula support; set to "false" to disable spreadsheet calculations in table cells, such as: =SUM(A1:A8) formulas="false" formulas="true"
maxCols Maximum number of columns maxCols="10" (unlimited)
maxRows Maximum number of rows maxRows="1000" (unlimited)
minSpareCols When set to 1 (or more), automatically add a new column at the right if there are no more empty columns minSpareCols="1" minSpareCols="0"
minSpareRows When set to 1 (or more), automatically add a new row at the bottom if there are no more empty rows minSpareRows="1" minSpareRows="0"
wordWrap Word wrap; set to "false" to disable cell content wrapping if it does not fit in the fixed column width wordWrap="false" wordWrap="true"
more... Additional Handsontable options can be used. Notes on types of values:
• Number value: Specify the number, such as width="500"
• String value: Enclose the string in single quotes, such as preventOverflow="'horizontal'"
• Boolean value: Specify "true" or "false", such as manualRowResize="true"
• Array value: Specify the array, such as manualRowResize="[40, 50]"
• Object value: Specify the object, such as columnSorting="{ column: 2, sortOrder: true }"
• Function: Specify the JavaScript function, such as:
   cells="function( row, col, prop ) { var cp = {}; if( row===0 ) { cp.readOnly = true; } return cp; }"
     

TWiki Sheet Editing

Formulas

Formulas can be added to a cell. Start with an = equal sign and add a formula like you would in Excel or Google Sheets.

Many functions are available, see http://handsontable.github.io/ruleJS/

Formulas are considered alpha at this time; for example, cell ranges are not adjusted on copy & paste.

Modes of Operation

You can chose from these modes of operation, controlled by the mode="" parameter:

Notes:

Concurrent Editing

If enabled, multiple people can edit TWiki Sheet and see each other's changes. This includes editing cells, pasting a range of cells, drag-filling cells, adding/removing rows, adding/removing columns, and undo. Concurrent editing is enabled by the concurrent="" parameter:

Notes:

Examples

mode="edit"

Raw text:
%TWIKISHEET{ mode="edit" save="0" }%
|  | 16Q1 | 16Q2 | 16Q3 | 16Q4 |
| East: | 163 | 182 | 208 | 193 |
| Center: | 82 | 97 | 126 | 91 |
| West: | 217 | 231 | 294 | 249 |
| Total: | =SUM(B2:B4) | =SUM(C2:C4) | =SUM(D2:D4) | =SUM(E2:E4) |
| Min: | =MIN(B2:B4) | =MIN(C2:C4) | =MIN(D2:D4) | =MIN(E2:E4) |
| Max: | =MAX(B2:B4) | =MAX(C2:C4) | =MAX(D2:D4) | =MAX(E2:E4) |
   Renders as TWiki Sheet (if installed):

   Screenshot:
screenshot.png

mode="toggle"

Raw text:
%TWIKISHEET{ mode="toggle" save="0" }%
|  | 16Q1 | 16Q2 | 16Q3 | 16Q4 |
| East: | 163 | 182 | 208 | 193 |
| Center: | 82 | 97 | 126 | 91 |
| West: | 217 | 231 | 294 | 249 |
| Total: | =SUM(B2:B4) | =SUM(C2:C4) | =SUM(D2:D4) | =SUM(E2:E4) |
| Min: | =MIN(B2:B4) | =MIN(C2:C4) | =MIN(D2:D4) | =MIN(E2:E4) |
| Max: | =MAX(B2:B4) | =MAX(C2:C4) | =MAX(D2:D4) | =MAX(E2:E4) |
   Renders as TWiki Sheet (if installed):

   Screenshot:
screenshot-toggle.png

mode="classic"

Raw text:
%TWIKISHEET{ mode="classic" save="0" }%
|  | 16Q1 | 16Q2 | 16Q3 | 16Q4 |
| East: | 163 | 182 | 208 | 193 |
| Center: | 82 | 97 | 126 | 91 |
| West: | 217 | 231 | 294 | 249 |
| Names: | WEB | TOPIC | BULLET | WIKINAME |
| Variables: | %WEB% | %TOPIC% | %BULLET% | %WIKINAME% |
   Renders as TWiki table or TWiki Sheet (if installed):
  16Q1 16Q2 16Q3 16Q4
East: 163 182 208 193
Center: 82 97 126 91
West: 217 231 294 249
Names: WEB TOPIC BULLET WIKINAME
Variables: TWiki TWikiSheetPlugin TWikiGuest

   Screenshot:
screenshot-classic.png

Known Limitations

1. TWiki variables in TWiki Sheets are not expanded unless mode="classic" is used. That this, they are preserved as is, but that means they do not show the actual value.

2. Cell span and row span are not supported.

3. Formulas are considered alpha at this time. For example, cell ranges are not adjusted on copy & paste.

Technical Detail

This section is for developers who would like to learn how TWiki Sheets work.

Show details toggleopen.gif Hide details toggleclose.gif

The TWiki Sheet user interface is done client side using the Handsontable JavaScript widget. Handsontable is a data grid component with an Excel-like appearance that integrates with any data source and comes with many features like data validation, sorting, grouping, data binding or column ordering.

The Handsontable is initialized with an array-of-an-array object representing the table, such as:

var tableData = [
  ["", "Maserati", "Mazda", "Mercedes", "Mini"],
  [2013, 2941, 4303, 3154, 2814],
  [2014, 2905, 2867, 4128, 2195],
  [2015, 2517, 4822, 3552, 3127],
  [2016, 2174, 3184, 3912, 2651]
];

A Handsontable is initialized as follows:

<script src="/common/handsontable/dist/handsontable.full.js"></script>
<link  href="/common/handsontable/dist/handsontable.full.css" rel="stylesheet" media="screen" />

<div id="tableContainer"></div>
<script>
  var container = $( '#tableContainer' );
  container.handsontable({
    data: tableData,
    rowHeaders: true,
    colHeaders: true,
    contextMenu: true,
    manualColumnResize: true,
    afterChange: function( changes, source ) {
      // save changes to server
    }
  });
</script>

A %TWIKISHEET{}% followed by a TWiki table is converted to an array-of-an-array JavaScript object, which is used to initialize a Handsontable table.

On each cell change, an Ajax POST is initiated that calls the REST API of the TWikiSheetPlugin on the TWiki server. Here is the code snippet that does the Ajax call:

  function twSheetAfterChange( n, changes, source ) {
    if( changes ) {
      var tws = twSheet[n];
      if( $authenticated && tws.save ) {
        var sendData = {
          action: 'change',
          webTopic: tws.webTopic,
          tableNumber: n,
          tableData: JSON.stringify(tws.data),
          changes: JSON.stringify(changes)
        }
        var jqxhr = $.ajax({
          url: '/do/rest/TWikiSheetPlugin/save',
          method: 'POST',
          data: sendData
        })
        .done(function( result ) {
          console.log( '- save ok: ' + JSON.stringify( result, null, ' ') );
        })
        .fail(function() {
          alert( 'TWiki Sheet Error: Failed to save changes' );
        });
      }
    }
  }

The rest/TWikiSheetPlugin/save API takes the table data, and updates the TWiki topic if the user has permission.

Note to plugin maintainer: The formula code has a bug that prevents the fill-handle from functioning properly. Patch to fix:

--- pub/TWiki/TWikiSheetPlugin/handsontable-ruleJS/src/handsontable.formula.js   (revision 30103)
+++ pub/TWiki/TWikiSheetPlugin/handsontable-ruleJS/src/handsontable.formula.js   (working copy)
@@ -130,7 +130,7 @@
           var cellId = instance.plugin.utils.translateCellCoords({row: row, col: col});
 
           // if changed value, all references cells should be recalculated
-          if (value[0] !== '=' || prevValue !== value) {
+          if (value && (value[0] !== '=' || prevValue !== value)) {
             instance.plugin.matrix.removeItem(cellId);
 
             // get referenced cells
@@ -155,13 +155,20 @@
       var instance = this;
 
       var r = index.row,
-        c = index.col,
-        value = data[r][c],
+        c = index.col;
+      if( !data || !data[r] || !data[r][c]) {
+        return {
+          value: value,
+          iterators: iterators
+        };
+      }
+
+      var value = data[r][c],
         delta = 0,
         rlength = data.length, // rows
         clength = data ? data[0].length : 0; //cols
 
-      if (value[0] === '=') { // formula
+      if (value && value[0] === '=') { // formula
 
         if (['down', 'up'].indexOf(direction) !== -1) {
           delta = rlength * iterators.row;

Plugin Installation & Configuration

You do not need to install anything on the browser to use this plugin. These instructions are for the administrator who installs the plugin on the TWiki server.

Show details toggleopen.gif Hide details toggleclose.gif

Plugin Info

Author: TWiki:Main.PeterThoeny, TWiki.org
Copyright: © 2016-2018 TWiki:Main.PeterThoeny
© 2016-2018 TWiki:TWiki.TWikiContributor
License: GPL (GNU General Public License)
Handsontable: Version: 0.24.1; License: MIT; Copyright: © 2012-2014 Marcin Warpechowskiy; © 2015 Handsoncode sp. z o.o., hello@handsoncode[dot]net
RuleJS: Version: 0.0.3; License: MIT; Copyright: © 2012-2014 Marcin Warpechowskiy, hello@handsoncode[dot]net
Jison: Version: 0.4.15; License: MIT; Copyright: © 2009-2014 Zachary Carter
formula.js: Version: 1.0.5; License: MIT & ASF; Copyright: © 2014 Sutoiku, Inc.
Version: 2018-07-15

Show Change History toggleopen.gif Hide Change History toggleclose.gif
2018-07-15: TWikibug:Item7845: Add missing screenshots to manifest; set default mode to classic
2018-07-06: TWikibug:Item7842: Update HTML table after edit session in classic mode; Item7841: Copyright update to 2018
2016-05-13: TWikibug:Item7737: Document JSON module dependency
2016-04-17: TWikibug:Item7737: The same user can now concurrent-edit the same TWiki Sheet in multiple windows
2016-04-14: TWikibug:Item7737: Concurrent editing support - see each others changes while editing TWiki Sheet
2016-04-09: TWikibug:Item7737: Aggregate changes to a moving time window of 500ms - this is to prevent saving changes out of sync due to network timing issues, which would cause corrupted tables; fix another formula.js bug to prevent a crash on undo
2016-04-06: TWikibug:Item7737: Support Handsontable options to customize the TWiki Sheet; fix VBAR and BR issue
2016-04-02: TWikibug:Item7737: Add modes of operation: "classic", "toggle", "toggle-edit" and "edit"; preserve TWiki variables; support TWiki Sheets in included topics; fix formula.js bug that prevented fill-handle from functioning properly
2016-03-30: TWikibug:Item7737: Protect VBAR and BR by converting them to vertical bar and \n
2016-03-29: TWikibug:Item7737: Initial version of TWikiSheetPlugin
TWiki Dependency: $TWiki::Plugins::VERSION 1.2
CPAN Dependencies: CPAN:JSON >= 2.0
Other Dependencies: JavaScript libraries: Handsontable (included); RuleJS (included); formula.js (included)
Perl Version: 5.008
Plugin Benchmark: GoodStyle nn%, FormattedSearch nn%, TWikiSheetPlugin nn%
Home: http://TWiki.org/cgi-bin/view/Plugins/TWikiSheetPlugin
Feedback: http://TWiki.org/cgi-bin/view/Plugins/TWikiSheetPluginDev
Appraisal: http://TWiki.org/cgi-bin/view/Plugins/TWikiSheetPluginAppraisal

Related: VarTWIKISHEET, TablePlugin, EditTablePlugin, SpreadSheetPlugin, TWikiPlugins

Revision r1 - 2018-07-16 - 01:42:02 - TWikiContributor Edit