[Bps-public-commit] rt-extension-repeatticket branch, master, updated. b064b829fdc4847c33145fc0db41950f49dddc80

Jim Brandt jbrandt at bestpractical.com
Mon Jul 1 11:35:41 EDT 2013


The branch, master has been updated
       via  b064b829fdc4847c33145fc0db41950f49dddc80 (commit)
       via  f2e8a1324afcd7e7897182c7c49d83cb9dfea536 (commit)
       via  35d7e25ddfe5e9ecb4728aaa54af63a09b0d4091 (commit)
       via  f72e1a7c0c5c936ad1522e0865b0d00ef1a05d8b (commit)
       via  913e68ac8b630c71024b4e2db63734cdc99bc2b8 (commit)
       via  7c28f7178c7a9d481400272f650f05b794387925 (commit)
       via  2e621bddf489b47e1f5cf8be35d0863ad4c842fb (commit)
       via  2351ed1f30dfef2250767499808c67ee29d37952 (commit)
       via  853f1e4f8d17c8dcb268bb4aeb1372d6fc054f8c (commit)
       via  87d17dc12c4a855f79869c7d657fb3188da0e3d9 (commit)
      from  cc67c5ab0d277fe5bd9d355789664502a448982a (commit)

Summary of changes:
 Changes                             |   9 +-
 MANIFEST                            |  18 +-
 META.yml                            |   4 +-
 Makefile.PL                         |   2 +
 README                              | 184 +++++++++++++++
 README.pod                          |   1 -
 inc/Module/Install/RTx.pm           |  41 +---
 inc/Module/Install/RTx/Factory.pm   | 451 +-----------------------------------
 inc/Module/Install/ReadmeFromPod.pm | 138 +++++++++++
 lib/RT/Extension/RepeatTicket.pm    | 104 +++++----
 {t => xt}/cf.t                      |   1 +
 {t => xt}/daily.t                   |   7 +-
 {t => xt}/end_conditions.t          |   0
 {t => xt}/monthly.t                 |  10 +-
 {t => xt}/on_complete.t             |   0
 {t => xt}/start_date.t              |   0
 {t => xt}/weekly.t                  |   0
 {t => xt}/yearly.t                  |   0
 18 files changed, 443 insertions(+), 527 deletions(-)
 create mode 100644 README
 delete mode 120000 README.pod
 create mode 100644 inc/Module/Install/ReadmeFromPod.pm
 rename {t => xt}/cf.t (98%)
 rename {t => xt}/daily.t (95%)
 rename {t => xt}/end_conditions.t (100%)
 rename {t => xt}/monthly.t (86%)
 rename {t => xt}/on_complete.t (100%)
 rename {t => xt}/start_date.t (100%)
 rename {t => xt}/weekly.t (100%)
 rename {t => xt}/yearly.t (100%)

- Log -----------------------------------------------------------------
commit 87d17dc12c4a855f79869c7d657fb3188da0e3d9
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Thu Mar 28 13:15:58 2013 -0400

    Generate README with Module::Install

diff --git a/Makefile.PL b/Makefile.PL
index cbd56ae..44a98da 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -1,6 +1,7 @@
 use inc::Module::Install;
 RTx('RT-Extension-RepeatTicket');
 all_from('lib/RT/Extension/RepeatTicket.pm');
+readme_from('lib/RT/Extension/RepeatTicket.pm');
 requires('DateTime::Event::ICal');
 
 my ($lp) = ($INC{'RT.pm'} =~ /^(.*)[\\\/]/);
