Skip to content

Commit

Permalink
Merge branch 'neos:9.0' into 9.0
Browse files Browse the repository at this point in the history
  • Loading branch information
samsauter authored Aug 12, 2024
2 parents 4f993ea + b2d5f28 commit 621ba52
Show file tree
Hide file tree
Showing 18 changed files with 526 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ private function determineRequiredSqlStatements(): array
{
$schemaManager = $this->dbal->createSchemaManager();
$workspaceTable = new Table($this->tableName, [
(new Column('workspacename', Type::getType(Types::STRING)))->setLength(255)->setNotnull(true)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION),
(new Column('baseworkspacename', Type::getType(Types::STRING)))->setLength(255)->setNotnull(false)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION),
(new Column('workspacename', Type::getType(Types::STRING)))->setLength(WorkspaceName::MAX_LENGTH)->setNotnull(true)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION),
(new Column('baseworkspacename', Type::getType(Types::STRING)))->setLength(WorkspaceName::MAX_LENGTH)->setNotnull(false)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION),
(new Column('workspacetitle', Type::getType(Types::STRING)))->setLength(255)->setNotnull(true)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION),
(new Column('workspacedescription', Type::getType(Types::STRING)))->setLength(255)->setNotnull(true)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION),
(new Column('workspaceowner', Type::getType(Types::STRING)))->setLength(255)->setNotnull(false)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

