How to add an application to SaltOS

To add an application jumps to do the following:

  • Create the XML file of the application (xml/example.xml for example)
  • Add the tables that use the application (xml/dbschema.xml)
  • Add the application in tbl_aplicaciones (xml/dbstatic.xml)
  • Add permissions for the application tbl_aplicaciones_p (xml/db_static.xml)
  • Add the application to the menu (xml/menu.xml)
  • Add application icons (xml/iconset.xml)
  • Add the application text (xml/lang/es_ES.xml, for example if you just want in Spanish for Spain)

Create the XML file for the application

The XML file that defines an application may contain the following nodes:

  • <list>
  • <form>
  • <insert>
  • <update>
  • <delete>
  • <pdf>
  • <excel>

Although not necessary that contains all. For example, if the application xml/documentos.xml seen, this does not include nodes <pdf>  neither <excel>However, the xml/partes.xml application if it includes all these options.

Node <list>

This node is defined as the list of the application. Below is a snippet (modified) with the options shown may contain:

  <list>
  	<title lang="true">list</title>
  	<actions include="xml/common/actions.xml" replace="true"/>
  	<width>100%</width>
  	<fields>
  		<field>
  			<name>id</name>
  			<label lang="true">id</label>
  			<tip>My tip message</tip>
  			<width>100px</width>
  			<sort>true</sort>
  			<order>id</order>
  			<orderasc>id</orderasc>
  			<orderdesc>id</orderdesc>
  			<size>20</size>
  			<class>mycssclass</class>
  		</field>
  		<field>
  			.
  			.
  			.
  		</field>
  		.
  		.
  		.
  	</fields>
  	<javascript>
  		.
  		.
  		.
  	</javascript>
  	<styles>
  		.
  		.
  		.
  	</styles>
  	<quick>
  		<row>
  			<field>
  				<type>button</type>
  				<value lang="true">create</value>
  				<tip lang="true">create</tip>
  				<onclick>create()</onclick>
  				<icon>create</icon>
  				<class>nowrap contextmenu</class>
  				<class2>shortcut_ctrl_insert</class2>
  				<disabled global="page" eval="true">check_user($page,"create")?"false":"true"</disabled>
  			</field>
  			<field>
  				<type>separator</type>
  				<width>100%</width>
  			</field>
  			<field ifeval="!ismobile() &amp;&amp; check_filter(array('campo1'=>'','comentarios'=>''))">
  				<type>label</type>
  				<label lang="true">usedfilter</label>
  				<class>nowrap</class>
  				<class2>info</class2>
  			</field>
  			<field>
  				<name>buscar</name>
  				<label lang="true">buscar</label>
  				<type>text</type>
  				<width>240px</width>
  				<value global="comentarios" eval="true">$comentarios=getParam("comentarios")</value>
  				<onchange>copy_value("comentarios","buscar");</onchange>
  				<onkey>if(is_enterkey(event)) { copy_value("comentarios","buscar");buscar(); }</onkey>
  				<focus>true</focus>
  				<speech>true</speech>
  				<class3>shortcut_ctrl_f</class3>
  			</field>
  			<field>
  				<type>button</type>
  				<value lang="true">buscar</value>
  				<tip lang="true">buscartip</tip>
  				<onclick>buscar()</onclick>
  				<icon>find</icon>
  				<class>nowrap</class>
  			</field>
  			<field>
  				<type>button</type>
  				<value lang="true">limpiar</value>
  				<tip lang="true">limpiartip</tip>
  				<onclick>limpiar()</onclick>
  				<icon>refresh</icon>
  				<class>nowrap contextmenu</class>
  			</field>
  		</row>
  	</quick>
  	<form>
  		<name>list</name>
  		<action></action>
  		<method>get</method>
  		<fields>
  			<title lang="true">filter</title>
  			<buttons>true</buttons>
  			<row>
  				<field include="xml/common/hiddenslist.xml" replace="true" />
  				<field>
  					<name>id_usuario</name>
  					<label lang="true">username</label>
  					<type>text</type>
  					<width>240px</width>
  					<value global="id_usuario" eval="true">$id_usuario=getParam("id_usuario")</value>
  					<colspan>3</colspan>
  				</field>
  				<field>
  					<type>separator</type>
  					<width>10px</width>
  				</field>
  				<field>
  					<name>id_cliente</name>
  					<label lang="true">cliente</label>
  					<type>text</type>
  					<width>240px</width>
  					<value global="id_cliente" eval="true">$id_cliente=getParam("id_cliente")</value>
  					<colspan>4</colspan>
  					<onchange>myjsfunction()</onchange>
  				</field>
  			</row>
  			<row>
  				.
  				.
  				.
  			</row>
  			.
  			.
  			.
  		</fields>
  		<buttons include="xml/common/buttonslist.xml" />
  	</form>
  	<pager include="xml/common/pagerlist.xml"/>
  	<query global="page,comentarios" eval="true">"
  	SELECT id,
  		campo1,
  		campo2,
  		campo3,
  		campo4,
  		campo5,
  	FROM (SELECT a.*
  		FROM tbl_example a
  		LEFT JOIN tbl_registros_i e ON e.id_aplicacion='".page2id($page)."' AND e.id_registro=a.id
  		LEFT JOIN tbl_usuarios b ON e.id_usuario=b.id
  		WHERE a.id IN (SELECT id_registro
  			FROM tbl_indexing
  			WHERE id_aplicacion='".page2id($page)."'
  			AND ".make_fulltext_query("search",$comentarios,"tbl_indexing")."
  		)
  	) d
  	WHERE ".check_sql($page,"list")</query>
  	<order global="order" eval="true">$order</order>
  	<limit global="limit" eval="true">$limit</limit>
  	<offset global="offset" eval="true">$offset</offset>
  </list>

Node <form>

This node is defined as the application form. Depending on whether it is a form to make an inquiry, to create a new record or update an existing record, you must configure the different views on the nodes <view>, <insert>  Y <update>. Below is a snippet (modified) with the options shown may contain:

  <form>
  	<views>
  		<view>
  			<title lang="true">formview</title>
  			<query>
  				<query include="xml/common/qpermview.xml" replace="true" />
  				<query include="xml/common/qdefaultview.xml" replace="true" />
  				<comments_old include="xml/common/qcommentsold.xml" />
  				<files_old include="xml/common/qfilesold.xml" />
  				<control include="xml/common/qcontrol.xml"/>
  				<folders include="xml/common/qfolders.xml" />
  			</query>
  		</view>
  		<insert>
  			<title lang="true">forminsert</title>
  			<query>
  				<query include="xml/common/qpermcreate.xml" replace="true" />
  				<default>SELECT '0' id,'0' id_cliente,'0' id_proyecto</default>
  				<files_new include="xml/common/qfilesnew.xml" replace="true"/>
  				<folders include="xml/common/qfolders.xml" />
  			</query>
  		</insert>
  		<update>
  			<title lang="true">formupdate</title>
  			<query>
  				<query include="xml/common/qpermupdate.xml" replace="true" />
  				<query include="xml/common/qdefaultview.xml" replace="true" />
  				<comments_old include="xml/common/qcommentsold.xml" />
  				<files_old include="xml/common/qfilesold.xml" />
  				<comments_new include="xml/common/qcommentnew.xml" replace="true"/>
  				<files_new include="xml/common/qfilesnew.xml" replace="true"/>
  				<control include="xml/common/qcontrol.xml"/>
  				<folders include="xml/common/qfolders.xml" />
  			</query>
  		</update>
  	</views>
  	<name>form</name>
  	<action></action>
  	<method>post</method>
  	<hiddens include="xml/common/hiddensform.xml" />
  	<fields>
  		<default>
  			<fieldset>
  				<title lang="true">defaultdata</title>
  				<quick global="id" eval="true">$id>=0?"false":"true"</quick>
  				<buttons>true</buttons>
  				<row>
  					<field>
  						<name>id</name>
  						<type>hidden</type>
  					</field>
  					<field>
  						<name>id_cliente</name>
  						<label lang="true">cliente</label>
  						<type>select</type>
  						<width>240px</width>
  						<query eval="true">make_extra_query_with_perms("clientes","tbl_clientes","nombre")." UNION ..."</query>
  						<focus global="id" eval="true">$id>=0?"true":"false"</focus>
  						<readonly global="id" eval="true">$id>=0?"false":"true"</readonly>
  						<onchange>update_proyectos()</onchange>
  						<link>openapp('clientes',-abs(ID))</link>
  						<icon>view</icon>
  					</field>
  				</row>
  				<row>
  					<field>
  						<name>id_proyecto</name>
  						<label lang="true">proyecto</label>
  						<type>select</type>
  						<width>240px</width>
  						<query eval="true">"SELECT '' value,'".LANG_ESCAPE("sinproyecto")."' label"</query>
  						<readonly global="id" eval="true">$id>=0?"false":"true"</readonly>
  						<colspan>3</colspan>
  						<link>openapp('proyectos',-abs(ID))</link>
  						<icon>view</icon>
  					</field>
  				</row>
  				<row>
  					<field>
  						<name>nombre</name>
  						<label lang="true">nombre</label>
  						<type>text</type>
  						<width>240px</width>
  						<readonly global="id" eval="true">$id>=0?"false":"true"</readonly>
  						<required>true</required>
  						<speech>true</speech>
  					</field>
  				</row>
  				<row>
  					<field>
  						<type>separator</type>
  					</field>
  				</row>
  				<row>
  					<field>
  						<name>descripcion</name>
  						<label lang="true">descripcion</label>
  						<type>textarea</type>
  						<width>600px</width>
  						<height>120px</height>
  						<colspan>6</colspan>
  						<readonly global="id" eval="true">$id>=0?"false":"true"</readonly>
  						<required>true</required>
  					</field>
  				</row>
  			</fieldset>
  		</default>
  		<comments_old include="xml/common/commentsold.xml"/>
  		<files_old include="xml/common/filesold.xml" />
  		<comments_new include="xml/common/commentnew.xml"/>
  		<files_new include="xml/common/filesnew.xml"/>
  		<control include="xml/common/control.xml" />
  		<folders include="xml/common/folders.xml" />
  	</fields>
  	<quick include="xml/common/quickform.xml" />
  	<buttons include="xml/common/buttonsform.xml" />
  	<javascript>
  		<javascript include="xml/common/jsform.xml" replace="true"/>
  		<cache>
  			<include>js/updateproyectos.js</include>
  		</cache>
  	</javascript>
  </form>

Node <insert>

This node defines the list of queries necessary to make an insert, that is, to create a record. Below is a snippet (modified) with the options shown may contain:

  <insert>
  	<query include="xml/common/qpermcreate.xml" replace="true"/>
  	<query include="xml/common/autonombre.xml" replace="true"/>
  	<query match="default" prefix="true" global="page" preeval="true" eval="true">preeval_insert_query(page2table($page))</query>
  	<query include="xml/common/qcontrolinsert.xml" replace="true"/>
  	<query include="xml/common/qfilesinsert.xml" replace="true"/>
  	<query include="xml/common/qfoldersinsert.xml" replace="true"/>
  </insert>

Node <update>

This node defines the list of queries necessary to make an update, that is, to modify a record. Below is a snippet (modified) with the options shown may contain:

  <update>
  	<query include="xml/common/qpermupdate.xml" replace="true"/>
  	<query match="default" prefix="true" global="page" preeval="true" eval="true">preeval_update_query(page2table($page))</query>
  	<query include="xml/common/qcontrolupdate.xml" replace="true"/>
  	<query include="xml/common/qfilesdelete.xml" replace="true" />
  	<query include="xml/common/qfilesupdate.xml" replace="true"/>
  	<query include="xml/common/qcommentsdelete.xml" replace="true" />
  	<query include="xml/common/qcommentsinsert.xml" replace="true" />
  	<query include="xml/common/qfoldersupdate.xml" replace="true"/>
  </update>

