Tutto .NET

Join us on IRCnet server, channel #asp.net if you want to ask anything asp.net related or simply hang out :-)


I have a new home now. In the coming days, i shall be moving all my content to http://weblogs.asp.net/alessandro

Thursday, September 6, 2007

One bit masks for Access Control (setting permissions) in your c# applications.

In my previous post, i wrote about a brushup on four binary bitwise operators. In this post, i would like to cover how we can make use of binary bitwise operators to create 1 bit masks, working at the bit level, saving us lots of memory, while also being easy to read and maintain our code. First, lets see how the bitwise operators we covered previously can be used, specifially we shall be using :

  1. Binary OR(|) operator (our bucket for storing permissions)
  2. Binary AND(&) operator (our tester, to tell us what if an item is in the bucket)

The binary (|) operator

 If either bit stacked ontop of one another are 1 or both are 1, then the result is 1, otherwise the result is 0. So 1 on 1 = 1, 1 on 0 = 1 but 0 on 0 = 0.

128 64 32 16 8 4 2 1 Calculation Result
0 0 0 0 0 1 1 1 (4+2+1) 7
0 0 0 0 1 0 0 1 (8+1) 9
0 0 0 0 1 1 1 1 (8+4+2+1) 15

 

As you can see from the above, binary (|) is being performed on each pair of corresponding bits. If either bit stacked ontop of one another are 1 or both are 1, then the result is 1, otherwise the result is 0. So 1 on 1 = 1, 1 on 0 = 1 but 0 on 0 = 0.

In the above example 7 | 9 = 15. How is this useful with our bitmasks ? Not very useful indeed. The result 15 is not making sense as a candidate for our one bit mask but thats because we OR'ed two numbers that are not made of power of 2^ ; Had we used two numbers with the power of 2^, we could use the binary OR(|) operator to add masks.

Now, lets see what happens when we use numbers made up of power of 2^ like below 1 | 8;

128 64 32 16 8 4 2 1 Calculation Result
0 0 0 0 0 0 0 1 (1) 1
0 0 0 0 1 0 0 0 (1) 8
0 0 0 0 1 0 0 1 (8+1) 9

As you can note from the above, 1 | 8 is giving us 9. It's a simple addition, 0001 | 1000 = 1001. So in short, when we OR'd, we can actually think of this simply asif we were adding items to a bucket. In our case we added 1,8 to the bucket. we can add more items. Eg : 1|4|8|16...

128 64 32 16 8 4 2 1 Calculation Result
0 0 0 0 0 0 0 1 (1) 1
0 0 0 0 0 1 0 0 (4) 4
0 0 0 0 1 0 0 0 (8) 8
0 0 0 1 0 0 0 0 (16) 16
0 0 0 1 1 1 0 1 (16+8+4+1) 29

 

See, we just got a result of 11101(16+8+4+1) ; Our result 11101 contains all 4 items added(or OR'd) to our bucket. In a real application, such as the bitmask we are writing, this can be used to add 1 or more permissions. First lets create a simple permissions table in our database mapping users to permissions, a single int field is all we need.

UserID Permission
1 3
2 5
3 7

The remaining of the job can be done using bitmasks in our front end. But first, why are one bit masks composed of integers to the power of 2 ? Remember that when OR'ing, "If either bit stacked ontop of one another are 1 or both are 1, then the result is 1, otherwise 0". Well, that's pretty much why. Basically, when using integers to the power of 2, we get a sequential shift, look at the table below :

128 64 32 16 8 4 2 1 Calculation Result
0 0 0 0 0 0 0 1 (1) 1
0 0 0 0 0 0 1 0 (2) 2
0 0 0 0 0 1 0 0 (4) 4
0 0 0 0 1 0 0 0 (8) 8
0 0 0 1 0 0 0 0 (16) 16
0 0 1 0 0 0 0 0 (32) 32
0 1 0 0 0 0 0 0 (64) 64
1 0 0 0 0 0 0 0 (128) 128

In the table above, follow the 1'ones hilighted in red. As you can see, the 1's are in sequential shift order. Thanks to this pattern, we never end up with 1's stacked ontop of one another and our items get added, so if we OR'd integer 1 | 8 which are both to the power 2, we end up with a beautiful 9(1+8), while had we used integers that were'nt to the power of 2, eg 7 | 9 which gives us 15(8+4+2+1) <-- not exactly the bucket type functionality we want.(we get one's stacked ontop of one another and lose the bucket type functionality we are seeking). This is why, one bit masks are always composed of integers to the power of 2.

So, next, let's create a class that describes the available permissions we plan to have via public static fields in our AccessControl (static) class. It makes sense to make this a non instance class, since it's common to all of our users.

public static class AccessControl
{
public static int View = 1;// 0001
public static int Post = 2;// 0010
public static int Reply = 4;//0100

public static int ViewOnly = View;// 0001
public static int ViewAndPost = View | Post;// 0001 | 0010 = 0011(1+2=3)
public static int ViewAndReply = View | Reply;// 0001 | 0100 = 0101(4+1=5)
public static int ViewAndPostAndReply = View | Post | Reply;// 0001 | 0010 | 0100 = 0111(4+2+1=7)
}

