%# BEGIN BPS TAGGED BLOCK {{{
%#
%# COPYRIGHT:
%#
%# This software is Copyright (c) 1996-2024 Best Practical Solutions, LLC
%#                                          <sales@bestpractical.com>
%#
%# (Except where explicitly superseded by other copyright notices)
%#
%#
%# LICENSE:
%#
%# This work is made available to you under the terms of Version 2 of
%# the GNU General Public License. A copy of that license should have
%# been provided with this software, but in any event can be snarfed
%# from www.gnu.org.
%#
%# This work 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 this program; if not, write to the Free Software
%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
%# 02110-1301 or visit their web page on the internet at
%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
%#
%#
%# CONTRIBUTION SUBMISSION POLICY:
%#
%# (The following paragraph is not intended to limit the rights granted
%# to you to modify and distribute this software under the terms of
%# the GNU General Public License and is only of importance to you if
%# you choose to contribute your changes and enhancements to the
%# community by submitting them to Best Practical Solutions, LLC.)
%#
%# By intentionally submitting any modifications, corrections or
%# derivatives to this work, or any other work intended for use with
%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
%# you are the copyright holder for those contributions and you grant
%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
%# royalty-free, perpetual, license to use, copy, create derivative
%# works based on those contributions, and sublicense and distribute
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
<%INIT>
Abort( loc( 'Permission Denied' ) ) unless RT->Config->Get('ShowEditSystemConfig');

my $title = loc('System Configuration');
unless ($session{'CurrentUser'}->HasRight( Object=> $RT::System, Right => 'SuperUser')) {
 Abort(loc('This feature is only available to system administrators'));
}

my $options = RT->Config->LoadSectionMap();
my $active_context = {
    tab        => CSSClass( $ARGS{tab}        || $options->[0]->{Name}) ,
    section    => CSSClass( $ARGS{section}    || $options->[0]->{Content}->[0]->{Name}) ,
    subsection => CSSClass( $ARGS{subsection} || $options->[0]->{Content}->[0]->{Content}->[0]->{Name}) ,
};

my @option_list;
for my $tab ( @$options ) {
    for my $section ( @{$tab->{Content}} ) {
        for my $subsection ( @{$section->{Content}} ) {
            for my $name ( map { $_->{Name} } @{$subsection->{Content}} ) {
                my $meta = RT->Config->Meta( $name );
                next if $meta->{Invisible} || $meta->{Deprecated} || $meta->{Obfuscate};
                push @option_list, $name;
            }
        }
    }
}
@option_list = sort { lc $a cmp lc $b } @option_list;

my @results;

