[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