Note above how we made use of the binary (|) OR operator ? As you can see, ViewOnly = 0001(1), but the other 3 permissions are OR'd, ViewAndPost = 0011(3), ViewAndReply = 0101(5), ViewAndPostAndReply = 0111(7).

Now, how do we check the bucket ? ViewAndPost contains two permissions assigned to it, View+Post, and ViewAndReply have two permissions assigned to it too, while ViewAndPostAndReply has 3 permissions assigned to it. Checking this can be easily done using the binary AND(&) Operator.

The binary (&) operator


As we have seen in my previous post, this operator is being performed on each pair of corresponding bits. The comparision is made based on both bits stacked ontop of one another. if both are one's, then we have a 1, anything else is a 0. so 1 on 1 = 1, 1 on 0 = 0, 0 on 0 = 0.















































1286432168421CalculationResult
00000111(7)7
00001001(9)9
00000001(1)1

In the above example 7 & 9 = 1. How can we use this in our bitmasks for checking permissions ? Lets watch another example, except this time lets binary AND(&) the result of a binary OR(|). So, we already know that 1|4|8|16 has given us the result (16+8+4+1), which is 29. Recall also that we called this a bucket of items, and that in this bucket we have the items 16, 8, 4 and 1. Now lets check this bucket for the item 8  by binary AND'ing 29 & 8 ?















































1286432168421CalculationResult
00011101(16+8+4+1)29
00001000(8)8
00001000(8)8

 


As you can see from the above table, 29 & 8 = 8. The return value of 8 indicates that 8 is indeed in our bucket. This means we can use the binary (&) operator to check our bucket for items. A simple code example below :


 



if ((AccessControl.ViewAndPostAndReply & AccessControl.View) == AccessControl.View)
// View is in the bucket
else
// blah


In a real scenario, we'd have done the following though. Firstly, note that all we need is a single (permission) property added in our User class as per our example below or anywhere else you'd want to map a permission :



public class MyUser
{
public MyUser()
{

}
private int permissionBucketValue;
public int PermissionBucket
{
get { return permissionBucketValue; }
set { permissionBucketValue = value; }
}

}


Then we need to fill it with the value from our database. Designing your db or retrieving a single value from your db is out of the scope of this post, however it's a nobrainer. So get the value and pass it to Permission property in your user class or where ever you need permissions. In your db, all you need is a single int field to hold the OR'd result. So if you planned on giving read access you would be storing the value of AccessControl.View, which is 1 in  your db. If you had planned to give the user ViewAndPost permission, then you will be storing the value of AccessControl.ViewAndPost, which is 3 in your db and so forth. This is just perfect.

Following is the static AccessControl class example we used earlier, with the addition of a new static method HasAccess. If you look at the HasAccess method a bit more closely you can see that the binary AND(&) operator is being used to test against a given permission :


public static class AccessControl
{
public static int View = 1;// 0001
public static int Post = 2;// 0010
public static int Reply = 4;//0100

public static int ViewOnly = View;// 0001
public static int ViewAndPost = View | Post;// 0001 | 0010 = 0011(1+2=3)
public static int ViewAndReply = View | Reply;// 0001 | 0100 = 0101(4+1=5)
public static int ViewAndPostAndReply = View | Post | Reply;// 0001 | 0010 | 0100 = 0111(4+2+1=7)

public static bool HasAccess(int bucket, Permission p)
{
switch (p)
{
case Permission.View:
return ((bucket & View) == View);
case Permission.Post:
return ((bucket & Post) == Post);
case Permission.Reply:
return ((bucket & Reply) == Reply);
}
return false;
}
}



And our Permissions enum. We want very readable code, so here it is :



public enum Permission
{
View,
Post,
Reply
}


See, how we check access permissions using the binary AND(&) operator in the static HasAccess method above. It's that simple. We just need to know if the permission we are checking against is valid for the user. So later in our code we can call HasAccess like this :


MyUser u = new MyUser();
//retrieve permissions from db and pass them to u.Permission
// later check access permission in your code anywhere eg :
if (AccessControl.HasAccess(u.Permission, Permission.Post))
// user has access
else
// user does not have acess


So really, what's wrong with using normal boolean flags and storing permissions in individual bit fields in your database Versus using bitmasks. I mean whats the big deal really ? Well, here are my favourite 3 issues with that :


  1. You end up creating an individual field in your database for each access permission you want. In this code sample here i have just 3 access permissions(to simplify). You could have 10, 20 and many more fields..
  2. Building onto problem 1 mentioned above, it's not easy to make a change. Say tomorrow you needed to add a new permission, then you end up adding a new field in your database ? and preparing your frontend for this new change.
  3. Your access control in your database is quite legible and easy to understand, since you just used individual fields, that are basically true or false fields for each permission set, so not the best security. Had you used one bit masks, then you'd have been storing only the OR'd result in a single field(which basically means nothing to who ever is reading your database table). Even though, i'll agree that if your db has been compromised, then knowing someone is able to view and edit your access control table is going to be the last of your worries.

