I am loading an local disk drive _test.htm file through IPersistMoniker Load method. From what I believe, it is supposed to add the path to the relative URLs as base path. Problem is - it does not do so. Instead, it takes a very long time trying to resolve the path from Internet until it gives up (about 20-30 seconds). What I want is to give up instantly, as soon as the unsolvable path is detected (since it is a local disk file anyway).
This is an example HTML I am loading:
<html>
<head>
<script src="//test/test.js"></script>
<head>
<body>
<img src="image.jpg">
<img src="/image.jpg">
<img src="//image.jpg">
</body>
</html>
Simplified code (C++ Builder) with no error checking:
WideString URL = "file:///" + StringReplace(ExtractFilePath(Application->ExeName), "\\", "/", TReplaceFlags() << rfReplaceAll) + "_test.htm";
TCppWebBrowser* WB = CppWebBrowser1;
DelphiInterface<IMoniker> pMoniker;
OleCheck(CreateURLMonikerEx(NULL, URL.c_bstr(), &pMoniker, URL_MK_UNIFORM));
DelphiInterface<IHTMLDocument2> diDoc2 = WB->Document;
DelphiInterface<IPersistMoniker> pPrstMnkr;
OleCheck(diDoc2->QueryInterface(IID_IPersistMoniker, (LPVOID*)&pPrstMnkr));
DelphiInterface<IBindCtx> pBCtx;
OleCheck(CreateBindCtx(0, &pBCtx));
pPrstMnkr->Load(0, pMoniker, pBCtx, STGM_READWRITE);
Problem - image.jpg loads fine, but the paths //test/test.js and /image.jpg and //image.jpg take a very long time to resolve/load. From what I understand CreateURLMonikerEx is supposed to use file:///path/to/executable/ and prepend that automatically to these paths in which case they would fail instantly - file:///path/to/executable//test/test.js for example. That does not happen.
I additionally tried to move image.jpg to a subfolder and then create custom IMoniker interface with the GetDisplayName and BindToStorage implementation which loaded the image from a custom path. However it doesn't do the same for paths which start with // or /. Even though I output file:///path/to/executable/ in the GetDisplayName through the *ppszDisplayName parameter.
How can I avoid extended time loading such unusable links (discard them), or redirect them to local path as above?
I found a partial solution to use about:blank in the *ppszDisplayName but then it doesn't load images with the valid path image.jpg as then it loads them as about:image.jpg which again is invalid path.
Additionally - I've tried adding IDocHostUIHandler interface with the implementation of Invoke method (DISPID_AMBIENT_DLCONTROL) with the pVarResult->lVal = DLCTL_NO_SCRIPTS | DLCTL_NO_JAVA | DLCTL_NO_RUNACTIVEXCTLS | DLCTL_NO_DLACTIVEXCTLS | DLCTL_NO_FRAMEDOWNLOAD | DLCTL_FORCEOFFLINE; - it it blocks the download of images entirely, but still does check 20-30 seconds for the links starting with // or /.
Update - this doesn't work well!
The code below doesn't work well! The problem is - it loses <BODY>
tag attributes. BODY tag turns out entirely empty after loading. I
ended up loading the message using IHTMLDocument2.write method.
See: Assigning IHTMLDocument2 instance to a TWebBrowser instance
After spending lots of time and no guidance of any kind here, I believe that it is not possible to avoid this wait 20-30 sec when the links are invalid. I found another solution and if someone wants to supplement this solution, feel free to do so.
Instead what I had to do is to create an instance of CLSID_HTMLDocument (IHTMLDocument3 or IHTMLDocument2 interface) and then load the document into that container and parse the links prior to doing anything with them. This is described on:
https://learn.microsoft.com/en-us/previous-versions/aa703592(v=vs.85)
This also helped:
How to load html contents from stream and then how to create style sheet to display the html file in preview pane (like HTML preview handler)
After parsing the document URLs and fixing the invalid ones, it can be saved/displayed in the actual TWebBrowser.
Rough solution (C++ Builder):
try
{
DelphiInterface<IHTMLDocument2> diDoc2;
OleCheck(CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_IHTMLDocument2, (void**)&diDoc2));
DelphiInterface<IPersistStreamInit> diPersist;
OleCheck(diDoc2->QueryInterface(IID_IPersistStreamInit, (void**)&diPersist));
OleCheck(diPersist->InitNew());
DelphiInterface<IMarkupServices> diMS;
OleCheck(diDoc2->QueryInterface(IID_IMarkupServices, (void**)&diMS));
DelphiInterface<IMarkupPointer> diMkStart;
DelphiInterface<IMarkupPointer> diMkFinish;
OleCheck(diMS->CreateMarkupPointer(&diMkStart));
OleCheck(diMS->CreateMarkupPointer(&diMkFinish));
// ...Load from file or memory stream into your WideString here...
DelphiInterface<IMarkupContainer> diMC;
OleCheck(diMS->ParseString(WideString(MsgHTMLSrc).c_bstr(), 0, &diMC, diMkStart, diMkFinish));
DelphiInterface<IHTMLDocument2> diDoc;
OleCheck(diMC->QueryInterface(IID_PPV_ARGS(&diDoc)));
DelphiInterface<IHTMLElementCollection> diCol;
OleCheck(diDoc->get_all(&diCol));
long ColLen = 0;
OleCheck(diCol->get_length(&ColLen));
for (int i = 0; i < ColLen; ++i)
{
DelphiInterface<IDispatch> diItem;
diCol->item(OleVariant(i), OleVariant(i), &diItem);
DelphiInterface<IHTMLElement> diElem;
OleCheck(diItem->QueryInterface(IID_IHTMLElement, (void**)&diElem));
WideString wTagName;
OleCheck(diElem->get_tagName(&wTagName));
if (StartsText("img", wTagName))
{
OleVariant vSrc;
OleCheck(diElem->getAttribute(OleVariant("src"), 4, vSrc));
// Make changes to vSrc here....
// And save it back to src
OleCheck(diElem->setAttribute(OleVariant("src"), vSrc, 0));
}
else if (StartsText("script", wTagName))
{
// More parsing here...
}
}
}
catch (EOleSysError& e)
{
// Process exception as needed
}
catch (Exception& e)
{
// Process exception as needed
}
After full parsing of all required elements (img/src, script/src, base/href etc.) save and load into TWebBrowser.
I only now have to see if the parsed HTML IHTMLDocument2 can be directly assigned to TWebBrowser without loading it again, but that is another question (See - Assigning IHTMLDocument2 instance to a TWebBrowser instance)
Related
I use MSHTML (IHTMLDocument) to display offline HTML which may contain various links. These are loaded from email HTML.
Some of them have URLs starting with // or / for example:
<img src="//www.example.com/image.jpg">
<img src="/www.example.com/image.jpg">
This takes a lot of time to resolve and show the document because it cannot find the URL obviously as it doesn't start with http:// or https://
I tried injecting <base> tag into <head> and adding a local known folder (which is empty) and that stopped this problem. For example:
<base href="C:\myemptypath\">
However, if links begin with \\ (UNC path) the same problem and long loading time begin again. Like:
<img src="\\www.something.com\image.jpg">
I also tried placing WebBrowser control into "offline" mode and all other tricks I could think of and couldn't come up with anything short of RegEx and replacing all the links in the HTML which would be terribly slow solution (or parsing HTML myself which defeats the purpose of MSHTML).
Is there a way to:
Detect these invalid URLs before the document is loaded? - Note: I already did navigate through DOM e.g. WebBrowser1.Document.body.all collection, to get all possible links from all tags and modify them and that works, but it only happens after the document is already loaded so the long waiting time before loading gives up is still happening
Maybe trigger some event to avoid loading these invalid links and simply replace them with about:blank or empty "" text like some sort of "OnURLPreview" event which I could inspect and reject loading of URLs that are invalid? There is only OnDownloadBegin event which is not it.
Any examples in any language are welcome although I use Delphi and C++ (C++ Builder) as I only need the principle here in what direction to look at.
After a long time this is the solution I used:
Created an instance of CLSID_HTMLDocument to parse HTML:
DelphiInterface<IHTMLDocument2> diDoc;
OleCheck(CoCreateInstance(CLSID_HTMLDocument, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&diDoc)));
Write IHTMLDocument2 to diDoc using Document->write
// Creates a new one-dimensional array
WideString HTML = "Example HTML here...";
SAFEARRAY *psaStrings = SafeArrayCreateVector(VT_VARIANT,0,1);
if (psaStrings)
{
VARIANT *param;
BSTR bstr = SysAllocString(HTML.c_bstr());
SafeArrayAccessData(psaStrings, (LPVOID*)¶m);
param->vt = VT_BSTR;
param->bstrVal = bstr;
SafeArrayUnaccessData(psaStrings);
diDoc->write(psaStrings);
diDoc->close();
// SafeArrayDestroy calls SysFreeString for each BSTR
//SysFreeString(bstr); // SafeArrayDestroy should be enough
SafeArrayDestroy(psaStrings);
return S_OK;
}
Parse unwanted links in diDoc
DelphiInterface<IHTMLElementCollection> diCol;
if (SUCCEEDED(diDoc->get_all(&diCol)) && diCol)
{
// Parse IHTMLElementCollection here...
}
Extract parsed HTML into WideString and write into TWebBrowser
DelphiInterface<IHTMLElement> diBODY;
OleCheck(diDoc->get_body(&diBODY));
if (diBODY)
{
DelphiInterface<IHTMLElement> diHTML;
OleCheck(diBODY->get_parentElement(&diHTML));
if (diHTML)
{
WideString wsHTML;
OleCheck(diHTML->get_outerHTML(&wsHTML));
// And finally use the `Document->write` like above to write into your final TWebBrowser document here...
}
}
i want display PDF file into region , i tried that by call application process using below code but always same file open.( plsql dynamic content region)
DECLARE
V_URL VARCHAR2(2500);
BEGIN
V_URL :='f?p=&APP_ID.:1:&APP_SESSION.:APPLICATION_PROCESS=display_emp_blob:::FILE_ID:' ||:P6_ID;
Sys.htp.p('<p align="center">');
sys.htp.p('<iframe src="'||V_URL||'"width="99%" height="1000">');
sys.htp.p('</iframe>');
sys.htp.p('</p>');
END;
and the application process code in below
CREATE OR REPLACE PROCEDURE OPEN_FILE (P_ID NUMBER)
IS
vBlob blob;
vmimetype varchar2(50);
BEGIN
SELECT ORG_FILES.FILE_CONTENT,MIME_TYPE INTO vBlob, vmimetype
FROM ORG_FILES
WHERE ID =P_ID ;
sys.HTP.init;
owa_util.mime_header(vmimetype,false);
htp.p('Content-Length: ' || dbms_lob.getlength(vBlob));
owa_util.http_header_close;
wpg_docload.download_file(vBlob);
apex_application.stop_apex_engine;
exception
when no_data_found then
null;
END;
How i can open different PDF file into region based a value in ITEM (P6_ID) .
I think the problem you have is that the browser caches the file.
You can specify the time the browser caches with the "Cache-control" header option. Below, you have the code that I use (I have this code in the application process, not in the database):
sys.htp.init;
sys.owa_util.mime_header( 'application/pdf', FALSE );
sys.htp.p('Content-length: ' || sys.dbms_lob.getlength( v_blob));
sys.htp.p('Content-Disposition: inline; filename="'|| v_filename || '"' ); -- "attachment" for download, "inline" for display
sys.htp.p('Cache-Control: max-age=3600'); -- in seconds. Tell the browser to cache for one hour, adjust as necessary
sys.owa_util.http_header_close;
sys.wpg_docload.download_file( v_blob );
apex_application.stop_apex_engine;
You can also try some lazy load, which is the way I access my files (It may be that the way to access your file is also part of the problem). This way you make the page load without waiting for the user and then it loads and shows the file. I don't use the iframe tag but the embed tag. The way to do it is as follows:
Create a region with static content with this html
<div id="view_pdf"></div>
creates a dynamic action when the page loads, which executes javascript and add the following code
$('#view_pdf').html('');
var url = 'f?p=&APP_ID.:1:&APP_SESSION.:APPLICATION_PROCESS=display_emp_blob:::FILE_ID:' + apex.item('P6_ID').getValue();
var preview = document.createElement('embed');
preview.type = "application/pdf";
preview.width="100%";
preview.height="625px";
preview.src = url;
$("#view_pdf").append(preview);
You can modify the values depending on what you need. The embed tag uses the default way to view pdf files from browsers.
Also if what you want is to change the pdf without reloading the page you must use the previous javacript in a dynamic action when you change the value of the item.
I hope you find it useful.
My apologies, on the rare occasion I use this region type, I always think it can be refreshed.
https://spendolini.blogspot.com/2015/11/refreshing-plsql-regions-in-apex.html
The solution is to create a classic report that calls a PL/SQL function that returns your HTML.
SELECT package_name.function_name(p_item => :P1_ITEM) result FROM dual
In Sitecore, I have a statically-bound renderer in my Razor:
#Html.Sitecore().Rendering("[item ID]")
This works great, but I want to cache. So:
#Html.Sitecore().Rendering("[item ID]", new { Cacheable = true } )
This also works, and seems to cache for about 60 seconds, which I understand is the default.
I'd like to cache for 20 minutes (1200 seconds), but I can't find any way to specify this. That second param to Rendering is an anonymous object, so there's not help from Intellisense around that. I've tried:
new { Cacheable = true, Duration = 1200 }
new { Cacheable = true, Cache_Duration = 1200 } // Someone said Cache_ was the trick. It wasn't.
What is the setting to vary the cache duration of these renderings?
Update
I reflected a bit, but to no avail. The properties on that anonymous object are iterated and used to set values on Rendering.RenderingProperties. This is a key/value data structure, keyed by string, so any cache settings are no longer strongly-typed, and thus my reflecting trail ran cold.
Another Update
#sitecore-climber did some more reflecting, and found a property of RenderingCachingDefiniton called Timeout which appears to parse the value of the Cache_Timeout key into a Timespan object.
However, my actual testing of this...
new { Cacheable = true, Cache_Timeout = "00:20:00"}
...doesn't seem to change the 60-second caching timeout. This is odd, since you can see all the other properties in the RenderingCachingDefinition class that parse off that anonymous object parameter (Cacheable, VaryByParam, etc.).
Of course, this might be parsing just fine, and Sitecore just doesn't do anything with the resulting value. But I followed that Timeout property around, and based on my reading, it's accurately used to set a timeout on the cache.
I tried on a Sitecore 8.1 solution and is working fine using
#Html.Sitecore().Rendering("{240A3624-8600-4A7B-8C07-5846A673F1F0}", new { Cacheable = true,Cache_Timeout = "00:20:00" } )
On my view container I have :
#Html.Sitecore().Rendering("{240A3624-8600-4A7B-8C07-5846A673F1F0}")
#Html.Sitecore().Rendering("{240A3624-8600-4A7B-8C07-5846A673F1F0}", new { Cacheable = true,Cache_Timeout = "00:20:00" } )
My view is very simple
#using Sitecore.Mvc
<div>
<h1>
#Html.Sitecore().Field("title")
</h1>
<div>
#Html.Sitecore().Field("text")
#System.DateTime.Now.Second
</div>
</div>
But caching is working just on normal mode not on experience editor or page editor. Did you try to publish and check your website?
Please check video I uploaded at : https://www.dropbox.com/s/57s5x0zw7yr8lyb/VideoAvi.avi?dl=0
I'm looking for help on implementing something that automatically includes versioned filenames in a Go HTML template. For example, in my template I have something like this in the head:
<link rel="stylesheet" href="{{ .MyCssFile }}" />
The stylesheets themselves have a chunk of MD5 hash appended to the name from a gulp script called gulp-rev
stylesheet-d861367de2.css
The purpose is to ensure new changes are picked up by browsers, but also allow caching. Here is an example implementation in Django for a better explanation:
https://docs.djangoproject.com/en/1.9/ref/contrib/staticfiles/#manifeststaticfilesstorage
A subclass of the StaticFilesStorage storage backend which stores the file names it handles by appending the MD5 hash of the file’s content to the filename. For example, the file css/styles.css would also be saved as css/styles.55e7cbb9ba48.css.
The purpose of this storage is to keep serving the old files in case some pages still refer to those files, e.g. because they are cached by you or a 3rd party proxy server. Additionally, it’s very helpful if you want to apply far future Expires headers to the deployed files to speed up the load time for subsequent page visits.
Now I'm wondering how to best pull this off in Go? I intend to serve the files from the built in file server.
My current thoughts are:
Have a loop that checks for the newest stylesheet file in a directory. Sounds slow.
Do some kind of redirect/rewrite to a generically named file (as in file.css is served on a request to file-hash.css).
Have Go manage the asset naming itself, appending the hash or timestamp.
Maybe its better handled with nginx or something else?
Write a template function to resolve the name. Here's an example template function:
func resolveName(p string) (string, error) {
i := strings.LastIndex(p, ".")
if i < 0 {
i = len(p)
}
g := p[:i] + "-*" + p[i:]
matches, err := filepath.Glob(g)
if err != nil {
return "", err
}
if len(matches) != 1 {
return "", fmt.Errorf("%d matches for %s", len(matches), p)
}
return matches[0], nil
}
and here's how to use it in a template when registered as the function "resolveName":
<link rel="stylesheet" href="{{ .MyCssFile | resolveName }}" />
playground example
This function resolves the name of the file every time the template is rendered. A more clever function might cache names as they are resolved or walk the directory tree at startup to prebuild a cache.
I knew it's too old, but maybe this library will help you. It allows to collect and hash static files. Also it has function to reverse file path from the orignal location to the hashed location:
staticFilesPrefix := "/static/"
staticFilesRoot := "output/dir"
storage := NewStorage(staticFilesRoot)
err := storage.LoadManifest()
funcs := template.FuncMap{
"static": func(relPath string) string {
return staticFilesPrefix + storage.Resolve(relPath)
},
}
tmpl := template.Must(
template.New("").Funcs(funcs).ParseFiles("templates/main.tpl")
)
Now you can call static function in templates like this {{static "css/style.css"}}. The call will be converted to /static/css/style.d41d8cd98f00b204e9800998ecf8427e.css.
I got problem when implementing my CMS using Codeigniter 1.7.2 and Dwoo. I use Phil Sturgeon Dwoo library. My problem is I want user create template from the admin panel, it means all template will be stored into database including all Dwoo variable and functions.My questions:
Is it possible to load dwoo template from database?
How to parse dwoo variable or function from database? I tried to load content from database which is include dwoo var and function inside it, and i have tried to do evaluation using dwoo eval() function and phil sturgeon string_parse() but still have no luck.
for example:
my controller
$data['header'] = "<h1>{$header}</h1>"; --> this could be loaded from database
$this->parser->parse('header',$data);
my view
{$header}
This is the error message:
<h4>A PHP Error was encountered</h4>
<p>Severity: Notice</p>
<p>Message: Undefined index: header_title</p>
<p>Filename: compiled/805659ab5e619e094cac7deb9c8cbfb5.d17.php</p>
<p>Line Number: 11</p>
header_title is dwoo variable that loaded from DB.
Thank you,
It is definitely possible to do this, but for performance reasons it would probably be faster to store the templates as files on the filesystem, even if they're edited by users. If you're smart with file naming you can avoid most hits to the database.
Anyway, if you really want to do it with the database, the following should work:
// rendering the header
$template = '<h1>{$header}</h1>'; // loaded from db, don't use double-quotes for examples or $header will be replaced by nothing
$data = array('header' => 'Hello'); // loaded from db as well I assume
$headerOutput = $this->parser->parse_string($template, $data, true); // true makes it return the output
// now rendering the full page (if needed?)
$data = array('header' => $headerOutput);
$this->parser->parse('header', $data); // no 'true', so goes straight to output
The view would then contain {$header} and the output from the header template is passed to that variable.
Now I'm not sure how CI works so it might be better to just output the result of the first template and skip the second one entirely, but I'll leave that up to you.