#9663 Update items with matching id option when uploading image

This commit is contained in:
Juan Ferrer 2019-02-07 14:09:56 +01:00
parent 0ae35c2622
commit 6ec94de7a3
14 changed files with 189 additions and 158 deletions

2
debian/changelog vendored
View File

@ -1,4 +1,4 @@
hedera-web (1.406.28) stable; urgency=low hedera-web (1.406.29) stable; urgency=low
* Initial Release. * Initial Release.

View File

@ -1,6 +1,8 @@
Images: Imatges Images: Imatges
Collection: Col·lecció Collection: Col·lecció
Click or drop files here: Prem o deixa anar els arxius aquí Click or drop files here: Prem o deixa anar els arxius aquí
Pending upload: Pujada pendent
Update items with matching id: Actualitzar els elements amb id coincident
Clear all: Netejar tot Clear all: Netejar tot
Upload files: Pujar arxius Upload files: Pujar arxius
Waiting for upload: Esperant per pujar Waiting for upload: Esperant per pujar

View File

@ -1,6 +1,8 @@
Images: Images Images: Images
Collection: Collection Collection: Collection
Click or drop files here: Click or drop files here Click or drop files here: Click or drop files here
Pending upload: Pending upload
Update items with matching id: Update items with matching id
Clear all: Clear all Clear all: Clear all
Upload files: Upload files Upload files: Upload files
Waiting for upload: Waiting for upload Waiting for upload: Waiting for upload

View File

@ -1,6 +1,8 @@
Images: Imágenes Images: Imágenes
Collection: Colección Collection: Colección
Click or drop files here: Pulsa o suelta los archivos aquí Click or drop files here: Pulsa o suelta los archivos aquí
Pending upload: Subida pendiente
Update items with matching id: Actualizar ítems con id coincidente
Clear all: Limpiar todo Clear all: Limpiar todo
Upload files: Subir archivos Upload files: Subir archivos
Waiting for upload: Esperando para subir Waiting for upload: Esperando para subir

View File

@ -1,6 +1,8 @@
Images: Images Images: Images
Collection: Collection Collection: Collection
Click or drop files here: Cliquez ici ou déposer des fichiers Click or drop files here: Cliquez ici ou déposer des fichiers
Pending upload: Hausse en attente
Update items with matching id: Mettre à jour les éléments avec l'identifiant correspondant
Clear all: Tout effacer Clear all: Tout effacer
Upload files: Upload Files Upload files: Upload Files
Waiting for upload: En attente de télécharger Waiting for upload: En attente de télécharger

View File

@ -1,6 +1,8 @@
Images: Imagens Images: Imagens
Collection: Coleção Collection: Coleção
Click or drop files here: Clique ou solte arquivos aqui Click or drop files here: Clique ou solte arquivos aqui
Pending upload: Ascensão pendente
Update items with matching id: Atualizar itens com id correspondente
Clear all: Limpar tudo Clear all: Limpar tudo
Upload files: Fazer upload de arquivos Upload files: Fazer upload de arquivos
Waiting for upload: Esperando para enviar Waiting for upload: Esperando para enviar

View File

