Quantcast
Channel: Oracle Blog : apex
Viewing all 142 articles
Browse latest View live

Restringir la carga de archivos en un Informe a solo PDF en Oracle Apex 5.0

$
0
0

En el artículo de hoy les voy a mostrar cómo pueden restringir la carga de archivos en su aplicación en Apex.

Para ésta demo vamos a crear una tabla en nuestro schema:

CREATE TABLE "DEMO_FILE_BLOB"
  ( "ID" NUMBER,
"BLOB_CONTENT" BLOB,
"MIME_TYPE" VARCHAR2(255),
"FILENAME" VARCHAR2(255),
"LAST_UPDATED" DATE,
"CHARACTER_SET" VARCHAR2(128)
)

Creamos la secuencia para la clave primaria:

create sequence "SEQ_DEMO_FILE_BLOB"
start with 1
increment by 1
maxvalue 999999999999999
nocache
nocycle
noorder
/

Y luego agregamos una constraint de tipo check en el campo MIME_TYPE

alter table "DEMO_FILE_BLOB" add constraint
"MY_TABLE_CON" check ( "MIME_TYPE" = 'application/pdf')

Creamos una aplicación en Apex con un informe y formulario y cargamos tres archivos PDF:

Ahora cargamos un archivo que no es del tipo PDF, en mi caso cargaré un documento de Word y se produce el error de la violación de la constraint de tipo check:

Ahora veremos cómo personalizar el error de la constraint de tipo check:

Para mostrar los errores personalizados vamos a crear una tabla en nuestro esquema llamada CONSTRAINT_LOOKUP

create table CONSTRAINT_LOOKUP

(

  CONSTRAINT_NAME VARCHAR2(30)   primary key,

  MESSAGE         VARCHAR2(4000) not null

);

Posteriormente creamos la función que manejará los errores, dicha función es una función de ejemplo que se puede personalizar según los requerimientos que se tengan.

Dicha función la podemos encontrar en la documentación de Oracle: Example of an Error Handling Function

 

create or replace function apex_error_handling_example (

   p_error in apex_error.t_error )

   return apex_error.t_error_result

is

   l_result         apex_error.t_error_result;

   l_reference_id   number;

   l_constraint_name varchar2(255);

begin

   l_result := apex_error.init_error_result (

                   p_error => p_error );

   -- If it's an internal error raised by APEX, like an invalid statement or

   -- code which cannot be executed, the error text might contain security sensitive

   -- information. To avoid this security problem rewrite the error to

   -- a generic error message and log the original error message for further

   -- investigation by the help desk.

   if p_error.is_internal_error then

       -- Access Denied errors raised by application or page authorization should

       -- still show up with the original error message

       if   p_error.apex_error_code <> 'APEX.AUTHORIZATION.ACCESS_DENIED'             and p_error.apex_error_code not like 'APEX.SESSION_STATE.%' then

           -- log error for example with an autonomous transaction and return

           -- l_reference_id as reference#

            -- l_reference_id := log_error (

           --                       p_error => p_error );

           --

           -- Change the message to the generic error message which is not exposed

           -- any sensitive information.

            l_result.message         := 'An unexpected internal application error has occurred. '||

                                       'Please get in contact with XXX and provide '||

                                       'reference# '||to_char(l_reference_id, '999G999G999G990')||

                                       ' for further investigation.';

           l_result.additional_info := null;

       end if;

   else

       -- Always show the error as inline error

       -- Note: If you have created manual tabular forms (using the package

       --       apex_item/htmldb_item in the SQL statement) you should still

       --       use "On error page" on that pages to avoid loosing entered data

       l_result.display_location := case

                                      when l_result.display_location = apex_error.c_on_error_page then apex_error.c_inline_in_notification

                                       else l_result.display_location

                                     end;

       -- If it's a constraint violation like

       --

       --   -) ORA-00001: unique constraint violated

       --   -) ORA-02091: transaction rolled back (-> can hide a deferred constraint)

       --   -) ORA-02290: check constraint violated

       --   -) ORA-02291: integrity constraint violated - parent key not found

       --   -) ORA-02292: integrity constraint violated - child record found

       --

       -- try to get a friendly error message from our constraint lookup configuration.

       -- If the constraint in our lookup table is not found, fallback to

       -- the original ORA error message.

       if p_error.ora_sqlcode in (-1, -2091, -2290, -2291, -2292) then

           l_constraint_name := apex_error.extract_constraint_name (

                                    p_error => p_error );

      

           begin

               select message

                 into l_result.message

                 from constraint_lookup

                 where constraint_name = l_constraint_name;

         exception when no_data_found then null; -- not every constraint has to be in our lookup table

           end;

       end if;

      

       -- If an ORA error has been raised, for example a raise_application_error(-20xxx, '...')

       -- in a table trigger or in a PL/SQL package called by a process and the

       -- error has not been found in the lookup table, then display

       -- the actual error text and not the full error stack with all the ORA error numbers.

       if p_error.ora_sqlcode is not null and l_result.message = p_error.message then

           l_result.message := apex_error.get_first_ora_error_text (

                                   p_error => p_error );

       end if;

       -- If no associated page item/tabular form column has been set, use

       -- apex_error.auto_set_associated_item to automatically guess the affected

       -- error field by examine the ORA error for constraint names or column names.

       if l_result.page_item_name is null and l_result.column_alias is null then

           apex_error.auto_set_associated_item (

               p_error       => p_error,

               p_error_result => l_result );

       end if;

   end if;

   return l_result;

end apex_error_handling_example;

 

Ahora necesitamos crear un registro en nuestra tabla con el mensaje personalizado:

insert into CONSTRAINT_LOOKUP

  (CONSTRAINT_NAME, MESSAGE)

values

  ('MY_TABLE_CON', 'Sólo se permite cargar archivos de tipo PDF');

Le indicamos a nuestra aplicación que debe usar la función recien creada y para ello hacemos clic en el botón Editar Propiedades de Aplicación e ingresamos el nombre de la función en la sección de  Manejo de Errores y aplicamos los cambios.

Ejecutamos la aplicación y cargamos un nuevo archivo en formato Word y veremos el siguiente resultado:

Con este ejemplo sencillo podemos ver cómo restringir en nuestros informes interactivos la carga de cierto de tipo de archivos y asignarle que se muestre un error personalizado.

Hasta Pronto!


Crear Informe-Formulario tipo Maestro-Detalle en Oracle APEX 5.0

$
0
0

Una de las más populares características en Oracle APEX es el asistente para crear un formulario del tipo Maestro/Detalle con el cual fácilmente podemos crear un informe y su correspondiente formulario (Pantalla) para manejar los datos almacenados en la base de datos.

Hoy vamos a crear un formulario Maestro/Detalle a partir de las tablas DEMO_ORDERS y DEMO_ORDER_ITEMS de la aplicación DEMO de Oracle Application Express.

Creando el Informe y Formulario Maestro/Detalle

Creamos una aplicación de tipo escritorio llamada Demo Informe M-D, luego desde la página de inicio de la aplicación, hacemos clic en el botón Crear Página:

  1. Seleccionamos Pantalla
  2. Seleccionamos Pantalla Maestro/Detalle
  3. En Tabla Maestro y Columnas: seleccionamos:
    1. Nombre de Tabla/Vista: DEMO_ORDERS (tabla)
    2. Seleccionar Columnas: seleccionamos todas las columnas y lo pasamos al recuadro de la derecha y hacemos clic en el botón Siguiente

  4. En Tabla de Detalles y Columnas
    1. Nombre de Tabla/Vista: DEMO_ORDER_ITEMS
      Nota: No siempre las tablas del detalle están relacionadas con la tabla maestro con la clave foránea, es por ello que Apex permite mostrar todas las tablas si tildamos la opción Mostrar Sólo Tablas Relacionadas en No
    2. Seleccionar Columnas: pasamos todas las columnas al lado derecho y hacemos clic en el botón Siguiente
  5. Definir Clave Primaria: seleccionamos la clave primaria de cada tabla, para la tabla maestra es ORDER_ID y para la tabla Detalle es ORDER_ITEM_ID y hacemos clic en el botón Siguiente
  6. Para la Clave Primaria ORDER_ID tildamos Disparador Existente y hacemos clic en el botón Siguiente
  7. Para la Clave Primaria ORDER_ITEM_ID tildamos Disparador Existente y hacemos clic en el botón Siguiente
  8. En Opciones Maestras:
    1. Orden de Navegación de Fila Maestra: ORDER_TIMESTAMP
    2. Incluir Informe Maestro: Sí
    3. Hacemos clic en el botón Siguiente
  9. En Diseño
    1. Crear Maestro/Detalle con: Editar Detalles como Pantalla Tabular en la misma Página. (crea dos páginas)
    2. Hacemos clic en el botón Siguiente

  10. En Atributos de Página, colocamos los títulos y en Ruta de Navegación le asignamos en Entrada principal - Inicio (Página 1) y hacemos clic en el botón Siguiente

  11. En Preferencia de Navegación, tildamos Crear nueva entrada del menú de navegación y seleccionamos Inicio, luego hacemos clic en el botón Siguiente
  12. En Confirmar vemos el resumen de los datos y hacemos clic en el botón Crear

Ejecutamos la aplicación para ver los resultados:

A creado la Página 2 referente al Informe de Ordenes

Al hacer clic en el lápiz para editar una Orden, se abre la página 3 que permite Gestionar la Orden y el detalle de la Orden en una misma página:

Para que el formulario tabular sea más representativo y podamos saber cuál es el producto y editar el registro que está en la orden podemos cambiar el campo de texto del Product Id por una lista de selección.

Para ello creamos una Lista de Valores Dinámica en Componentes Compartidos que la llamaremos Productos.

Luego a la columna PRODUCT_ID de la Pantalla Tabular “Detalle Orden” le asignamos en propiedades en la sección Lista de Valores:

  • Tipo: Componente Compartido
  • Lista de Valores: PRODUCTOS
  • Mostrar Valores Adicionales: No

Cómo hemos podido ver, crear este tipo de Informes Maestro/Detalle es muy sencillo en APEX.

Por ahora no podemos realizar con el asistente la creación de un Informe Maestro / Detalle / Detalle, para lograr este requerimiento se necesita un poco más de trabajo, en un artículo posterior hablaré de como realizar este tipo de Informes en nuestra Aplicación en Apex.

Pero a no desesperarse que en el Oracle Application Express Statement of Direction de Octubre de este año anuncian que Oracle Apex 5.1 tendrá la característica de disponer de un asistente que nos permita realizar un Informe de tipo Maestro / Detalle / Detalle, gracias al Apex Team!!!

Hasta Pronto!

Definir Valores de Interfaz de Usuario por Defecto (UI) en Oracle APEX 5.0

$
0
0

Muchas veces estamos usando en nuestra aplicación tablas de datos en Informes/Pantallas en diferentes páginas y lo que es habitual siempre necesitamos cambiar el nombre de los encabezados de columnas para que sea más representativo al usuario.

Para simplificar esta tarea y no tener que estar realizando los cambios de etiquetas cada vez que construimos un Informe o una pantalla, podemos crear Valores por Defecto de una tabla usando los Valores por Defecto de Interfaz de Usuario.

Por ejemplo si creamos una aplicación con un tipo de Pantalla Basada en Tabla con Informe usando la tabla EMP, visualizaremos el informe como se muestra a continuación:

Como podemos ver en la imagen de arriba los nombres de columnas no son amigables a la vista del usuario. Para ello vamos a aprender a crear Valores por Defecto de Interfaz de Usuario.

Ingresamos al Taller de SQL, hacemos clic en el ícono Explorador de Objetos y seleccionamos la tabla EMP.

Posteriormente, seleccionamos la ficha “Valores por Defecto de Interfaz de Usuario

Hacemos clic en el botón Crear Valores por Defecto, se abre una ventana modal y hacemos clic en el botón Crear Valores por Defecto. Posteriormente se muestra la lista de todos los objetos y lo que he hecho fue filtrar el reporte para que me muestre solo los objetos que tienen Valores por Defecto = Yes.

Como menciona la ayuda de Apex: “Los valores por defecto de interfaz de usuario se utilizan para rellenar con valores iniciales las propiedades de región y elemento, a fin de proporcionar consistencia entre páginas de una aplicación o de varias aplicaciones. El diccionario de tablas es específico de las tablas y columnas de un esquema seleccionado. Estos valores por defecto se utilizan en cualquier definición del diccionario de atributos.”

Hacemos clic en el botón Sincronizar y aparece una ventana modal, luego hacemos clic en el botón Sincronizar Valores por Defecto.


Después hacemos clic en el nombre del Objeto EMP para modificar los valores por defecto. En este caso vamos a modificar la etiqueta (nombre de la columna) y el texto de ayuda. Esto lo hacemos para cada una de las columnas de la tabla EMP.


En este ejemplo solo he cambiado las etiquetas de la tabla EMP, pero es aconsejable que cuando empezamos con un proyecto nuevo podamos crear todos los Valores por Defecto de la Interfaz de Usuario para ser usados en nuestra aplicación.

Volvemos a la página de inicio de nuestra aplicación y creamos un Informe Interactivo y le indicamos que use los Valores por Defecto de Interfaz de Usuario.


Como podemos ver en el Informe se muestran los nombres de columnas como lo habíamos ingresado en los valores por defecto. Y de igual modo en el formulario se muestran las etiquetas modificadas.

Si hacemos clic en el signo más (?) podemos ver la ventana modal de ayuda que aparece.

De esta forma podemos ingresar de una sola vez todos los valores por defecto que necesitamos de nuestras tablas para unificar todos los nombres de objetos en nuestra aplicación.

Hasta Pronto!

 

Crear Filtros en Pantalla Tabular en Oracle 5.0

$
0
0

En el artículo de hoy voy a responder una consulta que me hicieron de cómo colocar filtros a un Formulario Tabular en Oracle APEX 5.0

Para ello primero de todo creamos una Pantalla Tabular con el asistente y usamos la tabla EMP para ejemplo.

Nuestra Pantalla Tabular se visualizará como sigue:

Vamos a crear a continuación una Lista de Valores Dinámica que muestre los Departamentos de la Tabla DEPT.

Nos dirigimos a Componentes Compartidos, en la sección Otros Componentes, hacemos clic en Lista de Valores:

1)      Hacemos clic en el botón Crear

2)      Se abre el asistente, seleccionamos Nuevo, clic en Siguiente

3)      Nombre: Departamentos y Tipo Dinámico, clic en Siguiente

4)      Nos desplazamos hacia abajo y hacemos clic en el enlace Crear Lista de Valores Dinámica, se abre el asistente:

    1. Seleccionamos la tabla DEPT, clic en Siguiente
    2. Columna de Visualización: DENAME, Valor de Retorno: DEPTNO, clic en Siguiente
    3. Clic en Terminar

5)      Clic en Crear Lista de Valores

Regresamos a la página donde creamos nuestra Pantalla Tabular, en mi caso es la página 2.

Creamos una región de contenido estático y la arrastramos hacia arriba de la pantalla tabular y le colocamos el nombre Filtro.

Dentro de la región Filtro creamos un elemento de página:

1)      En la sección Identificación en el panel de propiedades del elemento

    1. Nombre: PX_DEPARTAMENTO
    2. Tipo: Listado de Selección

2)      En la sección Etiqueta

    1. Etiqueta: Seleccionar un Departamento

3)      En la sección Configuración

    1. Acción de Página al Seleccionar: Submit Page
    2. En la sección Lista de Valores:

4)      Tipo: Componente Compartido

    1. Lista de Valores: DEPARTAMENTOS
    2. Mostrar Valores Adicionales: Sí
    3. Mostrar Valor Nulo: No

5)      Hacemos clic en el botón Guardar

Para que el filtro funcione debemos agregar en la consulta SQL de la Pantalla Tabular la cláusula WHERE.

Seleccionamos la Pantalla Tabular desde el panel de la izquierda en el diseñador de página y pasamos nuestra mirada al panel de la derecha de propiedades en la sección Origen:

Cambiamos esta Consulta SQL:

 

select

"EMPNO",

"EMPNO" EMPNO_DISPLAY,

"ENAME",

"JOB",

"MGR",

"HIREDATE",

"SAL",

"COMM",

"DEPTNO"

from "#OWNER#"."EMP"

 

Por ésta otra consulta SQL

 

select

"EMPNO",

"EMPNO" EMPNO_DISPLAY,

"ENAME",

"JOB",

"MGR",

"HIREDATE",

"SAL",

"COMM",

"DEPTNO"

from "#OWNER#"."EMP"

where DEPTNO = TO_NUMBER(:P2_DEPARTAMENTO)

El resultado de la consulta no filtra los empleados según el departamento seleccionado.

Si queremos agregar otro filtro lo realizamos del mismo modo, por ejemplo, agregamos dos elementos de páginas de tipo Campo Numérico dentro de la región Filtros: P2_SAL_MIN y P2_SAL_MAX.

Indicamos que ambos elementos en la sección Cuadrícula, Iniciar Nueva Fila: Sí

En el elemento P2_DEPARTAMENTOS en la sección Configuración colocamos None en Acción de Página al Seleccionar, porque vamos a crear un botón que ejecute la consulta para el filtrado de la pantalla tabular.

Dentro de la región Filtro, creamos un botón que le colocaremos el nombre Ejecutar y en comportamiento seleccionamos Ejecutar Página.

Seleccionamos la Pantalla Tabular y en Origen de la Consulta SQL cambiamos la consulta por la siguiente:

select

"EMPNO",

"EMPNO" EMPNO_DISPLAY,

"ENAME",

"JOB",

"MGR",

"HIREDATE",

"SAL",

"COMM",

"DEPTNO"

from "#OWNER#"."EMP"

where DEPTNO = TO_NUMBER(:P2_DEPARTAMENTO)

and SAL >= :P2_SAL_MIN and SAL <= :P2_SAL_MAX

Vemos el resultado:

Será hasta la próxima!

Iniciar GlassFish en Windows “Remote server does not listen for requests on [localhost:4848]. Is the server up?...”

$
0
0

Hola a todos, en el artículo de hoy quiero compartir una situación que se me dio cuando quise iniciar el Servidor GlassFish que había instalado cuando expliqué como Desplegar Apex en el Servidor GlassFish.

Después de unos días que no utilizaba el servidor y luego de haber hecho una actualización en mi laptop, me encontré con el problema de que al querer iniciar el Servidor me arrojaba el siguiente error:

Remote server does not listen for request on [localhost:4848]. Is the Server up?...

Después de investigar y corroborar que la base de datos estaba arriba utilizando el Puerto 8585 sin el servidor y que tanto el Puerto 8080 y el 4848 estaban libres porque son los puertos donde corre GlassFish, encontré la siguiente solución al problema:

Desde el Explorador de Windows nos ubicamos en la carpeta doamin1

C:\glassfish4\glassfish\domains\domain1

Y seleccionamos la carpeta osgi-cache y luego la eliminamos.

Posteriormente volvemos a la ventana de comandos nos ubicamos en la siguiente carpeta para ejecutar el comando asadmin

C:\glassfish4\bin> asadmin start-domain


Como podemos ver el Servidor Glassfish se ha levantado con éxito!

Cuando se inicia el Servidor se vuelve a crear la carpeta osgi-cache.

La especificación OSGi establece que el framework debe almacenar en caché los paquetes y su estado en tiempo de ejecución, pero no define explícitamente cómo debe hacerse. Aquí pueden encontrar información como el framework FELIX maneja el almacenamiento en caché de paquete por defecto y también describe los mecanismos para modificar este comportamiento predeterminado.

Finalmente, podemos comprobar desde el navegador el funcionamiento del Servidor GlassFish:

http://localhost:8080

Y ahora nuestro Apex desplegado desde GlassFish lo podemos abrir sin problemas J

http://localhost:8080/apex

Espero les sea de utilidad, hasta pronto!!!

Descargar archivos PDF, Word y Excel desde un icono de una columna del Informe Interactivo en Oracle Apex 5.0

$
0
0

Seguramente en algún momento te encontraste con la situación de querer tener un listado de archivos y que los puedas descargar directamente desde un icono o enlace desde el informe interactivo.

Por ello es que he desarrollado este ejemplo para que puedas aplicarlo y adaptarlo a tus desarrollos en Apex.

Necesitamos para este ejemplo crear una tabla que contendrá los datos de los diferentes archivos.

Ingresamos al Taller de SQL y ejecutamos cada una de las siguientes sentencias:

CREATE TABLE "MY_FILE_BLOB"

   (    "ID" NUMBER,
        "TITLE" VARCHAR2(500), 
        "DESCRIPTION" VARCHAR2(500),  
        "BLOB_CONTENT" BLOB, 
        "MIMETYPE" VARCHAR2(255), 
        "FILENAME" VARCHAR2(255), 
        "LAST_UPDATED" DATE, 
        "CHARACTER_SET" VARCHAR2(128), 
        CONSTRAINT "MY_FILE_BLOB_PK" PRIMARY KEY ("ID") ENABLE
   )
/
CREATE SEQUENCE   "MY_FILE_BLOB_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20 NOORDER NOCYCLE
/

CREATE OR REPLACE TRIGGER "BI_MY_FILE_BLOB"

before insert on "MY_FILE_BLOB"              

for each row

begin  

if :NEW."ID" is null then

   select "MY_FILE_BLOB_SEQ".nextval into :NEW."ID" from sys.dual;

end if;

end;

/

ALTER TRIGGER "BI_MY_FILE_BLOB" ENABLE

/

Ahora creamos el Informe Interactivo por medio del asistente “Pantalla Basada en Tabla con Informe”, utilizando como origen la tabla MY_FILE_BLOB y en la página que corresponde al formulario de carga de datos seleccionamos las columnas: TITLE, DESCRIPTION y BLOB_CONTENT, el resto de las columnas no lo mostramos.

Cargar Iconos en nuestro Espacio de Ttrabajo

Ingresamos a los Componentes Compartidos de la aplicación, seleccionamos Archivos de Espacio de Trabajo Estático y cargamos los 3 iconos que representan los archivos de tipo PDF, Excel y Word.

  • #WORKSPACE_IMAGES#icon-excel-75.gif
  • #WORKSPACE_IMAGES#icon-word-75.png
  • #WORKSPACE_IMAGES#icon-pdf-75.png

 Editar Origen Consulta SQL del Informe Interactivo

 Desde el Diseñador de Páginas, hacemos clic en la región del Informe Interactivo y en el panel de la derecha en la sección Origen reemplazamos el origen con la siguiente consulta SQL:

 select "ID",

"TITLE",

"DESCRIPTION",

"FILENAME",

"MIMETYPE",

dbms_lob.getlength("BLOB_CONTENT") "BLOB_CONTENT",

CASE

WHEN FILENAME LIKE '%.pdf' THEN '<a href="'||apex_util.get_blob_file_src('P2_BLOB_CONTENT',ID)||'"><img alt="" title="Archivo PDF" src="#WORKSPACE_IMAGES#icon-pdf-75.png" width=48 /></a>'

WHEN FILENAME LIKE '%.docx' THEN '<a href="'||apex_util.get_blob_file_src('P2_BLOB_CONTENT',ID)||'"><img alt="" title="Archivo MS Word" src="#WORKSPACE_IMAGES#icon-word-75.png" width=48 /></a>'

WHEN FILENAME LIKE '%.xlsx' THEN '<a href="'||apex_util.get_blob_file_src('P2_BLOB_CONTENT',ID)||'"><img alt="" title="Archivo MS Excel" src="#WORKSPACE_IMAGES#icon-excel-75.gif" width=48 /></a>'

ELSE MIMETYPE END AS "FILE_ICONO",

"LAST_UPDATED",

"CHARACTER_SET"

from "#OWNER#"."MY_FILE_BLOB"

where mimetype NOT LIKE '%jpeg'

AND mimetype NOT LIKE '%png'

AND mimetype NOT LIKE '%gif'

ORDER BY ID

Guardamos los cambios.

La consulta SQL además de mostrar todas las columnas de la tabla MY_FILE_BLOB crea una columna adicional para mostrar el icono que corresponde a la aplicación que se muestra en el registro y ese dato lo toma de la columna FILENAME, para este ejemplo usamos archivos de PDF, Excel y Word.

Editar Página 1 - Informe Archivos Almacenados

Seleccionamos la columna BLOB_CONTENT y en la sección ATRIBUTOS_BLOB del panel de la derecha asignamos los siguientes valores:

  • Columna de Tipo MIME: MIMETYPE
  • Columna de Nombre de Archivo: FILENAME
  • Última Columna BLOB Actualizada: LAST_UPDATED
  • Columna de Juego de Caracteres: CHARACTER_SET

Ocultamos las columnas: ID, MIMETYPE, FILENAME, CHARACTER_SET, LAST_UPDATED

Editar Página 2 - Formulario Gestionar Archivos

Cambiamos los títulos y luego seleccionamos el elemento P2_BLOB_CONTENT y en la sección Configuración en el panel derecho de propiedades del elemento asignamos los campos como se muestra a continuación:

  • Columna de Tipo MIME: MIMETYPE
  • Columna de Nombre de Archivo: FILENAME
  • Columna de Juego de Caracteres: CHARACTER_SET
  • Última Columna BLOB Actualizada: LAST_UPDATED
  • Mostrar Enlace de Descarga: Sí
  • Texto de Enlace de Descarga: Descargar Archivo
  • Disposición del Contenido: Attachment

Cargar Archivos

Ejecutamos la aplicación y cargamos los tres tipos de archivos: PDF, Excel y Word.

La columna FILE_ICONO corresponde a la imagen que mostrará el registro dependiendo el tipo de archivo mostrado en la columna FILENAME.

Seleccionamos la columna FILE_ICONO y en el panel de la derecha en la sección Seguridad indicamos Caracteres Especiales de Escape = No.

Ejecutamos la aplicación y podemos observar que ahora se muestra el icono de acuerdo al archivo cargado.

La columna Archivo (la que corresponde al link de descarga) podemos ocultarla, haciendo clic en el botón Acciones y seleccionamos la columna y lo pasamos al cuadro de No Mostrar.

Al hacer clic en cualquiera de los iconos podemos descargar los archivos a nuestra PC.

Aquí terminamos y será hasta pronto!!!

Configurando Oracle REST Data Services como Print Server en Apex 5.0

$
0
0

Hola a todos, hace unos días tuve una consulta sobre como habilitar la opción de visualizar un PDF en un Informe en APEX y por ello he escrito este post.

Antes que nada para visualizar el Informe en PDF en APEX necesitamos tener instalado APEX con la configuración del Oracle REST Data Services que antes se llamaba Oracle APEX Listener.

 En este enlace puedes ver un artículo donde explico cómo desplegar Apex en el Servidor GlassFish y Oracle REST Data Services.

Para este ejemplo, vamos a crear una aplicación en Apex y luego creamos un informe interactivo de la tabla EMP..

Si deseamos visualizar el Informe en PDF, veremos que nos da un error al querer mostrarlo.

Hacemos clic en el botón Acciones à Descargar y seleccionamos PDF, se abre una ventana modal en el cual podemos ver el PDF o guardarlo, vamos a seleccionar la opción de Open with [Adobe Acrobat Reader].

Para habilitar la visualización de la impresión en PDF debemos hacerlo desde la cuenta del Admin de la Instancia.

Ingresamos las credenciales del Espacio de Trabajo INTERNAL, e ingresamos a Gestionar Instancia y seleccionamos el enlace Valores de Instancia del cuadro Valores de Instancia.

En la ficha Impresión de Informe nos aparecerá que tenemos seleccionado la opción External (Apache FOP) como la Print Server, lo vamos a cambiar y colocar Oracle REST Data Services y aplicamos los cambios.

Salimos de la administración de la Instancia e iniciamos sesión en nuestro Espacio de Trabajo.

Ejecutamos la aplicación y hacemos clic en el botón Acciones à Descargar y seleccionamos PDF y abrimos el PDF con el Adobe Acrobat.

