<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Sebaxtian</title>
	<atom:link href="http://www.sebaxtian.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.sebaxtian.com</link>
	<description>Tan solo es lo que pienso</description>
	<lastBuildDate>Wed, 13 Mar 2024 14:05:45 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.4.7</generator>
	<item>
		<title>El enemigo dentro</title>
		<link>https://www.sebaxtian.com/archivos/el-enemigo-dentro/</link>
					<comments>https://www.sebaxtian.com/archivos/el-enemigo-dentro/#respond</comments>
		
		<dc:creator><![CDATA[sebaxtian]]></dc:creator>
		<pubDate>Wed, 13 Mar 2024 14:04:24 +0000</pubDate>
				<category><![CDATA[Arte]]></category>
		<category><![CDATA[Lifestyle]]></category>
		<category><![CDATA[Punto de Vista]]></category>
		<guid isPermaLink="false">https://www.sebaxtian.com/?p=2716</guid>

					<description><![CDATA[Mientras el chico alto repartía los panfletos, el de la cara redonda se subió a la tarima y nos soltó su discurso. Las octavillas estaban escritas con el estilo simplista característico: «¡Hundamos las elecciones fraudulentas al rectorado! ¡Unamos nuestras fuerzas en una nueva huelga general en la universidad! ¡Demos un golpe decisivo a la conjunción [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Mientras el chico alto repartía los panfletos, el de la cara redonda se subió a la tarima y nos soltó su discurso. Las octavillas estaban escritas con el estilo simplista característico: «¡Hundamos las elecciones fraudulentas al rectorado! ¡Unamos nuestras fuerzas en una nueva huelga general en la universidad! ¡Demos un golpe decisivo a la conjunción poder industrial + poder académico = imperialismo japonés!». La teoría era magnífica, nada podía reprochársele al contenido, pero el texto carecía de poder de convicción. No inspiraba confianza ni movía corazones. Otro tanto sucedía con el discurso del chico de la cara redonda. La misma canción de siempre. La melodía era idéntica, solo diferían algunas comas. «El auténtico enemigo de estos tipos no es el poder estatal, es la falta de imaginación», pensé.</p>



<p>Extracto de «Tokio Blues» de <strong>Haruki Murakami</strong></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.sebaxtian.com/archivos/el-enemigo-dentro/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Subconjuntos en subpáneles</title>
		<link>https://www.sebaxtian.com/archivos/subconjuntos-en-subpaneles/</link>
					<comments>https://www.sebaxtian.com/archivos/subconjuntos-en-subpaneles/#respond</comments>
		
		<dc:creator><![CDATA[sebaxtian]]></dc:creator>
		<pubDate>Thu, 03 Nov 2016 14:25:54 +0000</pubDate>
				<category><![CDATA[Software y Hardware]]></category>
		<category><![CDATA[SuiteCRM]]></category>
		<guid isPermaLink="false">http://www.sebaxtian.com/?p=1941</guid>

					<description><![CDATA[Supongamos múltiples tipos de cuentas, todas con información que no difiere estructuralmente entre cada tipo y almacenadas todas en un mismo módulo. Para el ejemplo, y aunque lógicamente parezca una locura, podríamos suponer que instituciones, programas y departamentos se almacenarán todos como cuentas, bajo el supuesto de que la información requerida para cada uno de los tipos será siempre la misma.

Ahora bien, supongamos distintos tipos de relaciones entre contactos y cuentas, como por ejemplo estudiantes registrados, profesores, administrativos, docentes, además de otras tantas relaciones posibles.

Ahora supongamos la vista de una cuenta tipo Institución y a continuación imaginemos el subpanel con todos los contactos con los que tendrá relación. Ahora visualice la mezcla indistinta de estudiantes, docentes y administrativos que tendrá ese subpanel y comprenderá el problema en el que nos metimos por querer tener en un mismo módulo diferentes tipos de cuentas.]]></description>
										<content:encoded><![CDATA[
<p>Supongamos múltiples tipos de cuentas, todas con información que no difiere estructuralmente entre cada tipo y almacenadas todas en un mismo módulo. Para el ejemplo, y aunque lógicamente parezca una locura, podríamos suponer que instituciones, programas y departamentos se almacenarán todos como cuentas, bajo el supuesto de que la información requerida para cada uno de los tipos será siempre la misma.</p>



<p>Ahora bien, supongamos distintos tipos de relaciones entre contactos y cuentas, como por ejemplo estudiantes registrados, profesores, administrativos, docentes, además de otras tantas relaciones posibles.</p>



<p>Ahora supongamos la vista de una cuenta tipo Institución y a continuación imaginemos el subpanel con todos los contactos con los que tendrá relación. Ahora visualice la mezcla indistinta de estudiantes, docentes y administrativos que tendrá ese subpanel y comprenderá el problema en el que nos metimos por querer tener en un mismo módulo diferentes tipos de cuentas.</p>



<p>¿No sería genial poder separar el subpanel en conjuntos por cada tipo de relación y así evitar esa orgía de información?</p>



<figure class="wp-block-image alignright"><a href="https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/11/doble_subanel.png"><img decoding="async" width="221" height="77" src="https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/11/doble_subanel.png" alt="doble_subanel" class="wp-image-1951"/></a></figure>



<p>Para el ejemplo supondremos los siguientes datos:</p>



<ul>
<li>módulo_a: es el módulo que consultaremos</li>



<li>módulo_b: es el módulo que dividiremos en subpáneles</li>



<li>relación: es el nombre con el cuál declaró la relación entre Módulos A y B.</li>



<li>filtro: es el dato a filtrar. Debe corresponder a una variable que haya creado o que ya exista en el módulo. Dependiendo de eso deberá cambiar la forma como se hace la consulta SQL.</li>



<li>filtro_n: el dato n corresponde al elemento con el cuál se hará el filtro</li>
</ul>



<p>El primer paso es la declaración de los distintos subpáneles. Es posible que ya exista el archivo así que deberá cambiar el contenido.</p>



<p><code>/&lt;directorio_crm&gt;/custom/Extension/modules/&lt;Módulo_A&gt;/Ext/Layoutdefs/&lt;relación&gt;_&lt;Módulo_B&gt;.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php
	$layout_defs["&lt;Módulo_A&gt;"]["subpanel_setup"]['&lt;relación&gt;_&lt;filtro_1&gt;'] = array (
		'order' =&gt; 101,
		'module' =&gt; '&lt;Módulo_B&gt;',
		'subpanel_name' =&gt; 'default',
		'sort_order' =&gt; 'asc',
		'sort_by' =&gt; 'id',
		'title_key' =&gt; 'LBL_&lt;FILTRO_1&gt;',
		'get_subpanel_data' =&gt; 'function:get_&lt;módulo_b&gt;&lt;Filtro&gt;',
		'generate_select' =&gt; true,
		'top_buttons' =&gt; array(),
		'function_parameters' =&gt; array(
			'import_function_file' =&gt; 'custom/Extension/application/Ext/Utils/&lt;módulo_b&gt;_utils.php',
			'&lt;filtro&gt;' =&gt; '&lt;filtro_1&gt;',
		),
		'generate_select' =&gt; true,
	);
	
	$layout_defs["&lt;Módulo_A&gt;"]["subpanel_setup"]['&lt;relación&gt;_&lt;filtro_2&gt;'] = array (
		'order' =&gt; 101,
		'module' =&gt; '&lt;Módulo_B&gt;',
		'subpanel_name' =&gt; 'default',
		'sort_order' =&gt; 'asc',
		'sort_by' =&gt; 'id',
		'title_key' =&gt; 'LBL_&lt;FILTRO_2&gt;',
		'get_subpanel_data' =&gt; 'function:get_&lt;módulo_b&gt;&lt;Filtro&gt;',
		'generate_select' =&gt; true,
		'top_buttons' =&gt; array(),
		'function_parameters' =&gt; array(
			'import_function_file' =&gt; 'custom/Extension/application/Ext/Utils/&lt;módulo_b&gt;_utils.php',
			'&lt;filtro&gt;' =&gt; '&lt;filtro_2&gt;',
		),
		'generate_select' =&gt; true,
	);</code></pre>



<p>El siguiente paso es la declaración de la función que atenderá este sistema de filtro. Es posible que el archivo ya exista.</p>



<p> <code>/&lt;directorio_crm>/custom/Extension/application/Ext/Utils/&lt;módulo_b>_utils.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">function get_&lt;módulo_b&gt;&lt;Filtro&gt;($params) {
    $args = func_get_args();
    <pre wp-pre-tag-1=""></pre>lt;filtro&gt; = $args[0]['&lt;filtro&gt;'];
    $return_array['select'] = " SELECT &lt;módulo_b&gt;.*";
    $return_array['from'] = " FROM &lt;módulo_b&gt; ";
    $return_array['where'] = " WHERE &lt;módulo_b&gt;.deleted = '0' AND cstm.&lt;filtro&gt;_c = " . <pre wp-pre-tag-1=""></pre>lt;filtro&gt; . "";
    $return_array['join'] = " INNER JOIN &lt;módulo_b&gt;_cstm cstm ON cstm.id_c = &lt;módulo_b&gt;.id";
    $return_array['join_tables'][0] = "";
    return $return_array;
}</code></pre>



<p>También se deberá agregar las modificaciones a la traducción para incluir los nuevos campos</p>



<p><code>/&lt;directorio_crm&gt;/custom/Extension/modules/&lt;Módulo_B&gt;/Ext/Language/en_us.&lt;relación&gt;.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">	$mod_strings['LBL_&lt;FILTRO_1&gt;'] = '&lt;Filtro_1&gt;';
	$mod_strings['LBL_&lt;FILTRO_2&gt;'] = '&lt;Filtro_2&gt;';</code></pre>



<p>Paso final, ejecutamos la acción de “Reparar y Reconstruir“, y ya tendremos los subpáneles.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.sebaxtian.com/archivos/subconjuntos-en-subpaneles/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Unos agujeros en Siberia</title>
		<link>https://www.sebaxtian.com/archivos/unos-agujeros-en-siberia/</link>
					<comments>https://www.sebaxtian.com/archivos/unos-agujeros-en-siberia/#respond</comments>
		
		<dc:creator><![CDATA[sebaxtian]]></dc:creator>
		<pubDate>Sat, 22 Oct 2016 15:11:46 +0000</pubDate>
				<category><![CDATA[Lifestyle]]></category>
		<category><![CDATA[Punto de Vista]]></category>
		<category><![CDATA[comic]]></category>
		<guid isPermaLink="false">http://www.sebaxtian.com/?p=1930</guid>

					<description><![CDATA[«La liberación de metano en esas regiones inaccesibles, parece indicar que la capa de permafrost está comenzando a perforarse, lo que permite escapar al gas. Hemos encontrado niveles elevados de metano en la superficie del mar y aún más a ciertas profundidades.» &#8211; Örjan Gustafsson Comic tomado de Cyanide &#38; Happiness y cita tomada del [&#8230;]]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image aligncenter size-medium"><a href="https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/10/snowflake.png"><img fetchpriority="high" decoding="async" width="189" height="300" src="https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/10/snowflake-189x300.png" alt="snowflake" class="wp-image-1931" srcset="https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/10/snowflake-189x300.png 189w, https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/10/snowflake-768x1218.png 768w, https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/10/snowflake-646x1024.png 646w, https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/10/snowflake.png 820w" sizes="(max-width: 189px) 100vw, 189px" /></a></figure>



<p>«La liberación de metano en esas regiones inaccesibles, parece indicar que la capa de permafrost está comenzando a perforarse, lo que permite escapar al gas. Hemos encontrado niveles elevados de metano en la superficie del mar y aún más a ciertas profundidades.» &#8211; Örjan Gustafsson</p>



<p>Comic tomado de <a href="http://explosm.net/comics/4442/" target="_blank" rel="noopener">Cyanide &amp; Happiness</a> y cita tomada del artículo en Wikipedia sobre la <a href="https://es.wikipedia.org/wiki/Hip%C3%B3tesis_del_fusil_de_clatratos" target="_blank" rel="noopener">Hipótesis del fusil de clatratos</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.sebaxtian.com/archivos/unos-agujeros-en-siberia/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Almacenando arreglos en un campo</title>
		<link>https://www.sebaxtian.com/archivos/almacenando-arreglos-en-un-campo/</link>
					<comments>https://www.sebaxtian.com/archivos/almacenando-arreglos-en-un-campo/#respond</comments>
		
		<dc:creator><![CDATA[sebaxtian]]></dc:creator>
		<pubDate>Wed, 27 Apr 2016 01:19:47 +0000</pubDate>
				<category><![CDATA[Software y Hardware]]></category>
		<category><![CDATA[SuiteCRM]]></category>
		<guid isPermaLink="false">http://www.sebaxtian.com/?p=1872</guid>

					<description><![CDATA[Almacenando arreglos en un campo Declaración de la variable y sus funciones de almacenamiento Agregar las vistas Para los perspizaces Seguramente ya se habrán topado con el widget para agregar correos a una cuenta. Esta solución es muy interesante desde la óptica de la usabilidad porque se trata de un campo que puede ampliarse conforme [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class='indizar' id='right' style='width: 270px; float: right;'><ul><li>Almacenando arreglos en un campo</li><li><a href='https://www.sebaxtian.com/archivos/almacenando-arreglos-en-un-campo/chapter/2/'>Declaración de la variable y sus funciones de almacenamiento</a></li><li><a href='https://www.sebaxtian.com/archivos/almacenando-arreglos-en-un-campo/chapter/3/'>Agregar las vistas</a></li><li><a href='https://www.sebaxtian.com/archivos/almacenando-arreglos-en-un-campo/chapter/4/'>Para los perspizaces</a></li></ul></div>


<p>Seguramente ya se habrán topado con el widget para agregar correos a una cuenta. Esta solución es muy interesante desde la óptica de la usabilidad porque se trata de un campo que puede ampliarse conforme se vayan necesitando nuevos items, y si bien uno podría decir que con un relacionamiento es posible simular un elemento de este tipo, también es verdad que para el usuario es mejor tener un listado dentro de la declaración del módulo en vez de un subpanel. Así son los usuarios.</p>



<span id="more-1872"></span>



<p>Es necesario también hacer diferencia entre dos tipos de variables. En el caso de los correos se trata de relaciones entre la cuenta y una línea de correo, y esto se hace para que un cambio en un correo se vea reflejado al instante en todos los <em>beans</em> (cuentas, compañías, oportunidades, contactos, etc). La otra opción es un campo encargado de almacenar un arreglo o una matriz y que guardará el dato como una cadena JSON.</p>



<figure class="wp-block-image aligncenter"><a href="https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/04/lista_elementos.png"><img decoding="async" width="300" height="79" src="https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/04/lista_elementos-300x79.png" alt="lista_elementos" class="wp-image-1924" srcset="https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/04/lista_elementos-300x79.png 300w, https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/04/lista_elementos.png 566w" sizes="(max-width: 300px) 100vw, 300px" /></a></figure>



<p>Para el presente ejemplo supondremos que vamos a <strong>almacenar un arreglo como una cadena JSON dentro del campo</strong>.</p>


 <p class='indizar scroll'><strong>Cap&iacute;­tulos:</strong> | 1 | <a href='https://www.sebaxtian.com/archivos/almacenando-arreglos-en-un-campo/chapter/2/'>2</a> | <a href='https://www.sebaxtian.com/archivos/almacenando-arreglos-en-un-campo/chapter/3/'>3</a> | <a href='https://www.sebaxtian.com/archivos/almacenando-arreglos-en-un-campo/chapter/4/'>4</a> | <a href='https://www.sebaxtian.com/archivos/almacenando-arreglos-en-un-campo/chapter/2/'>Siguiente</a> |</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.sebaxtian.com/archivos/almacenando-arreglos-en-un-campo/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Campos personales en relaciones</title>
		<link>https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/</link>
					<comments>https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/#respond</comments>
		
		<dc:creator><![CDATA[sebaxtian]]></dc:creator>
		<pubDate>Wed, 13 Apr 2016 01:03:14 +0000</pubDate>
				<category><![CDATA[Software y Hardware]]></category>
		<category><![CDATA[SuiteCRM]]></category>
		<guid isPermaLink="false">http://www.sebaxtian.com/?p=1824</guid>

					<description><![CDATA[Campos personales en relaciones Extender la base de datos La clase controladora Declarar las variables Vista en los subpáneles La herramienta Studio de SuiteCRM facilita el trabajo de declaración de relaciones entre módulos, pero su funcionalidad se queda corta cuando además requerimos la declaración de campos al interior de la relación misma. En circunstancias como [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class='indizar' id='right' style='width: 260px; float: right;'><ul><li>Campos personales en relaciones</li><li><a href='https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/chapter/2/'>Extender la base de datos</a></li><li><a href='https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/chapter/3/'>La clase controladora</a></li><li><a href='https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/chapter/4/'>Declarar las variables</a></li><li><a href='https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/chapter/5/'>Vista en los subpáneles</a></li></ul></div>


<p>La herramienta <strong>Studio</strong> de <strong>SuiteCRM</strong> facilita el trabajo de declaración de relaciones entre módulos, pero su funcionalidad se queda corta cuando además requerimos la declaración de campos al interior de la relación misma. En circunstancias como esta no queda más alternativa que echar mano al código fuente.</p>



<span id="more-1824"></span>



<p>Para el presente ejemplo supondremos que ya está declarada la relación entre los módulos y que ha ejecutado la acción de <strong>reconstrucción</strong>.</p>


 <p class='indizar scroll'><strong>Cap&iacute;­tulos:</strong> | 1 | <a href='https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/chapter/2/'>2</a> | <a href='https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/chapter/3/'>3</a> | <a href='https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/chapter/4/'>4</a> | <a href='https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/chapter/5/'>5</a> | <a href='https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/chapter/2/'>Siguiente</a> |</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.sebaxtian.com/archivos/campos-personales-en-relaciones/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Campo para una imagen en la configuración</title>
		<link>https://www.sebaxtian.com/archivos/campo-para-una-imagen-en-la-configuracion/</link>
					<comments>https://www.sebaxtian.com/archivos/campo-para-una-imagen-en-la-configuracion/#respond</comments>
		
		<dc:creator><![CDATA[sebaxtian]]></dc:creator>
		<pubDate>Sat, 02 Apr 2016 17:33:31 +0000</pubDate>
				<category><![CDATA[Software y Hardware]]></category>
		<category><![CDATA[SuiteCRM]]></category>
		<guid isPermaLink="false">http://www.sebaxtian.com/?p=1789</guid>

					<description><![CDATA[En la anterior entrada expliqué cómo crear una vista para el manejo de variables de configuración. El ejemplo se limitaba a dos campos de texto, y en teoría agregar otros campos básicos (texto, área de texto, numérico y color) no debería representar problema alguno. Pero al hacer págnas configuración es muy común encontrar campos para [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>En la anterior entrada expliqué cómo crear una vista para el manejo de variables de configuración. El ejemplo se limitaba a dos campos de texto, y en teoría agregar otros campos básicos (texto, área de texto, numérico y color) no debería representar problema alguno. Pero al hacer págnas configuración es muy común encontrar campos para imágenes. El presente ejemplo extiende la <a href="https://www.sebaxtian.com/archivos/1742">anterior nota</a> para agregar este tipo de variables a la configuración y por lo tanto debe haber realizado el anterior ejemplo para poder realizar esta actividad.</p>



<span id="more-1789"></span>



<p>El primer punto a tener en cuenta es buscar la forma de subir la imagen. Uno pensaría que SuiteCRM integraría un punto de entrada de este tipo, pero por lo que pude entrever cada módulo crea su propio mecanismo, y el resultado es que cada uno tiene sus propias particularidades. En definitiva es aconsejable crear uno propio. Recuerde que la declaración del EntryPoint se hace en el directorio de Aplicaciones, pero el archivo del EntryPoint puede estar en cualquier directorio. Por facilidad mnemotécnica la ubico en el Configurator porque al fin de cuentas lo usaremos con este módulo.</p>



<p><code>/&lt;directorio_crm&gt;/custom/modules/Configurator/UploadImageFile.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php
if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
/*********************************************************************************
 * Entry point para subir una imagen
 ********************************************************************************/


require_once('include/JSON.php');
require_once('include/entryPoint.php');
require_once 'include/upload_file.php';

//Si no tenemos sección o nombre, retornar error
if(!isset($_REQUEST['section']) &amp;&amp; !isset($_REQUEST['name'])) {
    $returnArray['data']='not_recognize';
    echo $json-&gt;encode($returnArray);
    sugar_cleanup();
    exit();
}

global $sugar_config;
//Solo aceptamos jpg, jpeg y png.
//No podemos aceptar gif ni bmp porque internamente SUgar los descarta
$supportedExtensions = array('jpg', 'png', 'jpeg');

//Declaramos las variables del sistema
$json = getJSONobj();
$rmdir=true;
$returnArray = array();
$upload_ok = false;

//Obtenemos los datos de la foto
$section = $_REQUEST['section'];
$name = $_REQUEST['name'];

//Declarar el directorio donde quedará la imagen
$upload_path = $section . '/' . $name ;
//Debe venir el archivo file_1
if(isset($_FILES['file_1'])){
	//Declarar el mecanismo para manipular el archivo temporal
    $upload = new UploadFile('file_1');
    //Confirmar que hay archivo
    if($upload-&gt;confirm_upload()) {
		//Determinar dónde quedará la imagen
        $upload_dir  = 'upload://' . $upload_path;
        //Asegurar que podremos subir la imagen
        UploadStream::ensureDir($upload_dir);
        //Verificar que se subió la imagen a la posición temporal
        if(!verify_uploaded_image($upload-&gt;temp_file_location)){
			//Si no se subió, declarar el error
            $returnArray['data']='other';
            $returnArray['path'] = '';
            echo $json-&gt;encode($returnArray);
            sugar_cleanup();
            exit();
        }
        //Determinar el directorio donde se almacenará definitivamente la imagen
        $file_name = $upload_dir."/".$upload-&gt;get_stored_file_name();
        //Guardar la imagen en la ubicación definitiva
        if($upload-&gt;final_move($file_name)) {
            $upload_ok = true;
        }
    }
}

//Si no se almacenó laimagen en la posición definitiva
if(!$upload_ok) {
	//Declarar el error
    $returnArray['data']='not_recognize';
    echo $json-&gt;encode($returnArray);
    sugar_cleanup();
    exit();
}

//Determinar la url del upload
$returnArray['upload']=$upload-&gt;get_upload_url();

//Si el archivo existe
if(file_exists($file_name) &amp;&amp; is_file($file_name)) {
	//Obtener los datos del archivo y declarar las variables
    $encoded_file_name = rawurlencode($upload-&gt;get_stored_file_name());
	$returnArray['filename'] = $file_name;
    $returnArray['path'] = $upload_path . '/' . $encoded_file_name;
    $returnArray['url']= 'cache/images/'.$encoded_file_name;
    //Verificar que se subió la imagen
    if(!verify_uploaded_image($file_name, false)) {
        $returnArray['data']='other';
        $returnArray['path'] = '';
        unlink($file_name);
    } else {
		//Sino, copiarla
		copy($file_name, sugar_cached('images/'.$upload-&gt;get_stored_file_name()));
	}
	//Si hay 'data' es porque ubo un problema 
    if(!empty($returnArray['data'])){
        echo $json-&gt;encode($returnArray);
    }else{ //Sino, ndicar en data que el proceso se concluyó bien
        $rmdir=false;
        $returnArray['data']='ok';
        echo $json-&gt;encode($returnArray);
    }
}else{ //SI no existe el archivo, informar del error
    $returnArray['data']='file_error';
    echo $json-&gt;encode($returnArray);
}
sugar_cleanup();
exit();
</code></pre>



<p><code>/&lt;directorio_crm&gt;/custom/Extension/application/Ext/EntryPointRegistry/UploadImageFile.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php
	$entry_point_registry['UploadImageFile'] = array(
		'file' =&gt; 'custom/modules/Configurator/UploadImageFile.php',
		'auth' =&gt; true,
	);</code></pre>



<p>Ejecutamos la acción de “<strong>Reparar y Reconstruir</strong>“, y tendremos a nuestra disposición el punto de entrada para subir una imagen.</p>



<p>Ahora modificamos la declaración de la vista y agregamos la línea de la foto en la sección donde se declaran las variables de la configuración del paquete (líneas 22 a 30 del ejemplo de la <a href="https://www.sebaxtian.com/archivos/1742">anterior entrada</a>).</p>



<p><code>/&lt;directorio_crm&gt;/custom/modules/Configurator/views/view.&lt;acción&gt;.php </code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">        //Declarar la instancia para administrar
        //la configuración
        $cfg = new Configurator();
		//Declarar los elementos de la configuración
		//en caso de aun no estar declarados
		if (!array_key_exists('&lt;paquete&gt;', $cfg-&gt;config)) {
		    $cfg-&gt;config['&lt;paquete&gt;'] = array(
		        'name' =&gt; '',
		        'code' =&gt; '',
		    );
			$cfg-&gt;saveConfig();
		}
		
		//Agregar la nueva variable
		if(!isset($cfg-&gt;config['&lt;paquete&gt;']['logo'])) {
			$cfg-&gt;config['&lt;paquete&gt;']['logo'] = '';
			$cfg-&gt;saveConfig();
		}</code></pre>



<p>También se deberá agregar las modificaciones a la traducción para incluir los nuevos campos</p>



<p><code>/&lt;directorio_crm&gt;/custom/Extension/modules/Configurator/Ext/Language/en_us.&lt;paquete&gt;.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">	$mod_strings['LBL_&lt;PAQUETE&gt;_LOGO'] = 'Current Logo:';
	$mod_strings['LBL_&lt;PAQUETE&gt;_LOGO_HELP'] = 'This logo is displayed in the tickets.';
	$mod_strings['LBL_&lt;PAQUETE&gt;_NEW_LOGO'] = 'Select Logo:';</code></pre>



<p>A continuación creamos la plantilla con los agregados para hacer uso del entryPoint. Igual que en el anterior ejemplo no hay funciones para chequeo de variables así que se deja al lector la labor correspondiente.</p>



<p><code>/&lt;directorio_crm&gt;/custom/modules/Configurator/tpls/&lt;acción&gt;.tpl</code></p>



<pre title="" class="wp-block-code"><code lang="php" class="language-php">{*

/*********************************************************************************
 * Vista para la administración de la información del centro comercial
 ********************************************************************************/

*}
&lt;div class='moduleTitle'&gt;
	&lt;h2&gt;{$MOD.LBL_&lt;ACCIÓN&gt;_SETTINGS}&lt;/h2&gt;
	&lt;div class='clear'&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;form name="&lt;Paquete&gt;Settings" enctype='multipart/form-data' method="POST" action="index.php" onSubmit="return (add_checks(document.&lt;Paquete&gt;Settings) &amp;&amp; check_form('&lt;Paquete&gt;Settings'));"&gt;
&lt;input type='hidden' name='action' value='&lt;acción&gt;'/&gt;
&lt;input type='hidden' name='module' value='Configurator'/&gt;
&lt;input type='hidden' name='save' value='true'/&gt;
&lt;span class='error'&gt;{$error.main}&lt;/span&gt;
&lt;table width="100%" cellpadding="0" cellspacing="1" border="0" class="actionsContainer"&gt;
&lt;tr&gt;

	&lt;td&gt;
		&lt;input title="{$APP.LBL_SAVE_BUTTON_TITLE}" accessKey="{$APP.LBL_SAVE_BUTTON_KEY}" class="button primary" id="&lt;Paquete&gt;Settings_save_button" type="submit"  name="save" value="  {$APP.LBL_SAVE_BUTTON_LABEL}  " &gt;
		&amp;nbsp;&lt;input title="{$MOD.LBL_SAVE_BUTTON_TITLE}"  id="&lt;Paquete&gt;Settings_restore_button"  class="button"  type="submit" name="restore" value="  {$MOD.LBL_RESTORE_BUTTON_LABEL}  " &gt;
		&amp;nbsp;&lt;input title="{$MOD.LBL_CANCEL_BUTTON_TITLE}" id="&lt;Paquete&gt;Settings_cancel_button"   onclick="document.location.href='index.php?module=Configurator&amp;action=index'" class="button"  type="button" name="cancel" value="  {$APP.LBL_CANCEL_BUTTON_LABEL}  " &gt; &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;


&lt;table width="100%" border="0" cellspacing="1" cellpadding="0" class="edit view"&gt;
&lt;tr&gt;
	&lt;th align="left" scope="row" colspan="4"&gt;&lt;h4&gt;{$MOD.LBL_&lt;ACCIÓN&gt;_DESCRIPTION}&lt;/h4&gt;&lt;/th&gt;
&lt;/tr&gt;

	&lt;tr&gt;
		&lt;td scope="row" width='15%' nowrap&gt;{$MOD.LBL_&lt;PAQUETE&gt;_NAME} &lt;/td&gt;
		&lt;td width='35%'&gt;
			&lt;input type='text' name='&lt;paquete&gt;_name' value='{$config.&lt;paquete&gt;.name}'&gt;
		&lt;/td&gt;
		&lt;td scope="row" width='15%' nowrap&gt;{$MOD.LBL_&lt;PAQUETE&gt;_CODE} &lt;/td&gt;
		&lt;td width='35%'&gt;
			&lt;input type='text' name='&lt;paquete&gt;_code' value='{$config.&lt;paquete&gt;.code}'&gt;
		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
        &lt;td  scope="row" width='12%' nowrap&gt;
        {$MOD.LBL_&lt;PAQUETE&gt;_LOGO}&amp;nbsp;{sugar_help text=$MOD.LBL_&lt;PAQUETE&gt;_LOGO_HELP}
        &lt;/td&gt;
        &lt;td width='35%' &gt;
            &lt;img id="&lt;paquete&gt;_logo_image" style="width: auto; max-height: 150px;" src='{$config.&lt;paquete&gt;.logo}' alt='{$MOD.LBL_&lt;PAQUETE&gt;_LOGO}'&gt;
        &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
        &lt;td  scope="row" width='12%' nowrap&gt;
            {$MOD.LBL_&lt;PAQUETE&gt;_NEW_LOGO}&amp;nbsp;{sugar_help text=$MOD.LBL_&lt;PAQUETE&gt;_NEW_LOGO_HELP_NO_SPACE}
        &lt;/td&gt;
        &lt;td  width='35%'&gt;
            &lt;div id="container_upload"&gt;&lt;/div&gt;
            &lt;input type='text' id='&lt;paquete&gt;_logo' name='&lt;paquete&gt;_logo' value='{$config.&lt;paquete&gt;.logo}' style="display:none"&gt;
        &lt;/td&gt;
    &lt;/tr&gt;


&lt;/table&gt;


&lt;table  width="100%" border="0" cellspacing="1" cellpadding="0" class="edit view"&gt;
{if $logger_visible}
&lt;tr&gt;
&lt;th align="left" scope="row" colspan="6"&gt;&lt;h4&gt;{$MOD.LBL_LOGGER}&lt;/h4&gt;&lt;/th&gt;
&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td  scope="row" valign='middle'&gt;{$MOD.LBL_LOGGER_FILENAME}&lt;/td&gt;
		&lt;td   valign='middle' &gt;&lt;input type='text' name = 'logger_file_name'  value="{$config.logger.file.name}"&gt;&lt;/td&gt;
		&lt;td  scope="row"&gt;{$MOD.LBL_LOGGER_FILE_EXTENSION}&lt;/td&gt;
		&lt;td &gt;&lt;input name ="logger_file_ext" type="text" size="5" value="{$config.logger.file.ext}"&gt;&lt;/td&gt;
		&lt;td scope="row"&gt;{$MOD.LBL_LOGGER_FILENAME_SUFFIX}&lt;/td&gt;
		&lt;td &gt;&lt;select name = "logger_file_suffix" selected='{$config.logger.file.suffix}'&gt;{$filename_suffix}&lt;/select&gt;&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td scope="row"&gt;{$MOD.LBL_LOGGER_MAX_LOG_SIZE} &lt;/td&gt;
		&lt;td &gt; &lt;input name="logger_file_maxSize" size="4" value="{$config.logger.file.maxSize}"&gt;&lt;/td&gt;
		&lt;td scope="row"&gt;{$MOD.LBL_LOGGER_DEFAULT_DATE_FORMAT}&lt;/td&gt;
		&lt;td  &gt;&lt;input name ="logger_file_dateFormat" type="text" value="{$config.logger.file.dateFormat}"&gt;&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td scope="row"&gt;{$MOD.LBL_LOGGER_LOG_LEVEL} &lt;/td&gt;
		&lt;td &gt; &lt;select name="logger_level"&gt;{$log_levels}&lt;/select&gt;&lt;/td&gt;
		&lt;td scope="row"&gt;{$MOD.LBL_LOGGER_MAX_LOGS} &lt;/td&gt;
		&lt;td &gt; &lt;input name="logger_file_maxLogs" value="{$config.logger.file.maxLogs}"&gt;&lt;/td&gt;
	&lt;/tr&gt;
{/if}
	&lt;tr&gt;
	    &lt;td&gt;&lt;a href="index.php?module=Configurator&amp;action=LogView" target="_blank"&gt;{$MOD.LBL_LOGVIEW}&lt;/a&gt;&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;


&lt;div style="padding-top: 2px;"&gt;
&lt;input title="{$APP.LBL_SAVE_BUTTON_TITLE}" class="button primary"  type="submit" name="save" value="  {$APP.LBL_SAVE_BUTTON_LABEL}  " class="button primary"/&gt;
		&amp;nbsp;&lt;input title="{$MOD.LBL_SAVE_BUTTON_TITLE}"  class="button"  type="submit" name="restore" value="  {$MOD.LBL_RESTORE_BUTTON_LABEL} " /&gt;
		&amp;nbsp;&lt;input title="{$MOD.LBL_CANCEL_BUTTON_TITLE}"  onclick="document.location.href='index.php?module=Configurator&amp;action=index'" class="button"  type="button" name="cancel" value="  {$APP.LBL_CANCEL_BUTTON_LABEL}  " /&gt;
&lt;/div&gt;
{$JAVASCRIPT}

&lt;/form&gt;
&lt;div id='upload_panel' style="display:none"&gt;
    &lt;form id="upload_form" name="upload_form" method="POST" action='index.php' enctype="multipart/form-data"&gt;
        &lt;input type="file" id="my_file_&lt;paquete&gt;" name="file_1" size="20" onchange="uploadCheck('&lt;paquete&gt;')"/&gt;
        {sugar_getimage name="sqsWait" ext=".gif" alt=$mod_strings.LBL_LOADING other_attributes='id="loading_img_&lt;paquete&gt;" style="display:none" '}
    &lt;/form&gt;
&lt;/div&gt;
{if $error.&lt;paquete&gt;_logo}
&lt;script type='text/javascript'&gt;
{literal}$(function(){alert('{/literal}{$error.&lt;paquete&gt;_logo}{literal}');});{/literal}
&lt;/script&gt;
{/if}
{literal}
&lt;script type='text/javascript'&gt;
function init_logo(){
    document.getElementById('upload_panel').style.display="inline";
    document.getElementById('upload_panel').style.position="absolute";
    YAHOO.util.Dom.setX('upload_panel', YAHOO.util.Dom.getX('container_upload'));
    YAHOO.util.Dom.setY('upload_panel', YAHOO.util.Dom.getY('container_upload')-5);
}
YAHOO.util.Event.onDOMReady(function(){
    init_logo();
});
function toggleDisplay_2(div_string){
    toggleDisplay(div_string);
    init_logo();
}
 function uploadCheck(varname){
    //AJAX call for checking the file size and comparing with php.ini settings.
    var callback = {
        upload:function(r) {
            eval("var file_type = " + r.responseText);
            var forQuotes = varname;
            document.getElementById('loading_img_'+forQuotes).style.display="none";
            bad_image = SUGAR.language.get('Configurator',(forQuotes == 'quotes')?'LBL_ALERT_TYPE_JPEG':'LBL_ALERT_TYPE_IMAGE');
            switch(file_type['data']){
                case 'other':
                    alert(bad_image);
                    document.getElementById('my_file_' + forQuotes).value='';
                    break;
                case 'size':
                    alert(SUGAR.language.get('Configurator','LBL_ALERT_SIZE_RATIO'));
                    document.getElementById(forQuotes + "_logo").value=file_type['path'];
                    document.getElementById(forQuotes + "_logo_image").src=file_type['url'];
                    break;
                case 'file_error':
                    alert(SUGAR.language.get('Configurator','ERR_ALERT_FILE_UPLOAD'));
                    document.getElementById('my_file_' + forQuotes).value='';
                    break;
                //File good
                case 'ok':
                    document.getElementById(forQuotes + "_logo").value=file_type['url'];
                    document.getElementById(forQuotes + "_logo_image").src=file_type['url'];
                    break;
                //error in getimagesize because unsupported type
                default:
                   alert(bad_image);
                   document.getElementById('my_file_' + forQuotes).value='';
            }
            setTimeout(function() { init_logo(); }, 100);
        },
        failure:function(r){
            alert(SUGAR.language.get('app_strings','LBL_AJAX_FAILURE'));
        }
    }
    document.getElementById("&lt;paquete&gt;_logo").value='';
    document.getElementById('loading_img_&lt;paquete&gt;').style.display="inline";
    var file_name = document.getElementById('my_file_&lt;paquete&gt;').value;
    postData = '&amp;entryPoint=UploadImageFile&amp;section=&lt;paquete&gt;&amp;name=logo';
    YAHOO.util.Connect.setForm(document.getElementById('upload_form'), true,true);
    if(file_name){
        if(postData.substring(0,1) == '&amp;'){
            postData=postData.substring(1);
        }
        YAHOO.util.Connect.asyncRequest('POST', 'index.php', callback, postData);
    }
}

&lt;/script&gt;
{/literal}</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.sebaxtian.com/archivos/campo-para-una-imagen-en-la-configuracion/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Páginas de configuración y otra forma de agregar vistas</title>
		<link>https://www.sebaxtian.com/archivos/paginas-de-configuracion-y-otra-forma-de-agregar-vistas/</link>
					<comments>https://www.sebaxtian.com/archivos/paginas-de-configuracion-y-otra-forma-de-agregar-vistas/#respond</comments>
		
		<dc:creator><![CDATA[sebaxtian]]></dc:creator>
		<pubDate>Sat, 02 Apr 2016 14:48:06 +0000</pubDate>
				<category><![CDATA[Software y Hardware]]></category>
		<category><![CDATA[SuiteCRM]]></category>
		<guid isPermaLink="false">http://www.sebaxtian.com/?p=1742</guid>

					<description><![CDATA[Muchas veces he remarcado la importancia de replicar el lenguaje visual y de acciones en distintas páginas de un mismo sitio/programa/plataforma con el objeto de facilitar al usuario deducir nuevas acciones a partir de la experiencia que haya adquirido con el manejo del programa. La demostración más directa de lo que digo es que todos [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>Muchas veces he remarcado la importancia de replicar el lenguaje visual y de acciones en distintas páginas de un mismo sitio/programa/plataforma con el objeto de facilitar al usuario deducir nuevas acciones a partir de la experiencia que haya adquirido con el manejo del programa. La demostración más directa de lo que digo es que todos los usuarios usuario esperaría encontrar los procesos de configuración dentro del entorno de administración, y se extrañarían de que fuera de otra manera.</p>



<span id="more-1742"></span>



<p>Siguiendo con la lógica MVC de SuiteCRM encontramos que estas páginas de configuración son en realidad vistas de los módulos <strong>Administration</strong> y <strong>Configurator</strong>, que junto la clase <em>Configurator</em> ofrecen el grueso de funciones para acceder y administrar la información de configuración que vayamos creando.</p>



<p>En una anterior entrada explicaba la forma de crear una vista y la forma de emplear un controlador para llamar a la vista después de ejecutar las acciones, sin embargo para esta vez no crearemos directamente el controlador sino que declararemos el mapa con el cual vincular una acción a esta nueva vista, y por lo tanto al declarar la vista deberá incluirse la lógica funcional en su interior (es decir, MVC al carajo).</p>



<p>Empezamos creando la vista que deberá ir en el módulo <strong>Configurator</strong> y que deberá tener el nombre de la acción que ejecutará la vista de la página de configuración. <strong>Atención a las mayúsculas</strong> en la declaración de la Clase.</p>



<p>También preste especial atención a la diferencia entre &lt;acción&gt; y &lt;paquete&gt;, porque una cosa es la Acción para llamar a la página de configuración del paquete de módulos, y otra el paquete de módulos.</p>



<p><code>/&lt;directorio_crm&gt;/custom/modules/Configurator/views/view.&lt;acción&gt;.php </code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php

require_once('modules/Configurator/Configurator.php');

//Clase declaradora de la vista
class View&lt;Acción&gt; extends SugarView {
	
    /**
     * Acciones que se ejecutaran antes de visualizar la página 
     */
    public function preDisplay() {
        global $current_user;
		
		//Debe ser administrador, sino terminamos el proceso
        if (!is_admin($current_user)) {
            sugar_die("Unauthorized access to administration.");
        }
        
        //Declarar la instancia para administrar
        //la configuración
        $cfg = new Configurator();
		//Declarar los elementos de la configuración
		//en caso de aun no estar declarados
		if (!array_key_exists('&lt;paquete&gt;', $cfg-&gt;config)) {
		    $cfg-&gt;config['&lt;paquete&gt;'] = array(
		        'name' =&gt; '',
		        'code' =&gt; '',
		    );
			$cfg-&gt;saveConfig();
		}
        
        //Guardar configuración en caso de venir la solicitud
        if(isset($_REQUEST['save'])) {
			$cfg-&gt;saveConfig();
		}
    }

    /**
     * Función para visualizar la plantilla
     */
    public function display() {
		//Declarar que necestamos los elementos de traducción
        global $mod_strings, $app_strings;
        
        //Declarar la instancia para administrar
        //la configuración                
        $cfg = new Configurator();
        
        //Asignar elementos de traducción                 
		$this-&gt;ss-&gt;assign('MOD', $mod_strings);
        $this-&gt;ss-&gt;assign('APP', $app_strings);
        
        //Asinar elementos de configuración
        $this-&gt;ss-&gt;assign('config', $cfg-&gt;config);
        
        //Imprimir la plantilla		
		echo $this-&gt;ss-&gt;fetch('custom/modules/Configurator/tpls/&lt;acción&gt;.tpl');
       
    }
}
</code></pre>



<p>El siguiente paso es la declaración de la plantilla de la vista y su archivo de traducciones. Esta es una plantilla básica que no incluye procesos para chequear los, así que queda a consideración del usuario los métodos para declarar estas funciones.</p>



<p><code>/&lt;directorio_crm&gt;/custom/modules/Configurator/tpls/&lt;acción&gt;.tpl</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">{*

/*********************************************************************************
 * Vista para la administración de la información del centro comercial
 ********************************************************************************/

*}
&lt;div class='moduleTitle'&gt;
	&lt;h2&gt;{$MOD.LBL_&lt;ACCIÓN&gt;_SETTINGS}&lt;/h2&gt;
	&lt;div class='clear'&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;form name="&lt;Paquete&gt;Settings" enctype='multipart/form-data' method="POST" action="index.php" onSubmit="return (add_checks(document.&lt;Paquete&gt;Settings) &amp;&amp; check_form('&lt;Paquete&gt;Settings'));"&gt;
&lt;input type='hidden' name='action' value='&lt;acción&gt;'/&gt;
&lt;input type='hidden' name='module' value='Configurator'/&gt;
&lt;input type='hidden' name='save' value='true'/&gt;
&lt;span class='error'&gt;{$error.main}&lt;/span&gt;
&lt;table width="100%" cellpadding="0" cellspacing="1" border="0" class="actionsContainer"&gt;
&lt;tr&gt;

	&lt;td&gt;
		&lt;input title="{$APP.LBL_SAVE_BUTTON_TITLE}" accessKey="{$APP.LBL_SAVE_BUTTON_KEY}" class="button primary" id="&lt;Paquete&gt;Settings_save_button" type="submit"  name="save" value="  {$APP.LBL_SAVE_BUTTON_LABEL}  " &gt;
		&amp;nbsp;&lt;input title="{$MOD.LBL_SAVE_BUTTON_TITLE}"  id="&lt;Paquete&gt;Settings_restore_button"  class="button"  type="submit" name="restore" value="  {$MOD.LBL_RESTORE_BUTTON_LABEL}  " &gt;
		&amp;nbsp;&lt;input title="{$MOD.LBL_CANCEL_BUTTON_TITLE}" id="&lt;Paquete&gt;Settings_cancel_button"   onclick="document.location.href='index.php?module=Configurator&amp;action=index'" class="button"  type="button" name="cancel" value="  {$APP.LBL_CANCEL_BUTTON_LABEL}  " &gt; &lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;


&lt;table width="100%" border="0" cellspacing="1" cellpadding="0" class="edit view"&gt;
&lt;tr&gt;
	&lt;th align="left" scope="row" colspan="4"&gt;&lt;h4&gt;{$MOD.LBL_&lt;ACCIÓN&gt;_DESCRIPTION}&lt;/h4&gt;&lt;/th&gt;
&lt;/tr&gt;

	&lt;tr&gt;
		&lt;td scope="row" width='15%' nowrap&gt;{$MOD.LBL_&lt;PAQUETE&gt;_NAME} &lt;/td&gt;
		&lt;td width='35%'&gt;
			&lt;input type='text' name='&lt;paquete&gt;_name' value='{$config.&lt;paquete&gt;.name}'&gt;
		&lt;/td&gt;
		&lt;td scope="row" width='15%' nowrap&gt;{$MOD.LBL_&lt;PAQUETE&gt;_CODE} &lt;/td&gt;
		&lt;td width='35%'&gt;
			&lt;input type='text' name='&lt;paquete&gt;_code' value='{$config.&lt;paquete&gt;.code}'&gt;
		&lt;/td&gt;
	&lt;/tr&gt;

&lt;/table&gt;

&lt;div style="padding-top: 2px;"&gt;
&lt;input title="{$APP.LBL_SAVE_BUTTON_TITLE}" class="button primary"  type="submit" name="save" value="  {$APP.LBL_SAVE_BUTTON_LABEL}  " class="button primary"/&gt;
		&amp;nbsp;&lt;input title="{$MOD.LBL_SAVE_BUTTON_TITLE}"  class="button"  type="submit" name="restore" value="  {$MOD.LBL_RESTORE_BUTTON_LABEL} " /&gt;
		&amp;nbsp;&lt;input title="{$MOD.LBL_CANCEL_BUTTON_TITLE}"  onclick="document.location.href='index.php?module=Configurator&amp;action=index'" class="button"  type="button" name="cancel" value="  {$APP.LBL_CANCEL_BUTTON_LABEL}  " /&gt;
&lt;/div&gt;
{$JAVASCRIPT}

&lt;/form&gt;
</code></pre>



<p>El penúltimo paso es la declaración de la acción que ejecutará la vista y el archivo de traducciones.</p>



<p><code>/&lt;directorio_crm&gt;/custom/Extension/modules/Configurator/Ext/ActionViewMap/&lt;acción&gt;.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php
	$action_view_map['&lt;acción&gt;'] = '&lt;acción&gt;';</code></pre>



<p><code>/&lt;directorio_crm&gt;/custom/Extension/modules/Configurator/Ext/Language/en_us.&lt;paquete&gt;.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php
  
    $mod_strings['LBL_&lt;ACCIÓN&gt;_SETTINGS'] = 'Mall Settings';
    $mod_strings['LBL_&lt;ACCIÓN&gt;_DESCRIPTION'] = 'Change settings to adjust to the Mall';
    $mod_strings['LBL_&lt;PAQUETE&gt;_NAME'] = 'Mall Name:';
    $mod_strings['LBL_&lt;PAQUETE&gt;_CODE'] = 'Code:';</code></pre>



<p>El último paso es la declaración del acción para que se visualice en la página de administración y su correspondiente archivo de traducciones.</p>



<p>En el presente ejemplo agregaremos la acción al final de la sección del Sistema (segunda sección del administrador de SuiteCRM). Es posible crear una sección propia, pero en esta caso particular por tratarse de una única acción se vería como un desperdicio de espacio.</p>



<p>Este grupo de acciones se ejecutará en el módulo <strong><em>Administration</em></strong>.</p>



<p><code>/&lt;directorio_crm&gt;/custom/Extension/modules/Administration/Ext/Administration/&lt;paquete&gt;.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php
	$accion = array();
	$accion['&lt;acción&gt;'] = array(
		//Nombre del ícono. Los íconos disponibles están en ./themes/default/images
		'editlabels',
		
		//Nombre del enlace 
		'LBL_&lt;ACCIÓN&gt;_LINK_NAME',
		
		//Descripción
		'LBL_&lt;ACCIÓN&gt;_LINK_DESCRIPTION',
		
		//URL del enlace
		'./index.php?module=Configurator&amp;action=&lt;acción&gt;',
	);
	
	$admin_group_header[1][3]['&lt;paquete&gt;'] = $accion;</code></pre>



<p><code>/&lt;directorio_crm&gt;/custom/Extension/modules/Administration/Ext/Language/en_us.&lt;paquete&gt;.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php
	$mod_strings['LBL_&lt;ACCIÓN&gt;_LINK_NAME'] = 'Package Settings';
	$mod_strings['LBL_&lt;ACCIÓN&gt;_LINK_DESCRIPTION'] = 'Change settings to adjust the package';</code></pre>



<p>Paso final, ejecutamos la acción de “<strong>Reparar y Reconstruir</strong>“, y ya tendremos el botón para administrar.</p>



<h2 class="wp-block-heading">¿Y cómo usar la configuración almacenada?</h2>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php
 
//Declaramos el uso de la configuración
require_once('modules/Configurator/Configurator.php');
$cfg = new Configurator();

//Extraemos la configuración del paquete
$cfg_&lt;paquete&gt; = $cfg-&gt;config['&lt;paquete&gt;'];
</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.sebaxtian.com/archivos/paginas-de-configuracion-y-otra-forma-de-agregar-vistas/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Crear PDF con páginas de tamaño dinámico</title>
		<link>https://www.sebaxtian.com/archivos/crear-pdf-con-paginas-de-tamano-dinamico/</link>
					<comments>https://www.sebaxtian.com/archivos/crear-pdf-con-paginas-de-tamano-dinamico/#respond</comments>
		
		<dc:creator><![CDATA[sebaxtian]]></dc:creator>
		<pubDate>Fri, 25 Mar 2016 00:31:27 +0000</pubDate>
				<category><![CDATA[Software y Hardware]]></category>
		<guid isPermaLink="false">http://www.sebaxtian.com/?p=1731</guid>

					<description><![CDATA[La mayoría de los PDF tienen páginas del mismo tamaño, pero en casos particulares&#160;se hace evidente la necesidad de buscar una alternativa dinámica. como por ejemplo la impresión de varias boletas con una impresora para facturas&#160;que hace el corte según el tamaño de cada página. El siguiente código emplea la librería mpdf para generar el [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>La mayoría de los PDF tienen páginas del mismo tamaño, pero en casos particulares&nbsp;se hace evidente la necesidad de buscar una alternativa dinámica. como por ejemplo la impresión de varias boletas con una impresora para facturas&nbsp;que hace el corte según el tamaño de cada página.</p>



<span id="more-1731"></span>



<p>El siguiente código emplea la librería mpdf para generar el documento, una plantilla sobre la cuál se irán generando las distintas boletas, y dos arreglos con los títulos y párrafos para cada versión.</p>



<p>El algoritmo maneja una instancia del generador de documentos PDF que será la encargada del documento final, y otra donde se irá creando cada nueva boleta y que será empleado para conocer el tamaño del documento impreso. Una vez conocida esta información crearemos una nueva página con este tamaño dentro de la instancia que almacena el documento final, de tal manera que en esta nueva página quepa la nueva boleta.</p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php
	//Librerías para generador de pdf y parser de plantillas
	require('libs/mpdf/mpdf.php');
	
	//PDF en donde imprimiremos las boletas
	$pdf = new mPDF('en', array('74mm', '100mm'), 8, '', 5, 5, 4, 4);
	
	//Sistema de Plantilla
	$template = "&lt;style&gt;
			div.pagina { /*font-family: DejaVuSansMono;*/ } 
			h1, table.logos td { text-align: center; } 
			table.logos { width: 100%; }
		&lt;/style&gt;
		&lt;div class='pagina'&gt;
			&lt;h3&gt;%titulo%&lt;/h3&gt;
			%parrafo%
		&lt;/div&gt;";
	
	//Títulos
	$titulos = array(
		'Vivamus elementum rutrum mattis',
		'Donec consequat aliquet lorem vel',
		'Fusce mattis tellus vel nulla'
	);
		
	//Párrafos
	$parrafos = array(
		'Pellentesque tellus diam, sollicitudin a semper ac, semper et augue. Duis orci nulla, ornare a orci ut, consequat tempus lorem. Morbi venenatis nisi et rutrum ultricies. Nulla pulvinar posuere malesuada. Aliquam id odio feugiat, mattis ante ac, viverra magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut libero dui, eleifend at pulvinar sed, volutpat eu augue. Nullam non metus non felis suscipit pulvinar. Praesent at magna ex. Etiam blandit diam id interdum faucibus. Phasellus ac blandit nulla, id scelerisque lacus. Donec hendrerit eros in bibendum lobortis. Nulla tincidunt tellus at erat pulvinar tempus a commodo mauris. Vestibulum blandit arcu justo, non ultrices elit egestas quis. Integer fringilla non risus quis ullamcorper.',
		'Donec pulvinar, enim eu interdum egestas, nibh nunc pellentesque enim, quis egestas quam urna id ligula. Curabitur lobortis felis ex, vel viverra est feugiat ut. Fusce sed nisi et turpis consequat eleifend. Curabitur congue rhoncus volutpat. Suspendisse purus leo, euismod at malesuada ac, commodo a ex. Pellentesque consectetur mi facilisis ullamcorper consequat.',
		'Aenean in nisi sit amet magna congue dapibus at nec nulla. Nullam sit amet nisi magna. Curabitur egestas tellus lectus, eget malesuada enim imperdiet non. Cras vestibulum nunc ut aliquam dignissim. Nunc consequat sed tellus a elementum. Phasellus pharetra erat et metus consectetur sollicitudin. Praesent aliquet sed neque hendrerit tempor. Suspendisse lobortis id lacus et sollicitudin. Proin et justo ac mauris pellentesque egestas. Maecenas sit amet nisl vitae mauris luctus mollis. '
	);
	
	//Crearemos tantas páginas como items hay en el 
	//arreglo de nombres
	foreach( $titulos as $key =&gt; $titulo ) {
		//PDF con el que calcularemos el tamaño de la página
		$pdf_aux = new mPDF('en', array('74mm', '100mm'), 8, '', 5, 5, 4, 4);
		
		//Reemplazar las variables y obtener el HTML para esta página
		$data = str_replace(
			array('%titulo%', '%parrafo%'),
			array($titulos[$key] ,$parrafos[$key]),
			$template
		);
		
		//Agregar los datos al pdf de referencia 
		$pdf_aux-&gt;WriteHTML($data);
		//Calcular el tamaño de la zona impresa como
		//Total de páginas impresas completas multiplicado por la zona 
		//que se puede imprimir en cada página, mas la zona que se uso
		//en la última hoja mas el margen inferior
		$calculado = ((($pdf_aux-&gt;page - 1) * ($pdf_aux-&gt;fh - $pdf_aux-&gt;lMargin - $pdf_aux-&gt;bMargin)) + ($pdf_aux-&gt;y)) + $pdf_aux-&gt;bMargin;
		
		//Añadir al pdf que vamos a imprimir la nueva página
		//usando el tamaño calculado
		$pdf-&gt;AddPageByArray( 
			array(
				'orientation' =&gt; 'P',
				'sheet-size' =&gt; array('74mm', $calculado.'mm') 
			)
		);
		//Teniendo ya la nueva página, agregar
		//los datos
		$pdf-&gt;WriteHTML($data);
	}
	
	//Descargar PDF
	$pdf-&gt;Output();
?&gt;</code></pre>
]]></content:encoded>
					
					<wfw:commentRss>https://www.sebaxtian.com/archivos/crear-pdf-con-paginas-de-tamano-dinamico/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Puntos de entrada</title>
		<link>https://www.sebaxtian.com/archivos/puntos-de-entrada/</link>
					<comments>https://www.sebaxtian.com/archivos/puntos-de-entrada/#respond</comments>
		
		<dc:creator><![CDATA[sebaxtian]]></dc:creator>
		<pubDate>Wed, 23 Mar 2016 18:27:54 +0000</pubDate>
				<category><![CDATA[Software y Hardware]]></category>
		<category><![CDATA[SuiteCRM]]></category>
		<guid isPermaLink="false">http://www.sebaxtian.com/?p=1688</guid>

					<description><![CDATA[La lógica de los controles y las vistas en el Framework MVC de SuiteCRM (y en general en cualquier otro framework que siga este modelo) es ejecutar una acción y presentar los resultados en una vista, pero para aquellas circunstancias en donde el objetivo no es la visualización de una vista sino la generación de un archivo PDF para imprimir, agregar datos a una encuesta o generar un correo en forma automática a partir de de los datos de un formulario en un sitio web externo se debe usarotra aproximación.]]></description>
										<content:encoded><![CDATA[
<p>La lógica de los controles y las vistas en el Framework MVC de SuiteCRM (y en general en cualquier otro framework que siga este modelo) es ejecutar una acción y presentar los resultados en una vista, pero para aquellas circunstancias en donde el objetivo no es la visualización de una vista sino la generación de un archivo PDF para imprimir, agregar datos a una encuesta o generar un correo en forma automática a partir de de los datos de un formulario en un sitio web externo se debe usarotra aproximación.</p>



<p>Para estos casos SuiteCRM emplean los EntryPoints, que son descritos como páginas de acceso al entorno SuiteCRM.</p>



<p>El primer paso será la creación del script que atenderá los llamados al EntryPoint, archivo que puede ubicarse en cualquier lugar pero que se recomienda ubicar en<strong>:</strong></p>



<p><code>/&lt;directorio_crm&gt;/custom/&lt;nombrePuntoAcceso&gt;.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
	
	// Código de la acción a ejecutar
	$date = new DateTime();
	echo $date-&gt;format('c');</code></pre>



<p>Este script podrá recibir variables por GET o por POST (igual que cualquier otro script php), incluirá el entorno global ( por ejemplo <em>$this</em> ) y tendremos además acceso a todas las primitivas de SuiteCRM para administrar las instancias de los módulos.</p>



<p>La primera línea es de vital importancia, porque es la que evita que el script sea usado tras un llamado directo desde el navegador.</p>



<p>Una vez tenemos el script que atenderá el llamado al punto de entrada, es hora de crear el elemento con el cual se declara el EntryPoint.</p>



<p>Nóte que los puntos de entrada se declaran dentro de la aplicación, y no dentro de un módulo.</p>



<p>Una vez tengamos el archivo disponible, agregaremos una única línea para declarar el punto de ingreso.</p>



<p><code>/&lt;directorio_crm&gt;/custom/Extension/application/Ext/EntryPointRegistry/&lt;nombrePuntoAcceso&gt;.php</code></p>



<pre class="wp-block-code"><code lang="php" class="language-php">&lt;?php
	$entry_point_registry['&lt;nombrePuntoAcceso&gt;'] = array(
			'file' =&gt; 'custom/&lt;nombrePuntoAcceso&gt;.php',
			'auth' =&gt; false,
		);</code></pre>



<p>El arreglo tiene dos variables, una es la ubicación del archivo que atenderá el llamado al punto de entrada, y la otra indica si se permite usar el punto de entrada sin haber iniciado sesión. Una vez se tengan los dos archivos será hora de ejecutar la acción de “<strong>Reparar y Reconstruir</strong>“, y paso seguido tendremos disponible el llamado usando la siguiente URL<br>
<code>http://&lt;host_crm&gt;/&lt;directorio_crm&gt;/index.php?entryPoint=&lt;nombrePuntoAcceso&gt;</code></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.sebaxtian.com/archivos/puntos-de-entrada/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Agregar acciones en menús</title>
		<link>https://www.sebaxtian.com/archivos/agregar-acciones-en-menus/</link>
					<comments>https://www.sebaxtian.com/archivos/agregar-acciones-en-menus/#respond</comments>
		
		<dc:creator><![CDATA[sebaxtian]]></dc:creator>
		<pubDate>Tue, 22 Mar 2016 18:25:25 +0000</pubDate>
				<category><![CDATA[Software y Hardware]]></category>
		<category><![CDATA[SuiteCRM]]></category>
		<guid isPermaLink="false">http://www.sebaxtian.com/?p=1698</guid>

					<description><![CDATA[SuiteCRM hace una separación interesante en cuanto a dónde ubicar cada elemento para que al cargar el proceso de "Reparación y Reconstrucción Rápida" todos los nuevos elementos encajen en su lugar. Es como un tablero donde hay agujeros con formas especiales para cada tipo de pieza, de tal manera que solo las piezas ubicadas correctamente pasarán hasta la caja recolectora.]]></description>
										<content:encoded><![CDATA[<div class='indizar' id='right' style='width: 250px; float: right;'><ul><li>Agregar acciones en menús</li><li><a href='https://www.sebaxtian.com/archivos/agregar-acciones-en-menus/chapter/2/'>Crear el menú lateral para la acción</a></li><li><a href='https://www.sebaxtian.com/archivos/agregar-acciones-en-menus/chapter/3/'>Acción al visualizar en detalle</a></li><li><a href='https://www.sebaxtian.com/archivos/agregar-acciones-en-menus/chapter/4/'>Acción para lista de selección</a></li></ul></div>


<figure class="wp-block-image alignleft"><a href="https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/03/WyzSuuJ.png" rel="attachment wp-att-1631"><img loading="lazy" decoding="async" width="150" height="150" src="https://www.sebaxtian.com/wordpress/wp-content/uploads/2016/03/WyzSuuJ-150x150.png" alt="Hacia allá va el mundo" class="wp-image-1631"/></a></figure>



<p>SuiteCRM hace una separación interesante en cuanto a dónde ubicar cada elemento para que al cargar el proceso de «<strong>Reparación y Reconstrucción Rápida</strong>» todos los nuevos elementos encajen en su lugar. Es como un tablero donde hay agujeros con formas especiales para cada tipo de pieza, de tal manera que solo las piezas ubicadas correctamente pasarán hasta la caja recolectora. Acá entre nos esa forma de ver el mundo me perturba porque al fin de cuentas estos límites son funcionales para las interfaces de usuario en donde facilitar el lenguaje visual es un paso lógico en su diseño, pero limitar al desarrollador para que solo pueda usar las formas predefinidas sin permitirle agregar nuevas es como decirle a un escritor que escriba un libro usando tarjetas con frases preescritas. Pero como el modelo de programación a seguir para hacer buen uso de SuiteCRM es este, entonces así se deberá hacer.</p>



<p>Para la muestra agreguemos un botón.</p>


 <p class='indizar scroll'><strong>Cap&iacute;­tulos:</strong> | 1 | <a href='https://www.sebaxtian.com/archivos/agregar-acciones-en-menus/chapter/2/'>2</a> | <a href='https://www.sebaxtian.com/archivos/agregar-acciones-en-menus/chapter/3/'>3</a> | <a href='https://www.sebaxtian.com/archivos/agregar-acciones-en-menus/chapter/4/'>4</a> | <a href='https://www.sebaxtian.com/archivos/agregar-acciones-en-menus/chapter/2/'>Siguiente</a> |</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.sebaxtian.com/archivos/agregar-acciones-en-menus/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
