# 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 3233 2008-08-21 12:47:26Z josh $

package BigMed::App::Web::Prefs;
use strict;
use warnings;
use utf8;
use Carp;
$Carp::Verbose = 1;
use base qw(BigMed::App::Web::CP);
use BigMed::Format;
use BigMed::Plugin;
use BigMed::Prefs;
use BigMed::Elements;
use BigMed::Section;

sub setup {
    my $app = shift;
    $app->start_mode('main-menu');
    $app->set_cp_selected_nav('Settings');
    $app->run_modes(
        'AUTOLOAD'     => sub { $_[0]->rm_main_menu() },
        'main-menu'    => 'rm_main_menu',
        'menu'         => 'rm_menu',
        'edit'         => 'rm_edit',
        'save'         => 'rm_save',
        'section-menu' => 'rm_section_menu',
        'ajax-delete-group' => 'rm_ajax_delete_group',
    );
    return;
}

my @Formats;

sub cgiapp_prerun {
    my $app = shift;
    $app->SUPER::cgiapp_prerun;
    $app->require_privilege_level(5);
    @Formats = BigMed::Plugin->load_formats();
    return;
}

###########################################################
# RUN MODES
###########################################################

sub rm_main_menu {
    my $app     = shift;
    my %options = @_;
    my $site_id = $app->current_site->id;
    my @groups  = (
        {   label    => $app->language('BM_CP_NAVLABEL_HTMLPrefs'),
            edit_url => $app->build_url(
                script => 'bm-prefs.cgi',
                rm     => 'menu',
                site   => $site_id,
                args   => 'HTML'
            ),
            description => $app->language('PREFS_DESC_HTMLPrefs'),
        },
        {   label    => $app->language('BM_CP_NAVLABEL_RSSPrefs'),
            edit_url => $app->build_url(
                script => 'bm-prefs.cgi',
                rm     => 'edit',
                site   => $site_id,
                args   => ['RSS', 'rss_feeds']
            ),
            description => $app->language('PREFS_DESC_RSSPrefs'),
        },
        {   label    => $app->language('BM_CP_NAVLABEL_SiteProperties'),
            edit_url => $app->build_url(
                script => 'bm-siteconfig.cgi',
                rm     => 'properties',
                site   => $site_id,
            ),
            description => $app->language('PREFS_DESC_SiteProperties'),
        },
        {   label    => $app->language('BM_CP_NAVLABEL_SiteImageFormats'),
            edit_url => $app->build_url(
                script => 'bm-siteconfig.cgi',
                rm     => 'images',
                site   => $site_id,
            ),
            description => $app->language('PREFS_DESC_SiteImageFormats'),
        },
    );
    if ( $app->current_user->level > 5 ) {    #admin
        push @groups,
          ( {   label    => $app->language('BM_CP_NAVLABEL_SiteDir'),
                edit_url => $app->build_url(
                    script => 'bm-config.cgi',
                    site   => $site_id,
                    rm     => 'edit-site',
                ),
                description => $app->language('PREFS_DESC_SiteDir'),
            },
            {   label    => $app->language('BM_CP_NAVLABEL_NewSite'),
                edit_url => $app->build_url(
                    script => 'bm-config.cgi',
                    site   => $site_id,
                    rm     => 'new-site',
                ),
                description => $app->language('PREFS_DESC_NewSite'),
            },
            {   label    => $app->language('BM_CP_NAVLABEL_CloneSite'),
                edit_url => $app->build_url(
                    script => 'bm-config.cgi',
                    site   => $site_id,
                    rm     => 'clone-site',
                ),
                description => $app->language('PREFS_DESC_CloneSite'),
            },
            {   label    => $app->language('BM_CP_NAVLABEL_ReviewSites'),
                edit_url => $app->build_url(
                    script => 'bm-review.cgi',
                    site   => $site_id,
                    rm     => 'review',
                ),
                description => $app->language('PREFS_DESC_ReviewSites'),
            },
            {   label    => $app->language('BM_CP_NAVLABEL_ImportV1'),
                edit_url => $app->build_url(
                    script => 'bm-import.cgi',
                    site   => $site_id,
                    rm     => 'v1loc',
                ),
                description => $app->language('PREFS_DESC_ImportV1'),
            },
            {   label    => $app->language('BM_CP_NAVLABEL_FileUploads'),
                edit_url => $app->build_url(
                    script => 'bm-config.cgi',
                    rm     => 'filetypes',
                    site   => $site_id,
                ),
                description => $app->language('PREFS_DESC_FileUploads'),
            },
            {   label    => $app->language('BM_CP_NAVLABEL_Server'),
                edit_url => $app->build_url(
                    script => 'bm-config.cgi',
                    rm     => 'server',
                    site   => $site_id,
                ),
                description => $app->language('PREFS_DESC_Server'),
            },
            {   label    => $app->language('BM_CP_NAVLABEL_Register'),
                edit_url => $app->build_url(
                    script => 'bm-config.cgi',
                    rm     => 'register',
                    site   => $site_id,
                ),
                description => $app->language('PREFS_DESC_Register'),
            },
            {   label    => $app->language('BM_CP_NAVLABEL_About'),
                edit_url => $app->build_url(
                    script => 'bm-config.cgi',
                    rm     => 'about-you',
                    site   => $site_id,
                ),
                description => $app->language('PREFS_DESC_About'),
            },
          );
    }

    #page title and message
    my $title_unlocal = $options{head} || 'BM_CP_NAVLABEL_Settings';
    my ( $title, $message ) = $app->title_and_message(
        field_msg => $options{field_msg},
        message   => $options{message},
        title     => $title_unlocal,
    );
    $app->set_cp_breadcrumbs( { bc_label => 'BM_CP_NAVLABEL_Settings' } );

    return $app->html_template_screen(
        'screen_prefs_menu.tmpl',
        bmcp_title => $title,
        message    => $message,
        groups     => \@groups,
    );

}

