Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/1259 pipe in folder name #1268

Merged
merged 3 commits into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
[#1222](https://github.com/nextcloud/cookbook/pull/1222) @christianlupus
- Add overlay when app navigation is open
[1122](https://github.com/nextcloud/cookbook/pull/1122) @MarcelRobitaille
- Add filter to prevent special chars in folder names
[#1268](https://github.com/nextcloud/cookbook/pull/1268) @christianlupus

### Maintenance
- Use the pre-built database images for MySQL and PostgreSQL tests
Expand Down
44 changes: 44 additions & 0 deletions lib/Helper/FileSystem/RecipeNameHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace OCA\Cookbook\Helper\FileSystem;

use OCP\IL10N;
use Psr\Log\LoggerInterface;

/**
* This class should help to normalize the characters in the filesystem accordingly.
* Special chars are replaced by underscores, typically.
*/
class RecipeNameHelper {
/** @var IL10N */
private $l;
/** @var LoggerInterface */
private $logger;

public function __construct(
IL10N $l,
LoggerInterface $logger
) {
$this->l = $l;
$this->logger = $logger;
}

/**
* Replace the name of a recipe such that no special chars are present anymore.
*
* Additionally, the file name is truncated to 100 chars.
*
* @param string $recipeName The original recipe name
* @return string The cleaned recipe name
*/
public function getFolderName(string $recipeName): string {
$pattern = '/[\\/:?!"\\\\\'|&^#]/';
$recipeName = preg_replace($pattern, '_', $recipeName);

if (strlen($recipeName) > 100) {
$recipeName = substr($recipeName, 0, 97) . '...';
}

return $recipeName;
}
}
15 changes: 11 additions & 4 deletions lib/Service/RecipeService.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use OCA\Cookbook\Helper\UserFolderHelper;
use OCA\Cookbook\Exception\HtmlParsingException;
use OCA\Cookbook\Exception\ImportException;
use OCA\Cookbook\Helper\FileSystem\RecipeNameHelper;
use OCA\Cookbook\Helper\Filter\JSONFilter;

/**
Expand Down Expand Up @@ -57,6 +58,8 @@ class RecipeService {
* @var ImageService
*/
private $imageService;
/** @var RecipeNameHelper */
private $recipeNameHelper;

/** @var JSONFilter */
private $jsonFilter;
Expand All @@ -68,6 +71,7 @@ public function __construct(
UserConfigHelper $userConfigHelper,
UserFolderHelper $userFolder,
ImageService $imageService,
RecipeNameHelper $recipeNameHelper,
IL10N $il10n,
LoggerInterface $logger,
HtmlDownloadService $downloadService,
Expand All @@ -82,6 +86,7 @@ public function __construct(
$this->logger = $logger;
$this->userConfigHelper = $userConfigHelper;
$this->imageService = $imageService;
$this->recipeNameHelper = $recipeNameHelper;
$this->htmlDownloadService = $downloadService;
$this->recipeExtractionService = $extractionService;
$this->jsonFilter = $jsonFilter;
Expand Down Expand Up @@ -209,16 +214,18 @@ public function addRecipe($json, $importedHtml = null) {
$user_folder = $this->userFolder->getFolder();
$recipe_folder = null;

$recipeFolderName = $this->recipeNameHelper->getFolderName($json['name']);

// Recipe already has an id, update it
if (isset($json['id']) && $json['id']) {
$recipe_folder = $user_folder->getById($json['id'])[0];

$old_path = $recipe_folder->getPath();
$new_path = dirname($old_path) . '/' . $json['name'];
$new_path = dirname($old_path) . '/' . $recipeFolderName;

// The recipe is being renamed, move the folder
if ($old_path !== $new_path) {
if ($user_folder->nodeExists($json['name'])) {
if ($user_folder->nodeExists($recipeFolderName)) {
throw new RecipeExistsException($this->il10n->t('Another recipe with that name already exists'));
}

Expand All @@ -229,11 +236,11 @@ public function addRecipe($json, $importedHtml = null) {
} else {
$json['dateCreated'] = $now;

if ($user_folder->nodeExists($json['name'])) {
if ($user_folder->nodeExists($recipeFolderName)) {
throw new RecipeExistsException($this->il10n->t('Another recipe with that name already exists'));
}

$recipe_folder = $user_folder->newFolder($json['name']);
$recipe_folder = $user_folder->newFolder($recipeFolderName);
}

// Write JSON file to disk
Expand Down
44 changes: 44 additions & 0 deletions tests/Unit/Helper/FileSystem/RecipeNameHelperTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

namespace OCA\Cookbook\tests\Unit\Helper\FileSystem;

use OCA\Cookbook\Helper\FileSystem\RecipeNameHelper;
use OCP\IL10N;
use PHPUnit\Framework\MockObject\Stub;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;

class RecipeNameHelperFilter extends TestCase {
/** @var RecipeNameHelper */
private $dut;

protected function setUp(): void {
/** @var IL10N|Stub */
$l = $this->createStub(IL10N::class);
$l->method('t')->willReturnArgument(0);
$logger = $this->createStub(LoggerInterface::class);

$this->dut = new RecipeNameHelper($l, $logger);
}

public function dpGetFolderName() {
$tenChars = 'abcdefghij';
$ninetyChars = str_repeat($tenChars, 9);

return [
'short name' => ['recipe name', 'recipe name'],
'95 chars' => ["${ninetyChars}12345", "${ninetyChars}12345"],
'99 chars' => ["${ninetyChars}123456789", "${ninetyChars}123456789"],
'100 chars' => ["${ninetyChars}1234567890", "${ninetyChars}1234567890"],
'101 chars' => ["${ninetyChars}12345678901", "${ninetyChars}1234567..."],
'102 chars' => ["${ninetyChars}123456789012", "${ninetyChars}1234567..."],
'105 chars' => ["${ninetyChars}123456789012345", "${ninetyChars}1234567..."],
'special chars' => ['a/b:c?d!e"f|g\\h\'i^j&k#l', 'a_b_c_d_e_f_g_h_i_j_k_l'],
];
}

/** @dataProvider dpGetFolderName */
public function testGetFolderName($recipeName, $expected) {
$this->assertEquals($expected, $this->dut->getFolderName($recipeName));
}
}