diff --git a/lib/RT/Assets.pm b/lib/RT/Assets.pm
index 51a8527..c34d650 100644
--- a/lib/RT/Assets.pm
+++ b/lib/RT/Assets.pm
@@ -241,7 +241,7 @@ sub OrderByCols {
     my $class = $self->_RoleGroupClass;
 
     for my $row (@_) {
-        if ($row->{FIELD} =~ /^CF\.(?:\{(.*)\}|(.*))$/) {
+        if ($row->{FIELD} =~ /^(?:CF|CustomField)\.(?:\{(.*)\}|(.*))$/) {
             my $name = $1 || $2;
             my $cf = RT::CustomField->new( $self->CurrentUser );
             $cf->LoadByNameAndCatalog(
diff --git a/share/html/Asset/CreateLinkedTicket.html b/share/html/Asset/CreateLinkedTicket.html
index afcabe5..5d84e7d 100644
--- a/share/html/Asset/CreateLinkedTicket.html
+++ b/share/html/Asset/CreateLinkedTicket.html
@@ -47,12 +47,33 @@
 %# END BPS TAGGED BLOCK }}}
 <%args>
 $Asset => undef
+@Assets => ()
 $Requestors => ''
 </%args>
 <%init>
-my $asset = LoadAsset($Asset);
+my @asset_objs;
+my $single_asset;
+
+# Support passing a single asset or many, as with bulk update.
+if ( $Asset ){
+    push @asset_objs, LoadAsset($Asset);
+    $single_asset = 1;
+}
+elsif ( @Assets ){
+    @asset_objs = map { LoadAsset($_) } @Assets;
+    $single_asset = 1 if @asset_objs == 1;
+}
+else {
+    Abort('No asset specified');
+}
 </%init>
-<& /Elements/Header,
-    Title => loc("Create linked ticket for asset #[_1]: [_2]", $asset->id, $asset->Name) &>
+% if ($single_asset){
+    <& /Elements/Header,
+        Title => loc("Create linked ticket for asset #[_1]: [_2]", $asset_objs[0]->id, $asset_objs[0]->Name) &>
+%}
+%else{
+    <& /Elements/Header,
+        Title => loc("Create linked ticket for multiple assets") &>
+%}
 <& /Elements/Tabs &>
-<& /Asset/Elements/CreateLinkedTicket, AssetObj => $asset, Requestors => $Requestors &>
+<& /Asset/Elements/CreateLinkedTicket, AssetObj => \@asset_objs, Requestors => $Requestors &>
diff --git a/share/html/Asset/Elements/AssetSearchBasics b/share/html/Asset/Elements/AssetSearchBasics
index c07988b..b7b647a 100644
--- a/share/html/Asset/Elements/AssetSearchBasics
+++ b/share/html/Asset/Elements/AssetSearchBasics
@@ -54,7 +54,7 @@
 </td></tr>
 <tr class="asset-status"><td class="label"><label for="Status"><&|/l&>Status</&></label></td>
     <td class="value" colspan="3">
-<& /Asset/Elements/SelectStatus, Name => 'Status', CatalogObj => $CatalogObj, DefaultValue => 1,
+<& /Asset/Elements/SelectStatus, Name => 'Status', Catalogs => { $CatalogObj->id => 1 }, DefaultValue => 1,
        Default => ($ARGS{'Status'} || '') &>
 </td></tr>
 <tr class="asset-name"><td class="label"><label for="Name"><&|/l&>Name</&></label></td>
diff --git a/share/html/Asset/Elements/CreateLinkedTicket b/share/html/Asset/Elements/CreateLinkedTicket
index 08121e4..4312917 100644
--- a/share/html/Asset/Elements/CreateLinkedTicket
+++ b/share/html/Asset/Elements/CreateLinkedTicket
@@ -50,21 +50,50 @@ $AssetObj
 $Requestors => ''
 </%args>
 <%init>
+my @asset_objs;
+if ( ref $AssetObj eq 'ARRAY' ){
+    # Accept an array of asset objects
+    @asset_objs = @$AssetObj;
+}
+else{
+    # Also support passing a single asset object
+    push @asset_objs, $AssetObj;
+}
+
 my @description = map { $m->interp->apply_escapes($_, 'h') }
-    $AssetObj->id, $AssetObj->Name;
+    $asset_objs[0]->id, $asset_objs[0]->Name;
+
+my $refers_to = join ' ', map { "asset:" . $_->id } @asset_objs;
+
+# Find possible requestors
+my %role_addresses;
+foreach my $asset (@asset_objs){
+    for my $role ($asset->Roles) {
+        # Create a hash with email addresses to easily de-dupe the lists from multiple assets
+        map { $role_addresses{$role}{$_} = 1 } $asset->RoleGroup($role)->MemberEmailAddresses;
+    }
+}
+
 </%init>
 <form action="<% RT->Config->Get("WebPath") %><% $session{CurrentUser}->Privileged ? "/Ticket" : "/SelfService" %>/Create.html" id="AssetCreateLinkedTicket">
-  <input name="new-RefersTo" value="asset:<% $AssetObj->id %>" type="hidden">
-  <input name="Subject" value="<% $AssetObj->Name %>" type="hidden">
+  <input name="new-RefersTo" value="<% $refers_to %>" type="hidden">
+  <input name="Subject" value="<% $asset_objs[0]->Name %>" type="hidden">
+% if ( @asset_objs == 1 ){
   <&|/l_unsafe,
     $m->scomp("/Elements/SelectNewTicketQueue"),
     @description &>Create a new ticket in the [_1] queue about asset #[_2]: [_3].</&>
+% }
+% else {
+<&|/l_unsafe,
+    $m->scomp("/Elements/SelectNewTicketQueue"),
+    &>Create a new ticket in the [_1] queue about multiple assets.</&>
+% }
 % if ($Requestors) {
     <input type="hidden" name="Requestors" value="<% $Requestors%>" />
 % } else {
 %     my $first = 1;
-%     for my $role ($AssetObj->Roles) {
-%         my $addr = $AssetObj->RoleGroup($role)->MemberEmailAddressesAsString;
+%     for my $role ($asset_objs[0]->Roles) {
+%         my $addr = join ', ', keys %{$role_addresses{$role}};
 %         next unless defined $addr and length $addr;
   <br>
   <label>
diff --git a/share/html/Asset/Elements/SelectCatalog b/share/html/Asset/Elements/SelectCatalog
index bba70e5..11b4adb 100644
--- a/share/html/Asset/Elements/SelectCatalog
+++ b/share/html/Asset/Elements/SelectCatalog
@@ -48,7 +48,7 @@
 <& /Elements/SelectObject,
     Name           => "Catalog",
     ShowAll        => $ShowAll,
-    ShowNullOption => 0,
+    ShowNullOption => $ShowNullOption,
     CheckRight     => "CreateAsset",
     %ARGS,
     ObjectType     => "Catalog",
@@ -59,6 +59,7 @@
 $ShowAll => 0
 $Default => undef
 $UpdateSession => 1
+$ShowNullOption => 0
 </%args>
 <%init>
 my $catalog_obj = LoadDefaultCatalog($Default || '');
diff --git a/share/html/Asset/Elements/SelectStatus b/share/html/Asset/Elements/SelectStatus
index c5e4ae1..135a9c7 100644
--- a/share/html/Asset/Elements/SelectStatus
+++ b/share/html/Asset/Elements/SelectStatus
@@ -45,22 +45,27 @@
 %# those contributions and any derivatives thereof.
 %#
 %# END BPS TAGGED BLOCK }}}
-<& /Elements/SelectStatus, %ARGS &>
+<& /Elements/SelectStatus, %ARGS, Type => 'asset', Object => $AssetObj && $AssetObj->id ? $AssetObj : $CatalogObj, Lifecycles => \@Lifecycles &>
 <%init>
-if ($AssetObj and $AssetObj->Id) {
+my @Lifecycles;
+for my $id (keys %Catalogs) {
+    my $catalog = RT::Catalog->new($session{'CurrentUser'});
+    $catalog->Load($id);
+    push @Lifecycles, $catalog->LifecycleObj if $catalog->id;
+}
+
+if ($AssetObj && $AssetObj->id) {
     $ARGS{DefaultValue} = 0;
     $ARGS{Default} = $DECODED_ARGS->{Status} || $ARGS{Default};
     $ARGS{Object} = $AssetObj;
-} else {
-    my $lifecycle = ($CatalogObj || "RT::Catalog")->LifecycleObj;
-    if ( not $ARGS{DefaultValue} ){
-        $ARGS{DefaultValue} = 0;
-        $ARGS{Default} ||= $DECODED_ARGS->{Status} || $lifecycle->DefaultOnCreate;
-    }
-    $ARGS{Statuses} = [ $AssetObj ? $lifecycle->Transitions("") : $lifecycle->Valid ];
+} elsif ( $CatalogObj ) {
+    my $lifecycle = $CatalogObj->LifecycleObj;
+    $ARGS{DefaultValue} = 0;
+    $ARGS{Default} ||= $DECODED_ARGS->{Status} || $lifecycle->DefaultOnCreate;
 }
 </%init>
 <%args>
 $AssetObj   => undef
 $CatalogObj => undef
+%Catalogs => ()
 </%args>
diff --git a/share/html/Asset/Helpers/CreateLinkedTicket b/share/html/Asset/Helpers/CreateLinkedTicket
index e3f6202..4bb5ea7 100644
--- a/share/html/Asset/Helpers/CreateLinkedTicket
+++ b/share/html/Asset/Helpers/CreateLinkedTicket
@@ -46,11 +46,19 @@
 %#
 %# END BPS TAGGED BLOCK }}}
 <%args>
-$Asset
+@Asset => ()
+$Asset => undef
 $Requestors => ''
 </%args>
 <%init>
-my $asset = LoadAsset($Asset);
+my @asset_objs;
+if ( @Asset ){
+    @asset_objs = map { LoadAsset($_) } @Asset;
+}
+elsif ( $Asset ){
+    push @asset_objs, LoadAsset($Asset);
+}
+
 </%init>
-<& /Asset/Elements/CreateLinkedTicket, AssetObj => $asset, Requestors => $Requestors &>
+<& /Asset/Elements/CreateLinkedTicket, AssetObj => \@asset_objs, Requestors => $Requestors &>
 % $m->abort;
diff --git a/share/html/Asset/Search/index.html b/share/html/Asset/Search/index.html
index 80ada79..39e5da8 100644
--- a/share/html/Asset/Search/index.html
+++ b/share/html/Asset/Search/index.html
@@ -58,6 +58,7 @@ my $title = ( $ARGS{'SearchAssets'} or $ARGS{q} ) ?
       loc("Found [quant,_1,asset,assets]",$assets->Count)
     : loc("Assets");
 
+$m->callback( CallbackName => 'Initial', Assets => $assets, ARGSRef => \%ARGS);
 </%init>
 <& /Elements/Header, Title => $title &>
 <& /Elements/Tabs &>
diff --git a/share/html/Elements/CollectionList b/share/html/Elements/CollectionList
index 99a2f64..eb0ff8f 100644
--- a/share/html/Elements/CollectionList
+++ b/share/html/Elements/CollectionList
@@ -50,6 +50,10 @@ if (!$Collection && $Class eq 'RT::Tickets') {
     $Collection = RT::Tickets->new( $session{'CurrentUser'} );
     $Collection->FromSQL($Query);
 }
+elsif (!$Collection && $Class eq 'RT::Assets') {
+    $Collection = RT::Assets->new( $session{'CurrentUser'} );
+    $Collection->FromSQL($Query);
+}
 
 $TotalFound = $Collection->CountAll() unless defined $TotalFound;
 return '' if !$TotalFound && !$ShowEmpty;
@@ -66,7 +70,7 @@ if ( $Rows ) {
 
 # XXX: ->{'order_by'} is hacky, but there is no way to check if
 # collection is ordered or not
-if ( @OrderBy && ($AllowSorting || !$Collection->{'order_by'}) ) {
+if ( @OrderBy && ($AllowSorting || $PreferOrderBy || !$Collection->{'order_by'}) ) {
     if ( $OrderBy[0] =~ /\|/ ) {
         @OrderBy = split /\|/, $OrderBy[0];
         @Order = split /\|/,$Order[0];
@@ -197,7 +201,8 @@ $Title         => loc('Ticket Search')
 $BaseURL       => RT->Config->Get('WebPath') . $m->request_comp->path .'?'
 @PassArguments => qw( Query Format Rows Page Order OrderBy)
 
-$AllowSorting   => 0
+$AllowSorting   => 0  # Make headers in table links that will resort results
+$PreferOrderBy  => 0  # Prefer the passed-in @OrderBy to the collection default
 $ShowNavigation => 1
 $ShowHeader     => 1
 $ShowEmpty      => 0
diff --git a/share/html/Elements/SelectStatus b/share/html/Elements/SelectStatus
index e29e7cf..3820033 100644
--- a/share/html/Elements/SelectStatus
+++ b/share/html/Elements/SelectStatus
@@ -94,7 +94,7 @@ if ( @Statuses ) {
     }
 
     if (not keys %statuses_by_lifecycle) {
-        for my $lifecycle (map { RT::Lifecycle->Load($_) } RT::Lifecycle->List($Type)) {
+        for my $lifecycle (map { RT::Lifecycle->Load(Type => $Type, Name => $_) } RT::Lifecycle->List($Type)) {
             $statuses_by_lifecycle{$lifecycle->Name} = [ $lifecycle->Valid ];
         }
     }
diff --git a/share/html/Elements/ShowSearch b/share/html/Elements/ShowSearch
index 52e628b..8ada004 100644
--- a/share/html/Elements/ShowSearch
+++ b/share/html/Elements/ShowSearch
@@ -51,7 +51,7 @@
     titleright => $customize ? loc('Edit') : '',
     titleright_href => $customize,
     hideable => $hideable &>
-<& $query_display_component, hideable => $hideable, %$ProcessedSearchArg, ShowNavigation => 0, Class => 'RT::Tickets' &>
+<& $query_display_component, hideable => $hideable, %$ProcessedSearchArg, ShowNavigation => 0, Class => $SearchArg && ($SearchArg->{SearchType}||'') eq 'Asset' ? 'RT::Assets' : 'RT::Tickets', PreferOrderBy => 1 &>
 </&>
 <%init>
 my $search;
@@ -75,7 +75,13 @@ if ($SavedSearch) {
     }
     $SearchArg->{'SavedSearchId'} ||= $SavedSearch;
     $SearchArg->{'SearchType'} ||= 'Ticket';
-    if ( $SearchArg->{SearchType} ne 'Ticket' ) {
+    if ( $SearchArg->{SearchType} eq 'Asset' ) {
+        $query_link_url = RT->Config->Get('WebPath') . "/Asset/Search/Results.html";
+        $customize = RT->Config->Get('WebPath') . '/Asset/Search/Build.html?'
+            . $m->comp( '/Elements/QueryString',
+            SavedSearchLoad => $SavedSearch );
+    }
+    elsif ( $SearchArg->{SearchType} ne 'Ticket' ) {
 
         # XXX: dispatch to different handler here
         $query_display_component
diff --git a/share/html/Elements/Tabs b/share/html/Elements/Tabs
index 2ee374e..7aea539 100644
--- a/share/html/Elements/Tabs
+++ b/share/html/Elements/Tabs
@@ -1105,7 +1105,7 @@ my $build_main_nav = sub {
         PageMenu()->child( edit => title => loc('Edit'), path => '/Prefs/MyRT.html' );
     }
 
-    $m->callback( CallbackName => 'Privileged', Path => $request_path );
+    $m->callback( CallbackName => 'Privileged', Path => $request_path, ARGSRef => \%ARGS );
 };
 
 my $build_selfservice_nav = sub {
@@ -1172,7 +1172,7 @@ my $build_selfservice_nav = sub {
         }
     }
 
-    $m->callback( CallbackName => 'SelfService', Path => $request_path );
+    $m->callback( CallbackName => 'SelfService', Path => $request_path, ARGSRef => \%ARGS );
 };
 
 
diff --git a/share/html/Search/Elements/EditFormat b/share/html/Search/Elements/EditFormat
index 2d6f3c0..328d40f 100644
--- a/share/html/Search/Elements/EditFormat
+++ b/share/html/Search/Elements/EditFormat
@@ -97,10 +97,12 @@ jQuery( function() {
 <select name="Link">
 <option value="None">-</option>
 <option value="Display"><&|/l&>Display</&></option>
+% if ($IncludeTicketLinks) {
 <option value="Take"><&|/l&>Take</&></option>
 <option value="Respond"><&|/l&>Respond</&></option>
 <option value="Comment"><&|/l&>Comment</&></option>
 <option value="Resolve"><&|/l&>Resolve</&></option>
+% }
 </select>
 </span>
 </div>
@@ -159,4 +161,5 @@ $selected{$_}++ for grep {defined} @{ $selected };
 <%ARGS>
 $CurrentFormat => undef
 $AvailableColumns => undef
+$IncludeTicketLinks => 1
 </%ARGS>
diff --git a/share/static/js/assets.js b/share/static/js/assets.js
index 853ba86..8d5aaa0 100644
--- a/share/static/js/assets.js
+++ b/share/static/js/assets.js
@@ -34,6 +34,25 @@ jQuery(function() {
             showModal
         );
     });
+    jQuery("#bulk-update-create-linked-ticket").click(function(ev){
+        ev.preventDefault();
+        var chkArray = [];
+
+        jQuery("input[name='UpdateAsset']:checked").each(function() {
+            chkArray.push(jQuery(this).val());
+        });
+
+        var selected = '';
+        for (var i = 0; i < chkArray.length; i++) {
+            selected += 'Asset=' + chkArray[i] + '&';
+        }
+        /* selected = chkArray.join(','); */
+        var url = RT.Config.WebHomePath + '/Asset/Helpers/CreateLinkedTicket?' + selected;
+        jQuery.post(
+            url,
+            showModal
+        );
+    });
     jQuery("#assets-create").click(function(ev){
         ev.preventDefault();
         jQuery.get(