sub rm_menu {
    my $app      = shift;
    my %options  = @_;
    my $site     = $app->current_site;
    my ($format) = $app->path_args();
    my $fclass   = BigMed::Format->class_for_name($format);
    if ( !$fclass ) {
        $fclass = 'BigMed::Format::HTML';
        $format = $fclass->name;
    }

    my $edit_url = $app->build_url(
        script => 'bm-prefs.cgi',
        rm     => 'edit',
        site   => $site->id,
        args   => [$format],
    );
    my @groups =
      map {
        {   edit_url    => "$edit_url/$_",
            label       => $app->language("${format}GROUP_$_"),
            description => $app->language("${format}GROUP_DESC_$_"),
        }
      } $fclass->group_list;

    #page title and message
    my $title_unlocal = $options{head}
      || ['PREFS_Edit Format Prefs', $format];
    my ( $title, $message ) = $app->title_and_message(
        field_msg => $options{field_msg},
        message   => $options{message},
        title     => $title_unlocal,
    );
    $app->_set_breadcrumbs(
        { bc_label => ['PREFS_Edit Format Prefs', $format] } );

    return $app->html_template_screen(
        'screen_prefs_menu.tmpl',
        bmcp_title => $title,
        message    => $message,
        groups     => \@groups,
    );
}

