Sabtu, 20 Oktober 2012

Mimetype and size validation on APEX upload file

Beberapa hal yang harus anda perhatikan dalam implementasi upload image dalam APEX adalah:
  1. Aplikasi harus memakai otentifikasi
  2. Proses upload bisa memakai storage type :custom table atau WWV_FLOW_FILES (APEX_APPLICATION_FILES). Disini saya tidak akan menjelaskan bagaimana kedua teknik tersebut dipakai.
  3. Keduanya akan terbentuk di tabel APEX_APPLICATION_FILES, jadi anda harus hati-hati meskipun melakukan upload ke custom table maka otomatis juga terbentuk di WWV_FLOW_FILES. silahkan buktikan dengan melakukan query ke table WWV_FLOW_FILES saat anda berhasil upload di custom table. 
  4. Dengan fakta-fakta diatas maka:
    • Kita bisa melakukan validasi file upload terhadap jenis dan ukuran size dari file sebelum benar-benar kita upload ke dalam custom tabel dengan melakukan query ke APEX_APPLICATION_FILES.
    • Anda harus melakukan proses delete tabel APEX_APPLICATION_FILES saat melakukan validasi, setelah proses update/insert ke custom table. karena insert selalu terjadi ke table APEX_APPLICATION_FILES baik validasi berhasil atau gagal.

Dengan beberapa fakta diatas kita bisa implementasikan upload image dengan Storage Type: BLOB column specific in Item Source Attribute, kemudian melakukan validasi dengan bantuan WWV_FLOW_FILES pada contoh demo ini.

How to accomplish dynamic image on form and report?

Posting kali ini kita akan mencoba membuat sebuah dynamic image yang akan tampil di report dan form. Disebut dynamic karena kita bisa me-reference image dari column BLOB, external URL atau default image yang tersimpan dalam #WORKSPACE_IMAGES#.
  1. Alter table yang akan digunakan sebagai penampung dalam contoh ini saya pake EMP.
  2. ALTER TABLE emp ADD
    (EMP_PHOTO blob,
    ATTACH_MIMETYPE VARCHAR2(255),
    ATTACH_FILENAME VARCHAR2(255),
    ATTACH_LAST_UPDATE DATE,
    ATTACH_CHARSET VARCHAR2(128),
    EXTERNAL_URL VARCHAR2(255));
    
  3. Buat report-form dengan wizard.
  4. Pada bagian report edit query dengan seperti ini.
  5. select "EMPNO", 
    "ENAME",
    "JOB",
    "MGR",
    "HIREDATE",
    "SAL",
    "COMM",
    "DEPTNO",
    "EXTERNAL_URL",
    case when dbms_lob.getlength(emp_photo)<>0 then 
        '<img alt="emp Image" height="75" src="'||
        apex_util.get_blob_file_src('P24_EMP_PHOTO',empno)||
        '" style="-moz-border-radius: 4px; -webkit-border-radius: 4px; border: 4px solid #CCC;" title="emp Image" width="75" />'
           when  nvl(dbms_lob.getlength(emp_photo),0)=0  and external_url is not null   then
        '<img alt="emp Image" height="75" src="'||external_url||
        '" style="-moz-border-radius: 4px; -webkit-border-radius: 4px; border: 4px solid #CCC;" title="emp Image" width="75" />'       
           else     
        '<img alt="emp Image" height="75" src="#WORKSPACE_IMAGES#1.jpg" style="-moz-border-radius: 4px; -webkit-border-radius: 4px; border: 4px solid #CCC;" title="emp Image" width="75" />'       
    end as "PHOTO"
    from EMP
    
  6. Pada bagian form tambahkan item: display only dengan source :
  7. declare
     v_exist number(2);
    begin
    select count(1) into v_exist
    from emp
    where empno=:P24_EMPNO and DBMS_LOB.GETLENGTH(EMP_PHOTO)<>0;
    if  v_exist&lt;&gt;0 then
    return '<img src="'||apex_util.get_blob_file_src('P24_EMP_PHOTO',:P24_EMPNO)||'" />';
    elsif  v_exist=0 and :P24_EXTERNAL_URL is not null  then
    return '<img src="'||:P24_EXTERNAL_URL||'" />';
    else
    return '<img src="#WORKSPACE_IMAGES#1.jpg" />';
    end if;
    end;
    
  8. Update/create form dengan upload image ke dtatbase atau mereference URL link di EXTERNAL_URL

