From c950556cd6cab3d706f5e036cdc072e97b7daee5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Mart=C3=ADn=20Marrese?= Date: Wed, 3 Dec 2003 22:20:08 +0000 Subject: [PATCH] Interfaz de usuario casi completa. --- lib/YATTA/Archivo.php | 245 ++++++++++++++++++ lib/YATTA/Proceso.php | 25 +- script/generacion_base.sql | 1 + sistema/www/archivos.php | 162 +++++------- sistema/www/images/estado_1.gif | Bin 418 -> 702 bytes sistema/www/procesos.php | 2 - .../BandasHorarias_Resumen.pdf | Bin test/mmarre@mecon/Borrar.pdf | Bin 4282 -> 0 bytes 8 files changed, 336 insertions(+), 99 deletions(-) create mode 100644 lib/YATTA/Archivo.php rename test/{mmarre@mecon => }/BandasHorarias_Resumen.pdf (100%) delete mode 100755 test/mmarre@mecon/Borrar.pdf diff --git a/lib/YATTA/Archivo.php b/lib/YATTA/Archivo.php new file mode 100644 index 0000000..318cdee --- /dev/null +++ b/lib/YATTA/Archivo.php @@ -0,0 +1,245 @@ + +------------------------------------------------------------------------------- +$Id$ +-----------------------------------------------------------------------------*/ + +require_once 'MECON/DBO.php'; +require_once 'PEAR.php'; + + +define ('PATH', '/var/www/sistemas/yatta/test/'); + +/** + * Clase para el manejo de los archivos. + * + * @access public + */ +class YATTA_Archivo extends MECON_DBO { + + /** + * Identificador del proceso. + * + * @var int $id + * @access public + */ + var $id = null; + + /** + * Identificador del sistema en el cual se lanzo el proceso. + * + * @var int $id_sistema + * @access public + */ + var $id_sistema = null; + + /** + * Nombre del sistema al que pertenece. + * + * @var string $nombre_sistema + * @access public + */ + var $nombre_sistema = null; + + /** + * Descripcion del proceso. + * + * @var string $descripcion + * @access public + */ + var $descripcion = null; + + /** + * Identificador del usuario con el que se esta trabajando. + * + * @var string $usuario + * @access public + */ + var $usuario = null; + + /** + * Nombre del archivo para el download + * + * @var string $resultado + * @access public + */ + var $resultado = null; + + /** + * Nombre del archivo en el tacho + * + * @var string $archivo + * @access public + */ + var $archivo = null; + + /** + * Path del archivo + * + * @var string $path + * @access public + */ + var $path = null; + + /** + * Carga el objeto con los datos que se pasan por parametro. + * + * @param DB $db DB o DB_Result a usar para la carga. + * + * @return mixed + * @access public + */ + function cargar($db = null) + { + // Si es un resultado, obtengo los elemento. + if (is_a($db, 'db_result')) { + $this->predefinirAtributos(); + $res = $db->fetchRow(DB_FETCHMODE_ASSOC); + // Si hay error lo devuelve. + if (DB::isError($res)) { + return $res; + } + elseif (!$res) { + return false; + } + + $this->id = $res['id']; + $this->nombre_sistema = $res['nombre_sistema']; + $this->descripcion = $res['descripcion']; + $this->path = PATH; + $this->resultado = $res['resultado']; + $this->archivo = $res['archivo']; + + return true; + } + return false; + } + + /** + * Borra el objeto de una base de datos. + * + * @param DB $db Base de datos de donde borrar el objeto. + * + * @return mixed + * @access public + */ + function borrar($db = null) + { + trigger_error('Not implemented!', E_USER_WARNING); + //@TODO Solo permitir que el owner borre sus archivos. + //@TODO Borra el registro de la base. + //@TODO Borrar el archivo del tacho. + } + + /** + * Busca los datos en la base. + * + * @param DB $db Conexion a la base de datos. + * @param string $operador Indica como deben concatenarse las condiciones de busqueda + * @param string $orden Indica de que manera deben ordenarse los resultados de la busqueda + * + * @return mixed + * @access public + */ + function buscar($db = null, $operador = MECON_DBO_OR, $orden = null) + { + // Armo el WHERE. + $where = array(); + if (!is_null($this->id)) { + $where[] = 'p.id = '.$this->id; + } + if (!is_null($this->id_sistema)) { + $where[] = 'p.id_sistema = '. $this->id_sistema; + } + if (!is_null($this->nombre_sistema)) { + $where[] = 's.nombre_sistema LIKE '. $db->quote("%$this->nombre_sistema%"); + } + if (!is_null($this->descripcion)) { + $where[] = 'p.descripcion LIKE '. $db->quote("%$this->descripcion%"); + } + if (!is_null($this->resultado)) { + $where[] = 'p.resultado LIKE '. $db->quote("%$this->resultado%"); + } + if (!is_null($this->usuario)) { + $where[] = '(p.owner LIKE '. $db->quote("$this->usuario") .' OR '. + 'p.destinos LIKE '. $db->quote("%$this->usuario%") .')'; + } + if ($where) { + $where = 'WHERE p.status = 2 AND p.resultado is not null '. + 'AND p.id_sistema = s.id_sistema AND ('. join ("$operador + ", $where).') '; + } + else { + $where = 'WHERE p.owner = '. $db->quote("$this->owner"). + 'AND p.id_sistema = s.id_sistema'; + } + // Armo el ORDER BY. + if (is_string($orden)) { + $orden = array($orden); + } + if ($orden) { + $orden = 'ORDER BY '.join(',',$orden); + } + else { + $orden = ''; + } + + return $db->query( + "SELECT p.id AS id, p.id_sistema AS id_sistema, s.nombre_sistema ". + "AS nombre_sistema, p.descripcion AS descripcion, p.resultado ". + "AS resultado, p.archivo AS archivo ". + "FROM yatta.procesos AS p, samurai.sistema AS s ". + "$where ". + "$orden"); + } + + /** + * @param DB $db Conexion a la base de datos. + * @param bool $nuevo Indica si se trata de un nuevo registro en la base. + * + * @return mixed + * @access public + */ + function guardar($db = null, $nuevo = false) + { + trigger_error('Not implemented!', E_USER_WARNING); + } + + /** + * Hace un reset de los atributos. + * + * @return void + * @access public + */ + function predefinirAtributos() { + $this->id = null; + $this->id_sistema = null; + $this->nombre_sistema = null; + $this->descripcion = null; + $this->resultado = null; + $this->archivo = null; + $this->path = null; + $this->usuario = null; + } +} +?> \ No newline at end of file diff --git a/lib/YATTA/Proceso.php b/lib/YATTA/Proceso.php index df7310c..31fb122 100644 --- a/lib/YATTA/Proceso.php +++ b/lib/YATTA/Proceso.php @@ -105,10 +105,9 @@ class YATTA_Proceso extends MECON_DBO { * Estado del proceso. * 0 = En Espera * 1 = Procesando - * 2 = Finalizado sin copiar el resultado al tacho - * 3 = Finalizado - * 4 = Error - * 5 = Abortado + * 2 = Finalizado + * 3 = Error + * 4 = Abortado * * @var int $status * @access public @@ -164,6 +163,14 @@ class YATTA_Proceso extends MECON_DBO { */ var $resultado = null; + /** + * Nombre del archivo en el tacho + * + * @var string $archivo + * @access public + */ + var $archivo = null; + /** * Nota explicativa sobre el error producido o el motivo por el cual se * aborto el proceso. @@ -219,6 +226,7 @@ class YATTA_Proceso extends MECON_DBO { $this->scheduling = $res['scheduling']; $this->notificar = $res['notificar']; $this->resultado = $res['resultado']; + $this->archivo = $res['archivo']; $this->nota = $res['nota']; $this->icono = $res['icono']; return true; @@ -337,13 +345,15 @@ class YATTA_Proceso extends MECON_DBO { $orden = ''; } - return $db->query("SELECT p.id AS id, p.fecha AS fecha, p.script AS + return $db->query("SELECT p.id AS id, + DATE_FORMAT(p.fecha, '%d-%m-%Y') AS fecha, + p.script AS script, p.id_sistema AS id_sistema, s.nombre_sistema AS nombre_sistema, p.descripcion AS descripcion, p.pid AS pid, p.server AS server, p.status AS status, p.owner AS owner, p.destinos AS destinos, p.prioridad AS prioridad, p.scheduling AS scheduling, p.notificar AS notificar, p.resultado AS - resultado, p.nota AS nota,". + resultado, p.nota AS nota, p.archivo AS archivo, ". "IF (p.status = 0, 'El proceso esta en cola. Aun no se ejecuto.". "', ". "IF(p.status = 1, 'El proceso se esta ejecutando en este momento.". @@ -389,6 +399,7 @@ class YATTA_Proceso extends MECON_DBO { 'scheduling' => $this->scheduling, 'notificar' => $this->notificar, 'resultado' => $this->resultado, + 'archivo' => $this->archivo, 'nota' => $this->nota ); @@ -410,6 +421,7 @@ class YATTA_Proceso extends MECON_DBO { 'scheduling' => $this->scheduling, 'notificar' => $this->notificar, 'resultado' => $this->resultado, + 'archivo' => $this->archivo, 'nota' => $this->nota ); @@ -442,6 +454,7 @@ class YATTA_Proceso extends MECON_DBO { $this->scheduling = null; $this->notificar = null; $this->resultado = null; + $this->archivo = null; $this->nota = null; $this->icono = null; } diff --git a/script/generacion_base.sql b/script/generacion_base.sql index f63620a..67a8c2a 100644 --- a/script/generacion_base.sql +++ b/script/generacion_base.sql @@ -25,6 +25,7 @@ CREATE TABLE procesos ( scheduling varchar(50) default NULL, notificar tinyint(1) default 1, resultado varchar(255) default NULL, + archivo varchar(255) default NULL, nota text NOT NULL, PRIMARY KEY (id) ) TYPE=MyISAM; diff --git a/sistema/www/archivos.php b/sistema/www/archivos.php index 695cccb..e64bf19 100644 --- a/sistema/www/archivos.php +++ b/sistema/www/archivos.php @@ -26,130 +26,110 @@ $Id$ //Require Once {{{ require_once 'HTML/Table.php'; -require_once 'MECON/HTML/Tabla.php'; +require_once 'MECON/HTML/TablaDB.php'; require_once 'MECON/HTML/Image.php'; require_once 'MECON/HTML/Link.php'; +require_once 'YATTA/Archivo.php'; //}}} -//TODO Este path es el del tacho segun el usuario, etc. -$path = '/var/www/sistemas/yatta/test/'. $_SESSION['usuario']; - //TODO Definir la cuota en algun lado (Kb) $cuota = 2048; -//TODO Agregar class para manejar los colores y fuentes - //Actuar segun la accion, etc {{{ $accion = @$_REQUEST['accion']; -$archivo = @$_REQUEST['archivo']; -if (@$accion && @$archivo) { - $archivo2 = $path.'/'.$archivo; +$id = @$_REQUEST['_id']; +if (@$id && @$accion) { + $ARCHIVO =& new YATTA_Archivo; + $ARCHIVO->id = $id; + $res = $ARCHIVO->buscar($DB); + if (PEAR::isError($res)) { + die('Error: ' . $res->getMessage() . "\n"); + } + $ARCHIVO->cargar($res); switch ($accion) { case 'download': - header("Content-Disposition: attachment; filename=".$archivo); + header("Content-Disposition: attachment; + filename=".$ARCHIVO->resultado); header("Pragma: no-cache"); header("Expires: 0"); header("Content-Type: application"); - header("Content-Length: ".filesize($archivo2)); - readfile ($archivo2); + header("Content-Length: ".filesize($ARCHIVO->path.$ARCHIVO->archivo)); + readfile ($ARCHIVO->path.$ARCHIVO->archivo); exit; break; case 'borrar': - system ('rm '.$archivo2); + system ('rm '.$ARCHIVO->path.$ARCHIVO->archivo); header ('Location:archivos'); break; } } //}}} -//Obtengo la Lista de Archivos del Directorio en Cuestion {{{ -if ($dh = @opendir($path)) { - $ocupado =0; - while (($file = readdir($dh)) !== false) { - //FIXME Sacar el .svn cuando se pase a produccion - if ($file != '.' && $file != '..' && $file != '.svn') { - $archivos[$file]['ext'] = strtoupper(substr($file, strrpos($file, '.') + 1)); - $archivos[$file]['nombre'] = $file; - $archivos[$file]['tam'] = round((filesize($path.'/'.$file) / 1024),2); - $archivos[$file]['porc'] = round((filesize($path.'/'.$file) / 1024) * 100 / - $cuota, 2); - - $ocupado += $archivos[$file]['porc']; - } - } - closedir($dh); +//Obtengo la lista de archivos y lo agrego a la tabla {{{ +$ARCHIVO =& new YATTA_Archivo; +$ARCHIVO->usuario = $_SESSION['usuario']; + +$res = $ARCHIVO->buscar($DB); +if (PEAR::isError($res)) { + die('Error: ' . $res->getMessage() . "\n"); } -//}}} -//Tabla de Archivos {{{ -$TABLA_2 =& new MECON_HTML_Tabla (); -$TABLA_2->addRow(array( + +$TABLADB = new MECON_HTML_TablaDB ('Archivos'); +$TABLADB->addRow(array( 'Lista de Archivos Disponibles' - ), 'cabecera colspan="6" align="left"'); -$TABLA_2->addRow(array( - 'Ext', 'Nombre', 'Tam. Kb', 'Tam. %', 'Bajar', 'Borrar' + ), 'cabecera colspan="5" align="left"'); +$TABLADB->addRow(array( + 'Ext', 'Sistema', 'Descripcion', 'Bajar', 'Borrar' ), 'Titulo'); -if (@$archivos) { - asort($archivos); - foreach ($archivos as $arch) { - $TABLA_2->addRow(array( - new MECON_HTML_Image ('/MECON/images/EXTENSION_'.$arch['ext'].'.gif', $arch['ext']), - $arch['nombre'], - $arch['tam'].' Kb', - $arch['porc'].' %', - new MECON_HTML_Link ('archivos', new - MECON_HTML_Image('/MECON/images/general_download.gif', 'Bajar'), - array ( - 'accion'=>'download', - 'archivo'=>$arch['nombre'], - )), - new MECON_HTML_Link ('archivos', new MECON_HTML_Image - ('/MECON/images/general_eliminar.gif', 'Eliminar'), - array ( - 'accion'=>'borrar', - 'archivo'=>$arch['nombre']) - , 'OnClick="return confirm(\'Esta Seguro Bestia?\')"')) - ); - } - $TABLA_2->addRow(array( - '*Recuerde que los archivos se borran a los 7 dias - de antiguedad' - ), 'colspan="6" align="center" class="msg_rojo"'); -} -else { - $TABLA_2->addRow(array('No se encontraron archivos'), 'colspan="6" - class="msg_rojo"'); -} -$TABLA_2->updateColAttributes(0,'width="5%"'); -$TABLA_2->updateColAttributes(2,'width="10%"'); -$TABLA_2->updateColAttributes(3,'width="10%"'); -$TABLA_2->updateColAttributes(4,'width="8%"'); -$TABLA_2->updateColAttributes(5,'width="8%"'); -//}}} -//Tabla de Espacio Disponible {{{ -$TABLA_1 =& new HTML_Table ('width="100%"'); -$TABLA_1->addRow(array( - 'Espacio Utilizado: ', - (@$ocupado) ? $ocupado.' %' : '0'.' %' - )); -$TABLA_1->updateColAttributes(0,'width="95%" align="right"'); -$TABLA_1->updateColAttributes(1,'align="center"'); -if (@$ocupado > 80) { - $TABLA_1->updateCellAttributes(0,1,'class="msg_rojo"'); -} -else { - $TABLA_1->updateCellAttributes(0,1,'class="msg_negro"'); + +$TABLADB->addRowsData( + 'Ext', + array (array ('resultado', 'extension_callback')), + 'prepend' + ); + +function extension_callback($resultado) { + return strtoupper(substr($resultado, strrpos($resultado, '.') + 1)); } -$TABLA_1->updateCellAttributes(0,0,'class="msg_negro"'); + +//@TODO Hacer un addRowsData que agregue el tamanio del archivo (posiblemente +//con una funcion callback) + +$TABLADB->addRowsData( + new MECON_HTML_Link('archivos', + new MECON_HTML_Image('/MECON/images/general_download.gif', + 'Bajar'), + array ($TABLADB->getGetVarPrefix().'id' => "%s", + 'accion' => 'download') + ) + ); + +$TABLADB->addRowsIcon('borrar', array ('id'), new MECON_HTML_Link ('archivos', '', array + ('accion' => 'borrar'))); + +$pager = $TABLADB->addPager($res, null, + new MECON_HTML_Link + ('archivos', null + ) + ); + +$TABLADB->addRows($pager, array ('nombre_sistema', 'descripcion')); +$TABLADB->addRow(array('*Recuerde que los archivos se borran a los 7 dias + de antiguedad'), + 'colspan="5" align="center" class="msg_rojo"'); + +$TABLADB->updateColAttributes(0,'width="4%"'); +$TABLADB->updateColAttributes(1,'width="30%"'); +$TABLADB->updateColAttributes(2,'width="50%"'); +$TABLADB->updateColAttributes(3,'width="8%"'); +$TABLADB->updateColAttributes(4,'width="8%"'); //}}} //Agrego la info al marco y la muestro {{{ -$MARCO->addBody($TABLA_1); -$MARCO->addBody('
'); -$MARCO->addBody($TABLA_2); $MARCO->addStyleSheet('css/yatta.css'); +$MARCO->addBody($TABLADB); $MARCO->display(); //}}} - ?> diff --git a/sistema/www/images/estado_1.gif b/sistema/www/images/estado_1.gif index 1949a875195e9125700be49815d3163316c37573..8f5f2bb25d299de4a05d62e2150255b0cbc953dd 100644 GIT binary patch delta 47 pcmZ3)ypMImBF2debtZ0*MMVMvix?Rg;(=JdM8H8Q?ce?h_5i-N5nBKN delta 7 OcmdnTx`=thB1QlU;sU$? diff --git a/sistema/www/procesos.php b/sistema/www/procesos.php index 8e8181a..0ec8b8f 100644 --- a/sistema/www/procesos.php +++ b/sistema/www/procesos.php @@ -48,8 +48,6 @@ if (PEAR::isError($res)) { die('Error: ' . $res->getMessage() . "\n"); } -//@FIXME La fecha esta quedando en formato americano, arreglar eso. - $pager = $TABLADB->addPager($res, null, new MECON_HTML_Link ('procesos', null diff --git a/test/mmarre@mecon/BandasHorarias_Resumen.pdf b/test/BandasHorarias_Resumen.pdf similarity index 100% rename from test/mmarre@mecon/BandasHorarias_Resumen.pdf rename to test/BandasHorarias_Resumen.pdf diff --git a/test/mmarre@mecon/Borrar.pdf b/test/mmarre@mecon/Borrar.pdf deleted file mode 100755 index 21493c861310361f1da8fd7b2ce7f93f96095b4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4282 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 QETA