sub rm_edit {
    my $app     = shift;
    my %options = @_;
    my $site    = $app->current_site;
    my ( $format, $group, $sec_id ) = $app->path_args();
    my $menu_screen = 'rm_menu';

    #confirm section exists if requesting section
    my $section;
    if ( $sec_id && $sec_id > 0 ) {
        $section = $app->_get_pref_section( $site, $sec_id )
          or return $app->rm_section_menu();
        $menu_screen = 'rm_section_menu';
    }

    my $fclass = BigMed::Format->class_for_name($format);
    if ( !$fclass ) {
        $fclass = 'BigMed::Format::HTML';
        $format = $fclass->name;
    }
    return $app->$menu_screen() if !$app->_group_ok( $fclass, $group );

    my @fieldsets;
    my @general =
      $app->_pref_fields( $site, $sec_id,
        $fclass->group_level_pref_names($group) );
    if (@general) {
        push @fieldsets,
          $app->prompt_fieldset_ref(
            id        => 'bmfs_GENERAL',
            fields    => \@general,
            title     => 'PREFS_General',
            query     => $options{query},
            field_msg => $options{field_msg},
          );
    }
    foreach my $widget ( $fclass->widget_list($group) ) {
        my @fields =
          $app->_pref_fields( $site, $sec_id,
            $fclass->widget($widget)->pref_names );
        if (@fields) {
            push @fieldsets,
              $app->prompt_fieldset_ref(
                id        => "bmfs_$widget",
                fields    => \@fields,
                title     => ['PREFS_Widget:', $widget],
                query     => $options{query},
                field_msg => $options{field_msg},
              );
        }
    }

    my $orig_title = "${format}GROUP_$group";
    my $message;
    if (@fieldsets) {
        $message = "${format}GROUP_DESC_$group";
        push @fieldsets,
          $app->prompt_fieldset_ref(
            id     => 'bmfs_save',
            fields => [
                $app->prompt_field_ref(
                    id        => 'pref_submit',
                    prompt_as => 'submit',
                    value     => $app->language('BM_SUBMIT_LABEL_Save'),
                )
            ],
          );

        if ($section) {    #add a note about customization
            $orig_title = [
                'PREFS_Custom section prefs', $section->name,
                $app->language($orig_title)
            ];
            my $main_url = $app->build_url(
                script => 'bm-prefs.cgi',
                rm     => 'edit',
                site   => $site->id,
                args   => [$format, $group],
            );
            my $text =
              $section->is_homepage
              ? 'PREFS_DESC_Customizing home preferences'
              : 'PREFS_DESC_Customizing section preferences';
            $message =
              [$text, $app->language($message), $main_url, $section->name,];
        }
        elsif ( _load_pref_names( $fclass, $group, 1 ) ) {

            #we're editing sitewide defaults but this group has prefs
            #that may be customized at section level; update message
            #to mention this.
            my $section_url = $app->build_url(
                script => 'bm-prefs.cgi',
                rm     => 'section-menu',
                site   => $site->id,
            );
            $message = [
                'PREFS_DESC_Editing default prefs',
                $app->language($message),
                $section_url
            ];
        }
    }
    else {
        $message = 'PREFS_No prefs to edit in this category';
    }

    #page title and message
    my $title;
    ( $title, $message ) = $app->title_and_message(
        field_msg => $options{field_msg},
        message   => $options{message} || $message,
        title     => $options{head} || $orig_title,
    );

    my @args = ( $format, $group );
    push @args, $sec_id if $section;
    my $form_url = $app->build_url(
        script => 'bm-prefs.cgi',
        site   => $site->id,
        rm     => 'save',
        args   => \@args,
    );
    my @breadcrumbs;
    if ($section) {
        @breadcrumbs = (
            {   bc_label => 'PREFS_Section Menu',
                bc_url   => $app->build_url(
                    script => 'bm-prefs.cgi',
                    site   => $site->id,
                    rm     => 'section-menu',
                    args   => [$section->id],
                ),
            }
        );

    }
    else {
        @breadcrumbs = (
            {   bc_label => ['PREFS_Edit Format Prefs', $format],
                bc_url   => $app->build_url(
                    script => 'bm-prefs.cgi',
                    site   => $site->id,
                    rm     => 'menu',
                    args   => [$format, $group],
                ),
            }
        );
    }
    push @breadcrumbs, { bc_label => $orig_title };

    $app->_set_breadcrumbs(@breadcrumbs);

    return $app->html_template_screen(
        'screen_cp_generic.tmpl',
        form_url   => $form_url,
        bmcp_title => $title,
        message    => $message,
        fieldsets  => \@fieldsets,
    );
}