En el panel derecho de propiedades podemos configurar la Página si queremos que sea de tipo Carta, Legal, A4, etc., además del ancho y alto, bordes, y otras configuraciones para la impresión.

Disponemos de las secciones: Salida, Página, Cabecera de la Página, Cabeceras de Columna, Columnas, Pie de Página y Avanzada.

Ingresamos por ejemplo en la casilla Texto de la sección de Cabecera de Página lo siguiente: Informe Interactivo Ejemplo Visualización de PDF y modificamos el tamaño de letra, color, fuente, etc.

Lo mismo hacemos para la sección Pie de Página, ingresando por ejemplo el siguiente texto: Oracle Application Express en Español! Y guardamos los cambios.

Ejecutamos la aplicación y abrimos el PDF, como podemos ver, se muestra ahora un texto tanto en el encabezado como en el pie de página.

Cabe destacar que si queremos darle un formato más personalizado a nuestros informes necesitamos utilizar alguna herramienta de diseño de reportes e integrarlo a APEX.

Bueno hasta aquí éste post y será hasta pronto!

Elemento de tipo numérico que depende de un elemento de tipo lista de selección en Apex 5.0

$
0
0

En ésta ocasión voy a contestar una duda que me llegó, el cual necesitaban saber cómo obtener el valor de un elemento de tipo numérico que dependa del valor seleccionado desde una lista de selección.

 

Para contestar esto he creado una aplicación y en la página una región de contenido estático con dos elementos, uno de ellos de tipo campo numérico y el otro de tipo lista de selección.

Para el primer elemento de tipo Lista de Selección, configuramos:

 

Sección Apariencia:

  • Nombre: P1_EMPLEADO
  • Tipo: Lista de Selección

Sección Etiqueta:

  • Etiqueta: Empleado

Sección Configuración:

  • Acción de Página al Seleccionar Submit page

Sección Lista de Valores

  • Tipo: Consulta SQL
  • Consulta SQL: select ename, empno from emp order by 1

Para el segundo elemento de Campo Numérico, configuramos:

 

Sección Apariencia:

  • Nombre: P1_SALARIO
  • Tipo: Campo Numérico

Sección Etiqueta:

  • Etiqueta: Salario

Sección Origen

  • Tipo: Consulta SQL (devuelve valor único)
  • Consulta SQL: select sal from emp where empno = :P1_EMPLEADO
  • Usado: Siempre, sustituyendo cualquier valor existente en el estado de sesión

Ejecutamos la aplicación y vemos que cuando cambiamos la selección del primer elemento se muestra el salario en el campo numérico.

 

Ahora bien, como podemos ver cada vez que se selecciona un nuevo elemento de la lista toda la página se recarga y si tenemos innumerables elementos y regiones eso puede hacer que la aplicación decaiga en performance.

 

Para evitar que se recargue toda la página podemos usar Acciones Dinámicas para que cuando cambie el elemento seleccionado de la lista solo se refresque el elementoo de página de tipo numérico.

 

Desde el Diseñador de Páginas de la página que contiene nuestros elementos de página, hacemos clic en la ficha de Presentación.

 

Hacemos clic en el elemento P1_SALARIO y en la sección Origen, configuramos que el tipo sea Nulo.

 

Hacemos clic sobre el Elemento P1_EMPLEADO con el botón derecho del mouse y seleccionamos Crear Acción Dinámica.

  • Sección Identificación
    • Nombre: Mostrar Salario
  • Sección Cuando
    • Evento: Cambiar
    • Tipo de Selección: Elementos
    • Elementos: P1_EMPLEADO

Hacemos clic en la acción Verdadera “Mostrar” y configuramos:

  • Sección Identificación
    • Acción: Definir Valor
  • Sección Configuración
    • Definir Tipo: SQL Statement
    • Sentencia SQL: select sal from emp where empno = :P1_EMPLEADO
    • Elementos de Página a Ejecutar: P1_EMPLEADO
    • Caracteres Especiales de Identificación: Si
    • Suprimir Evento de Cambio: No
  • Sección Elementos Afectados
    • Tipo de Selección : Elementos
    • Elementos: P1:SALARIO
  • Opciones de Ejecución:
    • Evento: Mostrar Salario
    • Arrancar Cuando el Resultado de Evento Sea: Verdadero
    • Arrancar al Cargar Página: Si
  • Guardamos los cambios

Antes de ejecutar la página, recordemos configurar el elemento P1_EMPLEADO que la "Acción de Página al Seleccionar" sea None.

De ese modo sólo se refresca la región cuando se cambia la selección en la lista, no afectando las demás regiones y elementos que tiene nuestra página.

Hasta Pronto!


Cómo personalizar la página de Inicio de Sesión de nuestras aplicaciones en APEX 5

$
0
0

Muchas veces necesitamos personalizar la página de acceso a nuestras aplicaciones en APEX para darle un sentido de diferenciación entre cada aplicación. En la entrega de hoy voy a explicar cómo podemos llegar de esta página:

A esta otra página de inicio de sesión personalizada:

Ante todo ingresamos al Diseñador de Página de la página 101 de nuestra aplicación:

Lo primero que necesitamos cambiar es la plantilla que usa la región Conectar, donde se encuentran los elementos de página del username y password, en vez de ser la plantilla Standard la colocamos que sea Login.

Para que el botón LOGIN se ajuste al ancho de la región, seleccionamos el botón y en “Opciones de Plantilla” hacemos clic en el botón “Utilizar Valores por Defecto de Plantilla” para abrir la ventana modal y en el sector Avanzado, seleccionamos en la propiedad Width que sea Strech y guardamos los cambios.

Modificar Propiedades de Elementos de Páginas

Seleccionamos P101_USERNAME y en el panel de la derecha de propiedades en el sector de Apariencia ingresamos:

  • Plantilla: Hidden
  • Clases CSS: icon-login-username

De igual modo seleccionamos el elemento P101_PASSWORD e ingresamos:

  • Plantilla: Hidden
  • Clases CSS: icon-login-password

Agregar reglas CSS para personalizar colores y fondos

Seleccionamos el título: “Página 101: Página de Conexión” y en el panel de la derecha de propiedades del Diseñador de Páginas nos desplazamos hasta el sector de CSS e ingresamos las siguientes reglas en la casilla “En Línea”:

 /* Imagen del logo */

 span.t-Login-logo {

background-image: url("/img/mi-logo.png");

background-size: cover;

width: 376px;

height: 66px;

}

/* Color de fondo del botón LOGIN */

.t-Button--hot {

background-color: #57BB97

}

/* Color de fondo de la región Conectar */

.t-Login-region {

   background-color: #4B5B68;

}

/* Color del título Conectar que está debajo del logo */

body .t-Login-title {

   color: #fff;

}

/* Imagen de Fondo del cuerpo de la página */

.t-PageBody--login .t-Body {

background-image: url("/img/mi-bg-img.jpg");

background-size: cover;

}

Nota: Si queremos usar imagenes que esten alojadas dentro de nuestro espacio de trabajo en el CSS colocamos:  background: url(&WORKSPACE_IMAGES.mi-logo-.jpg); tambien podemos hacerlo para el fondo de página.

De esta forma muy sencilla podemos personalizar nuestra página de Inicio Sesión de nuestras aplicaciones en APEX.

Hasta Pronto!

Validación Numérica en una Pantalla Tabular en Oracle Apex 5

$
0
0

Recientemente me han hecho una consulta, de cómo se puede validar dos columnas de tipo numéricas en una pantalla tabular en el cual si se ingresa un valor en una columna, ese valor sea menor que otra columna de la pantalla tabular.

Por ejemplo tenemos dos columnas, Valor A y Valor B, necesitamos validar que cuando el usuario ingrese el valor B, dicho valor no sea más grande que el valor A.

Para este ejemplo vamos a crear una tabla desde el Taller de SQL:

CREATE TABLE "DEMO_VALIDACION"

   (    "CLI_ID" NUMBER(8,0) NOT NULL ENABLE,

        "CLI_NOMBRE" VARCHAR2(100) NOT NULL ENABLE,

        "CLI_CAMPO_A" NUMBER(8,0) NOT NULL ENABLE,

        "CLI_CAMPO_B" NUMBER(8,0) NOT NULL ENABLE,

        CONSTRAINT "DEMO_VALIDACION_PK" PRIMARY KEY ("CLI_ID") ENABLE

   )

/

CREATE OR REPLACE TRIGGER "BI_DEMO_VALIDACION"

before insert on "DEMO_VALIDACION"              

for each row

begin  

if :NEW."CLI_ID" is null then

   select "DEMO_VALIDACION_SEQ".nextval into :NEW."CLI_ID" from sys.dual;

end if;

end;

/

ALTER TRIGGER "BI_DEMO_VALIDACION" ENABLE

/

Ahora creamos una aplicación de tipo escritorio y luego creamos una página de tipo Pantalla Tabular que muestre los registros de la tabla DEMO_VALIDACION.

Ejecutamos la aplicación y cargamos varios registros, al no tener ninguna validación, salvo que las columnas del Campo A y Campo B deben ser numéricas, podemos ingresar los valores sin restricciones.

En mi ejemplo he cargado los siguientes registros:

Lo primero que necesitamos conocer es el número de la posición de cada columna en la pantalla tabular.

Para ello vamos a utilizar el Inspector de elementos, en mi caso, yo uso el navegador Firefox con el ADD-ONS Web Developer Aquí lo puedes instalar

