FCKeditor integration
The basic integration of FCKeditor is shown in this
OTN Thread
This example will only show, how to integrate into ApEx the FileBrowser and ImageUploader functionality of FCKeditor.
The API to this plugins is shown in detail in the
FCKeditor WiKi .
Here some general considerations:
To save and browse uploaded images FCKeditor assumes to have access to the filesystem of http server. As ApEx is the database driven application, i'll use instead the filesystem within the database, which is served by Oracle XML DB. On this way the images ( or other static files ) can be easily accessed by means of database and the backup of these files should not be the concern. It means, however, http access to these files will be provided through XML DB http listener, which is not always enabled.
In case of ApEx delivered with the Oracle XE, it is however default configuration, so i'll describe the steps required to get it working with XE edition
and at the end of article add some ideas, how can this setup be adjusted for regular ApEx ( i.e. with standalone mod_plsql).
The FCKeditor package itself should be copied per webdav/ftp into XMLDB filesystem as well. Before doing this , some files should be edited however, so archive should be extracted locally. Original lines are commented following by changed lines.
./FCKeditor/fckconfig.js
FCKConfig.LinkBrowser = true ;
//FCKConfig.LinkBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?Connector=connectors/' +
// _FileBrowserLanguage + '/connector.' + _FileBrowserExtension ;
FCKConfig.LinkBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?Connector=/apex/connector';
FCKConfig.LinkBrowserWindowWidth = FCKConfig.ScreenWidth * 0.7 ; // 70%
FCKConfig.LinkBrowserWindowHeight = FCKConfig.ScreenHeight * 0.7 ; // 70%
FCKConfig.ImageBrowser = true ;
//FCKConfig.ImageBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?Type=Image&Connector=connectors/' +
// _FileBrowserLanguage + '/connector.' + _FileBrowserExtension ;
FCKConfig.ImageBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?p_Type=Image&Connector=/apex/connector';
FCKConfig.ImageBrowserWindowWidth = FCKConfig.ScreenWidth * 0.7 ; // 70% ;
FCKConfig.ImageBrowserWindowHeight = FCKConfig.ScreenHeight * 0.7 ; // 70% ;
FCKConfig.FlashBrowser = true ;
//FCKConfig.FlashBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?Type=Flash&Connector=connectors/' +
// _FileBrowserLanguage + '/connector.' + _FileBrowserExtension ;
FCKConfig.FlashBrowserURL = FCKConfig.BasePath + 'filemanager/browser/default/browser.html?p_Type=Flash&Connector=/apex/connector';
FCKConfig.FlashBrowserWindowWidth = FCKConfig.ScreenWidth * 0.7 ; //70% ;
FCKConfig.FlashBrowserWindowHeight = FCKConfig.ScreenHeight * 0.7 ; //70% ;
./FCKeditor/editor/filemanager/browser/default/frmupload.html - here should be changed javascript ...
function SetCurrentFolder( resourceType, folderPath )
{
var sUrl = oConnector.ConnectorUrl + 'Command=FileUpload' ;
sUrl += '&Type=' + resourceType ;
sUrl += '&CurrentFolder=' + escape( folderPath ) ;
document.getElementById('frmUpload').action = sUrl ;
// Following 3 lines are new
document.getElementById('Command').value = 'FileUpload' ;
document.getElementById('p_Type').value = 'resourceType' ;
document.getElementById('CurrentFolder').value = escape( folderPath ) ;
}
./FCKeditor/editor/filemanager/browser/default/frmupload.html - ... and html
<td width="100%"><input id="NewFile" name="NewFile" style="WIDTH: 100%" type="file"></td>
<td nowrap="nowrap"> <input id="btnUpload" type="submit" value="Upload"></td>
<!-- Following three lines are new -->
<td><input id="Command" name="Command" type="hidden" value=""></td>
<td><input id="p_Type" name="p_Type" type="hidden" value=""></td>
<td><input id="CurrentFolder" name="CurrentFolder" type="hidden" value=""></td>
./FCKeditor/editor/filemanager/browser/default/browser.html
oConnector.SendCommand = function( command, params, callBackFunction )
{
var sUrl = this.ConnectorUrl + 'Command=' + command ;
// The following line was original, but, unforutnately, "Type" can't be a parameter name, while a reserved word
//sUrl += '&Type=' + this.ResourceType ;
// The following line replaces the original
sUrl += '&p_Type=' + this.ResourceType ;
sUrl += '&CurrentFolder=' + escape( this.CurrentFolder ) ;
if ( params ) sUrl += '&' + params ;
var oXML = new FCKXml() ;
if ( callBackFunction )
oXML.LoadUrl( sUrl, callBackFunction ) ; // Asynchronous load.
else
return oXML.LoadUrl( sUrl ) ;
}
After these changes the whole FCKeditor folder should be copied to your Oracle XE (as mentioned before, preferrably over webdav ),
for example into
http://localhost:8080/i/FCKeditor
Finally, the
connector itself - a pl sql stored procedure
CREATE OR REPLACE PROCEDURE Connector
(
Command VARCHAR2 DEFAULT NULL,
p_Type VARCHAR2 DEFAULT 'File',
Currentfolder VARCHAR2 DEFAULT NULL,
Serverpath VARCHAR2 DEFAULT '/public',
Newfoldername VARCHAR2 DEFAULT NULL,
Newfile VARCHAR2 DEFAULT NULL
) IS
l_Currentfolder VARCHAR2(1000) := Serverpath || Currentfolder;
l_Response_Xml Xmltype;
l_Child_Xml Xmltype;
l_Result BOOLEAN;
l_Blob_Content BLOB;
l_File_Name VARCHAR2(100);
l_Dot PLS_INTEGER;
l_Basename VARCHAR2(100);
l_Extension VARCHAR2(100);
l_Counter PLS_INTEGER := 1;
l_Message VARCHAR2(200);
BEGIN
IF Command != 'FileUpload' THEN
SELECT Xmlelement("Connector",
Xmlattributes(Command AS "command",
'File' AS "resourceType"))
INTO l_Response_Xml
FROM Dual;
SELECT Xmlelement("CurrentFolder",
Xmlattributes(Currentfolder AS "path",
l_Currentfolder AS "url"))
INTO l_Child_Xml
FROM Dual;
SELECT Appendchildxml(l_Response_Xml, 'Connector', l_Child_Xml)
INTO l_Response_Xml
FROM Dual;
IF Command = 'GetFolders' THEN
SELECT Xmlelement("Folders",
Xmlagg(Xmlelement("Folder",
Xmlattributes(Path(1) AS "name"))))
INTO l_Child_Xml
FROM Resource_View
WHERE Under_Path(Res, 1, l_Currentfolder, 1) = 1
AND Existsnode(Res, '/Resource[@Container="true"]') = 1;
ELSIF Command = 'GetFoldersAndFiles' THEN
SELECT Xmlelement("Folders",
Xmlagg(Xmlelement("Folder",
Xmlattributes(Path(1) AS "name"))))
INTO l_Child_Xml
FROM Resource_View
WHERE Under_Path(Res, 1, l_Currentfolder, 1) = 1
AND Existsnode(Res, '/Resource[@Container="true"]') = 1;
SELECT Appendchildxml(l_Response_Xml, 'Connector', l_Child_Xml)
INTO l_Response_Xml
FROM Dual;
-- The size calculation for XML DB Resources can be looked up in this thread on OTN
-- http://forums.oracle.com/forums/thread.jspa?threadID=417316&tstart=0
SELECT Xmlelement("Files",
Xmlagg(Xmlelement("File",
Xmlattributes(File_Name AS "name",
File_Size AS "size"))))
INTO l_Child_Xml
FROM (SELECT Path(1) File_Name,
round(Dbms_Lob.Getlength(extractValue(r.res,'/Resource/XMLLob')) / 1024 ) File_Size
FROM Resource_View r
WHERE Under_Path(Res, 1, l_Currentfolder, 1) = 1
AND Existsnode(Res, '/Resource[@Container="false"]') = 1);
ELSIF Command = 'CreateFolder' THEN
IF Dbms_Xdb.Existsresource(l_Currentfolder || Newfoldername) THEN
l_Message := '101';
ELSE
l_Result := Dbms_Xdb.Createfolder(l_Currentfolder || Newfoldername);
l_Message := '0';
END IF;
SELECT Xmlelement("Error", Xmlattributes(l_Message AS "number"))
INTO l_Child_Xml
FROM Dual;
END IF;
SELECT Appendchildxml(l_Response_Xml, 'Connector', l_Child_Xml)
INTO l_Response_Xml
FROM Dual;
SELECT Xmlroot(l_Response_Xml, Version '1.0', Standalone Yes)
INTO l_Response_Xml
FROM Dual;
Owa_Util.Mime_Header('text/xml', TRUE, 'ISO-8859-1');
Htp.p(l_Response_Xml.Getstringval());
ELSIF Command = 'FileUpload' THEN
SELECT Filename, Blob_Content
INTO l_File_Name, l_Blob_Content
FROM Htmldb_Application_Files
WHERE NAME = Newfile;
IF Dbms_Xdb.Existsresource(l_Currentfolder || l_File_Name) THEN
l_Dot := Instr(l_File_Name, '.', -1);
l_Basename := Substr(l_File_Name, 1, l_Dot - 1);
IF l_Dot > 0 THEN
l_Extension := Substr(l_File_Name, l_Dot + 1);
END IF;
WHILE TRUE LOOP
l_File_Name := l_Basename || '(' || l_Counter || ').' ||
l_Extension;
IF NOT Dbms_Xdb.Existsresource(l_Currentfolder || l_File_Name) THEN
l_Result := Dbms_Xdb.Createresource(l_Currentfolder ||
l_File_Name,
l_Blob_Content);
l_Message := '201,"' || l_File_Name || '"';
EXIT;
ELSE
l_Counter := l_Counter + 1;
END IF;
END LOOP;
ELSE
l_Result := Dbms_Xdb.Createresource(l_Currentfolder || l_File_Name,
l_Blob_Content);
l_Message := '0';
END IF;
-- optionally
-- DELETE FROM Htmldb_Application_Files
-- WHERE NAME = Newfile;
Htp.p('<script type="text/javascript">');
Htp.p('window.parent.frames[''frmUpload''].OnUploadCompleted(' ||
l_Message || ') ;');
Htp.p('</script>');
END IF;
END;
After procedure is created ( can be your application schema, for example HR ), a public synonym should be created and
grant to execute should be made.
create public synonym connector for connector;
grant execute on connector to anonymous;
In the Oracle XE ApEx version there is a little bit more restriction for execution of stored procedures, so to allow it to be executed
FLOWS_020100.WWV_FLOW_EPG_INCLUDE_MOD_LOCAL should be modified accordingly.
To make this setup working with regular Application Express installation:
- the url in the fckconfig.js should look like /pls/htmldb/connector instead of /apex/connector
- the XML DB listener should be enabled
BEGIN
DBMS_XDB.SETHTTPPORT(7778);
END;
/
- ./FCKeditor/editor/filemanager/browser/default/frmresourceslist.html should be modified
function OpenFile( fileUrl )
{
// URL to inserted image refer to another web server, so should be given as absolute path
// window.top.opener.SetUrl( fileUrl ) ;
window.top.opener.SetUrl('http://myhost:7778' + fileUrl ) ;
window.top.close() ;
window.top.opener.focus() ;
}
- optionally, XML DB acl's can be modified, to allow access to resources without providing password
If XML DB for some reasons should not be used, then it is possible to store uploaded images in the custom document table and emulate the filesystem with
additional selfreferencing path table , so, file hierarchy can be simply faked with a connect by query - however i personally still prefer XML DB solution - it
provides a really great functionality, is included in the database, so why don't use it ?
Security aspects are not covered by this example, so it might be a good idea, to modify this setup to cover your requirements.
There is also another rich editor -
TinyMCE , it hasn't however own free filemanager plugin. But some people have adopted the FCKeditor's plugin for this editor -
tinyfck . So, with minor modifications the setup above can be also used here.
--
MaximDemenko - 12 Aug 2006
Comments