Oracle Apex save PDF into Database with ORDS? - pdf

We have a function that turns sql query into xml and in shared components we have a xsl-fo stylesheet. With both combined we want to generate PDF's and save them into the Database.
Now we tried to use the Print API's (APEX_UTIL.GET_PRINT_DOCUMENT) but the PDF's are damaged and return a 401 Error (Unathorized).
We are using Apex 19.1 and as print option ORDS.Ive read somewhere that you cant use those tho because with ORDS the PDF is only returned to the client.
Since im pretty new to this whole PDF printing subject:
Is there a way to still save the PDF into the Database? Or is this a feature that only comes with the paid solutions? Are there any necessary settings we have to configure with REST?

You can save the PDF if you can convert it into BLOB, but APEX by itself cannot give you that functionality.
You are right about this "Ive read somewhere that you cant use those tho because with ORDS the PDF is only returned to the client."
For example I created a report in Jasper Server Community and save it into the database using PLSQL
declare
l_url VARCHAR2(3000) := '//apexdevtesting.com:8081/jasperserver/rest_v2/reports/reports/Blank_Letter.pdf?STATUS=';
l_request SYS.utl_http.req;
l_response SYS.utl_http.resp;
l_download RAW(32767);
l_file BLOB;
l_mimetype VARCHAR2(50) := 'application/pdf';
l_charset VARCHAR2(50) := 'UTF8';
l_filename varchar2(50) := 'archivo_'||ARCHIVOS_CARGADOS_SEQ.nextval||'.pdf';
begin
l_request := SYS.utl_http.begin_request(l_url ||'ASSIGNED&'||'j_username=jasperadmin'||'&'||'j_password=jasperadmin' );
SYS.utl_http.set_header(l_request, 'User-Agent', 'Mozilla/5.0 (Windows NT x.y; Win64; x64; rv:10.0) Gecko/20100101 Firefox/10.0');
l_response := SYS.utl_http.get_response(l_request);
dbms_lob.createtemporary(l_file, TRUE, dbms_lob.SESSION);
LOOP
BEGIN
SYS.utl_http.read_raw(l_response, l_download);
dbms_lob.writeappend(l_file, utl_raw.LENGTH(l_download), l_download);
INSERT INTO j_test2
VALUES (SYS_GUID(),
SYSTIMESTAMP,
l_file,
l_mimetype,
l_filename);
EXCEPTION WHEN SYS.utl_http.end_of_body THEN
EXIT;
END;
END LOOP;
SYS.utl_http.end_response(l_response);
commit;
end;

Related

Inno Setup 6 cannot use DLL function with string parameters, while it works in Inno Setup 5

We are currently using Inno Setup version 5.5.3 to build an installer. I am planning to upgrade this version to 6.1.2.
We use Inno Setup for installation of our product. We ship a license code along with installer, and user enters the license in a field during installation.
This license is validated using a custom DLL and a non negative result is returned by DLL for valid license.
This process works fine in 5.5.3 and 5.6.1, but fails in version 6 (tested with 6.0.5 and 6.1.2).
Unfortunately there are no logs generated that point the exact issue.
This custom DLL is 32-bit and is built using C++ over 10 years ago. There are no plans to redo this part. Is there any way to use the same DLL and fix the problem? Thank you.
[Files]
Source: mydll.dll; Flags: dontcopy
// This function calls the external mydll which parses licenseCode and
// returns an integer
function getFlags( secret, licenseCode : String) : Integer;
external 'getFlags#files:mydll.dll cdecl';
function checkLicense(license : String) : Boolean;
var
secret : String;
begin
Result := False; // default is Incorrect license
license := Trim(license);
secret := <secret>
// This line calls the above getFlags function and expects to get an integer
license_flags := getFlags( secret, license);
if license_flags = -1 then begin
if not suppressMsgBox then begin
MsgBoxLog( ‘Incorrect License’)
end;
Exit;
end;
Result := True;
end;
// This is the c++ function
PS_EXPORT(int) getFlags( const char * secret, const char * license ) {
// This function is returning -1 for Inno Setup 6
// but works fine for Inno Setup 5
if (strlen(license) == 0)
return -1;
...
}
This is not a 5.x vs 6.x issue, nor a bitness issue. This is an Ansi vs. Unicode issue.
In 5.x, there were two versions of Inno Setup, Ansi version and Unicode version. You were probably using the Ansi version and your code is designed for it. In 6.x, there's only the Unicode version. Your code does not work in the Unicode version. By upgrading to 6.x, you inadvertently also upgraded from Ansi to Unicode.
A quick and dirty solution is to change the declaration of the getFlags function to correctly declare the parameters to be Ansi strings (AnsiString type):
function getFlags( secret, licenseCode : AnsiString) : Integer;
external 'getFlags#files:mydll.dll cdecl';
The correct solution would be to reimplement your DLL to use Unicode strings (wchar_t pointers):
PS_EXPORT(int) getFlags( const wchar_t * secret, const wchar_t * license )
This is a similar question: Inno Setup Calling DLL with string as parameter.
See also Upgrading from Ansi to Unicode version of Inno Setup (any disadvantages).

Oracle apex download blob file from collection

I'm trying to download file from collection.
I looked through many forum, including here: APEX: Download BLOB from temporary table
But it is not working, I can't figure out why. no error is raised.
My PL/SQL code when pressing the button is:
DECLARE
v_mime VARCHAR2(255);
v_length NUMBER;
v_file_name VARCHAR2(255);
Lob_loc BLOB;
BEGIN
SELECT c001, c004, blob001
INTO v_file_name, v_mime, Lob_loc
FROM apex_collections
WHERE collection_name = 'COLLECTION_DOCS' AND seq_id = :P113_SEQ;
select sys.dbms_lob.getlength(Lob_loc) into v_length from dual;
/* This raise shows me the correct values taken form the collection table
raise_application_error (-20001,'
name:' || v_file_name || '
mime: ' || v_mime || '
length: ' || v_length || '
');
*/
sys.htp.init;
sys.owa_util.mime_header(v_mime, FALSE);
sys.htp.p('Content-length: ' || v_length);
sys.htp.p('Content-Disposition: attachment; filename="' || v_file_name || '"' );
sys.owa_util.http_header_close;
sys.wpg_docload.download_file( Lob_loc );
apex_application.stop_apex_engine;
END;
Note that the values inside the commented section (when enabled) are showing the correct file name, mimetype and size when I press the button. That means that the the retrieval from the collection is working OK.
Thanks for your help.
The code you have posted is for a new http request to the server. If you put that code in a dynamic action, it will be executed in the current database session/rendered apex page and nothing happens.That is expected behaviour.
One way to solve this is is to create a separate page for download with page item PNN_SEQ. Put the code above in a before header page process on this new page. That original stack overflow post you are referring to mentions this as well: This PL/SQL block is called 'on load before header'
On your download button in the original page, put a redirect to url and pass the value for P113_SEQ. This will fire off a new request to the server of mimetype you need.
You can also do it in an application process as described here: https://joelkallman.blogspot.com/2014/03/yet-another-post-how-to-link-to.html

Is there a FILE_READ equivalent in oracle?

I was trying to load data in to db on application startup.
With H2 I was using the below query and it worked perfectly. With Oracle it doesn't work. Can someone point me the right direction? I went through oracle documentation but didn't manage to find an equivalent.
INSERT INTO TEMPLATES(ID,NAME,BODY) VALUES('2b04469f31c445ca82c354322845b52b', 'Records', FILE_READ('/opt/bin/Records.txt'));
Oracle SQL has no equivalent of a file_read() function. However, it is possible to write your own.
However, before we start you need to known that Oracle is much more locked down when it comes to database interoperability with the OS. Many things are not enabled by default, and consequently you may require assistance from a friendly DBA to get this working.
For instance, we cannot use OS filepaths directly (at least in more recent versions) so we need to create a DIRECTORY object. Normally the privilege to do this is restricted to DBAs.
create directory opt_bin as '/opt/bin';
grant read on directory opt_bin to <<your_user>>;
Note that /opt/bin must be a directory to which the database has access. On a *nix environment that means the oracle OS user has at least read on the directory.
With that infrastructure in place we can create a function which loads an OS file into a blob. It uses the directory and file name to instantiate a BFILE then applies DBMS_LOB capability to load that BFILE into a BLOB.
create or replace file_to_blob
(p_dir in varchar2, p_file in varchar2)
return blob
is
bf bfile;
tmp_blob blob := empty_blob();
l_dest_offset pls_integer := 1;
l_src_offset pls_integer := 1;
begin
bf := bfilename(p_dir, b_file);
dbms_lob.createtemporary(tmp_blob, true);
dbms_open(bf, dbms_lob.file_readonly);
dbms_lob.loadblobfromfile(tmp_blob, bf, dbms_lob.lobmaxsize, l_dest_offset, l_src_offset);
dbms_lob.close(bf);
return tmp_blob;
end;
/
You can use this function in your insert statement like this:
INSERT INTO TEMPLATES(ID,NAME,BODY)
VALUES
('2b04469f31c445ca82c354322845b52b', 'Records', file_to_blob('opt_bin', 'Records.txt'));

Inno Setup Create individual shortcuts on all desktops of all users

I'm creating a shortcut on users Desktop with Inno Setup:
Name: "{commondesktop}\Setup"; Filename: "{app}\Setup.exe"; WorkingDir: "{pf}\Program"; IconFilename: "{app}\Setup.ico"
But users, with no admin rights, cannot delete this Shortcut, how to grant permissions to regular users, to delete this icon? Icon should be created on every user's desktop, but user should have permission to delete it.
The {commondesktop} shortcut is shared on a common desktop. So there's only one copy of the shortcut.
If you allow the users to delete, when one user deletes the icon, it's deleted for every other user. That's why regular users are not allowed to modify/delete shared shortcuts.
While you can grant a delete permission to all users to that shortcut, this is not what you should do.
If each machine is used by one user only, install the icon to userdesktop, not commondestop. But that works only if that user (not Administrator) actually run the installer. For a general discussion about this problem, see Installing application for currently logged in user from Inno Setup installer running as Administrator.
There's no easy way to install the icons to all desktops. You have to use Pascal Scripting and iterate all profiles.
Easy way is to iterate subfolders of C:\Users, creating a shortcut in Desktop subfolder of each user's subfolder:
procedure CurStepChanged(CurStep: TSetupStep);
var
UsersPath: string;
CommonDesktopShortPath: string;
DesktopPath: string;
ShortcutPath: string;
FindRec: TFindRec;
ShortcutsCount: Integer;
begin
{ Once the files are installed }
if CurStep = ssPostInstall then
begin
Log('Creating shortcuts');
{ Assuming the common users root (typically C:\Users) is two level up }
{ from the current user desktop folder }
UsersPath :=
AddBackslash(ExtractFilePath(RemoveBackslash(ExtractFilePath(
RemoveBackslash(ExpandConstant('{userdesktop}'))))));
Log(Format('Users root [%s]', [UsersPath]));
CommonDesktopShortPath := GetShortName(ExpandConstant('{commondesktop}'));
Log(Format('Common desktop [%s]', [CommonDesktopShortPath]));
ShortcutsCount := 0;
{ Iterate all users }
if FindFirst(UsersPath + '*', FindRec) then
begin
try
repeat
{ Just directories, not interested in files }
if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY <> 0 then
begin
{ Check if there is a Desktop subfolder }
DesktopPath := UsersPath + FindRec.Name + '\Desktop';
if DirExists(DesktopPath) then
begin
if CompareText(
CommonDesktopShortPath, GetShortName(DesktopPath)) = 0 then
begin
Log(Format('Skipping common desktop [%s]', [DesktopPath]));
end
else
begin
ShortcutPath := DesktopPath + '\My Program.lnk';
Log(Format(
'Found desktop folder for user [%s], creating shortcut [%s]', [
FindRec.Name, ShortcutPath]));
try
ShortcutPath := CreateShellLink(
ShortcutPath, 'My Program', ExpandConstant('{app}\MyProg.exe'), '',
ExpandConstant('{app}'), '', 0, SW_SHOWNORMAL);
Log(Format('Shortcut [%s] created', [ShortcutPath]));
Inc(ShortcutsCount);
except
Log(Format('Failed to create shortcut: %s', [GetExceptionMessage]));
end;
end;
end;
end;
until not FindNext(FindRec);
finally
FindClose(FindRec);
end;
Log(Format('%d shortcuts created', [ShortcutsCount]));
end
else
begin
Log(Format('Error listing [%s]', [UsersPath]));
end;
end;
end;
The code will work only, if the desktops are local and in common locations.
If you need a more robust solution, you can iterate profiles listed in
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList
Or use a WMI query like:
SELECT * FROM Win32_UserAccount WHERE localAccount = true and disabled = false
See Query list of Windows accounts in Inno Setup.

DWScript Write/Read a simple text file

I would like to write/read a simple text file using dwscript.
My code is here below... but I am non able to get it run, please someone might help...:
(I am using the Simple.exe in the Demos folder of DWS installation)
// uses Classes;
{$INCLUDE_ONCE 'c:/.../System.Classes.pas'}
var
s: TFileStream;
o: string; // out
i: integer;
f: word; // flag
f := fmOpenReadWrite;
if not FileExists('C:\Temp\Junkfile.txt') then
f := f or fmCreate;
s := TFileStream.Create('C:\Temp\Junkfile.txt', f);
try
s.Position := s.Size; // will be 0 if file created, end of text if not
for i := 1 to 10 do begin
o := Format('This is test line %d'#13#10, [i]);
s.Write(o[1], Length(o) * SizeOf(Char));
end;
finally
s.Free;
end;
By default the script engine keeps everything sand-boxed and nothing that gives access outside the sandbox is exposed. So if you want to give access to arbitrary files to script you need to expose functions & classes to achieve it (through TdwsUnit f.i.).
Also it won't compile the Delphi classes unit, DWScript is not meant to be an alternative to the Delphi compiler, but to offer scripting support, ie. allow end users to run code in a way over which you have full control over what they can do, and that can't crash or corrupt the host application (that last point being the key differentiation with the other notable Pascal scripting engines).
You can use dwsFileFunctions to get basic file I/O support, in which case an equivalent to the file creation portion of your code would be something like
var f := FileCreate('C:\Temp\Junkfile.txt');
for var i := 1 to 10 do
FileWrite(f, Format('This is test line %d'#13#10, [i]));
FileClose(f);