sub rm_save {
    my $app     = shift;
    my %options = @_;
    my $site    = $app->current_site;
    my ( $format, $group, $sec_id ) = $app->path_args();
    my $menu_screen = 'rm_menu';

    #confirm section exists if saving section prefs
    my $section;
    if ( $sec_id && $sec_id > 0 ) {
        $section = $app->_get_pref_section( $site, $sec_id )
          or return $app->rm_section_menu();
        $menu_screen = 'rm_section_menu';
    }

    my $fclass = BigMed::Format->class_for_name($format);
    if ( !$fclass ) {
        $app->set_error(
            head => 'PREFS_Unknown Preference Format',
            text => ['PREFS_TEXT_Unknown Preference Format', $format]
        );
        return $app->$menu_screen();
    }
    return $app->$menu_screen() if !$app->_group_ok( $fclass, $group );

    #load pref values
    my @prefs = _load_pref_names( $fclass, $group, $section );
    my %fields = $app->_parse_pref_fields($site, $sec_id, @prefs);
    if ( $fields{_ERROR} ) {
        return $app->rm_edit(
            query     => $app->query,
            field_msg => $fields{_ERROR}
        );
    }

    #save and show confirmation
    foreach my $pref (@prefs) {
        if (   ( $pref =~ /_elements$/ms && !@{ $fields{$pref} } )
            || ( $pref =~ /_sort_order$/ms && !$fields{$pref} ) )
        {
            undef $fields{$pref};
        }
        $site->store_pref( $pref, $fields{$pref}, $section )
          or return $app->rm_edit(
            query     => $app->query,
            field_msg => $fields{_ERROR}
          );
    }

    #do callback
    $fclass->call_trigger("after_saveprefs_$group", $site, $section);

    my ( $rebuild_url, $for_what, $build_pages );
    if ($section) {
        $rebuild_url = $app->build_url(
            script => 'bm-build.cgi',
            rm     => 'build-section',
            site   => $site->id,
            args   => [$section->id],
        );
        $for_what =
          $app->language( ['PREFS_Saved for section', $section->name] );
        $build_pages = $app->language('PREFS_Build for section');
    }
    else {
        $rebuild_url = $app->build_url(
            script => 'bm-build.cgi',
            rm     => 'build-all',
            site   => $site->id,
        );
        $for_what    = $app->language('PREFS_Saved defaults');
        $build_pages = $app->language('PREFS_Build for site');
    }

    return $app->$menu_screen(
        message => [
            'PREFS_Preferences saved',
            $app->language("${format}GROUP_$group"),
            $for_what, $rebuild_url, $build_pages,
        ],
        saved_section => $sec_id,
    );
}

sub rm_section_menu {
    my $app     = shift;
    my %options = @_;

    my $site = $app->current_site or return $app->rm_login;
    defined( my $homepage = $site->homepage_obj ) or $app->error_stop;
    if ( !$homepage ) {    #no sections
        my $url = $app->build_url(
            script => 'bm-sections.cgi',
            rm     => 'outline',
            site   => $site->id,
        );
        $app->set_error(
            head => 'EDITOR_No sections',
            text => ['EDITOR_TEXT_No sections', $url],
        );
        $app->error_stop;
    }

    my $rpref_group   = $app->_pref_group_info($site);
    my $rcustom_prefs = $app->_find_custom_prefs( $site, $rpref_group );

    my $rtop_sections = [$homepage->id, $homepage->kids];
    my $list_html =
      $app->_section_menu_level( $site, $rpref_group, $rcustom_prefs,
        $rtop_sections, $options{saved_section} );
    $app->error_stop if !defined $list_html;

    #PAGE INFO AND DISPLAY ----------------------------
    my $html_prefs = $app->build_url(
        script => 'bm-prefs.cgi',
        rm     => 'menu',
        site   => $site->id,
        args   => ['HTML'],
    );
    my ( $title, $message ) = $app->title_and_message(
        message => $options{message}
          || ['PREFS_DESC_Section Menu', $html_prefs],
        title => 'PREFS_Section Menu',
    );
    $app->_set_breadcrumbs( { bc_label => 'PREFS_Section Menu' } );

    $app->js_add_script( $app->env('BMADMINURL') . '/js/bm-prefs.js' );
    return $app->html_template_screen(
        'screen_prefs_section_menu.tmpl',
        bmcp_title => $title,
        message    => $message,
        menu       => $list_html,
    );
}

