Skip to content

Commit

Permalink
[4.x] Field based redirects may provide a status (#9417)
Browse files Browse the repository at this point in the history
Co-authored-by: Duncan McClean <[email protected]>
Co-authored-by: Jason Varga <[email protected]>
  • Loading branch information
3 people authored Mar 11, 2024
1 parent 66fd5e4 commit 963278a
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 4 deletions.
11 changes: 10 additions & 1 deletion src/Http/Controllers/CP/Collections/CollectionsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,16 @@ protected function createLinkBlueprint($namespace)
'title' => __('Link'),
'fields' => [
['handle' => 'title', 'field' => ['type' => 'text']],
['handle' => 'redirect', 'field' => ['type' => 'link', 'required' => true]],
[
'handle' => 'redirect',
'field' => [
'type' => 'group', 'required' => true, 'width' => '100',
'fields' => [
['handle' => 'url', 'field' => ['type' => 'link', 'required' => true, 'width' => '100', 'display' => __('Location')]],
['handle' => 'status', 'field' => ['type' => 'radio', 'inline' => 'true', 'required' => true, 'options' => [301 => __('301 (Permanent)'), 302 => __('302 (Temporary)')], 'width' => '100', 'display' => __('HTTP Status'), 'default' => 302]],
],
],
],
],
])
->save();
Expand Down
4 changes: 2 additions & 2 deletions src/Http/Responses/DataResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ protected function addViewPaths()

protected function getRedirect()
{
if (! $this->data->get('redirect')) {
if (! $raw = $this->data->get('redirect')) {
return;
}

Expand All @@ -86,7 +86,7 @@ protected function getRedirect()
throw new NotFoundHttpException;
}

return redirect($redirect);
return redirect($redirect, $raw['status'] ?? 302);
}

protected function protect()
Expand Down
10 changes: 10 additions & 0 deletions src/Routing/ResolveRedirect.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Statamic\Contracts\Entries\Entry;
use Statamic\Facades;
use Statamic\Facades\Site;
use Statamic\Fields\Values;
use Statamic\Structures\Page;
use Statamic\Support\Str;

Expand Down Expand Up @@ -39,6 +40,15 @@ public function item($redirect, $parent = null, $localize = false)
return $this->firstChild($parent);
}

if (is_array($redirect)) {
$redirect = $redirect['url'];
}

if ($redirect instanceof Values) {
// Assume it's a `group` fieldtype with a `url` subfield.
return $redirect->url->value();
}

if (Str::startsWith($redirect, 'entry::')) {
$id = Str::after($redirect, 'entry::');

Expand Down
2 changes: 1 addition & 1 deletion src/Tags/Collection/Entries.php
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,7 @@ protected function queryRedirects($query)
}

if (! $this->params->bool(['redirects', 'links'], false)) {
$query->where('redirect', '=', null);
$query->whereNull('redirect');
}
}

Expand Down
52 changes: 52 additions & 0 deletions tests/FrontendTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -910,6 +910,58 @@ public static function redirectProviderNoBlueprintProvider()
];
}

/**
* @test
*/
public function redirect_http_status_is_applied_when_present_in_blueprint()
{
$blueprint = Blueprint::makeFromFields([
'redirect' => [
'type' => 'group',
'fields' => [
['handle' => 'url', 'field' => ['type' => 'link']],
['handle' => 'status', 'field' => ['type' => 'radio', 'options' => [301, 302]]],
],
]]);
Blueprint::shouldReceive('in')->with('collections/pages')->andReturn(collect([$blueprint]));

tap($this->createPage('about', [
'with' => [
'title' => 'About',
'redirect' => [
'url' => '/test',
'status' => 301,
],
],
]))->save();

$response = $this->get('/about');

$response->assertRedirect('/test');
$response->assertStatus(301);
}

/**
* @test
*/
public function redirect_http_status_is_applied_when_missing_from_blueprint()
{
tap($this->createPage('about', [
'with' => [
'title' => 'About',
'redirect' => [
'url' => '/test',
'status' => 301,
],
],
]))->save();

$response = $this->get('/about');

$response->assertRedirect('/test');
$response->assertStatus(301);
}

/** @test */
public function it_protects_404_pages()
{
Expand Down
58 changes: 58 additions & 0 deletions tests/Routing/ResolveRedirectTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
use Statamic\Contracts\Assets\Asset;
use Statamic\Contracts\Entries\Entry;
use Statamic\Facades;
use Statamic\Fields\Value;
use Statamic\Fields\Values;
use Statamic\Fieldtypes\Link;
use Statamic\Fieldtypes\Link\ArrayableLink;
use Statamic\Routing\ResolveRedirect;
use Statamic\Structures\Page;
use Statamic\Structures\Pages;
Expand Down Expand Up @@ -210,4 +214,58 @@ public function it_can_invoke_the_class_or_call_resolve()

$this->assertEquals('hello', $resolve('foo', 'bar'));
}

/** @test */
public function it_can_resolve_a_group_field_with_url()
{
// When using the "Link" blueprint, the `redirect` field would be a `group` type
// contain `url` and `code` subfields. The `url` subfield is a `link` type.
// When resolving the redirect, the augmented value of the group field
// will be passed in which is a `Values` object.

$resolver = new ResolveRedirect;

$fieldtype = Mockery::mock(Link::class)->makePartial()->shouldReceive('augment')->andReturn(new ArrayableLink('/test'))->getMock();

$value = new Values([
'url' => new Value('/test', 'url', $fieldtype),
'code' => '', // irrelevant for this test
]);

$this->assertEquals('/test', $resolver($value));
$this->assertEquals('/test', $resolver->item($value));
}

/** @test */
public function it_can_resolve_a_group_field_with_entry()
{
// Same as above, but in this case, an entry was selected in the nested link field.

$resolver = new ResolveRedirect;

$entry = Mockery::mock(Entry::class)->shouldReceive('url')->once()->andReturn('/the-entry')->getMock();

$fieldtype = Mockery::mock(Link::class)->makePartial()->shouldReceive('augment')->andReturn(new ArrayableLink($entry))->getMock();

$value = new Values([
'url' => new Value('entry::123', 'url', $fieldtype),
'code' => '', // irrelevant for this test
]);

$this->assertEquals('/the-entry', $resolver($value));
$this->assertEquals($entry, $resolver->item($value));
}

/** @test */
public function it_can_resolve_arrays_with_url_and_code()
{
// Same as above, but without a blueprint field.
// Maybe a user put an array directly in their data.

$resolver = new ResolveRedirect;

$arr = ['url' => '/test', 'code' => 301];
$this->assertEquals('/test', $resolver($arr));
$this->assertEquals('/test', $resolver->item($arr));
}
}

0 comments on commit 963278a

Please sign in to comment.