Using one bit masks is really quite simple and neat and readable to me. Normally people prefer to use plenty of boolean fields, which is not a big deal and not that much of a memory consumption in a non systems level application. However, using bitmasks as i have shown here is much  much easier, very very readable, and less code. Less code in your front end, less code in your database, since all you need is a single int field to keep track of the permission set. And you save precious memory and time.

Using bitmasks you can avoid all the above issues and with style. It's up to you :-)

Wednesday, September 5, 2007

Bitwise operators in c# OR(|), XOR(^), AND(&), NOT(~)

This post is a brush up on some of the bitwise operators that we shall be using for creating bitmasks to set permissions(my next post will be on exactly this subject. Bitmasks).

The c# bitwise operators I shall be covering here and are of particular interest to me in my followup post are :

  1. binary OR(|) operator
  2. binary AND(&) operator
  3. XOR (^) operator
  4. Not (~) operator

 

Binary OR(|) operator

Quoting MSDN docs :

Binary | operators are predefined for the integral types and bool. For integral types, | computes the bitwise OR of its operands. For bool operands, | computes the logical OR of its operands; that is, the result is false if and only if both its operands are false.

Lets focus on the integral types(types that can be represented as numbers) and how the computation takes place.

protected void Page_Load(object sender, EventArgs e)
{
byte a = 7;
byte b = 9;
int orComputed = a | b;
Response.Write(
string.Format("<br />{0} | {1} OR computed :{2}", a, b, orComputed));
}


Output :
7 | 9 Result :15

To understand what just happened we need to look at bits and how each bit(which contains an integral type) is understood by the computer. The computer is going to understand this as binary data in the form of 0's and 1's eg. -->> 00101010(which represents the number 42), this is actaully much easier for a computer to handle since each bit(or integral type as in number), can be interpreted as either on or off signal.


0 = off
1 = on

To understand the bitwise operator with clarity and how it computes the result, lets first construct a base 2 table from right to left incrementing the exponent by 1 for each power. So, since we have 8 bits in a byte, we make a table that lists 8 elements 128, 64, 32, 16, 8, 4, 2, 1.





























































































1286432168421Result
000000011
000000102
000001004
000010008
0001000016
0010000032
0100000064
10000000128

If you will look at the above pattern more closely, we notice how the 1 keeps moving 1 digit to the left for every new value in binary. This is also known as a "bit shift".


Ok, now that we have our binary 8bit table with digits to the power of 2^ up to 128, so for a simple 7 | 9, which yielded the result 15, lets look at how it was computed by first creating the binary representation using our base 2 table and then computing the binary | operation, so :















































1286432168421CalculationResult
00000111(4+2+1)7
00001001(8+1)9
00001111(8+4+2+1)15

As you can see from the above, binary (|) is being performed on each pair of corresponding bits. If a 1 is present in the


either bit or in both bits, otherwise it's 0. Not clear ? Lets try that again. How did we get the result 1111 ? It's a simple comparision. If either bit stacked ontop of one another are 1 or both are 1, then the result is 1, otherwise the result is 0. So 1 on 1 = 1, 1 on 0 = 1 but 0 on 0 = 0.

Binary AND(&) operator


Quoting MSDN Docs :


Binary & operators are predefined for the integral types and bool. For integral types, & computes the logical bitwise AND of its operands. For bool operands, & computes the logical AND of its operands; that is, the result is true if and only if both its operands are true.


So, lets quickly test this :


protected void Page_Load(object sender, EventArgs e)
{
byte a = 7;
byte b = 9;
int orComputed = a & b;
Response.Write(
string.Format("<br />{0} & {1} Result :{2}", a, b, orComputed));
}


Output is :
7 & 9 Result :1

Lets convert to binary using our base 2 table, and dig deeper  :















































1286432168421CalculationResult
00000111(4+2+1)7
00001001(8+1)9
00000001(1)1

so, how did we get the binary result 0001(1) ?  binary (&) is being performed on each pair of corresponding bits. The comparision is made based on both bits stacked ontop of one another. if both are one's, then we have a 1, anything else is a 0. so 1 on 1 = 1, 1 on 0 = 0, 0 on 0 = 0.


Binary Xor (^) operator


Quoting MSDN Docs:



Binary ^ operators are predefined for the integral types and bool. For integral types, ^ computes the bitwise exclusive-OR of its operands. For bool operands, ^ computes the logical exclusive-or of its operands; that is, the result is true if and only if exactly one of its operands is true.


A simple c# example :


protected void Page_Load(object sender, EventArgs e)
{
sbyte a = 7;
sbyte b = 9;
int orComputed = a^b;//on a negative
Response.Write(string.Format("<br />{0} ^ {1} Result :{2}", a,b, orComputed.ToString()));
}


output :
7 ^ 9 Result :14

Now, how did binary (^) Xor'ing 7 ^ 9 produce 14 ? This is because binary (^) is being performed on each pair of corresponding bits. If we have two matching zero's or one's, then the result is 0, otherwise 1. SO, 1 on 1 = 0, 0 on 0 = 0,

1 on 0 = 1, 0 on 1 = 1. Lets look at an example in our base 2 table of 7 and 9 in binary format :















