Nos paramos con el mouse encima de la tercer celda de la columna del Campo A y hacemos clic con el botón derecho del mouse para abrir el inspector de elemento.

Al abrir el inspector podremos encontrar las etiquetas html del input de la columna, si sólo vemos las etiquetas <td> simplemente expandimos cada fila de la tabla hasta encontrar la etiqueta input y en el id podemos ver qué posición tiene en la pantalla tabular.

En este caso el Campo A es f04_000X y el Campo B es f05_000X.


Una vez identificado podemos armar la función de tipo PL/SQL que controlará la validación.

Cuando trabajamos con las pantallas tabulares, Apex crea una colección automáticamente para mantener los valores en memoria hasta que hagamos clic en el botón de Aplicar Cambios y lo guarde en la tabla y libere la colección.

Esta colección guarda los datos de cada columna de la tabla ("apex_application.g_f01", "apex_application.g_f02" etc ...) y la función que vamos a crear recorre los valores en las columnas 4 y 5 y compara dichos valores.

Ingresamos al Diseñador de Páginas de la pantalla tabular.

Hacemos clic en el icono de Procesamiento (tab número 3) en el panel de la izquierda y dentro de Validaciones creamos una nueva validación que la llamaremos: Campo B <= Campo A.


Pasamos al panel de la derecha, en la sección Validación:

  • Pantalla Tabular: Validación Numérica (Nombre de nuestra Pantalla Tabular)
  • Tipo: Cuerpo de la Función PL/SQL (devuelve un valor booleano)
  • Cuerpo de la Función PL/SQL que devuelve un Valor Booleano:

BEGIN

FOR i in 1..apex_application.g_f03.count

LOOP

IF TO_NUMBER(apex_application.g_f05(i)) >

TO_NUMBER(apex_application.g_f04(i))THEN

RETURN false;

END IF;

END LOOP;

END;

  • En la sección de Error, Mensaje de Error:
    Error. El número del campo B no puede ser mayor al número del campo A.
    Por favor, inténtelo de nuevo. Gracias.
  • En Condición:
    • Si Se Hace Clic en el Botón: SUBMIT
  • Guardamos los Cambios

Ejecutamos la Aplicación y cargamos un registro incorrecto y veremos cómo funciona la validación:


Si ingresamos correctamente los valores, es decir, que el campo A es menor al campo B, se carga correctamente el registro.

De este modo podemos crear nuestras propias validaciones haciendo referencia a los valores publicados por un formulario HTML mediante la matriz de PL/SQL que va de APEX_APLICATION.G_F01 a G_F50, cada elemento de página que se crea se le da un valor de índice entre 1 y 50 que se corresponde directamente a una matriz G_Fx el cual contiene los valores temporales de los elementos del formulario.

Espero sea de utilidad y será hasta pronto!

¿Cómo interpreta la Base de Datos Oracle el componente Siglo de la Fecha?

$
0
0

En la entrega de hoy quiero compartir con todos un tema que a veces no es muy facil de entender y por ello es que lo expongo aquí.

Las fechas son almacenadas internamente en la base de datos con formato numérico que soporta el centenario, año, mes y día y también información sobre las horas, minutos y segundos. 

Estos atributos están disponibles para cada literal, valor de columna o expresión que es del tipo Date.

Cuando se accede a la información de fecha desde una tabla, el formato por defecto de los resultados comprende dos dígitos que representan el día, una abreviatura de tres letras que corresponde al mes, y dos dígitos que representan el componente de año. 

Por defecto, estos componentes son separados con guiones en SQL * Plus y barras diagonales en SQL Developer. 

Aunque el componente del siglo no se muestra de forma predeterminada, se almacena en la base de datos cuando se inserta o se actualiza el valor de la fecha y se encuentra disponible para su recuperación. 

Entonces el formato DD-MON-RR es el valor predeterminado tanto para mostrar la fecha como para ingresarla. 

Al insertar o actualizar la información de fecha, el componente de siglo se obtiene de la función SYSDATE en el caso de que no se suministre. 

La máscara de formato de fecha RR difiere de la máscara de formato de fecha YY ya que se puede utilizar para especificar diferentes siglos basados en el corriente año y años especificados. 

Con respecto al siglo, como lo interpreta Oracle? 

Para entender cómo se asigna el siglo en Oracle, supongamos que la fecha actual es 02-FEB-2014.

El siglo que retorna para la fecha 24-JUL-14 en formato DD-MON-RR es de 20, ya que el año especificado está entre 0 y 49. 

En cambio si el año asignado está entre 50 y 99 el siglo retornado será 19, por ejemplo si la fecha actual es 02-FEB-2014, el siglo que retorna la fecha 24-JUL-94 es 19. 

Ahora que pasa si la fecha actual es por ejemplo 02-FEB-1975, en este caso si el año asignado está entre 50 y 99 el siglo devuelto para la fecha 24-JUL-94 es 19 y si el año asignado está entre 0 y 49 el siglo retornado para la fecha 24-JUL-14 será el siguiente, es decir 20. 

Para dar formato de fecha en la base de datos se usa el parámetro NLS_DATE_FORMAT.

Por ejemplo:

ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY MM DD';

select sysdate from dual; (para ver el resultado) 

Format Mask Format Description

DD -- Day of the month

MON -- Month of the year

YY -- Two-digit year

YYYY -- Four-digit year including century

RR -- Two-digit year (Year 2000–compliant)

CC -- Two-digit century

HH -- Hours with AM and PM

HH24 -- Twenty-four-hour time

MI -- Minutes

SS -- Seconds 

Para traducir los días se trabaja con el parámetro NLS_LANGUAGE

ALTER SESSION SET NLS_LANGUAGE=Spanish;

De verdad que el trabajo con las fechas siempre es un desafío constante ya que necesitamos conocer los diferentes tipos de formatos que trabaja la base de datos para poder construir nuestras aplicaciones en APEX sin errores.

Será hasta la próxima!

Mi segundo libro de APEX está publicado!

$
0
0

Hola!

Quiero anunciar con mucha alegría la publicación de mi segundo libro sobre Oracle APEX 5.0 en Español!!!
Lo he titulado: "Integración Sin Costo de JasperReports en Oracle APEX 5.0"
En esta oportunidad te presento una pequeña guía práctica para aprender a crear reportes personalizados listos para imprimir usando el JasperReports Integration kit desarrollado por Dietmar Aust en Oracle APEX 5.0.
Estoy dando un 10% de descuento por el lanzamiento del libro, solo tienes que aplicar el cupón  U3VCWNAU en el carrito de la compra del Store desde el siguiente link: Store de CreateSpace (Solo aplica al store de CreateSpace)
 
Los Códigos PL/SQL y SQL estan disponibles en el sitio web del libro Descargar Scripts y alli puedes ver el indice del libro.
Versión Paperback Aquí
Versión Kindle Aquí
Muchas gracias a todos los que siempre me apoyan y me impulsan a seguir trabajando para hacer que el conocimineto llegue a todos!
Hasta Pronto!!!!

Personalizar Botones con Imágenes almacenadas en nuestro Espacio de Trabajo usando CSS

$
0
0

Muchas veces nos encontramos en la necesidad de personalizar nuestros botones en nuestras aplicaciones en Oracle APEX, es por ello que el día de hoy te voy a enseñar cómo puedes colocar una imagen en el botón que se encuentra almacenada en nuestro espacio de trabajo para que los botones se vean cómo te maestro en la siguiente imagen:

Lo primero que debemos hacer es cargar en nuestro espacio de trabajo las imágenes que necesitamos, en el ejemplo que les traigo he subido tres imágenes de 65px de alto cada una con fondo transparente.

Ingresamos a nuestra aplicación, nos dirigimos a Componentes Compartidos à Archivos à Archivos de Aplicación Estáticos:

Reservamos las referencias porque las vamos a usar más adelante:

#APP_IMAGES#chart-bar-65px.png

#APP_IMAGES#pie-chart-65.png

#APP_IMAGES#reporte-65.png

Regresamos a la aplicación y creamos una región que contendrá tres Botones el cual los denominé: BOTON1, BOTON2 y BOTON3.

Plantillas de Botones

La aplicación que he creado para este ejemplo usa el Tema Universal 42 el cual trae un conjunto de plantillas para botones, calendario, etiquetas, informes, Listas, Listas de Valores Emergentes, Páginas, Regiones y Ruta de Navegación como todos los Temas que tenemos disponibles en APEX.

En el caso de las plantillas de Botones, el tema Universal 42 dispone de tres tipos diferentes de plantillas, una es la de tipo Texto (Text), la otra es de tipo Icono (Icon) y una más de tipo combinado, que es la de Texto con Icono (Text with Icon).

En el caso nuestro necesitamos crear una propia plantilla que sea de tipo Texto con Imagen, para ello lo que vamos a hacer es crear una plantilla basada en una copia de la plantilla de botón: “Text with Icon”.

Ingresamos a Componentes Compartidos de nuestra aplicación y desde la sección de Interfaz de Usuario hacemos clic en el link Plantillas.

Las tres primeras plantillas son la de tipo botón, hacemos clic en el icono Copiar de la plantilla “Text with Icon” y se abre una ventana modal el cual nos solicita el nombre de la nueva plantilla, lo colocaremos como: “Text with Image” y hacemos clic en el botón Copiar.

Hacemos clic en el enlace del nombre de la plantilla para abrirla y editarla. Nos desplazamos hasta la ficha Definición y allí podemos ver dos recuadros, uno es para la plantilla normal cuando el botón está en gris y el otro recuadro es para la plantilla cuando el botón está pintado en azul, es decir, que está configurado como Directo.