Demo bisa diakses di sini

Sabtu, 13 Oktober 2012

Bagaimana Memproteksi Kode PL/SQL Memakai Function dan Procedure wrapping DBMS_DDL?

Kebutuhan ini sangat vital dimiliki programmer APEX untuk melindungi hak intelektual atau kerahasian sebuah kode contoh menyembunyikan teknik otentifikasi dari user. Karena sampai versi terakhir disisi aplikasi belum menyediakan tool ini.

Dengan memakai feature dari oracle database 11g kita akan memakai CREATE_WRAPPED Procedures atau WRAP Functions yang dijelaskan di dokumentasi “Oracle® Database PL/SQL Packages and Types Reference 11g”. berbeda dengan teknik 10g pada 11g jauh lebih simple.

Namun penjelasannya sangat singkat dan membingungkan sehingga perlu penjelasan lebih lanjut. Contoh pemakaian DBMS_DDL.CREATE_WRAPPED ada dua cara:

  1. Cara singkat dengan single parameter tipe varchar2:
  2. DBMS_DDL.CREATE_WRAPPED (
       ddl VARCHAR2);
    
  3. Cara detil dengan 3 parameter tipe DBMS_SQL.VARCHAR2A dan PLS_INTEGER
  4. DBMS_DDL.CREATE_WRAPPED(
        ddl DBMS_SQL.VARCHAR2A,
        lb PLS_INTEGER,
        ub PLS_INTEGER);
Dengan bantuan mbah google akhirnya diperoleh contoh pemakaiannya yaitu:

  1. Cara pertama memakai bantuan quote q!' ddl script'! (sumber Tyler Muth’s Blog) :
  2. declare
      l_function    varchar2(32767);
    begin
      l_function := q'!
        create or replace function wrap_test
          return varchar2
        is
        begin
           return 'Yep, it worked';
        end wrap_test; !';
      sys.dbms_ddl.create_wrapped(l_function);
    end;
    /
    
  3. Cara kedua (sumber Oracle Base) :
  4. DECLARE
      l_source  DBMS_SQL.VARCHAR2A;
      l_wrap    DBMS_SQL.VARCHAR2A;
    BEGIN
      l_source(1) := 'CREATE OR REPLACE FUNCTION get_date_string RETURN VARCHAR2 AS ';
      l_source(2) := 'BEGIN ';
      l_source(3) := 'RETURN TO_CHAR(SYSDATE, ''DD-MON-YYYY''); ';
      l_source(4) := 'END get_date_string;';
    
      l_wrap := SYS.DBMS_DDL.WRAP(ddl => l_source,
                                  lb  => 1,
                                  ub  => l_source.count);
    
    
    END;
    /
    
Contoh hasil wrapping cara pertama :

CREATE OR REPLACE function wrap_test wrapped 
a000000
369
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
abcd
8
6e 9a
IgQHR6lRS46V6yUCBpIlirzcrOowg8eZgcfLCNL+XhaW8lbjlqFi0e4JptbKschQyiQCfMbK
FyjGyu9LTRd8dcDAMv7S1sXWJl7QWWJQzIVWzBZylpfcLszEOdmYkr4WRlutOdsHUzkHVNV8
pqbvCP2M
/
Anda bisa juga memakai function DBMS_DDL.WRAP dengan menganalisa dari contoh pemakaian procedure DBMS_DDL.CREATE_WRAPPED.

Mengirim HTML email melalui PL/SQL and Gmail (2)

Pada contoh posting sebelumnya masih sederhana dimana kita belum bisa mengirim email dengan attachment, html. untuk contoh selanjutnya kita akan memperluas procedure kirim email melalui procedure berikut dengan sedikit modifikasi pada bagian :
  • deklarasi  spesifik ke gmail :
  • v_smtp_domain    varchar2 (256)     := 'gmail.com';
  • menambahkan
  • utl_smtp.ehlo(v_mail_conn, v_smtp_domain);
  • menon aktikan
  • -- utl_smtp.helo(v_mail_conn, p_smtp_host);
Maka sekarang kita bisa gunakan untuk kebutuhan gmail dengan stunnel :

CREATE OR REPLACE PROCEDURE custom_send_mail (
  p_to          IN VARCHAR2,
  p_cc          IN VARCHAR2 DEFAULT NULL,
  p_bcc         IN VARCHAR2 DEFAULT NULL,
  p_from        IN VARCHAR2,
  p_subject     IN VARCHAR2,
  p_text_msg    IN VARCHAR2 DEFAULT NULL,
  p_html_msg   IN CLOB DEFAULT NULL,
  p_attachments IN table_attachments DEFAULT NULL,
  p_smtp_host   IN VARCHAR2,
  p_smtp_port   IN NUMBER DEFAULT 25,
  p_username    IN VARCHAR2 DEFAULT NULL,
  p_password    IN VARCHAR2 DEFAULT NULL)
AS
  v_mail_conn   UTL_SMTP.connection;
  v_boundary    VARCHAR2(50) := '----=*#abc1234321cba#*=';
  v_step        PLS_INTEGER  := 24573;
  v_length PLS_INTEGER;
  v_begin PLS_INTEGER := 1;
  v_buffer_size INTEGER := 75;
  v_raw RAW(32767);
  v_smtp_domain    varchar2 (256)     := 'gmail.com';
