[Rt-commit] rt branch, 4.4/core-assets, repushed

Todd Wade todd at bestpractical.com
Sat Oct 24 02:47:18 EDT 2015


The branch 4.4/core-assets was deleted and repushed:
       was dcee68733a9bc5409bff43ecb117b512c77fefad
       now 1e8dc8f2b9de3fb07d92c676cd22c895798e589d

 1:  791cad5 !  1:  1e8dc8f core RT::Extension::Assets
    @@ -26,11 +26,12 @@
                       sbin/rt-attributes-viewer
                       sbin/rt-preferences-viewer
     
    -diff --git a/docs/customizing/assets_introduction.pod b/docs/customizing/assets_introduction.pod
    +diff --git a/docs/assets.pod b/docs/assets.pod
     new file mode 100644
     --- /dev/null
    -+++ b/docs/customizing/assets_introduction.pod
    -@@
    ++++ b/docs/assets.pod
    +@@
    ++=encoding utf-8
     +
     +=head1 CONFIGURATION
     +
    @@ -67,14 +68,14 @@
     +
     +=head2 Adding fields
     +
    -+You can see the current asset CFs by navigating to Admin >
    -+Assets > Custom Fields.  From there you can use the "Create" link to create a
    ++You can see the current asset CFs by navigating to Admin →
    ++Assets → Custom Fields.  From there you can use the "Create" link to create a
     +new asset CF.  If you know you want to create a new CF right away, you can do
    -+so via Admin > Assets > Custom Fields > Create.
    ++so via Admin → Assets → Custom Fields → Create.
     +
     +When creating a CF, be sure to select "Assets" in the "Applies To" dropdown.
     +You'll also need to grant rights to the groups and/or roles which need to see
    -+the fields, otherwise they'll be hidden.  See the following section.
    ++the fields, otherwise they'll be hidden.  See the following section, Rights.
     +
     +Similar to ticket CFs, asset custom fields are added globally or to specific
     +catalogs.  Only assets within those specific catalogs will have the CFs
    @@ -90,7 +91,7 @@
     +
     +=head3 ShowAsset
     +
    -+Allows viewing an asset record and it's core fields (but not CFs).  Without
    ++Allows viewing an asset record and its core fields (but not CFs).  Without
     +it, no assets can be seen.  Similar to ShowTicket.
     +
     +=head3 CreateAsset
    @@ -188,7 +189,7 @@
     +lifecycle is named "assets".  You're free to modify it as much as you'd like,
     +or add your own lifecycles.  Each catalog may have its own lifecycle.
     +
    -+For the default "assets" configuration, see F<etc/Assets_Config.pm>.
    ++For the default "assets" configuration, see F<etc/RT_Config.pm>.
     +
     +=head2 Field organization
     +
    @@ -243,17 +244,18 @@
     +
     +=cut
     
    -diff --git a/docs/customizing/assets_tutorial.pod b/docs/customizing/assets_tutorial.pod
    +diff --git a/docs/customizing/assets/tutorial.pod b/docs/customizing/assets/tutorial.pod
     new file mode 100644
     --- /dev/null
    -+++ b/docs/customizing/assets_tutorial.pod
    -@@
    ++++ b/docs/customizing/assets/tutorial.pod
    +@@
    ++=encoding utf-8
     +
     +=head1 Introduction
     +
    -+This is a basic tutorial for setting up asset tracking in RT using Best
    -+Practical's Assets extension. At the end, you'll have a basic configuration
    -+that lets you add assets, search for them, link them to tickets, etc.
    ++This is a basic tutorial for setting up asset tracking in RT using the Assets
    ++functionality. At the end, you'll have a basic configuration that lets you add
    ++assets, search for them, link them to tickets, etc.
     +
     +=begin HTML
     +
    @@ -265,16 +267,13 @@
     +You can follow along with the tutorial and try setting things up yourself to
     +get a feel for all of the administrative controls. If you want to get a jump
     +start, the files to set up this basic configuration are provided in the
    -+L<RT::Extension::Assets> distribution in the F<etc> directory. For
    -+configuration, look in F<etc/Tutorial_Configuration.txt>. You can copy all or
    -+part of it and paste it into your F<RT_SiteConfig.pm>.
    -+
    -+To load the test catalog, custom fields, and users, follow the
    -+installation instructions in L<RT::Extension::Assets/INSTALLATION>, then
    -+run the following from your RT directory:
    ++F<docs/customizing/assets> directory.
    ++
    ++To load the test catalog, custom fields, and users, run the following from
    ++your RT directory:
     +
     +    sbin/rt-setup-database --action insert --datafile \
    -+      local/plugins/RT-Extension-Assets/etc/tutorialdata
    ++      docs/customizing/assets/tutorialdata
     +
     +This will change the default catalog name, create some users, and give those
     +users asset permissions. Only asset rights are granted, so you need to grant
    @@ -285,32 +284,26 @@
     +
     +=head1 Getting Started
     +
    -+Install the extension following the instructions and some new tables will be
    -+added to your RT database and the assets code will be installed. As with all
    -+extensions, first add C<RT::Extension::Assets> to your C<@Plugins> line.
    -+
     +There are a few configuration options you might set before starting. Assets
     +offers a C<$DefaultCatalog> feature that works similar to RT's
     +L<DefaultQueue|http://bestpractical.com/docs/rt/latest/RT_Config.html#DefaultQueue>,
    -+but you can probably skip it for now since you don't have any catalogs yet.
    ++but you can probably skip it for now since you only have the one catalog so far.
     +
     +More interesting are some optional portlets you can activate to add asset data
     +to RT's pages. MyAssets and FindAsset portlets are available for placement on
     +RT at a Glance or in dashboards and a UserAssets portlet is available for the
     +user summary pages.
     +
    -+These portlets are fairly self-explanatory and you can add them by finding
    -+C<$HomepageComponents> and C<@UserSummaryPortlets> respectively in
    -+F<RT_Config.pm>, copying to F<RT_SiteConfig.pm>, and adding the portlets you
    -+want. There are also examples in the tutorial sample configuration file. Note
    -+that C<$HomepageComponents> makes the portlets available, but doesn't put them
    -+on RT at a Glance. To add them, just click the Edit link on the upper right-hand
    -+corner of the homepage. C<@UserSummaryPortlets> does automatically add the
    -+"Assigned Assets" portlet to the User Summary page. It will appear based on the
    -+position in the configuration, so just place it in the list where you want it
    -+to appear.
    -+
    -+Once you have your configuration complete, restart your server and you're ready
    ++These portlets are fairly self-explanatory and for reference you can find them
    ++in C<$HomepageComponents> and C<@UserSummaryPortlets> respectively in
    ++F<RT_Config.pm>. Note that C<$HomepageComponents> makes the portlets available,
    ++but doesn't put them on RT at a Glance. To add them, just click the Edit link on
    ++the upper right-hand corner of the homepage. C<@UserSummaryPortlets> does
    ++automatically add the "Assigned Assets" portlet to the User Summary page. It
    ++will appear based on the position in the configuration, so to move it rearrange
    ++the list to where you want it to appear.
    ++
    ++If you've made any configuration changes, restart your server and you're ready
     +to go.
     +
     +=head1 Catalogs
    @@ -323,7 +316,7 @@
     +to look at catalogs and some other configuration. Catalogs are to assets what
     +queues are to tickets, so if you've used RT, the relationship should be fairly
     +familiar. Similar to the General queue, a "General assets" catalog is provided
    -+to get you started. You can see it at Admin > Assets > Catalogs.
    ++to get you started. You can see it at Admin → Assets → Catalogs.
     +
     +We're going to use the default, but change it to a name more appropriate for
     +our use. Clicking on the asset name brings us to the catalog edit page and we
    @@ -337,26 +330,22 @@
     +
     +=end HTML
     +
    -+You'll also notice that catalogs have a lifecycle just like queues. The assets
    -+extension comes with a default assets lifecycle, but just like queues you can
    -+create new ones with custom statuses and other configuration to allow RT to
    ++You'll also notice that catalogs have a lifecycle just like queues. Request
    ++tracker is installed with a default assets lifecycle, but just like queues you
    ++can create new ones with custom statuses and other configuration to allow RT to
     +reflect the states of your assets.
     +
    -+You can find the asset lifecycle in the asset configuration file in your RT
    -+installation at:
    -+
    -+    local/plugins/RT-Extension-Assets/etc/Assets_Config.pm
    -+
    -+The initial statuses are new, allocated, in-use, recycled, stolen, and deleted.
    -+Depending on your process, you might add new ones like surplussed, donated, or
    -+in-repair. To create a new asset lifecycle, just copy the default into
    -+F<RT_SiteConfig.pm>, replace the top-level "assets" key with a new name, and
    -+make your changes.
    ++You can find the asset lifecycle in the F<RT_Config.pm> file in your RT
    ++installation. The initial statuses are new, allocated, in-use, recycled,
    ++stolen, and deleted. Depending on your process, you might add new ones like
    ++surplussed, donated, or in-repair. To create a new asset lifecycle, just copy
    ++the default into F<RT_SiteConfig.pm>, replace the top-level "assets" key with
    ++a new name, and make your changes.
     +
     +=head1 Asset Custom Fields
     +
     +Next we need to create some custom fields to hold our asset metadata. You can
    -+find asset custom fields at Admin > Assets > Custom Fields and they work just
    ++find asset custom fields at Admin → Assets → Custom Fields and they work just
     +like custom fields for other RT objects.
     +
     +=begin HTML
    @@ -366,8 +355,8 @@
     +
     +=end HTML
     +
    -+The extension will automatically provide some core values for your assets. Each
    -+asset can have a Name and Description and, like tickets, they have statuses
    ++Request Tracker will automatically provide some core values for your assets.
    ++Each asset can have a Name and Description and, like tickets, they have statuses
     +based on the lifecycle configuration. You can use Name and Description however
     +you want and they are not required. However, many of the asset pages use these
     +fields so it's best to provide a descriptive name to make it easy for people
    @@ -426,7 +415,7 @@
     +
     +Any custom fields you create will be displayed on the asset display page in a
     +default "Custom Fields" section. That may be sufficient, but assets also
    -+supports RT's new custom field grouping feature, so we can group together some
    ++supports RT's custom field grouping feature, so we can group together some
     +similar custom fields and give them a custom name. If we add the following to
     +F<RT_SiteConfig.pm>:
     +
    @@ -458,8 +447,8 @@
     +rights individually on each custom field if you wanted to allow users to see
     +some but not others.
     +
    -+Similar to queues, you can set rights at the catalog level. Go to Admin >
    -+Assets > Catalogs and click on the catalog you want to edit. Click Group Rights
    ++Similar to queues, you can set rights at the catalog level. Go to Admin →
    ++Assets → Catalogs and click on the catalog you want to edit. Click Group Rights
     +in the submenu to assign asset rights to groups like the system Privileged
     +group.
     +
    @@ -530,11 +519,11 @@
     +
     +=head2 End User Asset Tickets
     +
    -+If an end user contacts us with some problems with their laptop, RT makes it
    ++If an end user contacts us with some problem with their laptop, RT makes it
     +easy to find the correct laptop record and create a ticket for them. Since our
     +support staff do this frequently, they have added the Find User portlet to
     +their RT at a glance page and can quickly search for the user and go to their
    -+User Summary page (new in RT 4.2).
    ++User Summary page.
     +
     +We have added the Assigned Assets portlet to the User Summary page, so the
     +laptop is right there on the page when we find the user. We can just click on
    @@ -569,16 +558,131 @@
     +
     +=head1 Summary
     +
    -+This tutorial is only a quick overview showing how the assets extension can
    ++This tutorial is only a quick overview showing how the assets functionality can
     +help you track assets. There are many more features you'll find as you explore
     +the assets interface, like stacking multiple assets on a single ticket, bulk
     +update features similar to tickets, and the search interface. Have fun!
     +
     +=cut
     
    +diff --git a/docs/customizing/assets/tutorialdata b/docs/customizing/assets/tutorialdata
    +new file mode 100644
    +--- /dev/null
    ++++ b/docs/customizing/assets/tutorialdata
    +@@
    ++ at Users = (
    ++    {  Name         => 'asset-tutorial-staff',
    ++       RealName     => 'Asset Staff User',
    ++       Password     => 'password',
    ++       EmailAddress => "asset-tutorial-staff\@localhost",
    ++       Comments     => "Sample Staff user for Assets tutorial",
    ++       Privileged   => '1',
    ++    },
    ++    {  Name         => 'asset-tutorial-user1',
    ++       RealName     => 'Asset End User',
    ++       Password     => 'password',
    ++       EmailAddress => "asset-tutorial-user1\@localhost",
    ++       Comments     => "Sample end user for Assets tutorial",
    ++    },
    ++);
    ++
    ++ at CustomFields = (
    ++    {   Name       => 'Serial Number',
    ++        Type       => 'FreeformSingle',
    ++        LookupType => 'RT::Catalog-RT::Asset',
    ++        ApplyTo    => 'General assets',
    ++    },
    ++    {   Name       => 'Tracking Number',
    ++        Type       => 'FreeformSingle',
    ++        LookupType => 'RT::Catalog-RT::Asset',
    ++        ApplyTo    => 'General assets',
    ++    },
    ++    {   Name       => 'Manufacturer',
    ++        Type       => 'SelectSingle',
    ++        LookupType => 'RT::Catalog-RT::Asset',
    ++        ApplyTo    => 'General assets',
    ++        MaxValues  => 1,
    ++        RenderType => 'Dropdown',
    ++        Values      => [
    ++            { Name => 'Apple', SortOrder => 1 },
    ++            { Name => 'Dell', SortOrder => 2 }, ],
    ++    },
    ++    {   Name       => 'Type',
    ++        Type       => 'SelectSingle',
    ++        LookupType => 'RT::Catalog-RT::Asset',
    ++        ApplyTo    => 'General assets',
    ++        MaxValues  => 1,
    ++        RenderType => 'Dropdown',
    ++        Values      => [
    ++            { Name => 'Desktop Computer', SortOrder => 1 },
    ++            { Name => 'Laptop Computer', SortOrder => 2 },
    ++            { Name => 'Server', SortOrder => 3 },
    ++            { Name => 'Mobile Phone', SortOrder => 4 },
    ++            { Name => 'Software', SortOrder => 5 },
    ++            { Name => 'Other', SortOrder => 6 }, ],
    ++    },
    ++    {   Name    => 'Issue Date',
    ++        Type    => 'Date',
    ++        LookupType => 'RT::Catalog-RT::Asset',
    ++        ApplyTo    => 'General assets',
    ++    },
    ++    {   Name    => 'Support Expiration',
    ++        Type    => 'Date',
    ++        LookupType => 'RT::Catalog-RT::Asset',
    ++        ApplyTo    => 'General assets',
    ++    },
    ++);
    ++
    ++push @ACL, map {
    ++        {
    ++            Right       => $_,
    ++            GroupDomain => 'SystemInternal',
    ++            GroupType   => 'Privileged',
    ++            ObjectType  => 'RT::Catalog',
    ++            ObjectId    => 'General assets',
    ++        }
    ++    } qw(ShowAsset ShowCatalog SeeCustomField CreateAsset
    ++         ModifyAsset ModifyCustomField);
    ++
    ++push @ACL, map {
    ++        {
    ++            Right       => $_,
    ++            GroupDomain => 'RT::System-Role',
    ++            GroupType   => 'HeldBy',
    ++        }
    ++    } qw(ShowAsset ShowCatalog);
    ++
    ++push @Final, sub {
    ++    # Update default catalog name
    ++    my $catalog = RT::Catalog->new(RT->SystemUser);
    ++    my ($ret, $msg) = $catalog->Load('General assets');
    ++    RT::Logger->error("Unable to load General assets catalog: $msg")
    ++        unless $ret;
    ++    $catalog->SetName('IT Department Assets');
    ++    return;
    ++};
    +
     diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
     --- a/etc/RT_Config.pm.in
     +++ b/etc/RT_Config.pm.in
    +@@
    + 
    + =cut
    + 
    +-Set(@UserSummaryPortlets, (qw/ExtraInfo CreateTicket ActiveTickets InactiveTickets/));
    ++Set(@UserSummaryPortlets, (qw/ExtraInfo CreateTicket ActiveTickets InactiveTickets UserAssets /));
    + 
    + =item C<$UserSummaryExtraInfo>
    + 
    +@@
    + Set(
    +     $HomepageComponents,
    +     [
    +-        qw(QuickCreate Quicksearch MyAdminQueues MySupportQueues MyReminders RefreshHomepage Dashboards SavedSearches FindUser) # loc_qw
    ++        qw(QuickCreate Quicksearch MyAdminQueues MySupportQueues MyReminders RefreshHomepage Dashboards SavedSearches FindUser MyAssets FindAsset) # loc_qw
    +     ]
    + );
    + 
     @@
      
      =back
    @@ -687,21 +791,6 @@
     +    Status,
     +]) unless $AssetSummaryRelatedTicketsFormat;
     +
    -+=item C<%AdminSearchResultFormat>
    -+
    -+The C<Catalogs> key of this standard RT configuration option (see
    -+L<RT_Config/%AdminSearchResultFormat>) controls how catalogs are
    -+displayed in their list in the admin pages.
    -+
    -+=cut
    -+
    -+Set(%AdminSearchResultFormat,
    -+    Catalogs =>
    -+        q{'<a href="__WebPath__/Admin/Assets/Catalogs/Modify.html?id=__id__">__id__</a>/TITLE:#'}
    -+        .q{,'<a href="__WebPath__/Admin/Assets/Catalogs/Modify.html?id=__id__">__Name__</a>/TITLE:Name'}
    -+        .q{,__Description__,__Lifecycle__,__Disabled__},
    -+) unless $AdminSearchResultFormat{Catalogs};
    -+
     +=item C<$AssetBasicCustomFieldsOnCreate>
     +
     +Specify a list of Asset custom fields to show in "Basics" widget on create.
    @@ -731,6 +820,9 @@
                  'deleted -> open'  => { label  => 'Undelete',                    }, # loc{label}
              ],
          },
    +-);
    +-
    +-
     +    assets => {
     +        type     => "asset",
     +        initial  => [ 
    @@ -745,11 +837,11 @@
     +            'stolen', # loc
     +            'deleted' # loc
     +        ],
    -+
    + 
     +        defaults => {
     +            on_create => 'new',
     +        },
    -+
    + 
     +        transitions => {
     +            ''        => [qw(new allocated in-use)],
     +            new       => [qw(allocated in-use stolen deleted)],
    @@ -777,85 +869,66 @@
     +            },
     +        },
     +    },
    ++);
    + 
    + =head1 Administrative interface
    + 
    +@@
    +         q{ '<a href="__WebPath__/Admin/Articles/Classes/Modify.html?id=__id__">__id__</a>/TITLE:#'}
    +         .q{,'<a href="__WebPath__/Admin/Articles/Classes/Modify.html?id=__id__">__Name__</a>/TITLE:Name'}
    +         .q{,__Description__,__Disabled__},
    ++
    ++    Catalogs =>
    ++        q{'<a href="__WebPath__/Admin/Assets/Catalogs/Modify.html?id=__id__">__id__</a>/TITLE:#'}
    ++        .q{,'<a href="__WebPath__/Admin/Assets/Catalogs/Modify.html?id=__id__">__Name__</a>/TITLE:Name'}
    ++        .q{,__Description__,__Lifecycle__,__Disabled__},
      );
      
    + =item C<%AdminSearchResultRows>
    +@@
    +     Scrips       => 50,
    +     Templates    => 50,
    +     Classes      => 50,
    ++    Catalogs     => 50,
    ++    Assets       => 50,
    + );
      
    + =back
     
     diff --git a/etc/acl.Pg b/etc/acl.Pg
     --- a/etc/acl.Pg
     +++ b/etc/acl.Pg
     @@
    -         }
    -     }
    -     return (@acls);
    -+
    -+{ # START assets ACL
    -+    my @tables = qw (
    +         ObjectTopics
    +         objectclasses_id_seq
    +         ObjectClasses
    ++        catalogs_id_seq
    ++        Catalogs
     +        assets_id_seq
     +        Assets
    -+        catalogs_id_seq
    -+        Catalogs
    -+    );
    -+
    -+    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\";"
    -+        }
    -+    }
    -+
    -+} # END Assets ACL
    -+
    -+    return (@acls);
    - }
    +     );
      
    - 1;
    +     my $db_user = RT->Config->Get('DatabaseUser');
     
     diff --git a/etc/initialdata b/etc/initialdata
     --- a/etc/initialdata
     +++ b/etc/initialdata
     @@
    +              Description => 'A system-internal queue for the approvals system',
    +              Disabled    => 2, } );
    + 
    ++ at Catalogs = ({
    ++    Name        => "General assets",      # loc
    ++    Description => "The default catalog", # loc
    ++});
    ++
    + @ScripActions = (
    + 
    +     {  Name        => 'Autoreply To Requestors',    # loc
    +@@
              },
          },
      );
    -+
    -+require RT::Asset;
    -+# Create global role groups
    -+push @Final, sub {
    -+    foreach my $type (RT::Asset->Roles) {
    -+        next if $type eq "Owner";   # There's a core global role group for Owner
    -+
    -+        my $group = RT::Group->new( RT->SystemUser );
    -+        my ($ok, $msg) = $group->CreateRoleGroup(
    -+            Object              => RT->System,
    -+            Name                => $type,
    -+            InsideTransaction   => 0,
    -+        );
    -+        RT->Logger->error("Couldn't create global asset role group '$type': $msg")
    -+            unless $ok;
    -+    }
    -+};
    -+
    -+# Create default catalog
    -+push @Final, sub {
    -+    my $catalog = RT::Catalog->new( RT->SystemUser );
    -+    my ($ok, $msg) = $catalog->Create(
    -+        Name        => "General assets",
    -+        Description => "The default catalog",
    -+    );
    -+    RT->Logger->error("Couldn't create default catalog 'General assets': $msg")
    -+        unless $ok;
    -+};
     +
     +1;
     
    @@ -1019,20 +1092,76 @@
     +CREATE INDEX CatalogsName ON Catalogs (Name);
     +CREATE INDEX CatalogsDisabled ON Catalogs (Disabled);
     
    +diff --git a/etc/upgrade/4.3.10/acl.Pg b/etc/upgrade/4.3.10/acl.Pg
    +new file mode 100644
    +--- /dev/null
    ++++ b/etc/upgrade/4.3.10/acl.Pg
    +@@
    ++
    ++sub acl {
    ++    my $dbh = shift;
    ++
    ++    my @acls;
    ++
    ++    my @tables = qw (
    ++        catalogs_id_seq
    ++        Catalogs
    ++        assets_id_seq
    ++        Assets
    ++    );
    ++
    ++    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
    ++        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.3.10/content b/etc/upgrade/4.3.10/content
     new file mode 100644
     --- /dev/null
     +++ b/etc/upgrade/4.3.10/content
     @@
    ++use strict;
    ++use warnings;
     +
     +require RT::Asset;
    ++
    ++our @Catalogs;
    ++
    ++ at Catalogs = ({
    ++    Name        => "General assets",      # loc
    ++    Description => "The default catalog", # loc
    ++});
    ++
    ++our @Final;
     +
     +# Create global role groups
     +push @Final, sub {
     +    foreach my $type (RT::Asset->Roles) {
     +        next if $type eq "Owner";   # There's a core global role group for Owner
     +
    -+        my $group = RT::Group->new( RT->SystemUser );
    ++        my $group = RT->System->RoleGroup( $type );
    ++        if ( $group->id ) {
    ++            RT->Logger->debug("Assets role '$type' already exists.");
    ++            next;
    ++        }
    ++
    ++        $group = RT::Group->new( RT->SystemUser );
     +        my ($ok, $msg) = $group->CreateRoleGroup(
     +            Object              => RT->System,
     +            Name                => $type,
    @@ -1041,17 +1170,6 @@
     +        RT->Logger->error("Couldn't create global asset role group '$type': $msg")
     +            unless $ok;
     +    }
    -+};
    -+
    -+# Create default catalog
    -+push @Final, sub {
    -+    my $catalog = RT::Catalog->new( RT->SystemUser );
    -+    my ($ok, $msg) = $catalog->Create(
    -+        Name        => "General assets",
    -+        Description => "The default catalog",
    -+    );
    -+    RT->Logger->error("Couldn't create default catalog 'General assets': $msg")
    -+        unless $ok;
     +};
     +
     +1;
    @@ -1280,9 +1398,9 @@
     +
     +my $dbh = $RT::Handle->dbh;
     +
    -+my $found_assets_tables;
    ++my $found_assets_tables = {};
     +foreach my $name ( $RT::Handle->_TableNames ) {
    -+    next unless $name =~ /^RTx_/i;
    ++    next unless grep $name eq $_, qw(RTxAssets RTxCatalogs);
     +    $found_assets_tables->{lc $name}++;
     +}
     +
    @@ -1343,7 +1461,7 @@
     +#
     +# COPYRIGHT:
     +#
    -+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +#                                          <sales at bestpractical.com>
     +#
     +# (Except where explicitly superseded by other copyright notices)
    @@ -1996,7 +2114,7 @@
     +#
     +# COPYRIGHT:
     +#
    -+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +#                                          <sales at bestpractical.com>
     +#
     +# (Except where explicitly superseded by other copyright notices)
    @@ -2148,17 +2266,6 @@
     +    $self->SUPER::AddRecord($asset, @_);
     +}
     +
    -+=head2 NewItem
    -+
    -+Returns a new empty RT::Asset item
    -+
    -+=cut
    -+
    -+sub NewItem {
    -+    my $self = shift;
    -+    return RT::Asset->new( $self->CurrentUser );
    -+}
    -+
     +=head1 PRIVATE METHODS
     +
     +=head2 _Init
    @@ -2313,7 +2420,7 @@
     +#
     +# COPYRIGHT:
     +#
    -+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +#                                          <sales at bestpractical.com>
     +#
     +# (Except where explicitly superseded by other copyright notices)
    @@ -2822,7 +2929,7 @@
     +#
     +# COPYRIGHT:
     +#
    -+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +#                                          <sales at bestpractical.com>
     +#
     +# (Except where explicitly superseded by other copyright notices)
    @@ -2915,17 +3022,6 @@
     +    $self->SUPER::AddRecord($catalog, @_);
     +}
     +
    -+=head2 NewItem
    -+
    -+Returns a new empty RT::Catalog item
    -+
    -+=cut
    -+
    -+sub NewItem {
    -+    my $self = shift;
    -+    return RT::Catalog->new( $self->CurrentUser );
    -+}
    -+
     +=head1 PRIVATE METHODS
     +
     +=head2 _Init
    @@ -3087,6 +3183,93 @@
      
      1;
     
    +diff --git a/lib/RT/Handle.pm b/lib/RT/Handle.pm
    +--- a/lib/RT/Handle.pm
    ++++ b/lib/RT/Handle.pm
    +@@
    +         return ($val, $msg) unless $val;
    +     }
    + 
    ++    # assets role groups
    ++    foreach my $name (RT::Asset->Roles) {
    ++        next if $name eq "Owner";
    ++
    ++        my $group = RT->System->RoleGroup( $name );
    ++        if ( $group->id ) {
    ++            push @warns, "Assets role '$name' already exists.";
    ++            next;
    ++        }
    ++
    ++        $group = RT::Group->new( RT->SystemUser );
    ++        my ($val, $msg) = $group->CreateRoleGroup(
    ++            Object              => RT->System,
    ++            Name                => $name,
    ++            InsideTransaction   => 0,
    ++        );
    ++        return ($val, $msg) unless $val;
    ++    }
    ++
    +     push @warns, "You appear to have a functional RT database."
    +         if @warns;
    + 
    +@@
    + 
    +     # Slurp in stuff to insert from the datafile. Possible things to go in here:-
    +     our (@Groups, @Users, @Members, @ACL, @Queues, @ScripActions, @ScripConditions,
    +-           @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
    ++           @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final,
    ++           @Catalogs, @Assets);
    +     local (@Groups, @Users, @Members, @ACL, @Queues, @ScripActions, @ScripConditions,
    +-           @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final);
    +-
    ++           @Templates, @CustomFields, @Scrips, @Attributes, @Initial, @Final,
    ++           @Catalogs, @Assets);
    +     local $@;
    +     $RT::Logger->debug("Going to load '$datafile' data file");
    +     eval { require $datafile }
    +@@
    +         }
    +         $RT::Logger->debug("done.");
    +     }
    ++
    ++    if ( @Catalogs ) {
    ++        $RT::Logger->debug("Creating Catalogs...");
    ++
    ++        for my $item (@Catalogs) {
    ++            my $new_entry = RT::Catalog->new(RT->SystemUser);
    ++            my ( $return, $msg ) = $new_entry->Create(%$item);
    ++            unless ( $return ) {
    ++                $RT::Logger->error( $msg );
    ++            }
    ++            else {
    ++                $RT::Logger->debug( $return ."." );
    ++            }
    ++        }
    ++
    ++        $RT::Logger->debug("done.");
    ++    }
    ++    if ( @Assets ) {
    ++        $RT::Logger->debug("Creating Assets...");
    ++
    ++        for my $item (@Catalogs) {
    ++            my $new_entry = RT::Asset->new(RT->SystemUser);
    ++            my ( $return, $msg ) = $new_entry->Create(%$item);
    ++            unless ( $return ) {
    ++                $RT::Logger->error( $msg );
    ++            }
    ++            else {
    ++                $RT::Logger->debug( $return ."." );
    ++            }
    ++        }
    ++
    ++        $RT::Logger->debug("done.");
    ++    }
    ++
    ++
    +     if ( @CustomFields ) {
    +         $RT::Logger->debug("Creating custom fields...");
    +         for my $item ( @CustomFields ) {
    +
     diff --git a/lib/RT/Interface/Web.pm b/lib/RT/Interface/Web.pm
     --- a/lib/RT/Interface/Web.pm
     +++ b/lib/RT/Interface/Web.pm
    @@ -3134,7 +3317,7 @@
     +    return $asset;
     +}
     +
    -+sub ProcessRoleMembers {
    ++sub ProcessAssetRoleMembers {
     +    my $object = shift;
     +    my %ARGS   = (@_);
     +    my @results;
    @@ -3359,7 +3542,7 @@
     +#
     +# COPYRIGHT:
     +#
    -+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +#                                          <sales at bestpractical.com>
     +#
     +# (Except where explicitly superseded by other copyright notices)
    @@ -3453,122 +3636,11 @@
     --- /dev/null
     +++ b/lib/RT/Test/Assets.pm
     @@
    -+use strict;
    -+use warnings;
    -+
    -+package RT::Test::Assets;
    -+use base 'RT::Test';
    -+
    -+our @EXPORT = qw(create_catalog create_asset create_assets create_cf apply_cfs);
    -+
    -+sub import {
    -+    my $class = shift;
    -+    my %args  = @_;
    -+
    -+    $class->SUPER::import( %args );
    -+    __PACKAGE__->export_to_level(1);
    -+}
    -+
    -+sub diag {
    -+    Test::More::diag(@_) if $ENV{TEST_VERBOSE};
    -+}
    -+
    -+sub create_catalog {
    -+    my %info  = @_;
    -+    my $catalog = RT::Catalog->new( RT->SystemUser );
    -+    my ($id, $msg) = $catalog->Create( %info );
    -+    if ($id) {
    -+        diag("Created catalog #$id: " . $catalog->Name);
    -+        return $catalog;
    -+    } else {
    -+        my $spec = join "/", map { "$_=$info{$_}" } keys %info;
    -+        RT->Logger->error("Failed to create catalog ($spec): $msg");
    -+        return;
    -+    }
    -+}
    -+
    -+sub create_asset {
    -+    my %info  = @_;
    -+    my $asset = RT::Asset->new( RT->SystemUser );
    -+    my ($id, $msg) = $asset->Create( %info );
    -+    if ($id) {
    -+        diag("Created asset #$id: " . $asset->Name);
    -+        return $asset;
    -+    } else {
    -+        my $spec = join "/", map { "$_=$info{$_}" } keys %info;
    -+        RT->Logger->error("Failed to create asset ($spec): $msg");
    -+        return;
    -+    }
    -+}
    -+
    -+sub create_assets {
    -+    my $error = 0;
    -+    for my $info (@_) {
    -+        create_asset(%$info)
    -+            or $error++;
    -+    }
    -+    return not $error;
    -+}
    -+
    -+sub create_cf {
    -+    my %args = (
    -+        Name        => "Test Asset CF ".($$ + rand(1024)),
    -+        Type        => "FreeformSingle",
    -+        LookupType  => RT::Asset->CustomFieldLookupType,
    -+        @_,
    -+    );
    -+    my $cf = RT::CustomField->new( RT->SystemUser );
    -+    my ($ok, $msg) = $cf->Create(%args);
    -+    RT->Logger->error("Can't create CF: $msg") unless $ok;
    -+    return $cf;
    -+}
    -+
    -+sub apply_cfs {
    -+    my $success = 1;
    -+    for my $cf (@_) {
    -+        my ($ok, $msg) = $cf->AddToObject( RT::Catalog->new(RT->SystemUser) );
    -+        if (not $ok) {
    -+            RT->Logger->error("Couldn't apply CF: $msg");
    -+            $success = 0;
    -+        }
    -+    }
    -+    return $success;
    -+}
    -+
    -+1;
    -
    -diff --git a/lib/RT/Transaction.pm b/lib/RT/Transaction.pm
    ---- a/lib/RT/Transaction.pm
    -+++ b/lib/RT/Transaction.pm
    -@@
    -             "#reminder-", $ticket->id, \'">', $ticket->Subject, \'</a>'
    -         ];
    -         return ("Reminder '[_1]' completed", $subject); #loc()
    --    }
    -+    },
    -+    'RT::Asset-Set-Catalog' => sub {
    -+        my $self = shift;
    -+        return ("[_1] changed from [_2] to [_3]",   #loc
    -+                $self->loc($self->Field), map {
    -+                    my $c = RT::Catalog->new($self->CurrentUser);
    -+                    $c->Load($_);
    -+                    $c->Name || $self->loc("~[a hidden catalog~]")
    -+                } $self->OldValue, $self->NewValue);
    -+    },
    - );
    - 
    - 
    -
    -diff --git a/lib/RT/URI/asset.pm b/lib/RT/URI/asset.pm
    -new file mode 100644
    ---- /dev/null
    -+++ b/lib/RT/URI/asset.pm
    -@@
     +# BEGIN BPS TAGGED BLOCK {{{
     +#
     +# COPYRIGHT:
     +#
    -+# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +#                                          <sales at bestpractical.com>
     +#
     +# (Except where explicitly superseded by other copyright notices)
    @@ -3615,6 +3687,165 @@
     +use strict;
     +use warnings;
     +
    ++package RT::Test::Assets;
    ++use base 'RT::Test';
    ++
    ++our @EXPORT = qw(create_catalog create_asset create_assets create_cf apply_cfs);
    ++
    ++sub import {
    ++    my $class = shift;
    ++    my %args  = @_;
    ++
    ++    $class->SUPER::import( %args );
    ++    __PACKAGE__->export_to_level(1);
    ++}
    ++
    ++sub diag {
    ++    Test::More::diag(@_) if $ENV{TEST_VERBOSE};
    ++}
    ++
    ++sub create_catalog {
    ++    my %info  = @_;
    ++    my $catalog = RT::Catalog->new( RT->SystemUser );
    ++    my ($id, $msg) = $catalog->Create( %info );
    ++    if ($id) {
    ++        diag("Created catalog #$id: " . $catalog->Name);
    ++        return $catalog;
    ++    } else {
    ++        my $spec = join "/", map { "$_=$info{$_}" } keys %info;
    ++        RT->Logger->error("Failed to create catalog ($spec): $msg");
    ++        return;
    ++    }
    ++}
    ++
    ++sub create_asset {
    ++    my %info  = @_;
    ++    my $asset = RT::Asset->new( RT->SystemUser );
    ++    my ($id, $msg) = $asset->Create( %info );
    ++    if ($id) {
    ++        diag("Created asset #$id: " . $asset->Name);
    ++        return $asset;
    ++    } else {
    ++        my $spec = join "/", map { "$_=$info{$_}" } keys %info;
    ++        RT->Logger->error("Failed to create asset ($spec): $msg");
    ++        return;
    ++    }
    ++}
    ++
    ++sub create_assets {
    ++    my $error = 0;
    ++    for my $info (@_) {
    ++        create_asset(%$info)
    ++            or $error++;
    ++    }
    ++    return not $error;
    ++}
    ++
    ++sub create_cf {
    ++    my %args = (
    ++        Name        => "Test Asset CF ".($$ + rand(1024)),
    ++        Type        => "FreeformSingle",
    ++        LookupType  => RT::Asset->CustomFieldLookupType,
    ++        @_,
    ++    );
    ++    my $cf = RT::CustomField->new( RT->SystemUser );
    ++    my ($ok, $msg) = $cf->Create(%args);
    ++    RT->Logger->error("Can't create CF: $msg") unless $ok;
    ++    return $cf;
    ++}
    ++
    ++sub apply_cfs {
    ++    my $success = 1;
    ++    for my $cf (@_) {
    ++        my ($ok, $msg) = $cf->AddToObject( RT::Catalog->new(RT->SystemUser) );
    ++        if (not $ok) {
    ++            RT->Logger->error("Couldn't apply CF: $msg");
    ++            $success = 0;
    ++        }
    ++    }
    ++    return $success;
    ++}
    ++
    ++1;
    +
    +diff --git a/lib/RT/Transaction.pm b/lib/RT/Transaction.pm
    +--- a/lib/RT/Transaction.pm
    ++++ b/lib/RT/Transaction.pm
    +@@
    +             "#reminder-", $ticket->id, \'">', $ticket->Subject, \'</a>'
    +         ];
    +         return ("Reminder '[_1]' completed", $subject); #loc()
    +-    }
    ++    },
    ++    'RT::Asset-Set-Catalog' => sub {
    ++        my $self = shift;
    ++        return ("[_1] changed from [_2] to [_3]",   #loc
    ++                $self->loc($self->Field), map {
    ++                    my $c = RT::Catalog->new($self->CurrentUser);
    ++                    $c->Load($_);
    ++                    $c->Name || $self->loc("~[a hidden catalog~]")
    ++                } $self->OldValue, $self->NewValue);
    ++    },
    + );
    + 
    + 
    +
    +diff --git a/lib/RT/URI/asset.pm b/lib/RT/URI/asset.pm
    +new file mode 100644
    +--- /dev/null
    ++++ b/lib/RT/URI/asset.pm
    +@@
    ++# BEGIN BPS TAGGED BLOCK {{{
    ++#
    ++# COPYRIGHT:
    ++#
    ++# This software is Copyright (c) 1996-2015 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::URI::asset;
     +use base qw/RT::URI::base/;
     +
    @@ -3786,7 +4017,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -3879,7 +4110,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -3946,7 +4177,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4034,7 +4265,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4104,7 +4335,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4198,7 +4429,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4251,8 +4482,8 @@
     +  <&| /Widgets/TitleBox, title => loc("Roles"), class => "catalog-roles" &>
     +    <table width="60%" class="edit">
     +      <tr>
    -+        <td><& /Elements/Assets/EditPeople, %ARGS, Object => $catalog &></td>
    -+        <td><& /Elements/Assets/AddPeople, Object => $catalog &></td>
    ++        <td><& /Asset/Elements/EditCatalogPeople, %ARGS, Object => $catalog &></td>
    ++        <td><& /Asset/Elements/AddCatalogPeople, Object => $catalog &></td>
     +      </tr>
     +    </table>
     +  </&>
    @@ -4268,7 +4499,7 @@
     +my @results;
     +
     +if ($Update) {
    -+    push @results, ProcessRoleMembers( $catalog => %ARGS );
    ++    push @results, ProcessAssetRoleMembers( $catalog => %ARGS );
     +
     +    MaybeRedirectForResults(
     +        Actions     => \@results,
    @@ -4286,7 +4517,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4356,7 +4587,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4410,6 +4641,7 @@
     +<& /Elements/CollectionList,
     +    OrderBy => 'Name',
     +    Order => 'ASC',
    ++    Rows => $Rows,
     +    %ARGS,
     +    Format => $Format,
     +    Collection => $catalogs,
    @@ -4463,6 +4695,7 @@
     +}
     +
     +my $Format = RT->Config->Get('AdminSearchResultFormat')->{'Catalogs'};
    ++my $Rows = RT->Config->Get('AdminSearchResultRows')->{'Catalogs'} || 50;
     +</%INIT>
     +<%ARGS>
     +$FindDisabled => 0
    @@ -4480,7 +4713,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4536,7 +4769,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4596,7 +4829,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4787,7 +5020,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4844,7 +5077,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4908,7 +5141,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -4980,6 +5213,79 @@
     +$m->callback(CallbackName => 'BeforeDisplay', ARGSRef => \%ARGS, Asset => $asset);
     +</%init>
     
    +diff --git a/share/html/Asset/Elements/AddCatalogPeople b/share/html/Asset/Elements/AddCatalogPeople
    +new file mode 100644
    +--- /dev/null
    ++++ b/share/html/Asset/Elements/AddCatalogPeople
    +@@
    ++%# BEGIN BPS TAGGED BLOCK {{{
    ++%#
    ++%# COPYRIGHT:
    ++%#
    ++%# This software is Copyright (c) 1996-2015 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 }}}
    ++<%args>
    ++$Object
    ++</%args>
    ++<div class="add-user">
    ++<h3><&|/l&>Add a person</&></h3>
    ++<& SelectRoleType, Object => $Object, Name => "AddUserRoleMember-Role" &>
    ++<input type="text" name="AddUserRoleMember"
    ++       data-autocomplete="Users"
    ++       data-autocomplete-return="Name"
    ++       placeholder="<% loc("Find a user...") %>">
    ++</div>
    ++
    ++<div class="add-group">
    ++<h3><&|/l&>Add a group</&></h3>
    ++<& SelectRoleType, Object => $Object, Name => "AddGroupRoleMember-Role" &>
    ++<input type="text" name="AddGroupRoleMember"
    ++       data-autocomplete="Groups"
    ++       data-autocomplete-return="Name"
    ++       placeholder="<% loc("Find a group...") %>">
    ++</div>
    +
     diff --git a/share/html/Asset/Elements/AssetSearchBasics b/share/html/Asset/Elements/AssetSearchBasics
     new file mode 100644
     --- /dev/null
    @@ -4989,7 +5295,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5082,7 +5388,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5160,7 +5466,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5249,7 +5555,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5308,7 +5614,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5392,7 +5698,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5474,6 +5780,71 @@
     +                  $AssetObj->WritableAttributes;
     +</%init>
     
    +diff --git a/share/html/Asset/Elements/EditCatalogPeople b/share/html/Asset/Elements/EditCatalogPeople
    +new file mode 100644
    +--- /dev/null
    ++++ b/share/html/Asset/Elements/EditCatalogPeople
    +@@
    ++%# BEGIN BPS TAGGED BLOCK {{{
    ++%#
    ++%# COPYRIGHT:
    ++%#
    ++%# This software is Copyright (c) 1996-2015 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 }}}
    ++<%args>
    ++$Object
    ++</%args>
    ++<%init>
    ++</%init>
    ++% for my $role ($Object->Roles( ACLOnly => 0 )) {
    ++<div class="role-<% CSSClass($role) %> role">
    ++  <h3><% loc($role) %></h3>
    ++  <& EditRoleMembers, Group => $Object->RoleGroup($role) &>
    ++</div>
    ++% }
    ++<em><&|/l&>(Check box to delete)</&></em>
    +
     diff --git a/share/html/Asset/Elements/EditDates b/share/html/Asset/Elements/EditDates
     new file mode 100644
     --- /dev/null
    @@ -5483,7 +5854,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5561,7 +5932,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5622,6 +5993,150 @@
     +$AssetObj
     +</%args>
     
    +diff --git a/share/html/Asset/Elements/EditRoleMembers b/share/html/Asset/Elements/EditRoleMembers
    +new file mode 100644
    +--- /dev/null
    ++++ b/share/html/Asset/Elements/EditRoleMembers
    +@@
    ++%# BEGIN BPS TAGGED BLOCK {{{
    ++%#
    ++%# COPYRIGHT:
    ++%#
    ++%# This software is Copyright (c) 1996-2015 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 }}}
    ++<%args>
    ++$Group       => undef
    ++$Recursively => 0
    ++</%args>
    ++<%init>
    ++my $field_name = "RemoveRoleMember-" . $Group->Name;
    ++</%init>
    ++<ul class="role-members">
    ++% my $Users = $Group->UserMembersObj( Recursively => $Recursively );
    ++% if ($Group->SingleMemberRoleGroup) {
    ++<input type="text" value="<% $Users->First->Name %>" name="SetRoleMember-<% $Group->Name %>" id="SetRoleMember-<% $Group->Name %>" data-autocomplete="Users" data-autocomplete-return="Name" /><br />
    ++% } else {
    ++% while ( my $user = $Users->Next ) {
    ++<li>
    ++  <label>
    ++    <input type="checkbox" name="<% $field_name %>" value="<% $user->PrincipalId %>">
    ++    <& /Elements/ShowUser, User => $user &>
    ++  </label>
    ++</li>
    ++% }
    ++% my $Groups = $Group->GroupMembersObj( Recursively => $Recursively );
    ++% $Groups->LimitToUserDefinedGroups;
    ++% while (my $group = $Groups->Next) {
    ++<li>
    ++  <label>
    ++    <input type="checkbox" name="<% $field_name %>" value="<% $group->PrincipalId %>">
    ++    <&|/l&>Group</&>: <% $group->Name %>
    ++  </label>
    ++</li>
    ++% }
    ++% }
    ++</ul>
    +
    +diff --git a/share/html/Asset/Elements/Search b/share/html/Asset/Elements/Search
    +new file mode 100644
    +--- /dev/null
    ++++ b/share/html/Asset/Elements/Search
    +@@
    ++%# BEGIN BPS TAGGED BLOCK {{{
    ++%#
    ++%# COPYRIGHT:
    ++%#
    ++%# This software is Copyright (c) 1996-2015 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 }}}
    ++<form ACTION="<% RT->Config->Get('WebPath') %>/Asset/Search/">
    ++  <input size="12" name="q" accesskey="0" class="field" value="<% $value %>" placeholder="<&|/l&>Search Assets</&>..." />
    ++</form>
    ++<%init>
    ++my $value = defined $DECODED_ARGS->{q} ? $DECODED_ARGS->{q} : '';
    ++</%init>
    +
     diff --git a/share/html/Asset/Elements/SelectCatalog b/share/html/Asset/Elements/SelectCatalog
     new file mode 100644
     --- /dev/null
    @@ -5631,7 +6146,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5697,6 +6212,72 @@
     +}
     +</%init>
     
    +diff --git a/share/html/Asset/Elements/SelectRoleType b/share/html/Asset/Elements/SelectRoleType
    +new file mode 100644
    +--- /dev/null
    ++++ b/share/html/Asset/Elements/SelectRoleType
    +@@
    ++%# BEGIN BPS TAGGED BLOCK {{{
    ++%#
    ++%# COPYRIGHT:
    ++%#
    ++%# This software is Copyright (c) 1996-2015 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 }}}
    ++<%args>
    ++$Object
    ++$Name
    ++$AllowNull  => 0
    ++</%args>
    ++<select name="<% $Name %>">
    ++% if ($AllowNull) {
    ++  <option value=""></option>
    ++% }
    ++% for my $role ($Object->Roles( ACLOnly => 0, Single => 0 )) {
    ++  <option value="<% $role %>"><% loc($role) %></option>
    ++% }
    ++</select>
    +
     diff --git a/share/html/Asset/Elements/SelectStatus b/share/html/Asset/Elements/SelectStatus
     new file mode 100644
     --- /dev/null
    @@ -5706,7 +6287,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5778,7 +6359,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5854,7 +6435,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -5929,7 +6510,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6007,7 +6588,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6061,7 +6642,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6143,7 +6724,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6239,7 +6820,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6328,7 +6909,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6460,7 +7041,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6515,7 +7096,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6577,7 +7158,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6647,7 +7228,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6778,7 +7359,7 @@
     +</%init>
     
     diff --git a/share/html/Asset/ModifyCFs.html b/share/html/Asset/ModifyCFs.html
    -new file mode 100755
    +new file mode 100644
     --- /dev/null
     +++ b/share/html/Asset/ModifyCFs.html
     @@
    @@ -6786,7 +7367,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6889,7 +7470,7 @@
     +</%init>
     
     diff --git a/share/html/Asset/ModifyDates.html b/share/html/Asset/ModifyDates.html
    -new file mode 100755
    +new file mode 100644
     --- /dev/null
     +++ b/share/html/Asset/ModifyDates.html
     @@
    @@ -6897,7 +7478,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -6992,7 +7573,7 @@
     +</%args>
     
     diff --git a/share/html/Asset/ModifyLinks.html b/share/html/Asset/ModifyLinks.html
    -new file mode 100755
    +new file mode 100644
     --- /dev/null
     +++ b/share/html/Asset/ModifyLinks.html
     @@
    @@ -7000,7 +7581,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -7096,7 +7677,7 @@
     +</%args>
     
     diff --git a/share/html/Asset/ModifyPeople.html b/share/html/Asset/ModifyPeople.html
    -new file mode 100755
    +new file mode 100644
     --- /dev/null
     +++ b/share/html/Asset/ModifyPeople.html
     @@
    @@ -7104,7 +7685,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -7161,8 +7742,8 @@
     +  <&| /Widgets/TitleBox, title => loc("People"), class => "asset-people", title_class => "inverse" &>
     +    <table width="100%" class="edit">
     +      <tr>
    -+        <td width="30%"><& /Elements/Assets/EditPeople, %ARGS, Object => $asset &></td>
    -+        <td width="30%"><& /Elements/Assets/AddPeople, Object => $asset &></td>
    ++        <td width="30%"><& /Asset/Elements/EditCatalogPeople, %ARGS, Object => $asset &></td>
    ++        <td width="30%"><& /Asset/Elements/AddCatalogPeople, Object => $asset &></td>
     +        <td>
     +          <& /Elements/EditCustomFields, Object => $asset, Grouping => 'People', AsTable => 1 &>
     +        </td>
    @@ -7192,7 +7773,7 @@
     +    );
     +
     +    if ($cf_ok) {
    -+        push @results, ProcessRoleMembers( $asset => %ARGS );
    ++        push @results, ProcessAssetRoleMembers( $asset => %ARGS );
     +        push @results, ProcessObjectCustomFieldUpdates( Object => $asset, ARGSRef => \%ARGS );
     +
     +        MaybeRedirectForResults(
    @@ -7221,7 +7802,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -7398,7 +7979,7 @@
     +            AttributesRef   => \@attributes,
     +            ARGSRef         => \%basics,
     +        );
    -+        push @tmp_res, ProcessRoleMembers( $asset => %ARGS );
    ++        push @tmp_res, ProcessAssetRoleMembers( $asset => %ARGS );
     +        push @tmp_res, ProcessObjectCustomFieldUpdates( Object => $asset, ARGSRef => \%ARGS );
     +        push @tmp_res, ProcessRecordLinks( RecordObj => $asset, RecordId => 'Asset', ARGSRef => \%ARGS );
     +        push @tmp_res, ProcessRecordBulkCustomFields( RecordObj => $asset, ARGSRef => \%ARGS );
    @@ -7424,7 +8005,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -7503,7 +8084,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -7617,7 +8198,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -7692,16 +8273,16 @@
      </i><br />
      % } elsif (ref($Object) eq 'RT::Queue') {
     
    -diff --git a/share/html/Elements/Assets/AddPeople b/share/html/Elements/Assets/AddPeople
    +diff --git a/share/html/Elements/FindAsset b/share/html/Elements/FindAsset
     new file mode 100644
     --- /dev/null
    -+++ b/share/html/Elements/Assets/AddPeople
    ++++ b/share/html/Elements/FindAsset
     @@
     +%# BEGIN BPS TAGGED BLOCK {{{
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -7744,37 +8325,23 @@
     +%# those contributions and any derivatives thereof.
     +%#
     +%# END BPS TAGGED BLOCK }}}
    -+<%args>
    -+$Object
    -+</%args>
    -+<div class="add-user">
    -+<h3><&|/l&>Add a person</&></h3>
    -+<& SelectRoleType, Object => $Object, Name => "AddUserRoleMember-Role" &>
    -+<input type="text" name="AddUserRoleMember"
    -+       data-autocomplete="Users"
    -+       data-autocomplete-return="Name"
    -+       placeholder="<% loc("Find a user...") %>">
    -+</div>
    -+
    -+<div class="add-group">
    -+<h3><&|/l&>Add a group</&></h3>
    -+<& SelectRoleType, Object => $Object, Name => "AddGroupRoleMember-Role" &>
    -+<input type="text" name="AddGroupRoleMember"
    -+       data-autocomplete="Groups"
    -+       data-autocomplete-return="Name"
    -+       placeholder="<% loc("Find a group...") %>">
    -+</div>
    -
    -diff --git a/share/html/Elements/Assets/EditPeople b/share/html/Elements/Assets/EditPeople
    ++<&|/Widgets/TitleBox, title => loc('Find an asset') &>
    ++<form action="<% RT->Config->Get('WebPath') %>/Asset/Search/">
    ++  <input type="text" name="q" />
    ++  <input type="submit" value="<&|/l&>Search</&>" class="button" />
    ++</form>
    ++</&>
    +
    +diff --git a/share/html/Elements/MyAssets b/share/html/Elements/MyAssets
     new file mode 100644
     --- /dev/null
    -+++ b/share/html/Elements/Assets/EditPeople
    ++++ b/share/html/Elements/MyAssets
     @@
     +%# BEGIN BPS TAGGED BLOCK {{{
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -7817,340 +8384,6 @@
     +%# those contributions and any derivatives thereof.
     +%#
     +%# END BPS TAGGED BLOCK }}}
    -+<%args>
    -+$Object
    -+</%args>
    -+<%init>
    -+</%init>
    -+% for my $role ($Object->Roles( ACLOnly => 0 )) {
    -+<div class="role-<% CSSClass($role) %> role">
    -+  <h3><% loc($role) %></h3>
    -+  <& EditRoleMembers, Group => $Object->RoleGroup($role) &>
    -+</div>
    -+% }
    -+<em><&|/l&>(Check box to delete)</&></em>
    -
    -diff --git a/share/html/Elements/Assets/EditRoleMembers b/share/html/Elements/Assets/EditRoleMembers
    -new file mode 100644
    ---- /dev/null
    -+++ b/share/html/Elements/Assets/EditRoleMembers
    -@@
    -+%# BEGIN BPS TAGGED BLOCK {{{
    -+%#
    -+%# COPYRIGHT:
    -+%#
    -+%# This software is Copyright (c) 1996-2014 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 }}}
    -+<%args>
    -+$Group       => undef
    -+$Recursively => 0
    -+</%args>
    -+<%init>
    -+my $field_name = "RemoveRoleMember-" . $Group->Name;
    -+</%init>
    -+<ul class="role-members">
    -+% my $Users = $Group->UserMembersObj( Recursively => $Recursively );
    -+% if ($Group->SingleMemberRoleGroup) {
    -+<input type="text" value="<% $Users->First->Name %>" name="SetRoleMember-<% $Group->Name %>" id="SetRoleMember-<% $Group->Name %>" data-autocomplete="Users" data-autocomplete-return="Name" /><br />
    -+% } else {
    -+% while ( my $user = $Users->Next ) {
    -+<li>
    -+  <label>
    -+    <input type="checkbox" name="<% $field_name %>" value="<% $user->PrincipalId %>">
    -+    <& /Elements/ShowUser, User => $user &>
    -+  </label>
    -+</li>
    -+% }
    -+% my $Groups = $Group->GroupMembersObj( Recursively => $Recursively );
    -+% $Groups->LimitToUserDefinedGroups;
    -+% while (my $group = $Groups->Next) {
    -+<li>
    -+  <label>
    -+    <input type="checkbox" name="<% $field_name %>" value="<% $group->PrincipalId %>">
    -+    <&|/l&>Group</&>: <% $group->Name %>
    -+  </label>
    -+</li>
    -+% }
    -+% }
    -+</ul>
    -
    -diff --git a/share/html/Elements/Assets/Search b/share/html/Elements/Assets/Search
    -new file mode 100644
    ---- /dev/null
    -+++ b/share/html/Elements/Assets/Search
    -@@
    -+%# BEGIN BPS TAGGED BLOCK {{{
    -+%#
    -+%# COPYRIGHT:
    -+%#
    -+%# This software is Copyright (c) 1996-2014 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 }}}
    -+<form ACTION="<% RT->Config->Get('WebPath') %>/Asset/Search/">
    -+  <input size="12" name="q" accesskey="0" class="field" value="<% $value %>" placeholder="<&|/l&>Search Assets</&>..." />
    -+</form>
    -+<%init>
    -+my $value = defined $DECODED_ARGS->{q} ? $DECODED_ARGS->{q} : '';
    -+</%init>
    -
    -diff --git a/share/html/Elements/Assets/SelectRoleType b/share/html/Elements/Assets/SelectRoleType
    -new file mode 100644
    ---- /dev/null
    -+++ b/share/html/Elements/Assets/SelectRoleType
    -@@
    -+%# BEGIN BPS TAGGED BLOCK {{{
    -+%#
    -+%# COPYRIGHT:
    -+%#
    -+%# This software is Copyright (c) 1996-2014 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 }}}
    -+<%args>
    -+$Object
    -+$Name
    -+$AllowNull  => 0
    -+</%args>
    -+<select name="<% $Name %>">
    -+% if ($AllowNull) {
    -+  <option value=""></option>
    -+% }
    -+% for my $role ($Object->Roles( ACLOnly => 0, Single => 0 )) {
    -+  <option value="<% $role %>"><% loc($role) %></option>
    -+% }
    -+</select>
    -
    -diff --git a/share/html/Elements/FindAsset b/share/html/Elements/FindAsset
    -new file mode 100644
    ---- /dev/null
    -+++ b/share/html/Elements/FindAsset
    -@@
    -+%# BEGIN BPS TAGGED BLOCK {{{
    -+%#
    -+%# COPYRIGHT:
    -+%#
    -+%# This software is Copyright (c) 1996-2014 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 }}}
    -+<&|/Widgets/TitleBox, title => loc('Find an asset') &>
    -+<form action="<% RT->Config->Get('WebPath') %>/Asset/Search/">
    -+  <input type="text" name="q" />
    -+  <input type="submit" value="<&|/l&>Search</&>" class="button" />
    -+</form>
    -+</&>
    -
    -diff --git a/share/html/Elements/MyAssets b/share/html/Elements/MyAssets
    -new file mode 100644
    ---- /dev/null
    -+++ b/share/html/Elements/MyAssets
    -@@
    -+%# BEGIN BPS TAGGED BLOCK {{{
    -+%#
    -+%# COPYRIGHT:
    -+%#
    -+%# This software is Copyright (c) 1996-2014 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 }}}
     +<& /User/Elements/AssetList, User => $session{'CurrentUser'}->UserObj, Roles => [qw(HeldBy)], Title => loc('My Assets') &>
     
     diff --git a/share/html/Elements/RT__Asset/ColumnMap b/share/html/Elements/RT__Asset/ColumnMap
    @@ -8162,7 +8395,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -8289,7 +8522,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -8369,187 +8602,212 @@
     --- a/share/html/Elements/Tabs
     +++ b/share/html/Elements/Tabs
     @@
    -         PageMenu()->child( edit => title => loc('Edit'), path => '/Prefs/MyRT.html' );
    +         description => loc('Select Custom Fields for Articles in all Classes'),
    +         path        => '/Admin/Global/CustomFields/Class-Article.html',
    +     );
    ++    $cfadmin->child( 'assets' =>
    ++        title       => loc('Assets'),
    ++        description => loc('Select Custom Fields for Assets in all Catalogs'),
    ++        path        => '/Admin/Global/CustomFields/Catalog-Assets.html',
    ++    );
    + 
    +     my $article_admin = $admin->child( articles => title => loc('Articles'), path => "/Admin/Articles/index.html" );
    +     my $class_admin = $article_admin->child(classes => title => loc('Classes'), path => '/Admin/Articles/Classes/' );
    +@@
    +         path => '/Admin/CustomFields/Modify.html?'.$m->comp("/Elements/QueryString", Create=>1, LookupType=> "RT::Class-RT::Article" ),
    +     );
    + 
    ++    my $assets_admin = $admin->child( assets => title => loc("Assets"), path => '/Admin/Assets/' );
    ++    my $catalog_admin = $assets_admin->child( catalogs =>
    ++        title       => loc("Catalogs"),
    ++        description => loc("Modify asset catalogs"),
    ++        path        => "/Admin/Assets/Catalogs/"
    ++    );
    ++    $catalog_admin->child( "select", title => loc("Select"), path => $catalog_admin->path );
    ++    $catalog_admin->child( "create", title => loc("Create"), path => "Create.html" );
    ++
    ++
    ++    my $assets_cfs = $assets_admin->child( "cfs",
    ++        title => loc("Custom Fields"),
    ++        description => loc("Modify asset custom fields"),
    ++        path => "/Admin/CustomFields/?Type=" . RT::Asset->CustomFieldLookupType
    ++    );
    ++    $assets_cfs->child( "select", title => loc("Select"), path => $assets_cfs->path );
    ++    $assets_cfs->child( "create", title => loc("Create"), path => "/Admin/CustomFields/Modify.html?Create=1&LookupType=" . RT::Asset->CustomFieldLookupType);
    ++
    +     $admin_global->child( 'group-rights' =>
    +         title       => loc('Group Rights'),
    +         description => loc('Modify global group rights'),
    +@@
    + 
    + my $build_main_nav = sub {
    + 
    +-    PageWidgets()->child( simple_search => raw_html => $m->scomp('SimpleSearch') );
    +-    PageWidgets()->child( create_ticket => raw_html => $m->scomp('CreateTicket') );
    ++    if ($request_path =~ m{^/Asset/}) {
    ++        PageWidgets()->child( asset_search => raw_html => $m->scomp('/Asset/Elements/Search') );
    ++    } else {
    ++        PageWidgets()->child( simple_search => raw_html => $m->scomp('SimpleSearch') );
    ++        PageWidgets()->child( create_ticket => raw_html => $m->scomp('CreateTicket') );
    ++    }
    + 
    +     my $home = Menu->child( home => title => loc('Homepage'), path => '/' );
    +     unless ($session{'dashboards_in_menu'}) {
    +@@
    + 
    +     $search->child( users => title => loc('Users'),   path => "/User/Search.html" );
    + 
    ++    $search->child( assets => title => loc("Assets"), path => "/Asset/Search/" );
    ++
    +     if ($session{CurrentUser}->HasRight( Right => 'ShowArticlesMenu', Object => RT->System )) {
    +         my $articles = Menu->child( articles => title => loc('Articles'), path => "/Articles/index.html");
    +         $articles->child( articles => title => loc('Overview'), path => "/Articles/index.html" );
    +@@
    +         $articles->child( search   => title => loc('Search'),   path => "/Articles/Article/Search.html" );
          }
      
    -+    { ### START ASSETS MENU
    -+        # Top level Assets menu
    -+        my $assets = Menu->child("tools")->add_before(
    -+                       "assets", title => loc("Assets"), path => "/Asset/Search/");
    -+        $assets->child("create", title => loc("Create"), path => "/Asset/CreateInCatalog.html");
    -+        $assets->child("search", title => loc("Search"), path => "/Asset/Search/");
    -+
    -+        Menu->child("search")->child("assets", title => loc("Assets"), path => "/Asset/Search/");
    -+
    -+        # Add global Assets custom field admin page
    -+        my $global_cfs = Menu();
    -+           $global_cfs = $global_cfs->child($_) or last
    -+                for "admin" => "global" => "custom-fields";
    -+        $global_cfs->child("assets", title => loc("Assets"), path => "/Admin/Global/CustomFields/Catalog-Assets.html")
    -+            if $global_cfs;
    -+
    -+        # Add a Catalog admin menu
    -+        my $config = Menu()->child("admin");
    -+        if ($config) {
    -+            my $assets   = $config->child("tools")->add_before("assets", title => loc("Assets"), path => "/Admin/Assets/");
    -+            my $catalogs = $assets->child("catalogs",
    -+                title => loc("Catalogs"),
    -+                description => loc("Modify asset catalogs"),
    -+                path => "/Admin/Assets/Catalogs/");
    -+            $catalogs->child("select", title => loc("Select"), path => $catalogs->path);
    -+            $catalogs->child("create", title => loc("Create"), path => "Create.html");
    -+
    -+            my $cfs = $assets->child("cfs",
    -+                title => loc("Custom Fields"),
    -+                description => loc("Modify asset custom fields"),
    -+                path => "/Admin/CustomFields/?Type=" . RT::Asset->CustomFieldLookupType);
    -+            $cfs->child("select", title => loc("Select"), path => $cfs->path);
    -+            $cfs->child("create", title => loc("Create"), path => "/Admin/CustomFields/Modify.html?Create=1&LookupType=" . RT::Asset->CustomFieldLookupType);
    -+        }
    -+
    -+        # Asset search
    -+        if ($request_path =~ m{^/Asset/}) {
    -+            PageWidgets()->child( asset_search => raw_html => $m->scomp('/Elements/Assets/Search') );
    -+            PageWidgets()->delete('create_ticket');
    -+            PageWidgets()->delete('simple_search');
    -+        }
    -+
    -+        # Page menus
    -+        my $page    = PageMenu();
    -+
    -+        if ($request_path =~ m{^/Asset/} and $DECODED_ARGS->{id} and $DECODED_ARGS->{id} !~ /\D/) {
    -+            my $id    = $DECODED_ARGS->{id};
    -+            my $asset = RT::Asset->new( $session{CurrentUser} );
    -+            $asset->Load($id);
    -+
    -+            if ($asset->id) {
    -+                $page->child("display",     title => loc("Display"),        path => "/Asset/Display.html?id=$id");
    -+                $page->child("history",     title => loc("History"),        path => "/Asset/History.html?id=$id");
    -+                $page->child("basics",      title => loc("Basics"),         path => "/Asset/Modify.html?id=$id");
    -+                $page->child("links",       title => loc("Links"),          path => "/Asset/ModifyLinks.html?id=$id");
    -+                $page->child("people",      title => loc("People"),         path => "/Asset/ModifyPeople.html?id=$id");
    -+                $page->child("dates",       title => loc("Dates"),          path => "/Asset/ModifyDates.html?id=$id");
    -+
    -+                for my $grouping (RT::CustomField->CustomGroupings($asset)) {
    -+                    my $cfs = $asset->CustomFields;
    -+                    $cfs->LimitToGrouping( $asset => $grouping );
    -+                    next unless $cfs->Count;
    -+                    $page->child(
    -+                        "cf-grouping-$grouping",
    -+                        title   => loc($grouping),
    -+                        path    => "/Asset/ModifyCFs.html?id=$id;Grouping=" . $m->interp->apply_escapes($grouping, 'u'),
    -+                    );
    -+                }
    -+
    -+                my $actions = $page->child("actions", title => loc("Actions"));
    -+                $actions->child("create-linked-ticket", title => loc("Create linked ticket"), path => "/Asset/CreateLinkedTicket.html?Asset=$id");
    -+
    -+                my $status    = $asset->Status;
    -+                my $lifecycle = $asset->LifecycleObj;
    -+                for my $action ( $lifecycle->Actions($status) ) {
    -+                    my $next = $action->{'to'};
    -+                    next unless $lifecycle->IsTransition( $status => $next );
    -+
    -+                    my $check = $lifecycle->CheckRight( $status => $next );
    -+                    next unless $asset->CurrentUserHasRight($check);
    -+
    -+                    my $label = $action->{'label'} || ucfirst($next);
    -+                    $actions->child(
    -+                        $label,
    -+                        title   => loc($label),
    -+                        path    => "/Asset/Modify.html?id=$id;Update=1;DisplayAfter=1;Status="
    -+                                    . $m->interp->apply_escapes($next, 'u'),
    -+
    -+                        class       => "asset-lifecycle-action",
    -+                        attributes  => {
    -+                            'data-current-status'   => $status,
    -+                            'data-next-status'      => $next,
    -+                        },
    -+                    );
    -+                }
    ++    my $assets = Menu->child( "assets", title => loc("Assets"), path => "/Asset/Search/" );
    ++    $assets->child( "create", title => loc("Create"), path => "/Asset/CreateInCatalog.html" );
    ++    $assets->child( "search", title => loc("Search"), path => "/Asset/Search/" );
    ++
    +     my $tools = Menu->child( tools => title => loc('Tools'), path => '/Tools/index.html' );
    + 
    +     $tools->child( my_day =>
    +@@
    + 
    +     }
    + 
    ++    if ($request_path =~ m{^/Asset/} and $DECODED_ARGS->{id} and $DECODED_ARGS->{id} !~ /\D/) {
    ++        my $page  = PageMenu();
    ++        my $id    = $DECODED_ARGS->{id};
    ++        my $asset = RT::Asset->new( $session{CurrentUser} );
    ++        $asset->Load($id);
    ++
    ++        if ($asset->id) {
    ++            $page->child("display",     title => loc("Display"),        path => "/Asset/Display.html?id=$id");
    ++            $page->child("history",     title => loc("History"),        path => "/Asset/History.html?id=$id");
    ++            $page->child("basics",      title => loc("Basics"),         path => "/Asset/Modify.html?id=$id");
    ++            $page->child("links",       title => loc("Links"),          path => "/Asset/ModifyLinks.html?id=$id");
    ++            $page->child("people",      title => loc("People"),         path => "/Asset/ModifyPeople.html?id=$id");
    ++            $page->child("dates",       title => loc("Dates"),          path => "/Asset/ModifyDates.html?id=$id");
    ++
    ++            for my $grouping (RT::CustomField->CustomGroupings($asset)) {
    ++                my $cfs = $asset->CustomFields;
    ++                $cfs->LimitToGrouping( $asset => $grouping );
    ++                next unless $cfs->Count;
    ++                $page->child(
    ++                    "cf-grouping-$grouping",
    ++                    title   => loc($grouping),
    ++                    path    => "/Asset/ModifyCFs.html?id=$id;Grouping=" . $m->interp->apply_escapes($grouping, 'u'),
    ++                );
    ++            }
    ++
    ++            my $actions = $page->child("actions", title => loc("Actions"));
    ++            $actions->child("create-linked-ticket", title => loc("Create linked ticket"), path => "/Asset/CreateLinkedTicket.html?Asset=$id");
    ++
    ++            my $status    = $asset->Status;
    ++            my $lifecycle = $asset->LifecycleObj;
    ++            for my $action ( $lifecycle->Actions($status) ) {
    ++                my $next = $action->{'to'};
    ++                next unless $lifecycle->IsTransition( $status => $next );
    ++
    ++                my $check = $lifecycle->CheckRight( $status => $next );
    ++                next unless $asset->CurrentUserHasRight($check);
    ++
    ++                my $label = $action->{'label'} || ucfirst($next);
    ++                $actions->child(
    ++                    $label,
    ++                    title   => loc($label),
    ++                    path    => "/Asset/Modify.html?id=$id;Update=1;DisplayAfter=1;Status="
    ++                                . $m->interp->apply_escapes($next, 'u'),
    ++
    ++                    class       => "asset-lifecycle-action",
    ++                    attributes  => {
    ++                        'data-current-status'   => $status,
    ++                        'data-next-status'      => $next,
    ++                    },
    ++                );
     +            }
     +        }
    -+        elsif ($request_path =~ m{^/Asset/Search/}) {
    -+            my %search = map @{$_},
    -+                grep defined $_->[1] && length $_->[1],
    -+                map {ref $DECODED_ARGS->{$_} ? [$_, $DECODED_ARGS->{$_}[0]] : [$_, $DECODED_ARGS->{$_}] }
    -+                grep /^(?:q|SearchAssets|!?(Name|Description|Catalog|Status|Role\..+|CF\..+)|Order(?:By)?|Page)$/,
    -+                keys %$DECODED_ARGS;
    -+            if ( $request_path =~ /Bulk/) {
    -+                $page->child('search',
    -+                    title => loc('Show Results'),
    -+                    path => '/Asset/Search/?'. $query_string->(%search),
    -+                );
    -+            } else {
    -+                $page->child('bulk',
    -+                    title => loc('Bulk Update'),
    -+                    path => '/Asset/Search/Bulk.html?'. $query_string->(%search),
    -+                );
    -+            }
    -+            $page->child('csv',
    -+                title => loc('Download Spreadsheet'),
    -+                path  => '/Asset/Search/Results.tsv?' . $query_string->(%search),
    ++    } elsif ($request_path =~ m{^/Asset/Search/}) {
    ++        my $page  = PageMenu();
    ++        my %search = map @{$_},
    ++            grep defined $_->[1] && length $_->[1],
    ++            map {ref $DECODED_ARGS->{$_} ? [$_, $DECODED_ARGS->{$_}[0]] : [$_, $DECODED_ARGS->{$_}] }
    ++            grep /^(?:q|SearchAssets|!?(Name|Description|Catalog|Status|Role\..+|CF\..+)|Order(?:By)?|Page)$/,
    ++            keys %$DECODED_ARGS;
    ++        if ( $request_path =~ /Bulk/) {
    ++            $page->child('search',
    ++                title => loc('Show Results'),
    ++                path => '/Asset/Search/?' . keys %search ? $query_string->(%search) : '',
    ++            );
    ++        } else {
    ++            $page->child('bulk',
    ++                title => loc('Bulk Update'),
    ++                path => '/Asset/Search/Bulk.html?' . keys %search ? $query_string->(%search) : '',
     +            );
     +        }
    -+        elsif ($request_path =~ m{^/Admin/Global/CustomFields/Catalog-Assets\.html$}) {
    -+            $page->child("create", title => loc("Create New"), path => "/Admin/CustomFields/Modify.html?Create=1;LookupType=" . RT::Asset->CustomFieldLookupType);
    ++        $page->child('csv',
    ++            title => loc('Download Spreadsheet'),
    ++            path  => '/Asset/Search/Results.tsv?' . keys %search ? $query_string->(%search) : '',
    ++        );
    ++    } elsif ($request_path =~ m{^/Admin/Global/CustomFields/Catalog-Assets\.html$}) {
    ++        my $page  = PageMenu();
    ++        $page->child("create", title => loc("Create New"), path => "/Admin/CustomFields/Modify.html?Create=1;LookupType=" . RT::Asset->CustomFieldLookupType);
    ++    } elsif ($request_path =~ m{^/Admin/CustomFields(/|/index\.html)?$}
    ++            and $DECODED_ARGS->{'Type'} and $DECODED_ARGS->{'Type'} eq RT::Asset->CustomFieldLookupType) {
    ++        my $page  = PageMenu();
    ++        $page->child("create")->path( $page->child("create")->path . "&LookupType=" . RT::Asset->CustomFieldLookupType );
    ++    } elsif ($request_path =~ m{^/Admin/Assets/Catalogs/}) {
    ++        my $page  = PageMenu();
    ++        my $actions = $request_path =~ m{/((index|Create)\.html)?$}
    ++            ? $page
    ++            : $page->child("catalogs", title => loc("Catalogs"), path => "/Admin/Assets/Catalogs/");
    ++
    ++        $actions->child("select", title => loc("Select"), path => "/Admin/Assets/Catalogs/");
    ++        $actions->child("create", title => loc("Create"), path => "/Admin/Assets/Catalogs/Create.html");
    ++
    ++        my $catalog = RT::Catalog->new( $session{CurrentUser} );
    ++        $catalog->Load($DECODED_ARGS->{id}) if $DECODED_ARGS->{id};
    ++
    ++        if ($catalog->id and $catalog->CurrentUserCanSee) {
    ++            my $query = "id=" . $catalog->id;
    ++            $page->child("modify", title => loc("Basics"), path => "/Admin/Assets/Catalogs/Modify.html?$query");
    ++            $page->child("people", title => loc("Roles"),  path => "/Admin/Assets/Catalogs/Roles.html?$query");
    ++
    ++            $page->child("cfs", title => loc("Asset Custom Fields"), path => "/Admin/Assets/Catalogs/CustomFields.html?$query");
    ++
    ++            $page->child("group-rights", title => loc("Group Rights"), path => "/Admin/Assets/Catalogs/GroupRights.html?$query");
    ++            $page->child("user-rights",  title => loc("User Rights"),  path => "/Admin/Assets/Catalogs/UserRights.html?$query");
     +        }
    -+        elsif ($request_path =~ m{^/Admin/CustomFields(/|/index\.html)?$}
    -+               and $DECODED_ARGS->{'Type'} and $DECODED_ARGS->{'Type'} eq RT::Asset->CustomFieldLookupType) {
    -+            $page->child("create")->path( $page->child("create")->path . "&LookupType=" . RT::Asset->CustomFieldLookupType );
    -+        }
    -+        elsif ($request_path =~ m{^/Admin/Assets/Catalogs/}) {
    -+            my $actions = $request_path =~ m{/((index|Create)\.html)?$}
    -+                ? $page
    -+                : $page->child("catalogs", title => loc("Catalogs"), path => "/Admin/Assets/Catalogs/");
    -+
    -+            $actions->child("select", title => loc("Select"), path => "/Admin/Assets/Catalogs/");
    -+            $actions->child("create", title => loc("Create"), path => "/Admin/Assets/Catalogs/Create.html");
    -+
    -+            my $catalog = RT::Catalog->new( $session{CurrentUser} );
    -+            $catalog->Load($DECODED_ARGS->{id}) if $DECODED_ARGS->{id};
    -+
    -+            if ($catalog->id and $catalog->CurrentUserCanSee) {
    -+                my $query = "id=" . $catalog->id;
    -+                $page->child("modify", title => loc("Basics"), path => "/Admin/Assets/Catalogs/Modify.html?$query");
    -+                $page->child("people", title => loc("Roles"),  path => "/Admin/Assets/Catalogs/Roles.html?$query");
    -+
    -+                $page->child("cfs", title => loc("Asset Custom Fields"), path => "/Admin/Assets/Catalogs/CustomFields.html?$query");
    -+
    -+                $page->child("group-rights", title => loc("Group Rights"), path => "/Admin/Assets/Catalogs/GroupRights.html?$query");
    -+                $page->child("user-rights",  title => loc("User Rights"),  path => "/Admin/Assets/Catalogs/UserRights.html?$query");
    -+            }
    -+        }
    -+    } ### END ASSETS MENU
    -+
    -     $m->callback( CallbackName => 'Privileged', Path => $request_path );
    - };
    ++    }
    ++
    +     if ( $request_path =~ m{^/User/(Summary|History)\.html} ) {
    +         if (PageMenu()->child('summary')) {
    +             # Already set up from having AdminUser and ShowConfigTab;
    +@@
    +     $tickets->child( open   => title => loc('Open tickets'),   path => '/SelfService/' );
    +     $tickets->child( closed => title => loc('Closed tickets'), path => '/SelfService/Closed.html' );
      
    ++    my $assets = Menu->child( "assets", title => loc("Assets"), path => "/SelfService/Asset/" );
    + 
    +     my $username = '<span class="current-user">'
    +                  . $m->interp->apply_escapes($session{'CurrentUser'}->Name, 'h')
     @@
      
          PageWidgets->child( goto => raw_html => $m->scomp('/SelfService/Elements/GotoTicket') );
      
    -+    { ### START ASSETS SELFSERVICE MENU
    -+        Menu->child("tickets")->add_after(
    -+            "assets",
    -+            title   => loc("Assets"),
    -+            path    => "/SelfService/Asset/",
    -+        );
    -+
    -+        # Page menus
    -+        my $page    = PageMenu();
    -+
    -+        if ($request_path =~ m{^/SelfService/Asset/} and $DECODED_ARGS->{id}) {
    -+            my $id = $DECODED_ARGS->{id};
    -+            $page->child("display",     title => loc("Display"),        path => "/SelfService/Asset/Display.html?id=$id");
    -+            $page->child("history",     title => loc("History"),        path => "/SelfService/Asset/History.html?id=$id");
    -+
    -+            if (Menu->child("new")) {
    -+                my $actions = $page->child("actions", title => loc("Actions"));
    -+                $actions->child("create-linked-ticket", title => loc("Create linked ticket"), path => "/SelfService/Asset/CreateLinkedTicket.html?Asset=$id");
    -+            }
    ++    if ($request_path =~ m{^/SelfService/Asset/} and $DECODED_ARGS->{id}) {
    ++        my $page = PageMenu();
    ++        my $id   = $DECODED_ARGS->{id};
    ++        $page->child("display",     title => loc("Display"),        path => "/SelfService/Asset/Display.html?id=$id");
    ++        $page->child("history",     title => loc("History"),        path => "/SelfService/Asset/History.html?id=$id");
    ++
    ++        if (Menu->child("new")) {
    ++            my $actions = $page->child("actions", title => loc("Actions"));
    ++            $actions->child("create-linked-ticket", title => loc("Create linked ticket"), path => "/SelfService/Asset/CreateLinkedTicket.html?Asset=$id");
     +        }
    -+    } ### END ASSETS SELFSERVICE MENU
    ++    }
     +
          $m->callback( CallbackName => 'SelfService', Path => $request_path );
      };
    @@ -8564,7 +8822,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -8627,7 +8885,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -8690,7 +8948,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -8751,7 +9009,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -8817,7 +9075,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -8899,7 +9157,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -9088,7 +9346,7 @@
     +  <div class="add-asset">
     +    <label>
     +      <&|/l&>Add an asset to this ticket</&>
    -+    <input size="5" name="<% $Ticket->id %>-RefersTo" placeholder="<&|/l&>Asset #</&>" type="text">
    ++    <input size="10" name="<% $Ticket->id %>-RefersTo" placeholder="<&|/l&>Asset #</&>" type="text">
     +    </label>
     +    <input type="submit" value="+">
     +  </div>
    @@ -9109,7 +9367,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -9248,7 +9506,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -9332,7 +9590,7 @@
     +%#
     +%# COPYRIGHT:
     +%#
    -+%# This software is Copyright (c) 1996-2014 Best Practical Solutions, LLC
    ++%# This software is Copyright (c) 1996-2015 Best Practical Solutions, LLC
     +%#                                          <sales at bestpractical.com>
     +%#
     +%# (Except where explicitly superseded by other copyright notices)
    @@ -9615,6 +9873,18 @@
     +        width: 10em;
     +    }
     +}
    +
    +diff --git a/share/static/css/base/main.css b/share/static/css/base/main.css
    +--- a/share/static/css/base/main.css
    ++++ b/share/static/css/base/main.css
    +@@
    + @import "record.css";
    + @import "admin.css";
    + @import "articles.css";
    ++ at import "assets.css";
    + @import "portlets.css";
    + @import "tools.css";
    + @import "login.css";
     
     diff --git a/share/static/js/assets.js b/share/static/js/assets.js
     new file mode 100644
    @@ -9666,6 +9936,19 @@
     +    });
     +});
     
    +diff --git a/t/api/template-parsing.t b/t/api/template-parsing.t
    +--- a/t/api/template-parsing.t
    ++++ b/t/api/template-parsing.t
    +@@
    + 
    + SimpleTemplateTest(
    +     Content => "\ntest { \$TicketOwnerId }",
    +-    Output  => "test 12",
    ++    Output  => "test 14",
    + );
    + 
    + SimpleTemplateTest(
    +
     diff --git a/t/assets/api.t b/t/assets/api.t
     new file mode 100644
     --- /dev/null
    @@ -10076,18 +10359,6 @@
     +}
     +
     +done_testing;
    -
    -diff --git a/t/assets/pod.t b/t/assets/pod.t
    -new file mode 100644
    ---- /dev/null
    -+++ b/t/assets/pod.t
    -@@
    -+use strict;
    -+use warnings;
    -+
    -+use Test::More;
    -+use Test::Pod;
    -+all_pod_files_ok( all_pod_files("lib","doc","etc"));
     
     diff --git a/t/assets/rights.t b/t/assets/rights.t
     new file mode 100644
    @@ -10373,3 +10644,28 @@
     +undef $m;
     +done_testing;
     
    +diff --git a/t/web/case-sensitivity.t b/t/web/case-sensitivity.t
    +--- a/t/web/case-sensitivity.t
    ++++ b/t/web/case-sensitivity.t
    +@@
    +     require JSON;
    +     is_deeply(
    +         JSON::from_json( $m->content ),
    +-        [{id => 12, "value" =>  "root\@localhost","label" => "root (Enoch Root)"}]
    ++        [{id => 14, "value" =>  "root\@localhost","label" => "root (Enoch Root)"}]
    +     );
    + }
    + 
    +
    +diff --git a/t/web/rest_user_cf.t b/t/web/rest_user_cf.t
    +--- a/t/web/rest_user_cf.t
    ++++ b/t/web/rest_user_cf.t
    +@@
    + is( $root->FirstCustomFieldValue('foo'), 'blabla', 'cf is set' );
    + 
    + ok( $m->login, 'logged in' );
    +-$m->post( "$baseurl/REST/1.0/show", [ id => 'user/12', ] );
    ++$m->post( "$baseurl/REST/1.0/show", [ id => 'user/14', ] );
    + like( $m->content, qr/CF-foo: blabla/, 'found the cf' );
    + 
    + undef $m;
 2:  8b54627 < --:  ------- fixes for failing tests
 3:  979b49a < --:  ------- commit sunnavy's feedback
 4:  222bbd4 < --:  ------- shawn's feedback
 5:  5516fdd < --:  ------- lib/RT/Interface/Web.pm share/html/Admin/Assets/Catalogs/Roles.html share/html/Asset/ModifyPeople.html share/html/Asset/Search/Bulk.html - rename ProcessRoleMembers to ProcessAssetRoleMembers
 6:  4f181d5 < --:  ------- change the last instance of the word 'extension'
 7:  192a09e < --:  ------- Fix encoding for tutorial.pod
 8:  b1ce73f < --:  ------- fix the (hopefully) last vestiges of references to the extension
 9:  a3264ce < --:  ------- fix tests (root is now getting id 14 instead of 12 as id?)
10:  dcee687 < --:  ------- move contents of share/html/Elements/Assets to share/html/Asset/Elements



More information about the rt-commit mailing list