Recuadro 1: Tenemos el siguiente Código HTML

Reemplazamos por el siguiente código HTML


El cual eliminamos las etiquetas que corresponden al Icono y en su lugar colocamos la etiqueta de tipo imagen y utilizaremos la variable de sustitución #ICON_CSS_CLASSES# para indicar la ruta donde se encuentra nuestra imagen. Además vamos a crear nuestra propia clase para el botón que la he llamado t-Button-img.

Recuadro 2: Tenemos el siguiente Código HTML


Reemplazamos por el siguiente código HTML


Lo único diferente aquí es que se tiene la clase t-Button--hot que indica que el botón es Directo y por lo tanto se pintará de azul.

Aplicamos los cambios.

Estilos CSS de Botones

Regresamos a la aplicación y hacemos clic en el título de la página en el Diseñador de Páginas y nos desplazamos a la sección de CSS En Línea y agregamos los siguientes estilos CSS:

.t-Button-img {

   font-size: 20px;

   line-height: 24px;

   font-weight: 400;

   text-align: center;

   padding-top: 20px;

   padding-right: 20px;

   padding-bottom: 20px;

   padding-left: 20px;

}

.button-img

{

margin-left: 15px;

margin-right: 15px;

align: middle;

}

Editar Botones

Hacemos clic en BOTON1 desde el panel de la izquierda de Presentación y vemos las propiedades del botón desde el panel de la derecha de propiedades.

Sección Identificación

            Nombre del Botón: BOTON1

Etiqueta: Generar Gráfico de Barras

Sección Diseño

            Posición de Botón: Parte Superior de la Región

Alineación Horizontal: Izquierda

Sección Apariencia

            Plantilla de Botón: Text with Image (nuestra plantilla recién creada)

Directa:

Classes CSS de Icono: #APP_IMAGES#chart-bar-65px.png


Realizamos lo mismo para los Botones 2 y 3, cambiando el texto y las imágenes.

BOTON2

Sección Apariencia

            Plantilla de Botón: Text with Image (nuestra plantilla recién creada)

Directa:

Classes CSS de Icono: #APP_IMAGES#reporte-65.png

BOTON3

Sección Apariencia

            Plantilla de Botón: Text with Image (nuestra plantilla recién creada)

Directa:

Classes CSS de Icono: #APP_IMAGES#pie-chart-65.png

Guardamos los cambios y ejecutamos la página.

Como podemos ver, personalizar nuestros botones usando CSS y nuestras imágenes alojadas en nuestro espacio de trabajo es muy sencillo, la única consideración es que las imágenes deben tener el mismo ancho y alto para que se vea uniforme todos los botones y siempre realizar copias de plantillas si vamos a modificar la plantilla para no trabajar en las plantillas originales.

Les dejo un archivo de texto con los codigos HTML para que puedan copiar y pegar en su aplicación de ejemplo.

Espero les sea de utilidad y será hasta la próxima!

Access Control List (ACL) y JasperReports Integration Kit

$
0
0

Hola amigos, el otro día un amigo me comentó que tenía problemas al trabajar con el JasperReports Integration kit cuando cambiaba de Workspace.

Básicamente cuando trabajaba con una aplicación en el workspace1 funcionaba bien la pre visualización de los reportes en PDF, pero cuando desde la misma PC cambiaba al workspace2 usando otra aplicación que también tiene instalado el Kit, ya no le funcionaba la pre visualización de los PDF.

El problema radica en el Access Control List(ACL), desde Oracle 11g los paquetes como UTL_TCP, UTL_SMTP, UTL_MAIL, UTL_HTTP, y UTL_INADDR los cuales pueden ser usados para acceder a los recursos de red externos, están más restringidos y asegurados. Oracle 11g introdujo Fine-Grained Access a estos paquetes mediante la creación de una lista de control de acceso (Access Control List ACL) para utilizar cualquier recurso de la red externa a través de estos paquetes.

Antes de esto cualquier usuario que tenía un privilegio de ejecución sobre estos paquetes era capaz de hacer cualquier cosa a cualquier recurso de red como servidores de correo web y locales, etc. Pero ahora un usuario necesita un poco más que sólo un privilegio EXECUTE sobre los paquetes de red. El kit utiliza el paquete UTL_HTTP y es por ello que necesitamos el ACL para dar permiso al esquema.

Por otro lado, tenemos que tener en cuenta que nosotros podemos asignar múltiples hosts a un ACL, por ejemplo: computadora, dominio o IP especificando el rango de los puertos, pero lo que no podemos hacer es asignar un host a múltiples ACL, si hacemos eso el usuario anterior (esquema) es eliminado para que tome efecto el usuario actual a la conexión, y es eso precisamente lo que pasa cuando se cambia de esquema al usar en este caso el kit, y por ello debemos crear una página que administre eso en la aplicación.

En el caso del uso de JasperReports Integration Kit en nuestras aplicaciones, debemos conceder el acceso y privilegio al esquema donde corre nuestra aplicación.

El Paquete PL/SQL que la aplicación de ejemplo del kit genera automáticamente es el siguiente:

declare

   l_acl_name varchar2(100) := 'JasperReportsIntegration-CLARTECH.xml';

begin

begin

   dbms_network_acl_admin.drop_acl(

     acl =>         l_acl_name

   );

exception

   when others then null; -- ACL does not exist yet

end;

-- Privilege to connect to a host

dbms_network_acl_admin.create_acl(

   acl =>         l_acl_name,

   description => 'Accessing the local host for printing with Tomcat',

   principal =>   upper('CLARTECH'), -- DB Schema (grantee)

   is_grant =>   true,

   privilege =>   'connect',

   start_date => null,

   end_date =>   null

);

-- Privilege to resolve a hostname (DNS lookup)

dbms_network_acl_admin.add_privilege (

   acl =>         l_acl_name,

   principal =>   upper('CLARTECH'), -- DB Schema (grantee)

   is_grant =>   true,

   privilege =>   'resolve',

   start_date => null,

   end_date =>   null

);

-- Privilege to connect to localhost

dbms_network_acl_admin.assign_acl(

   acl =>         l_acl_name,

   host =>       '127.0.0.1',

   lower_port => 80,

   upper_port => 10000

);

 

-- Privilege to connect to localhost

dbms_network_acl_admin.assign_acl(

   acl =>         l_acl_name,

   host =>       'localhost',

   lower_port => 80,

   upper_port => 10000

);

 

-- Privilege to connect to localhost

dbms_network_acl_admin.assign_acl(

   acl =>         l_acl_name,

   host =>       'localhost',

   lower_port => 80,

   upper_port => 10000

);

end;

/

commit

/

Donde podemos ver que el nombre del ACL es: 'JasperReportsIntegration-CLARTECH.xml' que agrega el nombre del esquema al final del nombre del archivo xml.

Se crea el ACL con el procedimiento: dbms_network_acl_admin.create_acl

Se asigna el privilegio al esquema con el procedimiento: dbms_network_acl_admin.add_privilege

Y se asigna el ACL a los distintos Hosts con el procedimiento: dbms_network_acl_admin.assign_acl

Este paquete lo debemos correr como sys dba para cada esquema que queramos usar el kit.

Por ello es importante tener en la aplicación un apartado de Administración y crear una página de Access Control para manejar estos aspectos propios de la aplicación.

Hasta Pronto!

 

Cómo Crear la funcionalidad de Ayuda en APEX 5.0 usando el APEX_APPLICATION.HELP

$
0
0

Hoy quiero compartir un procedimiento que la verdad es muy útil cuando tenemos una aplicación que tiene muchos elementos en las páginas que son complejos de entender su uso o cuando una página tiene mucha información. Es bueno contar con una página de ayuda que nos permita visualizar que significa cada elemento de la página y de qué se trata la página en sí misma.

 

Es por ello que quiero mostrarte cómo puedes usar el procedimiento APEX_APPLICATION.HELP.

 

Básicamente, este procedimiento muestra la ayuda con formato HTML tanto de las páginas como de los Elementos y nos permite personalizar la ayuda en nuestra aplicación.

Esta es la sintaxis del procedimiento:

APEX_APPLICATION.HELP (

   p_request       IN VARCHAR2 DEFAULT NULL,

   p_flow_id       IN VARCHAR2 DEFAULT NULL,

   p_flow_step_id   IN VARCHAR2 DEFAULT NULL,

   p_show_item_help IN VARCHAR2 DEFAULT 'YES',

   p_show_regions   IN VARCHAR2 DEFAULT 'YES',

   p_before_page_html     IN VARCHAR2 DEFAULT '<p>',

   p_after_page_html     IN VARCHAR2 DEFAULT NULL,

   p_before_region_html   IN VARCHAR2 DEFAULT NULL,

   p_after_region_html   IN VARCHAR2 DEFAULT '</td></tr></table></p>',

   p_before_prompt_html   IN VARCHAR2 DEFAULT '<p><b>',

   p_after_prompt_html   IN VARCHAR2 DEFAULT '</b></p>:&nbsp;',

   p_before_item_html     IN VARCHAR2 DEFAULT NULL,

   p_after_item_html     IN VARCHAR2 DEFAULT NULL);

Vamos a mostrar en un ejemplo como podemos hacer uso de este procedimiento.

Primero de todo es conveniente tener una aplicación en el cual ingresemos el texto de ayuda en las casillas que corresponden a la Ayuda el cual están disponibles tanto en páginas como en elementos de APEX.