BEGIN
  /* **** Author: Josep Coves
      **** Version: 1.0
      **** Date: 08/06/2012
      **** josepcoves@relational.es
      **** http://www.relational.es
      **** Based on Tim's procedure, Thanks  Tim!
      **** http://www.oracle-base.com/articles/misc/email-from-oracle-plsql.php#html
   */
  v_mail_conn := UTL_SMTP.open_connection(p_smtp_host, p_smtp_port);
  utl_smtp.ehlo(v_mail_conn, v_smtp_domain);
  utl_smtp.command( v_mail_conn, 'AUTH LOGIN'); 
 
  --Establish SMTP connection
  IF (p_username IS NOT NULL) THEN
    utl_smtp.command( v_mail_conn, UTL_RAW.cast_to_varchar2( UTL_ENCODE.base64_encode( UTL_RAW.cast_to_raw( p_username ))) );
    utl_smtp.command( v_mail_conn, UTL_RAW.cast_to_varchar2( UTL_ENCODE.base64_encode( UTL_RAW.cast_to_raw( p_password ))) );
  END IF;
 
 -- utl_smtp.helo(v_mail_conn, p_smtp_host);
  utl_smtp.mail(v_mail_conn, p_from);
  utl_smtp.rcpt(v_mail_conn, p_to);
 
  utl_smtp.open_data(v_mail_conn);
 
  utl_smtp.write_data(v_mail_conn, 'Date: ' || TO_CHAR(SYSDATE, 'DD-MON-YYYY HH24:MI:SS') || UTL_TCP.crlf);
  utl_smtp.write_data(v_mail_conn, 'To: ' || p_to || UTL_TCP.crlf);
  utl_smtp.write_data(v_mail_conn, 'From: ' || p_from || UTL_TCP.crlf);
  IF (p_cc IS NOT NULL) THEN
    utl_smtp.write_data(v_mail_conn, 'CC: ' || p_cc || UTL_TCP.crlf);
  END IF;
  IF (p_bcc IS NOT NULL) THEN
    utl_smtp.write_data(v_mail_conn, 'BCC: ' || p_bcc || UTL_TCP.crlf);
  END IF;
  utl_smtp.write_data(v_mail_conn, 'Subject: ' || p_subject || UTL_TCP.crlf);
  utl_smtp.write_data(v_mail_conn, 'Reply-To: ' || p_from || UTL_TCP.crlf);
  utl_smtp.write_data(v_mail_conn, 'MIME-Version: 1.0' || UTL_TCP.crlf);
  utl_smtp.write_data(v_mail_conn, 'Content-Type: multipart/mixed; boundary="' || v_boundary || '"' || UTL_TCP.crlf || UTL_TCP.crlf);
  --Plain Text Body
  IF p_text_msg IS NOT NULL THEN
    utl_smtp.write_data(v_mail_conn, '--' || v_boundary || UTL_TCP.crlf);
    utl_smtp.write_data(v_mail_conn, 'Content-Type: text/plain; charset="iso-8859-1"' || UTL_TCP.crlf || UTL_TCP.crlf);
 
    utl_smtp.write_data(v_mail_conn, p_text_msg);
    utl_smtp.write_data(v_mail_conn, UTL_TCP.crlf || UTL_TCP.crlf);
 
  ELSIF p_html_msg IS NOT NULL THEN
    -- HTML Body
    utl_smtp.write_data(v_mail_conn, '--' || v_boundary || UTL_TCP.crlf);
    utl_smtp.write_data(v_mail_conn, 'Content-Type: text/html; charset="iso-8859-1"' || UTL_TCP.crlf || UTL_TCP.crlf);
    FOR i IN 0 .. TRUNC((DBMS_LOB.getlength(p_html_msg) - 1 )/v_step) LOOP
      utl_smtp.write_data(v_mail_conn,  DBMS_LOB.SUBSTR(p_html_msg, v_step, i * v_step + 1));
    END LOOP;
 
    utl_smtp.write_data(v_mail_conn, UTL_TCP.crlf || UTL_TCP.crlf);
  END IF;
 
  IF p_attachments IS NOT NULL THEN
 
   FOR i IN 1..p_attachments.COUNT
   LOOP
 
     utl_smtp.write_data(v_mail_conn, '--' || v_boundary || UTL_TCP.crlf);
     utl_smtp.write_data(v_mail_conn, 'Content-Type: ' ||p_attachments(i).attachment_mime || UTL_TCP.crlf);
     utl_smtp.write_data(v_mail_conn, 'Content-Transfer-Encoding: base64' || UTL_TCP.crlf);
     utl_smtp.write_data(v_mail_conn, 'Content-Disposition: attachment; filename="' || p_attachments(i).attachment_name || '"' || UTL_TCP.crlf || UTL_TCP.crlf);
 
     v_length := DBMS_LOB.getlength(p_attachments(i).attachment_blob);
     v_begin := 1;
     v_raw := NULL;
     v_buffer_size := 75;
     WHILE v_begin < v_length LOOP
        DBMS_LOB.read( p_attachments(i).attachment_blob, v_buffer_size, v_begin, v_raw );
        utl_smtp.write_raw_data( v_mail_conn, UTL_ENCODE.base64_encode(v_raw) );
        utl_smtp.write_data( v_mail_conn, UTL_TCP.crlf );
        v_begin := v_begin + v_buffer_size;
     END LOOP ;
 
    utl_smtp.write_data(v_mail_conn, UTL_TCP.crlf || UTL_TCP.crlf);
 
   END LOOP;
 
  END IF;
 
  utl_smtp.write_data(v_mail_conn, '--' || v_boundary || '--' || UTL_TCP.crlf);
  utl_smtp.close_data(v_mail_conn);
 
  utl_smtp.quit(v_mail_conn);
 
END;
/

Selanjutnya kita bisa pakai untuk mengirim, text bisa, html dan attachement. berikut contoh pemakaian untuk mengirim gambar product dari tabel DEMO_PRODUCT_INFO.

DECLARE
  v_clob clob := 'Test email';
  v_blob blob;
 
  t_a table_attachments := NULL;
  v_a mail_attachment ;
BEGIN
 
   SELECT mail_attachment(t.FILENAME,t.MIMETYPE,t.PRODUCT_IMAGE)
              BULK COLLECT INTO t_a
     FROM DEMO_PRODUCT_INFO t;
 
     custom_send_mail (p_to     => '<your_recipient@mail.com>',
                       p_from   => '<yourgmailname@gmail.com>',
                       p_subject     => 'test',
                       p_html_msg => v_clob,
                       p_attachments => t_a,
                       p_smtp_host =>  'localhost',
                       p_smtp_port   => 1925,
                       p_username => 'yourgmailname@gmail.com',
                       p_password  =>  'yourgmailpassword');
