return $response;
}
+ /**
+ * Return download file
+ *
+ * @param Request $request The Request instance
+ * @param string $hash The hash
+ * @param string $path The image path
+ * @return Response The rendered image
+ */
+ public function download(Request $request, string $hash, string $path/*, string $_format*/): Response {
+ //Without matching hash
+ if ($hash !== $this->slugger->hash($path)) {
+ //Throw new exception
+ throw new NotFoundHttpException('Invalid download hash');
+ //Without valid format
+ #} elseif ($_format !== 'jpeg' && $_format !== 'png' && $_format !== 'webp') {
+ # //Throw new exception
+ # throw new NotFoundHttpException('Invalid download format');
+ }
+
+ //Unshort path
+ $path = $this->slugger->unshort($short = $path);
+
+ //Without file
+ if (!is_file($path) || !($mtime = stat($path)['mtime'])) {
+ //Throw new exception
+ throw new NotFoundHttpException('Unable to get thumb file');
+ }
+
+ //Read thumb from cache
+ $response = new BinaryFileResponse($path);
+
+ //Set file name
+ $response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, basename($path));
+
+ //Set etag
+ //TODO: set etag to file content md5 ? cache it ?
+ $response->setEtag(md5($hash));
+
+ //Set last modified
+ $response->setLastModified(\DateTime::createFromFormat('U', strval($mtime)));
+
+ //Set as public
+ $response->setPublic();
+
+ //Return 304 response if not modified
+ $response->isNotModified($request);
+
+ //Return response
+ return $response;
+ }
+
/**
* Return facebook image
*
$response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, 'facebook-'.$hash.'.'.$_format);
//Set etag
+ //TODO: set etag to file content md5 ? cache it ?
$response->setEtag(md5($hash));
//Set last modified
$response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, basename($map));
//Set etag
+ //TODO: set etag to file content md5 ? cache it ?
$response->setEtag(md5(serialize([$height, $width, $zoom, $latitude, $longitude])));
//Set last modified
$response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, basename($map));
//Set etag
+ //TODO: set etag to file content md5 ? cache it ?
$response->setEtag(md5(serialize([$height, $width, $zoom, $coordinate])));
//Set last modified
$path = $this->slugger->unshort($short = $path);
//Set thumb
- $thumb = $this->config['cache'].'/'.$this->config['prefixes']['thumb'].$path.'.'.$_format;
+ $thumb = $this->config['cache'].'/'.$this->config['prefixes']['thumb'].$path.'.'.$width.'x'.$height.'.'.$_format;
//Without file
- if (!is_file($path) || !($updated = stat($path)['mtime'])) {
+ if (!is_file($path) || !($mtime = stat($path)['mtime'])) {
//Throw new exception
throw new NotFoundHttpException('Unable to get thumb file');
}
//Without thumb up to date file
- if (!is_file($thumb) || !($mtime = stat($thumb)['mtime']) || $mtime < $updated) {
+ if (!is_file($thumb) || !($updated = stat($thumb)['mtime']) || $updated < $mtime) {
//Without existing thumb path
if (!is_dir($dir = dirname($thumb))) {
//Create filesystem object
throw new \Exception(sprintf('Unable to write image "%s"', $thumb));
}
- //Set mtime
- $mtime = stat($thumb)['mtime'];
+ //Set updated
+ $updated = stat($thumb)['mtime'];
}
//Read thumb from cache
$response->setContentDisposition(HeaderUtils::DISPOSITION_INLINE, 'thumb-'.$hash.'.'.$_format);
//Set etag
+ //TODO: set etag to file content md5 ? cache it ?
$response->setEtag(md5($hash));
//Set last modified
- $response->setLastModified(\DateTime::createFromFormat('U', strval($mtime)));
+ $response->setLastModified(\DateTime::createFromFormat('U', strval($updated)));
//Set as public
$response->setPublic();
'lemon' => dirname(__DIR__).'/public/woff2/lemon.woff2',
'notoemoji' => dirname(__DIR__).'/public/woff2/notoemoji.woff2'
],
+ 'formats' => [
+ 'ico',
+ 'jpeg',
+ 'jpg',
+ 'png',
+ 'svg',
+ 'tiff',
+ 'webm',
+ 'webp',
+ ],
'map' => [
'border' => '#00c3f9',
'fill' => '#cff',
'transport' => 'http://a.tile.thunderforest.com/transport/{Z}/{X}/{Y}.png'
],
'thumb' => [
+ 'format' => 'jpeg',
'height' => 128,
'width' => 128
],
->defaultValue($defaults['fonts'])
->scalarPrototype()->end()
->end()
+ ->arrayNode('formats')
+ ->treatNullLike([])
+ ->defaultValue($defaults['formats'])
+ ->scalarPrototype()->end()
+ ->end()
->arrayNode('map')
->addDefaultsIfNotSet()
->children()
->addDefaultsIfNotSet()
->children()
->scalarNode('height')->cannotBeEmpty()->defaultValue($defaults['thumb']['height'])->end()
+ ->scalarNode('format')->cannotBeEmpty()->defaultValue($defaults['thumb']['format'])->end()
->scalarNode('width')->cannotBeEmpty()->defaultValue($defaults['thumb']['width'])->end()
->end()
->end()
* @param ?ImageUtil $image The image instance
* @param ?SluggerUtil $slugger The slugger instance
* @param ?TranslatorInterface $translator The translator instance
+ * @param bool $enable Use captcha
*/
- public function __construct(protected ?ImageUtil $image = null, protected ?SluggerUtil $slugger = null, protected ?TranslatorInterface $translator = null) {
+ public function __construct(protected ?ImageUtil $image = null, protected ?SluggerUtil $slugger = null, protected ?TranslatorInterface $translator = null, protected bool $enable = false) {
}
/**
$builder->add('_captcha_token', HiddenType::class, ['data' => $captcha['token'], 'empty_data' => $captcha['token'], 'mapped' => false]);
//Add captcha
- $builder->add('captcha', IntegerType::class, ['label_attr' => ['class' => 'captcha'], 'label' => '<img src="'.htmlentities($captcha['src']).'" alt="'.htmlentities($captcha['equation']).'" />', 'label_html' => true, 'mapped' => false, 'translation_domain' => false]);
+ $builder->add('captcha', IntegerType::class, ['label_attr' => ['class' => 'captcha'], 'label' => '<img src="'.htmlentities($captcha['src']).'" alt="'.htmlentities($captcha['equation']).'" />', 'label_html' => true, 'mapped' => false, 'translation_domain' => false, 'required' => true]);
//Add event listener on captcha
$builder->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'validateCaptcha']);
parent::configureOptions($resolver);
//Set defaults
- $resolver->setDefaults(['captcha' => false, 'error_bubbling' => true, 'translation_domain' => RapsysPackBundle::getAlias()]);
+ $resolver->setDefaults(['captcha' => $this->enable, 'error_bubbling' => true, 'translation_domain' => RapsysPackBundle::getAlias()]);
//Add extra captcha option
$resolver->setAllowedTypes('captcha', 'boolean');
//Without captcha
if (empty($data['captcha'])) {
//Add error on captcha
+ //XXX: we need to add error on form
+ //XXX: see https://github.com/symfony/symfony/issues/35831
$form->addError(new FormError($this->translator->trans('Captcha is empty')));
//Reset captcha token
//With invalid captcha
} elseif ($this->slugger->hash($data['captcha']) !== $data['_captcha_token']) {
//Add error on captcha
+ //XXX: we need to add error on form
+ //XXX: see https://github.com/symfony/symfony/issues/35831
$form->addError(new FormError($this->translator->trans('Captcha is invalid')));
//Reset captcha token
--- /dev/null
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of the Rapsys PackBundle package.
+ *
+ * (c) Raphaël Gertz <symfony@rapsys.eu>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Rapsys\PackBundle\Form;
+
+use Symfony\Component\Form\FormBuilderInterface;
+use Symfony\Component\OptionsResolver\OptionsResolver;
+use Symfony\Component\Form\Extension\Core\Type\TextType;
+use Symfony\Component\Form\Extension\Core\Type\TextareaType;
+use Symfony\Component\Form\Extension\Core\Type\EmailType;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
+use Symfony\Component\Validator\Constraints\Email;
+use Symfony\Component\Validator\Constraints\NotBlank;
+
+/**
+ * {@inheritdoc}
+ */
+class ContactType extends CaptchaType {
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(FormBuilderInterface $builder, array $options): void {
+ //Add fields
+ $builder
+ ->add('name', TextType::class, ['attr' => ['placeholder' => 'Your name'], 'required' => true])
+ ->add('subject', TextType::class, ['attr' => ['placeholder' => 'Subject'], 'required' => true])
+ ->add('mail', EmailType::class, ['attr' => ['placeholder' => 'Your mail'], 'required' => true, 'invalid_message' => 'Your mail doesn\'t seems to be valid'])
+ ->add('message', TextareaType::class, ['attr' => ['placeholder' => 'Your message', 'cols' => 50, 'rows' => 15], 'required' => true])
+ ->add('submit', SubmitType::class, ['label' => 'Send', 'attr' => ['class' => 'submit']]);
+
+ //Call parent
+ parent::buildForm($builder, $options);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function configureOptions(OptionsResolver $resolver): void {
+ //Call parent configure options
+ parent::configureOptions($resolver);
+
+ //Set defaults
+ $resolver->setDefaults(['captcha' => true]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName() {
+ return 'contact_form';
+ }
+}
*
* Returns an absolute or root-relative public path
*
- * Transform @BundleBundle to bundle and remove /Resources/public fragment from path
+ * Transform @BundleBundle to bundle and remove [/Resources|]/public fragment from path
* This bundle name conversion and bundle prefix are the same as in asset:install command
*
* @link https://symfony.com/doc/current/bundles.html#overridding-the-bundle-directory-structure
*/
public function getUrl(string $path): string {
//Match url starting with a bundle name
- if (preg_match('%^@([A-Z][a-zA-Z]*?)(?:Bundle/Resources/public)?/(.*)$%', $path, $matches)) {
+ if (preg_match('%^@([A-Z][a-zA-Z]*?)(?:Bundle/(?:Resources/)?public)?/(.*)$%', $path, $matches)) {
//Handle empty or without replacement pattern basePath
if (empty($this->basePath) || strpos($this->basePath, '%s') === false) {
//Set path from hardcoded format
use Symfony\Component\Routing\RouterInterface;
use Twig\Error\Error;
-use Twig\Node\Expression\AssignNameExpression;
+use Twig\Node\Expression\Variable\AssignContextVariable;
use Twig\Node\Node;
+use Twig\Node\Nodes;
+use Twig\Node\EmptyNode;
use Twig\Node\SetNode;
use Twig\Node\TextNode;
use Twig\Source;
#throw new Error('Empty inputs token', $token->getLine(), $stream->getSourceContext());
//Send an empty node without inputs
- return new Node();
+ return new EmptyNode();
}
//Check filters
}
//Set name in context key
- $ref = new AssignNameExpression($this->token, $token->getLine());
+ $ref = new AssignContextVariable($this->token, $token->getLine());
//Set output in context value
$value = new TextNode($asset, $token->getLine());
//Send body with context set
- return new Node([
+ return new Nodes([
//This define name in twig template by prepending $context['<name>'] = '<output>';
new SetNode(true, $ref, $value, $token->getLine(), $this->getTag()),
//The tag captured body
### Step 3: Configure the Bundle
-Setup configuration file `config/packages/rapsys_pack.yaml` with the following
-content available in `Resources/config/packages/rapsys_pack.yaml`:
+Setup configuration file `config/packages/rapsyspack.yaml` with the following
+content available in `vendor/rapsys/packbundle/config/packages/rapsyspack.yaml`:
```yaml
#Services configuration
#Replace assets.packages definition
assets.packages:
class: 'Symfony\Component\Asset\Packages'
- arguments: [ '@rapsys_pack.path_package' ]
+ arguments: [ '@rapsyspack.path_package' ]
#Replace assets.context definition
assets.context:
class: 'Rapsys\PackBundle\Context\RequestStackContext'
arguments: [ '@request_stack', '%asset.request_context.base_path%', '%asset.request_context.secure%' ]
#Register assets pack package
- rapsys_pack.path_package:
+ rapsyspack.path_package:
class: 'Rapsys\PackBundle\Package\PathPackage'
arguments: [ '/', '@assets.empty_version_strategy', '@assets.context' ]
public: true
#Register twig pack extension
- rapsys_pack.pack_extension:
+ rapsyspack.pack_extension:
class: 'Rapsys\PackBundle\Extension\PackExtension'
- arguments: [ '@service_container', '@rapsys_pack.intl_util', '@file_locator', '@rapsys_pack.path_package', '@rapsys_pack.slugger_util' ]
+ arguments: [ '@service_container', '@rapsyspack.intl_util', '@file_locator', '@rapsyspack.path_package', '@rapsyspack.slugger_util' ]
tags: [ 'twig.extension' ]
#Register intl util service
- rapsys_pack.intl_util:
+ rapsyspack.intl_util:
class: 'Rapsys\PackBundle\Util\IntlUtil'
public: true
#Register facebook event subscriber
tags: [ 'kernel.event_subscriber' ]
#Register intl util class alias
Rapsys\PackBundle\Util\IntlUtil:
- alias: 'rapsys_pack.intl_util'
+ alias: 'rapsyspack.intl_util'
#Register facebook util service
- rapsys_pack.facebook_util:
+ rapsyspack.facebook_util:
class: 'Rapsys\PackBundle\Util\FacebookUtil'
- arguments: [ '@router', '%kernel.project_dir%/var/cache', '%rapsys_pack.path%' ]
+ arguments: [ '@router', '%kernel.project_dir%/var/cache', '%rapsyspack.path%' ]
public: true
#Register facebook util class alias
Rapsys\PackBundle\Util\FacebookUtil:
- alias: 'rapsys_pack.facebook_util'
+ alias: 'rapsyspack.facebook_util'
#Register image util service
- rapsys_pack.image_util:
+ rapsyspack.image_util:
class: 'Rapsys\PackBundle\Util\ImageUtil'
- arguments: [ '@router', '@rapsys_pack.slugger_util', '%kernel.project_dir%/var/cache', '%rapsys_pack.path%' ]
+ arguments: [ '@router', '@rapsyspack.slugger_util', '%kernel.project_dir%/var/cache', '%rapsyspack.path%' ]
public: true
#Register image util class alias
Rapsys\PackBundle\Util\ImageUtil:
- alias: 'rapsys_pack.image_util'
+ alias: 'rapsyspack.image_util'
#Register map util service
- rapsys_pack.map_util:
+ rapsyspack.map_util:
class: 'Rapsys\PackBundle\Util\MapUtil'
- arguments: [ '@router', '@rapsys_pack.slugger_util' ]
+ arguments: [ '@router', '@rapsyspack.slugger_util' ]
public: true
#Register map util class alias
Rapsys\PackBundle\Util\MapUtil:
- alias: 'rapsys_pack.map_util'
+ alias: 'rapsyspack.map_util'
#Register slugger util service
- rapsys_pack.slugger_util:
+ rapsyspack.slugger_util:
class: 'Rapsys\PackBundle\Util\SluggerUtil'
arguments: [ '%kernel.secret%' ]
public: true
#Register slugger util class alias
Rapsys\PackBundle\Util\SluggerUtil:
- alias: 'rapsys_pack.slugger_util'
+ alias: 'rapsyspack.slugger_util'
#Register image controller
Rapsys\PackBundle\Controller\ImageController:
- arguments: [ '@service_container', '@rapsys_pack.image_util', '@rapsys_pack.slugger_util', '%kernel.project_dir%/var/cache', '%rapsys_pack.path%' ]
+ arguments: [ '@service_container', '@rapsyspack.image_util', '@rapsyspack.slugger_util', '%kernel.project_dir%/var/cache', '%rapsyspack.path%' ]
tags: [ 'controller.service_arguments' ]
#Register map controller
Rapsys\PackBundle\Controller\MapController:
- arguments: [ '@service_container', '@rapsys_pack.map_util', '@rapsys_pack.slugger_util', '%kernel.project_dir%/var/cache', '%rapsys_pack.path%' ]
+ arguments: [ '@service_container', '@rapsyspack.map_util', '@rapsyspack.slugger_util', '%kernel.project_dir%/var/cache', '%rapsyspack.path%' ]
tags: [ 'controller.service_arguments' ]
Rapsys\PackBundle\Form\CaptchaType:
- arguments: [ '@rapsys_pack.image_util', '@rapsys_pack.slugger_util', '@translator' ]
+ arguments: [ '@rapsyspack.image_util', '@rapsyspack.slugger_util', '@translator' ]
tags: [ 'form.type' ]
```
Setup configuration file `config/packages/myproject.yaml` with the following
-content available in `Resources/config/packages/rapsys_pack.yaml`:
+content available in `vendor/rapsys/packbundle/config/packages/rapsyspack.yaml`:
```yaml
#Services configuration
arguments: [ '@router', [ 'en', 'en_gb', 'en_us', 'fr', 'fr_fr' ] ]
tags: [ 'kernel.event_subscriber' ]
#Register facebook util service
- rapsys_blog.facebook_util:
+ myproject.facebook_util:
class: 'Rapsys\PackBundle\Util\FacebookUtil'
- arguments: [ '@router', '%kernel.project_dir%/var/cache', '%rapsys_pack.path%', 'facebook', '%kernel.project_dir%/public/png/facebook.png' ]
+ arguments: [ '@router', '%kernel.project_dir%/var/cache', '%rapsyspack.path%', 'facebook', '%kernel.project_dir%/public/png/facebook.png' ]
public: true
```
### Step 4: Use the twig extension in your Template
-You can use a template like this to generate your first `rapsys_pack` enabled
+You can use a template like this to generate your first `rapsyspack` enabled
template:
```twig
<head>
<meta charset="UTF-8" />
<title>{% block title %}Welcome!{% endblock %}</title>
- {% stylesheet '//fonts.googleapis.com/css?family=Irish+Grover|La+Belle+Aurore' '@NamedBundle/Resources/public/css/{reset,screen}.css' '@Short/css/example.css' %}
+ {% stylesheet '//fonts.googleapis.com/css?family=Irish+Grover|La+Belle+Aurore' '@NamedBundle/public/css/{reset,screen}.css' '@Short/css/example.css' %}
<link rel="stylesheet" type="text/css" href="{{ asset_url }}" />
{% endstylesheet %}
</head>
use Twig\Error\Error;
-//This class will be defined in the parameter rapsys_pack.filters.(css|img|js).[x].class string
+//This class will be defined in the parameter rapsyspack.filters.(css|img|js).[x].class string
class MyPackFilter implements FilterInterface {
- //The constructor arguments ... will be replaced with values defined in the parameter rapsys_pack.filters.(css|img|js).[x].args array
+ //The constructor arguments ... will be replaced with values defined in the parameter rapsyspack.filters.(css|img|js).[x].args array
public function __construct(string $fileName, int $line, string $bin = 'mypack', ...) {
//Set fileName
$this->fileName = $fileName;
*/
public static function getVersion(): string {
//Return version
- return '0.5.4';
+ return '0.5.7';
}
}
--- /dev/null
+<?php declare(strict_types=1);
+
+/*
+ * This file is part of the Rapsys PackBundle package.
+ *
+ * (c) Raphaël Gertz <symfony@rapsys.eu>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Rapsys\PackBundle\Util;
+
+use Psr\Container\ContainerInterface;
+
+use Rapsys\PackBundle\RapsysPackBundle;
+use Rapsys\PackBundle\Util\ImageUtil;
+use Rapsys\PackBundle\Util\IntlUtil;
+use Rapsys\PackBundle\Util\SluggerUtil;
+
+use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
+use Symfony\Component\Routing\RouterInterface;
+use Symfony\Contracts\Translation\TranslatorInterface;
+
+/**
+ * Manages file informations
+ */
+class FileUtil {
+ /**
+ * Alias string
+ */
+ protected string $alias;
+
+ /**
+ * Config array
+ */
+ protected array $config;
+
+ /**
+ * Creates a new file util
+ *
+ * @param ContainerInterface $container The container instance
+ * @param ImageUtil $image The ImageUtil instance
+ * @param IntlUtil $intl The IntlUtil instance
+ * @param RouterInterface $router The router instance
+ * @param SluggerUtil $slugger The SluggerUtil instance
+ * @param TranslatorInterface $translator The translator instance
+ */
+ public function __construct(protected ContainerInterface $container, protected ImageUtil $image, protected IntlUtil $intl, protected RouterInterface $router, protected SluggerUtil $slugger, protected TranslatorInterface $translator) {
+ //Retrieve config
+ $this->config = $container->getParameter($this->alias = RapsysPackBundle::getAlias());
+ }
+
+ /**
+ * Get file infos
+ *
+ * @param string $path The base path
+ * @param string $realpath The real path
+ * @param string $name The route name
+ * @param array $parameters The route parameters
+ * @param bool $si Use si unit
+ */
+ public function getFile(string $path, string $realpath, string $name, array $parameters = [], bool $si = true): array {
+ //Set file
+ $file = new \SplFileObject($realpath);
+
+ //Set size
+ $size = $file->getSize();
+
+ //Set unit
+ $unit = $si ? 1000 : 1024;
+
+ //Set index
+ $index = [ '', $si ? 'k' : 'K', 'M', 'G', 'T', 'P', 'E' ];
+
+ //Get exp
+ $exp = intval((log($size) / log($unit)));
+
+ //Rebase number
+ $number = round($size / pow($unit, $exp), 2);
+
+ //Set ext, height, image, mime, preview, thumb and width
+ $ext = $height = $image = $mime = $preview = $thumb = $width = false;
+
+ //With supporter format extension
+ if (($pos = strrpos($realpath, '.')) && ($ext = substr($realpath, $pos + 1)) && in_array($ext, $this->config['formats'])) {
+ //With mime type
+ //XXX: getimagesize is too slow to run against all files
+ if (($image = getimagesize($realpath)) !== false) {
+ //Set ext
+ if (($ext = image_type_to_extension($image[2], false)) === false) {
+ //Throw error
+ throw new \Exception(sprintf('Unable to get "%s" file image extension', $realpath));
+ }
+
+ //Set height
+ $height = $image[1];
+
+ //Set mime
+ $mime = image_type_to_mime_type($image[2]);
+
+ //Set width
+ $width = $image[0];
+
+ //Set preview
+ $preview = $this->image->getThumb($realpath, $height, $width, $ext);
+
+ //Set preview
+ $thumb = $this->image->getThumb($realpath, $this->config['thumb']['height'], $this->config['thumb']['weight'], $ext);
+ }
+ }
+
+ //Return file infos
+ return [
+ 'created' => (new \DateTime())->setTimestamp($file->getCTime()),
+ 'height' => $height,
+ 'intlsize' => $intlsize = $this->intl->number($number),
+ 'intlunit' => $intlunit = $this->translator->trans($index[$exp].($si ? '' : 'i').'B'),
+ 'link' => $this->router->generate($name, ['path' => substr($realpath, strlen($path) + 1)] + $parameters),
+ 'download' => $this->router->generate('rapsyspack_download', ['path' => $short = $this->slugger->short($realpath), 'hash' => $this->slugger->hash($short), 'u' => $mtime = $file->getMTime()]),
+ 'mime' => $mime,
+ 'modified' => (new \DateTime())->setTimestamp($mtime),
+ 'name' => basename($realpath).' ('.$intlsize.' '.$intlunit.')',
+ 'preview' => $preview,
+ 'size' => $size,
+ 'thumb' => $thumb,
+ 'width' => $width,
+ //TODO: preview for images ?
+ //TODO: extra fields ? (preview, miniature, etc ?)
+ //TODO: mimetype decided extra fields ? (.pdf for doc, webm preview, img preview, etc ?)
+ //TODO: XXX: finish this !!!
+ ];
+ }
+
+ /**
+ * Get path infos
+ *
+ * @param string $path The base path
+ * @param string $realpath The real path
+ * @param string $name The route name
+ * @param array $parameters The route parameters
+ * @param bool $si Use si unit
+ */
+ //Route object instead of shitty array ?
+ public function getInfos(string $path, string $realpath, string $slug, string $name, array $parameters = []) {
+ //Set result
+ $result = [
+ 'breadcrumbs' => [/*$breadcrumb*/],
+ 'directories' => [],
+ 'file' => [],
+ 'files' => [],
+ ];
+
+ //Set base
+ $base = '';
+
+ //Iterate on breadcrumbs
+ foreach (explode('/', substr($realpath, strlen($path))) as $value) {
+ $result['breadcrumbs'][] = [
+ 'name' => $value ? '/ '.$value : $slug,
+ 'link' => $this->router->generate($name, ['path' => ($base .= ($base == '' ? '' : '/').$value)] + $parameters)
+ ];
+ }
+
+ //With file
+ if (is_file($realpath)) {
+ //Set file
+ $result['file'] = $this->getFile($path, $realpath, $name, $parameters);
+ //With directory
+ //TODO: for pagination, files and directories are to be placed in a single array
+ //TODO: only get informations on files and directories inside the pagination window !
+ } elseif (is_dir($realpath)) {
+ //Set dir
+ $dir = dir($realpath);
+
+ //Iterate on directory
+ while (($item = $dir->read()) !== false) {
+ //Skip ., .., .git, .svn, .htpasswd, .htgroup and .*.un~ items
+ //TODO: set this regexp in config ?
+ if (preg_match('/^(\.|\.\.|\.git|\.svn|\.ht(access|passwd|group)|\..*\.un~)$/', $item)) {
+ //Skip it
+ continue;
+ }
+
+ //Check item
+ if (
+ //Without item realpath
+ !($itempath = realpath($realpath.'/'.$item)) ||
+ //Item realpath not matching album path
+ //XXX: skip files outside of album/element path (symlink outside of tree)
+ //TODO: make it configurable ? by album/element/admin ?
+ $path !== substr($itempath, 0, strlen($path))
+ ) {
+ //Skip it
+ continue;
+ }
+
+ //With directory
+ if (is_dir($itempath)) {
+ //Append directory
+ //TODO: use this structure or revert to old $item.'/' => $link form
+ $result['directories'][] = [
+ 'name' => $item.'/',
+ 'link' => $this->router->generate($name, ['path' => substr($itempath, strlen($path) + 1)] + $parameters)
+ //TODO: add stats here ? like number of files ?
+ ];
+ //With file
+ } elseif (is_file($itempath)) {
+ //Set file
+ $result['files'][] = $this->getFile($path, $itempath, $name, $parameters);
+ //With unknown type
+ } else {
+ //Throw 404
+ throw new \Exception(sprintf('Unknown file "%s" type', $itempath));
+ }
+ }
+ //With unknown type
+ } else {
+ //Throw 404
+ throw new \Exception(sprintf('Unknown file "%s" type', $realpath));
+ }
+
+ //Return result
+ return $result;
+ }
+
+ /**
+ * Get file infos
+ *
+ * @param string $path The file path
+ * @param bool $si Use si units
+ * @return array The file infos
+ * /
+ public function infos(string $path, bool $si = true): array {
+ //Stat file
+ $stat = stat($path);
+
+ //Set unit
+ $unit = $si ? 1000 : 1024;
+
+ //Set index
+ $index = [ '', $si ? 'k' : 'K', 'M', 'G', 'T', 'P', 'E' ];
+
+ //Get exp
+ $exp = intval((log($stat['size']) / log($unit)));
+
+ //Rebase number
+ $number = round($stat['size'] / pow($unit, $exp), 2);
+
+ //Set file infos
+ $fileinfos = [
+ 'intlsize' => $intlsize = $this->intl->number($number),
+ 'intlunit' => $intlunit = $this->translator->trans($index[$exp].($si ? '' : 'i').'B'),
+ 'name' => basename($path).' ('.$intlsize.' '.$intlunit.')',
+ 'size' => $stat['size'],
+ 'ctime' => $stat['ctime'],
+ 'mtime' => $stat['mtime'],
+ //TODO: preview for images ?
+ //TODO: extra fields ? (preview, miniature, etc ?)
+ //TODO: mimetype decided extra fields ? (.pdf for doc, webm preview, img preview, etc ?)
+ //TODO: XXX: finish this !!!
+ ];
+
+ //With mimetype
+ if (($mimetype = mime_content_type($path)) !== false) {
+ //Set file mimetype
+ $fileinfos['mimetype'] = $mimetype;
+
+ //TODO: with image preview, movie webm preview, others a imagemagick file with format initials ?
+
+ //With image
+ if (
+ $mimetype == 'image/jpeg' ||
+ $mimetype == 'image/png' ||
+ $mimetype == 'image/bmp' ||
+ $mimetype == 'image/tiff' ||
+ $mimetype == 'image/svg+xml'
+ ) {
+ //Get image width and height
+ if (($fileinfos['image'] = getimagesize($path)) === false) {
+ //Throw error
+ throw new \Exception(sprintf('Unable to get "%s" file image size', $path));
+ }
+
+ //Set thumb
+ $fileinfos['thumb'] = $this->image->getThumb($path, 64, 64);
+ }
+ }
+ /*
+ 'src' =>
+ //With location user source image
+ if (($isFile = is_file($source = $this->config['path'].'/location/'.$location['id'].'/'.$id.'.png')) && ($mtime = stat($source)['mtime'])) {
+ //Set location image
+ $this->context['locations'][$locationId]['image'] = $this->image->getThumb($location['miniature'], $mtime, $source);
+
+ //With image mimetype
+ if (in_array($fileinfos['mimetype'], [ 'image/jpeg', 'image/png', 'image/webp' ])) {
+ header('Content-Type: text/plain');
+ var_dump($fileinfos);
+ exit;
+ $file['thumb'] = $this->router->generate('rapsyspack_thumb', [ 'hash' => $this->slugger->hash(''), 'path' => $path, 'slug' => $slug ]);
+ #public function thumb(Request $request, string $hash, int $updated, string $path, int $width, int $height): Response {
+ }
+ * /
+
+ //Return file infos
+ return $fileinfos;
+ }*/
+}
$a = $random % 10;
//Set b
- $b = $random / 10 % 10;
+ $b = intval($random / 10) % 10;
//Set c
- $c = $random / 100 % 10;
+ $c = intval($random / 100) % 10;
//Set equation
$equation = $a.' * '.$b.' + '.$c;
* @param string $path The path
* @param ?int $height The height
* @param ?int $width The width
+ * @param ?string $format The format
* @return array The thumb data
*/
- public function getThumb(string $path, ?int $height = null, ?int $width = null): array {
+ public function getThumb(string $path, ?int $height = null, ?int $width = null, ?string $format = null): array {
+ //Without format
+ if ($format === null || !in_array($format, $this->config['formats'])) {
+ //Set format from config
+ $format = $this->config['thumb']['format'];
+ }
+
//Without height
if ($height === null) {
//Set height from config
//Return array
return [
- 'src' => $this->router->generate('rapsyspack_thumb', ['hash' => $hash, 'path' => $short, 'height' => $height, 'width' => $width]),
- 'width' => $width,
- 'height' => $height
+ 'src' => $this->router->generate('rapsyspack_thumb', ['hash' => $hash, 'path' => $short, 'height' => $height, 'width' => $width, '_format' => $format]),
+ 'height' => $height,
+ 'width' => $width
];
}
//Set styles
static $styles = [
'decimal' => \NumberFormatter::DECIMAL,
- 'currency' => \NumberFormatter::CURRENCY,
+ #'currency' => \NumberFormatter::CURRENCY,
'percent' => \NumberFormatter::PERCENT,
'scientific' => \NumberFormatter::SCIENTIFIC,
'spellout' => \NumberFormatter::SPELLOUT,
'int32' => \NumberFormatter::TYPE_INT32,
'int64' => \NumberFormatter::TYPE_INT64,
'double' => \NumberFormatter::TYPE_DOUBLE,
- 'currency' => \NumberFormatter::TYPE_CURRENCY
+ #'currency' => \NumberFormatter::TYPE_CURRENCY
];
//Get formatter
# Register file util service
rapsyspack.file_util:
class: 'Rapsys\PackBundle\Util\FileUtil'
- arguments: [ '@rapsyspack.image_util', '@rapsyspack.intl_util', '@translator' ]
+ arguments: [ '@service_container', '@rapsyspack.image_util', '@rapsyspack.intl_util', '@router', '@rapsyspack.slugger_util', '@translator' ]
public: true
# Register image util service
rapsyspack.image_util:
path: '/bundles/rapsyspack/pack/css/{file<[a-zA-Z0-9]+>}.{!_format<css>?css}'
methods: GET
+rapsyspack_download:
+ path: '/download/{hash<[a-zA-Z0-9=_-]+>}/{path<[a-zA-Z0-9=_-]+>}'
+ controller: Rapsys\PackBundle\Controller::download
+ methods: GET
+
rapsyspack_facebook:
path: '/facebook/{hash<[a-zA-Z0-9=_-]+>}/{width<\d+>}/{height<\d+>}/{path<[a-zA-Z0-9=_-]+>}.{!_format<(jpeg|png|webp)>}'
controller: Rapsys\PackBundle\Controller::facebook