@ -1,5 +1,4 @@
(function() { (function() {
var Status = { var Status = {
NONE : 0 NONE : 0
,WAITING : 1 ,WAITING : 1
@ -16,220 +15,200 @@ Hedera.Photos = new Class
,uploadQueue: [] ,uploadQueue: []
,isUploading: false ,isUploading: false
,activate: function () ,activate: function() {
{
this.$('schema').value = 'catalog'; this.$('schema').value = 'catalog';
} }
,addFiles: function (files) ,addFiles: function(files) {
{
if (!files) if (!files)
return; return;
for (var i = 0; i < files.length; i++) for (var i = 0; i < files.length; i++)
this.addFile (files[i]); this.addFile(files[i]);
} }
,addFile: function (file) ,addFile: function(file) {
{
var doc = document; var doc = document;
var div = doc.createElement ('div'); var li = doc.createElement('div');
var button = new Htk.Button ({ var div = doc.createElement('div');
div.className = 'thumb';
li.appendChild(div);
var thumb = doc.createElement('img');
thumb.file = file;
div.appendChild(thumb);
var reader = new FileReader();
reader.onload = function(e) {
thumb.src = e.target.result;
};
reader.readAsDataURL(file);
var name = doc.createElement('input');
name.type = 'text';
name.value = getFileName(file.name);
li.appendChild(name);
var statusNode = doc.createElement('div');
statusNode.className = 'status';
li.appendChild(statusNode);
var button = new Htk.Button({
tip: 'Remove', tip: 'Remove',
icon: 'delete' icon: 'delete'
}); });
button.on ('click', this.onFileRemove, this); button.on('click', this.onFileRemove, this);
div.appendChild (button.node); li.appendChild(button.node);
var thumb = doc.createElement ('img');
thumb.file = file;
thumb.className = 'thumb';
div.appendChild (thumb);
var reader = new FileReader ();
reader.onload = function (e) { thumb.src = e.target.result; };
reader.readAsDataURL(file);
var name = doc.createElement ('input');
name.type = 'text';
name.value = getFileName (file.name);
div.appendChild (name);
var statusNode = doc.createElement ('span');
statusNode.className = 'status';
div.appendChild (statusNode);
var fileData = { var fileData = {
div: div, li: li,
file: file, file: file,
name: name, name: name,
statusNode: statusNode, statusNode: statusNode
status: Status.NONE
}; };
this.filesData.push (fileData); this.filesData.push(fileData);
button.value = fileData; button.value = fileData;
this.$('file-list').appendChild (div); this.$('file-list').appendChild(li);
this.setImageStatus(fileData, Status.NONE, 'add', _('Pending upload'));
} }
,onUploadClick: function () ,onUploadClick: function() {
{
var filesData = this.filesData; var filesData = this.filesData;
var count = 0; var count = 0;
for (var i = 0; i < filesData.length; i++) for (var i = 0; i < filesData.length; i++) {
{
var fileData = filesData[i]; var fileData = filesData[i];
if (fileData.status === Status.NONE) if (fileData.status === Status.NONE) {
{ this.setImageStatus(
this.setImageStatus (
fileData, Status.WAITING, 'cloud-upload', _('Waiting for upload')); fileData, Status.WAITING, 'cloud-upload', _('Waiting for upload'));
fileData.name.disabled = true; fileData.name.disabled = true;
this.uploadQueue.push (fileData); this.uploadQueue.push(fileData);
count++; count++;
} }
} }
if (count === 0) if (count === 0)
Htk.Toast.showWarning (_('There are no files to upload')); Htk.Toast.showWarning(_('There are no files to upload'));
else else
this.uploadNextFile (); this.uploadNextFile();
} }
,uploadNextFile: function () ,uploadNextFile: function() {
{
if (this.isUploading) if (this.isUploading)
return; return;
this.isUploading = true; this.isUploading = true;
var fileData = this.uploadQueue.shift (); var fileData = this.uploadQueue.shift();
this.setImageStatus ( this.setImageStatus(
fileData, Status.UPLOADING, 'upload', _('Uploading file')); fileData, Status.UPLOADING, 'upload', _('Uploading file'));
var formData = new FormData(); var formData = new FormData();
formData.append ('image', fileData.file); formData.append('updateMatching', this.$('update-matching').value);
formData.append ('name', fileData.name.value); formData.append('image', fileData.file);
formData.append ('schema', this.$('schema').value); formData.append('name', fileData.name.value);
formData.append ('srv', 'json:image/upload'); formData.append('schema', this.$('schema').value);
this.conn.sendFormData (formData, formData.append('srv', 'json:image/upload');
this.onFileUpload.bind (this, fileData)); this.conn.sendFormData(formData,
this.onFileUpload.bind(this, fileData));
} }
,onFileUpload: function (fileData, data, error) ,onFileUpload: function(fileData, data, error) {
{
this.isUploading = false; this.isUploading = false;
if (data) if (data) {
{ this.setImageStatus(
this.setImageStatus (
fileData, Status.UPLOADED, 'ok', _('Image uploaded')); fileData, Status.UPLOADED, 'ok', _('Image uploaded'));
} } else {
else this.setImageStatus(
{
this.setImageStatus (
fileData, Status.NONE, 'error', error.message); fileData, Status.NONE, 'error', error.message);
fileData.name.disabled = false; fileData.name.disabled = false;
this.errors = true; this.errors = true;
} }
if (this.uploadQueue.length === 0) if (this.uploadQueue.length === 0) {
{
if (this.errors) if (this.errors)
Htk.Toast.showError (_('Some errors happened on upload')); Htk.Toast.showError(_('Some errors happened on upload'));
else else
Htk.Toast.showMessage (_('Upload finished successfully')); Htk.Toast.showMessage(_('Upload finished successfully'));
this.errors = false; this.errors = false;
} } else
else this.uploadNextFile();
this.uploadNextFile ();
} }
,setImageStatus: function (fileData, status, icon, title) ,setImageStatus: function(fileData, status, icon, title) {
{
fileData.status = status; fileData.status = status;
var statusNode = fileData.statusNode; var statusNode = fileData.statusNode;
Vn.Node.removeChilds (statusNode); Vn.Node.removeChilds(statusNode);
var iconNode = new Htk.Icon ({icon: icon}); var iconNode = new Htk.Icon({icon: icon});
statusNode.appendChild (iconNode.node); statusNode.appendChild(iconNode.node);
statusNode.title = title ? title : ''; statusNode.title = title ? title : '';
} }
,onFileRemove: function (button) ,onFileRemove: function(button) {
{
var fileData = button.value; var fileData = button.value;
this.$('file-list').removeChild (fileData.div); this.$('file-list').removeChild(fileData.li);
for (var i = 0; i < this.filesData.length; i++) for (var i = 0; i < this.filesData.length; i++)
if (this.filesData[i] === fileData) if (this.filesData[i] === fileData) {
{ this.filesData.splice(i, 1);
this.filesData.splice (i, 1);
break; break;
} }
} }
,onClearClick: function () ,onClearClick: function() {
{
this.filesData = []; this.filesData = [];
Vn.Node.removeChilds (this.$('file-list')); Vn.Node.removeChilds(this.$('file-list'));
} }
,onDropzoneClick: function () ,onDropzoneClick: function() {
{ this.$('file').click();
this.$('file').click ();
} }
,onFileChange: function () ,onFileChange: function() {
{ this.addFiles(this.$('file').files);
this.addFiles (this.$('file').files);
} }
,onDragEnter: function (event) ,onDragEnter: function() {
{ this.$('dropzone').classList.add('dragover');
Vn.Node.addClass (this.$('dropzone'), 'dragover');
} }
,onDragLeave: function (event) ,onDragLeave: function() {
{ this.$('dropzone').classList.remove('dragover');
Vn.Node.removeClass (this.$('dropzone'), 'dragover');
} }
,onDragOver: function (event) ,onDragOver: function(event) {
{ event.preventDefault();
event.preventDefault ();
} }
,onDragEnd: function (event) ,onDragEnd: function(event) {
{ this.$('dropzone').classList.remove('dragover');
Vn.Node.removeClass (this.$('dropzone'), 'dragover'); event.dataTransfer.clearData();
event.dataTransfer.clearData ();
} }
,onDrop: function (event) ,onDrop: function(event) {
{ event.preventDefault();
event.preventDefault (); this.addFiles(event.dataTransfer.files);
this.addFiles (event.dataTransfer.files);
} }
}); });
function getFileName (path) function getFileName(path) {
{ var barIndex = path.lastIndexOf('/');
var barIndex = path.lastIndexOf ('/');
if (barIndex === -1) if (barIndex === -1)
barIndex = path.lastIndexOf ('\\'); barIndex = path.lastIndexOf('\\');
if (barIndex === -1) if (barIndex === -1)
barIndex = 0; barIndex = 0;
var dotIndex = path.lastIndexOf ('.'); var dotIndex = path.lastIndexOf('.');
if (dotIndex === -1) if (dotIndex === -1)
dotIndex = 0; dotIndex = 0;
return path.substr (barIndex, dotIndex); return path.substr(barIndex, dotIndex);
} }
})(); })();