END;
/

Maka pada email penerima akan muncul:

Jumat, 12 Oktober 2012

Mengirim HTML email melalui PL/SQL and Gmail (1)

Didalam apex memungkinkan kita untuk integrasi dengan system email smtp yang telah ada. Anda bisa setup melalui WS: internal pada bagian Manage Instance>>Instance Settings>>Email
Dengan integrasi itu kita bisa memakai package yg telah disediakan apex yaitu : APEX_MAIL dan bisa melakan report subscription dan download report melalui email.

Namun saat kita tidak memiliki server email yang bisa kita pake, maka kita bisa memakai alternatif dari gmail. untuk mengimplementasikannya tanpa banyak nyeng-nyong lagi anda bisa pelajari dari bang HÃ¥vard Kristiansen blog.

Catatan :
  • Anda harus mengerti konsep ACL (jika memakai 11g)
  • Anda telah memberi akses ke package UTL_SMTP dan UTL_TCP.
  • Email bisa dikirm melalui pesan html dengan memodifikasi code:
  • write_mime_header (l_conn, 'Content-Type', 'text/plain');
    
    Menjadi
    write_mime_header (l_conn, 'Content-Type', 'text/html');
  • Anda bisa menambahkan inline image (image harus public bisa dibaca) atau style
  • Saya sudah test kirim ke yahoo dan gmail, untuk yahoo image tampil, gmail tidak berhasil

Contoh Pemakaian :
DECLARE
  l_html VARCHAR2(32767);
BEGIN
  l_html := '<html>
    <head>
      <title>HTML with table message</title>
    </head>
    <body>
      <p>This is a <b>HTML with Table and Image</b> <i>version</i> of the test message.</p>
<table border="1"><tbody>
<tr> <th>Hari</th> <th>Topik</th>  </tr>
<tr> <td>Hari 1</td> 
<td><ul>
<li>Preview Oracle APEX</li>
<li>Instalasi APEX dengan arsitektur EPG</li>
<li>Membuat dan mengatur workspace</li>
<li>Akses file image memakai FireFTP</li>
</ul>
</td>
</tr>
<tr> <td>Hari 2</td> 
<td><ul>
<li>Konsep SQL Workshop</li>
<li>Manipulasi Objek Database memakai SQL Workshop (tutorial)</li>
<li>Konsep Application Builder</li>
<li>Konsep Session State &amp; URL Syntax</li>
<li>Membuat Aplikasi Database dari sebuah file spreadsheet (tutorial)</li>
</ul>
</td>
</tr>
<tr> <td>Hari 3</td> 
<td><ul>
<li>Konsep Form, Report</li>
<li>Konsep Page, region komponen (computation,process,validation dan branching)</li>
<li>Membuat Aplikasi Database From Scratch (tutorial)</li>
<li>Konsep chart, calendar dan map </li>
<li>Konsep Theme, template dan file </li>
<li>Membuat Aplikasi Database From Scratch II (tutorial)</li>
</ul>
</td>
</tr>
<tr> <td>Hari 4</td> 
<td><ul>
<li>Konsep Interactive Report</li>
<li>Konsep Shared Component</li>
<li>Konsep Navigasi,tree, list, tab, breadcrumb </li>
<li>Konsep Dynamic Action, Plugin</li>
<li>Demo tutorial </li>
</ul>
</td>
</tr>
<tr> <td>Hari 5</td> 
<td><ul>
<li>Konsep Administration</li>
<li>Konsep Application and Page Utilities</li>
<li>Konsep Security (authentication &amp; authorization)</li>
<li>Demo (tutorial)</li>
<li>Konsep Deploy Application</li>
<li>Demo (tutorial)</li>
</ul>
</td>
</tr>
</tbody></table>      
      <p><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDRQTzDp14vFVpCREZ3WKxvbK8RxY4y8XzLCrwjwcudiHVmn7uRIcQ_M3uPMl9kQp7HT5P8zPc9m_QnLRT4vxSdLc-QpurQ1ecPTqrDT_K1AjmP58ChPME9GLmrG8GL7511eiIVeHSq9w/s200/bonexlogo.png" alt="Bonex Logo" />
    </body>
  </html>';

           g_mail. send_mail_html (
      'yourgmailaccount@gmail.com'
    , 'yourmail_recipient'
    , 'subject html',l_html
   );
