From 736f2b3e4214564dd696f54c1aac1e161ab5d462 Mon Sep 17 00:00:00 2001 From: roberto Date: Wed, 26 Sep 2012 11:07:00 +0000 Subject: [PATCH] Tarea #1085 -> Falta la foto en el perfil del agente git-svn-id: https://192.168.0.254/svn/Proyectos.Incam_PROFIND_Web/trunk@31 3fe1ab16-cfe0-e34b-8c9f-7d8c168d430d --- www/images/user_photo.jpg | Bin 0 -> 12198 bytes .../controllers/UsuarioController.php | 24 ++- .../helpers/recursive_remove_directory.php | 46 ++++++ www/protected/models/Usuario.php | 141 +++++++++++++----- www/protected/models/UsuarioFotografia.php | 97 ++++++++++++ www/themes/profind/js/bootstrap-fileupload.js | 126 ++++++++++++++++ www/themes/profind/views/layouts/main.php | 2 +- www/themes/profind/views/usuario/_form.php | 47 +++++- 8 files changed, 433 insertions(+), 50 deletions(-) create mode 100644 www/images/user_photo.jpg create mode 100644 www/protected/helpers/recursive_remove_directory.php create mode 100644 www/protected/models/UsuarioFotografia.php create mode 100644 www/themes/profind/js/bootstrap-fileupload.js diff --git a/www/images/user_photo.jpg b/www/images/user_photo.jpg new file mode 100644 index 0000000000000000000000000000000000000000..af73630cbfbc4bc20ecf8cd146e052756a443a21 GIT binary patch literal 12198 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 PEx3us<`FS>Sl95MmHu}E literal 0 HcmV?d00001 diff --git a/www/protected/controllers/UsuarioController.php b/www/protected/controllers/UsuarioController.php index 42f8dc1..7762917 100644 --- a/www/protected/controllers/UsuarioController.php +++ b/www/protected/controllers/UsuarioController.php @@ -39,21 +39,30 @@ class UsuarioController extends Controller { if ($id != Yii::app()->user->id) throw new CHttpException(404, Yii::t('profind', 'La página solicitada no existe.')); - $model = $this->loadModel($id); - + $usuario = $this->loadModel($id); + // Uncomment the following line if AJAX validation is needed - // $this->performAjaxValidation($model); + // $this->performAjaxValidation($usuario); if (isset($_POST['Usuario'])) { - $model->attributes = $_POST['Usuario']; - if ($model->save()) { + $usuario->attributes = $_POST['Usuario']; + $ficheroFotografia = CUploadedFile::getInstance($usuario, 'ficheroFotografia'); + $quitarFotografia = Yii::app()->request->getParam('quitar_fotografia', '0'); + + if ($usuario->save()) { + if (($quitarFotografia == '1') && ($usuario->fotografia->tieneFotografia())) + $usuario->fotografia->eliminarFotografia(); + + if ($ficheroFotografia) + $usuario->fotografia->guardarFotografia($ficheroFotografia); + Yii::app()->user->setFlash('success', Yii::t('profind', 'Se ha actualizado el perfil')); - $this->redirect(array('modificar', 'id' => $model->id)); + $this->redirect(array('modificar', 'id' => $usuario->id)); } } $this->render('modificar', array( - 'model' => $model, + 'model' => $usuario, )); } @@ -66,6 +75,7 @@ class UsuarioController extends Controller { $model = Usuario::model()->findByPk($id); if ($model === null) throw new CHttpException(404, Yii::t('profind', 'La página solicitada no existe.')); + return $model; } diff --git a/www/protected/helpers/recursive_remove_directory.php b/www/protected/helpers/recursive_remove_directory.php new file mode 100644 index 0000000..daa4410 --- /dev/null +++ b/www/protected/helpers/recursive_remove_directory.php @@ -0,0 +1,46 @@ + \ No newline at end of file diff --git a/www/protected/models/Usuario.php b/www/protected/models/Usuario.php index 7a80d50..5820e0b 100644 --- a/www/protected/models/Usuario.php +++ b/www/protected/models/Usuario.php @@ -1,9 +1,9 @@ estado == self::ESTADO_ACTIVO); } - + /** * Returns the static model of the specified AR class. * @param string $className active record class name. @@ -43,7 +45,7 @@ class Usuario extends CActiveRecord { public static function model($className = __CLASS__) { return parent::model($className); } - + /** * @return string the associated database table name */ @@ -54,22 +56,27 @@ class Usuario extends CActiveRecord { /** * @return array validation rules for model attributes. */ - public function rules() - { + public function rules() { // NOTE: you should only define rules for those attributes that // will receive user inputs. return array( array('email, password', 'required'), - array('estado', 'length', 'max'=>1), + array('estado', 'length', 'max' => 1), array('email', 'email'), array('email', 'unique'), array('descripcion', 'safe'), - array('email, nombre, apellidos, password, tipo, titulo, localidad, telefono', 'length', 'max'=>255), - - array('id, id_empresa, estado, email, nombre, apellidos, tipo, titulo, localidad, telefono, descripcion', 'safe', 'on'=>'search'), + array('email, nombre, apellidos, password, tipo, titulo, localidad, telefono', 'length', 'max' => 255), + array('ficheroFotografia', 'file', + 'types' => 'jpg', + 'maxSize' => 1024 * 1024 * 1, // 1MB como máximo + 'tooLarge' => Yii::t('profind', 'La imagen es demasiado pesada. Elija otra fotografía más pequeña.'), + 'wrongType' => Yii::t('profind', 'Sólo se permiten imágenes en formato JPG.'), + 'allowEmpty' => 'true', + ), + array('id, id_empresa, estado, email, nombre, apellidos, tipo, titulo, localidad, telefono, descripcion', 'safe', 'on' => 'search'), ); - } - + } + /** * @return array relational rules. */ @@ -77,16 +84,17 @@ class Usuario extends CActiveRecord { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( - 'empresa' => array(self::HAS_ONE, 'Empresa', 'id'), + 'empresa' => array(self::HAS_ONE, 'Empresa', 'id'), ); } - + /** * @return array customized attribute labels (name=>label) */ public function attributeLabels() { return array( 'id' => 'ID', + 'ficheroFotografia' => 'Fotografía', 'estado' => 'Estado', 'email' => 'Email', 'nombre' => 'Nombre', @@ -97,37 +105,98 @@ class Usuario extends CActiveRecord { 'localidad' => 'Localidad', 'telefono' => 'Teléfono', 'descripcion' => 'Descripción', - ); + ); } /** * Retrieves a list of models based on the current search/filter conditions. * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions. */ - public function search() - { - $criteria=new CDbCriteria; + public function search() { + $criteria = new CDbCriteria; - $criteria->compare('id',$this->id); - $criteria->compare('id_empresa',$this->id_empresa); - $criteria->compare('estado',$this->estado,true); - $criteria->compare('email',$this->email,true); - $criteria->compare('nombre',$this->nombre,true); - $criteria->compare('apellidos',$this->apellidos,true); - $criteria->compare('tipo',$this->tipo,true); - $criteria->compare('titulo',$this->titulo,true); - $criteria->compare('localidad',$this->localidad,true); - $criteria->compare('telefono',$this->telefono,true); - $criteria->compare('last_login_time',$this->last_login_time,true); - $criteria->compare('descripcion',$this->descripcion,true); + $criteria->compare('id', $this->id); + $criteria->compare('id_empresa', $this->id_empresa); + $criteria->compare('estado', $this->estado, true); + $criteria->compare('email', $this->email, true); + $criteria->compare('nombre', $this->nombre, true); + $criteria->compare('apellidos', $this->apellidos, true); + $criteria->compare('tipo', $this->tipo, true); + $criteria->compare('titulo', $this->titulo, true); + $criteria->compare('localidad', $this->localidad, true); + $criteria->compare('telefono', $this->telefono, true); + $criteria->compare('last_login_time', $this->last_login_time, true); + $criteria->compare('descripcion', $this->descripcion, true); return new CActiveDataProvider($this, array( - 'criteria'=>$criteria, - )); + 'criteria' => $criteria, + )); + } + + protected function afterFind() { + parent::afterFind(); + $this->fotografia = new UsuarioFotografia(); + $this->fotografia->usuario = $this; } + protected function afterConstruct() { + parent::afterConstruct(); + $this->fotografia = new UsuarioFotografia(); + $this->fotografia->usuario = $this; + } + + protected function afterSave() { + parent::afterSave(); + if ($this->isNewRecord) + $this->createUploadDir(); + } + + protected function afterDelete() { + parent::afterDelete(); + $this->deleteUploadDir(); + } + + /* + * Devuelve la ruta con los ficheros del usuario. + * Incluye el separador de directorios al final de la ruta. + * @return string ruta + */ + public function getUploadPath() { + return Yii::getPathOfAlias('application.uploads') . DIRECTORY_SEPARATOR . $this->id . DIRECTORY_SEPARATOR; + } + + /* + * Crea un directorio para almacenar ficheros del usuario + * @return boolean + */ + private function createUploadDir() { + $upload = $this->getUploadPath(); + + if(!is_dir($upload)) { + return mkdir($upload); + } + else return false; + } + + /* + * Elimina el directorio del usuario y todos sus ficheros + * @return boolean + */ + private function deleteUploadDir() { + $upload = $this->getUploadPath(); + + if(is_dir($upload)) { + require_once( Yii::getPathOfAlias('application.helpers') . DIRECTORY_SEPARATOR . 'recursive_remove_directory.php'); + return recursive_remove_directory($upload); + } + else return false; + } + + /* + * Función para cifrar la contraseña del usuario (temporal) + * @return string contraseña cifrada + */ public function encrypt($value) { return md5($value); } - } diff --git a/www/protected/models/UsuarioFotografia.php b/www/protected/models/UsuarioFotografia.php new file mode 100644 index 0000000..fdee73e --- /dev/null +++ b/www/protected/models/UsuarioFotografia.php @@ -0,0 +1,97 @@ +usuario) + throw new CException(Yii::t('profind', 'Usuario no asignado.')); + + if ($this->tieneFotografia()) { + $fichero = $this->getRutaCompletaFicheroFotografia(); + } else { + $fichero = Yii::getPathOfAlias('webroot') . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR; + $fichero .= 'user_photo.jpg'; + } + $imgdata = base64_encode(file_get_contents($fichero)); + return 'data:image/jpeg;base64,' . $imgdata; + } + + /* + * Comprueba si el usuario ha subido su fotografía + * @return boolean + */ + public function tieneFotografia() { + if (!$this->usuario) + throw new CException(Yii::t('profind', 'Usuario no asignado.')); + + $fichero = $this->getRutaCompletaFicheroFotografia(); + return file_exists($fichero); + } + + /* + * Devuelve la ruta completa (ruta y nombre) del fichero con la fotografía del usuario. + * @return string ruta completa del fichero + */ + private function getRutaCompletaFicheroFotografia() { + if (!$this->usuario) + throw new CException(Yii::t('profind', 'Usuario no asignado.')); + + $upload = $this->usuario->getUploadPath(); + $nombre = $this->getNombreFicheroFotografia(); + + return $upload . $nombre; + } + + /* + * Devuelve el nombre del fichero con la fotografía del usuario. + * @return string nombre del fichero + */ + private function getNombreFicheroFotografia() { + if (!$this->usuario) + throw new CException(Yii::t('profind', 'Usuario no asignado.')); + + return $this->usuario->id . '.jpg'; + } + + /* + * Guarda una fotografía subida por el usuario + * return CUploadedFile fichero subido + */ + + public function guardarFotografia($fichero) { + if (!$this->usuario) + throw new CException(Yii::t('profind', 'Usuario no asignado.')); + + if ($fichero) { + $nombre = $this->getRutaCompletaFicheroFotografia(); + return $fichero->saveAs($nombre); + } + else + return false; + } + + /* + * Elimina la fotografía del usuario + * return bool + */ + + public function eliminarFotografia() { + if (!$this->usuario) + throw new CException(Yii::t('profind', 'Usuario no asignado.')); + + $fichero = $this->getRutaCompletaFicheroFotografia(); + return unlink($fichero); + } +} + +?> diff --git a/www/themes/profind/js/bootstrap-fileupload.js b/www/themes/profind/js/bootstrap-fileupload.js new file mode 100644 index 0000000..17cbc3c --- /dev/null +++ b/www/themes/profind/js/bootstrap-fileupload.js @@ -0,0 +1,126 @@ +/* =========================================================== + * bootstrap-fileupload.js j1a + * http://jasny.github.com/bootstrap/javascript.html#fileupload + * =========================================================== + * Copyright 2012 Jasny BV, Netherlands. + * + * Licensed under the Apache License, Version 2.0 (the "License") + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ========================================================== */ + +!function ($) { + + "use strict"; // jshint ;_ + + /* INPUTMASK PUBLIC CLASS DEFINITION + * ================================= */ + + var Fileupload = function (element, options) { + this.$element = $(element) + this.type = this.$element.data('uploadtype') || (this.$element.find('.thumbnail').length > 0 ? "image" : "file") + + this.$input = this.$element.find(':file') + if (this.$input.length === 0) return + + this.name = this.$input.attr('name') || options.name + + this.$hidden = this.$element.find(':hidden[name="'+this.name+'"]') + if (this.$hidden.length === 0) { + this.$hidden = $('') + this.$element.prepend(this.$hidden) + } + + this.$preview = this.$element.find('.fileupload-preview') + var height = this.$preview.css('height') + if (this.$preview.css('display') != 'inline' && height != '0px' && height != 'none') this.$preview.css('line-height', height) + + this.$remove = this.$element.find('[data-dismiss="fileupload"]') + + this.listen() + } + + Fileupload.prototype = { + + listen: function() { + this.$input.on('change.fileupload', $.proxy(this.change, this)) + if (this.$remove) this.$remove.on('click.fileupload', $.proxy(this.clear, this)) + }, + + change: function(e, invoked) { + var file = e.target.files !== undefined ? e.target.files[0] : { name: e.target.value.replace(/^.+\\/, '') } + if (!file || invoked === 'clear') return + + this.$hidden.val('') + this.$hidden.attr('name', '') + this.$input.attr('name', this.name) + + if (this.type === "image" && this.$preview.length > 0 && (typeof file.type !== "undefined" ? file.type.match('image.*') : file.name.match('\\.(gif|png|jpe?g)$')) && typeof FileReader !== "undefined") { + var reader = new FileReader() + var preview = this.$preview + var element = this.$element + + reader.onload = function(e) { + preview.html('') + element.addClass('fileupload-exists').removeClass('fileupload-new') + } + + reader.readAsDataURL(file) + } else { + this.$preview.text(file.name) + this.$element.addClass('fileupload-exists').removeClass('fileupload-new') + } + }, + + clear: function(e) { + this.$hidden.val('') + this.$hidden.attr('name', this.name) + this.$input.attr('name', '') + + this.$preview.html('') + this.$element.addClass('fileupload-new').removeClass('fileupload-exists') + + this.$input.trigger('change', [ 'clear' ]) + + e.preventDefault() + return false + } + } + + + /* INPUTMASK PLUGIN DEFINITION + * =========================== */ + + $.fn.fileupload = function (options) { + return this.each(function () { + var $this = $(this) + , data = $this.data('fileupload') + if (!data) $this.data('fileupload', (data = new Fileupload(this, options))) + }) + } + + $.fn.fileupload.Constructor = Fileupload + + + /* INPUTMASK DATA-API + * ================== */ + + $(function () { + $('body').on('click.fileupload.data-api', '[data-provides="fileupload"]', function (e) { + var $this = $(this) + if ($this.data('fileupload')) return + $this.fileupload($this.data()) + + if ($(e.target).data('dismiss') == 'fileupload') $(e.target).trigger('click.fileupload') + }) + }) + +}(window.jQuery) diff --git a/www/themes/profind/views/layouts/main.php b/www/themes/profind/views/layouts/main.php index 91c8689..3d4b419 100644 --- a/www/themes/profind/views/layouts/main.php +++ b/www/themes/profind/views/layouts/main.php @@ -35,7 +35,7 @@ name); ?>