1286432168421CalculationResult
00000111(4+2+1)7
00001001(8+1)9
00001110(8+4+2)14

 


Not (~) operator


Quoting MSDN Docs:



The ~ operator performs a bitwise complement operation on its operand, which has the effect of reversing each bit. Bitwise complement operators are predefined for int, uint, long, and ulong.


This is also a unary operator so we don't need to pass a value. Unary operators perform an operation on a single operand eg. ~n and not x~y.



Binary (~) operator is the most confusing one. It performs reversing of each bit, so reversing a positive can produce a negative value. Negative Values Use Two's Complement format and is quite tricky.


A simple example :



protected void Page_Load(object sender, EventArgs e)
{
sbyte a = 7;
int orComputed1 = ~-a;//on a negative
int orComputed2 = ~a;// on a positive
Response.Write(string.Format("<br />~-{0} Result :{1}", a, orComputed1));
Response.Write(
string.Format("<br />~+{0} Result :{1}", a, orComputed2));
}


output :
~-7 Result :6
~+7 Result :-8

As you can see, when the NOT(~) operator is applied to a positive number, the resulting value is a negative. This is because the value is reversed. Since this reversing yields a negative or positive depending on what value your reversing, use a signed type (a type that can store a negative value).

In the base table below, our binary number gets inverted. all 1's become 0 and 0 becomes 1.

A signed binary uses the left most bit to keep track of the sign(negative or positive).
In the c# compiler, negative values are represented internally in two's complement format. Two's complement can be obtained by negating each bit of the value, then adding 1. Performing two's complement twice generates the original value.

In the following base 2 table, one typical gotcha when inverting is to remember to start the inversion from the left of the first 1 value in our binary. So, say had we a binary representation of the digit 7(00000111), then we start inverting at the 2nd digit starting at the right, skipping the first(since it's 1)  and get 11111001. A last thing to also note is how in our calculation we added a negative -1 to our result instead of a positive 1 -->> (-128+64+32+16+8+1)-1 ; While the two's complement format states that we need add 1, we are actually deducting 1 versus adding 1. This is because we have to consider 0 in the equation.


An easy method to get the two's complement of +7 :(left most is used to track sign, so add -1 to the result) :




































1286432168421CalculationResult
00000111(4+2+1)7
11111001(-128+64+32+16+8+1)-1-8

 


Note the left most significant pair of corresponding bits in red. A value of 0 means positive and a value of 1 means negative and is used for tracking the sign in a signed binary.


One question still remains, why is the two's complement always a positive less(6) and a negative more(-8) ?
Why aren't we getting -7 for a 7 and vice versa. I guess the answer to this is simply "logic", we have to consider the 0.





















-8-7-6-5-4-3-2-1
76543210

Thursday, July 26, 2007

Range and selection object bug in Opera browser when designMode is enabled ? Here is a fix, read on!!

Opera ? why oh why :(
Opera has recently started to support rich text editing in it's browser, and honestly speaking, it was just about time they did. Their editor support looks great, and works as mentioned on their wiki pages :

we now have support for rich text editing in web pages via document.designMode. document.designMode is also what Firefox uses for rich text edting. That means Opera does it the same way Firefox does where you make a whole docuent editable, which is usually done on an iframe document or a document embedded with the object tag.

 And then, you are encouraged to try and run the midas demo in opera http://www.mozilla.org/editor/midasdemo/. And sure enough it runs. Nice one opera! But if you test it well, you will notice that it blows up suddenly. More specifically you will notice your content magically dissappear when trying to insert a table or any other custom html element insertion supported by your rich text editor(not using execCommand) into a blank area where you have a line break. What ? This one drove me crazy! Not because content was mysteriously vanishing. It was vanishing because the midas demo was not prepared for this bug in opera. What was happening is simply the following :

1. User saw a bit of blank space ?
2. User clicks in the blank space.
3. User does not make a selection, but instead proceeds to add an html table in the cursor location(note where i say html table, since inserting an html table is going to force you to create the table and add it in the desired location by using range object, selection objects) and insert the node. Your not using execCommand anymore here.
4. Wham, the range and selection objects in opera blow up and simply return startOffset and endOffset with a fixed value of 0 and 1, 2 Vs the real node location of the area where you clicked. 0, 1, 2 respectively which represent
0. <html element
1. <head if one exists,
2. <body element

mmm nasty :( The best part is, there was no simple hack i could think of to try to fix this. While i filed a bug with a test case to opera, their bug tracking system does not communicate anything back. It's just one of those, ohh you found a bug ? thanks, we will fix it. Bye bye :p damn, i went into delusional state totally :( Don't even know why i was expecting to be chatting with someone from their team and getting a quick workaround :p Think that is supposedly part of their support provided to premium members. Fair enough :D~

I was pretty desperate to fixing this. I just couldn't believe i've gotten this far with my html editor and now this bug manifests in opera(it was there since the start, i just happened to have noticed it now ;p). I did think of finding this pattern and exiting for opera, that is, doing nothing if the user clicked in a blank area without making a selection. But that just didn't seem right. At the moment my editor supported the exact same features cross browser, so why stop here.

A little bit of mind storming, staring at my screen, replaying the test case i filed to opera and boom! It just occurred to me, what if i inserted a non-breaking-whitespace(&nbsp;) right before the br so -->> &nbsp;<br /> ; And what do you know, opera loved that and that blank area was not a blank area anymore :p woohooo CHAMPAGNE!!!

At the time of this writing, almost all third party component vendors that chose to support rich text editing in opera and several of the top opensource or free editors, manifest this bug.

 To name a few :

1) tinyMCE,(works around and adds content at the end of document, not cursor location),

2) Spaw(works around and adds content at the end of document, not cursor location),

3) WebWizRichTextEditor,

