DotNetNuke ModuleAction server-side processing - c#

In DNN it is possible to add a ModuleAction menu item. According this article on the DNN site it is even possible to do some additional processing on the server-side. After converting the code to C#, the ActionHandler is never called.
This is my code:
public ModuleActionCollection ModuleActions
{
get
{
ModuleActionCollection Actions = new ModuleActionCollection();
ModuleAction urlEventAction = new ModuleAction(ModuleContext.GetNextActionID());
urlEventAction.Title = "Action Event Example";
urlEventAction.CommandName = "redirect";
urlEventAction.CommandArgument = "cancel";
urlEventAction.Url = "http://dotnetnuke.com";
urlEventAction.UseActionEvent = true;
urlEventAction.Secure = DotNetNuke.Security.SecurityAccessLevel.Admin;
Actions.Add(urlEventAction);
return Actions;
}
}
private void MyActions_Click(object sender, DotNetNuke.Entities.Modules.Actions.ActionEventArgs e)
{
DotNetNuke.UI.Skins.Skin.AddModuleMessage(this, string.Format(Localization.GetString("ClickMessage", LocalResourceFile), e.Action.CommandName), ModuleMessage.ModuleMessageType.BlueInfo);
switch (e.Action.CommandName.ToUpper())
{
case "REDIRECT":
if (e.Action.CommandArgument.ToUpper() != "CANCEL")
{
Response.Redirect(e.Action.Url);
}
else
{
DotNetNuke.UI.Skins.Skin.AddModuleMessage(this, "Canceled the Redirect", ModuleMessage.ModuleMessageType.YellowWarning);
}
break;
}
}
And in the page init I attach the event handler:
AddActionHandler(new ActionEventHandler(MyActions_Click));
I also tried attaching in the page load as done by DNN source itself.
The menu item is shown and the redirect to http://dotnetnuke.com is executed.
But my breakpoint in MyActions_Click is never hit.
What am I doing wrong?
I'm running in DotNetNuke 7.1 with module reference to DNN 6.2.

My solution with IPostBackEventHandler instead of the DNN way (until someone corrects me):
public ModuleActionCollection ModuleActions
{
get
{
ModuleActionCollection Actions = new ModuleActionCollection();
Actions.Add(ModuleContext.GetNextActionID(),
"Bla",
"",
"",
"",
"javascript:" + Page.ClientScript.GetPostBackEventReference(this, "ARGUMENT"),
Page.ClientScript.GetPostBackEventReference(this, "ARGUMENT"),
false,
DotNetNuke.Security.SecurityAccessLevel.Edit,
true,
false);
return Actions;
}
}
public void RaisePostBackEvent(String eventArgument)
{
if (eventArgument.ToUpper() == "ARGUMENT")
{
...
Globals.Redirect(HttpContext.Current.Request.RawUrl, false);
}
}
And don't forget to add IPostBackEventHandler to your page class name.
Namespace: using System.Web.UI;

I honestly dont think this is a DNN issue. I would clear down your temp cache and try to debug again.
Leigh

Related

Using MutationObserver in GeckoFx with C#?

