Remove status description from Session/Http, and update all users of setStatus.
This commit is contained in:
parent
4cf75513d3
commit
19c2c5fa1c
16 changed files with 50 additions and 85 deletions
|
|
@ -614,7 +614,7 @@ sub checkView {
|
|||
return "chunked";
|
||||
}
|
||||
elsif ($self->get("state") ne "published") { # tell em it doesn't exist anymore
|
||||
$http->setStatus("410");
|
||||
$http->setStatus(410);
|
||||
my $notFound = WebGUI::Asset->getNotFound($self->session);
|
||||
$self->session->asset($notFound);
|
||||
return $notFound->www_view;
|
||||
|
|
|
|||
|
|
@ -639,7 +639,7 @@ sub www_view {
|
|||
# Check to make sure it's not in the trash or some other weird place
|
||||
if ($self->state ne "published") {
|
||||
my $i18n = WebGUI::International->new($session,'Asset_File');
|
||||
$session->http->setStatus("404");
|
||||
$session->http->setStatus(404);
|
||||
return sprintf($i18n->get("file not found"), $self->getUrl());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ sub www_view {
|
|||
$self->session->http->setRedirect($self->getUrl("func=manageClipboard"));
|
||||
return undef;
|
||||
} else { # tell em it doesn't exist anymore
|
||||
$self->session->http->setStatus("410");
|
||||
$self->session->http->setStatus(410);
|
||||
return WebGUI::Asset->getNotFound($self->session)->www_view;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1635,7 +1635,7 @@ sub www_deleteThingDataViaAjax {
|
|||
$session->http->setMimeType("application/json");
|
||||
|
||||
unless ($thingId && $thingDataId) {
|
||||
$session->http->setStatus("400", "Bad Request");
|
||||
$session->http->setStatus(400);
|
||||
return JSON->new->encode({message => "Can't get thing data without a thingId and a thingDataId."});
|
||||
}
|
||||
|
||||
|
|
@ -1650,7 +1650,7 @@ sub www_deleteThingDataViaAjax {
|
|||
return JSON->new->encode({message => "Data with thingDataId $thingDataId was deleted."});
|
||||
}
|
||||
else {
|
||||
$session->http->setStatus("404", "Not Found");
|
||||
$session->http->setStatus(404);
|
||||
return JSON->new->encode({message => "The thingId you specified can not be found."});
|
||||
}
|
||||
}
|
||||
|
|
@ -2594,7 +2594,7 @@ sub www_editThingDataSaveViaAjax {
|
|||
my $i18n = WebGUI::International->new($self->session, "Asset_Thingy");
|
||||
|
||||
unless ($thingId && $thingDataId) {
|
||||
$session->http->setStatus("400", "Bad Request");
|
||||
$session->http->setStatus(400);
|
||||
return JSON->new->encode({message => "Can't get thing data without a thingId and a thingDataId."});
|
||||
}
|
||||
|
||||
|
|
@ -2606,19 +2606,19 @@ sub www_editThingDataSaveViaAjax {
|
|||
,$thingProperties);
|
||||
|
||||
if($thingDataId eq 'new' && $self->hasEnteredMaxPerUser($thingId)){
|
||||
$session->http->setStatus("400", "Bad Request");
|
||||
$session->http->setStatus(400);
|
||||
return JSON->new->encode({message => $i18n->get("has entered max per user message")});
|
||||
}
|
||||
|
||||
my ($newThingDataId,$errors) = $self->editThingDataSave($thingId,$thingDataId);
|
||||
|
||||
if ($errors){
|
||||
$session->http->setStatus("400", "Bad Request");
|
||||
$session->http->setStatus(400);
|
||||
return JSON->new->encode($errors);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$session->http->setStatus("404", "Not Found");
|
||||
$session->http->setStatus(404);
|
||||
return JSON->new->encode({message => "The thingId you requested can not be found."});
|
||||
}
|
||||
}
|
||||
|
|
@ -2707,7 +2707,7 @@ sub www_getThingViaAjax {
|
|||
$session->http->setMimeType("application/json");
|
||||
|
||||
unless ($thingId) {
|
||||
$session->http->setStatus("400", "Bad Request");
|
||||
$session->http->setStatus(400);
|
||||
return JSON->new->encode({message => "Can't return thing properties without a thingId."});
|
||||
}
|
||||
|
||||
|
|
@ -2729,7 +2729,7 @@ sub www_getThingViaAjax {
|
|||
return JSON->new->encode($thingProperties);
|
||||
}
|
||||
else {
|
||||
$session->http->setStatus("404", "Not Found");
|
||||
$session->http->setStatus(404);
|
||||
return JSON->new->encode({message => "The thingId you requested can not be found."});
|
||||
}
|
||||
}
|
||||
|
|
@ -2763,7 +2763,7 @@ sub www_getThingsViaAjax {
|
|||
return JSON->new->encode(\@visibleThings);
|
||||
}
|
||||
else {
|
||||
$session->http->setStatus("404", "Not Found");
|
||||
$session->http->setStatus(404);
|
||||
return JSON->new->encode({message => "No visible Things were found in this Thingy."});
|
||||
}
|
||||
}
|
||||
|
|
@ -3123,7 +3123,7 @@ sub www_searchViaAjax {
|
|||
my $i18n = WebGUI::International->new($self->session,"Asset_Thingy");
|
||||
|
||||
unless ($thingId) {
|
||||
$session->http->setStatus("400", "Bad Request");
|
||||
$session->http->setStatus(400);
|
||||
return JSON->new->encode({message => "Can't perform search without a thingId."});
|
||||
}
|
||||
|
||||
|
|
@ -3138,7 +3138,7 @@ sub www_searchViaAjax {
|
|||
return JSON->new->encode($var);
|
||||
}
|
||||
else {
|
||||
$session->http->setStatus("404", "Not Found");
|
||||
$session->http->setStatus(404);
|
||||
return JSON->new->encode({message => "The thingId you requested can not be found."});
|
||||
}
|
||||
}
|
||||
|
|
@ -3549,7 +3549,7 @@ sub www_viewThingDataViaAjax {
|
|||
$session->http->setMimeType("application/json");
|
||||
|
||||
unless ($thingId && $thingDataId) {
|
||||
$session->http->setStatus("400", "Bad Request");
|
||||
$session->http->setStatus(400);
|
||||
return JSON->new->encode({message => "Can't get thing data without a thingId and a thingDataId."});
|
||||
}
|
||||
|
||||
|
|
@ -3564,12 +3564,12 @@ sub www_viewThingDataViaAjax {
|
|||
return JSON->new->encode($output);
|
||||
}
|
||||
else{
|
||||
$session->http->setStatus("404", "Not Found");
|
||||
$session->http->setStatus(404);
|
||||
return JSON->new->encode({message => "The thingDataId you requested can not be found."});
|
||||
}
|
||||
}
|
||||
else {
|
||||
$session->http->setStatus("404", "Not Found");
|
||||
$session->http->setStatus(404);
|
||||
return JSON->new->encode({message => "The thingId you requested can not be found."});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ sub addChild {
|
|||
my $temp = WebGUI::Asset->newByPropertyHashRef($session, $properties) || croak "Couldn't create a new $properties->{className} asset!";
|
||||
my $newAsset = $temp->addRevision($properties, $now, $options);
|
||||
$self->updateHistory("added child ".$id);
|
||||
$session->http->setStatus(201,"Asset Creation Successful");
|
||||
$session->http->setStatus(201);
|
||||
return $newAsset;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@ sub createAccountSave {
|
|||
return undef;
|
||||
}
|
||||
else {
|
||||
$self->session->http->setStatus(201,"Account Registration Successful");
|
||||
$self->session->http->setStatus(201);
|
||||
}
|
||||
|
||||
return undef;
|
||||
|
|
|
|||
|
|
@ -775,7 +775,7 @@ sub hashPassword {
|
|||
sub login {
|
||||
my $self = shift;
|
||||
if(!$self->authenticate($self->session->form->process("username"),$self->session->form->process("identifier"))){
|
||||
$self->session->http->setStatus("401","Incorrect Credentials");
|
||||
$self->session->http->setStatus(401);
|
||||
$self->session->errorHandler->security("login to account ".$self->session->form->process("username")." with invalid information.");
|
||||
my $i18n = WebGUI::International->new($self->session);
|
||||
return $self->displayLogin("<h1>".$i18n->get(70)."</h1>".$self->error);
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ sub handler {
|
|||
if ($var->get("userId") eq "1"
|
||||
&& defined $asset
|
||||
&& !$http->ifModifiedSince($asset->getContentLastModified, $session->setting->get('maxCacheTimeout'))) {
|
||||
$http->setStatus("304","Content Not Modified");
|
||||
$http->setStatus(304);
|
||||
$http->sendHeader;
|
||||
return "chunked";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ The content handler for this package.
|
|||
|
||||
sub handler {
|
||||
my ($session) = @_;
|
||||
$session->http->setStatus("404","Page Not Found");
|
||||
$session->http->setStatus(404);
|
||||
my $output = "";
|
||||
my $notFound = WebGUI::Asset->getNotFound($session);
|
||||
if (defined $notFound) {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ The content handler for this package.
|
|||
sub handler {
|
||||
my ($session) = @_;
|
||||
if ($session->env->get("HTTP_X_MOZ") eq "prefetch") { # browser prefetch is a bad thing
|
||||
$session->http->setStatus("403","We don't allow prefetch, because it increases bandwidth, hurts stats, and can break web sites.");
|
||||
$session->http->setStatus(403);
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ sub www_ajaxCreateUser {
|
|||
# Verify access
|
||||
if ( !canAdd($session) || !canUseService($session) ) {
|
||||
# We need an automatic way to send a request for an http basic auth
|
||||
$session->http->setStatus(401,'Unauthorized');
|
||||
$session->http->setStatus(401);
|
||||
return createServiceResponse( $outputFormat, {
|
||||
error => "WebGUI::Error::Unauthorized",
|
||||
message => "",
|
||||
|
|
@ -441,7 +441,7 @@ sub www_ajaxDeleteUser {
|
|||
# Verify access
|
||||
if ( !canEdit($session) || !canUseService($session) ) {
|
||||
# We need an automatic way to send a request for an http basic auth
|
||||
$session->http->setStatus(401,'Unauthorized');
|
||||
$session->http->setStatus(401);
|
||||
return createServiceResponse( $outputFormat, {
|
||||
error => "WebGUI::Error::Unauthorized",
|
||||
message => "",
|
||||
|
|
@ -458,7 +458,7 @@ sub www_ajaxDeleteUser {
|
|||
} );
|
||||
}
|
||||
elsif ( $userId eq "1" || $userId eq "3" ) {
|
||||
$session->http->setStatus(403,"Forbidden");
|
||||
$session->http->setStatus(403);
|
||||
return createServiceResponse( $outputFormat, {
|
||||
error => 'WebGUI::Error::InvalidParam',
|
||||
param => 'userId',
|
||||
|
|
@ -508,7 +508,7 @@ sub www_ajaxUpdateUser {
|
|||
# Verify access
|
||||
if ( !canEdit($session) || !canUseService($session) ) {
|
||||
# We need an automatic way to send a request for an http basic auth
|
||||
$session->http->setStatus(401,'Unauthorized');
|
||||
$session->http->setStatus(401);
|
||||
return createServiceResponse( $outputFormat, {
|
||||
error => "WebGUI::Error::Unauthorized",
|
||||
message => "",
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ sub _httpBasicLogin {
|
|||
$self->session->request->headers_out->set(
|
||||
'WWW-Authenticate' => 'Basic realm="'.$self->session->setting->get('companyName').'"'
|
||||
);
|
||||
$self->session->http->setStatus(401,'Unauthorized');
|
||||
$self->session->http->setStatus(401);
|
||||
$self->session->http->sendHeader;
|
||||
return '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@ This package allows the manipulation of HTTP protocol information.
|
|||
|
||||
$mimetype = $http->getMimeType();
|
||||
$code = $http->getStatus();
|
||||
($code, $description) = $http->getStatus();
|
||||
$description = $http->getStatusDescription();
|
||||
$boolean = $http->isRedirect();
|
||||
|
||||
$http->setCookie($name,$value);
|
||||
|
|
@ -152,27 +150,11 @@ the code returned will be 200.
|
|||
|
||||
sub getStatus {
|
||||
my $self = shift;
|
||||
$self->{_http}{statusDescription} = $self->{_http}{statusDescription} || "OK";
|
||||
my $status = $self->{_http}{status} || "200";
|
||||
return $status;
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getStatusDescription ( ) {
|
||||
|
||||
Returns the current HTTP status description. If no description has
|
||||
been set, "OK" will be returned.
|
||||
|
||||
=cut
|
||||
|
||||
sub getStatusDescription {
|
||||
my $self = shift;
|
||||
return $self->{_http}{statusDescription} || "OK";
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getStreamedFile ( ) {
|
||||
|
|
@ -316,7 +298,6 @@ sub sendHeader {
|
|||
$response->header( 'Content-Disposition' => qq{attachment; filename="}.$self->getFilename().'"');
|
||||
}
|
||||
$response->status($self->getStatus());
|
||||
# $response->status_line($self->getStatus().' '.$self->getStatusDescription()); # TODO - re-enable
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
|
|
@ -331,7 +312,6 @@ sub _sendMinimalHeader {
|
|||
"Cache-Control" => "no-cache",
|
||||
);
|
||||
$response->status($self->getStatus());
|
||||
# $response->status_line($self->getStatus().' '.$self->getStatusDescription()); # TODO - re-enable
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
|
@ -527,7 +507,7 @@ sub setRedirect {
|
|||
return undef if ($url eq $self->session->url->page() && scalar(@params) < 1); # prevent redirecting to self
|
||||
$self->session->errorHandler->info("Redirecting to $url");
|
||||
$self->setRedirectLocation($url);
|
||||
$self->setStatus($type, "Redirect");
|
||||
$self->setStatus($type);
|
||||
$self->session->style->setMeta({"http-equiv"=>"refresh",content=>"0; URL=".$url});
|
||||
}
|
||||
|
||||
|
|
@ -547,7 +527,7 @@ sub setRedirectLocation {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 setStatus ( code, description )
|
||||
=head2 setStatus ( code )
|
||||
|
||||
Sets the HTTP status code.
|
||||
|
||||
|
|
@ -555,16 +535,11 @@ Sets the HTTP status code.
|
|||
|
||||
An HTTP status code. It is a 3 digit status number.
|
||||
|
||||
=head3 description
|
||||
|
||||
An HTTP status code description. It is a little one line of text that describes the status code.
|
||||
|
||||
=cut
|
||||
|
||||
sub setStatus {
|
||||
my $self = shift;
|
||||
$self->{_http}{status} = shift;
|
||||
$self->{_http}{statusDescription} = shift;
|
||||
my $self = shift;
|
||||
$self->{_http}{status} = shift;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ Returns a message stating that this functionality can only be used by administra
|
|||
sub adminOnly {
|
||||
my $self = shift;
|
||||
my $i18n = WebGUI::International->new($self->session);
|
||||
$self->session->http->setStatus("401", "Admin Only");
|
||||
$self->session->http->setStatus(401);
|
||||
my $output = '<h1>'.$i18n->get(35).'</h1>';
|
||||
$output .= $i18n->get(36);
|
||||
return $self->session->style->userStyle($output);
|
||||
|
|
@ -77,7 +77,7 @@ sub insufficient {
|
|||
my $self = shift;
|
||||
my $noStyle = shift;
|
||||
my $i18n = WebGUI::International->new($self->session);
|
||||
$self->session->http->setStatus("401", "Insufficient Privileges");
|
||||
$self->session->http->setStatus(401);
|
||||
my $output = '<h1>'.$i18n->get(37).'</h1>';
|
||||
if ($noStyle) {
|
||||
$self->session->style->useEmptyStyle(1);
|
||||
|
|
@ -102,7 +102,7 @@ sub locked {
|
|||
my $self = shift;
|
||||
my $noStyle = shift;
|
||||
my $i18n = WebGUI::International->new($self->session);
|
||||
$self->session->http->setStatus("401", "Insufficient Privileges");
|
||||
$self->session->http->setStatus(401);
|
||||
my $output = '<h1>'.$i18n->get(37).'</h1>';
|
||||
if ($noStyle) {
|
||||
$self->session->style->useEmptyStyle(1);
|
||||
|
|
@ -147,7 +147,7 @@ Returns a message stating that the user does not have the privileges necessary t
|
|||
|
||||
sub noAccess {
|
||||
my $self = shift;
|
||||
$self->session->http->setStatus("401", "No Access");
|
||||
$self->session->http->setStatus(401);
|
||||
if ($self->session->user->isVisitor) {
|
||||
return WebGUI::Operation::Auth::www_auth($self->session, "init");
|
||||
} else {
|
||||
|
|
@ -170,7 +170,7 @@ Returns a message stating that the user they requested information about is no l
|
|||
sub notMember {
|
||||
my $self = shift;
|
||||
my $i18n = WebGUI::International->new($self->session);
|
||||
$self->session->http->setStatus("400", "Not A Member");
|
||||
$self->session->http->setStatus(400);
|
||||
my ($output);
|
||||
$output = '<h1>'.$i18n->get(345).'</h1>';
|
||||
$output .= $i18n->get(346);
|
||||
|
|
@ -202,7 +202,7 @@ Returns a message stating that the user made a request to delete something that
|
|||
sub vitalComponent {
|
||||
my $self = shift;
|
||||
my $i18n = WebGUI::International->new($self->session);
|
||||
$self->session->http->setStatus("403", "Vital Component");
|
||||
$self->session->http->setStatus(403);
|
||||
my ($output);
|
||||
$output = '<h1>'.$i18n->get(40).'</h1>';
|
||||
$output .= $i18n->get(41);
|
||||
|
|
|
|||
|
|
@ -894,7 +894,7 @@ sub www_processRecurringTransactionPostback {
|
|||
# First check whether the original transaction actualy exists
|
||||
if (WebGUI::Error->caught || !(defined $baseTransaction) ) {
|
||||
$session->errorHandler->warn("Check recurring postback: No base transction for XID: [$originatingXid]");
|
||||
$session->http->setStatus('500', "No base transction for XID: [$originatingXid]");
|
||||
$session->http->setStatus(500);
|
||||
return "Check recurring postback. No base transction for XID: [$originatingXid]";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use Data::Dumper;
|
|||
use Test::More; # increment this value for each test you create
|
||||
use Test::Deep;
|
||||
|
||||
plan tests => 57;
|
||||
plan tests => 48;
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
|
||||
|
|
@ -43,24 +43,20 @@ isa_ok($http, 'WebGUI::Session::Http', 'session has correct object type');
|
|||
|
||||
####################################################
|
||||
#
|
||||
# setStatus, getStatus, getStatusDescription
|
||||
# setStatus, getStatus
|
||||
#
|
||||
####################################################
|
||||
|
||||
$http->setStatus('123');
|
||||
|
||||
is($http->getStatus, '123', 'getStatus: returns correct code');
|
||||
is($http->getStatusDescription, 'OK', 'getStatusDescription: returns default description via getStatus');
|
||||
|
||||
$http->setStatus('');
|
||||
|
||||
is($http->getStatusDescription, 'OK', 'getStatusDescription: returns default description via itself');
|
||||
is($http->getStatus, '200', 'getStatus: returns default code');
|
||||
|
||||
$http->setStatus('', 'packets are great');
|
||||
|
||||
is($http->getStatusDescription, 'packets are great', 'getStatusDescription: returns correct description');
|
||||
|
||||
####################################################
|
||||
#
|
||||
# isRedirect
|
||||
|
|
@ -71,10 +67,10 @@ $http->setStatus('200');
|
|||
ok(!$http->isRedirect, 'isRedirect: 200 is not');
|
||||
|
||||
$http->setStatus('301');
|
||||
ok($http->isRedirect, 'isRedirect: 301 is');
|
||||
ok($http->isRedirect, '... 301 is');
|
||||
|
||||
$http->setStatus('302');
|
||||
ok($http->isRedirect, 'isRedirect: 302 is too');
|
||||
ok($http->isRedirect, '... 302 is too');
|
||||
$http->setStatus('200');
|
||||
|
||||
####################################################
|
||||
|
|
@ -159,7 +155,6 @@ $session->request->uri('/here/later');
|
|||
|
||||
$http->setRedirect('/here/now');
|
||||
is($http->getStatus, 302, 'setRedirect: sets HTTP status');
|
||||
is($http->getStatusDescription, 'Redirect', 'setRedirect: sets HTTP status description');
|
||||
is($http->getRedirectLocation, '/here/now', 'setRedirect: redirect location');
|
||||
|
||||
$session->style->useEmptyStyle(1);
|
||||
|
|
@ -214,14 +209,14 @@ is($http->sendHeader, undef, 'sendHeader returns undef when no request object is
|
|||
|
||||
{
|
||||
##A new, clean session
|
||||
my $session = WebGUI::Test->newSession('nocleanup');
|
||||
my $guard = WebGUI::Test->cleanupGuard($session);
|
||||
my $session1 = WebGUI::Test->newSession('noCleanup');
|
||||
my $guard = WebGUI::Test->cleanupGuard($session1);
|
||||
|
||||
$session->http->setRedirect('/here/there');
|
||||
$session->http->sendHeader;
|
||||
is($session->response->status, 302, 'sendHeader as redirect: status set to 301');
|
||||
$session1->http->setRedirect('/here/there');
|
||||
$session1->http->sendHeader;
|
||||
is($session1->response->status, 302, 'sendHeader as redirect: status set to 301');
|
||||
cmp_deeply(
|
||||
headers_out($session->response->headers),
|
||||
headers_out($session1->response->headers),
|
||||
{
|
||||
'Location' => '/here/there',
|
||||
},
|
||||
|
|
@ -249,11 +244,6 @@ is($http->sendHeader, undef, 'sendHeader returns undef when no request object is
|
|||
|
||||
$http->sendHeader();
|
||||
is($response->status, 200, 'sendHeader: status set');
|
||||
my $can_status_line = ok($response->can('status_line'), 'response can set a status line');
|
||||
SKIP: {
|
||||
skip 'no status_line method in Plack::Response', 1 unless $can_status_line;
|
||||
is($response->status_line, '200 Just spiffy', '... status_line set');
|
||||
}
|
||||
cmp_deeply(
|
||||
[ $response->content_type ],
|
||||
[ 'text/html', 'charset=UTF-8']
|
||||
|
|
@ -456,7 +446,7 @@ is($http->sendHeader, undef, 'sendHeader returns undef when no request object is
|
|||
my $http_request = HTTP::Request::Common::GET('http://'.$session->config->get('sitename')->[0]);
|
||||
$http_request->header('If-Modified-Since' => '');
|
||||
my $session = WebGUI::Test->newSession('nocleanup', $http_request);
|
||||
my $guard = WebGUI::Test->cleanupGuard($session);
|
||||
my $guard = WebGUI::Test->addToCleanup($session);
|
||||
ok $session->http->ifModifiedSince(0), 'ifModifiedSince: empty header always returns true';
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue