#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.

View File

@ -1,6 +1,8 @@
Images: Imatges
Collection: Col·lecció
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
Upload files: Pujar arxius
Waiting for upload: Esperant per pujar

View File

@ -1,6 +1,8 @@
Images: Images
Collection: Collection
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
Upload files: Upload files
Waiting for upload: Waiting for upload

View File

@ -1,6 +1,8 @@
Images: Imágenes
Collection: Colección
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
Upload files: Subir archivos
Waiting for upload: Esperando para subir

View File

@ -1,6 +1,8 @@
Images: Images
Collection: Collection
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
Upload files: Upload Files
Waiting for upload: En attente de télécharger

View File

@ -1,6 +1,8 @@
Images: Imagens
Collection: Coleção
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
Upload files: Fazer upload de arquivos
Waiting for upload: Esperando para enviar

View File

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

View File

@ -1,18 +1,15 @@
.photos
{
.photos {
padding: 1em;
}
.photos .box
{
.photos .box {
max-width: 25em;
padding: 2em;
}
/* Dropzone */
.photos .dropzone
{
.photos .dropzone {
background-color: white;
border-style: dashed;
border-radius: .4em;
@ -22,52 +19,71 @@
color: #666;
cursor: pointer;
}
.photos .dropzone.dragover
{
.photos .dropzone.dragover {
color: #CCC;
border-style: solid;
}
.photos input[type=file]
{
.photos input[type=file] {
display: none;
}
/* File list */
.photos .file-list
{
.photos .file-list {
margin-top: 1em;
}
.photos .file-list > div
{
.photos .file-list > div {
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-width: 2em;
vertical-align: middle;
margin: 0 1em;
}
.photos .file-list input
{
max-width: 10em;
.photos .file-list input {
flex: 1;
min-width: 0;
}
.photos .file-list .status
{
margin-left: .5em;
.photos .file-list .status {
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 */
.photos .footer
{
.photos .update-matching {
margin-top: 1.5em;
}
.photos .footer {
margin-top: 1.5em;
text-align: center;
}
.photos .footer > button
{
.photos .footer > button {
font-size: 1.2em;
margin-left: 1em;
}

View File

@ -31,6 +31,12 @@
name="image"
on-change="onFileChange"/>
<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">
<button class="thin" on-click="onClearClick">
<t>Clear all</t>

View File

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

View File

@ -7,9 +7,10 @@
,"File save error": "Failed to save the file: %s"
,"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 schema name": "Invalid schema name"
,"Schema not exists": "Schema does not exist"
,"Bad collection name": "Invalid collection name"
,"Collection not exists": "Collection does not exist"
,"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"
,"Image open error": "Error opening the image file"
,"Operation disabled": "Operation disabled for security"

View File

@ -7,9 +7,10 @@
,"File save error": "Error al guardar el fichero: %s"
,"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 schema name": "Nombre de esquema no válido"
,"Schema not exists": "El esquema no existe"
,"Bad collection name": "Nombre de colección no válido"
,"Collection not exists": "La colección no existe"
,"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"
,"Image open error": "Error al abrir el archivo de imagen"
,"Operation disabled": "Operación deshabilitada por seguridad"

View File

@ -7,9 +7,10 @@
,"File save error": "Erro ao salvar o arquivo: %s"
,"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 schema name": "Nome de esquema inválido"
,"Schema not exists": "Esquema não existe"
,"Bad collection name": "Nome de coleção inválido"
,"Collection not exists": "Coleção não existe"
,"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"
,"Image open error": "Erro ao abrir a imagem"
,"Operation disabled": "Operação desativada por segurança"

View File

@ -11,25 +11,26 @@ use Vn\Lib\UserException;
class Upload extends Vn\Web\JsonRequest {
const PARAMS = [
'name',
'schema'
'schema',
'updateMatching'
];
function run($db) {
$util = new Util($this->app);
$schema = $_REQUEST['schema'];
$collection = $_REQUEST['schema'];
$name = $_REQUEST['name'];
// Checks schema
$info = $util->loadInfo($schema);
$info = $util->loadInfo($collection);
if (!$info)
throw new UserException(s('Schema not exists'));
throw new UserException(s('Collection not exists'));
// 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'));
// Checks for file errors
@ -73,19 +74,35 @@ class Upload extends Vn\Web\JsonRequest {
if ($_FILES['image']['size'] > $maxSize * 1048576)
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
$tmpName = $_FILES['image']['tmp_name'];
$fileName = "{$name}.png";
$schemaPath = "{$util->dataDir}/$schema";
$fullFile = "$schemaPath/full/$fileName";
$collectionPath = "{$util->dataDir}/$collection";
$fullFile = "$collectionPath/full/$fileName";
$symbolicSrc = "../full/$fileName";
$image = Image::create($tmpName);
Image::resizeSave($image, $fullFile, $info['maxHeight'], $info['maxWidth']);
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);
}