Bug 29407: Make the pickup locations dropdown JS reusable
[koha.git] / C4 / ImportExportFramework.pm
1 package C4::ImportExportFramework;
2
3 # Copyright 2010-2011 MASmedios.com y Ministerio de Cultura
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use strict;
21 use warnings;
22 use XML::LibXML;
23 use XML::LibXML::XPathContext;
24 use Digest::MD5;
25 use POSIX qw( strftime );
26 use Text::CSV_XS;
27 use List::MoreUtils qw( indexes );
28
29 use C4::Context;
30 use Koha::Logger;
31
32 our (@ISA, @EXPORT_OK);
33 BEGIN {
34     require Exporter;
35     @ISA    = qw(Exporter);
36     @EXPORT_OK = qw(
37         ExportFramework
38         ImportFramework
39         createODS
40     );
41 }
42
43
44 use constant XMLSTR => '<?xml version="1.0" encoding="UTF-8"?>
45 <?mso-application progid="Excel.Sheet"?>
46 <Workbook
47   xmlns:x="urn:schemas-microsoft-com:office:excel"
48   xmlns="urn:schemas-microsoft-com:office:spreadsheet"
49   xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
50
51 <Styles>
52  <Style ss:ID="Default" ss:Name="Normal">
53   <Alignment ss:Vertical="Bottom"/>
54   <Borders/>
55   <Font/>
56   <Interior/>
57   <NumberFormat/>
58   <Protection/>
59  </Style>
60  <Style ss:ID="s27">
61   <Font x:Family="Swiss" ss:Color="#0000FF" ss:Bold="1"/>
62  </Style>
63  <Style ss:ID="s21">
64   <NumberFormat ss:Format="yyyy\-mm\-dd"/>
65  </Style>
66  <Style ss:ID="s22">
67   <NumberFormat ss:Format="yyyy\-mm\-dd\ hh:mm:ss"/>
68  </Style>
69  <Style ss:ID="s23">
70   <NumberFormat ss:Format="hh:mm:ss"/>
71  </Style>
72 </Styles>
73
74 </Workbook>
75 ';
76
77
78 use constant ODSSTR => '<?xml version="1.0" encoding="UTF-8"?>
79 <office:document-content xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" office:version="1.0">
80 <office:scripts/>
81 <office:font-face-decls/>
82 <office:automatic-styles/>
83 </office:document-content>';
84
85
86 use constant ODS_STYLES_STR => '<?xml version="1.0" encoding="UTF-8"?>
87 <office:document-styles xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" office:version="1.0">
88 <office:font-face-decls></office:font-face-decls>
89 <office:styles></office:styles>
90 <office:automatic-styles></office:automatic-styles>
91 <office:master-styles></office:master-styles>
92 </office:document-styles>';
93
94
95 use constant ODS_SETTINGS_STR => '<?xml version="1.0" encoding="UTF-8"?>
96 <office:document-settings xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" office:version="1.0"><office:settings>
97 <config:config-item-set config:name="ooo:view-settings">
98 <config:config-item config:name="VisibleAreaTop" config:type="int">0</config:config-item>
99 <config:config-item config:name="VisibleAreaLeft" config:type="int">0</config:config-item>
100 <config:config-item config:name="VisibleAreaWidth" config:type="int">2000</config:config-item>
101 <config:config-item config:name="VisibleAreaHeight" config:type="int">900</config:config-item>
102 <config:config-item-map-indexed config:name="Views"><config:config-item-map-entry>
103 <config:config-item config:name="ViewId" config:type="string">View1</config:config-item>
104 <config:config-item-map-named config:name="Tables">
105 <config:config-item-map-entry config:name="Sheet1"><config:config-item config:name="CursorPositionX" config:type="int">0</config:config-item><config:config-item config:name="CursorPositionY" config:type="int">1</config:config-item><config:config-item config:name="HorizontalSplitMode" config:type="short">0</config:config-item><config:config-item config:name="VerticalSplitMode" config:type="short">0</config:config-item><config:config-item config:name="HorizontalSplitPosition" config:type="int">0</config:config-item><config:config-item config:name="VerticalSplitPosition" config:type="int">0</config:config-item><config:config-item config:name="ActiveSplitRange" config:type="short">2</config:config-item><config:config-item config:name="PositionLeft" config:type="int">0</config:config-item><config:config-item config:name="PositionRight" config:type="int">0</config:config-item><config:config-item config:name="PositionTop" config:type="int">0</config:config-item><config:config-item config:name="PositionBottom" config:type="int">0</config:config-item>
106 </config:config-item-map-entry>
107 </config:config-item-map-named>
108 <config:config-item config:name="ActiveTable" config:type="string">Sheet1</config:config-item>
109 <config:config-item config:name="HorizontalScrollbarWidth" config:type="int">270</config:config-item>
110 <config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
111 <config:config-item config:name="ZoomValue" config:type="int">100</config:config-item>
112 <config:config-item config:name="PageViewZoomValue" config:type="int">50</config:config-item>
113 <config:config-item config:name="ShowPageBreakPreview" config:type="boolean">false</config:config-item>
114 <config:config-item config:name="ShowZeroValues" config:type="boolean">true</config:config-item>
115 <config:config-item config:name="ShowNotes" config:type="boolean">true</config:config-item>
116 <config:config-item config:name="ShowGrid" config:type="boolean">true</config:config-item>
117 <config:config-item config:name="GridColor" config:type="long">12632256</config:config-item>
118 <config:config-item config:name="ShowPageBreaks" config:type="boolean">true</config:config-item>
119 <config:config-item config:name="HasColumnRowHeaders" config:type="boolean">true</config:config-item>
120 <config:config-item config:name="HasSheetTabs" config:type="boolean">true</config:config-item>
121 <config:config-item config:name="IsOutlineSymbolsSet" config:type="boolean">true</config:config-item>
122 <config:config-item config:name="IsSnapToRaster" config:type="boolean">false</config:config-item>
123 <config:config-item config:name="RasterIsVisible" config:type="boolean">false</config:config-item>
124 <config:config-item config:name="IsRasterAxisSynchronized" config:type="boolean">true</config:config-item></config:config-item-map-entry></config:config-item-map-indexed>
125 </config:config-item-set>
126 <config:config-item-set config:name="ooo:configuration-settings">
127 <config:config-item config:name="ShowZeroValues" config:type="boolean">true</config:config-item>
128 <config:config-item config:name="ShowNotes" config:type="boolean">true</config:config-item>
129 <config:config-item config:name="ShowGrid" config:type="boolean">true</config:config-item>
130 <config:config-item config:name="GridColor" config:type="long">12632256</config:config-item>
131 <config:config-item config:name="ShowPageBreaks" config:type="boolean">true</config:config-item>
132 <config:config-item config:name="LinkUpdateMode" config:type="short">3</config:config-item>
133 <config:config-item config:name="HasColumnRowHeaders" config:type="boolean">true</config:config-item>
134 <config:config-item config:name="HasSheetTabs" config:type="boolean">true</config:config-item>
135 <config:config-item config:name="IsOutlineSymbolsSet" config:type="boolean">true</config:config-item>
136 <config:config-item config:name="IsSnapToRaster" config:type="boolean">false</config:config-item>
137 <config:config-item config:name="RasterIsVisible" config:type="boolean">false</config:config-item>
138 <config:config-item config:name="IsRasterAxisSynchronized" config:type="boolean">true</config:config-item>
139 <config:config-item config:name="AutoCalculate" config:type="boolean">true</config:config-item>
140 <config:config-item config:name="PrinterName" config:type="string">Generic Printer</config:config-item>
141 <config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
142 <config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
143 <config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
144 <config:config-item config:name="UpdateFromTemplate" config:type="boolean">false</config:config-item>
145 <config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
146 <config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
147 </config:config-item-set>
148 </office:settings></office:document-settings>';
149
150
151 use constant ODS_MANIFEST_STR => '<?xml version="1.0" encoding="UTF-8"?>
152 <manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
153  <manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.spreadsheet" manifest:full-path="/"/>
154  <manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/statusbar/"/>
155  <manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/accelerator/"/>
156  <manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/floater/"/>
157  <manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/popupmenu/"/>
158  <manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/progressbar/"/>
159  <manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/menubar/"/>
160  <manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/toolbar/"/>
161  <manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/images/Bitmaps/"/>
162  <manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/images/"/>
163  <manifest:file-entry manifest:media-type="application/vnd.sun.xml.ui.configuration" manifest:full-path="Configurations2/"/>
164  <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
165  <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="styles.xml"/>
166  <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="meta.xml"/>
167  <manifest:file-entry manifest:media-type="" manifest:full-path="Thumbnails/"/>
168  <manifest:file-entry manifest:media-type="text/xml" manifest:full-path="settings.xml"/>
169 </manifest:manifest>';
170
171
172 =head1 NAME
173
174 C4::ImportExportFramework - Import/Export Framework to Excel-xml/ODS Module Functions
175
176 =head1 SYNOPSIS
177
178   use C4::ImportExportFramework;
179
180 =head1 DESCRIPTION
181
182 Module to Import/Export Framework to Excel-xml/ODS on intranet administration - MARC Frameworks section
183
184 Module to Import/Export Framework to Excel-xml/ODS on intranet administration - MARC Frameworks section
185 exporting the tables marc_tag_structure, marc_subfield_structure to excel-xml/ods or viceversa
186
187 Functions for handling import/export.
188
189
190 =head1 SUBROUTINES
191
192
193
194 =head2 ExportFramework
195
196 Export all the information of a Framework to an excel "xml" file or OpenDocument SpreadSheet "ods" file.
197
198 return :
199 succes
200
201 =cut
202
203 sub ExportFramework
204 {
205     my ($frameworkcode, $xmlStrRef, $mode) = @_;
206
207     my $dbh = C4::Context->dbh;
208     if ($dbh) {
209         my $dom;
210         my $root;
211         my $elementSS;
212         if ($mode eq 'ods' || $mode eq 'excel') {
213             eval {
214                 my $parser = XML::LibXML->new();
215                 $dom = $parser->parse_string(($mode && $mode eq 'ods')?ODSSTR:XMLSTR);
216                 if ($dom) {
217                     $root = $dom->documentElement();
218                     if ($mode && $mode eq 'ods') {
219                         my $elementBody = $dom->createElement('office:body');
220                         $root->appendChild($elementBody);
221                         $elementSS = $dom->createElement('office:spreadsheet');
222                         $elementBody->appendChild($elementSS);
223                     }
224                 }
225             };
226             if ($@) {
227                 Koha::Logger->get->warn("Error ExportFramework $@");
228                 return 0;
229             }
230         }
231
232         if (_export_table('marc_tag_structure', $dbh, ($mode eq 'csv')?$xmlStrRef:$dom, ($mode eq 'ods')?$elementSS:$root, $frameworkcode, $mode)) {
233             if (_export_table('marc_subfield_structure', $dbh, ($mode eq 'csv')?$xmlStrRef:$dom, ($mode eq 'ods')?$elementSS:$root, $frameworkcode, $mode)) {
234                 $$xmlStrRef = $dom->toString(1) if ($mode eq 'ods' || $mode eq 'excel');
235                 return 1;
236             }
237         }
238     }
239     return 0;
240 }#ExportFramework
241
242
243
244
245 # Export all the data from a mysql table to an spreadsheet.
246 sub _export_table
247 {
248     my ($table, $dbh, $dom, $root, $frameworkcode, $mode) = @_;
249     if ($mode eq 'csv') {
250         _export_table_csv($table, $dbh, $dom, $root, $frameworkcode);
251     } elsif ($mode eq 'ods') {
252         _export_table_ods($table, $dbh, $dom, $root, $frameworkcode);
253     } else {
254         _export_table_excel($table, $dbh, $dom, $root, $frameworkcode);
255     }
256 }
257
258 # Export the mysql table to an csv file
259 sub _export_table_csv
260 {
261     my ($table, $dbh, $strCSV, $root, $frameworkcode) = @_;
262
263     eval {
264         # First row with the name of the columns
265         my $query = 'SHOW COLUMNS FROM ' . $table;
266         my $sth = $dbh->prepare($query);
267         $sth->execute();
268         my @fields = ();
269         while (my $hashRef = $sth->fetchrow_hashref) {
270             $$strCSV .= '"' . $hashRef->{Field} . '",';
271             push @fields, $hashRef->{Field};
272         }
273         chop $$strCSV;
274         $$strCSV .= chr(10);
275         # Populate rows with the data from mysql
276         $query = 'SELECT * FROM ' . $table . ' WHERE frameworkcode=?';
277         $sth = $dbh->prepare($query);
278         $sth->execute($frameworkcode);
279         my $data;
280         while (my $hashRef = $sth->fetchrow_hashref) {
281             for my $field (@fields) {
282                 my $value = $hashRef->{$field} // q||;
283                 $value =~ s/[\r\n]//g;
284                 $$strCSV .= '"' . $value . '",';
285             }
286             chop $$strCSV;
287             $$strCSV .= chr(10);
288         }
289         $$strCSV .= chr(10);
290         for (@fields) {
291             # Separator for change of table
292             $$strCSV .= '"#-#",';
293         }
294         chop $$strCSV;
295         $$strCSV .= chr(10);
296         $$strCSV .= chr(10);
297     };
298     if ($@) {
299         Koha::Logger->get->warn("Error _export_table_csv $@");
300         return 0;
301     }
302     return 1;
303 }#_export_table_csv
304
305
306 # Export the mysql table to an ods file
307 sub _export_table_ods
308 {
309     my ($table, $dbh, $dom, $root, $frameworkcode) = @_;
310
311     eval {
312         my $elementTable = $dom->createElement('table:table');
313         $elementTable->setAttribute('table:name', $table);
314         $elementTable->setAttribute('table:print', 'false');
315         $root->appendChild($elementTable);
316         my $elementRow = $dom->createElement('table:table-row');
317         $elementTable->appendChild($elementRow);
318
319         my $elementCell;
320         my $elementData;
321         # First row with the name of the columns
322         my $query = 'SHOW COLUMNS FROM ' . $table;
323         my $sth = $dbh->prepare($query);
324         $sth->execute();
325         my @fields = ();
326         while (my $hashRef = $sth->fetchrow_hashref) {
327             $elementCell = $dom->createElement('table:table-cell');
328             $elementCell->setAttribute('office:value-type', 'string');
329             $elementCell->setAttribute('office:value', $hashRef->{Field});
330             $elementRow->appendChild($elementCell);
331             $elementData = $dom->createElement('text:p');
332             $elementCell->appendChild($elementData);
333             $elementData->appendTextNode($hashRef->{Field});
334             push @fields, {name => $hashRef->{Field}, type => ($hashRef->{Type} =~ /int/i)?'float':'string'};
335         }
336         # Populate rows with the data from mysql
337         $query = 'SELECT * FROM ' . $table . ' WHERE frameworkcode=?';
338         $sth = $dbh->prepare($query);
339         $sth->execute($frameworkcode);
340         my $data;
341         while (my $hashRef = $sth->fetchrow_hashref) {
342             $elementRow = $dom->createElement('table:table-row');
343             $elementTable->appendChild($elementRow);
344             for (@fields) {
345                 $data = $hashRef->{$_->{name}};
346                 if ($_->{type} eq 'float' && !defined($data)) {
347                     $data = '0';
348                 } elsif ($_->{type} eq 'string' && !defined($data)) {
349                     $data = q{};
350                 } elsif ($_->{type} eq 'string' && (!$data && $data ne '0')) {
351                     $data = '#';
352                 }
353                 $data = _parseContent2Xml($data) if ($_->{type} eq 'string');
354                 $elementCell = $dom->createElement('table:table-cell');
355                 $elementCell->setAttribute('office:value-type', $_->{type});
356                 $elementCell->setAttribute('office:value', $data);
357                 $elementRow->appendChild($elementCell);
358                 $elementData = $dom->createElement('text:p');
359                 $elementCell->appendChild($elementData);
360                 $elementData->appendTextNode($data);
361             }
362         }
363     };
364     if ($@) {
365         Koha::Logger->get->warn("Error _export_table_ods $@");
366         return 0;
367     }
368     return 1;
369 }#_export_table_ods
370
371
372 # Export the mysql table to an excel-xml (openoffice/libreoffice compatible) file
373 sub _export_table_excel
374 {
375     my ($table, $dbh, $dom, $root, $frameworkcode) = @_;
376
377     eval {
378         my $elementWS = $dom->createElement('Worksheet');
379         $elementWS->setAttribute('ss:Name', $table);
380         $root->appendChild($elementWS);
381         my $elementTable = $dom->createElement('ss:Table');
382         $elementWS->appendChild($elementTable);
383         my $elementRow = $dom->createElement('ss:Row');
384         $elementTable->appendChild($elementRow);
385
386         # First row with the name of the columns
387         my $elementCell;
388         my $elementData;
389         my $query = 'SHOW COLUMNS FROM ' . $table;
390         my $sth = $dbh->prepare($query);
391         $sth->execute();
392         my @fields = ();
393         while (my $hashRef = $sth->fetchrow_hashref) {
394             $elementCell = $dom->createElement('ss:Cell');
395             $elementCell->setAttribute('ss:StyleID', 's27');
396             $elementRow->appendChild($elementCell);
397             $elementData = $dom->createElement('ss:Data');
398             $elementData->setAttribute('ss:Type', 'String');
399             $elementCell->appendChild($elementData);
400             $elementData->appendTextNode($hashRef->{Field});
401             push @fields, {name => $hashRef->{Field}, type => ($hashRef->{Type} =~ /int/i)?'Number':'String'};
402         }
403         # Populate rows with the data from mysql
404         $query = 'SELECT * FROM ' . $table . ' WHERE frameworkcode=?';
405         $sth = $dbh->prepare($query);
406         $sth->execute($frameworkcode);
407         my $data;
408         while (my $hashRef = $sth->fetchrow_hashref) {
409             $elementRow = $dom->createElement('ss:Row');
410             $elementTable->appendChild($elementRow);
411             for (@fields) {
412                 $elementCell = $dom->createElement('ss:Cell');
413                 $elementRow->appendChild($elementCell);
414                 $elementData = $dom->createElement('ss:Data');
415                 $elementData->setAttribute('ss:Type', $_->{type});
416                 $elementCell->appendChild($elementData);
417                 $data = $hashRef->{$_->{name}};
418                 if ($_->{type} eq 'Number' && !defined($data)) {
419                     $data = '0';
420                 } elsif ($_->{type} eq 'String' && !defined($data)) {
421                     $data = q{};
422                 } elsif ($_->{type} eq 'String' && (!$data && $data ne '0')) {
423                     $data = '#';
424                 }
425                 $elementData->appendTextNode(($_->{type} eq 'String')?_parseContent2Xml($data):$data);
426             }
427         }
428     };
429     if ($@) {
430         Koha::Logger->get->warn("Error _export_table_excel $@");
431         return 0;
432     }
433     return 1;
434 }#_export_table_excel
435
436
437
438
439
440
441
442 # Format chars problematics to a correct format for xml.
443 sub _parseContent2Xml
444 {
445     my $content = shift;
446
447     $content =~ s/\&(?![a-zA-Z#0-9]{1,4};)/&amp;/g;
448     $content =~ s/</&lt;/g;
449     $content =~ s/>/&gt;/g;
450     return $content;
451 }#_parseContent2Xml
452
453
454 # Get the tmp directory on the system
455 sub _getTmp
456 {
457     my $tmp = '/tmp';
458     if ($ENV{'TMP'} && -d $ENV{'TMP'}) {
459         $tmp = $ENV{'TMP'};
460     } elsif ($ENV{'TMPDIR'} && -d $ENV{'TMPDIR'}) {
461         $tmp = $ENV{'TMPDIR'};
462     } elsif ($ENV{'TEMP'} && -d $ENV{'TEMP'}) {
463         $tmp = $ENV{'TEMP'};
464     }
465     return $tmp;
466 }#_getTmp
467
468
469 # Create our tempdir directory for the ods process
470 sub _createTmpDir
471 {
472     my $tmp = shift;
473
474     my $tempdir = (-d $tmp)?$tmp . '/':'./';
475     $tempdir .= 'tmp_ods_' . Digest::MD5::md5_hex(Digest::MD5::md5_hex(time().{}.rand().{}.$$));
476     eval {
477         mkdir $tempdir;
478     };
479     if ($@) {
480         return;
481     } else {
482         return $tempdir;
483     }
484 }#_createTmpDir
485
486 =head2 createODS
487
488 Creates a temporary directory to create the ods file and read it to store its content in a string.
489
490 return :
491 success
492
493 =cut
494
495 sub createODS
496 {
497     my ($strContent, $lang, $strODSRef) = @_;
498
499     my $tmp = _getTmp();
500     my $tempModule = 1;
501     my $tempdir;
502     eval {
503         require File::Temp;
504         import File::Temp qw/ tempfile tempdir /;
505         $tempdir = tempdir ( 'tmp_ods_' . $$ . '_XXXXXXXX', DIR => (-d $tmp)?$tmp:'.', CLEANUP => 1);
506     };
507     if ($@) {
508         $tempModule = 0;
509         $tempdir = _createTmpDir($tmp);
510     }
511     if ($tempdir) {
512         my $fh;
513         # populate tempdir directory with the ods elements
514         eval {
515             if (open($fh, '>',  "$tempdir/content.xml")) {
516                 print {$fh} $strContent;
517                 close($fh);
518             }
519             if (open($fh, '>', "$tempdir/mimetype")) {
520                 print {$fh} 'application/vnd.oasis.opendocument.spreadsheet';
521                 close($fh);
522             }
523             if (open($fh, '>', "$tempdir/meta.xml")) {
524                 print {$fh} _getMeta($lang);
525                 close($fh);
526             }
527             if (open($fh, '>', "$tempdir/styles.xml")) {
528                 print {$fh} ODS_STYLES_STR;
529                 close($fh);
530             }
531             if (open($fh, '>', "$tempdir/settings.xml")) {
532                 print {$fh} ODS_SETTINGS_STR;
533                 close($fh);
534             }
535             mkdir($tempdir.'/META-INF/');
536             mkdir($tempdir.'/Configurations2/');
537             mkdir($tempdir.'/Configurations2/acceleator/');
538             mkdir($tempdir.'/Configurations2/images/');
539             mkdir($tempdir.'/Configurations2/popupmenu/');
540             mkdir($tempdir.'/Configurations2/statusbar/');
541             mkdir($tempdir.'/Configurations2/floater/');
542             mkdir($tempdir.'/Configurations2/menubar/');
543             mkdir($tempdir.'/Configurations2/progressbar/');
544             mkdir($tempdir.'/Configurations2/toolbar/');
545
546             if (open($fh, '>', "$tempdir/META-INF/manifest.xml")) {
547                 print {$fh} ODS_MANIFEST_STR;
548                 close($fh);
549             }
550         };
551         if ($@) {
552             Koha::Logger->get->warn("Error createODS $@");
553         } else {
554             # create ods file from tempdir directory
555             eval {
556                 require Archive::Zip;
557                 import Archive::Zip qw( :ERROR_CODES :CONSTANTS );
558                 my $zip = Archive::Zip->new();
559                 $zip->addTree( $tempdir, '' );
560                 $zip->writeToFileNamed($tempdir . '/new.ods');
561             };
562             if ($@) {
563                 my $cmd = qx(which zip 2>/dev/null || whereis zip);
564                 chomp $cmd;
565                 $cmd = 'zip' if (!$cmd || !-x $cmd);
566                 system("cd $tempdir && $cmd -r new.ods ./");
567             }
568             my $ok = 0;
569             # read ods file and return as a string
570             if (-f "$tempdir/new.ods") {
571                 if (open ($fh, '<', "$tempdir/new.ods")) {
572                     binmode $fh;
573                     my $buffer;
574                     while (read ($fh, $buffer, 65536)) {
575                         $$strODSRef .= $buffer;
576                     }
577                     close($fh);
578                     $ok = 1;
579                 }
580             }
581             # delete tempdir directory
582             if (!$tempModule && $tempdir) {
583                 eval {
584                     require File::Path;
585                     import File::Temp qw/ rmtree /;
586                     rmtree($tempdir);
587                 };
588                 if ($@) {
589                     system("rm -rf $tempdir");
590                 }
591             }
592             return 1 if ($ok);
593         }
594     }
595     return 0;
596 }#createODS
597
598
599 # return Meta content for ods file
600 sub _getMeta
601 {
602     my $lang = shift;
603
604     my $myDate = strftime ("%Y-%m-%dT%H:%M:%S", localtime(time()));
605     my $meta = '<?xml version="1.0" encoding="UTF-8"?>
606     <office:document-meta xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:ooo="http://openoffice.org/2004/office" office:version="1.0">
607         <office:meta>
608             <meta:generator>ods-php</meta:generator>
609             <meta:creation-date>' . $myDate . '</meta:creation-date>
610             <dc:date>' . $myDate . '</dc:date>
611             <dc:language>' . $lang . '</dc:language>
612             <meta:editing-cycles>2</meta:editing-cycles>
613             <meta:editing-duration>PT15S</meta:editing-duration>
614             <meta:user-defined meta:name="Info 1"/>
615             <meta:user-defined meta:name="Info 2"/>
616             <meta:user-defined meta:name="Info 3"/>
617             <meta:user-defined meta:name="Info 4"/>
618         </office:meta>
619     </office:document-meta>';
620     return $meta;
621 }#_getMeta
622
623
624 =head2 ImportFramework
625
626 Import all the information of a Framework from a excel-xml/ods file.
627
628 return :
629 success
630
631 =cut
632
633 sub ImportFramework
634 {
635     my ($filename, $frameworkcode, $deleteFilename) = @_;
636
637     my $tempdir;
638     my $ok = -1;
639     my $dbh = C4::Context->dbh;
640     if (-r $filename && $dbh) {
641         my $extension = '';
642         if ($filename =~ /\.(csv|ods|xml)$/i) {
643             $extension = lc($1);
644         } else {
645             unlink ($filename) if ($deleteFilename); # remove temporary file
646             return -1;
647         }
648         if ($extension eq 'ods') {
649             ($tempdir, $filename) = _openODS($filename, $deleteFilename);
650         }
651         if ($filename) {
652             my $dom;
653             eval {
654                 if ($extension eq 'ods' || $extension eq 'xml') {
655                     # They have xml structure, so read it on a dom object
656                     my $parser = XML::LibXML->new();
657                     $dom = $parser->parse_file($filename);
658                     if ($dom) {
659                         my $root = $dom->documentElement();
660                     }
661                 } else {
662                     # They are text files, so open it to read
663                     open($dom, '<', $filename);
664                 }
665                 if ($dom) {
666                     # Process both tables
667                     my $numDeleted = 0;
668                     my $numDeletedAux = 0;
669                     if (($numDeletedAux = _import_table($dbh, 'marc_tag_structure', $frameworkcode, $dom, ['frameworkcode', 'tagfield'], $extension)) >= 0) {
670                         $numDeleted += $numDeletedAux if ($numDeletedAux > 0);
671                         if (($numDeletedAux = _import_table($dbh, 'marc_subfield_structure', $frameworkcode, $dom, ['frameworkcode', 'tagfield', 'tagsubfield'], $extension)) >= 0) {
672                             $numDeleted += $numDeletedAux if ($numDeletedAux > 0);
673                             $ok = ($numDeleted > 0)?$numDeleted:0;
674                         }
675                     }
676                 } else {
677                     Koha::Logger->get->warn("Error ImportFramework couldn't create dom");
678                 }
679             };
680             if ($@) {
681                 Koha::Logger->get->warn("Error ImportFramework $@");
682             } else {
683                 if ($extension eq 'csv') {
684                     close($dom) if ($dom);
685                 }
686             }
687         }
688         unlink ($filename) if ($deleteFilename); # remove temporary file
689     } else {
690         Koha::Logger->get->warn("Error ImportFramework no conex to database or not readeable $filename");
691     }
692     if ($deleteFilename && $tempdir && -d $tempdir && -w $tempdir) {
693         eval {
694             require File::Path;
695             import File::Temp qw/ rmtree /;
696             rmtree($tempdir);
697         };
698         if ($@) {
699             system("rm -rf $tempdir");
700         }
701     }
702     return $ok;
703 }#ImportFramework
704
705 # Open (uncompress) ods file and return the content.xml file
706 sub _openODS
707 {
708     my ($filename, $deleteFilename) = @_;
709
710     my $tmp = _getTmp();
711     my $tempModule = 1;
712     my $tempdir;
713     eval {
714         require File::Temp;
715         import File::Temp qw/ tempfile tempdir /;
716         $tempdir = tempdir ( 'tmp_ods_' . $$ . '_XXXXXXXX', DIR => (-d $tmp)?$tmp:'.', CLEANUP => 1);
717     };
718     if ($@) {
719         $tempModule = 0;
720         $tempdir = _createTmpDir($tmp);
721     }
722     if ($tempdir) {
723         eval {
724             require Archive::Zip;
725             import Archive::Zip qw( :ERROR_CODES :CONSTANTS );
726             my $zip = Archive::Zip->new($filename);
727             foreach my $file ($zip->members) {
728                 next if ($file->isDirectory);
729                 (my $extractName = $file->fileName) =~ s{.*/}{};
730                 next unless ($extractName eq 'content.xml');
731                 $file->extractToFileNamed("$tempdir/$extractName");
732             }
733         };
734         if ($@) {
735             my $cmd = qx(which unzip 2>/dev/null || whereis unzip);
736             chomp $cmd;
737             $cmd = 'unzip' if (!$cmd || !-x $cmd);
738             system("$cmd $filename -d $tempdir");
739         }
740         if (-f "$tempdir/content.xml") {
741             unlink ($filename) if ($deleteFilename);
742             return ($tempdir, "$tempdir/content.xml");
743         }
744     }
745     unlink ($filename) if ($deleteFilename);
746     return ($tempdir, undef);
747 }#_openODS
748
749
750
751 # Check the table and columns corresponds with worksheet
752 sub _check_validity_worksheet
753 {
754     my ($dbh, $table, $nodeFields, $fieldsA, $format) = @_;
755
756     my $ret = 0;
757     eval {
758         my $query = 'DESCRIBE ' . $table;
759         my $sth = $dbh->prepare($query);
760         $sth->execute();
761         $sth->finish;
762         $query = 'SHOW COLUMNS FROM ' . $table;
763         $sth = $dbh->prepare($query);
764         $sth->execute();
765         my $fields = {};
766         while (my $hashRef = $sth->fetchrow_hashref) {
767             $fields->{$hashRef->{Field}} = $hashRef->{Field};
768         }
769         my @fields;
770         my $fieldsR;
771         if ($fieldsA) {
772             $fieldsR = $fieldsA;
773         } else {
774             $fieldsR = \@fields;
775             _getFields($nodeFields, $fieldsR, $format);
776         }
777         $ret = 1;
778         for (@$fieldsR) {
779             unless (exists($fields->{$_})) {
780                 $ret = 0;
781                 last;
782             }
783         }
784     };
785     return $ret;
786 }#_check_validity_worksheet
787
788
789 # Import the data from an excel-xml/ods to mysql tables.
790 sub _import_table
791 {
792     my ($dbh, $table, $frameworkcode, $dom, $PKArray, $format) = @_;
793     my %fields2Delete;
794     my $query;
795     my @fields;
796     # Create hash with all elements defined by primary key to know which ones to delete after parsing the spreadsheet
797     eval {
798         @fields = @$PKArray;
799         shift @fields;
800         $query = 'SELECT ' . join(',', @fields) . ' FROM ' . $table . ' WHERE frameworkcode=?';
801         my $sth = $dbh->prepare($query);
802         $sth->execute($frameworkcode);
803         my $field;
804         while (my $hashRef = $sth->fetchrow_hashref) {
805             $field = '';
806             map { $field .= $hashRef->{$_} . '_'; } @fields;
807             chop $field;
808             $fields2Delete{$field} = 1;
809         }
810         $sth->finish;
811     };
812     my $ok = 0;
813     if ($format eq 'csv') {
814         my @fieldsName = ();
815         eval {
816             my $query = 'SHOW COLUMNS FROM ' . $table;
817             my $sth = $dbh->prepare($query);
818             $sth->execute();
819             while (my $hashRef = $sth->fetchrow_hashref) {
820                 push @fieldsName, $hashRef->{Field};
821             }
822         };
823         $ok = _import_table_csv($dbh, $table, $frameworkcode, $dom, $PKArray, \%fields2Delete, \@fieldsName);
824     } elsif ($format eq 'ods') {
825         $ok = _import_table_ods($dbh, $table, $frameworkcode, $dom, $PKArray, \%fields2Delete);
826     } else {
827         $ok = _import_table_excel($dbh, $table, $frameworkcode, $dom, $PKArray, \%fields2Delete);
828     }
829     if ($ok) {
830         if (($ok = scalar(keys %fields2Delete)) > 0) {
831             $query = 'DELETE FROM ' . $table . ' WHERE ';
832             map {$query .= $_ . '=? AND ';} @$PKArray;
833             $query = substr($query, 0, -4);
834             my $sth = $dbh->prepare($query);
835             for (keys %fields2Delete) {
836                 eval {
837                     $sth->execute(($frameworkcode, split('_', $_)));
838                 };
839             }
840         }
841     } else {
842         $ok = -1;
843     }
844     return $ok;
845 }#_import_table
846
847
848 # Insert/Update the row from the spreadsheet in the database
849 sub _processRow_DB
850 {
851     my ($dbh, $table, $fields, $dataStr, $updateStr, $dataFields, $dataFieldsHash, $PKArray, $fieldsPK, $fields2Delete) = @_;
852
853     my $ok = 0;
854     my $query;
855     $query = 'INSERT INTO ' . $table . ' (' . $fields . ') VALUES (' . $dataStr . ') ON DUPLICATE KEY UPDATE ' . $updateStr;
856     eval {
857         my $sth = $dbh->prepare($query);
858         $sth->execute((@$dataFields, @$dataFields));
859     };
860     if ($@) {
861         Koha::Logger->get->warn("Error _processRow_DB $@");
862     } else {
863         $ok = 1;
864     }
865     if ($ok) {
866         my $field = '';
867         map { $field .= $dataFieldsHash->{$_} . '_'; } @$fieldsPK;
868         chop $field;
869         delete $fields2Delete->{$field} if (exists($fields2Delete->{$field}));
870     }
871     return $ok;
872 }#_processRow_DB
873
874
875 # Process the rows of a worksheet and insert/update them in a mysql table.
876 sub _processRows_Table
877 {
878     my ($dbh, $frameworkcode, $nodeR, $table, $PKArray, $format, $fields2Delete) = @_;
879
880     my $query;
881     my @fields = ();
882     my $fields = '';
883     my $dataStr = '';
884     my $updateStr = '';
885     my $j = 0;
886     my $ok = 0;
887     my @fieldsPK = @$PKArray;
888     shift @fieldsPK;
889     while ($nodeR) {
890         if ($nodeR->nodeType == 1 && (($format && $format eq 'ods' && $nodeR->nodeName =~ /(?:table:)?table-row/) || ($nodeR->nodeName =~ /(?:ss:)?Row/)) && $nodeR->hasChildNodes()) {
891             if ($j == 0) {
892                 # Get name columns
893                 _getFields($nodeR, \@fields, $format);
894                 return 0 unless _check_validity_worksheet($dbh, $table, $nodeR, \@fields, $format);
895                 $fields = join(',', @fields);
896                 $dataStr = '';
897                 map { $dataStr .= '?,';} @fields;
898                 chop($dataStr) if ($dataStr);
899                 $updateStr = '';
900                 map { $updateStr .= $_ . '=?,';} @fields;
901                 chop($updateStr) if ($updateStr);
902             } else {
903                 # Get data from row
904                 my ($dataFields, $dataFieldsR) = _getDataFields($frameworkcode, $nodeR, \@fields, $format);
905                 if (scalar(@fields) == scalar(@$dataFieldsR)) {
906                     $ok = _processRow_DB($dbh, $table, $fields, $dataStr, $updateStr, $dataFieldsR, $dataFields, $PKArray, \@fieldsPK, $fields2Delete);
907                 } else {
908                     warn "$j don't match number of fields " . scalar(@fields) . ' vs ' . scalar(@$dataFieldsR) . "($dataStr)";
909                 }
910             }
911             $j++;
912         }
913         $nodeR = $nodeR->nextSibling;
914     }
915     return 1;
916 }#_processRows_Table
917
918
919
920
921 # Import worksheet from the csv file to the mysql table
922 sub _import_table_csv
923 {
924     my ($dbh, $table, $frameworkcode, $dom, $PKArray, $fields2Delete, $fields) = @_;
925
926     my $row = '';
927     my $partialRow = '';
928     my $numFields = @$fields;
929     my $fieldsNameRead = 0;
930     my @arrData;
931     my ($fieldsStr, $dataStr, $updateStr, @empty_indexes);
932     my @fieldsPK = @$PKArray;
933     shift @fieldsPK;
934     my $ok = 0;
935     my $pos = 0;
936     my $csv = Text::CSV_XS->new ({ binary => 1 });
937     while ( my $row = $csv->getline($dom) ) {
938         my @fields = @$row;
939         @arrData = @fields;
940         next if scalar @arrData == grep { $_ eq '' } @arrData; # Emtpy lines
941         #$arrData[0] = substr($arrData[0], 1) if ($arrData[0] =~ /^"/);
942         #$arrData[$#arrData] =~ s/[\r\n]+$//;
943         #chop $arrData[$#arrData] if ($arrData[$#arrData] =~ /"$/);
944         if (@arrData) {
945             if ($arrData[0] eq '#-#' && $arrData[$#arrData] eq '#-#') {
946                 # Change of table with separators #-#
947                 return 1;
948             } elsif ($fieldsNameRead && $arrData[0] eq 'tagfield') {
949                 # Change of table because we begin with field name with former field names read
950                 seek($dom, $pos, 0);
951                 return 1;
952             }
953             if (!$fieldsNameRead) {
954                 # New table, we read the field names
955                 $fieldsNameRead = 1;
956                 $fields = [@arrData];
957                 my $non_empty_fields = [ grep { $_ ne '' } @$fields ];
958                 @empty_indexes = indexes { $_ eq '' } @$fields;
959                 $fieldsStr = join(',', @$non_empty_fields);
960                 $dataStr = '';
961                 map { $dataStr .= '?,';} @$non_empty_fields;
962                 chop($dataStr) if ($dataStr);
963                 $updateStr = '';
964                 map { $updateStr .= $_ . '=?,';} @$non_empty_fields;
965                 chop($updateStr) if ($updateStr);
966             } else {
967                 # Read data
968                 my $j = 0;
969                 my %dataFields = ();
970                 my @values;
971                 for my $value (@arrData) {
972                     if ( grep { $_ == $j } @empty_indexes ) {
973                         # empty field
974                     } elsif ($fields->[$j] eq 'frameworkcode' && $value ne $frameworkcode) {
975                         $dataFields{$fields->[$j]} = $frameworkcode;
976                         push @values, $frameworkcode;
977                     } elsif ($fields->[$j] eq 'isurl' && defined $value && $value eq q{}) {
978                         $dataFields{$fields->[$j]} = undef;
979                         push @values, undef;
980                     } else {
981                         $dataFields{$fields->[$j]} = $value;
982                         push @values, $value;
983                     }
984                     $j++
985                 }
986                 $ok = _processRow_DB($dbh, $table, $fieldsStr, $dataStr, $updateStr, \@values, \%dataFields, $PKArray, \@fieldsPK, $fields2Delete);
987             }
988             $pos = tell($dom);
989         }
990         @arrData = ();
991     }
992     return $ok;
993 }#_import_table_csv
994
995
996 # Import worksheet from the ods content.xml file to the mysql table
997 sub _import_table_ods
998 {
999     my ($dbh, $table, $frameworkcode, $dom, $PKArray, $fields2Delete) = @_;
1000
1001     my $xc = XML::LibXML::XPathContext->new($dom);
1002     $xc->registerNs('xmlns:office','urn:oasis:names:tc:opendocument:xmlns:office:1.0');
1003     $xc->registerNs('xmlns:table','urn:oasis:names:tc:opendocument:xmlns:table:1.0');
1004     $xc->registerNs('xmlns:text','urn:oasis:names:tc:opendocument:xmlns:text:1.0');
1005     my @nodes;
1006     @nodes = $xc->findnodes('//table:table[@table:name="' . $table . '"]');
1007     if (@nodes == 1 && $nodes[0]->hasChildNodes()) {
1008         my $nodeR = $nodes[0]->firstChild;
1009         return _processRows_Table($dbh, $frameworkcode, $nodeR, $table, $PKArray, 'ods', $fields2Delete);
1010     } else {
1011         Koha::Logger->get->warn("Error _import_table_ods there's not worksheet for $table");
1012     }
1013     return 0;
1014 }#_import_table_ods
1015
1016
1017 # Import worksheet from the excel-xml file to the mysql table
1018 sub _import_table_excel
1019 {
1020     my ($dbh, $table, $frameworkcode, $dom, $PKArray, $fields2Delete) = @_;
1021
1022     my $xc = XML::LibXML::XPathContext->new($dom);
1023     $xc->registerNs('xmlns','urn:schemas-microsoft-com:office:spreadsheet');
1024     $xc->registerNs('xmlns:ss','urn:schemas-microsoft-com:office:spreadsheet');
1025     $xc->registerNs('xmlns:x','urn:schemas-microsoft-com:office:excel');
1026     my @nodes;
1027     @nodes = $xc->findnodes('//ss:Worksheet[@ss:Name="' . $table . '"]');
1028     if (@nodes > 0) {
1029         for (my $i=0; $i < @nodes; $i++) {
1030             my @nodesT = $nodes[$i]->getElementsByTagNameNS('urn:schemas-microsoft-com:office:spreadsheet', 'Table');
1031             if (@nodesT == 1 && $nodesT[0]->hasChildNodes()) {
1032                 my $nodeR = $nodesT[0]->firstChild;
1033                 return _processRows_Table($dbh, $frameworkcode, $nodeR, $table, $PKArray, undef, $fields2Delete);
1034             }
1035         }
1036     } else {
1037         Koha::Logger->get->warn("Error _import_table_excel there's not worksheet for $table");
1038     }
1039     return 0;
1040 }#_import_table_excel
1041
1042
1043 # Get the data from a cell on a ods file through the value attribute or the text node
1044 sub _getDataNodeODS
1045 {
1046     my $node = shift;
1047
1048     my $data;
1049     my $repeated = 0;
1050     if ($node->nodeType == 1 && $node->nodeName =~ /(?:table:)?table-cell/) {
1051         if ($node->hasAttributeNS('urn:oasis:names:tc:opendocument:xmlns:office:1.0', 'value')) {
1052             $data = $node->getAttributeNS('urn:oasis:names:tc:opendocument:xmlns:office:1.0', 'value');
1053         } elsif ($node->hasChildNodes()) {
1054             my @nodes2 = $node->getElementsByTagNameNS('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 'p');
1055             if (@nodes2 == 1 && $nodes2[0]->hasChildNodes()) {
1056                 $data = $nodes2[0]->firstChild->nodeValue;
1057             }
1058         }
1059         if ($node->hasAttributeNS('urn:oasis:names:tc:opendocument:xmlns:table:1.0', 'number-columns-repeated')) {
1060             $repeated = $node->getAttributeNS('urn:oasis:names:tc:opendocument:xmlns:table:1.0', 'number-columns-repeated');
1061         }
1062     }
1063     return ($data, $repeated);
1064 }#_getDataNodeODS
1065
1066
1067 # Get the data from a row of a spreadsheet
1068 sub _getDataFields
1069 {
1070     my ($frameworkcode, $node, $fields, $format) = @_;
1071
1072     my $dataFields = {};
1073     my @dataFieldsA = ();
1074     if ($node && $node->hasChildNodes()) {
1075         my $node2 = $node->firstChild;
1076         my ($data, $repeated);
1077         my $i = 0;
1078         my $ok = 0;
1079         $repeated = 0;
1080         while ($node2) {
1081             if ($format && $format eq 'ods') {
1082                 ($data, $repeated) = _getDataNodeODS($node2) if ($repeated <= 0);
1083                 $repeated--;
1084                 $ok = 1;
1085             } else {
1086                 if ($node2->nodeType == 1 && $node2->nodeName  =~ /(?:ss:)?Cell/) {
1087                     my @nodes3 = $node2->getElementsByTagNameNS('urn:schemas-microsoft-com:office:spreadsheet', 'Data');
1088                     if (@nodes3 == 1 && $nodes3[0]->hasChildNodes()) {
1089                         $data = $nodes3[0]->firstChild->nodeValue;
1090                         $ok = 1;
1091                     }
1092                 }
1093             }
1094             if ($ok) {
1095                 $data //= '';
1096                 $data = '' if ($data eq '#');
1097                 if ( $fields->[$i] eq 'frameworkcode' ) {
1098                     $data = $frameworkcode;
1099                 }
1100                 elsif ( $fields->[$i] eq 'isurl' ) {
1101                     $data = undef if defined $data && $data eq q{};
1102                 }
1103                 $dataFields->{$fields->[$i]} = $data;
1104                 push @dataFieldsA, $data;
1105                 $i++;
1106             }
1107             $ok = 0;
1108             $node2 = $node2->nextSibling if ($repeated <= 0);
1109         }
1110     }
1111     return ($dataFields, \@dataFieldsA);
1112 }#_getDataFields
1113
1114
1115 # Get the data from the first row to know the column names
1116 sub _getFields
1117 {
1118     my ($node, $fields, $format) = @_;
1119
1120     if ($node && $node->hasChildNodes()) {
1121         my $node2 = $node->firstChild;
1122         my ($data, $repeated);
1123         while ($node2) {
1124             if ($format && $format eq 'ods') {
1125                 ($data, $repeated) = _getDataNodeODS($node2);
1126                 push @$fields, $data if (defined($data));
1127             } else {
1128                 if ($node2->nodeType == 1 && $node2->nodeName =~ /(?:ss:)?Cell/) {
1129                     my @nodes3 = $node2->getElementsByTagNameNS('urn:schemas-microsoft-com:office:spreadsheet', 'Data');
1130                     if (@nodes3 == 1 && $nodes3[0]->hasChildNodes()) {
1131                         $data = $nodes3[0]->firstChild->nodeValue;
1132                         push @$fields, $data;
1133                     }
1134                 }
1135             }
1136             $node2 = $node2->nextSibling;
1137         }
1138     }
1139 }#_getFields
1140
1141
1142
1143
1144 1;
1145 __END__
1146
1147 =head1 AUTHOR
1148
1149 Koha Development Team <http://koha-community.org/>
1150
1151 =cut
1152
1153