My Oracle Database cannot currently handle TLS 1.2 needed to communicate with Office 365 for mail. So I am using an old mail server to send out my mail because it does not need authentication and is located on premise. For internal communication I need to alter all "to" addresses to change the domain so the mail is sent to Microsoft and not back to the internal mail server.
Example: I need to change all emails from user#domain.com to user#domain.onmicrosoft.com. Is there a way I can alter the APEX_MAIL package in Oracle so that any use of domain.com in the "TO" field will automatically change to domain.onmicrosoft.com?
Typically you don't change a proprietary package - even if you could, it would be wrong. Instead you wrap your own pl/sql procedure around apex_mail. Create a package with the same signature as the apex_mail procedures you are using and within those procedures invoke apex_mail. Before the call to apex_mail, do whatever magic you need (change the email adresses in your case). The only downside is that you cannot use this in processes of type "send email" - you'll have to use pl/sql.
Here is an example of a procedure:
PROCEDURE send (
mail_to_i IN VARCHAR2,
mail_from_i IN VARCHAR2,
mail_reply_to_i IN VARCHAR2,
mail_body_i IN VARCHAR2,
mail_body_html_i IN VARCHAR2 DEFAULT NULL,
mail_subject_i IN VARCHAR2 DEFAULT NULL,
mail_cc_i IN VARCHAR2 DEFAULT NULL,
mail_bcc_i IN VARCHAR2 DEFAULT NULL
)
IS
l_mail_to VARCHAR2(4000) := NULL;
l_mail_cc VARCHAR2(4000) := NULL;
l_mail_bcc VARCHAR2(4000) := NULL;
BEGIN
l_mail_to := REPLACE (LOWER(mail_to_i),'#domain.com','#domain.onmicrosoft.com');
-- do other substitutions if needed
apex_mail.send
(
p_to => l_mail_to,
p_from => mail_from_i,
p_cc => mail_cc_i,
p_subj => mail_subject_i,
p_body => mail_body_i,
p_body_html => mail_body_html_i,
p_replyto => mail_reply_to_i,
p_bcc => mail_bcc_i
);
END send;
You cannot and should not modify the APEX_MAIL package. You should modify the process that is calling the APEX_MAIL package to do any replacements if necessary or just validate the input of the email addresses so they are what you expect.
If you have emails that have already been added to apex_mail_queue and have errored, you can update apex_190200.wwv_flow_mail_queue (or whatever the proper APEX schema is for your environment) to correct the addresses in the MAIL_TO column. You will also need to reset mail_send_count and mail_send_error so that it is processed next time emails are attempted to be sent.
UPDATE apex_190200.wwv_flow_mail_queue
SET mail_to = 'some.email#domain.onmicrosoft.com', mail_send_count = 0, mail_send_error = NULL;
Related
Is there a way to automatically create a user in my application based on values in a table?
So I have a table named EMP as employee in that table I have attributes name, surname, username and password. What do I need to do to automatically create a user account in my application when a new employee is inserted into the database?
I have no idea where to start, is that done in processes or is there a pre-built feature that covers this?
Is there a sample code that everyone just uses and adapts to their needs?
In such a case, maybe it would be better to use custom authentication scheme instead of default, built-in Apex authentication.
How to create new users? Just insert them into the table. It would be a good idea to create a package which contains procedures used to manipulate with users' data.
You shouldn't store passwords as text for security reasons.
For example:
procedure p_create_user (p_name in varchar2, p_surname in varchar2,
p_username in varchar2, p_password in varchar2)
is
l_hash raw (2000);
begin
l_hash :=
dbms_crypto.hash (
utl_i18n.string_to_raw (p_password, 'AL32UTF8'),
dbms_crypto.hash_sh1);
insert into emp (name, surname, username, password)
values
(p_name, p_surname, p_username, l_hash);
end;
In Apex, authentication scheme requires you to create your own function (and set its name into the "Authentication Function Name" property) which accepts username/password combination and returns Boolean: TRUE (if credentials are valid) or FALSE (if not). Note that parameters' names MUST be p_username and p_password.
Use code similar to that in previous procedure:
function f_auth (p_username in varchar2, p_password in varchar2)
return boolean
is
l_pwd emp.password%type;
begin
select password
into l_pwd
from emp
where username = p_username;
return l_pwd = dbms_crypto.hash (
utl_i18n.string_to_raw (p_password, 'AL32UTF8'),
dbms_crypto.hash_sh1);
end;
That's all you need for basic functionality. You can add another procedure to let users change their password etc.
I have an Interactive Report with column having arabic Characters displaying well in the Report , however i am exporting the Reports into csv but the arabic characters turns to ????? in csv file , Any suggestions or workaround i may follow either at Interactive Report level or query level or this something csv don't support.
I am not entirely sure, but I think the problem might happen because the Java script function associated with Actions Menu --> Download might not take in consideration the encoding on database side, rather than the one on client side.
Normally, when I want to control the export to csv from a page, I disable the actions menu to avoid that the user can do it using that menu, instead I prefer to create a PL/SQL procedure to be triggered by an application express process.
How to do that ?
Download CSV File Using PL/SQL Procedure and Application Process in Oracle Apex
In order to do this , follow the instructions:
1.Create a PL/SQL Procedure
Create a database procedure which will return the CSV as CLOB data.
create or replace procedure tab_to_csv(o_Clobdata OUT CLOB) IS
l_Blob BLOB;
l_Clob CLOB;
BEGIN
Dbms_Lob.Createtemporary(Lob_Loc => l_Clob,
Cache => TRUE,
Dur => Dbms_Lob.Call);
SELECT Clob_Val
INTO l_Clob
FROM (SELECT Xmlcast(Xmlagg(Xmlelement(e,
Col_Value || Chr(13) ||
Chr(10))) AS CLOB) AS Clob_Val,
COUNT(*) AS Number_Of_Rows
FROM (SELECT 'your columns for the header split by the separator' AS Col_Value
FROM Dual
UNION ALL
SELECT col1||',' ||col2||','|| col3||','|| col4||','|| col5||','|| col6 as Col_Value
FROM (SELECT col1,col2,col3,col4,col5,col6 from yourtable)));
o_Clobdata := l_Clob;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
/
You can adapt that procedure the way you want. I use header, so that is the reason for the first select. In my example, the separator was , , but you can use another one if you like, or even use a parameter for it instead.
2.Create an Application Process in Oracle Apex
In Oracle Apex, click on the Shared Components --> Application Process and then click on the Create button. Then follow these steps:
Then press next and put the following code
DECLARE
L_BLOB BLOB;
L_CLOB CLOB;
L_DEST_OFFSET INTEGER := 1;
L_SRC_OFFSET INTEGER := 1;
L_LANG_CONTEXT INTEGER := DBMS_LOB.DEFAULT_LANG_CTX;
L_WARNING INTEGER;
L_LENGTH INTEGER;
BEGIN
-- create new temporary BLOB
DBMS_LOB.CREATETEMPORARY(L_BLOB, FALSE);
--get CLOB
tab_to_csv( L_CLOB);
-- tranform the input CLOB into a BLOB of the desired charset
DBMS_LOB.CONVERTTOBLOB( DEST_LOB => L_BLOB,
SRC_CLOB => L_CLOB,
AMOUNT => DBMS_LOB.LOBMAXSIZE,
DEST_OFFSET => L_DEST_OFFSET,
SRC_OFFSET => L_SRC_OFFSET,
BLOB_CSID => NLS_CHARSET_ID('WE8MSWIN1252'),
LANG_CONTEXT => L_LANG_CONTEXT,
WARNING => L_WARNING
);
-- determine length for header
L_LENGTH := DBMS_LOB.GETLENGTH(L_BLOB);
-- first clear the header
HTP.FLUSH;
HTP.INIT;
-- create response header
OWA_UTIL.MIME_HEADER( 'text/csv', FALSE, 'AL32UTF8');
HTP.P('Content-length: ' || L_LENGTH);
HTP.P('Content-Disposition: attachment; filename="yourfile.csv"');
HTP.P('Set-Cookie: fileDownload=true; path=/');
OWA_UTIL.HTTP_HEADER_CLOSE;
-- download the BLOB
WPG_DOCLOAD.DOWNLOAD_FILE( L_BLOB );
-- stop APEX
-- APEX_APPLICATION.STOP_APEX_ENGINE;
EXCEPTION
WHEN OTHERS THEN
DBMS_LOB.FREETEMPORARY(L_BLOB);
RAISE;
END;
After that click on the Next button and on the next screen click on the Create button to finish the wizard. Your application process has been created.
3.Create a Button on a Page in Oracle Apex
Now open a page in Page designer in Oracle Apex in which you want to add a button to download the CSV file.
Then do the right-click on the Region and click on the option Create Button.
Set the Action to Redirect to URL.
Paste the following URL in the URL target.
f?p=&APP_ID.:0:&SESSION.:APPLICATION_PROCESS=download_emp_csv:NO
Notice that we are calling the application process download_emp_csv, we just created in the second step.
Now save the changes and run the page. On click of the button, the CSV file will be download.
I want to send newly inserted entries of audit table through EMAIL along with newly inserted data in CSV format. I am using after insert trigger to keep track of inserted rows, but my code is unable to send email. It's throwing error ORA-29279 : SMTP permanent error : 550 RBL:
http://www.barracudanetwork.com/reputation/>pr=1&ip=xxx.xxx.xxx.xx
I am using my organization's email server. Below is my Code:
CREATE OR REPLACE TRIGGER emp_after_insert
AFTER INSERT
ON scott.emp
FOR EACH ROW
enable
DECLARE
v_username varchar2(10);
v_no number(4);
v_name varchar2(10);
DIR_NAME VARCHAR2(10):= 'UTL_FILE';
FILE_NAME VARCHAR2(10):= 'EINFO.csv';
F1 UTL_FILE.FILE_TYPE;
PRESENT BOOLEAN;
FLENGTH NUMBER;
BSIZE PLS_INTEGER;
CNTR NUMBER:=0;
BEGIN
insert into scott.emp_audit(id,name,insertion_date) values(:new.empno,:new.ename,sysdate);
dbms_output.put_line('inserted');
UTL_FILE.FGETATTR(LOCATION=>DIR_NAME,FILENAME=>FILE_NAME,
FEXISTS=>PRESENT,FILE_LENGTH=>FLENGTH,
BLOCK_SIZE=>BSIZE);
IF PRESENT THEN
F1:=UTL_FILE.FOPEN(DIR_NAME,FILE_NAME,'a'); ------APPEND MODE-
UTL_FILE.PUT_LINE(F1,RPAD('',LENGTH(CURRENT_TIMESTAMP),''));
ELSE
F1:=UTL_FILE.FOPEN(DIR_NAME,FILE_NAME,'W'); -----WRITE MODE-----
END IF;
UTL_FILE.PUT_LINE(F1,RPAD(:new.empno,10,' ')||RPAD(:new.ename,10,' '));
DBMS_OUTPUT.PUT_LINE('FILE CREATED ...'||FILE_NAME||'...AS ON ...'||CURRENT_TIMESTAMP);
UTL_FILE.FCLOSE(F1);
UTL_MAIL.send_attach_varchar2 (
sender => 'abc#gmail.com',
recipients => 'xyz123#gmail.com',
subject => 'UTL_MAIL Test',
message => 'If you get this message it worked!',
attachment => 'The is the contents of the attachment.',
att_filename => 'C:\export\EINFO.csv'
);
end;
I also execute ALTER SYSTEM SET smtp_out_server='mail.abc.com'
Also executed the scripts of utlmail and prvtmail. Please Help!
How can I send an email based on what is entered into a apex Text field. eg If I enter me#test.com in :p6_supervisor, I would like the email to be sent to that person.
At present I have a preset UTL_MAIL.send which is working.
begin
UTL_MAIL.send(sender => 'test#test.com',
recipients => 'test1#test.com',
subject => 'Test,
message => 'Please Note this is a test' );
end;
But of course its for another purpose, which is sending email to one recipient from a trigger.
Below is the Cursor example
create or replace function "email"
( name_in IN varchar2 )
RETURN number
IS
supervisoremail varchar2(30);
CURSOR c1
IS
select
supervisoremail
from
EMPLOYEE,supervisors
where TO_DATE(contract_start_period,'DD-MM-YYYY') < TO_DATE (SYSDATE,'DD-MM-YYYY') - 275
and (supervisors.supervisorname = employee.supervisorname1
or supervisors.supervisorname = employee.supervisorname2)
and employee_name ='test'
;
BEGIN
OPEN c1;
FETCH c1 INTO supervisoremail;
CLOSE c1;
RETURN supervisoremail;
END;
It'll work, no problem. A simple way is to
create a text item (P6_SUPERVISOR)
create a button which submits the page; you need it so that P6_SUPERVISOR's value is set into the session state
create a process which calls UTL_MAIL; P6_SUPERVISOR item's contents is used as a source for the RECIPIENTS parameter. For example:
UTL_MAIL.send (sender => 'test#test.com',
recipients => :P6_SUPERVISOR,
subject => 'Test message - subject',
MESSAGE => 'Test message - message body');
[EDIT: how to send mails in a loop?]
I'm not sure what you meant by creating a function; it returns just one e-mail address that, probably, belongs to name passed through the NAME_IN parameter (but you used the 'test' name in cursor query).
However, you specified that the function returns a NUMBER, while variable you're selecting the result into is a VARCHAR2. So, which one of these is true?
If you insist on a function, don't enclose its name into double quotes as you'll always have to reference it that way (double quotes, correct upper/lower/mixed case).
Furthermore, what is contract_start_period column's datatype? If it is DATE, don't TO_DATE it. The same goes for SYSDATE - it is a function that returns DATE datatype anyway, so it is wrong to convert it to date once again.
Here's an example which uses a cursor FOR loop (as it is easier to maintain) and sends mail to all supervisoremail addresses returned by that SELECT statement.
begin
for cur_r in (select supervisoremail
from employee e join supervisors s on s.supervisorname = e.supervisorname1
or s.supervisorname = e.supervisorname2
where contract_start_period < sysdate - 275
and e.employee_name = 'test'
)
loop
utl_mail.send(sender => 'test#test.com',
recipients => cur_r.supervisoremail,
subject => 'Test message - subject',
message => 'Test message - message body');
end loop;
end;
I created a Concurrent Program that creates an Excel File from a long, parametrized query using PL/SQL.
Once the Program successfully completes, the file is placed in the remote server's directory and is usually around 4 MB in Size.
I'm thinking of an approach to notify the requestor and enable him/her to save the file to their local directory.
However, I cannot use UTL_MAIL to attach and send the file via email due to the 32 Kilobyte Limitation. (Does UTL_MAIL have an attachment limit of 32k).
In the same post, Tom Kyte preferred approach would be to:
store the attachment to the database.
email a very small email with a link. the link points to my database - using a URL.
With that, i was thinking taking the same approach and use the block below to notify the requestor and enable him/her to download the said Excel file:
declare
l_url_link varchar2(100); -- how can i get the URL of the File?
BEGIN
UTL_MAIL.SEND(sender => 'xxx#oracle.com'
, recipients => 'Migs.Isip.23#Gmail.com'
, subject => 'Testmail'
, message => 'Your File is Ready to be downloaded, click the link here: '||l_url_link);
END;
My Questions would be:
How can i generate the "URL" of the Remote file using PL/SQL?
Do the users need to be granted access to the remote server to download the file?
Thank you!
Oracle Database Version:
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
PL/SQL Release 11.2.0.4.0 - Production
"CORE 11.2.0.4.0 Production"
TNS for Solaris: Version 11.2.0.4.0 - Production
NLSRTL Version 11.2.0.4.0 - Production
Here is a pl/sql function I wrote to retrieve the URL of either the concurrent log file or output file. If you write your Excel file to the concurrent output, this should work fine. Let me know how you get on. I have not checked to see if this will give the correct mime-type or extension - not sure how EBS handles this but the function itself will definitely compile as is for 12.1.3.
Spec
FUNCTION get_concurrent_url (p_file_type IN VARCHAR2
,p_request_id IN NUMBER
,p_expiry IN NUMBER)
RETURN VARCHAR2;
Body
/* Get a URL to view the log/output
File Type is LOG or OUT
Request ID is the concurrent request ID
Expiry is in minutes */
FUNCTION get_concurrent_url (p_file_type IN VARCHAR2
,p_request_id IN NUMBER
,p_expiry IN NUMBER)
RETURN VARCHAR2
IS
CURSOR c_gwyuid
IS
SELECT profile_option_value
FROM fnd_profile_options FPO
,fnd_profile_option_values FPOV
WHERE FPO.profile_option_name = 'GWYUID'
AND FPO.application_id = FPOV.application_id
AND FPO.profile_option_id = FPOV.profile_option_id;
CURSOR c_two_task
IS
SELECT profile_option_value
FROM fnd_profile_options FPO
,fnd_profile_option_values FPOV
WHERE FPO.profile_option_name = 'TWO_TASK'
AND FPO.application_id = FPOV.application_id
AND FPO.profile_option_id = FPOV.profile_option_id;
l_request_id NUMBER;
l_file_type VARCHAR2 (3 BYTE);
l_expiry NUMBER;
l_two_task VARCHAR2 (100 BYTE);
l_gwyuid VARCHAR2 (100 BYTE);
l_url VARCHAR2 (1024 BYTE);
BEGIN
l_request_id := p_request_id;
l_file_type := p_file_type;
l_expiry := p_expiry;
FOR i IN c_gwyuid LOOP
l_gwyuid := i.profile_option_value;
END LOOP;
FOR i IN c_two_task LOOP
l_two_task := i.profile_option_value;
END LOOP;
IF l_file_type = 'LOG' THEN
l_url := fnd_webfile.get_url
(file_type => fnd_webfile.request_log
,id => l_request_id
,gwyuid => l_gwyuid
,two_task => l_two_task
,expire_time => l_expiry);
ELSE
l_url := fnd_webfile.get_url
(file_type => fnd_webfile.request_out
,id => l_request_id
,gwyuid => l_gwyuid
,two_task => l_two_task
,expire_time => l_expiry);
END IF;
RETURN l_url;
END get_concurrent_url;
I was able to find a solution for this using a (slightly different) method using the FND_GFM File Uploader Package in Oracle EBS.
FND_GFM is a package usually used in Oracle EBS when uploading files from the front-end application pages.
First, generate the Excel file (xlsx) using the code from the previous post: Create an Excel File (.xlsx) using PL/SQL,
Then the file is inserted into FND_LOBS and removed the from the OS (for good housekeeping), and finally sent as an email using UTL_FILE:
procedure generate_and_send_excel
is
l_content varchar2(250);
l_file_url varchar2(4000);
l_directory varchar2(250);
l_filename varchar2(250);
l_message clob;
l_instance varchar2(100);
l_ebs_url varchar2(100);
begin
/* your excel generation code here */
l_content := 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
l_directory := 'EXT_TAB_DATA';
l_filename := 'report.xlsx';
select instance_name
into l_instance
from v$instance;
select home_url
into l_ebs_url
from icx_parameters;
IMPORT_TO_LOB (p_file_name => l_filename -- this is the actual filename of the saved OS File
, p_directory => l_directory -- should be a defined directory in the Database
, p_content_type => l_content -- standard for Excel Files
, p_program_name => 'your prog here'
, p_program_tag => 'your prog here'
, p_file_url => l_file_url); -- this will be the generated URL of your File
utl_file.fremove(l_directory, l_filename);
l_message := l_message||'<h2 style="color: #5e9ca0;">'||l_title||'</h2>';
l_message := l_message||'<h3 style="color: #2e6c80;">Report is Ready for Download: '||l_filename||'</h3>';
l_message := l_message||'<p>File was generated on '|| sysdate ||' from '||l_instance||'</p>';
l_message := l_message||'<strong>Regards,</strong><br/><strong>Sample Team</strong>';
l_message := l_message||'<br/>Sample#sample.com';
UTL_MAIL.SEND(sender => 'SAMPLE#SAMPLE.com'
, recipients => 'Migs.Isip.23#gmail.com'
, subject => 'Hello message'
, message => l_message
, mime_type => 'text/html; charset=us-ascii');
end generate_and_send_excel;
Procedure below to insert into FND_LOBS (there's no available seeded API):
Procedure IMPORT_TO_LOB (p_file_name IN FND_LOBS.FILE_NAME%TYPE
, p_directory IN dba_directories.directory_name%type
, p_content_type IN FND_LOBS.file_content_type%type
, p_program_name IN FND_LOBS.program_name%type
, p_program_tag IN FND_LOBS.program_tag%type
, p_language IN FND_LOBS.language%type default 'US'
, p_file_format IN FND_LOBS.file_format%type default 'binary'
, p_file_url OUT varchar2)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
lBlob BLOB;
lFile BFILE := BFILENAME(p_directory, p_file_name);
L_ORA_CHARSET VARCHAR2(100);
P_COUNT NUMBER;
BEGIN
SELECT value
into l_ora_charset
FROM nls_database_parameters
where parameter = 'NLS_CHARACTERSET';
insert into FND_LOBS
(
file_id
, file_name
, file_content_type
, file_data
, upload_date
, expiration_date
, program_name
, program_tag
, LANGUAGE
, oracle_charset
, file_format
)
values
(
fnd_lobs_s.NEXTVAL -- FILE_ID
, p_file_name -- FILE_NAME
, p_content_type -- FILE_CONTENT_TYPE
, EMPTY_BLOB() -- FILE_DATA
, sysdate -- UPLOAD_DATE
, NULL -- EXPIRATION_DATE
, p_program_name -- PROGRAM_NAME
, p_program_tag -- PROGRAM_TAG
, p_language -- LANGUAGE
, l_ora_charset -- ORACLE_CHARSET
, p_file_format -- FILE_FORMAT
)
RETURNING file_data INTO lBlob;
DBMS_LOB.OPEN(lFile, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(lBlob, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(DEST_LOB => lBlob,
SRC_LOB => lFile,
AMOUNT => DBMS_LOB.GETLENGTH(lFile));
DBMS_LOB.CLOSE(lFile);
DBMS_LOB.CLOSE(lBlob);
commit;
p_file_url := fnd_gfm.construct_download_url (fnd_web_config.gfm_agent, fnd_lobs_s.currval);
END IMPORT_TO_LOB;
Note that this is an AUTONOMOUS_TRANSACTION so it needs to be committed before returning to the calling package/block.
Hope that Helps!