forked from javigomez/lib_tbs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
tbs_plugin_excel.php
executable file
·360 lines (307 loc) · 12.3 KB
/
tbs_plugin_excel.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
<?php
/*
********************************************************
TinyButStrong plug-in: Excel Spreadsheets
Version 1.1.0, on 2011-02-03, by Skrol29, for TBS version >= 3.6.2
********************************************************
*/
// Name of the class is a keyword used for Plug-In authentication. So i'ts better to save it into a constant.
define('TBS_EXCEL','clsTbsExcel');
define('TBS_EXCEL_FILENAME', 'clsTbsExcel.FILENAME'); // deprecated command (=1)
define('TBS_EXCEL_DOWNLOAD',1); // deprecated command (=2) & Render option (by default) = TBS_OUTPUT
define('TBS_EXCEL_INLINE',4); // deprecated command (=3) & Render option (do not use value 2 wich is reserved for TBS_EXIT)
define('TBS_EXCEL_FILE',8); // Render option
define('TBS_EXCEL_STRING',16); // Render option
class clsTbsExcel {
// TBS events -----------------------------
function OnInstall() {
$this->Version = '1.1.0'; // Version can be displayed using [onshow..tbs_info] since TBS 3.2.0
// Constants for the plug-in
$this->TypeLst = array('xlNum'=>'Number', 'xlDT'=>'DateTime', 'xlStr'=>'String', 'xlErr'=>'Error', 'xlBoo'=>'Boolean');
$this->TypeLst2 = array('number'=>'Number', 'date'=>'DateTime', 'string'=>'String', 'error'=>'Error', 'boolean'=>'Boolean');
// Usefulle properies
$this->Oldies_FileName = '';
$this->TemplateFileName = '';
return array('OnCommand','BeforeLoadTemplate','AfterShow','OnOperation','BeforeMergeBlock','AfterMergeBlock','OnCacheField');
}
function OnCommand($Cmd,$Value='') {
if ($Cmd==TBS_EXCEL_FILENAME) { // deprecated, compatibility with version 1.0
// Change file name
$this->Oldies_FileName = $Value;
} elseif ($Cmd==TBS_EXCEL_DOWNLOAD) { // deprecated, compatibility with version 1.0
// Force output to download file
$this->TBS->Render = TBS_EXCEL_DOWNLOAD;
} elseif ($Cmd==TBS_EXCEL_INLINE) { // deprecated, compatibility with version 1.0
// Enables output to display inline (Internet Exlorer only)
$this->TBS->Render = TBS_EXCEL_INLINE;
}
}
function BeforeLoadTemplate(&$File, &$Charset) {
if ($this->TemplateFileName==='') $this->TemplateFileName = $File;
if ($Charset==='') $this->TBS->LoadTemplate('', array(&$this,'ConvXmlUtf8')); // Define the function for string conversion
}
function OnOperation($FieldName,&$Value,&$PrmLst,&$Source,$PosBeg,$PosEnd,$Loc) {
if (isset($this->TypeLst[$PrmLst['ope']])) {
if (!isset($Loc->xlType)) $this->tag_ChangeCellType($Source,$Loc,$this->TypeLst[$PrmLst['ope']]);
} elseif ($PrmLst['ope']==='xlPushRef') {
$this->tag_ChangeFormula($Source,$Loc,$Value,true);
}
}
function OnCacheField($BlockName,&$Loc,&$Txt,$PrmProc) {
if (isset($Loc->PrmLst['ope'])) {
// in this event, ope parameter is not exploded yet
$ope_lst = explode(',', $Loc->PrmLst['ope']);
$ope_att = false;
foreach ($ope_lst as $ope) {
$ope=trim($ope,' ');
if (isset($this->TypeLst[$ope])) $ope_att = $this->TypeLst[$ope];
}
// if a the cell type must be modified, then we add a new TBS field for changing the attribute value
if ($ope_att!==false) {
$this->tag_ChangeCellFormat($Loc, $ope_att);
$newfield = '['.$BlockName.';att=Data#ss:Type;if 1=1;then '.$ope_att.']';
$Txt = substr_replace($Txt, $newfield, $Loc->PosEnd+1,0); // the new field is added after the current field so it will be scanned by TBS
$Loc->xlType = true; // avoid a double process of such ope values
}
}
}
function BeforeMergeBlock(&$TplSource,&$BlockBeg,&$BlockEnd,$PrmLst,&$DataSrc,&$LocR) {
/* Manage attribute ssIndex.
Attribute ssIndex can be put by Excel on the first <Row> and <Cell> item of a <Table> element.
But this attribute must be put only once per series, it must not be repeated on next items when it is merged for a block.
So if we have it (it must be on the first item) we save it, delete it, and then and add it only for the first item merged (see AfterMergeBlock).
*/
$this->ssIndex = false;
if ($LocR->SectionNbr>0) {
$Src1 =& $LocR->SectionLst[1]->Src ;
// Check if the first block begins with Row or Cell.
$Start = '';
if (substr($Src1,0,4)==='<Row') $Start='<Row' ;
if (substr($Src1,0,5)==='<Cell') $Start='<Cell' ;
if ($Start!=='') {
// The attribute ss:Index can be there.
// Search for the end of the tag
$p = strpos($Src1,'>');
if ($p!==false) {
$tag = substr($Src1,0,$p+1);
// Searche for attribute ss:Index
$p = strpos($tag,'ss:Index');
if ($p!==false) {
// ss:Index is found, we have to save it and take it off from the block's source
$p1 = strpos($tag,'"',$p);
if ($p1!==false) {
$p2 = strpos($tag,'"',$p1+1);
if ($p2!==false) {
$len = $p2 - $p + 1;
$this->ssIndex = true;
$this->ssIndexStart = $Start;
$this->ssIndexSource = substr($Src1,$p,$len);
$Src1 = substr_replace($Src1,str_repeat(' ',$len),$p,$len);
}
}
}
}
}
}
}
function AfterMergeBlock(&$Buffer,&$DataSrc,&$LocR) {
// Replace attribute ss:Index if necessary, only one time for the block.
if ($this->ssIndex) {
$len = strlen($this->ssIndexStart);
if (substr($Buffer,0,$len)===$this->ssIndexStart) {
$Buffer = substr_replace($Buffer,' '.$this->ssIndexSource,$len,0);
}
$this->ssIndex = false;
}
}
function AfterShow(&$Render, $File='') {
$TBS =& $this->TBS;
// Delete optional XML attributes that could become invalide after the merge
$this->tag_DelOptionalTableAtt();
if ( ($TBS->ErrCount>0) && (!$TBS->NoErr) ) {
$TBS->meth_Misc_Alert('Show() Method', 'The output is cancelled by the Excel plugin because at least one TBS error has occured.');
exit;
}
if ($File==='') {
if ($this->Oldies_FileName==='') {
if ($this->TemplateFileName==='') {
$File = 'result_'.date('Y-m-d').'.xml';
} else {
$File = basename($this->TemplateFileName);
}
} else {
$File = $this->Oldies_FileName;
}
}
// Makes a download instead of displaying the result.
if (($Render & TBS_OUTPUT)==TBS_OUTPUT) { // TBS_OUTPUT = TBS_EXCEL_DOWNLOAD
// content downloaded
$this->f_HttpHeader_Download($File);
} elseif (($Render & TBS_EXCEL_INLINE)==TBS_EXCEL_INLINE) {
// content displayed in the browser
$this->f_HttpHeader_Inline($File);
$Render = $Render - TBS_EXCEL_INLINE;
if (($Render & TBS_OUTPUT)!=TBS_OUTPUT) $Render = $Render + TBS_OUTPUT; // needed for final output
} elseif (($Render & TBS_EXCEL_FILE)==TBS_EXCEL_FILE) {
// content saved in a file
$hndl = fopen($File, 'w');
fwrite($hndl, $this->TBS->Source);
fclose($hndl);
} elseif (($Render & TBS_EXCEL_STRING)==TBS_EXCEL_STRING) {
// content returned in the TBS->Source property (nothing to do)
}
// the TBS::Show() method will then recover the process and perform the TBS_OUTPUT and TBS_EXIT options, if any
}
// --------------------------
// Functions for internal job
// --------------------------
function ConvXmlOnly($Txt, $ConvBr) {
// Used by TBS to convert special chars and new lines.
$x = htmlspecialchars($Txt);
if ($ConvBr) $this->ConvBr($x);
return $x;
}
function ConvXmlUtf8($Txt, $ConvBr) {
// Used by TBS to convert special chars and new lines.
$x = htmlspecialchars(utf8_encode($Txt));
if ($ConvBr) $this->ConvBr($x);
return $x;
}
function f_HttpHeader_Download($FileName) {
header ('Pragma: no-cache');
// header ('Content-type: application/x-msexcel');
header ('Content-Type: application/vnd.ms-excel');
header ('Content-Disposition: attachment; filename="'.$FileName.'"');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: public');
header('Content-Description: File Transfer');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.strlen($this->TBS->Source));
}
function f_HttpHeader_Inline($FileName) {
header('Content-Type: application/x-msexcel; name="'.$FileName.'"');
header('Content-Disposition: inline; filename="'.$FileName.'"');
}
function tag_ChangeCellType(&$Txt, $Loc, $Type) {
// Change the cell type. This function assumes that the current cell has a type set to String. String type is expected since the cell contains a TBS tag.
if ($Type==='String') return true;
$t0 = clsTinyButStrong::f_Xml_FindTagStart($Txt,'Data',true,$Loc->PosBeg,false,true);
if ($t0===false) return false; // error in the XML structure
$te = strpos($Txt, '>', $t0);
if ( ($te===false) || ($te>$Loc->PosBeg) ) return false; // error in the XML structure
$len = $te - $t0 + 1;
$tag = substr($Txt, $t0, $len);
$len = strlen($tag);
$att = ' ss:Type="String"';
$att2 = ' ss:Type="'.$Type.'"';
$tag = str_replace($att, $att2, $tag);
$Txt = substr_replace($Txt, $tag, $t0, $len);
$diff = strlen($tag) - $len;
if ($diff!==0) {
$Loc->PosBeg += $diff;
$Loc->PosEnd += $diff;
}
$this->tag_ChangeCellFormat($Loc, $Type);
return true;
}
function tag_ChangeCellFormat(&$Loc, $Type) {
if ($Type==='DateTime') {
$Loc->PrmLst['frm'] = 'yyyy-mm-ddThh:nn:ss';
// activate the FRM mode
$Loc->ConvMode = 0;
$Loc->ConvProtect = false;
}
}
function tag_ChangeFormula(&$Txt,&$Loc,&$Value,$First) {
// The process assumes that the TBS is embeded into a N("") function
if (isset($Loc->xlExend)) {
if (!$Loc->xlExend) return false;
} else {
$Loc->xlExend = false;
// So we first search for the begining of the TBS expression. Going backward.
$p = $Loc->PosBeg - 1;
$cont = true;
$exp_n = false; // true if N is met
$exp_beg = false; //
do {
$x = $Txt[$p];
if ($p<2) {
$cont = false;
} elseif ($x==='"') {
$cont = false; // end of the formula
} elseif ($x==='(') { // begining of the function's arguments
$exp_n = true;
} elseif (($x==='+') or ($x==='&')) { // The expression can be added with + or & (wich is saved as &)
if ($exp_n) {
$exp_beg = $p;
$cont = false;
}
}
if ($cont) $p--;
} while ($cont);
if ($exp_beg===false) return false;
// Search for the end of the TBS expression.
$frm_end = strpos($Txt,'"',$Loc->PosEnd+1);
if ($frm_end===false) return false;
$exp_end = strpos($Txt,')',$Loc->PosEnd+1);
if ($exp_end===false) return false;
if ($exp_end>$frm_end) return false;
// Now we searching backard, the relative cell
$cont = true;
$br_o = false; // position of [
$br_c = false; // position of ]
$p = $exp_beg - 1;
do {
$x = $Txt[$p];
if ($p<2) {
$cont = false;
} elseif ($x===']') {
if ($br_o===false) $br_c = $p;
} elseif ($x==='[') {
if ($br_c!==false) $br_o = $p;
$cont=false;
} elseif ($x==='"') {
$cont=false;
}
if ($cont) $p--;
} while ($cont);
if ($br_o===false) return false;
// Calculate the relative index
$x = intval(substr($Txt,$br_o+1,$br_c-$br_o-1));
if ($x==0) return false;
// Save information in the locator, useful for cached locators
$Loc->xlVal = $x;
$Loc->xlExtraStr = substr($Txt,$br_c,$exp_beg-$br_c);
$Loc->xlExend = true;
$Loc->PosBeg = $br_o + 1;
$Loc->PosEnd = $exp_end;
$Loc->ConvMode = -1; // No contents conversion
}
// Calculate the new index
$v = intval($Value);
$x = $Loc->xlVal;
if ($x!=0) $x = $x + $v - 1;
// Replace the index value
$Value = strval($x).$Loc->xlExtraStr;
}
function tag_DelOptionalTableAtt() {
// Two Table attributes give the size of the zone, this zone may be extended after a MergeBlock()
// Hopefully, those attributes are optional, so we delete them.
$Txt =& $this->TBS->Source;
$att_lst = array('ss:ExpandedColumnCount', 'ss:ExpandedRowCount');
$Pos = 0;
while ( ($Loc=clsTinyButStrong::f_Xml_FindTag($Txt,'Table',true,$Pos,true,false,true,true))!==false ) {
foreach ($att_lst as $att) {
// delete the attributes by replacing them with spaces
$att = strtolower($att); // The TBS method does turn attributes anmes into lowercase.
if (isset($Loc->PrmPos[$att])) {
$p = $Loc->PrmPos[$att];
$n = $p[3] - $p[0] + 1;
$Txt = substr_replace($Txt, str_repeat(' ',$n), $p[0], $n);
}
}
$Pos = $Loc->PosEnd+1;
}
}
}