Yo he creado para este ejemplo una aplicación con un Informe Interactivo de las tablas EMP y DEPT usando el asistente de Pantalla Basada en Tabla con Informe.

En la edición de la página 1 - Inicio, en la propiedad Ayuda ingresamos un texto de ejemplo, en mi caso:

Ejemplo demo para aprender a utilizar el procedimiento HELP de APEX.

De este mismo modo en las demás páginas ingresamos textos de ayuda en cada página y en los elementos que componen las páginas.

Crear Página Ayuda

Ahora vamos a crear una página en blanco que le pondremos como nombre Ayuda. En Ruta de Navegación, no le asignamos ninguna ruta y tampoco le asociamos al Menú de navegación. Podemos asignarle que el modo de página sea: Cuadro de Diálogo Modal.

En el Diseñador de Página de la página Ayuda, creamos una Región de tipo Contenido Dinámico PL/SQL.

Ingresamos el siguiente código PL/SQL en Origen de la Región:

APEX_APPLICATION.HELP(
   p_flow_id => :APP_ID,
   p_flow_step_id => :REQUEST,
   p_before_region_html => '<p><br/><table bgcolor="#A3BED8" width="100%"><tr><td><b>',
   p_after_prompt_html => '</b></p>&nbsp;&nbsp;');

Y guardamos la página.

Agregar link en Lista de Barra de Navegación

Ahora lo que necesitamos crear es un link a la Página de Ayuda en la Lista de Barra de Navegación al lado del link Desconectar, en la parte superior derecha de la aplicación.

Para ello, ingresamos a Componentes Compartidos de la aplicación y en la sección Navegación, hacemos clic en el enlace: “Lista de Barra de Navegación”.

Abrimos la lista “Escritorio Barra de Navegación” y hacemos clic en el botón Crear Entrada de Lista >

Configuramos lo siguiente:

- Image/Clase: fa-question-circle

- Etiqueta de Entrada de Lista: Ayuda

- Tipo de Destino: Página en ésta aplicación

- Página: <colocamos el número de la página Ayuda>

- Solicitud: &APP_PAGE_ID.

- Hacemos clic en el botón Crear Entrada de Lista.

Volvemos a la Aplicación y ejecutamos la misma para probar que se visualice el texto de ayuda.

Si estamos en la página de inicio por ejemplo solo tengo puesto el texto de ayuda de la página si hago clic en el icono de ayuda se abrirá la ventana modal mostrando la ayuda.

Ahora si vamos al formulario de edición de datos de Empleados y llamamos a la ayuda nos mostrará todos los textos de ayuda que hayamos ingresado en la página de Edición de Empleados.

De este modo usando el procedimiento APEX_APPLICATION.HELP podemos personalizar nuestros mensajes de ayuda y mostrarlos todos juntos en una página de Ayuda.

Hasta la próxima!

 


Trends in Oracle development tools

$
0
0

One of my most popular conference presentations is the development tools comparison. I've been giving this in various forms for almost 20 years, and will be giving it again at the OUGN Spring Seminar next month. Oracle keeps putting out new development tools, keeping the topic as relevant as ever. 

The "big three" Oracle tools out there right now is the venerable Oracle Forms, just out in release 12c, Oracle APEX and Oracle ADF. I follow many projects and discussions with all of these tools, and the latest Google trends report pretty well matches what I see elsewhere:

(the blue line is Forms, the yellow is ADF and the red is APEX).

You can see Oracle Forms declining slowly and leveling off at about the same level of interest as APEX and ADF. So from the perspective of developer mindshare, either of these three tools is fine. There are of course many other considerations - if you want to learn more, come to my talk at OUGN next month or in June at KScope16 in Chicago.

Usando el Acumulador de Temas de la Barra de Herramientas del Desarrollador

$
0
0

Cuando se ejecuta una aplicación de escritorio en el Creador de Aplicaciones, aparece la barra de herramientas del Desarrollador en Tiempo de Ejecución en la parte inferior la página. Con ella podemos editar rápidamente la aplicación actual, ir a la página que actualmente se está ejecutando, ver el estado de sesión o cambiar los colores de la plantilla que estamos usando con la funcionalidad Acumulador de Temas entre otras cosas.

Rápidamente veamos los elementos que conforman la Barra de Herramientas del Desarrollador:

  • Inicio: nos lleva a la página de inicio de Oracle Application Express.
  • Aplicación 112: con ella accedemos a la página de inicio de la aplicación con la cual estamos trabajando.
  • Editar Página 1: permite el acceso al diseñador de páginas correspondiente a la página que se está ejecutando.
  • Sesión: nos muestra la información del estado de sesión de la página actual.
  • Ver Depuración: recoge los informes de la depuración.
  • Depurar: nos permite alternar la página entre depuración y sin modo de depuración.
  • Mostrar Cuadrícula: cambia entre Mostrar Cuadrícula y Ocultar Cuadrícula. Esta opción solo se aplica si se utiliza un diseño de cuadrícula y la página tiene más de una columna.
  • Edición Rápida: nos lleva al modo de edición rápida solo seleccionando el componente deseado para acceder instantáneamente a su Diseñador de Página.
  • Acumulador de Temas (Theme Roller): nos permite personalizar fácil y rápidamente la apariencia de una aplicación con el selector de color y el establecimiento de valores.

El acumulador de Temas es una nueva funcionalidad de Apex 5.0 que nos permite cambiar fácilmente el color de nuestras plantillas cuando estamos usando el Tema Universal 42.

Cuando hacemos clic en la opción Theme Roller o Acumulador de Temas se abre el selector de colores.

Vamos a ver las secciones que trae esta nueva funcionalidad:

Style: hay dos estilos que vienen con APEX 5.0: el Vita (azul) y el Vita Slate (gris). Podemos ver que estamos usando actualmente el Vita y si cambiamos a Vita Slate veremos que cambia los colores a combinaciones de grises.

Color Wheel: cuando queremos cambiar los colores, una forma fácil de ver diferentes opciones es mediante el uso de la rueda de color. Se puede usar en dos modos: monocromo (2 puntos) y dual de dos colores (3 puntos).

Al cambiar un punto se va a asignar el otro punto a un color complementario. Si usamos la opción de 3 puntos se puede mover el tercer punto para jugar más con los colores.

Global Colors: si con la rueda de color no logramos lo que necesitamos, podemos personalizar los colores globales. Esos son los colores principales del tema Universal y se utiliza para indicar los colores a los diferentes componentes y éstos se pueden personalizar, por ejemplo, yo he cambiado los diferentes colores a colores muy vividos para que se note la diferencia en cada componente.

Containers: nos permite definir los componentes específicos. El icono de comprobación es decir el Check significa que el color es estándar y viene con el estilo seleccionado. Una "x" significa que el color fue cambiado y un "!" significa que el contraste no es la mejor opción de color.

De igual modo se visualiza en la sección Regions.

Y por último tenemos la sección de Custom CSS que nos permite crear nuestras propias reglas de estilos CSS.

Y todos los cambios podemos guardarlos y asignarle un nombre el Estilo del Tema.

Si no nos gusta como quedó la combinación de colores simplemente podemos resetear haciendo clic en el botón Reset que está en el costado del icono de lupa y volvemos a tener los colores del tema original.

La verdad es una gran característica el poder ver directamente los colores que asignamos a los distintos componentes en nuestra aplicación en APEX en ejecución y el hecho de contar con esta nueva funcionalidad hace que cambiar los colores ahora sea muy fácil de implementar.

Hasta pronto!!!

Mi Aplicación Demo en Oracle APEX está disponible!

$
0
0
Hola a tod@s!!!
Estoy muy contenta de anunciar que he terminado mi aplicación demo en APEX 5 para mostrar en acción los articulos que escribo regularmente para la comunidad hispana fans de Oracle APEX!

En esta aplicación iré subiendo los ejerciciios que voy escribiendo en la medida que se pueda, ya que como esta en la instancia de Oracle hay ciertos demos que no los puedo hacer por no tener acceso a la base de datos y al sistema de archivos.
Espero veas esta aplicación Demo como un recurso más en Español sobre Oracle Application Express!
Te invito a que la visites y me cuentes que te parece mi idea :)
Ingresa al siguiente link para visitar mi Aplicación Demo en APEX.

Será hasta la próxima!

Error 503 - Service Unavailable - Oracle REST Data Services y APEX 5.0

$
0
0

Un problema que tuve el otro día que me llevó bastante tiempo tratar de encontrar la solución y es el que quiero compartir con ustedes.

Tengo una instalación de Apex 5.0.3.00.03 desplegándose en GlassFish 4.1.1 con el Oracle REST Data Services 3.0.2.294.08.40.

Resulta que tengo una aplicación que estoy trabajando que inicialmente la estaba trabajando con la configuración del PL/SQL Gateway, después decidí desplegar APEX en GlassFish para después trabajar con los reportes en Jasper Report Integration, el tema es que al realizar todos los pasos para instalar GlassFish y el Oracle REST Data Services, mi aplicación no mostraba las imágenes propias del Workspace, no había problemas con las imágenes del propio Apex pero sí tenía problemas por ejemplo para mostrar el logo de la aplicación, como podemos ver en la imagen siguiente:

En la parte superior vemos que no se muestra la imagen y en el src de la imagen vemos el mensaje que dice que no puede cargar la imagen.

