task #9
0000009: ereg n'est plus supporté, a changer par preg_match, preg_match_all,preg_*
This commit is contained in:
parent
1a5975ad86
commit
9d72c3e1e0
6 changed files with 214 additions and 180 deletions
|
|
@ -73,7 +73,8 @@ $array=array(
|
|||
array(31,'IBOX'),
|
||||
array(32,'Acc_Operation & children'),
|
||||
array(33,'Ledger : reverse op'),
|
||||
array(34,'Reconciliation')
|
||||
array(34,'Reconciliation'),
|
||||
array(35,'Bilan')
|
||||
);
|
||||
$r='<form method="get">';
|
||||
$r.='<select name="test_select" >';
|
||||
|
|
@ -293,6 +294,10 @@ case 34:
|
|||
require_once('class_acc_reconciliation.php');
|
||||
Acc_Reconciliation::test_me();
|
||||
break;
|
||||
case 35:
|
||||
require_once('class_acc_bilan.php');
|
||||
Acc_Bilan::test_me();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ function isNumber(&$p_int)
|
|||
function isDate ( $p_date)
|
||||
{
|
||||
if ( strlen (trim($p_date)) == 0 ) return null;
|
||||
if (! myereg ("^[0-9]{1,2}\.[0-9]{1,2}\.20[0-9]{2}",$p_date) )
|
||||
if ( preg_match("/^[0-9]{1,2}\.[0-9]{1,2}\.20[0-9]{2}$/",$p_date) == 0 )
|
||||
{
|
||||
|
||||
return null;
|
||||
|
|
@ -642,7 +642,7 @@ function format_date ($p_date)
|
|||
*@brief ereg is not supported from the version 5.3 and is marked as
|
||||
*obsolete, this function will call preg_match and returns
|
||||
* false is nothing is found or the length of the string found
|
||||
*@param
|
||||
*@param
|
||||
*@param
|
||||
*@return
|
||||
*@see
|
||||
|
|
|
|||
|
|
@ -329,6 +329,11 @@ class Acc_Bilan
|
|||
/*!\brief generate the ods document
|
||||
* \param the handle to the template file
|
||||
* \return the xml
|
||||
*@note
|
||||
* Sur une seule ligne il y a plusieurs données, donc il y a plusieurs boucles, pour les autres documents
|
||||
* cela devrait être fait aussi, actuellement ces documents, n'acceptent qu'une formule par ligne.
|
||||
*@note
|
||||
* Pas de header dans les entêtes car n'est pas compris dans le document qu'on utilise
|
||||
*/
|
||||
function generate_odt()
|
||||
{
|
||||
|
|
@ -361,75 +366,80 @@ class Acc_Bilan
|
|||
}
|
||||
|
||||
$r="";
|
||||
$regex="<<\\$[A-Z]*[0-9]*>>";
|
||||
$regex="/<<\\$[A-Z]*[0-9]*>>/";
|
||||
$lt="<";
|
||||
$gt=">";
|
||||
$header_txt=header_txt($this->db);
|
||||
|
||||
while ( !feof($p_file) )
|
||||
{
|
||||
$line_rtf=fgets($p_file);
|
||||
|
||||
/* replace the header tag */
|
||||
while( myereg('<<header>>',$line_rtf,$head) == true )
|
||||
{
|
||||
foreach ($head as $h)
|
||||
{
|
||||
// Create the header
|
||||
$line_rtf=str_replace($h,header_txt($this->db),$line_rtf);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* replace the header tag, doesn't work if inside header
|
||||
*/
|
||||
$line_rtf=preg_replace('/<<header>>/',$header_txt,$line_rtf);
|
||||
|
||||
|
||||
// the line contains the magic <<
|
||||
$tmp="";
|
||||
while (myereg($regex,$line_rtf,$f2) == true)
|
||||
{
|
||||
|
||||
|
||||
while (preg_match_all($regex,$line_rtf,$f2) > 0 )
|
||||
{
|
||||
// the f2 array contains all the magic << in the line
|
||||
foreach ($f2 as $f2_str)
|
||||
{
|
||||
|
||||
$to_remove=$f2_str;
|
||||
$f2_value=str_replace("<","",$f2_str);
|
||||
$f2_value=str_replace(">","",$f2_value);
|
||||
$f2_value=str_replace("$","",$f2_value);
|
||||
|
||||
// check for missing variables and labels (N vars)
|
||||
if( ! isset($this->$f2_value))
|
||||
{
|
||||
$a = "!!".$f2_value."!!";
|
||||
if( substr($f2_value, 0, 1) == "N" )
|
||||
{
|
||||
$ret = $this->db->get_array("SELECT pcm_lib AS acct_name FROM tmp_pcmn WHERE pcm_val::text LIKE ".
|
||||
" substr($1, 2)||'%' ORDER BY pcm_val ASC LIMIT 1",array($f2_value));
|
||||
if($ret[0]['acct_name'])
|
||||
{
|
||||
$a = $ret[0]['acct_name'];
|
||||
$a=str_replace('<','<',$a);
|
||||
$a=str_replace('>','>',$a);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
$a=$this->$f2_value;
|
||||
}
|
||||
if ( $a=='-0' ) $a=0;
|
||||
|
||||
/* allow numeric cel in ODT for the formatting and formula */
|
||||
if ( is_numeric($a) )
|
||||
{
|
||||
$searched='office:value-type="string"><text:p>'.$f2_str;
|
||||
$replaced='office:value-type="float" office:value="'.$a.'"><text:p>'.$f2_str;
|
||||
$line_rtf=str_replace($searched, $replaced, $line_rtf);
|
||||
}
|
||||
foreach ($f2 as $f2_array)
|
||||
{
|
||||
foreach ($f2_array as $f2_str)
|
||||
{
|
||||
$to_remove=$f2_str;
|
||||
$f2_value=str_replace("<","",$f2_str);
|
||||
$f2_value=str_replace(">","",$f2_value);
|
||||
$f2_value=str_replace("$","",$f2_value);
|
||||
|
||||
|
||||
$line_rtf=str_replace($f2_str,$a,$line_rtf);
|
||||
|
||||
}// foreach end
|
||||
} // while myereg
|
||||
// check for missing variables and labels (N vars)
|
||||
if( ! isset($this->$f2_value))
|
||||
{
|
||||
|
||||
$a = "!!".$f2_value."!!";
|
||||
if( substr($f2_value, 0, 1) == "N" )
|
||||
{
|
||||
$ret = $this->db->get_array("SELECT pcm_lib AS acct_name FROM tmp_pcmn WHERE pcm_val::text LIKE ".
|
||||
" substr($1, 2)||'%' ORDER BY pcm_val ASC LIMIT 1",array($f2_value));
|
||||
if($ret[0]['acct_name'])
|
||||
{
|
||||
$a = $ret[0]['acct_name'];
|
||||
$a=str_replace('<','<',$a);
|
||||
$a=str_replace('>','>',$a);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$a=$this->$f2_value;
|
||||
}
|
||||
if ( $a=='-0' ) $a=0;
|
||||
|
||||
/* allow numeric cel in ODT for the formatting and formula */
|
||||
if ( is_numeric($a) )
|
||||
{
|
||||
$searched='office:value-type="string"><text:p>'.$f2_str;
|
||||
$replaced='office:value-type="float" office:value="'.$a.'"><text:p>'.$f2_str;
|
||||
$line_rtf=str_replace($searched, $replaced, $line_rtf);
|
||||
}
|
||||
|
||||
|
||||
$line_rtf=str_replace($f2_str,$a,$line_rtf);
|
||||
|
||||
}// foreach end
|
||||
} // foreach
|
||||
} // preg_match_all
|
||||
$r.=$line_rtf;
|
||||
|
||||
}// odt file is read
|
||||
|
||||
return $r;
|
||||
|
||||
}
|
||||
|
|
@ -445,25 +455,26 @@ class Acc_Bilan
|
|||
{
|
||||
$lt='<';
|
||||
$gt='>';
|
||||
$pattern='/<<header>>/';
|
||||
}
|
||||
else
|
||||
{
|
||||
$lt='<';
|
||||
$gt='>';
|
||||
$pattern='/<<header>>/';
|
||||
}
|
||||
|
||||
$header_txt=header_txt($this->db);
|
||||
|
||||
while ( !feof($p_file) )
|
||||
{
|
||||
$line_rtf=fgets($p_file);
|
||||
if ( myereg($lt.$lt.'header'.$gt.$gt,$line_rtf) )
|
||||
{
|
||||
// Create the header
|
||||
$line_rtf=str_replace($lt.$lt.'header'.$gt.$gt,header_txt($this->db),$line_rtf);
|
||||
$r.=$line_rtf;
|
||||
continue;
|
||||
}
|
||||
|
||||
$line_rtf=preg_replace($pattern,$header_txt,$line_rtf);
|
||||
|
||||
|
||||
// the line contains the magic <<
|
||||
if (myereg($lt.$lt."\\$[a-zA-Z]*[0-9]*".$gt.$gt,$line_rtf,$f2) == true)
|
||||
if (preg_match_all("/".$lt.$lt."\\$[a-zA-Z]*[0-9]*".$gt.$gt."/",$line_rtf,$f2) > 0)
|
||||
{
|
||||
// DEBUG
|
||||
// echo $r.'<br>';
|
||||
|
|
@ -476,6 +487,7 @@ class Acc_Bilan
|
|||
$f2_value=str_replace($lt,"",$f2_str);
|
||||
$f2_value=str_replace($gt,"",$f2_value);
|
||||
$f2_value=str_replace("$","",$f2_value);
|
||||
$f2_value=$f2_value[0];
|
||||
|
||||
// check for missing variables and labels (N vars)
|
||||
if( ! isset($this->$f2_value))
|
||||
|
|
@ -511,6 +523,7 @@ class Acc_Bilan
|
|||
}// rtf file is read
|
||||
// DEBUG
|
||||
// fwrite($out,$r);
|
||||
|
||||
return $r;
|
||||
|
||||
|
||||
|
|
@ -676,6 +689,8 @@ class Acc_Bilan
|
|||
|
||||
echo '<form method="get">';
|
||||
echo $a->display_form();
|
||||
echo HtmlInput::hidden('test_select',$_GET['test_select']).dossier::hidden();
|
||||
echo HtmlInput::submit('result','Sauve');
|
||||
echo '</form>';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1331,7 +1331,7 @@ class Acc_Ledger
|
|||
|
||||
if ( $owner->MY_ANALYTIC!='nu') // use of AA
|
||||
{
|
||||
if ( myereg("^[6,7]+",$strPoste))
|
||||
if ( preg_match("/^[6,7]+/",$strPoste)==1)
|
||||
{
|
||||
// show form
|
||||
$op=new Anc_Operation($this->db);
|
||||
|
|
@ -1804,7 +1804,7 @@ class Acc_Ledger
|
|||
$tot_cred+=($acc_op->type=='c')?$acc_op->amount:0;
|
||||
if ( $owner->MY_ANALYTIC != "nu" )
|
||||
{
|
||||
if ( myereg("^[6,7]+",$poste))
|
||||
if ( preg_match("/^[6,7]+/",$poste)==1)
|
||||
{
|
||||
|
||||
// for each item, insert into operation_analytique */
|
||||
|
|
|
|||
|
|
@ -186,58 +186,68 @@ class Document
|
|||
// compute the regex
|
||||
if ( $p_type=='OOo')
|
||||
{
|
||||
$regex="=*<<[A-Z]+_*[A-Z]*_*[A-Z]*_*[A-Z]*_*[0-9]*>>";
|
||||
$regex="/=*<<[A-Z]+_*[A-Z]*_*[A-Z]*_*[A-Z]*_*[0-9]*>>/i";
|
||||
$lt="<";
|
||||
$gt=">";
|
||||
}
|
||||
else
|
||||
{
|
||||
$regex="=*<<[A-Z]+_*[A-Z]*_*[A-Z]*_*[A-Z]*_*[0-9]*>>";
|
||||
$regex="/=*<<[A-Z]+_*[A-Z]*_*[A-Z]*_*[A-Z]*_*[0-9]*>>/i";
|
||||
$lt="<";
|
||||
$gt=">";
|
||||
}
|
||||
|
||||
//read the file
|
||||
while(! feof($h))
|
||||
{
|
||||
{
|
||||
// replace the tag
|
||||
$buffer=fgets($h);
|
||||
// search in the buffer the magic << and >>
|
||||
// while myereg finds something to replace
|
||||
while ( @eregi ($regex,$buffer,$f) )
|
||||
{
|
||||
foreach ( $f as $pattern )
|
||||
{
|
||||
$to_remove=$pattern;
|
||||
// we remove the < and > from the pattern
|
||||
$pattern=str_replace($lt,'',$pattern);
|
||||
$pattern=str_replace($gt,'',$pattern);
|
||||
// while preg_match_all finds something to replace
|
||||
while ( preg_match_all ($regex,$buffer,$f) >0 )
|
||||
{
|
||||
|
||||
|
||||
foreach ( $f as $apattern )
|
||||
{
|
||||
|
||||
|
||||
// if the pattern if found we replace it
|
||||
$value=$this->Replace($pattern);
|
||||
if ( strpos($value,'ERROR') != false ) $value="";
|
||||
// replace into the $buffer
|
||||
// take the position in the buffer
|
||||
$pos=strpos($buffer,$to_remove);
|
||||
// get the length of the string to remove
|
||||
$len=strlen($to_remove);
|
||||
if ( $p_type=='OOo' )
|
||||
{
|
||||
$value=str_replace('&','&',$value);
|
||||
$value=str_replace('<','<',$value);
|
||||
$value=str_replace('>','>',$value);
|
||||
$value=str_replace('"','"',$value);
|
||||
$value=str_replace("'",''',$value);
|
||||
}
|
||||
$buffer=substr_replace($buffer,$value,$pos,$len);
|
||||
foreach($apattern as $pattern)
|
||||
{
|
||||
|
||||
// if the pattern if found we replace it
|
||||
}
|
||||
}
|
||||
|
||||
$to_remove=$pattern;
|
||||
// we remove the < and > from the pattern
|
||||
$pattern=str_replace($lt,'',$pattern);
|
||||
$pattern=str_replace($gt,'',$pattern);
|
||||
|
||||
|
||||
// if the pattern if found we replace it
|
||||
$value=$this->Replace($pattern);
|
||||
if ( strpos($value,'ERROR') != false ) $value="";
|
||||
// replace into the $buffer
|
||||
// take the position in the buffer
|
||||
$pos=strpos($buffer,$to_remove);
|
||||
// get the length of the string to remove
|
||||
$len=strlen($to_remove);
|
||||
if ( $p_type=='OOo' )
|
||||
{
|
||||
$value=str_replace('&','&',$value);
|
||||
$value=str_replace('<','<',$value);
|
||||
$value=str_replace('>','>',$value);
|
||||
$value=str_replace('"','"',$value);
|
||||
$value=str_replace("'",''',$value);
|
||||
}
|
||||
$buffer=substr_replace($buffer,$value,$pos,$len);
|
||||
|
||||
// if the pattern if found we replace it
|
||||
}
|
||||
}
|
||||
}
|
||||
// write into the output_file
|
||||
fwrite($output_file,$buffer);
|
||||
|
||||
}
|
||||
}
|
||||
fclose($h);
|
||||
fclose($output_file);
|
||||
if ( ($ret=copy ($output_name,$infile_name)) == FALSE )
|
||||
|
|
|
|||
|
|
@ -55,98 +55,102 @@ function ParseFormula($p_cn,$p_label,$p_formula,$p_start,$p_end,$p_eval=true,$p_
|
|||
else
|
||||
$cond="( j_date >= to_date('$p_start','DD.MM.YYYY') and j_date <= to_date('$p_end','DD.MM.YYYY'))";
|
||||
include_once("class_acc_account_ledger.php");
|
||||
while (@ereg("(\[[0-9]*%*D*C*S*\])",$p_formula,$e) == true)
|
||||
{
|
||||
|
||||
// while (@ereg("(\[[0-9]*%*D*C*S*\])",$p_formula,$e) == true)
|
||||
while (preg_match_all("(\[[0-9]*%*D*C*S*\])",$p_formula,$e) == true)
|
||||
{
|
||||
|
||||
// remove the [ ]
|
||||
$x=$e;
|
||||
$compute='all';
|
||||
if ( strpos($e[0],'D') != 0 )
|
||||
$compute='deb';
|
||||
if ( strpos($e[0],'C') != 0 )
|
||||
$compute='cred';
|
||||
if ( strpos($e[0],'S') != 0 )
|
||||
$compute='signed';
|
||||
$e[0]=str_replace ("[","",$e[0]);
|
||||
$e[0]=str_replace ("]","",$e[0]);
|
||||
$e[0]=str_replace ("D","",$e[0]);
|
||||
$e[0]=str_replace ("C","",$e[0]);
|
||||
$e[0]=str_replace ("S","",$e[0]);
|
||||
// If there is a FROM clause we must recompute
|
||||
// the time cond
|
||||
$x=$e[0];
|
||||
foreach ($x as $line)
|
||||
{
|
||||
$compute='all';
|
||||
if ( strpos($line,'D') != 0 )
|
||||
$compute='deb';
|
||||
if ( strpos($line,'C') != 0 )
|
||||
$compute='cred';
|
||||
if ( strpos($line,'S') != 0 )
|
||||
$compute='signed';
|
||||
$line=str_replace ("[","",$line);
|
||||
$line=str_replace ("]","",$line);
|
||||
$line=str_replace ("D","",$line);
|
||||
$line=str_replace ("C","",$line);
|
||||
$line=str_replace ("S","",$line);
|
||||
// If there is a FROM clause we must recompute
|
||||
// the time cond
|
||||
|
||||
if ($p_type_date == 0 && @ereg ("FROM=[0-9]+\.[0-9]+", $p_formula,$afrom) == true )
|
||||
{
|
||||
// There is a FROM clause
|
||||
// then we must modify the cond for the periode
|
||||
$from=str_replace("FROM=","",$afrom[0]);
|
||||
if ($p_type_date == 0 && preg_match ("/FROM=[0-9]+\.[0-9]+/", $p_formula,$afrom) == 1 )
|
||||
{
|
||||
// There is a FROM clause
|
||||
// then we must modify the cond for the periode
|
||||
$from=str_replace("FROM=","",$afrom[0]);
|
||||
|
||||
// Get the periode
|
||||
/*! \note special value for the clause FROM=00.0000
|
||||
*/
|
||||
if ( $from == '00.0000' )
|
||||
{
|
||||
// Get the periode
|
||||
/*! \note special value for the clause FROM=00.0000
|
||||
*/
|
||||
if ( $from == '00.0000' )
|
||||
{
|
||||
|
||||
// retrieve the first month of this periode
|
||||
$User=new User($p_cn);
|
||||
$user_periode=$User->get_periode();
|
||||
$oPeriode=new Periode($p_cn);
|
||||
$periode=$oPeriode->get_exercice($user_periode);
|
||||
list($first,$last)=$oPeriode->get_limit($periode);
|
||||
$ret=$first->get_date_limit();
|
||||
$end_date=$oPeriode->get_date_limit($p_end);
|
||||
if ($ret == null ) throw new Exception ('Pas de limite à cette période',1);
|
||||
$cond=sql_filter_per($p_cn,$ret['p_start'],$end_date['p_end'],'date','j_tech_per');
|
||||
// retrieve the first month of this periode
|
||||
$User=new User($p_cn);
|
||||
$user_periode=$User->get_periode();
|
||||
$oPeriode=new Periode($p_cn);
|
||||
$periode=$oPeriode->get_exercice($user_periode);
|
||||
list($first,$last)=$oPeriode->get_limit($periode);
|
||||
$ret=$first->get_date_limit();
|
||||
$end_date=$oPeriode->get_date_limit($p_end);
|
||||
if ($ret == null ) throw new Exception ('Pas de limite à cette période',1);
|
||||
$cond=sql_filter_per($p_cn,$ret['p_start'],$end_date['p_end'],'date','j_tech_per');
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPeriode=new Periode($p_cn);
|
||||
try
|
||||
{
|
||||
$from=$oPeriode->find_periode('01'.$from);
|
||||
}
|
||||
catch (Exception $exp)
|
||||
{
|
||||
/* if none periode is found
|
||||
then we take the first periode of the year
|
||||
*/
|
||||
$User=new User($p_cn);
|
||||
$user_periode=$User->get_periode();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPeriode=new Periode($p_cn);
|
||||
try
|
||||
{
|
||||
$from=$oPeriode->find_periode('01'.$from);
|
||||
}
|
||||
catch (Exception $exp)
|
||||
{
|
||||
/* if none periode is found
|
||||
then we take the first periode of the year
|
||||
*/
|
||||
$User=new User($p_cn);
|
||||
$user_periode=$User->get_periode();
|
||||
|
||||
$year=$oPeriode->get_exercice($user_periode);
|
||||
list($first,$last)=$oPeriode->get_limit($year);
|
||||
$ret=$first->get_date_limit();
|
||||
$end_date=$oPeriode->get_date_limit($p_end);
|
||||
if ($ret == null ) throw new Exception ('Pas de limite à cette période',1);
|
||||
$cond=sql_filter_per($p_cn,$ret['p_start'],$end_date['p_end'],'date','j_tech_per');
|
||||
}
|
||||
}
|
||||
}
|
||||
$year=$oPeriode->get_exercice($user_periode);
|
||||
list($first,$last)=$oPeriode->get_limit($year);
|
||||
$ret=$first->get_date_limit();
|
||||
$end_date=$oPeriode->get_date_limit($p_end);
|
||||
if ($ret == null ) throw new Exception ('Pas de limite à cette période',1);
|
||||
$cond=sql_filter_per($p_cn,$ret['p_start'],$end_date['p_end'],'date','j_tech_per');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( strpos($p_formula,"FROM") != 0)
|
||||
{
|
||||
// We remove FROM out of the p_formula
|
||||
$p_formula=substr_replace($p_formula,"",strpos($p_formula,"FROM"));
|
||||
}
|
||||
if ( strpos($p_formula,"FROM") != 0)
|
||||
{
|
||||
// We remove FROM out of the p_formula
|
||||
$p_formula=substr_replace($p_formula,"",strpos($p_formula,"FROM"));
|
||||
}
|
||||
|
||||
// Get sum of account
|
||||
$P=new Acc_Account_Ledger($p_cn,$e[0]);
|
||||
$detail=$P->get_solde_detail($cond);
|
||||
// Get sum of account
|
||||
$P=new Acc_Account_Ledger($p_cn,$line);
|
||||
$detail=$P->get_solde_detail($cond);
|
||||
|
||||
|
||||
if ( $compute=='all')
|
||||
$i=$detail['solde'];
|
||||
if ( $compute=='deb')
|
||||
$i=$detail['debit'];
|
||||
if ( $compute=='cred')
|
||||
$i=$detail['credit'];
|
||||
if ( $compute=='signed')
|
||||
$i=$detail['debit']-$detail['credit'];
|
||||
$p_formula=str_replace($x[0],$i,$p_formula);
|
||||
|
||||
}
|
||||
if ( $compute=='all')
|
||||
$i=$detail['solde'];
|
||||
if ( $compute=='deb')
|
||||
$i=$detail['debit'];
|
||||
if ( $compute=='cred')
|
||||
$i=$detail['credit'];
|
||||
if ( $compute=='signed')
|
||||
$i=$detail['debit']-$detail['credit'];
|
||||
$p_formula=str_replace($x[0],$i,$p_formula);
|
||||
}
|
||||
}
|
||||
|
||||
// $p_eval is true then we eval and returns result
|
||||
if ( $p_eval == true)
|
||||
|
|
@ -155,7 +159,7 @@ function ParseFormula($p_cn,$p_label,$p_formula,$p_start,$p_end,$p_eval=true,$p_
|
|||
|
||||
eval("$p_formula");
|
||||
|
||||
while (@ereg("\[([0-9]+)([Tt]*)\]",trim($p_label),$e) == true)
|
||||
while (preg_match("/\[([0-9]+)([Tt]*)\]/",trim($p_label),$e) == 1)
|
||||
{
|
||||
$nom = "!!".$e[1]."!!";
|
||||
if (CheckFormula($e[0]))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue