Adds Form files upload type

parent 6211bbed
......@@ -9,7 +9,7 @@ framework:
web_profiler:
toolbar: true
intercept_redirects: false
intercept_redirects: true
swiftmailer:
delivery_address: sebastian.castro@protonmail.com
......
......@@ -48,6 +48,11 @@ vich_uploader:
directory_namer: biopen.upload_directory_namer
namer: vich_uploader.namer_origname
delete_on_remove: true
element_file:
upload_destination: '%kernel.root_dir%/../web'
directory_namer: biopen.upload_directory_namer
namer: vich_uploader.namer_origname
delete_on_remove: true
partner_image:
upload_destination: '%kernel.root_dir%/../web'
directory_namer: biopen.upload_directory_namer
......
......@@ -9,21 +9,21 @@ parameters:
base_protocol: https
base_url: saas.localhost # my-site.com
base_path: ~ # on localhost its probably something like /GoGoCarto/web/app_dev.php
# EMAIL SERVER
mailer_transport: smtp
mailer_host: 127.0.0.1
mailer_host: 127.0.0.1
mailer_user: user@localhost.fr
mailer_password: ~
# CONTACT EMAIL (displayed on error messages)
contact_email: "contact@localhost.fr"
instance_name: "GoGoCarto"
# A secret key that's used to generate certain security-related tokens
secret: ThisTokenIsNotSoSecretChangeIt
csrf_protection: false # active csrf protection on production servers
# OAUTH CONF
oauth_communs_id: "disabled"
oauth_communs_secret: "disabled"
......@@ -33,4 +33,8 @@ parameters:
oauth_facebook_secret: "disabled"
# MONGODB
mongodb_server: "mongodb://localhost:27017"
\ No newline at end of file
mongodb_server: "mongodb://localhost:27017"
# UPLOAD
images_max_filesize: '8M' # for public images upload
files_max_filesize: '1M' # for other public file upload
\ No newline at end of file
......@@ -33,7 +33,7 @@ class AbstractFile implements \Serializable
public function unserialize($serialized)
{
list (
// $path,
$path,
// $fileName
) = unserialize($serialized);
// TODO : on element creation, when potential duplicate is detected, the element is stored in the session (serialized)
......
......@@ -18,7 +18,7 @@ class ImageResizer
$destImage = preg_replace('/(\.jpe?g|\.png)$/i', '-'.$w.'x'.$h.'.png', $srcImage);
$image = InterventionImage::make($srcImage)->fit($w, $h)->save($destImage);
}
if ($document instanceof ElementImage)
else if ($document instanceof ElementImage)
{
if (!$document->getFile()) return;
$srcImage = $document->calculateFilePath();
......
......@@ -19,14 +19,15 @@ class UploadDirectoryNamer implements DirectoryNamerInterface
private $PATHS = [
"image" => "/images",
"element_image" => "/images/elements",
"element_file" => "/files/elements",
"partner_image" => "/images/partners",
"config_image" => "/images/config",
"import_file" => "/imports",
"default_file" => '/default'
];
];
public function directoryName($object, PropertyMapping $mapping)
{
{
$name = $this->getDirectoryPathFromKey($object->getVichUploadFileKey());
return $name;
}
......
......@@ -63,7 +63,6 @@ class ElementFormController extends GoGoController
}
$addOrEditComplete = false;
$securityContext = $this->container->get('security.context');
$userRoles = [];
$session = $this->getRequest()->getSession();
......@@ -168,6 +167,7 @@ class ElementFormController extends GoGoController
{
$element = $session->get('elementWaitingForDuplicateCheck');
$element->resetImages(); // see comment in AbstractFile:59
$element->resetFiles();
// filling the form with the previous element created in case we want to recopy its informations (only for admins)
$elementForm = $this->get('form.factory')->create(ElementType::class, $element);
......@@ -315,7 +315,8 @@ class ElementFormController extends GoGoController
"isAllowedDirectModeration" => $isAllowedDirectModeration,
"isAnonymousWithEmail" => $session->has('userEmail'),
"config" => $configService->getConfig(),
"uploadMaxFilesize" => $this->detectMaxUploadFileSize()
"imagesMaxFilesize" => $this->detectMaxUploadFileSize('images'),
"filesMaxFilesize" => $this->detectMaxUploadFileSize('files')
));
}
......@@ -374,7 +375,7 @@ class ElementFormController extends GoGoController
*
* @return int Max file size in bytes
*/
private function detectMaxUploadFileSize()
private function detectMaxUploadFileSize($key = null)
{
/**
* Converts shorthands like "2M" or "512K" to bytes
......@@ -396,6 +397,11 @@ class ElementFormController extends GoGoController
$max_post = $normalize(ini_get('post_max_size'));
$memory_limit = $normalize(ini_get('memory_limit'));
$maxFileSize = min($max_upload, $max_post, $memory_limit);
if ($key) {
$appMaxsize = $this->container->getParameter($key."_max_filesize");
$maxFileSize = min($maxFileSize, $normalize($appMaxsize));
}
return $maxFileSize;
}
}
......@@ -17,6 +17,7 @@ use JMS\Serializer\Annotation\Expose;
use Gedmo\Mapping\Annotation as Gedmo;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Biopen\CoreBundle\Document\EmbeddedImage;
use Biopen\CoreBundle\Document\AbstractFile;
abstract class ElementStatus
{
......@@ -57,6 +58,15 @@ class ElementImage extends EmbeddedImage
protected $vichUploadFileKey = "element_image";
}
/**
* @MongoDB\EmbeddedDocument
* @Vich\Uploadable
*/
class ElementFile extends AbstractFile
{
protected $vichUploadFileKey = "element_file";
}
/**
* Element
*
......@@ -180,6 +190,13 @@ class Element
*/
private $images;
/**
* Files linked to an element
*
* @MongoDB\EmbedMany(targetDocument="Biopen\GeoDirectoryBundle\Document\ElementFile")
*/
private $files;
/**
* @var string
*
......@@ -385,6 +402,11 @@ class Element
$this->images = [];
}
public function resetFiles()
{
$this->files = [];
}
public function resetContributions()
{
$this->contributions = [];
......@@ -608,6 +630,7 @@ class Element
if (property_exists($this,$key)) {
$method = 'get' . ucfirst($key);
if ($key == 'images') $method = 'getImagesUrls';
if ($key == 'files') $method = 'getFilesUrls';
return $this->$method();
}
else return $this->getCustomProperty($key);
......@@ -1458,4 +1481,48 @@ class Element
{
return $this->isExternal;
}
/**
* Add file
*
* @param Biopen\GeoDirectoryBundle\Document\ElementFile $file
*/
public function addFile($file)
{
$this->files[] = $file;
}
public function setFiles($files)
{
$this->files = array_filter($files, function($el) {
return $el->getFile() != null;
});
}
/**
* Remove file
*
* @param Biopen\GeoDirectoryBundle\Document\ElementFile $file
*/
public function removeFile($file)
{
$this->files->removeElement($file);
}
/**
* Get files
*
* @return \Doctrine\Common\Collections\Collection $files
*/
public function getFiles()
{
return $this->files;
}
public function getFilesUrls()
{
$result = [];
foreach ($this->files as $file) $result[] = $file->getFileUrl();
return $result;
}
}
......@@ -107,6 +107,7 @@ class ElementJsonGenerator
// SPECIFIC DATA
$baseJson .= $this->encodeArrayObjectToJson("stamps", $element->getStamps());
$baseJson .= $this->encodeArrayObjectToJson("images", $element->getImages());
$baseJson .= $this->encodeArrayObjectToJson("files", $element->getFiles());
$baseJson = rtrim($baseJson, ',');
// MODIFIED ELEMENT (for pending modification)
......
<?php
namespace Biopen\GeoDirectoryBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Vich\UploaderBundle\Form\Type\VichFileType;
class ElementFileType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', VichFileType::class, [
'required' => false, 'label' => false
]);
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Biopen\GeoDirectoryBundle\Document\ElementFile'
));
}
/**
* @return string
*/
public function getName()
{
return 'biopen_geodirectorybundle_element_file';
}
}
<?php
/**
* This file is part of the GoGoCarto project.
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright Copyright (c) 2016 Sebastian Castro - 90scastro@gmail.com
* @license MIT License
* @Last Modified time: 2018-06-08 19:41:29
*/
namespace Biopen\GeoDirectoryBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Vich\UploaderBundle\Form\Type\VichImageType;
class ElementImageType extends AbstractType
......@@ -27,7 +16,6 @@ class ElementImageType extends AbstractType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// ->add('file', FileType::class, array('label' => 'Image à importer', 'required' => false));
->add('file', VichImageType::class, [
'required' => false, 'label' => false
]);
......
......@@ -36,7 +36,7 @@ use Biopen\GeoDirectoryBundle\Form\OpenHoursType;
use Biopen\GeoDirectoryBundle\Form\PostalAddressType;
use Biopen\GeoDirectoryBundle\Form\CoordinatesType;
use Biopen\GeoDirectoryBundle\Form\ElementImageType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Biopen\GeoDirectoryBundle\Form\ElementFileType;
use Doctrine\ODM\MongoDB\DocumentRepository;
use Doctrine\ODM\MongoDB\DocumentManager;
......@@ -61,6 +61,13 @@ class ElementType extends AbstractType
->add('fullAddress', TextType::class, array('mapped' => false))
->add('address', PostalAddressType::class)
->add('geo', CoordinatesType::class)
->add('files', CollectionType::class, array(
'entry_type' => ElementFileType::class,
'allow_add' => true,
'allow_delete' => true,
'label' => ''
))
->add('images', CollectionType::class, array(
'entry_type' => ElementImageType::class,
'allow_add' => true,
......
......@@ -46,28 +46,31 @@ jQuery(document).ready(function()
// recopy info
$('.redo-time-slot-button').click(function() { redoTimeSlot($(this).attr('id').split("_")[0]); });
var imagesCounter = $('#images-fields-list').children().length;
$('.btn-add-image').click(function (e) {
$('.btn-add-file').click(function (e) {
var type = $(this).data('type');
var maxsize = $(this).data('maxsize');
// grab the prototype template
var newWidget = $('#new-image-template').html().replace(/__count__/g, imagesCounter);
imagesCounter++;
var counter = parseInt($(this).data('count'));
var newWidget = $('.new-file-template.' + type).html().replace(/__count__/g, counter);
console.log("add file", type, counter);
$(this).data('count', counter + 1);
// create a new list element and add it to the list
var newElem = $('<li></li>').html(newWidget);
newElem.appendTo($('#new-images-fields-list'));
newElem.appendTo($('.new-file-fields-list.' + type));
var uploadField = newElem.find('input[type=file]');
uploadField.trigger('click');
uploadField.change(function() {
newElem.find('.file-too-big').hide();
if(this.files[0].size > uploadMaxFilesize){
if(this.files[0].size > maxsize){
newElem.find('.file-too-big').show();
this.value = "";
};
});
});
$('#images-fields-list .icon-close').click(function() {
$('.file-fields-list .icon-close').click(function() {
$(this).closest('li').remove();
})
});
......
......@@ -367,7 +367,7 @@
}
.redo-time-slot-button { line-height: 3rem; }
#images-fields-list {
.file-fields-list.images {
display: flex;
.image-container {
margin: 8px 10px;
......@@ -384,8 +384,8 @@
}
.icon-close {
position: absolute;
top: -10px;
right: -10px;
top: -12px;
right: -14px;
cursor: pointer;
padding: 5px;
&:before {
......@@ -402,20 +402,20 @@
}
}
#images-fields-list, #new-images-fields-list {
.file-fields-list, .new-file-fields-list {
margin: 0;
}
#new-images-fields-list li:first-child { margin-top: .8rem; }
.new-file-fields-list.images li:first-child { margin-top: .8rem; }
.btn-add-image, .btn-pick-image {
.btn-add-file, .btn-pick-file {
font-size: .9rem;
height: 2rem !important;
line-height: 2rem !important;
padding: 0 1.3rem !important;
}
.btn-pick-image {
.btn-pick-file {
width: calc(3rem - 10px);
padding: 0 !important;
}
......@@ -437,13 +437,24 @@
.input-field .prefix~button {
margin-left: 3rem;
}
.input-field.images {
.input-field.file-field {
line-height: 2.2rem;
.file-path-wrapper {
height: 2.5rem;
a { color: inherit !important; }
}
}
.input-field.images, .input-field.files {
line-height: 3rem;
margin-top: 0;
.prefix { line-height: 2.8rem; }
}
.error {
.field-images, .field-files {
margin-bottom: 15px;
}
.error, .form-error {
color: $error-color;
}
}
......
......@@ -23,6 +23,7 @@
{ label: 'Case à cocher', name: "checkbox", attrs: { type: 'checkbox' }, icon: '' },
{ label: 'Email principal', name: "email", attrs: { type: 'email' }, icon: '@' },
{ label: 'Images (upload)', name: "images", attrs: { type: 'images' }, icon: '' },
{ label: 'Fichiers (upload)', name: "files", attrs: { type: 'files' }, icon: '' },
];
var templates = {
title: function(fieldData) { return { field: '<input id="' + fieldData.name + '"><span class="mandatory"> Ce champ est indispensable</span>' }; },
......@@ -32,7 +33,8 @@
separator: function(fieldData) { return { field: '<hr>' }; },
checkbox: function(fieldData) { return { field: '<input id="' + fieldData.name + '"' + (fieldData.defaultvalue == "yes" ? 'checked="checked"' : '') + ' type="checkbox"/>' }; },
email: function(fieldData) { return { field: '<input id="' + fieldData.name + '"' + ' type="email"/><span class="mandatory"> Ce champ est conseillé</span>' }; },
images: function(fieldData) { return { field: '<input id="' + fieldData.name + '"' + ' type="file"/>' }; },
images: function(fieldData) { return { field: '<input id="' + fieldData.name + '"' + ' type="file" accept="images/*"/>' }; },
files: function(fieldData) { return { field: '<input id="' + fieldData.name + '"' + ' type="file" accept="'+fieldData.accept+'"/>' }; },
image: function(fieldData) { return { field: '<input id="' + fieldData.name + '"' + ' type="text"/>' }; },
};
......@@ -90,6 +92,11 @@
images: {
icon: iconAttr,
separator: { label: '' }, // separate important attrs from others
},
files: {
icon: iconAttr,
accept: { label: "Fichier acceptés", placeholder: ".pdf, audio/*, .mp3 (séparés par des virgules)"},
separator: { label: '' }, // separate important attrs from others
}
};
......@@ -102,8 +109,8 @@
locale: 'fr-FR',
location: '{{ asset("assets/js/") }}'
},
disableFields: ['hidden', 'file', 'button', 'autocomplete', 'title', 'taxonomy', 'address'],
controlOrder: ['text', 'email', 'images', 'textarea', 'checkbox', 'checkbox-group', 'radio-group', 'select', 'date', 'number'],
disableFields: ['hidden', 'file', 'button', 'autocomplete', 'title', 'taxonomy', 'address', 'image'],
controlOrder: ['text', 'email', 'images', 'textarea', 'checkbox', 'checkbox-group', 'radio-group', 'select', 'date', 'number', 'files'],
disabledAttrs: ['className', 'inline', 'toggle', 'description', 'other', 'multiple'],
formData: {{ formData|json_encode|raw }},
roles: { 1: "Administrateur" },
......@@ -115,9 +122,10 @@
});
setInterval(function() {
// prevent adding two contat email attributes
// prevent adding two of those fields
$('.input-control[data-type=email]').toggle($('.email-field').length == 0);
$('.input-control[data-type=images]').toggle($('.images-field').length == 0);
$('.input-control[data-type=files]').toggle($('.files-field').length == 0);
// $('.name-wrap input[name=name]').val('email');
// get all input names (used after for uniqueness)
......@@ -146,6 +154,7 @@
$('.email-field input[name=name]').val('email');
$('.images-field input[name=name]').val('images');
$('.files-field input[name=name]').val('files');
$('.iconpicker-popover button').click(function(e) {
e.stopPropagation();
......@@ -168,7 +177,7 @@
.taxonomy-field .field-actions .del-button, .taxonomy-field .field-actions .copy-button,
.address-field .field-actions .del-button, .address-field .field-actions .copy-button,
.title-field .field-actions .del-button, .title-field .field-actions .copy-button,
.openhours-field .field-actions .copy-button, .email-field .copy-button, .images-field .copy-button, .image-field .copy-button
.openhours-field .field-actions .copy-button, .email-field .copy-button, .images-field .copy-button, .image-field .copy-button, .files-field .copy-button
{ display: none !important; }
.separator-field label, .separator-field .copy-button, .separator-field .toggle-form { display: none !important; }
......@@ -180,7 +189,7 @@
.title-field .name-wrap, .title-field .access-wrap, .title-field .required-wrap,
.textarea-field .subtype-wrap, .checkbox-field .field-options, .checkbox-field .required-wrap,
.paragraph-field .subtype-wrap, .header-field .subtype-wrap,
.email-field .name-wrap, .image-field .name-wrap, .images-field .name-wrap, .images-field .placeholder-wrap, .images-field .value-wrap, .images-field .required-wrap
.email-field .name-wrap, .image-field .name-wrap, .images-field .name-wrap, .images-field .placeholder-wrap, .images-field .value-wrap, .images-field .required-wrap, .files-field .name-wrap, .files-field .placeholder-wrap, .files-field .value-wrap, .files-field .required-wrap
{ display: none !important; }
/* specific fields styling */
......
......@@ -37,7 +37,6 @@
var editMode = false;
{% if editMode %} editMode = true; {% endif %}
var defaultTileLayer = "{{ config.defaultTileLayer.url }}";
var uploadMaxFilesize = {{ uploadMaxFilesize }};
initMap();
</script>
......
<div class="form-error">{{ form_errors(form.images) }}</div>
{# PREVIOUS UPLOADS #}
<ul class="file-fields-list {{field.name}}">
{% for formField in form[field.name] %}
{# Display images #}
{% if field.name == 'images' %}
{% set image_url = formField.vars.value.fileUrl ? formField.vars.value.fileUrl : formField.vars.value.externalImageUrl %}
{% if image_url %}
<li class="image-container">
<img src="{{ image_url }}" />
<span class="icon-close material-icons gogo-icon-close"></span>
<input style="display:none" type="file" name="{{ formField.vars.form.vars.full_name }}[file][file]" value="{{image_url}}">
</li>
{% endif %}
{# Display Files #}
{% else %}
<div class="file-field input-field">
<div class="btn btn-pick-file btn-neutral disabled">
<span class="fa fa-upload"></span>
<input style="display:none" type="file" name="{{ formField.vars.form.vars.full_name }}[file][file]" value="{{formField.vars.value.fileUrl}}">
</div>
<div class="file-path-wrapper">
<a href="{{formField.vars.value.fileUrl}}" target="_blank">{{ formField.vars.value.fileName }}</a>
</div>
<span class="icon-close far fa-times-circle" onclick="$(this).closest('.input-field').remove();"></span>
</div>
{% endif %}
{% endfor %}
</ul>
{