10 сент. 2008 г.

We are the champions

В минувшую субботу принял участие в любительском волейбольном турнире за кубок улицы Кубикова :) Вот как этот кубок выглядит:


Надо сказать погода выдалась жаркая, а играть пришлось с 9 утра до 6 вечера. Так что к окончанию турнира я был выжат как лимон. Еще бы, так прыгать :)


Но усилия были не напрасны! Наша команда, а назывались мы Hauzové, заняла первое место!

8 сент. 2008 г.

Apex. Экспорт отчетов.

Как реализовать экспортов отчетов в word, excel и иже своими силами.
Общая идея:
1. Создать таблицу с полем BLOB, куда будут генериться отчеты.
2. Создать OnDemand процесс, который будет из APEX_APPLICATION_PAGE_REGIONS выдергивать запрос для отчета. На основе этого запроса сгенерить xml данные. К этим данным применить xslt stylesheet и результат записывать в созданную до этого таблицу.
3. Написать javascript, который запустит этот процесс, а потом запустит на скачивание сформированный документ.

Ниже приводится "рыба". Понятно, что в итоге можно получить очень гибкую систему, позволяющую иметь полный контроль над выводом отчетов с помощью xslt.
Рабочий пример находится здесь: http://apex.oracle.com/pls/otn/f?p=33753:1:0

Итак, создаем таблицу и последовательность для первичного ключа:

create table breports(
id number(10) constraint pk_breports primary key,
breport blob,
mimetype varchar2(30),
filename varchar2(255),
charset varchar2(10),
created_by varchar2(30),
created_on date default sysdate
)
/
create sequence s_breports start with 1 increment by 1 maxvalue 9999999999
/


Функции:

-- Вспомогательная функция для конвертации в BLOB
create or replace function clob_to_blob(c in clob ) return blob is
pos pls_integer := 1;
buffer raw( 32767 );
res blob;
lob_len pls_integer := dbms_lob.getlength( c );
begin
dbms_lob.createtemporary(res, true);
dbms_lob.open(res, dbms_lob.lob_readwrite);

loop
buffer := utl_raw.cast_to_raw( dbms_lob.substr( c, 16000, pos));

if utl_raw.length( buffer ) > 0 then
dbms_lob.writeappend( res, utl_raw.length( buffer ), buffer );
end if;

pos := pos + 16000;
exit when pos > lob_len;
end loop;

return res; -- res is open here
end clob_to_blob;
/

-- функция экспорта отчета
create or replace
function export_report(p_app number, p_page number, p_region varchar2) return number as
l_query clob;
l_str varchar2(4000);
l_stylesheet xmltype := xmltype('<?xml version="1.0" ?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"><xsl:template match="/"><html><body><table><tr><xsl:for-each select="./ROW[1]/*"><xsl:call-template name="header"/></xsl:for-each></tr><xsl:apply-templates select="ROW"/></table></body></html></xsl:template><xsl:template match="ROW"><tr><xsl:for-each select="./*"><xsl:call-template name="data"/></xsl:for-each></tr></xsl:template><xsl:template name="header"><td><xsl:value-of select="name(.)"/></td></xsl:template><xsl:template name="data"><td><xsl:value-of select="text()"/></td></xsl:template></xsl:stylesheet>');
l_mimetype varchar2(30) := 'application/msword';
l_filename varchar2(30);
l_charset varchar2(30) := 'UTF8';
l_created_by varchar2(30) := v('APP_USER');
l_ln number;
l_null varchar2(30);
begin

select region_source into l_query
from APEX_APPLICATION_PAGE_REGIONS
where application_id = p_app
and page_id = p_page
and region_name = p_region;

select s_breports.nextval into l_ln from dual;
l_filename := 'report_'||to_char(l_ln)||'.doc';

l_str := 'insert into breports (id, breport, mimetype, filename, charset, created_by)
select '||to_char(l_ln)||', clob_to_blob(xmltransform(d,s).getclobval()), '''||l_mimetype||''', '''||l_filename||''', '''||l_charset||''', '''||l_created_by||''' from (select xmlagg(column_value) d from table(xmlsequence(cursor('||l_query||')))) xd, (select xmltype('''||l_stylesheet.getclobval()||''') s from dual) xs';
execute immediate l_str using l_null;
commit;
return l_ln;
exception
when no_data_found
then return 0;
end export_report;


