4
Work in progress: changing the table based on form parameters
6
(sub handleTableChangeParams)
13
---++ Add rows from table 1
14
<form name="edittable1" action="%SCRIPTURL{"viewauth"}%/Sandbox/EditTablePluginTest2#edittable1" method="post">
15
<input type="hidden" name="ettablenr" value="1" />
16
Position: <input type="text" class="foswikiInputField" size="8" name="etaddrows_position" value="1" />
17
Count: <input type="text" class="foswikiInputField" size="8" name="etaddrows_count" value="5" />
18
<input type="submit" name="etsave" id="etsave" value="Add rows" class="foswikiSubmit" />
21
---++ Delete rows from table 1
22
<form name="edittable1" action="%SCRIPTURL{"viewauth"}%/Sandbox/EditTablePluginTest2#edittable1" method="post">
23
<input type="hidden" name="ettablenr" value="1" />
24
Position: <input type="text" class="foswikiInputField" size="8" name="etdeleterows_position" value="1" />
25
Count: <input type="text" class="foswikiInputField" size="8" name="etdeleterows_count" value="5" />
26
<input type="submit" name="etsave" id="etsave" value="Delete rows" class="foswikiSubmit" />
31
package Foswiki::Plugins::EditTablePlugin::Core;
38
use Foswiki::Plugins::EditTablePlugin::Data;
39
use Foswiki::Plugins::EditTablePlugin::EditTableData;
41
my $DEFAULT_FIELD_SIZE = 16;
42
my $PLACEHOLDER_BUTTONROW_TOP = 'PLACEHOLDER_BUTTONROW_TOP';
43
my $PLACEHOLDER_BUTTONROW_BOTTOM = 'PLACEHOLDER_BUTTONROW_BOTTOM';
44
my $PLACEHOLDER_SEPARATOR_SEARCH_RESULTS =
45
'PLACEHOLDER_SEPARATOR_SEARCH_RESULTS';
47
qr'var|ul|u|tt|tr|th|td|table|sup|sub|strong|strike|span|small|samp|s|pre|p|ol|li|kbd|ins|img|i|hr|h|font|em|div|dfn|del|code|cite|center|br|blockquote|big|b|address|acronym|abbr|a'o;
52
my $prefQUIET_SAVE_BUTTON;
53
my $prefADD_ROW_BUTTON;
54
my $prefDELETE_LAST_ROW_BUTTON;
55
my $prefCANCEL_BUTTON;
56
my $prefMESSAGE_INCLUDED_TOPIC_DOES_NOT_EXIST;
65
my $PATTERN_EDITTABLEPLUGIN =
66
$Foswiki::Plugins::EditTablePlugin::Data::PATTERN_EDITTABLEPLUGIN;
67
my $PATTERN_TABLEPLUGIN =
68
$Foswiki::Plugins::EditTablePlugin::Data::PATTERN_TABLEPLUGIN;
69
my $PATTERN_EDITCELL = qr'%EDITCELL{(.*?)}%'o;
70
my $PATTERN_TABLE_ROW_FULL = qr'^(\s*)\|.*\|\s*$'o;
71
my $PATTERN_TABLE_ROW = qr'^(\s*)\|(.*)'o;
72
my $PATTERN_SPREADSHEETPLUGIN_CALC = qr'%CALC(?:{(.*?)})?%'o;
73
my $SPREADSHEETPLUGIN_CALC_SUBSTITUTION = "<span class='editTableCalc'>CALC</span>";
78
SAVEQUIET => ( 1 << 4 ),
80
EDITNOTALLOWED => ( 1 << 6 ),
87
Initializes variables.
96
$prefCHANGEROWS = undef;
97
$prefEDIT_BUTTON = undef;
98
$prefSAVE_BUTTON = undef;
99
$prefQUIET_SAVE_BUTTON = undef;
100
$prefADD_ROW_BUTTON = undef;
101
$prefDELETE_LAST_ROW_BUTTON = undef;
102
$prefDELETE_LAST_ROW_BUTTON = undef;
103
$prefQUIETSAVE = undef;
106
$warningMessage = '';
109
getPreferencesValues();
114
Init variables again. If called from INCLUDE this is the first time we init
118
sub initIncludedTopic {
119
$preSp = '' unless $preSp;
120
getPreferencesValues();
125
StaticMethod parseTables($text, $topic, $web)
127
Read and parse table data once for each topic.
128
Stores data in hash $tableMatrix{webname}{topicname}.
129
Even if we are just viewing table data (not editing), we can deal with text inside edit tables in a special way. For instance by calling handleTmlInTables on the table text.
134
my ( $inText, $inTopic, $inWeb ) = @_;
138
return if defined $tableMatrix{$inWeb}{$inTopic};
140
my $tableData = Foswiki::Plugins::EditTablePlugin::Data->new();
141
$_[0] = $tableData->parseText($inText);
143
_debug("EditTablePlugin::Core::parseTables - after parseText, text=$_[0]");
145
$tableMatrix{$inWeb}{$inTopic} = $tableData;
150
---+++ process( $text, $topic, $web, $includingTopic, $includingWeb )
152
Called from commonTagsHandler. Pass over to processText in 'no Save' mode.
161
# my $includingTopic = $_[3]
162
# my $includingWeb = $_[4]
164
my $mode = $MODE->{READ};
166
processText( $mode, $saveTableNr, @_ );
171
---+++ processText( $mode, $saveTableNr, $text, $topic, $web, $includingTopic, $includingWeb )
173
Process the text line by line.
174
When a EditTablePlugin table is encountered, its contents is rendered according to the view:
175
* View mode - default
176
* Edit mode - when an Edit button is clicked, renders the rest of the table in edit mode
177
* Save mode - when called from a Save button: calls processText again, only renders the selected table number, then saves the topic text
182
my ( $inMode, $inSaveTableNr, $inText, $inTopic, $inWeb, $inIncludingTopic,
187
my $doSave = ( $mode & $MODE->{SAVE} ) ? 1 : 0;
188
$query = Foswiki::Func::getCgiQuery();
190
# Item1458 ignore all saving unless it happened using POST method.
192
if ( $query && $query->method() && uc( $query->method() ) ne 'POST' );
195
"EditTablePlugin::Core::processText( inSaveTableNr=$inSaveTableNr; inText=$inText\ninTopic=$inTopic\ninWeb=$inWeb\ninIncludingTopic=$inIncludingTopic\ninIncludingWeb=$inIncludingWeb\nmode="
196
. _modeToString($mode) );
197
_debug( "query params=" . Dumper( $query->{param} ) );
199
my $topic = $query->param('ettabletopic') || $inTopic;
200
my $web = $query->param('ettableweb') || $inWeb;
202
my $paramTableNr = $query->param('ettablenr') || 0;
205
my $topicText = $inText;
208
( $meta, $topicText ) = Foswiki::Func::readTopic( $web, $topic );
210
# fill the matrix with fresh new table
211
undef $tableMatrix{$web}{$topic};
212
parseTables( $topicText, $topic, $web );
215
parseTables( $inText, $topic, $web );
217
my $tableData = $tableMatrix{$web}{$topic};
219
# ========================================
220
# LOOP THROUGH TABLES
222
my $tableNr = 0; # current EditTable table
223
foreach my Foswiki::Plugins::EditTablePlugin::EditTableData $editTableData (
224
@{ $tableData->{editTableDataList} } )
227
if ($Foswiki::Plugins::EditTablePlugin::debug) {
229
_debug( "EditTablePlugin::Core::processText; editTableData="
230
. Dumper($editTableData) );
233
my $isEditingTable = 0;
234
my $editTableTag = $editTableData->{'tagline'};
236
# store processed lines of this tableText
237
# the list of lines will be put back into the topic text after processing
242
# ========================================
243
# START HANDLE EDITTABLE TAG
245
if ( $mode & $MODE->{READ} ) {
247
# process the tag contents
248
handleEditTableTag( $web, $topic, $editTableData->{'params'} );
250
# remove the original EDITTABLE{} in the tag pre_EDITTABLE{}_post
251
# so we just have pre__post
253
$editTableData->{'pretag'} . $editTableData->{'posttag'};
255
# expand macros in tagline without creating infinite recursion:
256
$editTableTag =~ s/%EDITTABLE{/%TMP_ETP_STUB_TAG{/o;
257
$editTableTag = Foswiki::Func::expandCommonVariables($editTableTag);
260
$editTableTag =~ s/TMP_ETP_STUB_TAG/EDITTABLE/o;
263
# END HANDLE EDITTABLE TAG
264
# ========================================
266
# ========================================
267
# START FOOTER AND HEADER ROW COUNT
269
( $editTableData->{headerRowCount}, $editTableData->{footerRowCount} ) =
270
getHeaderAndFooterCount($editTableTag);
273
"EditTablePlugin::Core::processText; headerRowCount=$editTableData->{headerRowCount}; footerRowCount=$editTableData->{footerRowCount}; tableText="
274
. join( "\n", @{ $editTableData->{'lines'} } ) );
276
# END FOOTER AND HEADER ROW COUNT
277
# ========================================
279
# ========================================
280
# START HANDLE TABLE CHANGE PARAMETERS
283
Foswiki::Plugins::EditTablePlugin::EditTableData::createTableChangesMap(
284
$query->param('ettablechanges') )
285
; # a mapping of rows and their changed state; keys are row numbers (starting with 0), values are row states: 0 (not changed), 1 (row to be added), -1 (row to be deleted); the map is created using the param 'ettablechanges'; the map is created and updated in EDIT mode, and used in SAVE mode.
287
my $tableStats = $editTableData->getTableStatistics($tableChanges);
289
if ( ( $mode & $MODE->{READ} ) || ( $tableNr == $inSaveTableNr ) ) {
291
my $allowedToEdit = 0;
293
if ( $tableNr == $inSaveTableNr ) {
295
handleTableChangeParams( $mode, $editTableData, $tableStats,
296
$tableChanges, $web, $topic );
297
$tableStats = $editTableData->getTableStatistics($tableChanges);
301
( $paramTableNr == $tableNr )
304
"$Foswiki::Plugins::EditTablePlugin::web.$Foswiki::Plugins::EditTablePlugin::topic"
310
# handle button actions
311
if ( $mode & $MODE->{READ} ) {
314
handleButtonActions( $mode, $editTableData, $tableStats,
315
$tableChanges, $web, $topic );
317
if ( ( $mode & $MODE->{SAVE} )
318
|| ( $mode & $MODE->{SAVEQUIET} ) )
320
return processText( $mode, $tableNr, $inText, $topic,
321
$web, $inIncludingTopic, $inIncludingWeb );
323
elsif ( $mode & $MODE->{CANCEL} ) {
324
return; # in case browser does not redirect
326
elsif ( $mode & $MODE->{EDITNOTALLOWED} ) {
331
} # if ( ( $mode & $MODE->{READ} ) || ( $tableNr == $inSaveTableNr ) )
333
# END HANDLE TABLE CHANGE PARAMETERS
334
# ========================================
336
# ========================================
339
my $doEdit = $isEditingTable ? 1 : 0;
341
if ( ( $mode & $MODE->{READ} ) ) {
342
my $tableStart = handleTableStart( $web, $topic, $inIncludingWeb,
343
$inIncludingTopic, $tableNr, $doEdit );
344
push( @result, $tableStart );
348
# ========================================
350
# ========================================
351
# START PROCESSING ROWS
353
if ($isEditingTable) {
354
( my $processedTableData, $tableChanges ) =
355
processTableData( $tableNr, $editTableData, $tableChanges,
356
$doEdit, $doSave, $web, $topic );
357
push( @result, @{$processedTableData} );
361
my $lines = $editTableData->{'lines'};
363
# render the row: EDITCELL and format tokens
367
s/$PATTERN_TABLE_ROW/handleTableRow( $1, $2, $tableNr, $isNewRow, $rowNr++, $doEdit, $doSave, $web, $topic )/eo;
369
@{$lines} = map { $_ .= "\n" } @{$lines};
370
handleTmlInTables($lines) if !$doSave;
372
push( @result, @{$lines} );
375
# END PROCESSING ROWS
376
# ========================================
378
# ========================================
379
# START PUT PROCESSED TABLE BACK IN TEXT
381
my $resultText = join( "", @result );
383
# We do not want TablePlugin to sort the table we are editing
384
# So we use the special setting disableallsort which is added
385
# to TABLE for exactly this purpose. Please do not remove this
389
&& ( $editTableTag !~ /%TABLE{.*?disableallsort="on".*?}%/ ) )
391
$editTableTag =~ s/(%TABLE{.*?)(}%)/$1 disableallsort="on"$2/;
394
# The Data::parseText merges a TABLE and EDITTABLE to one line
395
# We split it again to make editing easier for the user
396
# If the two were originally one line - they now become two unless
397
# there was white space between them
398
$editTableTag =~ s/(%EDITTABLE{.*?}%)(%TABLE{.*?}%)/$1\n$2/;
399
$editTableTag =~ s/(%TABLE{.*?}%)(%EDITTABLE{.*?}%)/$1\n$2/;
401
$resultText = "$editTableTag\n$resultText";
403
# END PUT PROCESSED TABLE BACK IN TEXT
404
# ========================================
406
# ========================================
409
if ( ( $mode & $MODE->{READ} ) ) {
410
my $tableEnd = handleTableEnd(
411
$doEdit, $tableChanges,
412
$tableStats->{headerRowCount},
413
$tableStats->{footerRowCount}
415
$resultText .= $tableEnd;
417
chomp $resultText; # remove spurious newline at end
420
# ========================================
422
# ========================================
425
# button row at top or bottom
426
if ( ( $mode & $MODE->{READ} ) ) {
427
my $pos = $params{'buttonrow'} || 'bottom';
429
createButtonRow( $web, $topic, $inIncludingWeb, $inIncludingTopic,
431
if ( $pos eq 'top' ) {
432
$resultText =~ s/$PLACEHOLDER_BUTTONROW_BOTTOM//go; # remove
433
$resultText =~ s/$PLACEHOLDER_BUTTONROW_TOP/$buttonRow/go;
436
$resultText =~ s/$PLACEHOLDER_BUTTONROW_TOP//go; # remove
437
$resultText =~ s/$PLACEHOLDER_BUTTONROW_BOTTOM/$buttonRow/go;
441
# render variables (only in view mode)
442
$resultText = Foswiki::Func::expandCommonVariables($resultText)
443
if ( $mode & $MODE->{READ} );
445
_debug("After parsing, resultText=$resultText");
446
_debug("After parsing, tableNr=$tableNr");
448
$topicText =~ s/<!--%EDITTABLESTUB\{$tableNr\}%-->/$resultText/g;
451
# ========================================
455
# ========================================
460
Foswiki::Func::saveTopic( $web, $topic, $meta, $topicText,
461
{ dontlog => ( $mode & $MODE->{SAVEQUIET} ) } );
463
Foswiki::Func::setTopicEditLock( $web, $topic, 0 ); # unlock Topic
464
my $url = Foswiki::Func::getViewUrl( $web, $topic );
465
$url .= "#edittable$inSaveTableNr";
468
Foswiki::Func::getOopsUrl( $web, $topic, 'oopssaveerr', $error );
470
Foswiki::Func::redirectCgiQuery( $query, $url );
475
# ========================================
480
_debug("After parsing, topic text=$_[2]");
485
NOT FULLY IMPLEMENTED YET
487
Change table by means of parameters:
489
* param etaddrows_position
490
* param etaddrows_count
492
* param etdeleterows_position
493
* param etdeleterows_count
496
* addRows: existing rows need to shift down
497
* deleteRows: check limit start and end of table
499
* write documentation
503
sub handleTableChangeParams {
504
my ( $inMode, $inEditTableData, $inTableStats, $inTableChanges, $inWeb,
508
return $inMode; # until fully implemented
512
my $position = $query->param('etaddrows_position');
513
my $count = $query->param('etaddrows_count');
514
if ( defined $position && defined $count ) {
515
if ( !doEnableEdit( $inWeb, $inTopic, 1 ) ) {
516
my $mode = $MODE->{EDITNOTALLOWED};
519
addRows( $inTableStats, $inTableChanges, $position, $count );
525
my $position = $query->param('etdeleterows_position');
526
my $count = $query->param('etdeleterows_count');
527
if ( defined $position && defined $count ) {
528
if ( !doEnableEdit( $inWeb, $inTopic, 1 ) ) {
529
my $mode = $MODE->{EDITNOTALLOWED};
532
deleteRows( $inTableStats, $inTableChanges, $position, $count );
539
StaticMethod handleButtonActions( $mode, $editTableData, $tableStats, $tableChanges, $web, $topic ) -> $mode
541
Handles button interaction; for each state updates the $mode to a value of $MODE.
545
sub handleButtonActions {
546
my ( $inMode, $inEditTableData, $inTableStats, $inTableChanges, $inWeb,
552
if ( $query->param('etcancel') ) {
554
# [Cancel] button pressed
555
doCancelEdit( $inWeb, $inTopic );
556
$mode = $MODE->{CANCEL};
561
if ( !doEnableEdit( $inWeb, $inTopic, 1 ) ) {
562
$mode = $MODE->{EDITNOTALLOWED};
567
if ( $query->param('etsave') ) {
569
# [Save table] button pressed
570
$mode = $MODE->{SAVE};
572
elsif ( $query->param('etqsave') ) {
574
# [Quiet save] button pressed
575
$mode = $MODE->{SAVE} | $MODE->{SAVEQUIET};
577
elsif ( $query->param('etaddrow') ) {
579
# [Add row] button pressed
581
$inTableStats->{rowCount} - $inTableStats->{footerRowCount} + 1;
582
addRows( $inTableStats, $inTableChanges, $rowNum, 1 );
584
elsif ( $query->param('etdelrow') ) {
586
# [Delete row] button pressed
588
$inTableStats->{rowCount} - $inTableStats->{footerRowCount};
589
deleteRows( $inTableStats, $inTableChanges, $rowNum, 1 );
591
elsif ( $query->param('etedit') ) {
593
# [Edit table] button pressed
602
StaticMethod addRows( $tableStats, $tableChanges, $position, $count )
604
Adds one or more rows.
609
my ( $inTableStats, $inTableChanges, $inPosition, $inCount ) = @_;
611
my $row = $inPosition;
613
_debug("ADD ROW:$row");
615
while ( $inCount-- ) {
617
# if we have set a row to 'delete' before, reset it to 2 first; otherwise set an existing row to 'add'
618
$inTableChanges->{$row} =
619
( defined $inTableChanges->{$row} && $inTableChanges->{$row} == -1 )
628
StaticMethod deleteRows( $tableStats, $tableChanges, $position, $count )
630
Deletes one or more rows.
635
my ( $inTableStats, $inTableChanges, $inPosition, $inCount ) = @_;
637
my $row = $inPosition;
639
# run backwards in rows to see which row has not been deleted yet
642
if ( !defined $inTableChanges->{$row}
643
|| $inTableChanges->{$row} != -1 );
647
while ( $inCount-- ) {
649
# if we have set a row to 'add' before, reset it to 0 first; otherwise set an existing row to 'delete'
650
$inTableChanges->{$row} =
651
( defined $inTableChanges->{$row} && $inTableChanges->{$row} == 1 )
660
StaticMethod processTableData( $tableNr, $editTableData, $tableChanges, $doEdit, $doSave, $web, $topic ) -> (\@processedText, \%tableChanges)
664
sub processTableData {
665
my ( $inTableNr, $inEditTableData, $inTableChanges, $inDoEdit, $inDoSave,
670
my $tableChanges = $inTableChanges;
672
my $tableStats = $inEditTableData->getTableStatistics($tableChanges);
674
_debug( "EditTablePlugin::processTableData - inEditTableData at start="
675
. Dumper($inEditTableData) );
676
_debug( "EditTablePlugin::processTableData - tableStats at start="
677
. Dumper($tableStats) );
678
_debug( "EditTablePlugin::processTableData - tableChanges at start="
679
. Dumper($tableChanges) );
685
# ========================================
686
# START GET THE ROW TYPE
690
HEADER => ( 1 << 1 ),
691
FOOTER => ( 1 << 2 ),
695
$inEditTableData->{'rowCount'} -
696
$inEditTableData->{'footerRowCount'} -
697
$inEditTableData->{'headerRowCount'};
698
$bodyRowCount = 0 if $bodyRowCount < 0;
700
# map 'real' rows to types
701
# at this point we still use the order as used in the topic
702
# so the footer will be in the last rows
703
# we are not yet using the updated state of $tableStats
705
my @headerRowNums = ();
706
my @footerRowNums = ();
707
my @bodyRowNums = ();
710
for ( @{ $inEditTableData->{'lines'} } ) {
713
$tableChanges->{$rowNr} ||= 0;
715
if ( $rowNr <= $inEditTableData->{'headerRowCount'} ) {
717
push @headerRowNums, $rowNr if ( $tableChanges->{$rowNr} != -1 );
719
elsif ( ( $rowNr > $inEditTableData->{'headerRowCount'} )
721
$rowNr <= $inEditTableData->{'headerRowCount'} + $bodyRowCount )
724
push @bodyRowNums, $rowNr if ( $tableChanges->{$rowNr} != -1 );
727
push @footerRowNums, $rowNr;
731
# END GET THE ROW TYPE
732
# ========================================
734
# ========================================
735
# START RENDER CURRENT ROWS
737
foreach my $rowNr (@headerRowNums) {
740
&& defined $inTableChanges->{$rowNr}
741
&& $inTableChanges->{$rowNr} == -1 );
742
local $_ = $inEditTableData->{'lines'}->[ $rowNr - 1 ];
747
# a header row will not be edited, so do not render table row with handleTableRow
749
_handleSpreadsheetFormula($_);
750
_debug("RENDER ROW: HEADER:rowNr=$rowNr; id=$rowId=$_");
751
push( @headerRows, $_ );
754
foreach my $rowNr (@footerRowNums) {
757
&& defined $inTableChanges->{$rowNr}
758
&& $inTableChanges->{$rowNr} == -1 );
759
local $_ = $inEditTableData->{'lines'}->[ $rowNr - 1 ];
764
# a footer row will not be edited, so do not render table row with handleTableRow
766
_handleSpreadsheetFormula($_);
767
_debug("RENDER ROW: FOOTER:rowNr=$rowNr; id=$rowId=$_");
768
push( @footerRows, $_ );
771
foreach my $rowNr (@bodyRowNums) {
774
&& defined $inTableChanges->{$rowNr}
775
&& $inTableChanges->{$rowNr} == -1 );
776
local $_ = $inEditTableData->{'lines'}->[ $rowNr - 1 ];
778
my $rowId = @headerRows + scalar @bodyRows + 1;
780
if ( $tableChanges->{$rowId} && $tableChanges->{$rowId} == 2 ) {
782
$tableChanges->{$rowId} = 0;
785
s/$PATTERN_TABLE_ROW/handleTableRow( $1, $2, $inTableNr, $isNewRow, $rowId, $inDoEdit, $inDoSave, $inWeb, $inTopic )/eo;
787
_debug("RENDER ROW: BODY:rowNr=$rowNr; id=$rowId=$_");
788
push( @bodyRows, $_ );
791
# END RENDER CURRENT ROWS
792
# ========================================
794
# ========================================
797
if ( !$query->param('etdelrow') ) {
799
# START ADDING HEADER ROWS
801
# add a header row if there is none,
802
# but only if there are no body rows yet
803
# (and not when a row has just been deleted)
805
if ( !( scalar @bodyRows ) ) {
807
my $headerRows = $query->param('etheaderrows') || 0;
808
if ( $headerRows > scalar @headerRows ) {
809
my $rowNr = scalar @headerRows;
810
my $newHeaderRow = $headerRows - @headerRows;
811
while ( $newHeaderRow-- ) {
816
scalar @bodyRows + 1;
818
if ( $tableChanges->{$rowId} == -1
819
|| $tableChanges->{$rowId} == 2 );
821
$tableChanges->{$rowId} = 1; # store the new row
823
my $newRow = handleTableRow(
824
'', '', $inTableNr, $isNewRow,
825
$rowId, $inDoEdit, $inDoSave, $inWeb,
828
push @headerRows, $newRow;
832
# to start with table editing right away, add a minimum of 1 body row
833
if ( !$tableStats->{bodyRowCount} ) {
839
scalar @bodyRows + 1;
840
$tableChanges->{$rowId} ||= 0;
843
$tableChanges->{$rowId} == -1
844
|| $tableChanges->{$rowId} == 2
848
$tableChanges->{$rowId} = 1; # store the new row
850
my $newRow = handleTableRow(
851
'', '', $inTableNr, $isNewRow,
852
$rowId, $inDoEdit, $inDoSave, $inWeb,
855
push @bodyRows, $newRow;
860
$tableStats = $inEditTableData->getTableStatistics($tableChanges);
863
# END ADDING HEADER ROWS
865
# START ADDING BODY ROWS
867
my $bodyRows = $tableStats->{bodyRowCount};
869
if ( $bodyRows > scalar @bodyRows ) {
872
scalar @headerRows + scalar @footerRows + scalar @bodyRows;
873
my $newBodyRow = $bodyRows - @bodyRows;
875
_debug("ADD ROW: BODY: number=$newBodyRow");
877
while ( $newBodyRow-- ) {
883
defined $inTableChanges->{$rowNr}
884
&& $inTableChanges->{$rowNr} == -1
886
|| ( defined $inTableChanges->{$rowNr}
887
&& $inTableChanges->{$rowNr} == 2 )
892
scalar @bodyRows + 1;
894
_debug("ADD ROW: BODY: rowId=$rowId");
897
; # otherwise values entered in new rows are not preserved when adding yet more rows
899
my $newRow = handleTableRow(
900
'', '', $inTableNr, $isNewRow,
901
$rowId, $inDoEdit, $inDoSave, $inWeb,
905
_debug("ADD ROW: BODY: newRow=$newRow");
908
push @bodyRows, $newRow;
912
$tableStats = $inEditTableData->getTableStatistics($tableChanges);
915
# END ADDING BODY ROWS
918
# ========================================
921
_debug( "EditTablePlugin::processTableData - tableChanges at end="
922
. Dumper($tableChanges) );
923
_debug( "EditTablePlugin::processTableData - tableStats at end="
924
. Dumper($tableStats) );
927
"EditTablePlugin::processTableData - headerRows=\n---------------------\n"
928
. Dumper(@headerRows) );
930
"EditTablePlugin::processTableData - bodyRows=\n---------------------\n"
931
. Dumper(@bodyRows) );
933
"EditTablePlugin::processTableData - footerRows=\n---------------------\n"
934
. Dumper(@footerRows) );
936
my @combinedRows = ( @headerRows, @bodyRows, @footerRows );
939
"EditTablePlugin::processTableData - combinedRows=\n---------------------\n"
940
. Dumper(@combinedRows) );
942
push( @result, @combinedRows );
944
@result = map { $_ .= "\n" } @result;
946
return ( \@result, $tableChanges );
951
StaticMethod getPreferencesValues()
953
Read preferences from plugin topic of preferences.
957
sub getPreferencesValues {
959
my $pluginName = $Foswiki::Plugins::EditTablePlugin::pluginName;
962
Foswiki::Func::getPreferencesValue("\U$pluginName\E_CHANGEROWS") || 'on';
965
Foswiki::Func::getPreferencesValue("\U$pluginName\E_QUIETSAVE") || 'on';
968
Foswiki::Func::getPreferencesValue("\U$pluginName\E_EDIT_BUTTON")
969
|| '%MAKETEXT{"Edit this table"}%, %ATTACHURL%/edittable.gif';
972
Foswiki::Func::getPreferencesValue("\U$pluginName\E_SAVE_BUTTON")
973
|| '%MAKETEXT{"Save table"}%';
975
$prefQUIET_SAVE_BUTTON =
976
Foswiki::Func::getPreferencesValue("\U$pluginName\E_QUIET_SAVE_BUTTON")
977
|| '%MAKETEXT{"Quiet save"}%';
979
$prefADD_ROW_BUTTON =
980
Foswiki::Func::getPreferencesValue("\U$pluginName\E_ADD_ROW_BUTTON")
981
|| '%MAKETEXT{"Add row"}%';
983
$prefDELETE_LAST_ROW_BUTTON = Foswiki::Func::getPreferencesValue(
984
"\U$pluginName\E_DELETE_LAST_ROW_BUTTON")
985
|| '%MAKETEXT{"Delete last row"}%';
988
Foswiki::Func::getPreferencesValue("\U$pluginName\E_CANCEL_BUTTON")
989
|| '%MAKETEXT{"Cancel"}%';
991
$prefMESSAGE_INCLUDED_TOPIC_DOES_NOT_EXIST =
992
Foswiki::Func::getPreferencesValue(
993
"\U$pluginName\E_INCLUDED_TOPIC_DOES_NOT_EXIST")
994
|| '<span class="foswikiAlert">%MAKETEXT{"Warning: \'include\' topic does not exist!"}%</span>';
999
StaticMethod extractParams( $arguments, \%params )
1004
my ( $inArguments, $inParams ) = @_;
1008
$tmp = Foswiki::Func::extractNameValuePair( $inArguments, 'header' );
1009
$$inParams{'header'} = $tmp if ($tmp);
1011
$tmp = Foswiki::Func::extractNameValuePair( $inArguments, 'footer' );
1012
$$inParams{'footer'} = $tmp if ($tmp);
1014
$tmp = Foswiki::Func::extractNameValuePair( $inArguments, 'headerislabel' );
1015
$$inParams{'headerislabel'} = $tmp if ($tmp);
1017
$tmp = Foswiki::Func::extractNameValuePair( $inArguments, 'format' );
1018
$tmp =~ s/^\s*\|*\s*//o;
1019
$tmp =~ s/\s*\|*\s*$//o;
1020
$$inParams{'format'} = $tmp if ($tmp);
1022
$tmp = Foswiki::Func::extractNameValuePair( $inArguments, 'changerows' );
1023
$$inParams{'changerows'} = $tmp if ($tmp);
1025
$tmp = Foswiki::Func::extractNameValuePair( $inArguments, 'quietsave' );
1026
$$inParams{'quietsave'} = $tmp if ($tmp);
1028
$tmp = Foswiki::Func::extractNameValuePair( $inArguments, 'helptopic' );
1029
$$inParams{'helptopic'} = $tmp if ($tmp);
1031
$tmp = Foswiki::Func::extractNameValuePair( $inArguments, 'editbutton' );
1032
$$inParams{'editbutton'} = $tmp if ($tmp);
1034
$tmp = Foswiki::Func::extractNameValuePair( $inArguments,
1035
'javascriptinterface' );
1036
$$inParams{'javascriptinterface'} = $tmp if ($tmp);
1038
$tmp = Foswiki::Func::extractNameValuePair( $inArguments, 'buttonrow' );
1039
$$inParams{'buttonrow'} = $tmp if ($tmp);
1047
my ( $theFormat, $inTopic, $inWeb, $doExpand ) = @_;
1049
$theFormat =~ s/\$nop(\(\))?//gos; # remove filler
1050
$theFormat =~ s/\$quot(\(\))?/\"/gos; # expand double quote
1051
$theFormat =~ s/\$percnt(\(\))?/\%/gos; # expand percent
1052
$theFormat =~ s/\$dollar(\(\))?/\$/gos; # expand dollar
1056
# expanded form to be able to use %-vars in format
1057
$theFormat =~ s/<nop>//gos;
1059
Foswiki::Func::expandCommonVariables( $theFormat, $inTopic, $inWeb );
1062
my @aFormat = split( /\s*\|\s*/, $theFormat );
1063
$aFormat[0] = "text,$DEFAULT_FIELD_SIZE" unless @aFormat;
1072
sub handleEditTableTag {
1073
my ( $inWeb, $inTopic, $inArguments ) = @_;
1078
'headerislabel' => "1",
1080
'changerows' => $prefCHANGEROWS,
1081
'quietsave' => $prefQUIETSAVE,
1084
'javascriptinterface' => '',
1087
$warningMessage = '';
1089
# include topic to read definitions
1090
my $iTopic = Foswiki::Func::extractNameValuePair( $inArguments, 'include' );
1092
( $inWeb, $iTopic ) =
1093
Foswiki::Func::normalizeWebTopicName( $inWeb, $iTopic );
1095
unless ( Foswiki::Func::topicExists( $inWeb, $iTopic ) ) {
1096
$warningMessage = $prefMESSAGE_INCLUDED_TOPIC_DOES_NOT_EXIST;
1100
my $text = Foswiki::Func::readTopicText( $inWeb, $iTopic );
1101
$text =~ /$PATTERN_EDITTABLEPLUGIN/os;
1104
if ( $inWeb ne $Foswiki::Plugins::EditTablePlugin::web
1105
|| $iTopic ne $Foswiki::Plugins::EditTablePlugin::topic )
1108
# expand common vars, unless oneself to prevent recursion
1110
Foswiki::Func::expandCommonVariables( $args, $iTopic,
1113
extractParams( $args, \%params );
1118
# We allow expansion of macros in the EDITTABLE arguments so one can
1119
# set a macro that defines the arguments
1121
Foswiki::Func::expandCommonVariables( $inArguments, $inTopic, $inWeb );
1123
extractParams( $arguments, \%params );
1125
# FIXME: should use Foswiki::Func::extractParameters
1126
$params{'header'} = '' if ( $params{header} =~ /^(off|no)$/oi );
1127
$params{'header'} =~ s/^\s*\|//o;
1128
$params{'header'} =~ s/\|\s*$//o;
1129
$params{'headerislabel'} = ''
1130
if ( $params{headerislabel} =~ /^(off|no)$/oi );
1131
$params{'footer'} = '' if ( $params{footer} =~ /^(off|no)$/oi );
1132
$params{'footer'} =~ s/^\s*\|//o;
1133
$params{'footer'} =~ s/\|\s*$//o;
1135
$params{'changerows'} = '' if ( $params{changerows} =~ /^(off|no)$/oi );
1136
$params{'quietsave'} = '' if ( $params{quietsave} =~ /^(off|no)$/oi );
1137
$params{'javascriptinterface'} = 'off'
1138
if ( $params{javascriptinterface} =~ /^(off|no)$/oi );
1140
@format = parseFormat( $params{format}, $inTopic, $inWeb, 0 );
1141
@formatExpanded = parseFormat( $params{format}, $inTopic, $inWeb, 1 );
1142
$nrCols = scalar @format;
1147
Creates the HTML for the start of the table.
1151
sub handleTableStart {
1152
my ( $inWeb, $inTopic, $inIncludingWeb, $inIncludingTopic, $theTableNr,
1157
require Foswiki::Contrib::JSCalendarContrib;
1159
Foswiki::Contrib::JSCalendarContrib::addHEAD('foswiki');
1163
my $viewUrl = Foswiki::Func::getScriptUrl( $inWeb, $inTopic, 'viewauth' )
1164
. "\#edittable$theTableNr";
1167
$text .= "$preSp<noautolink>\n" if $doEdit;
1168
$text .= "$preSp<a name=\"edittable$theTableNr\"></a>\n"
1169
if ( "$inWeb.$inTopic" eq "$inIncludingWeb.$inIncludingTopic" );
1170
my $cssClass = 'editTable';
1172
$cssClass .= ' editTableEdit';
1174
$text .= "<div class=\"" . $cssClass . "\">\n";
1175
my $formName = "edittable$theTableNr";
1176
$formName .= "\_$inIncludingWeb\_$inIncludingTopic"
1177
if ( "$inWeb\_$inTopic" ne "$inIncludingWeb\_$inIncludingTopic" );
1179
"$preSp<form name=\"$formName\" action=\"$viewUrl\" method=\"post\">\n";
1181
$text .= $PLACEHOLDER_BUTTONROW_TOP;
1183
$text .= hiddenField( $preSp, 'ettablenr', $theTableNr, "\n" );
1184
$text .= hiddenField( $preSp, 'etedit', 'on', "\n" );
1193
sub handleTableEnd {
1194
my ( $inDoEdit, $inTableChanges, $inHeaderRowCount, $inFooterRowCount ) =
1199
$text .= hiddenField(
1202
Foswiki::Plugins::EditTablePlugin::EditTableData::tableChangesMapToParamString(
1206
$text .= hiddenField( $preSp, 'etheaderrows', $inHeaderRowCount, "\n" );
1207
$text .= hiddenField( $preSp, 'etfooterrows', $inFooterRowCount, "\n" );
1209
$text .= $PLACEHOLDER_BUTTONROW_BOTTOM;
1211
$text .= "$preSp</form>\n";
1212
$text .= "</div><!-- /editTable -->";
1213
$text .= "</noautolink>" if $inDoEdit;
1223
my ( $inPrefix, $inName, $inValue, $inSuffix ) = @_;
1225
my $prefix = defined $inPrefix ? $inPrefix : '';
1226
my $suffix = defined $inSuffix ? $inSuffix : '';
1228
# Somehow this does not work at all:
1236
"$prefix<input type=\"hidden\" name=\"$inName\" value=\"$inValue\" />$suffix";
1243
sub createButtonRow {
1244
my ( $inWeb, $inTopic, $inIncludingWeb, $inIncludingTopic, $doEdit ) = @_;
1248
&& ( "$inWeb.$inTopic" eq "$inIncludingWeb.$inIncludingTopic" ) )
1253
"$preSp<input type=\"submit\" name=\"etsave\" id=\"etsave\" value=\"$prefSAVE_BUTTON\" class=\"foswikiSubmit\" />\n";
1254
if ( $params{'quietsave'} ) {
1256
"$preSp<input type=\"submit\" name=\"etqsave\" id=\"etqsave\" value=\"$prefQUIET_SAVE_BUTTON\" class=\"foswikiButton\" />\n";
1258
if ( $params{'changerows'} ) {
1260
"$preSp<input type=\"submit\" name=\"etaddrow\" id=\"etaddrow\" value=\"$prefADD_ROW_BUTTON\" class=\"foswikiButton\" />\n";
1262
"$preSp<input type=\"submit\" name=\"etdelrow\" id=\"etdelrow\" value=\"$prefDELETE_LAST_ROW_BUTTON\" class=\"foswikiButton\" />\n"
1263
unless ( $params{'changerows'} =~ /^add$/oi );
1266
"$preSp<input type=\"submit\" name=\"etcancel\" id=\"etcancel\" value=\"$prefCANCEL_BUTTON\" class=\"foswikiButtonCancel\" />\n";
1268
if ( $params{'helptopic'} ) {
1270
# read help topic and show below the table
1271
if ( $params{'helptopic'} =~ /^([^\.]+)\.(.*)$/o ) {
1273
$params{'helptopic'} = $2;
1276
Foswiki::Func::readTopicText( $inWeb, $params{'helptopic'} );
1278
#Strip out the meta data so it won't be displayed.
1279
$helpText =~ s/%META:[A-Za-z0-9]+{.*?}%//g;
1281
$helpText =~ s/.*?%STARTINCLUDE%//os;
1282
$helpText =~ s/%STOPINCLUDE%.*//os;
1287
# table specific script
1288
my $tableNr = $query->param('ettablenr');
1289
&Foswiki::Plugins::EditTablePlugin::addEditModeHeadersToHead( $tableNr,
1290
$params{'javascriptinterface'} );
1291
&Foswiki::Plugins::EditTablePlugin::addJavaScriptInterfaceDisabledToHead
1293
if ( $params{'javascriptinterface'} eq 'off' );
1294
&Foswiki::Plugins::EditTablePlugin::addJavaScriptInterfaceDisabledToHead
1296
if ( $params{'changerows'} eq '' );
1299
$params{editbutton} |= '';
1302
if ( $params{editbutton} eq "hide" ) {
1304
# do nothing, button assumed to be in a cell
1308
# Add edit button to end of table
1310
$preSp . viewEditCell("editbutton, 1, $params{'editbutton'}");
1320
sub parseEditCellFormat {
1321
$_[1] = Foswiki::Func::extractNameValuePair( $_[0] );
1332
my $attributes = Foswiki::Func::extractNameValuePair($inAttr);
1333
return '' unless ( $attributes =~ /^editbutton/ );
1335
$params{editbutton} = 'hide'
1336
unless ( $params{editbutton} ); # Hide below table edit button
1338
my @bits = split( /,\s*/, $attributes );
1340
$value = $bits[2] if ( @bits > 2 );
1342
$img = $bits[3] if ( @bits > 3 );
1345
$value = $prefEDIT_BUTTON || '';
1347
if ( $value =~ s/(.+),\s*(.+)/$1/o ) {
1349
$img =~ s|%ATTACHURL%|%PUBURL%/%SYSTEMWEB%/EditTablePlugin|o;
1350
$img =~ s|%WEB%|%SYSTEMWEB%|o;
1355
"<input class=\"editTableEditImageButton\" type=\"image\" src=\"$img\" alt=\"$value\" /> $warningMessage";
1359
"<input class=\"foswikiButton editTableEditButton\" type=\"submit\" value=\"$value\" /> $warningMessage";
1367
sub saveEditCellFormat {
1368
my ( $theFormat, $theName ) = @_;
1370
return '' unless ($theFormat);
1371
$theName =~ s/cell/format/;
1372
return hiddenField( '', $theName, $theFormat, '' );
1377
digestedCellValue: properly handle labels whose result may have been moved around by javascript, and therefore no longer correspond to the raw saved table text.
1382
my ( $inTableNr, $inRowNr, $inColumnNr, $inName, $inValue,
1383
$inDigestedCellValue, $inWeb, $inTopic )
1386
my $rawValue = $inValue;
1388
my $i = @format - 1;
1389
$i = $inColumnNr if ( $inColumnNr < $i );
1391
my @bits = split( /,\s*/, $format[$i] );
1392
my @bitsExpanded = split( /,\s*/, $formatExpanded[$i] );
1394
my $cellFormat = '';
1396
s/\s*$PATTERN_EDITCELL/&parseEditCellFormat( $1, $cellFormat )/eo;
1398
# If cell is empty we remove the space to not annoy the user when
1399
# he needs to add text to empty cell.
1400
$inValue = '' if ( $inValue eq ' ' );
1403
my @aFormat = parseFormat( $cellFormat, $inTopic, $inWeb, 0 );
1404
@bits = split( /,\s*/, $aFormat[0] );
1405
@aFormat = parseFormat( $cellFormat, $inTopic, $inWeb, 1 );
1406
@bitsExpanded = split( /,\s*/, $aFormat[0] );
1410
$type = $bits[0] if @bits > 0;
1412
# a table header is considered a label if read only header flag set
1414
if ( ( $params{'headerislabel'} ) && ( $inValue =~ /^\s*\*.*\*\s*$/ ) );
1415
$type = 'label' if ( $type eq 'editbutton' ); # Hide [Edit table] button
1417
$size = $bits[1] if @bits > 1;
1420
my $valExpanded = '';
1423
if ( $type eq 'select' ) {
1425
Foswiki::Func::expandCommonVariables( $inValue, $inTopic, $inWeb );
1426
$size = 1 if $size < 1;
1428
"<select class=\"foswikiSelect\" name=\"$inName\" size=\"$size\">";
1430
while ( $i < @bits ) {
1431
$val = $bits[$i] || '';
1432
$valExpanded = $bitsExpanded[$i] || '';
1433
$expandedValue =~ s/^\s+//;
1434
$expandedValue =~ s/\s+$//;
1435
$valExpanded =~ s/^\s+//;
1436
$valExpanded =~ s/\s+$//;
1438
if ( $valExpanded eq $expandedValue ) {
1439
$text .= " <option selected=\"selected\">$val</option>";
1442
$text .= " <option>$val</option>";
1446
$text .= "</select>";
1447
$text .= saveEditCellFormat( $cellFormat, $inName );
1450
elsif ( $type eq "radio" ) {
1452
&Foswiki::Func::expandCommonVariables( $inValue, $inTopic, $inWeb );
1453
$size = 1 if $size < 1;
1454
my $elements = ( @bits - 2 );
1455
my $lines = $elements / $size;
1456
$lines = ( $lines == int($lines) ) ? $lines : int( $lines + 1 );
1457
$text .= "<table class=\"editTableInnerTable\"><tr><td valign=\"top\">"
1460
while ( $i < @bits ) {
1461
$val = $bits[$i] || "";
1462
$valExpanded = $bitsExpanded[$i] || "";
1463
$expandedValue =~ s/^\s+//;
1464
$expandedValue =~ s/\s+$//;
1465
$valExpanded =~ s/^\s+//;
1466
$valExpanded =~ s/\s+$//;
1467
$text .= "<input type=\"radio\" name=\"$inName\" value=\"$val\"";
1469
# make space to expand variables
1470
$val = addSpaceToBothSides($val);
1471
$text .= " checked=\"checked\""
1472
if ( $valExpanded eq $expandedValue );
1476
if ( ( $i - 1 ) % $lines ) {
1479
elsif ( $i - 1 < $elements ) {
1480
$text .= "</td><td valign=\"top\">";
1485
$text .= "</td></tr></table>" if ( $lines > 1 );
1486
$text .= saveEditCellFormat( $cellFormat, $inName );
1489
elsif ( $type eq "checkbox" ) {
1491
&Foswiki::Func::expandCommonVariables( $inValue, $inTopic, $inWeb );
1492
$size = 1 if $size < 1;
1493
my $elements = ( @bits - 2 );
1494
my $lines = $elements / $size;
1495
my $names = "Chkbx:";
1496
$lines = ( $lines == int($lines) ) ? $lines : int( $lines + 1 );
1497
$text .= "<table class=\"editTableInnerTable\"><tr><td valign=\"top\">"
1501
while ( $i < @bits ) {
1502
$val = $bits[$i] || "";
1503
$valExpanded = $bitsExpanded[$i] || "";
1504
$expandedValue =~ s/^\s+//;
1505
$expandedValue =~ s/\s+$//;
1506
$valExpanded =~ s/^\s+//;
1507
$valExpanded =~ s/\s+$//;
1508
$names .= " ${inName}x$i";
1510
" <input type=\"checkbox\" name=\"${inName}x$i\" value=\"$val\"";
1512
$val = addSpaceToBothSides($val);
1514
$text .= " checked=\"checked\""
1515
if ( $expandedValue =~ /(^|\s*,\s*)\Q$valExpanded\E(\s*,\s*|$)/ );
1519
if ( ( $i - 1 ) % $lines ) {
1522
elsif ( $i - 1 < $elements ) {
1523
$text .= "</td><td valign=\"top\">";
1528
$text .= "</td></tr></table>" if ( $lines > 1 );
1529
$text .= hiddenField( $preSp, $inName, $names );
1530
$text .= saveEditCellFormat( $cellFormat, $inName, "\n" );
1533
elsif ( $type eq 'row' ) {
1534
$size = $size + $inRowNr;
1536
"<span class=\"et_rowlabel\">"
1537
. hiddenField( $size, $inName, $size )
1539
$text .= saveEditCellFormat( $cellFormat, $inName );
1541
elsif ( $type eq 'label' ) {
1543
# show label text as is, and add a hidden field with value
1545
$isHeader = 1 if ( $inValue =~ s/^\s*\*(.*)\*\s*$/$1/o );
1548
# Replace CALC in labels with the fixed string CALC to avoid errors
1550
_handleSpreadsheetFormula($text);
1552
# To optimize things, only in the case where a read-only column is
1553
# being processed (inside of this unless() statement) do we actually
1554
# go out and read the original topic. Thus the reason for the
1555
# following unless() so we only read the topic the first time through.
1557
unless ( defined $tableMatrix{$inWeb}{$inTopic}
1558
and $inDigestedCellValue )
1561
# To deal with the situation where Foswiki variables, like
1562
# %CALC%, have already been processed and end up getting saved
1563
# in the table that way (processed), we need to read in the
1564
# topic page in raw format
1565
my $topicContents = Foswiki::Func::readTopicText(
1566
$Foswiki::Plugins::EditTablePlugin::web,
1567
$Foswiki::Plugins::EditTablePlugin::topic
1569
parseTables( $topicContents, $inTopic, $inWeb );
1571
my $table = $tableMatrix{$inWeb}{$inTopic};
1573
$inDigestedCellValue
1574
? $table->getCell( $inTableNr, $inRowNr - 1, $inColumnNr )
1576
$inValue = $cell if ( defined $cell ); # original value from file
1577
Foswiki::Plugins::EditTablePlugin::encodeValue($inValue)
1578
unless ( $inValue eq '' );
1580
#$inValue = "\*$inValue\*" if ( $isHeader and $inDigestedCellValue );
1581
$text = "\*$text\*" if $isHeader;
1582
$text .= ' ' . hiddenField( $preSp, $inName, $inValue );
1584
elsif ( $type eq 'textarea' ) {
1585
my ( $rows, $cols ) = split( /x/, $size );
1587
$rows |= 3 if !defined $rows;
1588
$cols |= 30 if !defined $cols;
1589
Foswiki::Plugins::EditTablePlugin::encodeValue($inValue)
1590
unless ( $inValue eq '' );
1592
"<textarea class=\"foswikiTextarea editTableTextarea\" rows=\"$rows\" cols=\"$cols\" name=\"$inName\">$inValue</textarea>";
1593
$text .= saveEditCellFormat( $cellFormat, $inName );
1596
elsif ( $type eq 'date' ) {
1598
$ifFormat = $bits[3] if ( @bits > 3 );
1599
$ifFormat ||= $Foswiki::cfg{JSCalendarContrib}{format} || '%e %B %Y';
1600
$size = 10 if ( !$size || $size < 1 );
1601
Foswiki::Plugins::EditTablePlugin::encodeValue($inValue)
1602
unless ( $inValue eq '' );
1603
$text .= CGI::textfield(
1606
class => 'foswikiInputField editTableInput',
1607
id => 'id' . $inName,
1613
$text .= saveEditCellFormat( $cellFormat, $inName );
1614
eval 'use Foswiki::Contrib::JSCalendarContrib';
1617
$text .= '<span class="foswikiMakeVisible">';
1618
$text .= CGI::image_button(
1619
-class => 'editTableCalendarButton',
1620
-name => 'calendar',
1621
-onclick => "return showCalendar('id$inName','$ifFormat')",
1622
-src => Foswiki::Func::getPubUrlPath() . '/'
1623
. $Foswiki::cfg{SystemWebName}
1624
. '/JSCalendarContrib/img.gif',
1630
$query->{'jscalendar'} = 1;
1632
# prevent wrapping of button below input field
1633
$text = "<nobr>$text</nobr>";
1635
else { # if( $type eq 'text')
1636
$size = $DEFAULT_FIELD_SIZE if $size < 1;
1637
Foswiki::Plugins::EditTablePlugin::encodeValue($inValue)
1638
unless ( $inValue eq '' );
1640
"<input class=\"foswikiInputField editTableInput\" type=\"text\" name=\"$inName\" size=\"$size\" value=\"$inValue\" />";
1641
$text .= saveEditCellFormat( $cellFormat, $inName );
1644
if ( $type ne 'textarea' ) {
1646
s/ /<br \/>/go; # change unicode linebreak character to <br />
1655
sub handleTableRow {
1657
$thePre, $theRow, $theTableNr, $isNewRow, $theRowNr,
1658
$doEdit, $doSave, $inWeb, $inTopic
1661
_debug( "EditTablePlugin::Core::handleTableRow; params="
1662
. "\n\t thePre=$thePre."
1663
. "\n\t theRow=$theRow."
1664
. "\n\t theTableNr=$theTableNr"
1665
. "\n\t isNewRow=$isNewRow"
1666
. "\n\t theRowNr=$theRowNr"
1667
. "\n\t doEdit=$doEdit"
1668
. "\n\t doSave=$doSave"
1669
. "\n\t inWeb=$inWeb"
1670
. "\n\t inTopic=$inTopic" );
1673
my $text = "$thePre\|";
1676
$theRow =~ s/\|\s*$//o;
1678
# retrieve any params sent by javascript interface (see edittable.js)
1679
my $rowID = $query->param("etrow_id$theRowNr");
1680
$rowID = $theRowNr if !defined $rowID;
1683
my $isNewRowFromHeader = ( $theRowNr <= 1 ) && ( $params{'header'} );
1686
? split( /\|/, $params{'header'} )
1687
: split( /\|/, $theRow );
1689
$nrCols = $tmp if ( $tmp > $nrCols ); # expand number of cols
1691
my $cellFormat = '';
1694
my $cellDefined = 0;
1697
while ( $col < $nrCols ) {
1700
my $cellValueParam = "etcell${rowID}x$col";
1701
$val = $isNewRow ? undef : $query->param($cellValueParam);
1703
#my $tmpVal = defined $val ? $val : '';
1704
#_debug( "\t col=$col, cellValueParam=$cellValueParam; val=$tmpVal");
1706
if ( defined $val && $val =~ /^Chkbx: (etcell.*)/ ) {
1708
# Multiple checkboxes, val has format "Chkbx: etcell4x2x2 etcell4x2x3 ..."
1709
my $checkBoxNames = $1;
1710
my $checkBoxValues = "";
1711
foreach ( split( /\s/, $checkBoxNames ) ) {
1712
$val = $query->param($_);
1714
#$checkBoxValues .= "$val," if ( defined $val );
1715
if ( defined $val ) {
1717
# make space to expand variables
1718
$val = addSpaceToBothSides($val);
1719
$checkBoxValues .= $val . ',';
1722
$checkBoxValues =~ s/,\s*$//;
1723
$val = $checkBoxValues;
1726
# SMELL NOTE: etformat is not specified. What should it do?
1727
$cellFormat = $query->param("etformat${rowID}x$col");
1728
$val .= " %EDITCELL{$cellFormat}%" if ($cellFormat);
1730
if ( defined $val ) {
1732
# change any new line character sequences to <br />
1733
$val =~ s/[\n\r]{2,}?/<br \/>/gos;
1735
# escape "|" to HTML entity
1736
$val =~ s/\|/\&\#124;/gos;
1742
elsif ( $col <= @cells ) {
1744
$cell = $cells[ $col - 1 ];
1745
$digested = 1; # Flag that we are using non-raw cell text.
1746
$cellDefined = 1 if ( length($cell) > 0 );
1747
$cell =~ s/^\s*(.+?)\s*$/$1/o
1748
; # remove spaces around content, but do not void a cell with just spaces
1753
if ($isNewRowFromHeader) {
1756
if ( $params{'header'} =~ /^on$/i ) {
1757
if ( ( @format >= $col )
1758
&& ( $format[ $col - 1 ] =~ /(.*?)\,/ ) )
1762
$cell = 'text' unless $cell;
1766
my @hCells = split( /\|/, $params{'header'} );
1767
$cell = $hCells[ $col - 1 ] if ( @hCells >= $col );
1768
$cell = "*text*" unless $cell;
1771
$cell = addSpaceToBothSides($cell);
1775
$cell = addSpaceToBothSides($cell);
1777
# Item5217 Avoid that deleting content of cell creates unwanted span
1778
$cell = ' ' if $cell eq '';
1785
&& ( @format >= $col )
1786
&& ( $format[ $col - 1 ] =~
1787
/^\s*(.*?)\,\s*(.*?)\,\s*(.*?)\s*$/ )
1791
# default value of "| text, 20, a, b, c |" cell is "a, b, c"
1792
# default value of '| select, 1, a, b, c |' cell is "a"
1797
unless ( defined $cell && $cell ne '' )
1798
; # Proper handling of '0'
1800
if ( $val eq 'select' || $val eq 'date' );
1803
$cell = '' if $isNewRow;
1805
inputElement( $theTableNr, $theRowNr, $col - 1,
1806
"etcell${theRowNr}x$col", $cell, $digested, $inWeb,
1808
$element = " $element \|";
1815
# render EDITCELL in view mode
1816
$theRow =~ s/$PATTERN_EDITCELL/viewEditCell($1)/geo if !$doSave;
1821
# render final value in view mode (not edit or save)
1822
Foswiki::Plugins::EditTablePlugin::decodeFormatTokens($text)
1823
if ( !$doSave && !$doEdit );
1830
Add one space to both sides of the text to allow TML expansion.
1831
Convert multiple (existing) spaces to one space.
1835
sub addSpaceToBothSides {
1837
return $text if $text eq '';
1840
$text =~ s/^[[:space:]]+/ /; # remove extra spaces
1841
$text =~ s/[[:space:]]+$/ /;
1850
my ( $inWeb, $inTopic ) = @_;
1852
Foswiki::Func::setTopicEditLock( $inWeb, $inTopic, 0 );
1854
Foswiki::Func::redirectCgiQuery( $query,
1855
Foswiki::Func::getViewUrl( $inWeb, $inTopic ) );
1863
my ( $inWeb, $inTopic, $doCheckIfLocked ) = @_;
1865
my $wikiUserName = Foswiki::Func::getWikiName();
1867
!Foswiki::Func::checkAccessPermission(
1868
'change', $wikiUserName, undef, $inTopic, $inWeb
1873
# user has no permission to change the topic
1874
throw Foswiki::OopsException(
1877
def => 'topic_access',
1880
params => [ 'change', 'denied' ]
1884
my $breakLock = $query->param('breaklock') || '';
1885
unless ($breakLock) {
1886
my ( $oopsUrl, $lockUser ) =
1887
Foswiki::Func::checkTopicEditLock( $inWeb, $inTopic, 'view' );
1889
my $loginUser = Foswiki::Func::wikiToUserName($wikiUserName);
1890
if ( $lockUser ne $loginUser ) {
1892
# change the default oopsleaseconflict url
1893
# use viewauth instead of view
1894
$oopsUrl =~ s/param4=view/param4=viewauth/;
1896
# add info of the edited table
1898
$query = Foswiki::Func::getCgiQuery();
1899
$params .= ';ettablenr=' . $query->param('ettablenr');
1900
$params .= ';etedit=on';
1901
$oopsUrl =~ s/($|#\w*)/$params/;
1903
# warn user that other person is editing this topic
1904
Foswiki::Func::redirectCgiQuery( $query, $oopsUrl );
1910
# We are allowed to edit
1911
Foswiki::Func::setTopicEditLock( $inWeb, $inTopic, 1 );
1918
stripCommentsFromRegex($pattern) -> $pattern
1920
For debugging: removes all spaces and comments from a regular expression.
1924
sub stripCommentsFromRegex {
1927
( my $cleanRegex = $inRegex ) =~ s/\s*(.*?)\s*(#.*?)*(\r|\n|$)/$1/go;
1933
StaticMethod _handleSpreadsheetFormula( $text ) -> $text
1935
Replaces a SpreadSheetPlugin formula by a static text.
1939
sub _handleSpreadsheetFormula {
1942
$_[0] =~ s/$PATTERN_SPREADSHEETPLUGIN_CALC/$SPREADSHEETPLUGIN_CALC_SUBSTITUTION/go;
1948
StaticMethod handleTmlInTables( \@lines )
1950
Users using the plugin would be confused when they enter newlines,
1951
which get replaced with %BR%, and thus might not render their TML
1953
So we hack it here so that all TML and HTML tags have spaces around them:
1954
- adds spaces around %BR% to render TML around linebreaks
1955
- add spaces around TML next to HTML tags, again to render TML
1956
- expands variables, for example %CALC%
1957
Check Foswikibug:Item1017
1961
sub handleTmlInTables {
1965
map { $_ =~ s/(%BR%)/ $1 /gox; addSpacesToTmlNextToHtml($_) } @{ $_[0] };
1970
StaticMethod addSpacesToTmlNextToHtml( \$text )
1974
| *bold*<br />_italic_ |
1978
|*bold* <br /> _italic_|
1982
sub addSpacesToTmlNextToHtml {
1986
# also remove spaces at both sides to prevent extra spaces are added to the
1987
# cell, resulting in wrong alignment (when html tags are stripped in the
1988
# core table renderer)
1990
my $TMLpattern = qr/[_*=]*/o;
1992
[[:space:]]* # any space
1993
($TMLpattern) # i1: optional TML syntax before html tag
1995
</* # start of tag (optional closing tag)
1996
(?:$HTML_TAGS)+ # any of the html tags
1997
[[:space:]]* # any space
1998
.*? # anything before the end of tag
1999
/*> # end of tag (optional closing tag)
2001
($TMLpattern) # i3: optional TML syntax after html tag
2002
[[:space:]]* # any space
2005
$_[0] =~ s/$pattern/$1 $2 $3/go;
2010
StaticMethod getHeaderAndFooterCount( $text ) -> ($headerRowCount, $footerRowCount)
2012
Reads the headerrows and footerrows parameters from the TABLE macro (if any) and returns them as tuple.
2014
If no TABLE tag is present, returns (0,0).
2018
sub getHeaderAndFooterCount {
2023
# expand macros in tagline without creating infinite recursion,
2024
# so delete EDITTABLE as we won't need it here
2025
$tag =~ s/%EDITTABLE{/_DELETED_/o;
2026
$tag = Foswiki::Func::expandCommonVariables($tag);
2028
my $headerRowCount = 0;
2029
my $footerRowCount = 0;
2031
if ( $tag =~ m/$PATTERN_TABLEPLUGIN/ ) {
2033
# We want this info also when viewing, because the row count takes
2034
# header and footer rows into account
2035
# match with a TablePlugin line
2036
# works when TABLE tag is just above OR just below the EDITTABLE tag
2037
my %tablePluginParams = Foswiki::Func::extractParameters($1);
2038
$headerRowCount = $tablePluginParams{'headerrows'} || 0;
2039
$footerRowCount = $tablePluginParams{'footerrows'} || 0;
2041
return ( $headerRowCount, $footerRowCount );
2048
$text .= "; mode is READ" if ( $mode & $MODE->{READ} );
2049
$text .= "; mode is EDIT" if ( $mode & $MODE->{EDIT} );
2050
$text .= "; mode is SAVE" if ( $mode & $MODE->{SAVE} );
2051
$text .= "; mode is SAVEQUIET" if ( $mode & $MODE->{SAVEQUIET} );
2052
$text .= "; mode is CANCEL" if ( $mode & $MODE->{CANCEL} );
2053
$text .= "; mode is EDITNOTALLOWED" if ( $mode & $MODE->{EDITNOTALLOWED} );
2061
Foswiki::Func::writeDebug($inText);
2067
Foswiki::Func::writeDebug($inText)
2068
if $Foswiki::Plugins::EditTablePlugin::debug;
2074
# Plugin for Foswiki - The Free and Open Source Wiki, http://foswiki.org/
2076
# Copyright (C) 2008-2009 Arthur Clemens, arthur@visiblearea.com and Foswiki contributors
2077
# Copyright (C) 2002-2007 Peter Thoeny, peter@thoeny.org and
2078
# TWiki Contributors.
2080
# This program is free software; you can redistribute it and/or
2081
# modify it under the terms of the GNU General Public License
2082
# as published by the Free Software Foundation; either version 2
2083
# of the License, or (at your option) any later version. For
2084
# more details read LICENSE in the root of this distribution.
2086
# This program is distributed in the hope that it will be useful,
2087
# but WITHOUT ANY WARRANTY; without even the implied warranty of
2088
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
2090
# As per the GPL, removal of this notice is prohibited.
2092
# This is the EditTablePlugin used to edit tables in place.