/*
* This file is part of the Neos.ContentRepository package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

declare(strict_types=1);

namespace Neos\ContentRepository\Core\SharedModel\ContentRepository;

/**
* @implements \IteratorAggregate<ContentRepositoryId>
* @api
*/
final readonly class ContentRepositoryIds implements \IteratorAggregate, \Countable
{
/**
* @var array<ContentRepositoryId>
*/
private array $ids;

private function __construct(ContentRepositoryId ...$ids)
{
$this->ids = $ids;
}

/**
* @param array<ContentRepositoryId|string> $ids
*/
public static function fromArray(array $ids): self
{
$processedIds = [];
foreach ($ids as $id) {
if (is_string($id)) {
$id = ContentRepositoryId::fromString($id);
}
if (!$id instanceof ContentRepositoryId) {
throw new \InvalidArgumentException(sprintf('Expected string or instance of %s, got: %s', ContentRepositoryId::class, get_debug_type($id)), 1720424666);
}
$processedIds[] = $id;
}
return new self(...$processedIds);
}

public function getIterator(): \Traversable
{
yield from $this->ids;
}

public function count(): int
{
return count($this->ids);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
*/
final class WorkspaceName implements \JsonSerializable
{
public const MAX_LENGTH = 30;

private const PATTERN = '/^[a-z][a-z0-9\-]{0,' . (self::MAX_LENGTH - 1) . '}$/';

public const WORKSPACE_NAME_LIVE = 'live';

/**
Expand All @@ -33,8 +37,8 @@ final class WorkspaceName implements \JsonSerializable
private function __construct(
public readonly string $value
) {
if (preg_match('/^[\p{L}\p{P}\d \.]{1,200}$/u', $value) !== 1) {
throw new \InvalidArgumentException('Invalid workspace name given.', 1505826610318);
if (!self::hasValidFormat($value)) {
throw new \InvalidArgumentException('Invalid workspace name given.', 1505826610);
}
}

Expand All @@ -48,6 +52,11 @@ public static function fromString(string $value): self
return self::instance($value);
}

public static function tryFromString(string $value): ?self
{
return self::hasValidFormat($value) ? self::instance($value) : null;
}

public static function forLive(): self
{
return self::instance(self::WORKSPACE_NAME_LIVE);
Expand All @@ -61,30 +70,30 @@ public static function forLive(): self
*/
public static function transliterateFromString(string $name): self
{
try {
// Check if name already match name pattern to prevent unnecessary transliteration
if (self::hasValidFormat($name)) {
return self::fromString($name);
} catch (\InvalidArgumentException $e) {
// Okay, let's transliterate
}

$originalName = $name;
$originalName = strtolower($name);

// Transliterate (transform 北京 to 'Bei Jing')
$name = Transliterator::transliterate($name);

// Urlization (replace spaces with dash, special special characters)
$name = Transliterator::urlize($name);

// Ensure only allowed characters are left
$name = preg_replace('/[^a-z0-9\-]/', '', $name);
$name = (string)preg_replace('/[^a-z0-9\-]/', '', $name);

// Make sure we don't have an empty string left.
if (empty($name)) {
$name = 'workspace-' . strtolower(md5($originalName));
// Ensure max length...
if (strlen($name) > self::MAX_LENGTH) {
$name = substr($name, 0, self::MAX_LENGTH);
}

return new self($name);
// If the name is still invalid at this point, we fall back to md5
if (!self::hasValidFormat($name)) {
$prefix = 'workspace-';
$name = $prefix . substr(md5($originalName), 0, self::MAX_LENGTH - strlen($prefix));
}

return self::fromString($name);
}

public function isLive(): bool
Expand All @@ -101,4 +110,9 @@ public function equals(self $other): bool
{
return $this === $other;
}

private static function hasValidFormat(string $value): bool
{
return preg_match(self::PATTERN, $value) === 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace Neos\ContentRepository\Core\Tests\Unit\SharedModel\ContentRepository;

/*
* This file is part of the Neos.ContentRepository package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryIds;
use PHPUnit\Framework\TestCase;

class ContentRepositoryIdsTest extends TestCase
{
/**
* @test
*/
public function fromArraySupportsEmptyArray(): void
{
$contentRepositoryIds = ContentRepositoryIds::fromArray([]);
self::assertCount(0, $contentRepositoryIds);
}

/**
* @test
*/
public function fromArrayConvertsStringsToContentRepositoryIds(): void
{
$contentRepositoryIds = ContentRepositoryIds::fromArray(['some_cr_id', ContentRepositoryId::fromString('other_cr_id')]);
self::assertEquals([ContentRepositoryId::fromString('some_cr_id'), ContentRepositoryId::fromString('other_cr_id')], iterator_to_array($contentRepositoryIds));
}

/**
* @test
*/
public function fromArrayThrowsExceptionForInvalidItem(): void
{
$this->expectException(\InvalidArgumentException::class);
ContentRepositoryIds::fromArray([1234]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<?php

declare(strict_types=1);

namespace Neos\ContentRepository\Core\Tests\Unit\SharedModel\Workspace;

/*
* This file is part of the Neos.ContentRepository package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
use PHPUnit\Framework\TestCase;

final class WorkspaceNameTest extends TestCase
{
/**
* @test
*/
public function sameNameDoesNotCreateANewInstance(): void
{
$instance1 = WorkspaceName::fromString('workspace-name');
$instance2 = WorkspaceName::tryFromString('workspace-name');
self::assertSame($instance1, $instance2);
}

private static function validWorkspaceNames(): iterable
{
yield ['a'];
yield ['abcdefghijklmnopqrstuvwxyz'];
yield ['a0123456789'];
yield ['this-is-valid'];
}

/**
* @test
* @dataProvider validWorkspaceNames
*/
public function fromStringWorksForValidValues(string $value): void
{
self::assertSame(WorkspaceName::fromString($value)->value, $value);
}

/**
* @test
* @dataProvider validWorkspaceNames
*/
public function tryFromStringReturnsInstanceForValidValues(string $value): void
{
self::assertSame(WorkspaceName::tryFromString($value)->value, $value);
}

private static function invalidWorkspaceNames(): iterable
{
yield 'empty string' => [''];
yield 'only digits' => ['123'];
yield 'leading dash' => ['-invalid'];
yield 'upper case characters' => ['thisIsNotAllowed'];
yield 'whitespace' => ['this neither'];
yield 'exceeding max length' => ['this-is-just-a-little-too-long-'];
}

/**
* @test
* @dataProvider invalidWorkspaceNames
*/
public function fromStringFailsForInvalidValues(string $value): void
{
$this->expectException(\InvalidArgumentException::class);
WorkspaceName::fromString($value);
}

/**
* @test
* @dataProvider invalidWorkspaceNames
*/
public function tryFromStringReturnsNullForInvalidValues(string $value): void
{
self::assertNull(WorkspaceName::tryFromString($value));
}

/**
* @test
*/
public function forLiveReturnsAConstantInstance(): void
{
self::assertSame(WorkspaceName::fromString(WorkspaceName::WORKSPACE_NAME_LIVE), WorkspaceName::forLive());
}

private static function transliterateFromStringDataProvider(): iterable
{
yield 'valid name is not changed' => ['value' => 'already-valid', 'expectedResult' => 'already-valid'];
yield 'name is lower-cased' => ['value' => 'mixedCase', 'expectedResult' => 'mixedcase'];
yield 'chinese characters' => ['value' => '北京', 'expectedResult' => 'bei-jing'];
yield 'german umlauts' => ['value' => 'ümläute', 'expectedResult' => 'umlaute'];
yield 'white space' => ['value' => ' Contains spaces ', 'expectedResult' => 'contains-spaces'];
yield 'exceeding max length' => ['value' => 'This name is just a little too long', 'expectedResult' => 'this-name-is-just-a-little-too'];
yield 'only special characters' => ['value' => '-', 'expectedResult' => 'workspace-336d5ebc5436534e61d1'];
}

/**
* @test
* @dataProvider transliterateFromStringDataProvider
*/
public function transliterateFromStringTests(string $value, string $expectedResult): void
{
self::assertSame($expectedResult, WorkspaceName::transliterateFromString($value)->value);
}

/**
* @test
*/
public function isLiveReturnsFalseByDefault(): void
{
self::assertFalse(WorkspaceName::fromString('not-live')->isLive());
}

/**
* @test
*/
public function isLiveReturnsTrueForLiveWorkspace(): void
{
self::assertTrue(WorkspaceName::forLive()->isLive());
}

/**
* @test
*/
public function jsonSerializeReturnsPlainValue(): void
{
self::assertJsonStringEqualsJsonString(json_encode(WorkspaceName::forLive()), '"live"');
}

/**
* @test
*/
public function equalsReturnsFalseIfTwoInstancesDontMatch(): void
{
self::assertFalse(WorkspaceName::fromString('some-workspace')->equals(WorkspaceName::fromString('some-other-workspace')));
}

/**
* @test
*/
public function equalsReturnsTrueIfTwoInstancesMatch(): void
{
self::assertTrue(WorkspaceName::fromString('some-workspace')->equals(WorkspaceName::fromString('some-workspace')));
}
}
Loading

0 comments on commit 621ba52

Please sign in to comment.