<?php
/*
	Este controlador se encarga de las operaciones relacionadas con envío de emails. Su funcionamiento es el siguiente:
	Cuando se instancie enviándole parámetros de email, se dará de alta una tupla en la tabla homónima y se quedará relacionada la instancia con la tupla creada.
	Cuando se instancie enviándole un código de email se cargará dicho email
	Cuando se ejecute la función de enviar email, se guardará en la tupla asociada de la BD el resultado del envío así como la fecha y la hora del último intento
	Para su correcto funcionamiento, esta clase utilizará la clase "PHPmailer" que no es nativa de WebStore y por tanto se almacena en la carpeta de librerías. Es 
	por ello que la clase necesita una variable dónde almacenar un objeto PHPmailer instanciado. Mediante métodos mágicos hará el get/set correspondiente para modificar los valores de dicho objeto creado
	Además incluye unas funciones estáticas que permiten enviar un email pasando sólo el destinatario, de manera que se obtenga el resto de datos de la configuración de webStore. Este método estático instanciará
	esta misma clase para enviar el email.
	Añadiremos otra función estática que intentará enviar todos los emails que haya fallidos en la web.
	@version 1.0
*/

//La clase email necesita a su vez la clase Php mailer. Pero esta clase es externa a webStore, no la ha programado arminet, y por tanto la ubicamos en la carpeta "librerías"
//require_once(_WS_LIB_DIR_ . _WS_EMAIL_LIB . '/' . _WS_EMAIL_LIB . '.php');	// ==> Podría haberme creado un controlador con el nombre de la librería que haga esto, pero es intentar quitarle las esquinas a un círculo
//Pedro 03/Septiembre/2020 ==> A partir de ahora para cargar las librerías de email vamos a utilizar un fichero creado por mi llamado "loadLib.php" que es el que hace los "require" necesarios
require_once(_WS_LIB_DIR_ . _WS_EMAIL_LIB . '/loadLib.php');	// ==> Podría haberme creado un controlador con el nombre de la librería que haga esto, pero es intentar quitarle las esquinas a un círculo
	//Si la librería sólo la uso aquí, mejor definirla aquí
	
class SuperEmail extends Objeto{
	public static $campos_tabla = array();
	public static $alias = '';
	public static $nombreTabla = '';
	public static $camposSelect = '';
	public static $whereSelect = '';
	public static $publicFieldList = array();

	//Variables:
	private $libreriaEmail = NULL;
	private $nombreLibreriaEmail = _WS_EMAIL_LIB . '\\' . _WS_EMAIL_LIB . '\\' . _WS_EMAIL_LIB;
	private $libSendFunction = 'Send';
	private $libSendFunctionParams = NULL;
	private $destinatario = '';
	private $bcc = '';
	private $asunto = '';
	private $contenido = '';
	private static $lastError = '';
	private $templateFile = 'email.php';	//Esta variable define el fichero a utilizar para el cuerpo del email. Si no se especifica, se añade el texto plano
	//Datos de la tabla
	private $ID = 0;
	private $fecha = 0;
	private $texto_error = '';
	private $ultimo_intento = '';
	private $enviado = 0;

	//Variable privada que contiene el último email, para que cualquier clase pueda consultarlo
	private static $lastEmail = NULL;
	
	public static function getNombreTabla($soloPrincipal = NULL){
		//Incluimos los globals para coger el valor del idioma:
		static::$nombreTabla = strtolower(get_called_class());
		static::getTablaRelacionada();
		//Ahora vamos a componer el nombre de la tabla teniendo en cuenta si es sólo principal, si no o si es todo (NULL)
		$consulta = ' ';
		if ($soloPrincipal || $soloPrincipal === NULL){
			$consulta .= static::$nombreTabla . ' as `' .  static::getAlias() . '` ';
		}
		//Sólo asignamos el nombre de la tabla si no nos piden más parámetros
		if ($soloPrincipal == NULL){
			static::$nombreTabla = $consulta;
		}
		return($consulta);
	}

