# 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: Sections.pm 3043 2008-03-31 14:00:38Z josh $

package BigMed::App::Web::Sections;
use Carp;
use strict;
use warnings;
use utf8;
$Carp::Verbose = 1;
use base qw(BigMed::App::Web::CP);
use BigMed::Site;
use BigMed::Section;
use BigMed::DiskUtil qw(bm_file_path bm_delete_dir);

sub setup {
    my $app = shift;
    $app->start_mode('outline');
    $app->set_cp_selected_nav('Layout');
    $app->run_modes(
        'AUTOLOAD' => sub { $_[0]->rm_outline() },
        'first-site-sections' => 'rm_first_site_sections',
        'new-site-sections'   => 'rm_new_site_sections',
        'outline'             => 'rm_outline',
        'save-outline'        => 'rm_save_outline',
        'save-first-outline'  => 'rm_save_first_outline',
        'save-new-outline'    => 'rm_save_new_outline',
        'ajax-add-section'    => 'rm_ajax_add_section',
        'ajax-edit-section'   => 'rm_ajax_edit_section',
        'ajax-edit-home'      => 'rm_ajax_edit_home',
        'ajax-delete'         => 'rm_ajax_delete',
    );
    return;
}

sub cgiapp_prerun {
    my $app = shift;
    $app->SUPER::cgiapp_prerun;
    $app->require_privilege_level(5);
    return;
}

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

sub rm_first_site_sections {
    my $app     = shift;
    my %options = @_;
    ( $options{site} ||= $app->current_site ) or return $app->rm_login_site;
    $options{head}        ||= 'SECTIONS_Create Sections';
    $options{submit_text} ||= 'START_Submit_Button_Text';
    $options{template}    ||= 'screen_sections_first-site-sections.tmpl';
    $options{form_url}    ||= $app->build_url(
        script => 'bm-sections.cgi',
        rm     => 'save-first-outline',
        site   => $options{site}->id,
    );
    return $app->rm_outline(%options);
}

sub rm_new_site_sections {
    my $app     = shift;
    my %options = @_;
    ( $options{site} ||= $app->current_site ) or return $app->rm_login_site;
    $options{head}     ||= 'SECTIONS_Create Sections';
    $options{form_url} ||= $app->build_url(
        script => 'bm-sections.cgi',
        rm     => 'save-new-outline',
        site   => $options{site}->id,
    );
    return $app->rm_outline(%options);
}

sub rm_outline {
    my $app     = shift;
    my %options = @_;
    my $site    = $options{site} || $app->current_site
      or return $app->rm_login_site;

    #list html
    defined( my $items = $app->_build_subsection_items($site) )
      or $app->error_stop;

    #form info
    my $site_id = $app->prompt_field_ref(
        id          => 'BMsite',
        prompt_as   => 'hidden',
        value       => $site->id,
        no_validate => 1,
    );
    $site_id = $app->prompt( @{$site_id} );
    my $section_order = $app->prompt_field_ref(
        id          => 'sec_order',
        prompt_as   => 'hidden',
        value       => q{},
        no_validate => 1,
    );
    $section_order = $app->prompt( @{$section_order} );
    my $submit_text = $options{submit_text} || 'BM_SUBMIT_LABEL_Save';
    my $submit = $app->prompt_field_ref(
        prompt_as   => 'submit',
        value       => $app->language($submit_text),
        no_validate => 1,
    );
    $submit = $app->prompt( @{$submit} );

    #page info
    my $title_unlocal = $options{head}
      || 'SECTIONS_Section Structure';
    my ( $title, $message ) = $app->title_and_message(
        field_msg => $options{field_msg},
        message   => $options{message} || 'SECTIONS_Section Instructions',
        title     => $title_unlocal,
    );
    $app->_set_breadcrumbs( $title_unlocal, $options{breadcrumbs} );
    my $form_url = $options{form_url}
      || $app->build_url(
        script => 'bm-sections.cgi',
        rm     => 'save-outline',
        site   => $site->id
      );
    my $template = $options{template} || 'screen_sections_outline.tmpl';

    #javascript init
    $app->js_add_script( $app->env('BMADMINURL') . '/js/bm-sections.js' );
    $app->js_make_lists_sortable('AllSections');
    $app->js_make_toggle( 'SectionHelpLink', 'SectionHelpBox', 'slide' );


    my $homepage = $site->homepage_obj;
    return $app->html_template_screen(
        $template,
        form_url            => $form_url,
        bmcp_title          => $title,
        step5               => 1,                            #for setup wizard
        message             => $message,
        field_site_id       => $site_id->{FIELD_HTML},
        field_submit        => $submit->{FIELD_HTML},
        field_section_order => $section_order->{FIELD_HTML},
        home_id             => $homepage->id,
        homepage_name       => $homepage->name,
        section_items       => $items,
        section_help => $app->language('SECTIONS_HELP_What is a section?'),
    );
}

