Friday, October 28, 2011

Microsoft Lync 2010 No Audio without Microphone Fail

PROBLEM: In Microsoft Lync 2010, you cannot listen in a web meeting if you don't have a microphone plugged in. It would make sense that you could at least listen in a web meeting (e.g. someone is presenting and you just want to listen) and if you need to provide feedback you can just type a question. But alas that isn't supported (fail) - it's a bug, and Microsoft has said they won't be changing their product (fail fail).

Microsoft has already documented this as a bug with no comments on if it will ever be repaired.

This means that audio cannot be received by a participant in a Lync Server Online Meeting unless the participant has a microphone plugged in.

I was asked by the MS support tech (who was as helpful and polite as he could be) to submit a BIS (Business Impact Statement). Hopefully the BIS will be viewed as worthy of getting the developers to repair this problem.

I'm surprised that I had to go this far to find an answer if not a solution. Is it really that odd for a typical training to be held using Lync/OCS or do most companies install a microphone or webcam on every PC?
Unfortunately the response that fellow got was:
Microsoft just sent me an update:

"During our review we noticed that the Microphone parameter is intertwined to the nerve center of the MCUs.

Making a change would involve too many components and the high cost associated for making these changes would not justified to qualify it as a Cumulative Update within this product release lifecycle.

Unfortunately, the bug that has been filed for this case has been rejected."

He goes on to recommend buying a few hundred microphones from Amazon.

Insane.
The only workaround talked about is:
As far as I can tell, the software is satisfied as long as you plug anything into the microphone jack (even a disconnected plug from an old headset worked for me), though you can't unplug it afterwards without the call getting interrupted (so multiple people can't share the same plug).
Someone else tried this:
I found that out and ended up buying 500 audio adapters for less than a quarter each. Explaining over and over to people why they have to be plugged in was pretty humiliating. I tested about 20 machines and found 2 that had sound cards/sound card drivers smart enough to recognize that the adapters weren't microphones. I'll have to get them real mics.

I haven't yet tried to find out what happens with outside callers using web app. I have a good guess as to what the results will be...

MS recognizes this is a bug, and decided to fix it it CU4, but then decided not to. The only thing I can come up with for that is the recent purchase of Skype. Maybe the more difficult repairs will be delayed/cancelled while Skype is being integrated into the next version of Lync? I really don't have any idea if that's the plan or not - I'm just speculating.
SOLUTION: So the alternatives are:

1. Get headphones with a microphone built in; or
2. Plug anything into the microphone plug; or
3. Watch a recorded webinar afterwards (rather than participate live); or
4. Use a different product (e.g. Skype).

If you can figure out where to log this as a problem with MS and can be bothered that would be great. I think we'll just go with option 1 for those who require it. Overall, sorry MSFT but this is a fail.

Thursday, October 27, 2011

Facebook Graph API and redirect_uri and QueryString parameters

