Skip to content

Commit

Permalink
Fix language permissions #6497
Browse files Browse the repository at this point in the history
  • Loading branch information
afbora committed Aug 19, 2024
1 parent d138680 commit af9b0a5
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 9 deletions.
3 changes: 3 additions & 0 deletions i18n/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,12 @@
"error.form.notSaved": "The form could not be saved",

"error.language.code": "Please enter a valid code for the language",
"error.language.create.permission": "You are not allowed to create a language",
"error.language.delete.permission": "You are not allowed to delete the language",
"error.language.duplicate": "The language already exists",
"error.language.name": "Please enter a valid name for the language",
"error.language.notFound": "The language could not be found",
"error.language.update.permission": "You are not allowed to update the language",

"error.layout.validation.block": "There's an error on the \"{field}\" field in block {blockIndex} using the \"{fieldset}\" block type in layout {layoutIndex}",
"error.layout.validation.settings": "There's an error in layout {index} settings",
Expand Down
24 changes: 21 additions & 3 deletions panel/src/components/Views/LanguagesView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<k-button-group slot="left">
<k-button
:text="$t('language.create')"
:disabled="!$permissions.languages.create"
icon="add"
@click="$dialog('languages/create')"
/>
Expand All @@ -30,14 +31,23 @@
v-if="secondaryLanguages.length"
:items="secondaryLanguages"
/>
<k-empty v-else icon="globe" @click="$dialog('languages/create')">
<k-empty
v-else
icon="globe"
:disabled="!$permissions.languages.create"
@click="$dialog('languages/create')"
>
{{ $t("languages.secondary.empty") }}
</k-empty>
</section>
</template>

<template v-else-if="languages.length === 0">
<k-empty icon="globe" @click="$dialog('languages/create')">
<k-empty
icon="globe"
:disabled="!$permissions.languages.create"
@click="$dialog('languages/create')"
>
{{ $t("languages.empty") }}
</k-empty>
</template>
Expand Down Expand Up @@ -66,20 +76,28 @@ export default {
icon: "globe"
},
link: () => {
if (!this.$permissions.languages.update) {
return null;
}
this.$dialog(`languages/${language.id}/update`);
},
options: [
{
icon: "edit",
text: this.$t("edit"),
disabled: !this.$permissions.languages.update,
click() {
this.$dialog(`languages/${language.id}/update`);
}
},
{
icon: "trash",
text: this.$t("delete"),
disabled: language.default && this.languages.length !== 1,
disabled: (
(language.default && this.languages.length !== 1) ||
!this.$permissions.languages.delete
),
click() {
this.$dialog(`languages/${language.id}/delete`);
}
Expand Down
30 changes: 28 additions & 2 deletions src/Cms/Language.php
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,17 @@ protected static function converter(string $from, string $to): bool
*/
public static function create(array $props)
{
$kirby = App::instance();
$user = $kirby->user();

if (
$user === null ||
$user->role()->permissions()->for('languages', 'create') === false
) {
throw new PermissionException(['key' => 'language.create.permission']);
}

$props['code'] = Str::slug($props['code'] ?? null);
$kirby = App::instance();
$languages = $kirby->languages();

// make the first language the default language
Expand Down Expand Up @@ -256,10 +265,18 @@ public static function create(array $props)
public function delete(): bool
{
$kirby = App::instance();
$user = $kirby->user();
$languages = $kirby->languages();
$code = $this->code();
$isLast = $languages->count() === 1;

if (
$user === null ||
$user->role()->permissions()->for('languages', 'delete') === false
) {
throw new PermissionException(['key' => 'language.delete.permission']);
}

// trigger before hook
$kirby->trigger('language.delete:before', [
'language' => $this
Expand Down Expand Up @@ -672,13 +689,22 @@ public function url(): string
*/
public function update(array $props = null)
{
$kirby = App::instance();
$user = $kirby->user();

if (
$user === null ||
$user->role()->permissions()->for('languages', 'update') === false
) {
throw new PermissionException(['key' => 'language.update.permission']);
}

// don't change the language code
unset($props['code']);

// make sure the slug is nice and clean
$props['slug'] = Str::slug($props['slug'] ?? null);

$kirby = App::instance();
$updated = $this->clone($props);

// validate the updated language
Expand Down
3 changes: 2 additions & 1 deletion src/Cms/Permissions.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ class Permissions
],
'languages' => [
'create' => true,
'delete' => true
'delete' => true,
'update' => true
],
'pages' => [
'changeSlug' => true,
Expand Down
155 changes: 152 additions & 3 deletions tests/Cms/Languages/LanguageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Kirby\Data\Data;
use Kirby\Exception\InvalidArgumentException;
use Kirby\Exception\PermissionException;
use Kirby\Filesystem\Dir;
use Kirby\Filesystem\F;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -472,6 +473,8 @@ public function testBaseUrl($kirbyUrl, $url, $expected)

public function testCreate()
{
$this->app->impersonate('kirby');

$language = Language::create([
'code' => 'en'
]);
Expand All @@ -485,6 +488,8 @@ public function testCreate()

public function testDelete()
{
$this->app->impersonate('kirby');

$language = Language::create([
'code' => 'en'
]);
Expand All @@ -496,6 +501,8 @@ public function testUpdate()
{
Dir::make($contentDir = $this->fixtures . '/content');

$this->app->impersonate('kirby');

$language = Language::create([
'code' => 'en'
]);
Expand All @@ -505,12 +512,55 @@ public function testUpdate()
$this->assertSame('English', $language->name());
}

/**
* @covers ::create
*/
public function testCreateNoPermissions()
{
$app = $this->app->clone([
'blueprints' => [
'users/editor' => [
'name' => 'editor',
'permissions' => [
'languages' => [
'create' => false
]
]
],
],
'users' => [
['email' => 'test@getkirby.com', 'role' => 'editor']
]
]);

$this->expectException(PermissionException::class);
$this->expectExceptionMessage('You are not allowed to create a language');

$app->impersonate('test@getkirby.com');
Language::create([
'code' => 'en'
]);
}

/**
* @covers ::create
*/
public function testCreateWithoutLoggedUser()
{
$this->expectException(PermissionException::class);
$this->expectExceptionMessage('You are not allowed to create a language');

Language::create([
'code' => 'en'
]);
}

public function testCreateHooks()
{
$calls = 0;
$phpunit = $this;

new App([
$app = $this->app->clone([
'roots' => [
'index' => $this->fixtures = __DIR__ . '/fixtures/CreateHooksTest',
],
Expand All @@ -530,6 +580,8 @@ public function testCreateHooks()
]
]);

$app->impersonate('kirby');

Language::create([
'code' => 'de'
]);
Expand All @@ -545,7 +597,7 @@ public function testUpdateHooks()
$this->fixtures = __DIR__ . '/fixtures/UpdateHooksTest';
Dir::make($this->fixtures . '/content');

new App([
$app = $this->app->clone([
'roots' => [
'index' => $this->fixtures,
],
Expand Down Expand Up @@ -573,18 +625,113 @@ public function testUpdateHooks()
]
]);

$app->impersonate('kirby');

$language = Language::create(['code' => 'en']);
$language->update(['name' => 'English']);

$this->assertSame(2, $calls);
}

/**
* @covers ::update
*/
public function testUpdateNoPermissions()
{
$app = $this->app->clone([
'blueprints' => [
'users/editor' => [
'name' => 'editor',
'permissions' => [
'languages' => [
'create' => true,
'update' => false
]
]
],
],
'users' => [
['email' => 'test@getkirby.com', 'role' => 'editor']
]
]);

$this->expectException(PermissionException::class);
$this->expectExceptionMessage('You are not allowed to update the language');

$app->impersonate('test@getkirby.com');

$language = Language::create(['code' => 'en']);
$language->update(['name' => 'English']);
}

/**
* @covers ::update
*/
public function testUpdateWithoutLoggedUser()
{
$this->app->impersonate('kirby');
$language = Language::create(['code' => 'en']);

$this->expectException(PermissionException::class);
$this->expectExceptionMessage('You are not allowed to update the language');

// unimpersonate and test the method
$this->app->impersonate();
$language->update(['name' => 'English']);
}

/**
* @covers ::delete
*/
public function testDeleteNoPermissions()
{
$app = $this->app->clone([
'blueprints' => [
'users/editor' => [
'name' => 'editor',
'permissions' => [
'languages' => [
'create' => true,
'delete' => false
]
]
],
],
'users' => [
['email' => 'test@getkirby.com', 'role' => 'editor']
]
]);

$this->expectException(PermissionException::class);
$this->expectExceptionMessage('You are not allowed to delete the language');

$app->impersonate('test@getkirby.com');
$language = Language::create(['code' => 'en']);
$language->delete();
}

/**
* @covers ::delete
*/
public function testDeleteWithoutLoggedUser()
{
$this->app->impersonate('kirby');
$language = Language::create(['code' => 'en']);

$this->expectException(PermissionException::class);
$this->expectExceptionMessage('You are not allowed to delete the language');

// unimpersonate and test the method
$this->app->impersonate();
$language->delete();
}

public function testDeleteHooks()
{
$calls = 0;
$phpunit = $this;

new App([
$app =new App([
'roots' => [
'index' => $this->fixtures = __DIR__ . '/fixtures/DeleteHooksTest',
],
Expand All @@ -604,6 +751,8 @@ public function testDeleteHooks()
]
]);

$app->impersonate('kirby');

$language = Language::create([
'code' => 'en',
'name' => 'English'
Expand Down
2 changes: 2 additions & 0 deletions tests/Cms/Languages/LanguagesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ public function testMultipleDefault()

public function testCreate()
{
$this->app->impersonate('kirby');

$language = $this->app->languages()->create([
'code' => 'tr'
]);
Expand Down

0 comments on commit af9b0a5

Please # to comment.