I'm building a Flex widget for a private vBulletin site, and the Flex widget needs to access an XML file on the vBulletin server in order to display data.
For security reasons, the XML URL will need to have the value in the bbsessionhash cookie passed along in the URL request from Flex. The Flex widget will be embedded in the private area that the user has logged into, so the Flex request will be coming from the same website the cookie is from.
Is there any way to access the cookies directly within Flex? I would prefer not to use ExternalInterface to grab the cookie data from JavaScript, as it could get a little messy (the templates are developed by a completely different dev team).
I have never tried this, but this library might just do the trick.
As per the flash or flex cookies are concern developer can use shared object which is one kind of cookie used for flex application.
The sample code snippet is as followes
import flash.net.SharedObject;
// get/create the shared object with a unique name.
// If the shared object exists this grab it, if not
// then it will create a new one
var so: SharedObject = SharedObject.getLocal("UniqueName");
// the shared object has a propery named data, it's
// an object on which you can create, read, or modify
// properties (you can't set the data property itself!)
// you can check to see if it already has something set
// using hasOwnProperty, so we'll check if it has a var
// use it if it does, or set it to a default if it doesn't
if (so.data.hasOwnProperty("theProp"))
{
trace("already has data! It reads: " + so.data.theProp);
}
else
{
so.data.theProp = "default value";
so.flush(); // flush saves the data
trace("It didn't have a value, so we set it.");
}
Accessing Flex SharedObject is NOT the same as accessing the Browser cookies, to access the browser cookies, you may use the ExternalInterface class, please check the following reference to see samples:
http://livedocs.adobe.com/flex/3/html/help.html?content=passingarguments_4.html
A reference of how to use and control cookies using JavaScript can be found here:
http://www.quirksmode.org/js/cookies.html
I would use the following Flex code:
var myCookie:String = ExternalInterface.call("getCookie('cookieName')");
And in the HTML I would add the following Javascript:
function getCookie(c_name) {
var i,x,y,ARRcookies=document.cookie.split(";");
for (i=0;i<ARRcookies.length;i++) {
x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
x=x.replace(/^\s+|\s+$/g,"");
if (x==c_name) return unescape(y);
}
}
If you require more help you could also check the Flex documentation.
Related
I have implemented a reporting engine in mvc using html rendering. I had to create custom paging functionality based on the metadata returned via the render method. I am now looking at implementing interactive sorting in the same fashion which is looking like a more daunting task. It seems there is a setting in the HTML Device Information Settings header for html rendering called ActionScript.
ActionScript(*)-
Specifies the name of the JavaScript function to use when an action event occurs, such as a drillthrough or bookmark click. If this parameter is specified, an action event will trigger the named JavaScript function instead of a postback to the server.
My question is has anyone used this feature? Since I lose authentication after the report is rendered I will have to somehow make authenticated callbacks in the controller. It seems to me it may be easier just to add parameters for sorting in the report, however I would like to keep the interactive sorting in the report.
--Edit Solution1---
It turns out that the * next to script name above means the ActionScript parameter is being depreciated after ssrs 2012. Thus, I decided not to pursue it. If anyone else stumbles upon this then the best way I thought of to simulate interactive sorting without post backs is detailed below:
On the Report
1. Add a parameter to the report SortField1
2. Add a Sort condition to the Tablix or group you wish to sort. Tablix-Sort Expressions
3. Set the expression of the sort to Fields(Paremeters!SortField1.Value).Value
4. Set the default value of parameter SortField1 to the default sort field
5. Set Allow Nulls of the parameter to true.
6. Add a image or label for each column you would like to sort by
7. Create a action for the element created in step 7 with code similar to
="javascript:void(reportSortRequest('ColumsFieldName'))"
NOTE: Your view or view descendant will have need to have an identical function defined to accept the action
In the View
Implement the function reportSortRequest(fieldName). This should set
Model.SortField1 and invoke the post back to the controller to
re-render the report.
In the Controller (This is for Ajax post backs that sends a model that will have a field SortField1)
Call the render with report information including SortField1.
Updated with client script that works to access the report markup wrapper.
The report element generated by the ssrs markup can be accessed in your view by its tags. I have that found the following will work using the predetermined ssrs wrapper element #oReportCell":
Loaded in iframe :
var frameContent = $("#myIFrame").contents();
var ssrsContentElement = frameContent.find("#oReportCell");
Loaded in div:
var ssrsContentElement = $("#myDiv").find("#oReportCell");
Function to load html blob into either a frame or a div. Depending how the report was configured, the content is either a url of a temp file or the html markup.
function setReportContent(content, isUrl, renderInIFrame) {
if (isUrl) {
if (renderInIFrame) {
$("#reportContent").html("<iframe id='reportFrame' sandbox='allow-same-origin allow-scripts' width='100%' height='300' scrolling='yes' onload='onReportFrameLoad();'\></iframe>");
$('#reportFrame').attr('src', content);
}
else
$("#reportContent").load(content);
}
else {
if (renderInIFrame) {
$("#reportContent").html("<iframe id='reportFrame' sandbox='allow-same-origin allow-scripts' width='100%' height='300' scrolling='yes' onload='onReportFrameLoad();'\></iframe>");
$('#reportFrame').contents().find('html').html(content);
}
else
$("#reportContent").html(content);
}
if(!renderInIFrame)
showReportWaitIndicator(false);
$("#reportContent").show();
}
Notice that if you want report images with auto-resize property set to be rendered correctly you have to turn on allow-scripts so that the ssrs resize js functions can fire, be careful.
I have my views setup to pre-compile, and therefore, at runtime if I were to try and read the view file (e.g. "~\Views\User\Report.cshtml") I'd get the following dummy-text, as opposed to the contents of my view:
This is a marker file generated by the precompilation tool, and should not be deleted!
Problem is, I'd like to re-use the cshtml view, and rerender it another way at runtime, but I cannot due to the above restriction.
The scenario:
An admin can see a list of users in a /User/Report route. It outputs some HTML that has a list of all users, and their information in an HTML table. These admins frequently want to download this html file (styles and all) to email it as an attachment to someone else. They could, of course, go to File->Save in their browser, but I wanted to simplify that action by adding a link to the page "Download this report as HTML" that would simply return the same page's content, as a forced-downloaded HTML file (2012-07-11_UserReport.html).
So, what I tried to do was re-render the view by running the Report.cshtml file's contents through ASP.NET's File() method, like this:
var html = System.IO.File.ReadAllText(Server.MapPath(#"~\Views\User\Report.cshtml"));
var bytes = System.Text.Encoding.UTF8.GetBytes(html);
return File(bytes,"text/html",string.Format("{0}_UserReport.html",DateTime.Now.ToString("yyyy-MM-dd")));
But, like I mentioned earlier, the file comes back as the dummy-text, not the view, since I'm pre-compiling the views.
I understand that to get around the pre-compilition, I could simply copy the Report.cshtml file, and rename it to Report.uncompiled (adding it to the csproj as of course) and read the contents of it, that's an ok solution, but not good enough.
What I would really like to know is: Is there a way I can get at that pre-compiled content? I looked in the Assembly's embedded resources, and they are not there. Any ideas/suggestions?
Updated with current solution
So after searching around some more, and trying to use WebClient/WebRequest to just make a request to the route's URL and send the response back down to the user to download while at the same time trying to pass the user's .ASPXAUTH cookie (that made WebClient/WebRequest time out for some reason? I even tried to create a new ticket, same result) I ended up going with what I didn't want to do: duplicate the view file, and rename it so it's not precompiled.
The view file (Report.uncompiled) had to be modified a bit as it was, and then I ran it through RazorEngine's Razor.Parse method and got what I needed, but it just felt hackey. Would still like a way to access the view file (Report.cshtml) even after it's compiled.
var templateHtml = Razor.Parse(System.IO.File.ReadAllText(Server.MapPath(#"~\Views\User\Report.uncompiled")),model);
var bytes = System.Text.Encoding.UTF8.GetBytes(templateHtml);
return File(bytes, "text/html", string.Format("{0}_UserReport.html", DateTime.Now.ToString("yyyy-MM-dd")));
Would the WebClient class work?
using System.Net;
using (WebClient client = new WebClient ())
{
client.DownloadFile("http://yourwebsite.com/test.html", #"C:\directory.html");
// If you just want access to the html, see below
string html = client.DownloadString("http://yourwebsite.com/test.html");
}
Just have this fire whenever your user clicks a button and then it will save the current content of the page wherever? You could probably also have a directory selector and feed whatever they select into that second parameter.
It essentially does the same thing as the browser save as, if that's what you want.
Publishing a component which has multiple dynamic templates will usually result in all the possible dynamic component presentations being published to the broker.
When you create a DCT with the option to place the item on a page, a content editor may not want to publish the components directly, simply relying on the Page publish to do the right thing. We could consider three possible desired publishing scenarios:
That publishing the page should only cause the static component presentations to be rendered, (plus whatever CD code is necessary to display the dynamic ones)
That in addition to static CPs, any dynamic CPs should be published. Other possible dynamic renderings of the same component are not published.
If a dynamic CP is published, the usual component publishing semantics are followed,
and all dynamic renderings will go to the broker.
Tridion's default behaviour appears to be scenario 2), whereas my experience is that often what you want is scenario 3), giving you a complete and
consistent view of any given component on the CD side.
What is the best way to implement scenario 3 (including getting unpublish to work correctly)?
In my opinion, the best answer for your question is to implement a custom Resolver that would include the required Dynamic Component Presentations. I would be wary of doing anything when unpublishing, as sometimes you may want to keep the DCPs after unpublishing a given page (for "latest news" type of functionality or any other sort of dynamic queries), but the code sample below would make it simple for you to adapt if you need to unpublish all DCPs.
Warning: Below code is not production-tested.
using Tridion.ContentManager;
using Tridion.ContentManager.CommunicationManagement;
using Tridion.ContentManager.ContentManagement;
using Tridion.ContentManager.Publishing;
using Tridion.ContentManager.Publishing.Resolving;
public class IncludeDynamicComponentPresentations : IResolver
{
public void Resolve(
IdentifiableObject item,
ResolveInstruction instruction,
PublishContext context,
Tridion.Collections.ISet<ResolvedItem> resolvedItems)
{
if (!(instruction.Purpose == ResolvePurpose.Publish ||
instruction.Purpose == ResolvePurpose.RePublish))
{
// Do nothing more when unpublishing
return;
}
Session session = item.Session;
foreach (ResolvedItem resolvedItem in resolvedItems)
{
// Only do something if we're dealing with a page
if (!(resolvedItem.Item is Page)) continue;
Page page = (Page)resolvedItem.Item;
if (page.ComponentPresentations.Count > 0)
{
UsingItemsFilter filter = new UsingItemsFilter(session);
filter.InRepository = page.ContextRepository;
filter.ItemTypes = new[] { ItemType.ComponentTemplate };
foreach (ComponentPresentation cp in page.ComponentPresentations)
{
// Find all component templates linked to this component's schema
Schema schema = cp.Component.Schema;
foreach (ComponentTemplate ct in schema.GetUsingItems(filter))
{
if (!ct.Id.Equals(cp.ComponentTemplate.Id))
{
if (ct.IsRepositoryPublishable)
{
resolvedItems.Add(new ResolvedItem(cp.Component, ct));
}
}
}
}
}
}
}
}
You would now need to add this to the GAC and modify [Tridion]\Config\Tridion.ContentManager.Config so this Resolver is called after every resolve action (under resolving/mappings for every item type).
Perhaps a Custom Resolver would help in this situation? This would give you access to all the items the result from a publish action, allowing you to change the default behaviour.
There's a good example of this in the SDL Tridion documentation portal, but it basically allows you to create a custom resolver class in .net, where you can implement your custom logic.
I have created a simple SWF-loader in ActionScript 3.0. It loads an SWF from a server and then plays it. While downloading, it displays the "loading" screen.
Its main defect is that it can load only one Flash application - the one which it is compiled for. Let's say it's named test1.swf.
Is there any way to make the loader support more than one Flash app (for example test2.swf and test3.swf)? I mean by passing external parameters to it and not by creating another loader. Is using Javascript the only way to do it? I don't want my loader to require the Javascript support.
And I really don't want to create separate loaders for all of my apps...
Thanks in advance.
In order to load an external SWF your loader only need the url of the swf to be loaded, this url doesn't have to be hardcoded. There are many ways to pass parameters to a SWF file and they don't necessarily require Javascript.
You could load a XML file for instance, a simple text file would work too , you could also use a PHP script. Using flahsvars would require Javascript, although only to set your application in your HTML page.
With the following example , your app doesn't need to recompile , you simply change the url in the text file.
Example with a text file containing a url, something like this:
http://yourwebsite.com/test1.swf
var urlLoader:URLLoader = new URLLoader();
urlLoader.addEventListener(Event.COMPLETE , completeHandler );
urlLoader.load( new URLRequest('swfURL.txt') );
function completeHandler(event:Event):void
{
loadExternalSWF(event.target.data );
event.target.removeEventListener(Event.COMPLETE , completeHandler );
}
function loadExternalSWF(url:String ):void
{
//your code here , using the url value
trace(url );//should return your text file content
}
I have an application with a launch page that needs to determine what is already opened, so it does not reopen things that are opened already in another new tab. In Firefox, I was able to make this work, by using window.sessionStorage to store the titles of pages that are open, and then use window.opener with the following code to remove the titles from the list.
Gecko Session Storage Info Page
if (window.sessionStorage) {
if (window.sessionStorage.getItem(code)) {
return; // page already open
}
else {
window.sessionStorage.setItem(code, code);
window.open("Sheet.aspx", "_blank");
}
}
And on the pages that are opened:
function signalPageExit() {
if (window.opener.sessionStorage) {
window.opener.sessionStorage.removeItem(
document.getElementById("runcode").childNodes[0].textContent);
}
This doesn't work in IE so I decided to use a cookie strategy, but the cookies were never successfully deleted from code on the dynamically launched pages, and therefore pages couldn't be reopened from the launch page once they had been launched until the cookie expired.
My second attempt was to define my own sessionStorage when it did not exist. That looked like this:
function setStoreItem(name, val) {
this.storage[name] = val;
}
function getStoreItem(name) {
return(this.storage[name]);
}
function removeStoreItem(name) {
this.storage[name] = null;
}
function sesStorage() {
this.storage = new storageData();
this.setItem = setStoreItem;
this.getItem = getStoreItem;
this.removeItem = removeStoreItem;
}
// storage object type declaration
function storageData() {
}
// IE 7 and others
else {
window.sessionStorage = new sesStorage();
window.sessionStorage.setItem(code, code);
window.open("Sheet.aspx", "_blank");
}
But it seems the real session storage is special, this ordinary object of the window did not stay alive across postbacks and therefore when my launch page posted back, the list of created page titles was wiped out.
So now I'm looking for a way to make this work. I have a launch page called scoresheets.aspx that creates dynamic pages based on user requests. These pages share a substantial amount of javascript code that can be modified to make this work.
I don't want to refresh the launched pages when a user tries to reopen them, but if there is some way to detect the titles of opened pages or some other way to use window.opener to communicate with the same persistence that sessionStorage has, I'd be glad to use it.
Eric Garside’s jStore plugin provides a jquery based api to several client side storage engines.
you should go with that cookie strategy and set those cookies to expire when the windows (tab) is closed. that should work across browsers.