PROBLEM: If you are using the Facebook API e.g. to do the login for your website etc (http://developers.facebook.com/docs/authentication/), you may find yourself wanting to get the user to login to Facebook but then return to the page they came from. My first reaction was to add the URL they came from to the end of the redirect_uri querystring parameter e.g.

https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_URL?orig_url=http://example.com/default.aspx

Of course you would url encode the orig_url parameter and then decode it when you wanted to use it... it actually does work for the above type of call... but when calling the app authentication api it doesn't like it. Base64 encoding it doesn't work either. And remember, a little known fact is that the redirect_uri needs to be the same for when you are doing the app authorization as the app authentication (see below url)

https://graph.facebook.com/oauth/access_token?client_id=YOUR_APP_ID&redirect_uri
=YOUR_URL?orig_url=http://example.com/default.aspx&client_secret=YOUR_APP_SECRET&code=THE_CODE_FROM_ABOVE

Bottom line is that I tried the above approaches and kept getting a HTTP 400 error "Error validating verification code.".

SOLUTION: Taking a step back, rather than try and get what should work working, try a different approach. In my case, simply put the original URL into a session variable for use later on (http://stackoverflow.com/questions/5747320/how-to-encode-the-redirect-uri-for-facebook-login).

e.g.

Session["UrlToReturnToAfterLogin"] = HttpContext.Current.Request.Url.OriginalString;

Too easy.

Tuesday, October 18, 2011

Add the optgroup tag to your DropDownList

Problem: The asp.net DropDownList control does not support rendering of the optgroup tag (it will only generate option tags).

Solution: Once again, a bit like trying to get the validation controls to use the display:block style when its display is set to dynamic, sometimes it can be tough to bend asp.net to do what you want when it isn't supported out of the box.


Your options seem to be:

1. Use an adapter to change the html that the asp.net DropDownList control renders (affects all DropDownLists in your solution).
2. Inherit from the DropDownList and change the html that your new control renders.
3. Use javascript to do some magic once the control has rendered.
4. Don't use optgroup - depending on your scenario, it may not be the best UI solution.

I went with option 1. and a lot of trial and error based on various examples on the net. My main problem was that there weren't enough complete code solutions showing all the bits of code and how to use it.

The examples I tried from the stackoverflow page ended up giving me the following error when I tried submitting the page:

Invalid postback or callback argument. Event validation is enabled using ...
I didn't have time to investigate so I went with a different example. In the end the best example I found came from Christoff Truter: http://www.cstruter.com/blog/259. The only clarifications: Step 3 should all be within:
public class DropDownListAdapter : WebControlAdapter
{
...
}
In the overridden version of OnLoad in Step 3, you need to add a null check before setting the Group attribute value (in case you have provided a listelement in your dropdownlist without the "Group" attribute e.g. a blank 'please select a value' item). i.e.
if (groups[i] != null)
{
dropDownList.Items[i].Attributes["Group"] = groups[i].ToString();
}
The final piece of the puzzle was how to create your DropDownList. The big gotcha was that I couldn't use DataSource/DataBind because when I did I lost the attribute that I had set on the listitems (perhaps it was because of the issue described here: http://www.4guysfromrolla.com/articles/091405-1.aspx). This was my code (if you have a way to get DataSource/DataBind working let me know).

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
//Find all currently open clubs which have indicated that they provide X-Service
List currentXServiceClubs = DBHelper.GetAllCurrentClubsOfferingXService();

//Create list with the custom "Group" attribute
List listItemsForCurrentXServiceClubs = new List();

lstXServiceClubs.Items.Add("");
foreach (var club in currentXServiceClubs)
{
ListItem newListItem = new ListItem();
newListItem.Attributes.Add("Group", club.State);
newListItem.Text = club.CentreName;
newListItem.Value = club.CentreID.ToString();
lstXServiceClubs.Items.Add(newListItem);
//NOTE: If you use the DataSource / DataBind notation then
//the custom attribute will not be passed through to the DropDownListAdapter
//and hence you will lose the OptGroup functionality. As a future project
//review if there is a way around this.
}
}
}



ASP.NET Validation Controls and Styles

PROBLEM: RequiredFieldValidator with Display="Dynamic" places display:inline on the error text when displayed (and I want it to be display:block)

SOLUTION: Well there is no elegant solution! Sometimes the rapid development tools that are normally so helpful in .net just aren't flexible enough to allow what you want to do. The issue is discussed here: http://www.stackoverflow.com/questions/2243498.

Essentially when the validator is first displayed it sets (via .net generated javascript) style="display:none" on the span tag. This element then gets switched from "none" to "inline" when there is an error to display.

Your options?

1. Modify the automatic asp.net javascript - not nice, and could possibly break after future upgrades or browser changes etc. - but probably your best bet if you are desperate - here is how: http://caliberwebgroup.blogspot.com/2009/02/overriding-javascript-in-webresourceaxd.html

2. Roll your own validation controls - possible but then you lose all the default out of the box goodness that come with the asp.net validator controls.

3. Live with it. I went with this option. Sometimes 95% is good enough.

Thursday, October 13, 2011

ASP.NET code in app_code files compile but "Go to definition" doesn't work

PROBLEM: Created some class files in the App_Code folder of an asp.net web application - compiled and there were no errors, but "Go to definition" (F12) wasn't working on some of the classes etc.

SOLUTION: Really easy solution for a beginner's problem - ensure the "Build Action" property of your classes is set to "Compile", not "Content" (it might have defaulted to "Content" because it was a web application not a website (I was just following the App_Code folder name as a convention)).

Wednesday, October 12, 2011

Firefox no likey innerText

PROBLEM: You've gotten your hands dirty on some client side coding a.k.a. javascript, and its working fine in all browsers like IE and Chrome but not in Firefox. The offending line is something like:

" var iCurrent = parseInt(document.getElementById('" + lblWordCount.ClientID + "').innerText);" +
SOLUTION: Use innerHTML instead of innerText e.g.

//Note: Don't use innerText as it isn't supported by Firefox
" var iCurrent = parseInt(document.getElementById('" + lblWordCount.ClientID + "').innerHTML);" +

