+=head2 AddUpgradeHistory package, data
+
+Adds an entry to the upgrade history database. The package can be either C<RT>
+for core RT upgrades, or the fully qualified name of a plugin. The data must be
+a hash reference.
+
+=cut
+
+sub AddUpgradeHistory {
+ my $self = shift;
+ my $package = shift;
+ my $data = shift;
+
+ $data->{timestamp} ||= time;
+ $data->{rt_version} ||= $RT::VERSION;
+
+ my $upgrade_history_attr = $self->FirstAttribute('UpgradeHistory');
+ my $upgrade_history = $upgrade_history_attr ? $upgrade_history_attr->Content : {};
+
+ push @{ $upgrade_history->{$package} }, $data;
+
+ $self->SetAttribute(
+ Name => 'UpgradeHistory',
+ Content => $upgrade_history,
+ );
+}
+
+=head2 UpgradeHistory [package]
+
+Returns the entries of RT's upgrade history. If a package is specified, the list
+of upgrades for that package will be returned. Otherwise a hash reference of
+C<< package => [upgrades] >> will be returned.
+
+=cut
+
+sub UpgradeHistory {
+ my $self = shift;
+ my $package = shift;
+
+ my $upgrade_history_attr = $self->FirstAttribute('UpgradeHistory');
+ my $upgrade_history = $upgrade_history_attr ? $upgrade_history_attr->Content : {};
+
+ if ($package) {
+ return @{ $upgrade_history->{$package} || [] };
+ }
+
+ return $upgrade_history;
+}
+
+sub ParsedUpgradeHistory {
+ my $self = shift;
+ my $package = shift;
+
+ my $version_status = "Current version: ";
+ if ( $package eq 'RT' ){
+ $version_status .= $RT::VERSION;
+ } elsif ( grep {/$package/} @{RT->Config->Get('Plugins')} ) {
+ no strict 'refs';
+ $version_status .= ${ $package . '::VERSION' };
+ } else {
+ $version_status = "Not currently loaded";
+ }
+
+ my %ids;
+ my @lines;
+
+ my @events = $self->UpgradeHistory( $package );
+ for my $event (@events) {
+ if ($event->{stage} eq 'before' or (($event->{action}||'') eq 'insert' and not $event->{full_id})) {
+ if (not $event->{full_id}) {
+ # For upgrade done in the 4.1 series without GUIDs
+ if (($event->{type}||'') eq 'full upgrade') {
+ $event->{full_id} = $event->{individual_id} = Data::GUID->new->as_string;
+ } else {
+ $event->{individual_id} = Data::GUID->new->as_string;
+ $event->{full_id} = (@lines ? $lines[-1]{full_id} : Data::GUID->new->as_string);
+ }
+ $event->{return_value} = [1] if $event->{stage} eq 'after';
+ }
+ if ($ids{$event->{full_id}}) {
+ my $kids = $ids{$event->{full_id}}{sub_events} ||= [];
+ # Stitch non-"upgrade"s beneath the previous "upgrade"
+ if ( @{$kids} and $event->{action} ne 'upgrade' and $kids->[-1]{action} eq 'upgrade') {
+ push @{ $kids->[-1]{sub_events} }, $event;
+ } else {
+ push @{ $kids }, $event;
+ }
+ } else {
+ push @lines, $event;
+ }
+ $ids{$event->{individual_id}} = $event;
+ } elsif ($event->{stage} eq 'after') {
+ if (not $event->{individual_id}) {
+ if (($event->{type}||'') eq 'full upgrade') {
+ $lines[-1]{end} = $event->{timestamp} if @lines;
+ } elsif (($event->{type}||'') eq 'individual upgrade') {
+ $lines[-1]{sub_events}[-1]{end} = $event->{timestamp}
+ if @lines and @{ $lines[-1]{sub_events} };
+ }
+ } elsif ($ids{$event->{individual_id}}) {
+ my $end = $event;
+ $event = $ids{$event->{individual_id}};
+ $event->{end} = $end->{timestamp};
+
+ $end->{return_value} = [ split ', ', $end->{return_value}, 2 ]
+ if $end->{return_value} and not ref $end->{return_value};
+ $event->{return_value} = $end->{return_value};
+ $event->{content} ||= $end->{content};
+ }
+ }
+ }
+
+ return ($version_status, @lines);
+}
+
+