View File

@ -1,18 +1,15 @@
.photos .photos {
{
padding: 1em; padding: 1em;
} }
.photos .box .photos .box {
{
max-width: 25em; max-width: 25em;
padding: 2em; padding: 2em;
} }
/* Dropzone */ /* Dropzone */
.photos .dropzone .photos .dropzone {
{
background-color: white; background-color: white;
border-style: dashed; border-style: dashed;
border-radius: .4em; border-radius: .4em;
@ -22,52 +19,71 @@
color: #666; color: #666;
cursor: pointer; cursor: pointer;
} }
.photos .dropzone.dragover .photos .dropzone.dragover {
{
color: #CCC; color: #CCC;
border-style: solid; border-style: solid;
} }
.photos input[type=file] .photos input[type=file] {
{
display: none; display: none;
} }
/* File list */ /* File list */
.photos .file-list .photos .file-list {
{
margin-top: 1em; margin-top: 1em;
} }
.photos .file-list > div .photos .file-list > div {
{
height: 2.5em; height: 2.5em;
display: flex;
align-items: center;
} }
.photos .file-list .thumb .photos .file-list > div > * {
{ overflow: hidden;
}
.photos .file-list .thumb {
width: 2em;
padding-right: .5em;
text-align: center;
}
.photos .file-list .thumb > img {
max-height: 2em; max-height: 2em;
max-width: 2em; max-width: 2em;
vertical-align: middle; vertical-align: middle;
margin: 0 1em;
} }
.photos .file-list input .photos .file-list input {
{ flex: 1;
max-width: 10em; min-width: 0;
} }
.photos .file-list .status .photos .file-list .status {
{
margin-left: .5em;
cursor: pointer; cursor: pointer;
width: 1em;
padding-left: .5em;
padding-right: .5em;
}
.photos .file-list .status > img {
display: block;
}
.photos .file-list .htk-button {
opacity: .2;
}
.photos .file-list .htk-button:hover {
background-color: transparent;
opacity: 1;
}
.photos .file-list .htk-button > img {
display: block;
} }
/* Footer */ /* Footer */
.photos .footer .photos .update-matching {
{ margin-top: 1.5em;
}
.photos .footer {
margin-top: 1.5em; margin-top: 1.5em;
text-align: center; text-align: center;
} }
.photos .footer > button .photos .footer > button {
{
font-size: 1.2em; font-size: 1.2em;
margin-left: 1em; margin-left: 1em;
} }

