[Rt-commit] rt branch, 4.2/apply-scrips-to-multiple-queues, created. rt-4.0.8-520-gb8686e1

Alex Vandiver alexmv at bestpractical.com
Wed Nov 7 12:36:42 EST 2012


The branch, 4.2/apply-scrips-to-multiple-queues has been created
        at  b8686e159b1a72714fa6d7b464ace204f8a9fa2b (commit)

- Log -----------------------------------------------------------------
commit 740d510df11e7b407a57b379e8ded3f9f665e541
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Dec 12 19:05:31 2011 +0400

    extract re-usable code from ObjectCustomField[s]
    
    Two new classes for records and collections

diff --git a/lib/RT/ObjectCustomField.pm b/lib/RT/ObjectCustomField.pm
index 61bc355..459164f 100644
--- a/lib/RT/ObjectCustomField.pm
+++ b/lib/RT/ObjectCustomField.pm
@@ -45,93 +45,23 @@
 # those contributions and any derivatives thereof.
 #
 # END BPS TAGGED BLOCK }}}
-
-package RT::ObjectCustomField;
-
 use strict;
 use warnings;
 
+package RT::ObjectCustomField;
+use base 'RT::Record::ApplyAndSort';
 
 use RT::CustomField;
-use base 'RT::Record';
+use RT::ObjectCustomFields;
 
 sub Table {'ObjectCustomFields'}
 
-
-
-
-
-
-sub Create {
-    my $self = shift;
-    my %args = (
-        CustomField => 0,
-        ObjectId    => 0,
-        SortOrder   => undef,
-        @_
-    );
-
-    my $cf = $self->CustomFieldObj( $args{'CustomField'} );
-    unless ( $cf->id ) {
-        $RT::Logger->error("Couldn't load '$args{'CustomField'}' custom field");
-        return 0;
-    }
-
-    #XXX: Where is ACL check for 'AssignCustomFields'?
-
-    my $ObjectCFs = RT::ObjectCustomFields->new($self->CurrentUser);
-    $ObjectCFs->LimitToObjectId( $args{'ObjectId'} );
-    $ObjectCFs->LimitToCustomField( $cf->id );
-    $ObjectCFs->LimitToLookupType( $cf->LookupType );
-    if ( my $first = $ObjectCFs->First ) {
-        $self->Load( $first->id );
-        return $first->id;
-    }
-
-    unless ( defined $args{'SortOrder'} ) {
-        my $ObjectCFs = RT::ObjectCustomFields->new( RT->SystemUser );
-        $ObjectCFs->LimitToObjectId( $args{'ObjectId'} );
-        $ObjectCFs->LimitToObjectId( 0 ) if $args{'ObjectId'};
-        $ObjectCFs->LimitToLookupType( $cf->LookupType );
-        $ObjectCFs->OrderBy( FIELD => 'SortOrder', ORDER => 'DESC' );
-        if ( my $first = $ObjectCFs->First ) {
-            $args{'SortOrder'} = $first->SortOrder + 1;
-        } else {
-            $args{'SortOrder'} = 0;
-        }
-    }
-
-    return $self->SUPER::Create(
-        CustomField => $args{'CustomField'},
-        ObjectId    => $args{'ObjectId'},
-        SortOrder   => $args{'SortOrder'},
-    );
-}
-
-sub Delete {
-    my $self = shift;
-
-    my $ObjectCFs = RT::ObjectCustomFields->new($self->CurrentUser);
-    $ObjectCFs->LimitToObjectId($self->ObjectId);
-    $ObjectCFs->LimitToLookupType($self->CustomFieldObj->LookupType);
-
-    # Move everything below us up
-    my $sort_order = $self->SortOrder;
-    while (my $OCF = $ObjectCFs->Next) {
-        my $this_order = $OCF->SortOrder;
-        next if $this_order <= $sort_order; 
-        $OCF->SetSortOrder($this_order - 1);
-    }
-
-    $self->SUPER::Delete;
-}
-
+# XXX: Where is ACL check when we create a record?
 
 =head2 CustomFieldObj
 
 Returns the CustomField Object which has the id returned by CustomField
 
-
 =cut
 
 sub CustomFieldObj {
@@ -154,148 +84,17 @@ sub CustomFieldObj {
     return $CF;
 }
 
-=head2 Sorting custom fields applications
-
-Custom fields sorted on multiple layers. First of all custom
-fields with different lookup type are sorted independently. All
-global custom fields have fixed order for all objects, but you
-can insert object specific custom fields between them. Object
-specific custom fields can be applied to several objects and
-be on different place. For example you have GCF1, GCF2, LCF1,
-LCF2 and LCF3 that applies to tickets. You can place GCF2
-above GCF1, but they will be in the same order in all queues.
-However, LCF1 and other local can be placed at any place
-for particular queue: above global, between them or below.
-
-=head3 MoveUp
-
-Moves custom field up. See </Sorting custom fields applications>.
-
-=cut
-
-sub MoveUp {
+sub Neighbors {
     my $self = shift;
+    my %args = @_;
 
-    my $ocfs = RT::ObjectCustomFields->new( $self->CurrentUser );
-
-    my $oid = $self->ObjectId;
-    $ocfs->LimitToObjectId( $oid );
-    if ( $oid ) {
-        $ocfs->LimitToObjectId( 0 );
-    }
-
-    my $cf = $self->CustomFieldObj;
-    $ocfs->LimitToLookupType( $cf->LookupType );
-
-    $ocfs->Limit( FIELD => 'SortOrder', OPERATOR => '<', VALUE => $self->SortOrder );
-    $ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'DESC' } );
-
-    my @above = ($ocfs->Next, $ocfs->Next);
-    unless ($above[0]) {
-        return (0, "Can not move up. It's already at the top");
-    }
-
-    my $new_sort_order;
-    if ( $above[0]->ObjectId == $self->ObjectId ) {
-        $new_sort_order = $above[0]->SortOrder;
-        my ($status, $msg) = $above[0]->SetSortOrder( $self->SortOrder );
-        unless ( $status ) {
-            return (0, "Couldn't move custom field");
-        }
-    }
-    elsif ( $above[1] && $above[0]->SortOrder == $above[1]->SortOrder + 1 ) {
-        my $move_ocfs = RT::ObjectCustomFields->new( RT->SystemUser );
-        $move_ocfs->LimitToLookupType( $cf->LookupType );
-        $move_ocfs->Limit(
-            FIELD => 'SortOrder',
-            OPERATOR => '>=',
-            VALUE => $above[0]->SortOrder,
-        );
-        $move_ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'DESC' } );
-        while ( my $record = $move_ocfs->Next ) {
-            my ($status, $msg) = $record->SetSortOrder( $record->SortOrder + 1 );
-            unless ( $status ) {
-                return (0, "Couldn't move custom field");
-            }
-        }
-        $new_sort_order = $above[0]->SortOrder;
-    } else {
-        $new_sort_order = $above[0]->SortOrder - 1;
-    }
-
-    my ($status, $msg) = $self->SetSortOrder( $new_sort_order );
-    unless ( $status ) {
-        return (0, "Couldn't move custom field");
-    }
-
-    return (1,"Moved custom field up");
-}
-
-=head3 MoveDown
-
-Moves custom field down. See </Sorting custom fields applications>.
-
-=cut
-
-sub MoveDown {
-    my $self = shift;
-
-    my $ocfs = RT::ObjectCustomFields->new( $self->CurrentUser );
-
-    my $oid = $self->ObjectId;
-    $ocfs->LimitToObjectId( $oid );
-    if ( $oid ) {
-        $ocfs->LimitToObjectId( 0 );
-    }
-
-    my $cf = $self->CustomFieldObj;
-    $ocfs->LimitToLookupType( $cf->LookupType );
-
-    $ocfs->Limit( FIELD => 'SortOrder', OPERATOR => '>', VALUE => $self->SortOrder );
-    $ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'ASC' } );
-
-    my @below = ($ocfs->Next, $ocfs->Next);
-    unless ($below[0]) {
-        return (0, "Can not move down. It's already at the bottom");
-    }
-
-    my $new_sort_order;
-    if ( $below[0]->ObjectId == $self->ObjectId ) {
-        $new_sort_order = $below[0]->SortOrder;
-        my ($status, $msg) = $below[0]->SetSortOrder( $self->SortOrder );
-        unless ( $status ) {
-            return (0, "Couldn't move custom field");
-        }
-    }
-    elsif ( $below[1] && $below[0]->SortOrder + 1 == $below[1]->SortOrder ) {
-        my $move_ocfs = RT::ObjectCustomFields->new( RT->SystemUser );
-        $move_ocfs->LimitToLookupType( $cf->LookupType );
-        $move_ocfs->Limit(
-            FIELD => 'SortOrder',
-            OPERATOR => '<=',
-            VALUE => $below[0]->SortOrder,
-        );
-        $move_ocfs->OrderByCols( { FIELD => 'SortOrder', ORDER => 'ASC' } );
-        while ( my $record = $move_ocfs->Next ) {
-            my ($status, $msg) = $record->SetSortOrder( $record->SortOrder - 1 );
-            unless ( $status ) {
-                return (0, "Couldn't move custom field");
-            }
-        }
-        $new_sort_order = $below[0]->SortOrder;
-    } else {
-        $new_sort_order = $below[0]->SortOrder + 1;
-    }
-
-    my ($status, $msg) = $self->SetSortOrder( $new_sort_order );
-    unless ( $status ) {
-        return (0, "Couldn't move custom field");
-    }
-
-    return (1,"Moved custom field down");
+    my $res = $self->CollectionClass->new( $self->CurrentUser );
+    $res->LimitToLookupType(
+        ($args{'CustomField'} || $self->CustomFieldObj)->LookupType
+    );
+    return $res;
 }
 
-
 =head2 id
 
 Returns the current value of id.
diff --git a/lib/RT/ObjectCustomFields.pm b/lib/RT/ObjectCustomFields.pm
index 9864949..c863dab 100644
--- a/lib/RT/ObjectCustomFields.pm
+++ b/lib/RT/ObjectCustomFields.pm
@@ -45,48 +45,23 @@
 # those contributions and any derivatives thereof.
 #
 # END BPS TAGGED BLOCK }}}
-
-package RT::ObjectCustomFields;
-
 use strict;
 use warnings;
 
+package RT::ObjectCustomFields;
+use base 'RT::SearchBuilder::ApplyAndSort';
 
+use RT::CustomField;
 use RT::ObjectCustomField;
 
-use base 'RT::SearchBuilder';
-
 sub Table { 'ObjectCustomFields'}
 
-sub _Init {
-    my $self = shift;
-
-  # By default, order by SortOrder
-  $self->OrderByCols(
-	 { ALIAS => 'main',
-	   FIELD => 'SortOrder',
-	   ORDER => 'ASC' },
-	 { ALIAS => 'main',
-	   FIELD => 'id',
-	   ORDER => 'ASC' },
-     );
-
-    return ( $self->SUPER::_Init(@_) );
-}
-
-
 sub LimitToCustomField {
     my $self = shift;
     my $id = shift;
     $self->Limit( FIELD => 'CustomField', VALUE => $id );
 }
 
-sub LimitToObjectId {
-    my $self = shift;
-    my $id = shift || 0;
-    $self->Limit( FIELD => 'ObjectId', VALUE => $id );
-}
-
 sub LimitToLookupType {
     my $self = shift;
     my $lookup = shift;
@@ -139,17 +114,6 @@ sub _DoSearch {
     $self->SUPER::_DoSearch()
 }
 
-
-=head2 NewItem
-
-Returns an empty new RT::ObjectCustomField item
-
-=cut
-
-sub NewItem {
-    my $self = shift;
-    return(RT::ObjectCustomField->new($self->CurrentUser));
-}
 RT::Base->_ImportOverlays();
 
 1;
diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
new file mode 100644
index 0000000..572f0f9
--- /dev/null
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -0,0 +1,274 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+#                                          <sales at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+use strict;
+use warnings;
+
+package RT::Record::ApplyAndSort;
+use base 'RT::Record';
+
+sub CollectionClass {
+    return (ref($_[0]) || $_[0]).'s';
+}
+
+sub TargetField {
+    my $class = ref($_[0]) || $_[0];
+    $class =~ s/.*::Object// or return undef;
+    return $class;
+}
+
+sub Create {
+    my $self = shift;
+    my %args = (
+        Scrip       => 0,
+        ObjectId    => 0,
+        SortOrder   => undef,
+        @_
+    );
+
+    my $tfield = $self->TargetField;
+
+    my $target = $self->TargetObj( $args{ $tfield } );
+    unless ( $target->id ) {
+        $RT::Logger->error("Couldn't load ". ref($target) ." '$args{'Scrip'}'");
+        return 0;
+    }
+
+    my $exist = $self->new($self->CurrentUser);
+    $exist->LoadByCols( ObjectId => $args{'ObjectId'}, $tfield => $target->id );
+    if ( $exist->id ) {
+        $self->Load( $exist->id );
+        return $self->id;
+    }
+
+    unless ( defined $args{'SortOrder'} ) {
+        $args{'SortOrder'} = $self->NextSortOrder(
+            $tfield  => $target,
+            ObjectId => $args{'ObjectId'},
+        );
+    }
+
+    return $self->SUPER::Create(
+        %args,
+        $tfield   => $target->id,
+    );
+}
+
+sub Delete {
+    my $self = shift;
+
+    my $siblings = $self->Neighbors;
+    $siblings->LimitToObjectId( $self->ObjectId );
+    $siblings->Limit( FIELD => 'SortOrder', OPERATOR => '>', VALUE => $self->SortOrder );
+
+    # Move everything below us up
+    my $sort_order = $self->SortOrder;
+    while (my $record = $siblings->Next) {
+        $record->SetSortOrder($record->SortOrder - 1);
+    }
+
+    $self->SUPER::Delete;
+}
+
+=head2 Sorting scrips applications
+
+scrips sorted on multiple layers. First of all custom
+fields with different lookup type are sorted independently. All
+global scrips have fixed order for all objects, but you
+can insert object specific scrips between them. Object
+specific scrips can be applied to several objects and
+be on different place. For example you have GCF1, GCF2, LCF1,
+LCF2 and LCF3 that applies to tickets. You can place GCF2
+above GCF1, but they will be in the same order in all queues.
+However, LCF1 and other local can be placed at any place
+for particular queue: above global, between them or below.
+
+=head3 MoveUp
+
+Moves scrip up. See </Sorting scrips applications>.
+
+=cut
+
+sub MoveUp {
+    my $self = shift;
+
+    my $siblings = $self->Siblings;
+    $siblings->Limit( FIELD => 'SortOrder', OPERATOR => '<', VALUE => $self->SortOrder );
+    $siblings->OrderByCols( { FIELD => 'SortOrder', ORDER => 'DESC' } );
+
+    my @above = ($siblings->Next, $siblings->Next);
+    unless ($above[0]) {
+        return (0, "Can not move up. It's already at the top");
+    }
+
+    my $new_sort_order;
+    if ( $above[0]->ObjectId == $self->ObjectId ) {
+        $new_sort_order = $above[0]->SortOrder;
+        my ($status, $msg) = $above[0]->SetSortOrder( $self->SortOrder );
+        unless ( $status ) {
+            return (0, "Couldn't move scrip");
+        }
+    }
+    elsif ( $above[1] && $above[0]->SortOrder == $above[1]->SortOrder + 1 ) {
+        my $move_siblings = $self->Neighbors;
+        $move_siblings->Limit(
+            FIELD => 'SortOrder',
+            OPERATOR => '>=',
+            VALUE => $above[0]->SortOrder,
+        );
+        $move_siblings->OrderByCols( { FIELD => 'SortOrder', ORDER => 'DESC' } );
+        while ( my $record = $move_siblings->Next ) {
+            my ($status, $msg) = $record->SetSortOrder( $record->SortOrder + 1 );
+            unless ( $status ) {
+                return (0, "Couldn't move scrip");
+            }
+        }
+        $new_sort_order = $above[0]->SortOrder;
+    } else {
+        $new_sort_order = $above[0]->SortOrder - 1;
+    }
+
+    my ($status, $msg) = $self->SetSortOrder( $new_sort_order );
+    unless ( $status ) {
+        return (0, "Couldn't move scrip");
+    }
+
+    return (1,"Moved scrip up");
+}
+
+=head3 MoveDown
+
+Moves scrip down. See </Sorting scrips applications>.
+
+=cut
+
+sub MoveDown {
+    my $self = shift;
+
+    my $siblings = $self->Siblings;
+    $siblings->Limit( FIELD => 'SortOrder', OPERATOR => '>', VALUE => $self->SortOrder );
+    $siblings->OrderByCols( { FIELD => 'SortOrder', ORDER => 'ASC' } );
+
+    my @below = ($siblings->Next, $siblings->Next);
+    unless ($below[0]) {
+        return (0, "Can not move down. It's already at the bottom");
+    }
+
+    my $new_sort_order;
+    if ( $below[0]->ObjectId == $self->ObjectId ) {
+        $new_sort_order = $below[0]->SortOrder;
+        my ($status, $msg) = $below[0]->SetSortOrder( $self->SortOrder );
+        unless ( $status ) {
+            return (0, "Couldn't move scrip");
+        }
+    }
+    elsif ( $below[1] && $below[0]->SortOrder + 1 == $below[1]->SortOrder ) {
+        my $move_siblings = $self->Neighbors;
+        $move_siblings->Limit(
+            FIELD => 'SortOrder',
+            OPERATOR => '<=',
+            VALUE => $below[0]->SortOrder,
+        );
+        $move_siblings->OrderByCols( { FIELD => 'SortOrder', ORDER => 'ASC' } );
+        while ( my $record = $move_siblings->Next ) {
+            my ($status, $msg) = $record->SetSortOrder( $record->SortOrder - 1 );
+            unless ( $status ) {
+                return (0, "Couldn't move scrip");
+            }
+        }
+        $new_sort_order = $below[0]->SortOrder;
+    } else {
+        $new_sort_order = $below[0]->SortOrder + 1;
+    }
+
+    my ($status, $msg) = $self->SetSortOrder( $new_sort_order );
+    unless ( $status ) {
+        return (0, "Couldn't move scrip");
+    }
+
+    return (1,"Moved scrip down");
+}
+
+sub NextSortOrder {
+    my $self = shift;
+    my $siblings = $self->Siblings( @_ );
+    $siblings->OrderBy( FIELD => 'SortOrder', ORDER => 'DESC' );
+    return 0 unless my $first = $siblings->First;
+    return $first->SortOrder + 1;
+}
+
+sub TargetObj {
+    my $self = shift;
+    my $id   = shift;
+
+    my $method = $self->TargetField .'Obj';
+    return $self->$method( $id );
+}
+
+sub Neighbors {
+    my $self = shift;
+    return $self->CollectionClass->new( $self->CurrentUser );
+}
+
+sub Siblings {
+    my $self = shift;
+    my %args = @_;
+
+    my $oid = $args{'ObjectId'};
+    $oid = $self->ObjectId unless defined $oid;
+    $oid ||= 0;
+
+    my $res = $self->Neighbors( %args );
+    $res->LimitToObjectId( $oid );
+    $res->LimitToObjectId( 0 ) if $oid;
+    return $res;
+}
+
+RT::Base->_ImportOverlays();
+
+1;
diff --git a/lib/RT/ObjectCustomFields.pm b/lib/RT/SearchBuilder/ApplyAndSort.pm
similarity index 57%
copy from lib/RT/ObjectCustomFields.pm
copy to lib/RT/SearchBuilder/ApplyAndSort.pm
index 9864949..95813ea 100644
--- a/lib/RT/ObjectCustomFields.pm
+++ b/lib/RT/SearchBuilder/ApplyAndSort.pm
@@ -46,39 +46,32 @@
 #
 # END BPS TAGGED BLOCK }}}
 
-package RT::ObjectCustomFields;
-
 use strict;
 use warnings;
 
-
-use RT::ObjectCustomField;
-
+package RT::SearchBuilder::ApplyAndSort;
 use base 'RT::SearchBuilder';
 
-sub Table { 'ObjectCustomFields'}
+sub RecordClass {
+    my $class = ref($_[0]) || $_[0];
+    $class =~ s/s$// or return undef;
+    return $class;
+}
 
 sub _Init {
     my $self = shift;
 
-  # By default, order by SortOrder
-  $self->OrderByCols(
-	 { ALIAS => 'main',
-	   FIELD => 'SortOrder',
-	   ORDER => 'ASC' },
-	 { ALIAS => 'main',
-	   FIELD => 'id',
-	   ORDER => 'ASC' },
-     );
-
-    return ( $self->SUPER::_Init(@_) );
-}
-
+    # By default, order by SortOrder
+    $self->OrderByCols(
+         { ALIAS => 'main',
+           FIELD => 'SortOrder',
+           ORDER => 'ASC' },
+         { ALIAS => 'main',
+           FIELD => 'id',
+           ORDER => 'ASC' },
+    );
 
-sub LimitToCustomField {
-    my $self = shift;
-    my $id = shift;
-    $self->Limit( FIELD => 'CustomField', VALUE => $id );
+    return $self->SUPER::_Init(@_);
 }
 
 sub LimitToObjectId {
@@ -87,69 +80,17 @@ sub LimitToObjectId {
     $self->Limit( FIELD => 'ObjectId', VALUE => $id );
 }
 
-sub LimitToLookupType {
-    my $self = shift;
-    my $lookup = shift;
-
-    $self->{'_cfs_alias'} ||= $self->Join(
-        ALIAS1 => 'main',
-        FIELD1 => 'CustomField',
-        TABLE2 => 'CustomFields',
-        FIELD2 => 'id',
-    );
-    $self->Limit(
-        ALIAS    => $self->{'_cfs_alias'},
-        FIELD    => 'LookupType',
-        OPERATOR => '=',
-        VALUE    => $lookup,
-    );
-}
-
-sub HasEntryForCustomField {
-    my $self = shift;
-    my $id = shift;
-
-    my @items = grep {$_->CustomField == $id } @{$self->ItemsArrayRef};
-
-    if ($#items > 1) {
-	die "$self HasEntry had a list with more than one of $id in it. this can never happen";
-    }
-    if ($#items == -1 ) {
-	return undef;
-    }
-    else {
-	return ($items[0]);
-    }  
-}
-
-sub CustomFields {
-    my $self = shift;
-    my %seen;
-    map { $_->CustomFieldObj } @{$self->ItemsArrayRef};
-}
-
-sub _DoSearch {
-    my $self = shift;
-    if ($self->{'_cfs_alias'}) {
-    $self->Limit( ALIAS           => $self->{'_cfs_alias'},
-                 FIELD           => 'Disabled',
-                 OPERATOR        => '!=',
-                 VALUE           =>  1);
-    }
-    $self->SUPER::_DoSearch()
-}
-
-
 =head2 NewItem
 
-Returns an empty new RT::ObjectCustomField item
+Returns an empty new collection's item
 
 =cut
 
 sub NewItem {
     my $self = shift;
-    return(RT::ObjectCustomField->new($self->CurrentUser));
+    return $self->RecordClass->new( $self->CurrentUser );
 }
+
 RT::Base->_ImportOverlays();
 
 1;

commit 0716e43cc3abadeec6c14d53c21ed4052c5f6ded
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Tue Nov 6 19:13:41 2012 -0500

    Add a method to disable all applications of this object

diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
index 572f0f9..4570746 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -65,7 +65,6 @@ sub TargetField {
 sub Create {
     my $self = shift;
     my %args = (
-        Scrip       => 0,
         ObjectId    => 0,
         SortOrder   => undef,
         @_
@@ -115,6 +114,30 @@ sub Delete {
     $self->SUPER::Delete;
 }
 
+sub SetDisabledOnAll {
+    my $self = shift;
+    my %args = (@_);
+
+    my $field = $self->TargetField;
+
+    my $id = $args{ $field };
+    $id = $id->id if ref $id;
+    $id ||= $self->TargetObj->id;
+
+    my $list = $self->CollectionClass->new( $self->CurrentUser );
+    $list->Limit( FIELD => $field, VALUE => $id );
+    $RT::Handle->BeginTransaction;
+    foreach ( @{ $list->ItemsArrayRef } ) {
+        my ($status, $msg) = $_->SetDisabled( $args{Value} || 0 );
+        unless ( $status ) {
+            $RT::Handle->Rollback;
+            return ($status, $msg);
+        }
+    }
+    $RT::Handle->Commit;
+    return (1, $self->loc("Disabled all applications") );
+}
+
 =head2 Sorting scrips applications
 
 scrips sorted on multiple layers. First of all custom

commit c6357dfb2509edb21b68cb302bf58cd5c7f3ed4e
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Dec 13 04:04:11 2011 +0400

    extract some code from CustomField.pm into new modules

diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index a409c3a..8d5623f 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -1160,9 +1160,7 @@ sub SetLookupType {
     my $lookup = shift;
     if ( $lookup ne $self->LookupType ) {
         # Okay... We need to invalidate our existing relationships
-        my $ObjectCustomFields = RT::ObjectCustomFields->new($self->CurrentUser);
-        $ObjectCustomFields->LimitToCustomField($self->Id);
-        $_->Delete foreach @{$ObjectCustomFields->ItemsArrayRef};
+        RT::ObjectCustomField->new($self->CurrentUser)->DeleteAll( CustomField => $self );
     }
     return $self->_Set(Field => 'LookupType', Value =>$lookup);
 }
@@ -1360,7 +1358,6 @@ Takes an object
 
 =cut
 
-
 sub AddToObject {
     my $self  = shift;
     my $object = shift;
@@ -1374,26 +1371,9 @@ sub AddToObject {
         return ( 0, $self->loc('Permission Denied') );
     }
 
-    if ( $self->IsApplied( $id ) ) {
-        return ( 0, $self->loc("Custom field is already applied to the object") );
-    }
-
-    if ( $id ) {
-        # applying locally
-        return (0, $self->loc("Couldn't apply custom field to an object as it's global already") )
-            if $self->IsApplied( 0 );
-    }
-    else {
-        my $applied = RT::ObjectCustomFields->new( $self->CurrentUser );
-        $applied->LimitToCustomField( $self->id );
-        while ( my $record = $applied->Next ) {
-            $record->Delete;
-        }
-    }
-
     my $ocf = RT::ObjectCustomField->new( $self->CurrentUser );
-    my ( $oid, $msg ) = $ocf->Create(
-        ObjectId => $id, CustomField => $self->id,
+    my ( $oid, $msg ) = $ocf->Apply(
+        CustomField => $self->id, ObjectId => $id,
     );
     return ( $oid, $msg );
 }
diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
index 4570746..936dcb2 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -98,6 +98,46 @@ sub Create {
     );
 }
 
+sub Apply {
+    my $self = shift;
+    my %args = (@_);
+
+    my $field = $self->TargetField;
+
+    my $tid = $args{ $field };
+    $tid = $tid->id if ref $tid;
+    $tid ||= $self->TargetObj->id;
+
+    my $oid = $args{'ObjectId'};
+    $oid = $oid->id if ref $oid;
+    $oid ||= 0;
+
+    if ( $self->IsApplied( $tid => $oid ) ) {
+        return ( 0, $self->loc("Is already applied to the object") );
+    }
+
+    if ( $oid ) {
+        # applying locally
+        return (0, $self->loc("Couldn't apply as it's global already") )
+            if $self->IsApplied( $tid => 0 );
+    }
+    else {
+        $self->DeleteAll( $field => $tid );
+    }
+
+    return $self->Create(
+        $field => $tid, ObjectId => $oid,
+    );
+}
+
+sub IsApplied {
+    my $self = shift;
+    my ($tid, $oid) = @_;
+    my $record = $self->new( $self->CurrentUser );
+    $record->LoadByCols( $self->TargetField => $tid, ObjectId => $oid );
+    return $record->id;
+}
+
 sub Delete {
     my $self = shift;
 
@@ -114,6 +154,21 @@ sub Delete {
     $self->SUPER::Delete;
 }
 
+sub DeleteAll {
+    my $self = shift;
+    my %args = (@_);
+
+    my $field = $self->TargetField;
+
+    my $id = $args{ $field };
+    $id = $id->id if ref $id;
+    $id ||= $self->TargetObj->id;
+
+    my $list = $self->CollectionClass->new( $self->CurrentUser );
+    $list->Limit( FIELD => $field, VALUE => $id );
+    $_->Delete foreach @{ $list->ItemsArrayRef };
+}
+
 sub SetDisabledOnAll {
     my $self = shift;
     my %args = (@_);

commit 4add674be059ece685605c606ed99015324e151b
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Dec 13 17:02:29 2011 +0400

    move *AppliedTo methods

diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index 8d5623f..ec436e3 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -1263,18 +1263,8 @@ Doesn't takes into account if object is applied globally.
 
 sub AppliedTo {
     my $self = shift;
-
-    my ($res, $ocfs_alias) = $self->_AppliedTo;
-    return $res unless $res;
-
-    $res->Limit(
-        ALIAS     => $ocfs_alias,
-        FIELD     => 'id',
-        OPERATOR  => 'IS NOT',
-        VALUE     => 'NULL',
-    );
-
-    return $res;
+    return RT::ObjectCustomField->new( $self->CurrentUser )
+        ->AppliedTo( CustomField => $self );
 }
 
 =head1 NotAppliedTo
@@ -1289,48 +1279,8 @@ Doesn't takes into account if object is applied globally.
 
 sub NotAppliedTo {
     my $self = shift;
-
-    my ($res, $ocfs_alias) = $self->_AppliedTo;
-    return $res unless $res;
-
-    $res->Limit(
-        ALIAS     => $ocfs_alias,
-        FIELD     => 'id',
-        OPERATOR  => 'IS',
-        VALUE     => 'NULL',
-    );
-
-    return $res;
-}
-
-sub _AppliedTo {
-    my $self = shift;
-
-    my ($class) = $self->CollectionClassFromLookupType;
-    return undef unless $class;
-
-    my $res = $class->new( $self->CurrentUser );
-
-    # If CF is a Group CF, only display user-defined groups
-    if ( $class eq 'RT::Groups' ) {
-        $res->LimitToUserDefinedGroups;
-    }
-
-    $res->OrderBy( FIELD => 'Name' );
-    my $ocfs_alias = $res->Join(
-        TYPE   => 'LEFT',
-        ALIAS1 => 'main',
-        FIELD1 => 'id',
-        TABLE2 => 'ObjectCustomFields',
-        FIELD2 => 'ObjectId',
-    );
-    $res->Limit(
-        LEFTJOIN => $ocfs_alias,
-        ALIAS    => $ocfs_alias,
-        FIELD    => 'CustomField',
-        VALUE    => $self->id,
-    );
-    return ($res, $ocfs_alias);
+    return RT::ObjectCustomField->new( $self->CurrentUser )
+        ->NotAppliedTo( CustomField => $self );
 }
 
 =head2 IsApplied
diff --git a/lib/RT/ObjectCustomField.pm b/lib/RT/ObjectCustomField.pm
index 459164f..f4d2483 100644
--- a/lib/RT/ObjectCustomField.pm
+++ b/lib/RT/ObjectCustomField.pm
@@ -56,6 +56,12 @@ use RT::ObjectCustomFields;
 
 sub Table {'ObjectCustomFields'}
 
+sub ObjectCollectionClass {
+    my $self = shift;
+    my %args = (@_);
+    return $args{'CustomField'}->CollectionClassFromLookupType;
+}
+
 # XXX: Where is ACL check when we create a record?
 
 =head2 CustomFieldObj
diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
index 936dcb2..0d3f297 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -56,6 +56,8 @@ sub CollectionClass {
     return (ref($_[0]) || $_[0]).'s';
 }
 
+sub ObjectCollectionClass { die "should be subclassed" }
+
 sub TargetField {
     my $class = ref($_[0]) || $_[0];
     $class =~ s/.*::Object// or return undef;
@@ -138,6 +140,90 @@ sub IsApplied {
     return $record->id;
 }
 
+=head1 AppliedTo
+
+Returns collection with objects this custom field is applied to.
+Class of the collection depends on L</LookupType>.
+See all L</NotAppliedTo> .
+
+Doesn't takes into account if object is applied globally.
+
+=cut
+
+sub AppliedTo {
+    my $self = shift;
+
+    my ($res, $alias) = $self->_AppliedTo( @_ );
+    return $res unless $res;
+
+    $res->Limit(
+        ALIAS     => $alias,
+        FIELD     => 'id',
+        OPERATOR  => 'IS NOT',
+        VALUE     => 'NULL',
+    );
+
+    return $res;
+}
+
+=head1 NotAppliedTo
+
+Returns collection with objects this custom field is not applied to.
+Class of the collection depends on L</LookupType>.
+See all L</AppliedTo> .
+
+Doesn't takes into account if object is applied globally.
+
+=cut
+
+sub NotAppliedTo {
+    my $self = shift;
+
+    my ($res, $alias) = $self->_AppliedTo( @_ );
+    return $res unless $res;
+
+    $res->Limit(
+        ALIAS     => $alias,
+        FIELD     => 'id',
+        OPERATOR  => 'IS',
+        VALUE     => 'NULL',
+    );
+
+    return $res;
+}
+
+sub _AppliedTo {
+    my $self = shift;
+    my %args = (@_);
+
+    my $field = $self->TargetField;
+    my $target = $args{ $field } || $self->TargetObj;
+
+    my ($class) = $self->ObjectCollectionClass( $field => $target );
+    return undef unless $class;
+
+    my $res = $class->new( $self->CurrentUser );
+
+    # If target applied to a Group, only display user-defined groups
+    $res->LimitToUserDefinedGroups if $class eq 'RT::Groups';
+
+    $res->OrderBy( FIELD => 'Name' );
+    my $alias = $res->Join(
+        TYPE   => 'LEFT',
+        ALIAS1 => 'main',
+        FIELD1 => 'id',
+        TABLE2 => $self->Table,
+        FIELD2 => 'ObjectId',
+    );
+    $res->Limit(
+        LEFTJOIN => $alias,
+        ALIAS    => $alias,
+        FIELD    => $field,
+        VALUE    => $target->id,
+    );
+    return ($res, $alias);
+}
+
 sub Delete {
     my $self = shift;
 

commit bfbc82c73b2a6f9ef1bb7bc6b904ea16cb8107bf
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Dec 14 01:44:31 2011 +0400

    move more methods, now related to collections

diff --git a/lib/RT/CustomFields.pm b/lib/RT/CustomFields.pm
index d4a5bc7..bf15b16 100644
--- a/lib/RT/CustomFields.pm
+++ b/lib/RT/CustomFields.pm
@@ -170,28 +170,6 @@ sub LimitToGlobalOrObjectId {
                  ENTRYAGGREGATOR => 'OR' ) unless $global_only;
 }
 
-sub _LimitToOCFs {
-    my $self = shift;
-    my @ids = @_;
-
-    my $ocfs_alias = $self->_OCFAlias( New => 1, Left => 1 );
-    if ( @ids ) {
-        # XXX: we need different EA in join clause, but DBIx::SB
-        # doesn't support them, use IN (X) instead
-        my $dbh = $self->_Handle->dbh;
-        $self->Limit(
-            LEFTJOIN   => $ocfs_alias,
-            ALIAS      => $ocfs_alias,
-            FIELD      => 'ObjectId',
-            OPERATOR   => 'IN',
-            QUOTEVALUE => 0,
-            VALUE      => "(". join( ',', map $dbh->quote($_), @ids ) .")",
-        );
-    }
-
-    return $ocfs_alias;
-}
-
 =head2 LimitToNotApplied
 
 Takes either list of object ids or nothing. Limits collection
@@ -202,17 +180,8 @@ zero to mean global.
 
 sub LimitToNotApplied {
     my $self = shift;
-    my @ids = @_;
-
-    my $ocfs_alias = $self->_LimitToOCFs(@ids);
-
-    $self->Limit(
-        ENTRYAGGREGATOR => 'AND',
-        ALIAS    => $ocfs_alias,
-        FIELD    => 'id',
-        OPERATOR => 'IS',
-        VALUE    => 'NULL',
-    );
+    return RT::ObjectCustomFields->new( $self->CurrentUser )
+        ->LimitTargetToNotApplied( $self => @_ );
 }
 
 =head2 LimitToApplied
@@ -224,17 +193,8 @@ zero to mean global.
 
 sub LimitToApplied {
     my $self = shift;
-    my @ids = @_;
-
-    my $ocfs_alias = $self->_LimitToOCFs(@ids);
-
-    $self->Limit(
-        ENTRYAGGREGATOR => 'AND',
-        ALIAS    => $ocfs_alias,
-        FIELD    => 'id',
-        OPERATOR => 'IS NOT',
-        VALUE    => 'NULL',
-    );
+    return RT::ObjectCustomFields->new( $self->CurrentUser )
+        ->LimitTargetToApplied( $self => @_ );
 }
 
 =head2 LimitToGlobalOrQueue QUEUEID
@@ -344,19 +304,8 @@ sub SetContextObject {
 
 sub _OCFAlias {
     my $self = shift;
-    my %args = ( New => 0, Left => 0, @_ );
-
-    return $self->{'_sql_ocfalias'} if $self->{'_sql_ocfalias'} && !$args{'New'};
-
-    my $alias = $self->Join(
-        $args{'Left'} ? (TYPE => 'LEFT') : (),
-        ALIAS1 => 'main',
-        FIELD1 => 'id',
-        TABLE2 => 'ObjectCustomFields',
-        FIELD2 => 'CustomField'
-    );
-    return $alias if $args{'New'};
-    return $self->{'_sql_ocfalias'} = $alias;
+    return RT::ObjectCustomFields->new( $self->CurrentUser )
+        ->JoinTargetToThis( $self => @_ );
 }
 
 
diff --git a/lib/RT/SearchBuilder/ApplyAndSort.pm b/lib/RT/SearchBuilder/ApplyAndSort.pm
index 95813ea..49d65f9 100644
--- a/lib/RT/SearchBuilder/ApplyAndSort.pm
+++ b/lib/RT/SearchBuilder/ApplyAndSort.pm
@@ -80,6 +80,97 @@ sub LimitToObjectId {
     $self->Limit( FIELD => 'ObjectId', VALUE => $id );
 }
 
+=head2 LimitTargetToNotApplied
+
+Takes either list of object ids or nothing. Limits collection
+to custom fields to listed objects or any corespondingly. Use
+zero to mean global.
+
+=cut
+
+sub LimitTargetToNotApplied {
+    my $self = shift;
+    my $collection = shift;
+    my @ids = @_;
+
+    my $alias = $self->JoinTargetToApplied($collection => @ids);
+
+    $collection->Limit(
+        ENTRYAGGREGATOR => 'AND',
+        ALIAS    => $alias,
+        FIELD    => 'id',
+        OPERATOR => 'IS',
+        VALUE    => 'NULL',
+    );
+}
+
+=head2 LimitTargetToApplied
+
+Limits collection to custom fields to listed objects or any corespondingly. Use
+zero to mean global.
+
+=cut
+
+sub LimitTargetToApplied {
+    my $self = shift;
+    my $collection = shift;
+    my @ids = @_;
+
+    my $alias = $self->JoinTargetToApplied($collection => @ids);
+
+    $collection->Limit(
+        ENTRYAGGREGATOR => 'AND',
+        ALIAS    => $alias,
+        FIELD    => 'id',
+        OPERATOR => 'IS NOT',
+        VALUE    => 'NULL',
+    );
+}
+
+sub JoinTargetToApplied {
+    my $self = shift;
+    my $collection = shift;
+    my @ids = @_;
+
+    my $alias = $self->JoinTargetToThis( $collection, New => 1, Left => 1 );
+    return $alias unless @ids;
+
+    # XXX: we need different EA in join clause, but DBIx::SB
+    # doesn't support them, use IN (X) instead
+    my $dbh = $self->_Handle->dbh;
+    $collection->Limit(
+        LEFTJOIN   => $alias,
+        ALIAS      => $alias,
+        FIELD      => 'ObjectId',
+        OPERATOR   => 'IN',
+        QUOTEVALUE => 0,
+        VALUE      => "(". join( ',', map $dbh->quote($_), @ids ) .")",
+    );
+
+    return $alias;
+}
+
+sub JoinTargetToThis {
+    my $self = shift;
+    my $collection = shift;
+    my %args = ( New => 0, Left => 0, @_ );
+
+    my $table = $self->Table;
+    my $key = "_sql_${table}_alias";
+
+    return $collection->{ $key } if $collection->{ $key } && !$args{'New'};
+
+    my $alias = $collection->Join(
+        $args{'Left'} ? (TYPE => 'LEFT') : (),
+        ALIAS1 => 'main',
+        FIELD1 => 'id',
+        TABLE2 => $table,
+        FIELD2 => $self->RecordClass->TargetField,
+    );
+    return $alias if $args{'New'};
+    return $collection->{ $key } = $alias;
+}
+
 =head2 NewItem
 
 Returns an empty new collection's item

commit 4729c32ef5c32a4737f442d6ee77427b79cd91cd
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Dec 14 23:36:40 2011 +0400

    ObjectScrips table in schemas

diff --git a/etc/acl.Pg b/etc/acl.Pg
index 9da28db..461dbfd 100755
--- a/etc/acl.Pg
+++ b/etc/acl.Pg
@@ -23,6 +23,8 @@ sub acl {
         Transactions 
         scrips_id_seq
         Scrips 
+        objectscrips_id_seq
+        ObjectScrips
         acl_id_seq
         ACL 
         groupmembers_id_seq
diff --git a/etc/schema.Oracle b/etc/schema.Oracle
index 4bcae6c..751d03f 100755
--- a/etc/schema.Oracle
+++ b/etc/schema.Oracle
@@ -150,6 +150,19 @@ CREATE TABLE Scrips (
   	LastUpdated 	DATE  
 );
 
+CREATE SEQUENCE OBJECTSCRIPS_seq;
+CREATE TABLE ObjectScrips (
+	id		NUMBER(11,0)
+                 CONSTRAINT ObjectScrips_Key PRIMARY KEY,
+        Scrip       NUMBER(11,0)  NOT NULL,
+        ObjectId              NUMBER(11,0)  NOT NULL,
+	SortOrder	NUMBER(11,0) DEFAULT 0 NOT NULL,
+	Creator		NUMBER(11,0) DEFAULT 0 NOT NULL,
+	Created		DATE,
+	LastUpdatedBy	NUMBER(11,0) DEFAULT 0 NOT NULL,
+	LastUpdated	DATE
+);
+
 
 CREATE SEQUENCE ACL_seq;
 CREATE TABLE ACL (
diff --git a/etc/schema.Pg b/etc/schema.Pg
index 565f76b..570b614 100755
--- a/etc/schema.Pg
+++ b/etc/schema.Pg
@@ -242,6 +242,21 @@ CREATE TABLE Scrips (
 );
 
 
+CREATE SEQUENCE objectscrips_id_seq;
+
+CREATE TABLE ObjectScrips (
+  id INTEGER DEFAULT nextval('objectscrips_id_seq'),
+  Scrip integer NOT NULL,
+  ObjectId integer NOT NULL,
+  SortOrder integer NOT NULL DEFAULT 0  ,
+
+  Creator integer NOT NULL DEFAULT 0  ,
+  Created TIMESTAMP NULL  ,
+  LastUpdatedBy integer NOT NULL DEFAULT 0  ,
+  LastUpdated TIMESTAMP NULL  ,
+  PRIMARY KEY (id)
+
+);
 
 
 
diff --git a/etc/schema.SQLite b/etc/schema.SQLite
index 6897be2..5a770f5 100755
--- a/etc/schema.SQLite
+++ b/etc/schema.SQLite
@@ -162,6 +162,19 @@ CREATE TABLE Scrips (
 
 --- }}}
 
+CREATE TABLE ObjectScrips (
+  id INTEGER NOT NULL  ,
+  Scrip int NOT NULL  ,
+  ObjectId integer NOT NULL,
+  SortOrder integer NOT NULL DEFAULT 0  ,
+
+  Creator integer NOT NULL DEFAULT 0  ,
+  Created DATETIME NULL  ,
+  LastUpdatedBy integer NOT NULL DEFAULT 0  ,
+  LastUpdated DATETIME NULL  ,
+  PRIMARY KEY (id)
+);
+
 --- {{{ ACL
 CREATE TABLE ACL (
   id INTEGER PRIMARY KEY  ,
diff --git a/etc/schema.mysql b/etc/schema.mysql
index 9ed0337..adf0ef1 100755
--- a/etc/schema.mysql
+++ b/etc/schema.mysql
@@ -152,6 +152,19 @@ CREATE TABLE Scrips (
   PRIMARY KEY (id)
 ) ENGINE=InnoDB CHARACTER SET utf8;
 
+CREATE TABLE ObjectScrips (
+  id INTEGER NOT NULL  AUTO_INCREMENT,
+  Scrip integer NOT NULL  ,
+  ObjectId integer NOT NULL,
+  SortOrder integer NOT NULL DEFAULT 0  ,
+
+  Creator integer NOT NULL DEFAULT 0  ,
+  Created DATETIME NULL  ,
+  LastUpdatedBy integer NOT NULL DEFAULT 0  ,
+  LastUpdated DATETIME NULL  ,
+  PRIMARY KEY (id)
+) ENGINE=InnoDB CHARACTER SET utf8;
+
 
 CREATE TABLE ACL (
   id INTEGER NOT NULL  AUTO_INCREMENT,

commit 045c08cd26d4fe2ded99ff968ce29570844fd394
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Dec 14 23:38:43 2011 +0400

    ObjectScrip[s] classes
    
    * scrips <-> queues many to many relation
    * custom sorting

diff --git a/lib/RT/ObjectScrip.pm b/lib/RT/ObjectScrip.pm
new file mode 100644
index 0000000..d08a2dc
--- /dev/null
+++ b/lib/RT/ObjectScrip.pm
@@ -0,0 +1,208 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+#                                          <sales at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+use strict;
+use warnings;
+
+package RT::ObjectScrip;
+use base 'RT::Record::ApplyAndSort';
+
+use RT::Scrip;
+use RT::ObjectScrips;
+
+sub Table {'ObjectScrips'}
+sub ObjectCollectionClass {'RT::Queues'}
+
+=head2 ScripObj
+
+Returns the Scrip Object which has the id returned by Scrip
+
+=cut
+
+sub ScripObj {
+    my $self = shift;
+    my $id = shift || $self->Scrip;
+    my $obj = RT::Scrip->new( $self->CurrentUser );
+    $obj->Load( $id );
+    return $obj;
+}
+
+sub Neighbors {
+    my $self = shift;
+    my %args = @_;
+
+    my $res = $self->CollectionClass->new( $self->CurrentUser );
+    return $res;
+}
+
+=head2 id
+
+Returns the current value of id.
+(In the database, id is stored as int(11).)
+
+
+=cut
+
+
+=head2 Scrip
+
+Returns the current value of Scrip.
+(In the database, Scrip is stored as int(11).)
+
+
+
+=head2 SetScrip VALUE
+
+
+Set Scrip to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Scrip will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 ObjectId
+
+Returns the current value of ObjectId.
+(In the database, ObjectId is stored as int(11).)
+
+
+
+=head2 SetObjectId VALUE
+
+
+Set ObjectId to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, ObjectId will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 SortOrder
+
+Returns the current value of SortOrder.
+(In the database, SortOrder is stored as int(11).)
+
+
+
+=head2 SetSortOrder VALUE
+
+
+Set SortOrder to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, SortOrder will be stored as a int(11).)
+
+
+=cut
+
+
+=head2 Creator
+
+Returns the current value of Creator.
+(In the database, Creator is stored as int(11).)
+
+
+=cut
+
+
+=head2 Created
+
+Returns the current value of Created.
+(In the database, Created is stored as datetime.)
+
+
+=cut
+
+
+=head2 LastUpdatedBy
+
+Returns the current value of LastUpdatedBy.
+(In the database, LastUpdatedBy is stored as int(11).)
+
+
+=cut
+
+
+=head2 LastUpdated
+
+Returns the current value of LastUpdated.
+(In the database, LastUpdated is stored as datetime.)
+
+
+=cut
+
+
+
+sub _CoreAccessible {
+    {
+
+        id =>
+		{read => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
+        Scrip =>
+		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
+        ObjectId =>
+		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
+        SortOrder =>
+		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
+        Creator =>
+		{read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
+        Created =>
+		{read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
+        LastUpdatedBy =>
+		{read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
+        LastUpdated =>
+		{read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
+
+ }
+};
+
+RT::Base->_ImportOverlays();
+
+1;
diff --git a/lib/RT/ObjectScrips.pm b/lib/RT/ObjectScrips.pm
new file mode 100644
index 0000000..d8bbf35
--- /dev/null
+++ b/lib/RT/ObjectScrips.pm
@@ -0,0 +1,68 @@
+# BEGIN BPS TAGGED BLOCK {{{
+#
+# COPYRIGHT:
+#
+# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+#                                          <sales at bestpractical.com>
+#
+# (Except where explicitly superseded by other copyright notices)
+#
+#
+# LICENSE:
+#
+# This work is made available to you under the terms of Version 2 of
+# the GNU General Public License. A copy of that license should have
+# been provided with this software, but in any event can be snarfed
+# from www.gnu.org.
+#
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301 or visit their web page on the internet at
+# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+#
+#
+# CONTRIBUTION SUBMISSION POLICY:
+#
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of
+# the GNU General Public License and is only of importance to you if
+# you choose to contribute your changes and enhancements to the
+# community by submitting them to Best Practical Solutions, LLC.)
+#
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with
+# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+# you are the copyright holder for those contributions and you grant
+# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+# royalty-free, perpetual, license to use, copy, create derivative
+# works based on those contributions, and sublicense and distribute
+# those contributions and any derivatives thereof.
+#
+# END BPS TAGGED BLOCK }}}
+
+use strict;
+use warnings;
+
+package RT::ObjectScrips;
+use base 'RT::SearchBuilder::ApplyAndSort';
+
+use RT::Scrips;
+use RT::ObjectScrip;
+
+sub Table { 'ObjectScrips'}
+
+sub LimitToScrip {
+    my $self = shift;
+    my $id = shift;
+    $self->Limit( FIELD => 'Scrip', VALUE => $id );
+}
+
+RT::Base->_ImportOverlays();
+
+1;

commit 74b8b7b6e066a026462e9abf6de146776380fd59
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sun Dec 18 02:32:24 2011 +0400

    drop Queue column in Scrips and usage of it in most places

diff --git a/etc/schema.Oracle b/etc/schema.Oracle
index 751d03f..1f82447 100755
--- a/etc/schema.Oracle
+++ b/etc/schema.Oracle
@@ -142,7 +142,6 @@ CREATE TABLE Scrips (
 	CustomPrepareCode	CLOB,
 	CustomCommitCode	CLOB,
 	Stage		VARCHAR2(32),
-	Queue		NUMBER(11,0) DEFAULT 0 NOT NULL,
 	Template	NUMBER(11,0) DEFAULT 0 NOT NULL,
   	Creator 	NUMBER(11,0) DEFAULT 0 NOT NULL,
   	Created 	DATE,
diff --git a/etc/schema.Pg b/etc/schema.Pg
index 570b614..b57e0bf 100755
--- a/etc/schema.Pg
+++ b/etc/schema.Pg
@@ -231,7 +231,6 @@ CREATE TABLE Scrips (
   CustomPrepareCode text NULL  ,
   CustomCommitCode text NULL  ,
   Stage varchar(32) NULL  ,
-  Queue integer NOT NULL DEFAULT 0  ,
   Template integer NOT NULL DEFAULT 0  ,
   Creator integer NOT NULL DEFAULT 0  ,
   Created TIMESTAMP NULL  ,
diff --git a/etc/schema.SQLite b/etc/schema.SQLite
index 5a770f5..ed37ad4 100755
--- a/etc/schema.SQLite
+++ b/etc/schema.SQLite
@@ -151,7 +151,6 @@ CREATE TABLE Scrips (
   CustomPrepareCode text NULL  ,
   CustomCommitCode text NULL  ,
   Stage varchar(32) NULL  ,
-  Queue integer NULL DEFAULT 0 ,
   Template integer NULL DEFAULT 0 ,
   Creator integer NULL DEFAULT 0 ,
   Created DATETIME NULL  ,
diff --git a/etc/schema.mysql b/etc/schema.mysql
index adf0ef1..31bf3b8 100755
--- a/etc/schema.mysql
+++ b/etc/schema.mysql
@@ -143,7 +143,6 @@ CREATE TABLE Scrips (
   CustomPrepareCode text NULL  ,
   CustomCommitCode text NULL  ,
   Stage varchar(32) CHARACTER SET ascii NULL  ,
-  Queue integer NOT NULL DEFAULT 0  ,
   Template integer NOT NULL DEFAULT 0  ,
   Creator integer NOT NULL DEFAULT 0  ,
   Created DATETIME NULL  ,
diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index 40b030c..5a37fc6 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -170,7 +170,6 @@ sub Create {
         unless $condition->Id;
 
     my ( $id, $msg ) = $self->SUPER::Create(
-        Queue                  => $args{'Queue'},
         Template               => $template->Id,
         ScripCondition         => $condition->id,
         Stage                  => $args{'Stage'},
@@ -208,21 +207,10 @@ sub Delete {
 
 
 
-=head2 QueueObj
-
-Retuns an RT::Queue object with this Scrip's queue
-
-=cut
 
 sub QueueObj {
     my $self = shift;
 
-    if ( !$self->{'QueueObj'} ) {
-        require RT::Queue;
-        $self->{'QueueObj'} = RT::Queue->new( $self->CurrentUser );
-        $self->{'QueueObj'}->Load( $self->__Value('Queue') );
-    }
-    return ( $self->{'QueueObj'} );
 }
 
 
@@ -504,8 +492,7 @@ sub _Set {
     );
 
     unless ( $self->CurrentUserHasRight('ModifyScrips') ) {
-        $RT::Logger->debug(
-                 "CurrentUser can't modify Scrips for " . $self->Queue . "\n" );
+        $RT::Logger->debug( "CurrentUser can't modify Scrips" );
         return ( 0, $self->loc('Permission Denied') );
     }
 
@@ -550,9 +537,7 @@ sub _Value {
     my $self = shift;
 
     unless ( $self->CurrentUserHasRight('ShowScrips') ) {
-        $RT::Logger->debug( "CurrentUser can't modify Scrips for "
-                            . $self->__Value('Queue')
-                            . "\n" );
+        $RT::Logger->debug( "CurrentUser can't see scrip #". $self->__Value('id') );
         return (undef);
     }
 
@@ -904,24 +889,6 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 =cut
 
 
-=head2 Queue
-
-Returns the current value of Queue.
-(In the database, Queue is stored as int(11).)
-
-
-
-=head2 SetQueue VALUE
-
-
-Set Queue to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Queue will be stored as a int(11).)
-
-
-=cut
-
-
 =head2 Template
 
 Returns the current value of Template.
@@ -1000,8 +967,6 @@ sub _CoreAccessible {
 		{read => 1, write => 1, sql_type => -4, length => 0,  is_blob => 1,  is_numeric => 0,  type => 'text', default => ''},
         Stage =>
 		{read => 1, write => 1, sql_type => 12, length => 32,  is_blob => 0,  is_numeric => 0,  type => 'varchar(32)', default => ''},
-        Queue =>
-		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
         Template =>
 		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
         Creator =>
diff --git a/share/html/Admin/Elements/EditScrip b/share/html/Admin/Elements/EditScrip
index e4c49ab..bd73b83 100644
--- a/share/html/Admin/Elements/EditScrip
+++ b/share/html/Admin/Elements/EditScrip
@@ -190,7 +190,7 @@ else {
     return (undef, loc("Couldn't load scrip #[_1]", $id))
         unless $scrip->id;
 
-    my @attribs = qw(Queue ScripAction ScripCondition Template Stage
+    my @attribs = qw(ScripAction ScripCondition Template Stage
         Description CustomPrepareCode CustomCommitCode CustomIsApplicableCode);
     my @results = UpdateRecordObject(
         AttributesRef   => \@attribs,

commit 9eae6edd43b92eab2719a5dbdfaadf3d553c6628
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sun Dec 18 02:34:33 2011 +0400

    use new ApplySort API in Scrip[s] classes

diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index 5a37fc6..e7b36ed 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -73,6 +73,9 @@ use RT::Queue;
 use RT::Template;
 use RT::ScripCondition;
 use RT::ScripAction;
+use RT::Scrips;
+use RT::ObjectScrip;
+
 use base 'RT::Record';
 
 sub Table {'Scrips'}
@@ -179,12 +182,15 @@ sub Create {
         CustomCommitCode       => $args{'CustomCommitCode'},
         CustomIsApplicableCode => $args{'CustomIsApplicableCode'},
     );
-    if ( $id ) {
-        return ( $id, $self->loc('Scrip Created') );
-    }
-    else {
-        return ( $id, $msg );
+    return ( $id, $msg ) unless $id;
+
+    unless ( $args{'Stage'} eq 'Disabled' ) {
+        my ($status, $msg) = RT::ObjectScrip->new( $self->CurrentUser )
+            ->Apply( Scrip => $self, ObjectId => $args{'Queue'} );
+        $RT::Logger->error( "Couldn't apply scrip: $msg" ) unless $status;
     }
+
+    return ( $id, $self->loc('Scrip Created') );
 }
 
 
@@ -205,15 +211,42 @@ sub Delete {
     return ( $self->SUPER::Delete(@_) );
 }
 
+sub IsAdded {
+    my $self = shift;
+    my $record = RT::ObjectScrip->new( $self->CurrentUser );
+    $record->LoadByCols( Scrip => $self->id, ObjectId => shift || 0 );
+    return $record->id;
+}
 
+sub AddedTo {
+    my $self = shift;
+    return RT::ObjectScrip->new( $self->CurrentUser )
+        ->AppliedTo( Scrip => $self );
+}
 
+sub NotAddedTo {
+    my $self = shift;
+    return RT::ObjectScrip->new( $self->CurrentUser )
+        ->NotAppliedTo( Scrip => $self );
+}
 
-sub QueueObj {
+sub AddToObject {
     my $self = shift;
+    my $object = shift;
 
+    my $rec = RT::ObjectScrip->new( $self->CurrentUser );
+    return $rec->Apply( Scrip => $self, ObjectId => $object );
 }
 
+sub RemoveFromObject {
+    my $self = shift;
+    my $object = shift;
 
+    my $rec = RT::ObjectScrip->new( $self->CurrentUser );
+    $rec->LoadByCols( Scrip => $self->id, ObjectId => $object );
+    return (0, $self->loc('Scrip is not applied') ) unless $rec->id;
+    return $rec->Delete;
+}
 
 =head2 ActionObj
 
@@ -577,18 +610,20 @@ sub HasRight {
                  Principal => undef,
                  @_ );
 
-    if ( $self->SUPER::_Value('Queue') ) {
-        return $args{'Principal'}->HasRight(
-            Right  => $args{'Right'},
-            Object => $self->QueueObj
-        );
-    }
-    else {
-        return $args{'Principal'}->HasRight(
-            Object => $RT::System,
+    my $queues = $self->AddedTo;
+    my $found = 0;
+    while ( my $queue = $queues->Next ) {
+        return 1 if $args{'Principal'}->HasRight(
             Right  => $args{'Right'},
+            Object => $queue,
         );
+        $found = 1;
     }
+    return $args{'Principal'}->HasRight(
+        Object => $RT::System,
+        Right  => $args{'Right'},
+    ) unless $found;
+    return 0;
 }
 
 
diff --git a/lib/RT/Scrips.pm b/lib/RT/Scrips.pm
index fa33f7e..f7c114f 100644
--- a/lib/RT/Scrips.pm
+++ b/lib/RT/Scrips.pm
@@ -70,6 +70,7 @@ use strict;
 use warnings;
 
 use RT::Scrip;
+use RT::ObjectScrips;
 
 use base 'RT::SearchBuilder';
 
@@ -85,14 +86,17 @@ another call to this method
 =cut
 
 sub LimitToQueue  {
-   my $self = shift;
-  my $queue = shift;
- 
-  $self->Limit (ENTRYAGGREGATOR => 'OR',
-		FIELD => 'Queue',
-		VALUE => "$queue")
-      if defined $queue;
-  
+    my $self = shift;
+    my $queue = shift;
+    return unless defined $queue;
+
+    my $alias = RT::ObjectScrips->new( $self->CurrentUser )
+        ->JoinTargetToThis( $self );
+    $self->Limit(
+        ALIAS => $alias,
+        FIELD => 'ObjectId',
+        VALUE => int $queue,
+    );
 }
 
 
@@ -106,12 +110,32 @@ another call to this method or LimitToQueue
 
 
 sub LimitToGlobal  {
-   my $self = shift;
- 
-  $self->Limit (ENTRYAGGREGATOR => 'OR',
-		FIELD => 'Queue',
-		VALUE => 0);
-  
+    my $self = shift;
+    return $self->LimitToQueue(0);
+}
+
+sub LimitToAdded {
+    my $self = shift;
+    return RT::ObjectScrips->new( $self->CurrentUser )
+        ->LimitTargetToApplied( $self => @_ );
+}
+
+sub LimitToNotAdded {
+    my $self = shift;
+    return RT::ObjectScrips->new( $self->CurrentUser )
+        ->LimitTargetToNotApplied( $self => @_ );
+}
+
+sub ApplySortOrder {
+    my $self = shift;
+    my $order = shift || 'ASC';
+    $self->OrderByCols( {
+        ALIAS => RT::ObjectScrips->new( $self->CurrentUser )
+            ->JoinTargetToThis( $self => @_ )
+        ,
+        FIELD => 'SortOrder',
+        ORDER => $order,
+    } );
 }
 
 # {{{ sub Next 

commit df26fe5581153190f3a228f2554eb641b1a235e0
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sun Dec 18 02:36:15 2011 +0400

    AdminURL column map for scrips
    
    use only one format string in the config

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 43618c5..2487875 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2578,13 +2578,8 @@ Set(%AdminSearchResultFormat,
         .q{,__AppliedTo__, __FriendlyType__, __FriendlyPattern__},
 
     Scrips =>
-        q{'<a href="__WebPath__/Admin/Queues/Scrip.html?id=__id__&Queue=__QueueId__">__id__</a>/TITLE:#'}
-        .q{,'<a href="__WebPath__/Admin/Queues/Scrip.html?id=__id__&Queue=__QueueId__">__Description__</a>/TITLE:Description'}
-        .q{,__Stage__, __Condition__, __Action__, __Template__},
-
-    GlobalScrips =>
-        q{'<a href="__WebPath__/Admin/Global/Scrip.html?id=__id__">__id__</a>/TITLE:#'}
-        .q{,'<a href="__WebPath__/Admin/Global/Scrip.html?id=__id__">__Description__</a>/TITLE:Description'}
+        q{'<a href="__WebPath__/__AdminURL__">__id__</a>/TITLE:#'}
+        .q{,'<a href="__WebPath__/__AdminURL__">__Description__</a>/TITLE:Description'}
         .q{,__Stage__, __Condition__, __Action__, __Template__},
 
     Templates =>
diff --git a/share/html/Admin/Elements/ListGlobalScrips b/share/html/Admin/Elements/ListGlobalScrips
index 701ffbf..fdc94db 100644
--- a/share/html/Admin/Elements/ListGlobalScrips
+++ b/share/html/Admin/Elements/ListGlobalScrips
@@ -61,7 +61,7 @@
 % }
 
 <%init>
-my $Format = RT->Config->Get('AdminSearchResultFormat')->{'GlobalScrips'};
+my $Format = RT->Config->Get('AdminSearchResultFormat')->{'Scrips'};
 
 my $Scrips = RT::Scrips->new( $session{'CurrentUser'} );
 $Scrips->LimitToGlobal;
diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index 776a2d8..6cc2d54 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -98,6 +98,20 @@ my $COLUMN_MAP = {
 	attribute => 'Stage',
 	value     => sub { return $_[0]->Stage() },
     },
+
+    AdminURL => {
+	value     => sub {
+            my $queue = $_[0]->AddedTo->First;
+            my $res = 'Admin';
+            if ( $queue ) {
+                $res .= '/Queues/Scrip.html?Queue='. $queue->id .'&';
+            } else {
+                $res .= '/Global/Scrip.html?';
+            }
+            $res .= 'id='. $_[0]->id;
+            return $res;
+        },
+    },
 };
 
 </%ONCE>

commit 6841c4bebc3498722d9438420257c8654a82a3f8
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sun Dec 18 02:38:10 2011 +0400

    column maps fro removing scrips and moving them

diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index 6cc2d54..bf3dde9 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -112,6 +112,60 @@ my $COLUMN_MAP = {
             return $res;
         },
     },
+    RemoveCheckBox => {
+        title => sub {
+            my $name = 'RemoveScrip';
+            my $checked = $m->request_args->{ $name .'All' }? 'checked="checked"': '';
+
+            return \qq{<input type="checkbox" name="${name}All" value="1" $checked
+                              onclick="setCheckbox(this.form, '$name', this.checked)" />};
+        },
+        value => sub {
+            my $id = $_[0]->id;
+            return '' if $_[0]->IsAdded;
+
+            my $name = 'RemoveScrip';
+            my $arg = $m->request_args->{ $name };
+
+            my $checked = '';
+            if ( $arg && ref $arg ) {
+                $checked = 'checked="checked"' if grep $_ == $id, @$arg;
+            }
+            elsif ( $arg ) {
+                $checked = 'checked="checked"' if $arg == $id;
+            }
+            return \qq{<input type="checkbox" name="$name" value="$id" $checked />}
+        },
+    },
+    Move => {
+        title => 'Move',
+        value => sub {
+            my $id = $_[0]->id;
+
+            my $context = $_[2] || 0;
+            return '' unless $_[0]->IsAdded( $context );
+
+            my $name = 'MoveCustomField';
+            my $args = $m->caller_args( 1 );
+            my @pass = ref $args->{'PassArguments'}
+                ? @{$args->{'PassArguments'}}
+                : ($args->{'PassArguments'});
+            my %pass = map { $_ => $args->{$_} } grep exists $args->{$_}, @pass;
+
+            my $uri = RT->Config->Get('WebPath') . $m->request_path;
+
+            my @res = (
+                \'<a href="',
+                $uri .'?'. $m->comp("/Elements/QueryString", %pass, MoveScripUp => $id ),
+                \'">', loc('[Up]'), \'</a>',
+                \' <a href="',
+                $uri .'?'. $m->comp("/Elements/QueryString", %pass, MoveScripDown => $id ),
+                \'">', loc('[Down]'), \'</a>'
+            );
+
+            return @res;
+        },
+    },
 };
 
 </%ONCE>

commit 34b43e120bfa2ae28138767b41cb10f766d5144a
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sun Dec 18 02:38:52 2011 +0400

    new UI for scrips management

diff --git a/share/html/Admin/Elements/EditScrips b/share/html/Admin/Elements/EditScrips
index 2fdcae6..410fa01 100644
--- a/share/html/Admin/Elements/EditScrips
+++ b/share/html/Admin/Elements/EditScrips
@@ -50,70 +50,122 @@
 <form action="Scrips.html" method="post">
 <input type="hidden" class="hidden" name="id" value="<% $id %>" />
 
-<h2><&|/l&>Current Scrips</&></h2>
+<h2><&|/l&>On create scrips</&></h2>
+% my $scrips = $find_scrips->(Stage => 'TransactionCreate');
+<& /Elements/CollectionList, %common_applied_args, Collection => $scrips &>
+% unless ( $scrips->Count ) {
+<p><i><&|/l&>(No scrips)</&></i></p>
+% }
+
+<h2><&|/l&>Batch scrips</&></h2>
+% $scrips = $find_scrips->(Stage => 'TransactionBatch');
+<& /Elements/CollectionList, %common_applied_args, Collection => $scrips &>
+% unless ( $scrips->Count ) {
+<p><i><&|/l&>(No scrips)</&></i></p>
+% }
+
+<& /Elements/Submit,
+    Name => 'RemoveScrips',
+    Caption => loc("Un-apply selected scrips"),
+    Label => loc("Update"),
+&>
+
+<h2><&|/l&>Not applied scrips</&></h2>
+% $scrips = $find_scrips->(Applied => 0);
 <& /Elements/CollectionList,
-    OrderBy => 'Description',
-    Order => 'ASC',
-    Rows  => 100,
+    Rows => 50,
+    Page => 1,
     %ARGS,
-    Format => $Format,
-    DisplayFormat => "__CheckBox.{DeleteScrip}__, $Format",
-    Collection => $Scrips,
+    Collection => $scrips,
+    Format     => $Format,
+    DisplayFormat => "__CheckBox.{AddScrip}__, $Format",
     AllowSorting => 1,
-    PassArguments => [ qw(Query Format Rows Page Order OrderBy id) ],
+    PassArguments => [ qw(Format Rows Page Order OrderBy id) ],
 &>
-
-% if ( $Scrips->Count ) {
-<p><i><&|/l&>(Check box to delete)</&></i></p>
-% } else  {
+% unless ( $scrips->Count ) {
 <p><i><&|/l&>(No scrips)</&></i></p>
 % }
+
 <& /Elements/Submit,
-    Caption => loc("Delete selected scrips"),
-    Label => loc("Delete")
+    Name => 'AddScrips',
+    Caption => loc("Apply selected scrips"),
+    Label => loc("Update"),
 &>
+
 </form>
 
 <%init>
 my (@actions);
 
-my $Scrips = RT::Scrips->new($session{'CurrentUser'});
-
-my $QueueObj = RT::Queue->new($session{'CurrentUser'});
 if ( $id ) {
+    my $QueueObj = RT::Queue->new($session{'CurrentUser'});
     $QueueObj->Load( $id );
-    unless ( $QueueObj->id ) {
-        push @actions, loc("Couldn't load queue #[_1]", $id)
-    }
+    Abort(loc("Couldn't load queue #[_1]", $id)) unless $QueueObj->id;
 }
 
-if ($QueueObj->id) {
-    $Scrips->LimitToQueue($id);
-    $Format ||= RT->Config->Get('AdminSearchResultFormat')->{'Scrips'};
-}
-else {
-    $Scrips->LimitToGlobal();
-    $Format ||= RT->Config->Get('AdminSearchResultFormat')->{'GlobalScrips'};
+my $find_scrips = sub {
+    my %args = (Applied => 1, @_);
+    my $scrips = RT::Scrips->new($session{'CurrentUser'});
+    $scrips->Limit( FIELD => 'Stage', VALUE => $args{'Stage'} )
+        if $args{'Stage'};
+    my $method = $args{'Applied'}? 'LimitToAdded' : 'LimitToNotAdded';
+    $scrips->$method(0, $id);
+    $scrips->ApplySortOrder if $args{'Applied'};
+    return $scrips;
+};
+
+$Format ||= RT->Config->Get('AdminSearchResultFormat')->{'Scrips'};
+my $DisplayFormat = $Format;
+if ( $id ) {
+    $DisplayFormat = "__RemoveCheckBox__, $DisplayFormat";
+} else {
+    $DisplayFormat = "__CheckBox.{RemoveScrip}__, $DisplayFormat";
 }
+$DisplayFormat .= ", __Move__";
+
+my %common_applied_args = (
+    %ARGS,
+    Format => $Format,
+    DisplayFormat => $DisplayFormat,
+    Rows => 0,
+    Page => 1,
+    AllowSorting => 0,
+    PassArguments => [ qw(Format id) ],
+);
 
-# deal with modifying and deleting existing scrips
-# we still support DeleteScrip-id format but array is preferred
-foreach my $id ( grep $_, @DeleteScrip, map /^DeleteScrip-(\d+)/, keys %ARGS ) {
-    my $scrip = RT::Scrip->new($session{'CurrentUser'});
-    $scrip->Load( $id );
-    my ($retval, $msg) = $scrip->Delete;
-    if ($retval) {
-        push @actions, loc("Scrip deleted");
+if ( $RemoveScrips ) {
+    foreach my $sid ( @RemoveScrip ) {
+        my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
+        $scrip->Load( $sid );
+        next unless $scrip->id;
+
+        my ($status, $msg) = $scrip->RemoveFromObject( $id );
+        push @actions, $msg;
     }
-    else {
+}
+
+if ( $AddScrips ) {
+    foreach my $sid ( @AddScrip ) {
+        my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
+        $scrip->Load( $sid );
+        next unless $scrip->id;
+
+        my ($status, $msg) = $scrip->AddToObject( $id );
         push @actions, $msg;
     }
 }
+
 </%init>
 
 <%ARGS>
 $id => undef
 $title => undef
 $Format => undef
- at DeleteScrip => ()
+
+ at RemoveScrip => ()
+$RemoveScrips => undef
+
+ at AddScrip => ()
+$AddScrips => undef
+
 </%ARGS>

commit c62b2be8cf06f5428c2d73e12c59eeb052c0e9c1
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Dec 20 17:00:52 2011 +0400

    default Disabled to zero
    
    explicit NULL value doesn't trigger DB side default

diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
index 0d3f297..89d99ef 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -93,6 +93,7 @@ sub Create {
             ObjectId => $args{'ObjectId'},
         );
     }
+    $args{'Disabled'} ||= 0;
 
     return $self->SUPER::Create(
         %args,

commit bf421867afd65cd45e162cf79bcfd3278032132e
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Dec 20 17:03:52 2011 +0400

    drop handling of disabled column
    
    we move column into OCFs table and will handle it
    differently

diff --git a/lib/RT/ObjectCustomFields.pm b/lib/RT/ObjectCustomFields.pm
index c863dab..72b1894 100644
--- a/lib/RT/ObjectCustomFields.pm
+++ b/lib/RT/ObjectCustomFields.pm
@@ -103,17 +103,6 @@ sub CustomFields {
     map { $_->CustomFieldObj } @{$self->ItemsArrayRef};
 }
 
-sub _DoSearch {
-    my $self = shift;
-    if ($self->{'_cfs_alias'}) {
-    $self->Limit( ALIAS           => $self->{'_cfs_alias'},
-                 FIELD           => 'Disabled',
-                 OPERATOR        => '!=',
-                 VALUE           =>  1);
-    }
-    $self->SUPER::_DoSearch()
-}
-
 RT::Base->_ImportOverlays();
 
 1;

commit c7d58bc1c3d81ea71b171d176ea0eb5ea6a9e729
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Dec 20 17:07:36 2011 +0400

    move Disabled column from things we apply
    
    We move it into Object{CustomField,Scrip}s table, so
    particular application can be disabled in theory and
    disabling doesn't change order when we re-enable.
    
    In first implementation we disable/enable all records
    at once through applied object.

diff --git a/etc/schema.Oracle b/etc/schema.Oracle
index 1f82447..e570a4f 100755
--- a/etc/schema.Oracle
+++ b/etc/schema.Oracle
@@ -156,6 +156,7 @@ CREATE TABLE ObjectScrips (
         Scrip       NUMBER(11,0)  NOT NULL,
         ObjectId              NUMBER(11,0)  NOT NULL,
 	SortOrder	NUMBER(11,0) DEFAULT 0 NOT NULL,
+	Disabled	NUMBER(11,0) DEFAULT 0 NOT NULL,
 	Creator		NUMBER(11,0) DEFAULT 0 NOT NULL,
 	Created		DATE,
 	LastUpdatedBy	NUMBER(11,0) DEFAULT 0 NOT NULL,
@@ -331,6 +332,7 @@ CREATE TABLE ObjectCustomFields (
         CustomField       NUMBER(11,0)  NOT NULL,
         ObjectId              NUMBER(11,0)  NOT NULL,
 	SortOrder	NUMBER(11,0) DEFAULT 0 NOT NULL,
+	Disabled	NUMBER(11,0) DEFAULT 0 NOT NULL,
 	Creator		NUMBER(11,0) DEFAULT 0 NOT NULL,
 	Created		DATE,
 	LastUpdatedBy	NUMBER(11,0) DEFAULT 0 NOT NULL,
@@ -378,8 +380,7 @@ CREATE TABLE CustomFields (
 	Creator		NUMBER(11,0) DEFAULT 0 NOT NULL,
 	Created		DATE,
 	LastUpdatedBy	NUMBER(11,0) DEFAULT 0 NOT NULL,
-	LastUpdated	DATE,
-	Disabled	NUMBER(11,0) DEFAULT 0 NOT NULL
+	LastUpdated	DATE
 );
 
 
diff --git a/etc/schema.Pg b/etc/schema.Pg
index b57e0bf..79a0158 100755
--- a/etc/schema.Pg
+++ b/etc/schema.Pg
@@ -248,6 +248,7 @@ CREATE TABLE ObjectScrips (
   Scrip integer NOT NULL,
   ObjectId integer NOT NULL,
   SortOrder integer NOT NULL DEFAULT 0  ,
+  Disabled integer NOT NULL DEFAULT 0 ,
 
   Creator integer NOT NULL DEFAULT 0  ,
   Created TIMESTAMP NULL  ,
@@ -568,7 +569,6 @@ CREATE TABLE CustomFields (
   Created TIMESTAMP NULL  ,
   LastUpdatedBy integer NOT NULL DEFAULT 0  ,
   LastUpdated TIMESTAMP NULL  ,
-  Disabled integer NOT NULL DEFAULT 0 ,
   PRIMARY KEY (id)
 
 );
@@ -584,6 +584,7 @@ CREATE TABLE ObjectCustomFields (
   CustomField integer NOT NULL,
   ObjectId integer NOT NULL,
   SortOrder integer NOT NULL DEFAULT 0  ,
+  Disabled integer NOT NULL DEFAULT 0 ,
 
   Creator integer NOT NULL DEFAULT 0  ,
   Created TIMESTAMP NULL  ,
diff --git a/etc/schema.SQLite b/etc/schema.SQLite
index ed37ad4..42eacf7 100755
--- a/etc/schema.SQLite
+++ b/etc/schema.SQLite
@@ -166,6 +166,7 @@ CREATE TABLE ObjectScrips (
   Scrip int NOT NULL  ,
   ObjectId integer NOT NULL,
   SortOrder integer NOT NULL DEFAULT 0  ,
+  Disabled int2 NOT NULL DEFAULT 0 ,
 
   Creator integer NOT NULL DEFAULT 0  ,
   Created DATETIME NULL  ,
@@ -402,7 +403,6 @@ CREATE TABLE CustomFields (
   Created DATETIME NULL  ,
   LastUpdatedBy integer NOT NULL DEFAULT 0  ,
   LastUpdated DATETIME NULL  ,
-  Disabled int2 NOT NULL DEFAULT 0 ,
   PRIMARY KEY (id)
 ) ;
 
@@ -413,6 +413,7 @@ CREATE TABLE ObjectCustomFields (
   CustomField int NOT NULL  ,
   ObjectId integer NOT NULL,
   SortOrder integer NOT NULL DEFAULT 0  ,
+  Disabled int2 NOT NULL DEFAULT 0 ,
 
   Creator integer NOT NULL DEFAULT 0  ,
   Created DATETIME NULL  ,
diff --git a/etc/schema.mysql b/etc/schema.mysql
index 31bf3b8..3453621 100755
--- a/etc/schema.mysql
+++ b/etc/schema.mysql
@@ -156,6 +156,7 @@ CREATE TABLE ObjectScrips (
   Scrip integer NOT NULL  ,
   ObjectId integer NOT NULL,
   SortOrder integer NOT NULL DEFAULT 0  ,
+  Disabled int2 NOT NULL DEFAULT 0 ,
 
   Creator integer NOT NULL DEFAULT 0  ,
   Created DATETIME NULL  ,
@@ -380,7 +381,6 @@ CREATE TABLE CustomFields (
   Created DATETIME NULL  ,
   LastUpdatedBy integer NOT NULL DEFAULT 0  ,
   LastUpdated DATETIME NULL  ,
-  Disabled int2 NOT NULL DEFAULT 0 ,
   PRIMARY KEY (id)
 ) ENGINE=InnoDB CHARACTER SET utf8;
 
@@ -391,6 +391,7 @@ CREATE TABLE ObjectCustomFields (
   CustomField integer NOT NULL  ,
   ObjectId integer NOT NULL,
   SortOrder integer NOT NULL DEFAULT 0  ,
+  Disabled int2 NOT NULL DEFAULT 0 ,
 
   Creator integer NOT NULL DEFAULT 0  ,
   Created DATETIME NULL  ,
diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index ec436e3..1db0514 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -383,7 +383,6 @@ sub Create {
         BasedOn     => $args{'BasedOn'},
         ValuesClass => $args{'ValuesClass'},
         Description => $args{'Description'},
-        Disabled    => $args{'Disabled'},
         LookupType  => $args{'LookupType'},
         Repeated    => $args{'Repeated'},
     );
@@ -404,6 +403,7 @@ sub Create {
         $OCF->Create(
             CustomField => $self->Id,
             ObjectId => $args{'Queue'},
+            Disabled => $args{'Disabled'},
         );
     }
 
@@ -488,8 +488,10 @@ sub LoadByName {
     # When loading by name, we _can_ load disabled fields, but prefer
     # non-disabled fields.
     $CFs->FindAllRows;
+    my $alias = RT::ObjectCustomFields->new( $self->CurrentUser )
+        ->JoinTargetToThis( $CFs, Left => 1, New => 1 );
     $CFs->OrderByCols(
-        { FIELD => "Disabled", ORDER => 'ASC' },
+        { ALIAS => $alias, FIELD => "Disabled", ORDER => 'ASC' },
     );
 
     # We only want one entry.
@@ -991,6 +993,19 @@ sub _Value {
     return $self->__Value( @_ );
 }
 
+=head2 Disabled
+
+Returns the current value of Disabled. 
+(In the database, Disabled is stored as smallint(6).)
+
+=cut
+
+sub Disabled {
+    my $self = shift;
+    my $record = RT::ObjectCustomField->new( $self->CurrentUser );
+    $record->LoadByCols( CustomField => $self->id );
+    return $record->Disabled;
+}
 
 =head2 SetDisabled
 
@@ -1000,6 +1015,12 @@ Takes a boolean.
 
 =cut
 
+sub SetDisabled {
+    my $self = shift;
+    return RT::ObjectCustomField->new( $self->CurrentUser )
+        ->SetDisabledOnAll( CustomField => $self->id, Value => shift );
+}
+
 
 =head2 SetTypeComposite
 
@@ -1949,29 +1970,9 @@ Returns the current value of LastUpdatedBy.
 Returns the current value of LastUpdated. 
 (In the database, LastUpdated is stored as datetime.)
 
-
 =cut
 
 
-=head2 Disabled
-
-Returns the current value of Disabled. 
-(In the database, Disabled is stored as smallint(6).)
-
-
-
-=head2 SetDisabled VALUE
-
-
-Set Disabled to VALUE. 
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Disabled will be stored as a smallint(6).)
-
-
-=cut
-
-
-
 sub _CoreAccessible {
     {
      
@@ -2005,8 +2006,6 @@ sub _CoreAccessible {
         {read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
         LastUpdated => 
         {read => 1, auto => 1, sql_type => 11, length => 0,  is_blob => 0,  is_numeric => 0,  type => 'datetime', default => ''},
-        Disabled => 
-        {read => 1, write => 1, sql_type => 5, length => 6,  is_blob => 0,  is_numeric => 1,  type => 'smallint(6)', default => '0'},
 
  }
 };
diff --git a/lib/RT/CustomFields.pm b/lib/RT/CustomFields.pm
index bf15b16..37ca251 100644
--- a/lib/RT/CustomFields.pm
+++ b/lib/RT/CustomFields.pm
@@ -91,7 +91,6 @@ sub _Init {
 	   FIELD => 'id',
 	   ORDER => 'ASC' },
      );
-    $self->{'with_disabled_column'} = 1;
 
     return ( $self->SUPER::_Init(@_) );
 }
diff --git a/lib/RT/ObjectCustomField.pm b/lib/RT/ObjectCustomField.pm
index f4d2483..72c9c24 100644
--- a/lib/RT/ObjectCustomField.pm
+++ b/lib/RT/ObjectCustomField.pm
@@ -212,6 +212,8 @@ sub _CoreAccessible {
 		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
         SortOrder =>
 		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
+        Disabled => 
+        {read => 1, write => 1, sql_type => 5, length => 6,  is_blob => 0,  is_numeric => 1,  type => 'smallint(6)', default => '0'},
         Creator =>
 		{read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
         Created =>
diff --git a/lib/RT/ObjectScrip.pm b/lib/RT/ObjectScrip.pm
index d08a2dc..0b02a39 100644
--- a/lib/RT/ObjectScrip.pm
+++ b/lib/RT/ObjectScrip.pm
@@ -191,6 +191,8 @@ sub _CoreAccessible {
 		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
         SortOrder =>
 		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
+        Disabled => 
+        {read => 1, write => 1, sql_type => 5, length => 6,  is_blob => 0,  is_numeric => 1,  type => 'smallint(6)', default => '0'},
         Creator =>
 		{read => 1, auto => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
         Created =>

commit 5df3accd76a118fbc945d504b7a56f8c32760ca0
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Dec 20 18:48:12 2011 +0400

    dummy Create methods to pass only things we need

diff --git a/lib/RT/ObjectCustomField.pm b/lib/RT/ObjectCustomField.pm
index 72c9c24..5f838e3 100644
--- a/lib/RT/ObjectCustomField.pm
+++ b/lib/RT/ObjectCustomField.pm
@@ -64,6 +64,19 @@ sub ObjectCollectionClass {
 
 # XXX: Where is ACL check when we create a record?
 
+sub Create {
+    my $self = shift;
+    my %args = (@_);
+    return $self->SUPER::Create(
+        map { $_ => $args{ $_ } } qw(
+            CustomField ObjectId
+            SortOrder Disabled
+            Created Creator
+            LastUpdated LastUpdatedBy
+        )
+    );
+}
+
 =head2 CustomFieldObj
 
 Returns the CustomField Object which has the id returned by CustomField
diff --git a/lib/RT/ObjectScrip.pm b/lib/RT/ObjectScrip.pm
index 0b02a39..dab264f 100644
--- a/lib/RT/ObjectScrip.pm
+++ b/lib/RT/ObjectScrip.pm
@@ -58,6 +58,19 @@ use RT::ObjectScrips;
 sub Table {'ObjectScrips'}
 sub ObjectCollectionClass {'RT::Queues'}
 
+sub Create {
+    my $self = shift;
+    my %args = (@_);
+    return $self->SUPER::Create(
+        map { $_ => $args{ $_ } } qw(
+            Scrip ObjectId
+            SortOrder Disabled
+            Created Creator
+            LastUpdated LastUpdatedBy
+        )
+    );
+}
+
 =head2 ScripObj
 
 Returns the Scrip Object which has the id returned by Scrip

commit 090653a42631a7d7d552999ae4a5f51b2543413c
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Dec 22 16:04:26 2011 +0400

    sort scrips by sort order, not description

diff --git a/lib/RT/Scrips.pm b/lib/RT/Scrips.pm
index f7c114f..4304ed0 100644
--- a/lib/RT/Scrips.pm
+++ b/lib/RT/Scrips.pm
@@ -391,8 +391,7 @@ sub _FindScrips {
         ENTRYAGGREGATOR => 'OR',
     );
 
-    # Promise some kind of ordering
-    $self->OrderBy( FIELD => 'Description' );
+    $self->ApplySortOrder;
 
     # we call Count below, but later we always do search
     # so just do search and get count from results

commit 4eba94744cb4e87a4c9d6d22d4367d4ef55ab82c
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Dec 22 16:05:14 2011 +0400

    allow to control join via LimitToQueue

diff --git a/lib/RT/Scrips.pm b/lib/RT/Scrips.pm
index 4304ed0..8844595 100644
--- a/lib/RT/Scrips.pm
+++ b/lib/RT/Scrips.pm
@@ -87,15 +87,15 @@ another call to this method
 
 sub LimitToQueue  {
     my $self = shift;
-    my $queue = shift;
-    return unless defined $queue;
+    my %args = @_%2? (Queue => @_) : @_;
+    return unless defined $args{'Queue'};
 
     my $alias = RT::ObjectScrips->new( $self->CurrentUser )
-        ->JoinTargetToThis( $self );
+        ->JoinTargetToThis( $self, %args );
     $self->Limit(
         ALIAS => $alias,
         FIELD => 'ObjectId',
-        VALUE => int $queue,
+        VALUE => int $args{'Queue'},
     );
 }
 

commit 9a59f32f6c96c59904f52a6e5f0678f09daea63a
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Dec 22 16:05:43 2011 +0400

    pass through arguments in LimitToGlobal

diff --git a/lib/RT/Scrips.pm b/lib/RT/Scrips.pm
index 8844595..ca5bdbd 100644
--- a/lib/RT/Scrips.pm
+++ b/lib/RT/Scrips.pm
@@ -111,7 +111,7 @@ another call to this method or LimitToQueue
 
 sub LimitToGlobal  {
     my $self = shift;
-    return $self->LimitToQueue(0);
+    return $self->LimitToQueue(0, @_);
 }
 
 sub LimitToAdded {

commit d62cb4203b95352ab9af27f44fe283ccdee7beda
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Dec 22 16:06:28 2011 +0400

    move Stage column into ObjectScrips table

diff --git a/etc/schema.Oracle b/etc/schema.Oracle
index e570a4f..33787a9 100755
--- a/etc/schema.Oracle
+++ b/etc/schema.Oracle
@@ -141,7 +141,6 @@ CREATE TABLE Scrips (
 	CustomIsApplicableCode	CLOB,
 	CustomPrepareCode	CLOB,
 	CustomCommitCode	CLOB,
-	Stage		VARCHAR2(32),
 	Template	NUMBER(11,0) DEFAULT 0 NOT NULL,
   	Creator 	NUMBER(11,0) DEFAULT 0 NOT NULL,
   	Created 	DATE,
@@ -154,6 +153,7 @@ CREATE TABLE ObjectScrips (
 	id		NUMBER(11,0)
                  CONSTRAINT ObjectScrips_Key PRIMARY KEY,
         Scrip       NUMBER(11,0)  NOT NULL,
+	Stage		VARCHAR2(32) DEFAULT 'TransactionCreate' NOT NULL,
         ObjectId              NUMBER(11,0)  NOT NULL,
 	SortOrder	NUMBER(11,0) DEFAULT 0 NOT NULL,
 	Disabled	NUMBER(11,0) DEFAULT 0 NOT NULL,
diff --git a/etc/schema.Pg b/etc/schema.Pg
index 79a0158..22730e4 100755
--- a/etc/schema.Pg
+++ b/etc/schema.Pg
@@ -230,7 +230,6 @@ CREATE TABLE Scrips (
   CustomIsApplicableCode text NULL  ,
   CustomPrepareCode text NULL  ,
   CustomCommitCode text NULL  ,
-  Stage varchar(32) NULL  ,
   Template integer NOT NULL DEFAULT 0  ,
   Creator integer NOT NULL DEFAULT 0  ,
   Created TIMESTAMP NULL  ,
@@ -246,6 +245,7 @@ CREATE SEQUENCE objectscrips_id_seq;
 CREATE TABLE ObjectScrips (
   id INTEGER DEFAULT nextval('objectscrips_id_seq'),
   Scrip integer NOT NULL,
+  Stage varchar(32) NOT NULL DEFAULT 'TransactionCreate' ,
   ObjectId integer NOT NULL,
   SortOrder integer NOT NULL DEFAULT 0  ,
   Disabled integer NOT NULL DEFAULT 0 ,
diff --git a/etc/schema.SQLite b/etc/schema.SQLite
index 42eacf7..25b1a48 100755
--- a/etc/schema.SQLite
+++ b/etc/schema.SQLite
@@ -150,7 +150,6 @@ CREATE TABLE Scrips (
   CustomIsApplicableCode text NULL  ,
   CustomPrepareCode text NULL  ,
   CustomCommitCode text NULL  ,
-  Stage varchar(32) NULL  ,
   Template integer NULL DEFAULT 0 ,
   Creator integer NULL DEFAULT 0 ,
   Created DATETIME NULL  ,
@@ -164,6 +163,7 @@ CREATE TABLE Scrips (
 CREATE TABLE ObjectScrips (
   id INTEGER NOT NULL  ,
   Scrip int NOT NULL  ,
+  Stage varchar(32) NOT NULL DEFAULT 'TransactionCreate' ,
   ObjectId integer NOT NULL,
   SortOrder integer NOT NULL DEFAULT 0  ,
   Disabled int2 NOT NULL DEFAULT 0 ,
diff --git a/etc/schema.mysql b/etc/schema.mysql
index 3453621..d9e9b73 100755
--- a/etc/schema.mysql
+++ b/etc/schema.mysql
@@ -142,7 +142,6 @@ CREATE TABLE Scrips (
   CustomIsApplicableCode text NULL  ,
   CustomPrepareCode text NULL  ,
   CustomCommitCode text NULL  ,
-  Stage varchar(32) CHARACTER SET ascii NULL  ,
   Template integer NOT NULL DEFAULT 0  ,
   Creator integer NOT NULL DEFAULT 0  ,
   Created DATETIME NULL  ,
@@ -154,6 +153,7 @@ CREATE TABLE Scrips (
 CREATE TABLE ObjectScrips (
   id INTEGER NOT NULL  AUTO_INCREMENT,
   Scrip integer NOT NULL  ,
+  Stage varchar(32) CHARACTER SET ascii NOT NULL DEFAULT 'TransactionCreate',
   ObjectId integer NOT NULL,
   SortOrder integer NOT NULL DEFAULT 0  ,
   Disabled int2 NOT NULL DEFAULT 0 ,
diff --git a/lib/RT.pm b/lib/RT.pm
index 7e3da22..a881e72 100644
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -464,7 +464,7 @@ sub InitClasses {
     if ( $args{'Heavy'} ) {
         # load scrips' modules
         my $scrips = RT::Scrips->new(RT->SystemUser);
-        $scrips->Limit( FIELD => 'Stage', OPERATOR => '!=', VALUE => 'Disabled' );
+        $scrips->LimitToEnabled;
         while ( my $scrip = $scrips->Next ) {
             local $@;
             eval { $scrip->LoadModules } or
diff --git a/lib/RT/ObjectScrip.pm b/lib/RT/ObjectScrip.pm
index dab264f..d7be981 100644
--- a/lib/RT/ObjectScrip.pm
+++ b/lib/RT/ObjectScrip.pm
@@ -116,9 +116,16 @@ Set Scrip to VALUE.
 Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 (In the database, Scrip will be stored as a int(11).)
 
+=head2 Stage
 
-=cut
+Returns the current value of Stage.
+(In the database, Stage is stored as varchar(32).)
+
+=head2 SetStage VALUE
 
+Set Stage to VALUE.
+Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
+(In the database, Stage will be stored as a varchar(32).)
 
 =head2 ObjectId
 
@@ -200,6 +207,8 @@ sub _CoreAccessible {
 		{read => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
         Scrip =>
 		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
+        Stage =>
+		{read => 1, write => 1, sql_type => 12, length => 32,  is_blob => 0,  is_numeric => 0,  type => 'varchar(32)', default => 'TransactionCreate'},
         ObjectId =>
 		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => ''},
         SortOrder =>
diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index e7b36ed..d0a59e6 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -175,7 +175,6 @@ sub Create {
     my ( $id, $msg ) = $self->SUPER::Create(
         Template               => $template->Id,
         ScripCondition         => $condition->id,
-        Stage                  => $args{'Stage'},
         ScripAction            => $action->Id,
         Description            => $args{'Description'},
         CustomPrepareCode      => $args{'CustomPrepareCode'},
@@ -184,12 +183,20 @@ sub Create {
     );
     return ( $id, $msg ) unless $id;
 
-    unless ( $args{'Stage'} eq 'Disabled' ) {
-        my ($status, $msg) = RT::ObjectScrip->new( $self->CurrentUser )
-            ->Apply( Scrip => $self, ObjectId => $args{'Queue'} );
-        $RT::Logger->error( "Couldn't apply scrip: $msg" ) unless $status;
+    if ( $args{'Stage'} eq 'Disabled' ) {
+        $RT::Logger->warning("Disabled Stage is deprecated");
+        $args{'Stage'} = 'TransactionCreate';
+        $args{'Disabled'} = 1;
     }
 
+    (my $status, $msg) = RT::ObjectScrip->new( $self->CurrentUser )->Apply(
+        Scrip    => $self,
+        Stage    => $args{'Stage'},
+        ObjectId => $args{'Queue'},
+        Disabled => $args{'Disabled'},
+    );
+    $RT::Logger->error( "Couldn't apply scrip: $msg" ) unless $status;
+
     return ( $id, $self->loc('Scrip Created') );
 }
 
@@ -317,7 +324,27 @@ sub TemplateObj {
     return ( $self->{'TemplateObj'} );
 }
 
+=head2 Stage
+
+Takes TicketObj named argument and returns scrip's stage when
+applied to ticket's queue.
+
+=cut
+
+sub Stage {
+    my $self = shift;
+    my %args = ( TicketObj => undef, @_ );
+
+    my $queue = $args{'TicketObj'}->Queue;
+    my $rec = RT::ObjectScrip->new( $self->CurrentUser );
+    $rec->LoadByCols( Scrip => $self->id, ObjectId => $queue );
+    return $rec->Stage if $rec->id;
 
+    $rec->LoadByCols( Scrip => $self->id, ObjectId => 0 );
+    return $rec->Stage if $rec->id;
+
+    return undef;
+}
 
 
 =head2 Apply { TicketObj => undef, TransactionObj => undef}
@@ -404,16 +431,24 @@ sub IsApplicable {
 
 	my @Transactions;
 
-        if ( $self->Stage eq 'TransactionCreate') {
+        my $stage = $self->Stage( TicketObj => $args{'TicketObj'} );
+        unless ( $stage ) {
+	    $RT::Logger->error(
+                "Scrip #". $self->id ." is not applied to"
+                ." queue #". $args{'TicketObj'}->Queue
+            );
+	    return (undef);
+        }
+        elsif ( $stage eq 'TransactionCreate') {
 	    # Only look at our current Transaction
 	    @Transactions = ( $args{'TransactionObj'} );
         }
-        elsif ( $self->Stage eq 'TransactionBatch') {
+        elsif ( $stage eq 'TransactionBatch') {
 	    # Look at all Transactions in this Batch
             @Transactions = @{ $args{'TicketObj'}->TransactionBatch || [] };
         }
 	else {
-	    $RT::Logger->error( "Unknown Scrip stage:" . $self->Stage );
+	    $RT::Logger->error( "Unknown Scrip stage: '$stage'" );
 	    return (undef);
 	}
 	my $ConditionObj = $self->ConditionObj;
@@ -906,24 +941,6 @@ Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
 =cut
 
 
-=head2 Stage
-
-Returns the current value of Stage.
-(In the database, Stage is stored as varchar(32).)
-
-
-
-=head2 SetStage VALUE
-
-
-Set Stage to VALUE.
-Returns (1, 'Status message') on success and (0, 'Error Message') on failure.
-(In the database, Stage will be stored as a varchar(32).)
-
-
-=cut
-
-
 =head2 Template
 
 Returns the current value of Template.
@@ -1000,8 +1017,6 @@ sub _CoreAccessible {
 		{read => 1, write => 1, sql_type => -4, length => 0,  is_blob => 1,  is_numeric => 0,  type => 'text', default => ''},
         CustomCommitCode =>
 		{read => 1, write => 1, sql_type => -4, length => 0,  is_blob => 1,  is_numeric => 0,  type => 'text', default => ''},
-        Stage =>
-		{read => 1, write => 1, sql_type => 12, length => 32,  is_blob => 0,  is_numeric => 0,  type => 'varchar(32)', default => ''},
         Template =>
 		{read => 1, write => 1, sql_type => 4, length => 11,  is_blob => 0,  is_numeric => 1,  type => 'int(11)', default => '0'},
         Creator =>
diff --git a/lib/RT/Scrips.pm b/lib/RT/Scrips.pm
index ca5bdbd..5a7dc81 100644
--- a/lib/RT/Scrips.pm
+++ b/lib/RT/Scrips.pm
@@ -126,6 +126,39 @@ sub LimitToNotAdded {
         ->LimitTargetToNotApplied( $self => @_ );
 }
 
+sub LimitByStage  {
+    my $self = shift;
+    my %args = @_%2? (Stage => @_) : @_;
+    return unless defined $args{'Stage'};
+
+    my $alias = RT::ObjectScrips->new( $self->CurrentUser )
+        ->JoinTargetToThis( $self, %args );
+    $self->Limit(
+        ALIAS => $alias,
+        FIELD => 'Stage',
+        VALUE => $args{'Stage'},
+    );
+}
+
+=head2 LimitToEnabled
+
+Limits scrips to that are applied to any queue or globally
+and application is not disabled.
+
+=cut
+
+sub LimitToEnabled {
+    my $self = shift;
+
+    my $alias = RT::ObjectScrips->new( $self->CurrentUser )
+        ->JoinTargetToThis( $self );
+    $self->Limit(
+        ALIAS => $alias,
+        FIELD => 'Disabled',
+        VALUE => 0,
+    );
+}
+
 sub ApplySortOrder {
     my $self = shift;
     my $order = shift || 'ASC';
@@ -354,12 +387,9 @@ sub _FindScrips {
                  @_ );
 
 
-    $self->LimitToQueue( $self->{'TicketObj'}->QueueObj->Id )
-      ;    #Limit it to  $Ticket->QueueObj->Id
-    $self->LimitToGlobal();
-      # or to "global"
-
-    $self->Limit( FIELD => "Stage", VALUE => $args{'Stage'} );
+    $self->LimitToQueue( $self->{'TicketObj'}->QueueObj->Id );
+    $self->LimitToGlobal;
+    $self->LimitByStage( $args{'Stage'} );
 
     my $ConditionsAlias = $self->NewAlias('ScripConditions');
 

commit 925cbafb0f6bde84aeba63e992de316466bca5d0
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Dec 22 16:17:26 2011 +0400

    remove Stage column map

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 2487875..92cc510 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2580,7 +2580,7 @@ Set(%AdminSearchResultFormat,
     Scrips =>
         q{'<a href="__WebPath__/__AdminURL__">__id__</a>/TITLE:#'}
         .q{,'<a href="__WebPath__/__AdminURL__">__Description__</a>/TITLE:Description'}
-        .q{,__Stage__, __Condition__, __Action__, __Template__},
+        .q{, __Condition__, __Action__, __Template__},
 
     Templates =>
         q{'<a href="__WebPath__/__WebRequestPathDir__/Template.html?Queue=__QueueId__&Template=__id__">__id__</a>/TITLE:#'}
diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index bf3dde9..84e70f2 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -93,11 +93,6 @@ my $COLUMN_MAP = {
 	attribute => 'Description',
 	value     => sub { return $_[0]->Description() },
     },
-    Stage => {
-        title     => 'Stage', # loc
-	attribute => 'Stage',
-	value     => sub { return $_[0]->Stage() },
-    },
 
     AdminURL => {
 	value     => sub {

commit 56dec7a12264d1b77ce02fe0523baa37fd58d24e
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Dec 22 16:19:51 2011 +0400

    no more disabled stage

diff --git a/share/html/Admin/Elements/SelectStage b/share/html/Admin/Elements/SelectStage
index 369dee9..dc2746b 100644
--- a/share/html/Admin/Elements/SelectStage
+++ b/share/html/Admin/Elements/SelectStage
@@ -66,8 +66,6 @@ my @stages = 'TransactionCreate';
 push @stages, RT->Config->Get('UseTransactionBatch')
             ? 'TransactionBatch'
             : ['TransactionBatch', 'TransactionBatch (DISABLED)'];
-
-push @stages, 'Disabled';
 </%INIT>
 <%ARGS>
 $Default => 'TransactionCreate'

commit 9dec1347cbb7bfb4684678ddf1cfc2a0fde5b9b0
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Dec 23 02:23:36 2011 +0400

    use LimitByStage instead of direct Limit
    
    Stage column has been moved to ObjectScrips table

diff --git a/share/html/Admin/Elements/EditScrips b/share/html/Admin/Elements/EditScrips
index 410fa01..4db2987 100644
--- a/share/html/Admin/Elements/EditScrips
+++ b/share/html/Admin/Elements/EditScrips
@@ -106,7 +106,7 @@ if ( $id ) {
 my $find_scrips = sub {
     my %args = (Applied => 1, @_);
     my $scrips = RT::Scrips->new($session{'CurrentUser'});
-    $scrips->Limit( FIELD => 'Stage', VALUE => $args{'Stage'} )
+    $scrips->LimitByStage( $args{'Stage'} )
         if $args{'Stage'};
     my $method = $args{'Applied'}? 'LimitToAdded' : 'LimitToNotAdded';
     $scrips->$method(0, $id);

commit 6825c2134eb1c648d23b029e5ac1d7e1214121f0
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Dec 23 02:24:59 2011 +0400

    we don't need separate list of global scrips
    
    global scrips are mixed with global in one list for sorting
    purposes

diff --git a/share/html/Admin/Elements/ListGlobalScrips b/share/html/Admin/Elements/ListGlobalScrips
deleted file mode 100644
index fdc94db..0000000
--- a/share/html/Admin/Elements/ListGlobalScrips
+++ /dev/null
@@ -1,68 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%#                                          <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-% unless ( $Scrips->Count ) {
-<p><i><&|/l&>(No scrips)</&></i></p>
-% } else {
-
-<& /Elements/CollectionList,
-    OrderBy => 'Description',
-    Order => 'ASC',
-    Rows  => 0,
-    %ARGS,
-    Format => $Format,
-    Collection => $Scrips,
-&>
-
-% }
-
-<%init>
-my $Format = RT->Config->Get('AdminSearchResultFormat')->{'Scrips'};
-
-my $Scrips = RT::Scrips->new( $session{'CurrentUser'} );
-$Scrips->LimitToGlobal;
-</%INIT>
diff --git a/share/html/Admin/Queues/Scrips.html b/share/html/Admin/Queues/Scrips.html
index 56e4466..c24ba11 100644
--- a/share/html/Admin/Queues/Scrips.html
+++ b/share/html/Admin/Queues/Scrips.html
@@ -47,12 +47,6 @@
 %# END BPS TAGGED BLOCK }}}
 <& /Admin/Elements/Header, Title => $title &>
 <& /Elements/Tabs &>
-
-% unless ( $QueueObj->Disabled ) { # Global scrips does not apply to disabled queues
-<h2><&|/l&>Scrips which apply to all queues</&></h2>
-<& /Admin/Elements/ListGlobalScrips &>
-<br />
-% }
 <& /Admin/Elements/EditScrips, title => $title, %ARGS &>
 <%init>
 my $QueueObj = RT::Queue->new($session{'CurrentUser'});

commit 0608e85adb8164736c96fcab85590bdb8bdb1951
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Dec 23 02:30:35 2011 +0400

    shrink number of lines of code

diff --git a/share/html/Admin/Queues/Scrips.html b/share/html/Admin/Queues/Scrips.html
index c24ba11..42b2c64 100644
--- a/share/html/Admin/Queues/Scrips.html
+++ b/share/html/Admin/Queues/Scrips.html
@@ -51,14 +51,9 @@
 <%init>
 my $QueueObj = RT::Queue->new($session{'CurrentUser'});
 $QueueObj->Load($id);
+Abort(loc("Queue [_1] not found",$id)) unless $QueueObj->id;
 
-my $title;
-
-if ($QueueObj->id) {
-    $title = loc("Modify scrips for queue [_1]", $QueueObj->Name);
-} else {
-    Abort(loc("Queue [_1] not found",$id));
-}
+my $title = loc("Modify scrips for queue [_1]", $QueueObj->Name);
 </%init>
 
 <%ARGS>

commit f597b18787d2490a4a9738489fd414a280a4a397
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Dec 23 04:11:20 2011 +0400

    Move column map needs context

diff --git a/share/html/Admin/Elements/EditScrips b/share/html/Admin/Elements/EditScrips
index 4db2987..025e7bc 100644
--- a/share/html/Admin/Elements/EditScrips
+++ b/share/html/Admin/Elements/EditScrips
@@ -102,6 +102,7 @@ if ( $id ) {
     $QueueObj->Load( $id );
     Abort(loc("Couldn't load queue #[_1]", $id)) unless $QueueObj->id;
 }
+$id ||= 0;
 
 my $find_scrips = sub {
     my %args = (Applied => 1, @_);
@@ -121,7 +122,7 @@ if ( $id ) {
 } else {
     $DisplayFormat = "__CheckBox.{RemoveScrip}__, $DisplayFormat";
 }
-$DisplayFormat .= ", __Move__";
+$DisplayFormat .= ", __Move.{$id}__";
 
 my %common_applied_args = (
     %ARGS,

commit 71fb52dba927978ae39f6efb6b1f545a9ea95dbf
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Dec 23 18:17:13 2011 +0400

    pass all arguments for tables with additional columns
    
    like Stage column in ObjectScrips table

diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
index 89d99ef..7e239ac 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -89,8 +89,8 @@ sub Create {
 
     unless ( defined $args{'SortOrder'} ) {
         $args{'SortOrder'} = $self->NextSortOrder(
+            %args,
             $tfield  => $target,
-            ObjectId => $args{'ObjectId'},
         );
     }
     $args{'Disabled'} ||= 0;
@@ -129,7 +129,7 @@ sub Apply {
     }
 
     return $self->Create(
-        $field => $tid, ObjectId => $oid,
+        %args, $field => $tid, ObjectId => $oid,
     );
 }
 

commit 263b8ea7ec374529fcaf8815d0b6a89b1612ddf0
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Dec 23 18:18:21 2011 +0400

    missing closing tag (</select>)

diff --git a/share/html/Admin/Elements/SelectStage b/share/html/Admin/Elements/SelectStage
index dc2746b..7bbbf3b 100644
--- a/share/html/Admin/Elements/SelectStage
+++ b/share/html/Admin/Elements/SelectStage
@@ -57,6 +57,7 @@
 </option>
 
 % }
+</select>
 <%INIT>
 if ( !defined $Default || $Default eq '') {
     $Default = 'TransactionCreate';

commit 6f89ea0c3fe0c14bb900c17511b7a1544e8be6a6
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Dec 23 18:19:04 2011 +0400

    pass Stage argument when ObjectScrip created

diff --git a/lib/RT/ObjectScrip.pm b/lib/RT/ObjectScrip.pm
index d7be981..c28f806 100644
--- a/lib/RT/ObjectScrip.pm
+++ b/lib/RT/ObjectScrip.pm
@@ -63,7 +63,7 @@ sub Create {
     my %args = (@_);
     return $self->SUPER::Create(
         map { $_ => $args{ $_ } } qw(
-            Scrip ObjectId
+            Scrip Stage ObjectId
             SortOrder Disabled
             Created Creator
             LastUpdated LastUpdatedBy
@@ -90,6 +90,7 @@ sub Neighbors {
     my %args = @_;
 
     my $res = $self->CollectionClass->new( $self->CurrentUser );
+    $res->Limit( FIELD => 'Stage', VALUE => $args{'Stage'} || $self->Stage );
     return $res;
 }
 

commit 1eb2d607d0c40a8e11e8ebb305120643b567d018
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Dec 23 18:19:42 2011 +0400

    process stage when we apply scrips to queues
    
    now stage is property of a link between queue and scrip

diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index d0a59e6..d149a4e 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -239,10 +239,10 @@ sub NotAddedTo {
 
 sub AddToObject {
     my $self = shift;
-    my $object = shift;
+    my %args = @_%2? (ObjectId => @_) : (@_);
 
     my $rec = RT::ObjectScrip->new( $self->CurrentUser );
-    return $rec->Apply( Scrip => $self, ObjectId => $object );
+    return $rec->Apply( %args, Scrip => $self );
 }
 
 sub RemoveFromObject {
diff --git a/share/html/Admin/Elements/EditScrips b/share/html/Admin/Elements/EditScrips
index 025e7bc..b8da77c 100644
--- a/share/html/Admin/Elements/EditScrips
+++ b/share/html/Admin/Elements/EditScrips
@@ -86,6 +86,10 @@
 <p><i><&|/l&>(No scrips)</&></i></p>
 % }
 
+<div style="text-align:right">
+<% loc('Select stage for scrips you add') %>: <& SelectStage &>
+</div>
+
 <& /Elements/Submit,
     Name => 'AddScrips',
     Caption => loc("Apply selected scrips"),
@@ -151,7 +155,7 @@ if ( $AddScrips ) {
         $scrip->Load( $sid );
         next unless $scrip->id;
 
-        my ($status, $msg) = $scrip->AddToObject( $id );
+        my ($status, $msg) = $scrip->AddToObject( $id, Stage => $Stage );
         push @actions, $msg;
     }
 }
@@ -168,5 +172,6 @@ $RemoveScrips => undef
 
 @AddScrip => ()
 $AddScrips => undef
+$Stage     => 'TransactionCreate'
 
 </%ARGS>

commit 81fbf5b747acf45f26d26ee586d032e46abe372f
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Dec 23 19:05:08 2011 +0400

    move scrips up and down

diff --git a/share/html/Admin/Elements/EditScrips b/share/html/Admin/Elements/EditScrips
index b8da77c..beaa3c2 100644
--- a/share/html/Admin/Elements/EditScrips
+++ b/share/html/Admin/Elements/EditScrips
@@ -160,6 +160,24 @@ if ( $AddScrips ) {
     }
 }
 
+if ( $MoveScripUp ) {
+    my $scrip = RT::ObjectScrip->new( $session{'CurrentUser'} );
+    $scrip->LoadByCols( Scrip => $MoveScripUp, ObjectId => $id );
+    if ( $scrip->id ) {
+        my ($status, $msg) = $scrip->MoveUp;
+        push @actions, $msg;
+    }
+}
+
+if ( $MoveScripDown ) {
+    my $scrip = RT::ObjectScrip->new( $session{'CurrentUser'} );
+    $scrip->LoadByCols( Scrip => $MoveScripDown, ObjectId => $id );
+    if ( $scrip->id ) {
+        my ($status, $msg) = $scrip->MoveDown;
+        push @actions, $msg;
+    }
+}
+
 </%init>
 
 <%ARGS>
@@ -174,4 +192,7 @@ $RemoveScrips => undef
 $AddScrips => undef
 $Stage     => 'TransactionCreate'
 
+$MoveScripUp => undef
+$MoveScripDown => undef
+
 </%ARGS>
diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index 84e70f2..d093f98 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -140,7 +140,7 @@ my $COLUMN_MAP = {
             my $context = $_[2] || 0;
             return '' unless $_[0]->IsAdded( $context );
 
-            my $name = 'MoveCustomField';
+            my $name = 'MoveScrip';
             my $args = $m->caller_args( 1 );
             my @pass = ref $args->{'PassArguments'}
                 ? @{$args->{'PassArguments'}}

commit 374338db6f5a83db95724c7152bbcceacca42a35
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Dec 24 03:29:26 2011 +0400

    delete all applications when scrip is deleted

diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index d149a4e..9a1b717 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -215,6 +215,8 @@ sub Delete {
         return ( 0, $self->loc('Permission Denied') );
     }
 
+    RT::ObjectScrip->new( $self->CurrentUser )->DeleteAll( Scrip => $self );
+
     return ( $self->SUPER::Delete(@_) );
 }
 

commit 12ba04826bda3e0e713795b06998ca3b17d81488
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Dec 27 14:57:39 2011 +0400

    an unique index on ObjectScrips table

diff --git a/etc/schema.Oracle b/etc/schema.Oracle
index 33787a9..039a646 100755
--- a/etc/schema.Oracle
+++ b/etc/schema.Oracle
@@ -162,7 +162,7 @@ CREATE TABLE ObjectScrips (
 	LastUpdatedBy	NUMBER(11,0) DEFAULT 0 NOT NULL,
 	LastUpdated	DATE
 );
-
+CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
 
 CREATE SEQUENCE ACL_seq;
 CREATE TABLE ACL (
diff --git a/etc/schema.Pg b/etc/schema.Pg
index 22730e4..b4742fc 100755
--- a/etc/schema.Pg
+++ b/etc/schema.Pg
@@ -258,6 +258,7 @@ CREATE TABLE ObjectScrips (
 
 );
 
+CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
 
 
 
diff --git a/etc/schema.SQLite b/etc/schema.SQLite
index 25b1a48..8a912ed 100755
--- a/etc/schema.SQLite
+++ b/etc/schema.SQLite
@@ -174,6 +174,7 @@ CREATE TABLE ObjectScrips (
   LastUpdated DATETIME NULL  ,
   PRIMARY KEY (id)
 );
+CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
 
 --- {{{ ACL
 CREATE TABLE ACL (
diff --git a/etc/schema.mysql b/etc/schema.mysql
index d9e9b73..5e72bf9 100755
--- a/etc/schema.mysql
+++ b/etc/schema.mysql
@@ -165,6 +165,7 @@ CREATE TABLE ObjectScrips (
   PRIMARY KEY (id)
 ) ENGINE=InnoDB CHARACTER SET utf8;
 
+CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
 
 CREATE TABLE ACL (
   id INTEGER NOT NULL  AUTO_INCREMENT,

commit c791e8faf0268a366b520a679df620bb5a240a08
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Dec 27 15:25:59 2011 +0400

    show stage selector only when we create a scrip

diff --git a/share/html/Admin/Elements/EditScrip b/share/html/Admin/Elements/EditScrip
index bd73b83..227cec2 100644
--- a/share/html/Admin/Elements/EditScrip
+++ b/share/html/Admin/Elements/EditScrip
@@ -79,11 +79,13 @@
     Queue => $Queue,
 &></td></tr>
 
+% if ( $id eq 'new' ) {
 <tr><td class="label"><&|/l&>Stage</&>:</td><td class="value">\
 <& /Admin/Elements/SelectStage,
     Name => "Scrip-$id-Stage",
-    Default => $ARGS{"Scrip-$id-Stage"} || $scrip->Stage,
+    Default => $ARGS{"Scrip-$id-Stage"},
 &></td></tr>
+% }
 
 </table>
 </&>

commit 9e059b0d1a1947fa2e37c3c56ce873600c2ec340
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Dec 28 00:05:07 2011 +0400

    scrips disabling in the UI and API

diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index 9a1b717..86958a0 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -348,6 +348,36 @@ sub Stage {
     return undef;
 }
 
+=head2 Disabled
+
+Returns the current value of Disabled. 
+
+=cut
+
+sub Disabled {
+    my $self = shift;
+    # we check only one record as at this moment all applications
+    # should be disabled or none, this method should be dropped when
+    # we implement full functionality
+    my $record = RT::ObjectScrip->new( $self->CurrentUser );
+    $record->LoadByCols( Scrip => $self->id );
+    return $record->Disabled;
+}
+
+=head2 SetDisabled
+
+Takes a boolean.
+1 will cause this scrip to no longer be avaialble for objects.
+0 will re-enable this field.
+
+=cut
+
+sub SetDisabled {
+    my $self = shift;
+    return RT::ObjectScrip->new( $self->CurrentUser )
+        ->SetDisabledOnAll( Scrip => $self->id, Value => shift );
+}
+
 
 =head2 Apply { TicketObj => undef, TransactionObj => undef}
 
diff --git a/share/html/Admin/Elements/EditScrip b/share/html/Admin/Elements/EditScrip
index 227cec2..9cd3cb4 100644
--- a/share/html/Admin/Elements/EditScrip
+++ b/share/html/Admin/Elements/EditScrip
@@ -87,6 +87,31 @@
 &></td></tr>
 % }
 
+% if ( $id ne 'new' && $added_to_any ) {
+<tr><td class="label"><&|/l&>Added</&>:</td><td class="value">\
+% if ( $scrip->IsAdded(0) ) {
+<% loc('Global') %>
+% } else {
+% my $added_to = $scrip->AddedTo;
+% my $found = 0;
+% while ( my $queue = $added_to->Next ) {
+% $m->out(', ') if $found++;
+<% $queue->Name %>
+% last if $found == 10;
+% }
+% $m->out(', ...') if $found == 10;
+% }
+<td></tr>
+% }
+
+% if ( $id eq 'new' || $added_to_any  ) {
+<tr><td class="label"> </td><td>
+<input type="hidden" class="hidden" name="SetEnabled" value="1" />
+<input type="checkbox" class="checkbox" name="Scrip-<% $id %>-Enabled" value="1" <% $EnabledChecked |n%> />
+<label for="Scrip-<% $id %>-Enabled"><&|/l&>Enabled (Unchecking this box disables this scrip)</&></label>
+</td></tr>
+% }
+
 </table>
 </&>
 
@@ -147,10 +172,18 @@ if ( $id ) {
     $SubmitLabel = loc('Save Changes');
 }
 
+my $EnabledChecked = qq[checked="checked"];
+my $added_to_any = 0;
+
 unless ( $id ) {
     $id = 'new';
     $SubmitLabel = loc('Create');
 }
+else {
+    my $disabled = $scrip->Disabled;
+    $added_to_any = 1 if defined $disabled;
+    $EnabledChecked = '' if $disabled;
+}
 
 my $min_lines = 10;
 
@@ -185,6 +218,7 @@ if ( $id eq 'new' ) {
         CustomCommitCode       => $ARGS{"Scrip-new-CustomCommitCode"},
         CustomIsApplicableCode => $ARGS{"Scrip-new-CustomIsApplicableCode"},
         Stage                  => $ARGS{"Scrip-new-Stage"},
+        Disabled               => $ARGS{"Scrip-new-Enabled"}? 0 : 1,
     );
 }
 else {
@@ -192,8 +226,11 @@ else {
     return (undef, loc("Couldn't load scrip #[_1]", $id))
         unless $scrip->id;
 
+    $ARGS{"Scrip-$id-Disabled"} = $ARGS{"Scrip-$id-Enabled"}? 0 : 1
+        if $ARGS{"SetEnabled"};
+
     my @attribs = qw(ScripAction ScripCondition Template Stage
-        Description CustomPrepareCode CustomCommitCode CustomIsApplicableCode);
+        Description CustomPrepareCode CustomCommitCode CustomIsApplicableCode Disabled);
     my @results = UpdateRecordObject(
         AttributesRef   => \@attribs,
         AttributePrefix => 'Scrip-'.$scrip->Id,

commit 8bc1f694158fe8dd05346b2cc3c1bb5dbfaf398a
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Dec 28 00:06:25 2011 +0400

    Stage in the UI only available during creation

diff --git a/t/ticket/scrips_batch.t b/t/ticket/scrips_batch.t
index 44d7f8e..a6d22b5 100644
--- a/t/ticket/scrips_batch.t
+++ b/t/ticket/scrips_batch.t
@@ -37,7 +37,12 @@ my $sid;
     is value_name($form, "Scrip-$sid-ScripCondition"), 'On Transaction', 'correct condition';
     is value_name($form, "Scrip-$sid-ScripAction"), 'User Defined', 'correct action';
     is value_name($form, "Scrip-$sid-Template"), 'Global template: Blank', 'correct template';
-    is value_name($form, "Scrip-$sid-Stage"), 'TransactionBatch', 'correct stage';
+
+    {
+        my $rec = RT::ObjectScrip->new( RT->SystemUser );
+        $rec->LoadByCols( Scrip => $sid, ObjectId => $queue->id );
+        is $rec->Stage, 'TransactionBatch', "correct stage";
+    }
 
     my $tmp_fn = File::Spec->catfile( RT::Test->temp_directory, 'transactions' );
     open my $tmp_fh, '+>', $tmp_fn or die $!;

commit cb0a32247a0ba2671b7318b0274b95801bc4906a
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Dec 28 02:03:42 2011 +0400

    new default order of script is different
    
    fix tests by re-ordering them

diff --git a/t/approval/admincc.t b/t/approval/admincc.t
index 3062ce5..55c2c9f 100644
--- a/t/approval/admincc.t
+++ b/t/approval/admincc.t
@@ -84,7 +84,12 @@ mail_ok {
         Requestor => 'minion',
         Queue     => $q->Id,
     );
-} { from => qr/RT System/,
+} { from => qr/PO via RT/,
+    to => 'minion at company.com',
+    subject => qr/PO for stationary/,
+    body => qr/automatically generated in response/
+},
+{ from => qr/RT System/,
     bcc => qr/ceo.*coo|coo.*ceo/i,
     subject => qr/PO for stationary/i,
 },
@@ -92,12 +97,8 @@ mail_ok {
     to => 'cto at company.com',
     subject => qr/New Pending Approval: CTO Approval/,
     body => qr/pending your approval.*Your approval is requested.*Blah/s
-},
-{ from => qr/PO via RT/,
-    to => 'minion at company.com',
-    subject => qr/PO for stationary/,
-    body => qr/automatically generated in response/
-};
+}
+;
 
 ok ($tid,$tmsg);
 
diff --git a/t/approval/basic.t b/t/approval/basic.t
index 92ffc42..931556f 100644
--- a/t/approval/basic.t
+++ b/t/approval/basic.t
@@ -86,14 +86,14 @@ mail_ok {
         $t->Create(Subject => "PO for stationary",
                    Owner => "root", Requestor => 'minion',
                    Queue => $q->Id);
-} { from => qr/RT System/,
-    to => 'cfo at company.com',
-    subject => qr/New Pending Approval: CFO Approval/,
-    body => qr/pending your approval.*Your approval is requested.*Blah/s
-},{ from => qr/PO via RT/,
+} { from => qr/PO via RT/,
     to => 'minion at company.com',
     subject => qr/PO for stationary/,
     body => qr/automatically generated in response/
+}, { from => qr/RT System/,
+    to => 'cfo at company.com',
+    subject => qr/New Pending Approval: CFO Approval/,
+    body => qr/pending your approval.*Your approval is requested.*Blah/s
 };
 
 ok ($tid,$tmsg);

commit fe844be720b24200b2d0ed155a71872fdebf073e
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Dec 30 01:30:00 2011 +0400

    update tests, scrip order no longer description based

diff --git a/t/api/scrip_order.t b/t/api/scrip_order.t
index 6fba7e5..d6b0ecc 100644
--- a/t/api/scrip_order.t
+++ b/t/api/scrip_order.t
@@ -3,51 +3,73 @@ use strict;
 use warnings;
 
 use RT;
-use RT::Test tests => 7;
-
-
-
-my $scrip_queue = RT::Queue->new(RT->SystemUser);
-my ($queue_id, $msg) = $scrip_queue->Create( Name => "ScripOrdering-$$", 
-    Description => 'Test scrip ordering by description' );
-ok($queue_id, "Created scrip-ordering test queue? ".$msg);
-
-my $priority_ten_scrip = RT::Scrip->new(RT->SystemUser);
-(my $id, $msg) = $priority_ten_scrip->Create( 
-    Description => "10 set priority $$",
-    Queue => $queue_id, 
-    ScripCondition => 'On Create',
-    ScripAction => 'User Defined', 
-    CustomPrepareCode => '$RT::Logger->debug("Setting priority to 10..."); return 1;',
-    CustomCommitCode => '$self->TicketObj->SetPriority(10);',
-    Template => 'Blank',
-    Stage => 'TransactionCreate',
-);
-ok($id, "Created priority-10 scrip? ".$msg);
-
-my $priority_five_scrip = RT::Scrip->new(RT->SystemUser);
-($id, $msg) = $priority_ten_scrip->Create( 
-    Description => "05 set priority $$",
-    Queue => $queue_id, 
-    ScripCondition => 'On Create',
-    ScripAction => 'User Defined', 
-    CustomPrepareCode => '$RT::Logger->debug("Setting priority to 5..."); return 1;',
-    CustomCommitCode => '$self->TicketObj->SetPriority(5);', 
-    Template => 'Blank',
-    Stage => 'TransactionCreate',
-);
-ok($id, "Created priority-5 scrip? ".$msg);
-
-my $ticket = RT::Ticket->new(RT->SystemUser);
-($id, $msg) = $ticket->Create( 
-    Queue => $queue_id, 
-    Requestor => 'order at example.com',
-    Subject => "Scrip order test $$",
-);
-ok($ticket->id, "Created ticket? id=$id");
-
-isnt($ticket->Priority , 0, "Ticket shouldn't be priority 0");
-isnt($ticket->Priority , 5, "Ticket shouldn't be priority 5");
-is  ($ticket->Priority , 10, "Ticket should be priority 10");
+use RT::Test tests => 9;
+
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+ok $queue && $queue->id, 'loaded or created queue';
+
+{
+    my $ten = main->create_scrip_ok(
+        Description => "Set priority to 10",
+        Queue => $queue->id, 
+        CustomCommitCode => '$self->TicketObj->SetPriority(10);',
+    );
+
+    my $five = main->create_scrip_ok(
+        Description => "Set priority to 5",
+        Queue => $queue->id,
+        CustomCommitCode => '$self->TicketObj->SetPriority(5);', 
+    );
+
+    my $ticket = RT::Ticket->new(RT->SystemUser);
+    my ($id, $msg) = $ticket->Create( 
+        Queue => $queue->id, 
+        Subject => "Scrip order test $$",
+    );
+    ok($ticket->id, "Created ticket? id=$id");
+    is($ticket->Priority , 5, "By default newer scrip is last");
+
+    main->move_scrip_ok( $five, $queue->id, 'up' );
+
+    $ticket = RT::Ticket->new(RT->SystemUser);
+    ($id, $msg) = $ticket->Create(
+        Queue => $queue->id,
+        Subject => "Scrip order test $$",
+    );
+    ok($ticket->id, "Created ticket? id=$id");
+    is($ticket->Priority , 10, "Moved scrip and result is different");
+}
+
+sub create_scrip_ok {
+    my $self = shift;
+    my %args = (
+        ScripCondition => 'On Create',
+        ScripAction => 'User Defined', 
+        CustomPrepareCode => 'return 1',
+        CustomCommitCode => 'return 1', 
+        Template => 'Blank',
+        Stage => 'TransactionCreate',
+        @_
+    );
+
+    my $scrip = RT::Scrip->new( RT->SystemUser );
+    my ($id, $msg) = $scrip->Create( %args );
+    ok($id, "Created scrip") or diag "error: $msg";
+
+    return $scrip;
+}
+
+sub move_scrip_ok {
+    my $self = shift;
+    my ($scrip, $queue, $dir) = @_;
+
+    my $rec = RT::ObjectScrip->new( RT->SystemUser );
+    $rec->LoadByCols( Scrip => $scrip->id, ObjectId => $queue );
+    ok $rec->id, 'found application of the scrip';
+
+    my $method = 'Move'. ucfirst lc $dir;
+    my ($status, $msg) = $rec->$method();
+    ok $status, "moved scrip $dir" or diag "error: $msg";
+}
 
 

commit 6b40695a98abdd588edd86202d3ae7efe65b9298
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jan 4 15:29:50 2012 +0400

    more tests for scrips ordering

diff --git a/t/api/scrip_order.t b/t/api/scrip_order.t
index d6b0ecc..cf3c79d 100644
--- a/t/api/scrip_order.t
+++ b/t/api/scrip_order.t
@@ -3,11 +3,12 @@ use strict;
 use warnings;
 
 use RT;
-use RT::Test tests => 9;
+use RT::Test tests => 110;
 
 my $queue = RT::Test->load_or_create_queue( Name => 'General' );
 ok $queue && $queue->id, 'loaded or created queue';
 
+note "check that execution order reflects sort order";
 {
     my $ten = main->create_scrip_ok(
         Description => "Set priority to 10",
@@ -40,6 +41,83 @@ ok $queue && $queue->id, 'loaded or created queue';
     is($ticket->Priority , 10, "Moved scrip and result is different");
 }
 
+my $queue_B = RT::Test->load_or_create_queue( Name => 'Other' );
+ok $queue_B && $queue_B->id, 'loaded or created queue';
+
+note "move around two local scrips";
+{
+    main->delete_all_scrips();
+
+    my @scrips;
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    main->check_scrips_order(\@scrips, [$queue]);
+
+    main->move_scrip_ok( $scrips[0], $queue->id, 'down' );
+    @scrips = @scrips[1, 0];
+    main->check_scrips_order(\@scrips, [$queue]);
+
+    main->move_scrip_ok( $scrips[0], $queue->id, 'down' );
+    @scrips = @scrips[1, 0];
+    main->check_scrips_order(\@scrips, [$queue]);
+
+    main->move_scrip_ok( $scrips[1], $queue->id, 'up' );
+    @scrips = @scrips[1, 0];
+    main->check_scrips_order(\@scrips, [$queue]);
+
+    main->move_scrip_ok( $scrips[1], $queue->id, 'up' );
+    @scrips = @scrips[1, 0];
+    main->check_scrips_order(\@scrips, [$queue]);
+}
+
+note "move local scrip below global";
+{
+    main->delete_all_scrips();
+    my @scrips;
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+    push @scrips, main->create_scrip_ok( Queue => 0 );
+    push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+
+    main->move_scrip_ok( $scrips[0], $queue->id, 'down' );
+    @scrips = @scrips[1, 2, 0, 3];
+    main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+}
+
+note "move global scrip";
+{
+    main->delete_all_scrips();
+    my @scrips;
+    push @scrips, main->create_scrip_ok( Queue => 0 );
+    push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    push @scrips, main->create_scrip_ok( Queue => 0 );
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+
+    main->move_scrip_ok( $scrips[0], 0, 'down' );
+    @scrips = @scrips[1, 2, 3, 0, 4];
+    main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+}
+
+note "delete scrips one by one";
+{
+    main->delete_all_scrips();
+    my @scrips;
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+    push @scrips, main->create_scrip_ok( Queue => 0 );
+    push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    push @scrips, main->create_scrip_ok( Queue => 0 );
+
+    main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+
+    foreach my $idx (3, 2, 0 ) {
+        $_->Delete foreach splice @scrips, $idx, 1;
+        main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+    }
+}
+
 sub create_scrip_ok {
     my $self = shift;
     my %args = (
@@ -59,6 +137,13 @@ sub create_scrip_ok {
     return $scrip;
 }
 
+sub delete_all_scrips {
+    my $self = shift;
+    my $scrips = RT::Scrips->new( RT->SystemUser );
+    $scrips->UnLimit;
+    $_->Delete foreach @{ $scrips->ItemsArrayRef };
+}
+
 sub move_scrip_ok {
     my $self = shift;
     my ($scrip, $queue, $dir) = @_;
@@ -72,4 +157,46 @@ sub move_scrip_ok {
     ok $status, "moved scrip $dir" or diag "error: $msg";
 }
 
+sub check_scrips_order {
+    my $self = shift;
+    my $scrips = shift;
+    my $queues = shift;
+
+    foreach my $qid ( 0, map $_->id, @$queues ) {
+        my $list = RT::Scrips->new( RT->SystemUser );
+        $list->LimitToGlobal;
+        $list->LimitToQueue( $qid ) if $qid;
+        $list->ApplySortOrder;
+        is_deeply(
+            [map $_->id, @{ $list->ItemsArrayRef } ],
+            [map $_->id, grep $_->IsAdded( $qid ) || $_->IsAdded( 0 ), @$scrips],
+            'list of scrips match expected'
+        )
+    }
+
+    foreach my $qid ( map $_->id, @$queues ) {
+        my $list = RT::ObjectScrips->new( RT->SystemUser );
+        $list->LimitToObjectId( 0 );
+        $list->LimitToObjectId( $qid );
+
+        my %so;
+        $so{ $_->SortOrder }++ foreach @{ $list->ItemsArrayRef };
+        ok( !grep( {$_ != 1} values %so), 'no dublicate order' );
+    }
+    {
+        my $list = RT::ObjectScrips->new( RT->SystemUser );
+        $list->UnLimit;
+        $list->OrderBy( FIELD => 'SortOrder', ORDER => 'ASC' );
+
+        my $prev;
+        foreach my $rec ( @{ $list->ItemsArrayRef } ) {
+            my $so = $rec->SortOrder;
+            do { $prev = $so; next } unless defined $prev;
+
+            ok $so == $prev || $so == $prev+1, "sequential order";
+            $prev = $so;
+        }
+    }
+}
+
 

commit ed29d328d1abd29c7c90a6dcb89dfff516d510e3
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jan 4 15:31:44 2012 +0400

    fix NextSortOrder result for global records
    
    when global records are added we were checking only
    other global records, but there can be a local record
    with bigger sort order number than the last applied
    global. Fix it by checking every neighbor for a new
    global application.
    
    It's ok to ignore neighbors for local applications
    as those can share sort order.

diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
index 7e239ac..a5e3559 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -401,9 +401,21 @@ sub MoveDown {
 
 sub NextSortOrder {
     my $self = shift;
-    my $siblings = $self->Siblings( @_ );
-    $siblings->OrderBy( FIELD => 'SortOrder', ORDER => 'DESC' );
-    return 0 unless my $first = $siblings->First;
+    my %args = (@_);
+
+    my $oid = $args{'ObjectId'};
+    $oid = $self->ObjectId unless defined $oid;
+    $oid ||= 0;
+
+    my $neighbors = $self->Neighbors( %args );
+    if ( $oid ) {
+        $neighbors->LimitToObjectId( $oid );
+        $neighbors->LimitToObjectId( 0 );
+    } elsif ( !$neighbors->_isLimited ) {
+        $neighbors->UnLimit;
+    }
+    $neighbors->OrderBy( FIELD => 'SortOrder', ORDER => 'DESC' );
+    return 0 unless my $first = $neighbors->First;
     return $first->SortOrder + 1;
 }
 

commit 934acbf8c4a2f8bfbc27496e33b6b4f840128112
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jan 4 15:41:16 2012 +0400

    don't always move applications when we delete
    
    when we delete a record that shares sort order
    with other record(s), we shouldn't move anything

diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
index a5e3559..9c0a2f3 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -229,12 +229,18 @@ sub Delete {
     my $self = shift;
 
     my $siblings = $self->Neighbors;
-    $siblings->LimitToObjectId( $self->ObjectId );
-    $siblings->Limit( FIELD => 'SortOrder', OPERATOR => '>', VALUE => $self->SortOrder );
+    $siblings->Limit( FIELD => 'SortOrder', OPERATOR => '>=', VALUE => $self->SortOrder );
+    $siblings->OrderBy( FIELD => 'SortOrder', ORDER => 'ASC' );
 
-    # Move everything below us up
     my $sort_order = $self->SortOrder;
     while (my $record = $siblings->Next) {
+        next if $record->id == $self->id;
+        return $self->SUPER::Delete if $self->SortOrder == $record->SortOrder;
+        last;
+    }
+
+    # Move everything below us up
+    foreach my $record ( @{ $siblings->ItemsArrayRef } ) {
         $record->SetSortOrder($record->SortOrder - 1);
     }
 

commit f29264c37843594ce96e54063ee698a2fe424770
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jan 4 23:27:47 2012 +0400

    more tests around scrips' order

diff --git a/t/api/scrip_order.t b/t/api/scrip_order.t
index cf3c79d..cf5a827 100644
--- a/t/api/scrip_order.t
+++ b/t/api/scrip_order.t
@@ -3,7 +3,7 @@ use strict;
 use warnings;
 
 use RT;
-use RT::Test tests => 110;
+use RT::Test tests => 204;
 
 my $queue = RT::Test->load_or_create_queue( Name => 'General' );
 ok $queue && $queue->id, 'loaded or created queue';
@@ -70,6 +70,32 @@ note "move around two local scrips";
     main->check_scrips_order(\@scrips, [$queue]);
 }
 
+note "move around two global scrips";
+{
+    main->delete_all_scrips();
+
+    my @scrips;
+    push @scrips, main->create_scrip_ok( Queue => 0 );
+    push @scrips, main->create_scrip_ok( Queue => 0 );
+    main->check_scrips_order(\@scrips, [$queue]);
+
+    main->move_scrip_ok( $scrips[0], 0, 'down' );
+    @scrips = @scrips[1, 0];
+    main->check_scrips_order(\@scrips, [$queue]);
+
+    main->move_scrip_ok( $scrips[0], 0, 'down' );
+    @scrips = @scrips[1, 0];
+    main->check_scrips_order(\@scrips, [$queue]);
+
+    main->move_scrip_ok( $scrips[1], 0, 'up' );
+    @scrips = @scrips[1, 0];
+    main->check_scrips_order(\@scrips, [$queue]);
+
+    main->move_scrip_ok( $scrips[1], 0, 'up' );
+    @scrips = @scrips[1, 0];
+    main->check_scrips_order(\@scrips, [$queue]);
+}
+
 note "move local scrip below global";
 {
     main->delete_all_scrips();
@@ -77,14 +103,30 @@ note "move local scrip below global";
     push @scrips, main->create_scrip_ok( Queue => $queue->id );
     push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
     push @scrips, main->create_scrip_ok( Queue => 0 );
-    push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    main->check_scrips_order(\@scrips, [$queue, $queue_B]);
 
     main->move_scrip_ok( $scrips[0], $queue->id, 'down' );
     @scrips = @scrips[1, 2, 0, 3];
     main->check_scrips_order(\@scrips, [$queue, $queue_B]);
 }
 
-note "move global scrip";
+note "move local scrip above global";
+{
+    main->delete_all_scrips();
+    my @scrips;
+    push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+    push @scrips, main->create_scrip_ok( Queue => 0 );
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+    main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+
+    main->move_scrip_ok( $scrips[-1], $queue_B->id, 'up' );
+    @scrips = @scrips[0, 3, 1, 2];
+    main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+}
+
+note "move global scrip down with local in between";
 {
     main->delete_all_scrips();
     my @scrips;
@@ -93,12 +135,29 @@ note "move global scrip";
     push @scrips, main->create_scrip_ok( Queue => $queue->id );
     push @scrips, main->create_scrip_ok( Queue => 0 );
     push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    main->check_scrips_order(\@scrips, [$queue, $queue_B]);
 
     main->move_scrip_ok( $scrips[0], 0, 'down' );
     @scrips = @scrips[1, 2, 3, 0, 4];
     main->check_scrips_order(\@scrips, [$queue, $queue_B]);
 }
 
+note "move global scrip up with local in between";
+{
+    main->delete_all_scrips();
+    my @scrips;
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    push @scrips, main->create_scrip_ok( Queue => 0 );
+    push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
+    push @scrips, main->create_scrip_ok( Queue => $queue->id );
+    push @scrips, main->create_scrip_ok( Queue => 0 );
+    main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+
+    main->move_scrip_ok( $scrips[-1], 0, 'up' );
+    @scrips = @scrips[0, 4, 1, 2, 3];
+    main->check_scrips_order(\@scrips, [$queue, $queue_B]);
+}
+
 note "delete scrips one by one";
 {
     main->delete_all_scrips();
@@ -109,7 +168,6 @@ note "delete scrips one by one";
     push @scrips, main->create_scrip_ok( Queue => $queue_B->id );
     push @scrips, main->create_scrip_ok( Queue => $queue->id );
     push @scrips, main->create_scrip_ok( Queue => 0 );
-
     main->check_scrips_order(\@scrips, [$queue, $queue_B]);
 
     foreach my $idx (3, 2, 0 ) {
@@ -119,6 +177,7 @@ note "delete scrips one by one";
 }
 
 sub create_scrip_ok {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
     my $self = shift;
     my %args = (
         ScripCondition => 'On Create',
@@ -138,6 +197,7 @@ sub create_scrip_ok {
 }
 
 sub delete_all_scrips {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
     my $self = shift;
     my $scrips = RT::Scrips->new( RT->SystemUser );
     $scrips->UnLimit;
@@ -145,6 +205,7 @@ sub delete_all_scrips {
 }
 
 sub move_scrip_ok {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
     my $self = shift;
     my ($scrip, $queue, $dir) = @_;
 
@@ -158,6 +219,7 @@ sub move_scrip_ok {
 }
 
 sub check_scrips_order {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
     my $self = shift;
     my $scrips = shift;
     my $queues = shift;
@@ -199,4 +261,13 @@ sub check_scrips_order {
     }
 }
 
+sub dump_sort_order {
+
+    diag " id oid so";
+    diag join "\n", map { join "\t", @$_ } map @$_, $RT::Handle->dbh->selectall_arrayref(
+        "select Scrip, ObjectId, SortOrder from ObjectScrips ORDER BY SortOrder"
+    );
+
+}
+
 

commit 8e0cba60a06dc90a09ac087ce1f0059d1bbdb27d
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jan 4 23:28:40 2012 +0400

    generalize, fix and comment Move{Up,Down} methods
    
    Split moving into three primary cases:
    1) global moving
    2) local with local
    3) local with global
    
    First case was broken and other had corner cases.
    Comment every branch of code. Use one method for
    moving up and down.

diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
index 9c0a2f3..9383187 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -305,52 +305,7 @@ Moves scrip up. See </Sorting scrips applications>.
 
 =cut
 
-sub MoveUp {
-    my $self = shift;
-
-    my $siblings = $self->Siblings;
-    $siblings->Limit( FIELD => 'SortOrder', OPERATOR => '<', VALUE => $self->SortOrder );
-    $siblings->OrderByCols( { FIELD => 'SortOrder', ORDER => 'DESC' } );
-
-    my @above = ($siblings->Next, $siblings->Next);
-    unless ($above[0]) {
-        return (0, "Can not move up. It's already at the top");
-    }
-
-    my $new_sort_order;
-    if ( $above[0]->ObjectId == $self->ObjectId ) {
-        $new_sort_order = $above[0]->SortOrder;
-        my ($status, $msg) = $above[0]->SetSortOrder( $self->SortOrder );
-        unless ( $status ) {
-            return (0, "Couldn't move scrip");
-        }
-    }
-    elsif ( $above[1] && $above[0]->SortOrder == $above[1]->SortOrder + 1 ) {
-        my $move_siblings = $self->Neighbors;
-        $move_siblings->Limit(
-            FIELD => 'SortOrder',
-            OPERATOR => '>=',
-            VALUE => $above[0]->SortOrder,
-        );
-        $move_siblings->OrderByCols( { FIELD => 'SortOrder', ORDER => 'DESC' } );
-        while ( my $record = $move_siblings->Next ) {
-            my ($status, $msg) = $record->SetSortOrder( $record->SortOrder + 1 );
-            unless ( $status ) {
-                return (0, "Couldn't move scrip");
-            }
-        }
-        $new_sort_order = $above[0]->SortOrder;
-    } else {
-        $new_sort_order = $above[0]->SortOrder - 1;
-    }
-
-    my ($status, $msg) = $self->SetSortOrder( $new_sort_order );
-    unless ( $status ) {
-        return (0, "Couldn't move scrip");
-    }
-
-    return (1,"Moved scrip up");
-}
+sub MoveUp { return shift->Move( Up => @_ ) }
 
 =head3 MoveDown
 
@@ -358,51 +313,98 @@ Moves scrip down. See </Sorting scrips applications>.
 
 =cut
 
-sub MoveDown {
+sub MoveDown { return shift->Move( Down => @_ ) }
+
+sub Move {
     my $self = shift;
+    my $dir = lc(shift || 'up');
+
+    my %meta;
+    if ( $dir eq 'down' ) {
+        %meta = qw(
+            next_op    >
+            next_order ASC
+            prev_op    <=
+            diff       +1
+        );
+    } else {
+        %meta = qw(
+            next_op    <
+            next_order DESC
+            prev_op    >=
+            diff       -1
+        );
+    }
 
     my $siblings = $self->Siblings;
-    $siblings->Limit( FIELD => 'SortOrder', OPERATOR => '>', VALUE => $self->SortOrder );
-    $siblings->OrderByCols( { FIELD => 'SortOrder', ORDER => 'ASC' } );
-
-    my @below = ($siblings->Next, $siblings->Next);
-    unless ($below[0]) {
-        return (0, "Can not move down. It's already at the bottom");
+    $siblings->Limit( FIELD => 'SortOrder', OPERATOR => $meta{'next_op'}, VALUE => $self->SortOrder );
+    $siblings->OrderBy( FIELD => 'SortOrder', ORDER => $meta{'next_order'} );
+
+    my @next = ($siblings->Next, $siblings->Next);
+    unless ($next[0]) {
+        return $dir eq 'down'
+            ? (0, "Can not move down. It's already at the bottom")
+            : (0, "Can not move up. It's already at the top")
+        ;
     }
 
-    my $new_sort_order;
-    if ( $below[0]->ObjectId == $self->ObjectId ) {
-        $new_sort_order = $below[0]->SortOrder;
-        my ($status, $msg) = $below[0]->SetSortOrder( $self->SortOrder );
-        unless ( $status ) {
-            return (0, "Couldn't move scrip");
+    my ($new_sort_order, $move);
+
+    unless ( $self->ObjectId ) {
+        # moving global, it can not share sort order, so just move it
+        # on place of next global and move everything in between one number
+
+        $new_sort_order = $next[0]->SortOrder;
+        $move = $self->Neighbors;
+        $move->Limit(
+            FIELD => 'SortOrder', OPERATOR => $meta{'next_op'}, VALUE => $self->SortOrder,
+        );
+        $move->Limit(
+            FIELD => 'SortOrder', OPERATOR => $meta{'prev_op'}, VALUE => $next[0]->SortOrder,
+            ENTRYAGGREGATOR => 'AND',
+        );
+    }
+    elsif ( $next[0]->ObjectId == $self->ObjectId ) {
+        # moving two locals, just swap them, they should follow 'so = so+/-1' rule
+        $new_sort_order = $next[0]->SortOrder;
+        $move = $next[0];
+    }
+    else {
+        # moving local behind global
+        unless ( $self->IsSortOrderShared ) {
+            # not shared SO allows us to swap
+            $new_sort_order = $next[0]->SortOrder;
+            $move = $next[0];
+        }
+        elsif ( $next[1] ) {
+            # more records there and shared SO, we have to move everything
+            $new_sort_order = $next[0]->SortOrder;
+            $move = $self->Neighbors;
+            $move->Limit(
+                FIELD => 'SortOrder', OPERATOR => $meta{prev_op}, VALUE => $next[0]->SortOrder,
+            );
+        }
+        else {
+            # shared SO and place after is free, so just jump
+            $new_sort_order = $next[0]->SortOrder + $meta{'diff'};
         }
     }
-    elsif ( $below[1] && $below[0]->SortOrder + 1 == $below[1]->SortOrder ) {
-        my $move_siblings = $self->Neighbors;
-        $move_siblings->Limit(
-            FIELD => 'SortOrder',
-            OPERATOR => '<=',
-            VALUE => $below[0]->SortOrder,
-        );
-        $move_siblings->OrderByCols( { FIELD => 'SortOrder', ORDER => 'ASC' } );
-        while ( my $record = $move_siblings->Next ) {
-            my ($status, $msg) = $record->SetSortOrder( $record->SortOrder - 1 );
-            unless ( $status ) {
-                return (0, "Couldn't move scrip");
-            }
+
+    if ( $move ) {
+        foreach my $record ( $move->isa('RT::Record')? ($move) : @{ $move->ItemsArrayRef } ) {
+            my ($status, $msg) = $record->SetSortOrder(
+                $record->SortOrder - $meta{'diff'}
+            );
+            return (0, "Couldn't move: $msg") unless $status;
         }
-        $new_sort_order = $below[0]->SortOrder;
-    } else {
-        $new_sort_order = $below[0]->SortOrder + 1;
     }
 
     my ($status, $msg) = $self->SetSortOrder( $new_sort_order );
     unless ( $status ) {
-        return (0, "Couldn't move scrip");
+        return (0, "Couldn't move: $msg");
     }
 
-    return (1,"Moved scrip down");
+    return (1,"Moved");
 }
 
 sub NextSortOrder {
@@ -425,6 +427,16 @@ sub NextSortOrder {
     return $first->SortOrder + 1;
 }
 
+sub IsSortOrderShared {
+    my $self = shift;
+    return 0 unless $self->ObjectId;
+
+    my $neighbors = $self->Neighbors;
+    $neighbors->Limit( FIELD => 'id', OPERATOR => '!=', VALUE => $self->id );
+    $neighbors->Limit( FIELD => 'SortOrder', VALUE => $self->SortOrder );
+    return $neighbors->Count;
+}
+
 sub TargetObj {
     my $self = shift;
     my $id   = shift;

commit 19001a938fc90ff876cc28ac4e422f504f27c1c6
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jan 4 23:35:37 2012 +0400

    use IsSortOrderShared in Delete
    
    current version has some tiny performance benefits,
    win is so small that it doesn't worth code complexity.

diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
index 9383187..9e7d623 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -228,23 +228,17 @@ sub _AppliedTo {
 sub Delete {
     my $self = shift;
 
+    return $self->SUPER::Delete if $self->IsSortOrderShared;
+
+    # Move everything below us up
     my $siblings = $self->Neighbors;
     $siblings->Limit( FIELD => 'SortOrder', OPERATOR => '>=', VALUE => $self->SortOrder );
     $siblings->OrderBy( FIELD => 'SortOrder', ORDER => 'ASC' );
-
-    my $sort_order = $self->SortOrder;
-    while (my $record = $siblings->Next) {
-        next if $record->id == $self->id;
-        return $self->SUPER::Delete if $self->SortOrder == $record->SortOrder;
-        last;
-    }
-
-    # Move everything below us up
     foreach my $record ( @{ $siblings->ItemsArrayRef } ) {
         $record->SetSortOrder($record->SortOrder - 1);
     }
 
-    $self->SUPER::Delete;
+    return $self->SUPER::Delete;
 }
 
 sub DeleteAll {

commit c4c939ac8f395b4e8a85d4e8d3c9a896958c7b3b
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Jan 9 16:32:11 2012 +0400

    check rights when we apply/remove scrips to/from queues

diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index 86958a0..b5d98cb 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -243,16 +243,42 @@ sub AddToObject {
     my $self = shift;
     my %args = @_%2? (ObjectId => @_) : (@_);
 
+    my $queue;
+    if ( $args{'ObjectId'} ) {
+        $queue = RT::Queue->new( $self->CurrentUser );
+        $queue->Load( $args{'ObjectId'} );
+        return (0, $self->loc('Invalid queue'))
+            unless $queue->id;
+    }
+    return ( 0, $self->loc('Permission Denied') )
+        unless $self->CurrentUser->PrincipalObj->HasRight(
+            Object => $queue || $RT::System, Right => 'ModifyScrips',
+        )
+    ;
+
     my $rec = RT::ObjectScrip->new( $self->CurrentUser );
     return $rec->Apply( %args, Scrip => $self );
 }
 
 sub RemoveFromObject {
     my $self = shift;
-    my $object = shift;
+    my %args = @_%2? (ObjectId => @_) : (@_);
+
+    my $queue;
+    if ( $args{'ObjectId'} ) {
+        $queue = RT::Queue->new( $self->CurrentUser );
+        $queue->Load( $args{'ObjectId'} );
+        return (0, $self->loc('Invalid queue id'))
+            unless $queue->id;
+    }
+    return ( 0, $self->loc('Permission Denied') )
+        unless $self->CurrentUser->PrincipalObj->HasRight(
+            Object => $queue || $RT::System, Right => 'ModifyScrips',
+        )
+    ;
 
     my $rec = RT::ObjectScrip->new( $self->CurrentUser );
-    $rec->LoadByCols( Scrip => $self->id, ObjectId => $object );
+    $rec->LoadByCols( Scrip => $self->id, ObjectId => $args{'ObjectId'} );
     return (0, $self->loc('Scrip is not applied') ) unless $rec->id;
     return $rec->Delete;
 }

commit 4c49e74cc03faa3e084756a8a29d306fecd5d611
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Jan 9 17:54:21 2012 +0400

    tidy test file

diff --git a/t/api/scrip.t b/t/api/scrip.t
index eb54347..147dc79 100644
--- a/t/api/scrip.t
+++ b/t/api/scrip.t
@@ -4,46 +4,39 @@ use warnings;
 use RT;
 use RT::Test tests => 25;
 
+my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+ok $queue && $queue->id, 'loaded or created queue';
 
+note 'basic scrips functionality test: create+execute';
 {
-
-ok (require RT::Scrip);
-
-
-my $q = RT::Queue->new(RT->SystemUser);
-$q->Create(Name => 'ScripTest');
-ok($q->Id, "Created a scriptest queue");
-
-my $s1 = RT::Scrip->new(RT->SystemUser);
-my ($val, $msg) =$s1->Create( Queue => $q->Id,
-             ScripAction => 'User Defined',
-             ScripCondition => 'User Defined',
-             CustomIsApplicableCode => 'if ($self->TicketObj->Subject =~ /fire/) { return (1);} else { return(0)}',
-             CustomPrepareCode => 'return 1',
-             CustomCommitCode => '$self->TicketObj->SetPriority("87");',
-             Template => 'Blank'
+    my $s1 = RT::Scrip->new(RT->SystemUser);
+    my ($val, $msg) = $s1->Create(
+        Queue => $queue->Id,
+        ScripAction => 'User Defined',
+        ScripCondition => 'User Defined',
+        CustomIsApplicableCode => '$self->TicketObj->Subject =~ /fire/? 1 : 0',
+        CustomPrepareCode => 'return 1',
+        CustomCommitCode => '$self->TicketObj->SetPriority("87");',
+        Template => 'Blank'
     );
-ok($val,$msg);
-
-my $ticket = RT::Ticket->new(RT->SystemUser);
-my ($tv,$ttv,$tm) = $ticket->Create(Queue => $q->Id,
-                                    Subject => "hair on fire",
-                                    );
-ok($tv, $tm);
-
-is ($ticket->Priority , '87', "Ticket priority is set right");
-
-
-my $ticket2 = RT::Ticket->new(RT->SystemUser);
-my ($t2v,$t2tv,$t2m) = $ticket2->Create(Queue => $q->Id,
-                                    Subject => "hair in water",
-                                    );
-ok($t2v, $t2m);
-
-isnt ($ticket2->Priority , '87', "Ticket priority is set right");
+    ok($val,$msg);
 
+    my $ticket = RT::Ticket->new(RT->SystemUser);
+    my ($tv,$ttv,$tm) = $ticket->Create(
+        Queue => $queue->Id,
+        Subject => "hair on fire",
+    );
+    ok($tv, $tm);
 
+    is ($ticket->Priority , '87', "Ticket priority is set right");
 
+    my $ticket2 = RT::Ticket->new(RT->SystemUser);
+    my ($t2v,$t2tv,$t2m) = $ticket2->Create(
+        Queue => $queue->Id,
+        Subject => "hair in water",
+    );
+    ok($t2v, $t2m);
+    isnt ($ticket2->Priority , '87', "Ticket priority is set right");
 }
 
 

commit 27b3c7d3c128340c2731dc40a279f8254cb5acad
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Jan 9 17:55:04 2012 +0400

    check that template exists when we apply scrip

diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index b5d98cb..cb07124 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -256,6 +256,18 @@ sub AddToObject {
         )
     ;
 
+    my $tname = $self->TemplateObj->Name;
+    my $template = RT::Template->new( $self->CurrentUser );
+    $template->LoadQueueTemplate( Queue => $queue? $queue->id : 0, Name => $tname );
+    $template->LoadGlobalTemplate( $tname ) if $queue && !$template->id;
+    unless ( $template->id ) {
+        if ( $queue ) {
+            return (0, $self->loc('No template [_1] in the queue', $tname));
+        } else {
+            return (0, $self->loc('No global template [_1]', $tname));
+        }
+    }
+
     my $rec = RT::ObjectScrip->new( $self->CurrentUser );
     return $rec->Apply( %args, Scrip => $self );
 }

commit 5e98cb512af86001d0b34cb6799e7b92f54a8439
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Jan 9 17:55:34 2012 +0400

    test adding/removing scrips from queues

diff --git a/t/api/scrip.t b/t/api/scrip.t
index 147dc79..df15599 100644
--- a/t/api/scrip.t
+++ b/t/api/scrip.t
@@ -2,7 +2,7 @@
 use strict;
 use warnings;
 use RT;
-use RT::Test tests => 25;
+use RT::Test tests => 61;
 
 my $queue = RT::Test->load_or_create_queue( Name => 'General' );
 ok $queue && $queue->id, 'loaded or created queue';
@@ -39,7 +39,7 @@ note 'basic scrips functionality test: create+execute';
     isnt ($ticket2->Priority , '87', "Ticket priority is set right");
 }
 
-
+note 'modify properties of a scrip';
 {
     my $scrip = RT::Scrip->new($RT::SystemUser);
     my ( $val, $msg ) = $scrip->Create(
@@ -110,3 +110,82 @@ note 'basic scrips functionality test: create+execute';
 
     ok( $scrip->Delete, 'delete the scrip' );
 }
+
+my $queue_B = RT::Test->load_or_create_queue( Name => 'B' );
+ok $queue_B && $queue_B->id, 'loaded or created queue';
+
+note 'check applications vs. templates';
+{
+    my $template = RT::Template->new( RT->SystemUser );
+    my ($status, $msg) = $template->Create( Queue => $queue->id, Name => 'foo' );
+    ok $status, 'created a template';
+
+    my $scrip = RT::Scrip->new(RT->SystemUser);
+    ($status, $msg) = $scrip->Create(
+        Queue          => $queue->Id,
+        ScripAction    => 'User Defined',
+        ScripCondition => 'User Defined',
+        Template       => 'bar',
+    );
+    ok(!$status, "couldn't create scrip, incorrect template");
+
+    ($status, $msg) = $scrip->Create(
+        Queue          => $queue->Id,
+        ScripAction    => 'User Defined',
+        ScripCondition => 'User Defined',
+        Template       => 'foo',
+    );
+    ok($status, 'created a scrip') or diag "error: $msg";
+    main->check_applications($scrip, [$queue], [0, $queue_B]);
+
+    ($status, $msg) = $scrip->AddToObject( $queue_B->id );
+    ok(!$status, $msg);
+    main->check_applications($scrip, [$queue], [0, $queue_B]);
+
+    $template = RT::Template->new( RT->SystemUser );
+    ($status, $msg) = $template->Create( Queue => $queue_B->id, Name => 'foo' );
+    ok $status, 'created a template';
+
+    ($status, $msg) = $scrip->AddToObject( $queue_B->id );
+    ok($status, 'added scrip to another queue');
+    main->check_applications($scrip, [$queue, $queue_B], [0]);
+
+    ($status, $msg) = $scrip->RemoveFromObject( $queue_B->id );
+    ok($status, 'removed scrip from queue');
+
+    ($status, $msg) = $template->Delete;
+    ok $status, 'deleted template foo in queue B';
+
+    ($status, $msg) = $scrip->AddToObject( $queue_B->id );
+    ok(!$status, $msg);
+    main->check_applications($scrip, [$queue], [0, $queue_B]);
+
+    ($status, $msg) = $template->Create( Queue => 0, Name => 'foo' );
+    ok $status, 'created a global template';
+
+    ($status, $msg) = $scrip->AddToObject( $queue_B->id );
+    ok($status, 'added scrip');
+    main->check_applications($scrip, [$queue, $queue_B], [0]);
+}
+
+sub check_applications {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+    my $self = shift;
+    my $scrip = shift;
+    my $to = shift || [];
+    my $not_to = shift || [];
+
+    ok($scrip->IsAdded(ref $_? $_->id : $_), 'added to queue' ) foreach @$to;
+    ok(!$scrip->IsAdded(ref $_? $_->id : $_), 'not added' ) foreach @$not_to;
+    is_deeply(
+        [sort map $_->id, @{ $scrip->AddedTo->ItemsArrayRef }],
+        [sort grep $_, map ref $_? $_->id : $_, @$to],
+        'correct list of queues',
+    );
+    is_deeply(
+        [sort map $_->id, @{ $scrip->NotAddedTo->ItemsArrayRef }],
+        [sort grep $_, map ref $_? $_->id : $_, @$not_to],
+        'correct list of queues',
+    );
+}
+

commit b4f635990fa0a9232215b1b4dee10fc07acab3e8
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Jan 9 17:56:17 2012 +0400

    not sure why, undef is not replaced with default

diff --git a/lib/RT/ObjectScrip.pm b/lib/RT/ObjectScrip.pm
index c28f806..676354d 100644
--- a/lib/RT/ObjectScrip.pm
+++ b/lib/RT/ObjectScrip.pm
@@ -61,6 +61,7 @@ sub ObjectCollectionClass {'RT::Queues'}
 sub Create {
     my $self = shift;
     my %args = (@_);
+    $args{'Stage'} ||= 'TransactionCreate'; #XXX: why don't we turn undef into default?
     return $self->SUPER::Create(
         map { $_ => $args{ $_ } } qw(
             Scrip Stage ObjectId

commit 9f0dd99a63902bb33e9824eb05d4db5a6411af2d
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Mon Jan 9 18:03:53 2012 +0400

    scrip->IsGlobal method, alias to ->IsAdded(0)

diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index cb07124..c52e58b 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -220,6 +220,8 @@ sub Delete {
     return ( $self->SUPER::Delete(@_) );
 }
 
+sub IsGlobal { return shift->IsAdded(0) }
+
 sub IsAdded {
     my $self = shift;
     my $record = RT::ObjectScrip->new( $self->CurrentUser );
diff --git a/share/html/Admin/Elements/EditScrip b/share/html/Admin/Elements/EditScrip
index 9cd3cb4..b1a0974 100644
--- a/share/html/Admin/Elements/EditScrip
+++ b/share/html/Admin/Elements/EditScrip
@@ -89,7 +89,7 @@
 
 % if ( $id ne 'new' && $added_to_any ) {
 <tr><td class="label"><&|/l&>Added</&>:</td><td class="value">\
-% if ( $scrip->IsAdded(0) ) {
+% if ( $scrip->IsGlobal ) {
 <% loc('Global') %>
 % } else {
 % my $added_to = $scrip->AddedTo;
diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index d093f98..d0dbf39 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -117,7 +117,7 @@ my $COLUMN_MAP = {
         },
         value => sub {
             my $id = $_[0]->id;
-            return '' if $_[0]->IsAdded;
+            return '' if $_[0]->IsGlobal;
 
             my $name = 'RemoveScrip';
             my $arg = $m->request_args->{ $name };

commit c90b528289927f8c3952e94c3d5d3071387649bb
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Jan 12 00:59:38 2012 +0400

    very basic tests for disabling scrips

diff --git a/t/api/scrip.t b/t/api/scrip.t
index df15599..a8437f7 100644
--- a/t/api/scrip.t
+++ b/t/api/scrip.t
@@ -2,7 +2,7 @@
 use strict;
 use warnings;
 use RT;
-use RT::Test tests => 61;
+use RT::Test tests => 66;
 
 my $queue = RT::Test->load_or_create_queue( Name => 'General' );
 ok $queue && $queue->id, 'loaded or created queue';
@@ -168,6 +168,27 @@ note 'check applications vs. templates';
     main->check_applications($scrip, [$queue, $queue_B], [0]);
 }
 
+note 'basic check for disabling scrips';
+{
+    my $scrip = RT::Scrip->new(RT->SystemUser);
+    my ($status, $msg) = $scrip->Create(
+        Queue          => $queue->Id,
+        ScripAction    => 'User Defined',
+        ScripCondition => 'User Defined',
+        Template       => 'Blank',
+    );
+    ok($status, "created scrip");
+    is($scrip->Disabled, 0, "not disabled");
+
+    ($status,$msg) = $scrip->SetDisabled(1);
+    is($scrip->Disabled, 1, "disabled");
+
+    ($status, $msg) = $scrip->RemoveFromObject( $queue->id );
+    ok($status, 'removed scrip from queue');
+
+    is($scrip->Disabled, undef, "not applied");
+}
+
 sub check_applications {
     local $Test::Builder::Level = $Test::Builder::Level + 1;
     my $self = shift;

commit 467f598832764abf40c9b5ffb1a029373fff1230
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Jan 11 16:58:18 2012 -0500

    Better heading for TransactionCreate scrips and short explanatory text
    
    Finally we'll have some description of the difference in the interface!

diff --git a/share/html/Admin/Elements/EditScrips b/share/html/Admin/Elements/EditScrips
index beaa3c2..aad378b 100644
--- a/share/html/Admin/Elements/EditScrips
+++ b/share/html/Admin/Elements/EditScrips
@@ -50,7 +50,8 @@
 <form action="Scrips.html" method="post">
 <input type="hidden" class="hidden" name="id" value="<% $id %>" />
 
-<h2><&|/l&>On create scrips</&></h2>
+<h2><&|/l&>Scrips</&></h2>
+<div class="admin-hint">Scrips run after each individual change to a ticket.</div>
 % my $scrips = $find_scrips->(Stage => 'TransactionCreate');
 <& /Elements/CollectionList, %common_applied_args, Collection => $scrips &>
 % unless ( $scrips->Count ) {
@@ -58,6 +59,7 @@
 % }
 
 <h2><&|/l&>Batch scrips</&></h2>
+<div class="admin-hint">Batch scrips run after a set of related changes to a ticket.</div>
 % $scrips = $find_scrips->(Stage => 'TransactionBatch');
 <& /Elements/CollectionList, %common_applied_args, Collection => $scrips &>
 % unless ( $scrips->Count ) {
diff --git a/share/html/NoAuth/css/base/admin.css b/share/html/NoAuth/css/base/admin.css
index 5f5d6fc..f68d1f3 100644
--- a/share/html/NoAuth/css/base/admin.css
+++ b/share/html/NoAuth/css/base/admin.css
@@ -95,3 +95,10 @@ ul.list-menu ul li {
     padding-bottom: 0;
 }
 
+.admin-hint {
+    font-style: italic;
+}
+
+h2 + .admin-hint {
+    margin-top: -1em;
+}

commit dfc55fa3c4a063af67272eeb55c1347ed5aa49ef
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Wed Jan 11 17:15:10 2012 -0500

    Display Normal and Batch instead of TransactionCreate and TransactionBatch
    
    Additionally, disable the displayed Batch option if UseTransactionBatch
    is turned off by RT's configuration.

diff --git a/share/html/Admin/Elements/EditScrips b/share/html/Admin/Elements/EditScrips
index aad378b..b72a645 100644
--- a/share/html/Admin/Elements/EditScrips
+++ b/share/html/Admin/Elements/EditScrips
@@ -51,7 +51,7 @@
 <input type="hidden" class="hidden" name="id" value="<% $id %>" />
 
 <h2><&|/l&>Scrips</&></h2>
-<div class="admin-hint">Scrips run after each individual change to a ticket.</div>
+<div class="admin-hint">Scrips normally run after each individual change to a ticket.</div>
 % my $scrips = $find_scrips->(Stage => 'TransactionCreate');
 <& /Elements/CollectionList, %common_applied_args, Collection => $scrips &>
 % unless ( $scrips->Count ) {
diff --git a/share/html/Admin/Elements/SelectStage b/share/html/Admin/Elements/SelectStage
index 7bbbf3b..0b700bc 100644
--- a/share/html/Admin/Elements/SelectStage
+++ b/share/html/Admin/Elements/SelectStage
@@ -53,6 +53,7 @@
 
 <option value="<%$value%>"
 <% ($value eq $Default) && qq[ selected="selected"] |n %>
+<% ($value eq 'TransactionBatch' and not RT->Config->Get('UseTransactionBatch')) && qq[ disabled ] %>
 ><% loc($display) %>
 </option>
 
@@ -62,11 +63,11 @@
 if ( !defined $Default || $Default eq '') {
     $Default = 'TransactionCreate';
 }
-my @stages = 'TransactionCreate';
+my @stages = ['TransactionCreate', loc('Normal')];
 
 push @stages, RT->Config->Get('UseTransactionBatch')
-            ? 'TransactionBatch'
-            : ['TransactionBatch', 'TransactionBatch (DISABLED)'];
+            ? ['TransactionBatch', loc('Batch')]
+            : ['TransactionBatch', loc('Batch (disabled by config)')];
 </%INIT>
 <%ARGS>
 $Default => 'TransactionCreate'

commit 8e3fadbc5b0150af2d26de7279e6450193d1b796
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Jan 13 16:18:46 2012 +0400

    add Admin/Scrips/ dir with index

diff --git a/share/html/Admin/Scrips/index.html b/share/html/Admin/Scrips/index.html
new file mode 100644
index 0000000..a31ebe5
--- /dev/null
+++ b/share/html/Admin/Scrips/index.html
@@ -0,0 +1,70 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+%#                                          <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Admin/Elements/Header, Title => loc('Select a Scrip') &>
+<& /Elements/Tabs &>
+
+<& /Elements/CollectionList,
+    OrderBy       => 'Description',
+    Order         => 'ASC',
+    Rows          => 50,
+    %ARGS,
+    Collection    => $scrips,
+    Format        => $Format,
+    AllowSorting  => 1,
+&>
+<%args>
+$Format => undef
+</%args>
+<%INIT>
+my $scrips = RT::Scrips->new( $session{'CurrentUser'} );
+$scrips->UnLimit;
+
+$m->callback(CallbackName => 'Massage', Scrips => $scrips);
+
+$Format ||= RT->Config->Get('AdminSearchResultFormat')->{'Scrips'};
+</%INIT>
\ No newline at end of file
diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index 015c2e3..70556e6 100644
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -97,6 +97,16 @@ my $build_admin_menu = sub {
         $cfs->child( create => title => loc('Create'), path => "/Admin/CustomFields/Modify.html?Create=1" );
     }
 
+    if ( $session{'CurrentUser'}->HasRight( Object => RT->System, Right => 'ModifyScrips' ) ) {
+        my $scrips = $admin->child( 'scrips' =>
+            title       => loc('Scrips'),
+            description => loc('Manage scrips'),
+            path        => '/Admin/Scrips/',
+        );
+        $scrips->child( select => title => loc('Select'), path => "/Admin/Scrips/" );
+        $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Modify.html?Create=1" );
+    }
+
     my $admin_global = $admin->child( global =>
         title       => loc('Global'),
         description => loc('Manage properties and configuration which apply to all queues'),

commit c7db7a61b21ab14b70b4176e0e50ac42d0745b34
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Jan 13 21:46:32 2012 +0400

    Move scrip editor into one place

diff --git a/share/html/Admin/Queues/Scrip.html b/share/html/Admin/Queues/Scrip.html
deleted file mode 100644
index ac0a783..0000000
--- a/share/html/Admin/Queues/Scrip.html
+++ /dev/null
@@ -1,77 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%#                                          <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<& /Admin/Elements/Header, Title => $title &>
-<& /Elements/Tabs &>
-
-<& /Elements/ListActions, actions => \@results &>
-<& /Admin/Elements/EditScrip, title => $title,  %ARGS, id => $id &>
-
-<%init>
-my $QueueObj = RT::Queue->new( $session{'CurrentUser'} );
-$QueueObj->Load( $Queue );
-unless( $QueueObj->id ) {
-    Abort(loc("Queue [_1] not found", $id));
-}
-
-my ($title);
-
-($id, my @results) = $m->comp( '/Admin/Elements/EditScrip:Process', %ARGS );
-
-if ( $id ) {
-    $title = loc("Modify a scrip for queue [_1]", $QueueObj->Name);
-} else {
-    $title = loc("Create a scrip for queue [_1]", $QueueObj->Name);
-}
-
-
-</%init>
-
-<%ARGS>
-$id => undef
-$Queue => undef
-</%ARGS>
diff --git a/share/html/Admin/Global/Scrip.html b/share/html/Admin/Scrips/Modify.html
similarity index 100%
rename from share/html/Admin/Global/Scrip.html
rename to share/html/Admin/Scrips/Modify.html

commit 325ccc53ede124be7a5972e25bdeb0d3c342361f
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Jan 13 21:49:12 2012 +0400

    we don't need AdminURL column map

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 92cc510..ea780b6 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2578,8 +2578,8 @@ Set(%AdminSearchResultFormat,
         .q{,__AppliedTo__, __FriendlyType__, __FriendlyPattern__},
 
     Scrips =>
-        q{'<a href="__WebPath__/__AdminURL__">__id__</a>/TITLE:#'}
-        .q{,'<a href="__WebPath__/__AdminURL__">__Description__</a>/TITLE:Description'}
+        q{'<a href="__WebPath__/Admin/Scrips/Modify.html?id=__id__">__id__</a>/TITLE:#'}
+        .q{,'<a href="__WebPath__/Admin/Scrips/Modify.html?id=__id__">__Description__</a>/TITLE:Description'}
         .q{, __Condition__, __Action__, __Template__},
 
     Templates =>
diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index d0dbf39..d90a536 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -93,20 +93,6 @@ my $COLUMN_MAP = {
 	attribute => 'Description',
 	value     => sub { return $_[0]->Description() },
     },
-
-    AdminURL => {
-	value     => sub {
-            my $queue = $_[0]->AddedTo->First;
-            my $res = 'Admin';
-            if ( $queue ) {
-                $res .= '/Queues/Scrip.html?Queue='. $queue->id .'&';
-            } else {
-                $res .= '/Global/Scrip.html?';
-            }
-            $res .= 'id='. $_[0]->id;
-            return $res;
-        },
-    },
     RemoveCheckBox => {
         title => sub {
             my $name = 'RemoveScrip';

commit b2d0bec5ef966c05550cb0891e68a5ad389d1348
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sun Jan 15 03:38:46 2012 +0400

    turn EditScrip into Create and Modify html comps
    
    EditScrip was used in two places, now it's one, so
    it's not required.
    
    Create and Modify are different for scrips, so we
    split them up and use smaller components for common
    ports.

diff --git a/share/html/Admin/Elements/EditScrip b/share/html/Admin/Elements/EditScrip
deleted file mode 100644
index b1a0974..0000000
--- a/share/html/Admin/Elements/EditScrip
+++ /dev/null
@@ -1,243 +0,0 @@
-%# BEGIN BPS TAGGED BLOCK {{{
-%#
-%# COPYRIGHT:
-%#
-%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
-%#                                          <sales at bestpractical.com>
-%#
-%# (Except where explicitly superseded by other copyright notices)
-%#
-%#
-%# LICENSE:
-%#
-%# This work is made available to you under the terms of Version 2 of
-%# the GNU General Public License. A copy of that license should have
-%# been provided with this software, but in any event can be snarfed
-%# from www.gnu.org.
-%#
-%# This work is distributed in the hope that it will be useful, but
-%# WITHOUT ANY WARRANTY; without even the implied warranty of
-%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-%# General Public License for more details.
-%#
-%# You should have received a copy of the GNU General Public License
-%# along with this program; if not, write to the Free Software
-%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-%# 02110-1301 or visit their web page on the internet at
-%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
-%#
-%#
-%# CONTRIBUTION SUBMISSION POLICY:
-%#
-%# (The following paragraph is not intended to limit the rights granted
-%# to you to modify and distribute this software under the terms of
-%# the GNU General Public License and is only of importance to you if
-%# you choose to contribute your changes and enhancements to the
-%# community by submitting them to Best Practical Solutions, LLC.)
-%#
-%# By intentionally submitting any modifications, corrections or
-%# derivatives to this work, or any other work intended for use with
-%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
-%# you are the copyright holder for those contributions and you grant
-%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
-%# royalty-free, perpetual, license to use, copy, create derivative
-%# works based on those contributions, and sublicense and distribute
-%# those contributions and any derivatives thereof.
-%#
-%# END BPS TAGGED BLOCK }}}
-<& /Elements/ListActions, actions => \@actions &>
-  
-<form method="post" action="Scrip.html" id="ModifyScrip" name="ModifyScrip">
-<input type="hidden" class="hidden" name="id" value="<% $id %>" />
-<input type="hidden" class="hidden" name="Queue" value="<% $Queue %>" />
-
-<&| /Widgets/TitleBox, title => loc('Scrip Fields') &>
-<table>
-
-<tr><td class="label"><&|/l&>Description</&>:</td><td class="value">\
-<input name="Scrip-<% $id %>-Description" \
-    size="60" \
-    value="<% $ARGS{"Scrip-$id-Description"} || $scrip->Description || '' %>" />
-</td></tr>
-
-<tr><td class="label"><&|/l&>Condition</&>:</td><td class="value">\
-<& /Admin/Elements/SelectScripCondition,
-    Name => "Scrip-$id-ScripCondition",
-    Default => $ARGS{"Scrip-$id-ScripCondition"} || $scrip->ConditionObj->Id,
-&></td></tr>
-
-<tr><td class="label"><&|/l&>Action</&>:</td><td class="value">\
-<& /Admin/Elements/SelectScripAction,
-    Name => "Scrip-$id-ScripAction",
-    Default => $ARGS{"Scrip-$id-ScripAction"} || $scrip->ActionObj->Id,
-&></td></tr>
-
-<tr><td class="label"><&|/l&>Template</&>:</td><td class="value">\
-<& /Admin/Elements/SelectTemplate,
-    Name => "Scrip-$id-Template",
-    Default => $ARGS{"Scrip-$id-Template"} || $scrip->TemplateObj->Id,
-    Queue => $Queue,
-&></td></tr>
-
-% if ( $id eq 'new' ) {
-<tr><td class="label"><&|/l&>Stage</&>:</td><td class="value">\
-<& /Admin/Elements/SelectStage,
-    Name => "Scrip-$id-Stage",
-    Default => $ARGS{"Scrip-$id-Stage"},
-&></td></tr>
-% }
-
-% if ( $id ne 'new' && $added_to_any ) {
-<tr><td class="label"><&|/l&>Added</&>:</td><td class="value">\
-% if ( $scrip->IsGlobal ) {
-<% loc('Global') %>
-% } else {
-% my $added_to = $scrip->AddedTo;
-% my $found = 0;
-% while ( my $queue = $added_to->Next ) {
-% $m->out(', ') if $found++;
-<% $queue->Name %>
-% last if $found == 10;
-% }
-% $m->out(', ...') if $found == 10;
-% }
-<td></tr>
-% }
-
-% if ( $id eq 'new' || $added_to_any  ) {
-<tr><td class="label"> </td><td>
-<input type="hidden" class="hidden" name="SetEnabled" value="1" />
-<input type="checkbox" class="checkbox" name="Scrip-<% $id %>-Enabled" value="1" <% $EnabledChecked |n%> />
-<label for="Scrip-<% $id %>-Enabled"><&|/l&>Enabled (Unchecking this box disables this scrip)</&></label>
-</td></tr>
-% }
-
-</table>
-</&>
-
-% if ($session{CurrentUser}->HasRight(Object => $RT::System, Right => 'ExecuteCode')) {
-<& /Elements/Submit,
-    Label => $SubmitLabel,
-    Reset => 1,
-&><br />
-
-<&| /Widgets/TitleBox, title => loc('User Defined conditions and actions') &>
-<table>
-<tr><td colspan="2">
-<i><&|/l&>(Use these fields when you choose 'User Defined' for a condition or action)</&></i>
-</td></tr>
-
-<tr><td class="labeltop"><&|/l&>Custom condition</&>:</td><td class="value">
-% my $code = $ARGS{"Scrip-$id-CustomIsApplicableCode"} || $scrip->CustomIsApplicableCode || '';
-% my $lines = @{[ $code =~ /\n/gs ]} + 3;
-% $lines = $min_lines if $lines < $min_lines;
-<textarea cols="80" rows="<% $lines %>" name="Scrip-<% $id %>-CustomIsApplicableCode"><% $code %></textarea>
-</td></tr>
-
-<tr><td class="labeltop"><&|/l&>Custom action preparation code</&>:</td><td class="value">
-% $code = $ARGS{"Scrip-$id-CustomPrepareCode"} || $scrip->CustomPrepareCode || '';
-% $lines = @{[ $code =~ /\n/gs ]} + 3;
-% $lines = $min_lines if $lines < $min_lines;
-<textarea cols="80" rows="<% $lines %>" name="Scrip-<% $id %>-CustomPrepareCode"><% $code %></textarea>
-</td></tr>
-
-<tr><td class="labeltop"><&|/l&>Custom action cleanup code</&>:</td><td class="value">
-% $code = $ARGS{"Scrip-$id-CustomCommitCode"} || $scrip->CustomCommitCode || '';
-% $lines = @{[ $code =~ /\n/gs ]} + 3;
-% $lines = $min_lines if $lines < $min_lines;
-<textarea cols="80" rows="<% $lines %>" name="Scrip-<% $id || '' %>-CustomCommitCode"><% $code %></textarea>
-</td></tr>
-
-</table>
-</&>
-
-% }
-
-<& /Elements/Submit,
-    Label => $SubmitLabel,
-    Reset => 1,
-&>
-
-</form>
-<%init>
-
-my (@actions, $SubmitLabel);
-my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
-
-if ( $id ) {
-    $scrip->Load( $id );
-    unless ( $id = $scrip->id ) {
-        push @actions, loc("Couldn't load scrip #[_1]", $id);
-    }
-    $SubmitLabel = loc('Save Changes');
-}
-
-my $EnabledChecked = qq[checked="checked"];
-my $added_to_any = 0;
-
-unless ( $id ) {
-    $id = 'new';
-    $SubmitLabel = loc('Create');
-}
-else {
-    my $disabled = $scrip->Disabled;
-    $added_to_any = 1 if defined $disabled;
-    $EnabledChecked = '' if $disabled;
-}
-
-my $min_lines = 10;
-
-my ($ok, $msg) = $scrip->CompileCheck;
-push @actions, $msg if !$ok;
-
-</%init>
-
-<%ARGS>
-$id => undef
-$title => undef
-$Queue => 0
-</%ARGS>
-
-<%METHOD Process>
-<%ARGS>
-$id => undef
-$Queue => undef
-</%ARGS>
-<%INIT>
-return ($id) unless $id;
-
-my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
-if ( $id eq 'new' ) {
-    return $scrip->Create(
-        Queue                  => $Queue,
-        ScripAction            => $ARGS{"Scrip-new-ScripAction"},
-        ScripCondition         => $ARGS{"Scrip-new-ScripCondition"},
-        Template               => $ARGS{"Scrip-new-Template"},
-        Description            => $ARGS{"Scrip-new-Description"},
-        CustomPrepareCode      => $ARGS{"Scrip-new-CustomPrepareCode"},
-        CustomCommitCode       => $ARGS{"Scrip-new-CustomCommitCode"},
-        CustomIsApplicableCode => $ARGS{"Scrip-new-CustomIsApplicableCode"},
-        Stage                  => $ARGS{"Scrip-new-Stage"},
-        Disabled               => $ARGS{"Scrip-new-Enabled"}? 0 : 1,
-    );
-}
-else {
-    $scrip->Load( $id );
-    return (undef, loc("Couldn't load scrip #[_1]", $id))
-        unless $scrip->id;
-
-    $ARGS{"Scrip-$id-Disabled"} = $ARGS{"Scrip-$id-Enabled"}? 0 : 1
-        if $ARGS{"SetEnabled"};
-
-    my @attribs = qw(ScripAction ScripCondition Template Stage
-        Description CustomPrepareCode CustomCommitCode CustomIsApplicableCode Disabled);
-    my @results = UpdateRecordObject(
-        AttributesRef   => \@attribs,
-        AttributePrefix => 'Scrip-'.$scrip->Id,
-        Object          => $scrip,
-        ARGSRef         => \%ARGS
-    );
-    return ($scrip->id, @results);
-}
-</%INIT>
-</%METHOD>
diff --git a/share/html/Admin/Scrips/Create.html b/share/html/Admin/Scrips/Create.html
new file mode 100644
index 0000000..c301125
--- /dev/null
+++ b/share/html/Admin/Scrips/Create.html
@@ -0,0 +1,137 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+%#                                          <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions, actions => \@results &>
+
+<form method="post" action="Create.html" id="CreateScrip" name="CreateScrip">
+<input type="hidden" class="hidden" name="Queue" value="<% $Queue %>" />
+
+<&| /Widgets/TitleBox, title => loc('Basics') &>
+<table>
+
+<& Elements/EditBasics, %ARGS, Scrip => $scrip &>
+
+<tr><td class="label"><&|/l&>Stage</&>:</td><td class="value">\
+<& /Admin/Elements/SelectStage, Default => $ARGS{"Stage"} &></td></tr>
+
+<tr><td class="label"> </td><td>
+<input type="hidden" class="hidden" name="SetEnabled" value="1" />
+<input type="checkbox" class="checkbox" name="Enabled" value="1" <% $ARGS{'Enabled'}? 'checked="checked"': '' |n%> />
+<label for="Enabled"><&|/l&>Enabled (Unchecking this box disables this scrip)</&></label>
+</td></tr>
+
+</table>
+</&>
+
+<& /Elements/Submit,
+    Label => loc('Create'),
+    Name => 'Create',
+&>
+
+% if ($session{CurrentUser}->HasRight(Object => $RT::System, Right => 'ExecuteCode')) {
+<& Elements/EditCustomCode, %ARGS, Scrip => $scrip &>
+<& /Elements/Submit,
+    Label => loc('Create'),
+    Name => 'Create',
+&>
+% }
+
+</form>
+<%ARGS>
+$Queue => 0
+$Create => undef
+</%ARGS>
+<%INIT>
+my @results;
+
+$ARGS{'Enabled'} = 1 unless $ARGS{'SetEnabled'};
+
+my $queue_obj;
+if ( $Queue ) {
+    $queue_obj = RT::Queue->new( $session{'CurrentUser'} );
+    $queue_obj->Load( $Queue );
+    Abort( loc("Couldn't load queue [_1]", $Queue) )
+        unless $queue_obj->id;
+}
+
+my $title;
+if ( $queue_obj ) {
+    $title = loc('Create a scrip and add to queue [_1]', $queue_obj->Name );
+} else {
+    $title = loc('Create a global scrip');
+}
+
+my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
+if ( $Create ) {
+    my ($status, $msg) = $scrip->Create(
+        Description            => $ARGS{"Description"},
+
+        Queue                  => $Queue || 0,
+        Stage                  => $ARGS{"Stage"},
+
+        ScripAction            => $ARGS{"ScripAction"},
+        ScripCondition         => $ARGS{"ScripCondition"},
+        Template               => $ARGS{"Template"},
+
+        CustomPrepareCode      => $ARGS{"CustomPrepareCode"},
+        CustomCommitCode       => $ARGS{"CustomCommitCode"},
+        CustomIsApplicableCode => $ARGS{"CustomIsApplicableCode"},
+    );
+
+    MaybeRedirectForResults(
+        Force     => 1,
+        Actions   => [ $msg ],
+        Path      => 'Admin/Scrips/Modify.html',
+        Arguments => { id => $scrip->id },
+    ) if $status;
+
+    push @results, $msg;
+}
+
+</%INIT>
\ No newline at end of file
diff --git a/share/html/Admin/Scrips/Modify.html b/share/html/Admin/Scrips/Elements/EditBasics
similarity index 72%
copy from share/html/Admin/Scrips/Modify.html
copy to share/html/Admin/Scrips/Elements/EditBasics
index 3a6ed10..d7746b2 100644
--- a/share/html/Admin/Scrips/Modify.html
+++ b/share/html/Admin/Scrips/Elements/EditBasics
@@ -45,19 +45,29 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<& /Admin/Elements/Header, Title => $title &>
-<& /Elements/Tabs &>
-<& /Elements/ListActions, actions => \@results &>
-<& /Admin/Elements/EditScrip, title => $title,  %ARGS, id => $id &>
+<tr><td class="label"><&|/l&>Description</&>:</td><td class="value">\
+<input name="Description" \
+    size="60" \
+    value="<% $ARGS{"Description"} || $Scrip->Description || '' %>" />
+</td></tr>
 
-<%init>
-my ($id, @results) = $m->comp( '/Admin/Elements/EditScrip:Process', %ARGS );
+<tr><td class="label"><&|/l&>Condition</&>:</td><td class="value">\
+<& /Admin/Elements/SelectScripCondition,
+    Default => $ARGS{"ScripCondition"} || $Scrip->ConditionObj->Id,
+&></td></tr>
 
-my ($title);
-if ( $id ) {
-    $title = loc("Modify a scrip that applies to all queues");
-}
-else {
-    $title = loc("Add a scrip which will apply to all queues");
-}
-</%init>
+<tr><td class="label"><&|/l&>Action</&>:</td><td class="value">\
+<& /Admin/Elements/SelectScripAction,
+    Default => $ARGS{"ScripAction"} || $Scrip->ActionObj->Id,
+&></td></tr>
+
+<tr><td class="label"><&|/l&>Template</&>:</td><td class="value">\
+<& /Admin/Elements/SelectTemplate,
+    Default => $ARGS{"Template"} || $Scrip->TemplateObj->Id,
+&></td></tr>
+
+<%ARGS>
+$Scrip
+</%ARGS>
+<%INIT>
+</%INIT>
diff --git a/share/html/Admin/Scrips/Modify.html b/share/html/Admin/Scrips/Elements/EditCustomCode
similarity index 70%
copy from share/html/Admin/Scrips/Modify.html
copy to share/html/Admin/Scrips/Elements/EditCustomCode
index 3a6ed10..775dd51 100644
--- a/share/html/Admin/Scrips/Modify.html
+++ b/share/html/Admin/Scrips/Elements/EditCustomCode
@@ -45,19 +45,33 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<& /Admin/Elements/Header, Title => $title &>
-<& /Elements/Tabs &>
-<& /Elements/ListActions, actions => \@results &>
-<& /Admin/Elements/EditScrip, title => $title,  %ARGS, id => $id &>
+<&| /Widgets/TitleBox, title => loc('User Defined conditions and results') &>
 
-<%init>
-my ($id, @results) = $m->comp( '/Admin/Elements/EditScrip:Process', %ARGS );
+<table>
+<tr><td colspan="2" class="comment">
+<i><&|/l&>(Use these fields when you choose 'User Defined' for a condition or action)</&></i>
+</td></tr>
 
-my ($title);
-if ( $id ) {
-    $title = loc("Modify a scrip that applies to all queues");
-}
-else {
-    $title = loc("Add a scrip which will apply to all queues");
-}
-</%init>
+% while ( my ($method, $desc) = splice @list, 0, 2 ) {
+<tr><td class="labeltop"><% $desc %>:</td><td class="value">
+% my $code = $ARGS{ $method } || $Scrip->$method() || '';
+% my $lines = @{[ $code =~ /\n/gs ]} + 3;
+% $lines = $min_lines if $lines < $min_lines;
+<textarea cols="80" rows="<% $lines %>" name="<% $method %>"><% $code %></textarea>
+</td></tr>
+% }
+
+</table>
+</&>
+<%ARGS>
+$Scrip
+</%ARGS>
+<%INIT>
+my @list = (
+    CustomIsApplicableCode => loc('Custom condition'),
+    CustomPrepareCode      => loc('Custom action preparation code'),
+    CustomCommitCode       => loc('Custom action cleanup code'),
+);
+
+my $min_lines = 10;
+</%INIT>
diff --git a/share/html/Admin/Scrips/Modify.html b/share/html/Admin/Scrips/Modify.html
index 3a6ed10..0c8ed76 100644
--- a/share/html/Admin/Scrips/Modify.html
+++ b/share/html/Admin/Scrips/Modify.html
@@ -45,19 +45,93 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<& /Admin/Elements/Header, Title => $title &>
+<& /Admin/Elements/Header, Title => loc("Modify scrip #[_1]", $id) &>
 <& /Elements/Tabs &>
 <& /Elements/ListActions, actions => \@results &>
-<& /Admin/Elements/EditScrip, title => $title,  %ARGS, id => $id &>
 
-<%init>
-my ($id, @results) = $m->comp( '/Admin/Elements/EditScrip:Process', %ARGS );
+<form method="post" action="Modify.html" id="ModifyScrip" name="ModifyScrip">
+<input type="hidden" class="hidden" name="id" value="<% $id %>" />
 
-my ($title);
-if ( $id ) {
-    $title = loc("Modify a scrip that applies to all queues");
-}
-else {
-    $title = loc("Add a scrip which will apply to all queues");
+<&| /Widgets/TitleBox, title => loc('Basics') &>
+<table>
+
+<& Elements/EditBasics, %ARGS, Scrip => $scrip &>
+
+% if ( $added_to_any ) {
+<tr><td class="label"><&|/l&>Added</&>:</td><td class="value">\
+% if ( $scrip->IsGlobal ) {
+<% loc('Global') %>
+% } else {
+% my $added_to = $scrip->AddedTo;
+% my $found = 0;
+% while ( my $queue = $added_to->Next ) {
+% $m->out(', ') if $found++;
+<% $queue->Name %>
+% last if $found == 10;
+% }
+% $m->out(', ...') if $found == 10;
+% }
+<td></tr>
+% }
+
+% if ( $added_to_any  ) {
+<tr><td class="label"> </td><td>
+<input type="hidden" class="hidden" name="SetEnabled" value="1" />
+<input type="checkbox" class="checkbox" name="Enabled" value="1" <% $EnabledChecked |n%> />
+<label for="Enabled"><&|/l&>Enabled (Unchecking this box disables this scrip)</&></label>
+</td></tr>
+% }
+
+</table>
+</&>
+
+<& /Elements/Submit, Label => loc('Save Changes'), Name => 'Update', Reset => 1 &>
+
+% if ($session{CurrentUser}->HasRight(Object => $RT::System, Right => 'ExecuteCode')) {
+<& Elements/EditCustomCode, %ARGS, Scrip => $scrip &>
+<& /Elements/Submit, Label => loc('Save Changes'), Name => 'Update', Reset => 1 &>
+% }
+
+</form>
+<%ARGS>
+$id     => undef
+$Update => undef
+</%ARGS>
+<%INIT>
+my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
+$scrip->Load( $id );
+Abort(loc("Couldn't load scrip #[_1]", $id))
+    unless $scrip->id;
+
+my $disabled = $scrip->Disabled;
+my $added_to_any = 0;
+$added_to_any = 1 if defined $disabled;
+
+if ( $Update ) {
+    my @attribs = qw(
+        Description
+        ScripAction ScripCondition Template
+        CustomPrepareCode CustomCommitCode CustomIsApplicableCode
+    );
+    if ( $ARGS{'SetEnabled'} && $added_to_any ) {
+        push @attribs, 'Disabled';
+        $ARGS{"Disabled"} = $ARGS{"Enabled"}? 0 : 1;
+    }
+    my @results = UpdateRecordObject(
+        AttributesRef   => \@attribs,
+        Object          => $scrip,
+        ARGSRef         => \%ARGS
+    );
+    MaybeRedirectForResults(
+        Actions   => \@results,
+        Arguments => { id => $scrip->id },
+    );
 }
-</%init>
+
+my $EnabledChecked = qq[checked="checked"];
+$EnabledChecked = '' if $disabled;
+
+my @results;
+my ($ok, $msg) = $scrip->CompileCheck;
+push @results, $msg if !$ok;
+</%INIT>
diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index 70556e6..9558d90 100644
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -104,7 +104,7 @@ my $build_admin_menu = sub {
             path        => '/Admin/Scrips/',
         );
         $scrips->child( select => title => loc('Select'), path => "/Admin/Scrips/" );
-        $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Modify.html?Create=1" );
+        $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html" );
     }
 
     my $admin_global = $admin->child( global =>

commit 8fe02c7ff7ef977664506240d7a094a9b50cd141
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Jan 20 03:45:28 2012 +0400

    update validator, check ObjectScrips

diff --git a/sbin/rt-validator.in b/sbin/rt-validator.in
index fce7b55..a06f53b 100644
--- a/sbin/rt-validator.in
+++ b/sbin/rt-validator.in
@@ -130,6 +130,7 @@ my @models = qw(
     ScripAction
     ScripCondition
     Scrip
+    ObjectScrip
     Template
     Ticket
     Transaction
@@ -152,6 +153,7 @@ $redo_on{'Delete'} = {
     Queues => [],
 
     Scrips => [],
+    ObjectScrips => [],
     ScripActions => [],
     ScripConditions => [],
     Templates => [],
@@ -781,9 +783,6 @@ push @CHECKS, Templates => sub {
 
 push @CHECKS, Scrips => sub {
     check_integrity(
-        'Scrips', 'Queue' => 'Queues', 'id',
-    );
-    check_integrity(
         'Scrips', 'ScripCondition' => 'ScripConditions', 'id',
     );
     check_integrity(
@@ -792,6 +791,12 @@ push @CHECKS, Scrips => sub {
     check_integrity(
         'Scrips', 'Template' => 'Templates', 'id',
     );
+    check_integrity(
+        'ObjectScrips', 'Scrip' => 'Scrips', 'id',
+    );
+    check_integrity(
+        'ObjectScrips', 'ObjectId' => 'Queues', 'id',
+    );
 };
 
 push @CHECKS, Attributes => sub {
@@ -1073,6 +1078,15 @@ while ( my $check = shift @do_check ) {
     }
 }
 
+=head2 check_integrity
+
+Takes two (table name, column(s)) pairs. First pair
+is reference we check and second is destination that
+must exist. Array reference can be used for multiple
+columns.
+
+=cut
+
 sub check_integrity {
     my ($stable, @scols) = (shift, shift);
     my ($ttable, @tcols) = (shift, shift);

commit 56e6f5d27a06e2708fc304761d39504cb9df5410
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Jan 20 18:49:22 2012 +0400

    change Create links for scrips

diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index 9558d90..8ddf5b4 100644
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -119,7 +119,7 @@ my $build_admin_menu = sub {
         path        => '/Admin/Global/Scrips.html',
     );
     $scrips->child( select => title => loc('Select'), path => "/Admin/Global/Scrips.html" );
-    $scrips->child( create => title => loc('Create'), path => "/Admin/Global/Scrip.html?Create=1" );
+    $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html" );
 
     my $templates = $admin_global->child( templates =>
         title       => loc('Templates'),
@@ -294,7 +294,7 @@ my $build_admin_menu = sub {
 
             my $scrips = $queue->child( scrips => title => loc('Scrips'), path => "/Admin/Queues/Scrips.html?id=" . $id);
             $scrips->child( select => title => loc('Select'), path => "/Admin/Queues/Scrips.html?id=" . $id );
-            $scrips->child( create => title => loc('Create'), path => "/Admin/Queues/Scrip.html?Create=1;Queue=" . $id);
+            $scrips->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html?Queue=" . $id);
 
             my $ticket_cfs = $queue->child( 'ticket-custom-fields' => title => loc('Ticket Custom Fields'),
                 path => '/Admin/Queues/CustomFields.html?SubType=RT::Ticket&id=' . $id );

commit e38e02a32adff463197507ab49b5b5489c76a9b6
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Jan 20 21:20:21 2012 +0400

    Admin/Scrips/Objects.html page

diff --git a/share/html/Admin/Scrips/Objects.html b/share/html/Admin/Scrips/Objects.html
new file mode 100644
index 0000000..5c900b0
--- /dev/null
+++ b/share/html/Admin/Scrips/Objects.html
@@ -0,0 +1,145 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+%#                                          <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Admin/Elements/Header, Title => $title &>
+<& /Elements/Tabs &>
+<& /Elements/ListActions &>
+
+<form action="Objects.html" method="post">
+<input type="hidden" class="hidden" name="id" value="<% $id %>" />
+
+% if ( $is_global ) {
+<h2><&|/l&>Applies to all objects</&></h2>
+<input type="checkbox" name="RemoveScrip-<% $id %>" value="0" />
+<&|/l&>check this box to remove this scrip from all objects and be able to choose specific objects.</&>
+% } else {
+<h2><% loc('Stage') %></h2>
+<& /Admin/Elements/SelectStage, Default => $Stage &>
+
+<h2><&|/l&>Apply globally</&></h2>
+
+<input type="checkbox" name="AddScrip-<% $id %>" value="0" />
+<&|/l&>check this box to apply this scrip to all objects.</&>
+
+<h2><&|/l&>Selected objects</&></h2>
+<& /Elements/CollectionList,
+    OrderBy => 'id',
+    Order => 'ASC',
+    %ARGS,
+    Collection => $applied,
+    Rows => 0,
+    Page => 1,
+    Format        => $format,
+    DisplayFormat => "'__CheckBox.{RemoveScrip-$id}__','__ScripStage.{$id}__',". $format,
+    AllowSorting => 0,
+    ShowEmpty    => 0,
+    PassArguments => [
+        qw(id Stage Format Rows Page Order OrderBy),
+    ],
+&>
+
+<h2><&|/l&>Unselected objects</&></h2>
+<& /Elements/CollectionList,
+    OrderBy => 'Name',
+    Order   => 'ASC',
+    %ARGS,
+    Collection    => $not_applied,
+    Rows          => 50,
+    Format        => $format,
+    DisplayFormat => "'__CheckBox.{AddScrip-". $id ."}__',". $format,
+    AllowSorting  => 1,
+    ShowEmpty     => 0,
+    PassArguments => [
+        qw(id Stage Format Rows Page Order OrderBy),
+    ],
+&>
+
+% }
+
+<& /Elements/Submit, Name => 'Update' &>
+</form>
+
+<%ARGS>
+$id => undef
+$Stage => undef
+$Update => 0
+</%ARGS>
+<%INIT>
+my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
+$scrip->Load($id) or Abort(loc("Could not load scrip #[_1]", $id));
+$id = $scrip->id;
+
+if ( $Update ) {
+    my (@results);
+    if ( defined (my $del = $ARGS{"RemoveScrip-$id"}) ) {
+        foreach my $id ( ref $del? (@$del) : ($del) ) {
+            my ($status, $msg) = $scrip->RemoveFromObject( $id );
+            push @results, $msg;
+        }
+    }
+    if ( defined (my $add = $ARGS{"AddScrip-$id"}) ) {
+        foreach my $id ( ref $add? (@$add) : ($add) ) {
+            my ($status, $msg) = $scrip->AddToObject( $id, Stage => $Stage );
+            push @results, $msg;
+        }
+    }
+    MaybeRedirectForResults(
+        Actions   => \@results,
+        Arguments => { id => $id },
+    );
+}
+
+my $is_global = $scrip->IsGlobal;
+
+my $applied = $scrip->AddedTo;
+my $not_applied = $scrip->NotAddedTo;
+
+my $format = RT->Config->Get('AdminSearchResultFormat')->{'Queues'};
+
+my $title = loc('Modify associated objects for scrip #[_1]', $id);
+
+</%INIT>
diff --git a/share/html/Elements/RT__Queue/ColumnMap b/share/html/Elements/RT__Queue/ColumnMap
index e08dd7c..6b52763 100644
--- a/share/html/Elements/RT__Queue/ColumnMap
+++ b/share/html/Elements/RT__Queue/ColumnMap
@@ -89,6 +89,15 @@ my $COLUMN_MAP = {
         attribute => 'Lifecycle',
         value => sub { return $_[0]->Lifecycle->Name },
     },
+    ScripStage => {
+        title => 'Stage', # loc
+        value => sub {
+            my $sid = $_[-1];
+            my $os = RT::ObjectScrip->new( $_[0]->CurrentUser );
+            $os->LoadByCols( Scrip => $_[-1], ObjectId => $_[0]->id );
+            return $os->Stage;
+        },
+    },
 };
 
 foreach my $field (qw(
diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index 8ddf5b4..a4351d2 100644
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -362,6 +362,18 @@ my $build_admin_menu = sub {
         }
     }
 
+    if ( $request_path =~ m{^/Admin/Scrips/} ) {
+        if ( $m->request_args->{'id'} && $m->request_args->{'id'} =~ /^\d+$/ ) {
+            my $id = $m->request_args->{'id'};
+            my $obj = RT::Scrip->new( $session{'CurrentUser'} );
+            $obj->Load($id);
+
+            my $tabs = PageMenu();
+            $tabs->child( basics => title => loc('Basics') => path => "/Admin/Scrips/Modify.html?id=".$id );
+            $tabs->child( 'added-to' => title => loc('Added to'), path => "/Admin/Scrips/Objects.html?id=" . $id );
+        }
+    }
+
     if ( $request_path =~ m{^/Admin/Global/(Scrip|Template)s?\.html} ) {
         my $type = $1;
         my $tabs = PageMenu();

commit 77ca82f238b41682f822388c0c6795419294dddc
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Jan 20 21:47:03 2012 +0400

    link a few pages right from inside Modify.html

diff --git a/share/html/Admin/Scrips/Modify.html b/share/html/Admin/Scrips/Modify.html
index 0c8ed76..e6766c8 100644
--- a/share/html/Admin/Scrips/Modify.html
+++ b/share/html/Admin/Scrips/Modify.html
@@ -58,15 +58,17 @@
 <& Elements/EditBasics, %ARGS, Scrip => $scrip &>
 
 % if ( $added_to_any ) {
-<tr><td class="label"><&|/l&>Added</&>:</td><td class="value">\
+<tr><td class="label"><a href="Objects.html?id=<% $id %>"><&|/l&>Added</&></a>:</td>
+<td class="value">\
 % if ( $scrip->IsGlobal ) {
-<% loc('Global') %>
+<a href="<% RT->Config->Get('WebPath') %>/Admin/Global/Scrips.html"><% loc('Global') %></a>
 % } else {
 % my $added_to = $scrip->AddedTo;
 % my $found = 0;
 % while ( my $queue = $added_to->Next ) {
 % $m->out(', ') if $found++;
-<% $queue->Name %>
+<a href="<% RT->Config->Get('WebPath') %>/Admin/Queues/Scrips.html?id=<% $queue->id %>">\
+<% $queue->Name %></a>\
 % last if $found == 10;
 % }
 % $m->out(', ...') if $found == 10;

commit 60ee7d1b5f44588c895652c740516348c70facbc
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Jan 20 22:20:57 2012 +0400

    new templates picker that accounts multiple queues

diff --git a/share/html/Admin/Scrips/Elements/EditBasics b/share/html/Admin/Scrips/Elements/EditBasics
index d7746b2..94233ee 100644
--- a/share/html/Admin/Scrips/Elements/EditBasics
+++ b/share/html/Admin/Scrips/Elements/EditBasics
@@ -62,12 +62,13 @@
 &></td></tr>
 
 <tr><td class="label"><&|/l&>Template</&>:</td><td class="value">\
-<& /Admin/Elements/SelectTemplate,
-    Default => $ARGS{"Template"} || $Scrip->TemplateObj->Id,
+<& SelectTemplate,
+    Default => $ARGS{"Template"}, Scrip => $Scrip, Queue => $Queue,
 &></td></tr>
 
 <%ARGS>
 $Scrip
+$Queue => undef
 </%ARGS>
 <%INIT>
 </%INIT>
diff --git a/share/html/Admin/Scrips/Elements/EditBasics b/share/html/Admin/Scrips/Elements/SelectTemplate
similarity index 62%
copy from share/html/Admin/Scrips/Elements/EditBasics
copy to share/html/Admin/Scrips/Elements/SelectTemplate
index d7746b2..1148e42 100644
--- a/share/html/Admin/Scrips/Elements/EditBasics
+++ b/share/html/Admin/Scrips/Elements/SelectTemplate
@@ -45,29 +45,56 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<tr><td class="label"><&|/l&>Description</&>:</td><td class="value">\
-<input name="Description" \
-    size="60" \
-    value="<% $ARGS{"Description"} || $Scrip->Description || '' %>" />
-</td></tr>
-
-<tr><td class="label"><&|/l&>Condition</&>:</td><td class="value">\
-<& /Admin/Elements/SelectScripCondition,
-    Default => $ARGS{"ScripCondition"} || $Scrip->ConditionObj->Id,
-&></td></tr>
-
-<tr><td class="label"><&|/l&>Action</&>:</td><td class="value">\
-<& /Admin/Elements/SelectScripAction,
-    Default => $ARGS{"ScripAction"} || $Scrip->ActionObj->Id,
-&></td></tr>
-
-<tr><td class="label"><&|/l&>Template</&>:</td><td class="value">\
-<& /Admin/Elements/SelectTemplate,
-    Default => $ARGS{"Template"} || $Scrip->TemplateObj->Id,
-&></td></tr>
+<select name="<% $Name %>">
+<option value=""><% $current || '-' %></option>
+% foreach my $name ( @list ) {
+<option value="<% $name %>" \
+    <% lc($Default||'') eq lc $name ? 'selected="selected"' : '' |n %>
+><% loc($name) %></option>
+% }
+</select>
 
 <%ARGS>
-$Scrip
+$Name => 'Template'
+$Queue => undef
+$Scrip => undef
+$Default => undef
 </%ARGS>
 <%INIT>
+
+my $current;
+$current = $Scrip->TemplateObj->Name if $Scrip;
+
+my $global = RT::Templates->new($session{'CurrentUser'});
+$global->LimitToGlobal;
+
+my %global;
+$global{ lc $_ } = $_ foreach map $_->Name, @{ $global->ItemsArrayRef };
+
+my @queues;
+if ( $Scrip && $Scrip->id ) {
+    @queues = @{ $Scrip->AddedTo->ItemsArrayRef };
+}
+elsif ( $Queue && $Queue->id ) {
+    @queues = ($Queue);
+}
+
+my (%names, %counters);
+foreach my $queue ( @queues ) {
+    my $templates = RT::Templates->new($session{'CurrentUser'});
+    $templates->LimitToQueue( $queue->id );
+    foreach my $name ( map $_->Name, @{ $templates->ItemsArrayRef } ) {
+        next if $global{ lc $name };
+        $counters{ lc $name }++;
+        $names{lc $name} = $name;
+    }
+}
+delete $counters{ $_ }
+    foreach grep $counters{$_} != @queues,
+    keys %counters;
+
+my @list =
+    sort { lc loc($a) cmp lc loc($b) }
+    map $global{$_} || $names{$_},
+    keys %global, keys %counters;
 </%INIT>

commit 0901606c58bf8307fb17da34001b6ffbdf76aca7
Author: Kevin Falcone <falcone at bestpractical.com>
Date:   Fri Sep 30 11:47:14 2011 -0400

    Rename this to be less confusing
    
    Internally, this is commit.  We always explain it as commit.
    It actually runs during the commit, not as a 'cleanup' stage
    (TransactionBatch is actually much closer to being a 'cleanup' stage)

diff --git a/share/html/Admin/Scrips/Elements/EditCustomCode b/share/html/Admin/Scrips/Elements/EditCustomCode
index 775dd51..ecafa23 100644
--- a/share/html/Admin/Scrips/Elements/EditCustomCode
+++ b/share/html/Admin/Scrips/Elements/EditCustomCode
@@ -70,7 +70,7 @@ $Scrip
 my @list = (
     CustomIsApplicableCode => loc('Custom condition'),
     CustomPrepareCode      => loc('Custom action preparation code'),
-    CustomCommitCode       => loc('Custom action cleanup code'),
+    CustomCommitCode       => loc('Custom action commit code'),
 );
 
 my $min_lines = 10;

commit c5def1dab15e71e94dd99ad5d48d05c989c50a65
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jan 24 00:57:58 2012 +0400

    sort scrips in initialdata by description
    
    this keeps new and upgraded installations on
    the same page

diff --git a/etc/initialdata b/etc/initialdata
index 2c63555..96867e0 100755
--- a/etc/initialdata
+++ b/etc/initialdata
@@ -511,14 +511,30 @@ Hour:         { $SubscriptionObj->SubValue('Hour') }
 );
 
 @Scrips = (
+    {  Description    => 'On Comment Notify AdminCcs as Comment',
+       ScripCondition => 'On Comment',
+       ScripAction    => 'Notify AdminCcs As Comment',
+       Template       => 'Admin Comment' },
+    {  Description    => 'On Comment Notify Other Recipients as Comment',
+       ScripCondition => 'On Comment',
+       ScripAction    => 'Notify Other Recipients As Comment',
+       Template       => 'Correspondence' },
+    {  Description    => 'On Correspond Notify AdminCcs',
+       ScripCondition => 'On Correspond',
+       ScripAction    => 'Notify AdminCcs',
+       Template       => 'Admin Correspondence' },
+    {  Description    => 'On Correspond Notify Other Recipients',
+       ScripCondition => 'On Correspond',
+       ScripAction    => 'Notify Other Recipients',
+       Template       => 'Correspondence' },
+    {  Description    => 'On Correspond Notify Requestors and Ccs',
+       ScripCondition => 'On Correspond',
+       ScripAction    => 'Notify Requestors And Ccs',
+       Template       => 'Correspondence' },
     {  Description    => 'On Correspond Open Tickets',
        ScripCondition => 'On Correspond',
        ScripAction    => 'Open Tickets',
        Template       => 'Blank' },
-    {  Description    => 'On Owner Change Notify Owner',
-       ScripCondition => 'On Owner Change',
-       ScripAction    => 'Notify Owner',
-       Template       => 'Transaction' },
     {  Description    => 'On Create Autoreply To Requestors',
        ScripCondition => 'On Create',
        ScripAction    => 'AutoReply To Requestors',
@@ -527,26 +543,10 @@ Hour:         { $SubscriptionObj->SubValue('Hour') }
        ScripCondition => 'On Create',
        ScripAction    => 'Notify AdminCcs',
        Template       => 'Transaction' },
-    {  Description    => 'On Correspond Notify AdminCcs',
-       ScripCondition => 'On Correspond',
-       ScripAction    => 'Notify AdminCcs',
-       Template       => 'Admin Correspondence' },
-    {  Description    => 'On Correspond Notify Requestors and Ccs',
-       ScripCondition => 'On Correspond',
-       ScripAction    => 'Notify Requestors And Ccs',
-       Template       => 'Correspondence' },
-    {  Description    => 'On Correspond Notify Other Recipients',
-       ScripCondition => 'On Correspond',
-       ScripAction    => 'Notify Other Recipients',
-       Template       => 'Correspondence' },
-    {  Description    => 'On Comment Notify AdminCcs as Comment',
-       ScripCondition => 'On Comment',
-       ScripAction    => 'Notify AdminCcs As Comment',
-       Template       => 'Admin Comment' },
-    {  Description    => 'On Comment Notify Other Recipients as Comment',
-       ScripCondition => 'On Comment',
-       ScripAction    => 'Notify Other Recipients As Comment',
-       Template       => 'Correspondence' },
+    {  Description    => 'On Owner Change Notify Owner',
+       ScripCondition => 'On Owner Change',
+       ScripAction    => 'Notify Owner',
+       Template       => 'Transaction' },
     {  Description    => 'On Resolve Notify Requestors',
        ScripCondition => 'On Resolve',
        ScripAction    => 'Notify Requestors',

commit 64c051b519e9f162a6d353a9c29554a080850cd0
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jan 24 00:59:57 2012 +0400

    fix Scrip's tabs

diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index a4351d2..54dbc4d 100644
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -374,15 +374,16 @@ my $build_admin_menu = sub {
         }
     }
 
-    if ( $request_path =~ m{^/Admin/Global/(Scrip|Template)s?\.html} ) {
-        my $type = $1;
+    if ( $request_path =~ m{^/Admin/Global/Scrips\.html} ) {
         my $tabs = PageMenu();
+        $tabs->child( select => title => loc('Select'), path => "/Admin/Global/Scrips.html" );
+        $tabs->child( create => title => loc('Create'), path => "/Admin/Scrips/Create.html" );
+    }
 
-        # With only two elements, swapping between dropdown and menu is kinda dumb
-        # In the glorious future this should be cleaner.
-
-        $tabs->child( select => title => loc('Select'), path => "/Admin/Global/${type}s.html" );
-        $tabs->child( create => title => loc('Create'), path => "/Admin/Global/${type}.html?Create=1" );
+    if ( $request_path =~ m{^/Admin/Global/Templates?\.html} ) {
+        my $tabs = PageMenu();
+        $tabs->child( select => title => loc('Select'), path => "/Admin/Global/Templates.html" );
+        $tabs->child( create => title => loc('Create'), path => "/Admin/Global/Template.html?Create=1" );
     }
 
     if ( $request_path =~ m{^/Admin/Articles/Classes/} ) {

commit aa4c1b82d17f67d24b989e782dff140ac46791fe
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jan 24 01:00:38 2012 +0400

    fix walk.t, page has been moved

diff --git a/t/web/walk.t b/t/web/walk.t
index 97aa36e..2f72727 100644
--- a/t/web/walk.t
+++ b/t/web/walk.t
@@ -53,7 +53,7 @@ my @links = (
     '/Admin/Groups/Modify.html?id=' . $group->id,
     '/Admin/Queues/Modify.html?id=' . $queue->id,
     '/Admin/CustomFields/Modify.html?id=' . $cf->id,
-    '/Admin/Global/Scrip.html?id=1',
+    '/Admin/Scrips/Modify.html?id=1',
     '/Admin/Global/Template.html?Template=1',
     '/Admin/Articles/Classes/Modify.html?id=' . $class->id,
     '/Search/Build.html?Query=id<10',

commit 6bbf4555de81924f10ebbe3a71160efecdd7d8db
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jan 24 01:22:53 2012 +0400

    fix tests, we changed admin UI for scrips

diff --git a/t/web/scrips.t b/t/web/scrips.t
index 0ff46bf..855029a 100644
--- a/t/web/scrips.t
+++ b/t/web/scrips.t
@@ -49,14 +49,14 @@ sub prepare_code_with_value {
         diag "Create Scrip (Cond #$condition)" if $ENV{TEST_VERBOSE};
         $m->follow_link_ok({id => 'tools-config-global-scrips-create'});
         my $prepare_code = prepare_code_with_value($prepare_code_value);
-        $m->form_name('ModifyScrip');
+        $m->form_name('CreateScrip');
         $m->set_fields(
-            'Scrip-new-ScripCondition'    => $condition,
-            'Scrip-new-ScripAction'       => 15, # User Defined
-            'Scrip-new-Template'          => 1,  # Blank
-            'Scrip-new-CustomPrepareCode' => $prepare_code,
+            'ScripCondition'    => $condition,
+            'ScripAction'       => 15, # User Defined
+            'Template'          => 1,  # Blank
+            'CustomPrepareCode' => $prepare_code,
         );
-        $m->submit;
+        $m->click('Create');
     }
 
     my $ticket_obj = RT::Test->create_ticket(

commit 365fa1f2eb2effb0af1ed189c408933cdc114877
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jan 24 02:03:58 2012 +0400

    we have to pass queue obj as it's expected

diff --git a/share/html/Admin/Scrips/Create.html b/share/html/Admin/Scrips/Create.html
index c301125..07ff3dd 100644
--- a/share/html/Admin/Scrips/Create.html
+++ b/share/html/Admin/Scrips/Create.html
@@ -55,7 +55,7 @@
 <&| /Widgets/TitleBox, title => loc('Basics') &>
 <table>
 
-<& Elements/EditBasics, %ARGS, Scrip => $scrip &>
+<& Elements/EditBasics, %ARGS, Scrip => $scrip, Queue => $queue_obj &>
 
 <tr><td class="label"><&|/l&>Stage</&>:</td><td class="value">\
 <& /Admin/Elements/SelectStage, Default => $ARGS{"Stage"} &></td></tr>

commit 21179382b79d569978d73d8d49a1d799d71ff2ba
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jan 24 02:04:58 2012 +0400

    update tests acording to recent UI changes

diff --git a/t/ticket/scrips_batch.t b/t/ticket/scrips_batch.t
index a6d22b5..625eabe 100644
--- a/t/ticket/scrips_batch.t
+++ b/t/ticket/scrips_batch.t
@@ -19,24 +19,24 @@ my $sid;
     $m->follow_link_ok( { id => 'tools-config-queues' } );
     $m->follow_link_ok( { text => $queue->Name } );
     $m->follow_link_ok( { id => 'page-scrips-create'});
-    $m->form_name('ModifyScrip');
-    $m->field('Scrip-new-Description' => 'test');
-    $m->select('Scrip-new-ScripCondition' => 'On Transaction');
-    $m->select('Scrip-new-ScripAction' => 'User Defined');
-    $m->select('Scrip-new-Template' => 'Global template: Blank');
-    $m->select('Scrip-new-Stage' => 'TransactionBatch');
-    $m->field('Scrip-new-CustomPrepareCode' => 'return 1;');
-    $m->field('Scrip-new-CustomCommitCode' => 'return 1;');
-    $m->submit;
-    $m->content_contains("Scrip Created");
 
+    $m->form_name('CreateScrip');
+    $m->field('Description' => 'test');
+    $m->select('ScripCondition' => 'On Transaction');
+    $m->select('ScripAction' => 'User Defined');
+    $m->select('Template' => 'Blank');
+    $m->select('Stage' => 'Batch');
+    $m->field('CustomPrepareCode' => 'return 1;');
+    $m->field('CustomCommitCode' => 'return 1;');
+    $m->click('Create');
+    $m->content_contains("Scrip Created");
 
     my $form = $m->form_name('ModifyScrip');
     $sid = $form->value('id');
-    is $m->value("Scrip-$sid-Description"), 'test', 'correct description';
-    is value_name($form, "Scrip-$sid-ScripCondition"), 'On Transaction', 'correct condition';
-    is value_name($form, "Scrip-$sid-ScripAction"), 'User Defined', 'correct action';
-    is value_name($form, "Scrip-$sid-Template"), 'Global template: Blank', 'correct template';
+    is $m->value("Description"), 'test', 'correct description';
+    is value_name($form, "ScripCondition"), 'On Transaction', 'correct condition';
+    is value_name($form, "ScripAction"), 'User Defined', 'correct action';
+    is value_name($form, "Template"), 'Blank', 'correct template';
 
     {
         my $rec = RT::ObjectScrip->new( RT->SystemUser );
@@ -61,8 +61,8 @@ foreach my \$txn ( \@\$batch ) {
 return 1;
 END
 
-    $m->field( "Scrip-$sid-CustomCommitCode" => $code );
-    $m->submit;
+    $m->field( "CustomCommitCode" => $code );
+    $m->click('Update');
 
     $m->goto_create_ticket( $queue );
     $m->form_name('TicketCreate');

commit 928bc2240de2311a541a386f30b8bd695c05f19a
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jan 24 20:28:57 2012 +0400

    test basics of administrating scrips

diff --git a/lib/RT/Test/Web.pm b/lib/RT/Test/Web.pm
index 8fae537..062626e 100644
--- a/lib/RT/Test/Web.pm
+++ b/lib/RT/Test/Web.pm
@@ -355,6 +355,24 @@ sub custom_field_input {
     return $res;
 }
 
+sub value_name {
+    my $self = shift;
+    my $field = shift;
+
+    my $input = $self->current_form->find_input( $field )
+        or return undef;
+
+    my @names = $input->value_names;
+    return $input->value unless @names;
+
+    my @values = $input->possible_values;
+    for ( my $i = 0; $i < @values; $i++ ) {
+        return $names[ $i ] if $values[ $i ] eq $input->value;
+    }
+    return undef;
+}
+
+
 sub check_links {
     my $self = shift;
     my %args = @_;
diff --git a/t/web/scrips.t b/t/web/scrips.t
index 855029a..a9f8e7d 100644
--- a/t/web/scrips.t
+++ b/t/web/scrips.t
@@ -1,7 +1,7 @@
 use strict;
 use warnings;
 
-use RT::Test tests => 14;
+use RT::Test tests => 41;
 
 # TODO:
 # Test the rest of the conditions.
@@ -101,3 +101,51 @@ sub prepare_code_with_value {
 
     RT::Test->clean_caught_mails;
 }
+
+note "check basics in scrip's admin interface";
+{
+    $m->follow_link_ok( { id => 'tools-config-global-scrips-create' } );
+    ok $m->form_name('CreateScrip');
+    is $m->value_name('Description'), '', 'empty value';
+    is $m->value_name('ScripAction'), '-', 'empty value';
+    is $m->value_name('ScripCondition'), '-', 'empty value';
+    is $m->value_name('Template'), '-', 'empty value';
+    $m->field('Description' => 'test');
+    $m->click('Create');
+    $m->content_contains("Action is mandatory argument");
+
+    ok $m->form_name('CreateScrip');
+    is $m->value_name('Description'), 'test', 'value stays on the page';
+    $m->select('ScripAction' => 'Notify Ccs');
+    $m->click('Create');
+    $m->content_contains("Template is mandatory argument");
+
+    ok $m->form_name('CreateScrip');
+    is $m->value_name('Description'), 'test', 'value stays on the page';
+    is $m->value_name('ScripAction'), 'Notify Ccs', 'value stays on the page';
+    $m->select('Template' => 'Blank');
+    $m->click('Create');
+    $m->content_contains("Condition is mandatory argument");
+
+    ok $m->form_name('CreateScrip');
+    is $m->value_name('Description'), 'test', 'value stays on the page';
+    is $m->value_name('ScripAction'), 'Notify Ccs', 'value stays on the page';
+    $m->select('ScripCondition' => 'On Close');
+    $m->click('Create');
+    $m->content_contains("Scrip Created");
+
+    ok $m->form_name('ModifyScrip');
+    is $m->value_name('Description'), 'test', 'correct value';
+    is $m->value_name('ScripCondition'), 'On Close', 'correct value';
+    is $m->value_name('ScripAction'), 'Notify Ccs', 'correct value';
+    is $m->value_name('Template'), 'Blank', 'correct value';
+    $m->field('Description' => 'test test');
+    $m->click('Update');
+    # regression
+    $m->content_lacks("Template is mandatory argument");
+
+    ok $m->form_name('ModifyScrip');
+    is $m->value_name('Description'), 'test test', 'correct value';
+    $m->content_contains("Description changed from", "found action result message");
+}
+

commit ea83bcb27e1913dcaad74a8621a0c334a4187dcf
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jan 24 20:32:08 2012 +0400

    treat empty value as no update

diff --git a/share/html/Admin/Scrips/Modify.html b/share/html/Admin/Scrips/Modify.html
index e6766c8..0672d22 100644
--- a/share/html/Admin/Scrips/Modify.html
+++ b/share/html/Admin/Scrips/Modify.html
@@ -112,9 +112,10 @@ $added_to_any = 1 if defined $disabled;
 if ( $Update ) {
     my @attribs = qw(
         Description
-        ScripAction ScripCondition Template
         CustomPrepareCode CustomCommitCode CustomIsApplicableCode
     );
+    push @attribs, grep defined $ARGS{$_} && length $ARGS{$_},
+        qw(ScripAction ScripCondition Template);
     if ( $ARGS{'SetEnabled'} && $added_to_any ) {
         push @attribs, 'Disabled';
         $ARGS{"Disabled"} = $ARGS{"Enabled"}? 0 : 1;

commit 7e38749a32f73470fa056528429fcbc9334baab0
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jan 24 22:42:55 2012 +0400

    make sure limiting by stage works with OID limit

diff --git a/lib/RT/SearchBuilder/ApplyAndSort.pm b/lib/RT/SearchBuilder/ApplyAndSort.pm
index 49d65f9..2b3d4b8 100644
--- a/lib/RT/SearchBuilder/ApplyAndSort.pm
+++ b/lib/RT/SearchBuilder/ApplyAndSort.pm
@@ -102,6 +102,7 @@ sub LimitTargetToNotApplied {
         OPERATOR => 'IS',
         VALUE    => 'NULL',
     );
+    return $alias;
 }
 
 =head2 LimitTargetToApplied
@@ -125,6 +126,7 @@ sub LimitTargetToApplied {
         OPERATOR => 'IS NOT',
         VALUE    => 'NULL',
     );
+    return $alias;
 }
 
 sub JoinTargetToApplied {
@@ -132,7 +134,7 @@ sub JoinTargetToApplied {
     my $collection = shift;
     my @ids = @_;
 
-    my $alias = $self->JoinTargetToThis( $collection, New => 1, Left => 1 );
+    my $alias = $self->JoinTargetToThis( $collection, New => 0, Left => 1 );
     return $alias unless @ids;
 
     # XXX: we need different EA in join clause, but DBIx::SB
diff --git a/share/html/Admin/Scrips/Objects.html b/share/html/Admin/Scrips/Objects.html
index 5c900b0..8d67804 100644
--- a/share/html/Admin/Scrips/Objects.html
+++ b/share/html/Admin/Scrips/Objects.html
@@ -49,7 +49,7 @@
 <& /Elements/Tabs &>
 <& /Elements/ListActions &>
 
-<form action="Objects.html" method="post">
+<form action="Objects.html" method="post" name="AddRemoveScrip">
 <input type="hidden" class="hidden" name="id" value="<% $id %>" />
 
 % if ( $is_global ) {
diff --git a/t/web/scrips.t b/t/web/scrips.t
index a9f8e7d..0f745cd 100644
--- a/t/web/scrips.t
+++ b/t/web/scrips.t
@@ -1,7 +1,9 @@
 use strict;
 use warnings;
 
-use RT::Test tests => 41;
+use RT::Test tests => 54;
+
+RT->Config->Set( UseTransactionBatch => 1 );
 
 # TODO:
 # Test the rest of the conditions.
@@ -149,3 +151,38 @@ note "check basics in scrip's admin interface";
     $m->content_contains("Description changed from", "found action result message");
 }
 
+note "apply scrip in different stage to different queues";
+{
+    my $queue = RT::Test->load_or_create_queue( Name => 'Regression' );
+    ok $queue && $queue->id, 'loaded or created queue';
+
+    $m->follow_link_ok( { id => 'tools-config-queues' } );
+    $m->follow_link_ok( { text => 'General' } );
+    $m->follow_link_ok( { id => 'page-scrips-create'});
+
+    ok $m->form_name('CreateScrip');
+    $m->field('Description' => 'test stage');
+    $m->select('ScripCondition' => 'On Close');
+    $m->select('ScripAction' => 'Notify Ccs');
+    $m->select('Template' => 'Blank');
+    $m->click('Create');
+    $m->content_contains("Scrip Created");
+
+    my ($sid) = ($m->content =~ /Modify scrip #(\d+)/);
+    ok $sid, "found scrip id on the page";
+
+    $m->follow_link_ok({ text => 'Added to' });
+    ok $m->form_name('AddRemoveScrip');
+    $m->select('Stage' => 'Batch');
+    $m->tick( "AddScrip-$sid" => $queue->id );
+    $m->click('Update');
+    $m->content_contains("Object created");
+
+    $m->follow_link_ok({ text => 'General' });
+    $m->follow_link_ok({ id => 'page-scrips' });
+
+    my (@matches) = $m->content =~ /test stage/g;
+    # regression
+    is scalar @matches, 1, 'scrip mentioned only once';
+}
+

commit e54124166ed5ae35a54a04a8cf74c13317934e9e
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Jan 25 01:10:18 2012 +0400

    account scrip and queue if both passed in templates selector
    
    Further limits choice of templates and would be handy if
    next step is to apply the scrip to the queue.

diff --git a/share/html/Admin/Scrips/Elements/SelectTemplate b/share/html/Admin/Scrips/Elements/SelectTemplate
index 1148e42..3bcb212 100644
--- a/share/html/Admin/Scrips/Elements/SelectTemplate
+++ b/share/html/Admin/Scrips/Elements/SelectTemplate
@@ -72,12 +72,8 @@ my %global;
 $global{ lc $_ } = $_ foreach map $_->Name, @{ $global->ItemsArrayRef };
 
 my @queues;
-if ( $Scrip && $Scrip->id ) {
-    @queues = @{ $Scrip->AddedTo->ItemsArrayRef };
-}
-elsif ( $Queue && $Queue->id ) {
-    @queues = ($Queue);
-}
+push @queues, @{ $Scrip->AddedTo->ItemsArrayRef } if $Scrip && $Scrip->id;
+push @queues, $Queue if $Queue && $Queue->id;
 
 my (%names, %counters);
 foreach my $queue ( @queues ) {

commit 4a38b2af56ed19473fc2ae42d588b74808391353
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Tue Jan 31 00:45:05 2012 +0400

    upgrade script for scrips on mysql

diff --git a/etc/upgrade/4.1.1/content b/etc/upgrade/4.1.1/content
new file mode 100644
index 0000000..02d1321
--- /dev/null
+++ b/etc/upgrade/4.1.1/content
@@ -0,0 +1,35 @@
+use strict; use warnings;
+
+our @Initial = (
+    sub {
+        require RT::ObjectScrips;
+        foreach my $stage ('TransactionCreate', 'TransactionBatch') {
+            my $applications = RT::ObjectScrips->new( RT->SystemUser );
+            $applications->Limit( FIELD => 'Stage', VALUE => $stage );
+            my $alias = $applications->Join(
+                FIELD1 => 'Scrip',
+                TABLE2 => 'Scrips', FIELD2 => 'id'
+            );
+            $applications->OrderByCols(
+                { ALIAS => $alias, FIELD => 'Description', ORDER => 'ASC' },
+            );
+            my %h; my $top_so = $h{0} = 0;
+            while ( my $record = $applications->Next ) {
+                my $oid = $record->ObjectId || 0;
+
+                my $so;
+                unless ( $oid ) {
+                    %h = (); $h{0} = $so = ++$top_so;
+                }
+                else {
+                    $so = $h{ $oid } = ($h{$oid}||$h{0}) + 1;
+                }
+                next if $record->SortOrder == $so;
+
+                my ($status, $msg) = $record->SetSortOrder($so);
+                RT->Logger->error("Couldn't set sort order: $msg")
+                    unless $status;
+            }
+        }
+    },
+);
diff --git a/etc/upgrade/4.1.1/schema.mysql b/etc/upgrade/4.1.1/schema.mysql
new file mode 100644
index 0000000..c9061a7
--- /dev/null
+++ b/etc/upgrade/4.1.1/schema.mysql
@@ -0,0 +1,38 @@
+CREATE TABLE ObjectScrips (
+  id INTEGER NOT NULL  AUTO_INCREMENT,
+  Scrip integer NOT NULL  ,
+  Stage varchar(32) CHARACTER SET ascii NOT NULL DEFAULT 'TransactionCreate',
+  ObjectId integer NOT NULL,
+  SortOrder integer NOT NULL DEFAULT 0  ,
+  Disabled int2 NOT NULL DEFAULT 0 ,
+
+  Creator integer NOT NULL DEFAULT 0  ,
+  Created DATETIME NULL  ,
+  LastUpdatedBy integer NOT NULL DEFAULT 0  ,
+  LastUpdated DATETIME NULL  ,
+  PRIMARY KEY (id)
+) ENGINE=InnoDB CHARACTER SET utf8;
+
+INSERT INTO ObjectScrips(
+    Scrip, Stage, ObjectId,
+    Creator, Created, LastUpdatedBy, LastUpdated
+)
+SELECT id, Stage, Queue, Creator, Created, LastUpdatedBy, LastUpdated
+FROM Scrips
+;
+
+UPDATE ObjectScrips SET Disabled = 1 WHERE Scrip IN (
+    SELECT id FROM Scrips WHERE Stage = 'Disabled'
+);
+UPDATE ObjectScrips SET Stage = 'TransactionCreate' WHERE Stage = 'Disabled';
+
+CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
+
+ALTER TABLE Scrips DROP COLUMN Stage;
+ALTER TABLE Scrips DROP COLUMN Queue;
+
+ALTER TABLE ObjectCustomFields ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
+UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
+    SELECT id FROM CustomFields WHERE Disabled != 0
+);
+ALTER TABLE CustomFields DROP COLUMN Disabled;

commit 5818c3cca49ce25bb4d1e01cea811c19d453a541
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Feb 1 14:39:56 2012 +0400

    don't load RT in tests before ::Test module

diff --git a/t/api/scrip.t b/t/api/scrip.t
index a8437f7..4b709f0 100644
--- a/t/api/scrip.t
+++ b/t/api/scrip.t
@@ -1,7 +1,6 @@
 
 use strict;
 use warnings;
-use RT;
 use RT::Test tests => 66;
 
 my $queue = RT::Test->load_or_create_queue( Name => 'General' );
diff --git a/t/api/scrip_order.t b/t/api/scrip_order.t
index cf5a827..caa6d95 100644
--- a/t/api/scrip_order.t
+++ b/t/api/scrip_order.t
@@ -2,7 +2,6 @@
 use strict;
 use warnings;
 
-use RT;
 use RT::Test tests => 204;
 
 my $queue = RT::Test->load_or_create_queue( Name => 'General' );

commit 9e4c69cdfd8ab5c519b6a16099e2b4c6d5625ead
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Feb 3 22:42:08 2012 +0400

    use "applies" in the UI
    
    Apply is used in scrips' API already, so we use add there,
    but in UI to be similar to CF UI we should use 'applies'.

diff --git a/share/html/Admin/Scrips/Modify.html b/share/html/Admin/Scrips/Modify.html
index 0672d22..c83e27e 100644
--- a/share/html/Admin/Scrips/Modify.html
+++ b/share/html/Admin/Scrips/Modify.html
@@ -58,7 +58,7 @@
 <& Elements/EditBasics, %ARGS, Scrip => $scrip &>
 
 % if ( $added_to_any ) {
-<tr><td class="label"><a href="Objects.html?id=<% $id %>"><&|/l&>Added</&></a>:</td>
+<tr><td class="label"><a href="Objects.html?id=<% $id %>"><&|/l&>Applies to</&></a>:</td>
 <td class="value">\
 % if ( $scrip->IsGlobal ) {
 <a href="<% RT->Config->Get('WebPath') %>/Admin/Global/Scrips.html"><% loc('Global') %></a>
diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index 54dbc4d..d32e1da 100644
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -370,7 +370,7 @@ my $build_admin_menu = sub {
 
             my $tabs = PageMenu();
             $tabs->child( basics => title => loc('Basics') => path => "/Admin/Scrips/Modify.html?id=".$id );
-            $tabs->child( 'added-to' => title => loc('Added to'), path => "/Admin/Scrips/Objects.html?id=" . $id );
+            $tabs->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/Scrips/Objects.html?id=" . $id );
         }
     }
 
diff --git a/t/web/scrips.t b/t/web/scrips.t
index 0f745cd..9667116 100644
--- a/t/web/scrips.t
+++ b/t/web/scrips.t
@@ -171,7 +171,7 @@ note "apply scrip in different stage to different queues";
     my ($sid) = ($m->content =~ /Modify scrip #(\d+)/);
     ok $sid, "found scrip id on the page";
 
-    $m->follow_link_ok({ text => 'Added to' });
+    $m->follow_link_ok({ text => 'Applies to' });
     ok $m->form_name('AddRemoveScrip');
     $m->select('Stage' => 'Batch');
     $m->tick( "AddScrip-$sid" => $queue->id );

commit f95134cb8d3b0a59806f4b2fe41e86c14cb94194
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Feb 4 00:25:50 2012 +0400

    Disabled column map for scrips

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index ea780b6..709766b 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2580,7 +2580,7 @@ Set(%AdminSearchResultFormat,
     Scrips =>
         q{'<a href="__WebPath__/Admin/Scrips/Modify.html?id=__id__">__id__</a>/TITLE:#'}
         .q{,'<a href="__WebPath__/Admin/Scrips/Modify.html?id=__id__">__Description__</a>/TITLE:Description'}
-        .q{, __Condition__, __Action__, __Template__},
+        .q{, __Condition__, __Action__, __Template__, __Disabled__},
 
     Templates =>
         q{'<a href="__WebPath__/__WebRequestPathDir__/Template.html?Queue=__QueueId__&Template=__id__">__id__</a>/TITLE:#'}
diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index d90a536..8dcd00e 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -93,6 +93,16 @@ my $COLUMN_MAP = {
 	attribute => 'Description',
 	value     => sub { return $_[0]->Description() },
     },
+    Disabled => {
+        title     => \' ',
+        value     => sub {
+            my $v = $_[0]->Disabled;
+            return !defined $v
+                ? $_[0]->loc('Not applied')
+                : $v ? $_[0]->loc('Disabled')
+                : $_[0]->loc('Enabled')
+        },
+    },
     RemoveCheckBox => {
         title => sub {
             my $name = 'RemoveScrip';

commit e0d638c06f9d637ec7787c8c4402bfa38c3508cd
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Feb 4 00:31:57 2012 +0400

    make sure disabled scrips are not executed

diff --git a/lib/RT/ObjectScrips.pm b/lib/RT/ObjectScrips.pm
index d8bbf35..b13d868 100644
--- a/lib/RT/ObjectScrips.pm
+++ b/lib/RT/ObjectScrips.pm
@@ -55,6 +55,12 @@ use base 'RT::SearchBuilder::ApplyAndSort';
 use RT::Scrips;
 use RT::ObjectScrip;
 
+sub _Init {
+    my $self = shift;
+    $self->{'with_disabled_column'} = 1;
+    return $self->SUPER::_Init( @_ );
+}
+
 sub Table { 'ObjectScrips'}
 
 sub LimitToScrip {
diff --git a/lib/RT/Scrips.pm b/lib/RT/Scrips.pm
index 5a7dc81..fde846f 100644
--- a/lib/RT/Scrips.pm
+++ b/lib/RT/Scrips.pm
@@ -389,6 +389,7 @@ sub _FindScrips {
 
     $self->LimitToQueue( $self->{'TicketObj'}->QueueObj->Id );
     $self->LimitToGlobal;
+    $self->LimitToEnabled;
     $self->LimitByStage( $args{'Stage'} );
 
     my $ConditionsAlias = $self->NewAlias('ScripConditions');
diff --git a/t/api/scrip.t b/t/api/scrip.t
index 4b709f0..c43fb53 100644
--- a/t/api/scrip.t
+++ b/t/api/scrip.t
@@ -1,7 +1,7 @@
 
 use strict;
 use warnings;
-use RT::Test tests => 66;
+use RT::Test tests => 70;
 
 my $queue = RT::Test->load_or_create_queue( Name => 'General' );
 ok $queue && $queue->id, 'loaded or created queue';
@@ -171,17 +171,39 @@ note 'basic check for disabling scrips';
 {
     my $scrip = RT::Scrip->new(RT->SystemUser);
     my ($status, $msg) = $scrip->Create(
-        Queue          => $queue->Id,
-        ScripAction    => 'User Defined',
-        ScripCondition => 'User Defined',
-        Template       => 'Blank',
+        Queue => $queue->id,
+        ScripCondition => 'On Create',
+        ScripAction => 'User Defined',
+        CustomPrepareCode => 'return 1',
+        CustomCommitCode => '$self->TicketObj->SetPriority("87"); return 1',
+        Template => 'Blank'
     );
     ok($status, "created scrip");
     is($scrip->Disabled, 0, "not disabled");
 
+    {
+        my $ticket = RT::Ticket->new(RT->SystemUser);
+        my ($tid, undef, $msg) = $ticket->Create(
+            Queue => $queue->id,
+            Subject => "test",
+        );
+        ok($tid, "created ticket") or diag "error: $msg";
+        is ($ticket->Priority , '87', "Ticket priority is set right");
+    }
+
     ($status,$msg) = $scrip->SetDisabled(1);
     is($scrip->Disabled, 1, "disabled");
 
+    {
+        my $ticket = RT::Ticket->new(RT->SystemUser);
+        my ($tid, undef, $msg) = $ticket->Create(
+            Queue => $queue->id,
+            Subject => "test",
+        );
+        ok($tid, "created ticket") or diag "error: $msg";
+        isnt ($ticket->Priority , '87', "Ticket priority is set right");
+    }
+
     ($status, $msg) = $scrip->RemoveFromObject( $queue->id );
     ok($status, 'removed scrip from queue');
 

commit bd65b7d998dd3d08ca97c1bfbda1b18cb299695a
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Apr 10 17:41:55 2012 -0400

    Schema upgrades for Postgres

diff --git a/etc/upgrade/4.1.1/acl.Pg b/etc/upgrade/4.1.1/acl.Pg
new file mode 100755
index 0000000..9e8fc0a
--- /dev/null
+++ b/etc/upgrade/4.1.1/acl.Pg
@@ -0,0 +1,31 @@
+
+sub acl {
+    my $dbh = shift;
+
+    my @acls;
+
+    my @tables = qw (
+        objectscrips_id_seq
+        ObjectScrips
+    );
+
+    my $db_user = RT->Config->Get('DatabaseUser');
+
+    my $sequence_right
+        = ( $dbh->{pg_server_version} >= 80200 )
+        ? "USAGE, SELECT, UPDATE"
+        : "SELECT, UPDATE";
+
+    foreach my $table (@tables) {
+        # Tables are upper-case, sequences are lowercase in @tables
+        if ( $table =~ /^[a-z]/ ) {
+            push @acls, "GRANT $sequence_right ON $table TO \"$db_user\";"
+        }
+        else {
+            push @acls, "GRANT SELECT, INSERT, UPDATE, DELETE ON $table TO \"$db_user\";"
+        }
+    }
+    return (@acls);
+}
+
+1;
diff --git a/etc/upgrade/4.1.1/schema.Pg b/etc/upgrade/4.1.1/schema.Pg
new file mode 100644
index 0000000..5865866
--- /dev/null
+++ b/etc/upgrade/4.1.1/schema.Pg
@@ -0,0 +1,41 @@
+CREATE SEQUENCE objectscrips_id_seq;
+
+CREATE TABLE ObjectScrips (
+  id INTEGER DEFAULT nextval('objectscrips_id_seq'),
+  Scrip integer NOT NULL,
+  Stage varchar(32) NOT NULL DEFAULT 'TransactionCreate' ,
+  ObjectId integer NOT NULL,
+  SortOrder integer NOT NULL DEFAULT 0  ,
+  Disabled integer NOT NULL DEFAULT 0 ,
+
+  Creator integer NOT NULL DEFAULT 0  ,
+  Created TIMESTAMP NULL  ,
+  LastUpdatedBy integer NOT NULL DEFAULT 0  ,
+  LastUpdated TIMESTAMP NULL  ,
+  PRIMARY KEY (id)
+
+);
+
+INSERT INTO ObjectScrips(
+    Scrip, Stage, ObjectId,
+    Creator, Created, LastUpdatedBy, LastUpdated
+)
+SELECT id, Stage, Queue, Creator, Created, LastUpdatedBy, LastUpdated
+FROM Scrips
+;
+
+UPDATE ObjectScrips SET Disabled = 1 WHERE Scrip IN (
+    SELECT id FROM Scrips WHERE Stage = 'Disabled'
+);
+UPDATE ObjectScrips SET Stage = 'TransactionCreate' WHERE Stage = 'Disabled';
+
+CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
+
+ALTER TABLE Scrips DROP COLUMN Stage;
+ALTER TABLE Scrips DROP COLUMN Queue;
+
+ALTER TABLE ObjectCustomFields ADD COLUMN Disabled integer NOT NULL DEFAULT 0;
+UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
+    SELECT id FROM CustomFields WHERE Disabled != 0
+);
+ALTER TABLE CustomFields DROP COLUMN Disabled;

commit 1c3f88ac9c1bdede48fb7605c92ff7e1cd64ca1b
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Apr 13 20:06:53 2012 +0400

    schema upgrade for Oracle

diff --git a/etc/upgrade/4.1.1/schema.Oracle b/etc/upgrade/4.1.1/schema.Oracle
new file mode 100644
index 0000000..097adbf
--- /dev/null
+++ b/etc/upgrade/4.1.1/schema.Oracle
@@ -0,0 +1,38 @@
+CREATE SEQUENCE OBJECTSCRIPS_seq;
+CREATE TABLE ObjectScrips (
+	id		NUMBER(11,0)
+                 CONSTRAINT ObjectScrips_Key PRIMARY KEY,
+        Scrip       NUMBER(11,0)  NOT NULL,
+	Stage		VARCHAR2(32) DEFAULT 'TransactionCreate' NOT NULL,
+        ObjectId              NUMBER(11,0)  NOT NULL,
+	SortOrder	NUMBER(11,0) DEFAULT 0 NOT NULL,
+	Disabled	NUMBER(11,0) DEFAULT 0 NOT NULL,
+	Creator		NUMBER(11,0) DEFAULT 0 NOT NULL,
+	Created		DATE,
+	LastUpdatedBy	NUMBER(11,0) DEFAULT 0 NOT NULL,
+	LastUpdated	DATE
+);
+
+INSERT INTO ObjectScrips(
+    id, Scrip, Stage, ObjectId,
+    Creator, Created, LastUpdatedBy, LastUpdated
+)
+(SELECT OBJECTSCRIPS_seq.nextval, id, Stage, Queue, Creator, Created, LastUpdatedBy, LastUpdated
+FROM Scrips)
+;
+
+UPDATE ObjectScrips SET Disabled = 1 WHERE Scrip IN (
+    SELECT id FROM Scrips WHERE Stage = 'Disabled'
+);
+UPDATE ObjectScrips SET Stage = 'TransactionCreate' WHERE Stage = 'Disabled';
+
+CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
+
+ALTER TABLE Scrips DROP COLUMN Stage;
+ALTER TABLE Scrips DROP COLUMN Queue;
+
+ALTER TABLE ObjectCustomFields ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
+UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
+    SELECT id FROM CustomFields WHERE Disabled != 0
+);
+ALTER TABLE CustomFields DROP COLUMN Disabled;
\ No newline at end of file

commit 477d515da52c232ab0be5e6054e8ec6e8683aeee
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Apr 13 20:07:08 2012 +0400

    incomplete schema upgrade for SQLite
    
    drop column is special

diff --git a/etc/upgrade/4.1.1/schema.SQLite b/etc/upgrade/4.1.1/schema.SQLite
new file mode 100644
index 0000000..15df601
--- /dev/null
+++ b/etc/upgrade/4.1.1/schema.SQLite
@@ -0,0 +1,39 @@
+
+CREATE TABLE ObjectScrips (
+  id INTEGER NOT NULL  ,
+  Scrip int NOT NULL  ,
+  Stage varchar(32) NOT NULL DEFAULT 'TransactionCreate' ,
+  ObjectId integer NOT NULL,
+  SortOrder integer NOT NULL DEFAULT 0  ,
+  Disabled int2 NOT NULL DEFAULT 0 ,
+
+  Creator integer NOT NULL DEFAULT 0  ,
+  Created DATETIME NULL  ,
+  LastUpdatedBy integer NOT NULL DEFAULT 0  ,
+  LastUpdated DATETIME NULL  ,
+  PRIMARY KEY (id)
+);
+
+INSERT INTO ObjectScrips(
+    Scrip, Stage, ObjectId,
+    Creator, Created, LastUpdatedBy, LastUpdated
+)
+SELECT id, Stage, Queue, Creator, Created, LastUpdatedBy, LastUpdated
+FROM Scrips
+;
+
+UPDATE ObjectScrips SET Disabled = 1 WHERE Scrip IN (
+    SELECT id FROM Scrips WHERE Stage = 'Disabled'
+);
+UPDATE ObjectScrips SET Stage = 'TransactionCreate' WHERE Stage = 'Disabled';
+
+CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
+
+# TODO: ALTER TABLE Scrips DROP COLUMN Stage;
+# TODO: ALTER TABLE Scrips DROP COLUMN Queue;
+
+ALTER TABLE ObjectCustomFields ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
+UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
+    SELECT id FROM CustomFields WHERE Disabled != 0
+);
+# TODO: ALTER TABLE CustomFields DROP COLUMN Disabled;
\ No newline at end of file

commit e6245f34981bb3ea6ff78916599c8dff472b1c07
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Sep 5 17:53:11 2012 +0400

    split upgrade scripts in two parts
    
    one for scrips and one for custom fields. It will help
    upgrade people who run scrips' part as an extension on
    4.0.

diff --git a/etc/upgrade/4.1.1/schema.Oracle b/etc/upgrade/4.1.1/schema.Oracle
index 097adbf..6db5535 100644
--- a/etc/upgrade/4.1.1/schema.Oracle
+++ b/etc/upgrade/4.1.1/schema.Oracle
@@ -29,10 +29,4 @@ UPDATE ObjectScrips SET Stage = 'TransactionCreate' WHERE Stage = 'Disabled';
 CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
 
 ALTER TABLE Scrips DROP COLUMN Stage;
-ALTER TABLE Scrips DROP COLUMN Queue;
-
-ALTER TABLE ObjectCustomFields ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
-UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
-    SELECT id FROM CustomFields WHERE Disabled != 0
-);
-ALTER TABLE CustomFields DROP COLUMN Disabled;
\ No newline at end of file
+ALTER TABLE Scrips DROP COLUMN Queue;
\ No newline at end of file
diff --git a/etc/upgrade/4.1.1/schema.Pg b/etc/upgrade/4.1.1/schema.Pg
index 5865866..2134428 100644
--- a/etc/upgrade/4.1.1/schema.Pg
+++ b/etc/upgrade/4.1.1/schema.Pg
@@ -33,9 +33,3 @@ CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
 
 ALTER TABLE Scrips DROP COLUMN Stage;
 ALTER TABLE Scrips DROP COLUMN Queue;
-
-ALTER TABLE ObjectCustomFields ADD COLUMN Disabled integer NOT NULL DEFAULT 0;
-UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
-    SELECT id FROM CustomFields WHERE Disabled != 0
-);
-ALTER TABLE CustomFields DROP COLUMN Disabled;
diff --git a/etc/upgrade/4.1.1/schema.SQLite b/etc/upgrade/4.1.1/schema.SQLite
index 15df601..77eccdb 100644
--- a/etc/upgrade/4.1.1/schema.SQLite
+++ b/etc/upgrade/4.1.1/schema.SQLite
@@ -30,10 +30,4 @@ UPDATE ObjectScrips SET Stage = 'TransactionCreate' WHERE Stage = 'Disabled';
 CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
 
 # TODO: ALTER TABLE Scrips DROP COLUMN Stage;
-# TODO: ALTER TABLE Scrips DROP COLUMN Queue;
-
-ALTER TABLE ObjectCustomFields ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
-UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
-    SELECT id FROM CustomFields WHERE Disabled != 0
-);
-# TODO: ALTER TABLE CustomFields DROP COLUMN Disabled;
\ No newline at end of file
+# TODO: ALTER TABLE Scrips DROP COLUMN Queue;
\ No newline at end of file
diff --git a/etc/upgrade/4.1.1/schema.mysql b/etc/upgrade/4.1.1/schema.mysql
index c9061a7..fbd6284 100644
--- a/etc/upgrade/4.1.1/schema.mysql
+++ b/etc/upgrade/4.1.1/schema.mysql
@@ -30,9 +30,3 @@ CREATE UNIQUE INDEX ObjectScrips1 ON ObjectScrips (ObjectId, Scrip);
 
 ALTER TABLE Scrips DROP COLUMN Stage;
 ALTER TABLE Scrips DROP COLUMN Queue;
-
-ALTER TABLE ObjectCustomFields ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
-UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
-    SELECT id FROM CustomFields WHERE Disabled != 0
-);
-ALTER TABLE CustomFields DROP COLUMN Disabled;
diff --git a/etc/upgrade/4.1.2/schema.Oracle b/etc/upgrade/4.1.2/schema.Oracle
new file mode 100644
index 0000000..4e173f6
--- /dev/null
+++ b/etc/upgrade/4.1.2/schema.Oracle
@@ -0,0 +1,5 @@
+ALTER TABLE ObjectCustomFields ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
+UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
+    SELECT id FROM CustomFields WHERE Disabled != 0
+);
+ALTER TABLE CustomFields DROP COLUMN Disabled;
\ No newline at end of file
diff --git a/etc/upgrade/4.1.2/schema.Pg b/etc/upgrade/4.1.2/schema.Pg
new file mode 100644
index 0000000..d388dae
--- /dev/null
+++ b/etc/upgrade/4.1.2/schema.Pg
@@ -0,0 +1,5 @@
+ALTER TABLE ObjectCustomFields ADD COLUMN Disabled integer NOT NULL DEFAULT 0;
+UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
+    SELECT id FROM CustomFields WHERE Disabled != 0
+);
+ALTER TABLE CustomFields DROP COLUMN Disabled;
diff --git a/etc/upgrade/4.1.2/schema.SQLite b/etc/upgrade/4.1.2/schema.SQLite
new file mode 100644
index 0000000..02f0367
--- /dev/null
+++ b/etc/upgrade/4.1.2/schema.SQLite
@@ -0,0 +1,5 @@
+ALTER TABLE ObjectCustomFields ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
+UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
+    SELECT id FROM CustomFields WHERE Disabled != 0
+);
+# TODO: ALTER TABLE CustomFields DROP COLUMN Disabled;
\ No newline at end of file
diff --git a/etc/upgrade/4.1.2/schema.mysql b/etc/upgrade/4.1.2/schema.mysql
new file mode 100644
index 0000000..20c9841
--- /dev/null
+++ b/etc/upgrade/4.1.2/schema.mysql
@@ -0,0 +1,5 @@
+ALTER TABLE ObjectCustomFields ADD COLUMN Disabled int2 NOT NULL DEFAULT 0;
+UPDATE ObjectCustomFields SET Disabled = 1 WHERE CustomField IN (
+    SELECT id FROM CustomFields WHERE Disabled != 0
+);
+ALTER TABLE CustomFields DROP COLUMN Disabled;

commit 755e4175229d60e7e0d36e19cc68d6e226b4fe6c
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Sep 6 01:10:10 2012 +0400

    make sure we pass id further, not a string
    
    If caller passes in queue name then we don't complain,
    but later it's INTifyed to zero. We shouldn't let
    this happen.

diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index c52e58b..38d23bc 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -251,6 +251,8 @@ sub AddToObject {
         $queue->Load( $args{'ObjectId'} );
         return (0, $self->loc('Invalid queue'))
             unless $queue->id;
+
+        $args{'ObjectId'} = $queue->id;
     }
     return ( 0, $self->loc('Permission Denied') )
         unless $self->CurrentUser->PrincipalObj->HasRight(

commit fdcd1de2f9262e02413f24d95305c32a7d09c4c5
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Sep 6 01:17:08 2012 +0400

    test return value of Disabled for falseness

diff --git a/t/api/scrip.t b/t/api/scrip.t
index c43fb53..7c9bf4a 100644
--- a/t/api/scrip.t
+++ b/t/api/scrip.t
@@ -207,7 +207,7 @@ note 'basic check for disabling scrips';
     ($status, $msg) = $scrip->RemoveFromObject( $queue->id );
     ok($status, 'removed scrip from queue');
 
-    is($scrip->Disabled, undef, "not applied");
+    ok(!$scrip->Disabled, "not applied");
 }
 
 sub check_applications {

commit d42dba9ef7902ee70efd21a1a9d0150874e0a564
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Sep 6 01:18:50 2012 +0400

    apply scrip to multiple queues from initialdata
    
    old style was to duplicate scrip in each queue, but
    now we create records in ObjectScrips

diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
index 6e9edd5..abc3db2 100644
--- a/lib/RT/Handle.pm
+++ b/lib/RT/Handle.pm
@@ -1062,14 +1062,22 @@ sub InsertData {
             my @queues = ref $item->{'Queue'} eq 'ARRAY'? @{ $item->{'Queue'} }: $item->{'Queue'} || 0;
             push @queues, 0 unless @queues; # add global queue at least
 
+            my ( $return, $msg ) = $new_entry->Create( %$item, Queue => shift @queues );
+            unless ( $return ) {
+                $RT::Logger->error( $msg );
+                next;
+            }
+            else {
+                $RT::Logger->debug( $return ."." );
+            }
             foreach my $q ( @queues ) {
-                my ( $return, $msg ) = $new_entry->Create( %$item, Queue => $q );
-                unless ( $return ) {
-                    $RT::Logger->error( $msg );
-                }
-                else {
-                    $RT::Logger->debug( $return ."." );
-                }
+                my ($return, $msg) = $new_entry->AddToObject(
+                    ObjectId => $q,
+                    Stage    => $item->{'Stage'},
+                    Disabled => $item->{'Disabled'},
+                );
+                $RT::Logger->error( "Couldn't apply scrip to $q: $msg" )
+                    unless $return;
             }
         }
         $RT::Logger->debug("done.");

commit 706c568c0dbb1561a413b6e95d9595d656f048f6
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Fri Sep 7 04:21:20 2012 +0400

    documentation for new ApplyAndSort API

diff --git a/lib/RT/ObjectScrip.pm b/lib/RT/ObjectScrip.pm
index 676354d..10e4500 100644
--- a/lib/RT/ObjectScrip.pm
+++ b/lib/RT/ObjectScrip.pm
@@ -55,9 +55,74 @@ use base 'RT::Record::ApplyAndSort';
 use RT::Scrip;
 use RT::ObjectScrips;
 
+=head1 NAME
+
+RT::ObjectScrip - record representing application of a scrip to a queue
+
+=head1 DESCRIPTION
+
+This record is created if you want to apply a scrip to a queue or globally.
+
+Inherits methods from L<RT::Record::ApplyAndSort>.
+
+For most operations it's better to use methods in L<RT::Scrip>.
+
+=head1 METHODS
+
+=head2 Table
+
+Returns table name for records of this class.
+
+=cut
+
 sub Table {'ObjectScrips'}
+
+=head2 ObjectCollectionClass
+
+Returns class name of collection of records scrips can be applied to.
+Now it's only L<RT::Queue>, so 'RT::Queues' is returned.
+
+=cut
+
 sub ObjectCollectionClass {'RT::Queues'}
 
+=head2 Create
+
+Creates a record, e.g. applies scrip to a queue or globally.
+
+It's better to use L<RT::Scrip/AddToObject> method instead.
+
+Takes:
+
+=over 4
+
+=item Scrip
+
+Scrip object or id.
+
+=item Stage
+
+Stage of the scrip. The same scrip can be run in different stages.
+
+=item ObjectId
+
+Id or an object this scrip should be applied to. For now it's a queue.
+Use 0 to mean globally.
+
+=item Disabled
+
+Boolean indicator whether this new application is active or not. For now
+all applications of the same Scrip should be either disabled or active.
+
+=item Created, Creator, LastUpdated, LastUpdatedBy
+
+Generated automatically from CurrentUser and the current time, but can be
+overriden.
+
+=back
+
+=cut
+
 sub Create {
     my $self = shift;
     my %args = (@_);
@@ -86,6 +151,12 @@ sub ScripObj {
     return $obj;
 }
 
+=head2 Neighbors
+
+Stage splits scrips into neighborhoods. See L<RT::Record::ApplyAndSort/Neighbors and Siblings>.
+
+=cut
+
 sub Neighbors {
     my $self = shift;
     my %args = @_;
diff --git a/lib/RT/ObjectScrips.pm b/lib/RT/ObjectScrips.pm
index b13d868..278c5e2 100644
--- a/lib/RT/ObjectScrips.pm
+++ b/lib/RT/ObjectScrips.pm
@@ -55,14 +55,38 @@ use base 'RT::SearchBuilder::ApplyAndSort';
 use RT::Scrips;
 use RT::ObjectScrip;
 
+=head1 NAME
+
+RT::ObjectScrips - collection of RT::ObjectScrip records
+
+=head1 DESCRIPTION
+
+Collection of L<RT::ObjectScrip> records. Inherits methods from L<RT::SearchBuilder::ApplyAndSort>.
+
+=head1 METHODS
+
+=cut
+
 sub _Init {
     my $self = shift;
     $self->{'with_disabled_column'} = 1;
     return $self->SUPER::_Init( @_ );
 }
 
+=head2 Table
+
+Returns name of the table where records are stored.
+
+=cut
+
 sub Table { 'ObjectScrips'}
 
+=head2 LimitToScrip
+
+Takes id of a L<RT::Scrip> object and limits this collection.
+
+=cut
+
 sub LimitToScrip {
     my $self = shift;
     my $id = shift;
diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/ApplyAndSort.pm
index 9e7d623..a8b5e70 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/ApplyAndSort.pm
@@ -52,11 +52,42 @@ use warnings;
 package RT::Record::ApplyAndSort;
 use base 'RT::Record';
 
+=head1 NAME
+
+RT::Record::ApplyAndSort - base class for records that can be applied and sorted
+
+=head1 DESCRIPTION
+
+Base class for L<RT::ObjectCustomField> and L<RT::ObjectScrip> that unifies
+application of L<RT::CustomField>s and L<RT::Scrip>s to various objects. Also,
+deals with order of the records.
+
+=head1 METHODS
+
+=head2 Meta information
+
+=head3 CollectionClass
+
+Returns class representing collection for this record class. Basicly adds 's'
+at the end. Should be overriden if default doesn't work.
+
+For example returns L<RT::ObjectCustomFields> when called on L<RT::ObjectCustomField>.
+
+=cut
+
 sub CollectionClass {
     return (ref($_[0]) || $_[0]).'s';
 }
 
-sub ObjectCollectionClass { die "should be subclassed" }
+=head3 TargetField
+
+Returns name of the field in the table where id of object we apply is stored.
+By default deletes everything up to '::Object' from class name.
+This method allows to use friendlier argument names and methods.
+
+For example returns 'Scrip' for L<RT::ObjectScrip>.
+
+=cut
 
 sub TargetField {
     my $class = ref($_[0]) || $_[0];
@@ -64,6 +95,42 @@ sub TargetField {
     return $class;
 }
 
+=head3 ObjectCollectionClass
+
+Takes an object under L</TargetField> name and should return class
+name representing collection the object can be applied to.
+
+Must be overriden by sub classes.
+
+See L<RT::ObjectScrip/ObjectCollectionClass> and L<RT::ObjectCustomField/CollectionClass>.
+
+=cut
+
+sub ObjectCollectionClass { die "should be subclassed" }
+
+=head2 Manipulation
+
+=head3 Create
+
+Takes 'ObjectId' with id of an object we apply to, object we apply to under L</TargetField>
+name, Disabled and SortOrder.
+
+This method doesn't create duplicates. If record already exists then it's not created, but
+loaded instead. Note that nothing is updated if record exist.
+
+If SortOrder is not defined then it's calculated to place new record last. If it's
+provided then it's caller's duty to make sure it is correct value.
+
+Example:
+
+    my $ocf = RT::ObjectCustomField->new( RT->SystemUser );
+    my ($id, $msg) = $ocf->Create( CustomField => 1, ObjectId => 0 );
+
+See L</Apply> which has more error checks. Also, L<RT::Scrip> and L<RT::CustomField>
+have more appropriate methods that B<should be> prefered over calling this directly.
+
+=cut
+
 sub Create {
     my $self = shift;
     my %args = (
@@ -101,6 +168,15 @@ sub Create {
     );
 }
 
+=head3 Apply
+
+Helper method that wraps L</Create> and does more checks to make sure result
+is consistent. Doesn't allow to apply a record to an object if the record
+is already global. Un-applies record from particular objects when asked
+to apply the record globally.
+
+=cut
+
 sub Apply {
     my $self = shift;
     my %args = (@_);
@@ -133,98 +209,12 @@ sub Apply {
     );
 }
 
-sub IsApplied {
-    my $self = shift;
-    my ($tid, $oid) = @_;
-    my $record = $self->new( $self->CurrentUser );
-    $record->LoadByCols( $self->TargetField => $tid, ObjectId => $oid );
-    return $record->id;
-}
-
-=head1 AppliedTo
-
-Returns collection with objects this custom field is applied to.
-Class of the collection depends on L</LookupType>.
-See all L</NotAppliedTo> .
+=head3 Delete
 
-Doesn't takes into account if object is applied globally.
+Deletes this record.
 
 =cut
 
-sub AppliedTo {
-    my $self = shift;
-
-    my ($res, $alias) = $self->_AppliedTo( @_ );
-    return $res unless $res;
-
-    $res->Limit(
-        ALIAS     => $alias,
-        FIELD     => 'id',
-        OPERATOR  => 'IS NOT',
-        VALUE     => 'NULL',
-    );
-
-    return $res;
-}
-
-=head1 NotAppliedTo
-
-Returns collection with objects this custom field is not applied to.
-Class of the collection depends on L</LookupType>.
-See all L</AppliedTo> .
-
-Doesn't takes into account if object is applied globally.
-
-=cut
-
-sub NotAppliedTo {
-    my $self = shift;
-
-    my ($res, $alias) = $self->_AppliedTo( @_ );
-    return $res unless $res;
-
-    $res->Limit(
-        ALIAS     => $alias,
-        FIELD     => 'id',
-        OPERATOR  => 'IS',
-        VALUE     => 'NULL',
-    );
-
-    return $res;
-}
-
-sub _AppliedTo {
-    my $self = shift;
-    my %args = (@_);
-
-    my $field = $self->TargetField;
-    my $target = $args{ $field } || $self->TargetObj;
-
-    my ($class) = $self->ObjectCollectionClass( $field => $target );
-    return undef unless $class;
-
-    my $res = $class->new( $self->CurrentUser );
-
-    # If target applied to a Group, only display user-defined groups
-    $res->LimitToUserDefinedGroups if $class eq 'RT::Groups';
-
-    $res->OrderBy( FIELD => 'Name' );
-    my $alias = $res->Join(
-        TYPE   => 'LEFT',
-        ALIAS1 => 'main',
-        FIELD1 => 'id',
-        TABLE2 => $self->Table,
-        FIELD2 => 'ObjectId',
-    );
-    $res->Limit(
-        LEFTJOIN => $alias,
-        ALIAS    => $alias,
-        FIELD    => $field,
-        VALUE    => $target->id,
-    );
-    return ($res, $alias);
-}
-
 sub Delete {
     my $self = shift;
 
@@ -241,6 +231,17 @@ sub Delete {
     return $self->SUPER::Delete;
 }
 
+=head3 DeleteAll
+
+Helper method to delete all applications for one target (Scrip, CustomField, ...).
+Target can be provided in arguments. If it's not then L</TargetObj> is used.
+
+    $object_scrip->DeleteAll;
+
+    $object_scrip->DeleteAll( Scrip => $scrip );
+
+=cut
+
 sub DeleteAll {
     my $self = shift;
     my %args = (@_);
@@ -256,6 +257,12 @@ sub DeleteAll {
     $_->Delete foreach @{ $list->ItemsArrayRef };
 }
 
+=head3 SetDisabledOnAll
+
+Like L</DeleteAll>, but changes Disabled field on all records.
+
+=cut
+
 sub SetDisabledOnAll {
     my $self = shift;
     my %args = (@_);
@@ -280,22 +287,17 @@ sub SetDisabledOnAll {
     return (1, $self->loc("Disabled all applications") );
 }
 
-=head2 Sorting scrips applications
-
-scrips sorted on multiple layers. First of all custom
-fields with different lookup type are sorted independently. All
-global scrips have fixed order for all objects, but you
-can insert object specific scrips between them. Object
-specific scrips can be applied to several objects and
-be on different place. For example you have GCF1, GCF2, LCF1,
-LCF2 and LCF3 that applies to tickets. You can place GCF2
-above GCF1, but they will be in the same order in all queues.
-However, LCF1 and other local can be placed at any place
-for particular queue: above global, between them or below.
+sub IsApplied {
+    my $self = shift;
+    my ($tid, $oid) = @_;
+    my $record = $self->new( $self->CurrentUser );
+    $record->LoadByCols( $self->TargetField => $tid, ObjectId => $oid );
+    return $record->id;
+}
 
 =head3 MoveUp
 
-Moves scrip up. See </Sorting scrips applications>.
+Moves record up.
 
 =cut
 
@@ -303,12 +305,18 @@ sub MoveUp { return shift->Move( Up => @_ ) }
 
 =head3 MoveDown
 
-Moves scrip down. See </Sorting scrips applications>.
+Moves record down.
 
 =cut
 
 sub MoveDown { return shift->Move( Down => @_ ) }
 
+=head3 Move
+
+Takes 'up' or 'down'. One method that implements L</MoveUp> and L</MoveDown>.
+
+=cut
+
 sub Move {
     my $self = shift;
     my $dir = lc(shift || 'up');
@@ -401,6 +409,117 @@ sub Move {
     return (1,"Moved");
 }
 
+=head2 Accessors, instrospection and traversing.
+
+=head3 TargetObj
+
+Returns target object of this record. Returns L<RT::Scrip> object for
+L<RT::ObjectScrip>.
+
+=cut
+
+sub TargetObj {
+    my $self = shift;
+    my $id   = shift;
+
+    my $method = $self->TargetField .'Obj';
+    return $self->$method( $id );
+}
+
+=head3 AppliedTo
+
+Returns collection with objects target of this record is applied to.
+Class of the collection depends on L</ObjectCollectionClass>.
+See all L</NotAppliedTo>.
+
+For example returns L<RT::Queues> collection if the target is L<RT::Scrip>.
+
+Returns empty collection if target is applied globally.
+
+=cut
+
+sub AppliedTo {
+    my $self = shift;
+
+    my ($res, $alias) = $self->_AppliedTo( @_ );
+    return $res unless $res;
+
+    $res->Limit(
+        ALIAS     => $alias,
+        FIELD     => 'id',
+        OPERATOR  => 'IS NOT',
+        VALUE     => 'NULL',
+    );
+
+    return $res;
+}
+
+=head3 NotAppliedTo
+
+Returns collection with objects target of this record is not applied to.
+Class of the collection depends on L</ObjectCollectionClass>.
+See all L</AppliedTo>.
+
+Returns empty collection if target is applied globally.
+
+=cut
+
+sub NotAppliedTo {
+    my $self = shift;
+
+    my ($res, $alias) = $self->_AppliedTo( @_ );
+    return $res unless $res;
+
+    $res->Limit(
+        ALIAS     => $alias,
+        FIELD     => 'id',
+        OPERATOR  => 'IS',
+        VALUE     => 'NULL',
+    );
+
+    return $res;
+}
+
+sub _AppliedTo {
+    my $self = shift;
+    my %args = (@_);
+
+    my $field = $self->TargetField;
+    my $target = $args{ $field } || $self->TargetObj;
+
+    my ($class) = $self->ObjectCollectionClass( $field => $target );
+    return undef unless $class;
+
+    my $res = $class->new( $self->CurrentUser );
+
+    # If target applied to a Group, only display user-defined groups
+    $res->LimitToUserDefinedGroups if $class eq 'RT::Groups';
+
+    $res->OrderBy( FIELD => 'Name' );
+    my $alias = $res->Join(
+        TYPE   => 'LEFT',
+        ALIAS1 => 'main',
+        FIELD1 => 'id',
+        TABLE2 => $self->Table,
+        FIELD2 => 'ObjectId',
+    );
+    $res->Limit(
+        LEFTJOIN => $alias,
+        ALIAS    => $alias,
+        FIELD    => $field,
+        VALUE    => $target->id,
+    );
+    return ($res, $alias);
+}
+
+=head3 NextSortOrder
+
+Returns next available SortOrder value in the L<neighborhood|/Neighbors>.
+Pass arguments to L</Neighbors> and can take optional ObjectId argument,
+calls ObjectId if it's not provided.
+
+=cut
+
 sub NextSortOrder {
     my $self = shift;
     my %args = (@_);
@@ -421,6 +540,12 @@ sub NextSortOrder {
     return $first->SortOrder + 1;
 }
 
+=head3 IsSortOrderShared
+
+Returns true if this record shares SortOrder value with a L<neighbor|/Neighbors>.
+
+=cut
+
 sub IsSortOrderShared {
     my $self = shift;
     return 0 unless $self->ObjectId;
@@ -431,19 +556,82 @@ sub IsSortOrderShared {
     return $neighbors->Count;
 }
 
-sub TargetObj {
-    my $self = shift;
-    my $id   = shift;
+=head2 Neighbors and Siblings
 
-    my $method = $self->TargetField .'Obj';
-    return $self->$method( $id );
-}
+These two methods should only be understood by developers who wants
+to implement new classes of records that can be applied to other records
+and sorted.
+
+Main purpose is to maintain SortOrder values.
+
+Let's take a look at custom fields. A custom field can be created for tickets,
+queues, transactions, users... Custom fields created for tickets can
+be applied globally or to particular set of queues. Custom fields for
+tickets are neighbors. Neighbor custom fields applied to the same objects
+are siblings. Custom field applied globally is sibling to all neighbors.
+
+For scrips Stage defines neighborhood.
+
+Let's look at the three scrips in create stage S1, S2 and S3, queues Q1 and Q2 and
+G for global.
+
+    S1 at Q1, S3 at Q2 0
+    S2 at G         1
+    S1 at Q2        2
+
+Above table says that S2 is applied globally, S1 is applied to Q1 and executed
+before S2 in this queue, also S1 is applied to Q1, but exectued after S2 in this
+queue, S3 is only applied to Q2 and executed before S2 and S1.
+
+Siblings are scrips applied to an object including globally applied or only
+globally applied. In our example there are three different collection
+of siblings: (S2) - global, (S1, S2) for Q1, (S3, S2, S1) for Q2.
+
+Sort order can be shared between neighbors, but can not be shared between siblings.
+
+Here is what happens with sort order if we move S1 at Q2 one position up:
+
+           S3 at Q2 0
+    S1 at Q1, S1 at Q2 1
+    S2 at G         2
+
+One position more:
+
+           S1 at Q2 0
+    S1 at Q1, S3 at Q2 1
+    S2 at G         2
+
+Hopefuly it's enough to understand how it works.
+
+Targets from different neighborhood can not be sorted against each other.
+
+=head3 Neighbors
+
+Returns collection of records of this class with all
+neighbors. By default all possible targets are neighbors.
+
+Takes the same arguments as L</Create> method. If arguments are not passed
+then uses the current record.
+
+See L</Neighbors and Siblings> for detailed description.
+
+See L<RT::ObjectCustomField/Neighbors> for example.
+
+=cut
 
 sub Neighbors {
     my $self = shift;
     return $self->CollectionClass->new( $self->CurrentUser );
 }
 
+=head3 Siblings
+
+Returns collection of records of this class with siblings.
+
+Takes the same arguments as L</Neighbors>. Siblings is subset of L</Neighbors>.
+
+=cut
+
 sub Siblings {
     my $self = shift;
     my %args = @_;
diff --git a/lib/RT/SearchBuilder/ApplyAndSort.pm b/lib/RT/SearchBuilder/ApplyAndSort.pm
index 2b3d4b8..425d394 100644
--- a/lib/RT/SearchBuilder/ApplyAndSort.pm
+++ b/lib/RT/SearchBuilder/ApplyAndSort.pm
@@ -52,11 +52,21 @@ use warnings;
 package RT::SearchBuilder::ApplyAndSort;
 use base 'RT::SearchBuilder';
 
-sub RecordClass {
-    my $class = ref($_[0]) || $_[0];
-    $class =~ s/s$// or return undef;
-    return $class;
-}
+=head1 NAME
+
+RT::SearchBuilder::ApplyAndSort - base class for 'apply and sort' collections
+
+=head1 DESCRIPTION
+
+Base class for collections where records can be applied to objects with order.
+See also L<RT::Record::ApplyAndSort>. Used by L<RT::ObjectScrips> and
+L<RT::ObjectCustomFields>.
+
+As it's about sorting then collection is sorted by SortOrder field.
+
+=head1 METHODS
+
+=cut
 
 sub _Init {
     my $self = shift;
@@ -74,17 +84,53 @@ sub _Init {
     return $self->SUPER::_Init(@_);
 }
 
+=head2 RecordClass
+
+Returns class name of records in this collection. This generic implementation
+just strips trailing 's'.
+
+=cut
+
+sub RecordClass {
+    my $class = ref($_[0]) || $_[0];
+    $class =~ s/s$// or return undef;
+    return $class;
+}
+
+=head2 LimitToObjectId
+
+Takes id of an object and limits collection.
+
+=cut
+
 sub LimitToObjectId {
     my $self = shift;
     my $id = shift || 0;
     $self->Limit( FIELD => 'ObjectId', VALUE => $id );
 }
 
+=head2 NewItem
+
+Returns an empty new collection's item
+
+=cut
+
+sub NewItem {
+    my $self = shift;
+    return $self->RecordClass->new( $self->CurrentUser );
+}
+
+=head1 METHODS FOR TARGETS
+
+Rather than implementing a base class for targets (L<RT::Scrip>,
+L<RT::CustomField>) and its collections. This class provides
+class methods to limit target collections.
+
 =head2 LimitTargetToNotApplied
 
-Takes either list of object ids or nothing. Limits collection
-to custom fields to listed objects or any corespondingly. Use
-zero to mean global.
+Takes a collection object and optional list of object ids. Limits
+the collection to records not applied to listed objects or if
+the list is empty then any object. Use 0 (zero) to mean global.
 
 =cut
 
@@ -107,8 +153,8 @@ sub LimitTargetToNotApplied {
 
 =head2 LimitTargetToApplied
 
-Limits collection to custom fields to listed objects or any corespondingly. Use
-zero to mean global.
+L</LimitTargetToNotApplied> with reverse meaning. Takes the same
+arguments.
 
 =cut
 
@@ -129,6 +175,16 @@ sub LimitTargetToApplied {
     return $alias;
 }
 
+=head2 JoinTargetToApplied
+
+Joins collection to this table using left join, limits joined table
+by ids if those are provided.
+
+Returns alias of the joined table. Join is cached and re-used for
+multiple calls.
+
+=cut
+
 sub JoinTargetToApplied {
     my $self = shift;
     my $collection = shift;
@@ -152,6 +208,15 @@ sub JoinTargetToApplied {
     return $alias;
 }
 
+=head2 JoinTargetToThis
+
+Joins target collection to this table using TargetField.
+
+Takes New and Left arguments. Use New to avoid caching and re-using
+this join. Use Left to create LEFT JOIN rather than inner.
+
+=cut
+
 sub JoinTargetToThis {
     my $self = shift;
     my $collection = shift;
@@ -173,17 +238,6 @@ sub JoinTargetToThis {
     return $collection->{ $key } = $alias;
 }
 
-=head2 NewItem
-
-Returns an empty new collection's item
-
-=cut
-
-sub NewItem {
-    my $self = shift;
-    return $self->RecordClass->new( $self->CurrentUser );
-}
-
 RT::Base->_ImportOverlays();
 
 1;

commit c8f9adac9c982b43bf61ed98de0f44ed16144ef1
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Sep 8 02:34:11 2012 +0400

    sync Scrip->IsAdded's return value with CustomField's

diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index 38d23bc..514fca3 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -226,7 +226,8 @@ sub IsAdded {
     my $self = shift;
     my $record = RT::ObjectScrip->new( $self->CurrentUser );
     $record->LoadByCols( Scrip => $self->id, ObjectId => shift || 0 );
-    return $record->id;
+    return undef unless $record->id;
+    return $record;
 }
 
 sub AddedTo {

commit 3f9253b97a07c84655f08d87843e900161a130ed
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Sep 8 02:37:04 2012 +0400

    Scrip->IsAddedToAny and CF->IsAppliedToAny methods

diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index 1db0514..355c97a 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -1321,6 +1321,20 @@ sub IsApplied {
     return $ocf;
 }
 
+=head2 IsAppliedToAny
+
+Returns true if custom field is applied to any object.
+
+=cut
+
+sub IsAppliedToAny {
+    my $self = shift;
+    my $id = shift;
+    my $ocf = RT::ObjectCustomField->new( $self->CurrentUser );
+    $ocf->LoadByCols( CustomField => $self->id );
+    return $ocf->id ? 1 : 0;
+}
+
 =head2 AddToObject OBJECT
 
 Add this custom field as a custom field for a single object, such as a queue or group.
diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index 514fca3..a648f51 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -230,6 +230,13 @@ sub IsAdded {
     return $record;
 }
 
+sub IsAddedToAny {
+    my $self = shift;
+    my $record = RT::ObjectScrip->new( $self->CurrentUser );
+    $record->LoadByCols( Scrip => $self->id );
+    return $record->id ? 1 : 0;
+}
+
 sub AddedTo {
     my $self = shift;
     return RT::ObjectScrip->new( $self->CurrentUser )

commit 1686fba144ca675c11cb6a5bb9ea95d1bbd8ca5d
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Sep 12 19:28:55 2012 +0400

    keep return value of Disabled as boolean
    
    don't rely on Disabled to return undef if CF or Scrip
    is not applied. Use a method we have for that.

diff --git a/share/html/Admin/Scrips/Modify.html b/share/html/Admin/Scrips/Modify.html
index c83e27e..54fea72 100644
--- a/share/html/Admin/Scrips/Modify.html
+++ b/share/html/Admin/Scrips/Modify.html
@@ -106,8 +106,7 @@ Abort(loc("Couldn't load scrip #[_1]", $id))
     unless $scrip->id;
 
 my $disabled = $scrip->Disabled;
-my $added_to_any = 0;
-$added_to_any = 1 if defined $disabled;
+my $added_to_any = $scrip->IsAddedToAny;
 
 if ( $Update ) {
     my @attribs = qw(

commit 8786a0ac98598829f5a06c0fc7664eb1dec5385d
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Wed Sep 12 19:32:11 2012 +0400

    CF disabling UI
    
    If CF is not applied to any object then Disabled is not defined
    and con not be set. We hide disabled UI on create and when CF
    is not applied.

diff --git a/share/html/Admin/CustomFields/Modify.html b/share/html/Admin/CustomFields/Modify.html
index 0110b7d..838af10 100644
--- a/share/html/Admin/CustomFields/Modify.html
+++ b/share/html/Admin/CustomFields/Modify.html
@@ -92,6 +92,12 @@
         Default => $CustomFieldObj->LookupType || $LookupType, &>
 </td></tr>
 
+% if ( $CustomFieldObj->id && !$CustomFieldObj->IsAppliedToAny ) {
+<tr><td></td>
+<td><i><&|/l&>Custom field is not added to any object.</&></i>
+</td></tr>
+% }
+
 <tr class="edit_validation"><td class="label"><&|/l&>Validation</&></td>
 <td><& /Widgets/ComboBox,
     Name    => 'Pattern',
@@ -130,11 +136,13 @@
 
 % $m->callback(CallbackName => 'BeforeEnabled', CustomField => $CustomFieldObj, CFvalidations => \@CFvalidations);
 
+% if ( $CustomFieldObj->id && $CustomFieldObj->IsAppliedToAny ) {
 <tr><td class="label"> </td><td>
 <input type="hidden" class="hidden" name="SetEnabled" value="1" />
 <input type="checkbox" class="checkbox" name="Enabled" value="1" <% $EnabledChecked |n%> />
 <&|/l&>Enabled (Unchecking this box disables this custom field)</&>
 </td></tr>
+% }
 
 % $m->callback(CallbackName => 'EndOfTable', CustomField => $CustomFieldObj, CFvalidations => \@CFvalidations);
 

commit 5210e96e5ff2ebe7a65b707c167fb4c4457d8db0
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Thu Sep 20 18:08:47 2012 +0400

    don't use Disabled to check IsAddedToAny

diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index 8dcd00e..9959d67 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -96,11 +96,10 @@ my $COLUMN_MAP = {
     Disabled => {
         title     => \' ',
         value     => sub {
-            my $v = $_[0]->Disabled;
-            return !defined $v
-                ? $_[0]->loc('Not applied')
-                : $v ? $_[0]->loc('Disabled')
-                : $_[0]->loc('Enabled')
+            return
+                $_[0]->IsAddedToAny
+                ? ($_[0]->Disabled ? $_[0]->loc('Disabled') : $_[0]->loc('Enabled') )
+                : $_[0]->loc('Not Applied')
         },
     },
     RemoveCheckBox => {

commit dffe779f2c09c964bf9f0005f9c82f24c32fffa8
Author: Ruslan Zakirov <ruz at bestpractical.com>
Date:   Sat Sep 22 01:08:44 2012 +0400

    rename Apply to Add
    
    Apply and Add were used in CustomField to mean the same thing.
    
    Apply in Scrip is used to run scrip. So we went for Add* for
    scrips.
    
    After review we decided that it's better to unify API to use
    Add term.

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 709766b..75cf296 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2575,7 +2575,7 @@ Set(%AdminSearchResultFormat,
     CustomFields =>
         q{'<a href="__WebPath__/Admin/CustomFields/Modify.html?id=__id__">__id__</a>/TITLE:#'}
         .q{,'<a href="__WebPath__/Admin/CustomFields/Modify.html?id=__id__">__Name__</a>/TITLE:Name'}
-        .q{,__AppliedTo__, __FriendlyType__, __FriendlyPattern__},
+        .q{,__AddedTo__, __FriendlyType__, __FriendlyPattern__},
 
     Scrips =>
         q{'<a href="__WebPath__/Admin/Scrips/Modify.html?id=__id__">__id__</a>/TITLE:#'}
diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index 355c97a..ddabb12 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -873,8 +873,8 @@ sub ACLEquivalenceObjects {
 =head2 ContextObject and SetContextObject
 
 Set or get a context for this object. It can be ticket, queue or another object
-this CF applies to. Used for ACL control, for example SeeCustomField can be granted on
-queue level to allow people to see all fields applied to the queue.
+this CF be added to. Used for ACL control, for example SeeCustomField can be granted on
+queue level to allow people to see all fields added to the queue.
 
 =cut
 
@@ -942,11 +942,11 @@ sub ValidateContextObject {
     my $self = shift;
     my $object = shift;
 
-    return 1 if $self->IsApplied(0);
+    return 1 if $self->IsAdded(0);
 
     # global only custom fields don't have objects
     # that should be used as context objects.
-    return if $self->ApplyGlobally;
+    return if $self->IsOnlyGlobal;
 
     # Otherwise, make sure we weren't passed a user object that we're
     # supposed to treat as a queue.
@@ -955,7 +955,7 @@ sub ValidateContextObject {
     # Check that it is applied correctly
     my ($applied_to) = grep {ref($_) eq $self->RecordClassFromLookupType} ($object, $object->ACLEquivalenceObjects);
     return unless $applied_to;
-    return $self->IsApplied($applied_to->id);
+    return $self->IsAdded($applied_to->id);
 }
 
 
@@ -1257,54 +1257,57 @@ sub CollectionClassFromLookupType {
     return $collection_class;
 }
 
-=head1 ApplyGlobally
+=head1 IsOnlyGlobal
 
-Certain custom fields (users, groups) should only be applied globally
+Certain custom fields (users, groups) should only be added globally
 but rather than regexing in code for LookupType =~ RT::Queue, we'll codify
 the rules here.
 
 =cut
 
-sub ApplyGlobally {
+sub IsOnlyGlobal {
     my $self = shift;
 
     return ($self->LookupType =~ /^RT::(?:Group|User)/io);
 
 }
+sub ApplyGlobally { warn "DEPRECATED, use IsOnlyGlobal"; return shift->IsOnlyGlobal(@_) }
 
-=head1 AppliedTo
+=head1 AddedTo
 
 Returns collection with objects this custom field is applied to.
 Class of the collection depends on L</LookupType>.
-See all L</NotAppliedTo> .
+See all L</NotAddedTo> .
 
 Doesn't takes into account if object is applied globally.
 
 =cut
 
-sub AppliedTo {
+sub AddedTo {
     my $self = shift;
     return RT::ObjectCustomField->new( $self->CurrentUser )
-        ->AppliedTo( CustomField => $self );
+        ->AddedTo( CustomField => $self );
 }
+sub AppliedTo { warn "DEPRECATED: use AddedTo"; shift->AddedTo(@_) };
 
-=head1 NotAppliedTo
+=head1 NotAddedTo
 
 Returns collection with objects this custom field is not applied to.
 Class of the collection depends on L</LookupType>.
-See all L</AppliedTo> .
+See all L</AddedTo> .
 
 Doesn't takes into account if object is applied globally.
 
 =cut
 
-sub NotAppliedTo {
+sub NotAddedTo {
     my $self = shift;
     return RT::ObjectCustomField->new( $self->CurrentUser )
-        ->NotAppliedTo( CustomField => $self );
+        ->NotAddedTo( CustomField => $self );
 }
+sub NotAppliedTo { warn "DEPRECATED: use NotAddedTo"; shift->NotAddedTo(@_) };
 
-=head2 IsApplied
+=head2 IsAdded
 
 Takes object id and returns corresponding L<RT::ObjectCustomField>
 record if this custom field is applied to the object. Use 0 to check
@@ -1312,7 +1315,7 @@ if custom field is applied globally.
 
 =cut
 
-sub IsApplied {
+sub IsAdded {
     my $self = shift;
     my $id = shift;
     my $ocf = RT::ObjectCustomField->new( $self->CurrentUser );
@@ -1320,14 +1323,15 @@ sub IsApplied {
     return undef unless $ocf->id;
     return $ocf;
 }
+sub IsApplied { warn "DEPRECATED: use IsAdded"; shift->IsAdded(@_) };
 
-=head2 IsAppliedToAny
+=head2 IsAddedToAny
 
 Returns true if custom field is applied to any object.
 
 =cut
 
-sub IsAppliedToAny {
+sub IsAddedToAny {
     my $self = shift;
     my $id = shift;
     my $ocf = RT::ObjectCustomField->new( $self->CurrentUser );
@@ -1357,7 +1361,7 @@ sub AddToObject {
     }
 
     my $ocf = RT::ObjectCustomField->new( $self->CurrentUser );
-    my ( $oid, $msg ) = $ocf->Apply(
+    my ( $oid, $msg ) = $ocf->Add(
         CustomField => $self->id, ObjectId => $id,
     );
     return ( $oid, $msg );
@@ -1385,7 +1389,7 @@ sub RemoveFromObject {
         return ( 0, $self->loc('Permission Denied') );
     }
 
-    my $ocf = $self->IsApplied( $id );
+    my $ocf = $self->IsAdded( $id );
     unless ( $ocf ) {
         return ( 0, $self->loc("This custom field does not apply to that object") );
     }
diff --git a/lib/RT/CustomFields.pm b/lib/RT/CustomFields.pm
index 37ca251..5bd281a 100644
--- a/lib/RT/CustomFields.pm
+++ b/lib/RT/CustomFields.pm
@@ -169,7 +169,7 @@ sub LimitToGlobalOrObjectId {
                  ENTRYAGGREGATOR => 'OR' ) unless $global_only;
 }
 
-=head2 LimitToNotApplied
+=head2 LimitToNotAdded
 
 Takes either list of object ids or nothing. Limits collection
 to custom fields to listed objects or any corespondingly. Use
@@ -177,23 +177,23 @@ zero to mean global.
 
 =cut
 
-sub LimitToNotApplied {
+sub LimitToNotAdded {
     my $self = shift;
     return RT::ObjectCustomFields->new( $self->CurrentUser )
-        ->LimitTargetToNotApplied( $self => @_ );
+        ->LimitTargetToNotAdded( $self => @_ );
 }
 
-=head2 LimitToApplied
+=head2 LimitToAdded
 
 Limits collection to custom fields to listed objects or any corespondingly. Use
 zero to mean global.
 
 =cut
 
-sub LimitToApplied {
+sub LimitToAdded {
     my $self = shift;
     return RT::ObjectCustomFields->new( $self->CurrentUser )
-        ->LimitTargetToApplied( $self => @_ );
+        ->LimitTargetToAdded( $self => @_ );
 }
 
 =head2 LimitToGlobalOrQueue QUEUEID
diff --git a/lib/RT/ObjectCustomField.pm b/lib/RT/ObjectCustomField.pm
index 5f838e3..0992cc9 100644
--- a/lib/RT/ObjectCustomField.pm
+++ b/lib/RT/ObjectCustomField.pm
@@ -49,7 +49,7 @@ use strict;
 use warnings;
 
 package RT::ObjectCustomField;
-use base 'RT::Record::ApplyAndSort';
+use base 'RT::Record::AddAndSort';
 
 use RT::CustomField;
 use RT::ObjectCustomFields;
diff --git a/lib/RT/ObjectCustomFields.pm b/lib/RT/ObjectCustomFields.pm
index 72b1894..8fd40b8 100644
--- a/lib/RT/ObjectCustomFields.pm
+++ b/lib/RT/ObjectCustomFields.pm
@@ -49,7 +49,7 @@ use strict;
 use warnings;
 
 package RT::ObjectCustomFields;
-use base 'RT::SearchBuilder::ApplyAndSort';
+use base 'RT::SearchBuilder::AddAndSort';
 
 use RT::CustomField;
 use RT::ObjectCustomField;
diff --git a/lib/RT/ObjectScrip.pm b/lib/RT/ObjectScrip.pm
index 10e4500..f5546b6 100644
--- a/lib/RT/ObjectScrip.pm
+++ b/lib/RT/ObjectScrip.pm
@@ -50,20 +50,20 @@ use strict;
 use warnings;
 
 package RT::ObjectScrip;
-use base 'RT::Record::ApplyAndSort';
+use base 'RT::Record::AddAndSort';
 
 use RT::Scrip;
 use RT::ObjectScrips;
 
 =head1 NAME
 
-RT::ObjectScrip - record representing application of a scrip to a queue
+RT::ObjectScrip - record representing addition of a scrip to a queue
 
 =head1 DESCRIPTION
 
-This record is created if you want to apply a scrip to a queue or globally.
+This record is created when you add a scrip to a queue or globally.
 
-Inherits methods from L<RT::Record::ApplyAndSort>.
+Inherits methods from L<RT::Record::AddAndSort>.
 
 For most operations it's better to use methods in L<RT::Scrip>.
 
@@ -88,7 +88,7 @@ sub ObjectCollectionClass {'RT::Queues'}
 
 =head2 Create
 
-Creates a record, e.g. applies scrip to a queue or globally.
+Creates a record, e.g. adds a scrip to a queue or globally.
 
 It's better to use L<RT::Scrip/AddToObject> method instead.
 
@@ -106,13 +106,13 @@ Stage of the scrip. The same scrip can be run in different stages.
 
 =item ObjectId
 
-Id or an object this scrip should be applied to. For now it's a queue.
+Id or an object this scrip should be added to. For now it's a queue.
 Use 0 to mean globally.
 
 =item Disabled
 
-Boolean indicator whether this new application is active or not. For now
-all applications of the same Scrip should be either disabled or active.
+Boolean indicator whether this new addition is active or not. For now
+all additions of the same Scrip should be either disabled or active.
 
 =item Created, Creator, LastUpdated, LastUpdatedBy
 
@@ -153,7 +153,7 @@ sub ScripObj {
 
 =head2 Neighbors
 
-Stage splits scrips into neighborhoods. See L<RT::Record::ApplyAndSort/Neighbors and Siblings>.
+Stage splits scrips into neighborhoods. See L<RT::Record::AddAndSort/Neighbors and Siblings>.
 
 =cut
 
diff --git a/lib/RT/ObjectScrips.pm b/lib/RT/ObjectScrips.pm
index 278c5e2..7201454 100644
--- a/lib/RT/ObjectScrips.pm
+++ b/lib/RT/ObjectScrips.pm
@@ -50,7 +50,7 @@ use strict;
 use warnings;
 
 package RT::ObjectScrips;
-use base 'RT::SearchBuilder::ApplyAndSort';
+use base 'RT::SearchBuilder::AddAndSort';
 
 use RT::Scrips;
 use RT::ObjectScrip;
@@ -61,7 +61,7 @@ RT::ObjectScrips - collection of RT::ObjectScrip records
 
 =head1 DESCRIPTION
 
-Collection of L<RT::ObjectScrip> records. Inherits methods from L<RT::SearchBuilder::ApplyAndSort>.
+Collection of L<RT::ObjectScrip> records. Inherits methods from L<RT::SearchBuilder::AddAndSort>.
 
 =head1 METHODS
 
diff --git a/lib/RT/Record/ApplyAndSort.pm b/lib/RT/Record/AddAndSort.pm
similarity index 96%
rename from lib/RT/Record/ApplyAndSort.pm
rename to lib/RT/Record/AddAndSort.pm
index a8b5e70..aefe0d5 100644
--- a/lib/RT/Record/ApplyAndSort.pm
+++ b/lib/RT/Record/AddAndSort.pm
@@ -49,12 +49,12 @@
 use strict;
 use warnings;
 
-package RT::Record::ApplyAndSort;
+package RT::Record::AddAndSort;
 use base 'RT::Record';
 
 =head1 NAME
 
-RT::Record::ApplyAndSort - base class for records that can be applied and sorted
+RT::Record::AddAndSort - base class for records that can be applied and sorted
 
 =head1 DESCRIPTION
 
@@ -126,7 +126,7 @@ Example:
     my $ocf = RT::ObjectCustomField->new( RT->SystemUser );
     my ($id, $msg) = $ocf->Create( CustomField => 1, ObjectId => 0 );
 
-See L</Apply> which has more error checks. Also, L<RT::Scrip> and L<RT::CustomField>
+See L</Add> which has more error checks. Also, L<RT::Scrip> and L<RT::CustomField>
 have more appropriate methods that B<should be> prefered over calling this directly.
 
 =cut
@@ -168,7 +168,7 @@ sub Create {
     );
 }
 
-=head3 Apply
+=head3 Add
 
 Helper method that wraps L</Create> and does more checks to make sure result
 is consistent. Doesn't allow to apply a record to an object if the record
@@ -177,7 +177,7 @@ to apply the record globally.
 
 =cut
 
-sub Apply {
+sub Add {
     my $self = shift;
     my %args = (@_);
 
@@ -191,14 +191,14 @@ sub Apply {
     $oid = $oid->id if ref $oid;
     $oid ||= 0;
 
-    if ( $self->IsApplied( $tid => $oid ) ) {
+    if ( $self->IsAdded( $tid => $oid ) ) {
         return ( 0, $self->loc("Is already applied to the object") );
     }
 
     if ( $oid ) {
         # applying locally
         return (0, $self->loc("Couldn't apply as it's global already") )
-            if $self->IsApplied( $tid => 0 );
+            if $self->IsAdded( $tid => 0 );
     }
     else {
         $self->DeleteAll( $field => $tid );
@@ -287,7 +287,7 @@ sub SetDisabledOnAll {
     return (1, $self->loc("Disabled all applications") );
 }
 
-sub IsApplied {
+sub IsAdded {
     my $self = shift;
     my ($tid, $oid) = @_;
     my $record = $self->new( $self->CurrentUser );
@@ -426,11 +426,11 @@ sub TargetObj {
     return $self->$method( $id );
 }
 
-=head3 AppliedTo
+=head3 AddedTo
 
 Returns collection with objects target of this record is applied to.
 Class of the collection depends on L</ObjectCollectionClass>.
-See all L</NotAppliedTo>.
+See all L</NotAddedTo>.
 
 For example returns L<RT::Queues> collection if the target is L<RT::Scrip>.
 
@@ -438,10 +438,10 @@ Returns empty collection if target is applied globally.
 
 =cut
 
-sub AppliedTo {
+sub AddedTo {
     my $self = shift;
 
-    my ($res, $alias) = $self->_AppliedTo( @_ );
+    my ($res, $alias) = $self->_AddedTo( @_ );
     return $res unless $res;
 
     $res->Limit(
@@ -454,20 +454,20 @@ sub AppliedTo {
     return $res;
 }
 
-=head3 NotAppliedTo
+=head3 NotAddedTo
 
 Returns collection with objects target of this record is not applied to.
 Class of the collection depends on L</ObjectCollectionClass>.
-See all L</AppliedTo>.
+See all L</AddedTo>.
 
 Returns empty collection if target is applied globally.
 
 =cut
 
-sub NotAppliedTo {
+sub NotAddedTo {
     my $self = shift;
 
-    my ($res, $alias) = $self->_AppliedTo( @_ );
+    my ($res, $alias) = $self->_AddedTo( @_ );
     return $res unless $res;
 
     $res->Limit(
@@ -480,7 +480,7 @@ sub NotAppliedTo {
     return $res;
 }
 
-sub _AppliedTo {
+sub _AddedTo {
     my $self = shift;
     my %args = (@_);
 
diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index a648f51..49b2dee 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -189,13 +189,13 @@ sub Create {
         $args{'Disabled'} = 1;
     }
 
-    (my $status, $msg) = RT::ObjectScrip->new( $self->CurrentUser )->Apply(
+    (my $status, $msg) = RT::ObjectScrip->new( $self->CurrentUser )->Add(
         Scrip    => $self,
         Stage    => $args{'Stage'},
         ObjectId => $args{'Queue'},
         Disabled => $args{'Disabled'},
     );
-    $RT::Logger->error( "Couldn't apply scrip: $msg" ) unless $status;
+    $RT::Logger->error( "Couldn't add scrip: $msg" ) unless $status;
 
     return ( $id, $self->loc('Scrip Created') );
 }
@@ -240,13 +240,13 @@ sub IsAddedToAny {
 sub AddedTo {
     my $self = shift;
     return RT::ObjectScrip->new( $self->CurrentUser )
-        ->AppliedTo( Scrip => $self );
+        ->AddedTo( Scrip => $self );
 }
 
 sub NotAddedTo {
     my $self = shift;
     return RT::ObjectScrip->new( $self->CurrentUser )
-        ->NotAppliedTo( Scrip => $self );
+        ->NotAddedTo( Scrip => $self );
 }
 
 sub AddToObject {
@@ -281,7 +281,7 @@ sub AddToObject {
     }
 
     my $rec = RT::ObjectScrip->new( $self->CurrentUser );
-    return $rec->Apply( %args, Scrip => $self );
+    return $rec->Add( %args, Scrip => $self );
 }
 
 sub RemoveFromObject {
@@ -303,7 +303,7 @@ sub RemoveFromObject {
 
     my $rec = RT::ObjectScrip->new( $self->CurrentUser );
     $rec->LoadByCols( Scrip => $self->id, ObjectId => $args{'ObjectId'} );
-    return (0, $self->loc('Scrip is not applied') ) unless $rec->id;
+    return (0, $self->loc('Scrip is not added') ) unless $rec->id;
     return $rec->Delete;
 }
 
@@ -379,7 +379,7 @@ sub TemplateObj {
 =head2 Stage
 
 Takes TicketObj named argument and returns scrip's stage when
-applied to ticket's queue.
+added to ticket's queue.
 
 =cut
 
diff --git a/lib/RT/Scrips.pm b/lib/RT/Scrips.pm
index fde846f..8456136 100644
--- a/lib/RT/Scrips.pm
+++ b/lib/RT/Scrips.pm
@@ -117,13 +117,13 @@ sub LimitToGlobal  {
 sub LimitToAdded {
     my $self = shift;
     return RT::ObjectScrips->new( $self->CurrentUser )
-        ->LimitTargetToApplied( $self => @_ );
+        ->LimitTargetToAdded( $self => @_ );
 }
 
 sub LimitToNotAdded {
     my $self = shift;
     return RT::ObjectScrips->new( $self->CurrentUser )
-        ->LimitTargetToNotApplied( $self => @_ );
+        ->LimitTargetToNotAdded( $self => @_ );
 }
 
 sub LimitByStage  {
@@ -142,8 +142,8 @@ sub LimitByStage  {
 
 =head2 LimitToEnabled
 
-Limits scrips to that are applied to any queue or globally
-and application is not disabled.
+Limits scrips to that are added to any queue or globally
+and it is not disabled.
 
 =cut
 
diff --git a/lib/RT/SearchBuilder/ApplyAndSort.pm b/lib/RT/SearchBuilder/AddAndSort.pm
similarity index 91%
rename from lib/RT/SearchBuilder/ApplyAndSort.pm
rename to lib/RT/SearchBuilder/AddAndSort.pm
index 425d394..a3f16f0 100644
--- a/lib/RT/SearchBuilder/ApplyAndSort.pm
+++ b/lib/RT/SearchBuilder/AddAndSort.pm
@@ -49,17 +49,17 @@
 use strict;
 use warnings;
 
-package RT::SearchBuilder::ApplyAndSort;
+package RT::SearchBuilder::AddAndSort;
 use base 'RT::SearchBuilder';
 
 =head1 NAME
 
-RT::SearchBuilder::ApplyAndSort - base class for 'apply and sort' collections
+RT::SearchBuilder::AddAndSort - base class for 'apply and sort' collections
 
 =head1 DESCRIPTION
 
 Base class for collections where records can be applied to objects with order.
-See also L<RT::Record::ApplyAndSort>. Used by L<RT::ObjectScrips> and
+See also L<RT::Record::AddAndSort>. Used by L<RT::ObjectScrips> and
 L<RT::ObjectCustomFields>.
 
 As it's about sorting then collection is sorted by SortOrder field.
@@ -126,7 +126,7 @@ Rather than implementing a base class for targets (L<RT::Scrip>,
 L<RT::CustomField>) and its collections. This class provides
 class methods to limit target collections.
 
-=head2 LimitTargetToNotApplied
+=head2 LimitTargetToNotAdded
 
 Takes a collection object and optional list of object ids. Limits
 the collection to records not applied to listed objects or if
@@ -134,12 +134,12 @@ the list is empty then any object. Use 0 (zero) to mean global.
 
 =cut
 
-sub LimitTargetToNotApplied {
+sub LimitTargetToNotAdded {
     my $self = shift;
     my $collection = shift;
     my @ids = @_;
 
-    my $alias = $self->JoinTargetToApplied($collection => @ids);
+    my $alias = $self->JoinTargetToAdded($collection => @ids);
 
     $collection->Limit(
         ENTRYAGGREGATOR => 'AND',
@@ -151,19 +151,19 @@ sub LimitTargetToNotApplied {
     return $alias;
 }
 
-=head2 LimitTargetToApplied
+=head2 LimitTargetToAdded
 
-L</LimitTargetToNotApplied> with reverse meaning. Takes the same
+L</LimitTargetToNotAdded> with reverse meaning. Takes the same
 arguments.
 
 =cut
 
-sub LimitTargetToApplied {
+sub LimitTargetToAdded {
     my $self = shift;
     my $collection = shift;
     my @ids = @_;
 
-    my $alias = $self->JoinTargetToApplied($collection => @ids);
+    my $alias = $self->JoinTargetToAdded($collection => @ids);
 
     $collection->Limit(
         ENTRYAGGREGATOR => 'AND',
@@ -175,7 +175,7 @@ sub LimitTargetToApplied {
     return $alias;
 }
 
-=head2 JoinTargetToApplied
+=head2 JoinTargetToAdded
 
 Joins collection to this table using left join, limits joined table
 by ids if those are provided.
@@ -185,7 +185,7 @@ multiple calls.
 
 =cut
 
-sub JoinTargetToApplied {
+sub JoinTargetToAdded {
     my $self = shift;
     my $collection = shift;
     my @ids = @_;
diff --git a/share/html/Admin/CustomFields/Modify.html b/share/html/Admin/CustomFields/Modify.html
index 838af10..095702f 100644
--- a/share/html/Admin/CustomFields/Modify.html
+++ b/share/html/Admin/CustomFields/Modify.html
@@ -92,7 +92,7 @@
         Default => $CustomFieldObj->LookupType || $LookupType, &>
 </td></tr>
 
-% if ( $CustomFieldObj->id && !$CustomFieldObj->IsAppliedToAny ) {
+% if ( $CustomFieldObj->id && !$CustomFieldObj->IsAddedToAny ) {
 <tr><td></td>
 <td><i><&|/l&>Custom field is not added to any object.</&></i>
 </td></tr>
@@ -136,7 +136,7 @@
 
 % $m->callback(CallbackName => 'BeforeEnabled', CustomField => $CustomFieldObj, CFvalidations => \@CFvalidations);
 
-% if ( $CustomFieldObj->id && $CustomFieldObj->IsAppliedToAny ) {
+% if ( $CustomFieldObj->id && $CustomFieldObj->IsAddedToAny ) {
 <tr><td class="label"> </td><td>
 <input type="hidden" class="hidden" name="SetEnabled" value="1" />
 <input type="checkbox" class="checkbox" name="Enabled" value="1" <% $EnabledChecked |n%> />
@@ -285,10 +285,10 @@ if ( $CustomFieldObj->id && $CustomFieldObj->LookupType =~ /^RT::(?:User|Group)$
     my ( $ret, $msg );
     my $object = $CustomFieldObj->RecordClassFromLookupType->new( $session{'CurrentUser'} );
 
-    if ( $CustomFieldObj->Disabled && $CustomFieldObj->IsApplied(0) ) {
+    if ( $CustomFieldObj->Disabled && $CustomFieldObj->IsAdded(0) ) {
         ( $ret, $msg ) = $CustomFieldObj->RemoveFromObject($object);
     }
-    elsif ( !$CustomFieldObj->Disabled && !$CustomFieldObj->IsApplied(0) ) {
+    elsif ( !$CustomFieldObj->Disabled && !$CustomFieldObj->IsAdded(0) ) {
         ( $ret, $msg ) = $CustomFieldObj->AddToObject($object);
     }
 
diff --git a/share/html/Admin/CustomFields/Objects.html b/share/html/Admin/CustomFields/Objects.html
index 91dc5dc..050b080 100644
--- a/share/html/Admin/CustomFields/Objects.html
+++ b/share/html/Admin/CustomFields/Objects.html
@@ -63,7 +63,7 @@
 <input type="checkbox" name="AddCustomField-<% $CF->id %>" value="0" />
 <&|/l&>check this box to apply this Custom Field to all objects.</&>
 
-% unless ( $CF->ApplyGlobally ) {
+% unless ( $CF->IsOnlyGlobal ) {
 <h2><&|/l&>Selected objects</&></h2>
 <& /Elements/CollectionList,
     OrderBy => 'id',
@@ -141,10 +141,10 @@ if ( $UpdateObjs ) {
     }
 }
 
-my $is_global = $CF->IsApplied(0);
+my $is_global = $CF->IsAdded(0);
 
-my $applied = $CF->AppliedTo;
-my $not_applied = $CF->NotAppliedTo;
+my $applied = $CF->AddedTo;
+my $not_applied = $CF->NotAddedTo;
 
 my $collection_class = ref($applied);
 $collection_class =~ s/^RT:://;
diff --git a/share/html/Admin/Elements/EditCustomFields b/share/html/Admin/Elements/EditCustomFields
index d9d9134..93fc192 100644
--- a/share/html/Admin/Elements/EditCustomFields
+++ b/share/html/Admin/Elements/EditCustomFields
@@ -160,7 +160,7 @@ $applied_cfs->ApplySortOrder;
 
 my $not_applied_cfs = RT::CustomFields->new( $session{'CurrentUser'} );
 $not_applied_cfs->LimitToLookupType($lookup);
-$not_applied_cfs->LimitToNotApplied( $id ? ($id, 0) : (0) );
+$not_applied_cfs->LimitToNotAdded( $id ? ($id, 0) : (0) );
 
 my $format = RT->Config->Get('AdminSearchResultFormat')->{'CustomFields'};
 
diff --git a/share/html/Admin/Elements/EditScrips b/share/html/Admin/Elements/EditScrips
index b72a645..b422909 100644
--- a/share/html/Admin/Elements/EditScrips
+++ b/share/html/Admin/Elements/EditScrips
@@ -73,7 +73,7 @@
 &>
 
 <h2><&|/l&>Not applied scrips</&></h2>
-% $scrips = $find_scrips->(Applied => 0);
+% $scrips = $find_scrips->(Added => 0);
 <& /Elements/CollectionList,
     Rows => 50,
     Page => 1,
@@ -111,13 +111,13 @@ if ( $id ) {
 $id ||= 0;
 
 my $find_scrips = sub {
-    my %args = (Applied => 1, @_);
+    my %args = (Added => 1, @_);
     my $scrips = RT::Scrips->new($session{'CurrentUser'});
     $scrips->LimitByStage( $args{'Stage'} )
         if $args{'Stage'};
-    my $method = $args{'Applied'}? 'LimitToAdded' : 'LimitToNotAdded';
+    my $method = $args{'Added'}? 'LimitToAdded' : 'LimitToNotAdded';
     $scrips->$method(0, $id);
-    $scrips->ApplySortOrder if $args{'Applied'};
+    $scrips->ApplySortOrder if $args{'Added'};
     return $scrips;
 };
 
diff --git a/share/html/Elements/RT__CustomField/ColumnMap b/share/html/Elements/RT__CustomField/ColumnMap
index b043984..644bbb4 100644
--- a/share/html/Elements/RT__CustomField/ColumnMap
+++ b/share/html/Elements/RT__CustomField/ColumnMap
@@ -86,14 +86,14 @@ my $COLUMN_MAP = {
             return !$v ? $_[0]->loc('unlimited') : $v == 0 ? $_[0]->loc('one') : $v;
         },
     },
-    AppliedTo => {
-        title     => 'Applied', # loc
+    AddedTo => {
+        title     => 'Added', # loc
 	value     => sub {
-            if ( $_[0]->IsApplied ) {
+            if ( $_[0]->IsAdded ) {
                 return $_[0]->loc('Global');
             }
 
-            my $collection = $_[0]->AppliedTo;
+            my $collection = $_[0]->AddedTo;
             return '' unless $collection;
 
             $collection->RowsPerPage(10);
@@ -127,7 +127,7 @@ my $COLUMN_MAP = {
         },
         value => sub {
             my $id = $_[0]->id;
-            return '' if $_[0]->IsApplied;
+            return '' if $_[0]->IsAdded;
 
             my $name = 'RemoveCustomField';
             my $arg = $DECODED_ARGS->{ $name };
@@ -148,7 +148,7 @@ my $COLUMN_MAP = {
             my $id = $_[0]->id;
             
             my $context = $_[2] || 0;
-            return '' unless $_[0]->IsApplied( $context );
+            return '' unless $_[0]->IsAdded( $context );
 
             my $name = 'MoveCustomField';
             my $args = $m->caller_args( 1 );
@@ -173,6 +173,8 @@ my $COLUMN_MAP = {
     },
 };
 
+$COLUMN_MAP->{'AppliedTo'} = $COLUMN_MAP->{'AddedTo'};
+
 </%ONCE>
 <%INIT>
 $m->callback( COLUMN_MAP => $COLUMN_MAP, CallbackName => 'ColumnMap', CallbackOnce => 1 );
diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index 9959d67..fe36d19 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -99,7 +99,7 @@ my $COLUMN_MAP = {
             return
                 $_[0]->IsAddedToAny
                 ? ($_[0]->Disabled ? $_[0]->loc('Disabled') : $_[0]->loc('Enabled') )
-                : $_[0]->loc('Not Applied')
+                : $_[0]->loc('Not Added')
         },
     },
     RemoveCheckBox => {

commit f15a0af1da4c01315de9b6e5b072309284245c87
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Tue Oct 16 12:03:50 2012 -0700

    Let defaults in RT::Record::AddAndSort->Create take effect
    
    Avoids "autovivifying" explicit undef values for columns which were
    omitted in the original call to Create.  Now the default of
    
        ObjectId => 0
    
    in SUPER::Create is actually used.
    
    The dropping of the default ObjectId meant non-queue/ticket custom
    fields created from an initialdata failed to be applied globally (the
    intended behaviour).

diff --git a/lib/RT/ObjectCustomField.pm b/lib/RT/ObjectCustomField.pm
index 0992cc9..c4b362f 100644
--- a/lib/RT/ObjectCustomField.pm
+++ b/lib/RT/ObjectCustomField.pm
@@ -68,7 +68,8 @@ sub Create {
     my $self = shift;
     my %args = (@_);
     return $self->SUPER::Create(
-        map { $_ => $args{ $_ } } qw(
+         map { $_ => $args{ $_ } }
+        grep { exists $args{$_} } qw(
             CustomField ObjectId
             SortOrder Disabled
             Created Creator
diff --git a/lib/RT/ObjectScrip.pm b/lib/RT/ObjectScrip.pm
index f5546b6..301188b 100644
--- a/lib/RT/ObjectScrip.pm
+++ b/lib/RT/ObjectScrip.pm
@@ -128,7 +128,8 @@ sub Create {
     my %args = (@_);
     $args{'Stage'} ||= 'TransactionCreate'; #XXX: why don't we turn undef into default?
     return $self->SUPER::Create(
-        map { $_ => $args{ $_ } } qw(
+         map { $_ => $args{ $_ } }
+        grep { exists $args{$_} } qw(
             Scrip Stage ObjectId
             SortOrder Disabled
             Created Creator

commit 67039e93945bfbf7dcc5a996d3abda61f87b67d5
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Thu Nov 1 15:39:35 2012 -0700

    carp() deprecation warnings for easier tracing to the origin callsite

diff --git a/lib/RT/CustomField.pm b/lib/RT/CustomField.pm
index ddabb12..cbf56b3 100644
--- a/lib/RT/CustomField.pm
+++ b/lib/RT/CustomField.pm
@@ -1271,7 +1271,7 @@ sub IsOnlyGlobal {
     return ($self->LookupType =~ /^RT::(?:Group|User)/io);
 
 }
-sub ApplyGlobally { warn "DEPRECATED, use IsOnlyGlobal"; return shift->IsOnlyGlobal(@_) }
+sub ApplyGlobally { Carp::carp("DEPRECATED, use IsOnlyGlobal"); return shift->IsOnlyGlobal(@_) }
 
 =head1 AddedTo
 
@@ -1288,7 +1288,7 @@ sub AddedTo {
     return RT::ObjectCustomField->new( $self->CurrentUser )
         ->AddedTo( CustomField => $self );
 }
-sub AppliedTo { warn "DEPRECATED: use AddedTo"; shift->AddedTo(@_) };
+sub AppliedTo { Carp::carp("DEPRECATED: use AddedTo"); shift->AddedTo(@_) };
 
 =head1 NotAddedTo
 
@@ -1305,7 +1305,7 @@ sub NotAddedTo {
     return RT::ObjectCustomField->new( $self->CurrentUser )
         ->NotAddedTo( CustomField => $self );
 }
-sub NotAppliedTo { warn "DEPRECATED: use NotAddedTo"; shift->NotAddedTo(@_) };
+sub NotAppliedTo { Carp::carp("DEPRECATED: use NotAddedTo"); shift->NotAddedTo(@_) };
 
 =head2 IsAdded
 
@@ -1323,7 +1323,7 @@ sub IsAdded {
     return undef unless $ocf->id;
     return $ocf;
 }
-sub IsApplied { warn "DEPRECATED: use IsAdded"; shift->IsAdded(@_) };
+sub IsApplied { Carp::carp("DEPRECATED: use IsAdded"); shift->IsAdded(@_) };
 
 =head2 IsAddedToAny
 

commit f1580e92d0a5fe495686ccd6a0e75ec0204e557f
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Nov 2 12:47:17 2012 -0700

    Tests: Avoid uninitialized warnings from the User Defined condition/action

diff --git a/t/api/scrip.t b/t/api/scrip.t
index 7c9bf4a..ddd0158 100644
--- a/t/api/scrip.t
+++ b/t/api/scrip.t
@@ -133,6 +133,9 @@ note 'check applications vs. templates';
         ScripAction    => 'User Defined',
         ScripCondition => 'User Defined',
         Template       => 'foo',
+        CustomIsApplicableCode  => "1;",
+        CustomPrepareCode       => "1;",
+        CustomCommitCode        => "1;",
     );
     ok($status, 'created a scrip') or diag "error: $msg";
     main->check_applications($scrip, [$queue], [0, $queue_B]);

commit 71542dcb07ff8562491a2de76f494da81e15af13
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Fri Nov 2 15:24:54 2012 -0700

    Shred ObjectScrip records when shredding a Scrip
    
    Shredder tests now pass.

diff --git a/lib/RT/Shredder/Scrip.pm b/lib/RT/Shredder/Scrip.pm
index 8828e5b..0b4ce5f 100644
--- a/lib/RT/Shredder/Scrip.pm
+++ b/lib/RT/Shredder/Scrip.pm
@@ -57,4 +57,29 @@ use RT::Shredder::Constants;
 use RT::Shredder::Exceptions;
 use RT::Shredder::Dependencies;
 
+sub __DependsOn
+{
+    my $self = shift;
+    my %args = (
+            Shredder => undef,
+            Dependencies => undef,
+            @_,
+           );
+    my $deps = $args{'Dependencies'};
+    my $list = [];
+
+    my $objs = RT::ObjectScrips->new( $self->CurrentUser );
+    $objs->LimitToScrip( $self->Id );
+    push @$list, $objs;
+
+    $deps->_PushDependencies(
+        BaseObject    => $self,
+        Flags         => DEPENDS_ON,
+        TargetObjects => $list,
+        Shredder      => $args{'Shredder'}
+    );
+
+    return $self->SUPER::__DependsOn( %args );
+}
+
 1;

commit eb8b166443a9d7490c1c98e70ec0a3a63aab54f0
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 5 16:19:48 2012 -0800

    Tests: Add a helper method to check a scrip's ObjectScrips records

diff --git a/lib/RT/Test.pm b/lib/RT/Test.pm
index 2e3e8b3..16c65ce 100644
--- a/lib/RT/Test.pm
+++ b/lib/RT/Test.pm
@@ -1097,6 +1097,44 @@ sub clean_caught_mails {
     unlink $tmp{'mailbox'};
 }
 
+=head2 object_scrips_are
+
+Takes an L<RT::Scrip> object as the first argument and an arrayref of
+L<RT::Queue> objects and/or Queue IDs as the second argument.
+
+The scrip's applications (L<RT::ObjectScrip> records) are tested to ensure they
+exactly match the arrayref.
+
+An optional third arrayref may be passed to enumerate and test the queues the
+scrip is B<not> added to.  This is most useful for testing the API returns the
+correct results.
+
+=cut
+
+sub object_scrips_are {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+    my $self    = shift;
+    my $scrip   = shift;
+    my $to      = shift || [];
+    my $not_to  = shift;
+
+    Test::More::ok($scrip->IsAdded(ref $_? $_->id : $_), 'added to queue' ) foreach @$to;
+    Test::More::is_deeply(
+        [sort map $_->id, @{ $scrip->AddedTo->ItemsArrayRef }],
+        [sort grep $_, map ref $_? $_->id : $_, @$to],
+        'correct list of added to queues',
+    );
+
+    if ($not_to) {
+        Test::More::ok(!$scrip->IsAdded(ref $_? $_->id : $_), 'not added to queue' ) foreach @$not_to;
+        Test::More::is_deeply(
+            [sort map $_->id, @{ $scrip->NotAddedTo->ItemsArrayRef }],
+            [sort grep $_, map ref $_? $_->id : $_, @$not_to],
+            'correct list of not added to queues',
+        );
+    }
+}
+
 =head2 get_relocatable_dir
 
 Takes a path relative to the location of the test file that is being
diff --git a/t/api/scrip.t b/t/api/scrip.t
index ddd0158..279be5d 100644
--- a/t/api/scrip.t
+++ b/t/api/scrip.t
@@ -138,11 +138,11 @@ note 'check applications vs. templates';
         CustomCommitCode        => "1;",
     );
     ok($status, 'created a scrip') or diag "error: $msg";
-    main->check_applications($scrip, [$queue], [0, $queue_B]);
+    RT::Test->object_scrips_are($scrip, [$queue], [0, $queue_B]);
 
     ($status, $msg) = $scrip->AddToObject( $queue_B->id );
     ok(!$status, $msg);
-    main->check_applications($scrip, [$queue], [0, $queue_B]);
+    RT::Test->object_scrips_are($scrip, [$queue], [0, $queue_B]);
 
     $template = RT::Template->new( RT->SystemUser );
     ($status, $msg) = $template->Create( Queue => $queue_B->id, Name => 'foo' );
@@ -150,7 +150,7 @@ note 'check applications vs. templates';
 
     ($status, $msg) = $scrip->AddToObject( $queue_B->id );
     ok($status, 'added scrip to another queue');
-    main->check_applications($scrip, [$queue, $queue_B], [0]);
+    RT::Test->object_scrips_are($scrip, [$queue, $queue_B], [0]);
 
     ($status, $msg) = $scrip->RemoveFromObject( $queue_B->id );
     ok($status, 'removed scrip from queue');
@@ -160,14 +160,14 @@ note 'check applications vs. templates';
 
     ($status, $msg) = $scrip->AddToObject( $queue_B->id );
     ok(!$status, $msg);
-    main->check_applications($scrip, [$queue], [0, $queue_B]);
+    RT::Test->object_scrips_are($scrip, [$queue], [0, $queue_B]);
 
     ($status, $msg) = $template->Create( Queue => 0, Name => 'foo' );
     ok $status, 'created a global template';
 
     ($status, $msg) = $scrip->AddToObject( $queue_B->id );
     ok($status, 'added scrip');
-    main->check_applications($scrip, [$queue, $queue_B], [0]);
+    RT::Test->object_scrips_are($scrip, [$queue, $queue_B], [0]);
 }
 
 note 'basic check for disabling scrips';
@@ -212,25 +212,3 @@ note 'basic check for disabling scrips';
 
     ok(!$scrip->Disabled, "not applied");
 }
-
-sub check_applications {
-    local $Test::Builder::Level = $Test::Builder::Level + 1;
-    my $self = shift;
-    my $scrip = shift;
-    my $to = shift || [];
-    my $not_to = shift || [];
-
-    ok($scrip->IsAdded(ref $_? $_->id : $_), 'added to queue' ) foreach @$to;
-    ok(!$scrip->IsAdded(ref $_? $_->id : $_), 'not added' ) foreach @$not_to;
-    is_deeply(
-        [sort map $_->id, @{ $scrip->AddedTo->ItemsArrayRef }],
-        [sort grep $_, map ref $_? $_->id : $_, @$to],
-        'correct list of queues',
-    );
-    is_deeply(
-        [sort map $_->id, @{ $scrip->NotAddedTo->ItemsArrayRef }],
-        [sort grep $_, map ref $_? $_->id : $_, @$not_to],
-        'correct list of queues',
-    );
-}
-

commit ed40d943e25c78fccbd1f4b28ac544afba07b3ab
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 5 16:33:39 2012 -0800

    Tests: Improve convenience of object_scrips_are
    
    Accepts a scrip ID instead of an object as the first argument.
    
    The individual test descriptions include the queue ID for easier
    debugging.

diff --git a/lib/RT/Test.pm b/lib/RT/Test.pm
index 16c65ce..91fc7f4 100644
--- a/lib/RT/Test.pm
+++ b/lib/RT/Test.pm
@@ -58,6 +58,7 @@ use Socket;
 use File::Temp qw(tempfile);
 use File::Path qw(mkpath);
 use File::Spec;
+use Scalar::Util qw(blessed);
 
 our @EXPORT = qw(is_empty diag parse_mail works fails);
 
@@ -1099,7 +1100,7 @@ sub clean_caught_mails {
 
 =head2 object_scrips_are
 
-Takes an L<RT::Scrip> object as the first argument and an arrayref of
+Takes an L<RT::Scrip> object or ID as the first argument and an arrayref of
 L<RT::Queue> objects and/or Queue IDs as the second argument.
 
 The scrip's applications (L<RT::ObjectScrip> records) are tested to ensure they
@@ -1118,18 +1119,26 @@ sub object_scrips_are {
     my $to      = shift || [];
     my $not_to  = shift;
 
-    Test::More::ok($scrip->IsAdded(ref $_? $_->id : $_), 'added to queue' ) foreach @$to;
+    unless (blessed($scrip)) {
+        my $id = $scrip;
+        $scrip = RT::Scrip->new( RT->SystemUser );
+        $scrip->Load($id);
+    }
+
+    $to = [ map { blessed($_) ? $_->id : $_ } @$to ];
+    Test::More::ok($scrip->IsAdded($_), "added to queue $_" ) foreach @$to;
     Test::More::is_deeply(
         [sort map $_->id, @{ $scrip->AddedTo->ItemsArrayRef }],
-        [sort grep $_, map ref $_? $_->id : $_, @$to],
+        [sort grep $_, @$to ],
         'correct list of added to queues',
     );
 
     if ($not_to) {
-        Test::More::ok(!$scrip->IsAdded(ref $_? $_->id : $_), 'not added to queue' ) foreach @$not_to;
+        $not_to = [ map { blessed($_) ? $_->id : $_ } @$not_to ];
+        Test::More::ok(!$scrip->IsAdded($_), "not added to queue $_" ) foreach @$not_to;
         Test::More::is_deeply(
             [sort map $_->id, @{ $scrip->NotAddedTo->ItemsArrayRef }],
-            [sort grep $_, map ref $_? $_->id : $_, @$not_to],
+            [sort grep $_, @$not_to ],
             'correct list of not added to queues',
         );
     }

commit d2c3bd3440ad5b7242ee2621f8064a1632ac8097
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 5 16:35:37 2012 -0800

    Tests: Ensure scrips can't be applied both globally and to individual queues from the UI

diff --git a/t/web/scrips.t b/t/web/scrips.t
index 9667116..cf12427 100644
--- a/t/web/scrips.t
+++ b/t/web/scrips.t
@@ -1,7 +1,7 @@
 use strict;
 use warnings;
 
-use RT::Test tests => 54;
+use RT::Test tests => undef;
 
 RT->Config->Set( UseTransactionBatch => 1 );
 
@@ -151,6 +151,40 @@ note "check basics in scrip's admin interface";
     $m->content_contains("Description changed from", "found action result message");
 }
 
+note "check application in admin interface";
+{
+    $m->follow_link_ok({ id => 'tools-config-global-scrips-create' });
+    $m->submit_form_ok({
+        with_fields => {
+            Description     => "testing application",
+            ScripCondition  => "On Create",
+            ScripAction     => "Open Tickets",
+            Template        => "Blank",
+        },
+        button => 'Create',
+    }, "created scrip");
+    $m->content_contains("Scrip Created", "found result message");
+
+    my ($sid) = ($m->content =~ /Modify scrip #(\d+)/);
+    ok $sid, "found scrip id on the page";
+    RT::Test->object_scrips_are($sid, [0]);
+
+    $m->follow_link_ok({ id => 'page-applies-to' });
+    ok $m->form_name("AddRemoveScrip"), "found form";
+    $m->tick("RemoveScrip-$sid", 0);
+    $m->click_ok("Update", "update scrip application");
+    RT::Test->object_scrips_are($sid, []);
+
+    my $queue = RT::Test->load_or_create_queue( Name => 'General' );
+    ok $queue && $queue->id, "loaded queue";
+
+    ok $m->form_name("AddRemoveScrip"), "found form";
+    $m->tick("AddScrip-$sid", 0);
+    $m->tick("AddScrip-$sid", $queue->id);
+    $m->click_ok("Update", "update scrip application");
+    RT::Test->object_scrips_are($sid, [0], [$queue->id]);
+}
+
 note "apply scrip in different stage to different queues";
 {
     my $queue = RT::Test->load_or_create_queue( Name => 'Regression' );
@@ -186,3 +220,5 @@ note "apply scrip in different stage to different queues";
     is scalar @matches, 1, 'scrip mentioned only once';
 }
 
+undef $m;
+done_testing;

commit 3334e6ae3b2d31a52708bf571281ec471efbdbdf
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 5 17:01:44 2012 -0800

    Disable selection of individual objects when "apply globally" is checked
    
    Unapplied scrips and CFs show both an "apply globally" checkbox as well
    as collection lists of available individual objects.  If "apply
    globally" is checked, prevent checking individual objects as well
    before the page is submitted.  The API already prevents both changes
    from going through, so this is simply a bit of icing on the admin
    interface.

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 75cf296..e04122a 100755
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -874,6 +874,7 @@ Set(@JSFiles, qw/
     supersubs.js
     jquery.supposition.js
     history-folding.js
+    event-registration.js
     late.js
 /);
 
diff --git a/share/html/NoAuth/js/event-registration.js b/share/html/NoAuth/js/event-registration.js
new file mode 100644
index 0000000..bbeeec8
--- /dev/null
+++ b/share/html/NoAuth/js/event-registration.js
@@ -0,0 +1,62 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+%#                                          <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+jQuery(function() {
+    var global_checkboxes = [
+        "form[name=AddRemoveScrip] input[type=checkbox][name^=AddScrip-][value=0]",
+        "form input[type=checkbox][name^=AddCustomField-][value=0]"
+    ];
+    jQuery(global_checkboxes.join(", "))
+        .change(function(){
+            var self    = jQuery(this);
+            var checked = self.attr("checked");
+
+            self.closest("form")
+                .find("table.collection input[type=checkbox]")
+                .attr("disabled", checked ? "disabled" : "");
+        });
+});

commit 98213587612e97803a97764393edf2f5f08cbefc
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 5 22:14:35 2012 -0800

    Turn the apply globally checkbox labels into real clickable <label>s

diff --git a/share/html/Admin/Scrips/Objects.html b/share/html/Admin/Scrips/Objects.html
index 8d67804..662dd30 100644
--- a/share/html/Admin/Scrips/Objects.html
+++ b/share/html/Admin/Scrips/Objects.html
@@ -54,16 +54,20 @@
 
 % if ( $is_global ) {
 <h2><&|/l&>Applies to all objects</&></h2>
+<label>
 <input type="checkbox" name="RemoveScrip-<% $id %>" value="0" />
 <&|/l&>check this box to remove this scrip from all objects and be able to choose specific objects.</&>
+</label>
 % } else {
 <h2><% loc('Stage') %></h2>
 <& /Admin/Elements/SelectStage, Default => $Stage &>
 
 <h2><&|/l&>Apply globally</&></h2>
 
+<label>
 <input type="checkbox" name="AddScrip-<% $id %>" value="0" />
 <&|/l&>check this box to apply this scrip to all objects.</&>
+</label>
 
 <h2><&|/l&>Selected objects</&></h2>
 <& /Elements/CollectionList,

commit 06be7bc4619d574339f579b23e6a8a3a2ed5277d
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 5 22:21:25 2012 -0800

    Grab the parent form and checkbox state from the current element in setCheckbox()
    
    This provides more flexibility for setCheckbox() than passing in the
    form and state as arguments themselves.
    
    Desired state may still be passed as the third argument, if necessary,
    as with a button using this function.

diff --git a/share/html/Elements/ColumnMap b/share/html/Elements/ColumnMap
index 7295e3f..1ad3524 100644
--- a/share/html/Elements/ColumnMap
+++ b/share/html/Elements/ColumnMap
@@ -119,9 +119,9 @@ my $COLUMN_MAP = {
             my $checked = $DECODED_ARGS->{ $name .'All' }? 'checked="checked"': '';
 
             return \qq{<input type="checkbox" name="}, $name, \qq{All" value="1" $checked
-                              onclick="setCheckbox(this.form, },
+                              onclick="setCheckbox(this, },
                               $m->interp->apply_escapes($name,'j'),
-                              \qq{, this.checked)" />};
+                              \qq{)" />};
         },
         value => sub {
             my $id = $_[0]->id;
diff --git a/share/html/Elements/RT__CustomField/ColumnMap b/share/html/Elements/RT__CustomField/ColumnMap
index 644bbb4..fae2655 100644
--- a/share/html/Elements/RT__CustomField/ColumnMap
+++ b/share/html/Elements/RT__CustomField/ColumnMap
@@ -121,9 +121,9 @@ my $COLUMN_MAP = {
             my $checked = $DECODED_ARGS->{ $name .'All' }? 'checked="checked"': '';
 
             return \qq{<input type="checkbox" name="}, $name, \qq{All" value="1" $checked
-                              onclick="setCheckbox(this.form, },
+                              onclick="setCheckbox(this, },
                               $m->interp->apply_escapes($name,'j'),
-                              \qq{, this.checked)" />};
+                              \qq{)" />};
         },
         value => sub {
             my $id = $_[0]->id;
diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index fe36d19..9d5ea31 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -108,7 +108,7 @@ my $COLUMN_MAP = {
             my $checked = $m->request_args->{ $name .'All' }? 'checked="checked"': '';
 
             return \qq{<input type="checkbox" name="${name}All" value="1" $checked
-                              onclick="setCheckbox(this.form, '$name', this.checked)" />};
+                              onclick="setCheckbox(this, '$name')" />};
         },
         value => sub {
             my $id = $_[0]->id;
diff --git a/share/html/Elements/Submit b/share/html/Elements/Submit
index b7840d3..7629707 100644
--- a/share/html/Elements/Submit
+++ b/share/html/Elements/Submit
@@ -52,10 +52,10 @@ id="<%$id%>"
 >
   <div class="extra-buttons">
 % if ($CheckAll) {
-  <input type="button" value="<%$CheckAllLabel%>" onclick="setCheckbox(this.form, <% $match %>, true);return false;" class="button" />
+  <input type="button" value="<%$CheckAllLabel%>" onclick="setCheckbox(this, <% $match %>);return false;" class="button" />
 % }
 % if ($ClearAll) {
-  <input type="button" value="<%$ClearAllLabel%>" onclick="setCheckbox(this.form, <% $match %>, false);return false;" class="button" />
+  <input type="button" value="<%$ClearAllLabel%>" onclick="setCheckbox(this, <% $match %>);return false;" class="button" />
 % }
 % if ($Reset) {
   <input type="reset" value="<%$ResetLabel%>" class="button" />
diff --git a/share/html/NoAuth/js/util.js b/share/html/NoAuth/js/util.js
index fe5c0a3..3c9b151 100644
--- a/share/html/NoAuth/js/util.js
+++ b/share/html/NoAuth/js/util.js
@@ -127,7 +127,10 @@ function focusElementById(id) {
     if (e) e.focus();
 }
 
-function setCheckbox(form, name, val) {
+function setCheckbox(input, name, val) {
+    if (val == null) val = input.checked;
+
+    var form    = input.form;
     var myfield = form.getElementsByTagName('input');
     for ( var i = 0; i < myfield.length; i++ ) {
         if ( myfield[i].type != 'checkbox' ) continue;

commit 04f5e9af5ed201ac770d6a79eb0e9541fc284195
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 5 22:45:08 2012 -0800

    setCheckbox()es within the closest form _or_ collection list
    
    If input is inside a collection list, we don't want to affect other
    collection lists.
    
    This prevents the checkboxes to apply scrips and CFs from checking the
    "apply globally" checkbox inside the form but outside the collection
    list.

diff --git a/share/html/NoAuth/js/util.js b/share/html/NoAuth/js/util.js
index 3c9b151..6e045a4 100644
--- a/share/html/NoAuth/js/util.js
+++ b/share/html/NoAuth/js/util.js
@@ -130,8 +130,9 @@ function focusElementById(id) {
 function setCheckbox(input, name, val) {
     if (val == null) val = input.checked;
 
-    var form    = input.form;
-    var myfield = form.getElementsByTagName('input');
+    // Find inputs within the current form or collection list, whichever is closest.
+    var container = jQuery(input).closest("form, table.collection-as-table").get(0);
+    var myfield   = container.getElementsByTagName('input');
     for ( var i = 0; i < myfield.length; i++ ) {
         if ( myfield[i].type != 'checkbox' ) continue;
         if ( name ) {

commit e5a6fc857f56cff9de166826fde3e0b82fcafe95
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 5 22:56:44 2012 -0800

    Standardize stage selector text and location when applying scrips

diff --git a/share/html/Admin/Elements/EditScrips b/share/html/Admin/Elements/EditScrips
index b422909..d89bc03 100644
--- a/share/html/Admin/Elements/EditScrips
+++ b/share/html/Admin/Elements/EditScrips
@@ -88,9 +88,7 @@
 <p><i><&|/l&>(No scrips)</&></i></p>
 % }
 
-<div style="text-align:right">
-<% loc('Select stage for scrips you add') %>: <& SelectStage &>
-</div>
+<& SelectStageForAdded &>
 
 <& /Elements/Submit,
     Name => 'AddScrips',
diff --git a/share/html/Admin/Elements/SelectStageForAdded b/share/html/Admin/Elements/SelectStageForAdded
new file mode 100644
index 0000000..d137bb4
--- /dev/null
+++ b/share/html/Admin/Elements/SelectStageForAdded
@@ -0,0 +1,51 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2012 Best Practical Solutions, LLC
+%#                                          <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<div style="text-align:right">
+<% loc('Select scrip stage for newly added queues:') %>
+<& SelectStage, %ARGS &>
+</div>
diff --git a/share/html/Admin/Scrips/Objects.html b/share/html/Admin/Scrips/Objects.html
index 662dd30..bd7b658 100644
--- a/share/html/Admin/Scrips/Objects.html
+++ b/share/html/Admin/Scrips/Objects.html
@@ -59,9 +59,6 @@
 <&|/l&>check this box to remove this scrip from all objects and be able to choose specific objects.</&>
 </label>
 % } else {
-<h2><% loc('Stage') %></h2>
-<& /Admin/Elements/SelectStage, Default => $Stage &>
-
 <h2><&|/l&>Apply globally</&></h2>
 
 <label>
@@ -104,6 +101,8 @@
 
 % }
 
+<& /Admin/Elements/SelectStageForAdded, Default => $Stage &>
+
 <& /Elements/Submit, Name => 'Update' &>
 </form>
 

commit b8686e159b1a72714fa6d7b464ace204f8a9fa2b
Author: Thomas Sibley <trs at bestpractical.com>
Date:   Mon Nov 5 23:30:57 2012 -0800

    Only show stage selector when the scrip isn't applied globally
    
    Otherwise individual queues aren't shown the stage selector is
    nonsensical.

diff --git a/share/html/Admin/Scrips/Objects.html b/share/html/Admin/Scrips/Objects.html
index bd7b658..784f4d5 100644
--- a/share/html/Admin/Scrips/Objects.html
+++ b/share/html/Admin/Scrips/Objects.html
@@ -99,9 +99,8 @@
     ],
 &>
 
-% }
-
 <& /Admin/Elements/SelectStageForAdded, Default => $Stage &>
+% }
 
 <& /Elements/Submit, Name => 'Update' &>
 </form>

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


More information about the Rt-commit mailing list