Adding some Error Proof on C4::output
[koha.git] / C4 / Output.pm
index 3730828..15d5fa4 100644 (file)
@@ -4,7 +4,6 @@ package C4::Output;
 #You will need to edit parts of this pm
 #set the value of path to be where your html lives
 
-
 # Copyright 2000-2002 Katipo Communications
 #
 # This file is part of Koha.
@@ -22,541 +21,399 @@ package C4::Output;
 # Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
 # Suite 330, Boston, MA  02111-1307 USA
 
-use strict;
-require Exporter;
-
-use C4::Database;
-
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
-
-# set the version for version checking
-$VERSION = 0.01;
-
-@ISA = qw(Exporter);
-@EXPORT = qw(&startpage &endpage 
-            &mktablehdr &mktableft &mktablerow &mklink
-            &startmenu &endmenu &mkheadr 
-            &center &endcenter 
-            &mkform &mkform2 &bold
-            &gotopage &mkformnotable &mkform3
-            &getkeytableselectoptions
-            &picktemplate);
-%EXPORT_TAGS = ( );     # eg: TAG => [ qw!name1 name2! ],
-
-# your exported package globals go here,
-# as well as any optionally exported functions
-
-@EXPORT_OK   = qw($Var1 %Hashit);
 
+# NOTE: I'm pretty sure this module is deprecated in favor of
+# templates.
 
-# non-exported package globals go here
-use vars qw(@more $stuff);
-
-# initalize package globals, first exported ones
-
-my $Var1   = '';
-my %Hashit = ();
-
-
-# then the others (which are still accessible as $Some::Module::stuff)
-my $stuff  = '';
-my @more   = ();
+use strict;
+use warnings;
 
-# all file-scoped lexicals must be created before
-# the functions below that use them.
+use C4::Context;
+use C4::Languages qw(getTranslatedLanguages get_bidi regex_lang_subtags language_get_description accept_language );
 
