Upload files

This commit is contained in:
2021-12-22 01:38:34 -03:00
parent fddba2fb87
commit 93f77bfbb8
9 changed files with 275 additions and 20 deletions

View File

@ -1,5 +1,9 @@
FROM php:8-fpm
RUN apt-get update -y && apt-get install -y git libzip-dev zip
RUN docker-php-ext-install zip
COPY --from=composer /usr/bin/composer /usr/bin/composer
WORKDIR /app

View File

@ -0,0 +1,24 @@
<?php
namespace Contabilidad\Common\Controller;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Slim\Views\Blade as View;
use GuzzleHttp\Client;
class Uploads {
public function __invoke(Request $request, Response $response, View $view): Response {
return $view->render($response, 'uploads.list');
}
public function get(Request $request, Response $response, Client $client, $folder, $filename): Response {
$resp = $client->get(implode('/', ['upload', $folder, $filename]));
$file = $resp->getBody();
return $response
->withHeader('Content-Type', $resp->getHeader('Content-Type'))
->withHeader('Content-Disposition', 'attachment; filename=' . $filename)
->withAddedHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->withHeader('Cache-Control', 'post-check=0, pre-check=0')
->withHeader('Pragma', 'no-cache')
->withBody($file);
}
}

View File

@ -8,7 +8,8 @@
"rubellum/slim-blade-view": "^0.1.1",
"nyholm/psr7-server": "^1.0",
"zeuxisoo/slim-whoops": "^0.7.3",
"nyholm/psr7": "^1.4"
"nyholm/psr7": "^1.4",
"guzzlehttp/guzzle": "^7.4"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
@ -24,11 +25,5 @@
"psr-4": {
"Contabilidad\\Common\\": "common"
}
},
"repositories": [
{
"type": "git",
"url": "http://git.provm.cl/ProVM/controller.git"
}
]
}
}

View File

@ -0,0 +1,210 @@
class Archivo {
constructor({folder, filename}) {
this.folder = folder
this.filename = filename
this.modal = null
}
setModal(modal) {
this.modal = modal
return this
}
draw() {
return $('<tr></tr>').append(
$('<td></td>').append(
$('<a></a>').attr('class', 'item').attr('href', _urls.base + ['upload', this.folder, this.filename].join('/')).html(this.filename)
)
).append(
$('<td></td>').attr('class', 'right aligned').append(
$('<button></button>').attr('class', 'ui mini circular icon button').append(
$('<i></i>').attr('class', 'edit icon')
).click((e) => {
e.preventDefault()
const t = e.currentTarget
this.edit()
return false
})
).append(
$('<button></button>').attr('class', 'ui mini red circular icon button').append(
$('<i></i>').attr('class', 'remove icon')
).click((e) => {
e.preventDefault()
const t = e.currentTarget
this.remove()
return false
})
)
)
}
edit() {
this.modal.find('form').trigger('reset')
this.modal.find('form').find("[name='folder']").val(this.folder)
this.modal.find('form').find("[name='old_filename']").val(this.filename)
this.modal.find('form').find("[name='filename']").val(this.filename)
this.modal.modal('show')
}
remove() {
return sendDelete([_urls.api, 'upload', this.folder, this.filename].join('/')).then((data) => {
if (data.deleted) {
archivos.get()
}
})
}
}
const archivos = {
id: '#archivos',
archivos: [],
modals: {
add: null,
edit: null
},
get: function() {
return {
parent: () => {
let parent = $(this.id).find('tbody')
if (parent.length === 0) {
const table = $('<table></table>').attr('class', 'ui striped table').append(
$('<thead></thead>').append(
$('<tr></tr>').append(
$('<th></th>').html('Archivo')
).append(
$('<th></th>').attr('class', 'right aligned').append(
$('<button></button>').attr('class', 'ui tiny green circular icon button').append(
$('<i></i>').attr('class', 'plus icon')
).click((e) => {
e.preventDefault()
this.add()
return false
})
)
)
)
)
parent = $('<tbody></tbody>')
table.append(parent)
$(this.id).append(table)
}
return parent
},
archivos: () => {
return sendGet(_urls.api + '/uploads').then((data) => {
if (data.files === null || data.files.length === 0) {
return
}
$.each(data.files, (i, el) => {
const arch = new Archivo(el)
arch.setModal(this.modals.edit)
this.archivos.push(arch)
})
}).then(() => {
this.draw()
})
},
cuentas: () => {
return sendGet(_urls.api + '/cuentas')
}
}
},
draw: function() {
const tbody = this.get().parent()
tbody.html('')
$.each(this.archivos, (i, el) => {
tbody.append(el.draw())
})
},
add: function() {
this.modals.add.find('form').trigger('reset')
this.modals.add.modal('show')
},
doAdd: function() {
const data = new FormData(this.modals.add.find('form'))
return sendPost(_urls.api + '/categorias/add', data, true).then((resp) => {
this.modals.add.modal('hide')
this.getCategorias()
})
},
doEdit: function() {
const folder = this.modals.edit.find("[name='folder']").val()
const filename = this.modals.edit.find("[name='old_filename']").val()
const data = JSON.stringify({
cuenta: this.modals.edit.find("[name='cuenta']").val(),
fecha: this.modals.edit.find("[name='fecha']").val()
})
sendPut([_urls.api, 'upload', folder, filename].join('/'), data).then((resp) => {
this.modals.edit.modal('hide')
if (resp.edited) {
this.get().archivos()
}
})
},
updateCalendar: function(modal) {
const today = new Date()
const start = new Date(today.getFullYear(), today.getMonth() - 1)
modal.find('.ui.calendar').calendar({
type: 'month',
initialDate: start,
maxDate: start,
months: ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'],
monthsShort: ['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Oct', 'Nov', 'Dic'],
formatter: {
date: function(date, settings) {
if (!date) return ''
const year = date.getFullYear()
const month = date.getMonth() + 1
return [year, month].join('-')
}
}
})
},
updateCuentas: function(modal, data) {
if (data.cuentas === null || data.cuentas.length === 0) {
return
}
const select = modal.find("select[name='cuenta']")
let values = []
$.each(data.cuentas, (i, el) => {
const nombre = [el.nombre, el.categoria.nombre].join(' - ')
values.push({
name: nombre,
value: el.id,
text: nombre
})
})
select.dropdown({values})
},
setupModal: function() {
this.modals.add = $('#add_modal')
this.modals.edit = $('#edit_modal')
$.each(this.modals, (i, el) => {
el.modal().find('.close.icon').click(() => {
el.modal('hide')
})
this.updateCalendar(el)
})
this.modals.add.find('form').submit((e) => {
e.preventDefault()
this.doAdd()
return false
})
this.modals.add.find('#archivo_btn').css('cursor', 'pointer').click(() => {
this.modals.add.find("[name='archivo']").trigger('click')
})
this.modals.add.find("[name='archivo']").change((e) => {
const arch = $(e.currentTarget)
const filename = arch[0].files[0].name
this.modals.add.find('#archivo_btn').find('input').val(filename)
})
this.modals.edit.find('form').submit((e) => {
e.preventDefault()
this.doEdit()
return false
})
this.get().cuentas().then((data) => {
this.updateCuentas(this.modals.add, data)
this.updateCuentas(this.modals.edit, data)
})
},
setup: function() {
this.setupModal()
this.get().archivos()
}
}