sub rm_ajax_delete_group {
    my $app = shift;
    $app->require_post() or return $app->ajax_system_error;

    my ( $sec_id, $format_group ) = $app->path_args;
    my $site = $app->current_site;
    my $section = $app->_get_pref_section( $site, $sec_id )
      or return $app->ajax_system_error();

    $format_group ||= q{};
    my ( $format, $group ) = split( /[.]/ms, $format_group );
    my $fclass = BigMed::Format->class_for_name($format);
    if ( !$fclass ) {
        $app->set_error(
            head => 'PREFS_Unknown Preference Format',
            text => ['PREFS_TEXT_Unknown Preference Format', $format]
        );
        return $app->ajax_system_error();
    }
    return $app->ajax_system_error() if !$app->_group_ok( $fclass, $group );

    #looks good, gather the prefs and delete
    my @prefs = _load_pref_names( $fclass, $group, $section );
    if (@prefs) {    #otherwise, we would delete all of the section's prefs
        my $select =
          BigMed::Prefs->select(
            { site => $site->id, section => $sec_id, name => \@prefs } )
          or return $app->ajax_system_error();
        $select->trash_all or return $app->ajax_system_error;
    }

    my $edit_url = $app->build_url(
        script => 'bm-prefs.cgi',
        rm     => 'edit',
        site   => $site->id,
        args   => [$format, $group, $sec_id],
    );
    my $local_pref = $app->language( $fclass->name . "GROUP_$group" );
    return $app->ajax_html(qq{<a href="$edit_url">$local_pref</a>});
}

sub _pref_group_info {
    my ( $app, $site ) = @_;

    #organizes preference groups by format into a hash (only formats
    #with non-sitewide preferences are included in the hash).
    # {
    #   format_name1 => [
    #       { group => group_name,
    #         label => localized group name,
    #         edit_url => base url for editing groupt prefs,
    #         prefs => [ array of group's non-sitewide pref names ],
    #       },
    #       { ... },
    #       etc.
    #   ],
    #   format_name2 => [ ... ],
    #   etc,
    # }

    my %pref_groups;
    my $site_id = $site->id;
    foreach my $fclass ( BigMed::Plugin->load_formats() ) {
        my $format = $fclass->name or next;
        my $edit_url = $app->build_url(
            script => 'bm-prefs.cgi',
            rm     => 'edit',
            site   => $site_id,
            args   => [$format],
        );
        my @pref_groups;

        foreach my $group ( $fclass->group_list ) {
            my @widget_prefs =
              map { $fclass->widget($_)->pref_names }
              $fclass->widget_list($group);
            my @prefs =
              grep { !BigMed::Prefs->pref_sitewide($_) }
              ( $fclass->group_level_pref_names($group), @widget_prefs );
            next if !@prefs;    #only care about section-customizable prefs

            push @pref_groups,
              { edit_url => "$edit_url/$group",
                label    => $app->language("${format}GROUP_$group"),
                group    => $group,
                prefs    => \@prefs,
              };
        }

        $pref_groups{$format} = \@pref_groups if @pref_groups;
    }
    return \%pref_groups;
}

sub _find_custom_prefs {
    my ( $app, $site, $rpref_group ) = @_;

    #returns a hash reference keyed to section ids whose values are
    #keyed to "format_name.group_name"

    #map pref names to format.group
    my %pref_map;
    while ( my ( $format, $rgroups ) = each( %{$rpref_group} ) ) {
        my $fclass = BigMed::Format->class_for_name($format) or next;
        foreach my $rprofile ( @{$rgroups} ) {
            foreach my $pref ( @{ $rprofile->{prefs} } ) {
                $pref_map{$pref} = "$format." . $rprofile->{group};
            }
        }
    }

    #load all custom prefs
    my @id = ( $site->homepage_id, $site->all_descendants_ids );
    my $custom_prefs =
      BigMed::Prefs->select( { site => $site->id, section => \@id } )
      or return;

    my %custom_map;
    my $custom;
    while ( $custom = $custom_prefs->next ) {
        my $group = $pref_map{ $custom->name } or next;
        $custom_map{ $custom->section }->{$group} = 1;
    }
    return if !defined $custom;    #error

    return \%custom_map;
}

