[Rt-commit] r16471 - rt/branches/3.999-DANGEROUS/lib/RT

ruz at bestpractical.com ruz at bestpractical.com
Tue Oct 21 22:05:46 EDT 2008


Author: ruz
Date: Tue Oct 21 22:05:46 2008
New Revision: 16471

Added:
   rt/branches/3.999-DANGEROUS/lib/RT/StatusSchema.pm

Log:
* add RT::StatusSchema

Added: rt/branches/3.999-DANGEROUS/lib/RT/StatusSchema.pm
==============================================================================
--- (empty file)
+++ rt/branches/3.999-DANGEROUS/lib/RT/StatusSchema.pm	Tue Oct 21 22:05:46 2008
@@ -0,0 +1,433 @@
+
+use strict;
+use warnings;
+
+package RT::StatusSchema;
+
+our %STATUS_SCHEMAS;
+our %STATUS_SCHEMAS_CACHE;
+
+# cache structure:
+#    {
+#        '' => { # all valid statuses
+#            '' => [...],
+#            initial => [...],
+#            active => [...],
+#            inactive => [...],
+#        },
+#        schema_x => {
+#            '' => [...], # all valid in schema
+#            initial => [...],
+#            active => [...],
+#            inactive => [...],
+#            transitions => {
+#               status_x => [status_next1, status_next2,...],
+#            },
+#            actions => {
+#               'status_y -> status_y' => [ transition_label, transition_action ],
+#               ....
+#            }
+#        }
+#    }
+=head1 NAME
+
+RT::StatusSchema - class to access and manipulate status schemas
+
+=head1 DESCRIPTION
+
+Status schema is a list statuses tickets can have, splitted into three groups:
+initial, active and inactive. As well it defines possible transitions between
+statuses, for example from 'stalled' status of default schema you can change
+status to 'open' only.
+
+Also it is possible to define interface labels and actions user should do during
+a transition, for example open -> resolved transition labeled 'Stall' and action
+is comment. Action only defines what's showed for user, but is not required. User
+can leave comment box empty and change status anyway or can use ticket's basics
+interface to change status.
+
+=head1 METHODS
+
+=head2 new
+
+Simple constructor, takes no arguments.
+
+=cut
+
+sub new {
+    my $proto = shift;
+    my $self = bless {}, ref($proto) || $proto;
+
+    $self->fill_status_schemas_cache
+        unless keys %STATUS_SCHEMAS_CACHE;
+
+    return $self;
+}
+
+=head2 load
+
+Takes a name of the schema and loads it. If name is empty or undefined then
+loads the global schema with statuses from all named schemas.
+
+Can be called as class method, returns a new object, for example:
+
+    my $schema = RT::StatusSchema->load('default');
+
+=cut
+
+sub load {
+    my $self = shift;
+    my $name = shift || '';
+    return $self->new->load( $name, @_ )
+        unless ref $self;
+
+    return unless exists $STATUS_SCHEMAS_CACHE{ $name };
+
+    $self->{'name'} = $name;
+    $self->{'data'} = $STATUS_SCHEMAS_CACHE{ $name };
+
+    return $self;
+}
+
+=head2 list
+
+Returns sorted list of the schemas' names.
+
+=cut
+
+sub list {
+    my $self = shift;
+
+    $self->fill_status_schemas_cache
+        unless keys %STATUS_SCHEMAS_CACHE;
+
+    return sort grep length, keys %STATUS_SCHEMAS_CACHE;
+}
+
+=head2 name
+
+Returns name of the laoded schema.
+
+=cut
+
+sub name { return $_[0]->{'name'} }
+
+=head2 Getting statuses and validatiing.
+
+Methods to get statuses in different sets or validating them.
+
+=head3 valid
+
+Returns an array of all valid statuses for the current schema.
+Statuses are not sorted alphabetically, instead initial goes first,
+then active and then inactive.
+
+=cut
+
+sub valid {
+    my $self = shift;
+    my $type = shift;
+
+    return @{ $self->{'data'}{ $type || '' } || [] };
+}
+
+=head3 is_valid
+
+Takes a status and returns true if value is a valid status for the current
+schema. Otherwise, returns false.
+
+=cut
+
+sub is_valid {
+    my $self  = shift;
+    my $value = lc shift;
+    return scalar grep lc($_) eq $value, $self->valid;
+}
+
+=head3 initial
+
+Returns an array of all initial statuses for the current schema.
+
+=cut
+
+sub initial {
+    my $self = shift;
+    return $self->valid('initial');
+}
+
+=head3 is_initial
+
+Takes a status and returns true if value is a valid initial status.
+Otherwise, returns false.
+
+=cut
+
+sub is_initial {
+    my $self  = shift;
+    my $value = lc shift;
+    return scalar grep lc($_) eq $value, $self->valid('initial');
+}
+
+=head3 active
+
+Returns an array of all active statuses for this schema.
+
+=cut
+
+sub active {
+    my $self = shift;
+    return $self->valid('active');
+}
+
+=head3 is_active
+
+Takes a value and returns true if value is a valid active status.
+Otherwise, returns false.
+
+=cut
+
+sub is_active {
+    my $self  = shift;
+    my $value = lc shift;
+    return scalar grep lc($_) eq $value, $self->valid('active');
+}
+
+=head3 inactive
+
+Returns an array of all inactive statuses for this schema.
+
+=cut
+
+sub inactive {
+    my $self = shift;
+    return $self->valid('inactive');
+}
+
+=head3 is_inactive
+
+Takes a value and returns true if value is a valid inactive status.
+Otherwise, returns false.
+
+=cut
+
+sub is_inactive {
+    my $self  = shift;
+    my $value = lc shift;
+    return scalar grep lc($_) eq $value, $self->valid('inactive');
+}
+
+=head2 Transitions, labels and actions.
+
+=head3 transitions
+
+Takes status and returns list of statuses it can be changed to.
+
+If status is ommitted then returns a hash with all possible transitions
+in the following format:
+    
+    status_x => [ next_status, next_status, ... ],
+    status_y => [ next_status, next_status, ... ],
+
+=cut
+
+sub transitions {
+    my $self = shift;
+    my $status = shift;
+    if ( $status ) {
+        return @{ $self->{'data'}{'transitions'}{ $status } || [] };
+    } else {
+        return %{ $self->{'data'}{'transitions'} || {} };
+    }
+}
+
+=head3 transition_label
+
+Takes two statuses (from -> to) and returns label for the transition,
+if custom label is not defined then default equal to the second status.
+
+=cut
+
+sub transition_label {
+    my $self = shift;
+    my $from = shift;
+    my $to = shift;
+    return $self->{'data'}{'actions'}{ $from .' -> '. $to }[0] || $to;
+}
+
+=head3 transition_action
+
+Takes two statuses (from -> to) and returns action for the transition.
+
+At this moment it can be:
+
+=over 4
+
+=item '' (empty string) - no action (default)
+
+=item hide - hide this button from the Web UI
+
+=item comment - comment page is shown
+
+=item respond - reply page is shown
+
+=back
+
+=cut
+
+sub transition_action {
+    my $self = shift;
+    my $from = shift;
+    my $to = shift;
+    return $self->{'data'}{'actions'}{ $from .' -> '. $to }[1] || '';
+}
+
+=head2 Creation and manipulation
+
+=head3 create
+
+Creates a new status schema in the DB. Takes a param hash with
+'name', 'initial', 'active', 'inactive' and 'transitions' keys.
+
+All arguments except 'name' are optional and can be filled later
+with other methods.
+
+Returns (status, message) pair, status is false on error.
+
+=cut
+
+sub create {
+    my $self = shift;
+    my %args = (
+        name => undef,
+        initial => undef,
+        active => undef,
+        inactive => undef,
+        transitions => undef,
+        @_
+    );
+    @{ $self }{qw(name data)} = (undef, undef);
+
+    my $name = delete $args{'name'};
+    return (0, _('Invalid schema name'))
+        unless defined $name && length $name;
+    return (0, _('Already exist'))
+        if $STATUS_SCHEMAS_CACHE{ $name };
+
+    $STATUS_SCHEMAS{ $name } = \%args;
+    my ($status, $msg) = $RT::System->set_attribute(
+        name => 'StatusSchemas',
+        description => 'all system status schemas',
+        content => \%STATUS_SCHEMAS,
+    );
+    $self->fill_status_schemas_cache;
+    return ($status, _("Couldn't store schema")) unless $status;
+
+    $self->{'name'} = $name;
+    $self->{'data'} = $STATUS_SCHEMAS_CACHE{ $name };
+
+    return (1, _('Created a new status schema'));
+}
+
+sub set_statuses {
+    my $self = shift;
+    my %args = (
+        initial  => [],
+        active   => [],
+        inactive => [],
+        @_
+    );
+
+    my $name = $self->name or return (0, _("Status schema is not loaded"));
+    $STATUS_SCHEMAS{ $name }{ $_ } = $args{ $_ }
+        foreach qw(initial active inactive);
+
+    my ($status, $msg) = $RT::System->set_attribute(
+        name => 'StatusSchemas',
+        description => 'all system status schemas',
+        content => \%STATUS_SCHEMAS,
+    );
+    $self->fill_status_schemas_cache;
+    $self->load( $name );
+    return ($status, _("Couldn't store schema")) unless $status;
+
+    return (1, _('Updated schema'));
+}
+
+sub set_transitions {
+    my $self = shift;
+    my %args = (
+        @_
+    );
+
+    my $name = $self->name or return (0, _("Status schema is not loaded"));
+
+    $STATUS_SCHEMAS{ $name }{ 'transitions' } = \%args;
+
+    my ($status, $msg) = $RT::System->set_attribute(
+        name => 'StatusSchemas',
+        description => 'all system status schemas',
+        content => \%STATUS_SCHEMAS,
+    );
+    $self->fill_status_schemas_cache;
+    $self->load( $name );
+    return ($status, _("Couldn't store schema")) unless $status;
+
+    return (1, _('Updated schema with transitions data'));
+}
+
+sub set_actions {
+    my $self = shift;
+    my %args = (
+        @_
+    );
+
+    my $name = $self->name or return (0, _("Status schema is not loaded"));
+
+    $STATUS_SCHEMAS{ $name }{ 'actions' } = \%args;
+
+    my ($status, $msg) = $RT::System->set_attribute(
+        name => 'StatusSchemas',
+        description => 'all system status schemas',
+        content => \%STATUS_SCHEMAS,
+    );
+    $self->fill_status_schemas_cache;
+    $self->load( $name );
+    return ($status, _("Couldn't store schema")) unless $status;
+
+    return (1, _('Updated schema with actions data'));
+}
+
+sub fill_status_schemas_cache {
+    my $self = shift;
+    my $map = $RT::System->first_attribute('StatusSchemas')
+        or return;
+    $map = $map->content or return;
+
+    %STATUS_SCHEMAS_CACHE = %STATUS_SCHEMAS = %$map;
+    my %all = (
+        '' => [],
+        initial => [],
+        active => [],
+        inactive => [],
+    );
+    foreach my $schema ( values %STATUS_SCHEMAS_CACHE ) {
+        my @res;
+        foreach my $type ( qw(initial active inactive) ) {
+            push @{ $all{ $type } }, @{ $schema->{ $type } || [] };
+            push @res,                     @{ $schema->{ $type } || [] };
+        }
+
+        my %seen;
+        @res = grep !$seen{ lc $_ }++, @res;
+        $schema->{''} = \@res;
+    }
+    foreach my $type ( qw(initial active inactive), '' ) {
+        my %seen;
+        @{ $all{ $type } } = grep !$seen{ lc $_ }++, @{ $all{ $type } };
+        push @{ $all{''} }, @{ $all{ $type } } if $type;
+    }
+    $STATUS_SCHEMAS_CACHE{''} = \%all;
+    return;
+}
+
+1;


More information about the Rt-commit mailing list