[Bps-public-commit] rt-extension-formtools branch dynamic-forms-from-config created. 0.53-12-gff6d0ac
BPS Git Server
git at git.bestpractical.com
Mon Sep 18 20:03:27 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-extension-formtools".
The branch, dynamic-forms-from-config has been created
at ff6d0ac06a7adfbbfc618e08460f647af2807926 (commit)
- Log -----------------------------------------------------------------
commit ff6d0ac06a7adfbbfc618e08460f647af2807926
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Mon Sep 18 16:01:19 2023 -0400
Move all form pages into a defined entry in config
This makes it possible to separate form metadata from
page configurations.
diff --git a/etc/sample_form.json b/etc/sample_form.json
index e716882..8987269 100644
--- a/etc/sample_form.json
+++ b/etc/sample_form.json
@@ -1,108 +1,110 @@
{
- "submit" : {
- "content" : [
- {
- "html" : "<h1>Account Request</h1>",
- "type" : "raw_html"
- },
- {
- "type" : "raw_html",
- "html" : "<h2>Request Submitted</h2>"
- },
- {
- "type" : "raw_html",
- "html" : "<p>Your request has been submitted.</p>"
- }
- ],
- "name" : "Submit Request",
- "next" : ""
+ "formtools-pages" : {
+ "submit" : {
+ "content" : [
+ {
+ "html" : "<h1>Account Request</h1>",
+ "type" : "raw_html"
+ },
+ {
+ "type" : "raw_html",
+ "html" : "<h2>Request Submitted</h2>"
+ },
+ {
+ "type" : "raw_html",
+ "html" : "<p>Your request has been submitted.</p>"
+ }
+ ],
+ "name" : "Submit Request",
+ "next" : ""
+ },
+ "page2" : {
+ "validation" : 1,
+ "name" : "Page Two",
+ "content" : [
+ {
+ "html" : "<h1>Account Request</h1>",
+ "type" : "raw_html"
+ },
+ {
+ "comp_name" : "Field",
+ "arguments" : {
+ "name" : "Requestors",
+ "label" : "Requested by",
+ "default" : "root at localhost"
+ },
+ "type" : "component"
+ },
+ {
+ "arguments" : {
+ "name" : "Systems"
+ },
+ "type" : "component",
+ "comp_name" : "Field"
+ }
+ ],
+ "next" : "page3"
+ },
+ "page3" : {
+ "content" : [
+ {
+ "type" : "raw_html",
+ "html" : "<h1>Account Request</h1>"
+ },
+ {
+ "type" : "raw_html",
+ "html" : "<h2>Confirm Selections</h2>"
+ },
+ {
+ "type" : "component",
+ "comp_name" : "ShowChoices"
+ },
+ {
+ "type" : "raw_html",
+ "html" : "<hr/>\n<i><b>\n<p>What else should we know as we act on your request?</p>\n<p> Do you have special time constraints?</p>\n</b></i>"
+ },
+ {
+ "comp_name" : "Field",
+ "arguments" : {
+ "name" : "Content"
+ },
+ "type" : "component"
+ },
+ {
+ "input-value" : "create_ticket",
+ "type" : "hidden",
+ "input-name" : "create_ticket"
+ }
+ ],
+ "name" : "Review Selections",
+ "validation" : 1,
+ "next" : "submit"
+ },
+ "page1" : {
+ "validation" : 1,
+ "name" : "Page One",
+ "content" : [
+ {
+ "html" : "<h1>Account Request</h1>",
+ "type" : "raw_html"
+ },
+ {
+ "type" : "raw_html",
+ "html" : "<h2>Enter your preferred username</h2>"
+ },
+ {
+ "comp_name" : "Field",
+ "type" : "component",
+ "arguments" : {
+ "default" : "preferred username",
+ "show_validation" : 1,
+ "name" : "Username"
+ }
+ }
+ ],
+ "next" : "page2"
+ }
},
"queue" : 1,
- "page2" : {
- "validation" : 1,
- "name" : "Page Two",
- "content" : [
- {
- "html" : "<h1>Account Request</h1>",
- "type" : "raw_html"
- },
- {
- "comp_name" : "Field",
- "arguments" : {
- "name" : "Requestors",
- "label" : "Requested by",
- "default" : "root at localhost"
- },
- "type" : "component"
- },
- {
- "arguments" : {
- "name" : "Systems"
- },
- "type" : "component",
- "comp_name" : "Field"
- }
- ],
- "next" : "page3"
- },
- "formtools-start-page" : "page1",
- "page3" : {
- "content" : [
- {
- "type" : "raw_html",
- "html" : "<h1>Account Request</h1>"
- },
- {
- "type" : "raw_html",
- "html" : "<h2>Confirm Selections</h2>"
- },
- {
- "type" : "component",
- "comp_name" : "ShowChoices"
- },
- {
- "type" : "raw_html",
- "html" : "<hr/>\n<i><b>\n<p>What else should we know as we act on your request?</p>\n<p> Do you have special time constraints?</p>\n</b></i>"
- },
- {
- "comp_name" : "Field",
- "arguments" : {
- "name" : "Content"
- },
- "type" : "component"
- },
- {
- "input-value" : "create_ticket",
- "type" : "hidden",
- "input-name" : "create_ticket"
- }
- ],
- "name" : "Review Selections",
- "validation" : 1,
- "next" : "submit"
- },
- "page1" : {
- "validation" : 1,
- "name" : "Page One",
- "content" : [
- {
- "html" : "<h1>Account Request</h1>",
- "type" : "raw_html"
- },
- {
- "type" : "raw_html",
- "html" : "<h2>Enter your preferred username</h2>"
- },
- {
- "comp_name" : "Field",
- "type" : "component",
- "arguments" : {
- "default" : "preferred username",
- "show_validation" : 1,
- "name" : "Username"
- }
- }
- ],
- "next" : "page2"
- }
+ "formtools-start-page" : "page1"
}
diff --git a/html/Admin/FormTools/Modify.html b/html/Admin/FormTools/Modify.html
index 9e0ae3d..479614a 100644
--- a/html/Admin/FormTools/Modify.html
+++ b/html/Admin/FormTools/Modify.html
@@ -11,7 +11,7 @@
</div>
<div class="formtools-form-pages boxcontainer col-md-9" id="formtools-pages-wrapper">
<&| /Widgets/TitleBox, title => loc('FormTools Pages') &>
-% my @form_pages = keys %$form;
+% my @form_pages = keys %{$form->{'formtools-pages'}};
<ul class="nav nav-<% $nav_type %>s" id="formtools-pages">
% my $current_context = {};
% foreach my $page_name (@form_pages) {
diff --git a/html/Forms/dhandler b/html/Forms/dhandler
index 242c143..790aece 100644
--- a/html/Forms/dhandler
+++ b/html/Forms/dhandler
@@ -1,5 +1,5 @@
-<&|/FormTools/Form, next => '/Forms/' . $form_name . '/' . $form_config->{$page}{'next'},
- validation => $form_config->{$page}{'validation'},
+<&|/FormTools/Form, next => '/Forms/' . $form_name . '/' . $form_config->{'formtools-pages'}{$page}{'next'},
+ validation => $form_config->{'formtools-pages'}{$page}{'validation'},
next_for_validation => '/Forms/' . $form_name . '/' . $page,
results_ref => \@results,
&>
@@ -7,7 +7,7 @@
<%perl>
# Build the current page here dyamically from config
-foreach my $element ( @{$form_config->{$page}{'content'}} ) {
+foreach my $element ( @{$form_config->{'formtools-pages'}{$page}{'content'}} ) {
if ( $element->{type} eq 'raw_html' ) {
$m->out( $element->{html} );
}
@@ -81,7 +81,7 @@ unless ( $ok ) {
}
$m->notes( queue => $queue_obj );
-$m->notes( page_title => $form_config->{$page}{'name'} );
+$m->notes( page_title => $form_config->{'formtools-pages'}{$page}{'name'} );
# Try to create a ticket if we're on the last page and
# "create_ticket" is submitted as an arg from the second-to-last
commit 8ac4f6caf61aa8dc25c715b5f5a2b21f7cb948e3
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri Sep 15 17:03:18 2023 -0400
Create initial admin pages
diff --git a/html/Admin/Elements/FormToolsHelp b/html/Admin/Elements/FormToolsHelp
new file mode 100644
index 0000000..2df8c64
--- /dev/null
+++ b/html/Admin/Elements/FormToolsHelp
@@ -0,0 +1,52 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2023 Best Practical Solutions, LLC
+%# <sales at 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 }}}
+<div class="help" id="rt-help-text">
+<&| /Widgets/TitleBox, title => loc('FormTools Configuration') &>
+<p>FormTools help goes here.</p>
+</&>
+</div>
diff --git a/html/Admin/FormTools/Modify.html b/html/Admin/FormTools/Modify.html
new file mode 100644
index 0000000..9e0ae3d
--- /dev/null
+++ b/html/Admin/FormTools/Modify.html
@@ -0,0 +1,62 @@
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+<div id="formtools-edit">
+<& /Admin/Elements/FormToolsHelp &>
+<div class="row">
+<div class="formtools-component-menu boxcontainer col-md-3" id="formtools-component-wrapper">
+<&| /Widgets/TitleBox, title => loc('FormTools Components') &>
+<p>FormTools help goes here.</p>
+</&>
+</div>
+<div class="formtools-form-pages boxcontainer col-md-9" id="formtools-pages-wrapper">
+<&| /Widgets/TitleBox, title => loc('FormTools Pages') &>
+% my @form_pages = keys %$form;
+ <ul class="nav nav-<% $nav_type %>s" id="formtools-pages">
+% my $current_context = {};
+% foreach my $page_name (@form_pages) {
+% my $tab_id = CSSClass( $page_name );
+% $current_context->{tab} = $tab_id;
+% # my( $active, $aria_selected) = $tab_id eq $active_context->{tab} ? ('active', 'true') : ('', 'false');
+% my( $active, $aria_selected) = ('active', 'true');
+% 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 %>"><% $page_name %></a>
+ </li>
+% }
+ </ul>
+</&>
+</div><!-- row -->
+</div><!-- formtools-form-pages -->
+</div><!-- formtools-edit -->
+
+<p><&|/l&>Loaded form <% $form_attribute->Description %></&></p>
+
+<pre>
+% use Data::Printer;
+% $m->out(np($form));
+</pre>
+
+<%INIT>
+
+Abort("No form id found") unless $id;
+
+my $form_attribute = RT::Attribute->new($session{'CurrentUser'});
+my ($ok, $msg) = $form_attribute->Load($id);
+
+unless ( $ok ) {
+ Abort("Unable to load form with id $id");
+}
+
+my $form = $form_attribute->Content;
+
+my ($title, @results);
+$title = loc("Modify form [_1]", $form_attribute->Description);
+
+my $nav_type = 'pill'; # 'tab' or 'pill'
+
+</%INIT>
+<%ARGS>
+$id => undef
+</%ARGS>
diff --git a/html/Admin/FormTools/index.html b/html/Admin/FormTools/index.html
new file mode 100644
index 0000000..68f0913
--- /dev/null
+++ b/html/Admin/FormTools/index.html
@@ -0,0 +1,57 @@
+<& /Admin/Elements/Header, Title => loc("Admin FormTools") &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+
+ <table class="table collection collection-as-table" cellspacing="0">
+ <tbody>
+ <tr class="collection-as-table">
+ <th class="collection-as-table"><&|/l&>Name</&></th>
+ <th class="collection-as-table"><&|/l&>Creates Tickets in Queue</&></th>
+ </tr>
+% my $i = 0;
+% for my $form_ref ( @forms ) {
+
+ <tr class="<% $i % 2 ? 'oddline' : 'evenline' %>">
+ <td class="collection-as-table"><a href="<% RT->Config->Get('WebURL') %>Admin/FormTools/Modify.html?id=<% $form_ref->{'id'} %>"><% $form_ref->{'name'} %></a></td>
+ <td class="collection-as-table"><% $form_ref->{'queue_name'} %></td>
+ </tr>
+% ++$i;
+% }
+ </tbody>
+ </table>
+
+<%init>
+my @results;
+
+my $forms = RT::Attributes->new( $session{'CurrentUser'} );
+$forms->Limit( FIELD => 'Name', VALUE => 'FormTools Form' );
+
+my @unsorted_forms;
+while ( my $form = $forms->Next ) {
+ my $form_ref = $form->Content;
+ $form_ref->{'name'} = $form->Description;
+ $form_ref->{'id'} = $form->Id;
+
+ my $queue = RT::Queue->new( $session{'CurrentUser'} );
+ my ($ok, $msg) = $queue->Load($form_ref->{'queue'});
+ if ( $ok ) {
+ $form_ref->{'queue_name'} = $queue->Name;
+ }
+ else {
+ RT->Logger->error("FormTools unable to load queue " . $form_ref->{'queue'} . " $msg");
+ }
+
+ push @unsorted_forms, $form_ref;
+}
+
+my @forms = sort { $a->{'name'} cmp $b->{'name'} } @unsorted_forms;
+
+$m->callback(
+ CallbackName => 'Initial',
+ FormsRef => \@forms,
+ ARGSRef => \%ARGS
+);
+
+</%init>
+<%args>
+</%args>
commit 6e8a44fe07c82304f36b080197c6c7be67a1ddb9
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri Sep 8 14:47:25 2023 -0400
Create tickets when create_ticket is passed
The final result of any form wizard is to eventually
create a ticket. Provide a standard way of doing
that, passing in all accumulated arguments for
values on that new ticket.
diff --git a/html/FormTools/Form b/html/FormTools/Form
index b5839eb..58418b6 100644
--- a/html/FormTools/Form
+++ b/html/FormTools/Form
@@ -12,6 +12,7 @@ $form_id => undef
$form_name => undef
$form_classes => undef
$self_service => 0
+$results_ref => []
</%args>
<%init>
use RT::Extension::FormTools;
@@ -81,6 +82,10 @@ foreach my $key (keys %request_args) {
delete $request_args{$key} if ($core_fields{$key});
}
+# Add in any @results passed in. These should not be errors,
+# so they will be shown on the next page.
+push @results, @{$results_ref};
+
$next_for_validation ||= $m->caller(1)->path;
</%init>
diff --git a/html/Forms/dhandler b/html/Forms/dhandler
index eb2a0eb..242c143 100644
--- a/html/Forms/dhandler
+++ b/html/Forms/dhandler
@@ -1,6 +1,7 @@
<&|/FormTools/Form, next => '/Forms/' . $form_name . '/' . $form_config->{$page}{'next'},
validation => $form_config->{$page}{'validation'},
next_for_validation => '/Forms/' . $form_name . '/' . $page,
+ results_ref => \@results,
&>
<%perl>
@@ -82,7 +83,23 @@ unless ( $ok ) {
$m->notes( queue => $queue_obj );
$m->notes( page_title => $form_config->{$page}{'name'} );
+# Try to create a ticket if we're on the last page and
+# "create_ticket" is submitted as an arg from the second-to-last
+# page.
+
+my ($ticket_obj, @results);
+if ( $create_ticket ) {
+ ($ticket_obj, @results) = CreateTicket(
+ Subject => 'Ticket created from FormTools form ' . $form_name,
+ Queue => $queue_obj->Id,
+ Status => 'new',
+ Requestors => $session{'CurrentUser'}->EmailAddress,
+ %ARGS,
+ );
+}
+
</%init>
<%args>
$_form_tools_next => undef
+$create_ticket => undef
</%args>
commit f48884ee2b567433d3c288b7e021e67af5ecd8d6
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri Sep 8 14:41:31 2023 -0400
Align validation hint handling with RT updates
RT now allows custom messages as validation hints.
diff --git a/html/FormTools/Field b/html/FormTools/Field
index bc74274..ca20a17 100644
--- a/html/FormTools/Field
+++ b/html/FormTools/Field
@@ -192,9 +192,13 @@ $default = '' unless defined $default;
QueueObj => $queue,
($default ? (Default => $default) : ())
&>
-% if ($show_validation && $cf->FriendlyPattern) {
- <span class="cfhints"><% loc("Input must match [_1]", $cf->FriendlyPattern) %></span>
-%}
+% if (my $msg = $m->notes('InvalidField-' . $cf->Id)) {
+ <span class="cfinvalidfield my-1 d-inline-block"><% $msg %></span>
+% } elsif ($show_validation and $cf->FriendlyPattern) {
+ <span class="cfhints my-1 d-inline-block">
+ <% $cf->FriendlyPattern %>
+ </span>
+% }
% } elsif ($render_as =~ /^boolean/i) {
% my $value = 'Yes';
<div class="custom-control custom-checkbox" style="margin-top: 5px">
commit 9ef88c4a6d6da87bc35d052b4a911f500433369d
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri Sep 8 14:38:42 2023 -0400
Replace custom validation with standard RT version
diff --git a/html/FormTools/Form b/html/FormTools/Form
index 665df7b..b5839eb 100644
--- a/html/FormTools/Form
+++ b/html/FormTools/Form
@@ -53,18 +53,15 @@ if ($validation && $real_next) {
}
}
- # cf validation based on record pattern
- foreach my $key (keys %request_args) {
- next if $key =~ /Values-Magic$/;
- next unless ($key =~ /CustomField-(\d+)/ );
- my $id = $1;
- my $cf = RT::CustomField->new($session{'CurrentUser'});
- $cf->Load($id);
- next unless exists $request_args{"Object-RT::Ticket--CustomField-@{[ $cf->Id ]}-Values-Magic"};
- my ($ok, @res) = RT::Extension::FormTools::validate_cf($cf, \%request_args);
- push @results, @res unless $ok;
-
+ my ($status, @msg) = $m->comp(
+ '/Elements/ValidateCustomFields',
+ CustomFields => $queue->TicketCustomFields,
+ ARGSRef => \%request_args,
+ );
+ unless ($status) {
+ push @results, @msg;
}
+
unless (@results) {
$real_next = $m->caller(1)->dir_path . '/' . $real_next
unless $real_next =~ m'^/';
commit fa89e10fe2e4cc2555d239f838a6a967c1b27185
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri Sep 8 14:50:34 2023 -0400
Update support files for new utility
diff --git a/.gitignore b/.gitignore
index cff0b80..cc52a86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ pod2htm*.tmp
/MYMETA.*
/t/tmp
/xt/tmp
+/bin/rt-insert-formtools-config
diff --git a/inc/Module/Install/Substitute.pm b/inc/Module/Install/Substitute.pm
new file mode 100644
index 0000000..56af7fe
--- /dev/null
+++ b/inc/Module/Install/Substitute.pm
@@ -0,0 +1,131 @@
+#line 1
+package Module::Install::Substitute;
+
+use strict;
+use warnings;
+use 5.008; # I don't care much about earlier versions
+
+use Module::Install::Base;
+our @ISA = qw(Module::Install::Base);
+
+our $VERSION = '0.03';
+
+require File::Temp;
+require File::Spec;
+require Cwd;
+
+#line 89
+
+sub substitute
+{
+ my $self = shift;
+ $self->{__subst} = shift;
+ $self->{__option} = {};
+ if( UNIVERSAL::isa( $_[0], 'HASH' ) ) {
+ my $opts = shift;
+ while( my ($k,$v) = each( %$opts ) ) {
+ $self->{__option}->{ lc( $k ) } = $v || '';
+ }
+ }
+ $self->_parse_options;
+
+ my @file = @_;
+ foreach my $f (@file) {
+ $self->_rewrite_file( $f );
+ }
+
+ return;
+}
+
+sub _parse_options
+{
+ my $self = shift;
+ my $cwd = Cwd::getcwd();
+ foreach my $t ( qw(from to) ) {
+ $self->{__option}->{$t} = $cwd unless $self->{__option}->{$t};
+ my $d = $self->{__option}->{$t};
+ die "Couldn't read directory '$d'" unless -d $d && -r _;
+ }
+}
+
+sub _rewrite_file
+{
+ my ($self, $file) = @_;
+ my $source = File::Spec->catfile( $self->{__option}{from}, $file );
+ $source .= $self->{__option}{sufix} if $self->{__option}{sufix};
+ unless( -f $source && -r _ ) {
+ print STDERR "Couldn't find file '$source'\n";
+ return;
+ }
+ my $dest = File::Spec->catfile( $self->{__option}{to}, $file );
+ return $self->__rewrite_file( $source, $dest );
+}
+
+sub __rewrite_file
+{
+ my ($self, $source, $dest) = @_;
+
+ my $mode = (stat($source))[2];
+
+ open my $sfh, "<$source" or die "Couldn't open '$source' for read";
+ print "Open input '$source' file for substitution\n";
+
+ my ($tmpfh, $tmpfname) = File::Temp::tempfile('mi-subst-XXXX', UNLINK => 1);
+ $self->__process_streams( $sfh, $tmpfh, ($source eq $dest)? 1: 0 );
+ close $sfh;
+
+ seek $tmpfh, 0, 0 or die "Couldn't seek in tmp file";
+
+ open my $dfh, ">$dest" or die "Couldn't open '$dest' for write";
+ print "Open output '$dest' file for substitution\n";
+
+ while( <$tmpfh> ) {
+ print $dfh $_;
+ }
+ close $dfh;
+ chmod $mode, $dest or "Couldn't change mode on '$dest'";
+}
+
+sub __process_streams
+{
+ my ($self, $in, $out, $replace) = @_;
+
+ my @queue = ();
+ my $subst = $self->{'__subst'};
+ my $re_subst = join('|', map {"\Q$_"} keys %{ $subst } );
+
+ while( my $str = <$in> ) {
+ if( $str =~ /^###\s*(before|replace|after)\:\s?(.*)$/s ) {
+ my ($action, $nstr) = ($1,$2);
+ $nstr =~ s/\@($re_subst)\@/$subst->{$1}/ge;
+
+ die "Replace action is bad idea for situations when dest is equal to source"
+ if $replace && $action eq 'replace';
+ if( $action eq 'before' ) {
+ die "no line before 'before' action" unless @queue;
+ # overwrite prev line;
+ pop @queue;
+ push @queue, $nstr;
+ push @queue, $str;
+ } elsif( $action eq 'replace' ) {
+ push @queue, $nstr;
+ } elsif( $action eq 'after' ) {
+ push @queue, $str;
+ push @queue, $nstr;
+ # skip one line;
+ <$in>;
+ }
+ } else {
+ push @queue, $str;
+ }
+ while( @queue > 3 ) {
+ print $out shift(@queue);
+ }
+ }
+ while( scalar @queue ) {
+ print $out shift(@queue);
+ }
+}
+
+1;
+
commit 413f78b5e974ee18813fc830e5e1ac07d35a0a08
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri Sep 8 13:46:28 2023 -0400
Add utility to insert a FormTools config from JSON
Mostly for testing, but could be useful for automatically
inserting pre-configured forms in new extensions.
diff --git a/META.yml b/META.yml
index 7cbfb2b..470eb1d 100644
--- a/META.yml
+++ b/META.yml
@@ -16,6 +16,7 @@ meta-spec:
name: RT-Extension-FormTools
no_index:
directory:
+ - etc
- html
- inc
requires:
diff --git a/Makefile.PL b/Makefile.PL
index 4d0e2ec..ed71e84 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -4,6 +4,30 @@ requires_rt('5.0.0');
repository('https://github.com/bestpractical/rt-extension-formtools');
+my ($lp) = ($INC{'RT.pm'} =~ /^(.*)[\\\/]/);
+my $lib_path = join( ' ', "$RT::LocalPath/lib", $lp );
+my $bin_path = $RT::BinPath || "$RT::BasePath/bin" || "/opt/rt5/bin";
+
+# Straight from perldoc perlvar
+use Config;
+my $secure_perl_path = $Config{perlpath};
+if ($^O ne 'VMS') {
+ $secure_perl_path .= $Config{_exe}
+ unless $secure_perl_path =~ m/$Config{_exe}$/i;
+}
+
+substitute(
+ {
+ RT_LIB_PATH => $lib_path,
+ RT_BIN_PATH => $bin_path,
+ PERL => $ENV{PERL} || $secure_perl_path,
+ },
+ {
+ sufix => '.in'
+ },
+ qw(bin/rt-insert-formtools-config),
+);
+
sign();
WriteAll();
diff --git a/bin/rt-insert-formtools-config.in b/bin/rt-insert-formtools-config.in
new file mode 100644
index 0000000..6cf3573
--- /dev/null
+++ b/bin/rt-insert-formtools-config.in
@@ -0,0 +1,84 @@
+#!/usr/bin/env perl
+### before: #!@PERL@
+
+use strict;
+use warnings;
+
+### after: use lib qw(@RT_LIB_PATH@);
+use lib '/opt/rt5/local/lib /opt/rt5/lib';
+
+use RT::Interface::CLI qw(Init);
+
+use JSON;
+use Getopt::Long;
+
+my %OPT = (
+ 'form-name' => 1,
+);
+
+Init(
+ \%OPT,
+ "help|h",
+ "form-name=s",
+);
+
+Pod::Usage::pod2usage({verbose => 1}) if $OPT{help};
+Pod::Usage::pod2usage() unless @ARGV == 1;
+
+unless ( $OPT{'form-name'} ) {
+ die "form-name is required to name the new form";
+}
+
+my ($filename) = @ARGV;
+die "Not a file: $filename\n" unless -f $filename;
+die "Cannot read file: $filename\n" unless -r $filename;
+
+open(my $fh, "<", $filename)
+ or die "Can't read $filename: $!";
+
+my $json_input = do {local $/; <$fh>} if $fh;
+my $formtools_hash = decode_json($json_input);
+
+my $form = RT::Attribute->new( RT->SystemUser );
+my ( $ok, $msg ) = $form->Create(
+ Name => 'FormTools Form',
+ Description => $OPT{'form-name'},
+ Object => RT->System,
+ Content => $formtools_hash,
+);
+
+if ( $ok ) {
+ print "Form \"" . $OPT{'form-name'} . "\" created.\n";
+}
+else {
+ print "Error: $msg\n";
+}
+
+__END__
+
+=head1 NAME
+
+rt-insert-formtools-config - Process a JSON FormTools configuration
+file and insert it into the RT database.
+
+=head1 SYNOPSIS
+
+ rt-insert-formtools-config --form-name="Form One" formtools-config.json
+
+=head1 DESCRIPTION
+
+This script accepts a file containing a JSON structure to define
+a set of FormTools forms. It will insert this configuration into
+the RT database.
+
+=head1 OPTIONS
+
+=over
+
+=item C<--form-name>
+
+The name of the form to be created.
+
+=back
+
+=cut
diff --git a/etc/sample_form.json b/etc/sample_form.json
new file mode 100644
index 0000000..e716882
--- /dev/null
+++ b/etc/sample_form.json
@@ -0,0 +1,108 @@
+{
+ "submit" : {
+ "content" : [
+ {
+ "html" : "<h1>Account Request</h1>",
+ "type" : "raw_html"
+ },
+ {
+ "type" : "raw_html",
+ "html" : "<h2>Request Submitted</h2>"
+ },
+ {
+ "type" : "raw_html",
+ "html" : "<p>Your request has been submitted.</p>"
+ }
+ ],
+ "name" : "Submit Request",
+ "next" : ""
+ },
+ "queue" : 1,
+ "page2" : {
+ "validation" : 1,
+ "name" : "Page Two",
+ "content" : [
+ {
+ "html" : "<h1>Account Request</h1>",
+ "type" : "raw_html"
+ },
+ {
+ "comp_name" : "Field",
+ "arguments" : {
+ "name" : "Requestors",
+ "label" : "Requested by",
+ "default" : "root at localhost"
+ },
+ "type" : "component"
+ },
+ {
+ "arguments" : {
+ "name" : "Systems"
+ },
+ "type" : "component",
+ "comp_name" : "Field"
+ }
+ ],
+ "next" : "page3"
+ },
+ "formtools-start-page" : "page1",
+ "page3" : {
+ "content" : [
+ {
+ "type" : "raw_html",
+ "html" : "<h1>Account Request</h1>"
+ },
+ {
+ "type" : "raw_html",
+ "html" : "<h2>Confirm Selections</h2>"
+ },
+ {
+ "type" : "component",
+ "comp_name" : "ShowChoices"
+ },
+ {
+ "type" : "raw_html",
+ "html" : "<hr/>\n<i><b>\n<p>What else should we know as we act on your request?</p>\n<p> Do you have special time constraints?</p>\n</b></i>"
+ },
+ {
+ "comp_name" : "Field",
+ "arguments" : {
+ "name" : "Content"
+ },
+ "type" : "component"
+ },
+ {
+ "input-value" : "create_ticket",
+ "type" : "hidden",
+ "input-name" : "create_ticket"
+ }
+ ],
+ "name" : "Review Selections",
+ "validation" : 1,
+ "next" : "submit"
+ },
+ "page1" : {
+ "validation" : 1,
+ "name" : "Page One",
+ "content" : [
+ {
+ "html" : "<h1>Account Request</h1>",
+ "type" : "raw_html"
+ },
+ {
+ "type" : "raw_html",
+ "html" : "<h2>Enter your preferred username</h2>"
+ },
+ {
+ "comp_name" : "Field",
+ "type" : "component",
+ "arguments" : {
+ "default" : "preferred username",
+ "show_validation" : 1,
+ "name" : "Username"
+ }
+ }
+ ],
+ "next" : "page2"
+ }
+}
commit e9989c1119285a42bdf62d74b7e1b16641567c21
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Fri Sep 1 16:57:22 2023 -0400
Create new page to dynamically generate forms from config
diff --git a/html/Forms/dhandler b/html/Forms/dhandler
new file mode 100644
index 0000000..eb2a0eb
--- /dev/null
+++ b/html/Forms/dhandler
@@ -0,0 +1,88 @@
+<&|/FormTools/Form, next => '/Forms/' . $form_name . '/' . $form_config->{$page}{'next'},
+ validation => $form_config->{$page}{'validation'},
+ next_for_validation => '/Forms/' . $form_name . '/' . $page,
+&>
+
+<%perl>
+# Build the current page here dyamically from config
+
+foreach my $element ( @{$form_config->{$page}{'content'}} ) {
+ if ( $element->{type} eq 'raw_html' ) {
+ $m->out( $element->{html} );
+ }
+ elsif ( $element->{type} eq 'hidden' ) {
+ $m->out('<input type="hidden" class="hidden" name="' . $element->{'input-name'}
+ . '" value="' . $element->{'input-value'} . '" />');
+ }
+ elsif ( $element->{type} eq 'component' ) {
+ $m->comp('/FormTools/' . $element->{comp_name}, %{$element->{arguments}});
+ }
+}
+</%perl>
+
+<& /FormTools/Next &>
+</&>
+<%init>
+
+my $path = $m->dhandler_arg;
+my ($form_name, $page_name);
+
+if ( $path =~ /^([\w|\s]+)\/(\w+)$/ ) {
+ $form_name = $1;
+ $page_name = $2;
+}
+else {
+ $form_name = $path;
+}
+
+# Limit to names to letters, numbers, underscore, spaces
+unless ( $form_name =~ /^[\w|\s]+$/ ) {
+ RT->Logger->error("FormTools called with invalid form name: $form_name");
+ Abort('Invalid form name');
+}
+
+if ( $page_name ) {
+ unless ( $page_name =~ /^\w+$/ ) {
+ RT->Logger->error("FormTools called with invalid page name: $page_name");
+ Abort('Invalid page name');
+ }
+}
+
+# Load FormTools configration and look for a configured
+# form with the provided name.
+my $form_attribute = RT::Attribute->new( RT->SystemUser );
+$form_attribute->LoadByCols( Name => 'FormTools Form', Description => $form_name );
+my $form_config;
+
+if ( $form_attribute->Id ) {
+ $form_config = $form_attribute->Content;
+}
+else {
+ # We didn't find a form, so show a not found page
+ Abort('Form not found');
+}
+
+my $page;
+
+if ( $page_name ) {
+ $page = $page_name;
+}
+else {
+ $page = $form_config->{'formtools-start-page'};
+}
+
+my $queue_obj = RT::Queue->new( RT->SystemUser );
+my ($ok, $msg) = $queue_obj->Load( $form_config->{'queue'} );
+
+unless ( $ok ) {
+ RT->Logger->error('FormTools unable to load queue: ' . $form_config->{'queue'});
+ Abort('Unable to load form, invalid queue');
+}
+
+$m->notes( queue => $queue_obj );
+$m->notes( page_title => $form_config->{$page}{'name'} );
+
+</%init>
+<%args>
+$_form_tools_next => undef
+</%args>
commit 6e5109aff433e0b96aa7abcf8dba7d44d42693ef
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Wed Sep 6 08:59:42 2023 -0400
Remove unused variable
$next_comp is created, but not used anywhere after
it is set.
diff --git a/html/FormTools/Form b/html/FormTools/Form
index 7b6ba7d..665df7b 100644
--- a/html/FormTools/Form
+++ b/html/FormTools/Form
@@ -66,7 +66,6 @@ if ($validation && $real_next) {
}
unless (@results) {
- my $next_comp = $real_next;
$real_next = $m->caller(1)->dir_path . '/' . $real_next
unless $real_next =~ m'^/';
$m->subexec("$real_next", %ARGS, %request_args);
commit 51d16014358ce63a6afa790f64a311c7e6c41e7e
Author: Jim Brandt <jbrandt at bestpractical.com>
Date: Wed Sep 6 09:01:19 2023 -0400
Update Module::Install
diff --git a/META.yml b/META.yml
index 7ab87ec..7cbfb2b 100644
--- a/META.yml
+++ b/META.yml
@@ -8,7 +8,7 @@ configure_requires:
ExtUtils::MakeMaker: 6.59
distribution_type: module
dynamic_config: 1
-generated_by: 'Module::Install version 1.19'
+generated_by: 'Module::Install version 1.21'
license: gpl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
diff --git a/inc/Module/Install.pm b/inc/Module/Install.pm
index 7ba98c2..3dd721b 100644
--- a/inc/Module/Install.pm
+++ b/inc/Module/Install.pm
@@ -31,7 +31,7 @@ BEGIN {
# This is not enforced yet, but will be some time in the next few
# releases once we can make sure it won't clash with custom
# Module::Install extensions.
- $VERSION = '1.19';
+ $VERSION = '1.21';
# Storage for the pseudo-singleton
$MAIN = undef;
diff --git a/inc/Module/Install/Base.pm b/inc/Module/Install/Base.pm
index 9fa42c2..67ce900 100644
--- a/inc/Module/Install/Base.pm
+++ b/inc/Module/Install/Base.pm
@@ -4,7 +4,7 @@ package Module::Install::Base;
use strict 'vars';
use vars qw{$VERSION};
BEGIN {
- $VERSION = '1.19';
+ $VERSION = '1.21';
}
# Suspend handler for "redefined" warnings
diff --git a/inc/Module/Install/Can.pm b/inc/Module/Install/Can.pm
index d65c753..93fc4f9 100644
--- a/inc/Module/Install/Can.pm
+++ b/inc/Module/Install/Can.pm
@@ -8,7 +8,7 @@ use Module::Install::Base ();
use vars qw{$VERSION @ISA $ISCORE};
BEGIN {
- $VERSION = '1.19';
+ $VERSION = '1.21';
@ISA = 'Module::Install::Base';
$ISCORE = 1;
}
diff --git a/inc/Module/Install/Fetch.pm b/inc/Module/Install/Fetch.pm
index 3072b08..3c9390a 100644
--- a/inc/Module/Install/Fetch.pm
+++ b/inc/Module/Install/Fetch.pm
@@ -6,7 +6,7 @@ use Module::Install::Base ();
use vars qw{$VERSION @ISA $ISCORE};
BEGIN {
- $VERSION = '1.19';
+ $VERSION = '1.21';
@ISA = 'Module::Install::Base';
$ISCORE = 1;
}
diff --git a/inc/Module/Install/Include.pm b/inc/Module/Install/Include.pm
index 13fdcd0..b9b926f 100644
--- a/inc/Module/Install/Include.pm
+++ b/inc/Module/Install/Include.pm
@@ -6,7 +6,7 @@ use Module::Install::Base ();
use vars qw{$VERSION @ISA $ISCORE};
BEGIN {
- $VERSION = '1.19';
+ $VERSION = '1.21';
@ISA = 'Module::Install::Base';
$ISCORE = 1;
}
diff --git a/inc/Module/Install/Makefile.pm b/inc/Module/Install/Makefile.pm
index 13a4464..1e214a0 100644
--- a/inc/Module/Install/Makefile.pm
+++ b/inc/Module/Install/Makefile.pm
@@ -8,7 +8,7 @@ use Fcntl qw/:flock :seek/;
use vars qw{$VERSION @ISA $ISCORE};
BEGIN {
- $VERSION = '1.19';
+ $VERSION = '1.21';
@ISA = 'Module::Install::Base';
$ISCORE = 1;
}
diff --git a/inc/Module/Install/Metadata.pm b/inc/Module/Install/Metadata.pm
index 11bf971..2ae8036 100644
--- a/inc/Module/Install/Metadata.pm
+++ b/inc/Module/Install/Metadata.pm
@@ -6,7 +6,7 @@ use Module::Install::Base ();
use vars qw{$VERSION @ISA $ISCORE};
BEGIN {
- $VERSION = '1.19';
+ $VERSION = '1.21';
@ISA = 'Module::Install::Base';
$ISCORE = 1;
}
@@ -455,12 +455,8 @@ sub author_from {
my %license_urls = (
perl => 'http://dev.perl.org/licenses/',
apache => 'http://apache.org/licenses/LICENSE-2.0',
- apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1',
artistic => 'http://opensource.org/licenses/artistic-license.php',
- artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php',
lgpl => 'http://opensource.org/licenses/lgpl-license.php',
- lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php',
- lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html',
bsd => 'http://opensource.org/licenses/bsd-license.php',
gpl => 'http://opensource.org/licenses/gpl-license.php',
gpl2 => 'http://opensource.org/licenses/gpl-2.0.php',
@@ -471,6 +467,12 @@ my %license_urls = (
unrestricted => undef,
restrictive => undef,
unknown => undef,
+
+ # these are not actually allowed in meta-spec v1.4 but are left here for compatibility:
+ apache_1_1 => 'http://apache.org/licenses/LICENSE-1.1',
+ artistic_2 => 'http://opensource.org/licenses/artistic-license-2.0.php',
+ lgpl2 => 'http://opensource.org/licenses/lgpl-2.1.php',
+ lgpl3 => 'http://opensource.org/licenses/lgpl-3.0.html',
);
sub license {
diff --git a/inc/Module/Install/Win32.pm b/inc/Module/Install/Win32.pm
index f7aa615..b6c1d37 100644
--- a/inc/Module/Install/Win32.pm
+++ b/inc/Module/Install/Win32.pm
@@ -6,7 +6,7 @@ use Module::Install::Base ();
use vars qw{$VERSION @ISA $ISCORE};
BEGIN {
- $VERSION = '1.19';
+ $VERSION = '1.21';
@ISA = 'Module::Install::Base';
$ISCORE = 1;
}
diff --git a/inc/Module/Install/WriteAll.pm b/inc/Module/Install/WriteAll.pm
index 2db861a..d87eb9a 100644
--- a/inc/Module/Install/WriteAll.pm
+++ b/inc/Module/Install/WriteAll.pm
@@ -6,7 +6,7 @@ use Module::Install::Base ();
use vars qw{$VERSION @ISA $ISCORE};
BEGIN {
- $VERSION = '1.19';
+ $VERSION = '1.21';
@ISA = qw{Module::Install::Base};
$ISCORE = 1;
}
diff --git a/inc/YAML/Tiny.pm b/inc/YAML/Tiny.pm
index fb157a6..db3ae5c 100644
--- a/inc/YAML/Tiny.pm
+++ b/inc/YAML/Tiny.pm
@@ -2,12 +2,12 @@
use 5.008001; # sane UTF-8 support
use strict;
use warnings;
-package YAML::Tiny; # git description: v1.72-7-g8682f63
+package YAML::Tiny; # git description: v1.73-12-ge02f827
# XXX-INGY is 5.8.1 too old/broken for utf8?
# XXX-XDG Lancaster consensus was that it was sufficient until
# proven otherwise
-our $VERSION = '1.73';
+our $VERSION = '1.74';
#####################################################################
# The YAML::Tiny API.
-----------------------------------------------------------------------
hooks/post-receive
--
rt-extension-formtools
More information about the Bps-public-commit
mailing list