if (delete $ARGS{Update}) {
    RT->Config->BeginDatabaseConfigChanges;
    $RT::Handle->BeginTransaction;
    my $has_error;

    if ( delete $ARGS{CustomDateRanges} ) {
        push @results, ProcessCustomDateRanges( ARGSRef => \%ARGS );
    }

    eval {
        for my $key (keys %ARGS) {
            next if $key =~ /-(?:Current|file)$/;
            next if $key eq 'tab' || $key eq 'section' || $key eq 'subsection';
            # Get rid of extra arguments like in CustomDateRanges
            next if !exists $ARGS{$key . '-Current'};

            my $setting = RT::Configuration->new($session{CurrentUser});
            $setting->LoadByCols(Name => $key, Disabled => 0);
            if ( $ARGS{"$key-file"} ) {
                if ( $setting->Id ) {
                    my ( $ok, $msg ) = $setting->Delete;
                    push @results, $msg;
                    $has_error++ if !$ok;
                }
                next;
            }

            my $meta = RT->Config->Meta( $key );
            my $widget = $meta->{Widget} || '/Widgets/Form/JSON';
            my $is_json = $widget eq '/Widgets/Form/JSON';

            my $val = $ARGS{$key};
            $val = '' if $val eq '__empty_value__';
            my $prev = $ARGS{$key . '-Current'};
            next if $val eq $prev;

            # for bools, check for truthiness since 0, '', and undef are equivalent
            if ($widget eq '/Widgets/Form/Boolean') {
                next if !!$val eq !!$prev;
            }

            if ( $meta->{Immutable} || $meta->{Obfuscate} ) {
                push @results, loc("Cannot change [_1]: Permission Denied", $key);
                $has_error++;
                next;
            }

            if ($is_json) {
                my $json = $val;
                ($val) = RT::Configuration->new( $session{CurrentUser} )->_DeJSONContent($json);
                if (!defined $val) {
                    my $error = $@;
                    push @results, loc("Couldn't decode [_1] JSON '[_2]': [_3]", $key, $json, $error);
                    $has_error++;
                    next;
                }
            }

            if ( $meta->{Type} eq 'HASH' ) {
                my $old_value = RT->Config->_GetFromFilesOnly($key) || {};
                for my $item ( sort keys %$old_value ) {
                    if ( !exists $val->{$item} ) {
                        my @file_sources = grep { !$_->{Database} && exists $_->{Value}{$item} } @{ $meta->{Sources} };
                        push @results,
                            loc( "To delete this entry, you must remove '[_1]' in %[_2] from the following config file: [_3]",
                            $item, $key, join ', ', map {"$_->{File} line $_->{Line}"} @file_sources );
                    }
                }
            }

            if ($setting->Id) {
                my ($ok, $msg) = $setting->SetContent($val);
                push @results, $msg;
                $has_error++ if !$ok;
            }
            else {
                my ($ok, $msg) = $setting->Create(
                    Name    => $key,
                    Content => $val,
                );
                push @results, $msg;
                $has_error++ if !$ok;
            }
        }
    };

    if ($@) {
        push @results, $@;
        $has_error++;
    }

    @results = map { s/^Stack:.*//ms; $_ } @results;

    if ($has_error) {
        push @results, loc("No changes made.");
        $RT::Handle->Rollback;
    }
    else {
        $RT::Handle->Commit;
    }
    RT->Config->EndDatabaseConfigChanges;
}

my $nav_type='pill'; # 'tab' or 'pill'

</%INIT>
<& /Admin/Elements/Header, Title => $title &>
<& /Elements/Tabs &>
<& /Elements/ListActions, actions => \@results &>
<div class="configuration">
<& /Admin/Elements/ConfigHelp &>
<div class="titlebox-content">
% my @tab_names = map { $_->{Name} } @$options;
  <ul class="nav nav-<% $nav_type %>s" id="config-tabs">
% my $current_context = {};
% foreach my $tab_name (@tab_names) {
%     my $tab_id = CSSClass( $tab_name );
%     $current_context->{tab} = $tab_id;
%     my( $active, $aria_selected) = $tab_id eq $active_context->{tab} ? ('active', 'true') : ('', 'false');
%     my $nav_id = join '-', 'nav', $current_context->{tab};
%     my $content_id = join '-', 'content', $current_context->{tab};
    <li class="nav-item">
      <a class="nav-link <% $active %>" id="<% $nav_id %>" data-toggle="<% $nav_type %>" href="#<% $content_id %>" role="<% $nav_type %>" aria-controls="<% $content_id %>" aria-selected="<% $aria_selected %>"><% $tab_name %></a>
    </li>
% }
    <li class="nav-item ml-3">
      <input class="form-control option-search" placeholder="<&|/l&>Search</&>..." type="search" />
    </li>
  </ul>
  <div class="tab-content" id="content-all" >
% foreach my $tab ( @$options) {
%     my $tab_id = CSSClass( $tab->{Name} );
%     $current_context->{tab} = $tab_id;
%     my $active = $tab_id eq $active_context->{tab} ? ' show active' : '';
%     my $nav_id = join '-', 'nav', $current_context->{tab};
%     my $content_id = join '-', 'content', $current_context->{tab};
    <div class="tab-pane fade<% $active %>" role="tabpanel" id="<% $content_id %>" aria-labelledby="<% $nav_id %>">
      <& /Admin/Tools/Config/Elements/Tab, tab => $tab, active_context => $active_context, current_context => $current_context &>
    </div><!-- <% $content_id %> -->
% }
  </div><!-- content-all -->
</div><!-- titlebox-content -->
</div><!-- configuration -->

<script type="text/javascript">

jQuery(function() {

    // navs are not long, here we use modified id as hash to not scroll page for better user experiences.
    jQuery('div.configuration a.nav-link[data-toggle=tab]').on('show.bs.tab', function(e) {
        // Find the active subnav
        var target =
            jQuery(jQuery(e.target).attr('href')).find('a.nav-link.active[data-toggle=pill]').get(0) || e.target;
        window.location.hash = target.id.replace(/^nav-/, '');
    });

    jQuery('div.configuration a.nav-link[data-toggle=pill]').on('show.bs.tab', function(e) {
        window.location.hash = e.target.id.replace(/^nav-/, '');
    });

    var show_option = function(option) {
        if ( option.match(/^#form-box/) ) {
            var pane_id = jQuery(option).closest('div.tab-pane').attr('id');

            if ( pane_id ) {
                var parts = pane_id.split(/-/, 3); // e.g. content-System-Outgoing_mail

                if ( parts.length === 3 ) {
                    if ( jQuery(option).is(':visible') ) {
                        window.location.hash = ''; // Make sure hash is refreshed
                        window.location.hash = option;
                    }
                    else {
                        jQuery('#nav-' + parts[1]).tab('show');
                        jQuery('#nav-' + parts[1] + '-' + parts[2]).tab('show');
                        var interval;
                        interval = setInterval(function () {
                            if ( jQuery(option).is(':visible') ) {
                                window.location.hash = option;
                                clearInterval(interval);
                            }
                        }, 10);
                    }
                }
            }
        }
        else {
            option = option.replace(/^#/, '');
            var parts = option.split(/-/, 2); // e.g. System or System-Database_connection
            jQuery('#nav-' + parts[0]).tab('show');
            if ( parts.length === 2 ) {
                jQuery('#nav-' + option).tab('show');
            }
        }

        return false;
    };

    var options = <% JSON(\@option_list) |n %>;
    jQuery('input.option-search').autocomplete({ source: options, select: function(event, ui) {
        jQuery('input.option-search').val('');
        var option = ui.item.value;
        if ( option === 'CustomDateRanges' ) {
            show_option('#Features-Custom_Date_Ranges');
        }
        else {
            show_option('#form-box-' + option.toLowerCase());
        }
        return false;
    } });

    if ( window.location.hash ) {
        show_option(window.location.hash);
    }
});
</script>
