[Rt-commit] rt branch, 3.9-workflow, created. rt-3.8.8-309-g14d9502

Jesse Vincent jesse at bestpractical.com
Fri Aug 13 19:14:37 EDT 2010


The branch, 3.9-workflow has been created
        at  14d9502fe2f2d5a9ab588995a57424260701c437 (commit)

- Log -----------------------------------------------------------------
commit 0cce0f694adebb22e4b4059630aee0f1ea74ee84
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Thu Aug 12 17:41:47 2010 -0400

    Initial merge of StatusSchemas

diff --git a/lib/RT/Action/AutoOpen.pm b/lib/RT/Action/AutoOpen.pm
index 4112830..2d5c030 100755
--- a/lib/RT/Action/AutoOpen.pm
+++ b/lib/RT/Action/AutoOpen.pm
@@ -51,49 +51,72 @@ package RT::Action::AutoOpen;
 
 use strict;
 use warnings;
-
 use base qw(RT::Action);
 
 =head1 DESCRIPTION
 
-Opens a ticket unless it's allready open, but only unless transaction
-L<RT::Transaction/IsInbound is inbound>.
+AutoOpen in RT 3.8 sets status of a ticket to 'open' without considering
+custom statuses and possibility that there is no 'open' status in the system.
+This extension overrides behavior of the action.
+
+Status is not changed if there is no active statuses in the schema.
+
+Status is not changed if the current status is first active for ticket's status
+schema. For example if ticket's status is 'processing' and active statuses are
+'processing', 'on hold' and 'waiting' then status is not changed, but for ticket
+with status 'on hold' other rules are checked.
+
+Status is not changed if it's initial and creator of the current transaction
+is one of requestors.
+
+Status is not changed if message's head has field C<RT-Control> with C<no-autoopen>
+substring.
 
-Doesn't open a ticket if message's head has field C<RT-Control> with
-C<no-autoopen> substring.
+Status is set to the first possible active status. It means that if ticket's
+status is X then RT finds all possible transitions from this status and selects
+first active status in the list.
 
 =cut
 
 sub Prepare {
     my $self = shift;
 
-    # if the ticket is already open or the ticket is new and the message is more mail from the
-    # requestor, don't reopen it.
+    my $ticket = $self->TicketObj;
+    my $schema = $ticket->QueueObj->status_schema;
+    my $status = $ticket->Status;
 
-    my $status = $self->TicketObj->Status;
-    return undef if $status eq 'open';
-    return undef if $status eq 'new' && $self->TransactionObj->IsInbound;
+    my @active = $schema->active;
+    # no change if no active statuses in the schema
+    return 1 unless @active;
+
+    # no change if the ticket is already has first status from the list of active
+    return 1 if lc $status eq lc $active[0];
+
+    # no change if the ticket is in initial status and the message is a mail
+    # from a requestor
+    return 1 if $schema->is_initial($status) && $self->TransactionObj->IsInbound;
 
     if ( my $msg = $self->TransactionObj->Message->First ) {
-        return undef if ($msg->GetHeader('RT-Control') || '') =~ /\bno-autoopen\b/i;
+        return 1 if ($msg->GetHeader('RT-Control') || '') =~ /\bno-autoopen\b/i;
     }
 
+    my ($next) = grep $schema->is_active($_), $schema->transitions($status);
+
+    $self->{'set_status_to'} = $next;
+
     return 1;
 }
 
 sub Commit {
     my $self = shift;
 
-    my $oldstatus = $self->TicketObj->Status;
-    $self->TicketObj->__Set( Field => 'Status', Value => 'open' );
-    $self->TicketObj->_NewTransaction(
-        Type     => 'Status',
-        Field    => 'Status',
-        OldValue => $oldstatus,
-        NewValue => 'open',
-        Data     => 'Ticket auto-opened on incoming correspondence'
-    );
+    return 1 unless my $new_status = $self->{'set_status_to'};
 
+    my ($val, $msg) = $self->TicketObj->SetStatus( $new_status );
+    unless ( $val ) {
+        $RT::Logger->error( "Couldn't auto-open ticket: ". $msg );
+        return 0;
+    }
     return 1;
 }
 
diff --git a/lib/RT/Action/SetStatus.pm b/lib/RT/Action/SetStatus.pm
new file mode 100755
index 0000000..c4c58f1
--- /dev/null
+++ b/lib/RT/Action/SetStatus.pm
@@ -0,0 +1,151 @@
+# BEGIN BPS TAGGED BLOCK {{{
+# 
+# COPYRIGHT:
+# 
+# This software is Copyright (c) 1996-2008 Best Practical Solutions, LLC
+#                                          <jesse 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 }}}
+package RT::Action::SetStatus;
+
+use strict;
+use warnings;
+use base qw(RT::Action);
+
+=head1 NAME
+
+RT::Action::SetStatus - RT's scrip action to set status of a ticket
+
+=head1 DESCRIPTION
+
+This action changes status to a new value according to L</ARGUMENT>.
+Status is not changed if transition is invalid or on other errors. All
+issues are logged with different level.
+
+=head1 ARGUMENT
+
+Argument can be one of the following:
+
+=over 4
+
+=item status literally
+
+Status is changed from the current value to a new defined by the argument,
+but only if it's valid status and allowed by transitions of the current schema,
+for example:
+
+    * The current status is 'stalled'
+    * Argument of this action is 'open'
+    * The only possible transition in the scheam from 'stalled' is 'open'
+    * Status is changed
+
+However, in the example above Status is not changed if argument is anything
+else as it's just not allowed by the schema.
+
+=item 'initial', 'active' or 'inactive'
+
+Status is changed from the current value to first possible 'initial',
+'active' or 'inactive' correspondingly. First possible value is figured
+according to transitions to the target set, for example:
+
+    * The current status is 'open'
+    * Argument of this action is 'inactive'
+    * Possible transitions from 'open' are 'resolved', 'rejected' or 'deleted'
+    * Status is changed to 'resolved'
+
+=back
+
+=cut
+
+sub Prepare {
+    my $self = shift;
+
+    my $ticket = $self->TicketObj;
+    my $schema = $ticket->QueueObj->status_schema;
+    my $status = $ticket->Status;
+
+    my $argument = $self->Argument;
+    unless ( $argument ) {
+        $RT::Logger->error("Argument is mandatory for SetStatus action");
+        return 0;
+    }
+
+    my $next = '';
+    if ( $argument =~ /^(initial|active|inactive)$/i ) {
+        my $method = 'is_'. lc $argument;
+        ($next) = grep $schema->$method($_), $schema->transitions($status);
+        unless ( $next ) {
+            $RT::Logger->info("No transition from '$status' to $argument set");
+            return 1;
+        }
+    }
+    elsif ( $schema->is_valid( $argument ) ) {
+        unless ( $schema->is_transition( $status => $argument ) ) {
+            $RT::Logger->warning("Transition '$status -> $argument' is not valid");
+            return 1;
+        }
+        $next = $argument;
+    }
+    else {
+        $RT::Logger->error("Argument for SetStatus action is not valid status or one of set");
+        return 0;
+    }
+
+    $self->{'set_status_to'} = $next;
+
+    return 1;
+}
+
+sub Commit {
+    my $self = shift;
+
+    return 1 unless my $new_status = $self->{'set_status_to'};
+
+    my ($val, $msg) = $self->TicketObj->SetStatus( $new_status );
+    unless ( $val ) {
+        $RT::Logger->error( "Couldn't set status: ". $msg );
+        return 0;
+    }
+    return 1;
+}
+
+1;
diff --git a/lib/RT/Condition/StatusChange.pm b/lib/RT/Condition/StatusChange.pm
index 0bdc69a..0dc1d37 100755
--- a/lib/RT/Condition/StatusChange.pm
+++ b/lib/RT/Condition/StatusChange.pm
@@ -50,23 +50,90 @@ package RT::Condition::StatusChange;
 use base 'RT::Condition';
 use strict;
 
+=head2 DESCRIPTION
 
-=head2 IsApplicable
+Condition check passes if the current transaction is a status change.
 
-If the argument passed in is equivalent to the new value of
-the Status Obj
+Argument can be used to apply additional conditions on old and new values.
+
+If argument is empty then check pass for any change of the status field.
+
+If argument is equal to new value then check is passed. This is behavior
+is close to RT 3.8 and older. For example argumen equal 'resolved' means
+'fire scrip when status changed from any to resolved'.
+
+The following extended format is supported:
+
+    old: comma separated list; new: comma separated list
+
+For example:
+
+    old: open; new: resolved
+
+You can omit old or new part, for example:
+
+    old: open
+
+    new: resolved
+
+You can specify multiple values, for example:
+
+    old: new, open; new: resolved, rejected
+
+Status sets ('initial', 'active' or 'inactive') can be used, for example:
+
+    old: active; new: inactive
+
+    old: initial, active; new: resolved
 
 =cut
 
 sub IsApplicable {
     my $self = shift;
-    if (($self->TransactionObj->Field eq 'Status') and 
-    ($self->Argument eq $self->TransactionObj->NewValue())) {
-	return(1);
-    } 
+    my $txn = $self->TransactionObj;
+    my ($type, $field) = ($txn->Type, $txn->Field);
+    return 0 unless $type eq 'Status' || ($type eq 'Set' && $field eq 'Status');
+
+    my $argument = $self->Argument;
+    return 1 unless $argument;
+
+    my $new = $txn->NewValue || '';
+    return 1 if $argument eq $new;
+
+    # let's parse argument
+    my ($old_must_be, $new_must_be) = ('', '');
+    if ( $argument =~ /^\s*old:\s*(.*);\s*new:\s*(.*)\s*$/i ) {
+        ($old_must_be, $new_must_be) = ($1, $2);
+    }
+    elsif ( $argument =~ /^\s*new:\s*(.*)\s*$/i ) {
+        $new_must_be = $1;
+    }
+    elsif ( $argument =~ /^\s*old:\s*(.*)\s*$/i ) {
+        $old_must_be = $1;
+    }
     else {
-	return(undef);
+        $RT::Logger->error("Argument '$argument' is incorrect.")
+            unless RT::StatusSchema->load('')->is_valid( $argument );
+        return 0;
+    }
+
+    my $schema = $self->TicketObj->QueueObj->status_schema;
+    if ( $new_must_be ) {
+        return 0 unless grep lc($new) eq lc($_),
+            map {m/^(initial|active|inactive)$/i? $schema->valid(lc $_): $_ }
+            grep defined && length,
+            map { s/^\s+//; s/\s+$//; $_ }
+            split /,/, $new_must_be;
+    }
+    if ( $old_must_be ) {
+        my $old = lc($txn->OldValue || '');
+        return 0 unless grep $old eq lc($_),
+            map {m/^(initial|active|inactive)$/i? $schema->valid(lc $_): $_ }
+            grep defined && length,
+            map { s/^\s+//; s/\s+$//; $_ }
+            split /,/, $old_must_be;
     }
+    return 1;
 }
 
 eval "require RT::Condition::StatusChange_Vendor";
diff --git a/lib/RT/Queue_Overlay.pm b/lib/RT/Queue_Overlay.pm
index 3eedb3c..cb97479 100755
--- a/lib/RT/Queue_Overlay.pm
+++ b/lib/RT/Queue_Overlay.pm
@@ -188,6 +188,20 @@ sub AvailableRights {
 
 # {{{ ActiveStatusArray
 
+sub status_schema {
+    my $self = shift;
+    require RT::StatusSchema;
+    return RT::StatusSchema->load('') unless ref $self && $self->id;
+
+    # If you don't have StatusSchemas set, name is default
+    my $schemas = RT->Config->Get('StatusSchemas');
+    my $name = $schemas ? ($schemas->{ $self->Name } || 'default') : 'default';
+
+    my $res = RT::StatusSchema->load( $name );
+    $RT::Logger->error("Status schema '$name' for queue '".$self->Name."' doesn't exist") unless $res;
+    return $res;
+}
+
 =head2 ActiveStatusArray
 
 Returns an array of all ActiveStatuses for this queue
@@ -196,18 +210,9 @@ Returns an array of all ActiveStatuses for this queue
 
 sub ActiveStatusArray {
     my $self = shift;
-    if (RT->Config->Get('ActiveStatus')) {
-    	return (RT->Config->Get('ActiveStatus'))
-    } else {
-        $RT::Logger->warning("RT::ActiveStatus undefined, falling back to deprecated defaults");
-        return (@DEFAULT_ACTIVE_STATUS);
-    }
+    return $self->status_schema->valid('initial', 'active');
 }
 
-# }}}
-
-# {{{ InactiveStatusArray
-
 =head2 InactiveStatusArray
 
 Returns an array of all InactiveStatuses for this queue
@@ -216,18 +221,9 @@ Returns an array of all InactiveStatuses for this queue
 
 sub InactiveStatusArray {
     my $self = shift;
-    if (RT->Config->Get('InactiveStatus')) {
-    	return (RT->Config->Get('InactiveStatus'))
-    } else {
-        $RT::Logger->warning("RT::InactiveStatus undefined, falling back to deprecated defaults");
-        return (@DEFAULT_INACTIVE_STATUS);
-    }
+    return $self->status_schema->inactive;
 }
 
-# }}}
-
-# {{{ StatusArray
-
 =head2 StatusArray
 
 Returns an array of all statuses for this queue
@@ -236,71 +232,45 @@ Returns an array of all statuses for this queue
 
 sub StatusArray {
     my $self = shift;
-    return ($self->ActiveStatusArray(), $self->InactiveStatusArray());
+    return $self->status_schema->valid( @_ );
 }
 
-# }}}
-
-# {{{ IsValidStatus
-
-=head2 IsValidStatus VALUE
-
-Returns true if VALUE is a valid status.  Otherwise, returns 0.
+=head2 IsValidStatus value
 
+Returns true if value is a valid status.  Otherwise, returns 0.
 
 =cut
 
 sub IsValidStatus {
     my $self  = shift;
-    my $value = shift;
-
-    my $retval = grep ( $_ eq $value, $self->StatusArray );
-    return ($retval);
-
+    return $self->status_schema->is_valid( shift );
 }
 
-# }}}
-
-# {{{ IsActiveStatus
-
-=head2 IsActiveStatus VALUE
-
-Returns true if VALUE is a Active status.  Otherwise, returns 0
+=head2 IsActiveStatus value
 
+Returns true if value is a Active status.  Otherwise, returns 0
 
 =cut
 
 sub IsActiveStatus {
     my $self  = shift;
-    my $value = shift;
-
-    my $retval = grep ( $_ eq $value, $self->ActiveStatusArray );
-    return ($retval);
-
+    return $self->status_schema->is_valid( shift, 'initial', 'active');
 }
 
-# }}}
 
-# {{{ IsInactiveStatus
 
-=head2 IsInactiveStatus VALUE
+=head2 IsInactiveStatus value
 
-Returns true if VALUE is a Inactive status.  Otherwise, returns 0
+Returns true if value is a Inactive status.  Otherwise, returns 0
 
 
 =cut
 
 sub IsInactiveStatus {
     my $self  = shift;
-    my $value = shift;
-
-    my $retval = grep ( $_ eq $value, $self->InactiveStatusArray );
-    return ($retval);
-
+    return $self->status_schema->is_inactive( shift );
 }
 
-# }}}
-
 
 # {{{ sub Create
 
diff --git a/lib/RT/StatusSchema.pm b/lib/RT/StatusSchema.pm
new file mode 100644
index 0000000..88d00ed
--- /dev/null
+++ b/lib/RT/StatusSchema.pm
@@ -0,0 +1,656 @@
+
+use strict;
+use warnings;
+
+package RT::StatusSchema;
+
+sub loc { return $RT::SystemUser->loc( @_ ) }
+
+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,...],
+#            },
+#            rights => {
+#               'status_y -> status_y' => 'right',
+#               ....
+#            }
+#            actions => {
+#               'status_y -> status_y' => [ transition_label, transition_action ],
+#               ....
+#            }
+#        }
+#    }
+
+=head1 NAME
+
+RT::StatusSchema - class to access and manipulate status schemas
+
+=head1 DESCRIPTION
+
+A status schema is a list of statuses that a ticket can have. There are three
+groups of statuses: initial, active and inactive. A status schema also defines
+possible transitions between statuses. For example, in the 'default' schema,
+you may only change status from 'stalled' to 'open'.
+
+It is also possible to define user-interface labels and the action a user
+should perform during a transition. For example, the "open -> stalled"
+transition would have a 'Stall' label and the action would be Comment. The
+action only defines what form is showed to the user, but actually performing
+the action is not required. The user can leave the comment box empty yet still
+Stall a ticket. Finally, the user can also just use the Basics or Jumbo form to
+change the status with the usual dropdown.
+
+=head1 METHODS
+
+=head2 new
+
+Simple constructor, takes no arguments.
+
+=cut
+
+sub new {
+    my $proto = shift;
+    my $self = bless {}, ref($proto) || $proto;
+
+    $self->fill_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_cache unless keys %STATUS_SCHEMAS_CACHE;
+
+    return sort grep length && $_ ne '__maps__', 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.
+
+Takes optional list of status types, from 'initial', 'active' or
+'inactive'. For example:
+
+    $schema->valid('initial', 'active');
+
+=cut
+
+sub valid {
+    my $self = shift;
+    my @types = @_;
+    unless ( @types ) {
+        return @{ $self->{'data'}{''} || [] };
+    }
+    
+    my @res;
+    push @res, @{ $self->{'data'}{ $_ } || [] } foreach @types;
+    return @res;
+}
+
+=head3 is_valid
+
+Takes a status and returns true if value is a valid status for the current
+schema. Otherwise, returns false.
+
+Takes optional list of status types after the status, so it's possible check
+validity in particular sets, for example:
+
+    # returns true if status is valid and from initial or active set
+    $schema->is_valid('some_status', 'initial', 'active');
+
+See also </valid>.
+
+=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, rights, 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'} || {} };
+    }
+}
+
+=head1 is_transition
+
+Takes two statuses (from -> to) and returns true if it's valid
+transition and false otherwise.
+
+=cut
+
+sub is_transition {
+    my $self = shift;
+    my $from = shift or return 0;
+    my $to   = shift or return 0;
+    return scalar grep lc($_) eq lc($to), $self->transitions($from);
+}
+
+=head3 check_right
+
+Takes two statuses (from -> to) and returns the right that should
+be checked on the ticket.
+
+=cut
+
+sub check_right {
+    my $self = shift;
+    my $from = shift;
+    my $to = shift;
+    if ( my $rights = $self->{'data'}{'rights'} ) {
+        my $check =
+            $rights->{ $from .' -> '. $to }
+            || $rights->{ '* -> '. $to }
+            || $rights->{ $from .' -> *' }
+            || $rights->{ '* -> *' };
+        return $check if $check;
+    }
+    return $to eq 'deleted' ? 'DeleteTicket' : 'ModifyTicket';
+}
+
+sub register_rights {
+    my $self = shift;
+
+    $self->fill_cache unless keys %STATUS_SCHEMAS_CACHE;
+
+    my %tmp;
+    foreach my $schema ( values %STATUS_SCHEMAS_CACHE ) {
+        next unless exists $schema->{'rights'};
+        while ( my ($transition, $right) = each %{ $schema->{'rights'} } ) {
+            push @{ $tmp{ $right } ||=[] }, $transition;
+        }
+    }
+
+    require RT::ACE;
+    require RT::Queue;
+    my $RIGHTS = $RT::Queue::RIGHTS;
+    while ( my ($right, $transitions) = each %tmp ) {
+        next if exists $RIGHTS->{ $right };
+
+        my (@from, @to);
+        foreach ( @$transitions ) {
+            ($from[@from], $to[@to]) = split / -> /, $_;
+        }
+        my $description = 'Change status'
+            . ( (grep $_ eq '*', @from)? '' : ' from '. join ', ', @from )
+            . ( (grep $_ eq '*', @to  )? '' : ' to '. join ', ', @from );
+
+        $RIGHTS->{ $right } = $description;
+        $RT::ACE::LOWERCASERIGHTNAMES{ lc $right } = $right;
+    }
+}
+
+=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,
+        actions => undef,
+        @_
+    );
+    @{ $self }{qw(name data)} = (undef, undef);
+
+    my $name = delete $args{'name'};
+    return (0, loc('Invalid schema name'))
+        unless defined $name && length $name;
+    return (0, loc('Already exist'))
+        if $STATUS_SCHEMAS_CACHE{ $name };
+
+    foreach my $method (qw(_set_statuses _set_transitions _set_actions)) {
+        my ($status, $msg) = $self->$method( %args, name => $name );
+        return ($status, $msg) unless $status;
+    }
+
+    my ($status, $msg) = $self->_store_schemas( $name );
+    return ($status, $msg) unless $status;
+
+    return (1, loc('Created a new status schema'));
+}
+
+sub set_statuses {
+    my $self = shift;
+    my %args = (
+        initial  => [],
+        active   => [],
+        inactive => [],
+        @_
+    );
+
+    my $name = $self->name or return (0, loc("Status schema is not loaded"));
+
+    my ($status, $msg) = $self->_set_statuses( %args, name => $name );
+    return ($status, $msg) unless $status;
+
+    ($status, $msg) = $self->_store_schemas( $name );
+    return ($status, $msg) unless $status;
+
+    return (1, loc('Updated schema'));
+}
+
+sub set_transitions {
+    my $self = shift;
+    my %args = @_;
+
+    my $name = $self->name or return (0, loc("Status schema is not loaded"));
+
+    my ($status, $msg) = $self->_set_transitions(
+        transitions => \%args, name => $name
+    );
+    return ($status, $msg) unless $status;
+
+    ($status, $msg) = $self->_store_schemas( $name );
+    return ($status, $msg) unless $status;
+
+    return (1, loc('Updated schema with transitions data'));
+}
+
+sub set_actions {
+    my $self = shift;
+    my %args = @_;
+
+    my $name = $self->name or return (0, loc("Status schema is not loaded"));
+
+    my ($status, $msg) = $self->_set_actions(
+        actions => \%args, name => $name
+    );
+    return ($status, $msg) unless $status;
+
+    ($status, $msg) = $self->_store_schemas( $name );
+    return ($status, $msg) unless $status;
+
+    return (1, loc('Updated schema with actions data'));
+}
+
+sub fill_cache {
+    my $self = shift;
+
+    my $map = RT->Config->Get('StatusSchemaMeta') or return;
+#    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;
+}
+
+sub for_localization {
+    my $self = shift;
+    $self->fill_cache unless keys %STATUS_SCHEMAS_CACHE;
+
+    my @res = ();
+
+    push @res, @{ $STATUS_SCHEMAS_CACHE{''}{''} || [] };
+    foreach my $schema ( values %STATUS_SCHEMAS ) {
+        push @res,
+            grep defined && length,
+            map $_->[0],
+            grep ref($_),
+            values %{ $schema->{'actions'} || {} };
+    }
+
+    my %seen;
+    return grep !$seen{lc $_}++, @res;
+}
+
+sub _store_schemas {
+    my $self = shift;
+    my $name = shift;
+    my ($status, $msg) = $RT::System->set_attribute(
+        name => 'StatusSchemas',
+        description => 'all system status schemas',
+        content => \%STATUS_SCHEMAS,
+    );
+    $self->fill_cache;
+    $self->load( $name );
+    return ($status, loc("Couldn't store schema")) unless $status;
+    return 1;
+}
+
+sub _set_statuses {
+    my $self = shift;
+    my %args = @_;
+
+    my @all;
+    my %tmp = (
+        initial  => [],
+        active   => [],
+        inactive => [],
+    );
+    foreach my $type ( qw(initial active inactive) ) {
+        foreach my $status ( grep defined && length, @{ $args{ $type } || [] } ) {
+            return (0, loc('Status should contain ASCII characters only. Translate via po files.'))
+                unless $status =~ /^[a-zA-Z0-9.,! ]+$/;
+            return (0, loc('Statuses must be unique in one schema'))
+                if grep lc($_) eq lc($status), @all;
+            push @all, $status;
+            push @{ $tmp{ $type } }, $status;
+        }
+    }
+
+    $STATUS_SCHEMAS{ $args{'name'} }{ $_ } = $tmp{ $_ }
+        foreach qw(initial active inactive);
+
+    return 1;
+}
+
+sub _set_transitions {
+    my $self = shift;
+    my %args = @_;
+
+    # XXX, TODO: more tests on data
+    $STATUS_SCHEMAS{ $args{'name'} }{'transitions'} = $args{'transitions'};
+    return 1;
+}
+
+sub _set_actions {
+    my $self = shift;
+    my %args = @_;
+
+    # XXX, TODO: more tests on data
+    $STATUS_SCHEMAS{ $args{'name'} }{'actions'} = $args{'actions'};
+    return 1;
+}
+
+sub from_set {
+    my $self = shift;
+    my $status = shift;
+    foreach my $set ( qw(initial active inactive) ) {
+        return $set if $self->is_valid( $status, $set );
+    }
+    return '';
+}
+
+sub map {
+    my $from = shift;
+    my $to = shift;
+    $to = RT::StatusSchema->load( $to ) unless ref $to;
+    return $STATUS_SCHEMAS{'__maps__'}{ $from->name .' -> '. $to->name } || {};
+}
+
+sub set_map {
+    my $self = shift;
+    my $to = shift;
+    $to = RT::StatusSchema->load( $to ) unless ref $to;
+    my %map = @_;
+    $map{ lc $_ } = delete $map{ $_ } foreach keys %map;
+
+    return (0, loc("Status schema is not loaded"))
+        unless $self->name;
+
+    return (0, loc("Status schema is not loaded"))
+        unless $to->name;
+
+
+    $STATUS_SCHEMAS{'__maps__'}{ $self->name .' -> '. $to->name } = \%map;
+
+    my ($status, $msg) = $self->_store_schemas( $self->name );
+    return ($status, $msg) unless $status;
+
+    return (1, loc('Updated schema with actions data'));
+}
+
+sub has_map {
+    my $self = shift;
+    my $map = $self->map( @_ );
+    return 0 unless $map && keys %$map;
+    return 0 unless grep defined && length, values %$map;
+    return 1;
+}
+
+sub no_maps {
+    my $self = shift;
+    my @list = $self->list;
+    my @res;
+    foreach my $from ( @list ) {
+        foreach my $to ( @list ) {
+            next if $from eq $to;
+            push @res, $from, $to
+                unless RT::StatusSchema->load( $from )->has_map( $to );
+        }
+    }
+    return @res;
+}
+
+sub queues {
+    my $self = shift;
+    require RT::Queues;
+    my $queues = RT::Queues->new( $RT::SystemUser );
+    $queues->limit( column => 'status_schema', value => $self->name );
+    return $queues;
+}
+
+1;
diff --git a/lib/RT/StatusSchemas.pm b/lib/RT/StatusSchemas.pm
new file mode 100644
index 0000000..ba718bc
--- /dev/null
+++ b/lib/RT/StatusSchemas.pm
@@ -0,0 +1,301 @@
+use strict;
+use warnings;
+no warnings 'redefine';
+
+package RT::StatusSchemas;
+
+our $VERSION = '0.02';
+
+=head1 NAME
+
+RT::Estension::StatusSchemas - define different set of statuses for queues in RT
+
+=head1 SYNOPSIS
+
+    # schema close to RT's default, but with some nice
+    # additions
+    Set( %StatusSchemaMeta,
+        default => {
+            initial  => ['new'],
+            active   => ['open', 'stalled'],
+            inactive => ['resolved', 'rejected', 'deleted'],
+
+            transitions => {
+                # from   => [ to list ],
+                new      => [qw(open resolved rejected deleted)],
+                open     => [qw(stalled resolved rejected deleted)],
+                stalled  => [qw(open)],
+                resolved => [qw(open)],
+                rejected => [qw(open)],
+                deleted  => [qw(open)],
+            },
+            rights  => {
+                '* -> deleted'  => 'DeleteTicket',
+                '* -> rejected' => 'RejectTicket',
+                '* -> *'        => 'ModifyTicketStatus',
+            },
+            actions => {
+                # 'from -> to'    => [action text, Respond/Comment/hide/''],
+                'new -> open'     => ['Open It', 'Respond'],
+                'new -> resolved' => ['Resolve', 'Comment'],
+                'new -> rejected' => ['Reject',  'Respond'],
+                'new -> deleted'  => ['Delete',  ''],
+
+                'open -> stalled'  => ['Stall',   'Comment'],
+                'open -> resolved' => ['Resolve', 'Comment'],
+                'open -> rejected' => ['Reject',  'Respond'],
+                'open -> deleted'  => ['Delete',  'hide'],
+
+                'stalled -> open'  => ['Open It',  ''],
+                'resolved -> open' => ['Re-open',  'Comment'],
+                'rejected -> open' => ['Re-open',  'Comment'],
+                'deleted -> open'  => ['Undelete', ''],
+            },
+        },
+    );
+
+=head1 DESCRIPTION
+
+By default RT has one set of statuses for all queues with this extension you can
+define multiple schemas with different statuses and other interesting things.
+
+=head1 CONFIGURATION
+
+=head2 Basics
+
+This extension is configured in the RT config file using several options:
+
+=over 4
+
+=item %StatusSchemas - use to define status schemas for queues, if there is no
+record for some queue then 'default' is used. For example:
+
+    Set( %StatusSchemas,
+        General => 'some_schema',
+        'Another Queue' => 'another schema',
+    );
+
+=item %StatusSchemaMeta - use to describe status schemas. Below you can read more
+about format of this option, but here is basic format:
+
+    Set( %StatusSchemaMeta,
+        default => {
+            ... description of default schema ...
+        },
+        'another schema' => {
+            ... description of another schema ...
+        },
+    );
+
+=back
+
+=head2 Statuses
+
+Each schema is a list of statues splitted into three logic sets:
+initial, active and inactive (read below). All statuses in a schema
+must be unique. Each set may have any number of statuses.
+
+For example:
+
+    default => {
+        initial  => ['new'],
+        active   => ['open', 'stalled'],
+        inactive => ['resolved', 'rejected', 'deleted'],
+        ...
+    },
+
+Note that size of status field in the DB is limitted to 10 ASCII
+characters. You can increase size in the DB, but shorter statuses
+are usually better for UI and DB performance. Also ASCII may looks
+contradictionary with multi-language interface, but you can translate
+statuses and other things using po files.
+
+=head3 Status sets
+
+Unlike RT 3.8 this extension adds 'intial' set in addition to 'active'
+and 'inactive' sets.
+
+=over 4
+
+=item intial
+
+This set is new for RT and covers one thing that has not been
+covered in RT 3.8 and earlier. First time you change a status
+from 'new' to another in 3.8 and earlier Started date is set
+to now. Status 'new' is hardcoded in RT 3.8 for this purpose.
+
+With this extension you can define multiple intial statuses and
+Started date is only set when you change from one of initial
+statuses to status from active or inactive set.
+
+This allow you to get better statistics over dates. For example
+you may have initial statuses 'new' and 'negotiation' and active
+status 'processing'. Created date is set whenever ticket was created
+and started date is set once status changed to 'processing'.
+
+As well, you may have this set empty when you're sure all tickets
+with this status schema are active when created.
+
+Started date is set to now on create if ticket is created with
+not initial status and started date is not defined.
+
+=item active
+
+Active set is something well know as active statuses from RT 3.8
+except that it's splitted into two sets: initial and active.
+
+=item inactive
+
+Inactive statuses haven't been changed much from implementation
+in RT 3.8.
+
+Resolved date is set to now when status is changed from any
+intial or active status to inactive. As well, on create if status
+of new ticket is inactive and resolved date is not defined.
+
+'deleted' is still a special status and protected by 'DeleteTicket'
+right, you have to add it to set manually or avoid if you don't
+want tickets to be deleted.
+
+=back
+
+Statuses in each set are ordered and listed in the UI in the defined
+order. It worth to mention that order of statuses may influence
+behavior a little when it's ambiguose which status to choose.
+
+Changes between statuses are constrolled by possible transitions
+described below.
+
+=head2 Allowing transitions, protecting with rights, labeling them and defining actions
+
+Transition - is a change of status from A to B. You should define
+all possible transitions in each schema using the following format:
+
+    default => {
+        ...
+        transitions => {
+            new      => [qw(open resolved rejected deleted)],
+            open     => [qw(stalled resolved rejected deleted)],
+            stalled  => [qw(open)],
+            resolved => [qw(open)],
+            rejected => [qw(open)],
+            deleted  => [qw(open)],
+        },
+        ...
+    },
+
+=head3 Protecting with rights
+
+A transation or group of transitions can be protected by a right,
+for example:
+
+    default => {
+        ...
+        rights => {
+            '* -> deleted'  => 'DeleteTicket',
+            '* -> rejected' => 'RejectTicket',
+            '* -> *'        => 'ModifyTicketStatus',
+        },
+        ...
+    },
+
+On the left hand side you can have the following variants:
+
+    '<from> -> <to>'
+    '* -> <to>'
+    '<from> -> *'
+    '* -> *'
+
+Variants are listed in order by priority, so if user want
+to change status from X to Y then schema checked for presence
+of exact match, then for presence of 'any to Y', 'X to any' and
+finally 'any to any'.
+
+If you don't define any rights or there is no match for some
+transition then DeleteTicket and ModifyTicket rights are
+checked, like RT does by default.
+
+=head3 Labeling and defining actions
+
+Each transition can be named, by default it's named as B what often
+is not that good. At this point this label is required for the UI
+only where all transitions are listed on ticket's page as possible
+actions on the ticket. Each such action may be acompanied with
+comment, correspond or hidden by default. For example you may want
+your users to write a reply when they change status from new to open.
+Or it's possible to hide open -> delete transition by default from
+ticket's main view, but still make it legal for 'edit basics' page
+or API.
+
+Additional comment or correspond is not mandatory and may be skipped
+by users. By default no action there is no need in action and one-click
+link is showed.
+
+Use the following format to define labels and actions of transitions:
+
+    default => {
+        ...
+        actions => {
+            'new -> open'     => ['Open It', 'Respond'],
+            'new -> resolved' => ['Resolve', 'Comment'],
+            'new -> rejected' => ['Reject',  'Respond'],
+            'new -> deleted'  => ['Delete',  ''],
+
+            'open -> stalled'  => ['Stall',   'Comment'],
+            'open -> resolved' => ['Resolve', 'Comment'],
+            'open -> rejected' => ['Reject',  'Respond'],
+            'open -> deleted'  => ['Delete',  'hide'],
+
+            'stalled -> open'  => ['Open It',  ''],
+            'resolved -> open' => ['Re-open',  'Comment'],
+            'rejected -> open' => ['Re-open',  'Comment'],
+            'deleted -> open'  => ['Undelete', ''],
+        },
+        ...
+    },
+
+=head2 Moving tickets between queues with different schemas
+
+Unless there is some mapping between statuses in schema A and B,
+you can not move tickets between queues with these schemas.
+
+    __maps__ => {
+        'from schema -> to schema' => {
+            'status in left schema' => 'status in right schema',
+            ...
+        },
+        ...
+    },
+
+=head2 Changes in the UI and Scrips
+
+=head3 Quicksearch portlet
+
+This portlet has been rewriten and shows initial and active
+statuses in all schemas. If status is not valid for a queue then
+users will see '-' instead of number.
+
+For setups with many different schemas or schemas that has no
+equal statuses at all it may be better to group queues by status
+schemas. You can achieve this by using QuicksearchBySchema
+portlet. Change HomepageComponents config option first.
+
+=head3 AutoOpen scrip action
+
+Auto open scrip action has been rewritten completly. You can
+read description of its behavior in F<lib/RT/Action/AutoOpen_Vendor.pm>
+using perldoc program.
+
+=head3 StatusChange scrip condition
+
+StatusChange scrip condition has been extended with new format of
+the Argument to better serve systems with multiple status schemas.
+Read description in F<lib/RT/Condition/StatusChange_Vendor.pm>.
+
+=cut
+
+
+require RT::StatusSchema;
+RT::StatusSchema->register_rights;
+
+1;
diff --git a/lib/RT/Ticket_Overlay.pm b/lib/RT/Ticket_Overlay.pm
index a9efc46..2c32350 100755
--- a/lib/RT/Ticket_Overlay.pm
+++ b/lib/RT/Ticket_Overlay.pm
@@ -306,9 +306,13 @@ sub Create {
             $self->loc( "No permission to create tickets in the queue '[_1]'", $QueueObj->Name));
     }
 
-    unless ( $QueueObj->IsValidStatus( $args{'Status'} ) ) {
+    unless ( $QueueObj->IsValidStatus( $args{'Status'} ) 
+            && $QueueObj->status_schema->is_initial( $args{'Status'} )) {
         return ( 0, 0, $self->loc('Invalid value for status') );
     }
+    
+
+
 
     #Since we have a queue, we can set queue defaults
 
@@ -1723,13 +1727,24 @@ sub SetQueue {
         return ( 0, $self->loc("You may not create requests in that queue.") );
     }
 
+    my $new_status;
+    my $old_schema = $self->QueueObj->status_schema;
+    my $new_schema = $NewQueueObj->status_schema;
+    if ( $old_schema->name ne $new_schema->name ) {
+        unless ( $old_schema->has_map( $new_schema ) ) {
+            return ( 0, $self->loc("There is no mapping for statuses between these queues. Contact your system administrator.") );
+        }
+        $new_status = $old_schema->map( $new_schema )->{ $self->Status };
+        return ( 0, $self->loc("Mapping between queues' status schemas is incomplete. Contact your system administrator.") )
+            unless $new_status;
+    }
+
     unless (
         $self->OwnerObj->HasRight(
             Right    => 'OwnTicket',
             Object => $NewQueueObj
         )
-      )
-    {
+    ) {
         my $clone = RT::Ticket->new( $RT::SystemUser );
         $clone->Load( $self->Id );
         unless ( $clone->Id ) {
@@ -1739,6 +1754,49 @@ sub SetQueue {
         $RT::Logger->error("Couldn't set owner on queue change: $msg") unless $status;
     }
 
+    if ( $new_status ) {
+        my $clone = RT::Ticket->new( $RT::SystemUser );
+        $clone->Load( $self->Id );
+        unless ( $clone->Id ) {
+            return ( 0, $self->loc("Couldn't load copy of ticket #[_1].", $self->Id) );
+        }
+
+        my $now = RT::Date->new( $self->CurrentUser );
+        $now->SetToNow;
+
+        my $old_status = $clone->Status;
+
+        #If we're changing the status from initial in old to not intial in new,
+        # record that we've started
+        if ( $old_schema->is_initial($old_status) && !$new_schema->is_initial($new_status) ) {
+            #Set the Started time to "now"
+            $clone->_Set(
+                Field             => 'Started',
+                Value             => $now->ISO,
+                RecordTransaction => 0
+            );
+        }
+
+        #When we close a ticket, set the 'Resolved' attribute to now.
+        # It's misnamed, but that's just historical.
+        if ( $new_schema->is_inactive($new_status) ) {
+            $clone->_Set(
+                Field             => 'Resolved',
+                Value             => $now->ISO,
+                RecordTransaction => 0,
+            );
+        }
+
+        #Actually update the status
+        my ($val, $msg)= $clone->_Set(
+            Field             => 'Status',
+            Value             => $new_status,
+            RecordTransaction => 0,
+        );
+        $RT::Logger->error( 'Status change failed on queue change: '. $msg )
+            unless $val;
+    }
+
     my ($status, $msg) = $self->_Set( Field => 'Queue', Value => $NewQueueObj->Id() );
 
     if ( $status ) {
@@ -1749,7 +1807,7 @@ sub SetQueue {
             $RT::Logger->error('Queue change failed for reminder #' . $reminder->Id . ': ' . $msg) unless $status;
         }
     }
-    
+
     return ($status, $msg);
 }
 
@@ -2960,12 +3018,14 @@ sub ValidateStatus {
     my $status = shift;
 
     #Make sure the status passed in is valid
-    unless ( $self->QueueObj->IsValidStatus($status) ) {
-        return (undef);
-    }
+    return 1 if $self->QueueObj->IsValidStatus($status);
 
-    return (1);
+    my $i = 0;
+    while ( my $caller = (caller($i++))[3] ) {
+        return 1 if $caller eq 'RT::Ticket::SetQueue';
+    }
 
+    return 0;
 }
 
 # }}}
@@ -2983,28 +3043,43 @@ Alternatively, you can pass in a list of named parameters (Status => STATUS, For
 =cut
 
 sub SetStatus {
-    my $self   = shift;
+    my $self = shift;
     my %args;
-
     if (@_ == 1) {
-    $args{Status} = shift;
+        $args{Status} = shift;
     }
     else {
-    %args = (@_);
+        %args = (@_);
     }
 
-    #Check ACL
-    if ( $args{Status} eq 'deleted') {
-            unless ($self->CurrentUserHasRight('DeleteTicket')) {
-            return ( 0, $self->loc('Permission Denied') );
-       }
-    } else {
-            unless ($self->CurrentUserHasRight('ModifyTicket')) {
-            return ( 0, $self->loc('Permission Denied') );
-       }
+    my $schema = $self->QueueObj->status_schema;
+
+    my $new = $args{'Status'};
+    unless ( $schema->is_valid( $new ) ) {
+        return (0,
+            $self->loc("Status '[_1]' is not valid for schema '[_2]'.",
+                $self->loc($new), $self->loc($schema->name)
+            )
+        );
     }
 
-    if (!$args{Force} && ($args{'Status'} eq 'resolved') && $self->HasUnresolvedDependencies) {
+    my $old = $self->__Value('Status');
+    unless ( $schema->is_transition( $old => $new ) ) {
+        return (0,
+            $self->loc("You can't change status from '[_1]' to '[_2]'.",
+                $self->loc($old), $self->loc($new)
+            )
+        );
+    }
+
+    my $check_right = $schema->check_right( $old => $new );
+    unless ( $self->CurrentUserHasRight( $check_right ) ) {
+        return ( 0, $self->loc('Permission Denied') );
+    }
+
+    if ( !$args{Force} && $schema->is_inactive( $new )
+        && $self->HasUnresolvedDependencies
+    ) {
         return (0, $self->loc('That ticket has unresolved dependencies'));
     }
 
@@ -3012,30 +3087,34 @@ sub SetStatus {
     $now->SetToNow();
 
     #If we're changing the status from new, record that we've started
-    if ( $self->Status eq 'new' && $args{Status} ne 'new' ) {
-
+    if ( $schema->is_initial($old) && !$schema->is_initial($new) ) {
         #Set the Started time to "now"
-        $self->_Set( Field             => 'Started',
-                     Value             => $now->ISO,
-                     RecordTransaction => 0 );
+        $self->_Set(
+            Field             => 'Started',
+            Value             => $now->ISO,
+            RecordTransaction => 0
+        );
     }
 
     #When we close a ticket, set the 'Resolved' attribute to now.
     # It's misnamed, but that's just historical.
-    if ( $self->QueueObj->IsInactiveStatus($args{Status}) ) {
-        $self->_Set( Field             => 'Resolved',
-                     Value             => $now->ISO,
-                     RecordTransaction => 0 );
+    if ( $schema->is_inactive($new) ) {
+        $self->_Set(
+            Field             => 'Resolved',
+            Value             => $now->ISO,
+            RecordTransaction => 0,
+        );
     }
 
     #Actually update the status
-   my ($val, $msg)= $self->_Set( Field           => 'Status',
-                          Value           => $args{Status},
-                          TimeTaken       => 0,
-                          CheckACL      => 0,
-                          TransactionType => 'Status'  );
-
-    return($val,$msg);
+    my ($val, $msg)= $self->_Set(
+        Field           => 'Status',
+        Value           => $args{Status},
+        TimeTaken       => 0,
+        CheckACL        => 0,
+        TransactionType => 'Status',
+    );
+    return ($val, $msg);
 }
 
 # }}}
diff --git a/share/html/Elements/QueueSummaryByStatus b/share/html/Elements/QueueSummaryByStatus
new file mode 100644
index 0000000..2c18af9
--- /dev/null
+++ b/share/html/Elements/QueueSummaryByStatus
@@ -0,0 +1,128 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%# 
+%# This software is Copyright (c) 1996-2008 Best Practical Solutions, LLC
+%#                                          <jesse 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 }}}
+<table border="0" cellspacing="0" cellpadding="1" width="100%" class="queue-summary">
+
+<tr>
+    <th class="collection-as-table"><&|/l&>Queue</&></th>
+% for my $status ( @statuses ) {
+    <th class="collection-as-table"><% loc($status) %></th>
+% }
+</tr>
+
+<%PERL>
+my $i = 0;
+for my $queue (@queues) {
+    $i++;
+    my $name = $queue->{Name};
+    $name =~ s/'/\\'/g;
+    my $queue_cond = "Queue = '$name' AND ";
+    my $schema = $schema{ lc $queue->{'Schema'} };
+    my $all_q = $queue_cond . '(' . join( " OR ", map "Status = '$_'", grep $schema->is_valid($_), @statuses ) . ')';
+</%PERL>
+<tr class="<% $i%2 ? 'oddline' : 'evenline'%>" >
+
+<td><a href="<% RT->Config->Get('WebPath') %>/Search/Results.html?Query=<% $all_q |u,n %>" title="<% $queue->{Description} %>"><% $queue->{Name} %></a></td>
+
+%   for my $status (@statuses) {
+%   if ( $schema->is_valid( $status ) ) {
+%       $Tickets->FromSQL( "Queue = $queue->{id} AND Status = '$status'" );
+<td align="right"><a href="<% RT->Config->Get('WebPath') %>/Search/Results.html?Query=<% $queue_cond ."Status = '$status'" |u,n %>"><% $Tickets->Count %></a></td>
+%   } else {
+<td align="right">-</td>
+%   }
+%   }
+</tr>
+% }
+</table>
+<%INIT>
+my @queues;
+if ($cache && exists $session{$cache}) {
+    @queues = @{$session{$cache}};
+
+}
+else {
+    my $Queues = RT::Queues->new($session{'CurrentUser'});
+    $Queues->UnLimit();
+    @queues = grep $queue_filter->($_), @{$Queues->ItemsArrayRef};
+
+    $m->callback( CallbackName => 'Filter', Queues => \@queues );
+
+    @queues = map { {
+        id          => $_->Id,
+        Name        => $_->Name,
+        Description => $_->Description,
+        Schema      => $_->status_schema->name,
+    } } grep $_, @queues;
+
+    $session{$cache} = \@queues if $cache;
+}
+
+use RT::StatusSchema;
+
+my %schema;
+$schema{ lc $_->name } = $_ foreach
+    grep $_, map RT::StatusSchema->load($_),
+    map $_->{'Schema'}, @queues;
+
+
+unless ( @statuses ) {
+    my %seen;
+    foreach my $set('initial', 'active') {
+        foreach my $schema ( map $schema{$_}, sort keys %schema ) {
+            push @statuses, grep !$seen{lc $_}++, $schema->$set();
+        }
+    }
+}
+
+my $Tickets = RT::Tickets->new($session{'CurrentUser'});
+</%INIT>
+<%ARGS>
+$cache => undef
+$queue_filter => undef
+ at statuses => ()
+</%ARGS>
diff --git a/share/html/Elements/QueueSummaryByStatusSchema b/share/html/Elements/QueueSummaryByStatusSchema
new file mode 100644
index 0000000..f8317dd
--- /dev/null
+++ b/share/html/Elements/QueueSummaryByStatusSchema
@@ -0,0 +1,129 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%# 
+%# COPYRIGHT:
+%# 
+%# This software is Copyright (c) 1996-2008 Best Practical Solutions, LLC
+%#                                          <jesse 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 }}}
+% foreach my $schema ( map $schema{$_}, sort keys %schema ) {
+% my @cur_statuses = grep $schema->is_valid($_), @statuses;
+% next unless @cur_statuses;
+<table border="0" cellspacing="0" cellpadding="1" width="100%" class="queue-summary">
+
+<tr>
+    <th class="collection-as-table"><&|/l&>Queue</&></th>
+% for my $status ( @cur_statuses ) {
+    <th class="collection-as-table"><% loc($status) %></th>
+% }
+</tr>
+
+<%PERL>
+my $i = 0;
+for my $queue (@queues) {
+    next if lc $queue->{Schema} ne lc $schema->name;
+
+    $i++;
+    my $name = $queue->{Name};
+    $name =~ s/'/\\'/g;
+    my $queue_cond = "Queue = '$name' AND ";
+    my $all_q = $queue_cond . '(' . join( " OR ", map "Status = '$_'", grep $schema->is_valid($_), @statuses ) . ')';
+</%PERL>
+<tr class="<% $i%2 ? 'oddline' : 'evenline'%>" >
+
+<td><a href="<% RT->Config->Get('WebPath') %>/Search/Results.html?Query=<% $all_q |u,n %>" title="<% $queue->{Description} %>"><% $queue->{Name} %></a></td>
+
+%   for my $status (@cur_statuses) {
+%       $Tickets->FromSQL( "Queue = $queue->{id} AND Status = '$status'" );
+<td align="right"><a href="<% RT->Config->Get('WebPath') %>/Search/Results.html?Query=<% $queue_cond ."Status = '$status'" |u,n %>"><% $Tickets->Count %></a></td>
+%   }
+</tr>
+% }
+</table>
+% }
+<%INIT>
+my @queues;
+if ($cache && exists $session{$cache}) {
+    @queues = @{$session{$cache}};
+
+}
+else {
+    my $Queues = RT::Queues->new($session{'CurrentUser'});
+    $Queues->UnLimit();
+    @queues = grep $queue_filter->($_), @{$Queues->ItemsArrayRef};
+
+    $m->callback( CallbackName => 'Filter', Queues => \@queues );
+
+    @queues = map { {
+        id          => $_->Id,
+        Name        => $_->Name,
+        Description => $_->Description,
+        Schema      => $_->status_schema->name,
+    } } grep $_, @queues;
+
+    $session{$cache} = \@queues if $cache;
+}
+
+use RT::StatusSchema;
+
+my %schema;
+$schema{ lc $_->name } = $_ foreach
+    grep $_, map RT::StatusSchema->load($_),
+    map $_->{'Schema'}, @queues;
+
+
+unless ( @statuses ) {
+    my %seen;
+    foreach my $set('initial', 'active') {
+        foreach my $schema ( map $schema{$_}, sort keys %schema ) {
+            push @statuses, grep !$seen{lc $_}++, $schema->$set();
+        }
+    }
+}
+
+my $Tickets = RT::Tickets->new($session{'CurrentUser'});
+</%INIT>
+<%ARGS>
+$cache => undef
+$queue_filter => undef
+ at statuses => ()
+</%ARGS>
diff --git a/share/html/Elements/Quicksearch b/share/html/Elements/Quicksearch
old mode 100755
new mode 100644
index 6b11f73..f02079d
--- a/share/html/Elements/Quicksearch
+++ b/share/html/Elements/Quicksearch
@@ -1,65 +1,20 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%# 
-%# COPYRIGHT:
-%# 
-%# This software is Copyright (c) 1996-2010 Best Practical Solutions, LLC
-%#                                          <jesse 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="ticket-overview">
-<&|/Widgets/TitleBox, title => loc("Quick search"), bodyclass => "",
-   titleright => loc("Edit"), titleright_href => RT->Config->Get('WebPath').'/Prefs/Quicksearch.html' &>
-<& /Elements/QueueSummary,
+<&|/Widgets/TitleBox,
+    title => loc("Quick search"),
+    bodyclass => "",
+    titleright => loc("Edit"),
+    titleright_href => RT->Config->Get('WebPath').'/Prefs/Quicksearch.html',
+&>
+<& $comp,
    cache => 'quick_search_queues',
    queue_filter => sub { $_->CurrentUserHasRight('ShowTicket') && !exists $unwanted->{$_->Name} },
-   conditions => \@conditions,
 &>
 </&>
 </div>
 <%INIT>
 my $unwanted = $session{'CurrentUser'}->UserObj->Preferences('QuickSearch', {});
-
-my @conditions = ();
-foreach ( RT::Queue->ActiveStatusArray ) {
-    push @conditions, { cond => "Status = '$_'", name => loc($_) };
-}
+my $comp = $SplitBySchema? '/Elements/QueueSummaryByStatusSchema' : '/Elements/QueueSummaryByStatus';
 </%INIT>
+<%ARGS>
+$SplitBySchema => 0
+</%ARGS>
diff --git a/share/html/Elements/QuicksearchBySchema b/share/html/Elements/QuicksearchBySchema
new file mode 100644
index 0000000..16147a6
--- /dev/null
+++ b/share/html/Elements/QuicksearchBySchema
@@ -0,0 +1 @@
+<& /Elements/Quicksearch, SplitBySchema => 1 &>
diff --git a/share/html/Elements/SelectStatus b/share/html/Elements/SelectStatus
old mode 100755
new mode 100644
index 88409ee..64bf5d6
--- a/share/html/Elements/SelectStatus
+++ b/share/html/Elements/SelectStatus
@@ -1,73 +1,54 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%# 
-%# COPYRIGHT:
-%# 
-%# This software is Copyright (c) 1996-2010 Best Practical Solutions, LLC
-%#                                          <jesse 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 }}}
-<select name="<%$Name%>" <% $Multiple ? qq{multiple="multiple" size="$Size"} : '' |n%>>
+<select name="<%$Name%>">
 %if ($DefaultValue) {
-<option value=""<% not keys(%default) && qq[ selected="selected"] |n %>><%$DefaultLabel%></option>
+<option value=""<% !$Default && qq[ selected="selected"] |n %>><%$DefaultLabel%></option>
 %}
 %foreach my $status (@status) {
 %next if ($SkipDeleted && $status eq 'deleted');
-<option value="<%$status%>"<% $default{$status} && qq[ selected="selected"] |n %>><%loc($status)%></option>
+<option value="<%$status%>"<% (defined $Default && $status eq $Default) && qq[ selected="selected"] |n %>><%loc($status)%></o
+ption>
 % }
 </select>
-<%ONCE>
-my $queue = new RT::Queue($session{'CurrentUser'});
-my @status = $queue->StatusArray();
-</%ONCE>
-<%init>
-my %default;
-$default{$_}++ for grep $_, ref $Default ? @{$Default} : ( $Default );
-</%init>
+<%INIT>
+my $caller = $m->callers(1)->path;
+if ( $caller eq '/Ticket/Update.html' ) {
+    $Ticket = LoadTicket( $m->caller_args(1)->{'id'} );
+}
+elsif ( $caller eq '/Ticket/Create.html' ) {
+    $Queue = RT::Queue->new( $session{'CurrentUser'} );
+    $Queue->Load( $m->caller_args(1)->{'Queue'} );
+}
+elsif ( $caller eq '/Ticket/Elements/EditBasics' ) {
+    $Ticket = $m->caller_args(1)->{'TicketObj'};
+}
+### XXX: no cover for Tools/MyDay.html
+
+
+my @status;
+if ( $Ticket ) {
+    my $current = $Ticket->Status;
+    my $schema = $Ticket->QueueObj->status_schema;
+
+    my %has = ();
+    foreach my $next ( $schema->transitions( $current ) ) {
+        my $check = $schema->check_right( $current => $next );
+        $has{ $check } = $Ticket->CurrentUserHasRight( $check )
+            unless exists $has{ $check };
+        push @status, $next if $has{ $check };
+    }
+}
+elsif ( $Queue ) {
+    @status = $Queue->status_schema->valid;
+}
+else {
+    @status = RT::Queue->status_schema->valid;
+}
+</%INIT>
 <%ARGS>
 $Name => undef
+$Ticket => undef
+$Queue => undef
 $Default => ''
 $SkipDeleted => 0
 $DefaultValue => 1
 $DefaultLabel => "-"
-$Multiple => 0
-$Size => 6
 </%ARGS>
diff --git a/share/html/Ticket/Elements/Tabs b/share/html/Ticket/Elements/Tabs
index b1428b0..3dc47af 100755
--- a/share/html/Ticket/Elements/Tabs
+++ b/share/html/Ticket/Elements/Tabs
@@ -61,6 +61,12 @@ if ($Ticket) {
 
     my $id = $Ticket->id();
 
+my $current = $Ticket->Status;
+my $schema = $Ticket->QueueObj->status_schema;
+my %has;
+my $i = 1;
+
+
     if ( defined $session{'tickets'} ) {
 
         # we have to update session data if we get new ItemMap
@@ -203,58 +209,69 @@ if ($Ticket) {
     }
 
     if ( $can{'ModifyTicket'} ) {
-        if ( $Ticket->Status ne 'resolved' ) {
-            $actions->{'G'} = {
-                path =>
-                    "Ticket/Update.html?Action="
-                    . RT->Config->Get('ResolveDefaultUpdateType', $session{'CurrentUser'})
-                    . "&DefaultStatus=resolved&id="
-                    . $id,
-                title => loc('Resolve')
+        foreach my $next ( $schema->transitions($current) ) {
+            my $action = $schema->transition_action( $current => $next );
+            next if $action eq 'hide';
+
+            my $check = $schema->check_right( $current => $next );
+            $has{$check} = $Ticket->CurrentUserHasRight($check)
+                unless exists $has{$check};
+            next unless $has{$check};
+
+            my $path = 'Ticket/';
+            if ($action) {
+                $path .= "Update.html?"
+                    . $m->comp(
+                    '/Elements/QueryString',
+                    Action        => $action,
+                    DefaultStatus => $next,
+                    id            => $id
+                    );
+            } else {
+                $path .= "Display.html?"
+                    . $m->comp(
+                    '/Elements/QueryString',
+                    Status => $next,
+                    id     => $id
+                    );
+            }
+            $actions->{ 'G' . $i++ } = {
+                path  => $path,
+                title => loc( $schema->transition_label( $current => $next ) ),
             };
         }
-        if ( $Ticket->Status ne 'open' ) {
-            $actions->{'A'} = {
-                path  => "Ticket/Display.html?Status=open&id=" . $id,
-                title => loc('Open it')
-            };
+        if ( $Ticket->CurrentUserHasRight('OwnTicket') ) {
+            if ( $Ticket->OwnerObj->Id == $RT::Nobody->id ) {
+                $actions->{'B'} = {
+                    path  => "Ticket/Display.html?Action=Take&id=" . $id,
+                    title => loc('Take'),
+                    }
+                    if $can{'ModifyTicket'}
+                        or $Ticket->CurrentUserHasRight('TakeTicket');
+            } elsif ( $Ticket->OwnerObj->id != $session{CurrentUser}->id ) {
+                $actions->{'C'} = {
+                    path  => "Ticket/Display.html?Action=Steal&id=" . $id,
+                    title => loc('Steal'),
+                    }
+                    if $can{'ModifyTicket'}
+                        or $Ticket->CurrentUserHasRight('StealTicket');
+            }
         }
-    }
 
-    if ( $Ticket->CurrentUserHasRight('OwnTicket') ) {
-        if ( $Ticket->OwnerObj->Id == $RT::Nobody->id ) {
-            $actions->{'B'} = {
-                path  => "Ticket/Display.html?Action=Take&id=" . $id,
-                title => loc('Take'),
-                }
-                if $can{'ModifyTicket'}
-                    or $Ticket->CurrentUserHasRight('TakeTicket');
-        } elsif ( $Ticket->OwnerObj->id != $session{CurrentUser}->id ) {
-            $actions->{'C'} = {
-                path  => "Ticket/Display.html?Action=Steal&id=" . $id,
-                title => loc('Steal'),
-                }
-                if $can{'ModifyTicket'}
-                    or $Ticket->CurrentUserHasRight('StealTicket');
+        if (   $can{'ModifyTicket'}
+            or $Ticket->CurrentUserHasRight('CommentOnTicket') )
+        {
+            $actions->{'E'} = {
+                title => loc('Comment'),
+                path  => "Ticket/Update.html?Action=Comment&id=" . $id,
+            };
         }
-    }
 
-    if (   $can{'ModifyTicket'}
-        or $Ticket->CurrentUserHasRight('CommentOnTicket') )
-    {
-        $actions->{'E'} = {
-            title => loc('Comment'),
-            path  => "Ticket/Update.html?Action=Comment&id=" . $id,
-        };
-    }
-
-    $actions->{'_ZZ'}
-        = { html => $m->scomp( '/Ticket/Elements/Bookmark', id => $Ticket->id ),
-        };
+        $actions->{'_ZZ'} = { html => $m->scomp( '/Ticket/Elements/Bookmark', id => $Ticket->id ), };
 
-}
+    }
 
-if ( ( defined $actions->{A} || defined $actions->{B} || defined $actions->{C} )
+if ( (  defined $actions->{B} || defined $actions->{C} )
     && (   defined $actions->{E}
         || defined $actions->{F}
         || defined $actions->{G} ) )
@@ -262,9 +279,16 @@ if ( ( defined $actions->{A} || defined $actions->{B} || defined $actions->{C} )
 
     if    ( defined $actions->{C} ) { $actions->{C}->{separator} = 1 }
     elsif ( defined $actions->{B} ) { $actions->{B}->{separator} = 1 }
-    elsif ( defined $actions->{A} ) { $actions->{A}->{separator} = 1 }
 }
 
+$actions->{'G' . 1}{pre_separator} = 1;
+
+# Separator between the last status and the next set of actions
+$actions->{'G' . ($i-1)}{separator} = 1;
+
+
+
+
 my $args = '';
 my $has_query = '';
 my %query_args;
diff --git a/t/status-schemas/basics.t b/t/status-schemas/basics.t
new file mode 100644
index 0000000..e35ae4e
--- /dev/null
+++ b/t/status-schemas/basics.t
@@ -0,0 +1,215 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Data::Dumper;
+
+require 't/utils.pl';
+use Test::More tests => 49;
+
+my $general = RT::Test->load_or_create_queue(
+    Name => 'General',
+);
+ok $general && $general->id, 'loaded or created a queue';
+
+my $tstatus = sub {
+    DBIx::SearchBuilder::Record::Cachable->FlushCache;
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    $ticket->Load( $_[0] );
+    return $ticket->Status;
+};
+
+diag "check basic API";
+{
+    my $schema = $general->status_schema;
+    isa_ok($schema, 'RT::StatusSchema');
+    is $schema->name, 'default', "it's a default schema";
+    is join(', ', $schema->valid),
+        join(', ', qw(new open stalled resolved rejected deleted)),
+        'this is default set';
+}
+
+my ($baseurl, $m) = RT::Test->started_ok;
+ok $m->login, 'logged in';
+
+diag "check status input on create";
+{
+    $m->goto_create_ticket( $general );
+
+    my $form = $m->form_name('TicketCreate');
+    ok my $input = $form->find_input('Status'), 'found status selector';
+
+    my @form_values = $input->possible_values;
+    ok scalar @form_values, 'some options in the UI';
+
+    my $valid = 1;
+    foreach ( @form_values ) {
+        $valid = 0 unless $general->status_schema->is_valid($_);
+    }
+    ok $valid, 'all statuses in the form are valid';
+}
+
+diag "create a ticket";
+my $tid;
+{
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    ($tid) = $ticket->Create( Queue => $general->id, Subject => 'test' );
+    ok $tid, "created a ticket #$tid";
+    is $ticket->Status, 'new', 'correct status';
+}
+
+diag "new ->(open it)-> open";
+{
+    ok $m->goto_ticket( $tid ), 'opened a ticket';
+
+    {
+        my @links = $m->followable_links;
+        ok scalar @links, 'found links';
+        my $found = 1;
+        foreach my $t ('Open It', 'Resolve', 'Reject', 'Delete') {
+            $found = 0 unless grep $_->text eq $t, @links;
+        }
+        ok $found, 'found all transitions';
+
+        $found = 0;
+        foreach my $t ('Stall', 'Re-open', 'Undelete') {
+            $found = 1 if grep $_->text eq $t, @links;
+        }
+        ok !$found, 'no unwanted transitions';
+    }
+
+    $m->follow_link_ok({text => 'Open It'});
+    $m->form_number(3);
+    $m->click('SubmitTicket');
+
+    is $tstatus->($tid), 'open', 'changed status';
+}
+
+diag "open ->(stall)-> stalled";
+{
+    is $tstatus->($tid), 'open', 'ticket is open';
+
+    ok $m->goto_ticket( $tid ), 'opened a ticket';
+
+    {
+        my @links = $m->followable_links;
+        ok scalar @links, 'found links';
+        my $found = 1;
+        foreach my $t ('Stall', 'Resolve', 'Reject') {
+            $found = 0 unless grep $_->text eq $t, @links;
+        }
+        ok $found, 'found all transitions';
+
+        $found = 0;
+        foreach my $t ('Open It', 'Delete', 'Re-open', 'Undelete') {
+            $found = 1 if grep $_->text eq $t, @links;
+        }
+        ok !$found, 'no unwanted transitions';
+    }
+
+    $m->follow_link_ok({text => 'Stall'});
+    $m->form_number(3);
+    $m->click('SubmitTicket');
+
+    is $tstatus->($tid), 'stalled', 'changed status';
+}
+
+diag "stall ->(open it)-> open";
+{
+    is $tstatus->($tid), 'stalled', 'ticket is stalled';
+
+    ok $m->goto_ticket( $tid ), 'opened a ticket';
+
+    {
+        my @links = $m->followable_links;
+        ok scalar @links, 'found links';
+        my $found = 1;
+        foreach my $t ('Open It') {
+            $found = 0 unless grep $_->text eq $t, @links;
+        }
+        ok $found, 'found all transitions';
+
+        $found = 0;
+        foreach my $t ('Delete', 'Re-open', 'Undelete', 'Stall', 'Resolve', 'Reject') {
+            $found = 1 if grep $_->text eq $t, @links;
+        }
+        ok !$found, 'no unwanted transitions';
+    }
+
+    $m->follow_link_ok({text => 'Open It'});
+
+    is $tstatus->($tid), 'open', 'changed status';
+}
+
+diag "open -> deleted, only via modify";
+{
+    is $tstatus->($tid), 'open', 'ticket is open';
+
+    $m->get_ok( '/Ticket/Modify.html?id='. $tid );
+    my $form = $m->form_number(3);
+    ok my $input = $form->find_input('Status'), 'found status selector';
+
+    my @form_values = $input->possible_values;
+    ok scalar @form_values, 'some options in the UI';
+
+    ok grep($_ eq 'deleted', @form_values), "has deleted";
+
+    $m->select( Status => 'deleted' );
+    $m->submit;
+
+    is $tstatus->($tid), 'deleted', 'deleted ticket';
+}
+
+diag "deleted -> X via modify, only open is available";
+{
+    is $tstatus->($tid), 'deleted', 'ticket is deleted';
+
+    $m->get_ok( '/Ticket/Modify.html?id='. $tid );
+    my $form = $m->form_number(3);
+    ok my $input = $form->find_input('Status'), 'found status selector';
+
+    my @form_values = $input->possible_values;
+    ok scalar @form_values, 'some options in the UI';
+
+    is join('-', @form_values), '-open', 'only open and default available';
+}
+
+diag "check illegal values and transitions";
+{
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'illegal',
+        );
+        ok !$id, 'have not created a ticket';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'new',
+        );
+        ok $id, 'created a ticket';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'new',
+        );
+        ok $id, 'created a ticket';
+
+        (my $status, $msg) = $ticket->SetStatus( 'illeagal' );
+        ok !$status, "couldn't set illeagal status";
+        is $ticket->Status, 'new', 'status is steal the same';
+
+        ($status, $msg) = $ticket->SetStatus( 'stalled' );
+        ok !$status, "couldn't set status, transition is illeagal";
+        is $ticket->Status, 'new', 'status is steal the same';
+    }
+}
+
diff --git a/t/status-schemas/dates.t b/t/status-schemas/dates.t
new file mode 100644
index 0000000..a924d49
--- /dev/null
+++ b/t/status-schemas/dates.t
@@ -0,0 +1,299 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Data::Dumper;
+
+require 't/utils.pl';
+use Test::More tests => 82;
+
+my $general = RT::Test->load_or_create_queue(
+    Name => 'General',
+);
+ok $general && $general->id, 'loaded or created a queue';
+
+my $delivery = RT::Test->load_or_create_queue(
+    Name => 'delivery',
+);
+ok $delivery && $delivery->id, 'loaded or created a queue';
+
+my $tstatus = sub {
+    DBIx::SearchBuilder::Record::Cachable->FlushCache;
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    $ticket->Load( $_[0] );
+    return $ticket->Status;
+};
+
+my ($baseurl, $m) = RT::Test->started_ok;
+ok $m->login, 'logged in';
+
+diag "check basic API";
+{
+    my $schema = $general->status_schema;
+    isa_ok($schema, 'RT::StatusSchema');
+    is $schema->name, 'default', "it's a default schema";
+
+    $schema = $delivery->status_schema;
+    isa_ok($schema, 'RT::StatusSchema');
+    is $schema->name, 'delivery', "it's a delivery schema";
+}
+
+diag "dates on create for default schema";
+{
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'new',
+        );
+        ok $id, 'created a ticket';
+        ok $ticket->StartedObj->Unix <= 0, 'started is not set';
+        ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'open',
+        );
+        ok $id, 'created a ticket';
+        ok $ticket->StartedObj->Unix > 0, 'started is set';
+        ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'resolved',
+        );
+        ok $id, 'created a ticket';
+        ok $ticket->StartedObj->Unix > 0, 'started is set';
+        ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
+    }
+
+    my $test_date = '2008-11-28 12:00:00';
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'new',
+            Started => $test_date,
+            Resolved => $test_date,
+        );
+        ok $id, 'created a ticket';
+        is $ticket->StartedObj->ISO, $test_date, 'started is set';
+        is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'open',
+            Started => $test_date,
+            Resolved => $test_date,
+        );
+        ok $id, 'created a ticket';
+        is $ticket->StartedObj->ISO, $test_date, 'started is set';
+        is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'resolved',
+            Started => $test_date,
+            Resolved => $test_date,
+        );
+        ok $id, 'created a ticket';
+        is $ticket->StartedObj->ISO, $test_date, 'started is set';
+        is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set';
+    }
+}
+
+diag "dates on create for delivery schema";
+{
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $delivery->id,
+            Subject => 'test',
+            Status => 'ordered',
+        );
+        ok $id, 'created a ticket';
+        ok $ticket->StartedObj->Unix <= 0, 'started is not set';
+        ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $delivery->id,
+            Subject => 'test',
+            Status => 'on way',
+        );
+        ok $id, 'created a ticket';
+        ok $ticket->StartedObj->Unix > 0, 'started is set';
+        ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $delivery->id,
+            Subject => 'test',
+            Status => 'delivered',
+        );
+        ok $id, 'created a ticket';
+        ok $ticket->StartedObj->Unix > 0, 'started is set';
+        ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
+    }
+
+    my $test_date = '2008-11-28 12:00:00';
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $delivery->id,
+            Subject => 'test',
+            Status => 'ordered',
+            Started => $test_date,
+            Resolved => $test_date,
+        );
+        ok $id, 'created a ticket';
+        is $ticket->StartedObj->ISO, $test_date, 'started is set';
+        is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $delivery->id,
+            Subject => 'test',
+            Status => 'on way',
+            Started => $test_date,
+            Resolved => $test_date,
+        );
+        ok $id, 'created a ticket';
+        is $ticket->StartedObj->ISO, $test_date, 'started is set';
+        is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $delivery->id,
+            Subject => 'test',
+            Status => 'delivered',
+            Started => $test_date,
+            Resolved => $test_date,
+        );
+        ok $id, 'created a ticket';
+        is $ticket->StartedObj->ISO, $test_date, 'started is set';
+        is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set';
+    }
+}
+
+diag "dates on status change for default schema";
+{
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    my ($id, $msg) = $ticket->Create(
+        Queue => $general->id,
+        Subject => 'test',
+        Status => 'new',
+    );
+    ok $id, 'created a ticket';
+    ok $ticket->StartedObj->Unix <= 0, 'started is not set';
+    ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+
+    (my $status, $msg) = $ticket->SetStatus('open');
+    ok $status, 'changed status' or diag "error: $msg";
+    ok $ticket->StartedObj->Unix > 0, 'started is set';
+    ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+
+    my $started = $ticket->StartedObj->Unix;
+
+    ($status, $msg) = $ticket->SetStatus('stalled');
+    ok $status, 'changed status' or diag "error: $msg";
+    is $ticket->StartedObj->Unix, $started, 'started is set and the same';
+    ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+
+    ($status, $msg) = $ticket->SetStatus('open');
+    ok $status, 'changed status' or diag "error: $msg";
+    is $ticket->StartedObj->Unix, $started, 'started is set and the same';
+    ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+
+    ($status, $msg) = $ticket->SetStatus('resolved');
+    ok $status, 'changed status' or diag "error: $msg";
+    is $ticket->StartedObj->Unix, $started, 'started is set and the same';
+    ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
+}
+
+diag "dates on status change for delivery schema";
+{
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    my ($id, $msg) = $ticket->Create(
+        Queue => $delivery->id,
+        Subject => 'test',
+        Status => 'ordered',
+    );
+    ok $id, 'created a ticket';
+    ok $ticket->StartedObj->Unix <= 0, 'started is not set';
+    ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+
+    (my $status, $msg) = $ticket->SetStatus('delayed');
+    ok $status, 'changed status' or diag "error: $msg";
+    ok $ticket->StartedObj->Unix > 0, 'started is set';
+    ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+
+    my $started = $ticket->StartedObj->Unix;
+
+    ($status, $msg) = $ticket->SetStatus('on way');
+    ok $status, 'changed status' or diag "error: $msg";
+    is $ticket->StartedObj->Unix, $started, 'started is set and the same';
+    ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+
+    ($status, $msg) = $ticket->SetStatus('delivered');
+    ok $status, 'changed status' or diag "error: $msg";
+    is $ticket->StartedObj->Unix, $started, 'started is set and the same';
+    ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
+}
+
+diag "add partial map between general->delivery";
+{
+    my $schemas = RT->Config->Get('StatusSchemaMeta');
+    $schemas->{'__maps__'} = {
+        'default -> delivery' => {
+            new => 'on way',
+        },
+        'delivery -> default' => {
+            'on way' => 'resolved',
+        },
+    };
+    RT::StatusSchema->fill_cache;
+}
+
+diag "check date changes on moving a ticket";
+{
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    my ($id, $msg) = $ticket->Create(
+        Queue => $general->id,
+        Subject => 'test',
+        Status => 'new',
+    );
+    ok $id, 'created a ticket';
+    ok $ticket->StartedObj->Unix <= 0, 'started is not set';
+    ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+
+    (my $status, $msg) = $ticket->SetQueue( $delivery->id );
+    ok $status, "moved ticket between queues with different schemas";
+    is $ticket->Status, 'on way', 'status has been changed';
+    ok $ticket->StartedObj->Unix > 0, 'started is set';
+    ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+
+    ($status, $msg) = $ticket->SetQueue( $general->id );
+    ok $status, "moved ticket between queues with different schemas";
+    is $ticket->Status, 'resolved', 'status has been changed';
+    ok $ticket->StartedObj->Unix > 0, 'started is set';
+    ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
+}
diff --git a/t/status-schemas/moving.t b/t/status-schemas/moving.t
new file mode 100644
index 0000000..b68979e
--- /dev/null
+++ b/t/status-schemas/moving.t
@@ -0,0 +1,98 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Data::Dumper;
+
+require 't/utils.pl';
+use Test::More tests => 18;
+
+my $general = RT::Test->load_or_create_queue(
+    Name => 'General',
+);
+ok $general && $general->id, 'loaded or created a queue';
+
+my $delivery = RT::Test->load_or_create_queue(
+    Name => 'delivery',
+);
+ok $delivery && $delivery->id, 'loaded or created a queue';
+
+my $tstatus = sub {
+    DBIx::SearchBuilder::Record::Cachable->FlushCache;
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    $ticket->Load( $_[0] );
+    return $ticket->Status;
+};
+
+diag "check moving without a map";
+{
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    my ($id, $msg) = $ticket->Create(
+        Queue => $general->id,
+        Subject => 'test',
+        Status => 'new',
+    );
+    ok $id, 'created a ticket';
+    (my $status, $msg) = $ticket->SetQueue( $delivery->id );
+    ok !$status, "couldn't change queue when there is no maps between schemas";
+    is $ticket->Queue, $general->id, 'queue is steal the same';
+    is $ticket->Status, 'new', 'status is steal the same';
+}
+
+diag "add partial map";
+{
+    my $schemas = RT->Config->Get('StatusSchemaMeta');
+    $schemas->{'__maps__'} = {
+        'default -> delivery' => {
+            new => 'ordered',
+        },
+    };
+    RT::StatusSchema->fill_cache;
+}
+
+diag "check moving with a partial map";
+{
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'new',
+        );
+        ok $id, 'created a ticket';
+        (my $status, $msg) = $ticket->SetQueue( $delivery->id );
+        ok $status, "moved ticket between queues with different schemas";
+        is $ticket->Queue, $delivery->id, 'queue has been changed'
+            or diag "error: $msg";
+        is $ticket->Status, 'ordered', 'status has been changed';
+    }
+    {
+        my $ticket = RT::Ticket->new( $RT::SystemUser );
+        my ($id, $msg) = $ticket->Create(
+            Queue => $general->id,
+            Subject => 'test',
+            Status => 'open',
+        );
+        ok $id, 'created a ticket';
+        (my $status, $msg) = $ticket->SetQueue( $delivery->id );
+        ok !$status, "couldn't change queue when map is not complete";
+        is $ticket->Queue, $general->id, 'queue is steal the same';
+        is $ticket->Status, 'open', 'status is steal the same';
+    }
+}
+
+diag "one way map doesn't work backwards";
+{
+    my $ticket = RT::Ticket->new( $RT::SystemUser );
+    my ($id, $msg) = $ticket->Create(
+        Queue => $delivery->id,
+        Subject => 'test',
+        Status => 'ordered',
+    );
+    ok $id, 'created a ticket';
+    (my $status, $msg) = $ticket->SetQueue( $general->id );
+    ok !$status, "couldn't change queue when there is no maps between schemas";
+    is $ticket->Queue, $delivery->id, 'queue is steal the same';
+    is $ticket->Status, 'ordered', 'status is steal the same';
+}
+
diff --git a/t/status-schemas/utils.pl b/t/status-schemas/utils.pl
new file mode 100644
index 0000000..00c0c65
--- /dev/null
+++ b/t/status-schemas/utils.pl
@@ -0,0 +1,69 @@
+#!/usr/bin/perl 
+
+use strict;
+use warnings;
+
+### after: use lib qw(@RT_LIB_PATH@);
+use lib qw(/opt/rt3/local/lib /opt/rt3/lib);
+
+my $config;
+BEGIN {
+$config = <<END;
+Set(\@Plugins, 'RT::Extension::StatusSchemas');
+
+Set(\%StatusSchemaMeta,
+    default => {
+        initial  => ['new'],
+        active   => [qw(open stalled)],
+        inactive => [qw(resolved rejected deleted)],
+        transitions => {
+            new      => [qw(open resolved rejected deleted)],
+            open     => [qw(stalled resolved rejected deleted)],
+            stalled  => [qw(open)],
+            resolved => [qw(open)],
+            rejected => [qw(open)],
+            deleted  => [qw(open)],
+        },
+        actions => {
+            'new -> open'     => ['Open It', 'Respond'],
+            'new -> resolved' => ['Resolve', 'Comment'],
+            'new -> rejected' => ['Reject',  'Respond'],
+            'new -> deleted'  => ['Delete',  ''],
+
+            'open -> stalled'  => ['Stall',   'Comment'],
+            'open -> resolved' => ['Resolve', 'Comment'],
+            'open -> rejected' => ['Reject',  'Respond'],
+            'open -> deleted'  => ['Delete',  'hide'],
+
+            'stalled -> open'  => ['Open It',  ''],
+            'resolved -> open' => ['Re-open',  'Comment'],
+            'rejected -> open' => ['Re-open',  'Comment'],
+            'deleted -> open'  => ['Undelete', ''],
+        },
+    },
+    delivery => {
+        initial  => ['ordered'],
+        active   => ['on way', 'delayed'],
+        inactive => ['delivered'],
+        transitions => {
+            ordered   => ['on way', 'delayed'],
+            'on way'  => ['delivered'],
+            delayed   => ['on way'],
+            delivered => [],
+        },
+        actions => {
+            'ordered -> on way'   => ['Put On Way', 'Respond'],
+            'ordered -> delayed'  => ['Delay',      'Respond'],
+
+            'on way -> delivered' => ['Done',       'Respond'],
+            'delayed -> on way'   => ['Put On Way', 'Respond'],
+        },
+    },
+);
+Set(\%StatusSchemas, delivery => 'delivery');
+END
+}
+
+use RT::Test config => $config;
+
+1;

