Adding an "anti-robot" enhancement to the CreateUserWizard - asp.net

I want to add an "anti-robot" question to the CreateUserWizard as a more accessible alternative to a Captcha control. I'm fairly new to asp and finding that I'm a bit stuck in a WinForms mindset. However, I have come up with something that appears to work.
Markup:
<asp:CreateUserWizard ID="CreateUserWizard1" runat="server">
.
.
<tr>
<td align="right">
<asp:Label ID="AntiRobotQuestion" runat="server" AssociatedControlID="AntiRobotAnswer">
Question:
</asp:Label>
</td>
<td>
<asp:TextBox ID="AntiRobotAnswer" runat="server"></asp:TextBox>
<asp:RequiredFieldValidator ID="AntiRobotAnswerRequired" runat="server" ControlToValidate="AntiRobotAnswer" ErrorMessage="Answer is required." ToolTip="Answer is required." ValidationGroup="CreateUserWizard1">
</asp:RequiredFieldValidator>
</td>
</tr>
<tr>
<td align="center" colspan="2" style="color:Red;">
<asp:Literal ID="CustomErrorMessage" runat="server" Visible="False" EnableViewState="False"></asp:Literal>
</td>
</tr>
.
.
</asp:CreateUserWizard>
Code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) {
//Set up the Anti-Robot Question and Answer
Label robotQuestion = (Label)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("AntiRobotQuestion");
//Simulate randomly selecting a question and answer from a database table...
robotQuestion.Text = "What is the capital of France";
Session["AntiRobotAnswer"] = "Paris";
}
}
protected void CreateUserWizard1_CreatingUser(object sender, LoginCancelEventArgs e)
{
//Check the anti-robot Q & A
TextBox robotAnswer = (TextBox)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("AntiRobotAnswer");
if (robotAnswer.Text != (string)Session["AntiRobotAnswer"])
{
Literal errorMessage = (Literal)CreateUserWizard1.CreateUserStep.ContentTemplateContainer.FindControl("CustomErrorMessage");
errorMessage.Text = "Wrong answer! Are you a robot?";
errorMessage.Visible = true;
e.Cancel = true;
}
}
Is this an acceptable way to code this? Two things in particular look a bit "untidy" to me:
The use of FindControl to pull out references to controls in the markup.
Storing the expected answer in a session variable. (How secure is it?)
EDIT (2012-01-23)
Some valid design alternatives have been given. However, I have a valid reason to use this question and answer technique (possibly in addition to the honeypot idea). For example, a question relevant to the subject of a forum can help to prevent human spammers as well as bots. The question is: is the code outlined above an acceptable way to do this? Coming from a WinForms background, it looks a bit clunky to me - but maybe that's what asp is supposed to look like.

As I say, I do not like the idea of you to ask for Paris.
The simplest way is to use a non visible field and see if a bot fill it with data, the honeypot idea http://haacked.com/archive/2007/09/11/honeypot-captcha.aspx
also you can use the NoBot from asp.net toolkit
http://www.asp.net/ajaxLibrary/AjaxControlToolkitSampleSite/NoBot/NoBot.aspx
There are many other ideas on this SO article Practical non-image based CAPTCHA approaches?

Related

ID of items in ASP.NET ListView is automatically extended

