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