commit 1b2dd319f69ea4c7b459733acc9de83cbad06e65
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Thu Aug 12 18:00:07 2010 -0400

    merge fixups

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 11699e1..af83f9a 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -1726,19 +1726,51 @@ Set($ApprovalRejectionNotes, 1);
 
 =head1 Miscellaneous Configuration
 
-=over 4
+=cut
+
+    Set( %StatusSchemaMeta,
+        default => {
+            initial  => ['new'],
+            active   => ['open', 'stalled'],
+            inactive => ['resolved', 'rejected', 'deleted'],
+
+            transitions => {
+                # from   => [ to list ],
+                new      => [qw(open resolved rejected deleted)],
+                open     => [qw(stalled resolved rejected deleted)],
+                stalled  => [qw(open)],
+                resolved => [qw(open)],
+                rejected => [qw(open)],
+                deleted  => [qw(open)],
+            },
+            rights  => {
+                '* -> deleted'  => 'DeleteTicket',
+                '* -> rejected' => 'RejectTicket',
+                '* -> *'        => 'ModifyTicketStatus',
+            },
+            actions => {
+                # 'from -> to'    => [action text, Respond/Comment/hide/''],
+                'new -> open'     => ['Open It', 'Respond'],
+                'new -> resolved' => ['Resolve', 'Comment'],
+                'new -> rejected' => ['Reject',  'Respond'],
+                'new -> deleted'  => ['Delete',  ''],
+
+                'open -> stalled'  => ['Stall',   'Comment'],
+                'open -> resolved' => ['Resolve', 'Comment'],
+                'open -> rejected' => ['Reject',  'Respond'],
+                'open -> deleted'  => ['Delete',  'hide'],
+
+                'stalled -> open'  => ['Open It',  ''],
+                'resolved -> open' => ['Re-open',  'Comment'],
+                'rejected -> open' => ['Re-open',  'Comment'],
+                'deleted -> open'  => ['Undelete', ''],
+            },
+        },
+    );
 