sub _section_menu_level {
    my ( $app, $site, $rpref_group, $rcustom_prefs, $rsection_ids, $active ) =
      @_;
    my @sec_ids = @{$rsection_ids};
    return q{} if !@sec_ids;

    my @sections;
    my @formats        = sort keys %{$rpref_group};
    my $site_id        = $site->id;
    my $properties_url = $app->build_url(
        script => 'bm-siteconfig.cgi',
        rm     => 'section',
        site   => $site_id,
    );

    foreach my $id (@sec_ids) {
        defined( my $sec = $site->section_obj_by_id($id) ) or return;
        next if !$sec;    #not found...?!

        my $sec_id = $sec->id;
        my @sec_prefs;
        my $rcustom = $rcustom_prefs->{$sec_id} || {};
        
        my $sec_detail = '&nbsp; ';
        if (!$sec->active) {
            $sec_detail = '[' . $app->language('PREFS_Inactive') . '] &nbsp;';
        }
        if ($sec->alias) {
            $sec_detail .= '[' . $app->language('PREFS_Aliased') . '] &nbsp;';
        }

        foreach my $format (@formats) {
            foreach my $group ( @{ $rpref_group->{$format} } ) {
                my $format_group = "$format." . $group->{group};
                push @sec_prefs,
                  { NAME       => $group->{label},
                    URL        => $group->{edit_url} . "/$sec_id",
                    HAS_CUSTOM => $rcustom->{$format_group},
                    SEC_PREF   => "$sec_id-$format_group",
                  };
            }
        }
        my @kids    = $sec->kids;
        my $subhtml = q{};
        if ( @kids && !$sec->is_homepage ) {
            $subhtml =
              $app->_section_menu_level( $site, $rpref_group, $rcustom_prefs,
                \@kids, $active );
        }
        my $is_active = ( $active && $active == $sec_id );
        push @sections,
          { SEC_NAME   => $sec->name,
            SEC_DETAIL => $sec_detail,
            URL        => "$properties_url/$sec_id",
            PREFS      => \@sec_prefs,
            SUBSECS    => $subhtml,
            HAS_CUSTOM => scalar keys %{$rcustom},
            SEC_ID     => $sec_id,
            DISPLAY    => $is_active ? 'block' : 'none',
            TRIGGERED  => $is_active ? ' bmTriggered' : q{},
          };

        $app->js_make_toggle( "bmcpPrefsTog-$sec_id", "bmcpPrefsSec-$sec_id",
            'slide' );
    }

    return $app->html_template_screen( 'wi_prefs_section_menu_level.tmpl',
        sections => \@sections, );
}

sub _group_ok {
    my ( $app, $fclass, $group ) = @_;
    my %group_ok = map { $_ => 1 } $fclass->group_list;
    return exists $group_ok{$group}
      ? 1
      : $app->set_error(
        head => 'PREFS_Unknown Preference Group',
        text => ['PREFS_TEXT_Unknown Preference Group', $group]
      );
}

sub _load_pref_names {
    my ( $fclass, $group, $section ) = @_;

    #returns the names of all preferences in the format class's group.
    #if section is specified, then all sitewide preferences are removed.

    my @prefs = $fclass->group_level_pref_names($group);
    foreach my $widget ( $fclass->widget_list($group) ) {
        push @prefs, $fclass->widget($widget)->pref_names;
    }
    return $section
      ? ( grep { !BigMed::Prefs->pref_sitewide($_) } @prefs )
      : @prefs;
}