Node <delete>

This node defines the list of queries necessary to make a delete, that is, to delete a record. Below is a snippet (modified) with the options shown may contain:

  <delete>
  	<query include="xml/common/qpermdelete.xml" replace="true"/>
  	<query include="xml/common/qdelete.xml" replace="true" />
  	<query include="xml/common/qdeletecomments.xml" replace="true" />
  	<query include="xml/common/qdeletefiles.xml" replace="true" />
  	<query include="xml/common/qcontroldelete.xml" replace="true"/>
  	<query include="xml/common/qfoldersdelete.xml" replace="true"/>
  </delete>

Node <pdf>

This node defines the query and graphic design need to generate a PDF. Below is a snippet (modified) with the options shown may contain:

  <pdf>
  	<constructor>"P","mm","A4"</constructor>
  	<margins>50,30,30,30</margins>
  	<query>"SELECT CASE id_cliente ... FROM (SELECT a.*,
  	".make_extra_query_with_login("b.")." usuario, ...
  	FROM tbl_partes a
  	LEFT JOIN tbl_registros_i e ON e.id_aplicacion='".page2id(getParam("page"))."' AND e.id_registro=a.id
  	LEFT JOIN tbl_usuarios b ON e.id_usuario=b.id LEFT JOIN tbl_clientes c ON a.id_cliente=c.id
  	LEFT JOIN tbl_proyectos p ON a.id_proyecto=p.id) d
  	WHERE ROUND(id) IN (".check_ids(getParam("id")).")"</query>
  	<foreach>
  		<!-- TEMPLATE -->
  		<header>
  			<!-- LOGO -->
  			<margins>0,0,0,0</margins>
  			<image>CONFIG("logo_left"),CONFIG("logo_top"), ...)</image>
  			<!-- PONER MARCA DE AGUA -->
  			<font>"normal","B",CONFIG("water_partes_size"),"#eeeeee"</font>
  			<text>CONFIG("water_partes_posx"),CONFIG("water_partes_posy"), ...</text>
  			<margins>50,30,30,30</margins>
  			<setxy>30,50</setxy>
  		</header>
  		<footer>
  			<margins>0,0,0,0</margins>
  			<!-- PONER PIE DE PAGINA -->
  			<font>"normal","",6,CONFIG("color_text2")</font>
  			<pageno>30,279,150,0,"R",LANG("paginaspc"),LANG("spcdespc")</pageno>
  			<margins>50,30,30,30</margins>
  		</footer>
  		<!-- BEGIN -->
  		<newpage></newpage>
  		<color>CONFIG("color_line"),"#000000"</color>
  		<!-- VOLCAR DATOS -->
  		<setxy>30,50</setxy>
  		<getxy>"x","y"</getxy>
  		<font>"normal","B",8,CONFIG("color_text1")</font>
  		<textarea>30,$row["y"],25,4,"R",LANG("parte")</textarea>
  		<font>"normal","",8,CONFIG("color_text2")</font>
  		<textarea>55,$row["y"],75,4,"L",$row["id"]</textarea>
  		<font>"normal","B",8,CONFIG("color_text1")</font>
  		<textarea>105,$row["y"],25,4,"R",LANG("userinsert")</textarea>
  		<font>"normal","",8,CONFIG("color_text2")</font>
  		<textarea>130,$row["y"],75,4,"L",$row["usuario"]</textarea>
  		.
  		.
  		.
  	</foreach>
  	<output>encode_bad_chars(str_replace(".","",strpos(check_ids(getParam("id")),",")===false?execute_query("
  	SELECT CONCAT('".LANG_ESCAPE("parte")."',' ',LPAD(id,".intval(CONFIG("zero_padding_digits")).",0),' ',tarea) subject
  	FROM tbl_partes WHERE id IN (".check_ids(getParam("id")).")"):LANG("partes"))).getDefault("exts/pdfext",".pdf")</output>
  </pdf>

Node <excel>

This node defines the query required to generate a file in Excel format. Below is a snippet (modified) with the options shown may contain:

  <excel>
  	<query require="php/listsim.php" global="page" eval="true">"
  	SELECT
  		CONCAT(''',LPAD(a.id,".intval(CONFIG("zero_padding_digits")).",0)) '".LANG_ESCAPE("codigo")."',
  		CASE a.id_cliente WHEN '0' THEN '".LANG_ESCAPE("sincliente")."' ELSE c.nombre END '".LANG_ESCAPE("cliente")."',
  		CASE a.id_proyecto WHEN '0' THEN '".LANG_ESCAPE("sinproyecto")."' ELSE p.nombre END '".LANG_ESCAPE("proyecto")."',
  		tarea '".LANG_ESCAPE("tarea")."',
  		fecha '".LANG_ESCAPE("date")."',
  		horas '".LANG_ESCAPE("horas")."',
  		precio '".LANG_ESCAPE("precio")."',
  		total '".LANG_ESCAPE("total")."',
  		CASE liquidado WHEN 1 THEN '".LANG_ESCAPE("yes")."' ELSE '".LANG_ESCAPE("no")."' END '".LANG_ESCAPE("liquidado")."',
  		fecha2 '".LANG_ESCAPE("dateliq")."'
  	FROM tbl_partes a
  	LEFT JOIN tbl_clientes c ON a.id_cliente=c.id
  	LEFT JOIN tbl_proyectos p ON a.id_proyecto=p.id
  	WHERE a.id IN (".list_simulator($page).")"</query>
  </excel>

Node <javascript>

This node appears at nodes <list>  Y <form>  and JavaScript code to define who want to run for that screen. Below is a snippet (modified) with the options shown may contain:

  <javascript>
  	<javascript include="xml/common/jslist.xml" replace="true" />
  	<function>myfunction(args) { alert(args); }</function>
  	<inline>alert('myfunction executed now');</inline>
  	<include>js/myjsfile.js</include>
  </javascript>

Node <styles>

This node appears at nodes <list>  Y <form>  and to define CSS codes that you want to include for that screen. Below is a snippet (modified) with the options shown may contain:

  <styles>
  	<inline>.mystyle { color:#336699; };</inline>
  	<include>css/mycssfile.css</include>
  </styles>

Add the tables that use the application (xml/dbschema.xml)

This file defines the database schema will use jumps. When you modify this file, SaltOS seeks to differences between the schema specified in this file and the schema's in the database. When it finds changes, make SQL queries necessary to modify the schema without losing data. Below is a snippet (modified) with the options shown may contain:

  <tables>
  	.
  	.
  	.
  	<table>
  		<name>tbl_documentos</name>
  		<fields>
  			<field>
  				<name>id</name>
  				<type>/*MYSQL INT(11) *//*SQLITE INTEGER */</type>
  				<pkey>true</pkey>
  			</field>
  			<field>
  				<name>id_cliente</name>
  				<type>INT(11)</type>
  				<fkey>tbl_clientes</fkey>
  			</field>
  			<field>
  				<name>nombre</name>
  				<type>VARCHAR(255)</type>
  			</field>
  			<field>
  				<name>descripcion</name>
  				<type>TEXT</type>
  			</field>
  			<field>
  				<name>id_proyecto</name>
  				<type>INT(11)</type>
  				<fkey>tbl_proyectos</fkey>
  			</field>
  		</fields>
  	</table>
  	.
  	.
  	.
  </tables>

Add the application in tbl_aplicaciones (xml/dbstatic.xml)

This file defines the data of the master tables of jumps. When you edit this file, the master tables SaltOS updated with the new data. Here's how to add an application to the master table tbl_aplicaciones. Below is a snippet (modified) with the options shown may contain:

  <tbl_aplicaciones>
  	.
  	.
  	.
  	<row>
  		<id>100</id>
  		<codigo>examples</codigo>
  		<nombre>Ejemplos</nombre>
  		<tabla>tbl_example</tabla>
  		<campo>nombre</campo>
  	</row>
  	.
  	.
  	.
  </tbl_aplicaciones>

Add permissions for the application in tbl_aplicaciones_p (xml/db_static.xml)

This file defines the data of the master tables of jumps. When you edit this file, the master tables SaltOS updated with the new data. Here's how to add an application to the master table tbl_aplicaciones_p allowing SaltOS relate to permissions can have each application, as shown in the tab permits applications users and groups. Below is a snippet (modified) with the options shown may contain:

  <tbl_aplicaciones_p>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>10</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>3</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>1</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>8</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>4</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>5</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>11</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>12</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>13</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>14</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>2</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>9</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>6</id_permiso>
  	</row>
  	<row>
  		<id_aplicacion>6</id_aplicacion>
  		<id_permiso>7</id_permiso>
  	</row>
  </tbl_aplicaciones_p>

The permission list is defined in the table tbl_permisos is also found in the xml/dbstatic.xml file. If you want to add more permissions, simply add the permission on the tbl_permisos and create in tbl_aplicaciones_p table shows the relationship between the new license and the new application.

Add the application to the menu (xml/menu.xml)

This file defines the menu jumps. It contains the specification of how it should be the head of SaltOS and specifying how it should be the menu on the left of jumps. Below is a snippet (modified) with the options shown may contain:

  <group>
  	<label lang="true">gestiongeneral</label>
  	<name>gestiongeneral</name>
  	<class>menu</class>
  	.
  	.
  	.
  	<temp global="temp" eval="true">$temp="example"</temp>
  	<option include="xml/common/menuoptionslist.xml" replace="true"/>
  	<option include="xml/common/menuoptionsquery.xml" replace="true"/>
  	<option include="xml/common/menuoptionscreate.xml" replace="true"/>
  	<option include="xml/common/menuoptionsfilter.xml" replace="true"/>
  	<option include="xml/common/menuoptionsreset.xml" replace="true"/>
  	.
  	.
  	.
  	<temp global="temp" eval="true">$temp="about"</temp>
  	<option include="xml/common/menuoptionslist.xml" replace="true"/>
  	<temp global="temp" eval="true">$temp="logout"</temp>
  	<option include="xml/common/menuoptionslist.xml" replace="true"/>
  </group>

In this example, it has been added in the application Example General applications group. If you want to add or even create another group, simply follow the same format as the previous example.

Add the application icons (xml/iconset.xml)

This file defines the icons of jumps. Currently, it contains 3 groups for each of the styles of icons SaltOS available. Below is a snippet (modified) with the options shown may contain:

  <highcontrast>
  	.
  	.
  	.
  	<example>lib/highcontrast/actions/document-open.png</example>
  	.
  	.
  	.
  </highcontrast>
  <crystal>
  	.
  	.
  	.
  	<example>lib/crystal/apps/mydocuments.png</example>
  	.
  	.
  	.
  </crystal>
  <silk>
  	.
  	.
  	.
  	<example>lib/silk/icons/table_multiple.png</example>
  	.
  	.
  	.
  </silk>
  

Add the application text (xml/lang/*.xml)

This file defines the texts used SaltOS nodes specify the attribute lang="True". Below is a snippet (modified) with the options shown may contain:

  <dir>ltr</dir>
  <default>common</default>
  <common>
  	.
  	.
  	.
  </common>
  <login>
  	.
  	.
  	.
  </login>
  <menu>
  	.
  	.
  	.
  	<documentos>Documentos</documentos>
  	.
  	.
  	.
  </menu>
  .
  .
  .
  <documentos>
  	<list>Listado de documentos</list>
  	<create>Nuevo documento</create>
  	<formview>Detalle del documento</formview>
  	<forminsert>Alta de un nuevo documento</forminsert>
  	<formupdate>Modificación del documento</formupdate>
  	<defaultdata>Datos del documento</defaultdata>
  </documentos>
  .
  .
  .

This file is also explained in the FAQ about translating texts SaltOS.


XML lines
60,895
PHP lines
18,634
JS lines
11,611
XSLT lines
2,498
CSV lines
1,919
CSS lines
577