sub rm_save_new_outline {
    my $app     = shift;
    my %options = @_;
    $options{prev_screen} = \&rm_new_site_sections;
    $options{next_rm}     = 'new-first-theme';
    return $app->rm_save_first_outline(%options);
}

sub rm_save_first_outline {
    my $app     = shift;
    my %options = @_;
    ( $options{site} ||= $app->current_site ) or return $app->rm_login_site;
    $options{prev_screen} ||= \&rm_first_site_sections;
    $options{next_screen} = sub {
        my $app = shift;
        $app->header_type('redirect');
        $app->header_props(
            -url => $app->build_url(
                script => 'bm-themes.cgi',
                rm     => $options{next_rm} || 'first-theme',
                site   => $options{site}->id
            )
        );
        'Redirecting...';
    };
    return $app->rm_save_outline(%options);
}

sub rm_save_outline {
    my $app     = shift;
    my %options = @_;
    my $site    = $options{site} || $app->current_site
      or return $app->rm_login_site;
    my $prev_screen_ref = $options{prev_screen_ref} || \&rm_outline;

    #the interface shouldn't allow any changes among sections, so this
    #should just be reordering of existing subsections. If a section doesn't
    #have a list in the hash, that should mean that it has no subsections;
    #we can ignore those and simply look at the sections we have received.

    #for safety, bail out if we find a section that does not belong to the
    #site, and ignore any changes that have a different number of subsections
    #than the saved site.

    #organize the serialized results into a hash
    my $serial_secs = $app->utf8_param('sec_order') || q{};
    my %kidlist;
    while ( $serial_secs =~ m{section(\d+)\[\]=(\d+)}msg ) {
        push @{ $kidlist{$1} }, $2;
    }

    #build lookup hash of valid sections
    my %section_id;
    my @all_id = $site->all_descendants_ids();
    @section_id{@all_id} = (1) x @all_id;

    foreach my $id ( keys %kidlist ) {
        defined( my $sec = $site->section_obj_by_id($id) )
          or return $app->_return_to_rm( $prev_screen_ref, %options );
        if ( !$sec ) {
            $app->set_error(
                head => 'SECTIONS_HEAD_Section could not be found',
                text =>
                  ['SECTIONS_Section could not be found', $id, $site->name]
            );
            return $app->_return_to_rm( $prev_screen_ref, %options );
        }
        my @orig_kids = $sec->kids;
        my @new_kids  = @{ $kidlist{$id} };
        next if scalar @orig_kids != scalar @new_kids;    #discard change
        my @check = grep { !$section_id{$_} } @new_kids;
        if (@check) {
            $app->set_error(
                head => 'SECTIONS_HEAD_Section could not be found',
                text => [
                    'SECTIONS_Found unknown section in list', $check[0],
                    $sec->name
                ]
            );
            return $app->_return_to_rm( $prev_screen_ref, %options );
        }

        #check for changes...
        my $changed;
        for my $i ( 0 .. ( @orig_kids - 1 ) ) {
            last if $changed = $orig_kids[$i] != $new_kids[$i];
        }
        $sec->set_kids( \@new_kids ) if $changed;
    }

    #didn't do save on first pass, since an error could throw things
    #out of whack with the section indices. instead, do the save now,
    #after all changes have been processed.
    foreach my $id ( keys %kidlist ) {
        my $sec = $site->section_obj_by_id($id);

        #no need to do the error check since they were cached above
        $sec->save
          or return $app->_return_to_rm( $prev_screen_ref, %options );
    }

    my $rnext_screen = $options{next_screen} || \&_forward_to_builder;
    return $rnext_screen->($app);
}