В приложении создадим три айтема уровня приложения: TEMP_ITEM_1, TEMP_ITEM_2, TEMP_ITEM_3
И OnDemand процесс:

declare
l_ret number;
begin
l_ret := export_report(:TEMP_ITEM_1, :TEMP_ITEM_2, :TEMP_ITEM_3);
htp.p(apex_util.get_blob_file_src('P2_BREPORT', l_ret));
end;


Дальше финт ушами: нужно мастером создать форму на основе таблицы BREPORTS.Обозначить первичный ключ ID, источник ключа Existing trigger. Из колонок выбрать только BREPORT и не создавать никаких кнопок типа delete и update... В созданной странице (пусть это будет PAGE 2), нужно зайти в свойства айтема P2_BREPORT (типа File Browse) и изменить свойство Source на BREPORT:MIMETYPE:FILENAME::CHARSET:attachment:Download. Это нужно для работы функции apex_util.get_blob_file_src в OnDemand процессе: там параметр 'P2_BREPORT' - имя айтема File Browse на этой форме.

Теперь на странице с отчетом нужно в header вписать скрипт:

<script language="JavaScript" type="text/javascript">
function exportReport(pApp, pPage, pRegion){
var get = new htmldb_Get(null,$x('pFlowID').value,'APPLICATION_PROCESS=EXPORT_REPORT',1);
get.add('TEMP_ITEM_1', pApp);
get.add('TEMP_ITEM_2', pPage);
get.add('TEMP_ITEM_3', pRegion);
gRet = get.get(null);
get = null;
open(gRet, '_parent');
}
</script>


И последнее, в области отчета создать элемент Display as Text (does not save state) у которого source =

<a href="javascript:exportReport(&APP_ID.,&APP_PAGE_ID., 'Report')">Export to Word</a>

Здесь внимание: 'Report' - это имя region'а c отчетом.

Собственно всё.

5 сент. 2008 г.

apex.oracle.com

Вот как лежит apex.oracle.com :)

4 сент. 2008 г.

Хромированный Apex

Тут на днях наделал шуму выпуск гуглом нового браузера Chrome. Оставлю за кадром свои личные пристрастия к браузером, а отмечу лишь то, ради чего можно установить этот браузер разработчикам приложений на Oracle Apex.

1. Создание ярлыков приложений. Эта функция позволяет создать на рабочем столе ярлык, который запустит апекс приложение без всяких лишних браузеровских меню и панелей. Удобно например создать ярлык для Application Builder'а:


2. Растягивающиеся textarea! Наверное самая "вкусная" фича при разработке в апексе. В Firefox для этого приходилось устанавливать Apex Builder plugin.


3. Встроенный оттладчик html/javascript/css - Chrome Inspector. Собственно местная альтернатива Firebug:


В целом для разработки вполне пригоден без лишней плагиноустановки. Неплохой ход от гугла.

2 сент. 2008 г.

Custom authentication Apex

Долгое время собирался написать сам, но за неимением времени лучше дам ссылку на хорошую заметку, как реализовать свою аутентификацию для приложения Apex. Ничего сложного. Таблица с пользователями и паролями + пакет для управления. Читать здесь.

29 авг. 2008 г.

Вышел патчсет 3.1.2 для Apex

Новость не особо радостная, но всё же. Вышел патчсет для апекса. Доступен как в виде полной инсатляции Apex 3.1.2, так и в виде патчсета для 3.1.1 (MetaLink Patch Number 7313609).

В списке фикснутых багов с ходу не увидел багу, с которой недавно столкнулся, но подробно не исследовал. А именно, что если создать процесс уровня приложения с типом On New Session: After Authentication, то в нем то ли пременная APP_USER не устанавливается, то ли сам процесс не запускается. Времени потестить не было. Но факт, что это штуковина у меня с ходу не завелась :( Или я не умею её готовить? :)

27 авг. 2008 г.

Полный список текстовых сообщений APEX


select * from flows_020100.wwv_flow_messages$

Здесь есть все сообщения Апекс. Чтобы перевести или изменить их, достаточно в Shared Components -> Text Messages создать сообщение для языка вашего приложения с соответсвующим именем wwv_flow_messages$.name и вашим текстом.