CAVEATS: Ok, so after a bit more googling (http://stackoverflow.com/questions/1359469/innertext-works-in-ie-but-not-in-firefox) it's definitely not the preferred solution and has issues itself, but for my simple scenario it worked ok. Ideally I should probably be using some jquery or accessing the DOM properly etc. I'm open to here better solutions.

Panel visible=true has no effect

PROBLEM: I have a Panel that I'm setting visible=true explicitly. The debugger passes over that line and visible still evaluates to False on the next line. Obviously as a result, the Panel is not shown. How is this possible?

SOLUTION: Ensure the panel is not nested inside another panel or any other type of container which has Visible set to false e.g. if you are going through some code to make some panels visible/invisible, then make sure you've first set the parent panel to visible=true (if relevant).

Exact same problem and solution was discussed on Stack Overflow http://stackoverflow.com/questions/2539204/panel-visible-true-has-no-effect


LINQ Error "Nullable object must have a value"

PROBLEM: You get a runtime error when one of your LINQtoSQL queries runs which says "Nullable object must have a value".

SOLUTION: This one gets me every time... you've most likely got some logic which says something alone the lines of "if the value is either null or equals blah"... the problem is that with LINQ unlike your normal C# code, it will try and convert/evaluate both parts of the statement and hence result in a null error. The quick solution is to use a ternary operator and make sure you don't call anything on the nullable object e.g. with a DateTime? object don't explicitly call .value on it. Here is an example:


GOOD:

(competition.CompetitionEndDate.HasValue ? competition.CompetitionEndDate < DateTime.Today : false)

BAD:

(competition.CompetitionEndDate == null || competition.CompetitionEndDate.Value < DateTime.Today)

This comes up a lot... here are a few posts which might explain it better than me:

http://peetbrits.wordpress.com/2008/10/18/linq-breaking-your-logic/
http://msdn.microsoft.com/en-us/library/bb882535.aspx



Preserving the value of an updated Label between PostBacks

Problem: Preserving the value of a label that has been updated via javascript between postbacks.

Issue: When an asp.net page does a postback, only input fields (e.g. textboxes) will return their current value (as updated by the user or javascript etc). If you have a label (not an input field) (in my case it was a word count) that you want to be able to access after a postback on the server you'll need a workaround.

Solution: It's a bit ugly but use a TextBox and set its style to "display:none" and then update this at the same time you update your label. Don't set it to Visible="false" because then the whole control won't render at all. You can't use a HiddenField because it isn't an input field and hence on postback its updated value won't be returned. Then in Page_Load set your label again based on the value of this hidden TextBox (remember it will need to be done when Page.IsPostBack == true).

ASPX:

<asp:TextBox ID="txtCurrentWordCount" runat="server" Text="0" style="display:none"/>

Code Behind:

protected void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
//Because we update the word count via javascript, it won't persist between postbacks (which occur when
//we change the club selected etc). Therefore we store it in a hidden text field and update the label
//each time the page is reloaded.
lblWordCount.Text = txtCurrentWordCount.Text;
}
}

Mandatory CheckBox

Problem: The RequiredFieldValidator doesn't allow you to enforce that a CheckBox in asp.net is checked/selected.

Solution: Best approach I could find was to simply use a CustomValidator (ideally with client and server side validation but server side would be sufficient). Example code is as follows:

ASPX PAGE:


ClientValidationFunction="ValidateTermsAndConditionsAgreement"
OnServerValidate="ValidateTermsAndConditionsAgreement"
runat="server"
Text=" You must agree to the terms and conditions before entering. "
ValidationGroup="vgEntryDetails"
CssClass="error"
Display="Dynamic">

CODE BEHIND:

Since I was actually optionally displaying my checkbox I actually added the validation script optionally in Page_Load (note, it needs to be done regardless of whether Page_Load is being called as part of a postback or not otherwise if there is a postback before you click submit the javascript you've injected via the code behind won't be there and hence the client side validation will not work).

//Script to use as part of the page validation
if (!Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), "TermsAndConditionsScript"))
{
string scriptText = " function ValidateTermsAndConditionsAgreement(sender, args){" +
" if (document.getElementById('" + chkAgreeToTermsAndConditions.ClientID + "').checked){" +
" args.IsValid = true;" +
" }" +
" else {" +
" args.IsValid = false;" +
" }" +
" return;" +
"}";

Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"TermsAndConditionsScript", scriptText, true);
}

...and the server side validation

///
/// Validate that the terms and conditions have been agreed to if they exist
///
///
///
protected void ValidateTermsAndConditionsAgreement(object source, ServerValidateEventArgs args)
{
if (chkAgreeToTermsAndConditions.Checked == false)
{
args.IsValid = false;
}
}


...and finally don't forget to check IsValid in your method called by the submit button.


protected void btnSubmitEntry_Click(object sender, EventArgs e)
{
//Is valid check is required because we have a CustomerValidator control
//for the terms and conditions checkbox - if it's not valid we don't want to proceed and the error will be
//displayed.
if (Page.IsValid)
{
//Save competition entry
....