source: gsdl/trunk/perllib/plugouts/FedoraMETSPlugout.pm@ 17200

Last change on this file since 17200 was 17063, checked in by ak19, 16 years ago

Special xml element added for images is now commented out since that prevents standalone images from being ingested into Fedora on Linux machines.

File size: 19.2 KB
Line 
1###########################################################################
2#
3# FedoraMETSPlugout.pm -- the plugout module for METS archives
4# A component of the Greenstone digital library software
5# from the New Zealand Digital Library Project at the
6# University of Waikato, New Zealand.
7#
8# Copyright (C) 2006 New Zealand Digital Library Project
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation; either version 2 of the License, or
13# (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# But WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23#
24###########################################################################
25
26package FedoraMETSPlugout;
27
28use strict;
29no strict 'refs';
30
31#eval {require bytes};
32#use util;
33use METSPlugout;
34#use docprint; # for escape_text
35
36sub BEGIN {
37 @FedoraMETSPlugout::ISA = ('METSPlugout');
38}
39
40my $arguments = [
41 { 'name' => "fedora_namespace",
42 'desc' => "{FedoraPlugout.fedora_namespace}",
43 'type' => "string",
44 'deft' => "greenstone",
45 'reqd' => "no",
46 'hiddengli' => "no"}
47 ];
48
49
50
51my $options = { 'name' => "FedoraMETSPlugout",
52 'desc' => "{FedoraMETSPlugout.desc}",
53 'abstract' => "no",
54 'inherits' => "yes",
55 'args' => $arguments
56 };
57
58
59sub new
60{
61 my ($class) = shift (@_);
62 my ($plugoutlist, $inputargs,$hashArgOptLists) = @_;
63 push(@$plugoutlist, $class);
64
65
66 if(defined $arguments){ push(@{$hashArgOptLists->{"ArgList"}},@{$arguments});}
67 if(defined $options) { push(@{$hashArgOptLists->{"OptList"}},$options)};
68
69 my $self = (defined $hashArgOptLists)? new METSPlugout($plugoutlist,$inputargs,$hashArgOptLists): new METSPlugout($plugoutlist,$inputargs);
70
71
72 return bless $self, $class;
73}
74
75
76sub output_mets_xml_header
77{
78 my $self = shift(@_);
79 my ($handle, $OID, $doc_title) = @_;
80
81 my $fnamespace = $self->{'fedora_namespace'};
82 my $oid_namespace = (defined $fnamespace) ? $fnamespace : "test";
83
84 my $collection = $ENV{'GSDLCOLLECTION'};
85
86 # Might need the following in the schemeLocation attribute for Fedora3
87 # http://www.fedora.info/definitions/1/0/mets-fedora-ext1-1.xsd
88 my $extra_attr = "OBJID=\"$oid_namespace:$collection-$OID\" TYPE=\"FedoraObject\" LABEL=\"$doc_title\"";
89
90 my $extra_schema = undef;
91
92 if ($ENV{'FEDORA_VERSION'} =~ m/^2/) { # checking if major version is 2
93 $extra_schema = "http://www.fedora.info/definitions/1/0/mets-fedora-ext.xsd";
94 }
95 else {
96 $extra_attr .= " EXT_VERSION=\"1.1\"";
97 }
98
99 $self->output_mets_xml_header_extra_attribute($handle,$extra_attr,$extra_schema);
100
101 print $handle '<mets:metsHdr RECORDSTATUS="A"/>'. "\n"; # A = active
102
103}
104
105#
106# Print out "family" of doctxt.xml files
107#
108
109sub saveas_doctxt_section
110{
111 my $self = shift (@_);
112 my ($doc_obj,$working_dir,$section) = @_;
113
114 my $section_ptr=$doc_obj->_lookup_section($section);
115 return unless defined $section_ptr;
116
117 my $section_fnum ="1". $section;
118 $section_fnum =~ s/\./_/g;
119
120 my $doc_txt_file = &util::filename_cat ($working_dir,"doctxt$section_fnum.xml");
121
122 $self->open_xslt_pipe($doc_txt_file,$self->{'xslt_txt'});
123
124 my $outhandler;
125
126 if (defined $self->{'xslt_writer'}){
127 $outhandler = $self->{'xslt_writer'};
128 }
129 else{
130 $outhandler = $self->get_output_handler($doc_txt_file);
131 }
132
133 $self->output_xml_header($outhandler);
134 $self->output_txt_section($outhandler,$doc_obj, $section);
135 $self->output_xml_footer($outhandler);
136
137
138 if (defined $self->{'xslt_writer'}){
139 $self->close_xslt_pipe();
140 }
141 else{
142 close($outhandler);
143 }
144
145
146 # Output all the subsections as separate files
147 foreach my $subsection (@{$section_ptr->{'subsection_order'}}){
148
149 $self->saveas_doctxt_section($doc_obj, $working_dir, "$section.$subsection");
150 }
151
152
153}
154
155
156sub saveas_doctxt
157{
158 my $self = shift (@_);
159 my ($doc_obj,$working_dir) = @_;
160
161 my $section = $doc_obj->get_top_section();
162
163 $self->saveas_doctxt_section($doc_obj,$working_dir,$section);
164
165 $self->saveas_toc($doc_obj,$working_dir);
166}
167
168sub buffer_toc
169{
170 my $self = shift (@_);
171 my ($doc_obj,$working_dir,$section,$depth) = @_;
172
173 my $section_ptr=$doc_obj->_lookup_section($section);
174 return "" unless defined $section_ptr;
175
176 my $all_text = "";
177
178 my $section_num ="1". $section;
179 my $indent = " " x ($depth*2);
180
181 $all_text .= "$indent<Section id=\"$section_num\">\n";
182
183 # Output all the subsections as separate files
184 foreach my $subsection (@{$section_ptr->{'subsection_order'}})
185 {
186 $all_text
187 .= $self->buffer_toc($doc_obj, $working_dir,
188 "$section.$subsection",$depth+1);
189 }
190
191 $all_text .= "$indent</Section>\n";
192
193 return $all_text;
194}
195
196
197sub saveas_toc
198{
199 my $self = shift (@_);
200 my ($doc_obj,$working_dir) = @_;
201
202 my $section = $doc_obj->get_top_section();
203 my $section_ptr=$doc_obj->_lookup_section($section);
204 my $num_subsections = scalar(@{$section_ptr->{'subsection_order'}});
205
206 # If num_subsections is 0, then there is no nested TOC
207
208 if ($num_subsections>0) {
209
210 my $doc_txt_file = &util::filename_cat ($working_dir,"doctoc.xml");
211
212 $self->open_xslt_pipe($doc_txt_file,$self->{'xslt_txt'});
213
214 my $outhandler;
215
216 if (defined $self->{'xslt_writer'}){
217 $outhandler = $self->{'xslt_writer'};
218 }
219 else{
220 $outhandler = $self->get_output_handler($doc_txt_file);
221 }
222 print $outhandler $self->buffer_toc($doc_obj, $working_dir, $section, 0);
223
224 if (defined $self->{'xslt_writer'}){
225 $self->close_xslt_pipe();
226 }
227 else{
228 close($outhandler);
229 }
230 }
231
232}
233
234
235sub buffer_mets_relsext_xml
236{
237 my $self = shift(@_);
238 my ($doc_obj) = @_;
239
240 my $OID = $doc_obj->get_OID();
241
242 my $fnamespace = $self->{'fedora_namespace'};
243 my $oid_namespace = (defined $fnamespace) ? $fnamespace : "test";
244 my $collection = $ENV{'GSDLCOLLECTION'};
245
246 my $fed_id = "$oid_namespace:$collection-$OID";
247
248 my $all_text = "";
249
250 my $top_section = $doc_obj->get_top_section();
251 my $plugin_type = $doc_obj->get_metadata_element($top_section,"Plugin");
252
253# Images do not get ingested into Fedora when on Linux if the following is included
254# Needs more investigation, since we'd like a working version of the following
255# in order to get thumbnails working and other stuff.
256# if ((defined $plugin_type) && ($plugin_type eq "ImagePlugin"))
257# {
258#
259# $all_text .= "<mets:amdSec ID=\"RELS-EXT\">\n";
260# $all_text .= " <mets:techMD ID=\"RELS-EXT1.0\" STATUS=\"A\">\n";
261# $all_text .= " <mets:mdWrap LABEL=\"RELS-EXT - RDF formatted relationship metadata\" MDTYPE=\"OTHER\" MIMETYPE=\"text/xml\">\n";
262# $all_text .= " <mets:xmlData>\n";
263# $all_text .= " <rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:fedora-model=\"info:fedora/fedora-system:def/model#\">\n";
264# $all_text .= " <rdf:Description rdf:about=\"info:fedora/$fed_id\">\n";
265# $all_text .= " <fedora-model:hasContentModel rdf:resource=\"info:fedora/demo:UVA_STD_IMAGE\"/>\n";
266# $all_text .= " </rdf:Description>\n";
267# $all_text .= " </rdf:RDF>\n";
268# $all_text .= " </mets:xmlData>\n";
269# $all_text .= " </mets:mdWrap>\n";
270# $all_text .= " </mets:techMD>\n";
271# $all_text .= "</mets:amdSec>\n";
272# }
273
274 return $all_text;
275}
276
277
278#
279# Print out docmets.xml file
280#
281sub output_mets_section
282{
283 my $self = shift(@_);
284 my ($handle, $doc_obj, $section, $working_dir) = @_;
285
286 # print out the dmdSection
287 print $handle $self->buffer_mets_dmdSection_section_xml($doc_obj,$section);
288
289 print $handle $self->buffer_mets_relsext_xml($doc_obj);
290
291 print $handle "<mets:fileSec>\n";
292 print $handle " <mets:fileGrp ID=\"DATASTREAMS\">\n";
293
294 # Generate Filestream for Table of Contents (TOC)
295 my $section_ptr=$doc_obj->_lookup_section($section);
296 my $num_subsections = scalar(@{$section_ptr->{'subsection_order'}});
297
298 # If num_subsections is 0, then there is no nested TOC
299
300 if ($num_subsections>0) {
301 print $handle $self->buffer_mets_fileSection_toc($doc_obj,$section,$working_dir);
302 }
303
304 # print out the fileSection by sections
305 print $handle $self->buffer_mets_fileSection_section_xml($doc_obj,$section,$working_dir);
306
307 # print out the whole fileSection
308 print $handle $self->buffer_mets_fileWhole_section_xml($doc_obj,$section,$working_dir);
309
310 print $handle " </mets:fileGrp>\n";
311 print $handle "</mets:fileSec>\n";
312
313 # print out the StructMapSection by sections
314
315 my $struct_type = "fedora:dsBindingMap";
316
317 # If document is going to make use of deminators (BMech and BDef) then
318 # need to code up more output XML here (structMap)and in
319 # METS:behaviorSec (Fedora extension?) sections
320
321}
322
323sub buffer_mets_amdSec_header
324{
325 my $self = shift(@_);
326 my ($section,$id) = @_;
327
328 # convert section number
329 my $section_num ="1". $section;
330
331 my $all_text = "";
332
333 my $label_attr = "";
334
335 $all_text .= "<mets:amdSec ID=\"$id$section\" >\n";
336 $all_text .= " <mets:techMD ID=\"$id$section.0\">\n"; # .0 fedora version number?
337
338 $label_attr = "LABEL=\"Metadata\"";
339
340 $all_text .= " <mets:mdWrap $label_attr MDTYPE=\"OTHER\" OTHERMDTYPE=\"gsdl3\" ID=\"".$id."gsdl$section_num\">\n";
341 $all_text .= " <mets:xmlData>\n";
342
343 return $all_text;
344
345}
346
347sub buffer_mets_amdSec_footer
348{
349 my $self = shift(@_);
350
351 my $all_text = "";
352
353 $all_text .= " </mets:xmlData>\n";
354 $all_text .= " </mets:mdWrap>\n";
355
356 $all_text .= " </mets:techMD>\n";
357 $all_text .= "</mets:amdSec>\n";
358
359 return $all_text;
360
361}
362
363sub oai_dc_metadata_xml
364{
365 my $self = shift(@_);
366 my ($doc_obj,$section) = @_;
367
368 my $all_text = "";
369
370 my $dc_namespace = "";
371 $dc_namespace .= "xmlns:dc=\"http://purl.org/dc/elements/1.1/\"";
372 $dc_namespace .= " xmlns:oai_dc=\"http://www.openarchives.org/OAI/2.0/oai_dc/\" ";
373
374 $all_text .= " <oai_dc:dc $dc_namespace>\n";
375
376 $all_text .= $self->get_dc_metadata($doc_obj, $section,"oai_dc");
377 $all_text .= " </oai_dc:dc>\n";
378
379 return $all_text;
380}
381
382
383
384
385
386# Work out the what the metadata set prefixes (dc,dls etc.) are for
387# this document
388
389sub metadata_set_prefixes
390{
391 my $self = shift(@_);
392 my ($doc_obj, $section) = @_;
393
394 $section="" unless defined $section;
395
396 my $section_ptr = $doc_obj->_lookup_section($section);
397 return {} unless defined $section_ptr;
398
399 my $unique_prefix = {};
400
401 foreach my $data (@{$section_ptr->{'metadata'}})
402 {
403 my ($prefix) = ($data->[0]=~ m/^(.*?)\./);
404
405 if (defined $prefix)
406 {
407 next if ($prefix eq "dc"); # skip dublin core as handled separately elsewhere
408
409 $unique_prefix->{$prefix} = 1;
410 }
411 else
412 {
413 $unique_prefix->{"ex"} = 1;
414 }
415
416 }
417
418 return $unique_prefix;
419}
420
421
422sub mds_metadata_xml
423{
424 my $self = shift(@_);
425 my ($doc_obj, $section, $mds_prefix, $namespace) = @_;
426
427 # build up string of metadata with $mds_prefix
428 $section="" unless defined $section;
429
430 my $section_ptr = $doc_obj->_lookup_section($section);
431 return "" unless defined $section_ptr;
432
433 my $all_text="";
434 $all_text .= " <$mds_prefix:$mds_prefix $namespace>\n";
435
436
437 foreach my $data (@{$section_ptr->{'metadata'}})
438 {
439 if ($data->[0]=~ m/^(?:(.*?)\.)?(.*)$/)
440 {
441 my $curr_mds_prefix = $1;
442 my $mds_full_element = $2;
443
444 $curr_mds_prefix = "ex" unless defined $curr_mds_prefix;
445
446 if ($curr_mds_prefix eq $mds_prefix)
447 {
448 # split up full element in the form Title^en into element=Title, attr="en"
449 my ($mds_element,$subelem) = ($mds_full_element =~ m/^(.*?)(?:\^(.*))?$/);
450 my $mds_attr = (defined $subelem) ? "qualifier=\"$subelem\"" : "";
451
452 my $escaped_value = &docprint::escape_text($data->[1]);
453
454 $all_text .= " <$mds_prefix:metadata name=\"$mds_element\" $mds_attr>$escaped_value</$mds_prefix:metadata>\n";
455 }
456 }
457 }
458
459 $all_text .= " </$mds_prefix:$mds_prefix>\n";
460
461
462 $all_text =~ s/[\x00-\x09\x0B\x0C\x0E-\x1F]//g;
463
464 return $all_text;
465}
466
467
468
469sub buffer_mets_dmdSection_section_xml
470{
471 my $self = shift(@_);
472 my ($doc_obj,$section) = @_;
473
474 $section="" unless defined $section;
475
476 my $section_ptr=$doc_obj->_lookup_section($section);
477 return "" unless defined $section_ptr;
478
479 my $all_text = "";
480
481 $all_text .= $self->buffer_mets_amdSec_header($section,"DC");
482 $all_text .= $self->oai_dc_metadata_xml($doc_obj,$section);
483 $all_text .= $self->buffer_mets_amdSec_footer($section);
484
485 # for each metadata set
486 my $md_sets = $self->metadata_set_prefixes($doc_obj,$section);
487
488 foreach my $md_set (keys %$md_sets)
489 {
490 # Greenstone's agnostic approach to metadata sets conflicts with
491 # Fedoras more clinically prescribed one. Fake a namespace for
492 # each $md_set to keep both sides happy
493
494 my $fake_namespace
495 = "xmlns:$md_set=\"http://www.greenstone.org/namespace/fake/$md_set\"";
496 my $id_caps = $md_set;
497 $id_caps =~ tr/[a-z]/[A-Z]/;
498
499 $all_text .= $self->buffer_mets_amdSec_header($section,$id_caps);
500 $all_text .= $self->mds_metadata_xml($doc_obj,$section,$md_set,$fake_namespace);
501 $all_text .= $self->buffer_mets_amdSec_footer($section);
502 }
503
504
505 foreach my $subsection (@{$section_ptr->{'subsection_order'}}){
506 $all_text .= $self->buffer_mets_dmdSection_section_xml($doc_obj,"$section.$subsection");
507 }
508
509 $all_text =~ s/[\x00-\x09\x0B\x0C\x0E-\x1F]//g;
510
511 return $all_text;
512}
513
514
515sub doctxt_to_xlink
516{
517 my $self = shift @_;
518 my ($fname,$working_dir) = @_;
519
520 my $xlink_href;
521
522 my $fedora_prefix = $ENV{'FEDORA_HOME'};
523 if (!defined $fedora_prefix) {
524 $xlink_href = "file:$fname";
525 }
526 else
527 {
528 my $collectparent;
529 if (defined $ENV{'GSDL3SRCHOME'}) { # we're dealing with a GS3 server
530 if(defined $ENV{'GSDL3HOME'}) { # in case the web directory is located in a separate place
531 $collectparent = &util::filename_cat($ENV{'GSDL3HOME'},"sites","localsite");
532 }
533 else { # try the default location for the web directory
534 $collectparent = &util::filename_cat($ENV{'GSDL3SRCHOME'},"web","sites","localsite");
535 }
536 }
537 else {
538 # greenstone 2
539 $collectparent = $ENV{'GSDLHOME'};
540 }
541
542 my $gsdl_href = &util::filename_cat($working_dir, $fname);
543 $collectparent =~ s/\\/\\\\/g; # escape reserved metacharacter \ in path (by replacing it with \\) for substitution
544 $gsdl_href =~ s/^$collectparent(\/|\\)?//; # remove the collectparent path in gsdl_href and any trailing slash
545 $gsdl_href =~ s/\\/\//g; # make sure we have url paths (which only use / not \)
546 $gsdl_href = "/gsdl/$gsdl_href"; # prepend gsdl
547
548 my $fserver = $ENV{'FEDORA_HOSTNAME'};
549 my $fport = $ENV{'FEDORA_SERVER_PORT'};
550
551 my $fdomain = "http://$fserver:$fport";
552 $xlink_href = "$fdomain$gsdl_href";
553 }
554
555
556 return $xlink_href;
557
558}
559
560
561sub buffer_mets_fileSection_toc
562{
563 my $self = shift(@_);
564 my ($doc_obj,$section,$working_dir) = @_;
565
566 my $opt_attr = "OWNERID=\"M\"";
567
568 my $all_text = ' <mets:fileGrp ID="TOC">'. "\n";
569 $all_text .= " <mets:file MIMETYPE=\"text/xml\" ID=\"FILETOC\" $opt_attr >\n";
570 my $xlink = $self->doctxt_to_xlink("doctoc.xml",$working_dir);
571
572 $all_text .= ' <mets:FLocat LOCTYPE="URL" xlink:href="'.$xlink.'"';
573
574 $all_text .= ' xlink:title="Table of Contents"/>' . "\n";
575 $all_text .= " </mets:file>\n";
576 $all_text .= " </mets:fileGrp>\n";
577
578 return $all_text;
579}
580
581
582sub buffer_mets_fileSection_section_xml
583{
584 my $self = shift(@_);
585 my ($doc_obj,$section,$working_dir) = @_;
586
587 my $is_txt_split = 1;
588 my $opt_owner_id = "OWNERID=\"M\"";
589
590 my $all_text
591 = $self->SUPER::buffer_mets_fileSection_section_xml($doc_obj,$section,$working_dir,$is_txt_split, $opt_owner_id,"SECTION");
592
593
594 return $all_text;
595}
596
597sub buffer_mets_fileWhole_section_xml
598{
599 my $self = shift(@_);
600 my ($doc_obj,$section,$working_dir) = @_;
601
602 my $section_ptr = $doc_obj-> _lookup_section($section);
603 return "" unless defined $section_ptr;
604
605 my $all_text="";
606
607 my $fileID=0;
608
609 # Output the fileSection for the whole section
610 # => get the sourcefile and associative file
611
612 my $id_root = "";
613 my $opt_owner_id = "OWNERID=\"M\"";
614
615
616 my $first_assocfile = 1;
617
618 foreach my $data (@{$section_ptr->{'metadata'}}){
619 my $escaped_value = &docprint::escape_text($data->[1]);
620
621 if ($data->[0] eq "gsdlassocfile"){
622
623 $escaped_value =~ m/^(.*?):(.*):(.*)$/;
624 my $assoc_file = $1;
625 my $mime_type = $2;
626 my $assoc_dir = $3;
627
628 if ($first_assocfile) {
629 $id_root = "url";
630 $first_assocfile = 0;
631 }
632 else {
633 $id_root = "FG$assoc_file";
634 }
635
636 $id_root =~ s/\//_/g;
637 $all_text .= " <mets:fileGrp ID=\"$id_root\">\n";
638
639 # The assoc_file's name may be url-encoded, so the xlink_href in the <mets:FLocat>
640 # element must be the url to this (possibly url-encoded) filename
641 my $assocfile_url = &unicode::filename_to_url($assoc_file);
642 my $assfilePath = ($assoc_dir eq "") ? $assocfile_url : "$assoc_dir/$assocfile_url";
643 ++$fileID;
644
645 my $mime_attr = "MIMETYPE=\"$mime_type\"";
646 my $xlink_title = "xlink:title=\"$assoc_file\"";
647
648 my $id_attr;
649 my $xlink_href;
650
651 $id_attr = "ID=\"F$id_root.0\"";
652
653 my $fedora_prefix = $ENV{'FEDORA_HOME'};
654 if (!defined $fedora_prefix) {
655 $xlink_href = "xlink:href=\"$assfilePath\"";
656 }
657 else
658 {
659 my $collectparent;
660 if (defined $ENV{'GSDL3SRCHOME'}) { # we're dealing with a GS3 server
661 if(defined $ENV{'GSDL3HOME'}) { # in case the web directory is located in a separate place
662 $collectparent = &util::filename_cat($ENV{'GSDL3HOME'},"sites","localsite");
663 }
664 else { # try the default location for the web directory
665 $collectparent = &util::filename_cat($ENV{'GSDL3SRCHOME'},"web","sites","localsite");
666 }
667 }
668 else {
669 # greenstone 2
670 $collectparent = $ENV{'GSDLHOME'};
671 }
672
673 my $gsdl_href = &util::filename_cat($working_dir,$assfilePath);
674 $collectparent =~ s/\\/\\\\/g; # escape reserved metacharacter \ in path (by replacing it with \\) for substitution
675 $gsdl_href =~ s/^$collectparent(\/|\\)?//; # remove the collectparent path in gsdl_href and any trailing slash
676 $gsdl_href =~ s/\\/\//g; # make sure we have url paths (which only use / not \)
677 $gsdl_href = "/gsdl/$gsdl_href"; # prepend gsdl
678
679 my $fserver = $ENV{'FEDORA_HOSTNAME'};
680 my $fport = $ENV{'FEDORA_SERVER_PORT'};
681
682 my $fdomain = "http://$fserver:$fport";
683 $xlink_href = "xlink:href=\"$fdomain$gsdl_href\"";
684 }
685
686 my $top_section = $doc_obj->get_top_section();
687 my $id = $doc_obj->get_metadata_element($top_section,"Identifier");
688
689 print STDERR "**** mime-type: $mime_attr\n";
690
691 $all_text .= " <mets:file $mime_attr $id_attr $opt_owner_id >\n";
692 $all_text .= " <mets:FLocat LOCTYPE=\"URL\" $xlink_href $xlink_title />\n";
693
694 $all_text .= " </mets:file>\n";
695
696 $all_text .= " </mets:fileGrp>\n";
697 }
698 }
699
700 $all_text =~ s/[\x00-\x09\x0B\x0C\x0E-\x1F]//g;
701
702 return $all_text;
703}
704
705
7061;
Note: See TracBrowser for help on using the repository browser.