Investigando me encontré con que lo primero que tenemos que hacer es configurar el Oracle REST Data Services ejecutando el script apex_rest_config.sql cosa que ya lo había hecho, donde se ingresa las contraseñas para los dos usuarios: APEX_LISTENER y APEX_REST_PUBLIC_USER.

Por otro lado, para ver que los archivos estáticos se mostraran por medio del Oracle REST Data Services, podemos verificar si el RESTful Services funciona en el Workspace, entonces para ello ingresamos al Taller de SQL luego a Servicios RESTful, y seleccionamos oracle.example.hr

Hacemos clic en GET de empinfo

Y luego en la parte inferior clic en el botón Probar.

Y vemos el error que nos muestra 503 Service Unavailable :(

Con la siguiente especificación del error:

  • The connection pool named: apex_rt is not correctly configured, due to the following error(s): ORA-28150: proxy not authorized to connect as client.

Es importante que el servicio REST sea invocado por el usuario de esquema que el servicio REST fue definido, en mi caso mi usuario del esquema se llama clartech.

El problema fue que ejecute el script apex_rest_config.sql para habilitar el RESTful Services después que ya había creado el workspace y el esquema.

Entonces para darle el GRANT de conexión al esquema, abrimos una ventana de comandos y luego accedemos al SQLPlus con las credenciales de SYSDBA y ejecutamos el siguiente alter:

ALTER USER <apex_workspace_name> GRANT CONNECT THROUGH apex_rest_public_user;

Volvemos a realizar la prueba del RESTful Services y ahora si descarga los datos de la tabla EMP en un archivo CSV.

Ingresamos a la aplicación con la cual teníamos problemas de carga de imágenes y podemos ver que las imágenes de nuestra aplicación se muestran correctamente.

Para revocar el permiso hacemos:

ALTER USER clartech REVOKE CONNECT THROUGH apex_rest_public_user;

Por último he realizado una prueba, creando un nuevo Workspace con un nuevo esquema y luego he probado el RESTful Services y descargaba correctamente los datos de la tabla EMP, sin que haya que tenido que dar ningún GRANT al esquema nuevo.

Espero les sea de utilidad la información, será hasta la próxima!

Error 404 - Not Found - RESTful Services en APEX 5 y GlassFish 4.1.1

$
0
0

Parece que los errores me persiguen…. Bueno en esta oportunidad se me presenta un error 404 en una de mis máquinas virtuales de trabajo que definitivamente no me dice nada del problema que pueda tener, así que en este post voy a mostrarles cómo habilitar el debug del ORDS para que nos muestre los errores en pantalla y podamos tener un poquito más de claridad a la hora de encontrarnos con estos errores tan incómodos!

Primero de todo es importante mencionar las versiones de cada software:

  • Oracle Apex 5.0.1.00.06
  • GlassFish 4.1.1
  • Oracle REST Data Services 3.0.2.294.08.40

El error se produjo al utilizar RESTful Services en mi Workspace. Desde el Taller de SQL ingresé al RESTful Services y realicé una prueba del Oracle.example.hr --> empinfo --> GET

Para entender un poco más sobre el error, lo mejor es activar el log de depuración del Oracle REST Data Services.

Para ello ingresamos a la instalación del ORDS buscamos los archivos de configuración que en mi caso los tengo dentro de una carpeta llamada config, y allí dentro de la carpeta apex (en mi caso porque le he cambiado el nombre al archivo war, sino sería ords) vamos a encontrar un archivo xml llamado defaults.xml, lo abrimos para editarlo:

Colocamos en true las dos líneas:

<entry key="debug.debugger">true</entry>

<entry key="debug.printDebugToScreen">true</entry>

Es necesario que bajemos el servidor de Glassfish (en mi caso) y luego lo subamos para que tome los cambios realizados.

Ingresamos al CMD y vamos a la siguiente carpeta:

Bajar el Servidor

C:\glassfish4/bin> asadmin stop-domain

Subir el Servidor

C:\glassfish4/bin> asadmin start-domain

Volvemos a probar el RESTful Services para que nos muestre el error en pantalla.

Este problema en mi caso, a parte de que no funciona las peticiones del RESTful Services, hace que no se muestren las imágenes que uso en mi aplicación en APEX como por ejemplo el logo. En otro post comento sobre el mismo problema pero me aparece el Error 503. El problema radica que el ORDS no puede encontrar las imágenes guardadas en el Workspace.

Como el error que veo no me indica nada muy específico también me dirijo a verificar el log del Servidor GlassFish, que está dentro del dominio, en mi caso en:

C:\glassfish4\glassfish\domains\domain1\logs\server.log

Si lo abrimos con un editor de textos, podemos revisar su contenido por si nos da mayor luz para el problema que tenemos.

En mi caso pude ver que me indicaba que el archivo de configuración apex_al estaba mal configurado, y me indicaba el error ORA-01017: invalid username/password; logon denied.


Aquí vemos claramente que el password estaba mal ingresado en la cuenta APEX_LISTENER.

Para ello ingresamos al SQLPlus y verificamos que tanto esa cuenta como el APEX_REST_PUBLIC_USER estén OPEN y que las contraseñas sean las correctas de la base de datos y de los archivos de configuración del ORDS, caso contrario tendríamos este error.

Verificamos las cuentas:

SQL> conn sys as sysdba

Enter password:

Connected.

SQL> select username, account_status from dba_users;

USERNAME                       ACCOUNT_STATUS

------------------------------ --------------------------------

APEX_PUBLIC_USER               OPEN

SYS                           OPEN

SYSTEM                        OPEN

ANONYMOUS                     OPEN

ORDS_PUBLIC_USER               OPEN

CLARTECH                       OPEN

FLOWS_FILES                   EXPIRED

XDB                           EXPIRED

APEX_LISTENER                 LOCKED(TIMED)

APEX_REST_PUBLIC_USER         LOCKED(TIMED)

APEX_040000                   LOCKED

OUTLN                         EXPIRED & LOCKED

DIP                           EXPIRED & LOCKED

ORACLE_OCM                     EXPIRED & LOCKED

XS$NULL                       EXPIRED & LOCKED

MDSYS                         EXPIRED & LOCKED

CTXSYS                         EXPIRED & LOCKED

DBSNMP                         EXPIRED & LOCKED

APPQOSSYS                     EXPIRED & LOCKED

HR                             EXPIRED & LOCKED

ORDS_METADATA                 EXPIRED & LOCKED

APEX_050000                   EXPIRED & LOCKED

Las cuentas APEX_LISTENER y APEX_REST_PUBLIC_USER están bloqueadas, eso es por la cantidad de veces que se ingresó mal la contraseña y en el profile indica que hasta cierta cantidad de veces se bloquea, también se puede editar el profile por default con asistencia del DBA.

Desbloqueamos las cuentas:

SQL> alter user APEX_LISTENER account unlock;

User altered.

SQL> alter user APEX_REST_PUBLIC_USER account unlock;

User altered.

SQL> alter user APEX_050000 account unlock;

User altered.

Y verificamos las contraseñas que sean las correctas.

Para volver a configurar el ORDS desde la instalación del mismo abrimos una ventana de comandos y ejecutamos:

C:\ords>java -jar apex.war setup xe

Enter the name of the database server [localhost]:

Enter the database listen port [1521]:

Enter the database SID [xe]:

Enter 1 if you want to verify/install Oracle REST Data Services schema or 2 to skip this step [1]:

Enter the database password for ORDS_PUBLIC_USER:

Confirm password:

Enter 1 if you want to use PL/SQL Gateway or 2 to skip this step.

If using Oracle Application Express or migrating from mod_plsql then you must enter 1 [1]:

Enter the PL/SQL Gateway database user name [APEX_PUBLIC_USER]:

Enter the database password for APEX_PUBLIC_USER:

Confirm password:

Enter 1 to specify passwords for Application Express RESTful Services database users (APEX_LISTENER, APEX_REST_PUBLIC_USER) or 2 to skip this step [1]:

Enter the database password for APEX_LISTENER:

Confirm password:

Enter the database password for APEX_REST_PUBLIC_USER:

Confirm password:

Mar 08, 2016 4:45:29 PM oracle.dbtools.rt.config.setup.SchemaSetup install

INFO: Oracle REST Data Services schema version 3.0.4.60.12.48

Si tenemos arriba el servicio de GlassFish lo bajamos y luego lo volvemos a subir para que tome los nuevos valores de los archivos de configuración del ORDS.

Lo que siempre hago es también verificar que en el SQLPlus pueda conectarme con los usuarios para verificar que la password es la correcta.

Por ejemplo:

SQL> conn apex_public_user/mi_password@xe

Connected.

SQL> conn apex_rest_public_user/mi_password@xe

Connected.

SQL> conn ords_public_user/mi_password@xe

Connected.

SQL> conn apex_listener/mi_password@xe

Connected.

Finalmente podemos ingresar al RESTFul Services y probar de nuevo la petición y vemos que ahora sí nos descarga el archivo CSV solicitado.:)

Ahora al poder ver que el RESTful Service funciona bien con las peticiones, podemos ver que las imágenes de la aplicación ya son mostradas correctamente. :)

El Error 404 puede ser un verdadero dolor de cabeza como lo son los errores tan generales, que no sabemos que puede ser, ya que cualquier cosa puede estar pasando para darnos ese error y es por eso que es muy bueno saber cómo ir paso a paso desmenuzando un problema e ir descartando posibilidades de errores y revisando todos los logs que tenemos disponibles para que de esa forma sea más fácil llegar a una solución.

Hasta la Próxima!!!

Viewing all 142 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>