<commit_before><?php
declare(strict_types=1);

/*
 * Copyright 2018 by <NAME>.
 * Licensed under MIT. See file /LICENSE.
 */

namespace AppBundle\Controller\Api\v1;

use AppBundle\Controller\Api\Exception\AccessDeniedException;
use AppBundle\Entity\User;
use AppBundle\Service\FilePoolPermissionCheckerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class FilesController extends Controller
{
    /** @var FilePoolPermissionCheckerInterface */
    private $filePoolPermissionChecker;

    /** @var TokenStorageInterface */
    private $tokenStorage;

    public function __construct(
        FilePoolPermissionCheckerInterface $filePoolPermissionChecker,
        TokenStorageInterface $tokenStorage
    ) {
        $this->filePoolPermissionChecker = $filePoolPermissionChecker;
        $this->tokenStorage = $tokenStorage;
    }

    public function getAction(string $path): Response
    {
        $this->checkPathPermissions($path);

        // @TODO if $path is file, return it (with correct mime type)
        // @TODO if $path is dir, return listing
    }

    public function putAction(string $path): Response
    {
        $this->checkPathPermissions($path);
        // @TODO if path end with slash => Exception
        // @TODO if path already exists and is directory => Exception
    }

    public function deleteAction(string $path): Response
    {
        $this->checkPathPermissions($path);
        // @TODO if path is file, delete
        // @TODO if path is directory, delete it recursively
    }

    private function checkPathPermissions(string $path): void
    {
        /** @var User $user */
        $user = $this->tokenStorage->getToken()->getUser();
        if (false === is_a($user, User::class)) {
            throw new AccessDeniedException('Not logged in.');
        }
        if (false === $this->filePoolPermissionChecker->mayUserAccessPath($user, $path)) {
            throw new AccessDeniedException();
        }
    }
}<commit_msg>implement new APIs files controller<commit_after><?php
declare(strict_types=1);

/*
 * Copyright 2018 by <NAME>.
 * Licensed under MIT. See file /LICENSE.
 */

namespace AppBundle\Controller\Api\v1;

use AppBundle\Controller\Api\Exception\AccessDeniedException;
use AppBundle\Entity\User;
use AppBundle\Service\FilePoolPermissionCheckerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;

class FilesController extends Controller
{
    /** @var FilePoolPermissionCheckerInterface */
    private $filePoolPermissionChecker;

    /** @var TokenStorageInterface */
    private $tokenStorage;

    /** @var string */
    private $filePoolBasePath;

    // @TODO Perhaps use flysystem?

    public function __construct(
        FilePoolPermissionCheckerInterface $filePoolPermissionChecker,
        TokenStorageInterface $tokenStorage,
        string $filePoolBasePath
    ) {
        $this->filePoolPermissionChecker = $filePoolPermissionChecker;
        $this->tokenStorage = $tokenStorage;
        $this->filePoolBasePath = $filePoolBasePath;
    }

    public function getAction(string $path): Response
    {
        $this->checkPathPermissions($path);

        $fullPath = $this->getFullPath($path);

        // @TODO Handle If-Modified-Since Header if set
        // @TODO Handle If-Unmodified-Since Header if set

        if (is_dir($fullPath)) {
            $directoryIterator = new \DirectoryIterator($fullPath);
            $files = [];
            foreach ($directoryIterator as $file) {
                if ($file->isFile()) {
                    $files[] = $file->getFilename();
                } elseif ($file->isDir() && false === $file->isDot()) {
                    $files[] = $file->getFilename() . '/';
                }
            }
            return new Response(
                json_encode($files),
                200,
                [
                    'Content-Type' => 'text/json',
                ]
            );
        } elseif (is_file($fullPath)) {
            $file = new File($fullPath);
            return new Response(
                file_get_contents($fullPath),
                200,
                [
                    'Content-Type' => $file->getMimeType(),
                    'Content-Length' => $file->getSize(),
                    'Last-Modified' => gmdate('D, d M Y G:i:s T', $file->getMTime()),
                ]
            );
        }

        throw new FileNotFoundException($path);
    }

    public function putAction(string $path): Response
    {
        $this->checkPathPermissions($path);
        // @TODO if path end with slash => Exception
        // @TODO if path already exists and is directory => Exception
    }

    public function deleteAction(string $path): Response
    {
        $this->checkPathPermissions($path);
        // @TODO if path is file, delete
        // @TODO if path is directory, delete it recursively
    }

    private function checkPathPermissions(string $path): void
    {
        /** @var User $user */
        $user = $this->tokenStorage->getToken()->getUser();
        if (false === is_a($user, User::class)) {
            throw new AccessDeniedException('Not logged in.');
        }
        if (false === $this->filePoolPermissionChecker->mayUserAccessPath($user, $path)) {
            throw new AccessDeniedException();
        }
    }

    private function getFullPath(string $filePath): string
    {
        return $this->filePoolBasePath . $filePath;
    }
}
{'fraction_non_alphanumeric': 0.11356251068924235, 'fraction_numerical': 0.0030785017957927143, 'mean_word_length': 2.5186522262334536, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 44, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '973938', 'n_tokens_mistral': 1547, 'n_tokens_neox': 1524, 'n_words': 358}