Bug 17025: Fix XSS in serials-search.pl
[koha.git] / C4 / Serials.pm
index 37e7c16..35d62c9 100644 (file)
@@ -32,8 +32,11 @@ use C4::Serials::Frequency;
 use C4::Serials::Numberpattern;
 use Koha::AdditionalField;
 use Koha::DateUtils;
+use Koha::Serial;
+use Koha::Subscriptions;
+use Koha::Subscription::Histories;
 
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
 # Define statuses
 use constant {
@@ -58,7 +61,6 @@ use constant MISSING_STATUSES => (
 );
 
 BEGIN {
-    $VERSION = 3.07.00.049;    # set version for version checking
     require Exporter;
     @ISA    = qw(Exporter);
     @EXPORT = qw(
@@ -75,7 +77,6 @@ BEGIN {
       &GetSerialInformation                   &AddItem2Serial
       &PrepareSerialsData &GetNextExpected    &ModNextExpected
 
-      &UpdateClaimdateIssues
       &GetSuppliersWithLateIssues             &getsupplierbyserialid
       &GetDistributedTo   &SetDistributedTo
       &getroutinglist     &delroutingmember   &addroutingmember
@@ -259,34 +260,6 @@ sub AddItem2Serial {
     return $rq->rows;
 }
 
-=head2 UpdateClaimdateIssues
-
-UpdateClaimdateIssues($serialids,[$date]);
-
-Update Claimdate for issues in @$serialids list with date $date
-(Take Today if none)
-
-=cut
-
-sub UpdateClaimdateIssues {
-    my ( $serialids, $date ) = @_;
-
-    return unless ($serialids);
-
-    my $dbh = C4::Context->dbh;
-    $date = strftime( "%Y-%m-%d", localtime ) unless ($date);
-    my $query = "
-        UPDATE serial
-        SET claimdate = ?,
-            status = ?,
-            claims_count = claims_count + 1
-        WHERE  serialid in (" . join( ",", map { '?' } @$serialids ) . ")
-    ";
-    my $rq = $dbh->prepare($query);
-    $rq->execute($date, CLAIMED, @$serialids);
-    return $rq->rows;
-}
-
 =head2 GetSubscription
 
 $subs = GetSubscription($subscriptionid)
@@ -863,7 +836,7 @@ $subscription is a hashref containing all the attributes of the table
 'subscription'.
 $pattern is a hashref containing all the attributes of the table
 'subscription_numberpatterns'.
-$planneddate is a C4::Dates object.
+$planneddate is a date string in iso format.
 This function get the next issue for the subscription given on input arg
 
 =cut
@@ -1172,8 +1145,9 @@ sub ModSerialStatus {
         }
     }
 
-    # create new waited entry if needed (ie : was a "waited" and has changed)
-    if ( $oldstatus == EXPECTED && $status != EXPECTED ) {
+    # create new expected entry if needed (ie : was "expected" and has changed)
+    my $otherIssueExpected = scalar findSerialsByStatus(EXPECTED, $subscriptionid);
+    if ( !$otherIssueExpected && $oldstatus == EXPECTED && $status != EXPECTED ) {
         my $subscription = GetSubscription($subscriptionid);
         my $pattern = C4::Serials::Numberpattern::GetSubscriptionNumberpattern($subscription->{numberpattern});
 
@@ -1187,12 +1161,13 @@ sub ModSerialStatus {
         # next date (calculated from actual date & frequency parameters)
         my $nextpublisheddate = GetNextDate($subscription, $publisheddate, 1);
         my $nextpubdate = $nextpublisheddate;
-        NewIssue( $newserialseq, $subscriptionid, $subscription->{'biblionumber'}, 1, $nextpubdate, $nextpubdate );
         $query = "UPDATE subscription SET lastvalue1=?, lastvalue2=?, lastvalue3=?, innerloop1=?, innerloop2=?, innerloop3=?
                     WHERE  subscriptionid = ?";
         $sth = $dbh->prepare($query);
         $sth->execute( $newlastvalue1, $newlastvalue2, $newlastvalue3, $newinnerloop1, $newinnerloop2, $newinnerloop3, $subscriptionid );
 
+        NewIssue( $newserialseq, $subscriptionid, $subscription->{'biblionumber'}, 1, $nextpubdate, $nextpubdate );
+
         # check if an alert must be sent... (= a letter is defined & status became "arrived"
         if ( $subscription->{letter} && $status == ARRIVED && $oldstatus != ARRIVED ) {
             require C4::Letters;
@@ -1444,13 +1419,20 @@ sub NewSubscription {
 
     # calculate issue number
     my $serialseq = GetSeq($subscription, $pattern) || q{};
-    $query = qq|
-        INSERT INTO serial
-            (serialseq,subscriptionid,biblionumber,status, planneddate, publisheddate)
-        VALUES (?,?,?,?,?,?)
-    |;
-    $sth = $dbh->prepare($query);
-    $sth->execute( $serialseq, $subscriptionid, $biblionumber, EXPECTED, $firstacquidate, $firstacquidate );
+
+    Koha::Serial->new(
+        {
+            serialseq      => $serialseq,
+            serialseq_x    => $subscription->{'lastvalue1'},
+            serialseq_y    => $subscription->{'lastvalue2'},
+            serialseq_z    => $subscription->{'lastvalue3'},
+            subscriptionid => $subscriptionid,
+            biblionumber   => $biblionumber,
+            status         => EXPECTED,
+            planneddate    => $firstacquidate,
+            publisheddate  => $firstacquidate,
+        }
+    )->store();
 
     logaction( "SERIAL", "ADD", $subscriptionid, "" ) if C4::Context->preference("SubscriptionLog");
 
@@ -1548,43 +1530,49 @@ sub NewIssue {
 
     return unless ($subscriptionid);
 
-    my $dbh   = C4::Context->dbh;
-    my $query = qq|
-        INSERT INTO serial (serialseq, subscriptionid, biblionumber, status,
-            publisheddate, publisheddatetext, planneddate, notes)
-        VALUES (?,?,?,?,?,?,?,?)
-    |;
-    my $sth = $dbh->prepare($query);
-    $sth->execute( $serialseq, $subscriptionid, $biblionumber, $status,
-        $publisheddate, $publisheddatetext, $planneddate, $notes );
-    my $serialid = $dbh->{'mysql_insertid'};
-    $query = qq|
-        SELECT missinglist,recievedlist
-        FROM   subscriptionhistory
-        WHERE  subscriptionid=?
-    |;
-    $sth = $dbh->prepare($query);
-    $sth->execute($subscriptionid);
-    my ( $missinglist, $recievedlist ) = $sth->fetchrow;
+    my $schema = Koha::Database->new()->schema();
+
+    my $subscription = Koha::Subscriptions->find( $subscriptionid );
+
+    my $serial = Koha::Serial->new(
+        {
+            serialseq         => $serialseq,
+            serialseq_x       => $subscription->lastvalue1(),
+            serialseq_y       => $subscription->lastvalue2(),
+            serialseq_z       => $subscription->lastvalue3(),
+            subscriptionid    => $subscriptionid,
+            biblionumber      => $biblionumber,
+            status            => $status,
+            planneddate       => $planneddate,
+            publisheddate     => $publisheddate,
+            publisheddatetext => $publisheddatetext,
+            notes             => $notes,
+        }
+    )->store();
+
+    my $serialid = $serial->id();
+
+    my $subscription_history = Koha::Subscription::Histories->find($subscriptionid);
+    my $missinglist = $subscription_history->missinglist();
+    my $recievedlist = $subscription_history->recievedlist();
 
     if ( $status == ARRIVED ) {
-      ### TODO Add a feature that improves recognition and description.
-      ### As such count (serialseq) i.e. : N18,2(N19),N20
-      ### Would use substr and index But be careful to previous presence of ()
-        $recievedlist .= "; $serialseq" unless (index($recievedlist,$serialseq)>0);
+        ### TODO Add a feature that improves recognition and description.
+        ### As such count (serialseq) i.e. : N18,2(N19),N20
+        ### Would use substr and index But be careful to previous presence of ()
+        $recievedlist .= "; $serialseq" unless ( index( $recievedlist, $serialseq ) > 0 );
     }
-    if ( grep {/^$status$/} ( MISSING_STATUSES ) ) {
-        $missinglist .= "; $serialseq" unless (index($missinglist,$serialseq)>0);
+    if ( grep { /^$status$/ } (MISSING_STATUSES) ) {
+        $missinglist .= "; $serialseq" unless ( index( $missinglist, $serialseq ) > 0 );
     }
-    $query = qq|
-        UPDATE subscriptionhistory
-        SET    recievedlist=?, missinglist=?
-        WHERE  subscriptionid=?
-    |;
-    $sth = $dbh->prepare($query);
+
     $recievedlist =~ s/^; //;
     $missinglist  =~ s/^; //;
-    $sth->execute( $recievedlist, $missinglist, $subscriptionid );
+
+    $subscription_history->recievedlist($recievedlist);
+    $subscription_history->missinglist($missinglist);
+    $subscription_history->store();
+
     return $serialid;
 }
 
@@ -1878,15 +1866,19 @@ called from claims.pl file
 =cut
 
 sub updateClaim {
-    my ($serialid) = @_;
-    my $dbh        = C4::Context->dbh;
-    $dbh->do(q|
+    my ($serialids) = @_;
+    return unless $serialids;
+    unless ( ref $serialids ) {
+        $serialids = [ $serialids ];
+    }
+    my $dbh = C4::Context->dbh;
+    return $dbh->do(q|
         UPDATE serial
         SET claimdate = NOW(),
-            claims_count = claims_count + 1
-        WHERE serialid = ?
-    |, {}, $serialid );
-    return;
+            claims_count = claims_count + 1,
+            status = ?
+        WHERE serialid in (| . join( q|,|, (q|?|) x @$serialids ) . q|)|,
+        {}, CLAIMED, @$serialids );
 }
 
 =head2 getsupplierbyserialid
@@ -2467,8 +2459,11 @@ sub GetNextDate {
 _numeration returns the string corresponding to $value in the num_type
 num_type can take :
     -dayname
+    -dayabrv
     -monthname
+    -monthabrv
     -season
+    -seasonabrv
 =cut
 
 #'
@@ -2479,7 +2474,7 @@ sub _numeration {
     $num_type //= '';
     $locale ||= 'en';
     my $string;
-    if ( $num_type =~ /^dayname$/ ) {
+    if ( $num_type =~ /^dayname$/ or $num_type =~ /^dayabrv$/ ) {
         # 1970-11-01 was a Sunday
         $value = $value % 7;
         my $dt = DateTime->new(
@@ -2488,19 +2483,27 @@ sub _numeration {
             day     => $value + 1,
             locale  => $locale,
         );
-        $string = $dt->strftime("%A");
-    } elsif ( $num_type =~ /^monthname$/ ) {
+        $string = $num_type =~ /^dayname$/
+            ? $dt->strftime("%A")
+            : $dt->strftime("%a");
+    } elsif ( $num_type =~ /^monthname$/ or $num_type =~ /^monthabrv$/ ) {
         $value = $value % 12;
         my $dt = DateTime->new(
             year    => 1970,
             month   => $value + 1,
             locale  => $locale,
         );
-        $string = $dt->strftime("%B");
+        $string = $num_type =~ /^monthname$/
+            ? $dt->strftime("%B")
+            : $dt->strftime("%b");
     } elsif ( $num_type =~ /^season$/ ) {
         my @seasons= qw( Spring Summer Fall Winter );
         $value = $value % 4;
         $string = $seasons[$value];
+    } elsif ( $num_type =~ /^seasonabrv$/ ) {
+        my @seasonsabrv= qw( Spr Sum Fal Win );
+        $value = $value % 4;
+        $string = $seasonsabrv[$value];
     } else {
         $string = $value;
     }
@@ -2670,6 +2673,25 @@ sub _can_do_on_subscription {
     return 0;
 }
 
+=head2 findSerialsByStatus
+
+    @serials = findSerialsByStatus($status, $subscriptionid);
+
+    Returns an array of serials matching a given status and subscription id.
+
+=cut
+
+sub findSerialsByStatus {
+    my ( $status, $subscriptionid ) = @_;
+    my $dbh   = C4::Context->dbh;
+    my $query = q| SELECT * from serial
+                    WHERE status = ?
+                    AND subscriptionid = ?
+                |;
+    my $serials = $dbh->selectall_arrayref( $query, { Slice => {} }, $status, $subscriptionid );
+    return @$serials;
+}
+
 1;
 __END__