# Copyright 2002-2008 Josh Clark and Global Moxie, LLC. This code cannot be 
# redistributed without permission from globalmoxie.com.  For more
# information, consult your Big Medium license.
#
# $Id: Prefs.pm 3043 2008-03-31 14:00:38Z josh $

package BigMed::Prefs;
use strict;
use utf8;
use Carp;
use base qw(BigMed::Data);
use BigMed::Elements;

my %registered;

###########################################################
# SET PREFS DATA SCHEMA
###########################################################

my @prefs_schema = (
    {   name  => 'site',
        type  => 'system_id',
        index => 1,
    },
    {   name  => 'section',
        type  => 'system_id',
        index => 1,
    },
    {   name    => 'name',
        type    => 'simple_text',
        index   => 1,
        default => '',
    },
    {   name             => 'pref_value',
        type             => 'raw_text',
        multiple         => 1,
        keep_line_breaks => 1,
        default          => '',
    },
);

BigMed::Prefs->set_schema(
    source   => 'prefs',
    label    => 'Preference',
    elements => \@prefs_schema,
);

###########################################################
# REGISTERING PREFERENCE DEFINITIONS
###########################################################

sub register_pref {
    my $class = shift;
    my $name = shift or croak "Preference name required to register pref";
    ref $_[0] eq "HASH"
      || croak "Preference attributes must be in a hash reference";

    my %submitted_pref = %{ $_[0] };
    my $rpref_definition = $registered{$name} || {};

    foreach my $attr
      qw(default edit_type options fallback sitewide labels edit_params priority)
    {
        next unless exists $submitted_pref{$attr};
        $rpref_definition->{$attr} = $submitted_pref{$attr};
    }
    $rpref_definition->{sitewide} = 1 if $rpref_definition->{sitewide};
    if ( $rpref_definition->{fallback} && $rpref_definition->{fallback} eq $name ) {
        croak "recursive fallback reference: $name names itself as fallback";
    }

    defined( $rpref_definition->{default} )
      || $rpref_definition->{fallback}
      || croak "Default value or fallback preference must be provided "
      . "to register preference '$name'";

    $registered{$name} = $rpref_definition;
    1;
}

sub list_registered_prefs {
    sort keys %registered;
}
###########################################################
# GET PREFERENCE ATTRIBUTES
###########################################################

sub pref_exists {
    $_[1] or croak "pref_exists requires preference name as argument";
    defined $registered{ $_[1] };
}

sub pref_fallback {
    my $class = shift;
    my $name  = shift
      or croak "Preference name required to load preference data.";
    $class->pref_exists($name)
      or croak "No preference '$name' is registered (have you "
      . "required the appropriate Format.pm subclass?)";
    $registered{$name}->{fallback};
}

sub pref_default {
    my $class = shift;
    my $name  = shift
      or croak "Preference name required to load preference data.";
    $class->pref_exists($name)
      or croak "No preference '$name' is registered (have you "
      . "required the appropriate Format.pm subclass?)";
    $registered{$name}->{default};
}

#for inheritable values...
sub pref_sitewide    { _find_value( $_[1], 'sitewide' ) }
sub pref_edit_type   { _find_value( $_[1], 'edit_type' ) }
sub pref_options     { _find_value( $_[1], 'options' ) }
sub pref_labels      { _find_value( $_[1], 'labels' ) }
sub pref_edit_params { _find_value( $_[1], 'edit_params' ) }
sub pref_priority    { _find_value( $_[1], 'priority' ) || 0 }

sub _find_value {
    my ( $name, $key ) = @_;
    $registered{$name}
      or croak "No preference '$name' is registered (have you "
      . "required the appropriate Format.pm subclass?)";
    if ( defined( $registered{$name}->{$key} ) ) {
        return $registered{$name}->{$key};
    }
    elsif ( $registered{$name}->{fallback} ) {
        return _find_value( $registered{$name}->{fallback}, $key );
    }
    else {
        return undef;
    }
}

1;
__END__


=head1 NAME

BigMed::Prefs - Big Medium widget preference record

=head1 DESCRIPTION

A BigMed::Prefs object represents the preference value for a single widget
preference for a section of a site (or for the default value for
a site).

=head1 USAGE