4) freerichtexteditor (this one does not even handle the exception) ,

5) qWebEditor(content gets deleted) ,

6) Telerik(content gets deleted and exception is thrown),

7) obout (content gets deleted and throws exception)

i was curious to see if they had gotten to actually fixing this, before i filed the bug :p  i have not tested others but probably the results are the same.

this is an output of my test case before apply the bug fix :

sel.anchorNode.nodeName: #text
sel.anchorNode content: #1 click in the empty space below this line
sel.focusNode.nodeName: #text
sel.focusNode content: #1 click in the empty space below this line
range.startContainer.nodeName: HTML
range.startOffset: 2
range.startContainer content: html - content too large, truncated
range.endContainer.nodeName: HTML
range.endOffset: 2
range.endContainer content: html - content too large, truncated
range.commonAncestorContainer: html - content too large, truncated

Always the same output for any blank area i click. Notice how anchorNode is always the first element in the document and the
start container is <html element, startOffset/endOffset is 2 which is the <body element. And the commonAncestorContainer is the <html element again :(

And now after the bug fix for the same operation that generated the above output :

sel.anchorNode.nodeName: #text
sel.anchorNode content:
sel.focusNode.nodeName: #text
sel.focusNode content:
range.startContainer.nodeName: BODY
range.startOffset: 5
range.startContainer content: body - content too large, truncated
range.endContainer.nodeName: BODY
range.endOffset: 5
range.endContainer content: body - content too large, truncated
range.commonAncestorContainer: body - content too large, truncated

ohh beautiful :D Notice how anchorNode content is empty, thats because it contains a &nbsp; and the &nbsp; is exactly the spot where we clicked, exactly what i'd expect to get :p

Following is the test case i filed to opera bug report, except here i have included my little non-breaking-whitespace fix.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
</head>
<body>
#1 click in the empty space below this line
<br />
<br />
#2 click in the empty space below this line
<br />
<br />
<span>#3 click in the empty space below this line</span>
<br />
<br />
<div>
#4 click in the empty space below this line
</div>
<br />
<br />
#5 click in the empty div below this line
<div style="background: yellow; border: 1px solid green; height: 20px">
&nbsp;</div>
<div id="testresult" style="border: 1px dashed red; color: red;">
<span style="text-decoration: underline">Test results</span><br />
<br />
</div>
</body>
<script type="text/javascript">




document.designMode = 'on';
// to test in firefox comment above line and uncomment below
//document.body.designMode = 'on';
window.onload = init;
function init()
{
fixOperaRangeSelectionBug();
document.onmouseup
= function(e){
var textNode = document.createTextNode('This text is inserted in the location i clicked. works perfectly in safari and firefox(and now opera, after this workaround)');
var sel = window.getSelection();
var range = sel.getRangeAt(0);
// with insertNode we are manipulating the dom, and causing a mutation
// because of which you will see startOffset and endOffset changing.
// however, commenting the insertNode line out, you can clearly see that
// startOffset and endOffset return a fixed value of 0 and 1, 2 Vs the real node
// location of the area where you clicked. 0, 1, 2 respectively which represent
// 0)html, 1)<head if one exists, 2) the body.

range.insertNode(textNode);
//insert our node
// now move the cursor to the last line of our inserted text.
range.setStartAfter(textNode);
range.setEndAfter(textNode);
sel.addRange(range);
var testResult = document.getElementById('testresult');
testResult.innerHTML
+= '<br />' + 'sel.anchorNode.nodeName: ' + sel.anchorNode.nodeName;
testResult.innerHTML
+= '<br />' + 'sel.anchorNode content: ' + getContents(sel.anchorNode);
testResult.innerHTML
+= '<br />' + 'sel.focusNode.nodeName: ' + sel.focusNode.nodeName;
testResult.innerHTML
+= '<br />' + 'sel.focusNode content: ' + getContents(sel.focusNode);
testResult.innerHTML
+= '<br />' + 'range.startContainer.nodeName: ' + range.startContainer.nodeName;
testResult.innerHTML
+= '<br />' + 'range.startOffset: ' + range.startOffset;
testResult.innerHTML
+= '<br />' + 'range.startContainer content: ' + getContents(range.startContainer);
testResult.innerHTML
+= '<br />' + 'range.endContainer.nodeName: ' + range.endContainer.nodeName;
testResult.innerHTML
+= '<br />' + 'range.endOffset: ' + range.endOffset;
testResult.innerHTML
+= '<br />' + 'range.endContainer content: ' + getContents(range.endContainer);
testResult.innerHTML
+= '<br />' + 'range.commonAncestorContainer: ' + getContents(range.commonAncestorContainer);
testResult.innerHTML
+= '<hr />';
}
}
function getContents(node)
{
if (node == null)
return 'null';
var nodeName = node.nodeName.toLowerCase();
var flag1 = (nodeName == 'html' || nodeName == 'head' || nodeName == 'body');
if (node.nodeType == 1 && !flag1)// element node
return node.innerHTML;
else if (node.nodeType == 3 && !flag1)// textnode
return node.nodeValue;
else if (flag1)
return nodeName + ' - content too large, truncated';
}
function fixOperaRangeSelectionBug()
{
var brCollection = document.body.getElementsByTagName('br');
for (var num1 = 0;num1 < brCollection.length; num1++)
{
var br = brCollection[num1];
var prevSibling = br.previousSibling;
if (prevSibling != null)
{
var hasNbsp = isNbsp(prevSibling);
if (!hasNbsp)
{
// insert the invisible non-breaking-whitespace
br.parentNode.insertBefore(document.createTextNode('\u00A0'), br);
}
}
}
}
function isNbsp(textNode)
{
if (textNode.nodeType != 3)
return false;
var pat = /\u00A0/;
return textNode.nodeValue.match(pat);
}





</script>
</html>


update : August - 09 - 2007


I had completely forgotten apart from filing this bug report, i had also made a post on the opera groups and here is the response i got


posted on google groups.


Apparently, it seems like they have fixed this in their new code base, which is not out yet, so this workaround sticks until then :D

Wednesday, July 4, 2007

FormView, DetailsView ItemInsertedTemplate DataItem is empty..Oops

You have a FormView, DetailsView and you went into InsertItemTemplate, but you are unable to get the Data to which your FormView is bound to ? You didnt do nothing wrong. It's a short coming of the FormView control. Between, at the time of this writing i think this is pretty much the same behaviour you will get also in a DetailsView, even though i have not tested with a DetailsView. Read on..

I love the FormView, gives you great control on the output rendered, so you basically have total control how you want to layout your data, its a single row display, so pretty much like the DetailsView Control, however I've noticed a limitation while going into InsertItemTemplate(When clicking the Insert button or rather, the button whose CommandName is "New" ;

I've noticed the FormView CurrentMode is ReadOnly when switching into this template. This has the drawback of the DataItem property being null. I can sort of understand why it was done in this way. I guess it's probably done in this way because when inserting a new record, you dont really want to keep any previous data in the controls that take input for inserting your new record.

It's a new record, so naturally you want to keep the input fields empty and ready to take input from the user. However I've had issues with this, and actually needed some data that was already in the FormView prior to switching into Add new record view (InsertItemTemplate).

To tackle this problem, what i did was collect the values from the forms collection. After all, this is indeed a postback, so it makes sense to take this route. This is however going to be a challenge since the controls nested in a datacontrol have an auto generated clientID, in the from --> ctl00$WebPartManager1$ShowNews1$ctl00$FormViewArticles$HiddenFieldCategoryId <-- as you can note here, i'm trying to reach the HiddenFieldCategoryId input element which is nested in my FormView, which in turn is nested in a user control "ShowNews1", who in turn is nested into a "webpartzone", which in turn is nested in a content page "ctl00" ; in a deply nested structure, its quite challenging to get the value of an input field from the forms collection.

Here below you will see a simple method GetDataControlPostBackFormValue

private string GetDataControlPostBackFormValue(string namingContainer, string key, char separator, NameValueCollection form)
{
string clientID = namingContainer.Replace('_', separator) + separator.ToString() + key;
return (string.IsNullOrEmpty(form[clientID])) ? string.Empty : form[clientID];
}


//how to call the method :

string hiddenFieldCategoryId = string.Empty;
protected void FormViewArticles_ModeChanging(object sender, EventArgs e)
{
FormView fv
= sender as FormView;
if (fv.CurrentMode == FormViewMode.ReadOnly)// means its gone in readonly mode
{
string namingContainer = fv.Row.NamingContainer.ClientID;
string key = "HiddenFieldCategoryId";
hiddenFieldCategoryId
= GetDataControlPostBackFormValue(namingContainer, key, this.Page.IdSeparator, HttpContext.Current.Request.Form);
}
}

 

As you may guess, this method is expecting 4 simple arguments, the names are very descriptive, actually it's a two liner method and surely does not deserves any explaination ;p

Friday, May 4, 2007

DetailsView and BoundField set to Readonly="true" when updating, does not contain original field value.

You can specify whether a bound field is editable using the ReadOnly property of the BoundField control. When the ReadOnly property is set to false, the field will be editable when the user clicks the Edit command button.

 When the ReadOnly property is true, the bound field will be displayed, but the user will not be able to edit the field. In this specific case when the BoundField is set to true, its value is not being collected and supplied to the ObjectDataSource control associated with the DetailsView. I would expect the value to be passed, since it is being output to the page, however this is not the case.

 The only workaround i have found to this problem is to manually collect the value from the OldValues collection from within the DetailsView ItemUpdating event, which contains the original field value and pass it to the object datasource parameter in question. Following is sample code :

  

<%@ Page Language="C#" %>

<%@ Import Namespace="System.Data" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
protected
void DetailsView1_ItemUpdating(object sender, DetailsViewUpdateEventArgs e)
{
Response.Write(string.Format(
"<br /> in ItemsUpdating-- userId: {0}, userName: {1}, country: {2}", e.OldValues["UserId"], e.OldValues["UserName"], e.OldValues["Country"]));
// add the old value back for the updateparameter
ObjectDataSource1.UpdateParameters["UserId"].DefaultValue = e.OldValues["UserId"].ToString();
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<asp:Button ID="Button1" runat="server" Text="Button" />
<div>
<asp:DetailsView AutoGenerateRows="False" ID="DetailsView1" DataSourceID="ObjectDataSource1"
runat
="server" AutoGenerateInsertButton="true" AutoGenerateEditButton="true"
AutoGenerateDeleteButton
="true" OnItemUpdating="DetailsView1_ItemUpdating">
<Fields>
<asp:BoundField DataField="UserId" HeaderText="UserId" ReadOnly="true" />
<asp:BoundField DataField="UserName" HeaderText="UserName" />
<asp:BoundField DataField="Country" HeaderText="Country" />
</Fields>
</asp:DetailsView>
<asp:ObjectDataSource ID="ObjectDataSource1" SelectMethod="SelectUserDetails" UpdateMethod="UpdateUserInfo"
TypeName
="MyDAL" runat="server">
<UpdateParameters>
<asp:Parameter Name="UserId" Type="Int32" />
<asp:Parameter Name="UserName" Type="string" />
<asp:Parameter Name="Country" Type="string" />
</UpdateParameters>
</asp:ObjectDataSource>
</div>
</form>
</body>
</html>

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Collections.Generic;
using System.Collections;
/// <summary>
/// Summary description for MyDAL
/// </summary>
public class MyDAL
{
public MyDAL()
{
//
// TODO: Add constructor logic here
//
}
public ICollection SelectUserDetails()
{
DataTable dt
= new DataTable();
DataRow dr;

dt.Columns.Add(
new DataColumn("UserId", typeof(Int32)));
dt.Columns.Add(
new DataColumn("UserName", typeof(string)));
dt.Columns.Add(
new DataColumn("Country", typeof(string)));
dr
= dt.NewRow();
dr[
0] = "123";
dr[
1] = "Alessandro";
dr[
2] = "Italy";
dt.Rows.Add(dr);

DataView dv
= new DataView(dt);
return dv;
}
public void UpdateUserInfo(int userId, string userName, string country)
{
HttpContext.Current.Response.Write(
string.Format("<br /> in UpdateUserInfo-- userId: {0}, userName: {1}, country: {2}", userId, userName, country));
}
}


Thursday, May 3, 2007

Howto: Show UpdateProgress conditionally for specific controls triggering an async postback

I have not found an automatic way to do this. The documentation on http://ajax.asp.net says :

If you need finer control over when an UpdateProgress control is displayed, you must provide client script for the beginRequest and endRequest events of the PageRequestManager class. In the beginRequest event handler, display the DOM element that represents the UpdateProgress control, and in the endRequest event handler, hide it.

This does not really help, because the UpdateProgress control fires up a timer and execute the code to show the udpateprogress bar after a specified interval has occured. This makes it almost imporssible to hide the updateprogress, because while you could hide it in the beginRequest event of the updatepanel, the timer will execute at a later time and re-enable the updateprogress control :-(

However the documentation also says :

In the following example scenarios, you must provide client script to show an UpdateProgress control when a target UpdatePanel is updated:

  1. When a postback from a control is registered as an asynchronous postback trigger for the panel, and there is an UpdateProgress control on the page but its AssociatedUpdatePanelID property is not set to the panel's ID.
  2. When postbacks from controls are registered as asynchronous postback controls by using the RegisterAsyncPostBackControl(Control) method of the ScriptManager control.

Note the text i marked in red above. This does give an idea. So, we can set the  AssociatedUpdatePanelID property to a non-existant control, this will nicely abort the UpdateProgress control from trying to associate a timer and show itself.

To be honest, it would be nice if the UpdateProgress clientside api exposed a cancel timer method. This would of cost just one line of extra code, so a one liner method, which is not there. Its not a big problem however because the workaround i show here is effective in the same way. If the AssociatedUpdatePanelID property is pointing to a dom element that does not exist, the timer is never executed. You can easily also add a switch case in the small demo i provide you below, in case you want to disable the UpdateProgress for a list of controls etc.

Alternatively, you can just add your own dom element. A basic div with display:none is more than enough. And then toggle the display in beginRequest and endRequest events of the PageRequestManager class. After all the updateProgress does not bring much to the table and this is exactly what it does while limiting what you can do with it. I suggested this route for fine grained control on your UpdateProgress Vs using the built in UpdateProgress control.

The following sample code shows the UpdateProgress control, which is nested in the updatepanel, only if Button2 triggered the callback. If it was Button1, the UpdateProgress does not show :

 

<%@ Page Language="c#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
protected
void Button1_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(
3000);
}
protected
void Button2_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(
3000);
}
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
</div>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
&nbsp;<asp:Button ID="Button1" runat="server" Text="Postback 1" OnClick="Button2_Click" />
<asp:Button ID="Button2" runat="server" Text="Postback 2" OnClick="Button1_Click" />
<asp:UpdateProgress ID="UpdateProgress1" AssociatedUpdatePanelID="UpdatePanel1" DisplayAfter="0" runat="server">
<ProgressTemplate>
UpdatePanel1 updating...
</ProgressTemplate>
</asp:UpdateProgress>
</ContentTemplate>
</asp:UpdatePanel>
</form>
<script type="text/javascript">
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequestHandler);
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);
var prm = Sys.WebForms.PageRequestManager.getInstance();
function BeginRequestHandler(sender, args)
{
var elem = args.get_postBackElement();
var prm = Sys.WebForms.PageRequestManager.getInstance();
// First set associatedUpdatePanelId to non existant control
// this will force the updateprogress not to try and show itself.
var o = $find('<%= UpdateProgress1.ClientID %>');
o.set_associatedUpdatePanelId(
'Non_Existant_Control_Id');
// Then based on the control ID, show the updateprogress
if (elem.id == '<%= Button2.ClientID %>')
{
var updateProgress1 = $get('<%= UpdateProgress1.ClientID %>');
updateProgress1.style.display
= '';
}
}
function EndRequestHandler(sender, args)
{
var updateProgress1 = $get('<%= UpdateProgress1.ClientID %>');
updateProgress1.style.display
= (updateProgress1.style.display == '') ? 'none' : '';
}
</script>

