package TWiki::Prefs;
# Module of TWiki Collaboration Platform, http://TWiki.org/
#
# Copyright (C) 2000-2004 Peter Thoeny, peter@thoeny.com
#
# For licensing info read license.txt file in the TWiki root.
# This program 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.
#
# This program 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, published at 
# http://www.gnu.org/copyleft/gpl.html
#
# Notes:
# - Latest version at http://twiki.org/
# - Installation instructions in $dataDir/TWiki/TWikiDocumentation.txt
# - Customize variables in wikicfg.pm when installing TWiki.
# - Optionally change wikicfg.pm for custom extensions of rendering rules.
# - Files wiki[a-z]+.pm are included by wiki.pm
# - Upgrading TWiki is easy as long as you only customize wikicfg.pm.
# - Check web server error logs for errors, i.e. % tail /var/log/httpd/error_log
use strict;

=begin twiki

---+ TWiki::Prefs Module

This module reads TWiki preferences of site-level, web-level and user-level
topics and implements routines to access those preferences.

=cut

sub new { 
	my $dummy = shift;
	my $config = shift;
	unshift @_,$dummy ;

   my ( $proto ) =@_ ;
   my $class = ref($proto) || $proto ;
   my $self;

	$self->{config}=$config;
   bless ( $self );

	$self->init_config();

   return $self ;
}


sub init_config { 

   my $self = shift;
	my $config = $self->{config} ;


$config->{TWiki}->{Prefs}->{finalPrefsName} = "FINALPREFERENCES";
$config->{TWiki}->{Prefs}->{formPrefPrefix} = "FORM_";

}# END of init_config


use TWiki::Prefs::Parser ; 

use TWiki::Prefs::TopicPrefs ; 

use TWiki::Prefs::PrefsCache ; 




=pod

---++ TWiki::Prefs package

This is the external interface to the Prefs module, and is how the rest of the
TWiki code accesses preferences.

---+++ sub $config->{TWiki}->{Prefs}->clearCache () 

This non-member function clears cached topic preferences, forcing all settings
to be reread.

---+++ sub $config->{TWiki}->{Prefs}->invalidateCache ( $web, $topic )

This non-member function invalidates the cache on a particular topic.

=cut

sub clearCache { 
   my $self = shift;
	my $config = $self->{config} ;

 
   undef %{$config->{TWiki}->{Prefs}->{topicCache}}; 
}
sub invalidateCache { 
   my $self = shift;
	my $config = $self->{config} ;

 
   delete $config->{TWiki}->{Prefs}->{topicCache}{$_[0]}{$_[1]}; 
}

=pod

---+++ sub $config->{TWiki}->{Prefs}->initializePrefs ( $webName )

Resets all preference globals (for mod_perl compatibility), and reads
preferences from TWiki::TWikiPreferences, Main::TWikiPreferences, and
$webName::WebPreferences.

=cut

sub initializePrefs
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my( $theWebName ) = @_;
    
    $config->{TWiki}->{Prefs}->clearCache (); # for mod_perl compatibility

    $config->{TWiki}->{Prefs}->{requestWeb} = $theWebName;
    $config->{TWiki}->{Prefs}->{globalPrefs} = TWiki::Prefs::PrefsCache->new($config, "global");
    $config->{TWiki}->{Prefs}->{webPrefs}{$config->{TWiki}->{Prefs}->{requestWeb}} = TWiki::Prefs::PrefsCache->new($config, "web", $config->{TWiki}->{Prefs}->{globalPrefs}, $config->{TWiki}->{Prefs}->{requestWeb});
    $config->{TWiki}->{Prefs}->{requestPrefs} = TWiki::Prefs::PrefsCache->new($config, "copy", $config->{TWiki}->{Prefs}->{webPrefs}{$config->{TWiki}->{Prefs}->{requestWeb}});

    return;
}

# =========================

=pod

---+++ sub $config->{TWiki}->{Prefs}->initializeUserPrefs ( $userPrefsTopic )

Called after user is known (potentially by Plugin), this function reads
preferences from the user's personal topic.  The parameter is the topic to read
user-level preferences from (Generally "Main.CurrentUserName").

=cut

sub initializeUserPrefs
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my( $theWikiUserName ) = @_;

    $theWikiUserName = "Main.TWikiGuest" unless $theWikiUserName;

    if( $theWikiUserName =~ /^(.*)\.(.*)$/ ) {
	$config->{TWiki}->{Prefs}->{requestPrefs} = TWiki::Prefs::PrefsCache->new($config, "request", $config->{TWiki}->{Prefs}->{webPrefs}{$config->{TWiki}->{Prefs}->{requestWeb}}, $config->{TWiki}->{topicName}, $2);
    }

    return;
}