-#
-# Change this value to reflect where you will store your includes
-#
-my %configfile;
-open (KC, "/etc/koha.conf");
-while (<KC>) {
-    chomp;
-    (next) if (/^\s*#/);
-    if (/(.*)\s*=\s*(.*)/) {
-        my $variable=$1;
-        my $value=$2;
-
-        $variable =~ s/^\s*//g;
-        $variable =~ s/\s*$//g;
-        $value    =~ s/^\s*//g;
-        $value    =~ s/\s*$//g;
-        $configfile{$variable}=$value;
-    } # if
-} # while
-close(KC);
-
-my $path=$configfile{'includes'};
-($path) || ($path="/usr/local/www/hdl/htdocs/includes");
-
-# make all your functions, whether exported or not;
-
-sub picktemplate {
-  my ($includes, $base) = @_;
-  my $dbh=C4Connect;
-  my $templates;
-  opendir (D, "$includes/templates");
-  my @dirlist=readdir D;
-  foreach (@dirlist) {
-    (next) if (/^\./);
-    #(next) unless (/\.tmpl$/);
-    (next) unless (-e "$includes/templates/$_/$base");
-    $templates->{$_}=1;
-  }                                                        
-  my $sth=$dbh->prepare("select value from systempreferences where
-  variable='template'");
-  $sth->execute;
-  my ($preftemplate) = $sth->fetchrow;
-  $sth->finish;
-  $dbh->disconnect;
-  if ($templates->{$preftemplate}) {
-    return $preftemplate;
-  } else {
-    return 'default';
-  }
-  
-}
-                                   
-sub startpage() {
-  return("<html>\n");
-}
+use HTML::Template::Pro;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
-sub gotopage($) {
-  my ($target) = shift;
-  #print "<br>goto target = $target<br>";
-  my $string = "<META HTTP-EQUIV=Refresh CONTENT=\"0;URL=http:$target\">";
-  return $string;
+BEGIN {
+    # set the version for version checking
+    $VERSION = 3.03;
+    require Exporter;
+    @ISA    = qw(Exporter);
+       @EXPORT_OK = qw(&is_ajax ajax_fail); # More stuff should go here instead
+       %EXPORT_TAGS = ( all =>[qw(&themelanguage &gettemplate setlanguagecookie pagination_bar
+                                                               &output_with_http_headers &output_html_with_http_headers)],
+                                       ajax =>[qw(&output_with_http_headers is_ajax)],
+                                       html =>[qw(&output_with_http_headers &output_html_with_http_headers)]
+                               );
+    push @EXPORT, qw(
+        &themelanguage &gettemplate setlanguagecookie pagination_bar
+    );
+    push @EXPORT, qw(
+        &output_html_with_http_headers &output_with_http_headers
+    );
 }
 
+=head1 NAME
 
-sub startmenu($) {
-  # edit the paths in here
-  my ($type)=shift;
-  if ($type eq 'issue') {
-    open (FILE,"$path/issues-top.inc") || die;
-  } elsif ($type eq 'opac') {
-    open (FILE,"$path/opac-top.inc") || die;
-  } elsif ($type eq 'member') {
-    open (FILE,"$path/members-top.inc") || die;
-  } elsif ($type eq 'acquisitions'){
-    open (FILE,"$path/acquisitions-top.inc") || die;
-  } elsif ($type eq 'report'){
-    open (FILE,"$path/reports-top.inc") || die;
-  } elsif ($type eq 'circulation') {
-    open (FILE,"$path/circulation-top.inc") || die;
-  } else {
-    open (FILE,"$path/cat-top.inc") || die;
-  }
-  my @string=<FILE>;
-  close FILE;
-  # my $count=@string;
-  # $string[$count]="<BLOCKQUOTE>";
-  return @string;
-}
+C4::Output - Functions for managing templates
 
+=head1 FUNCTIONS
 
-sub endmenu {
-  my ($type) = @_;
-  if ( ! defined $type ) { $type=''; }
-  if ($type eq 'issue') {
-    open (FILE,"$path/issues-bottom.inc") || die;
-  } elsif ($type eq 'opac') {
-    open (FILE,"$path/opac-bottom.inc") || die;
-  } elsif ($type eq 'member') {
-    open (FILE,"$path/members-bottom.inc") || die;
-  } elsif ($type eq 'acquisitions') {
-    open (FILE,"$path/acquisitions-bottom.inc") || die;
-  } elsif ($type eq 'report') {
-    open (FILE,"$path/reports-bottom.inc") || die;
-  } elsif ($type eq 'circulation') {
-    open (FILE,"$path/circulation-bottom.inc") || die;
-  } else {
-    open (FILE,"$path/cat-bottom.inc") || die;
-  }
-  my @string=<FILE>;
-  close FILE;
-  return @string;
-}
-
-sub mktablehdr() {
-    return("<table border=0 cellspacing=0 cellpadding=5>\n");
-}
+=over 2
 
+=cut
 
-sub mktablerow {
-    #the last item in data may be a backgroundimage
-    
-    # FIXME
-    # should this be a foreach (1..$cols) loop?
-
-  my ($cols,$colour,@data)=@_;
-  my $i=0;
-  my $string="<tr valign=top bgcolor=$colour>";
-  while ($i <$cols){
-      if (defined $data[$cols]) { # if there is a background image
-         $string.="<td background=\"$data[$cols]\">";
-      } else { # if there's no background image
-         $string.="<td>";
-      }
-      if (! defined $data[$i]) {$data[$i]="";}
-      if ($data[$i] eq "") {
-         $string.=" &nbsp; </td>";
-      } else {
-         $string.="$data[$i]</td>";
-      } 
-      $i++;
-  }
-  $string=$string."</tr>\n";
-  return($string);
-}
+#FIXME: this is a quick fix to stop rc1 installing broken
+#Still trying to figure out the correct fix.
+my $path = C4::Context->config('intrahtdocs') . "/prog/en/includes/";
+
+#---------------------------------------------------------------------------------------------------------
+# FIXME - POD
+sub gettemplate {
+    my ( $tmplbase, $interface, $query ) = @_;
+    ($query) or warn "no query in gettemplate";
+    my $htdocs;
+    if ( $interface ne "intranet" ) {
+        $htdocs = C4::Context->config('opachtdocs');
+    }
+    else {
+        $htdocs = C4::Context->config('intrahtdocs');
+    }
+    my $path = C4::Context->preference('intranet_includes') || 'includes';
+    my ( $theme, $lang ) = themelanguage( $htdocs, $tmplbase, $interface, $query );
+
+    # if the template doesn't exist, load the English one as a last resort
+    my $filename = "$htdocs/$theme/$lang/modules/$tmplbase";
+    unless (-f $filename) {
+        $lang = 'en';
+        $filename = "$htdocs/$theme/$lang/modules/$tmplbase";
+    }
+    my $template       = HTML::Template::Pro->new(
+        filename          => $filename,
+        die_on_bad_params => 1,
+        global_vars       => 1,
+        case_sensitive    => 1,
+        loop_context_vars => 1, # enable: __first__, __last__, __inner__, __odd__, __counter__ 
+        path              => ["$htdocs/$theme/$lang/$path"]
+    );
+    my $themelang=( $interface ne 'intranet' ? '/opac-tmpl' : '/intranet-tmpl' )
+          . "/$theme/$lang";
+    $template->param(
+        themelang => $themelang,
+        yuipath   => (C4::Context->preference("yuipath") eq "local"?"$themelang/lib/yui":C4::Context->preference("yuipath")),
+        interface => ( $interface ne 'intranet' ? '/opac-tmpl' : '/intranet-tmpl' ),
+        theme     => $theme,
+        lang      => $lang
+    );
+
+    # Bidirectionality
+    my $current_lang = regex_lang_subtags($lang);
+    my $bidi;
+    $bidi = get_bidi($current_lang->{script}) if $current_lang->{script};
+    # Languages
+    my $languages_loop = getTranslatedLanguages($interface,$theme,$lang);
+    my $num_languages_enabled = 0;
+    foreach my $lang (@$languages_loop) {
+        foreach my $sublang (@{ $lang->{'sublanguages_loop'} }) {
+            $num_languages_enabled++ if $sublang->{enabled};
+         }
+    }
+    $template->param(
+            languages_loop       => $languages_loop,
+            bidi                 => $bidi,
+            one_language_enabled => ($num_languages_enabled <= 1) ? 1 : 0, # deal with zero enabled langs as well
+    ) unless @$languages_loop<2;
 
-sub mktableft() {
-  return("</table>\n");
+    return $template;
 }
 
-sub mkform{
-  my ($action,%inputs)=@_;
-  my $string="<form action=$action method=post>\n";
-  $string=$string.mktablehdr();
-  my $key;
-  my @keys=sort keys %inputs;
-  
-  my $count=@keys;
-  my $i2=0;
-  while ( $i2<$count) {
-    my $value=$inputs{$keys[$i2]};
-    my @data=split('\t',$value);
-    #my $posn = shift(@data);
-    if ($data[0] eq 'hidden'){
-      $string=$string."<input type=hidden name=$keys[$i2] value=\"$data[1]\">\n";
+#---------------------------------------------------------------------------------------------------------
+# FIXME - POD
+sub themelanguage {
+    my ( $htdocs, $tmpl, $interface, $query ) = @_;
+    ($query) or warn "no query in themelanguage";
+
+    # Set some defaults for language and theme
+    # First, check the user's preferences
+    my $lang;
+    my $http_accept_language = $ENV{ HTTP_ACCEPT_LANGUAGE };
+    $lang = accept_language( $http_accept_language, 
+              getTranslatedLanguages($interface,'prog') )
+      if $http_accept_language;
+    # But, if there's a cookie set, obey it
+    $lang = $query->cookie('KohaOpacLanguage') if (defined $query && $query->cookie('KohaOpacLanguage'));
+    # Fall back to English
+    my @languages;
+    if ($interface eq 'intranet') {
+        @languages = split ",", C4::Context->preference("language");
     } else {
-      my $text;
-      if ($data[0] eq 'radio') {
-        $text="<input type=radio name=$keys[$i2] value=$data[1]>$data[1]
-       <input type=radio name=$keys[$i2] value=$data[2]>$data[2]";
-      } 
-      if ($data[0] eq 'text') {
-        $text="<input type=$data[0] name=$keys[$i2] value=\"$data[1]\">";
-      }
-      if ($data[0] eq 'textarea') {
-        $text="<textarea name=$keys[$i2] wrap=physical cols=40 rows=4>$data[1]</textarea>";
-      }
-      if ($data[0] eq 'select') {
-        $text="<select name=$keys[$i2]>";
-       my $i=1;
-               while ($data[$i] ne "") {
-         my $val = $data[$i+1];
-         $text = $text."<option value=$data[$i]>$val";
-         $i = $i+2;
-       }
-       $text=$text."</select>";
-      }        
-      $string=$string.mktablerow(2,'white',$keys[$i2],$text);
-      #@order[$posn] =mktablerow(2,'white',$keys[$i2],$text);
+        @languages = split ",", C4::Context->preference("opaclanguages");
     }
-    $i2++;
-  }
-  #$string=$string.join("\n",@order);
-  $string=$string.mktablerow(2,'white','<input type=submit>','<input type=reset>');
-  $string=$string.mktableft;
-  $string=$string."</form>";
-}
-
-sub mkform3 {
-  my ($action, %inputs) = @_;
-  my $string = "<form action=\"$action\" method=\"post\">\n";
-  $string   .= mktablehdr();
-  my $key;
-  my @keys = sort(keys(%inputs));
-  my @order;
-  my $count = @keys;
-  my $i2 = 0;
-  while ($i2 < $count) {
-    my $value=$inputs{$keys[$i2]};
-    my @data=split('\t',$value);
-    my $posn = $data[2];
-    if ($data[0] eq 'hidden'){
-      $order[$posn]="<input type=hidden name=$keys[$i2] value=\"$data[1]\">\n";
+    if ($lang){  
+        @languages=($lang,@languages);
     } else {
-      my $text;
-      if ($data[0] eq 'radio') {
-        $text="<input type=radio name=$keys[$i2] value=$data[1]>$data[1]
-       <input type=radio name=$keys[$i2] value=$data[2]>$data[2]";
-      } 
-      if ($data[0] eq 'text') {
-        $text="<input type=$data[0] name=$keys[$i2] value=\"$data[1]\" size=40>";
-      }
-      if ($data[0] eq 'textarea') {
-        $text="<textarea name=$keys[$i2] cols=40 rows=4>$data[1]</textarea>";
-      }
-      if ($data[0] eq 'select') {
-        $text="<select name=$keys[$i2]>";
-       my $i=1;
-               while ($data[$i] ne "") {
-         my $val = $data[$i+1];
-         $text = $text."<option value=$data[$i]>$val";
-         $i = $i+2;
-       }
-       $text=$text."</select>";
-      }        
-#      $string=$string.mktablerow(2,'white',$keys[$i2],$text);
-      $order[$posn]=mktablerow(2,'white',$keys[$i2],$text);
-    }
-    $i2++;
-  }
-  my $temp=join("\n",@order);
-  $string=$string.$temp;
-  $string=$string.mktablerow(1,'white','<input type=submit>');
-  $string=$string.mktableft;
-  $string=$string."</form>";
-}
-
-sub mkformnotable{
-  my ($action,@inputs)=@_;
-  my $string="<form action=$action method=post>\n";
-  my $count=@inputs;
-  for (my $i=0; $i<$count; $i++){
-    if ($inputs[$i][0] eq 'hidden'){
-      $string=$string."<input type=hidden name=$inputs[$i][1] value=\"$inputs[$i][2]\">\n";
+        $lang = $languages[0];
+    }      
+    my $theme = 'prog';        # in the event of theme failure default to 'prog' -fbcit
+    my $dbh = C4::Context->dbh;
+    my @themes;
+    if ( $interface eq "intranet" ) {
+        @themes    = split " ", C4::Context->preference("template");
     }
-    if ($inputs[$i][0] eq 'radio') {
-      $string.="<input type=radio name=$inputs[1] value=$inputs[$i][2]>$inputs[$i][2]";
-    } 
-    if ($inputs[$i][0] eq 'text') {
-      $string.="<input type=$inputs[$i][0] name=$inputs[$i][1] value=\"$inputs[$i][2]\">";
+    else {
+      # we are in the opac here, what im trying to do is let the individual user
+      # set the theme they want to use.
+      # and perhaps the them as well.
+        #my $lang = $query->cookie('KohaOpacLanguage');
+        @themes = split " ", C4::Context->preference("opacthemes");
     }
-    if ($inputs[$i][0] eq 'textarea') {
-        $string.="<textarea name=$inputs[$i][1] wrap=physical cols=40 rows=4>$inputs[$i][2]</textarea>";
-    }
-    if ($inputs[$i][0] eq 'reset'){
-      $string.="<input type=reset name=$inputs[$i][1] value=\"$inputs[$i][2]\">";
-    }    
-    if ($inputs[$i][0] eq 'submit'){
-      $string.="<input type=submit name=$inputs[$i][1] value=\"$inputs[$i][2]\">";
-    }    
-  }
-  $string=$string."</form>";
-}
 
-sub mkform2{
-    # FIXME
-    # no POD and no tests yet.  Once tests are written,
-    # this function can be cleaned up with the following steps:
-    #  turn the while loop into a foreach loop
-    #  pull the nested if,elsif structure back up to the main level
-    #  pull the code for the different kinds of inputs into separate
-    #   functions
-  my ($action,%inputs)=@_;
-  my $string="<form action=$action method=post>\n";
-  $string=$string.mktablehdr();
-  my $key;
-  my @order;
-  while ( my ($key, $value) = each %inputs) {
-    my @data=split('\t',$value);
-    my $posn = shift(@data);
-    my $reqd = shift(@data);
-    my $ltext = shift(@data);    
-    if ($data[0] eq 'hidden'){
-      $string=$string."<input type=hidden name=$key value=\"$data[1]\">\n";
-    } else {
-      my $text;
-      if ($data[0] eq 'radio') {
-        $text="<input type=radio name=$key value=$data[1]>$data[1]
-       <input type=radio name=$key value=$data[2]>$data[2]";
-      } elsif ($data[0] eq 'text') {
-        my $size = $data[1];
-        if ($size eq "") {
-          $size=40;
+ # searches through the themes and languages. First template it find it returns.
+ # Priority is for getting the theme right.
+    THEME:
+    foreach my $th (@themes) {
+        foreach my $la (@languages) {
+            #for ( my $pass = 1 ; $pass <= 2 ; $pass += 1 ) {
+                # warn "$htdocs/$th/$la/modules/$interface-"."tmpl";
+                #$la =~ s/([-_])/ $1 eq '-'? '_': '-' /eg if $pass == 2;
+                               if ( -e "$htdocs/$th/$la/modules/$tmpl") {
+                #".($interface eq 'intranet'?"modules":"")."/$tmpl" ) {
+                    $theme = $th;
+                    $lang  = $la;
+                    last THEME;
+                }
+                last unless $la =~ /[-_]/;
+            #}
         }
-        $text="<input type=$data[0] name=$key size=$size value=\"$data[2]\">";
-      } elsif ($data[0] eq 'textarea') {
-        my @size=split("x",$data[1]);
-        if ($data[1] eq "") {
-          $size[0] = 40;
-          $size[1] = 4;
-        }
-        $text="<textarea name=$key wrap=physical cols=$size[0] rows=$size[1]>$data[2]</textarea>";
-      } elsif ($data[0] eq 'select') {
-        $text="<select name=$key>";
-       my $sel=$data[1];
-       my $i=2;
-               while ($data[$i] ne "") {
-         my $val = $data[$i+1];
-                 $text = $text."<option value=\"$data[$i]\"";
-         if ($data[$i] eq $sel) {
-            $text = $text." selected";
-         }   
-          $text = $text.">$val";
-         $i = $i+2;
-       }
-       $text=$text."</select>";
-      }
-      if ($reqd eq "R") {
-        $ltext = $ltext." (Req)";
-       }
-      $order[$posn] =mktablerow(2,'white',$ltext,$text);
     }
-  }
-  $string=$string.join("\n",@order);
-  $string=$string.mktablerow(2,'white','<input type=submit>','<input type=reset>');
-  $string=$string.mktableft;
-  $string=$string."</form>";
+    return ( $theme, $lang );
 }
 
-=pod
+sub setlanguagecookie {
+    my ( $query, $language, $uri ) = @_;
+    my $cookie = $query->cookie(
+        -name    => 'KohaOpacLanguage',
+        -value   => $language,
+        -expires => ''
+    );
+    print $query->redirect(
+        -uri    => $uri,
+        -cookie => $cookie
+    );
+}
 
-=head2 &endpage
+=item pagination_bar
 
- &endpage does not expect any arguments, it returns the string:
-   </body></html>\n
+   pagination_bar($base_url, $nb_pages, $current_page, $startfrom_name)
 
-=cut
+Build an HTML pagination bar based on the number of page to display, the
+current page and the url to give to each page link.
 
-sub endpage() {
-  return("</body></html>\n");
-}
+C<$base_url> is the URL for each page link. The
+C<$startfrom_name>=page_number is added at the end of the each URL.
 
-=pod
+C<$nb_pages> is the total number of pages available.
 
-=head2 &mklink
+C<$current_page> is the current page number. This page number won't become a
+link.
 
- &mklink expects two arguments, the url to link to and the text of the link.
- It returns this string:
-   <a href="$url">$text</a>
- where $url is the first argument and $text is the second.
+This function returns HTML, without any language dependency.
 
 =cut
 
-sub mklink($$) {
-  my ($url,$text)=@_;
-  my $string="<a href=\"$url\">$text</a>";
-  return ($string);
-}
-
-=pod
+sub pagination_bar {
+       my $base_url = (@_ ? shift : $ENV{SCRIPT_NAME} . $ENV{QUERY_STRING}) or return undef;
+    my $nb_pages       = (@_) ? shift : 1;
+    my $current_page   = (@_) ? shift : undef; # delay default until later
+    my $startfrom_name = (@_) ? shift : 'page';
+
+    # how many pages to show before and after the current page?
+    my $pages_around = 2;
+
+       my $delim = qr/\&(?:amp;)?|;/;          # "non memory" cluster: no backreference
+       $base_url =~ s/$delim*\b$startfrom_name=(\d+)//g; # remove previous pagination var
+    unless (defined $current_page and $current_page > 0 and $current_page <= $nb_pages) {
+        $current_page = ($1) ? $1 : 1; # pull current page from param in URL, else default to 1
+               # $debug and    # FIXME: use C4::Debug;
+               # warn "with QUERY_STRING:" .$ENV{QUERY_STRING}. "\ncurrent_page:$current_page\n1:$1  2:$2  3:$3";
+    }
+       $base_url =~ s/($delim)+/$1/g;  # compress duplicate delims
+       $base_url =~ s/$delim;//g;              # remove empties
+       $base_url =~ s/$delim$//;               # remove trailing delim
+
+    my $url = $base_url . (($base_url =~ m/$delim/ or $base_url =~ m/\?/) ? '&amp;' : '?' ) . $startfrom_name . '=';
+    my $pagination_bar = '';
+
+    # navigation bar useful only if more than one page to display !
+    if ( $nb_pages > 1 ) {
+
+        # link to first page?
+        if ( $current_page > 1 ) {
+            $pagination_bar .=
+                "\n" . '&nbsp;'
+              . '<a href="'
+              . $url
+              . '1" rel="start">'
+              . '&lt;&lt;' . '</a>';
+        }
+        else {
+            $pagination_bar .=
+              "\n" . '&nbsp;<span class="inactive">&lt;&lt;</span>';
+        }
 
-=head2 &mkheadr
+        # link on previous page ?
+        if ( $current_page > 1 ) {
+            my $previous = $current_page - 1;
 
- &mkeadr expects two strings, a type and the text to use in the header.
- types are:
+            $pagination_bar .=
+                "\n" . '&nbsp;'
+              . '<a href="'
+              . $url
+              . $previous
+              . '" rel="prev">' . '&lt;' . '</a>';
+        }
+        else {
+            $pagination_bar .=
+              "\n" . '&nbsp;<span class="inactive">&lt;</span>';
+        }
 
-=over
+        my $min_to_display      = $current_page - $pages_around;
+        my $max_to_display      = $current_page + $pages_around;
+        my $last_displayed_page = undef;
+
+        for my $page_number ( 1 .. $nb_pages ) {
+            if (
+                   $page_number == 1
+                or $page_number == $nb_pages
+                or (    $page_number >= $min_to_display
+                    and $page_number <= $max_to_display )
+              )
+            {
+                if ( defined $last_displayed_page
+                    and $last_displayed_page != $page_number - 1 )
+                {
+                    $pagination_bar .=
+                      "\n" . '&nbsp;<span class="inactive">...</span>';
+                }
+
+                if ( $page_number == $current_page ) {
+                    $pagination_bar .=
+                        "\n" . '&nbsp;'
+                      . '<span class="currentPage">'
+                      . $page_number
+                      . '</span>';
+                }
+                else {
+                    $pagination_bar .=
+                        "\n" . '&nbsp;'
+                      . '<a href="'
+                      . $url
+                      . $page_number . '">'
+                      . $page_number . '</a>';
+                }
+                $last_displayed_page = $page_number;
+            }
+        }
 
-=item 1  ends with <br>
+        # link on next page?
+        if ( $current_page < $nb_pages ) {
+            my $next = $current_page + 1;
 
-=item 2  no special ending tag
+            $pagination_bar .= "\n"
+              . '&nbsp;<a href="'
+              . $url
+              . $next
+              . '" rel="next">' . '&gt;' . '</a>';
+        }
+        else {
+            $pagination_bar .=
+              "\n" . '&nbsp;<span class="inactive">&gt;</span>';
+        }
 
-=item 3  ends with <p>
+        # link to last page?
+        if ( $current_page != $nb_pages ) {
+            $pagination_bar .= "\n"
+              . '&nbsp;<a href="'
+              . $url
+              . $nb_pages
+              . '" rel="last">'
+              . '&gt;&gt;' . '</a>';
+        }
+        else {
+            $pagination_bar .=
+              "\n" . '&nbsp;<span class="inactive">&gt;&gt;</span>';
+        }
+    }
 
-=back
+    return $pagination_bar;
+}
 
- Other than this, the return value is the same:
-   <FONT SIZE=6><em>$text</em></FONT>$string
- Where $test is the text passed in and $string is the tag generated from 
- the type value.
+=item output_with_http_headers
 
-=cut
+   &output_with_http_headers($query, $cookie, $data, $content_type[, $status])
 
-sub mkheadr {
-    # FIXME
-    # would it be better to make this more generic by accepting an optional
-    # argument with a closing tag instead of a numeric type?
-
-  my ($type,$text)=@_;
-  my $string;
-  if ($type eq '1'){
-    $string="<FONT SIZE=6><em>$text</em></FONT><br>";
-  }
-  if ($type eq '2'){
-    $string="<FONT SIZE=6><em>$text</em></FONT><br>";
-  }
-  if ($type eq '3'){
-    $string="<FONT SIZE=6><em>$text</em></FONT><p>";
-  }
-  return ($string);
-}
+Outputs $data with the appropriate HTTP headers,
+the authentication cookie $cookie and a Content-Type specified in
+$content_type.
 
-=pod
+If applicable, $cookie can be undef, and it will not be sent.
 
-=head2 &center and &endcenter
+$content_type is one of the following: 'html', 'js', 'json', 'xml', 'rss', or 'atom'.
 
- &center and &endcenter take no arguments and return html tags <CENTER> and
- </CENTER> respectivley.
+$status is an HTTP status message, like '403 Authentication Required'. It defaults to '200 OK'.
 
 =cut
 
-sub center() {
-  return ("<CENTER>\n");
-}  
-
-sub endcenter() {
-  return ("</CENTER>\n");
-}  
+sub output_with_http_headers($$$$;$) {
+    my ( $query, $cookie, $data, $content_type, $status ) = @_;
+    $status ||= '200 OK';
+
+    my %content_type_map = (
+        'html' => 'text/html',
+        'js'   => 'text/javascript',
+        'json' => 'application/json',
+        'xml'  => 'text/xml',
+        # NOTE: not using application/atom+xml or application/rss+xml because of
+        # Internet Explorer 6; see bug 2078.
+        'rss'  => 'text/xml',
+        'atom' => 'text/xml'
+    );
+
+    die "Unknown content type '$content_type'" if ( !defined( $content_type_map{$content_type} ) );
+    my $options = {
+        type    => $content_type_map{$content_type},
+        status  => $status,
+        charset => 'UTF-8',
+        Pragma          => 'no-cache',
+        'Cache-Control' => 'no-cache',
+    };
+    $options->{cookie} = $cookie if $cookie;
+    if ($content_type eq 'html') {  # guaranteed to be one of the content_type_map keys, else we'd have died
+        $options->{'Content-Style-Type' } = 'text/css';
+        $options->{'Content-Script-Type'} = 'text/javascript';
+    }
+    print $query->header($options), $data;
+}
 
-=pod
+sub output_html_with_http_headers ($$$) {
+    my ( $query, $cookie, $data ) = @_;
+    output_with_http_headers( $query, $cookie, $data, 'html' );
+}
 
-=head2 &bold
+sub is_ajax () {
+    my $x_req = $ENV{HTTP_X_REQUESTED_WITH};
+    return ( $x_req and $x_req =~ /XMLHttpRequest/i ) ? 1 : 0;
+}
 
- &bold requires that a single string be passed in by the caller.  &bold 
- will return "<b>$text</b>" where $text is the string passed in.
+END { }    # module clean-up code here (global destructor)
 
-=cut
+1;
+__END__
 
-sub bold($) {
-  my ($text)=shift;
-  return("<b>$text</b>");
-}
+=back
 
-#---------------------------------------------
-# Create an HTML option list for a <SELECT> form tag by using
-#    values from a DB file
-sub getkeytableselectoptions {
-       use strict;
-       # inputs
-       my (
-               $dbh,           # DBI handle
-               $tablename,     # name of table containing list of choices
-               $keyfieldname,  # column name of code to use in option list
-               $descfieldname, # column name of descriptive field
-               $showkey,       # flag to show key in description
-               $default,       # optional default key
-       )=@_;
-       my $selectclause;       # return value
-
-       my (
-               $sth, $query, 
-               $key, $desc, $orderfieldname,
-       );
-       my $debug=0;
-
-       requireDBI($dbh,"getkeytableselectoptions");
-
-       if ( $showkey ) {
-               $orderfieldname=$keyfieldname;
-       } else {
-               $orderfieldname=$descfieldname;
-       }
-       $query= "select $keyfieldname,$descfieldname
-               from $tablename
-               order by $orderfieldname ";
-       print "<PRE>Query=$query </PRE>\n" if $debug; 
-       $sth=$dbh->prepare($query);
-       $sth->execute;
-       while ( ($key, $desc) = $sth->fetchrow) {
-           if ($showkey || ! $desc ) { $desc="$key - $desc"; }
-           $selectclause.="<option";
-           if (defined $default && $default eq $key) {
-               $selectclause.=" selected";
-           }
-           $selectclause.=" value='$key'>$desc\n";
-           print "<PRE>Sel=$selectclause </PRE>\n" if $debug; 
-       }
-       return $selectclause;
-} # sub getkeytableselectoptions
-
-#---------------------------------
-
-END { }       # module clean-up code here (global destructor)
-    
+=head1 AUTHOR
 
+Koha Developement team <info@koha.org>
 
+=cut