sub _return_to_rm {
    my ( $app, $prev_screen_ref, %param ) = @_;
    return $prev_screen_ref->(
        $app,
        head => 'BM_Please_Review_Your_Entry',
        %param,
    );
}

sub _forward_to_builder {
    my $app = shift;
    $app->set_session_message('SECTIONS_Changes saved, rebuild pages');
    my $url = $app->build_url(
        script => 'bm-build.cgi',
        rm     => 'menu',
        site   => $app->current_site->id,
    );
    $app->header_type('redirect');
    $app->header_props( -url => $url );
    return "Redirecting to $url";
}

sub rm_ajax_add_section {
    my $app  = shift;
    my $site = $app->current_site;
    if ( !$site ) {
        $app->set_error(
            head => 'BM_HEAD_Unknown site',
            text => 'BM_TEXT_Unknown site'
        );
        return $app->ajax_system_error;
    }
    my %field = $app->parse_submission(
        {   id       => 'parent',
            parse_as => 'id',
            required => 1,
        },
        {   data_class => 'BigMed::Section',
            column     => 'name',
            required   => 1,
        },
        {   data_class => 'BigMed::Section',
            column     => 'slug',
            required   => 1,
        },
    );
    return $app->ajax_parse_error( $field{_ERROR} ) if $field{_ERROR};

    #load the parent section object
    defined(
        my $parent = BigMed::Section->fetch(
            { site => $site->id, id => $field{parent} }
        )
      )
      or return $app->ajax_system_error;
    if ( !$parent ) {
        $app->set_error(
            text => [
                'SECTIONS_Parent section could not be found',
                $field{parent}, $site->name
            ]
        );

        return $app->ajax_system_error;
    }

    #create the section and set its name, slug, parents, etc
    my $section = BigMed::Section->new();
    $section->set_name( $field{name} );
    $section->set_slug( $field{slug} );
    if ( $parent->is_homepage ) { #main section
        $section->set_parents( [$parent->id] );
    }
    else { #subsection
        $section->set_parents( [$parent->parents, $field{parent}] );
        $section->set_flags({js_disable_feed=>1, rss_disable_feed=>1});
    }
    $section->set_site( $site->id );
    $section->set_active(1);

    #saving invokes the callbacks that handle uniqueness checks, associated
    #page creation, updating parent, etc
    $section->save or return $app->ajax_system_error;

    #return list html
    my $delete_url = $app->build_url(
        script => 'bm-sections.cgi',
        rm     => 'ajax-delete',
        site   => $site->id,
        args   => [$section->id],
    );

    my $list_item = $app->html_template_screen(
        'wi_sections_subsection_item.tmpl',
        SEC_ID     => $section->id,
        SEC_NAME   => $section->name,
        SEC_SLUG   => $section->slug,
        DELETE_URL => $delete_url,
    );
    return $app->ajax_html($list_item);
}

sub rm_ajax_edit_section {
    my $app   = shift;
    my %field = $app->parse_submission(
        {   id       => 'section_id',
            parse_as => 'id',
            required => 1,
        },
        {   data_class => 'BigMed::Section',
            column     => 'name',
            required   => 1,
        },
        {   data_class => 'BigMed::Section',
            column     => 'slug',
            required   => 1,
        },
    );
    return $app->_update_section_title_slug(%field);
}

sub rm_ajax_edit_home {
    my $app   = shift;
    my %field = $app->parse_submission(
        {   id       => 'section_id',
            parse_as => 'id',
            required => 1,
        },
        {   data_class => 'BigMed::Section',
            column     => 'name',
            required   => 1,
        },
    );
    return $app->_update_section_title_slug(%field);
}

sub rm_ajax_delete {
    my $app = shift;
    $app->require_post() or return $app->ajax_system_error;
    my $site = $app->current_site or return $app->rm_ajax_login_badsite;
    my $user = $app->current_user or return $app->rm_ajax_login;

    my $site_id = $site->id;
    my $sec_id = ( $app->path_args )[0] || q{};
    my $section;
    $section = BigMed::Section->fetch( { site => $site_id, id => $sec_id } )
      if $sec_id;

    if ( !$section ) {
        $app->set_error( text =>
              ['SECTIONS_Section could not be found', $sec_id, $site->name,],
        );
        return $app->ajax_system_error;
    }

    if ( $section->is_homepage ) {
        $app->set_error( text => 'SECTIONS_Cannot delete homepage' );
        return $app->ajax_system_error;
    }

    $section->trash or return $app->ajax_system_error;
    return $app->ajax_json_response( { valid => 1 } );
}