Within Big Medium, storage and access of data values for BigMed::Prefs
objects are managed almost exclusively via the BigMed::Site object (see that
module's C<get_pref_value>, C<store_pref> and C<clear_pref> methods). For
most uses like using preferences to generate widget HTML, it's generally
recommended to use those methods rather than calling the BigMed::Prefs methods
directly.

BigMed::Prefs is a subclass of BigMed::Data. In addition to the methods 
documented below, please see the BigMed::Data documentation for details about
topics including:

=over 4

=item Creating a new data object

=item Saving a data object

=item Finding and sorting saved data objects

=item Data access methods

=item Error handling

=back

=head1 METHODS

=head2 Preference Definition Methods

Preference objects each have a name field that identifies which preference
its value applies to.  This name associates the object with a specific
preference definition. Preference definitions reside in memory and determine
a preference's default value and options.

The following methods enable the registration and manipulation of preference
definitions.

=over 4

=item * BigMed::Prefs->register_pref($pref_name, \%attributes);

Establishes the definition for a preference within the system. The method
requires two arguments:

=over 4

=item * preference name

The name of the preference to register.

=item * attributes

Reference to a hash of attributes, where the keys are the attribute names
and the values are the attribute values. The attributes:

=over 4

=item * default => $value

The default value for the definition. If no custom value is specified for
a site, this value will be used. The default value may be a scalar, an array
reference, or a hash reference. (All preference definitions must include
a default value and/or a fallback).

=item * fallback => 'preference_name'

The name of a preference definition to use as a fallback. If this
preference definition does not include a default value, the default
value of the fallback preference will be used instead. The same applies
for the edit_type attribute. The fallback attribute is required if no
default attribute is specified.

This is useful when there are widgets that can share a common
format. For example, link widgets often share the same order
of links, and the preferences for the main C<LINKS> widget
are used as fallbacks for other link types like C<SPOTLIGHT>,
and C<MORELINKS>.


=item * sitewide => 1

A true value indicates that this preference should have only a single sitewide
value and cannot be customized to the section level.

=item * edit_type

The edit_type determines how the preference should be presented
in the admin interface for submitting preference values. Specify the
BigMed::Elements element type to use to edit and validate the entry.
If no edit_type is specified, the preference for editing will not be
displayed in the admin interface.

=item * options => ['value1','value2','value3']

Some edit_types can offer a set of pre-determined options to offer as
values. The options attribute is a reference to the list of options to use.
See the specific element type in BigMed::Elements for details.

You may alternatively provide a code reference to return an array of values.
The routine receives three arguments: the application object, the site object
and the section id (if applicable).

=item * labels => { 'value1'=>'label1' [, ...] }

For option-based edit_types, this hash refernce specifies the BigMed::Language
lexicon keys to use for each value in displaying the localized, escaped
label for each value.

You may alternatively provide a code reference to return a hash of values. The
routine receives three arguments: the application object, the site object
and the section id (if applicable).

=item * edit_params => { 'param1' => $value [,...] }

Additional prompt/parse parameters that the preference should supply
to the app's prompt and parse routines when the preference is presented
for editing.

=item * priority => $num

Number indicating sort order within preference editor. The higher the number,
the higher it appears in the preference list for editing.

=back

=back

The method returns a true value.

If C<register_pref> is called subsequently for the same preference name,
the new attributes will replace the attributes from the previous definition,
leaving any unspecified attributes intact from the previous registration.

=item * BigMed::Prefs->list_registered_prefs

Returns an array of all of the preference names, in alphabetical order.

=item * BigMed::Prefs->pref_exists($pref_name)

Returns a true value if the argument is a registered preference name.

=item * BigMed::Prefs->pref_default($pref_name)

Returns the default value for the specified preference name.

=item * BigMed::Prefs->pref_fallback($pref_name)

Returns the name of the fallback preference to use for this preference.

=item * BigMed::Prefs->pref_sitewide($pref_name)

Returns true if the sitewide flag is on for this preference.

=item * BigMed::Prefs->pref_edit_type($pref_name)

Returns the edit_type for this preference.

=item * BigMed::Prefs->pref_options($pref_name)

Returns option(s) for this reference in whatever format stored (e.g., if
the options attribute was stored as a hash reference in the preference
definition, that hash reference will be retured).

=item * BigMed::Prefs->pref_labels($pref_name)

Returns a reference to the preference's label hash, if any.

=item * BigMed::Prefs->pref_edit_params($pref_name)

Returns a reference to the preference's hash of additional prompt/parse
edit parameters, if any.

=item * BigMed::Prefs->pref_priority($pref_name)

Returns the preference's priority value.

=back

=head2 Data Access Methods

The BigMed::Site object holds the following pieces of data. They can be
accessed and set using the standard data access methods described in the
BigMed::Data documentation. See the L<"Searching and Sorting"> section below
for details on the data columns available to search and sort BigMed::Site
objects.

=over 4

=item * id

The unique numeric ID of the preference object

=item * site

The numeric ID of the site to which the preference applies

=item * section

The numeric ID of the section to which the preference applies (or 0 if this
value is the sitewide default).

=item * name

The name of the preference to which this preference value applies.

=item * pref_value

[Array reference]. Reference to the array of values for this preference.

=item * mod_time

The time when the preference object was last saved to disk, in UTC (Greenwich
Mean) time. The format: YYYY-MM-DD HH:MM:SS

=item * create_time

The time when the preference object was first saved to disk, in UTC (Greenwich
Mean) time. The format: YYYY-MM-DD HH:MM:SS

=back

=head3 Searching and Sorting

You can look up and sort records by any combination of the following fields.
See the C<fetch> and C<select> documentation in BigMed::Data for more info.

=over 4

=item * id

=item * mod_time

=item * site

=item * section

=item * name

=back

=head1 SEE ALSO

=over 4

=item * BigMed::Data


=item * BigMed::Elements

=back

=head1 AUTHOR & COPYRIGHTS

This module and all Big Medium modules are copyright Josh Clark
and Global Moxie. All rights reserved.

Use of this module and the Big Medium content
management system are governed by Global Moxie's software licenses
and may not be used outside of the terms and conditions outlined
there.

For more information, visit the Global Moxie website at
L<http://globalmoxie.com/>.

Big Medium and Global Moxie are service marks of Global Moxie
and Josh Clark. All rights reserved.

=cut