I have programmed a simple blog page - http://www.3don.net.br/Blog.aspx (another language, here only to show the structure). I want to use hashtags for pointing to the topics. For example, http://www.3don.net.br/Blog.aspx#19/04/16 should scroll the page to the topic created at 19/04/16.
However, I cannot get it!
The topics of the blog are ItemTemplates of a ListView control. When I define an ID="lblDatum" for the label control of the data of each topic (which is the first control of each topic), then this ID is modified by the NET machine to
id="ctl00_ContentPlaceHolder_lstBlog_ctrl0_ctl01_lblDatum" (you can see it in the source code of the page for the second topic, for example).
So, if I access in the browser www.3don.net.br/Blog.aspx#ctl00_ContentPlaceHolder_lstBlog_ctrl0_ctl01_lblDatum
the page indeed will scroll correctly. I can also programmatically change the ID for each topic differently and it still works for each topic.
However, the hashtag-name "ctl00_ContentPlaceHolder_lstBlog_ctrl0_ctl01_lblDatum" is not nice! Is there a possibility to suppress the ctl00_ContentPlaceHolder_lstBlog_ctrl0_ctl01_-part?
Or another idea for getting it?
Use ClientIdMode="Static" in your ListItem, which removes the autoGenerate ID, you got ID="lblDatum" on client.
ok, I added the clientIDMode="static" property to the Label control of the data:
<table id="table_intern" runat="server" >
<tr id="tr_intern" runat="server">
<td id="td_intern" runat="server">
<asp:Label ID="lblDatum" runat="server" Text='<%# Eval("data") %>' CssClass="rotfettschrift" clientidmode="static"/>
</td>
</tr>
<tr> ... </tr>
<tr> ... </tr>
</table>
... and, in code-behind, set the ID of this control of each topic igual of the text property of this control (the data):
lbl = CType(e.Item.FindControl("lblDatum"), Label)
lbl.ID = lbl.Text
However, the HTML-output is:
<tbody>
<tr id="ctl00_ContentPlaceHolder_lstBlog_ctrl0_ctl00_tr_intern">
<td id="ctl00_ContentPlaceHolder_lstBlog_ctrl0_ctl00_td_intern">
<span id="ctl00_ContentPlaceHolder_lstBlog_ctrl0_ctl00_19/05/16" class="rotfettschrift" clientidmode="static">19/05/16</span>
</td>
</tr>
<tr> ... </tr>
<tr> ... </tr>
</tbody>
Why is the ID of the span element ctl00_ContentPlaceHolder_lstBlog_ctrl0_ctl00_19/05/16 and not just 19/05/16 ???
Ok, i tested here, using reapeater, but with any other control works. You must set the id individually to each control, to make unique.
*I redid, using date.
MARKUP:
<table id="table_intern">
<asp:Repeater runat="server" ID="repeater" OnItemDataBound="repeater_ItemDataBound">
<ItemTemplate>
<tr>
<td>
<asp:Label Text="" runat="server" ID="label" />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
BACKEND:
protected void Page_Load(object sender, EventArgs e)
{
var list = new List<string>();
for (int i = 0; i < 10; i++)
{
list.Add(string.Format("Item_{0}", i.ToString().PadLeft(2, '0')));
}
repeater.DataSource = list;
repeater.DataBind();
}
protected void repeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
var label = (Label)e.Item.FindControl("label");
var item = (DateTime)e.Item.DataItem;
label.ID = item.ToShortDateString();
label.Text = item.ToShortDateString();
label.ClientIDMode = ClientIDMode.Static;
}
}
RESULT:
<table id="table_intern">
<tbody><tr>
<td>
<span id="23/05/2016">23/05/2016</span>
</td>
</tr>
<tr>
<td>
<span id="24/05/2016">24/05/2016</span>
</td>
</tr>...
Sorry, for bad formatting, i will improve my answers if necessary.
Daniel,
thank you for dedicating your time.
You are defining the ClientIDMode property of the Label control in the code behind, ok. I do so, but I see that it does not work here:
screenshot
When I go with the mouse pointer over the ClientIDMode at the left it advices: "'ClientIDMode' is not a member of 'Label'"
and over the ClientIDMode at the right, it pops "'ClientIDMode' is not declared. It may be inaccessible due to its protection level."
Is there something undefined in my system?
Ok, i understand you using Framework2.0 in your project. Can you change Framework to 4.0 or upper? Otherwise you need use a .js approach, to scroll like wish.
ClientIdMode is from .net 4.0, how you have said .net4.6 installed, I assumed that the project was also 4.6.
IF, you can't change the version, you need use something like jquery to scroll. OR maybe a more hard solution creating a item in your itemTemplate like:
<span id='<%# Eval("data") %>'></span>
I don't have tested this solution, later I'll be testing;
Daniel,
I always suspected that, but the
"About Visual Studio" window tells another story.
Adicionally, in the registry I can find the v4/Full key and the Version information (at the right side) confirms the (installed and activated?) version 4.6.01055.
???
Or is framework v4 installed but not "activated"? Does this exist? Where can I see this?

Required Validator Error Message Display

I'm having an issue with a standard ASP.NET portal.
Suppose that has a TextBox and a RequiredFieldValidator. I want the validator only shows the error message when I input a wrong format text.
However the current issue is even I haven't input nothing, it displays the message. After I type the correct text, it disappeared then.
My code:
<tr>
<td>
*First Name:
</td>
<td>
<asp:TextBox runat="server" ID="FirstName" MaxLength="50" />
<asp:RequiredFieldValidator runat="server" ID="RequiredFieldValidator1" ControlToValidate="FirstName"
ErrorMessage="First Name is required." EnableClientScript="True" />
</td>
</tr>
<tr>
The code behind is something like
protected void Page_Load(object sender, EventArgs e)
{
Page.Validate();
if (!Page.IsPostBack)
{
// blah
}
}
Thanks.
That's what the RequiredFieldValidator is supposed to do. Use another type of validator such as a RegularExpressionValidator or a CustomValidator.
Also how do you define the correct format for a person's name?
So you need to use RegularExpressionValidator not RequiredFieldValidator.Let us say you want your textbox to accept only alphanumeric characters, you may set the format this way:
<asp:RegularExpressionValidator id="val1" runat="server" ValidationExpression="[a-zA-Z0-9]+" ControlToValidate="TextBox1"/>