View File

@ -0,0 +1,10 @@
<?php
use Contabilidad\Common\Controller\Uploads;
$app->group('/uploads', function($app) {
$app->get('/add', [Uploads::class, 'upload']);
$app->get('[/]', Uploads::class);
});
$app->group('/upload/{folder}/{filename}', function($app) {
$app->get('[/]', [Uploads::class, 'get']);
});

View File

@ -1,4 +1,5 @@
<div class="ui vertical fluid borderless menu">
@include('config.menu.tipos_categorias')
@include('config.menu.tipos_cuentas')
@include('config.menu.files')
</div>

View File

@ -0,0 +1,4 @@
<a class="item" href="{{$urls->base}}uploads">
Archivos
</a>

View File

@ -2,7 +2,6 @@
<a class="item" href="{{$urls->base}}">Inicio</a>
@include('layout.body.menu.cuentas')
@include('layout.body.menu.categorias')
<a class="item" href="{{$urls->base}}importar">Importar</a>
<div class="right menu">
<a class="item" href="{{$urls->base}}config">Config</a>
</div>

View File

@ -2,15 +2,23 @@
use Psr\Container\ContainerInterface as Container;
return [
Slim\Views\Blade::class => function(Container $c) {
return new Slim\Views\Blade(
$c->get('folders')->templates,
$c->get('folders')->cache,
null,
[
'api_key' => $c->get('API_KEY'),
'urls' => $c->get('urls')
]
);
}
Slim\Views\Blade::class => function(Container $c) {
return new Slim\Views\Blade(
$c->get('folders')->templates,
$c->get('folders')->cache,
null,
[
'api_key' => $c->get('API_KEY'),
'urls' => $c->get('urls')
]
);
},
GuzzleHttp\Client::class => function(Container $c) {
return new GuzzleHttp\Client([
'base_uri' => 'http://api-proxy',
'headers' => [
'Authorization' => 'Bearer ' . $c->get('API_KEY')
]
]);
}
];