* adding a package Letters.pm, that manages Letters & alerts.
* adding feature : it's now possible to define a "letter" for any subscription created. If a letter is defined, users in OPAC can put an alert on the subscription. When an issue is marked "arrived", all users in the alert will recieve a mail (as defined in the "letter"). This last part (= send the mail) is not yet developped. (Should be done this week)
* adding feature : it's now possible to "put to an alert" in OPAC, for any serial subscription. The alert is stored in a new table, called alert. An alert can be put only if the librarian has activated them in subscription (and they activate it just by choosing a "letter" to sent to borrowers on new issues)
* adding feature : librarian can see in borrower detail which alerts they have put, and a user can see in opac-detail which alert they have put too.
Note that the system should be generic enough to manage any type of alert.
I plan to extend it soon to virtual shelves : a borrower will be able to put an alert on a virtual shelf, to be warned when something is changed in the virtual shelf (mail being sent once a day by cron, or manually by the shelf owner. Anyway, a mail won't be sent on every change, users would be spammed by Koha ;-) )
--- /dev/null
+package C4::Letters;
+
+
+# Copyright 2000-2002 Katipo Communications
+#
+# This file is part of Koha.
+#
+# Koha 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.
+#
+# Koha 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
+# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+# Suite 330, Boston, MA 02111-1307 USA
+
+use strict;
+use C4::Date;
+use Date::Manip;
+use C4::Suggestions;
+require Exporter;
+
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+
+# set the version for version checking
+$VERSION = 0.01;
+
+=head1 NAME
+
+C4::Letters - Give functions for Letters management
+
+=head1 SYNOPSIS
+
+ use C4::Letters;
+
+=head1 DESCRIPTION
+
+ "Letters" is the tool used in Koha to manage informations sent to the patrons and/or the library. This include some cron jobs like
+ late issues, as well as other tasks like sending a mail to users that have subscribed to a "serial issue alert" (= being warned every time a new issue has arrived at the library)
+
+ Letters are managed through "alerts" sent by Koha on some events. All "alert" related functions are in this module too.
+
+=cut
+
+@ISA = qw(Exporter);
+@EXPORT = qw(&GetLetterList &addalert &getalert &delalert &findrelatedto);
+
+=head2 GetLetterList
+
+ parameter : $module : the name of the module
+ This sub returns an array of hashes with all letters from a given module
+ Each hash entry contains :
+ - module : the module name
+ - code : the code of the letter, char(20)
+ - name : the complete name of the letter, char(200)
+ - title : the title that will be used as "subject" in mails, char(200)
+ - content : the content of the letter. Each field to be replaced by a value at runtime is enclosed in << and >>. The fields usually have the same name as in the DB
+
+=cut
+
+sub GetLetterList {
+ my ($module) = @_;
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare("select * from letter where module=?");
+ $sth->execute($module);
+ my @result;
+ while (my $line = $sth->fetchrow_hashref) {
+ push @result,$line;
+ }
+ return @result;
+}
+
+=head2 addalert
+
+ parameters :
+ - $borrowernumber : the number of the borrower subscribing to the alert
+ - $type : the type of alert.
+ - externalid : the primary key of the object to put alert on. For issues, the alert is made on subscriptionid.
+
+ create an alert and return the alertid (primary key)
+
+=cut
+
+sub addalert {
+ my ($borrowernumber,$type,$externalid) = @_;
+ my $dbh=C4::Context->dbh;
+ my $sth = $dbh->prepare("insert into alert (borrowernumber, type, externalid) values (?,?,?)");
+ $sth->execute($borrowernumber,$type,$externalid);
+ # get the alert number newly created and return it
+ my $alertid = $dbh->{'mysql_insertid'};
+ return $alertid;
+}
+
+=head2 delalert
+ parameters :
+ - alertid : the alert id
+ deletes the alert
+=cut
+
+sub delalert {
+ my ($alertid)=@_;
+# warn "ALERTID : $alertid";
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare("delete from alert where alertid=?");
+ $sth->execute($alertid);
+}
+
+=head2 getalert
+
+ parameters :
+ - $borrowernumber : the number of the borrower subscribing to the alert
+ - $type : the type of alert.
+ - externalid : the primary key of the object to put alert on. For issues, the alert is made on subscriptionid.
+ all parameters NON mandatory. If a parameter is omitted, the query is done without the corresponding parameter. For example, without $externalid, returns all alerts for a borrower on a topic.
+
+=cut
+
+sub getalert {
+ my ($borrowernumber,$type,$externalid) = @_;
+ my $dbh=C4::Context->dbh;
+ my $query = "select * from alert where";
+ my @bind;
+ if ($borrowernumber) {
+ $query .= " borrowernumber=? and";
+ push @bind,$borrowernumber;
+ }
+ if ($type) {
+ $query .= " type=? and";
+ push @bind,$type;
+ }
+ if ($externalid) {
+ $query .= " externalid=? and";
+ push @bind,$externalid;
+ }
+ $query =~ s/ and$//;
+# warn "Q : $query";
+ my $sth = $dbh->prepare($query);
+ $sth->execute(@bind);
+ my @result;
+ while (my $line = $sth->fetchrow_hashref) {
+ push @result,$line;
+ }
+ return \@result if $#result >=0; # return only if there is one result.
+ return;
+}
+=head2 findrelatedto
+ parameters :
+ - $type : the type of alert
+ - $externalid : the id of the "object" to query
+
+ In the table alert, a "id" is stored in the externalid field. This "id" is related to another table, depending on the type of the alert.
+ When type=issue, the id is related to a subscriptionid and this sub returns the name of the biblio.
+ When type=virtual, the id is related to a virtual shelf and this sub returns the name of the sub
+=cut
+sub findrelatedto {
+ my ($type,$externalid) = @_;
+ my $dbh=C4::Context->dbh;
+ my $sth;
+ if ($type eq "issue") {
+ $sth=$dbh->prepare("select title as result from subscription left join biblio on subscription.biblionumber=biblio.biblionumber where subscriptionid=?");
+ }
+ $sth->execute($externalid);
+ my ($result) = $sth->fetchrow;
+ return $result;
+}
+
+END { } # module clean-up code here (global destructor)
my $offset=$input->param('offset');
my $script_name="/cgi-bin/koha/admin/letter.pl";
my $code=$input->param('code');
+my $module = $input->param('module');
my $pagesize=20;
my $op = $input->param('op');
$searchfield=~ s/\,//g;
#---- if primkey exists, it's a modify action, so read values to modify...
my $letter;
if ($code) {
- my $sth=$dbh->prepare("select * from letter where code=?");
- $sth->execute($code);
+ my $sth=$dbh->prepare("select * from letter where module=? and code=?");
+ $sth->execute($module,$code);
$letter=$sth->fetchrow_hashref;
$sth->finish;
}
}
$template->param(name => $letter->{name},title => $letter->{title},
content => $letter->{content},
+ $letter->{module} => 1,
SQLfieldname => \@SQLfieldname,);
# END $OP eq ADD_FORM
################## ADD_VALIDATE ##################################
use C4::Context;
use HTML::Template;
use C4::Bull;
+use C4::Letters;
my $query = new CGI;
my $op = $query->param('op');
$add2,$every2,$whenmorethan2,$setto2,$lastvalue2,$innerloop2,
$add3,$every3,$whenmorethan3,$setto3,$lastvalue3,$innerloop3,
$numberingmethod, $status, $biblionumber,
- $bibliotitle, $notes);
+ $bibliotitle, $notes, $letter);
my @budgets;
my ($template, $loggedinuser, $cookie)
$biblionumber = $subs->{'biblionumber'};
$bibliotitle = $subs->{'bibliotitle'},
$notes = $subs->{'notes'};
+ $letter = $subs->{'letter'};
$template->param(
$op => 1,
user => $auser,
biblionumber => $biblionumber,
bibliotitle => $bibliotitle,
notes => $notes,
+ letter => $letter,
subscriptionid => $subscriptionid,
);
$template->param(
$template->param(budgets => \@budgets);
#FIXME : END Added by hdl on July, 14 2005
+my @letterlist = GetLetterList('serial');
+for (my $i=0;$i<=$#letterlist;$i++) {
+ warn "$letterlist[$i]->{'code'} eq ".$letter;
+ $letterlist[$i]->{'selected'} =1 if $letterlist[$i]->{'code'} eq $letter;
+}
+$template->param(letters => \@letterlist);
+
if ($op eq 'addsubscription') {
my $auser = $query->param('user');
my $aqbooksellerid = $query->param('aqbooksellerid');
my $status = 1;
my $biblionumber = $query->param('biblionumber');
my $notes = $query->param('notes');
+ my $letter = $query->param('letter');
my $subscriptionid = newsubscription($auser,$aqbooksellerid,$cost,$aqbudgetid,$biblionumber,
$startdate,$periodicity,$dow,$numberlength,$weeklength,$monthlength,
$add1,$every1,$whenmorethan1,$setto1,$lastvalue1,
$add2,$every2,$whenmorethan2,$setto2,$lastvalue2,
$add3,$every3,$whenmorethan3,$setto3,$lastvalue3,
- $numberingmethod, $status, $notes
+ $numberingmethod, $status, $notes, $letter
);
print $query->redirect("/cgi-bin/koha/bull/subscription-detail.pl?subscriptionid=$subscriptionid");
} else {
$add1,$every1,$whenmorethan1,$setto1,$lastvalue1,$innerloop1,
$add2,$every2,$whenmorethan2,$setto2,$lastvalue2,$innerloop2,
$add3,$every3,$whenmorethan3,$setto3,$lastvalue3,$innerloop3,
- $numberingmethod, $status, $biblionumber, $bibliotitle, $notes);
+ $numberingmethod, $status, $biblionumber, $bibliotitle, $notes,$letter);
$subscriptionid = $query->param('subscriptionid');
$numberingmethod = $query->param('numberingmethod');
$status = 1;
$notes = $query->param('notes');
+ $letter = $query->param('letter');
&modsubscription($auser,$aqbooksellerid,$cost,$aqbudgetid,$startdate,
$periodicity,$dow,$numberlength,$weeklength,$monthlength,
$add1,$every1,$whenmorethan1,$setto1,$lastvalue1,$innerloop1,
$add2,$every2,$whenmorethan2,$setto2,$lastvalue2,$innerloop2,
$add3,$every3,$whenmorethan3,$setto3,$lastvalue3,$innerloop3,
- $numberingmethod, $status, $biblionumber, $notes, $subscriptionid);
+ $numberingmethod, $status, $biblionumber, $notes, $letter, $subscriptionid);
}
if ($op eq 'del') {
my $subs = &getsubscription($subscriptionid);
my ($totalissues,@serialslist) = getserials($subscriptionid);
$totalissues-- if $totalissues; # the -1 is to have 0 if this is a new subscription (only 1 issue)
-# the subscription must be deletable if there is NO issues for a reason or another (should not happend, but...)
($template, $loggedinuser, $cookie)
= get_template_and_user({template_name => "bull/subscription-detail.tmpl",
debug => 1,
});
-my ($user, $cookie, $sessionID, $flags)
- = checkauth($query, 0, {catalogue => 1}, "intranet");
+my ($user, $cookie, $sessionID, $flags) = checkauth($query, 0, {catalogue => 1}, "intranet");
$template->param(
user => $subs->{auser},
biblionumber => $subs->{biblionumber},
bibliotitle => $subs->{bibliotitle},
notes => $subs->{notes},
+ letter => $subs->{letter},
subscriptionid => $subs->{subscriptionid},
serialslist => \@serialslist,
totalissues => $totalissues,
<input type="hidden" name="op" value="addsubscription">
<input type="hidden" name="user" value="<!-- TMPL_VAR name="user" -->">
<p>Librarian</td><td> <!-- TMPL_VAR name="user" --></p>
- <p><label>Supplier</label><input type="text" name="aqbooksellerid" value="<!-- TMPL_VAR name="aqbooksellerid" -->" size=4> (<input type="text" name="aqbooksellername" value="<!-- TMPL_VAR name="aqbooksellername" -->" disabled readonly>)<a href="#" onClick="FindAcqui(f)">...</a></p>
- <p><label>Biblio</label><input type="text" name="biblionumber" value="<!-- TMPL_VAR name="biblionumber" -->" size=4> (<input type="text" name="title" value="<!-- TMPL_VAR name="bibliotitle" -->" disabled readonly>)<a href="#" onClick="Plugin(f)">...</a></p>
- <p><label>Notes</label><textarea name="notes" cols="30" rows="2"><!-- TMPL_VAR name="notes" --></textarea></p>
+ <p><label class="label100">Supplier</label><input type="text" name="aqbooksellerid" value="<!-- TMPL_VAR name="aqbooksellerid" -->" size=4> (<input type="text" name="aqbooksellername" value="<!-- TMPL_VAR name="aqbooksellername" -->" disabled readonly>)<a href="#" onClick="FindAcqui(f)">...</a></p>
+ <p><label class="label100">Biblio</label><input type="text" name="biblionumber" value="<!-- TMPL_VAR name="biblionumber" -->" size=4> (<input type="text" name="title" value="<!-- TMPL_VAR name="bibliotitle" -->" disabled readonly>)<a href="#" onClick="Plugin(f)">...</a></p>
+ <p><label class="label100">Notes</label><textarea name="notes" cols="30" rows="2"><!-- TMPL_VAR name="notes" --></textarea></p>
+ <p><label class="label100">Enable issue alert</label>
+ <select name="letter">
+ <option value=""></option>
+ <!-- TMPL_LOOP name="letters" -->
+ <!-- TMPL_IF name="selected" -->
+ <option value="<!-- TMPL_VAR name="code" -->" selected><!-- TMPL_VAR name="name" --></option>
+ <!-- TMPL_ELSE -->
+ <option value="<!-- TMPL_VAR name="code" -->"><!-- TMPL_VAR name="name" --></option>
+ <!-- /TMPL_IF -->
+ <!-- /TMPL_LOOP -->
+ </select>
+ </p>
<p class="problem">warning</p>
<ul>
<li>remember you <b>must</b> have created a biblio <b>before</b> creating a subscription</li>
<div id="bloc25">
<h2 class="bull">Subscription information</h2>
<p>Librarian identity :</td><td> <!-- TMPL_VAR name="librarian" --></p>
- <p><label>Supplier </label><!-- TMPL_VAR name="aqbooksellername" --></p>
- <p><label>Cost</label><!-- TMPL_VAR name="cost" --></p>
- <p><label>Budget </label><!-- TMPL_VAR name="bookfundid" --></p>
- <p><label>Biblio</label><i>(<!-- TMPL_VAR name="biblionumber" -->)</i> <!-- TMPL_VAR name="bibliotitle" --></p>
- <p><label>Notes:</label><!-- TMPL_VAR name="notes" --></p>
+ <p><label class="label100">Supplier </label><!-- TMPL_VAR name="aqbooksellername" --></p>
+ <p><label class="label100">Cost</label><!-- TMPL_VAR name="cost" --></p>
+ <p><label class="label100">Budget </label><!-- TMPL_VAR name="bookfundid" --></p>
+ <p><label class="label100">Biblio</label><i>(<!-- TMPL_VAR name="biblionumber" -->)</i> <!-- TMPL_VAR name="bibliotitle" --></p>
+ <p><label class="label100">Notes:</label><!-- TMPL_VAR name="notes" --></p>
+ <p>
+ <!-- TMPL_IF name="letter" -->
+ <label class="label100">Issue alert with letter:</label><!-- TMPL_VAR name="letter" -->
+ <!-- TMPL_ELSE -->
+ <label class="label100">Borrowers can't subscribe to issue alerts</label>
+ <!-- /TMPL_IF -->
+ </p>
</div>
<div id="bloc25">
<h2 class="bull">Planning</h2>
None
<!-- /TMPL_IF -->
</div>
+
+ <div class="bloc25">
+ <h2 class="members">Alert subscriptions</h2>
+ <table>
+ <tr>
+ <th>Type</th>
+ <th>on</th>
+ </tr>
+
+ <!-- TMPL_LOOP NAME="alertloop" -->
+ <tr>
+ <td><!-- TMPL_IF name="issue" -->Serial issue<!-- /TMPL_IF --></td>
+ <td><!-- TMPL_VAR name="relatedto" --></td>
+ </tr>
+ <!-- /TMPL_LOOP -->
+ </table>
+ </div>
+
+
</div>
<script language="JavaScript" type="text/javascript">
function confirm_deletion() {
<td><!-- TMPL_VAR NAME="code" --></td>
<td><!-- TMPL_VAR NAME="name" --></td>
<td>
- <a href="/cgi-bin/koha/admin/letter.pl?op=add_form&code=<!-- TMPL_VAR NAME="code" -->">
+ <a href="/cgi-bin/koha/admin/letter.pl?op=add_form&module=<!-- TMPL_VAR name="module" -->&code=<!-- TMPL_VAR NAME="code" -->">
<img src="<!-- TMPL_VAR NAME="interface" -->/<!-- TMPL_VAR NAME="theme" -->/images/fileopen.png" width="32" hspace="0" vspace="0" border="0">
</a>
</td>
<!-- TMPL_IF name="enddate" --><p><b>This subscription is now ended. The last issue was recieved on <!-- TMPL_VAR name="enddate" --></b></p><!-- /TMPL_IF name="enddate" -->
<!--TMPL_UNLESS Name=fullinfo -->
<p><!-- TMPL_VAR name="notes" --></p>
+ <p>
+ <!-- TMPL_IF name="letter" -->
+ <!-- TMPL_IF name="hasalert" -->
+ <a href="opac-alert-subscribe.pl?op=cancel&externalid=<!-- TMPL_VAR name="subscriptionid" -->&alerttype=issue&biblionumber=<!-- TMPL_VAR name="biblionumber" -->" class="button" title="Cancel mail alert when a new issue arrives for this subscription">
+ Cancel alert
+ </a>
+ <!-- TMPL_ELSE -->
+ <a href="opac-alert-subscribe.pl?externalid=<!-- TMPL_VAR name="subscriptionid" -->&alerttype=issue&biblionumber=<!-- TMPL_VAR name="biblionumber" -->" class="button" title="be warned by mail when a new issue arrives for this subscription">
+ Stay in touch
+ </a>
+ <!-- /TMPL_IF -->
+ <!-- /TMPL_IF -->
+ </p>
<!-- TMPL_IF name="opacnote" -->
<h2 class="catalogue">Issues summary</h2>
<p>
<br />
</div>
<!-- /TMPL_IF -->
+ <!-- TMPL_IF name="alertloop" -->
+ <div class="bloc60">
+ <h2 class="members">Alert subscriptions</h2>
+ <table>
+ <tr>
+ <th>Type</th>
+ <th>on</th>
+ </tr>
+
+ <!-- TMPL_LOOP NAME="alertloop" -->
+ <tr>
+ <td><!-- TMPL_IF name="issue" -->Serial issue<!-- /TMPL_IF --></td>
+ <td><!-- TMPL_VAR name="relatedto" --></td>
+ </tr>
+ <!-- /TMPL_LOOP -->
+ </table>
+ </div>
+ <!-- /TMPL_IF -->
+
</div>
<!-- TMPL_INCLUDE NAME="opac-bottom.inc" -->
use C4::Reserves2;
use C4::Circulation::Circ2;
use C4::Koha;
+use C4::Letters;
use HTML::Template;
my $dbh = C4::Context->dbh;
my ($numaccts,$accts,$total)=getboracctrecord('',\%bor);
+#
+# current issues
+#
my ($count,$issue)=borrissues($bornum);
+
my $today=ParseDate('today');
my @issuedata;
my $totalprice = 0;
push (@issuedata, \%row);
}
+#
+# find reserves
+#
my ($rescount,$reserves)=FindReserves('',$bornum); #From C4::Reserves2
-
my @reservedata;
foreach my $reserveline (@$reserves) {
$reserveline->{'reservedate2'} = format_date($reserveline->{'reservedate'});
push (@reservedata, \%row);
}
+# current alert subscriptions
+my $alerts = getalert($bornum);
+foreach (@$alerts) {
+ $_->{$_->{type}}=1;
+ $_->{relatedto} = findrelatedto($_->{type},$_->{externalid});
+}
+
$template->param($data);
$template->param(
bornum => $bornum,
totalprice =>$totalprice,
totaldue =>$total,
issueloop => \@issuedata,
- reserveloop => \@reservedata
- );
-$template->param(
+ reserveloop => \@reservedata,
+ alertloop => $alerts);
independantbranches => C4::Context->preference("IndependantBranches"),
- samebranch => $samebranch) if (C4::Context->preference("IndependantBranches"));
+ samebranch => $samebranch) if (C4::Context->preference("IndependantBranches")
+ );
output_html_with_http_headers $input, $cookie, $template->output;
use C4::Koha;
use C4::Date;
use C4::Bull;
+use C4::Letters;
use C4::Output;
use C4::Interface::CGI::Output;
use C4::Context;
if ($selectview eq "full"){
my $subscriptions = get_full_subscription_list_from_biblionumber($biblionumber);
+ # now, check is there is an alert subscription for one of the subscriptions
+ foreach (@$subscriptions) {
+ if (getalert($loggedinuser,'issue',$_->{subscriptionid})) {
+ warn "SUBSCRIPTION FOR : $loggedinuser,'issue',$_->{subscriptionid}";
+ }
+ }
my $title = $subscriptions->[0]{bibliotitle};
-# warn "title ".$title;
my $yearmin=$subscriptions->[0]{year};
-# warn "yearmin ".$yearmin;
my $yearmax=$subscriptions->[scalar(@$subscriptions)-1]{year};
-# warn "yearmax ".$yearmax;
($template, $loggedinuser, $cookie)
} else {
my $subscriptions = get_subscription_list_from_biblionumber($biblionumber);
+ # now, check is there is an alert subscription for one of the subscriptions
+ foreach (@$subscriptions) {
+ if (getalert($loggedinuser,'issue',$_->{subscriptionid})) {
+ $_->{hasalert} = 1;
+ }
+ }
+
($template, $loggedinuser, $cookie)
= get_template_and_user({template_name => "opac-serial-issues.tmpl",
query => $query,
use C4::Interface::CGI::Output;
use HTML::Template;
use C4::Date;
+use C4::Letters;
my $query = new CGI;
my ($template, $borrowernumber, $cookie)
$wcount++;
}
}
-
$template->param(WAITING => \@waiting);
+
+# current alert subscriptions
+warn " B : $borrowernumber";
+my $alerts = getalert($borrowernumber);
+foreach (@$alerts) {
+ $_->{$_->{type}}=1;
+ $_->{relatedto} = findrelatedto($_->{type},$_->{externalid});
+}
+
$template->param(waiting_count => $wcount,
LibraryName => C4::Context->preference("LibraryName"),
suggestion => C4::Context->preference("suggestion"),
virtualshelves => C4::Context->preference("virtualshelves"),
textmessaging => $borr->{textmessaging},
+ alertloop => $alerts,
);
output_html_with_http_headers $query, $cookie, $template->output;