</body>
</html>


Update : May 12, 2007


My good buddy Lars-Eric kindblad(imnae), just pointed out that the UpdateProgress control does not show when the async postback is caused by a control that is not nested within the UpdatePanel. I have just verified this to be true and in this case, if you were not nesting the postback control inside the UpdatePanel but instead have it registered as a trigger to your UpdatePanel, then you want to use the workaround i provide above. Also as he has stated, currenly he'd rather use a normal div and hide/show it manually in BeginRequestHandler/EndRequestHandler since he wants to maintain a single UpdateProgress control which is currently not possible because each UpdateProgress needs to be associated to an individual UpdatePanel. You might want to do this if you have many UpdatePanels on the page and prefer to maintain a single UpdateProgress(which is by the way the most natural requirement).

Friday, March 30, 2007

Retrieve WebControls RenderContent whilst working around VerifyRenderingInServerForm without overriding in your page class.

How to get the rendering of a WebControl that depends on the HtmlForm object ?
These type of controls all have a check in their render method, the check is of type :

if (this.Page != null)
{
this.Page.VerifyRenderingInServerForm(this);
}

This will fireback at you when trying to collect the rendering of the control. There are a few fixes of which dont really help me like overriding VerifyRenderingInServerForm in the page and making this method do nothing. That is nice but i develop Custom WebControls. I cannot override the page methods in my control class ofcourse :P I cannot also tell the consumers of my control -->> "Caution, before you can use this control you must override VerifyRenderingInServerForm and void it"