Repeater binding # serverside

I have a repeater in my aspx page. The datasource for this will change for different types of call hence I cannot do
<ItemTemplate>
<tr >
<td bgcolor="" valign="top">
<%#DataBinder.Eval(Container.DataItem, "Id")%>
</td>
...
as the dataItem will be different. Which is the best way to do it server side?
Thanks,
Personally, the best way will be create interface and write adapters for every type. In repeater you gonna be work only with this interface.
You can do
<asp:Label ID="lable1" runat="server" ondatabinding="label1_ondatabinding" />
and manually fill data in the event handler.
protected void label1_ondatabinding(object sender, EventArgs e)
{
((Label)sender).Text = ...
}

UpdatePanel, Repeater, DataBinding Problem

In a user control, I've got a Repeater inside of an UpdatePanel (which id displayed inside of a ModalPopupExtender. The Repeater is databound using an array list of MyDTO objects. There are two buttons for each Item in the list. Upon binding the ImageURL and CommandArgument are set.
This code works fine the first time around but the CommandArgument is wrong thereafter. It seems like the display is updated correctly but the DTO isn't and the CommandArgument sent is the one that has just been removed.
Can anybody spot any problems with the code?
Edit : I've just added a CollapsiblePanelExtender to the code. When I now delete an item and expand the panel, the item that was previously deleted (and gone from the display) has come back. It seems that the Repeater hasn't been rebuilt correctly under the bonnet.
ASCX
<asp:UpdatePanel ID="ViewDataDetail" runat="server" ChildrenAsTriggers="true">
<Triggers>
<asp:PostBackTrigger ControlID="ViewDataCloseButton" />
<asp:AsyncPostBackTrigger ControlID="DataRepeater" />
</Triggers>
<ContentTemplate>
<table width="100%" id="DataResults">
<asp:Repeater ID="DataRepeater" runat="server" OnItemCommand="DataRepeater_ItemCommand" OnItemDataBound="DataRepeater_ItemDataBound">
<HeaderTemplate>
<tr>
<th><b>Name</b></th>
<th><b> </b></th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<b><%#((MyDTO)Container.DataItem).Name%></b>
</td>
<td>
<asp:ImageButton CausesValidation="false" ID="DeleteData" CommandName="Delete" runat="server" />
<asp:ImageButton CausesValidation="false" ID="RunData" CommandName="Run" runat="server" />
</td>
</tr>
<tr>
<td colspan="2">
<table>
<tr>
<td>Description : </td>
<td><%#((MyDTO)Container.DataItem).Description%></td>
</tr>
<tr>
<td>Search Text : </td>
<td><%#((MyDTO)Container.DataItem).Text%></td>
</tr>
</table>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
</ContentTemplate>
</asp:UpdatePanel>
Code-Behind
public DeleteData DeleteDataDelegate;
public RetrieveData PopulateDataDelegate;
public delegate ArrayList RetrieveData();
public delegate void DeleteData(String sData);
protected void Page_Load(object sender, EventArgs e)
{
//load the initial data..
if (!Page.IsPostBack)
{
if (PopulateDataDelegate != null)
{
this.DataRepeater.DataSource = this.PopulateDataDelegate();
this.DataRepeater.DataBind();
}
}
}
protected void DataRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "Delete")
{
if (DeleteDataDelegate != null)
{
DeleteDataDelegate((String)e.CommandArgument);
BindDataToRepeater();
}
}
else if (e.CommandName == "Run")
{
String sRunning = (String)e.CommandArgument;
this.ViewDataModalPopupExtender.Hide();
}
}
protected void DataRepeater_ItemDataBound(object source, RepeaterItemEventArgs e)
{
RepeaterItem item = e.Item;
if (item != null && item.DataItem != null)
{
MyDTO oQuery = (MyDTO)item.DataItem;
ImageButton oDeleteControl = (ImageButton) item.FindControl("DeleteData");
ImageButton oRunControl = (ImageButton)item.FindControl("RunData");
if (oDeleteControl != null && oRunControl !=null)
{
oRunControl.ImageUrl = "button_expand.gif";
oRunControl.CommandArgument = "MyID";
if (oQuery !=null)
{
//do something
}
oDeleteControl.ImageUrl = "btn_remove.gif";
oDeleteControl.CommandArgument = "MyID";
}
}
}
public void BindDataToRepeater()
{
this.DataRepeater.DataSource = this.PopulateDataDelegate();
this.DataRepeater.DataBind();
}
public void ShowModal(object sender, EventArgs e)
{
BindDataToRepeater();
this.ViewDataModalPopupExtender.Show();
}
Thanks for reminding me why I stopped using ASP.NET controls. This is the exact type of nightmare that has made too many projects go way over budget and schedule.
My advise to you is to think of the simplest way to implement this. You can try to bend over backwards in order to get this to work the ASP.NET way or take the shortest route.
All you're doing is generating HTML, it should never be that difficult.
The most likely cause of your problem is that the ViewState is stored in the page which doesn't get updated on a partial postback. So with every change in the update panel you'll postback the initial viewstate of the page.
Try replacing the repeater with a simple for-loop (and ignore the people who start complaining you shouldn't mix markup and code). Replace your databinding statements with <%= %>.
That eliminates the view state all together and should remove any removed row from re-appearing.
After many days of messing around with this I've not found a proper fix for the problem but do have a workable work-around.
The CollapsiblePanelExtender is set to NOT postback automatically which fixes the issue of the deleted data re-appearing when the extender is opened. The other issue, I believe, is related.
It seems that the ViewState for the Repeater is out of sync with the data. e.CommandArgument is not always correct and seems to reference the previous data. I made an attempt to fix it by storing the ArrayList of MyDTO objects in the ViewState when opening the Modal dialog and using the ID retrieved from e.Item.ItemIndex to find the correct element to delete. This didn't work correctly, the ArrayList pulled out of the ViewState was out of sync.
Storing the ArrayList in the session makes it all work which leads me to believe that I'm doing something fundamentally wrong or there is a subtle bug in the version of the toolkit that i'm using (we're still on VS2005 so are stuck with an older version of the toolkit)
Apologies if this makes no sense, contact me if you want clarification on anything.
try using
((IDataItemContainer)Container).DataItem
instead of "Container.DataItem"
It worked for me.

FormView not passing a value contained within "runat=server" row

I have the following code in the EditItemTemplate of my FormView:
<tr id="primaryGroupRow" runat="server">
<td class="Fieldname">Primary Group:</td>
<td><asp:DropDownList ID="iPrimaryGroupDropDownList" runat="server" DataSourceID="GroupDataSource" CssClass="PageText"
DataTextField="sGroupName" DataValueField="iGroupID" SelectedValue='<%# Bind("iPrimaryGroup") %>'></asp:DropDownList></td>
</tr>
If I remove the runat="server" for the table row, then the iPrimaryGroup field is bound 100% and passed to the business logic layer properly. However in the case of the code above, it is passed with a value of zero.
Can anyone tell me why this is or how to get around it? This is in a control that needs to hide this table row, based on whether or not an administrator or a regular user is editing it. ie: some fields are admin writeable only and I'd like to hide the controls from the view if the user isn't an admin.
If security is a concern perhaps this might work better
<tr>
<td colspan='2'>
<asp:panel runat='server' visible='<%= IsUserAdmin %>'>
<table>
<tr>
<td class="Fieldname">Primary Group:</td>
<td><asp:DropDownList ID="iPrimaryGroupDropDownList" runat="server" DataSourceID="GroupDataSource" CssClass="PageText" DataTextField="sGroupName" DataValueField="iGroupID" SelectedValue='<%# Bind("iPrimaryGroup") %>'></asp:DropDownList>
</td>
</tr>
</table>
</asp:panel>
</td>
If I'm not mistaken any markup within the panel will not be rendered if visible=false
Have a shot at this:
Remove the runat=server attribute
Define a css class
.hidden{ display:hidden;}
Then set the class attribute based on whether or not the user is an admin
<tr class='<%= if(IsUserAdmin) "" else "hidden" %>' >
It appears that this functionality is by design, although that's not exactly confirmed.
http://weblogs.asp.net/rajbk/archive/2009/08/03/formview-binding-gotcha.aspx
When using the FormView object, if you have a nested control, then two-way databinding isn't going to work properly. You can access the controls in code, and you can get at the data, but it's just not going to automatically update the value in the back end of your Business Logic Layer(BLL) like it's supposed to.
Fortunately, there's a workaround. The way to get it working is to create an event for ItemUpdating. It will have a signature like this:
protected void frmProfile_ItemUpdating(object sender, FormViewUpdateEventArgs e)
This gives you access to the FormViewUpdateEventArgs, which in turn allows you to make changes to the ObjectDataSource values while they are in flight and before they hit your BLL code, as follows:
protected void frmProfile_ItemUpdating(object sender, FormViewUpdateEventArgs e)
{
if (frmProfile.FindControl("iPrimaryGroupDropDownList") != null)
{
DropDownList iPrimaryGroupDropDownList = ((DropDownList)frmProfile.FindControl("iPrimaryGroupDropDownList"));
e.NewValues["iPrimaryGroup"] = iPrimaryGroupDropDownList.Text;
}
}

Resources