# =========================

=pod

---+++ sub $config->{TWiki}->{Prefs}->getPrefsFromTopic (  $web, $topic, $keyPrefix  )

Reads preferences from the topic at =$theWeb.$theTopic=, prefixes them with
=$theKeyPrefix= if one is provided, and adds them to the preference cache.

=cut

sub getPrefsFromTopic
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my ($web, $topic, $keyPrefix) = @_;
    $config->{TWiki}->{Prefs}->{requestPrefs}->loadPrefsFromTopic($web, $topic, $keyPrefix, 1);
}

# =========================

=pod

---+++ sub $config->{TWiki}->{Prefs}->updateSetFromForm (  $meta, $text  )
Return value: $newText

If there are any settings "Set SETTING = value" in =$text= for a setting
that is set in form metadata in =$meta=, these are changed so that the
value in the =$text= setting is the same as the one set in the =$meta= form.
=$text= is not modified; rather, a new copy with these changes is returned.

=cut

sub updateSetFromForm
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my( $meta, $text ) = @_;
    my( $key, $value );
    my $ret = "";
    
    my %form = $meta->findOne( "FORM" );
    if( %form ) {
        my @fields = $meta->find( "FIELD" );
        foreach my $line ( split( /\n/, $text ) ) {
            foreach my $field ( @fields ) {
                $key = $field->{"name"};
                $value = $field->{"value"};
                my $attributes = $field->{"attributes"};
                if( $attributes && $attributes =~ /[S]/o ) {
                    $value =~ s/\n/\\\n/o;
                    ##$config->{TWiki}->writeDebug ( "updateSetFromForm: \"$key\"=\"$value\"" );
                    # Worry about verbatim?  Multi-lines
                    if ( $line =~ s/^(\t+\*\sSet\s$key\s\=\s*).*$/$1$value/g ) {
                        last;
                    }
                }
            }
            $ret .= "$line\n";
        }
    } else {
        $ret = $text;
    }
    
    return $ret;
}

# =========================

=pod

---+++ sub $config->{TWiki}->{Prefs}->handlePreferencesTags ( $text )

Replaces %PREF% and %<nop>VAR{"pref"}% syntax in $text, modifying that parameter in-place.

=cut

sub handlePreferencesTags
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my $textRef = \$_[0];

    $config->{TWiki}->{Prefs}->{requestPrefs}->replacePreferencesTags($$textRef);
    
    if( $$textRef =~ /\%VAR{(.*?)}\%/ ) {
        # handle web specific variables
        $$textRef =~ s/\%VAR{(.*?)}\%/$config->{TWiki}->{Prefs}->prvGetWebVariable ($1)/ge;
    }
}

=pod

---+++ sub $config->{TWiki}->{Prefs}->prvGetWebVariable ( $attributeString )

Returns the value for a %<nop>VAR{"foo" web="bar"}% syntax, given the stuff inside the {}'s.

=cut

sub prvGetWebVariable
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my ( $attributeString ) = @_;
    
    my $key = $config->{TWiki}->extractNameValuePair ( $attributeString );
    my $attrWeb = $config->{TWiki}->extractNameValuePair ( $attributeString, "web" );
    if( $attrWeb =~ /%[A-Z]+%/ ) { # handle %MAINWEB%-type cases 
        $config->{TWiki}->handleInternalTags ( $attrWeb, $config->{TWiki}->{Prefs}->{requestWeb}, "dummy" );
    }
    
    return $config->{TWiki}->{Prefs}->getPreferencesValue ( $key, $attrWeb);
}

=pod

---+++ sub $config->{TWiki}->{Prefs}->formatAsFlag ( $prefValue )

Returns 1 if the =$prefValue= is "on", and 0 otherwise.  "On" means set to
something with a true Perl-truth-value, with the special cases that "off" and
"no" are forced to false.  (Both of the latter are case-insensitive.)  Note
also that leading and trailing whitespace on =$prefValue= will be stripped
prior to this conversion.

=cut

sub formatAsFlag
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my ( $value  ) = @_;

    $value =~ s/^\s*(.*?)\s*$/$1/gi;
    $value =~ s/off//gi;
    $value =~ s/no//gi;
    if( $value ) {
        return 1;
    } else {
        return 0;
    }
}

