KazV2/dockers/apikaz/source/Sympa/Tools/Data.pm
2024-06-03 18:43:35 +02:00

507 lines
13 KiB
Perl

# -*- indent-tabs-mode: nil; -*-
# vim:ft=perl:et:sw=4
# $Id$
# Sympa - SYsteme de Multi-Postage Automatique
#
# Copyright (c) 1997, 1998, 1999 Institut Pasteur & Christophe Wolfhugel
# Copyright (c) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
# 2006, 2007, 2008, 2009, 2010, 2011 Comite Reseau des Universites
# Copyright (c) 2011, 2012, 2013, 2014, 2015, 2016, 2017 GIP RENATER
# Copyright 2018 The Sympa Community. See the AUTHORS.md file at the
# top-level directory of this distribution and at
# <https://github.com/sympa-community/sympa.git>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
package Sympa::Tools::Data;
use strict;
use warnings;
use Encode qw();
use English qw(-no_match_vars);
use POSIX qw();
use XML::LibXML qw();
BEGIN { eval 'use Clone qw()'; }
use Sympa::Tools::Text;
## This applies recursively to a data structure
## The transformation subroutine is passed as a ref
sub recursive_transformation {
my ($var, $subref) = @_;
return unless (ref($var));
if (ref($var) eq 'ARRAY') {
foreach my $index (0 .. $#{$var}) {
if (ref($var->[$index])) {
recursive_transformation($var->[$index], $subref);
} else {
$var->[$index] = &{$subref}($var->[$index]);
}
}
} elsif (ref($var) eq 'HASH') {
foreach my $key (keys %{$var}) {
if (ref($var->{$key})) {
recursive_transformation($var->{$key}, $subref);
} else {
$var->{$key} = &{$subref}($var->{$key});
}
}
}
return;
}
## Dump a variable's content
sub dump_var {
my ($var, $level, $fd) = @_;
return undef unless ($fd);
if (ref($var)) {
if (ref($var) eq 'ARRAY') {
foreach my $index (0 .. $#{$var}) {
print $fd "\t" x $level . $index . "\n";
dump_var($var->[$index], $level + 1, $fd);
}
} elsif (ref($var) eq 'HASH'
|| ref($var) eq 'Sympa::Scenario'
|| ref($var) eq 'Sympa::List'
|| ref($var) eq 'CGI::Fast') {
foreach my $key (sort keys %{$var}) {
print $fd "\t" x $level . '_' . $key . '_' . "\n";
dump_var($var->{$key}, $level + 1, $fd);
}
} else {
printf $fd "\t" x $level . "'%s'" . "\n", ref($var);
}
} else {
if (defined $var) {
print $fd "\t" x $level . "'$var'" . "\n";
} else {
print $fd "\t" x $level . "UNDEF\n";
}
}
}
## Dump a variable's content
sub dump_html_var {
my ($var) = shift;
my $html = '';
if (ref($var)) {
if (ref($var) eq 'ARRAY') {
$html .= '<ul>';
foreach my $index (0 .. $#{$var}) {
$html .= '<li> ' . $index . ':';
$html .= dump_html_var($var->[$index]);
$html .= '</li>';
}
$html .= '</ul>';
} elsif (ref($var) eq 'HASH'
|| ref($var) eq 'Sympa::Scenario'
|| ref($var) eq 'Sympa::List') {
$html .= '<ul>';
foreach my $key (sort keys %{$var}) {
$html .= '<li>' . $key . '=';
$html .= dump_html_var($var->{$key});
$html .= '</li>';
}
$html .= '</ul>';
} else {
$html .= 'EEEEEEEEEEEEEEEEEEEEE' . ref($var);
}
} else {
if (defined $var) {
$html .= Sympa::Tools::Text::encode_html($var);
} else {
$html .= 'UNDEF';
}
}
return $html;
}
# Duplicates a complex variable (faster).
# CAUTION: This duplicates blessed elements even if they are
# singleton/multiton; this breaks subroutine references.
sub clone_var {
return Clone::clone($_[0]) if $Clone::VERSION;
goto &dup_var; # '&' needed
}
## Duplictate a complex variable
sub dup_var {
my ($var) = @_;
if (ref($var)) {
if (ref($var) eq 'ARRAY') {
my $new_var = [];
foreach my $index (0 .. $#{$var}) {
$new_var->[$index] = dup_var($var->[$index]);
}
return $new_var;
} elsif (ref($var) eq 'HASH') {
my $new_var = {};
foreach my $key (sort keys %{$var}) {
$new_var->{$key} = dup_var($var->{$key});
}
return $new_var;
}
}
return $var;
}
####################################################
# get_array_from_splitted_string
####################################################
# return an array made on a string splited by ','.
# It removes spaces.
#
#
# IN : -$string (+): string to split
#
# OUT : -ref(ARRAY)
#
######################################################
# Note: This is used only by Sympa::List.
sub get_array_from_splitted_string {
my ($string) = @_;
my @array;
foreach my $word (split /,/, $string) {
$word =~ s/^\s+//;
$word =~ s/\s+$//;
push @array, $word;
}
return \@array;
}
####################################################
# diff_on_arrays
####################################################
# Makes set operation on arrays (seen as set, with no double) :
# - deleted : A \ B
# - added : B \ A
# - intersection : A /\ B
# - union : A \/ B
#
# IN : -$setA : ref(ARRAY) - set
# -$setB : ref(ARRAY) - set
#
# OUT : -ref(HASH) with keys :
# deleted, added, intersection, union
#
#######################################################
sub diff_on_arrays {
my ($setA, $setB) = @_;
my $result = {
'intersection' => [],
'union' => [],
'added' => [],
'deleted' => []
};
my %deleted;
my %added;
my %intersection;
my %union;
my %hashA;
my %hashB;
foreach my $eltA (@$setA) {
$hashA{$eltA} = 1;
$deleted{$eltA} = 1;
$union{$eltA} = 1;
}
foreach my $eltB (@$setB) {
$hashB{$eltB} = 1;
$added{$eltB} = 1;
if ($hashA{$eltB}) {
$intersection{$eltB} = 1;
$deleted{$eltB} = 0;
} else {
$union{$eltB} = 1;
}
}
foreach my $eltA (@$setA) {
if ($hashB{$eltA}) {
$added{$eltA} = 0;
}
}
foreach my $elt (keys %deleted) {
next unless $elt;
push @{$result->{'deleted'}}, $elt if ($deleted{$elt});
}
foreach my $elt (keys %added) {
next unless $elt;
push @{$result->{'added'}}, $elt if ($added{$elt});
}
foreach my $elt (keys %intersection) {
next unless $elt;
push @{$result->{'intersection'}}, $elt if ($intersection{$elt});
}
foreach my $elt (keys %union) {
next unless $elt;
push @{$result->{'union'}}, $elt if ($union{$elt});
}
return $result;
}
####################################################
# is_in_array
####################################################
# Test if a value is on an array
#
# IN : -$setA : ref(ARRAY) - set
# -$value : a serached value
#
# OUT : boolean
#######################################################
sub is_in_array {
my $set = shift;
die 'missing parameter "$value"' unless @_;
my $value = shift;
if (defined $value) {
foreach my $elt (@{$set || []}) {
next unless defined $elt;
return 1 if $elt eq $value;
}
} else {
foreach my $elt (@{$set || []}) {
return 1 unless defined $elt;
}
}
return undef;
}
=over
=item smart_eq ( $a, $b )
I<Function>.
Check if two strings are identical.
Parameters:
=over
=item $a, $b
Operands.
If both of them are undefined, they are equal.
If only one of them is undefined, the are not equal.
If C<$b> is a L<Regexp> object and it matches to C<$a>, they are equal.
Otherwise, they are compared as strings.
=back
Returns:
If arguments matched, true value. Otherwise false value.
=back
=cut
sub smart_eq {
die 'missing argument' if scalar @_ < 2;
my ($a, $b) = @_;
if (defined $a and defined $b) {
if (ref $b eq 'Regexp') {
return 1 if $a =~ $b;
} else {
return 1 if $a eq $b;
}
} elsif (!defined $a and !defined $b) {
return 1;
}
return undef;
}
## convert a string formated as var1="value1";var2="value2"; into a hash.
## Used when extracting from session table some session properties or when
## extracting users preference from user table
## Current encoding is NOT compatible with encoding of values with '"'
##
sub string_2_hash {
my $data = shift;
my %hash;
pos($data) = 0;
while ($data =~ /\G;?(\w+)\=\"((\\[\"\\]|[^\"])*)\"(?=(;|\z))/g) {
my ($var, $val) = ($1, $2);
$val =~ s/\\([\"\\])/$1/g;
$hash{$var} = $val;
}
return (%hash);
}
## convert a hash into a string formated as var1="value1";var2="value2"; into
## a hash
sub hash_2_string {
my $refhash = shift;
return undef unless ref $refhash eq 'HASH';
my $data_string;
foreach my $var (keys %$refhash) {
next unless length $var;
my $val = $refhash->{$var};
$val = '' unless defined $val;
$val =~ s/([\"\\])/\\$1/g;
$data_string .= ';' . $var . '="' . $val . '"';
}
return ($data_string);
}
## compare 2 scalars, string/numeric independant
sub smart_lessthan {
my ($stra, $strb) = @_;
$stra =~ s/^\s+//;
$stra =~ s/\s+$//;
$strb =~ s/^\s+//;
$strb =~ s/\s+$//;
$ERRNO = 0;
my ($numa, $unparsed) = POSIX::strtod($stra);
my $numb;
$numb = POSIX::strtod($strb)
unless ($ERRNO || $unparsed != 0);
if (($stra eq '') || ($strb eq '') || ($unparsed != 0) || $ERRNO) {
return $stra lt $strb;
} else {
return $stra < $strb;
}
}
=over
=item sort_uniq ( [ \&comp ], @items )
Returns sorted array of unique elements in the list.
Parameters:
=over
=item \&comp
Optional subroutine reference to compare each pairs of elements.
It should take two arguments and return negative, zero or positive result.
=item @items
Items to be sorted.
=back
This function was added on Sympa 6.2.16.
=back
=cut
sub sort_uniq {
my $comp;
if (ref $_[0] eq 'CODE') {
$comp = shift;
}
my %items;
@items{@_} = ();
if ($comp) {
return sort { $comp->($a, $b) } keys %items;
} else {
return sort keys %items;
}
}
# Create a custom attribute from an XML description
# IN : A string, XML formed data as stored in database
# OUT : HASH data storing custome attributes.
# Old name: Sympa::List::parseCustomAttribute().
sub decode_custom_attribute {
my $xmldoc = shift;
return undef unless defined $xmldoc and length $xmldoc;
my $parser = XML::LibXML->new();
my $tree;
## We should use eval to parse to prevent the program to crash if it fails
if (ref($xmldoc) eq 'GLOB') {
$tree = eval { $parser->parse_fh($xmldoc) };
} else {
$tree = eval { $parser->parse_string($xmldoc) };
}
return undef unless defined $tree;
my $doc = $tree->getDocumentElement;
my @custom_attr = $doc->getChildrenByTagName('custom_attribute');
my %ca;
foreach my $ca (@custom_attr) {
my $id = Encode::encode_utf8($ca->getAttribute('id'));
my $value = Encode::encode_utf8($ca->getElementsByTagName('value'));
$ca{$id} = {value => $value};
}
return \%ca;
}
# Create an XML Custom attribute to be stored into data base.
# IN : HASH data storing custome attributes
# OUT : string, XML formed data to be stored in database
# Old name: Sympa::List::createXMLCustomAttribute().
sub encode_custom_attribute {
my $custom_attr = shift;
return
'<?xml version="1.0" encoding="UTF-8" ?><custom_attributes></custom_attributes>'
if (not defined $custom_attr);
my $XMLstr = '<?xml version="1.0" encoding="UTF-8" ?><custom_attributes>';
foreach my $k (sort keys %{$custom_attr}) {
my $value = $custom_attr->{$k}{value};
$value = '' unless defined $value;
$XMLstr .=
"<custom_attribute id=\"$k\"><value>"
. Sympa::Tools::Text::encode_html($value, '\000-\037')
. "</value></custom_attribute>";
}
$XMLstr .= "</custom_attributes>";
$XMLstr =~ s/\s*\n\s*/ /g;
return $XMLstr;
}
1;