That is not an option for me. So here is a simple solution. It might seem like cheating but it's a perfectly valid solution and easy :


TreeView tv = new TreeView();
HtmlForm dummyForm
= new HtmlForm();
StringBuilder sb
= new StringBuilder();
System.IO.TextWriter tw
= new System.IO.StringWriter(sb);
HtmlTextWriter htw
= new HtmlTextWriter(tw);
Page dummyPage
= new Page();
dummyPage.EnableViewState
= false;
tv.ID
= "tv1";
dummyForm.Controls.Add(tv);
dummyPage.Controls.Add(dummyForm);
dummyForm.RenderControl(htw);
int start = sb.ToString().IndexOf("</div>") + "</div>".Length;
int end = sb.ToString().LastIndexOf("</form>") - start;
TextBox1.Text
= sb.ToString().Substring(start, end);

So, how did we workaround the VerifyRenderingInServerForm ? We use a dummy form and page. These controls are never added to the actual pages control collection. Their sole purpose is to provide support to the control whose rendering we want.

Ofcourse, in the final product you will have the rendering of your control nested in a form. You can note how i use substring and only retrieve the treeview control's content, since in the final rendering we have an unwanted htmlform, and an empty hiddenfield used to store viewstate which we do not want. Normally even though this is not my case, i've seen people wanting to get the rendering of a gridview or datagrid so they can include that output in an html email which makes this post useful.

One creative solution was what i've seen being suggested by scott mitchell on his article :

http://aspnet.4guysfromrolla.com/articles/102203-1.aspx

It's a good suggestion but in his case he is looping through all controls in the form and setting them to invisble, while keeping just the control whose rendering he needs. The suggestion you see here with the dummy form and page are easier and help you avoid this step. A big thanks to scott mitchell btw, I enjoy all of his articles and he is a superb writer. I'm a big fan :P

About Me