	//Funciones de carga en la BD, van aquí arriba
	public static function getWhereTabla(){
		$argumentos = func_get_args();
		$id = isset($argumentos[0]) ? $argumentos[0] : NULL;
		$estado = isset($argumentos[1]) ? $argumentos[1] : NULL;
		$limitar = isset($argumentos[2]) ? $argumentos[2] : true;
		
		//include('comunes/globals.php');
		static::$whereSelect = '';
		if ($id !== NULL) {
			static::$whereSelect = ' `' . static::getAlias() . '`.`codigo` = "' . $id . '" ';
		}
		if ($estado !== NULL) {
			static::$whereSelect = ' `' . static::getAlias() . '`.`enviado` = "' . (($estado) ? 1 : 0) . '" ';
		}
		if ($limitar){
			static::$whereSelect .= ' LIMIT 1 ';
		}
		return(static::$whereSelect);
	}
	
	//Aquí colocaremos las variables esenciales por ahora (destinatario, remitente, servidor, usuario, password, cc, cco, etc... :
	
	//Constructor: Crea un nuevo objeto email recibiendo los parámetros de conexión (servidor smtp, usuario, pass, , asunto etc.
		//El constructor además grabará el email en la tabla o lo cargará de la misma
		//Si el constructor recibe como parámetro un código de email lo cargará de la tabla

	function __construct($params){
		//Si recibimos un número entero, cargamos el email por ese código, si no, tenemos que ver el OpcionesWidget
		if (is_int($params)){
			$this->ID = $params;
		}
		//Nos aseguramos de que siempre se lee el fichero de OpcionesWidget. Si no nos lo han pasado, lo creamos
		$params = new ObOptions($params);

		$this->ID = $params->getDefaultValue('ID', $this->ID);
		$filaEmail = $params->getDefaultValue('filaEmail', NULL);
		//Parámetros básicos
		$this->destinatario = $params->getDefaultValue('destinatario', '');
		$this->asunto = $params->getDefaultValue('asunto', '');
		$this->contenido = $params->getDefaultValue('contenido', '');
		$this->bcc = $params->getDefaultValue('bcc', '');
		$this->templateFile = $params->getDefaultValue('templateFile', '');

		//En caso de que sea necesario cargar el email de la BD, vamos a hacerlo aquí:
		//Si hemos recibido una fila, esta se cargará aunque hayamos recibido otro ID, la velocidad lo primero:
		$fila = NULL;
		
		if ($filaEmail == NULL){
			if ($this->ID > 0) {
				//Preparamos el FROM con las uniones correspondientes
				$sentencia_sql = 'SELECT ' . static::getCampos() . ' 
									FROM ' . static::getNombreTabla() . '
									WHERE ' . static::getWhereTabla($this->ID);
				$bd = new BaseDatos();
				if ($bd->isConectado()){
					$bd->setConsultaSQL($sentencia_sql);
					$fila = $bd->getFila();
				}
			}
		}else{
			$fila = $filaEmail;
		}
		if($fila != null){
			$this->extractData($fila);
		}
		
		
		//Ahora instanciamos la clase libreriaEmail:
		$this->libreriaEmail = new $this->nombreLibreriaEmail();
		//$this->configLib();
		//Al final del constructor, asignamos a lastEmail el valor del email para que se pueda consultar
		self::$lastEmail = $this;
    }
	
