[Rt-commit] rt branch 5.0/edit-plugins-config-ui created. rt-5.0.4-175-g16c2eeda4c
BPS Git Server
git at git.bestpractical.com
Mon Sep 4 08:19:02 UTC 2023
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt".
The branch, 5.0/edit-plugins-config-ui has been created
at 16c2eeda4cb092eaa9f5322905de363b04a890f3 (commit)
- Log -----------------------------------------------------------------
commit 16c2eeda4cb092eaa9f5322905de363b04a890f3
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date: Mon Sep 4 11:17:32 2023 +0300
make plugin's configs editabe in UI
diff --git a/lib/RT/Config.pm b/lib/RT/Config.pm
index ffd504a36e..4fbc408023 100644
--- a/lib/RT/Config.pm
+++ b/lib/RT/Config.pm
@@ -2494,6 +2494,49 @@ sub LoadSectionMap {
}
my $ConfigFile = "$RT::EtcPath/RT_Config.pm";
+ $SectionMap = $self->_LoadSectionMapFromCoreFile($ConfigFile);
+
+ push @$SectionMap, {
+ Name => 'Plugins',
+ Content => [],
+ };
+
+ foreach my $plugin (grep $_, RT->Config->Get('Plugins')) {
+ my $path = RT::Plugin->new( name => $plugin )->Path('etc');
+ next unless -d $path;
+
+ my @ext_map;
+ my @ext_configs = $self->_ConfigsInDir($path);
+ foreach my $file (@ext_configs) {
+ my $tmp = $self->_LoadSectionMapFromPluginFile("$path/$file");
+ push @ext_map, @$tmp;
+ }
+
+ my $short_plugin = $plugin;
+ $short_plugin =~ s/^RT::Extension:://;
+ push @{ $SectionMap->[-1]{Content} }, {
+ Name => $short_plugin,
+ Content => \@ext_map,
+ };
+ }
+
+ # Remove empty tabs/sections
+ for my $tab (@$SectionMap) {
+ for my $section ( @{ $tab->{Content} } ) {
+ @{ $section->{Content} } = grep { @{ $_->{Content} } } @{ $section->{Content} };
+ }
+ @{ $tab->{Content} } = grep { @{ $_->{Content} } } @{ $tab->{Content} };
+ }
+ @$SectionMap = grep { @{ $_->{Content} } } @$SectionMap;
+
+ $SectionMapLoaded = 1;
+ return $SectionMap;
+}
+
+sub _LoadSectionMapFromCoreFile {
+ my $self = shift;
+ my $ConfigFile = shift;
+
require Pod::Simple::HTML;
my $PodParser = Pod::Simple::HTML->new();
@@ -2501,23 +2544,25 @@ sub LoadSectionMap {
$PodParser->output_string( \$html );
$PodParser->parse_file($ConfigFile);
+ my $res = [];
+
my $has_subsection;
while ( $html =~ m{<(h[123]|dt)\b[^>]*>(.*?)</\1>}sg ) {
my ( $tag, $content ) = ( $1, $2 );
if ( $tag eq 'h1' ) {
my ($title) = $content =~ m{<a class='u'\s*name="[^"]*"\s*>([^<]*)</a>};
- next if $title =~ /^(?:NAME|DESCRIPTION)$/;
- push @$SectionMap, { Name => $title, Content => [] };
+ next if $title =~ /^(?:NAME|DESCRIPTION|WARNING)$/;
+ push @$res, { Name => $title, Content => [] };
}
- elsif (@$SectionMap) {
+ elsif (@$res) {
if ( $tag eq 'h2' ) {
my ($title) = $content =~ m{<a class='u'\s*name="[^"]*"\s*>([^<]*)</a>};
- push @{ $SectionMap->[-1]{Content} }, { Name => $title, Content => [] };
+ push @{ $res->[-1]{Content} }, { Name => $title, Content => [] };
$has_subsection = 0;
}
elsif ( $tag eq 'h3' ) {
my ($title) = $content =~ m{<a class='u'\s*name="[^"]*"\s*>([^<]*)</a>};
- push @{ $SectionMap->[-1]{Content}[-1]{Content} }, { Name => $title, Content => [] };
+ push @{ $res->[-1]{Content}[-1]{Content} }, { Name => $title, Content => [] };
$has_subsection ||= 1;
}
else {
@@ -2525,7 +2570,7 @@ sub LoadSectionMap {
if ( !$has_subsection ) {
# Create an empty subsection to keep the same data structure
- push @{ $SectionMap->[-1]{Content}[-1]{Content} }, { Name => '', Content => [] };
+ push @{ $res->[-1]{Content}[-1]{Content} }, { Name => '', Content => [] };
$has_subsection = 1;
}
@@ -2538,23 +2583,54 @@ sub LoadSectionMap {
if ( $META{$option} ) {
next if $META{$option}{Invisible};
}
- push @{ $SectionMap->[-1]{Content}[-1]{Content}[-1]{Content} }, { Name => $option, Help => $name };
+ push @{ $res->[-1]{Content}[-1]{Content}[-1]{Content} }, { Name => $option, Help => $name };
}
}
}
}
- # Remove empty tabs/sections
- for my $tab (@$SectionMap) {
- for my $section ( @{ $tab->{Content} } ) {
- @{ $section->{Content} } = grep { @{ $_->{Content} } } @{ $section->{Content} };
+ return $res;
+}
+
+sub _LoadSectionMapFromPluginFile {
+ my $self = shift;
+ my $file = shift;
+
+ require Pod::Simple::HTML;
+ my $PodParser = Pod::Simple::HTML->new();
+
+ my $html;
+ $PodParser->output_string( \$html );
+ $PodParser->parse_file($file);
+
+ my $res = [];
+
+ my $cur;
+ while ( $html =~ m{<(h1|dt)\b[^>]*>(.*?)</\1>}sg ) {
+ my ( $tag, $content ) = ( $1, $2 );
+ if ( $tag eq 'h1' ) {
+ my ($title) = $content =~ m{<a class='u'\s*name="[^"]*"\s*>([^<]*)</a>};
+ next if $title =~ /^(?:NAME|DESCRIPTION|WARNING)$/;
+ push @$res, { Name => $title, Content => [] };
+ $cur = $res->[-1];
+ }
+ elsif ($cur) { # <dt>
+
+ # a single item (dt) can document several options, in separate <code> elements
+ my ($name) = $content =~ m{name=".([^"]*)"};
+ $name =~ s{,_.}{-}g; # e.g. DatabaseHost,_$DatabaseRTHost
+ while ( $content =~ m{<code>(.)([^<]*)</code>}sg ) {
+ my ( $sigil, $option ) = ( $1, $2 );
+ next unless $sigil =~ m{[\@\%\$]}; # no sigil => this is a value for a select option
+ if ( $META{$option} ) {
+ next if $META{$option}{Invisible};
+ }
+ push @{ $cur->{Content} }, { Name => $option, Help => $name };
+ }
}
- @{ $tab->{Content} } = grep { @{ $_->{Content} } } @{ $tab->{Content} };
}
- @$SectionMap = grep { @{ $_->{Content} } } @$SectionMap;
- $SectionMapLoaded = 1;
- return $SectionMap;
+ return $res;
}
=head2 Configs
@@ -2569,12 +2645,7 @@ sub Configs {
my @configs = ();
foreach my $path ( $RT::LocalEtcPath, RT->PluginDirs('etc'), $RT::EtcPath ) {
- my $mask = File::Spec->catfile( $path, "*_Config.pm" );
- my @files = glob $mask;
- @files = grep !/^RT_Config\.pm$/,
- grep $_ && /^\w+_Config\.pm$/,
- map { s/^.*[\\\/]//; $_ } @files;
- push @configs, sort @files;
+ push @configs, $self->_ConfigsInDir($path);
}
my %seen;
@@ -2582,6 +2653,18 @@ sub Configs {
return @configs;
}
+sub _ConfigsInDir {
+ my $self = shift;
+ my $dir = shift;
+
+ my $mask = File::Spec->catfile( $dir, "*_Config.pm" );
+ my @files = glob $mask;
+ @files = grep !/^RT_Config\.pm$/,
+ grep $_ && /^\w+_Config\.pm$/,
+ map { s/^.*[\\\/]//; $_ } @files;
+ return sort @files;
+}
+
=head2 LoadedConfigs
Returns a list of hashrefs, one for each config file loaded. The keys of the
diff --git a/share/html/Admin/Tools/Config/Elements/Option b/share/html/Admin/Tools/Config/Elements/Option
index 2dab8ca807..86348172e8 100644
--- a/share/html/Admin/Tools/Config/Elements/Option
+++ b/share/html/Admin/Tools/Config/Elements/Option
@@ -83,6 +83,7 @@ return if $meta->{Invisible} || $meta->{Deprecated} || $meta->{Obfuscate};
my $raw_value = RT->Config->Get( $name );
my ($val, $format) = $stringify->($raw_value);
my $doc_url = "https://docs.bestpractical.com/rt/$doc_version/RT_Config.html#$option->{Help}";
+$doc_url = '' if $current_context->{tab} eq 'Plugins';
my $widget = $meta->{'Widget'} || '/Widgets/Form/JSON';
# CustomDateRanges could contain subrefs, but we supply limited/safe
@@ -92,6 +93,9 @@ my $is_immutable = $meta->{Immutable}
|| ( $format eq 'code' && $widget ne '/Widgets/Form/CustomDateRanges' );
my $current_value = $format ? $val : $raw_value;
+if ($current_value && !$format && $widget eq '/Widgets/Form/JSON') {
+ $current_value = to_json($current_value, {pretty => 1, canonical => 1});
+}
my $current_file_value;
@@ -131,7 +135,7 @@ elsif ($widget eq '/Widgets/Form/Select') {
my $row_start = qq{<div class="widget form-row" id="form-box-@{[ lc $name ]}">
<div class="label col-4">
<span data-toggle="tooltip" data-placement="top" data-original-title="$args->{Tooltip}">
- <a href="$doc_url" target="_blank">$name</a>
+ }. ($doc_url ? qq{<a href="$doc_url" target="_blank">$name</a>} : $name) .qq{
</span>
</div>
<div class="value col-8">
@@ -190,4 +194,5 @@ my $row_end = qq{</div></div>};
<!-- end option <% $name %> -->
<%ARGS>
$option
+$current_context => {}
</%ARGS>
diff --git a/share/html/Admin/Tools/Config/Elements/SubSection b/share/html/Admin/Tools/Config/Elements/SubSection
index fc00df3b32..fd1b0f6cdf 100644
--- a/share/html/Admin/Tools/Config/Elements/SubSection
+++ b/share/html/Admin/Tools/Config/Elements/SubSection
@@ -58,7 +58,7 @@
% my $can_edit;
% foreach my $option ( @{$subsection->{Content}} ) {
% $can_edit ||= 1 unless $RT::Config::META{$option->{Name}}{Immutable};
- <& /Admin/Tools/Config/Elements/Option, option => $option &>
+ <& /Admin/Tools/Config/Elements/Option, option => $option, current_context => $current_context &>
% }
<input type="hidden" name="tab" value="<% $current_context->{tab} %>" />
<input type="hidden" name="section" value="<% $current_context->{section} %>" />
-----------------------------------------------------------------------
hooks/post-receive
--
rt
More information about the rt-commit
mailing list