-=item C<@ActiveStatus>, C<@InactiveStatus>
 
-You can define new statuses and even reorder existing statuses here.
-WARNING. DO NOT DELETE ANY OF THE DEFAULT STATUSES. If you do, RT
-will break horribly. The statuses you add must be no longer than
-10 characters.
 
-=cut
 
-Set(@ActiveStatus, qw(new open stalled));
-Set(@InactiveStatus, qw(resolved rejected deleted));
 
 =item C<$LinkTransactionsRun1Scrip>
 
diff --git a/etc/RT_SiteConfig.pm b/etc/RT_SiteConfig.pm
index 1661e4d..fd62fb4 100755
--- a/etc/RT_SiteConfig.pm
+++ b/etc/RT_SiteConfig.pm
@@ -15,5 +15,5 @@
 #   perl -c /path/to/your/etc/RT_SiteConfig.pm
 
 Set( $rtname, 'example.com');
-#Set(@Plugins,(qw(Extension::QuickDelete RT::FM)));
+#Set(@Plugins,(qw(Extension::QuickDelete RT::FM RT::IR)));
 1;
diff --git a/lib/RT.pm.in b/lib/RT.pm.in
index 3d2fa7e..1858af2 100755
--- a/lib/RT.pm.in
+++ b/lib/RT.pm.in
@@ -454,6 +454,7 @@ sub InitClasses {
     require RT::Attributes;
     require RT::Dashboard;
     require RT::Approval;
+    require RT::StatusSchemas;
 
     # on a cold server (just after restart) people could have an object
     # in the session, as we deserialize it so we never call constructor
diff --git a/lib/RT/Queue_Overlay.pm b/lib/RT/Queue_Overlay.pm
index cb97479..4680dfd 100755
--- a/lib/RT/Queue_Overlay.pm
+++ b/lib/RT/Queue_Overlay.pm
@@ -109,11 +109,13 @@ our $RIGHTS = {
     CommentOnTicket => 'Comment on tickets',                          # loc_pair
     OwnTicket       => 'Own tickets',                                 # loc_pair
     ModifyTicket    => 'Modify tickets',                              # loc_pair
+    ModifyTicketStatus    => 'Modify ticket status',                              # loc_pair
     DeleteTicket    => 'Delete tickets',                              # loc_pair
+    RejectTicket    => 'Reject tickets',                              # loc_pair
     TakeTicket      => 'Take tickets',                                # loc_pair
     StealTicket     => 'Steal tickets',                               # loc_pair
 
-    ForwardMessage  => 'Forward messages to third person(s)',         # loc_pair
+    ForwardMessage  => 'Forward messages outside of RT',         # loc_pair
 
 };
 
diff --git a/lib/RT/StatusSchemas.pm b/lib/RT/StatusSchemas.pm
index ba718bc..76af64f 100644
--- a/lib/RT/StatusSchemas.pm
+++ b/lib/RT/StatusSchemas.pm
@@ -1,6 +1,5 @@
 use strict;
 use warnings;
-no warnings 'redefine';
 
 package RT::StatusSchemas;
 
diff --git a/share/html/Ticket/Elements/Tabs b/share/html/Ticket/Elements/Tabs
index 3dc47af..8a672c5 100755
--- a/share/html/Ticket/Elements/Tabs
+++ b/share/html/Ticket/Elements/Tabs
@@ -287,7 +287,7 @@ $actions->{'G' . 1}{pre_separator} = 1;
 $actions->{'G' . ($i-1)}{separator} = 1;
 
 
-
+}
 
 my $args = '';
 my $has_query = '';