END;
/
 
Tampilan di email yahoo penerima :
 

Jumat, 05 Oktober 2012

How to Obfuscate DADS Password on OHS 11g Windows

Pada posting sebelumnya untuk OHS 10g bisa dilihat disini.
Untuk OHS 11g ternyata berbeda, file dadtool.README yang memuat step-by-step obfuscate password tidak ada. tapi bisa di temui langsung di file dadtool.pl atau melalui script
perl dadTool.pl -h


Namun meskipun kita diikuti tetap tidak berhasil untuk kasus windows. Setelah coba sana-sini plus googling ternyata langkahnya berbeda. Yaitu:
  1. Lewat command prompt masuk ke direktori %ORACLE_HOME%\bin :
  2. cd D:\bea_default\Oracle_WT1\bin
    
  3. Set ORACLE_HOME :
  4. set ORACLE_HOME=D:\bea_default\Oracle_WT1
    
  5. Lakukan Obfuscating:
  6. perl dadTool.pl -f D:\bea_default\Oracle_WT1\instances\instance1\config\OHS\ohs1\mod_plsql\dads.conf
    
  7. Pastikan Hasilnya:
  8. All passwords successfully obfuscated. New obfuscations : 1
    

Catatan :
  • Oracle Home: D:\bea_default\Oracle_WT1
  • File dads.conf ada di: D:\bea_default\Oracle_WT1\instances\instance1\config\OHS\ohs1\mod_plsql

Kamis, 04 Oktober 2012

Migrating APEX from EPG to OHS

Mungkinkah hal itu bisa dilakukan dalam mesin yang sama?
Ternyata bisa dan cukup mudah. workspace dan aplikasi yang telah ada tidak ada pengaruhnya, hanya masalah memindahkan broker dan File images saja antara EPG menjadi OHS.
Lakukan hal-hal berikut:
  1. Install OHS. contoh home D:\bea_default\Oracle_WT1
  2. Pastikan akses ke http://localhost:7777 berhasil (default OHS 7777)
  3. Pindahkan file images ke folder images dimana /i/ OHS akan di definisikan. contoh D:\bea_default\APEX41. (pindahkan memakai fireFTP, Filezilla atau cyberduck).
  4. Unlock dan beri password user APEX_PUBLIC_USER
  5. Edit dads.conf
  6. Alias /i/ "D:\bea_default\APEX41\images/"
    <location /pls/apex>
    Order deny,allow
    PlsqlDocumentPath docs
    AllowOverride None
    PlsqlDocumentProcedure wwv_flow_file_mgr.process_download
    PlsqlDatabaseConnectString localhost:1521:ORCL 
    PlsqlNLSLanguage AMERICAN_AMERICA.AL32UTF8
    PlsqlAuthenticationMode Basic
    SetHandler pls_handler
    PlsqlDocumentTablename wwv_flow_file_objects$
    PlsqlDatabaseUsername APEX_PUBLIC_USER
    PlsqlDefaultPage apex
    PlsqlDatabasePassword password_APEX_PUBLIC_USER_ente
    PlsqlRequestValidationFunction wwv_flow_epg_include_modules.authorize
    Allow from all
    </location>
    
  7. Stop OPM.
  8. Delete file images yang ada di database (memakai fireFTP, Filezilla atau cyberduck).
  9. Tutup port 8080 yang dipake EPG
  10. EXEC DBMS_XDB.SETHTTPPORT (0);
    
  11. Lock user ANONYMOUS.
  12. Start OPM dan HTTP server.
  13. Akses kembali apex dengan http://localhost:7777/pls/apex 

Catatan : hati-hati pada langkah no 3 adalah memindahkan dari images EPG yg ada didatabase bukan images hasil extract apex. sehingga kerjaan sebelumnya tidak akan berubah.