51c0aae74218efbf18780802c6f30b0782815fe3
[srvgit] / 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 use Text::CSV_XS;
27 use List::MoreUtils qw(indexes);
28
29 use C4::Context;
30 use Koha::Logger;
31
32 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
33
34 BEGIN {
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                 Koha::Logger->get->warn("Error ExportFramework $@");
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 my $field (@fields) {
283                 my $value = $hashRef->{$field} // q||;
284                 $value =~ s/[\r\n]//g;
285                 $$strCSV .= '"' . $value . '",';
286             }
287             chop $$strCSV;
288             $$strCSV .= chr(10);
289         }
290         $$strCSV .= chr(10);
291         for (@fields) {
292             # Separator for change of table
293             $$strCSV .= '"#-#",';
294         }
295         chop $$strCSV;
296         $$strCSV .= chr(10);
297         $$strCSV .= chr(10);
298     };
299     if ($@) {
300         Koha::Logger->get->warn("Error _export_table_csv $@");
301         return 0;
302     }
303     return 1;
304 }#_export_table_csv
305
306
307 # Export the mysql table to an ods file
308 sub _export_table_ods
309 {
310     my ($table, $dbh, $dom, $root, $frameworkcode) = @_;
311
312     eval {
313         my $elementTable = $dom->createElement('table:table');
314         $elementTable->setAttribute('table:name', $table);
315         $elementTable->setAttribute('table:print', 'false');
316         $root->appendChild($elementTable);
317         my $elementRow = $dom->createElement('table:table-row');
318         $elementTable->appendChild($elementRow);
319
320         my $elementCell;
321         my $elementData;
322         # First row with the name of the columns
323         my $query = 'SHOW COLUMNS FROM ' . $table;
324         my $sth = $dbh->prepare($query);
325         $sth->execute();
326         my @fields = ();
327         while (my $hashRef = $sth->fetchrow_hashref) {
328             $elementCell = $dom->createElement('table:table-cell');
329             $elementCell->setAttribute('office:value-type', 'string');
330             $elementCell->setAttribute('office:value', $hashRef->{Field});
331             $elementRow->appendChild($elementCell);
332             $elementData = $dom->createElement('text:p');
333             $elementCell->appendChild($elementData);
334             $elementData->appendTextNode($hashRef->{Field});
335             push @fields, {name => $hashRef->{Field}, type => ($hashRef->{Type} =~ /int/i)?'float':'string'};
336         }
337         # Populate rows with the data from mysql
338         $query = 'SELECT * FROM ' . $table . ' WHERE frameworkcode=?';
339         $sth = $dbh->prepare($query);
340         $sth->execute($frameworkcode);
341         my $data;
342         while (my $hashRef = $sth->fetchrow_hashref) {
343             $elementRow = $dom->createElement('table:table-row');
344             $elementTable->appendChild($elementRow);
345             for (@fields) {
346                 $data = $hashRef->{$_->{name}};
347                 if ($_->{type} eq 'float' && !defined($data)) {
348                     $data = '0';
349                 } elsif ($_->{type} eq 'string' && !defined($data)) {
350                     $data = q{};
351                 } elsif ($_->{type} eq 'string' && (!$data && $data ne '0')) {
352                     $data = '#';
353                 }
354                 $data = _parseContent2Xml($data) if ($_->{type} eq 'string');
355                 $elementCell = $dom->createElement('table:table-cell');
356                 $elementCell->setAttribute('office:value-type', $_->{type});
357                 $elementCell->setAttribute('office:value', $data);
358                 $elementRow->appendChild($elementCell);
359                 $elementData = $dom->createElement('text:p');
360                 $elementCell->appendChild($elementData);
361                 $elementData->appendTextNode($data);
362             }
363         }
364     };
365     if ($@) {
366         Koha::Logger->get->warn("Error _export_table_ods $@");
367         return 0;
368     }
369     return 1;
370 }#_export_table_ods
371
372
373 # Export the mysql table to an excel-xml (openoffice/libreoffice compatible) file
374 sub _export_table_excel
375 {
376     my ($table, $dbh, $dom, $root, $frameworkcode) = @_;
377
378     eval {
379         my $elementWS = $dom->createElement('Worksheet');
380         $elementWS->setAttribute('ss:Name', $table);
381         $root->appendChild($elementWS);
382         my $elementTable = $dom->createElement('ss:Table');
383         $elementWS->appendChild($elementTable);
384         my $elementRow = $dom->createElement('ss:Row');
385         $elementTable->appendChild($elementRow);
386
387         # First row with the name of the columns
388         my $elementCell;
389         my $elementData;
390         my $query = 'SHOW COLUMNS FROM ' . $table;
391         my $sth = $dbh->prepare($query);
392         $sth->execute();
393         my @fields = ();
394         while (my $hashRef = $sth->fetchrow_hashref) {
395             $elementCell = $dom->createElement('ss:Cell');
396             $elementCell->setAttribute('ss:StyleID', 's27');
397             $elementRow->appendChild($elementCell);
398             $elementData = $dom->createElement('ss:Data');
399             $elementData->setAttribute('ss:Type', 'String');
400             $elementCell->appendChild($elementData);
401             $elementData->appendTextNode($hashRef->{Field});
402             push @fields, {name => $hashRef->{Field}, type => ($hashRef->{Type} =~ /int/i)?'Number':'String'};
403         }
404         # Populate rows with the data from mysql
405         $query = 'SELECT * FROM ' . $table . ' WHERE frameworkcode=?';
406         $sth = $dbh->prepare($query);
407         $sth->execute($frameworkcode);
408         my $data;
409         while (my $hashRef = $sth->fetchrow_hashref) {
410             $elementRow = $dom->createElement('ss:Row');
411             $elementTable->appendChild($elementRow);
412             for (@fields) {
413                 $elementCell = $dom->createElement('ss:Cell');
414                 $elementRow->appendChild($elementCell);
415                 $elementData = $dom->createElement('ss:Data');
416                 $elementData->setAttribute('ss:Type', $_->{type});
417                 $elementCell->appendChild($elementData);
418                 $data = $hashRef->{$_->{name}};
419                 if ($_->{type} eq 'Number' && !defined($data)) {
420                     $data = '0';
421                 } elsif ($_->{type} eq 'String' && !defined($data)) {
422                     $data = q{};
423                 } elsif ($_->{type} eq 'String' && (!$data && $data ne '0')) {
424                     $data = '#';
425                 }
426                 $elementData->appendTextNode(($_->{type} eq 'String')?_parseContent2Xml($data):$data);
427             }
428         }
429     };
430     if ($@) {
431         Koha::Logger->get->warn("Error _export_table_excel $@");
432         return 0;
433     }
434     return 1;
435 }#_export_table_excel
436
437
438
439
440
441
442
443 # Format chars problematics to a correct format for xml.
444 sub _parseContent2Xml
445 {
446     my $content = shift;
447
448     $content =~ s/\&(?![a-zA-Z#0-9]{1,4};)/&amp;/g;
449     $content =~ s/</&lt;/g;
450     $content =~ s/>/&gt;/g;
451     return $content;
452 }#_parseContent2Xml
453
454
455 # Get the tmp directory on the system
456 sub _getTmp
457 {
458     my $tmp = '/tmp';
459     if ($ENV{'TMP'} && -d $ENV{'TMP'}) {
460         $tmp = $ENV{'TMP'};
461     } elsif ($ENV{'TMPDIR'} && -d $ENV{'TMPDIR'}) {
462         $tmp = $ENV{'TMPDIR'};
463     } elsif ($ENV{'TEMP'} && -d $ENV{'TEMP'}) {
464         $tmp = $ENV{'TEMP'};
465     }
466     return $tmp;
467 }#_getTmp
468
469
470 # Create our tempdir directory for the ods process
471 sub _createTmpDir
472 {
473     my $tmp = shift;
474
475     my $tempdir = (-d $tmp)?$tmp . '/':'./';
476     $tempdir .= 'tmp_ods_' . Digest::MD5::md5_hex(Digest::MD5::md5_hex(time().{}.rand().{}.$$));
477     eval {
478         mkdir $tempdir;
479     };
480     if ($@) {
481         return;
482     } else {
483         return $tempdir;
484     }
485 }#_createTmpDir
486
487 =head2 createODS
488
489 Creates a temporary directory to create the ods file and read it to store its content in a string.
490
491 return :
492 success
493
494 =cut
495
496 sub createODS
497 {
498     my ($strContent, $lang, $strODSRef) = @_;
499
500     my $tmp = _getTmp();
501     my $tempModule = 1;
502     my $tempdir;
503     eval {
504         require File::Temp;
505         import File::Temp qw/ tempfile tempdir /;
506         $tempdir = tempdir ( 'tmp_ods_' . $$ . '_XXXXXXXX', DIR => (-d $tmp)?$tmp:'.', CLEANUP => 1);
507     };
508     if ($@) {
509         $tempModule = 0;
510         $tempdir = _createTmpDir($tmp);
511     }
512     if ($tempdir) {
513         my $fh;
514         # populate tempdir directory with the ods elements
515         eval {
516             if (open($fh, '>',  "$tempdir/content.xml")) {
517                 print {$fh} $strContent;
518                 close($fh);
519             }
520             if (open($fh, '>', "$tempdir/mimetype")) {
521                 print {$fh} 'application/vnd.oasis.opendocument.spreadsheet';
522                 close($fh);
523             }
524             if (open($fh, '>', "$tempdir/meta.xml")) {
525                 print {$fh} _getMeta($lang);
526                 close($fh);
527             }
528             if (open($fh, '>', "$tempdir/styles.xml")) {
529                 print {$fh} ODS_STYLES_STR;
530                 close($fh);
531             }
532             if (open($fh, '>', "$tempdir/settings.xml")) {
533                 print {$fh} ODS_SETTINGS_STR;
534                 close($fh);
535             }
536             mkdir($tempdir.'/META-INF/');
537             mkdir($tempdir.'/Configurations2/');
538             mkdir($tempdir.'/Configurations2/acceleator/');
539             mkdir($tempdir.'/Configurations2/images/');
540             mkdir($tempdir.'/Configurations2/popupmenu/');
541             mkdir($tempdir.'/Configurations2/statusbar/');
542             mkdir($tempdir.'/Configurations2/floater/');
543             mkdir($tempdir.'/Configurations2/menubar/');
544             mkdir($tempdir.'/Configurations2/progressbar/');
545             mkdir($tempdir.'/Configurations2/toolbar/');
546
547             if (open($fh, '>', "$tempdir/META-INF/manifest.xml")) {
548                 print {$fh} ODS_MANIFEST_STR;
549                 close($fh);
550             }
551         };
552         if ($@) {
553             Koha::Logger->get->warn("Error createODS $@");
554         } else {
555             # create ods file from tempdir directory
556             eval {
557                 require Archive::Zip;
558                 import Archive::Zip qw( :ERROR_CODES :CONSTANTS );
559                 my $zip = Archive::Zip->new();
560                 $zip->addTree( $tempdir, '' );
561                 $zip->writeToFileNamed($tempdir . '/new.ods');
562             };
563             if ($@) {
564                 my $cmd = qx(which zip 2>/dev/null || whereis zip);
565                 chomp $cmd;
566                 $cmd = 'zip' if (!$cmd || !-x $cmd);
567                 system("cd $tempdir && $cmd -r new.ods ./");
568             }
569             my $ok = 0;
570             # read ods file and return as a string
571             if (-f "$tempdir/new.ods") {
572                 if (open ($fh, '<', "$tempdir/new.ods")) {
573                     binmode $fh;
574                     my $buffer;
575                     while (read ($fh, $buffer, 65536)) {
576                         $$strODSRef .= $buffer;
577                     }
578                     close($fh);
579                     $ok = 1;
580                 }
581             }
582             # delete tempdir directory
583             if (!$tempModule && $tempdir) {
584                 eval {
585                     require File::Path;
586                     import File::Temp qw/ rmtree /;
587                     rmtree($tempdir);
588                 };
589                 if ($@) {
590                     system("rm -rf $tempdir");
591                 }
592             }
593             return 1 if ($ok);
594         }
595     }
596     return 0;
597 }#createODS
598
599
600 # return Meta content for ods file
601 sub _getMeta
602 {
603     my $lang = shift;
604
605     my $myDate = strftime ("%Y-%m-%dT%H:%M:%S", localtime(time()));
606     my $meta = '<?xml version="1.0" encoding="UTF-8"?>
607     <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">
608         <office:meta>
609             <meta:generator>ods-php</meta:generator>
610             <meta:creation-date>' . $myDate . '</meta:creation-date>
611             <dc:date>' . $myDate . '</dc:date>
612             <dc:language>' . $lang . '</dc:language>
613             <meta:editing-cycles>2</meta:editing-cycles>
614             <meta:editing-duration>PT15S</meta:editing-duration>
615             <meta:user-defined meta:name="Info 1"/>
616             <meta:user-defined meta:name="Info 2"/>
617             <meta:user-defined meta:name="Info 3"/>
618             <meta:user-defined meta:name="Info 4"/>
619         </office:meta>
620     </office:document-meta>';
621     return $meta;
622 }#_getMeta
623
624
625 =head2 ImportFramework
626
627 Import all the information of a Framework from a excel-xml/ods file.
628
629 return :
630 success
631
632 =cut
633
634 sub ImportFramework
635 {
636     my ($filename, $frameworkcode, $deleteFilename) = @_;
637
638     my $tempdir;
639     my $ok = -1;
640     my $dbh = C4::Context->dbh;
641     if (-r $filename && $dbh) {
642         my $extension = '';
643         if ($filename =~ /\.(csv|ods|xml)$/i) {
644             $extension = lc($1);
645         } else {
646             unlink ($filename) if ($deleteFilename); # remove temporary file
647             return -1;
648         }
649         if ($extension eq 'ods') {
650             ($tempdir, $filename) = _openODS($filename, $deleteFilename);
651         }
652         if ($filename) {
653             my $dom;
654             eval {
655                 if ($extension eq 'ods' || $extension eq 'xml') {
656                     # They have xml structure, so read it on a dom object
657                     my $parser = XML::LibXML->new();
658                     $dom = $parser->parse_file($filename);
659                     if ($dom) {
660                         my $root = $dom->documentElement();
661                     }
662                 } else {
663                     # They are text files, so open it to read
664                     open($dom, '<', $filename);
665                 }
666                 if ($dom) {
667                     # Process both tables
668                     my $numDeleted = 0;
669                     my $numDeletedAux = 0;
670                     if (($numDeletedAux = _import_table($dbh, 'marc_tag_structure', $frameworkcode, $dom, ['frameworkcode', 'tagfield'], $extension)) >= 0) {
671                         $numDeleted += $numDeletedAux if ($numDeletedAux > 0);
672                         if (($numDeletedAux = _import_table($dbh, 'marc_subfield_structure', $frameworkcode, $dom, ['frameworkcode', 'tagfield', 'tagsubfield'], $extension)) >= 0) {
673                             $numDeleted += $numDeletedAux if ($numDeletedAux > 0);
674                             $ok = ($numDeleted > 0)?$numDeleted:0;
675                         }
676                     }
677                 } else {
678                     Koha::Logger->get->warn("Error ImportFramework couldn't create dom");
679                 }
680             };
681             if ($@) {
682                 Koha::Logger->get->warn("Error ImportFramework $@");
683             } else {
684                 if ($extension eq 'csv') {
685                     close($dom) if ($dom);
686                 }
687             }
688         }
689         unlink ($filename) if ($deleteFilename); # remove temporary file
690     } else {
691         Koha::Logger->get->warn("Error ImportFramework no conex to database or not readeable $filename");
692     }
693     if ($deleteFilename && $tempdir && -d $tempdir && -w $tempdir) {
694         eval {
695             require File::Path;
696             import File::Temp qw/ rmtree /;
697             rmtree($tempdir);
698         };
699         if ($@) {
700             system("rm -rf $tempdir");
701         }
702     }
703     return $ok;
704 }#ImportFramework
705
706 # Open (uncompress) ods file and return the content.xml file
707 sub _openODS
708 {
709     my ($filename, $deleteFilename) = @_;
710
711     my $tmp = _getTmp();
712     my $tempModule = 1;
713     my $tempdir;
714     eval {
715         require File::Temp;
716         import File::Temp qw/ tempfile tempdir /;
717         $tempdir = tempdir ( 'tmp_ods_' . $$ . '_XXXXXXXX', DIR => (-d $tmp)?$tmp:'.', CLEANUP => 1);
718     };
719     if ($@) {
720         $tempModule = 0;
721         $tempdir = _createTmpDir($tmp);
722     }
723     if ($tempdir) {
724         eval {
725             require Archive::Zip;
726             import Archive::Zip qw( :ERROR_CODES :CONSTANTS );
727             my $zip = Archive::Zip->new($filename);
728             foreach my $file ($zip->members) {
729                 next if ($file->isDirectory);
730                 (my $extractName = $file->fileName) =~ s{.*/}{};
731                 next unless ($extractName eq 'content.xml');
732                 $file->extractToFileNamed("$tempdir/$extractName");
733             }
734         };
735         if ($@) {
736             my $cmd = qx(which unzip 2>/dev/null || whereis unzip);
737             chomp $cmd;
738             $cmd = 'unzip' if (!$cmd || !-x $cmd);
739             system("$cmd $filename -d $tempdir");
740         }
741         if (-f "$tempdir/content.xml") {
742             unlink ($filename) if ($deleteFilename);
743             return ($tempdir, "$tempdir/content.xml");
744         }
745     }
746     unlink ($filename) if ($deleteFilename);
747     return ($tempdir, undef);
748 }#_openODS
749
750
751
752 # Check the table and columns corresponds with worksheet
753 sub _check_validity_worksheet
754 {
755     my ($dbh, $table, $nodeFields, $fieldsA, $format) = @_;
756
757     my $ret = 0;
758     eval {
759         my $query = 'DESCRIBE ' . $table;
760         my $sth = $dbh->prepare($query);
761         $sth->execute();
762         $sth->finish;
763         $query = 'SHOW COLUMNS FROM ' . $table;
764         $sth = $dbh->prepare($query);
765         $sth->execute();
766         my $fields = {};
767         while (my $hashRef = $sth->fetchrow_hashref) {
768             $fields->{$hashRef->{Field}} = $hashRef->{Field};
769         }
770         my @fields;
771         my $fieldsR;
772         if ($fieldsA) {
773             $fieldsR = $fieldsA;
774         } else {
775             $fieldsR = \@fields;
776             _getFields($nodeFields, $fieldsR, $format);
777         }
778         $ret = 1;
779         for (@$fieldsR) {
780             unless (exists($fields->{$_})) {
781                 $ret = 0;
782                 last;
783             }
784         }
785     };
786     return $ret;
787 }#_check_validity_worksheet
788
789
790 # Import the data from an excel-xml/ods to mysql tables.
791 sub _import_table
792 {
793     my ($dbh, $table, $frameworkcode, $dom, $PKArray, $format) = @_;
794     my %fields2Delete;
795     my $query;
796     my @fields;
797     # Create hash with all elements defined by primary key to know which ones to delete after parsing the spreadsheet
798     eval {
799         @fields = @$PKArray;
800         shift @fields;
801         $query = 'SELECT ' . join(',', @fields) . ' FROM ' . $table . ' WHERE frameworkcode=?';
802         my $sth = $dbh->prepare($query);
803         $sth->execute($frameworkcode);
804         my $field;
805         while (my $hashRef = $sth->fetchrow_hashref) {
806             $field = '';
807             map { $field .= $hashRef->{$_} . '_'; } @fields;
808             chop $field;
809             $fields2Delete{$field} = 1;
810         }
811         $sth->finish;
812     };
813     my $ok = 0;
814     if ($format eq 'csv') {
815         my @fieldsName = ();
816         eval {
817             my $query = 'SHOW COLUMNS FROM ' . $table;
818             my $sth = $dbh->prepare($query);
819             $sth->execute();
820             while (my $hashRef = $sth->fetchrow_hashref) {
821                 push @fieldsName, $hashRef->{Field};
822             }
823         };
824         $ok = _import_table_csv($dbh, $table, $frameworkcode, $dom, $PKArray, \%fields2Delete, \@fieldsName);
825     } elsif ($format eq 'ods') {
826         $ok = _import_table_ods($dbh, $table, $frameworkcode, $dom, $PKArray, \%fields2Delete);
827     } else {
828         $ok = _import_table_excel($dbh, $table, $frameworkcode, $dom, $PKArray, \%fields2Delete);
829     }
830     if ($ok) {
831         if (($ok = scalar(keys %fields2Delete)) > 0) {
832             $query = 'DELETE FROM ' . $table . ' WHERE ';
833             map {$query .= $_ . '=? AND ';} @$PKArray;
834             $query = substr($query, 0, -4);
835             my $sth = $dbh->prepare($query);
836             for (keys %fields2Delete) {
837                 eval {
838                     $sth->execute(($frameworkcode, split('_', $_)));
839                 };
840             }
841         }
842     } else {
843         $ok = -1;
844     }
845     return $ok;
846 }#_import_table
847
848
849 # Insert/Update the row from the spreadsheet in the database
850 sub _processRow_DB
851 {
852     my ($dbh, $table, $fields, $dataStr, $updateStr, $dataFields, $dataFieldsHash, $PKArray, $fieldsPK, $fields2Delete) = @_;
853
854     my $ok = 0;
855     my $query;
856     $query = 'INSERT INTO ' . $table . ' (' . $fields . ') VALUES (' . $dataStr . ') ON DUPLICATE KEY UPDATE ' . $updateStr;
857     eval {
858         my $sth = $dbh->prepare($query);
859         $sth->execute((@$dataFields, @$dataFields));
860     };
861     if ($@) {
862         Koha::Logger->get->warn("Error _processRow_DB $@");
863     } else {
864         $ok = 1;
865     }
866     if ($ok) {
867         my $field = '';
868         map { $field .= $dataFieldsHash->{$_} . '_'; } @$fieldsPK;
869         chop $field;
870         delete $fields2Delete->{$field} if (exists($fields2Delete->{$field}));
871     }
872     return $ok;
873 }#_processRow_DB
874
875
876 # Process the rows of a worksheet and insert/update them in a mysql table.
877 sub _processRows_Table
878 {
879     my ($dbh, $frameworkcode, $nodeR, $table, $PKArray, $format, $fields2Delete) = @_;
880
881     my $query;
882     my @fields = ();
883     my $fields = '';
884     my $dataStr = '';
885     my $updateStr = '';
886     my $j = 0;
887     my $ok = 0;
888     my @fieldsPK = @$PKArray;
889     shift @fieldsPK;
890     while ($nodeR) {
891         if ($nodeR->nodeType == 1 && (($format && $format eq 'ods' && $nodeR->nodeName =~ /(?:table:)?table-row/) || ($nodeR->nodeName =~ /(?:ss:)?Row/)) && $nodeR->hasChildNodes()) {
892             if ($j == 0) {
893                 # Get name columns
894                 _getFields($nodeR, \@fields, $format);
895                 return 0 unless _check_validity_worksheet($dbh, $table, $nodeR, \@fields, $format);
896                 $fields = join(',', @fields);
897                 $dataStr = '';
898                 map { $dataStr .= '?,';} @fields;
899                 chop($dataStr) if ($dataStr);
900                 $updateStr = '';
901                 map { $updateStr .= $_ . '=?,';} @fields;
902                 chop($updateStr) if ($updateStr);
903             } else {
904                 # Get data from row
905                 my ($dataFields, $dataFieldsR) = _getDataFields($frameworkcode, $nodeR, \@fields, $format);
906                 if (scalar(@fields) == scalar(@$dataFieldsR)) {
907                     $ok = _processRow_DB($dbh, $table, $fields, $dataStr, $updateStr, $dataFieldsR, $dataFields, $PKArray, \@fieldsPK, $fields2Delete);
908                 } else {
909                     warn "$j don't match number of fields " . scalar(@fields) . ' vs ' . scalar(@$dataFieldsR) . "($dataStr)";
910                 }
911             }
912             $j++;
913         }
914         $nodeR = $nodeR->nextSibling;
915     }
916     return 1;
917 }#_processRows_Table
918
919
920
921
922 # Import worksheet from the csv file to the mysql table
923 sub _import_table_csv
924 {
925     my ($dbh, $table, $frameworkcode, $dom, $PKArray, $fields2Delete, $fields) = @_;
926
927     my $row = '';
928     my $partialRow = '';
929     my $numFields = @$fields;
930     my $fieldsNameRead = 0;
931     my @arrData;
932     my ($fieldsStr, $dataStr, $updateStr, @empty_indexes);
933     my @fieldsPK = @$PKArray;
934     shift @fieldsPK;
935     my $ok = 0;
936     my $pos = 0;
937     my $csv = Text::CSV_XS->new ({ binary => 1 });
938     while ( my $row = $csv->getline($dom) ) {
939         my @fields = @$row;
940         @arrData = @fields;
941         next if scalar @arrData == grep { $_ eq '' } @arrData; # Emtpy lines
942         #$arrData[0] = substr($arrData[0], 1) if ($arrData[0] =~ /^"/);
943         #$arrData[$#arrData] =~ s/[\r\n]+$//;
944         #chop $arrData[$#arrData] if ($arrData[$#arrData] =~ /"$/);
945         if (@arrData) {
946             if ($arrData[0] eq '#-#' && $arrData[$#arrData] eq '#-#') {
947                 # Change of table with separators #-#
948                 return 1;
949             } elsif ($fieldsNameRead && $arrData[0] eq 'tagfield') {
950                 # Change of table because we begin with field name with former field names read
951                 seek($dom, $pos, 0);
952                 return 1;
953             }
954             if (!$fieldsNameRead) {
955                 # New table, we read the field names
956                 $fieldsNameRead = 1;
957                 $fields = [@arrData];
958                 my $non_empty_fields = [ grep { $_ ne '' } @$fields ];
959                 @empty_indexes = indexes { $_ eq '' } @$fields;
960                 $fieldsStr = join(',', @$non_empty_fields);
961                 $dataStr = '';
962                 map { $dataStr .= '?,';} @$non_empty_fields;
963                 chop($dataStr) if ($dataStr);
964                 $updateStr = '';
965                 map { $updateStr .= $_ . '=?,';} @$non_empty_fields;
966                 chop($updateStr) if ($updateStr);
967             } else {
968                 # Read data
969                 my $j = 0;
970                 my %dataFields = ();
971                 my @values;
972                 for my $value (@arrData) {
973                     if ( grep { $_ == $j } @empty_indexes ) {
974                         # empty field
975                     } elsif ($fields->[$j] eq 'frameworkcode' && $value ne $frameworkcode) {
976                         $dataFields{$fields->[$j]} = $frameworkcode;
977                         push @values, $frameworkcode;
978                     } elsif ($fields->[$j] eq 'isurl' && defined $value && $value eq q{}) {
979                         $dataFields{$fields->[$j]} = undef;
980                         push @values, undef;
981                     } else {
982                         $dataFields{$fields->[$j]} = $value;
983                         push @values, $value;
984                     }
985                     $j++
986                 }
987                 $ok = _processRow_DB($dbh, $table, $fieldsStr, $dataStr, $updateStr, \@values, \%dataFields, $PKArray, \@fieldsPK, $fields2Delete);
988             }
989             $pos = tell($dom);
990         }
991         @arrData = ();
992     }
993     return $ok;
994 }#_import_table_csv
995
996
997 # Import worksheet from the ods content.xml file to the mysql table
998 sub _import_table_ods
999 {
1000     my ($dbh, $table, $frameworkcode, $dom, $PKArray, $fields2Delete) = @_;
1001
1002     my $xc = XML::LibXML::XPathContext->new($dom);
1003     $xc->registerNs('xmlns:office','urn:oasis:names:tc:opendocument:xmlns:office:1.0');
1004     $xc->registerNs('xmlns:table','urn:oasis:names:tc:opendocument:xmlns:table:1.0');
1005     $xc->registerNs('xmlns:text','urn:oasis:names:tc:opendocument:xmlns:text:1.0');
1006     my @nodes;
1007     @nodes = $xc->findnodes('//table:table[@table:name="' . $table . '"]');
1008     if (@nodes == 1 && $nodes[0]->hasChildNodes()) {
1009         my $nodeR = $nodes[0]->firstChild;
1010         return _processRows_Table($dbh, $frameworkcode, $nodeR, $table, $PKArray, 'ods', $fields2Delete);
1011     } else {
1012         Koha::Logger->get->warn("Error _import_table_ods there's not worksheet for $table");
1013     }
1014     return 0;
1015 }#_import_table_ods
1016
1017
1018 # Import worksheet from the excel-xml file to the mysql table
1019 sub _import_table_excel
1020 {
1021     my ($dbh, $table, $frameworkcode, $dom, $PKArray, $fields2Delete) = @_;
1022
1023     my $xc = XML::LibXML::XPathContext->new($dom);
1024     $xc->registerNs('xmlns','urn:schemas-microsoft-com:office:spreadsheet');
1025     $xc->registerNs('xmlns:ss','urn:schemas-microsoft-com:office:spreadsheet');
1026     $xc->registerNs('xmlns:x','urn:schemas-microsoft-com:office:excel');
1027     my @nodes;
1028     @nodes = $xc->findnodes('//ss:Worksheet[@ss:Name="' . $table . '"]');
1029     if (@nodes > 0) {
1030         for (my $i=0; $i < @nodes; $i++) {
1031             my @nodesT = $nodes[$i]->getElementsByTagNameNS('urn:schemas-microsoft-com:office:spreadsheet', 'Table');
1032             if (@nodesT == 1 && $nodesT[0]->hasChildNodes()) {
1033                 my $nodeR = $nodesT[0]->firstChild;
1034                 return _processRows_Table($dbh, $frameworkcode, $nodeR, $table, $PKArray, undef, $fields2Delete);
1035             }
1036         }
1037     } else {
1038         Koha::Logger->get->warn("Error _import_table_excel there's not worksheet for $table");
1039     }
1040     return 0;
1041 }#_import_table_excel
1042
1043
1044 # Get the data from a cell on a ods file through the value attribute or the text node
1045 sub _getDataNodeODS
1046 {
1047     my $node = shift;
1048
1049     my $data;
1050     my $repeated = 0;
1051     if ($node->nodeType == 1 && $node->nodeName =~ /(?:table:)?table-cell/) {
1052         if ($node->hasAttributeNS('urn:oasis:names:tc:opendocument:xmlns:office:1.0', 'value')) {
1053             $data = $node->getAttributeNS('urn:oasis:names:tc:opendocument:xmlns:office:1.0', 'value');
1054         } elsif ($node->hasChildNodes()) {
1055             my @nodes2 = $node->getElementsByTagNameNS('urn:oasis:names:tc:opendocument:xmlns:text:1.0', 'p');
1056             if (@nodes2 == 1 && $nodes2[0]->hasChildNodes()) {
1057                 $data = $nodes2[0]->firstChild->nodeValue;
1058             }
1059         }
1060         if ($node->hasAttributeNS('urn:oasis:names:tc:opendocument:xmlns:table:1.0', 'number-columns-repeated')) {
1061             $repeated = $node->getAttributeNS('urn:oasis:names:tc:opendocument:xmlns:table:1.0', 'number-columns-repeated');
1062         }
1063     }
1064     return ($data, $repeated);
1065 }#_getDataNodeODS
1066
1067
1068 # Get the data from a row of a spreadsheet
1069 sub _getDataFields
1070 {
1071     my ($frameworkcode, $node, $fields, $format) = @_;
1072
1073     my $dataFields = {};
1074     my @dataFieldsA = ();
1075     if ($node && $node->hasChildNodes()) {
1076         my $node2 = $node->firstChild;
1077         my ($data, $repeated);
1078         my $i = 0;
1079         my $ok = 0;
1080         $repeated = 0;
1081         while ($node2) {
1082             if ($format && $format eq 'ods') {
1083                 ($data, $repeated) = _getDataNodeODS($node2) if ($repeated <= 0);
1084                 $repeated--;
1085                 $ok = 1;
1086             } else {
1087                 if ($node2->nodeType == 1 && $node2->nodeName  =~ /(?:ss:)?Cell/) {
1088                     my @nodes3 = $node2->getElementsByTagNameNS('urn:schemas-microsoft-com:office:spreadsheet', 'Data');
1089                     if (@nodes3 == 1 && $nodes3[0]->hasChildNodes()) {
1090                         $data = $nodes3[0]->firstChild->nodeValue;
1091                         $ok = 1;
1092                     }
1093                 }
1094             }
1095             if ($ok) {
1096                 $data //= '';
1097                 $data = '' if ($data eq '#');
1098                 if ( $fields->[$i] eq 'frameworkcode' ) {
1099                     $data = $frameworkcode;
1100                 }
1101                 elsif ( $fields->[$i] eq 'isurl' ) {
1102                     $data = undef if defined $data && $data eq q{};
1103                 }
1104                 $dataFields->{$fields->[$i]} = $data;
1105                 push @dataFieldsA, $data;
1106                 $i++;
1107             }
1108             $ok = 0;
1109             $node2 = $node2->nextSibling if ($repeated <= 0);
1110         }
1111     }
1112     return ($dataFields, \@dataFieldsA);
1113 }#_getDataFields
1114
1115
1116 # Get the data from the first row to know the column names
1117 sub _getFields
1118 {
1119     my ($node, $fields, $format) = @_;
1120
1121     if ($node && $node->hasChildNodes()) {
1122         my $node2 = $node->firstChild;
1123         my ($data, $repeated);
1124         while ($node2) {
1125             if ($format && $format eq 'ods') {
1126                 ($data, $repeated) = _getDataNodeODS($node2);
1127                 push @$fields, $data if (defined($data));
1128             } else {
1129                 if ($node2->nodeType == 1 && $node2->nodeName =~ /(?:ss:)?Cell/) {
1130                     my @nodes3 = $node2->getElementsByTagNameNS('urn:schemas-microsoft-com:office:spreadsheet', 'Data');
1131                     if (@nodes3 == 1 && $nodes3[0]->hasChildNodes()) {
1132                         $data = $nodes3[0]->firstChild->nodeValue;
1133                         push @$fields, $data;
1134                     }
1135                 }
1136             }
1137             $node2 = $node2->nextSibling;
1138         }
1139     }
1140 }#_getFields
1141
1142
1143
1144
1145 1;
1146 __END__
1147
1148 =head1 AUTHOR
1149
1150 Koha Development Team <http://koha-community.org/>
1151
1152 =cut
1153
1154