	private function extractData($fila){
		//Fila es un array asociativo. De él obtenemos los valores a asignar a los campos. OJO, el índice contiene el alias de la tabla. Tenemos una función que nos facilita la tarea de extacción:
		//$this->getProp($fila, 'nombre_original_del_campo');
		//En el caso del email, puede que nos hayan pasado asunto, destinatario o texto a enviar. En este caso tendremos en cuentaq que no estén vacíos:
		$this->ID = $this->getProp($fila, 'codigo');
		$this->destinatario = ($this->destinatario != '') ? $this->destinatario : unserialize($this->getProp($fila, 'destinatarios')); // => Es el pluran porque es una cadena tal que así: web@arminet.es,web arminet;pedro@arminet.es Pedro Carranza
		$this->asunto = ($this->asunto != '') ? $this->asunto : unserialize($this->getProp($fila, 'asunto'));
		//Modificado por Pedro el 06/Febrero/2019 ==> En la tabla guardamos el contenido de manera serializada porque puede ser un objeto
			//28/Agosto/2019 ==> Había un error y des-serializábamos el asunto en vez del contenido
		$this->contenido = ($this->contenido != '') ? $this->contenido : unserialize($this->getProp($fila, 'texto'));
		$this->bcc = ($this->bcc != '') ? $this->bcc : unserialize($this->getProp($fila, 'bcc')); 
		//Ahora cargamos el resto de campos..........................
		/*Datos de la tabla*/
		$this->fecha = $this->getProp($fila, 'fecha'); 
		$this->ultimo_intento = $this->getProp($fila, 'ultimo_intento'); 
		$this->texto_error = unserialize($this->getProp($fila, 'texto_error'));
		$this->enviado = $this->getProp($fila, 'enviado');
		$this->templateFile = $this->getProp($fila, 'templateFile');
	}
	
		//enviar email: Esta función envía el email actual y devuelve true o false según si se ha podido enviar o no
		//Actualiza, en la tabla de emails el resultado del envío así como la fecha y la hora
	public function send(){
		//Aquí vamos a intentar enviar el email:
		//todo sendmail
		//Volvemos a configurar la librería por si las moscas
		$this->configLib();
		//Enviamos el email
		//ob_start();
		$retorno = $this->libreriaEmail->{$this->libSendFunction}($this->libSendFunctionParams); // Hecho así ya por Pedro para utilizar la función personalizada
		//$texto = ob_get_clean();
		//FileSystem::log($texto);
		$error = "";
		$enviado = 1;
		if (!$retorno){
			//Necesitamos obtener el error
			$enviado = 0;
			$error = self::$lastError = $this->ErrorInfo;
		}
		//Sea cual sea el resultado, es el momento de guardarlo en la BD.
			//SI el ID ES 0, es un email nuevo (INSERT INTO ....)
			//SI el ID NO es 0, entonces hay que hacer un UPDATE
		//todo insert or update (consultar esta función con Pedro)
		//KOSOVITO
		//Vamos a parsear el contenido del email:
		$contenido = serialize($this->contenido) . "\n";
		//Modificado por Pedro el 06/Febrero/2019 ==> En la tabla guardamos el contenido de manera serializada porque puede ser un objeto
		//Pedro 07/Septiembre/2020 ==> Serializamos TODO el email
		$sentencia_sql = 'INSERT INTO `' . static::getNombreTablaSinAlias() . '`
				(`codigo`,`destinatarios`,`asunto`,`texto`,`texto_error`,`bcc`,`enviado`,templateFile) 
				VALUES (\'' . $this->ID . '\',\'' . addslashes(serialize($this->destinatario)) . '\',\'' . addslashes(serialize($this->asunto)) . '\',\'' . addslashes(serialize($this->contenido)) . '\',\'' . addslashes(serialize($error)) . '\',\'' . addslashes(serialize($this->bcc)) . '\',\'' . $enviado . '\', \'' . $this->templateFile . '\') 
				ON DUPLICATE KEY UPDATE ' . '
				`codigo` = VALUES(`codigo`),
				`destinatarios` = VALUES(`destinatarios`),
				`asunto` = VALUES(`asunto`),
				`texto` = VALUES(`texto`),
				`texto_error` = VALUES(`texto_error`),
				`bcc` = VALUES(`bcc`),
				`enviado` = VALUES(`enviado`),
				`templateFile` = VALUES(`templateFile`)';
		/*echo("<br>\n");
		echo("$sentencia_sql");
		echo("\n<br/>");*/
		//FileSystem::log("$sentencia_sql");
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			//echo("Grabamos la select en la base de datos<br>");
			$bd->setConsultaSQL($sentencia_sql);
		}
		return($retorno);
	}
	//Función set ==> Aquí utilizaremos los métodos mágicos de PHP para establecer los valores del objeto PHPmailer instanciado
	public function __set($variable, $valor){
		//Esta función es invocada cuando intentemos asignar el valor de una propiedad del objeto Email que no esté definida arriba.
		//Lo que vamos a hacer es que según la propiedad, asignaremos este valor al objeto "libreriaEmail"
		$retorno = false;
		if ($this->libreriaEmail != NULL){
			if (property_exists($this->libreriaEmail, $variable)){
				$this->libreriaEmail->$variable = $valor;
				$retorno = true;
			}
		}
		return($retorno);
	}
	//Función get ==> Aquí utilizaremos los métodos mágicos de PHP para obtener los valores del objeto PHPmailer instanciado
	public function __get($variable){
		//Esta función es invocada cuando intentemos asignar el valor de una propiedad del objeto Email que no esté definida arriba.
		//Lo que vamos a hacer es que según la propiedad, asignaremos este valor al objeto "libreriaEmail"
		$retorno = NULL;
		if ($this->libreriaEmail != NULL){
			if (property_exists($this->libreriaEmail, $variable)){
				$retorno = $this->libreriaEmail->$variable;
			}
		}
		return($retorno);
	}
	//función estática de envío de emails: Función que sólo recibe los datos básicos de un email (destinatario, asunto y texto) y lo envía utilizando los datos de configuración
	public static function sendMail($opciones){
		//$opciones = (is_a($opciones, 'OpcionesWidget')) ? $opciones : new OpcionesWidget();
		$email = new Email($opciones);
		$retorno = $email->send();
		
		return($retorno);
	}