diff --git a/README b/README
new file mode 100644
index 0000000..a42627a
--- /dev/null
+++ b/README
@@ -0,0 +1,158 @@
+NAME
+    RT::Extension::RepeatTicket - Repeat tickets based on schedule
+
+INSTALLATION
+    To install this module, run the following commands:
+
+        perl Makefile.PL
+        make
+        make install # May need sudo/root
+        make initdb  # May need sudo/root
+
+    add RT::Extension::RepeatTicket to @Plugins in RT's
+    etc/RT_SiteConfig.pm:
+
+        Set( @Plugins, qw(... RT::Extension::RepeatTicket) );
+        Set( $RepeatTicketCoexistentNumber, 1 ); # Optional
+        Set( $RepeatTicketLeadTime, 14 ); # Optional
+
+    add bin/rt-repeat-ticket to the daily cron job.
+
+DESCRIPTION
+    The RepeatTicket extension allows you to set up recurring tickets so new
+    tickets are automatically created based on a schedule. The new tickets
+    are populated with the subject and initial content of the original
+    ticket in the recurrence.
+
+    After you activate the plugin by adding it to your RT_SiteConfig.pm
+    file, all tickets will have a Recurrence tab on the create and edit
+    pages. To set up a repeating ticket, click the checkbox to "Enable
+    Recurrence" and fill out the schedule for the new tickets.
+
+    New tickets are created when you initially save the recurrence, if new
+    tickets are needed, and when your daily cron job runs the
+    rt-repeat-ticket script.
+
+  $RepeatTicketCoexistentNumber
+    The $RepeatTicketCoexistentNumber determines how many tickets can be in
+    an active status for a recurrence at any time. A value of 1 means one
+    ticket at a time can be active. New tickets will not be created until
+    the current active ticket is resolved or set to some other inactive
+    status. You can also set this value per recurrence, overriding this
+    config value. The extension default is 1 ticket.
+
+  $RepeatTicketLeadTime
+    The $RepeatTicketLeadTime becomes the ticket Starts value and sets how
+    far in advance of a ticket's Due date you want the ticket to be created.
+    This essentially is how long you want to give people to work on the
+    ticket.
+
+    For example, if you create a weekly recurrence scheduled on Mondays and
+    set the lead time to 7 days, each Monday a ticket will be created with
+    the Starts date set to that Monday and a Due date of the following
+    Monday.
+
+    The value you set in RT_SiteConfig.pm becomes the system default, but
+    you can set this value on each ticket as well. The extension default is
+    14 days.
+
+  rt-repeat-ticket
+    The rt-repeat-ticket utility evaluates all of your repeating tickets and
+    creates any new tickets that are needed. With no parameters, it runs for
+    "today" each day. You can also pass a --date value in the form
+    YYYY-MM-DD to run the script for a specific day.
+
+        bin/rt-repeat-ticket --date 2012-07-25
+
+    This can be handy if your cron job doesn't run for some reason and you
+    want to make sure no repeating tickets have been missed. Just go back
+    and run the script for the days you missed. You can also pass dates in
+    the future which might be handy if you want to experiment with
+    recurrences in a test environment.
+
+USAGE
+  Initial Tickets
+    The initial ticket you create for a recurrence stores the schedule and
+    other details for the recurrence. If you need to change the recurrence
+    in the future, to make it more frequent or less frequent or anything
+    else, make the changes on the original ticket. To help you find this
+    initial ticket, which may have been resolved long ago, a custom field is
+    created on each ticket in the recurrence with link called "Original
+    Ticket."
+
+    When setting up the recurrence, you can use the original ticket as an
+    actual work ticket. When doing this, you'll need to set the Starts and
+    Due dates when you create the ticket. Scheduled tickets created
+    subsequently will set these values based on the recurrence. Resolving
+    the original ticket does not cancel the recurrence.
+
+  Start Value
+    You can set a Start date for a new recurrence. If you don't, it defaults
+    to the day you create the recurrence.
+
+  Cancelling Recurrences
+    You can cancel or end a recurrence in two ways:
+
+    *   Go to the original ticket in the recurrence and uncheck the Enable
+        Recurrence checkbox.
+
+    *   Set ending conditions on the recurrence with either a set number of
+        recurrences or an end date.
+
+  Recursive Recurrences
+    Creating recurrences on recurrences isn't supported and may do strange
+    things.
+
+FAQ
+    I'm not seeing new recurrences. Why not?
+        A few things to check:
+
+        *   Do you have rt-repeat-tickets scheduled in cron? Is it running?
+
+        *   Do you have previous tickets still in an active state? Resolve
+            those tickets or increase the concurrent active tickets value.
+
+        *   Is it the right day? Remember to subtract the lead time value to
+            determine the day new tickets should be created.
+
+        *   If you set a start date and another criteria like day of the
+            week, the new ticket will be created on the first time that day
+            of the week occurs after the start date you set (if the start
+            date isn't on that day of the week).
+
+METHODS
+  Run( RT::Attribute $attr, DateTime $checkday )
+    Repeat the ticket if $checkday meets the repeat settings. It also tries
+    to repeat more to meet config "RepeatTicketCoexistentNumber".
+
+    Return ids of new created tickets.
+
+  Repeat ( RT::Attribute $attr, DateTime $checkday_1, DateTime $checkday_2, ... )
+    Repeat the ticket for the check days that meet repeat settings.
+
+    Return ids of new created tickets.
+
+  MaybeRepeatMore ( RT::Attribute $attr )
+    Try to repeat more tickets to meet the coexistent ticket number.
+
+    Return ids of new created tickets.
+
+  SetRepeatAttribute ( RT::Ticket $ticket, %args )
+    Save %args to the ticket's "RepeatTicketSettings" attribute.
+
+    Return ( RT::Attribute, UPDATE MESSAGE )
+
+AUTHOR
+    sunnavy, <sunnavy at bestpractical.com>
+
+    Jim Brandt, <jbrandt at bestpractical.com>
+
+LICENSE AND COPYRIGHT
+    Copyright 2013 Best Practical Solutions, LLC.
+
+    This program is free software; you can redistribute it and/or modify it
+    under the terms of either: the GNU General Public License as published
+    by the Free Software Foundation; or the Artistic License.
+
+    See http://dev.perl.org/licenses/ for more information.
+
diff --git a/README.pod b/README.pod
deleted file mode 120000
index f92d99b..0000000
--- a/README.pod
+++ /dev/null
@@ -1 +0,0 @@
-lib/RT/Extension/RepeatTicket.pm
\ No newline at end of file
diff --git a/inc/Module/Install/ReadmeFromPod.pm b/inc/Module/Install/ReadmeFromPod.pm
new file mode 100644
index 0000000..6a80818
--- /dev/null
+++ b/inc/Module/Install/ReadmeFromPod.pm
@@ -0,0 +1,138 @@
+#line 1
+package Module::Install::ReadmeFromPod;
+
+use 5.006;
+use strict;
+use warnings;
+use base qw(Module::Install::Base);
+use vars qw($VERSION);
+
+$VERSION = '0.20';
+
+sub readme_from {
+  my $self = shift;
+  return unless $self->is_admin;
+
+  # Input file
+  my $in_file  = shift || $self->_all_from
+    or die "Can't determine file to make readme_from";
+
+  # Get optional arguments
+  my ($clean, $format, $out_file, $options);
+  my $args = shift;
+  if ( ref $args ) {
+    # Arguments are in a hashref
+    if ( ref($args) ne 'HASH' ) {
+      die "Expected a hashref but got a ".ref($args)."\n";
+    } else {
+      $clean    = $args->{'clean'};
+      $format   = $args->{'format'};
+      $out_file = $args->{'output_file'};
+      $options  = $args->{'options'};
+    }
+  } else {
+    # Arguments are in a list
+    $clean    = $args;
+    $format   = shift;
+    $out_file = shift;
+    $options  = \@_;
+  }
+
+  # Default values;
+  $clean  ||= 0;
+  $format ||= 'txt';
+
+  # Generate README
+  print "readme_from $in_file to $format\n";
+  if ($format =~ m/te?xt/) {
+    $out_file = $self->_readme_txt($in_file, $out_file, $options);
+  } elsif ($format =~ m/html?/) {
+    $out_file = $self->_readme_htm($in_file, $out_file, $options);
+  } elsif ($format eq 'man') {
+    $out_file = $self->_readme_man($in_file, $out_file, $options);
+  } elsif ($format eq 'pdf') {
+    $out_file = $self->_readme_pdf($in_file, $out_file, $options);
+  }
+
+  if ($clean) {
+    $self->clean_files($out_file);
+  }
+
+  return 1;
+}
+
+
+sub _readme_txt {
+  my ($self, $in_file, $out_file, $options) = @_;
+  $out_file ||= 'README';
+  require Pod::Text;
+  my $parser = Pod::Text->new( @$options );
+  open my $out_fh, '>', $out_file or die "Could not write file $out_file:\n$!\n";
+  $parser->output_fh( *$out_fh );
+  $parser->parse_file( $in_file );
+  close $out_fh;
+  return $out_file;
+}
+
+
+sub _readme_htm {
+  my ($self, $in_file, $out_file, $options) = @_;
+  $out_file ||= 'README.htm';
+  require Pod::Html;
+  Pod::Html::pod2html(
+    "--infile=$in_file",
+    "--outfile=$out_file",
+    @$options,
+  );
+  # Remove temporary files if needed
+  for my $file ('pod2htmd.tmp', 'pod2htmi.tmp') {
+    if (-e $file) {
+      unlink $file or warn "Warning: Could not remove file '$file'.\n$!\n";
+    }
+  }
+  return $out_file;
+}
+
+
+sub _readme_man {
+  my ($self, $in_file, $out_file, $options) = @_;
+  $out_file ||= 'README.1';
+  require Pod::Man;
+  my $parser = Pod::Man->new( @$options );
+  $parser->parse_from_file($in_file, $out_file);
+  return $out_file;
+}
+
+
+sub _readme_pdf {
+  my ($self, $in_file, $out_file, $options) = @_;
+  $out_file ||= 'README.pdf';
+  eval { require App::pod2pdf; }
+    or die "Could not generate $out_file because pod2pdf could not be found\n";
+  my $parser = App::pod2pdf->new( @$options );
+  $parser->parse_from_file($in_file);
+  open my $out_fh, '>', $out_file or die "Could not write file $out_file:\n$!\n";
+  select $out_fh;
+  $parser->output;
+  select STDOUT;
+  close $out_fh;
+  return $out_file;
+}
+
+
+sub _all_from {
+  my $self = shift;
+  return unless $self->admin->{extensions};
+  my ($metadata) = grep {
+    ref($_) eq 'Module::Install::Metadata';
+  } @{$self->admin->{extensions}};
+  return unless $metadata;
+  return $metadata->{values}{all_from} || '';
+}
+
+'Readme!';
+
+__END__
+
+#line 254
+

commit 853f1e4f8d17c8dcb268bb4aeb1372d6fc054f8c
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Thu Mar 28 13:19:43 2013 -0400

    Module::Install updates

diff --git a/inc/Module/Install/RTx.pm b/inc/Module/Install/RTx.pm
index 73b9cda..abf6aea 100644
--- a/inc/Module/Install/RTx.pm
+++ b/inc/Module/Install/RTx.pm
@@ -8,13 +8,13 @@ no warnings 'once';
 
 use Module::Install::Base;
 use base 'Module::Install::Base';
-our $VERSION = '0.29';
+our $VERSION = '0.30';
 
 use FindBin;
 use File::Glob     ();
 use File::Basename ();
 
-my @DIRS = qw(etc lib html bin sbin po var);
+my @DIRS = qw(etc lib html static bin sbin po var);
 my @INDEX_DIRS = qw(lib bin sbin);
 
 sub RTx {
@@ -62,10 +62,11 @@ sub RTx {
     unshift @INC, "$RT::LocalPath/lib" if $RT::LocalPath;
     unshift @INC, $lib_path;
 
-    $RT::LocalVarPath  ||= $RT::VarPath;
-    $RT::LocalPoPath   ||= $RT::LocalLexiconPath;
-    $RT::LocalHtmlPath ||= $RT::MasonComponentRoot;
-    $RT::LocalLibPath  ||= "$RT::LocalPath/lib";
+    $RT::LocalVarPath    ||= $RT::VarPath;
+    $RT::LocalPoPath     ||= $RT::LocalLexiconPath;
+    $RT::LocalHtmlPath   ||= $RT::MasonComponentRoot;
+    $RT::LocalStaticPath ||= $RT::StaticPath;
+    $RT::LocalLibPath    ||= "$RT::LocalPath/lib";
 
     my $with_subdirs = $ENV{WITH_SUBDIRS};
     @ARGV = grep { /WITH_SUBDIRS=(.*)/ ? ( ( $with_subdirs = $1 ), 0 ) : 1 }
@@ -129,18 +130,7 @@ install ::
 
     my %has_etc;
     if ( File::Glob::bsd_glob("$FindBin::Bin/etc/schema.*") ) {
-
-        # got schema, load factory module
         $has_etc{schema}++;
-        $self->load('RTxFactory');
-        $self->postamble(<< ".");
-factory ::
-\t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxFactory(qw($RTx $name))"
-
-dropdb ::
-\t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxFactory(qw($RTx $name drop))"
-
-.
     }
     if ( File::Glob::bsd_glob("$FindBin::Bin/etc/acl.*") ) {
         $has_etc{acl}++;
@@ -164,28 +154,19 @@ dropdb ::
         print "For first-time installation, type 'make initdb'.\n";
         my $initdb = '';
         $initdb .= <<"." if $has_etc{schema};
-\t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxInitDB(qw(schema))"
+\t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxInitDB(qw(schema \$(NAME) \$(VERSION)))"
 .
         $initdb .= <<"." if $has_etc{acl};
-\t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxInitDB(qw(acl))"
+\t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxInitDB(qw(acl \$(NAME) \$(VERSION)))"
 .
         $initdb .= <<"." if $has_etc{initialdata};
-\t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxInitDB(qw(insert))"
+\t\$(NOECHO) \$(PERL) -Ilib -I"$local_lib_path" -I"$lib_path" -Minc::Module::Install -e"RTxInitDB(qw(insert \$(NAME) \$(VERSION)))"
 .
         $self->postamble("initdb ::\n$initdb\n");
         $self->postamble("initialize-database ::\n$initdb\n");
     }
 }
 
-sub RTxInit {
-    unshift @INC, substr( delete( $INC{'RT.pm'} ), 0, -5 ) if $INC{'RT.pm'};
-    require RT;
-    RT::LoadConfig();
-    RT::ConnectToDatabase();
-
-    die "Cannot load RT" unless $RT::Handle and $RT::DatabaseType;
-}
-
 # stolen from RT::Handle so we work on 3.6 (cmp_versions came in with 3.8)
 { my %word = (
     a     => -4,
@@ -228,4 +209,4 @@ sub requires_rt {
 
 __END__
 
-#line 348
+#line 329
diff --git a/inc/Module/Install/RTx/Factory.pm b/inc/Module/Install/RTx/Factory.pm
index 23ce911..76ab761 100644
--- a/inc/Module/Install/RTx/Factory.pm
+++ b/inc/Module/Install/RTx/Factory.pm
@@ -6,7 +6,7 @@ use strict;
 use File::Basename ();
 
 sub RTxInitDB {
-    my ($self, $action) = @_;
+    my ($self, $action, $name, $version) = @_;
 
     unshift @INC, substr(delete($INC{'RT.pm'}), 0, -5) if $INC{'RT.pm'};
 
@@ -23,6 +23,8 @@ sub RTxInitDB {
 
     RT::LoadConfig();
 
+    require RT::System;
+
     my $lib_path = File::Basename::dirname($INC{'RT.pm'});
     my @args = ("-Ilib");
     push @args, "-I$RT::LocalPath/lib" if $RT::LocalPath;
@@ -32,452 +34,13 @@ sub RTxInitDB {
         "--action"      => $action,
         "--datadir"     => "etc",
         (($action eq 'insert') ? ("--datafile"    => "etc/initialdata") : ()),
-        "--dba"         => $RT::DatabaseUser,
-        "--prompt-for-dba-password" => ''
+        "--dba"         => $RT::DatabaseAdmin || $RT::DatabaseUser,
+        "--prompt-for-dba-password" => '',
+        (RT::System->can('AddUpgradeHistory') ? ("--package" => $name, "--ext-version" => $version) : ()),
     );
+
     print "$^X @args\n";
     (system($^X, @args) == 0) or die "...returned with error: $?\n";
 }
 
-sub RTxFactory {
-    my ($self, $RTx, $name, $drop) = @_;
-    my $namespace = "$RTx\::$name";
-
-    $self->RTxInit;
-
-    my $dbh = $RT::Handle->dbh;
-    # get all tables out of database
-    my @tables = $dbh->tables;
-    my ( %tablemap, %typemap, %modulemap );
-    my $driver = $RT::DatabaseType;
-
-    my $CollectionBaseclass = 'RT::SearchBuilder';
-    my $RecordBaseclass     = 'RT::Record';
-    my $LicenseBlock = << '.';
-# BEGIN LICENSE BLOCK
-# 
-# END LICENSE BLOCK
-.
-    my $Attribution = << '.';
-# Autogenerated by Module::Intall::RTx::Factory
-# WARNING: THIS FILE IS AUTOGENERATED. ALL CHANGES TO THIS FILE WILL BE LOST.  
-# 
-# !! DO NOT EDIT THIS FILE !!
-#
-
-use strict;
-.
-    my $RecordInit = '';
-
-    @tables = map { do { {
-	my $table = $_;
-	$table =~ s/.*\.//g;
-	$table =~ s/\W//g;
-	$table =~ s/^\Q$name\E_//i or next;
-	$table ne 'sessions' or next;
-
-	$table = ucfirst(lc($table));
-	$table =~ s/$_/\u$_/ for qw(field group custom member value);
-	$table =~ s/(?<=Scrip)$_/\u$_/ for qw(action condition);
-	$table =~ s/$_/\U$_/ for qw(Acl);
-	$table = $name . '_' . $table;
-
-	$tablemap{$table}  = $table;
-	$modulemap{$table} = $table;
-	if ( $table =~ /^(.*)s$/ ) {
-	    $tablemap{$1}  = $table;
-	    $modulemap{$1} = $1;
-	}
-	$table;
-    } } } @tables;
-
-    $tablemap{'CreatedBy'} = 'User';
-    $tablemap{'UpdatedBy'} = 'User';
-
-    $typemap{'id'}            = 'ro';
-    $typemap{'Creator'}       = 'auto';
-    $typemap{'Created'}       = 'auto';
-    $typemap{'Updated'}       = 'auto';
-    $typemap{'UpdatedBy'}     = 'auto';
-    $typemap{'LastUpdated'}   = 'auto';
-    $typemap{'LastUpdatedBy'} = 'auto';
-
-    $typemap{lc($_)} = $typemap{$_} for keys %typemap;
-
-    foreach my $table (@tables) {
-	if ($drop) {
-	    $dbh->do("DROP TABLE $table");
-	    $dbh->do("DROP sequence ${table}_id_seq") if $driver eq 'Pg';
-	    $dbh->do("DROP sequence ${table}_seq") if $driver eq 'Oracle';
-	    next;
-	}
-
-	my $tablesingle = $table;
-	$tablesingle =~ s/^\Q$name\E_//i;
-	$tablesingle =~ s/s$//;
-	my $tableplural = $tablesingle . "s";
-
-	if ( $tablesingle eq 'ACL' ) {
-	    $tablesingle = "ACE";
-	    $tableplural = "ACL";
-	}
-
-	my %requirements;
-
-	my $CollectionClassName = $namespace . "::" . $tableplural;
-	my $RecordClassName     = $namespace . "::" . $tablesingle;
-
-	my $path = $namespace;
-	$path =~ s/::/\//g;
-
-	my $RecordClassPath     = $path . "/" . $tablesingle . ".pm";
-	my $CollectionClassPath = $path . "/" . $tableplural . ".pm";
-
-	#create a collection class
-	my $CreateInParams;
-	my $CreateOutParams;
-	my $ClassAccessible = "";
-	my $FieldsPod       = "";
-	my $CreatePod       = "";
-	my $CreateSub       = "";
-	my %fields;
-	my $sth = $dbh->prepare("DESCRIBE $table");
-
-	if ( $driver eq 'Pg' ) {
-	    $sth = $dbh->prepare(<<".");
-  SELECT a.attname, format_type(a.atttypid, a.atttypmod),
-         a.attnotnull, a.atthasdef, a.attnum
-    FROM pg_class c, pg_attribute a
-   WHERE c.relname ILIKE '$table'
-         AND a.attnum > 0
-         AND a.attrelid = c.oid
-ORDER BY a.attnum
-.
-	}
-	elsif ( $driver eq 'mysql' ) {
-	    $sth = $dbh->prepare("DESCRIBE $table");
-	}
-	else {
-	    die "$driver is currently unsupported";
-	}
-
-	$sth->execute;
-
-	while ( my $row = $sth->fetchrow_hashref() ) {
-	    my ( $field, $type, $default );
-	    if ( $driver eq 'Pg' ) {
-
-		$field   = $row->{'attname'};
-		$type    = $row->{'format_type'};
-		$default = $row->{'atthasdef'};
-
-		if ( $default != 0 ) {
-		    my $tth = $dbh->prepare(<<".");
-SELECT substring(d.adsrc for 128)
-  FROM pg_attrdef d, pg_class c
- WHERE c.relname = 'acct'
-       AND c.oid = d.adrelid
-       AND d.adnum = $row->{'attnum'}
-.
-		    $tth->execute();
-		    my @default = $tth->fetchrow_array;
-		    $default = $default[0];
-		}
-
-	    }
-	    elsif ( $driver eq 'mysql' ) {
-		$field   = $row->{'Field'};
-		$type    = $row->{'Type'};
-		$default = $row->{'Default'};
-	    }
-
-	    $fields{$field} = 1;
-
-	    #generate the 'accessible' datastructure
-
-	    if ( $typemap{$field} eq 'auto' ) {
-		$ClassAccessible .= "        $field => 
-		    {read => 1, auto => 1,";
-	    }
-	    elsif ( $typemap{$field} eq 'ro' ) {
-		$ClassAccessible .= "        $field =>
-		    {read => 1,";
-	    }
-	    else {
-		$ClassAccessible .= "        $field => 
-		    {read => 1, write => 1,";
-
-	    }
-
-	    $ClassAccessible .= " type => '$type', default => '$default'},\n";
-
-	    #generate pod for the accessible fields
-	    $FieldsPod .= $self->_pod(<<".");
-^head2 $field
-
-Returns the current value of $field. 
-(In the database, $field is stored as $type.)
-
-.
-
-	    unless ( $typemap{$field} eq 'auto' || $typemap{$field} eq 'ro' ) {
-		$FieldsPod .= $self->_pod(<<".");
-
-^head2 Set$field VALUE
-
-
-Set $field to VALUE. 
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, $field will be stored as a $type.)
-
-.
-	    }
-
-	    $FieldsPod .= $self->_pod(<<".");
-^cut
-
-.
-
-	    if ( $modulemap{$field} ) {
-		$FieldsPod .= $self->_pod(<<".");
-^head2 ${field}Obj
-
-Returns the $modulemap{$field} Object which has the id returned by $field
-
-
-^cut
-
-sub ${field}Obj {
-	my \$self = shift;
-	my \$$field =  ${namespace}::$modulemap{$field}->new(\$self->CurrentUser);
-	\$$field->Load(\$self->__Value('$field'));
-	return(\$$field);
-}
-.
-		$requirements{ $tablemap{$field} } =
-		"use ${namespace}::$modulemap{$field};";
-
-	    }
-
-	    unless ( $typemap{$field} eq 'auto' || $field eq 'id' ) {
-
-		#generate create statement
-		$CreateInParams .= "                $field => '$default',\n";
-		$CreateOutParams .=
-		"                         $field => \$args{'$field'},\n";
-
-		#gerenate pod for the create statement	
-		$CreatePod .= "  $type '$field'";
-		$CreatePod .= " defaults to '$default'" if ($default);
-		$CreatePod .= ".\n";
-
-	    }
-
-	}
-
-	$CreateSub = <<".";
-sub Create {
-    my \$self = shift;
-    my \%args = ( 
-$CreateInParams
-		\@_);
-    \$self->SUPER::Create(
-$CreateOutParams);
-
-}
-.
-	$CreatePod .= "\n=cut\n\n";
-
-	my $CollectionClass = $LicenseBlock . $Attribution . $self->_pod(<<".") . $self->_magic_import($CollectionClassName);
-
-^head1 NAME
-
-$CollectionClassName -- Class Description
-
-^head1 SYNOPSIS
-
-use $CollectionClassName
-
-^head1 DESCRIPTION
-
-
-^head1 METHODS
-
-^cut
-
-package $CollectionClassName;
-
-use $CollectionBaseclass;
-use $RecordClassName;
-
-use vars qw( \@ISA );
-\@ISA= qw($CollectionBaseclass);
-
-
-sub _Init {
-    my \$self = shift;
-    \$self->{'table'} = '$table';
-    \$self->{'primary_key'} = 'id';
-
-.
-
-    if ( $fields{'SortOrder'} ) {
-
-	$CollectionClass .= $self->_pod(<<".");
-
-# By default, order by name
-\$self->OrderBy( ALIAS => 'main',
-		FIELD => 'SortOrder',
-		ORDER => 'ASC');
-.
-    }
-    $CollectionClass .= $self->_pod(<<".");
-    return ( \$self->SUPER::_Init(\@_) );
-}
-
-
-^head2 NewItem
-
-Returns an empty new $RecordClassName item
-
-^cut
-
-sub NewItem {
-    my \$self = shift;
-    return($RecordClassName->new(\$self->CurrentUser));
-}
-.
-
-    my $RecordClassHeader = $Attribution . "
-
-^head1 NAME
-
-$RecordClassName
-
-
-^head1 SYNOPSIS
-
-^head1 DESCRIPTION
-
-^head1 METHODS
-
-^cut
-
-package $RecordClassName;
-use $RecordBaseclass; 
-";
-
-    foreach my $key ( keys %requirements ) {
-	$RecordClassHeader .= $requirements{$key} . "\n";
-    }
-    $RecordClassHeader .= <<".";
-
-use vars qw( \@ISA );
-\@ISA= qw( $RecordBaseclass );
-
-sub _Init {
-my \$self = shift; 
-
-\$self->Table('$table');
-\$self->SUPER::_Init(\@_);
-}
-
-.
-
-    my $RecordClass = $LicenseBlock . $RecordClassHeader . $self->_pod(<<".") . $self->_magic_import($RecordClassName);
-
-$RecordInit
-
-^head2 Create PARAMHASH
-
-Create takes a hash of values and creates a row in the database:
-
-$CreatePod
-
-$CreateSub
-
-$FieldsPod
-
-sub _CoreAccessible {
-    {
-    
-$ClassAccessible
-}
-};
-
-.
-
-	print "About to make $RecordClassPath, $CollectionClassPath\n";
-	`mkdir -p $path`;
-
-	open( RECORD, ">$RecordClassPath" );
-	print RECORD $RecordClass;
-	close(RECORD);
-
-	open( COL, ">$CollectionClassPath" );
-	print COL $CollectionClass;
-	close(COL);
-
-    }
-}
-
-sub _magic_import {
-    my $self = shift;
-    my $class = ref($self) || $self;
-
-    #if (exists \$warnings::{unimport})  {
-    #        no warnings qw(redefine);
-
-    my $path = $class;
-    $path =~ s#::#/#gi;
-
-
-    my $content = $self->_pod(<<".");
-        eval \"require ${class}_Overlay\";
-        if (\$@ && \$@ !~ qr{^Can't locate ${path}_Overlay.pm}) {
-            die \$@;
-        };
-
-        eval \"require ${class}_Vendor\";
-        if (\$@ && \$@ !~ qr{^Can't locate ${path}_Vendor.pm}) {
-            die \$@;
-        };
-
-        eval \"require ${class}_Local\";
-        if (\$@ && \$@ !~ qr{^Can't locate ${path}_Local.pm}) {
-            die \$@;
-        };
-
-
-
-
-^head1 SEE ALSO
-
-This class allows \"overlay\" methods to be placed
-into the following files _Overlay is for a System overlay by the original author,
-_Vendor is for 3rd-party vendor add-ons, while _Local is for site-local customizations.  
-
-These overlay files can contain new subs or subs to replace existing subs in this module.
-
-If you'll be working with perl 5.6.0 or greater, each of these files should begin with the line 
-
-   no warnings qw(redefine);
-
-so that perl does not kick and scream when you redefine a subroutine or variable in your overlay.
-
-${class}_Overlay, ${class}_Vendor, ${class}_Local
-
-^cut
-
-
 1;
-.
-
-    return $content;
-}
-
-sub _pod {
-    my ($self, $text) = @_;
-    $text =~ s/^\^/=/mg;
-    return $text;
-}

commit 2351ed1f30dfef2250767499808c67ee29d37952
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Thu Mar 28 13:20:23 2013 -0400

    Prep for 0.03 release

diff --git a/Changes b/Changes
index fdcb9de..9d85875 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,6 @@
 Revision history for RT-Extension-RepeatTicket
+        MM/DD/YYY
 
-0.01    Date/time
+0.03    03/28/2013
         First version, released on an unsuspecting world.
 
diff --git a/MANIFEST b/MANIFEST
index 8f776fe..08a8d53 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -15,6 +15,7 @@ inc/Module/Install/Fetch.pm
 inc/Module/Install/Include.pm
 inc/Module/Install/Makefile.pm
 inc/Module/Install/Metadata.pm
+inc/Module/Install/ReadmeFromPod.pm
 inc/Module/Install/RTx.pm
 inc/Module/Install/RTx/Factory.pm
 inc/Module/Install/Substitute.pm
@@ -26,7 +27,7 @@ Makefile.PL
 MANIFEST			This list of files
 MANIFEST.SKIP
 META.yml
-README.pod
+README
 t/daily.t
 t/end_conditions.t
 t/monthly.t
diff --git a/META.yml b/META.yml
index 8771042..7998b0f 100644
--- a/META.yml
+++ b/META.yml
@@ -24,4 +24,4 @@ requires:
   DateTime::Event::ICal: 0
 resources:
   license: http://opensource.org/licenses/gpl-license.php
-version: 0.02_02
+version: 0.03
diff --git a/lib/RT/Extension/RepeatTicket.pm b/lib/RT/Extension/RepeatTicket.pm
index 6d1d78e..74b0b77 100644
--- a/lib/RT/Extension/RepeatTicket.pm
+++ b/lib/RT/Extension/RepeatTicket.pm
@@ -3,7 +3,7 @@ use strict;
 
 package RT::Extension::RepeatTicket;
 
-our $VERSION = "0.02_02";
+our $VERSION = "0.03";
 
 use RT::Interface::Web;
 use DateTime;
@@ -757,10 +757,6 @@ __END__
 
 RT::Extension::RepeatTicket - Repeat tickets based on schedule
 
-=head1 VERSION
-
-Version 0.01
-
 =head1 INSTALLATION
 
 To install this module, run the following commands:
@@ -944,10 +940,11 @@ Return ( RT::Attribute, UPDATE MESSAGE )
 
 sunnavy, <sunnavy at bestpractical.com>
 
+Jim Brandt, <jbrandt at bestpractical.com>
 
 =head1 LICENSE AND COPYRIGHT
 
-Copyright 2012 Best Practical Solutions, LLC.
+Copyright 2013 Best Practical Solutions, LLC.
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of either: the GNU General Public License as published

commit 2e621bddf489b47e1f5cf8be35d0863ad4c842fb
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Thu Mar 28 13:42:56 2013 -0400

    Move tests to xt

diff --git a/t/daily.t b/xt/daily.t
similarity index 100%
rename from t/daily.t
rename to xt/daily.t
diff --git a/t/end_conditions.t b/xt/end_conditions.t
similarity index 100%
rename from t/end_conditions.t
rename to xt/end_conditions.t
diff --git a/t/monthly.t b/xt/monthly.t
similarity index 100%
rename from t/monthly.t
rename to xt/monthly.t
diff --git a/t/on_complete.t b/xt/on_complete.t
similarity index 100%
rename from t/on_complete.t
rename to xt/on_complete.t
diff --git a/t/start_date.t b/xt/start_date.t
similarity index 100%
rename from t/start_date.t
rename to xt/start_date.t
diff --git a/t/weekly.t b/xt/weekly.t
similarity index 100%
rename from t/weekly.t
rename to xt/weekly.t
diff --git a/t/yearly.t b/xt/yearly.t
similarity index 100%
rename from t/yearly.t
rename to xt/yearly.t

commit 7c28f7178c7a9d481400272f650f05b794387925
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Thu Mar 28 13:44:21 2013 -0400

    Update MANIFEST for test move

diff --git a/MANIFEST b/MANIFEST
index 08a8d53..fc7a81e 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -28,10 +28,10 @@ MANIFEST			This list of files
 MANIFEST.SKIP
 META.yml
 README
-t/daily.t
-t/end_conditions.t
-t/monthly.t
-t/on_complete.t
-t/start_date.t
-t/weekly.t
-t/yearly.t
+xt/daily.t
+xt/end_conditions.t
+xt/monthly.t
+xt/on_complete.t
+xt/start_date.t
+xt/weekly.t
+xt/yearly.t

commit 913e68ac8b630c71024b4e2db63734cdc99bc2b8
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Mon Jun 10 16:40:17 2013 -0400

    Move CF test from t to xt directory

diff --git a/t/cf.t b/xt/cf.t
similarity index 100%
rename from t/cf.t
rename to xt/cf.t

commit f72e1a7c0c5c936ad1522e0865b0d00ef1a05d8b
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Mon Jul 1 11:11:44 2013 -0400

    Update to subject format feature
    
    Make the subject format pull values from the repeated ticket
    rather than the original ticket so the values will be different
    for each repeated ticket.
    
    Include docs and tests.

diff --git a/README b/README
index a42627a..1dabf70 100644
--- a/README
+++ b/README
@@ -15,6 +15,7 @@ INSTALLATION
         Set( @Plugins, qw(... RT::Extension::RepeatTicket) );
         Set( $RepeatTicketCoexistentNumber, 1 ); # Optional
         Set( $RepeatTicketLeadTime, 14 ); # Optional
+        Set( $RepeatTicketSubjectFormat, '__Subject__' );
 
     add bin/rt-repeat-ticket to the daily cron job.
 
@@ -56,6 +57,27 @@ DESCRIPTION
     you can set this value on each ticket as well. The extension default is
     14 days.
 
+  $RepeatTicketSubjectFormat
+    By default, repeated tickets will have the same subject as the original
+    ticket. You can modify this subject by setting a format with the
+    $RepeatTicketSubjectFormat option. This option accepts formats in the
+    same form as formats for RT searches. The placeholders take values from
+    the repeated ticket, not the original ticket, so you can use the format
+    to help differentiate the subjects in repeated tickets.
+
+    For example, if you wanted to put the due date in the subject, you could
+    set the format to:
+
+        Set($RepeatTicketSubjectFormat, '__Due__ __Subject__');
+
+    You'll want to use values that you don't expect to change since the
+    subject won't change if the ticket value (e.g., Due) is changed.
+
+    Since this uses RT formats, you can create a custom format by creating a
+    new RT ColumnMap. You can see the available formats by looking at the
+    columns available in the Display Columns portlet on the RT ticket search
+    page.
+
   rt-repeat-ticket
     The rt-repeat-ticket utility evaluates all of your repeating tickets and
     creates any new tickets that are needed. With no parameters, it runs for
diff --git a/lib/RT/Extension/RepeatTicket.pm b/lib/RT/Extension/RepeatTicket.pm
index 74b0b77..e0c5dde 100644
--- a/lib/RT/Extension/RepeatTicket.pm
+++ b/lib/RT/Extension/RepeatTicket.pm
@@ -489,38 +489,7 @@ sub _RepeatTicket {
     };
 
     $repeat->{$_} = $repeat_ticket->$_()
-      for qw/Owner FinalPriority TimeEstimated/;
-
-    my $subject_format = RT->Config->Get('RepeatTicketSubjectFormat');
-    if ($subject_format) {
-        # append original subject if the new one doesn't include it.
-        if ( $subject_format !~ /__Subject__/ ) {
-            $subject_format .= ' __Subject__';
-        }
-
-        my $subject = $subject_format;
-        unless ( $mason ) {
-            require File::Temp;
-            my $data_dir = File::Temp::tempdir(CLEANUP => 1);
-            $mason = HTML::Mason::Interp->new(
-                RT::Interface::Web::Handler->DefaultHandlerArgs,
-                autohandler_name => '', # disable forced login and more
-                data_dir => $data_dir,
-            );
-            $mason->set_escape( h => \&RT::Interface::Web::EscapeUTF8 );
-            $mason->set_escape( u => \&RT::Interface::Web::EscapeURI  );
-        }
-        $subject =~ s!__(.*?)__!$mason->exec(
-            "/Elements/ColumnMap",
-            Class => 'RT__Ticket',
-            Name  => $1,
-            Attr  => 'value'
-        )->($repeat_ticket);!eg;
-        $repeat->{Subject} = $subject;
-    }
-    else {
-        $repeat->{Subject} = $repeat_ticket->Subject;
-    }
+      for qw/Owner FinalPriority TimeEstimated Subject/;
 
     my $members = $repeat_ticket->Members;
     my ( @members, @members_of, @refers, @refers_by, @depends, @depends_by );
@@ -589,7 +558,41 @@ sub _RepeatTicket {
         $top->ContentAsMIME( Children => 1 )->as_string );
 
     my $ticket = RT::Ticket->new( $repeat_ticket->CurrentUser );
-    return $ticket->Create(%args);
+    my ($new_id, $new_txn, $new_msg) = $ticket->Create(%args);
+
+    if ($new_id){
+        # Update subject if custom format defined
+        my $subject_format = RT->Config->Get('RepeatTicketSubjectFormat');
+        if ($subject_format) {
+            # append original subject if the new one doesn't include it.
+            if ( $subject_format !~ /__Subject__/ ) {
+                $subject_format .= ' __Subject__';
+            }
+
+            my $subject = $subject_format;
+            unless ( $mason ) {
+                require File::Temp;
+                require RT::Interface::Web::Handler;
+                my $data_dir = File::Temp::tempdir(CLEANUP => 1);
+                $mason = HTML::Mason::Interp->new(
+                    RT::Interface::Web::Handler->DefaultHandlerArgs,
+                    autohandler_name => '', # disable forced login and more
+                    data_dir => $data_dir,
+                );
+                $mason->set_escape( h => \&RT::Interface::Web::EscapeUTF8 );
+                $mason->set_escape( u => \&RT::Interface::Web::EscapeURI  );
+            }
+            $subject =~ s!__(.*?)__!$mason->exec(
+                                                 "/Elements/ColumnMap",
+                                                 Class => 'RT__Ticket',
+                                                 Name  => $1,
+                                                 Attr  => 'value'
+                                                )->($ticket);!eg;
+            $ticket->SetSubject($subject);
+        }
+    }
+
+    return ($new_id, $new_txn, $new_msg);
 }
 
 sub MaybeRepeatMore {
@@ -815,6 +818,28 @@ Monday.
 The value you set in RT_SiteConfig.pm becomes the system default, but you can
 set this value on each ticket as well. The extension default is 14 days.
 
+=head2 C<$RepeatTicketSubjectFormat>
+
+By default, repeated tickets will have the same subject as the original
+ticket. You can modify this subject by setting a format with the
+C<$RepeatTicketSubjectFormat> option. This option accepts formats in the
+same form as formats for RT searches. The placeholders take values from
+the repeated ticket, not the original ticket, so you can use the format
+to help differentiate the subjects in repeated tickets.
+
+For example, if you wanted to put the due date in the subject, you could
+set the format to:
+
+    Set($RepeatTicketSubjectFormat, '__Due__ __Subject__');
+
+You'll want to use values that you don't expect to change since the subject
+won't change if the ticket value (e.g., Due) is changed.
+
+Since this uses RT formats, you can create a custom format by creating
+a new RT ColumnMap. You can see the available formats by looking at
+the columns available in the Display Columns portlet on the RT ticket
+search page.
+
 =head2 rt-repeat-ticket
 
 The rt-repeat-ticket utility evaluates all of your repeating tickets and creates
diff --git a/xt/daily.t b/xt/daily.t
index acbd9d9..862cb71 100644
--- a/xt/daily.t
+++ b/xt/daily.t
@@ -1,7 +1,7 @@
 use strict;
 use warnings;
 
-use RT::Extension::RepeatTicket::Test tests => 43;
+use RT::Extension::RepeatTicket::Test tests => undef;
 
 use_ok('RT::Extension::RepeatTicket');
 require_ok('bin/rt-repeat-ticket');
@@ -35,6 +35,8 @@ my ( $baseurl, $m ) = RT::Test->started_ok();
     is($ticket2->StartsObj->ISO(Time => 0), $tomorrow->ymd, 'Starts tomorrow');
     $tomorrow->add( days => 14 );
     is( $ticket2->DueObj->ISO(Time => 0), $tomorrow->ymd, 'Due in default 14 days');
+    is( $ticket2->Subject(), 'Set up recurring aperture maintenance',
+        'Got default subject: ' . $ticket2->Subject());
 }
 
 {
@@ -109,3 +111,6 @@ sub run_tests{
 
     return $daily_id;
 }
+
+undef $m;
+done_testing;
diff --git a/xt/monthly.t b/xt/monthly.t
index bbd5c8c..2e746f3 100644
--- a/xt/monthly.t
+++ b/xt/monthly.t
@@ -1,11 +1,13 @@
 use strict;
 use warnings;
 
-use RT::Extension::RepeatTicket::Test tests => 20;
+use RT::Extension::RepeatTicket::Test tests => undef;
 
 use_ok('RT::Extension::RepeatTicket');
 require_ok('bin/rt-repeat-ticket');
 
+RT::Config->Set('RepeatTicketSubjectFormat', '__Due__ __Subject__');
+
 my ( $baseurl, $m ) = RT::Test->started_ok();
 
 ok( $m->login( 'root', 'password' ), 'logged in' );
@@ -58,3 +60,9 @@ $ticket2->Load($second);
 is($ticket2->StartsObj->ISO(Time => 0), $day->ymd, 'Starts 14 days before due: ' . $day->ymd);
 $day->add( days => 14 );
 is( $ticket2->DueObj->ISO(Time => 0), $day->ymd, 'Due on: ' . $day->ymd);
+
+is( $ticket2->Subject, $ticket2->DueAsString . ' Set up monthly aperture maintenance',
+    'Ticket subject matches subject configuration: ' . $ticket2->Subject);
+
+undef $m;
+done_testing;

commit 35d7e25ddfe5e9ecb4728aaa54af63a09b0d4091
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Mon Jul 1 11:20:49 2013 -0400

    Note required version of RT

diff --git a/Makefile.PL b/Makefile.PL
index 44a98da..3a74070 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -3,6 +3,7 @@ RTx('RT-Extension-RepeatTicket');
 all_from('lib/RT/Extension/RepeatTicket.pm');
 readme_from('lib/RT/Extension/RepeatTicket.pm');
 requires('DateTime::Event::ICal');
+requires_rt('4.0.6');
 
 my ($lp) = ($INC{'RT.pm'} =~ /^(.*)[\\\/]/);
 my $lib_path = join( ' ', "$RT::LocalPath/lib", $lp );
diff --git a/README b/README
index 1dabf70..41c4907 100644
--- a/README
+++ b/README
@@ -19,6 +19,10 @@ INSTALLATION
 
     add bin/rt-repeat-ticket to the daily cron job.
 
+    This extension is known to work on RT 4.0.6 and later. Makefile.PL will
+    warn you if your RT is older than this. It relies on jQuery, so it won't
+    work as-is on RT 3.8 or earlier.
+
 DESCRIPTION
     The RepeatTicket extension allows you to set up recurring tickets so new
     tickets are automatically created based on a schedule. The new tickets
diff --git a/lib/RT/Extension/RepeatTicket.pm b/lib/RT/Extension/RepeatTicket.pm
index e0c5dde..9d84181 100644
--- a/lib/RT/Extension/RepeatTicket.pm
+++ b/lib/RT/Extension/RepeatTicket.pm
@@ -778,6 +778,10 @@ add RT::Extension::RepeatTicket to @Plugins in RT's etc/RT_SiteConfig.pm:
 
 add bin/rt-repeat-ticket to the daily cron job.
 
+This extension is known to work on RT 4.0.6 and later. Makefile.PL will
+warn you if your RT is older than this. It relies on jQuery, so it
+won't work as-is on RT 3.8 or earlier.
+
 =head1 DESCRIPTION
 
 The RepeatTicket extension allows you to set up recurring tickets so

commit f2e8a1324afcd7e7897182c7c49d83cb9dfea536
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Mon Jul 1 11:34:20 2013 -0400

    Add Content to ticket create to squash warning

diff --git a/xt/cf.t b/xt/cf.t
index 40e6667..ea291f2 100644
--- a/xt/cf.t
+++ b/xt/cf.t
@@ -30,6 +30,7 @@ $m->submit_form_ok(
         form_name => 'TicketCreate',
         fields    => {
             'Subject' => 'test cf values',
+            'Content' => 'Testing CF values',
             'Object-RT::Ticket--CustomField-' . $cf->id . '-Value' => 'bar',
             'repeat-coexistent-number'                             => 2,
             'repeat-enabled'                                       => 1,

commit b064b829fdc4847c33145fc0db41950f49dddc80
Author: Jim Brandt <jbrandt at bestpractical.com>
Date:   Mon Jul 1 11:35:07 2013 -0400

    Prep for new version 0.04

diff --git a/Changes b/Changes
index 9d85875..f267b45 100644
--- a/Changes
+++ b/Changes
@@ -1,6 +1,12 @@
 Revision history for RT-Extension-RepeatTicket
         MM/DD/YYY
 
+0.04    07/01/2013
+        * Fixed bug with CFs on repeated tickets
+        * Added subject format config option
+        * Move tests to xt
+        * Add note on supported versions of RT
+
 0.03    03/28/2013
         First version, released on an unsuspecting world.
 
diff --git a/MANIFEST b/MANIFEST
index fc7a81e..3ad7f20 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -28,6 +28,7 @@ MANIFEST			This list of files
 MANIFEST.SKIP
 META.yml
 README
+xt/cf.t
 xt/daily.t
 xt/end_conditions.t
 xt/monthly.t
diff --git a/META.yml b/META.yml
index 7998b0f..e4407f9 100644
--- a/META.yml
+++ b/META.yml
@@ -19,9 +19,9 @@ no_index:
     - etc
     - html
     - inc
-    - t
+    - xt
 requires:
   DateTime::Event::ICal: 0
 resources:
   license: http://opensource.org/licenses/gpl-license.php
-version: 0.03
+version: 0.04
diff --git a/lib/RT/Extension/RepeatTicket.pm b/lib/RT/Extension/RepeatTicket.pm
index 9d84181..0351cb4 100644
--- a/lib/RT/Extension/RepeatTicket.pm
+++ b/lib/RT/Extension/RepeatTicket.pm
@@ -3,7 +3,7 @@ use strict;
 
 package RT::Extension::RepeatTicket;
 
-our $VERSION = "0.03";
+our $VERSION = "0.04";
 
 use RT::Interface::Web;
 use DateTime;

-----------------------------------------------------------------------



More information about the Bps-public-commit mailing list