%# BEGIN BPS TAGGED BLOCK {{{ %# %# COPYRIGHT: %# %# This software is Copyright (c) 1996-2025 Best Practical Solutions, LLC %# <sales@bestpractical.com> %# %# (Except where explicitly superseded by other copyright notices) %# %# %# LICENSE: %# %# This work is made available to you under the terms of Version 2 of %# the GNU General Public License. A copy of that license should have %# been provided with this software, but in any event can be snarfed %# from www.gnu.org. %# %# This work is distributed in the hope that it will be useful, but %# WITHOUT ANY WARRANTY; without even the implied warranty of %# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU %# General Public License for more details. %# %# You should have received a copy of the GNU General Public License %# along with this program; if not, write to the Free Software %# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA %# 02110-1301 or visit their web page on the internet at %# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html. %# %# %# CONTRIBUTION SUBMISSION POLICY: %# %# (The following paragraph is not intended to limit the rights granted %# to you to modify and distribute this software under the terms of %# the GNU General Public License and is only of importance to you if %# you choose to contribute your changes and enhancements to the %# community by submitting them to Best Practical Solutions, LLC.) %# %# By intentionally submitting any modifications, corrections or %# derivatives to this work, or any other work intended for use with %# Request Tracker, to Best Practical Solutions, LLC, you confirm that %# you are the copyright holder for those contributions and you grant %# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable, %# royalty-free, perpetual, license to use, copy, create derivative %# works based on those contributions, and sublicense and distribute %# those contributions and any derivatives thereof. %# %# END BPS TAGGED BLOCK }}} <& /Elements/Header, Title => $title &> <& /Elements/Tabs &> <& /Elements/ListActions, actions => \@results &> <&| /Widgets/TitleBox, title => $title, class => 'article-basics', &> <& Elements/EditBasics, ArticleObj => $ArticleObj, EditClass => 1, DefaultClass => $DefaultClass, ClassObj => $ClassObj, %ARGS , id => $id, $id eq 'new' ? ( OnClassChange => "jQuery(this).closest('form').find('input[name=ClassChanged]').val(1);jQuery(this).closest('form').submit();" ) : (), &> <&| /Widgets/TitleBox, title => 'Content', class => 'article-content', &> <& Elements/EditCustomFields, ArticleObj => $ArticleObj, CFContent => \%CFContent, ClassObj => $ClassObj, ClassChanged => $ARGS{ClassChanged}, %ARGS, id => $id, ForCreation => ($id eq 'new'), &> <&| /Widgets/TitleBox, title => 'Links', class => 'article-links', &> <& Elements/EditLinks, ArticleObj => $ArticleObj, %ARGS, id => $id &> <&| /Widgets/TitleBox, title => 'Topics', class => 'article-topics', &> <& Elements/EditTopics, ArticleObj => $ArticleObj, Classes => [$ArticleObj->Id ? $ArticleObj->ClassObj : $ClassObj], OnlyThisClass => 1, %ARGS, id => $id &>
<& /Elements/Submit, Label => ($id eq 'new' ? loc('Create') : loc('Save Changes')), color => "#993333" &>
<%INIT> if ( exists $ARGS{'Topics'} ) { $ARGS{'Topics'} = ref( $ARGS{'Topics'} ) ? $ARGS{'Topics'} : [ $ARGS{'Topics'} ]; } my @results; my $title; my $Entries = {}; my $ArticleObj = RT::Article->new( $session{'CurrentUser'} ); my $ClassObj = RT::Class->new( $session{'CurrentUser'} ); my $DefaultClass; if ($Class) { $ClassObj->Load($Class); Abort(loc("'[_1]' isn't a valid class", $Class)) unless $ClassObj->Id; $DefaultClass = $ClassObj->Id; } # If we don't have a class, default to the first class that user can # see and in which user can create articles if (!$DefaultClass) { my $Classes = RT::Classes->new($session{'CurrentUser'}); $Classes->LimitToEnabled(); while (my $class = $Classes->Next) { next unless ($class->Name); $DefaultClass = $class->Id; $ClassObj = $class; last; } } my %create_args; my %CFContent; if ( $ARGS{SetEnabled} ) { $ARGS{Disabled} = $ARGS{Enabled} ? 0 : 1; } my $sortorder_ok = 1; if ($ARGS{SortOrder}) { if ($ARGS{SortOrder} !~ /^-?\d+$/) { push @results, (0, loc('Sort Order must be an integer')); $sortorder_ok = 0; } } if ( !$id ) { $title = loc('Create a new article'); foreach my $arg ( sort keys %ARGS ) { if ( $arg =~ /^Transaction-(\d+)$/ and $ARGS{$arg} ) { my $trans = RT::Transaction->new( $session{'CurrentUser'} ); $trans->Load($1); my $cf_id = $ARGS{$arg}; my $cf = RT::CustomField->new( $session{'CurrentUser'} ); $cf->LoadById( $cf_id ); if ($CFContent{$cf_id}) { if ($cf->Type eq 'HTML') { $CFContent{$cf_id} .= "
"; } else { $CFContent{$cf_id} .= "\n\n"; } } $CFContent{$cf_id} .= $trans->Content( ($cf->Type eq 'HTML') ? (Type => 'text/html') : () ); } } $id = 'new'; } elsif ( $id eq 'new' ) { if ( $ARGS{'RefersTo-new'} ) { @{ $create_args{'RefersTo-new'} } = split( /\s+/, $ARGS{'RefersTo-new'} ); } if ( $ARGS{'new-RefersTo'} ) { @{ $create_args{'new-RefersTo'} } = split( /\s+/, $ARGS{'new-RefersTo'} ); } my %cfs; if (!$ARGS{ClassChanged}) { if ($sortorder_ok) { %cfs = ProcessObjectCustomFieldUpdatesForCreate( ARGSRef => \%ARGS, ContextObject => $ClassObj, ); my $msg; ( $id, $msg ) = $ArticleObj->Create( Summary => $ARGS{'Summary'}, Name => $ARGS{'Name'}, SortOrder => $ARGS{'SortOrder'}, Class => $ARGS{'Class'}, Topics => $ARGS{'Topics'}, Disabled => $ARGS{'Disabled'}, %create_args, %cfs ); push( @results, $msg ); } else { $id = 0; } } else { $id = 0; } if ($id) { $ArticleObj->Load($id); $title = loc( 'Modify article #[_1]', $ArticleObj->Id ); delete $ARGS{id}; MaybeRedirectForResults( Actions => \@results, Arguments => { id => $ArticleObj->id }, ); } else { $ArticleObj = RT::Article->new( $session{'CurrentUser'} ); $id = 'new'; $title = loc('Create a new article'); } } else { $ArticleObj->Load($id); unless ( $ArticleObj->id ) { $m->comp( "/Elements/Error", Why => loc("Unable to load article") ); } # If it was a real submit, make changes. # If it was just a reload to change the Class, don't make changes. if (!$ARGS{ClassChanged}) { my @attribs = qw(Name Summary Class Disabled SortOrder); if ($sortorder_ok) { @results = UpdateRecordObject( AttributesRef => \@attribs, Object => $ArticleObj, ARGSRef => \%ARGS ); } my @cf_results = ProcessObjectCustomFieldUpdates( Object => $ArticleObj, ARGSRef => \%ARGS ); @results = ( @results, @cf_results ); # Delete links that are gone gone gone. foreach my $arg ( keys %ARGS ) { if ( $arg =~ /DeleteLink-(.*?)-(RefersTo|MemberOf|RefersTo)-(.*)$/ ) { my $base = $1; my $type = $2; my $target = $3; my ( $val, $msg ) = $ArticleObj->DeleteLink( Base => $base, Type => $type, Target => $target ); push @results, $msg; } } my @linktypes = qw(DependsOn MemberOf RefersTo ); foreach my $linktype (@linktypes) { for my $luri ( split( / /, ( $ARGS{ $ArticleObj->Id . "-$linktype" } || '' )) ) { $luri =~ s/\s*$//; # Strip trailing whitespace my ( $val, $msg ) = $ArticleObj->AddLink( Target => $luri, Type => $linktype ); push @results, $msg; delete $ARGS{ $ArticleObj->Id . "-$linktype" } if $val; } for my $luri ( split( / /, ( $ARGS{ "$linktype-" . $ArticleObj->Id } || '' )) ) { my ( $val, $msg ) = $ArticleObj->AddLink( Base => $luri, Type => $linktype ); push @results, $msg; delete $ARGS{ "$linktype-" . $ArticleObj->Id } if $val; } } my %topics; if ( $ARGS{'EditTopics'} ) { $topics{$_}++ for @{ $ARGS{'Topics'} }; my $objTopics = RT::ObjectTopics->new( $session{'CurrentUser'} ); $objTopics->LimitToObject($ArticleObj); while ( my $t = $objTopics->Next ) { $topics{ $t->Topic }--; } for my $id ( keys %topics ) { if ( $topics{$id} > 0 ) { my ( $val, $msg ) = $ArticleObj->AddTopic( Topic => $id ); push @results, $msg; } elsif ( $topics{$id} < 0 ) { my ( $val, $msg ) = $ArticleObj->DeleteTopic( Topic => $id ); push @results, $msg; } } } } $title = loc( 'Modify article #[_1]', $ArticleObj->Id ); } # if they're working on an existing article if ( $ArticleObj->id ) { unless ( $ArticleObj->CurrentUserHasRight('ShowArticle') ) { $m->comp( "/Elements/Error", Why => loc("No permission to view Article") ); } } <%ARGS> $id => undef $Class => undef