Merge branch 'bug_9076' into 3.12-master
This commit is contained in:
commit
fc1385a282
4 changed files with 107 additions and 75 deletions
|
@ -89,7 +89,7 @@ BEGIN {
|
||||||
GetBorrowerCategorycode
|
GetBorrowerCategorycode
|
||||||
&GetBorrowercategoryList
|
&GetBorrowercategoryList
|
||||||
|
|
||||||
&GetBorrowersWhoHaveNotBorrowedSince
|
&GetBorrowersToExpunge
|
||||||
&GetBorrowersWhoHaveNeverBorrowed
|
&GetBorrowersWhoHaveNeverBorrowed
|
||||||
&GetBorrowersWithIssuesHistoryOlderThan
|
&GetBorrowersWithIssuesHistoryOlderThan
|
||||||
|
|
||||||
|
@ -1938,52 +1938,65 @@ WHERE roadtypeid=?|;
|
||||||
return ($roadtype);
|
return ($roadtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
=head2 GetBorrowersWhoHaveNotBorrowedSince
|
=head2 GetBorrowersToExpunge
|
||||||
|
|
||||||
&GetBorrowersWhoHaveNotBorrowedSince($date)
|
$borrowers = &GetBorrowersToExpunge(
|
||||||
|
not_borrowered_since => $not_borrowered_since,
|
||||||
|
expired_before => $expired_before,
|
||||||
|
category_code => $category_code,
|
||||||
|
branchcode => $branchcode
|
||||||
|
);
|
||||||
|
|
||||||
this function get all borrowers who haven't borrowed since the date given on input arg.
|
This function get all borrowers based on the given criteria.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
sub GetBorrowersWhoHaveNotBorrowedSince {
|
sub GetBorrowersToExpunge {
|
||||||
my $filterdate = shift||POSIX::strftime("%Y-%m-%d",localtime());
|
my $params = shift;
|
||||||
my $filterexpiry = shift;
|
|
||||||
my $filterbranch = shift ||
|
my $filterdate = $params->{'not_borrowered_since'};
|
||||||
|
my $filterexpiry = $params->{'expired_before'};
|
||||||
|
my $filtercategory = $params->{'category_code'};
|
||||||
|
my $filterbranch = $params->{'branchcode'} ||
|
||||||
((C4::Context->preference('IndependantBranches')
|
((C4::Context->preference('IndependantBranches')
|
||||||
&& C4::Context->userenv
|
&& C4::Context->userenv
|
||||||
&& C4::Context->userenv->{flags} % 2 !=1
|
&& C4::Context->userenv->{flags} % 2 !=1
|
||||||
&& C4::Context->userenv->{branch})
|
&& C4::Context->userenv->{branch})
|
||||||
? C4::Context->userenv->{branch}
|
? C4::Context->userenv->{branch}
|
||||||
: "");
|
: "");
|
||||||
|
|
||||||
my $dbh = C4::Context->dbh;
|
my $dbh = C4::Context->dbh;
|
||||||
my $query = "
|
my $query = "
|
||||||
SELECT borrowers.borrowernumber,
|
SELECT borrowers.borrowernumber,
|
||||||
max(old_issues.timestamp) as latestissue,
|
MAX(old_issues.timestamp) AS latestissue,
|
||||||
max(issues.timestamp) as currentissue
|
MAX(issues.timestamp) AS currentissue
|
||||||
FROM borrowers
|
FROM borrowers
|
||||||
JOIN categories USING (categorycode)
|
JOIN categories USING (categorycode)
|
||||||
LEFT JOIN old_issues USING (borrowernumber)
|
LEFT JOIN old_issues USING (borrowernumber)
|
||||||
LEFT JOIN issues USING (borrowernumber)
|
LEFT JOIN issues USING (borrowernumber)
|
||||||
WHERE category_type <> 'S'
|
WHERE category_type <> 'S'
|
||||||
AND borrowernumber NOT IN (SELECT guarantorid FROM borrowers WHERE guarantorid IS NOT NULL AND guarantorid <> 0)
|
AND borrowernumber NOT IN (SELECT guarantorid FROM borrowers WHERE guarantorid IS NOT NULL AND guarantorid <> 0)
|
||||||
";
|
";
|
||||||
my @query_params;
|
my @query_params;
|
||||||
if ($filterbranch && $filterbranch ne ""){
|
if ( $filterbranch && $filterbranch ne "" ) {
|
||||||
$query.=" AND borrowers.branchcode= ?";
|
$query.= " AND borrowers.branchcode = ? ";
|
||||||
push @query_params,$filterbranch;
|
push( @query_params, $filterbranch );
|
||||||
}
|
}
|
||||||
if($filterexpiry){
|
if ( $filterexpiry ) {
|
||||||
$query .= " AND dateexpiry < ? ";
|
$query .= " AND dateexpiry < ? ";
|
||||||
push @query_params,$filterdate;
|
push( @query_params, $filterexpiry );
|
||||||
}
|
}
|
||||||
$query.=" GROUP BY borrowers.borrowernumber";
|
if ( $filtercategory ) {
|
||||||
if ($filterdate){
|
$query .= " AND categorycode = ? ";
|
||||||
$query.=" HAVING (latestissue < ? OR latestissue IS NULL)
|
push( @query_params, $filtercategory );
|
||||||
AND currentissue IS NULL";
|
}
|
||||||
|
$query.=" GROUP BY borrowers.borrowernumber HAVING currentissue IS NULL ";
|
||||||
|
if ( $filterdate ) {
|
||||||
|
$query.=" AND ( latestissue < ? OR latestissue IS NULL ) ";
|
||||||
push @query_params,$filterdate;
|
push @query_params,$filterdate;
|
||||||
}
|
}
|
||||||
warn $query if $debug;
|
warn $query if $debug;
|
||||||
|
|
||||||
my $sth = $dbh->prepare($query);
|
my $sth = $dbh->prepare($query);
|
||||||
if (scalar(@query_params)>0){
|
if (scalar(@query_params)>0){
|
||||||
$sth->execute(@query_params);
|
$sth->execute(@query_params);
|
||||||
|
|
|
@ -10,16 +10,14 @@
|
||||||
*/
|
*/
|
||||||
function checkForm(form) {
|
function checkForm(form) {
|
||||||
if((form.checkbox[0].checked)){
|
if((form.checkbox[0].checked)){
|
||||||
if(!(form.date1.value)){
|
if ( (!form.date1.value) && (!form.borrower_dateexpiry.value) && (!form.borrower_categorycode.value) ){
|
||||||
alert(_("please enter a date !"));
|
alert(_("Please enter at least one criterion for deletion!"));
|
||||||
document.form.date1.focus();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if((form.checkbox[1].checked)){
|
if((form.checkbox[1].checked)){
|
||||||
if(!(form.date2.value)){
|
if(!(form.date2.value)){
|
||||||
alert(_("please enter a date !"));
|
alert(_("please enter a date !"));
|
||||||
document.form.date2.focus();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,14 +55,29 @@
|
||||||
<form name="f1" onsubmit="return checkForm(this);" action="/cgi-bin/koha/tools/cleanborrowers.pl" method="post">
|
<form name="f1" onsubmit="return checkForm(this);" action="/cgi-bin/koha/tools/cleanborrowers.pl" method="post">
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>What do you want to do ?</legend>
|
<legend>What do you want to do ?</legend>
|
||||||
<p><input id="checkborrower" type="checkbox" name="checkbox" value="borrower" checked="checked" />
|
<h3><input id="checkborrower" type="checkbox" name="checkbox" value="borrower" /><label for="checkborrower"> Delete borrowers</label></h3>
|
||||||
<label for="checkborrower">Delete borrower who has not borrowed since:</label>
|
|
||||||
<input size="10" id="date1" name="filterdate1" value="[% filterdate1 %]" type="text" class="datepicker" />
|
<label for="date1">Who have not borrowed since:</label>
|
||||||
|
<input size="10" id="date1" name="filterdate1" type="text" class="datepicker" />
|
||||||
<span class="hint">[% INCLUDE 'date-format.inc' %]</span></p>
|
<span class="hint">[% INCLUDE 'date-format.inc' %]</span></p>
|
||||||
|
|
||||||
<p><input id="checkissue" type="checkbox" name="checkbox" value="issue" checked="checked" />
|
<label for="borrower_dateexpiry">Whose expiration date is before:</label>
|
||||||
<label for="checkissue">Anonymize check-out history older than</label>
|
<input size="10" id=borrower_dateexpiry" name="borrower_dateexpiry" type="text" class="datepicker" />
|
||||||
<input size="10" id="date2" name="filterdate2" value="[% filterdate2 %]" type="text" class="datepicker" />
|
<span class="hint">[% INCLUDE 'date-format.inc' %]</span></p>
|
||||||
|
|
||||||
|
<label for="borrower_categorycode">Whose patron category is:</label>
|
||||||
|
<select id="borrower_categorycode" name="borrower_categorycode">
|
||||||
|
<option value="" selected="selected">Any</option>
|
||||||
|
[% FOREACH bc IN borrower_categorycodes %]
|
||||||
|
[% UNLESS bc.categorycode == 'S' %]
|
||||||
|
<option value="[% bc.categorycode %]">[% bc.description %]</option>
|
||||||
|
[% END %]
|
||||||
|
[% END %]
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<h3><input id="checkissue" type="checkbox" name="checkbox" value="issue" /><label for="checkissue"> Anonymize history</label></h3>
|
||||||
|
<label for="date2">Anonymize check-out history older than</label>
|
||||||
|
<input size="10" id="date2" name="filterdate2" type="text" class="datepicker" />
|
||||||
<span class="hint">[% INCLUDE 'date-format.inc' %]</span></p>
|
<span class="hint">[% INCLUDE 'date-format.inc' %]</span></p>
|
||||||
|
|
||||||
<!-- hidden here -->
|
<!-- hidden here -->
|
||||||
|
@ -108,6 +121,8 @@
|
||||||
<input type="hidden" name="step3" value="1" />
|
<input type="hidden" name="step3" value="1" />
|
||||||
<input type="hidden" name="filterdate1" value="[% filterdate1 %]" />
|
<input type="hidden" name="filterdate1" value="[% filterdate1 %]" />
|
||||||
<input type="hidden" name="filterdate2" value="[% filterdate2 %]" />
|
<input type="hidden" name="filterdate2" value="[% filterdate2 %]" />
|
||||||
|
<input type="hidden" name="borrower_dateexpiry" value="[% borrower_dateexpiry %]" />
|
||||||
|
<input type="hidden" name="borrower_categorycode" value="[% borrower_categorycode %]" />
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="action"><input type="submit" value="Finish" /> <a class="cancel" href="/cgi-bin/koha/tools/cleanborrowers.pl">Cancel</a></fieldset>
|
<fieldset class="action"><input type="submit" value="Finish" /> <a class="cancel" href="/cgi-bin/koha/tools/cleanborrowers.pl">Cancel</a></fieldset>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -48,7 +48,7 @@ sub methods : Test( 1 ) {
|
||||||
GetPatronImage
|
GetPatronImage
|
||||||
PutPatronImage
|
PutPatronImage
|
||||||
RmPatronImage
|
RmPatronImage
|
||||||
GetBorrowersWhoHaveNotBorrowedSince
|
GetBorrowersToExpunge
|
||||||
GetBorrowersWhoHaveNeverBorrowed
|
GetBorrowersWhoHaveNeverBorrowed
|
||||||
GetBorrowersWithIssuesHistoryOlderThan
|
GetBorrowersWithIssuesHistoryOlderThan
|
||||||
GetBorrowersNamesAndLatestIssue
|
GetBorrowersNamesAndLatestIssue
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#
|
#
|
||||||
# Written by Antoine Farnault antoine@koha-fr.org on Nov. 2006.
|
# Written by Antoine Farnault antoine@koha-fr.org on Nov. 2006.
|
||||||
|
|
||||||
|
|
||||||
=head1 cleanborrowers.pl
|
=head1 cleanborrowers.pl
|
||||||
|
|
||||||
This script allows to do 2 things.
|
This script allows to do 2 things.
|
||||||
|
@ -33,6 +32,7 @@ This script allows to do 2 things.
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
|
|
||||||
#use warnings; FIXME - Bug 2505
|
#use warnings; FIXME - Bug 2505
|
||||||
use CGI;
|
use CGI;
|
||||||
use C4::Auth;
|
use C4::Auth;
|
||||||
|
@ -40,7 +40,7 @@ use C4::Output;
|
||||||
use C4::Dates qw/format_date format_date_in_iso/;
|
use C4::Dates qw/format_date format_date_in_iso/;
|
||||||
use C4::Members; # GetBorrowersWhoHavexxxBorrowed.
|
use C4::Members; # GetBorrowersWhoHavexxxBorrowed.
|
||||||
use C4::Circulation; # AnonymiseIssueHistory.
|
use C4::Circulation; # AnonymiseIssueHistory.
|
||||||
use C4::VirtualShelves (); #no import
|
use C4::VirtualShelves (); #no import
|
||||||
use Date::Calc qw/Today Add_Delta_YM/;
|
use Date::Calc qw/Today Add_Delta_YM/;
|
||||||
|
|
||||||
my $cgi = new CGI;
|
my $cgi = new CGI;
|
||||||
|
@ -51,13 +51,14 @@ my $cgi = new CGI;
|
||||||
# * multivalued CGI paramaters are returned as a packaged string separated by "\0" (null)
|
# * multivalued CGI paramaters are returned as a packaged string separated by "\0" (null)
|
||||||
my $params = $cgi->Vars;
|
my $params = $cgi->Vars;
|
||||||
|
|
||||||
my $filterdate1; # the date which filter on issue history.
|
my $filterdate1; # the date which filter on issue history.
|
||||||
my $filterdate2; # the date which filter on borrowers last issue.
|
my $filterdate2; # the date which filter on borrowers last issue.
|
||||||
|
my $borrower_dateexpiry;
|
||||||
|
my $borrower_categorycode;
|
||||||
|
|
||||||
# getting the template
|
# getting the template
|
||||||
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
|
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
|
||||||
{
|
{ template_name => "tools/cleanborrowers.tmpl",
|
||||||
template_name => "tools/cleanborrowers.tmpl",
|
|
||||||
query => $cgi,
|
query => $cgi,
|
||||||
type => "intranet",
|
type => "intranet",
|
||||||
authnotrequired => 0,
|
authnotrequired => 0,
|
||||||
|
@ -66,67 +67,74 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( $params->{'step2'} ) {
|
if ( $params->{'step2'} ) {
|
||||||
$filterdate1 = format_date_in_iso($params->{'filterdate1'});
|
$filterdate1 = format_date_in_iso( $params->{'filterdate1'} );
|
||||||
$filterdate2 = format_date_in_iso($params->{'filterdate2'});
|
$filterdate2 = format_date_in_iso( $params->{'filterdate2'} );
|
||||||
|
$borrower_dateexpiry = format_date_in_iso( $params->{'borrower_dateexpiry'} );
|
||||||
|
$borrower_categorycode = $params->{'borrower_categorycode'};
|
||||||
|
|
||||||
my %checkboxes = map { $_ => 1 } split /\0/, $params->{'checkbox'};
|
my %checkboxes = map { $_ => 1 } split /\0/, $params->{'checkbox'};
|
||||||
|
|
||||||
my $totalDel;
|
my $totalDel;
|
||||||
my $membersToDelete;
|
my $membersToDelete;
|
||||||
if ($checkboxes{borrower}) {
|
if ( $checkboxes{borrower} ) {
|
||||||
$membersToDelete = GetBorrowersWhoHaveNotBorrowedSince($filterdate1, 1);
|
$membersToDelete =
|
||||||
|
GetBorrowersToExpunge( { not_borrowered_since => $filterdate1, expired_before => $borrower_dateexpiry, category_code => $borrower_categorycode } );
|
||||||
$totalDel = scalar @$membersToDelete;
|
$totalDel = scalar @$membersToDelete;
|
||||||
|
|
||||||
}
|
}
|
||||||
my $totalAno;
|
my $totalAno;
|
||||||
my $membersToAnonymize;
|
my $membersToAnonymize;
|
||||||
if ($checkboxes{issue}) {
|
if ( $checkboxes{issue} ) {
|
||||||
$membersToAnonymize =
|
$membersToAnonymize = GetBorrowersWithIssuesHistoryOlderThan($filterdate2);
|
||||||
GetBorrowersWithIssuesHistoryOlderThan($filterdate2);
|
$totalAno = scalar @$membersToAnonymize;
|
||||||
$totalAno = scalar @$membersToAnonymize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$template->param(
|
$template->param(
|
||||||
step2 => 1,
|
step2 => 1,
|
||||||
totalToDelete => $totalDel,
|
totalToDelete => $totalDel,
|
||||||
totalToAnonymize => $totalAno,
|
totalToAnonymize => $totalAno,
|
||||||
memberstodelete_list => $membersToDelete,
|
memberstodelete_list => $membersToDelete,
|
||||||
memberstoanonymize_list => $membersToAnonymize,
|
memberstoanonymize_list => $membersToAnonymize,
|
||||||
filterdate1 => format_date($filterdate1),
|
filterdate1 => format_date($filterdate1),
|
||||||
filterdate2 => format_date($filterdate2),
|
filterdate2 => format_date($filterdate2),
|
||||||
|
borrower_dateexpiry => $borrower_dateexpiry,
|
||||||
|
borrower_categorycode => $borrower_categorycode,
|
||||||
);
|
);
|
||||||
### TODO : Use GetBorrowersNamesAndLatestIssue function in order to get the borrowers to delete or anonymize.
|
|
||||||
### Now, we are only using total, which is not enough imlo
|
### TODO : Use GetBorrowersNamesAndLatestIssue function in order to get the borrowers to delete or anonymize.
|
||||||
#writing the template
|
|
||||||
output_html_with_http_headers $cgi, $cookie, $template->output;
|
output_html_with_http_headers $cgi, $cookie, $template->output;
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $params->{'step3'} ) {
|
if ( $params->{'step3'} ) {
|
||||||
$filterdate1 = format_date_in_iso($params->{'filterdate1'});
|
$filterdate1 = format_date_in_iso( $params->{'filterdate1'} );
|
||||||
$filterdate2 = format_date_in_iso($params->{'filterdate2'});
|
$filterdate2 = format_date_in_iso( $params->{'filterdate2'} );
|
||||||
|
$borrower_dateexpiry = format_date_in_iso( $params->{'borrower_dateexpiry'} );
|
||||||
|
$borrower_categorycode = $params->{'borrower_categorycode'};
|
||||||
|
|
||||||
my $do_delete = $params->{'do_delete'};
|
my $do_delete = $params->{'do_delete'};
|
||||||
my $do_anonym = $params->{'do_anonym'};
|
my $do_anonym = $params->{'do_anonym'};
|
||||||
|
|
||||||
my ( $totalDel, $totalAno, $radio ) = ( 0, 0, 0 );
|
my ( $totalDel, $totalAno, $radio ) = ( 0, 0, 0 );
|
||||||
|
|
||||||
# delete members
|
# delete members
|
||||||
if ($do_delete) {
|
if ($do_delete) {
|
||||||
my $membersToDelete = GetBorrowersWhoHaveNotBorrowedSince($filterdate1, 1);
|
my $membersToDelete =
|
||||||
|
GetBorrowersToExpunge( { not_borrowered_since => $filterdate1, expired_before => $borrower_dateexpiry, category_code => $borrower_categorycode } );
|
||||||
$totalDel = scalar(@$membersToDelete);
|
$totalDel = scalar(@$membersToDelete);
|
||||||
$radio = $params->{'radio'};
|
$radio = $params->{'radio'};
|
||||||
if ( $radio eq 'trash' ) {
|
if ( $radio eq 'trash' ) {
|
||||||
my $i;
|
my $i;
|
||||||
for ( $i = 0 ; $i < $totalDel ; $i++ ) {
|
for ( $i = 0 ; $i < $totalDel ; $i++ ) {
|
||||||
MoveMemberToDeleted( $membersToDelete->[$i]->{'borrowernumber'} );
|
MoveMemberToDeleted( $membersToDelete->[$i]->{'borrowernumber'} );
|
||||||
C4::VirtualShelves::HandleDelBorrower($membersToDelete->[$i]->{'borrowernumber'});
|
C4::VirtualShelves::HandleDelBorrower( $membersToDelete->[$i]->{'borrowernumber'} );
|
||||||
DelMember( $membersToDelete->[$i]->{'borrowernumber'} );
|
DelMember( $membersToDelete->[$i]->{'borrowernumber'} );
|
||||||
}
|
}
|
||||||
}
|
} else { # delete completly.
|
||||||
else { # delete completly.
|
|
||||||
my $i;
|
my $i;
|
||||||
for ( $i = 0 ; $i < $totalDel ; $i++ ) {
|
for ( $i = 0 ; $i < $totalDel ; $i++ ) {
|
||||||
C4::VirtualShelves::HandleDelBorrower($membersToDelete->[$i]->{'borrowernumber'});
|
C4::VirtualShelves::HandleDelBorrower( $membersToDelete->[$i]->{'borrowernumber'} );
|
||||||
DelMember($membersToDelete->[$i]->{'borrowernumber'});
|
DelMember( $membersToDelete->[$i]->{'borrowernumber'} );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$template->param(
|
$template->param(
|
||||||
|
@ -134,7 +142,7 @@ if ( $params->{'step3'} ) {
|
||||||
TotalDel => $totalDel
|
TotalDel => $totalDel
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Anonymising all members
|
# Anonymising all members
|
||||||
if ($do_anonym) {
|
if ($do_anonym) {
|
||||||
$totalAno = AnonymiseIssueHistory($filterdate2);
|
$totalAno = AnonymiseIssueHistory($filterdate2);
|
||||||
|
@ -143,7 +151,7 @@ if ( $params->{'step3'} ) {
|
||||||
do_anonym => '1',
|
do_anonym => '1',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$template->param(
|
$template->param(
|
||||||
step3 => '1',
|
step3 => '1',
|
||||||
trash => ( $radio eq "trash" ) ? (1) : (0),
|
trash => ( $radio eq "trash" ) ? (1) : (0),
|
||||||
|
@ -154,16 +162,12 @@ if ( $params->{'step3'} ) {
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
#default value set to the template are the 'CNIL' value.
|
|
||||||
my ( $year, $month, $day ) = &Today();
|
|
||||||
$filterdate1 = format_date(sprintf("%-04.4d-%-02.2d-%02.2d", Add_Delta_YM($year, $month, $day, -1, 0)));
|
|
||||||
$filterdate2 = format_date(sprintf("%-04.4d-%-02.2d-%02.2d", Add_Delta_YM($year, $month, $day, 0, -3)));
|
|
||||||
|
|
||||||
$template->param(
|
$template->param(
|
||||||
step1 => '1',
|
step1 => '1',
|
||||||
filterdate1 => $filterdate1,
|
filterdate1 => $filterdate1,
|
||||||
filterdate2 => $filterdate2,
|
filterdate2 => $filterdate2,
|
||||||
DHTMLcalendar_dateformat => C4::Dates->DHTMLcalendar(),
|
DHTMLcalendar_dateformat => C4::Dates->DHTMLcalendar(),
|
||||||
|
borrower_categorycodes => GetBorrowercategoryList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
#writing the template
|
#writing the template
|
||||||
|
|
Loading…
Reference in a new issue