sub _update_section_title_slug {
    my $app   = shift;
    my %field = @_;
    return $app->ajax_parse_error( $field{_ERROR} ) if $field{_ERROR};

    my $site = $app->current_site;
    if ( !$site ) {
        $app->set_error(
            head => 'BM_HEAD_Unknown site',
            text => 'BM_TEXT_Unknown site'
        );
        return $app->ajax_system_error;
    }

    #load the legacy section object
    defined(
        my $section = BigMed::Section->fetch(
            { site => $site->id, id => $field{section_id} }
        )
      )
      or return $app->ajax_system_error;
    if ( !$section ) {
        $app->set_error(
            text => [
                'SECTIONS_Section could not be found', $field{section_id},
                $site->name,
            ],
        );
        return $app->ajax_system_error;
    }

    #note original values
    my $oname = $section->name;
    my $oslug = $section->slug;

    #save if there are changes
    $section->set_name( $field{name} );
    $section->set_slug( $field{slug} )
      if $field{slug};    #not provided for homepage
    if ( $section->is_modified ) {
        $section->save or return $app->ajax_system_error;

        if (!$section->is_homepage) {
            #update page title if necessary
            my $page     = $section->section_page_obj() or return;
            if (   defined $oname
                && $oname ne $section->name
                && $page->title eq $oname )
            {
                $page->set_title( $section->name );
                $page->save or return;
            }
            
            #update slug directory
            my $slug_change = defined $oslug
              && $oslug ne q{}    #zero value okay
              && $section->slug;
            if ($slug_change) {
                $section->update_html_directory($oslug);
            }
        }
    }

    my %result = ( name => $field{name} );
    $result{slug} = $field{slug} if $field{slug};
    return $app->ajax_data( \%result );
}

sub _build_subsection_items {
    my $app        = shift;
    my $site       = shift;
    my $section    = shift || $app->_load_or_create_homepage($site) or return;
    my $html       = q{};
    my $delete_url = $app->build_url(
        script => 'bm-sections.cgi',
        rm     => 'ajax-delete',
        site   => $site->id,
    );
    foreach my $sec_id ( $section->kids ) {
        defined( my $section = $site->section_obj_by_id($sec_id) )
          or return;
        next if !$section;
        $html .= $app->html_template(
            'wi_sections_subsection_item.tmpl',
            SEC_ID      => $sec_id,
            SEC_NAME    => $section->name,
            SEC_SLUG    => $section->slug,
            DELETE_URL  => "$delete_url/$sec_id",
            SUBSECTIONS => $app->_build_subsection_items( $site, $section ),
        );
    }
    return $html;
}

sub _load_or_create_homepage {
    my $app  = shift;
    my $site = shift;
    defined( my $homepage = $site->homepage_obj ) or return;
    if ( !$homepage ) {
        $homepage = BigMed::Section->new();
        $homepage->set_name( $app->language('BM_Home') );
        $homepage->make_homepage();
        $homepage->set_active(1);
        $homepage->set_kids( [] );
        $site->add_section($homepage) or return;
        $homepage->save               or return;

        #saving homepage automagically creates a section page, but
        #we add a special title and content to the homepage on first
        #creation
        my $page = $homepage->section_page_obj or return;
        $page->set_title(
            $app->language( ['SECTIONS_Welcome to site', $site->name] ) );
        $page->set_content(
            'RichText:'
              . $app->language(
                [   'SECTIONS_Home page welcome text',
                    q{%BM} . $page->title . q{%}
                ]
              )

        );
        $page->save or return;
    }
    return $homepage;
}

sub _set_breadcrumbs {
    my $app    = shift;
    my $title  = shift;
    my $ref_bc = shift;
    my @bc     =
      ref $ref_bc eq 'ARRAY'
      ? @{$ref_bc}
      : (
        {   bc_label => 'BM_CP_NAVLABEL_Layout',
            bc_url   => $app->build_url(
                script => 'bm-templates.cgi',
                rm     => 'main-menu',
                site   => $app->current_site->id,
            ),
        },
        { bc_label => $title, },
      );
    return $app->set_cp_breadcrumbs(@bc);
}

1;

__END__

=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