diff --git a/t/status-schemas/basics.t b/t/status-schemas/basics.t
index e35ae4e..50ab325 100644
--- a/t/status-schemas/basics.t
+++ b/t/status-schemas/basics.t
@@ -4,7 +4,7 @@ use strict;
 use warnings;
 use Data::Dumper;
 
-require 't/utils.pl';
+require 't/status-schemas/utils.pl';
 use Test::More tests => 49;
 
 my $general = RT::Test->load_or_create_queue(
diff --git a/t/status-schemas/utils.pl b/t/status-schemas/utils.pl
index 00c0c65..3534eee 100644
--- a/t/status-schemas/utils.pl
+++ b/t/status-schemas/utils.pl
@@ -3,14 +3,10 @@
 use strict;
 use warnings;
 
-### after: use lib qw(@RT_LIB_PATH@);
-use lib qw(/opt/rt3/local/lib /opt/rt3/lib);
 
 my $config;
 BEGIN {
 $config = <<END;
-Set(\@Plugins, 'RT::Extension::StatusSchemas');
-
 Set(\%StatusSchemaMeta,
     default => {
         initial  => ['new'],

commit 685fb6826428f1e066c207b350dae13e335f732f
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Thu Aug 12 18:18:42 2010 -0400

    updates to SelectStatus to remove a newline in the middle of /option

diff --git a/share/html/Elements/SelectStatus b/share/html/Elements/SelectStatus
index 64bf5d6..f4f1dab 100644
--- a/share/html/Elements/SelectStatus
+++ b/share/html/Elements/SelectStatus
@@ -4,8 +4,7 @@
 %}
 %foreach my $status (@status) {
 %next if ($SkipDeleted && $status eq 'deleted');
-<option value="<%$status%>"<% (defined $Default && $status eq $Default) && qq[ selected="selected"] |n %>><%loc($status)%></o
-ption>
+<option value="<%$status%>"<% (defined $Default && $status eq $Default) && qq[ selected="selected"] |n %>><%loc($status)%></option>
 % }
 </select>
 <%INIT>

commit 085eb5a8b32d3d47afacb4b8caee5c21d726c4a5
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Thu Aug 12 18:19:03 2010 -0400

    now actually RUN all status-schema tests - not all pass

diff --git a/t/status-schemas/basics.t b/t/status-schemas/basics.t
index 50ab325..8f20783 100644
--- a/t/status-schemas/basics.t
+++ b/t/status-schemas/basics.t
@@ -4,8 +4,7 @@ use strict;
 use warnings;
 use Data::Dumper;
 
-require 't/status-schemas/utils.pl';
-use Test::More tests => 49;
+BEGIN {require  't/status-schemas/utils.pl'};
 
 my $general = RT::Test->load_or_create_queue(
     Name => 'General',
@@ -36,6 +35,7 @@ diag "check status input on create";
 {
     $m->goto_create_ticket( $general );
 
+    diag $m->uri;
     my $form = $m->form_name('TicketCreate');
     ok my $input = $form->find_input('Status'), 'found status selector';
 
@@ -44,7 +44,9 @@ diag "check status input on create";
 
     my $valid = 1;
     foreach ( @form_values ) {
-        $valid = 0 unless $general->status_schema->is_valid($_);
+        next if $general->status_schema->is_valid($_);
+        $valid = 0;
+        diag("$_ doesn't appear to be a valid status, but it was in the form");
     }
     ok $valid, 'all statuses in the form are valid';
 }
@@ -67,13 +69,13 @@ diag "new ->(open it)-> open";
         ok scalar @links, 'found links';
         my $found = 1;
         foreach my $t ('Open It', 'Resolve', 'Reject', 'Delete') {
-            $found = 0 unless grep $_->text eq $t, @links;
+            $found = 0 unless grep {($_->text ||'') eq $t} @links;
         }
         ok $found, 'found all transitions';
 
         $found = 0;
         foreach my $t ('Stall', 'Re-open', 'Undelete') {
-            $found = 1 if grep $_->text eq $t, @links;
+            $found = 1 if grep {($_->text||'') eq $t} @links;
         }
         ok !$found, 'no unwanted transitions';
     }
@@ -96,13 +98,13 @@ diag "open ->(stall)-> stalled";
         ok scalar @links, 'found links';
         my $found = 1;
         foreach my $t ('Stall', 'Resolve', 'Reject') {
-            $found = 0 unless grep $_->text eq $t, @links;
+            $found = 0 unless grep { ($_->text ||'') eq $t} @links;
         }
         ok $found, 'found all transitions';
 
         $found = 0;
         foreach my $t ('Open It', 'Delete', 'Re-open', 'Undelete') {
-            $found = 1 if grep $_->text eq $t, @links;
+            $found = 1 if grep { ($_->text ||'') eq $t} @links;
         }
         ok !$found, 'no unwanted transitions';
     }
@@ -125,13 +127,13 @@ diag "stall ->(open it)-> open";
         ok scalar @links, 'found links';
         my $found = 1;
         foreach my $t ('Open It') {
-            $found = 0 unless grep $_->text eq $t, @links;
+            $found = 0 unless grep {($_->text ||'')eq $t} @links;
         }
         ok $found, 'found all transitions';
 
         $found = 0;
         foreach my $t ('Delete', 'Re-open', 'Undelete', 'Stall', 'Resolve', 'Reject') {
-            $found = 1 if grep $_->text eq $t, @links;
+            $found = 1 if grep { ($_->text ||'') eq $t} @links;
         }
         ok !$found, 'no unwanted transitions';
     }
diff --git a/t/status-schemas/dates.t b/t/status-schemas/dates.t
index a924d49..146d287 100644
--- a/t/status-schemas/dates.t
+++ b/t/status-schemas/dates.t
@@ -4,8 +4,7 @@ use strict;
 use warnings;
 use Data::Dumper;
 
-require 't/utils.pl';
-use Test::More tests => 82;
+BEGIN {require 't/status-schemas/utils.pl'};
 
 my $general = RT::Test->load_or_create_queue(
     Name => 'General',
diff --git a/t/status-schemas/moving.t b/t/status-schemas/moving.t
index b68979e..7f448bf 100644
--- a/t/status-schemas/moving.t
+++ b/t/status-schemas/moving.t
@@ -4,8 +4,7 @@ use strict;
 use warnings;
 use Data::Dumper;
 
-require 't/utils.pl';
-use Test::More tests => 18;
+BEGIN {require 't/status-schemas/utils.pl'};
 
 my $general = RT::Test->load_or_create_queue(
     Name => 'General',

commit 37d672db64910bc60f1fb3cbd7f30148dbf7a136
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Fri Aug 13 17:41:21 2010 -0400

    Update the "Default" status scheme to better match RT's 3.8's standard.

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index af83f9a..e240773 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -1730,23 +1730,23 @@ Set($ApprovalRejectionNotes, 1);
 
     Set( %StatusSchemaMeta,
         default => {
-            initial  => ['new'],
+            initial  => ['new', 'open'],
             active   => ['open', 'stalled'],
             inactive => ['resolved', 'rejected', 'deleted'],
 
             transitions => {
                 # from   => [ to list ],
-                new      => [qw(open resolved rejected deleted)],
-                open     => [qw(stalled resolved rejected deleted)],
-                stalled  => [qw(open)],
-                resolved => [qw(open)],
-                rejected => [qw(open)],
-                deleted  => [qw(open)],
+                new      => [qw(open stalled resolved rejected deleted)],
+                open     => [qw(new stalled resolved rejected deleted)],
+                stalled  => [qw(new open rejected resolved deleted)],
+                resolved => [qw(new open stalled rejected deleted)],
+                rejected => [qw(new open stalled resolved deleted)],
+                deleted  => [qw(new open stalled rejected resolved)],
             },
             rights  => {
                 '* -> deleted'  => 'DeleteTicket',
-                '* -> rejected' => 'RejectTicket',
-                '* -> *'        => 'ModifyTicketStatus',
+                '* -> rejected' => 'ModifyTicket',
+                '* -> *'        => 'ModifyTicket',
             },
             actions => {
                 # 'from -> to'    => [action text, Respond/Comment/hide/''],
@@ -1764,6 +1764,7 @@ Set($ApprovalRejectionNotes, 1);
                 'resolved -> open' => ['Re-open',  'Comment'],
                 'rejected -> open' => ['Re-open',  'Comment'],
                 'deleted -> open'  => ['Undelete', ''],
+                'open -> new'      => ['New', 'hide'],
             },
         },
     );

commit f40cc8e9b8eb465697762fcd3e1a6a877afc9b2c
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Fri Aug 13 17:42:34 2010 -0400

    warnings avoidance / cleanup

diff --git a/lib/RT/Rule.pm b/lib/RT/Rule.pm
index 43bf402..14fc9d6 100644
--- a/lib/RT/Rule.pm
+++ b/lib/RT/Rule.pm
@@ -56,7 +56,12 @@ use constant _Queue => undef;
 
 sub Prepare {
     my $self = shift;
-    return (0) if $self->_Queue && $self->TicketObj->QueueObj->Name ne $self->_Queue;
+    if (   $self->_Queue
+        && $self->TicketObj->QueueObj->Name
+        && ( $self->TicketObj->QueueObj->Name ne $self->_Queue ) )
+    {
+        return (0);
+    }
     return 1;
 }
 
diff --git a/lib/RT/Ticket_Overlay.pm b/lib/RT/Ticket_Overlay.pm
index 2c32350..62fff27 100755
--- a/lib/RT/Ticket_Overlay.pm
+++ b/lib/RT/Ticket_Overlay.pm
@@ -399,8 +399,9 @@ sub Create {
     elsif ( $args{'Owner'} ) {
         $Owner = RT::User->new( $self->CurrentUser );
         $Owner->Load( $args{'Owner'} );
-        $Owner->LoadByEmail( $args{'Owner'} )
-            unless $Owner->Id;
+        if (!$Owner->id) {
+            $Owner->LoadByEmail( $args{'Owner'} )
+        }
         unless ( $Owner->Id ) {
             push @non_fatal_errors,
                 $self->loc("Owner could not be set.") . " "
@@ -3315,7 +3316,7 @@ sub _ApplyTransactionBatch {
     my $batch = $self->TransactionBatch;
 
     my %seen;
-    my $types = join ',', grep !$seen{$_}++, grep defined, map $_->Type, grep defined, @{$batch};
+    my $types = join ',', grep !$seen{$_}++, grep defined, map $_->__Value('Type'), grep defined, @{$batch};
 
     require RT::Scrips;
     RT::Scrips->new($RT::SystemUser)->Apply(

commit d365721cb65aea68dda6c3239bcd0dd3137e67b1
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Fri Aug 13 17:42:50 2010 -0400

    Warnings avoidance

diff --git a/share/html/REST/1.0/Forms/ticket/default b/share/html/REST/1.0/Forms/ticket/default
index 0d403b2..4ceb463 100755
--- a/share/html/REST/1.0/Forms/ticket/default
+++ b/share/html/REST/1.0/Forms/ticket/default
@@ -280,7 +280,7 @@ else {
             $key = $simple{$key};
             $set = "Set$key";
 
-            next if (($val eq $ticket->$key)|| ($ticket->$key =~ /^\d+$/ && $val == $ticket->$key));
+            next if (($val eq $ticket->$key)|| ($ticket->$key =~ /^\d+$/ && $val =~ /^\d+$/ && $val == $ticket->$key));
             ($n, $s) = $ticket->$set("$val");
         }
         elsif (exists $dates{$key}) {

commit 2a9de87c5280d9fcb6ea6afe994bee8be5eeeca6
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Fri Aug 13 17:45:41 2010 -0400

    Further work to integrate status schemas

diff --git a/lib/RT/Ticket_Overlay.pm b/lib/RT/Ticket_Overlay.pm
index 62fff27..252068a 100755
--- a/lib/RT/Ticket_Overlay.pm
+++ b/lib/RT/Ticket_Overlay.pm
@@ -354,7 +354,17 @@ sub Create {
     if ( defined $args{'Started'} ) {
         $Started->Set( Format => 'ISO', Value => $args{'Started'} );
     }
-    elsif ( $args{'Status'} ne 'new' ) {
+    # We sort of want to ask "is this not an initial state
+    # But some schemes require active statuses to be duplicated
+    # in the initial state list so that tickets can be created
+    # in those states already open
+    #
+    # Instead, we check to make sure that it's either an "active" or "inactive" status
+    elsif (
+        $QueueObj->status_schema->is_active($args{'Status'}) ||
+        $QueueObj->status_schema->is_inactive($args{'Status'})
+
+    ){
         $Started->SetToNow;
     }
 
@@ -364,7 +374,7 @@ sub Create {
     }
 
     #If the status is an inactive status, set the resolved date
-    elsif ( $QueueObj->IsInactiveStatus( $args{'Status'} ) )
+    elsif ( $QueueObj->status_schema->is_inactive( $args{'Status'} ) )
     {
         $RT::Logger->debug( "Got a ". $args{'Status'}
             ."(inactive) ticket with undefined resolved date. Setting to now."
@@ -3058,10 +3068,9 @@ sub SetStatus {
     my $new = $args{'Status'};
     unless ( $schema->is_valid( $new ) ) {
         return (0,
-            $self->loc("Status '[_1]' is not valid for schema '[_2]'.",
-                $self->loc($new), $self->loc($schema->name)
-            )
-        );
+            $self->loc("Status '[_1]' isn't a valid status for tickets in this queue.",
+                $self->loc($new)
+        ));
     }
 
     my $old = $self->__Value('Status');
@@ -3088,7 +3097,7 @@ sub SetStatus {
     $now->SetToNow();
 
     #If we're changing the status from new, record that we've started
-    if ( $schema->is_initial($old) && !$schema->is_initial($new) ) {
+    if ( $schema->is_initial($old) && $schema->is_active($new) ) {
         #Set the Started time to "now"
         $self->_Set(
             Field             => 'Started',
diff --git a/share/html/Elements/SelectStatus b/share/html/Elements/SelectStatus
index f4f1dab..097882d 100644
--- a/share/html/Elements/SelectStatus
+++ b/share/html/Elements/SelectStatus
@@ -8,35 +8,24 @@
 % }
 </select>
 <%INIT>
-my $caller = $m->callers(1)->path;
-if ( $caller eq '/Ticket/Update.html' ) {
-    $Ticket = LoadTicket( $m->caller_args(1)->{'id'} );
-}
-elsif ( $caller eq '/Ticket/Create.html' ) {
-    $Queue = RT::Queue->new( $session{'CurrentUser'} );
-    $Queue->Load( $m->caller_args(1)->{'Queue'} );
-}
-elsif ( $caller eq '/Ticket/Elements/EditBasics' ) {
-    $Ticket = $m->caller_args(1)->{'TicketObj'};
-}
 ### XXX: no cover for Tools/MyDay.html
 
 
 my @status;
-if ( $Ticket ) {
-    my $current = $Ticket->Status;
-    my $schema = $Ticket->QueueObj->status_schema;
+if ( $TicketObj ) {
+    my $current = $TicketObj->Status;
+    my $schema = $TicketObj->QueueObj->status_schema;
 
     my %has = ();
     foreach my $next ( $schema->transitions( $current ) ) {
         my $check = $schema->check_right( $current => $next );
-        $has{ $check } = $Ticket->CurrentUserHasRight( $check )
+        $has{ $check } = $TicketObj->CurrentUserHasRight( $check )
             unless exists $has{ $check };
         push @status, $next if $has{ $check };
     }
 }
-elsif ( $Queue ) {
-    @status = $Queue->status_schema->valid;
+elsif ( $QueueObj ) {
+    @status = $QueueObj->status_schema->valid;
 }
 else {
     @status = RT::Queue->status_schema->valid;
@@ -44,8 +33,8 @@ else {
 </%INIT>
 <%ARGS>
 $Name => undef
-$Ticket => undef
-$Queue => undef
+$TicketObj => undef
+$QueueObj => undef
 $Default => ''
 $SkipDeleted => 0
 $DefaultValue => 1
diff --git a/share/html/Ticket/Create.html b/share/html/Ticket/Create.html
index 5a01466..8537714 100755
--- a/share/html/Ticket/Create.html
+++ b/share/html/Ticket/Create.html
@@ -87,6 +87,7 @@
                     Default         => $ARGS{Status} || 'new',
                     DefaultValue    => 0,
                     SkipDeleted     => 1,
+                    QueueObj        => $QueueObj
                 },
             },
             {   name => 'Owner',
diff --git a/share/html/Ticket/Elements/EditBasics b/share/html/Ticket/Elements/EditBasics
index 59345ea..5d33858 100755
--- a/share/html/Ticket/Elements/EditBasics
+++ b/share/html/Ticket/Elements/EditBasics
@@ -71,6 +71,8 @@ unless ( @fields ) {
                 Name => 'Queue',
                 Default => $defaults{'Queue'} || $TicketObj->QueueObj->Id,
                 ShowNullOption => 0,
+                TicketObj => $TicketObj,
+                QueueObj => $TicketObj->QueueObj
             }
         },
         {   name => 'Owner',
diff --git a/share/html/Ticket/Elements/Tabs b/share/html/Ticket/Elements/Tabs
index 8a672c5..98f3788 100755
--- a/share/html/Ticket/Elements/Tabs
+++ b/share/html/Ticket/Elements/Tabs
@@ -45,15 +45,8 @@
 %# those contributions and any derivatives thereof.
 %# 
 %# END BPS TAGGED BLOCK }}}
-% $m->callback( Ticket => $Ticket, actions=> $actions, tabs => $tabs, %ARGS, args => \%ARGS );
-<& /Elements/Tabs, 
-    tabs => $tabs, 
-    actions => $actions, 
-    current_tab => $current_tab, 
-    current_toptab => $ARGS{current_toptab} || 'Search/Build.html',
-    Title => $Title &> 
 <%INIT>
-my $tabs = {};
+my $tabs       = {};
 my $searchtabs = {};
 my $actions;
 
@@ -61,11 +54,10 @@ if ($Ticket) {
 
     my $id = $Ticket->id();
 
-my $current = $Ticket->Status;
-my $schema = $Ticket->QueueObj->status_schema;
-my %has;
-my $i = 1;
-
+    my $current = $Ticket->Status;
+    my $schema  = $Ticket->QueueObj->status_schema;
+    my %has;
+    my $i = 1;
 
     if ( defined $session{'tickets'} ) {
 
@@ -90,8 +82,7 @@ my $i = 1;
             }
             $searchtabs->{"_b"} = {
                 class => "nav",
-                path  => "Ticket/Display.html?id="
-                    . $item_map->{ $Ticket->Id }->{prev},
+                path  => "Ticket/Display.html?id=" . $item_map->{ $Ticket->Id }->{prev},
                 title => '< ' . loc('Prev')
             };
         }
@@ -100,8 +91,7 @@ my $i = 1;
         if ( $item_map->{ $Ticket->Id }->{next} ) {
             $searchtabs->{'d'} = {
                 class => "nav",
-                path  => "Ticket/Display.html?id="
-                    . $item_map->{ $Ticket->Id }->{next},
+                path  => "Ticket/Display.html?id=" . $item_map->{ $Ticket->Id }->{next},
                 title => loc('Next') . ' >'
             };
             if ( $item_map->{last} ) {
@@ -122,14 +112,20 @@ my $i = 1;
     };
 
     my %can = (
-        ModifyTicket => $Ticket->CurrentUserHasRight('ModifyTicket'),
+        ModifyTicket      => $Ticket->CurrentUserHasRight('ModifyTicket'),
+        OwnTicket         => $Ticket->CurrentUserHasRight('OwnTicket'),
+        StealTicket         => $Ticket->CurrentUserHasRight('StealTicket'),
+        TakeTicket         => $Ticket->CurrentUserHasRight('TakeTicket'),
+        ReplyToTicket     => $Ticket->CurrentUserHasRight('ReplyToTicket'),
+        CommentOnTicket     => $Ticket->CurrentUserHasRight('CommentOnTicket'),
         ModifyCustomField => $Ticket->CurrentUserHasRight('ModifyCustomField'),
+        _ModifyOwner      => (
+                   $Ticket->CurrentUserHasRight('OwnTicket')
+                || $Ticket->CurrentUserHasRight('TakeTicket')
+                || $Ticket->CurrentUserHasRight('StealTicket')
+        )
     );
 
-    $can{_ModifyOwner} = $Ticket->CurrentUserHasRight('OwnTicket')
-                      || $Ticket->CurrentUserHasRight('TakeTicket')
-                      || $Ticket->CurrentUserHasRight('StealTicket');
-
     my $ticket_page_tabs = {
         _A => {
             title => loc('Display'),
@@ -142,21 +138,21 @@ my $i = 1;
         },
     };
 
-    if ($can{ModifyTicket} || $can{ModifyCustomField}) {
+    if ( $can{ModifyTicket} || $can{ModifyCustomField} ) {
         $ticket_page_tabs->{_B} = {
             title => loc('Basics'),
             path  => "Ticket/Modify.html?id=" . $id,
         };
     }
 
-    if ($can{ModifyTicket} || $can{_ModifyOwner}) {
+    if ( $can{ModifyTicket} || $can{_ModifyOwner} ) {
         $ticket_page_tabs->{_D} = {
             title => loc('People'),
             path  => "Ticket/ModifyPeople.html?id=" . $id,
         };
     }
 
-    if ($can{ModifyTicket}) {
+    if ( $can{ModifyTicket} ) {
         $ticket_page_tabs->{_C} = {
             title => loc('Dates'),
             path  => "Ticket/ModifyDates.html?id=" . $id,
@@ -167,7 +163,7 @@ my $i = 1;
         };
     }
 
-    if ($can{ModifyTicket} || $can{ModifyCustomField} || $can{_ModifyOwner}) {
+    if ( $can{ModifyTicket} || $can{ModifyCustomField} || $can{_ModifyOwner} ) {
         $ticket_page_tabs->{_X} = {
             title => loc('Jumbo'),
             path  => "Ticket/ModifyAll.html?id=" . $id,
@@ -185,23 +181,20 @@ my $i = 1;
     foreach my $tab ( sort keys %{$ticket_page_tabs} ) {
         if ( $ticket_page_tabs->{$tab}->{'path'} eq $current_tab ) {
             $ticket_page_tabs->{$tab}->{"subtabs"} = $subtabs;
-            $tabs->{'this'}->{"current_subtab"}
-                = $ticket_page_tabs->{$tab}->{"path"};
+            $tabs->{'this'}->{"current_subtab"} = $ticket_page_tabs->{$tab}->{"path"};
         }
     }
     $tabs->{'this'}->{"subtabs"} = $ticket_page_tabs;
     $current_tab = "Ticket/Display.html?id=" . $id;
 
-    if ( $can{'ModifyTicket'} or $Ticket->CurrentUserHasRight('ReplyToTicket') )
-    {
+    if ( $can{'ModifyTicket'} or $can{'ReplyToTicket'} ) {
         $actions->{'F'} = {
             title => loc('Reply'),
-            path  => "Ticket/Update.html?Action=Respond&id=" . $id,
+            path  => "Ticket/Update.html?Action=Respond;id=" . $id,
         };
     }
 
-    if ( $Ticket->CurrentUserHasRight('ForwardMessage') )
-    {
+    if ( $Ticket->CurrentUserHasRight('ForwardMessage') ) {
         $actions->{'FA'} = {
             title => loc('Forward'),
             path  => "Ticket/Forward.html?id=" . $id,
@@ -240,84 +233,87 @@ my $i = 1;
                 title => loc( $schema->transition_label( $current => $next ) ),
             };
         }
-        if ( $Ticket->CurrentUserHasRight('OwnTicket') ) {
-            if ( $Ticket->OwnerObj->Id == $RT::Nobody->id ) {
-                $actions->{'B'} = {
-                    path  => "Ticket/Display.html?Action=Take&id=" . $id,
-                    title => loc('Take'),
-                    }
-                    if $can{'ModifyTicket'}
-                        or $Ticket->CurrentUserHasRight('TakeTicket');
-            } elsif ( $Ticket->OwnerObj->id != $session{CurrentUser}->id ) {
-                $actions->{'C'} = {
-                    path  => "Ticket/Display.html?Action=Steal&id=" . $id,
-                    title => loc('Steal'),
-                    }
-                    if $can{'ModifyTicket'}
-                        or $Ticket->CurrentUserHasRight('StealTicket');
-            }
-        }
-
-        if (   $can{'ModifyTicket'}
-            or $Ticket->CurrentUserHasRight('CommentOnTicket') )
+    }
+    if ( $can{OwnTicket} ) {
+        if ($Ticket->OwnerObj->Id == $RT::Nobody->id
+            && (   $can{'ModifyTicket'} or $can{'TakeTicket'})
+            )
         {
-            $actions->{'E'} = {
-                title => loc('Comment'),
-                path  => "Ticket/Update.html?Action=Comment&id=" . $id,
+            $actions->{'B'} = {
+                path  => "Ticket/Display.html?Action=Take;id=" . $id,
+                title => loc('Take'),
+                }
+
+        } elsif ( $Ticket->OwnerObj->id != $RT::Nobody->id
+                    && $Ticket->OwnerObj->id != $session{CurrentUser}->id
+                    && ( $can{'ModifyTicket'} or $can{'StealTicket'})
+                )
+        {
+
+            $actions->{'C'} = {
+                path  => "Ticket/Display.html?Action=Steal;id=" . $id,
+                title => loc('Steal'),
             };
         }
+    }
 
-        $actions->{'_ZZ'} = { html => $m->scomp( '/Ticket/Elements/Bookmark', id => $Ticket->id ), };
-
+    if (   $can{'ModifyTicket'} || $can{'CommentOnTicket'}) {
+        $actions->{'E'} = {
+            title => loc('Comment'),
+            path  => "Ticket/Update.html?Action=Comment;id=" . $id,
+        };
     }
 
-if ( (  defined $actions->{B} || defined $actions->{C} )
+    $actions->{'_ZZ'} = { html => $m->scomp( '/Ticket/Elements/Bookmark', id => $Ticket->id ), };
+
+
+if (( defined $actions->{B} || defined $actions->{C} )
     && (   defined $actions->{E}
         || defined $actions->{F}
-        || defined $actions->{G} ) )
+        || defined $actions->{G} )
+    )
 {
 
     if    ( defined $actions->{C} ) { $actions->{C}->{separator} = 1 }
     elsif ( defined $actions->{B} ) { $actions->{B}->{separator} = 1 }
 }
 
-$actions->{'G' . 1}{pre_separator} = 1;
+$actions->{ 'G' . 1 }{pre_separator} = 1;
 
 # Separator between the last status and the next set of actions
-$actions->{'G' . ($i-1)}{separator} = 1;
-
-
+$actions->{ 'G' . ( $i - 1 ) }{separator} = 1;
 }
 
-my $args = '';
+my $args      = '';
 my $has_query = '';
 my %query_args;
-my $search_id = $ARGS{'SavedSearchId'}
-            || $session{'CurrentSearchHash'}->{'SearchId'} || '';
+my $search_id
+    = $ARGS{'SavedSearchId'}
+    || $session{'CurrentSearchHash'}->{'SearchId'}
+    || '';
 my $chart_search_id = $ARGS{'SavedChartSearchId'} || '';
 
 $has_query = 1 if ( $ARGS{'Query'} or $session{'CurrentSearchHash'}->{'Query'} );
-  
-%query_args = (
-        SavedSearchId => ($search_id eq 'new') ? undef : $search_id,
-        SavedChartSearchId => $chart_search_id,
-        Query  => $ARGS{'Query'}  || $session{'CurrentSearchHash'}->{'Query'},
-        Format => $ARGS{'Format'} || $session{'CurrentSearchHash'}->{'Format'},
-        OrderBy => $ARGS{'OrderBy'}
-            || $session{'CurrentSearchHash'}->{'OrderBy'},
-        Order => $ARGS{'Order'} || $session{'CurrentSearchHash'}->{'Order'},
-        Page  => $ARGS{'Page'}  || $session{'CurrentSearchHash'}->{'Page'},
-        RowsPerPage  => $ARGS{'RowsPerPage'}  || $session{'CurrentSearchHash'}->{'RowsPerPage'},
-    );
 
-    $args = "?" . $m->comp( '/Elements/QueryString', %query_args );
+%query_args = (
+    SavedSearchId => ( $search_id eq 'new' ) ? undef : $search_id,
+    SavedChartSearchId => $chart_search_id,
+    Query              => $ARGS{'Query'} || $session{'CurrentSearchHash'}->{'Query'},
+    Format             => $ARGS{'Format'} || $session{'CurrentSearchHash'}->{'Format'},
+    OrderBy            => $ARGS{'OrderBy'} || $session{'CurrentSearchHash'}->{'OrderBy'},
+    Order              => $ARGS{'Order'} || $session{'CurrentSearchHash'}->{'Order'},
+    Page               => $ARGS{'Page'} || $session{'CurrentSearchHash'}->{'Page'},
+    RowsPerPage        => $ARGS{'RowsPerPage'} || $session{'CurrentSearchHash'}->{'RowsPerPage'},
+);
+
+$args = "?" . $m->comp( '/Elements/QueryString', %query_args );
 
 $tabs->{"f"} = {
     path  => "Search/Build.html?NewQuery=1",
     title => loc('New Search')
 };
 $tabs->{"g"} = {
-    path  => "Search/Build.html" . (($has_query) ? $args : ''),
+    path => "Search/Build.html" . ( ($has_query) ? $args : '' ),
     title => loc('Edit Search')
 };
 $tabs->{"h"} = {
@@ -325,14 +321,13 @@ $tabs->{"h"} = {
     title     => loc('Advanced'),
     separator => 1
 };
+
 if ($has_query) {
 
     if ( $current_tab =~ m{Search/Results.html} ) {
         $current_tab = "Search/Results.html$args";
 
-        if ( $session{'CurrentUser'}
-            ->HasRight( Right => 'SuperUser', Object => $RT::System ) )
-        {
+        if ( $session{'CurrentUser'}->HasRight( Right => 'SuperUser', Object => $RT::System ) ) {
             my $shred_args = $m->comp(
                 '/Elements/QueryString',
                 Search          => 1,
@@ -371,10 +366,23 @@ if ($has_query) {
 
 foreach my $searchtab ( keys %{$searchtabs} ) {
     ( $searchtab =~ /^_/ )
-        ? $tabs->{ "s" . $searchtab } = $searchtabs->{$searchtab}
+        ? $tabs->{ "s" . $searchtab }
+        = $searchtabs->{$searchtab}
         : $tabs->{ "z_" . $searchtab } = $searchtabs->{$searchtab};
 }
 
+$m->callback( Ticket => $Ticket, actions => $actions, tabs => $tabs, %ARGS, args => \%ARGS );
+$m->comp(
+    "/Elements/Tabs",
+    tabs           => $tabs,
+    actions        => $actions,
+    current_tab    => $current_tab,
+    current_toptab => $ARGS{current_toptab} || 'Search/Build.html',
+    Title          => $Title
+);
+
+return;
+
 </%INIT>
 
   
diff --git a/share/html/Ticket/Update.html b/share/html/Ticket/Update.html
index 2e7b8ee..38e9f48 100755
--- a/share/html/Ticket/Update.html
+++ b/share/html/Ticket/Update.html
@@ -94,6 +94,8 @@
                 Name => 'Status',
                 DefaultLabel => loc("[_1] (Unchanged)", loc($TicketObj->Status)),
                 Default => $ARGS{'Status'} || ($TicketObj->Status eq $DefaultStatus ? undef : $DefaultStatus),
+                TicketObj => $TicketObj,
+                QueueObj => $TicketObj->QueueObj
             },
         },
         {   name => 'Owner',
diff --git a/t/shredder/03plugin_tickets.t b/t/shredder/03plugin_tickets.t
index 3d742ff..2ee8f2f 100644
--- a/t/shredder/03plugin_tickets.t
+++ b/t/shredder/03plugin_tickets.t
@@ -31,7 +31,6 @@ use_ok('RT::Tickets');
     my $parent = RT::Ticket->new( $RT::SystemUser );
     my ($pid) = $parent->Create( Subject => 'parent', Queue => 1 );
     ok( $pid, "created new ticket" );
-
     my $child = RT::Ticket->new( $RT::SystemUser );
     my ($cid) = $child->Create( Subject => 'child', Queue => 1, MemberOf => $pid );
     ok( $cid, "created new ticket" );
@@ -110,15 +109,17 @@ cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint"
 
 { # create parent and child and check functionality of 'apply_query_to_linked' arg
     my $parent = RT::Ticket->new( $RT::SystemUser );
-    my ($pid) = $parent->Create( Subject => 'parent', Queue => 1, Status => 'resolved' );
+    my ($pid) = $parent->Create( Subject => 'parent', Queue => 1 );
     ok( $pid, "created new ticket" );
+    $parent->SetStatus('resolved');
 
     my $child1 = RT::Ticket->new( $RT::SystemUser );
     my ($cid1) = $child1->Create( Subject => 'child', Queue => 1, MemberOf => $pid );
     ok( $cid1, "created new ticket" );
     my $child2 = RT::Ticket->new( $RT::SystemUser );
-    my ($cid2) = $child2->Create( Subject => 'child', Queue => 1, MemberOf => $pid, Status => 'resolved' );
+    my ($cid2) = $child2->Create( Subject => 'child', Queue => 1, MemberOf => $pid);
     ok( $cid2, "created new ticket" );
+    $child2->SetStatus('resolved');
 
     my $plugin = new RT::Shredder::Plugin::Tickets;
     isa_ok($plugin, 'RT::Shredder::Plugin::Tickets');
diff --git a/t/status-schemas/basics.t b/t/status-schemas/basics.t
index 8f20783..4c9222f 100644
--- a/t/status-schemas/basics.t
+++ b/t/status-schemas/basics.t
@@ -23,9 +23,9 @@ diag "check basic API";
     my $schema = $general->status_schema;
     isa_ok($schema, 'RT::StatusSchema');
     is $schema->name, 'default', "it's a default schema";
-    is join(', ', $schema->valid),
-        join(', ', qw(new open stalled resolved rejected deleted)),
-        'this is default set';
+    is join(', ', sort $schema->valid),
+        join(', ', sort qw(new open stalled resolved rejected deleted)),
+        'this is the default set from our config file';
 }
 
 my ($baseurl, $m) = RT::Test->started_ok;
@@ -48,6 +48,8 @@ diag "check status input on create";
         $valid = 0;
         diag("$_ doesn't appear to be a valid status, but it was in the form");
     }
+
+
     ok $valid, 'all statuses in the form are valid';
 }
 
diff --git a/t/status-schemas/dates.t b/t/status-schemas/dates.t
index 146d287..c42a73d 100644
--- a/t/status-schemas/dates.t
+++ b/t/status-schemas/dates.t
@@ -125,8 +125,9 @@ diag "dates on create for delivery schema";
             Status => 'ordered',
         );
         ok $id, 'created a ticket';
-        ok $ticket->StartedObj->Unix <= 0, 'started is not set';
-        ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+        is $ticket->StartedObj->Unix , 0, 'started is not set';
+        is $ticket->ResolvedObj->Unix, 0, 'resolved is not set';
+
     }
     {
         my $ticket = RT::Ticket->new( $RT::SystemUser );
diff --git a/t/status-schemas/utils.pl b/t/status-schemas/utils.pl
index 3534eee..44bd2dd 100644
--- a/t/status-schemas/utils.pl
+++ b/t/status-schemas/utils.pl
@@ -9,7 +9,7 @@ BEGIN {
 $config = <<END;
 Set(\%StatusSchemaMeta,
     default => {
-        initial  => ['new'],
+        initial  => [qw(new open resolved )],
         active   => [qw(open stalled)],
         inactive => [qw(resolved rejected deleted)],
         transitions => {
diff --git a/t/web/command_line.t b/t/web/command_line.t
index 3fc279b..88cb9df 100644
--- a/t/web/command_line.t
+++ b/t/web/command_line.t
@@ -269,7 +269,7 @@ expect_send("show ticket/$ticket_id -f status", 'Verifying change...');
 expect_like(qr/Status: resolved/, 'Verified change');
 # try to set status to an illegal value
 expect_send("edit ticket/$ticket_id set status=quux", 'Changing status to an illegal value...');
-expect_like(qr/illegal value/i, 'Errored out');
+expect_like(qr/isn't a valid status/i, 'Errored out');
 expect_send("show ticket/$ticket_id -f status", 'Verifying lack of change...');
 expect_like(qr/Status: resolved/, 'Verified change');
 

commit 37262c5ac9737939e851a34c1add29f7e28b549b
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Fri Aug 13 17:46:29 2010 -0400

    grammar cleanup

diff --git a/lib/RT/Ticket_Overlay.pm b/lib/RT/Ticket_Overlay.pm
index 252068a..adb1931 100755
--- a/lib/RT/Ticket_Overlay.pm
+++ b/lib/RT/Ticket_Overlay.pm
@@ -399,8 +399,10 @@ sub Create {
         if ( $args{'Owner'}->id ) {
             $Owner = $args{'Owner'};
         } else {
-            $RT::Logger->error('passed not loaded owner object');
-            push @non_fatal_errors, $self->loc("Invalid owner object");
+            $RT::Logger->error('Passed an empty RT::User for owner');
+            push @non_fatal_errors,
+                $self->loc("Owner could not be set.") . " ".
+            $self->loc("Invalid value for [_1]",loc('owner'));
             $Owner = undef;
         }
     }

commit 74ee3c9b8be4de3a4bf3b4d1e7e611f2ff54b56e
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Fri Aug 13 17:46:47 2010 -0400

    Warning avoidance

diff --git a/share/html/Elements/PageLayout b/share/html/Elements/PageLayout
index e31e27f..a3e5c03 100755
--- a/share/html/Elements/PageLayout
+++ b/share/html/Elements/PageLayout
@@ -155,7 +155,7 @@
     <li<% $class->{li} ? qq[ class="$class->{li}"] : ''|n %>><% $count > 1 && !$postsep && qq[<span class="bullet">&#183; </span>]|n%>
 %         if ($type->{"$action"}->{'html'}) {
       <% $type->{"$action"}->{'html'} | n %>
-%         } else {
+%         } elsif ($type->{$action}->{path})  {
       <a href="<%RT->Config->Get('WebPath')%>/<%$type->{$action}->{'path'}%>"<% $type->{$action}->{class} && ' class="'.$type->{$action}->{class}.'"' |n %><% $type->{$action}->{id} && ' id="'.$type->{$action}->{id}.'"' |n %>><%$type->{$action}->{'title'}%></a>
 %         }
     </li>

commit 14d9502fe2f2d5a9ab588995a57424260701c437
Author: Jesse Vincent <jesse at bestpractical.com>
Date:   Fri Aug 13 19:14:37 2010 -0400

    Add a new "initial default status" to status schemas, since not every
    schema will have "new"
    
    Fix status schema tests to take into account that RT now prevents you
    from creating a ticket with a status that's not a valid initial status.
    
    All tests pass here. Pushing

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index e240773..12d209f 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -1730,6 +1730,7 @@ Set($ApprovalRejectionNotes, 1);
 
     Set( %StatusSchemaMeta,
         default => {
+            default_initial => 'new',
             initial  => ['new', 'open'],
             active   => ['open', 'stalled'],
             inactive => ['resolved', 'rejected', 'deleted'],
diff --git a/lib/RT/StatusSchema.pm b/lib/RT/StatusSchema.pm
index 88d00ed..cfdb638 100644
--- a/lib/RT/StatusSchema.pm
+++ b/lib/RT/StatusSchema.pm
@@ -193,6 +193,19 @@ sub is_initial {
     return scalar grep lc($_) eq $value, $self->valid('initial');
 }
 
+
+=head3 default_initial
+
+Returns the "default" initial status for this schema
+
+=cut
+
+sub default_initial {
+    my $self = shift;
+    return $self->{data}->{default_initial};
+}
+
+
 =head3 active
 
 Returns an array of all active statuses for this schema.
@@ -407,7 +420,7 @@ sub create {
     return (0, loc('Already exist'))
         if $STATUS_SCHEMAS_CACHE{ $name };
 
-    foreach my $method (qw(_set_statuses _set_transitions _set_actions)) {
+    foreach my $method (qw(_set_defaults _set_statuses _set_transitions _set_actions)) {
         my ($status, $msg) = $self->$method( %args, name => $name );
         return ($status, $msg) unless $status;
     }
@@ -438,6 +451,9 @@ sub set_statuses {
     return (1, loc('Updated schema'));
 }
 
+
+
+
 sub set_transitions {
     my $self = shift;
     my %args = @_;
@@ -567,6 +583,21 @@ sub _set_statuses {
     return 1;
 }
 
+
+sub _set_defaults {
+    my $self = shift;
+    my %args = @_;
+
+    $STATUS_SCHEMAS{ $args{'name'} }{$_ } = $args{ $_ }
+        foreach qw(default_initial);
+
+    return 1;
+}
+
+
+
+
+
 sub _set_transitions {
     my $self = shift;
     my %args = @_;
diff --git a/lib/RT/Ticket_Overlay.pm b/lib/RT/Ticket_Overlay.pm
index adb1931..c47f00d 100755
--- a/lib/RT/Ticket_Overlay.pm
+++ b/lib/RT/Ticket_Overlay.pm
@@ -259,7 +259,7 @@ sub Create {
         InitialPriority    => undef,
         FinalPriority      => undef,
         Priority           => undef,
-        Status             => 'new',
+        Status             => undef,
         TimeWorked         => "0",
         TimeLeft           => 0,
         TimeEstimated      => 0,
@@ -306,9 +306,15 @@ sub Create {
             $self->loc( "No permission to create tickets in the queue '[_1]'", $QueueObj->Name));
     }
 
+    if ( ! defined $args{'Status'}) {
+        $args{'Status'} = $QueueObj->status_schema->default_initial();
+    }
+
     unless ( $QueueObj->IsValidStatus( $args{'Status'} ) 
             && $QueueObj->status_schema->is_initial( $args{'Status'} )) {
-        return ( 0, 0, $self->loc('Invalid value for status') );
+        return ( 0, 0,
+            $self->loc("Status '[_1]' isn't a valid status for tickets in this queue.",
+                $self->loc($args{'Status'})));
     }
     
 
@@ -1730,13 +1736,7 @@ sub SetQueue {
     if ( $NewQueueObj->Id == $self->QueueObj->Id ) {
         return ( 0, $self->loc('That is the same value') );
     }
-    unless (
-        $self->CurrentUser->HasRight(
-            Right    => 'CreateTicket',
-            Object => $NewQueueObj
-        )
-      )
-    {
+    unless ( $self->CurrentUser->HasRight( Right    => 'CreateTicket', Object => $NewQueueObj)) {
         return ( 0, $self->loc("You may not create requests in that queue.") );
     }
 
@@ -1752,12 +1752,7 @@ sub SetQueue {
             unless $new_status;
     }
 
-    unless (
-        $self->OwnerObj->HasRight(
-            Right    => 'OwnTicket',
-            Object => $NewQueueObj
-        )
-    ) {
+    unless ( $self->OwnerObj->HasRight( Right    => 'OwnTicket', Object => $NewQueueObj)) {
         my $clone = RT::Ticket->new( $RT::SystemUser );
         $clone->Load( $self->Id );
         unless ( $clone->Id ) {
@@ -1781,7 +1776,7 @@ sub SetQueue {
 
         #If we're changing the status from initial in old to not intial in new,
         # record that we've started
-        if ( $old_schema->is_initial($old_status) && !$new_schema->is_initial($new_status) ) {
+        if ( $old_schema->is_initial($old_status) && !$new_schema->is_initial($new_status)  && $clone->StartedObj->Unix == 0 ) {
             #Set the Started time to "now"
             $clone->_Set(
                 Field             => 'Started',
@@ -3069,19 +3064,12 @@ sub SetStatus {
 
     my $new = $args{'Status'};
     unless ( $schema->is_valid( $new ) ) {
-        return (0,
-            $self->loc("Status '[_1]' isn't a valid status for tickets in this queue.",
-                $self->loc($new)
-        ));
+        return (0, $self->loc("Status '[_1]' isn't a valid status for tickets in this queue.", $self->loc($new)));
     }
 
     my $old = $self->__Value('Status');
     unless ( $schema->is_transition( $old => $new ) ) {
-        return (0,
-            $self->loc("You can't change status from '[_1]' to '[_2]'.",
-                $self->loc($old), $self->loc($new)
-            )
-        );
+        return (0, $self->loc("You can't change status from '[_1]' to '[_2]'.", $self->loc($old), $self->loc($new)));
     }
 
     my $check_right = $schema->check_right( $old => $new );
@@ -3089,17 +3077,18 @@ sub SetStatus {
         return ( 0, $self->loc('Permission Denied') );
     }
 
-    if ( !$args{Force} && $schema->is_inactive( $new )
-        && $self->HasUnresolvedDependencies
-    ) {
+    if ( !$args{Force} && $schema->is_inactive( $new ) && $self->HasUnresolvedDependencies) {
         return (0, $self->loc('That ticket has unresolved dependencies'));
     }
 
     my $now = RT::Date->new( $self->CurrentUser );
     $now->SetToNow();
 
+    my $raw_started = RT::Date->new($RT::SystemUser);
+    $raw_started->Set(Format => 'ISO', Value => $self->__Value('Started'));
+
     #If we're changing the status from new, record that we've started
-    if ( $schema->is_initial($old) && $schema->is_active($new) ) {
+    if ( $schema->is_initial($old) && !$schema->is_initial($new) && !$raw_started->Unix) {
         #Set the Started time to "now"
         $self->_Set(
             Field             => 'Started',
diff --git a/share/html/Ticket/Elements/EditBasics b/share/html/Ticket/Elements/EditBasics
index 5d33858..edbb065 100755
--- a/share/html/Ticket/Elements/EditBasics
+++ b/share/html/Ticket/Elements/EditBasics
@@ -63,6 +63,8 @@ unless ( @fields ) {
                 Name => 'Status',
                 DefaultLabel => loc("[_1] (Unchanged)",loc($TicketObj->Status)),
                 Default => $defaults{'Status'} || undef,
+                TicketObj => $TicketObj,
+                QueueObj => $TicketObj->QueueObj
             },
         },
         {   name => 'Queue',
@@ -71,8 +73,6 @@ unless ( @fields ) {
                 Name => 'Queue',
                 Default => $defaults{'Queue'} || $TicketObj->QueueObj->Id,
                 ShowNullOption => 0,
-                TicketObj => $TicketObj,
-                QueueObj => $TicketObj->QueueObj
             }
         },
         {   name => 'Owner',
diff --git a/t/status-schemas/dates.t b/t/status-schemas/dates.t
index c42a73d..54d9389 100644
--- a/t/status-schemas/dates.t
+++ b/t/status-schemas/dates.t
@@ -131,23 +131,33 @@ diag "dates on create for delivery schema";
     }
     {
         my $ticket = RT::Ticket->new( $RT::SystemUser );
-        my ($id, $msg) = $ticket->Create(
+        my ($id, $txn, $msg) = $ticket->Create(
             Queue => $delivery->id,
             Subject => 'test',
-            Status => 'on way',
         );
         ok $id, 'created a ticket';
-        ok $ticket->StartedObj->Unix > 0, 'started is set';
-        ok $ticket->ResolvedObj->Unix <= 0, 'resolved is not set';
+        diag($msg);
+        is $ticket->Status, 'ordered', "Status is ordered";
+        my ($statusval,$statusmsg) = $ticket->SetStatus('on way');
+        ok($statusval,$statusmsg);
+        ok $ticket->StartedObj->Unix > 0, 'started is set to ' .$ticket->StartedObj->AsString ;
+        is $ticket->ResolvedObj->Unix, 0, 'resolved is not set';
     }
+    exit;
     {
         my $ticket = RT::Ticket->new( $RT::SystemUser );
         my ($id, $msg) = $ticket->Create(
             Queue => $delivery->id,
             Subject => 'test',
-            Status => 'delivered',
         );
         ok $id, 'created a ticket';
+
+        my ($statusval,$statusmsg) = $ticket->SetStatus('on way');
+        ok($statusval,$statusmsg);
+
+        ($statusval,$statusmsg) = $ticket->SetStatus('delivered');
+        ok($statusval,$statusmsg);
+
         ok $ticket->StartedObj->Unix > 0, 'started is set';
         ok $ticket->ResolvedObj->Unix > 0, 'resolved is set';
     }
@@ -155,7 +165,7 @@ diag "dates on create for delivery schema";
     my $test_date = '2008-11-28 12:00:00';
     {
         my $ticket = RT::Ticket->new( $RT::SystemUser );
-        my ($id, $msg) = $ticket->Create(
+        my ($id, $statusmsg) = $ticket->Create(
             Queue => $delivery->id,
             Subject => 'test',
             Status => 'ordered',
@@ -168,27 +178,32 @@ diag "dates on create for delivery schema";
     }
     {
         my $ticket = RT::Ticket->new( $RT::SystemUser );
-        my ($id, $msg) = $ticket->Create(
+        my ($id, $statusmsg) = $ticket->Create(
             Queue => $delivery->id,
             Subject => 'test',
-            Status => 'on way',
+            Status => 'ordered',
             Started => $test_date,
             Resolved => $test_date,
         );
         ok $id, 'created a ticket';
+        my ($statusval,$statusmsg) = $ticket->SetStatus('on way');
+        ok($statusval,$statusmsg);
         is $ticket->StartedObj->ISO, $test_date, 'started is set';
         is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set';
     }
     {
         my $ticket = RT::Ticket->new( $RT::SystemUser );
-        my ($id, $msg) = $ticket->Create(
+        my ($id, $statusmsg) = $ticket->Create(
             Queue => $delivery->id,
             Subject => 'test',
-            Status => 'delivered',
             Started => $test_date,
             Resolved => $test_date,
         );
         ok $id, 'created a ticket';
+        my ($statusval,$statusmsg) = $ticket->SetStatus('on way');
+        ok($statusval,$statusmsg);
+        ($statusval,$statusmsg) = $ticket->SetStatus('delievered');
+        ok($statusval,$statusmsg);
         is $ticket->StartedObj->ISO, $test_date, 'started is set';
         is $ticket->ResolvedObj->ISO, $test_date, 'resolved is set';
     }
diff --git a/t/status-schemas/utils.pl b/t/status-schemas/utils.pl
index 44bd2dd..d5ff0ca 100644
--- a/t/status-schemas/utils.pl
+++ b/t/status-schemas/utils.pl
@@ -9,6 +9,7 @@ BEGIN {
 $config = <<END;
 Set(\%StatusSchemaMeta,
     default => {
+        default_initial => 'new',
         initial  => [qw(new open resolved )],
         active   => [qw(open stalled)],
         inactive => [qw(resolved rejected deleted)],
@@ -38,6 +39,7 @@ Set(\%StatusSchemaMeta,
         },
     },
     delivery => {
+        default_initial => 'ordered',
         initial  => ['ordered'],
         active   => ['on way', 'delayed'],
         inactive => ['delivered'],

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


More information about the Rt-commit mailing list