Add new system preferences editor
authorJesse Weaver <pianohacker@gmail.com>
Mon, 7 Sep 2009 05:02:47 +0000 (23:02 -0600)
committerJesse Weaver <pianohacker@gmail.com>
Mon, 7 Sep 2009 05:02:47 +0000 (23:02 -0600)
This new editor uses data files instead of descriptions stored in the
database. It also has improved search.

15 files changed:
C4/Bookfund.pm
C4/Context.pm
C4/Output.pm
C4/Service.pm
admin/preferences.pl [new file with mode: 0755]
koha-tmpl/intranet-tmpl/prog/en/css/preferences.css [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/css/staff-global.css
koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc
koha-tmpl/intranet-tmpl/prog/en/includes/prefs-admin-search.inc [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/includes/prefs-menu.inc [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/js/pages/preferences.js [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tmpl
koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences.tmpl [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/modules/intranet-main.tmpl
svc/config/systempreferences [new file with mode: 0755]

index 8e9c95f..d8dc6af 100644 (file)
@@ -24,7 +24,7 @@ use strict;
 use vars qw($VERSION @ISA @EXPORT);
 
 # set the version for version checking
-$VERSION = 3.00;
+$VERSION = 3.01;
 
 =head1 NAME
 
@@ -48,6 +48,7 @@ They allow to get and/or set some informations for a specific budget or currency
     &ModBookFund &ModCurrencies
     &SearchBookFund
     &Countbookfund 
+    &GetLocalCurrency
     &ConvertCurrency
     &DelBookFund
 );
@@ -201,6 +202,34 @@ sub GetCurrencies {
 
 #-------------------------------------------------------------#
 
+=head3 GetLocalCurrency
+
+$currency = GetLocalCurrency;
+
+Returns the currency with exchange rate 1 (the local currency).
+
+$currency is a reference-to-hash, whose keys are the fields from the currency
+table in the Koha database.
+
+=cut
+
+sub GetLocalCurrency {
+    my $dbh = C4::Context->dbh;
+
+    my $result = $dbh->selectrow_hashref("
+      SELECT
+        currency, symbol, timestamp, rate
+        FROM currency
+        WHERE rate = 1
+    ");
+
+    die "No currency found with rate 1" if ( !defined( $result ) );
+
+    return $result;
+}
+
+#-------------------------------------------------------------#
+
 =head3 GetBookFundBreakdown
 
 ( $spent, $comtd ) = &GetBookFundBreakdown( $id, $start, $end );
index 99019ea..b0b6293 100644 (file)
@@ -507,6 +507,37 @@ sub clear_syspref_cache {
     %sysprefs = ();
 }
 
+=head2 set_preference
+
+  C4::Context->set_preference( $variable, $value );
+
+  This updates a preference's value both in the systempreferences table and in
+  the sysprefs cache.
+
+=cut
+
+sub set_preference {
+    my $self = shift;
+    my $var = shift;
+    my $value = shift;
+
+    my $dbh = C4::Context->dbh or return 0;
+
+    my $type = $dbh->selectrow_array( "SELECT type FROM systempreferences WHERE variable = ?", {}, $var );
+
+    $value = 0 if ( $type && $type eq 'YesNo' && $value eq '' );
+
+    my $sth = $dbh->prepare( "
+      INSERT INTO systempreferences
+        ( variable, value )
+        VALUES( ?, ? )
+        ON DUPLICATE KEY UPDATE value = VALUES(value)
+    " );
+
+    $sth->execute( $var, $value );
+    $sth->finish;
+}
+
 # AUTOLOAD
 # This implements C4::Config->foo, and simply returns
 # C4::Context->config("foo"), as described in the documentation for
index 5bebf6c..344a45f 100644 (file)
@@ -69,17 +69,10 @@ my $path = C4::Context->config('intrahtdocs') . "/prog/en/includes/";
 
 #---------------------------------------------------------------------------------------------------------
 # FIXME - POD
-sub gettemplate {
+
+sub _get_template_file {
     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 $htdocs = C4::Context->config( $interface ne 'intranet' ? 'opachtdocs' : 'intrahtdocs' );
     my ( $theme, $lang ) = themelanguage( $htdocs, $tmplbase, $interface, $query );
 
     # if the template doesn't exist, load the English one as a last resort
@@ -88,6 +81,17 @@ sub gettemplate {
         $lang = 'en';
         $filename = "$htdocs/$theme/$lang/modules/$tmplbase";
     }
+
+    return ( $htdocs, $theme, $lang, $filename );
+}
+
+sub gettemplate {
+    my ( $tmplbase, $interface, $query ) = @_;
+    ($query) or warn "no query in gettemplate";
+    my $path = C4::Context->preference('intranet_includes') || 'includes';
+    my $opacstylesheet = C4::Context->preference('opacstylesheet');
+    my ( $htdocs, $theme, $lang, $filename ) = _get_template_file( $tmplbase, $interface, $query );
+
     my $template       = HTML::Template::Pro->new(
         filename          => $filename,
         die_on_bad_params => 1,
index e39334e..bf7ef57 100644 (file)
@@ -85,7 +85,7 @@ sub init {
 
     our $cookie = $cookie_; # I have no desire to offend the Perl scoping gods
 
-    $class->return_error( type => 'auth', message => $status ) if ( $status ne 'ok' );
+    $class->return_error( 'auth', $status ) if ( $status ne 'ok' );
 
     return ( $query, new C4::Output::JSONStream );
 }
diff --git a/admin/preferences.pl b/admin/preferences.pl
new file mode 100755 (executable)
index 0000000..c359d32
--- /dev/null
@@ -0,0 +1,340 @@
+#!/usr/bin/perl
+#
+# Copyright 2009 Jesse Weaver and the Koha Dev Team
+#
+# 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 warnings;
+
+use CGI;
+use C4::Auth;
+use C4::Context;
+use C4::Koha;
+use C4::Languages qw(getTranslatedLanguages);
+use C4::ClassSource;
+use C4::Log;
+use C4::Output;
+use C4::Bookfund qw(GetLocalCurrency);
+use File::Spec;
+use IO::File;
+use YAML::Syck qw();
+$YAML::Syck::ImplicitTyping = 1;
+our $lang;
+
+# use Smart::Comments;
+#
+
+sub GetTab {
+    my ( $input, $tab ) = @_;
+
+    my $tab_template = C4::Output::gettemplate( 'admin/preferences/' . $tab . '.pref', 'intranet', $input );
+
+    $tab_template->param(
+        local_currency => GetLocalCurrency()->{'currency'}, # currency code is used, because we do not know how a given currency is formatted.
+    );
+
+    return YAML::Syck::Load( $tab_template->output() );
+}
+
+sub _get_chunk {
+    my ( $value, %options ) = @_;
+
+    my $name = $options{'pref'};
+    my $chunk = { name => $name, value => $value, type => $options{'type'} || 'input', class => $options{'class'} };
+
+    if ( $options{'class'} && $options{'class'} eq 'password' ) {
+        $chunk->{'input_type'} = 'password';
+    } elsif ( $options{'type'} && ( $options{'type'} eq 'opac-languages' || $options{'type'} eq 'staff-languages' ) ) {
+        my $current_languages = { map { +$_, 1 } split( /\s*,\s*/, $value ) };
+
+        my $theme;
+        my $interface;
+        if ( $options{'type'} eq 'opac-languages' ) {
+            # this is the OPAC
+            $interface = 'opac';
+            $theme     = C4::Context->preference('opacthemes');
+        } else {
+            # this is the staff client
+            $interface = 'intranet';
+            $theme     = C4::Context->preference('template');
+        }
+        $chunk->{'languages'} = getTranslatedLanguages( $interface, $theme, $lang, $current_languages );
+        $chunk->{'type'} = 'languages';
+    } elsif ( $options{ 'choices' } ) {
+        if ( $options{'choices'} && ref( $options{ 'choices' } ) eq '' ) {
+            if ( $options{'choices'} eq 'class-sources' ) {
+                my $sources = GetClassSources();
+                $options{'choices'} = { map { $_ => $sources->{$_}->{'description'} } keys %$sources };
+            } elsif ( $options{'choices'} eq 'opac-templates' ) {
+                $options{'choices'} = { map { $_ => $_ } getallthemes( 'opac' ) }
+            } elsif ( $options{'choices'} eq 'staff-templates' ) {
+                $options{'choices'} = { map { $_ => $_ } getallthemes( 'intranet' ) }
+            } else {
+                die 'Unrecognized source of preference values: ' . $options{'choices'};
+            }
+        }
+
+        $value ||= 0;
+
+        $chunk->{'type'} = 'select';
+        $chunk->{'CHOICES'} = [
+            sort { $a->{'text'} cmp $b->{'text'} }
+            map { { text => $options{'choices'}->{$_}, value => $_, selected => ( $_ eq $value || ( $_ eq '' && ( $value eq '0' || !$value ) ) ) } }
+            keys %{ $options{'choices'} }
+        ];
+    }
+
+    $chunk->{ 'type_' . $chunk->{'type'} } = 1;
+
+    return $chunk;
+}
+
+sub TransformPrefsToHTML {
+    my ( $data, $highlighted_pref ) = @_;
+
+    my @lines;
+    my $dbh = C4::Context->dbh;
+    my $title = ( keys( %$data ) )[0];
+    my $tab = $data->{ $title };
+    $tab = { '' => $tab } if ( ref( $tab ) eq 'ARRAY' );
+
+    foreach my $group ( sort keys %$tab ) {
+        if ( $group ) {
+            push @lines, { is_group_title => 1, title => $group };
+        }
+
+        foreach my $line ( @{ $tab->{ $group } } ) {
+            my @chunks;
+            my @names;
+
+            foreach my $piece ( @$line ) {
+                if ( ref ( $piece ) eq 'HASH' ) {
+                    my $name = $piece->{'pref'};
+
+                    if ( $name ) {
+                        my $row = $dbh->selectrow_hashref( "SELECT value FROM systempreferences WHERE variable = ?", {}, $name );
+                        my $value;
+                        if ( !defined( $row ) && defined( $piece->{'default'} ) ) {
+                            $value = $piece->{'default'};
+                        } else {
+                            $value = $row->{'value'};
+                        }
+                        my $chunk = _get_chunk( $value, %$piece );
+
+                        $chunk->{'highlighted'} = 1 if ( $highlighted_pref && $name =~ /$highlighted_pref/ );
+
+                        push @chunks, $chunk;
+                        push @names, { name => $name, highlighted => ( $highlighted_pref && ( $name =~ /$highlighted_pref/i ? 1 : 0 ) ) };
+                    } else {
+                        push @chunks, $piece;
+                    }
+                } else {
+                    push @chunks, { type_text => 1, contents => $piece };
+                }
+            }
+
+            push @lines, { CHUNKS => \@chunks, NAMES => \@names };
+        }
+    }
+
+    return $title, \@lines;
+}
+
+sub _get_pref_files {
+    my ( $input, $open_files ) = @_;
+
+    my ( $htdocs, $theme, $lang, undef ) = C4::Output::_get_template_file( 'admin/preferences/admin.pref', 'intranet', $input );
+
+    my %results;
+
+    foreach my $file ( glob( "$htdocs/$theme/$lang/modules/admin/preferences/*.pref" ) ) {
+        my ( $tab ) = ( $file =~ /([a-z0-9_-]+)\.pref$/ );
+
+        $results{$tab} = $open_files ? new IO::File( $file, 'r' ) : '';
+    }
+
+    return %results;
+}
+
+sub JumpPref {
+    my ( $input, $tab, $jumpfield ) = @_;
+
+    return ( $tab ) if ( $jumpfield !~ /^[a-zA-Z_0-9-]+$/ );
+
+    my %tab_files = _get_pref_files( $input, 1 );
+
+    while ( my ( $tab, $tabfile ) = each %tab_files ) {
+        while ( <$tabfile> ) {
+            return ( $tab, $1 ) if ( /pref: ($jumpfield)/i );
+        }
+
+        close $tabfile;
+    }
+
+    return ( "", "" );
+}
+
+sub SearchPrefs {
+    my ( $input, $searchfield ) = @_;
+    my @tabs;
+
+    my %tab_files = _get_pref_files( $input );
+    our @terms = split( /\s+/, $searchfield );
+
+    sub matches {
+        my ( $text ) = @_;
+
+        return !grep( { $text !~ /$_/i } @terms );
+    }
+
+    foreach my $tab_name ( keys %tab_files ) {
+        my $data = GetTab( $input, $tab_name );
+        my $title = ( keys( %$data ) )[0];
+        my $tab = $data->{ $title };
+        $tab = { '' => $tab } if ( ref( $tab ) eq 'ARRAY' );
+
+        my $matched_groups;
+
+        while ( my ( $group_title, $contents ) = each %$tab ) {
+            if ( matches( $group_title ) ) {
+                $matched_groups->{$group_title} = $contents;
+                next;
+            }
+
+            my @new_contents;
+
+            foreach my $line ( @$contents ) {
+                my $matched;
+
+                foreach my $piece ( @$line ) {
+                    if ( ref( $piece ) eq 'HASH' ) {
+                        if ( ref( $piece->{'choices'} ) eq 'HASH' && grep( { $_ && matches( $_ ) } values( %{ $piece->{'choices'} } ) ) ) {
+                            $matched = 1;
+                        } elsif ( matches( $piece->{'pref'} ) ) {
+                            $matched = 1;
+                        }
+                    } elsif ( matches( $piece ) ) {
+                        $matched = 1;
+                    }
+                    last if ( $matched );
+                }
+
+                push @new_contents, $line if ( $matched );
+            }
+
+            $matched_groups->{$group_title} = \@new_contents if ( @new_contents );
+        }
+
+        if ( $matched_groups ) {
+            my ( $title, $LINES ) = TransformPrefsToHTML( { $title => $matched_groups }, $searchfield );
+
+            push @tabs, { tab => $tab, tab_title => $title, LINES => $LINES, };
+        }
+    }
+
+    return @tabs;
+}
+
+my $dbh = C4::Context->dbh;
+our $input = new CGI;
+
+my ( $template, $borrowernumber, $cookie ) = get_template_and_user(
+    {   template_name   => "admin/preferences.tmpl",
+        query           => $input,
+        type            => "intranet",
+        authnotrequired => 0,
+        flagsrequired   => { parameters => 1 },
+        debug           => 1,
+    }
+);
+
+$lang = $template->param( 'lang' );
+my $op = $input->param( 'op' ) || '';
+my $tab = $input->param( 'tab' );
+$tab ||= 'local-use';
+
+my $highlighted;
+
+if ( $op eq 'save' ) {
+    unless ( C4::Context->config( 'demo' ) ) {
+        foreach my $param ( $input->param() ) {
+            my ( $pref ) = ( $param =~ /pref_(.*)/ );
+
+            next if ( !defined( $pref ) );
+
+            my $value = join( ',', $input->param( $param ) );
+
+            C4::Context->set_preference( $pref, $value );
+            logaction( 'SYSTEMPREFERENCE', 'MODIFY', undef, $pref . " | " . $value );
+        }
+    }
+
+    print $input->redirect( '/cgi-bin/koha/admin/preferences.pl?tab=' . $tab );
+    exit;
+} elsif ( $op eq 'jump' ) {
+    my $jumpfield = $input->param( 'jumpfield' );
+    $template->param( jumpfield => $jumpfield );
+
+    my $new_tab;
+    ( $new_tab, $highlighted ) = JumpPref( $input, $tab, $jumpfield );
+
+    if ( $highlighted ) {
+        $tab = $new_tab;
+    } else {
+        $template->param( jump_not_found => 1 );
+    }
+}
+
+my @TABS;
+
+if ( $op eq 'search' ) {
+    my $searchfield = $input->param( 'searchfield' );
+
+    $searchfield =~ s/[^a-zA-Z0-9_ -]//g;
+
+    $template->param( searchfield => $searchfield );
+
+    @TABS = SearchPrefs( $input, $searchfield );
+
+    foreach my $tabh ( @TABS ) {
+        $template->param(
+            $tabh->{'tab'} => 1
+        );
+    }
+
+    if ( @TABS ) {
+        $tab = ''; # No need to load a particular tab, as we found results
+    } else {
+        $template->param(
+            search_not_found => 1,
+        );
+    }
+}
+
+if ( $tab ) {
+    my ( $tab_title, $LINES ) = TransformPrefsToHTML( GetTab( $input, $tab ), $highlighted, ( $op eq 'jump' ) );
+
+    push @TABS, { tab_title => $tab_title, LINES => $LINES };
+    $template->param(
+        $tab => 1,
+        tab => $tab,
+    );
+}
+
+$template->param( TABS => \@TABS );
+
+output_html_with_http_headers $input, $cookie, $template->output;
diff --git a/koha-tmpl/intranet-tmpl/prog/en/css/preferences.css b/koha-tmpl/intranet-tmpl/prog/en/css/preferences.css
new file mode 100644 (file)
index 0000000..54ff036
--- /dev/null
@@ -0,0 +1,25 @@
+.preference-url, .preference-multi, .preference-long, .preference-file {
+    width: 20em;
+}
+
+.preference-short, .preference-email {
+    width: 10em;
+}
+
+.preference-integer, .preference-percent, .preference-date, .preference-currency {
+    width: 5em;
+}
+
+textarea.preference {
+    width: 35em;
+       height: 20em;
+       display: block;
+}
+
+textarea.preference-code, .preference-file {
+       font-family: monospace;
+}
+
+a.expand-textarea {
+    display: block;
+}
index 076e405..61e6331 100644 (file)
@@ -1448,33 +1448,15 @@ div#header_search input.submit {
        font-size : 1em;
 }
 
-input[type=submit], input[type=button] {
-       border: 1px outset #999999;
-       border-top-color: #666;
-       border-left-color: #666;
-       padding: 0.25em;
-       background : #E8E8E8 url(../../img/button-bg.gif) top left repeat-x;
-       color: #333333;
-}
-
-input.submit,button.submit {
-       border: 1px solid #999999;
-       border-top-color: #666;
-       border-left-color: #666;
-       padding: 0.25em;
-       background : #E8E8E8 url(../../img/button-bg.gif) top left repeat-x;
-       color: #333333;
-}
-
 *html input.submit {
        padding : .1em;
 }
 
-input[type=submit]:active, input[type=button]:active {
+input[type=submit]:active, input[type=button]:active, button.submit:active {
        border : 1px inset #999999;
 }
 
-input[type=reset], input[type=button], input.submit, button.submit {
+input[type=submit], input[type=reset], input[type=button], input.submit, button.submit {
        border: 1px outset #999999;
        border-top-color: #666;
        border-left-color: #666;
@@ -1789,3 +1771,9 @@ h1#xml a {
        padding:44px 0 0;
        text-decoration:none;
        width:35px;
+}
+
+img.spinner {
+    vertical-align: middle;
+    padding-right: 0.3em;
+}
index e14f347..1ab3734 100644 (file)
@@ -1,6 +1,6 @@
 <h5>System Preferences</h5>
 <ul>
-       <li><a href="/cgi-bin/koha/admin/systempreferences.pl">System Preferences</a></li>
+       <li><a href="/cgi-bin/koha/admin/preferences.pl">System Preferences</a></li>
 </ul>
 
 <h5>Basic parameters</h5>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/prefs-admin-search.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/prefs-admin-search.inc
new file mode 100644 (file)
index 0000000..907a096
--- /dev/null
@@ -0,0 +1,28 @@
+<h1 id="logo"><a href="/cgi-bin/koha/mainpage.pl"><!-- TMPL_VAR NAME="LibraryName" --></a></h1><!-- Begin Stopwords Resident Search Box -->
+<div id="header_search">
+       <div id="syspref_search" class="residentsearch">
+       <p class="tip">System preference search:</p>
+           <form action="/cgi-bin/koha/admin/preferences.pl">
+        <input type="hidden" name="tab" value="<!-- TMPL_VAR NAME="last_tab" -->" />
+        <input type="hidden" name="op" value="search" />
+        <input type="text" size="40" name="searchfield" value="<!-- TMPL_VAR NAME="searchfield" -->" />
+        <input type="submit" class="submit" value="Search" />
+    </form>
+       </div>
+    <!-- TMPL_INCLUDE NAME="patron-search-box.inc" -->
+       <!-- TMPL_IF NAME="CAN_user_catalogue" -->
+       <div id="catalog_search" class="residentsearch" style="display:none;">
+       <p class="tip">Enter search keywords:</p>
+               <form action="/cgi-bin/koha/catalogue/search.pl"  method="get" id="cat-search-block">
+                        <input type="text" name="q" id="search-form" size="40" value="" title="Enter the terms you wish to search for." class="form-text" />
+                               <input type="submit" value="Submit" class="submit" />
+               </form>
+       </div>
+       <!-- /TMPL_IF -->
+                       <ul>
+                       <li><a href="/cgi-bin/koha/admin/roadtype.pl#syspref_search">Search System Preferences</a></li>
+                       <!-- TMPL_IF NAME="CAN_user_circulate" --><li><a href="/cgi-bin/koha/circ/circulation.pl#circ_search">Check Out</a></li><!-- /TMPL_IF -->
+                       <!-- TMPL_IF NAME="CAN_user_catalogue" --><li><a href="/cgi-bin/koha/catalogue/search.pl#catalog_search">Search the Catalog</a></li><!-- /TMPL_IF -->
+                       </ul>
+</div>
+<!-- End Stopwords Resident Search Box -->
diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/prefs-menu.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/prefs-menu.inc
new file mode 100644 (file)
index 0000000..5c5e1cf
--- /dev/null
@@ -0,0 +1,18 @@
+<div id="menu">
+<ul>
+<!-- TMPL_IF NAME="admin" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Administration" href="/cgi-bin/koha/admin/preferences.pl?tab=admin">Administration</a></li>
+<!-- TMPL_IF NAME="acquisitions" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Acquisitions" href="/cgi-bin/koha/admin/preferences.pl?tab=acquisitions">Acquisitions</a></li>
+<!-- TMPL_IF NAME="authorities" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Authority Control" href="/cgi-bin/koha/admin/preferences.pl?tab=authorities">Authorities</a></li>
+<!-- TMPL_IF NAME="cataloguing" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Cataloging" href="/cgi-bin/koha/admin/preferences.pl?tab=cataloguing">Cataloging</a></li>
+<!-- TMPL_IF NAME="circulation" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Circulation" href="/cgi-bin/koha/admin/preferences.pl?tab=circulation">Circulation</a></li>
+<!-- TMPL_IF NAME="enhanced-content" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Enhanced Content Settings" href="/cgi-bin/koha/admin/preferences.pl?tab=enhanced-content">Enhanced Content</a></li>
+<!-- TMPL_IF NAME="i18n/l10n" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Internationalization and Localization" href="/cgi-bin/koha/admin/preferences.pl?tab=i18n-l10n">I18N/L10N</a></li>
+<!-- TMPL_IF NAME="logs" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Transaction Logs" href="/cgi-bin/koha/admin/preferences.pl?tab=logs">Logs</a></li>
+<!-- TMPL_IF NAME="oai-pmh" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="OAI-PMH" href="/cgi-bin/koha/admin/preferences.pl?tab=oai-pmh">OAI-PMH</a></li>
+<!-- TMPL_IF NAME="opac" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Online Public Access Catalog" href="/cgi-bin/koha/admin/preferences.pl?tab=opac">OPAC</a></li>
+<!-- TMPL_IF NAME="patrons" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Patrons" href="/cgi-bin/koha/admin/preferences.pl?tab=patrons">Patrons</a></li>
+<!-- TMPL_IF NAME="searching" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Searching" href="/cgi-bin/koha/admin/preferences.pl?tab=searching">Searching</a></li>
+<!-- TMPL_IF NAME="staff-client" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a title="Staff Client" href="/cgi-bin/koha/admin/preferences.pl?tab=staff-client">Staff Client</a></li>
+<!-- TMPL_IF NAME="" --><li class="active"><!-- TMPL_ELSE --><li><!-- /TMPL_IF --><a href="/cgi-bin/koha/admin/preferences.pl">Local Use</a></li>
+</ul>
+</div>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/js/pages/preferences.js b/koha-tmpl/intranet-tmpl/prog/en/js/pages/preferences.js
new file mode 100644 (file)
index 0000000..bbc0b1b
--- /dev/null
@@ -0,0 +1,60 @@
+// We can assume 'KOHA' exists, as we depend on KOHA.AJAX
+
+KOHA.Preferences = {
+    Save: function ( form ) {
+        data = $( form ).find( '.modified' ).serialize();
+        if ( !data ) {
+            humanMsg.displayAlert( 'Nothing to save' );
+            return;
+        }
+        KOHA.AJAX.MarkRunning( $( form ).find( '.save-all' ), _( 'Saving...' ) );
+        KOHA.AJAX.Submit( {
+            data: data,
+            url: '/cgi-bin/koha/svc/config/systempreferences/',
+            success: function ( data ) { KOHA.Preferences.Success( form ) },
+            complete: function () { KOHA.AJAX.MarkDone( $( form ).find( '.save-all' ) ) }
+        } );
+    },
+    Success: function ( form ) {
+        humanMsg.displayAlert( 'Saved' );
+
+        $( form )
+            .find( '.modified-warning' ).remove().end()
+            .find( '.modified' ).removeClass('modified');
+        KOHA.Preferences.Modified = false;
+    }
+};
+
+$( document ).ready( function () {
+    $( '.prefs-tab .preference' ).change( function () {
+        $( this.form ).find( '.save-all' ).removeAttr( 'disabled' );
+        $( this ).addClass( 'modified' );
+        var name_cell = $( this ).parent().parent().find( '.name-cell' );
+
+               if ( !name_cell.find( '.modified-warning' ).length ) name_cell.append( '<em class="modified-warning">(modified)</em>' );
+        KOHA.Preferences.Modified = true;
+    } );
+
+    if ( document.location.search.indexOf( 'jumpfield' ) != -1 ) {
+        document.location.hash = "highlighted";
+    }
+
+    $( '.prefs-tab .expand-textarea' ).show().click( function () {
+        $( this ).hide().nextAll( 'textarea, input[type=submit]' )
+            .animate( { height: 'show', queue: false } )
+            .animate( { opacity: 1 } );
+
+        return false;
+    } ).nextAll( 'textarea, input[type=submit]' ).hide().css( { opacity: 0 } );
+
+    $( '.prefs-tab .save-all' ).attr( 'disabled', true ).click( function () {
+        KOHA.Preferences.Save( this.form );
+        return false;
+    } );
+
+    window.onbeforeunload = function () {
+        if ( KOHA.Preferences.Modified ) {
+            return _( "You have made changes to system preferences." );
+        }
+    }
+} );
index 11430dc..9b62be2 100644 (file)
        <div class="yui-g">
 
 <div class="yui-u first">
-<form action="/cgi-bin/koha/admin/systempreferences.pl" method="post">
+<form action="/cgi-bin/koha/admin/preferences.pl" method="post">
 <fieldset>
-<h4><a href="/cgi-bin/koha/admin/systempreferences.pl">Global system preferences</a></h4>
+<h4><a href="/cgi-bin/koha/admin/preferences.pl">Global system preferences</a></h4>
        <p>Manage global system preferences like MARC flavor, date format, administrator email, and templates.</p>
-               <input type="hidden" value="all" name="tab" />
+        <input type="hidden" name="op" value="search" />
         <input type="text" name="searchfield" value="<!-- TMPL_VAR NAME="searchfield" -->" />
         <input type="submit" name="ok" class="submit" value="Search" />
 </fieldset>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences.tmpl
new file mode 100644 (file)
index 0000000..ecaf758
--- /dev/null
@@ -0,0 +1,165 @@
+<!-- TMPL_INCLUDE NAME="doc-head-open.inc" -->
+<title>Koha &rsaquo; Administration &rsaquo; System Preferences</title>
+<!-- TMPL_INCLUDE NAME="doc-head-close.inc" -->
+<link rel="stylesheet" type="text/css" href="<!-- TMPL_VAR NAME="themelang"-->/css/preferences.css">
+<link rel="stylesheet" type="text/css" href="<!-- TMPL_VAR NAME="themelang"-->/css/humanmsg.css">
+<script src="<!-- TMPL_VAR NAME="themelang" -->/lib/jquery/plugins/humanmsg.js" type="text/javascript"></script>
+<script src="<!-- TMPL_VAR NAME="themelang" -->/js/ajax.js" type="text/javascript"></script>
+<script src="<!-- TMPL_VAR NAME="themelang" -->/js/pages/preferences.js" type="text/javascript"></script>
+<script src="<!-- TMPL_VAR NAME="themelang" -->/lib/jquery/plugins/jquery.highlight-3.js" type="text/javascript"></script>
+<script type="text/javascript">
+    <!--
+    <!-- TMPL_IF NAME="searchfield" -->
+    var to_highlight = "<!-- TMPL_VAR NAME="searchfield" ESCAPE="JS"-->";
+
+    if ( to_highlight ) {
+        var words = to_highlight.split( ' ' );
+        $( document ).ready( function () {
+            $( '.prefs-tab table' ).find( 'td, th' ).not( '.name-cell' ).each( function ( i, td ) {
+                $.each( words, function ( i, word ) { $( td ).highlight( word ) } );
+            } ).find( 'option' ).removeHighlight();
+        } );
+    }
+    <!-- /TMPL_IF -->
+    // -->
+</script>
+</head>
+<body>
+<!-- TMPL_INCLUDE NAME="header.inc" -->
+<!-- TMPL_INCLUDE NAME="prefs-admin-search.inc" -->
+
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a> &rsaquo; System Preferences</div>
+
+<div id="doc3" class="yui-t2">
+
+   <div id="bd">
+    <div id="yui-main">
+    <div class="yui-b">
+       <div id="toolbar">
+               <form action="/cgi-bin/koha/admin/preferences.pl" method="GET">
+                       <input type="hidden" name="op" value="jump" />
+                       <input type="hidden" name="tab" value="<!-- TMPL_VAR NAME="tab" -->" />
+                       <label for="jumpfield">Jump to named preference:</label>
+                       <input type="text" name="jumpfield" id="jumpfield" value="<!-- TMPL_VAR NAME="jumpfield" -->" />
+                       <input type="submit" value="Jump" />
+               </form>
+       </div>
+
+    <h1>System preferences</h1>
+    <!-- TMPL_IF NAME="jump_not_found" -->
+    <div class="dialog error">
+        Could not find a system preference named <code><!-- TMPL_VAR NAME="jumpfield" --></code>.
+    </div>
+    <!-- /TMPL_IF -->
+    <!-- TMPL_IF NAME="search_not_found" -->
+    <div class="dialog error">
+        No system preferences matched your search for <strong><!-- TMPL_VAR NAME="searchfield" --></strong>. You can try a different search or <a href="/cgi-bin/koha/admin/preferences.pl?tab=<!-- TMPL_VAR NAME="last_tab" -->">return to where you were before.</a>
+    </div>
+    <!-- /TMPL_IF -->
+    <!-- TMPL_LOOP NAME="TABS" -->
+    <div class="prefs-tab">
+    <h2><!-- TMPL_VAR NAME="tab_title" --> preferences</h2>
+    <form action="/cgi-bin/koha/admin/preferences.pl" method="POST">
+        <input type="hidden" name="op" value="save" />
+        <input type="hidden" name="tab" value="<!-- TMPL_VAR NAME="tab" -->" />
+        <table>
+            <thead><tr><th>Preference</th><th>Value</th></tr></thead>
+
+            <!-- TMPL_LOOP NAME="LINES" -->
+            <!-- TMPL_IF NAME="is_group_title" -->
+            <!-- TMPL_UNLESS NAME="__first__" --></tbody><!-- /TMPL_UNLESS -->
+                       <thead>
+                               <!-- TMPL_IF NAME="highlighted" -->
+                               <tr><th colspan="2"><span class="term"><!-- TMPL_VAR NAME="title" --></span></th></tr>
+                               <!-- TMPL_ELSE -->
+                               <tr><th colspan="2"><!-- TMPL_VAR NAME="title" --></th></tr>
+                               <!-- /TMPL_IF -->
+                       </thead>
+            <!-- TMPL_UNLESS NAME="__last__" --><tbody><!-- /TMPL_UNLESS -->
+            <!-- TMPL_ELSE -->
+            <!-- TMPL_IF NAME="__first__" --><tbody><!-- /TMPL_IF -->
+            <tr>
+                <td class="name-cell">
+                    <code>
+                        <!-- TMPL_LOOP NAME="NAMES" -->
+                                               <label for="pref_<!-- TMPL_VAR NAME="name" -->">
+                                                       <!-- TMPL_IF NAME="highlighted" -->
+                                                       <a name="highlighted"></a>
+                                                       <span class="term"><!-- TMPL_VAR NAME="name" --></span>
+                                                       <!-- TMPL_ELSE -->
+                                                       <!-- TMPL_VAR NAME="name" -->
+                                                       <!-- /TMPL_IF -->
+                                               </label>
+                        <!-- TMPL_UNLESS NAME="__last__" --><br /><!-- /TMPL_UNLESS -->
+                        <!-- /TMPL_LOOP -->
+                    </code>
+                </td>
+                <td>
+                    <!-- TMPL_LOOP NAME="CHUNKS" -->
+                    <!-- TMPL_IF NAME="type_text" -->
+                    <!-- TMPL_IF NAME="highlighted" -->
+                    <span class="term"><!-- TMPL_VAR NAME="contents" --></span>
+                    <!-- TMPL_ELSE -->
+                    <!-- TMPL_VAR NAME="contents" -->
+                    <!-- /TMPL_IF -->
+                    <!-- TMPL_ELSIF NAME="type_input" -->
+                    <input type="<!-- TMPL_VAR NAME="input_type" DEFAULT="text" -->" name="pref_<!-- TMPL_VAR NAME="name" -->" id="pref_<!-- TMPL_VAR NAME="name" -->" class="preference preference-<!-- TMPL_VAR NAME="class" DEFAULT="short" -->" value="<!-- TMPL_VAR NAME="value" -->" autocomplete="off" />
+                    <!-- TMPL_ELSIF NAME="type_select" -->
+                    <select name="pref_<!-- TMPL_VAR NAME="name" -->" id="pref_<!-- TMPL_VAR NAME="name" -->" class="preference preference-<!-- TMPL_VAR NAME="class" DEFAULT="choice" -->">
+                        <!-- TMPL_LOOP NAME="CHOICES" -->
+                        <!-- TMPL_IF NAME="selected" -->
+                        <option value="<!-- TMPL_VAR NAME="value" -->" selected="selected">
+                        <!-- TMPL_ELSE -->
+                        <option value="<!-- TMPL_VAR NAME="value" -->">
+                        <!-- /TMPL_IF -->
+                            <!-- TMPL_VAR NAME="text" -->
+                        </option>
+                        <!-- /TMPL_LOOP -->
+                    </select>
+                    <!-- TMPL_ELSIF NAME="type_textarea" -->
+                                       <a class="expand-textarea" style="display: none" href="#">Click to Edit</a>
+                                       <textarea name="pref_<!-- TMPL_VAR NAME="name" -->" id="pref_<!-- TMPL_VAR NAME="name" -->" class="preference preference-<!-- TMPL_VAR NAME="class" DEFAULT="short" -->"><!-- TMPL_VAR NAME="value" --></textarea>
+                    <!-- TMPL_ELSIF NAME="type_languages" -->
+                    <table>
+                    <!-- TMPL_LOOP NAME="languages" -->
+                    <tr><td>
+                        <!-- TMPL_IF NAME="plural" -->
+                        <!-- TMPL_IF NAME="native_description" --><!-- TMPL_VAR NAME="native_description" --><!-- TMPL_ELSE --><!-- TMPL_VAR NAME="rfc4646_subtag" --><!-- /TMPL_IF -->
+                        <!-- TMPL_LOOP NAME="sublanguages_loop" --><table><tr><td>
+                        <label for="<!-- TMPL_VAR NAME="rfc4646_subtag" -->"><!-- TMPL_VAR NAME="native_description" --> <!-- TMPL_VAR NAME="script_description" --> <!-- TMPL_VAR NAME="region_description" --> <!-- TMPL_VAR NAME="variant_description" -->(<!-- TMPL_VAR NAME="rfc4646_subtag" -->)</label>
+                        <!-- TMPL_IF NAME="enabled" -->
+                        <input value="<!-- TMPL_VAR NAME="rfc4646_subtag" -->" name="pref_<!-- TMPL_VAR NAME="name" -->" id="<!-- TMPL_VAR NAME="rfc4646_subtag" -->" type="checkbox" checked="checked" />
+                        <!-- TMPL_ELSE -->
+                        <input value="<!-- TMPL_VAR NAME="rfc4646_subtag" -->" name="pref_<!-- TMPL_VAR NAME="name" -->" id="<!-- TMPL_VAR NAME="rfc4646_subtag" -->" type="checkbox" />
+                        <!-- /TMPL_IF -->
+                        <!-- /TMPL_LOOP -->
+                        <!-- TMPL_ELSE -->
+                        <label for="<!-- TMPL_VAR NAME="rfc4646_subtag" -->"><!-- TMPL_VAR NAME="native_description" -->(<!-- TMPL_VAR NAME="rfc4646_subtag" -->)</label>
+                        <!-- TMPL_IF NAME="group_enabled" -->
+                        <input value="<!-- TMPL_VAR NAME="rfc4646_subtag" -->" name="pref_<!-- TMPL_VAR NAME="name" -->" id="<!-- TMPL_VAR NAME="rfc4646_subtag" -->" type="checkbox" checked="checked" />
+                        <!-- TMPL_ELSE -->
+                        <input value="<!-- TMPL_VAR NAME="rfc4646_subtag" -->" name="pref_<!-- TMPL_VAR NAME="name" -->" id="<!-- TMPL_VAR NAME="rfc4646_subtag" -->" type="checkbox" />
+                        <!-- /TMPL_IF -->
+                        <!-- /TMPL_IF -->
+                    </td></tr>
+                    <!-- /TMPL_LOOP -->
+                    </table>
+                    <!-- /TMPL_IF -->
+                    <!-- /TMPL_LOOP -->
+                </td>
+            </tr>
+            <!-- TMPL_IF NAME="__last__" --></tbody><!-- /TMPL_IF -->
+            <!-- /TMPL_IF -->
+        <!-- /TMPL_LOOP -->
+        </table>
+        <button class="save-all submit" type="submit">Save All</button>
+    </form>
+    </div>
+    <!-- /TMPL_LOOP -->
+</div>
+</div>
+<div class="yui-b">
+<!-- TMPL_INCLUDE NAME="prefs-menu.inc" -->
+</div>
+</div>
+<!-- TMPL_INCLUDE NAME="intranet-bottom.inc" -->
index 683ed7a..8dea0ba 100644 (file)
@@ -72,7 +72,7 @@
     <!-- TMPL_IF NAME="CAN_user_parameters" -->
    <h3> <a href="/cgi-bin/koha/admin/admin-home.pl">Koha administration</a></h3>
                        <ul>
-            <li><a href="/cgi-bin/koha/admin/systempreferences.pl">System preferences</a></li>
+            <li><a href="/cgi-bin/koha/admin/preferences.pl">System preferences</a></li>
         </ul>
     <!-- /TMPL_IF -->
     <!-- TMPL_IF NAME="CAN_user_tools" -->
diff --git a/svc/config/systempreferences b/svc/config/systempreferences
new file mode 100755 (executable)
index 0000000..0a96c75
--- /dev/null
@@ -0,0 +1,112 @@
+#!/usr/bin/perl
+
+# Copyright 2009 Jesse Weaver
+#
+# 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 warnings;
+
+use C4::Context;
+use C4::Service;
+use C4::Log;
+
+=head1 NAME
+
+svc/config/systempreferences - Web service for setting system preferences
+
+=head1 SYNOPSIS
+
+  POST /svc/config/systempreferences/insecure
+  POST /svc/config/systempreferences/
+
+=head1 DESCRIPTION
+
+This service is used to set system preferences, either one at a time or in
+batches.
+
+=head1 METHODS
+
+=cut
+
+our ( $query, $response ) = C4::Service->init( parameters => 1 );
+
+=head2 set_preference
+
+=over 4
+
+POST /svc/config/systempreferences/$preference
+
+value=$value
+
+=back
+
+Used to set a single system preference.
+
+=cut
+
+sub set_preference {
+    my ( $preference ) = @_;
+
+    unless ( C4::Context->config('demo') ) {
+        my $value = join( ',', $query->param( 'value' ) );
+        C4::Context->set_preference( $preference, $value );
+        logaction( 'SYSTEMPREFERENCE', 'MODIFY', undef, $preference . " | " . $value );
+    }
+
+    C4::Service->return_success( $response );
+}
+
+=head2 set_preferences
+
+=over 4
+
+POST /svc/config/systempreferences/
+
+pref_$pref1=$value1&pref_$pref2=$value2
+
+=back
+
+Used to set several system preferences at once. Each preference you want to set
+should be sent prefixed with pref. If you wanted to turn off the
+GranularPermissions syspref, for instance, you would POST the following:
+
+pref_GranularPermissions=0
+
+=cut
+
+sub set_preferences {
+    unless ( C4::Context->config( 'demo' ) ) {
+        foreach my $param ( $query->param() ) {
+            my ( $pref ) = ( $param =~ /pref_(.*)/ );
+
+            next if ( !defined( $pref ) );
+
+            my $value = join( ',', $query->param( $param ) );
+
+            C4::Context->set_preference( $pref, $value );
+            logaction( 'SYSTEMPREFERENCE', 'MODIFY', undef, $pref . " | " . $value );
+        }
+    }
+
+    C4::Service->return_success( $response );
+}
+
+C4::Service->dispatch(
+    [ 'POST /([A-Za-z0-9_-]+)', [ 'value' ], \&set_preference ],
+    [ 'POST /', [], \&set_preferences ],
+);