sub _pref_fields {
    my ( $app, $site, $sec_id, @names ) = @_;
    if ($sec_id) {
        @names = grep { !BigMed::Prefs->pref_sitewide($_) } @names;
    }
    return if !@names;

    my @fields;
    my $option_label = $app->language('PREFS_Customize for this widget');
    foreach my $pref (@names) {
        my $type = BigMed::Prefs->pref_edit_type($pref) or next;
        my $value;
        if ( BigMed::Elements->property( $type, 'array' ) ) {
            $value =
              [map { $app->escape($_) }
                  ( $site->get_pref_value( $pref, $sec_id ) )];
        }
        else {
            $value = $site->get_pref_value( $pref, $sec_id );
            $value = $app->escape($value)
              if $type ne 'simple_text' && index( $type, 'rich_text' ) < 0;

            #simple text stored in escaped form and rich_text manages itself
        }

        #yikes, very hack-y...
        if ( $pref eq 'html_links_window_intdomains' && !@{$value} ) {
            $value = [$site->homepage_url, q{/}];
        }

        my $roptions = $app->_pref_field_options($pref,$site,$sec_id);
        my $rlabels = $app->_pref_field_labels($pref,$site,$sec_id);

        my $rparam = BigMed::Prefs->pref_edit_params($pref) || {};

        my $can_fallback = BigMed::Prefs->pref_fallback($pref)
          && !defined BigMed::Prefs->pref_default($pref);
        my $does_fallback = $site->pref_inherits_from_fallback($pref, $sec_id);

        push @fields,
          $app->prompt_field_ref(
            id        => $pref,
            prompt_as => $type,
            value     => $value,
            options   => $roptions,
            labels    => $rlabels,
            label     => "PREFS_$pref",
            %{$rparam},
            optional_label => ( $can_fallback ? $option_label : q{} ),
            option_true => (!$does_fallback),
          );
    }
    return @fields;
}

sub _parse_pref_fields {
    my ( $app, $site, $sec_id, @names ) = @_;
    my @fields;
    foreach my $pref (@names) {
        my $type         = BigMed::Prefs->pref_edit_type($pref) or next;
        my $roptions     = BigMed::Prefs->pref_options($pref);
        $roptions = [ $roptions->($app, $site, $sec_id) ]
          if ref $roptions eq 'CODE';
        my $rparam       = BigMed::Prefs->pref_edit_params($pref) || {};
        my $can_fallback = BigMed::Prefs->pref_fallback($pref)
          && !defined BigMed::Prefs->pref_default($pref);
        push @fields,
          { id       => $pref,
            parse_as => $type,
            options  => $roptions,
            %{$rparam},
            optional_label => $can_fallback,
          };
    }
    return $app->parse_submission(@fields);
}

sub _pref_field_options {
    my ($app, $pref, $site, $sec_id) = @_;
    my $roptions = BigMed::Prefs->pref_options($pref);
    if ($roptions && ref $roptions eq 'ARRAY') {
        my @options = map { $app->escape($_) } @{$roptions};
        $roptions = \@options;
    }
    elsif ($roptions && ref $roptions eq 'CODE') {
        $roptions = [ $roptions->($app, $site, $sec_id) ];
    }
    return $roptions;
}
        
sub _pref_field_labels {
    my ($app, $pref, $site, $sec_id) = @_;
    my $rlabels = BigMed::Prefs->pref_labels($pref);
    if ($rlabels && ref $rlabels eq 'HASH') {
        my %labels;
        while ( my ($k,$v) = each (%{$rlabels} ) ){
            $labels{ $app->escape($k) } = $app->language( $v );
        }
        $rlabels = \%labels;
    }
    elsif ($rlabels && ref $rlabels eq 'CODE') {
        $rlabels = { $rlabels->($app,$site,$sec_id) };
    }
    return $rlabels;
}

sub _set_breadcrumbs {
    my ( $app, @crumbs ) = @_;
    my @bc = (
        {   bc_label => 'BM_CP_NAVLABEL_Settings',
            bc_url   => $app->build_url(
                script => 'bm-prefs.cgi',
                rm     => 'main-menu',
                site   => $app->current_site->id,
            ),
        },
        @crumbs,
    );
    return $app->set_cp_breadcrumbs(@bc);
}

sub _get_pref_section {
    my ( $app, $site, $sec_id ) = @_;

    my $section;
    if ( $sec_id && $sec_id > 0 ) {
        $section =
          BigMed::Section->fetch( { site => $site->id, id => $sec_id } );
        return if !defined $section;    #error
    }
    if ( !$section ) {                  #no such section, bad ID
        $app->set_error(
            head => 'SECTIONS_HEAD_Section could not be found',
            text =>
              ['SECTIONS_Section could not be found', $sec_id, $site->name],
        );
    }
    return $section;
}

1;

__END__

