Mailing List Archive

rt branch 5.0/asset-inline-edit created. rt-5.0.5-77-g6ae9368c1c
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt".

The branch, 5.0/asset-inline-edit has been created
at 6ae9368c1c32c8f1d73fe1871ad9cb74679af26b (commit)

- Log -----------------------------------------------------------------
commit 6ae9368c1c32c8f1d73fe1871ad9cb74679af26b
Author: sunnavy <sunnavy@bestpractical.com>
Date: Mon Dec 11 13:41:04 2023 -0500

Make sure inline edit is disabled for unprivileged users

Inline edit doesn't actually work for them anyway, because of the absent
helper component.

Previously we disabled it in /SelfService/Elements/MyRequests, which is
not enough any more as now we support various components for SelfService
especially via dashboards.

diff --git a/share/html/Elements/CollectionList b/share/html/Elements/CollectionList
index 273931177f..6aaa169cdc 100644
--- a/share/html/Elements/CollectionList
+++ b/share/html/Elements/CollectionList
@@ -125,7 +125,8 @@ foreach my $col (@Format) {

$Class ||= $Collection->ColumnMapClassName;

-$InlineEdit = 0 unless $Collection->isa('RT::Tickets') || $Collection->isa('RT::Assets');
+$InlineEdit = 0
+ unless $session{CurrentUser}->Privileged && ( $Collection->isa('RT::Tickets') || $Collection->isa('RT::Assets') );

$m->out('<div class="table-responsive">');
$m->out('<table cellspacing="0"');

commit fae6539af967584f3592cabd28707eb5f2a67de8
Author: sunnavy <sunnavy@bestpractical.com>
Date: Fri Dec 8 16:50:25 2023 -0500

Fix typo: selectpicker is not for text inputs

diff --git a/share/html/Asset/Elements/EditRoleMembers b/share/html/Asset/Elements/EditRoleMembers
index 4dcd65c1f4..008c0a12de 100644
--- a/share/html/Asset/Elements/EditRoleMembers
+++ b/share/html/Asset/Elements/EditRoleMembers
@@ -59,7 +59,7 @@ my $field_name = "RemoveRoleMember-" . $Role;
% if ($Object->Role($Role)->{Single}) {
% my $user = $Users->First || RT->Nobody;
<li class="list-group-item">
- <input class="form-control selectpicker" type="text" value="<% $user->Name %>" name="SetRoleMember-<% $Role %>" id="SetRoleMember-<% $Role %>" data-autocomplete="Users" data-autocomplete-return="Name" />
+ <input class="form-control" type="text" value="<% $user->Name %>" name="SetRoleMember-<% $Role %>" id="SetRoleMember-<% $Role %>" data-autocomplete="Users" data-autocomplete-return="Name" />
</li>
% } else {
% while ( my $user = $Users->Next ) {

commit fe4a3ae80603d0e6460efcc38f1635e7b34d2b0e
Author: sunnavy <sunnavy@bestpractical.com>
Date: Fri Dec 8 09:52:31 2023 -0500

Support inline edit for assets

diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index 31e3163168..2386026f7c 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -2583,6 +2583,11 @@ click, but also specifies different behaviors for several other panels.
Note that the non-standard panel names "Grouping Name" and "Another
Grouping" are created by the L</%CustomFieldGroupings> setting.

+As Assets don't have editable core date fields, inline edit is thus
+disabled in the "Dates" widget by default. If you have any date custom
+fields that are grouped there via L</%CustomFieldGroupings>, you can
+explicitly enable it via this config.
+
Set(%InlineEditPanelBehavior,
'RT::Ticket' => {
'_default' => 'click',
@@ -2593,6 +2598,15 @@ Grouping" are created by the L</%CustomFieldGroupings> setting.
'Links' => 'hide',
'People' => 'link',
},
+ 'RT::Asset' => {
+ '_default' => 'click',
+
+ 'Grouping Name' => 'link',
+ 'Another Grouping' => 'click',
+ 'Dates' => 'hide',
+ 'Links' => 'hide',
+ 'People' => 'link',
+ },
);

=back
diff --git a/share/html/Asset/Display.html b/share/html/Asset/Display.html
index 553fb4f66a..b1035b45fe 100644
--- a/share/html/Asset/Display.html
+++ b/share/html/Asset/Display.html
@@ -53,7 +53,7 @@
<& /Elements/ListActions, actions => \@results &>

<span class="catalog <% CSSClass($asset->CatalogObj->Name) %>">
-<& Elements/ShowSummary, AssetObj => $asset &>
+<& Elements/ShowSummary, AssetObj => $asset, InlineEdit => $InlineEdit &>

% $m->callback(CallbackName => 'AfterShowSummary', ARGSRef => \%ARGS, Asset => $asset);

@@ -68,9 +68,53 @@

<%args>
$id => undef
+$InlineEdit => RT->Config->Get( 'InlineEdit', $session{CurrentUser} )
</%args>
<%init>
my @results;
my $asset = LoadAsset($id);
+
+# fill ACL cache
+$asset->CurrentUser->PrincipalObj->HasRights( Object => $asset );
+
+my $SkipProcessing;
+
+$m->callback( CallbackName => 'BeforeProcessArguments',
+ AssetObj => $asset,
+ ActionsRef => \@results, ARGSRef => \%ARGS,
+ SkipProcessing => \$SkipProcessing );
+
+my ($status, @msg) = $m->comp(
+ '/Elements/ValidateCustomFields',
+ Object => $asset,
+ CustomFields => $asset->CustomFields,
+ ARGSRef => \%ARGS,
+);
+unless ($status) {
+ push @results, @msg;
+ $SkipProcessing = 1;
+}
+
+if ( !$SkipProcessing ) {
+
+ push @results, ProcessAssetRoleMembers( $asset => %ARGS );
+ push @results, ProcessRecordLinks( RecordObj => $asset, ARGSRef => \%ARGS );
+ push @results, ProcessObjectCustomFieldUpdates( Object => $asset, ARGSRef => \%ARGS );
+
+ push @results, UpdateRecordObject(
+ Object => $asset,
+ AttributesRef => [ $asset->WritableAttributes ],
+ ARGSRef => \%ARGS,
+ );
+
+}
+
$m->callback(CallbackName => 'BeforeDisplay', ARGSRef => \%ARGS, Asset => $asset, Results => \@results);
+
+MaybeRedirectForResults(
+ Actions => \@results,
+ Path => '/Asset/Display.html',
+ Anchor => $ARGS{'Anchor'},
+ Arguments => { id => $asset->Id },
+);
</%init>
diff --git a/share/html/Asset/Elements/ShowSummary b/share/html/Asset/Elements/EditPeopleInline
similarity index 62%
copy from share/html/Asset/Elements/ShowSummary
copy to share/html/Asset/Elements/EditPeopleInline
index 3aa12a3b22..b0a3fbaf6f 100644
--- a/share/html/Asset/Elements/ShowSummary
+++ b/share/html/Asset/Elements/EditPeopleInline
@@ -45,44 +45,49 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<%args>
-$AssetObj
-</%args>
-<%init>
-my @sections = (
- "Basics", #loc
- "People", #loc
- "Dates", #loc
- "Links", #loc
-);

-my $can_edit = $session{CurrentUser}->Privileged
- && $AssetObj->CurrentUserHasRight("ModifyAsset");
+% for my $role ($AssetObj->Roles( ACLOnly => 0 )) {
+ <div class="role-<% CSSClass($role) %> role">
+ <h5 class="mt-2"><% $AssetObj->LabelForRole($role) %></h5>
+ <& EditRoleMembers, Object => $AssetObj, Role => $role &>
+ </div>
+% }

-my %link;
-for my $section (@sections) {
- my $page = $section eq 'Basics' ? "Modify.html" : "Modify$section.html";
- $link{$section} =
- RT->Config->Get("WebPath")
- . "/Asset/$page?id="
- . $AssetObj->id;
-}
-</%init>
-<div class="asset-metadata">
+<div class="add-user">
+ <h5 class="mt-2"><&|/l&>Add a person</&></h5>
<div class="form-row">
-% for my $section (@sections) {
- <div class="col-4">
- <&| /Widgets/TitleBox, title => loc($section), title_href => $can_edit ? $link{$section} : "", title_class => "inverse", class => "asset-\L$section" &>
- <& "Show$section", AssetObj => $AssetObj &>
- </&>
- </div>
-% }
+ <div class="col-3">
+ <& SelectRoleType, Object => $AssetObj, Name => "AddUserRoleMember-Role" &>
+ </div>
+ <div class="col-9">
+ <input type="text" name="AddUserRoleMember"
+ data-autocomplete="Users"
+ data-autocomplete-return="Name"
+ placeholder="<% loc("Find a user...") %>"
+ class="form-control"
+ >
+ </div>
+ </div>
+</div>

- <& /Elements/ShowCustomFieldCustomGroupings,
- Object => $AssetObj,
- title_href => $can_edit ? RT->Config->Get("WebPath") . "/Asset/ModifyCFs.html" : "",
- TitleBoxARGS => { title_class => "inverse" },
- GroupingClass => 'col-4'
- &>
+<div class="add-group">
+ <h5 class="mt-2"><&|/l&>Add a group</&></h5>
+ <div class="form-row">
+ <div class="col-3">
+ <& SelectRoleType, Object => $AssetObj, Name => "AddGroupRoleMember-Role" &>
+ </div>
+ <div class="col-9">
+ <input type="text" name="AddGroupRoleMember"
+ data-autocomplete="Groups"
+ data-autocomplete-return="Name"
+ placeholder="<% loc("Find a group...") %>"
+ class="form-control"
+ >
+ </div>
</div>
</div>
+
+<& /Elements/EditCustomFields, Object => $AssetObj, Grouping => 'People', InTable => 1 &>
+<%ARGS>
+$AssetObj => undef
+</%ARGS>
diff --git a/share/html/Asset/Elements/ShowSummary b/share/html/Asset/Elements/ShowSummary
index 3aa12a3b22..b86e1ded2a 100644
--- a/share/html/Asset/Elements/ShowSummary
+++ b/share/html/Asset/Elements/ShowSummary
@@ -47,6 +47,7 @@
%# END BPS TAGGED BLOCK }}}
<%args>
$AssetObj
+$InlineEdit => 0
</%args>
<%init>
my @sections = (
@@ -56,9 +57,6 @@ my @sections = (
"Links", #loc
);

-my $can_edit = $session{CurrentUser}->Privileged
- && $AssetObj->CurrentUserHasRight("ModifyAsset");
-
my %link;
for my $section (@sections) {
my $page = $section eq 'Basics' ? "Modify.html" : "Modify$section.html";
@@ -67,22 +65,75 @@ for my $section (@sections) {
. "/Asset/$page?id="
. $AssetObj->id;
}
+
+my $can_modify = $AssetObj->CurrentUserHasRight('ModifyAsset');
+my $can_modify_cf = $AssetObj->CurrentUserHasRight('ModifyCustomField');
+
+$m->callback( CallbackName => 'ModifyRights', %ARGS, AssetObj => $AssetObj, ARGSRef => \%ARGS,
+ CanModify => \$can_modify, CanModifyCF => \$can_modify_cf );
+
+my $edit_label = $m->interp->apply_escapes( loc("Edit"), 'h' );
+my $cancel_label = $m->interp->apply_escapes( loc("Cancel"), 'h' );
+
+my %inline_edit_behavior;
+if (RT->Config->Get('InlineEditPanelBehavior')) {
+ %inline_edit_behavior = %{ RT->Config->Get('InlineEditPanelBehavior')->{'RT::Asset'} || { Dates => 'hide' } };
+}
+
+my $modify_inline
+ = '<a class="inline-edit-toggle edit" href="%s">'
+ . qq{<span class="fas fa-pencil-alt icon-bordered fa-2x" alt="$edit_label" data-toggle="tooltip" data-placement="top" data-original-title="$edit_label"></span>}
+ . '</a>'
+ . '<a class="inline-edit-toggle cancel hidden" href="#">'
+ . qq{<span class="fas fa-times icon-bordered fa-2x" alt="$cancel_label" data-toggle="tooltip" data-placement="top" data-original-title="$cancel_label"></span>}
+ . '</a>';
</%init>
<div class="asset-metadata">
<div class="form-row">
% for my $section (@sections) {
+% my $modify_url = sprintf( $modify_inline, $m->interp->apply_escapes( $link{$section}, 'h' ) );
+% my $modify_behavior = $InlineEdit ? ($inline_edit_behavior{$section} || $inline_edit_behavior{_default} || 'link') : 'hide';
+
<div class="col-4">
- <&| /Widgets/TitleBox, title => loc($section), title_href => $can_edit ? $link{$section} : "", title_class => "inverse", class => "asset-\L$section" &>
- <& "Show$section", AssetObj => $AssetObj &>
+ <&| /Widgets/TitleBox, title => loc($section), title_href => ($can_modify || $can_modify_cf) ? $link{$section} : "", title_class => "inverse",
+ (($can_modify || $can_modify_cf) && $modify_behavior =~ /^(link|click)$/ ? (titleright_raw => $modify_url) : ()),
+ class => (join " ", "asset-\L$section", ($modify_behavior eq 'always' ? 'editing' : ())),
+ data => { 'inline-edit-behavior' => $modify_behavior }
+ &>
+% unless ($modify_behavior eq 'always') {
+ <div class="inline-edit-display">
+ <& "Show$section", AssetObj => $AssetObj &>
+ </div>
+% }
+% if ($modify_behavior ne 'hide') {
+ <form class="inline-edit" action="<%RT->Config->Get('WebPath')%>/Asset/Display.html" method="post" enctype="multipart/form-data">
+ <input type="hidden" class="hidden" name="id" value="<% $AssetObj->id %>" />
+% if ( $section eq 'Links' ) {
+ <& /Elements/EditLinks, Object => $AssetObj, TwoColumn => 0 &>
+ <& /Elements/EditCustomFields, Object => $AssetObj, Grouping => $section, InTable => 1 &>
+% } elsif ( $section eq 'People' ) {
+ <& /Asset/Elements/EditPeopleInline, AssetObj => $AssetObj &>
+% } else {
+ <& "/Asset/Elements/Edit$section", AssetObj => $AssetObj &>
+% }
+ <div class="form-row">
+ <div class="col-12 text-right">
+ <input type="submit" class="button btn btn-primary" value="<&|/l&>Save</&>" />
+ </div>
+ </div>
+ </form>
+% }
</&>
</div>
% }

<& /Elements/ShowCustomFieldCustomGroupings,
Object => $AssetObj,
- title_href => $can_edit ? RT->Config->Get("WebPath") . "/Asset/ModifyCFs.html" : "",
+ title_href => ($can_modify || $can_modify_cf) ? RT->Config->Get("WebPath") . "/Asset/ModifyCFs.html" : "",
TitleBoxARGS => { title_class => "inverse" },
- GroupingClass => 'col-4'
+ GroupingClass => 'col-4',
+ InlineEdit => ($can_modify || $can_modify_cf) ? $InlineEdit : 0,
+ ActionURL => RT->Config->Get('WebPath') . '/Asset/Display.html',
&>
</div>
</div>
diff --git a/share/html/Asset/Search/Bulk.html b/share/html/Asset/Search/Bulk.html
index 1f7e94d9ed..41ade9d9f8 100644
--- a/share/html/Asset/Search/Bulk.html
+++ b/share/html/Asset/Search/Bulk.html
@@ -62,6 +62,7 @@
Collection => $assets,
AllowSorting => 1,
DisplayFormat => $DisplayFormat,
+ InlineEdit => 0,
&>
% if (not $assets->Count) {
<em><&|/l&>No assets matching search criteria found.</&></em>
diff --git a/share/html/Elements/CollectionAsTable/Row b/share/html/Elements/CollectionAsTable/Row
index d8265e1d9d..5cd3e8ecbc 100644
--- a/share/html/Elements/CollectionAsTable/Row
+++ b/share/html/Elements/CollectionAsTable/Row
@@ -92,9 +92,15 @@ foreach my $column (@Format) {

my %attrs;
my @possible_attrs = qw(style align);
- if ($InlineEdit && $record->isa('RT::Ticket') && $record->CurrentUserHasRight('ModifyTicket')) {
- push(@possible_attrs, 'edit');
+ if (
+ $InlineEdit
+ && ( $record->isa('RT::Ticket') && $record->CurrentUserHasRight('ModifyTicket')
+ || $record->isa('RT::Asset') && $record->CurrentUserHasRight('ModifyAsset') )
+ )
+ {
+ push( @possible_attrs, 'edit' );
}
+
foreach my $attr (@possible_attrs) {
if ( defined $column->{ $attr } ) {
$attrs{ $attr } = $column->{ $attr };
@@ -171,7 +177,8 @@ foreach my $column (@Format) {
$m->out('>');

if ( $attrs{edit} ) {
- $m->out( '<form method="POST" action="' . RT->Config->Get('WebPath') . '/Helpers/TicketUpdate?id=' . $record->id . '" class="editor" autocomplete="off">' );
+ my $helper_name = $record->isa('RT::Ticket') ? 'TicketUpdate' : 'AssetUpdate';
+ $m->out( '<form method="POST" action="' . RT->Config->Get('WebPath') . "/Helpers/$helper_name?id=" . $record->id . '" class="editor" autocomplete="off">' );
$m->out( $attrs{edit} );
$m->out( '<span class="cancel text-danger far fa-times-circle" data-toggle="tooltip" data-placement="left" data-original-title="' . loc('Cancel') . '"></span>' );
$m->out( '<span class="submit text-success far fa-check-circle" data-toggle="tooltip" data-placement="right" data-original-title="' . loc('Save') . '"></span>' );
diff --git a/share/html/Elements/CollectionList b/share/html/Elements/CollectionList
index 2f6aeceeb9..273931177f 100644
--- a/share/html/Elements/CollectionList
+++ b/share/html/Elements/CollectionList
@@ -125,7 +125,7 @@ foreach my $col (@Format) {

$Class ||= $Collection->ColumnMapClassName;

-$InlineEdit = 0 unless $Collection->isa('RT::Tickets');
+$InlineEdit = 0 unless $Collection->isa('RT::Tickets') || $Collection->isa('RT::Assets');

$m->out('<div class="table-responsive">');
$m->out('<table cellspacing="0"');
diff --git a/share/html/Elements/ColumnMap b/share/html/Elements/ColumnMap
index dae63ccca9..22fff375d8 100644
--- a/share/html/Elements/ColumnMap
+++ b/share/html/Elements/ColumnMap
@@ -314,7 +314,20 @@ $WCOLUMN_MAP = $COLUMN_MAP = {
my $role = $self->{load}->(@_);
return unless $role->Id;
if ($role->SingleValue) {
- return \($m->scomp("/Elements/SingleUserRoleInput", role => $role, Ticket => $_[0]));
+ if ( $_[0]->isa('RT::Ticket') ) {
+ return \($m->scomp("/Elements/SingleUserRoleInput", role => $role, Ticket => $_[0]));
+ }
+ elsif ( $_[0]->isa('RT::Asset') ) {
+ my $group = $_[0]->RoleGroup( $role->GroupType);
+ my $user = $group->UserMembersObj()->First || RT->Nobody;
+ my $user_name = $m->interp->apply_escapes( $user->Name, 'h' );
+ my $group_type = $role->GroupType;
+ return \qq{<input class="form-control" type="text" value="$user_name" name="SetRoleMember-$group_type" data-autocomplete="Users" data-autocomplete-return="Name" />};
+ }
+ else {
+ RT->Logger->warning( "Invalid object for custom roles: " . ref $_[0] );
+ return undef;
+ }
}
else {
return undef;
@@ -447,6 +460,17 @@ if ($RecordClass->DOES("RT::Record::Role::Roles")) {
}
},
value => sub { return $role_value->($role, @_, @_ == 2 ? '' : () ) },
+ edit => sub {
+ if ($attrs->{Single} && $RecordClass eq 'RT::Asset' ) {
+ my $group = $_[0]->RoleGroup($role);
+ my $user = $group->UserMembersObj()->First || RT->Nobody;
+ my $user_name = $m->interp->apply_escapes( $user->Name, 'h' );
+ return \qq{<input class="form-control" type="text" value="$user_name" name="SetRoleMember-$role" data-autocomplete="Users" data-autocomplete-return="Name" />};
+ }
+ else {
+ return undef;
+ }
+ },
};

$ROLE_MAP->{$RecordClass}{$role . "s"} = $ROLE_MAP->{$RecordClass}{$role}
diff --git a/share/html/Elements/RT__Asset/ColumnMap b/share/html/Elements/RT__Asset/ColumnMap
index 578ef038ec..9934d27dd3 100644
--- a/share/html/Elements/RT__Asset/ColumnMap
+++ b/share/html/Elements/RT__Asset/ColumnMap
@@ -77,21 +77,25 @@ my $COLUMN_MAP = {
attribute => 'Name',
title => 'Name',
value => sub { $_[0]->Name },
+ edit => sub { return \('<input name="Name" class="form-control" value="'.$m->interp->apply_escapes( $_[0]->Name, 'h' ).'" />') },
},
Description => {
attribute => 'Description',
title => 'Description',
value => sub { $_[0]->Description },
+ edit => sub { return \('<input name="Description" class="form-control" value="'.$m->interp->apply_escapes( $_[0]->Description, 'h' ).'" />') },
},
Catalog => {
attribute => 'Catalog',
title => 'Catalog', # loc
value => sub { $_[0]->CatalogObj->Name },
+ edit => sub { return \($m->scomp('/Asset/Elements/SelectCatalog', Default => $_[0]->Catalog, Name => 'Catalog', ShowNullOption => 0)) },
},
Status => {
title => 'Status',
attribute => 'Status',
- value => sub { loc($_[0]->Status) }
+ value => sub { loc($_[0]->Status) },
+ edit => sub { return \($m->scomp("/Asset/Elements/SelectStatus", AssetObj => $_[0], Name => 'Status' ) ) },
},
ActiveTickets => {
title => 'Active tickets', # loc
diff --git a/share/html/Elements/ShowCustomFieldCustomGroupings b/share/html/Elements/ShowCustomFieldCustomGroupings
index 890a80a1c4..ddbefb35c1 100644
--- a/share/html/Elements/ShowCustomFieldCustomGroupings
+++ b/share/html/Elements/ShowCustomFieldCustomGroupings
@@ -124,7 +124,7 @@ $css_class .= '-info-cfs';

my $TitleBoxARGS = delete $ARGS{TitleBoxARGS} || {};

-$InlineEdit = 0 unless $Object->isa('RT::Ticket');
+$InlineEdit = 0 unless $Object->isa('RT::Ticket') || $Object->isa('RT::Asset');
my %inline_edit_behavior;
if ( my $config = RT->Config->Get('InlineEditPanelBehavior') ) {
%inline_edit_behavior = %{ $config->{ (RT::CustomField->_GroupingClass($Object))[0] } || $config->{'RT::Ticket'} || {} };
diff --git a/share/html/Asset/Display.html b/share/html/Helpers/AssetUpdate
similarity index 71%
copy from share/html/Asset/Display.html
copy to share/html/Helpers/AssetUpdate
index 553fb4f66a..b95e163c48 100644
--- a/share/html/Asset/Display.html
+++ b/share/html/Helpers/AssetUpdate
@@ -45,32 +45,35 @@
%# those contributions and any derivatives thereof.
%#
%# END BPS TAGGED BLOCK }}}
-<& /Elements/Header, Title => loc("Asset #[_1]: [_2]", $asset->id, $asset->Name) &>
-<& /Elements/Tabs &>
+% $r->content_type('application/json; charset=utf-8');
+<% JSON( { actions => \@results } ) |n %>
+% $m->abort;

-% $m->callback(CallbackName => 'BeforeActionList', ARGSRef => \%ARGS, Asset => $asset, Results => \@results);
+<%ARGS>
+$id
+</%ARGS>

-<& /Elements/ListActions, actions => \@results &>
+<%INIT>
+my @results;

-<span class="catalog <% CSSClass($asset->CatalogObj->Name) %>">
-<& Elements/ShowSummary, AssetObj => $asset &>
+my $asset = LoadAsset($id);

-% $m->callback(CallbackName => 'AfterShowSummary', ARGSRef => \%ARGS, Asset => $asset);
+# fill ACL cache
+$asset->CurrentUser->PrincipalObj->HasRights( Object => $asset );

-<& /Elements/ShowHistory,
- Object => $asset,
- ShowDisplayModes => 0,
- DisplayPath => 'History.html',
-&>
+$m->callback(CallbackName => 'ProcessArguments',
+ Asset => $asset,
+ ARGSRef => \%ARGS,
+ results => \@results);

-% $m->callback(CallbackName => 'AfterShowHistory', ARGSRef => \%ARGS, Asset => $asset);
-</span>
+push @results, ProcessAssetRoleMembers( $asset => %ARGS );
+push @results, ProcessRecordLinks( RecordObj => $asset, ARGSRef => \%ARGS );
+push @results, ProcessObjectCustomFieldUpdates( Object => $asset, ARGSRef => \%ARGS );

-<%args>
-$id => undef
-</%args>
-<%init>
-my @results;
-my $asset = LoadAsset($id);
-$m->callback(CallbackName => 'BeforeDisplay', ARGSRef => \%ARGS, Asset => $asset, Results => \@results);
-</%init>
+push @results, UpdateRecordObject(
+ Object => $asset,
+ AttributesRef => [ $asset->WritableAttributes ],
+ ARGSRef => \%ARGS
+);
+
+</%INIT>
diff --git a/share/html/Helpers/CollectionListRow b/share/html/Helpers/CollectionListRow
index a993f767d8..6d1cdc2905 100644
--- a/share/html/Helpers/CollectionListRow
+++ b/share/html/Helpers/CollectionListRow
@@ -60,7 +60,7 @@ $Warning => undef
$DisplayFormat = $m->comp('/Elements/ScrubHTML', Content => $DisplayFormat);
my @Format = $m->comp('/Elements/CollectionAsTable/ParseFormat', Format => $DisplayFormat);

-$m->abort unless ( $ObjectClass // '' ) eq 'RT::Ticket';
+$m->abort unless ( $ObjectClass // '' ) =~ /^RT::(?:Ticket|Asset)$/;

my $record = $ObjectClass->new($session{CurrentUser});
$record->Load($ObjectId);
diff --git a/t/web/helpers-http-cache-headers.t b/t/web/helpers-http-cache-headers.t
index 2e661a231b..1f03859a8f 100644
--- a/t/web/helpers-http-cache-headers.t
+++ b/t/web/helpers-http-cache-headers.t
@@ -57,6 +57,19 @@ diag "create a ticket via the API";
$ticket_id = $id;
}

+my $asset_id;
+# Create an asset so requests to /Helpers/AssetUpdate can succeed.
+diag "create an asset via the API";
+{
+ my $asset = RT::Asset->new( RT->SystemUser );
+ my ($id, $txn, $msg) = $asset->Create(
+ Catalog => 'General assets',
+ Name => 'test asset',
+ );
+ ok $id, 'created a asset #'. $id or diag "error: $msg";
+ is $asset->Name, 'test asset', 'correct name';
+ $asset_id = $id;
+}

my $expected;
diag "set up expected date headers";
@@ -81,7 +94,12 @@ diag "set up expected date headers";
}

foreach my $endpoint ( @endpoints ) {
- $m->get_ok( $endpoint . "?id=${ticket_id}&Status=open&Requestor=root" );
+ if ( $endpoint =~ m{/Helpers/AssetUpdate} ) {
+ $m->get_ok( $endpoint . "?id=${asset_id}&Status=allocated" );
+ }
+ else {
+ $m->get_ok( $endpoint . "?id=${ticket_id}&Status=open&Requestor=root" );
+ }

my $header_key = 'default';
if ( $endpoint =~ m|Autocomplete| ) {

commit c9193de32b336b5747d90815dd3415053c16a26e
Author: sunnavy <sunnavy@bestpractical.com>
Date: Fri Dec 8 15:21:55 2023 -0500

Use role Name/GroupType in case corresponding role groups do not exist yet

Custom role groups could be absent if the corresponding custom roles
were created/applied after some assets or the lazy option is enabled.

diff --git a/share/html/Asset/Elements/EditRoleMembers b/share/html/Asset/Elements/EditRoleMembers
index e498f0b7fe..4dcd65c1f4 100644
--- a/share/html/Asset/Elements/EditRoleMembers
+++ b/share/html/Asset/Elements/EditRoleMembers
@@ -52,14 +52,14 @@ $Recursively => 0
</%args>
<%init>
my $Group = $Object->RoleGroup($Role);
-my $field_name = "RemoveRoleMember-" . $Group->Name;
+my $field_name = "RemoveRoleMember-" . $Role;
</%init>
<ul class="role-members list-group list-group-compact">
% my $Users = $Group->UserMembersObj( Recursively => $Recursively );
% if ($Object->Role($Role)->{Single}) {
% my $user = $Users->First || RT->Nobody;
<li class="list-group-item">
- <input class="form-control selectpicker" type="text" value="<% $user->Name %>" name="SetRoleMember-<% $Group->Name %>" id="SetRoleMember-<% $Group->Name %>" data-autocomplete="Users" data-autocomplete-return="Name" />
+ <input class="form-control selectpicker" type="text" value="<% $user->Name %>" name="SetRoleMember-<% $Role %>" id="SetRoleMember-<% $Role %>" data-autocomplete="Users" data-autocomplete-return="Name" />
</li>
% } else {
% while ( my $user = $Users->Next ) {
diff --git a/t/assets/web.t b/t/assets/web.t
index 7f3e2f8d52..3b3aca6b11 100644
--- a/t/assets/web.t
+++ b/t/assets/web.t
@@ -145,6 +145,90 @@ diag "Bulk update";
# TODO: test more bulk update actions
}

+diag "People update";
+{
+ my $asset = create_asset( Name => "Test asset", Catalog => $catalog->Id );
+ $m->get_ok( '/Asset/Display.html?id=' . $asset->Id );
+ $m->follow_link_ok( { text => 'People' } );
+
+ my $form = $m->form_id('ModifyAssetPeople');
+ my $owner_input = $form->find_input('SetRoleMember-Owner');
+ ok( $owner_input, 'Found owner input' );
+ is( $owner_input->value, 'Nobody', 'Default owner is Nobody' );
+ $m->submit_form_ok(
+ {
+ fields => {
+ 'SetRoleMember-Owner' => 'root',
+ },
+ button => 'Update',
+ },
+ 'Submit form ModifyAssetPeople'
+ );
+ $m->text_contains('Owner set to root');
+
+ $form = $m->form_id('ModifyAssetPeople');
+ $owner_input = $form->find_input('SetRoleMember-Owner');
+ ok( $owner_input, 'Found owner input' );
+ is( $owner_input->value, 'root', 'Input value of owner is root' );
+
+ my $staff = RT::Test->load_or_create_group('Staff');
+ $m->submit_form_ok(
+ {
+ fields => {
+ 'AddUserRoleMember-Role' => 'Contact',
+ AddUserRoleMember => 'alice@localhost',
+ 'AddGroupRoleMember-Role' => 'HeldBy',
+ AddGroupRoleMember => 'Staff',
+ },
+ button => 'Update',
+ },
+ 'Submit form ModifyAssetPeople'
+ );
+ $m->text_contains('Member added: alice@localhost');
+ $m->text_contains('Member added: Staff');
+
+ $form = $m->form_id('ModifyAssetPeople');
+ my $alice = RT::Test->load_or_create_user( Name => 'alice@localhost' );
+ $m->tick('RemoveRoleMember-Contact', $alice->Id);
+ $m->tick('RemoveRoleMember-HeldBy', $staff->Id);
+
+ $m->submit_form_ok(
+ {
+ button => 'Update',
+ },
+ 'Submit form ModifyAssetPeople'
+ );
+ $m->text_contains('Member deleted');
+
+
+ # Add manager later to test if the page works with absent role groups.
+ my $manager = RT::CustomRole->new( RT->SystemUser );
+ ok(
+ $manager->Create(
+ Name => 'Manager',
+ LookupType => RT::Asset->CustomFieldLookupType,
+ MaxValues => 1,
+ )
+ );
+ ok( $manager->AddToObject( $catalog->Id ) );
+
+ $m->reload;
+ $form = $m->form_id('ModifyAssetPeople');
+ my $manager_input = $form->find_input( 'SetRoleMember-' . $manager->GroupType );
+ ok( $manager_input, 'Found manager input' );
+ is( $manager_input->value, 'Nobody', 'Default manager is Nobody' );
+ $m->submit_form_ok(
+ {
+ fields => {
+ 'SetRoleMember-' . $manager->GroupType => 'root',
+ },
+ button => 'Update',
+ },
+ 'Submit form ModifyAssetPeople'
+ );
+ $m->text_contains('Manager set to root');
+}
+
# XXX TODO: test other modify pages

done_testing;

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


hooks/post-receive
--
rt
_______________________________________________
rt-commit mailing list
rt-commit@lists.bestpractical.com
https://lists.bestpractical.com/mailman/listinfo/rt-commit