View File

@ -31,6 +31,12 @@
name="image" name="image"
on-change="onFileChange"/> on-change="onFileChange"/>
<div id="file-list" class="file-list"/> <div id="file-list" class="file-list"/>
<div class="update-matching">
<label>
<htk-check id="update-matching" value="true"/>
<t>Update items with matching id</t>
</label>
</div>
<div class="footer"> <div class="footer">
<button class="thin" on-click="onClearClick"> <button class="thin" on-click="onClearClick">
<t>Clear all</t> <t>Clear all</t>

View File

@ -1,6 +1,6 @@
{ {
"name": "hedera-web", "name": "hedera-web",
"version": "1.406.28", "version": "1.406.29",
"description": "Verdnatura web page", "description": "Verdnatura web page",
"license": "GPL-3.0", "license": "GPL-3.0",
"repository": { "repository": {

View File

@ -7,9 +7,10 @@
,"File save error": "Failed to save the file: %s" ,"File save error": "Failed to save the file: %s"
,"File size error": "The file must be no longer than %.2f MB" ,"File size error": "The file must be no longer than %.2f MB"
,"Bad file name": "The file name must contain only lowercase letters, digits or the '_' character" ,"Bad file name": "The file name must contain only lowercase letters, digits or the '_' character"
,"Bad schema name": "Invalid schema name" ,"Bad collection name": "Invalid collection name"
,"Schema not exists": "Schema does not exist" ,"Collection not exists": "Collection does not exist"
,"Unreferenced file": "The file is not referenced by the database" ,"Unreferenced file": "The file is not referenced by the database"
,"Cannot update matching id": "Cannot update matching id"
,"Com error": "Error communicating with the server" ,"Com error": "Error communicating with the server"
,"Image open error": "Error opening the image file" ,"Image open error": "Error opening the image file"
,"Operation disabled": "Operation disabled for security" ,"Operation disabled": "Operation disabled for security"

View File

@ -7,9 +7,10 @@
,"File save error": "Error al guardar el fichero: %s" ,"File save error": "Error al guardar el fichero: %s"
,"File size error": "El fichero no debe ocupar más de %.2f MB" ,"File size error": "El fichero no debe ocupar más de %.2f MB"
,"Bad file name": "El nombre del archivo solo debe contener letras minúsculas, dígitos o el carácter '_'" ,"Bad file name": "El nombre del archivo solo debe contener letras minúsculas, dígitos o el carácter '_'"
,"Bad schema name": "Nombre de esquema no válido" ,"Bad collection name": "Nombre de colección no válido"
,"Schema not exists": "El esquema no existe" ,"Collection not exists": "La colección no existe"
,"Unreferenced file": "El archivo no está referenciado por la base de datos" ,"Unreferenced file": "El archivo no está referenciado por la base de datos"
,"Cannot update matching id": "No es posible actualizar los ítems con id coincidente"
,"Com error": "Error en la comunicación con el servidor" ,"Com error": "Error en la comunicación con el servidor"
,"Image open error": "Error al abrir el archivo de imagen" ,"Image open error": "Error al abrir el archivo de imagen"
,"Operation disabled": "Operación deshabilitada por seguridad" ,"Operation disabled": "Operación deshabilitada por seguridad"

View File

@ -7,9 +7,10 @@
,"File save error": "Erro ao salvar o arquivo: %s" ,"File save error": "Erro ao salvar o arquivo: %s"
,"File size error": "O arquivo não deve ser maior que: %.2f MB" ,"File size error": "O arquivo não deve ser maior que: %.2f MB"
,"Bad file name": "O nome do arquivo deve conter somente letras minusculas, numeros ou '_' " ,"Bad file name": "O nome do arquivo deve conter somente letras minusculas, numeros ou '_' "
,"Bad schema name": "Nome de esquema inválido" ,"Bad collection name": "Nome de coleção inválido"
,"Schema not exists": "Esquema não existe" ,"Collection not exists": "Coleção não existe"
,"Unreferenced file": "O arquivo não é referenciado pelo banco de dados" ,"Unreferenced file": "O arquivo não é referenciado pelo banco de dados"
,"Cannot update matching id": "Não é possível atualizar os itens com id coincidente"
,"Com error": "Erro de comunicação com o servidor" ,"Com error": "Erro de comunicação com o servidor"
,"Image open error": "Erro ao abrir a imagem" ,"Image open error": "Erro ao abrir a imagem"
,"Operation disabled": "Operação desativada por segurança" ,"Operation disabled": "Operação desativada por segurança"

View File

@ -11,25 +11,26 @@ use Vn\Lib\UserException;
class Upload extends Vn\Web\JsonRequest { class Upload extends Vn\Web\JsonRequest {
const PARAMS = [ const PARAMS = [
'name', 'name',
'schema' 'schema',
'updateMatching'
]; ];
function run($db) { function run($db) {
$util = new Util($this->app); $util = new Util($this->app);
$schema = $_REQUEST['schema']; $collection = $_REQUEST['schema'];
$name = $_REQUEST['name']; $name = $_REQUEST['name'];
// Checks schema // Checks schema
$info = $util->loadInfo($schema); $info = $util->loadInfo($collection);
if (!$info) if (!$info)
throw new UserException(s('Schema not exists')); throw new UserException(s('Collection not exists'));
// Checks file name // Checks file name
if (preg_match('/[^a-z0-9_]/', $_REQUEST['name']) !== 0) if (preg_match('/[^a-z0-9_]/', $name) !== 0)
throw new UserException(s('Bad file name')); throw new UserException(s('Bad file name'));
// Checks for file errors // Checks for file errors
@ -73,19 +74,35 @@ class Upload extends Vn\Web\JsonRequest {
if ($_FILES['image']['size'] > $maxSize * 1048576) if ($_FILES['image']['size'] > $maxSize * 1048576)
throw new UserException(sprintf(s('File size error'), $maxSize)); throw new UserException(sprintf(s('File size error'), $maxSize));
// Updates items with matching id, when option is checked
if ($_REQUEST['updateMatching'] === 'true') {
$schema = $db->quote($info['schema']);
$table = $db->quote($info['table']);
$column = $db->quote($info['column']);
$pk = $db->getRow ("SHOW INDEX FROM $schema.$table WHERE Key_name = 'PRIMARY'");
if (!pk) throw new UserException(s('Cannot update matching id'));
$pkColumn = $db->quote($pk['Column_name']);
$query = "UPDATE $schema.$table SET $column = #name WHERE $pkColumn = #name LIMIT 1";
$db->query ($query, ['name' => $name]);
}
// Resizes and saves the image // Resizes and saves the image
$tmpName = $_FILES['image']['tmp_name']; $tmpName = $_FILES['image']['tmp_name'];
$fileName = "{$name}.png"; $fileName = "{$name}.png";
$schemaPath = "{$util->dataDir}/$schema"; $collectionPath = "{$util->dataDir}/$collection";
$fullFile = "$schemaPath/full/$fileName"; $fullFile = "$collectionPath/full/$fileName";
$symbolicSrc = "../full/$fileName"; $symbolicSrc = "../full/$fileName";
$image = Image::create($tmpName); $image = Image::create($tmpName);
Image::resizeSave($image, $fullFile, $info['maxHeight'], $info['maxWidth']); Image::resizeSave($image, $fullFile, $info['maxHeight'], $info['maxWidth']);
foreach ($info['sizes'] as $size => $i) { foreach ($info['sizes'] as $size => $i) {
$dstFile = "$schemaPath/$size/$fileName"; $dstFile = "$collectionPath/$size/$fileName";
Image::resizeSave($image, $dstFile, $i['height'], $i['width'], $i['crop'], $symbolicSrc); Image::resizeSave($image, $dstFile, $i['height'], $i['width'], $i['crop'], $symbolicSrc);
} }