I am using GeckoFx to perform a login to a specific website. This website edits the page with new information should the login fail (or require additional authentication, such as a ReCaptcha). Unfortunately, it is vital that I have access an event when the page is updated. I have tried numerous approaches mainly
A continual check if the uri is still the same upon each login attempt and a subsequent check on the specific element in question (to see if the display: none property was changed. (This resulted in an infinite loop as it seems GeckoFx updates the page in a nonblocking way, causing the program to go into an infinite loop)
Sleeping for ~5 seconds between login requests and using the aforementioned uri check. All this did (predictably, I was grasping at straws) was freeze the browser for 5 seconds and still fail to update the page
Searching the GeckoFx codebase for a specific event when the page is updated similar to the DocumentCompleted event (no such luck).
The most common approach I have read about (and one that makes the most sense) is to use a MutationObserver. It seems that all of the answers across the internet involve injecting Javascript in order to perform the requisite task. Seeing as all of my programming background has not touched web development whatsoever, I'm trying to stick to what I know.
Here is my approach so far, unfortunately, it is not much.
public class GeckoTestWebLogin
{
private readonly string _user;
private readonly string _pass;
public GeckoWebBrowser Gweb;
public Uri LoginUri { get; } = new Uri("https://website.com/login/");
public bool LoginCompleted { get; private set; } = false;
public bool Loaded { get; private set; } = false;
public GeckoTestWebLogin(string user, string pass)
{
_user = user;
_pass = pass;
Xpcom.EnableProfileMonitoring = false;
Xpcom.Initialize("Firefox");
//this code is for testing purposes, it will be removed upon project completion
CookieManager.RemoveAll();
Gweb = new GeckoWebBrowser();
Gweb.DocumentCompleted += DocLoaded;
//right about here is where I get lost, where can I set a callback method for the observer to report back to? Is this even how it works?
MutationObserver mutationObserver = new MutationObserver(Gweb.Window.DomWindow, (nsISupports)Gweb.Document.DomObject);
}
private void TestObservedEvent(string parms, object[] objs)
{
MessageBox.Show("The page was changed # " + DateTime.Now);
}
public void DocLoaded(object obj, GeckoDocumentCompletedEventArgs e)
{
Loaded = true;
if (Gweb.Url != LoginUri) return;
AttemptLogin();
}
private void AttemptLogin()
{
GeckoElementCollection elements = Gweb.Document.GetElementsByTagName("input");
foreach (GeckoHtmlElement element in elements)
{
switch (element.Id)
{
case "username":
element.SetAttribute("value", _user);
break;
case "password":
element.SetAttribute("value", _pass);
break;
case "importantchangedinfo":
GeckoHtmlElement authcodeModal =
(GeckoHtmlElement)
Gweb.Document.GetElementsByClassName("login_modal").First();
if (authcodeModal.Attributes["style"].NodeValue != "display: none")
{
InputForm form = new InputForm { InputDescription = "Captcha Required!" };
form.ShowDialog();
elements.FirstOrDefault(x => x.Id == "captchabox")?.SetAttribute("value", form.Input);
}
break;
}
}
elements.FirstOrDefault(x => x.Id == "Login")?.Click();
}
public void Login()
{
//this will cause the DocLoaded event to fire after completion
Gweb.Navigate(LoginUri.ToString());
}
}
As stated in the above code in the comments, I am completely lost at
MutationObserver mutationObserver = new MutationObserver(Gweb.Window.DomWindow, (nsISupports)Gweb.Document.DomObject);
I can't seem to find anything in GeckoFx's source for MutationObserver that would allow me to set a callback/event/whathaveyou. Is my approach the correct way to go about things or am I left with no options other than to inject Javascript into the page?
Much appreciated, thank you in advance.
Here is my attempt at option 2 in Tom's answer:
(Added into GeckoTestWebLogin)
public void DocLoaded(object obj, GeckoDocumentCompletedEventArgs e)
{
Loaded = true;
if (Gweb.Url != LoginUri) return;
MutationEventListener mutationListener = new MutationEventListener();
mutationListener.OnDomMutation += TestObservedEvent;
nsIDOMEventTarget target = Xpcom.QueryInterface<nsIDOMEventTarget>(/*Lost here*/);
using (nsAString modified = new nsAString("DOMSubtreeModified"))
target.AddEventListener(modified, mutationListener, true, false, 0);
AttemptLogin();
}
MutationEventListener.cs:
public delegate void OnDomMutation(/*DomMutationArgs args*/);
public class MutationEventListener : nsIDOMEventListener
{
public event OnDomMutation OnDomMutation;
public void HandleEvent(nsIDOMEvent domEvent)
{
OnDomMutation?.Invoke(/*new DomMutationArgs(domEvent, this)*/);
}
}
I don't think Geckofx's webidl compiler is currently advanced enough to generate the callback constructor.
Option 1. - Enhance MutationObserver source.
You could modify MutationObserver source manually to add the necessary constructor callback. Then recompile Geckofx. (I haven't look to see how difficult this is)
Option 2. - Use old style Mutation events.
public class DOMSubtreeModifiedEventListener : nsIDOMEventListener
{
... // Implement HandleEvent
}
Then something like (maybe in DocumentCompleted event handler):
_domSubtreeModifiedEventListener = new DOMSubtreeModifiedEventListener(this);
var target = Xpcom.QueryInterface<nsIDOMEventTarget>(body);
using (nsAString subtreeModified = new nsAString("DOMSubtreeModified"))
target.AddEventListener(subtreeModified, _domSubtreeModifiedEventListener, true, false, 0);
Option 3. - Use Idle + Check.
Add an winforms Application.idle event handler - and examine the document, to know when its ready.
Option 4. - Inject a javascript callback.
(As you have already mentioned) - This example is waiting until after a resize is done.
basically inject: "<body onresize=fireResizedEventAfterDelay()>" : then inject something like this:
string fireResizedEventAfterDelayScript = "<script>\n" +
"var resizeListner;" +
"var msDelay = 20;" +
"function fireResizedEventAfterDelay() {" +
"clearTimeout(resizeListner);" +
"resizeListner = setTimeout(function() { document.dispatchEvent (new MessageEvent('resized')); }, msDelay);" +
"}\n" +
"</script>\n";
Then in the C#:
browser.AddMessageEventListener("resized", (s) => runafterImDone())

Using Awesomium in orchard cms module

I am trying to write an orchard module which will get some HTML pages from predefined urls (javascript executed) to extract some html from it. i ended up by Awesomium browser engine.as Awesomium intended to be run in a single thread , i have created a SingletonDependency and a thread in it which will continue running while the module is alive.thus far any thing is not a problem and the program works fine in local machine.but when i deploy the module on the server the Awesomium documentReady event never gets fired.i have tested the library (Awesomium) in a test ASP.NET mvc app and it works as expected.
following are some code snippet from what i have done so far :
public class DownloadService : IDownloadService
{
private Thread downloaderThread;
const int MillisecondsTimeout = 30000;
private List<DownloadQueueItem> downloadQueue = new System.Collections.Generic.List<DownloadQueueItem>();
private void DoDownloadTask()
{
while (true)
{
if (downloadQueue.Any())
{
var itemToDownload = downloadQueue.FirstOrDefault(qi => qi.IsCompleted == false);
if (itemToDownload != null)
{
var webPreferences = WebPreferences.Default;
using (var session = WebCore.CreateWebSession(webPreferences))
{
using (var view = WebCore.CreateWebView(800, 600, session))
{
view.Source = new Uri(itemToDownload.Url, UriKind.Absolute);
bool finishedLoading = false;
bool isReady = false;
view.LoadingFrameComplete += (s, e) =>
{
if (e.IsMainFrame)
finishedLoading = true;
};
view.DocumentReady += (s, e) =>
{
isReady = true;
};
int timeSpent = 0;
while (!finishedLoading || !isReady)
{
Thread.Sleep(500);
WebCore.Update();
timeSpent += 500;
if (timeSpent > MillisecondsTimeout)
{
isReady = true;
finishedLoading = true;
itemToDownload.Result = "";
}
}
if (timeSpent < MillisecondsTimeout)
{
itemToDownload.Result = view.ExecuteJavascriptWithResult("document.getElementsByTagName('html')[0].outerHTML");
}
itemToDownload.IsCompleted = true;
}
}
}
}
downloadQueue = downloadQueue.Where(qi => !qi.IsProcessed).ToList();
Thread.Sleep(500);
}
}
}
Update
The scenario : I am trying to setup an infinite loop to check for update within an specific interval and download some HTML pages and search for special tags in them with specific selectors (by means of CsQuery in my case) and then initialize parts and fields of ContentItem (with regard to Bindings rules i have defined earlier) with their contents.let me mention again "Everything works fine in local machine".
Here is a working example of Awesomium engine in an ASP.NET MVC application from this SO question.My code is something like the example provided.in that example IHttpModule is used to do download task but in my case a class drived from ISingletonDependency.
Update 2
After hours of debugging i finally figure out what is the problem.problem is Awesomium.Core dll has dependencies which asp.net server copies them to following directory:
"C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\Temporary ASP.NET Files\\root\\8b7dd3d5\\5e9b4019\\assembly\\dl3\\788672a2\\001db261_934ccf01"
in orchard dependencies copied to Dependencies folder automatically but not any type of files (only dll(s) i think).in my scenario there is a file called awesomium_process which orchard cannot copy this file to dependencies folder.and this is the exact ptoblem.
Update 3
I Added these files manually and everything is working like a charm.but still i am after a solution to copy them programmatically.

How to clear/change the query string in aspx page?

I have two pages
1. a.aspx and
2. b.aspx
I pass query string from "b.aspx?save=success" to a.aspx.
In Page Load of a.aspx I have the following code:
Page_Load()
{
if(!Postback)
{
if (Request.QueryString["save"] != null)
{
noDataFound.InnerHtml = "operation success";
}
}
}
Problem: On load of a.aspx page I get the message "operation success". This is Ok.But When I refresh the page again I get the same message as "operation success". How not to display again the same message on page refresh(pressing F5or reload).
function invokeMeMaster() {
var isPostBack = <%= Page.IsPostBack ? "true" : "false" %> ;
if (!isPostBack) {
/* START */
var query = getQueryParams(document.location.search);
var p = query.save;
if (sessionStorage.hits) {
sessionStorage.hits = Number(sessionStorage.hits) + 1;
} else {
sessionStorage.hits = 1;
}
if (p == "success" && (sessionStorage.hits) % 2 == 0) {
document.getElementById("<%=noDataFound.ClientID %>").innerText = "Testing...........";
}
function getQueryParams(qs) {
qs = qs.split("+").join(" ");
var params = {}, tokens,
re = /[?&]?([^=]+)=([^&]*)/g;
while (tokens = re.exec(qs)) {
params[decodeURIComponent(tokens[1])] = decodeURIComponent(tokens[2]);
}
return params;
}
/* END */
} else {
document.getElementById("<%=noDataFound.ClientID %>").innerText = "";
}
}
window.onload = function () {
invokeMeMaster();
};
untested solution (Keeping F5 or Reload of Page in mind), may be you have do something like below:
if(!IsPostBack)
{
if (Request.QueryString["save"] != null && Session["CheckSuccess"] == null)
{
noDataFound.InnerHtml = "operation success";
Session["CheckSuccess"] = "true";
}
else
noDataFound.InnerHtml = string.Empty;
}
The best I can think of is using the IsPostback property to check that.
if (!this.IsPostback)
{
// first try
if (Request.QueryString["save"] != null)
{noDataFound.InnerHtml = "operation success";}
}
NOTE: IsPostback is not set on refresh, only if clicking a button or something alike triggers the ASP.NET postback action.
The other thing you could do is set a Session variable then the 'operation succesful' must be shown (probably you determine this in another Page.
// other page
Session["showSaveMessage"] = true;
// this page
if (Session["showSaveMessage"] == true)
{
// show message
Session["showSaveMessage"] = false;
}
A third option is to move this client side. Create a javascript action on load of the page. When a specific part is added to the query string (#showmessage), you can catch that and show the message (How to get the value from the GET parameters?).
Then redirect to the parameterless version (#) by setting the url to the stripped version. Set window.location.href or window.location.search for that (this won't cause a call to the webserver, since it is all client side).
This circumvents the drawbacks of the first solution, but introduces more code client side. Luckily, ASP.NET MVC has some mechanisms for this. Unfortunately ASP.NET Web Forms doesn't have those.

How to pass a variable from one class to another in a URI?

This is what I have so far which calls a GetCoordinates method and navigates to the map on a button click. I'm wondering though how I would pass over the coordinate data.
Does anyone know how I could pass the MyGeoPosition variable of type GeoPosition to the OnNavigatedTo method of my map class? I know how to call a method from another class but not how to pass data such as a variable.
private async Task GetCoordinates(string name = "My Car")
{
await Task.Run(async () =>
{
// Get the phone's current location.
Geolocator MyGeolocator = new Geolocator();
//need to pass the below variable containing coordinate data..
MyGeolocator.DesiredAccuracyInMeters = 5;
Geoposition MyGeoPosition = null;
try
{
MyGeoPosition = await MyGeolocator.GetGeopositionAsync(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10));
}
catch (UnauthorizedAccessException)
{
MessageBox.Show("Location is disabled in phone settings or capabilities are not checked.");
}
catch (Exception ex)
{
// Something else happened while acquiring the location.
MessageBox.Show(ex.Message);
}
});
}
//sets location of parking space using the GetCoordinates method
//opens map
private async void setLocationBtn_Click(object sender, RoutedEventArgs e)
{
await this.GetCoordinates();
NavigationService.Navigate(new Uri("/Maps.xaml", UriKind.Relative));
}
Try something like this
FirstPage
this.NavigationService.Navigate(new Uri(string.Format("LocationView.xaml?GeoX={0}&GeoY={1}", GeoX, GeoY), UriKind.Relative));
secondPage
if (NavigationContext.QueryString.ContainsKey("GeoX") && NavigationContext.QueryString.ContainsKey("GeoY"))
{
double GeoX =Convert.ToDouble(NavigationContext.QueryString["GeoX"].ToString());
double GeoY = Convert.ToDouble(NavigationContext.QueryString["GeoY"].ToString());
....
}
You could use PhoneApplicationservice to pass data between pages in windows phone application.
Here is good example about PhoneApplicationservice. Here is a short example how PhoneApplicationService works, may this will help you.
private async void setLocationBtn_Click(object sender, RoutedEventArgs e)
{
await this.GetCoordinates();
PhoneApplicationService.Current.State["Data"] = your data;
NavigationService.Navigate(new Uri("/Maps.xaml", UriKind.Relative));
}
//On Second page
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var data =PhoneApplicationService.Current.State["Data"] as Cast your type
PhoneApplicationService.Current.State.Remove("Data");
}
You can pass data by four ways which is clearly explained in following post
http://nishantcop.blogspot.in/2011/08/passing-data-between-pages-in-windows.html
Found another way in my searching for another issue:
http://msdn.microsoft.com/en-us/library/windows/apps/hh771188.aspx
Scroll down to: Passing information between pages
Its a lot simpler than my solution above, but my solution has other requirements hence why I chose that one, but for your needs, this is a better way.
I had a similar issue where I was passing user credentials between classes and I decided to use IsolatedStorageSettings class. But I have read that Windows will be depreciating this class in the future as it moves to merge Windows and Windows Phone code.
SO, this is the class I believe Microsoft want you to use so that you dont get stuck with a depreciated class in the future, and its called Windows.storage.
Hope this helps.
My case as said if for passing username and password along with if the user was a premium user and if when the app starts if they are already logged on. It would then re-log the user on automatically.
Here I create the storage in MainPage class
IsolatedStorageSettings myUserSettings = IsolatedStorageSettings.ApplicationSettings;
Here is the MainPage class method:
private void GetUserData()
{
// System.Diagnostics.Debug.WriteLine("Grabbing Data");
if (IsolatedStorageSettings.ApplicationSettings.Contains("userLoggedIn"))
{
string isLoggedIn = IsolatedStorageSettings.ApplicationSettings["userLoggedIn"] as string;
if (isLoggedIn.EndsWith("rue"))
isLoggedOn = true;
else
isLoggedOn = false;
// System.Diagnostics.Debug.WriteLine("log in data " + isLoggedIn + " " + isLoggedOn);
}
else
{
myUserSettings.Add("userLoggedIn", "false");
isLoggedOn = false;
}
if (IsolatedStorageSettings.ApplicationSettings.Contains("fullAccess"))
{
string hasFullAccess = IsolatedStorageSettings.ApplicationSettings["fullAccess"] as string;
if (hasFullAccess.EndsWith("rue"))
fullAccess = true;
else
fullAccess = false;
}
else
{
myUserSettings.Add("fullAccess", "false");
fullAccess = false;
}
if (IsolatedStorageSettings.ApplicationSettings.Contains("username"))
{
username = IsolatedStorageSettings.ApplicationSettings["username"] as string;
}
else
{
myUserSettings.Add("username", "");
username = "me";
}
if (IsolatedStorageSettings.ApplicationSettings.Contains("password"))
{
password = IsolatedStorageSettings.ApplicationSettings["password"] as string;
}
else
{
myUserSettings.Add("password", "");
password = "v";
}
myUserSettings.Save();
}
Now in my Login Class I have to create the storage variable again
IsolatedStorageSettings myUserSettings = IsolatedStorageSettings.ApplicationSettings;
And now once I verfied the user I write the relevant information to the storage file: (parts of method missing as irrelevant)
// Here I have just finished using JSON to extra info from a JSON response
if (success.EndsWith("rue"))
{
if (!myUserSettings.Contains("userLoggedIn"))
{
myUserSettings.Add("userLoggedIn", success);
}
else
{
myUserSettings["userLoggedIn"] = success;
}
if (!myUserSettings.Contains("username"))
{
myUserSettings.Add("username", username);
}
else
{
myUserSettings["username"] = username;
}
if (!myUserSettings.Contains("password"))
{
myUserSettings.Add("password", password);
}
else
{
myUserSettings["password"] = password;
}
if (!myUserSettings.Contains("fullAccess"))
{
myUserSettings.Add("fullAccess", fullAccess);
}
else
{
myUserSettings["fullAccess"] = fullAccess;
}
myUserSettings.Save();
and if something does not work, check that you did save the file as follows:
myUserSettings.Save();
Hope you can make sense of my example but please refer to the doco from Microsoft. This link shows a simple example I used to solve my requirements.

Detect when the selenium webdriver changes url in browser

I need to detect when the url in the browser changes whether it was because of click on a link, a form post or I changed the url in code.
I need it because I'm creating an object to represent the page and I need to dispose recreate it when the url changes.
Here is what I have tried so far:
private string _pageUrl;
protected T _page = default(T);
protected T Page
{
get
{
if (_page == null || UrlHasChanged())
{
_page = GetPage<T>();
SetPageUrl();
}
return _page;
}
}
private bool UrlHasChanged()
{
var driver = Session.GetDriver();
return driver.Url != _pageUrl;
}
public void SetPageUrl()
{
_pageUrl = Session.GetDriver().Url;
}
This works in most cases but it fails when the test goes forward a page and then goes back to the initial page.
I need a way to detect when the url changes so I can reset the _page field.
I'm a Java developer, so, I search in the C# documentation what looked similar to the Java API. I think you should use the EventFiringWebDriver :
EventFiringWebDriver firingDriver = new EventFiringWebDriver(driver);
firingDriver.NavigatingBack += new EventHandler<WebDriverNavigationEventArgs>(...);
firingDriver.NavigatedBack += new EventHandler<WebDriverNavigationEventArgs>(...);
firingDriver.NavigatingForward += new EventHandler<WebDriverNavigationEventArgs>(...);
firingDriver.NavigatedForward += new EventHandler<WebDriverNavigationEventArgs>(...);
I looked at the unit tests and I found this one that may be useful for you :
http://selenium.googlecode.com/svn/trunk/dotnet/test/WebDriver.Support.Tests/Events/EventFiringWebDriverTest.cs

Categories