=pod

---+++ sub $config->{TWiki}->{Prefs}->formatAsNumber ( $prefValue )

Converts the string =$prefValue= to a number.  First any whitespace and commas
are removed.  <em>L10N note: assumes thousands separator is comma and decimal
point is period.</em>  Then, if the first character is a zero, the value is
passed to oct(), which will interpret hex (0x prefix), octal (leading zero
only), or binary (0b prefix) numbers.  If the first character is a digit
greater than zero, the value is assumed to be a decimal number and returned.
If the =$prefValue= is empty or not a number, zero is returned.  Finally, if
=$prefValue= is undefined, an undefined value is returned.  <strong>Undefined
preferences are automatically converted to empty strings, and so this function
will always return zero for these, rather than 'undef'.</strong>

=cut

sub formatAsNumber
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my ( $strValue ) = @_;
    return undef unless defined( $strValue ); 
    
    $strValue =~ s/[,\s]+//g;    
    
    if ($strValue =~ /^0/) {
	return oct($strValue); # hex/octal/binary
    } elsif ($strValue =~ /^(\d|\.\d)/) {
	return $strValue;      # decimal
    } else {
	return 0;              # empty/non-numeric
    }
}

=pod

---+++ sub $config->{TWiki}->{Prefs}->getPreferencesValue (  $theKey, $theWeb  )

Returns the value of the preference =$theKey=.  If =$theWeb= is also specified,
looks up the value with respect to that web instead of the current one; also,
in this case user/topic preferences are not considered.

In any case, if a plugin supports sessions and provides a value for =$theKey=,
this value overrides all preference settings in any web.

=cut

sub getPreferencesValue
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my ( $theKey, $theWeb ) = @_;

    my $sessionValue = $config->{TWiki}->getSessionValue ( $theKey );
    if( defined( $sessionValue ) ) {
        return $sessionValue;
    }

    if ($theWeb) {
	if (!exists $config->{TWiki}->{Prefs}->{webPrefs}{$theWeb}) {
	    $config->{TWiki}->{Prefs}->{webPrefs}{$theWeb} = TWiki::Prefs::PrefsCache->new($config, "web", $config->{TWiki}->{Prefs}->{globalPrefs}, $theWeb);
	}
	return $config->{TWiki}->{Prefs}->{webPrefs}{$theWeb}->getPreferenceValue($theKey);
    } else {
	return $config->{TWiki}->{Prefs}->{requestPrefs}->getPreferenceValue($theKey) if defined $config->{TWiki}->{Prefs}->{requestPrefs};
	if (exists $config->{TWiki}->{Prefs}->{webPrefs}{$config->{TWiki}->{Prefs}->{requestWeb}}) {
	  return $config->{TWiki}->{Prefs}->{webPrefs}{$config->{TWiki}->{Prefs}->{requestWeb}}->getPreferenceValue($theKey); # user/topic prefs not yet init'd
	}
    }    
}

# =========================

=pod

---+++ sub $config->{TWiki}->{Prefs}->getPreferencesFlag (  $theKey, $theWeb  )

Returns the preference =$theKey= from =$theWeb= as a flag.  See
=getPreferencesValue= for the semantics of the parameters, and
=[[#sub_formatAsFlag_prefValue][formatAsFlag]]= for the method of interpreting
a value as a flag.

=cut

sub getPreferencesFlag
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my ( $theKey, $theWeb ) = @_;

    my $value = $config->{TWiki}->{Prefs}->getPreferencesValue ( $theKey, $theWeb );
    return $config->{TWiki}->{Prefs}->formatAsFlag ($value);
}

=pod

---+++ sub $config->{TWiki}->{Prefs}->getPreferencesNumber (  $theKey, $theWeb  )

Returns the preference =$theKey= from =$theWeb= as a flag.  See
=getPreferencesValue= for the semantics of the parameters, and
=[[#sub_formatAsNumber_prefValue][formatAsNumber]]= for the method of
interpreting a value as a number.

=cut

sub getPreferencesNumber
{ 
   my $self = shift;
	my $config = $self->{config} ;


    my ( $theKey, $theWeb ) = @_;

    my $value = $config->{TWiki}->{Prefs}->getPreferencesValue ( $theKey, $theWeb );
    return $config->{TWiki}->{Prefs}->formatAsNumber ($value);
}

# =========================

1;

=end twiki

=cut

# EOF


1;