	public static function sendMailList($opciones){
		if (!(is_array($opciones))){
			$opciones = array($opciones);
		}
		$retorno = array();
		foreach($opciones as $opcion){
			$email = new Email(intval($opcion));
			$retorno[$opcion] = $email->send();
		}
		//var_dump($retorno);
		return($retorno);
	}

	
	//función estática de envío de emails pendientes: Carga la tabla de emails entera y con cada línea crea un objeto de esta misma clase e intenta enviarlo.
	public static function sendFailedEmails(){
		//Nos componemos la select de los emails:
		$sentencia_sql = 'SELECT ' . static::getCampos() . ' 
									FROM ' . static::getNombreTabla() . '
									WHERE ' .static::getWhereTabla(NULL, 0, false);
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			$bd->setConsultaSQL($sentencia_sql);
			while($fila = $bd->getFila()){
				$opciones = new OpcionesWidget(array('filaEmail' => $fila));
				$email = new Email($opciones);
				$email->send();
			}
		}
	}
	
	public static function remoteSend($params){
		//Vamos a capturar la salida:
		$retorno = new stdClass();
		//Vamos a extraer de "data" los datos a los que vamos a enviar:
		$data = $params;
		if (is_string($data)){
			$data = json_decode($params);
			if ($data == NULL){
				$data = $params;
			}
		}
		
		//Preparo los datos de envío inicializándolos a los valores por defecto que son los de contactar con la empresa:
		$destinatario = Shop::$empresa->direccion_email . ',' . Shop::$empresa->nombre_legal;
		$asunto = 'Contacto enviado a través de la web ' . Shop::$configuracion->url;
		$texto = (is_string($data)) ? $texto : '';
		//Si en data no hay una cadena de texto, puede que nos hayan pasado los valores de envío:
		if (is_a($data, 'stdClass')){
			$destinatario = (isset($data->destinatario)) ? $data->destinatario : $destinatario;
			$asunto = (isset($data->asunto)) ? $data->asunto : $asunto;
			$texto = (isset($data->texto)) ? $data->texto : $texto;
		}
		
		//Vamos a crear el objeto opciones
		$opciones = new OpcionesWidget(array(
			'destinatario' => $destinatario,
			'asunto' => $asunto,
			'contenido' => $texto
		));
		//Enviamos el email
		$retorno->resultado = static::sendMail($opciones);
		if (!$retorno->resultado){
			//Hacemos un echo con el error para que se guarde en el mensaje
			echo(static::getLastError());
		}
		//Capturamos la salida y la metemos en el msgs
		$retorno = json_encode($retorno);

		return($retorno);
	}
	
	//Con esta función configuraremos la librería de email. Si utilizamos otra diferente a PHPMailer, deberemos sobre escribir esta función
	public function configLib(){
		//Descomentar esta línea para activar el modo debug. Su valor va de 1 a cuatro según el nivel de información que queramos ver
		//$this->libreriaEmail->SMTPDebug = 4;
		$this->libreriaEmail->SMTPDebug = 0;
		//Vamos a ponerla en español:
		$this->libreriaEmail->SetLanguage('es', _WS_LIB_DIR_ . _WS_EMAIL_LIB . '/');
		//Indicamos el método a usar
		$this->libreriaEmail->isSMTP();
		//Ahora asignamos el resto de datos que ya tengamos disponibles:
		//todo asignamos los valores que necesitamos
		$this->libreriaEmail->Mailer = 'smtp';
		$this->libreriaEmail->Helo = Shop::$empresa->smtp_email; //Muy importante para que llegue a hotmail y otros
		$this->libreriaEmail->Port = 587;
		//$this->libreriaEmail->SMTPSecure = 'ssl';	//Esto sería la leche poder usarlo
		$this->libreriaEmail->SMTPSecure = $this->libreriaEmail::ENCRYPTION_STARTTLS;	//Esto es para el servidor dedicado
		//$this->libreriaEmail->SMTPSecure = '';		//Hacemos esto para las cuentas de hostalia
		$this->libreriaEmail->SMTPAuth = true;
		$this->libreriaEmail->Host = Shop::$empresa->smtp_email;
		//$this->libreriaEmail->Host = 'mail.interdominios.com';
		$this->libreriaEmail->Username = Shop::$empresa->usuario_email;
		$this->libreriaEmail->Password = Shop::$empresa->password_email;

		$this->libreriaEmail->FromName = Shop::$empresa->nombre;
		$this->libreriaEmail->From = Shop::$empresa->direccion_email;
		$this->libreriaEmail->Sender = Shop::$empresa->direccion_email;
		$this->libreriaEmail->ReturnPath = Shop::$empresa->direccion_email;
		//Vamos a obligar al envío con UTF8
		$this->libreriaEmail->CharSet = 'UTF-8';
		//Vamos a definir los destinatarios y los BCC
		if ($this->destinatario != ''){
			$destinatarios = explode(';', trim($this->destinatario));
			for ($i = 0; $i < count($destinatarios); $i++) {
				$direccion = explode(',', $destinatarios[$i]);
				if (!isset($direccion[1])) {
					$direccion[1] = '';
				}
				$this->libreriaEmail->AddAddress($direccion[0], $direccion[1]);
			}
		}
		//Vamos ahora con la copia oculta
		//Si es un array no hay que convertir
		if ($this->bcc != ''){
			$destinatarios = explode(';', trim($this->bcc));
			for ($i = 0; $i < count($destinatarios); $i++) {
				$direccion = explode(',', $destinatarios[$i]);
				if (!isset($direccion[1])) {
					$direccion[1] = '';
				}
				$this->libreriaEmail->AddBCC($direccion[0], $direccion[1]);
			}
		}
		//Aún faltan por definir algunos parámetros:
		$this->libreriaEmail->IsHTML( ($this->templateFile != '') ? true : false);
		//Añadimos las imágenes que se vayan a utilizar en el mail
		//Pedro 02/Abril/2020 ==> Nos vamos a asegurar de que no hay entidades html en el correo
		$this->libreriaEmail->Subject = html_entity_decode(Traduccion::aplyPhraseList($this->asunto), ENT_HTML5);
		$this->libreriaEmail->Body = html_entity_decode($this->formatEmail(), ENT_HTML5);
		if ($this->templateFile != ''){
			$this->libreriaEmail->MsgHTML($this->Body);
		}
		$this->libreriaEmail->WordWrap = 50;
		/*echo("<pre>");
		var_dump($this->libreriaEmail);
		echo("</pre>");*/
		
	}
	
	public static function getLastError(){
		return(self::$lastError);
	}
	
	public static function getLastEmail(){
		return(self::$lastEmail);
	}
	
	private function formatEmail(){
		$retorno = $this->contenido;
		//var_dump($this->templateFile);
		//SI tenemos fichero de plantilla de email definido, vamos a ejecutarlo para mostrarlo:
		if ($this->templateFile != ''){
			//file_put_contents('test.txt', $this->libreriaEmail->Body);
			ob_start();
			//Pedro 07/Septiembre/2020 ==> Vamos a aplicar directamente la lista de frases en vez de hacer la traducción al vuelo. Así me quito cualquier "traducir" que haya
			//Traduccion::onTheFlyOn();
			$incluido = Theme::include_file($this->templateFile, $this);
			//Traduccion::onTheFlyOff();
			$retorno = Traduccion::aplyPhraseList(ob_get_clean());
			
		}
		return($retorno);
	}
	//Unos cuantos getter y setter más:
	public function getAsunto(){
		return($this->asunto);
	}
	public function setAsunto($asunto){
		$this->asunto = $asunto;
	}
	public function getContenido(){
		return($this->contenido);
	}
	public function setContenido($contenido){
		$this->contenido = $contenido;
	}
	public function getDestinatario(){
		return($this->destinatario);
	}
	public function getID(){
		return($this->ID);
	}
	public function getFecha(){
		return($this->fecha);
	}
	public function getTexto_error(){
		return($this->texto_error);
	}
	public function getUltimo_intento(){
		return($this->ultimo_intento);
	}
	public function getEnviado(){
		return($this->enviado);
	}	


	//Marina (02/Diciembre/2019) Añadimos esta funcion para borrar emails
	public static function removeEmail($codigo){
		if (!(is_array($codigo))){
			$codigo = array($codigo);
		}
		$codigo = '(' . implode(',', $codigo) . ')';
		//Los campos son un array del tipo "nombre_campo" => "valor"
		$sentencia_sql = 'DELETE FROM ' . static::getNombreTablaSinAlias(true) . 
							' WHERE codigo IN ' . $codigo . ';';
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			$bd->setConsultaSQL($sentencia_sql);
		}
		//Recargamos la página:
		Sesion::reloadPage(Shop::$configuracion->url . Sections::$current_section);
	}

	//Marina (05/Diciembre/2019) Añadimos esta funcion para borrar emails enviados
	public static function removeSent(){
		//Los campos son un array del tipo "nombre_campo" => "valor"
		$sentencia_sql = 'DELETE FROM ' . static::getNombreTablaSinAlias(true) . 
							' WHERE enviado = 1;';
		$bd = new BaseDatos();
		if ($bd->isConectado()){
			//$bd->setConsultaSQL($sentencia_sql);
			echo $sentencia_sql;
		}
		//Recargamos la página:
		Sesion::reloadPage(Shop::$configuracion->url . Sections::$current_section);
	}
	
}
