Pages

Monday, December 14, 2009

Installing a new stereo in a 2003 Mitsubishi Lancer (with photos)

  1. Remove all 3 HVAC knobs. (aircon, heat etc. They just pull off)
  2. Unscrew 2 screws inside pocket holding it to the trim panel, both screws are inside at the top.
  3. Unscrew 2 screws behind temperature knob and vent selector knob.
  4. Remove trim panel around radio and HVAC controls.
  5. Unscrew 4 screws holding radio in place, two on left, two on right.
  6. Pull radio out.
  7. Unscrew 2 screws from adapters, one on each side.
  8. Remove wiring harness and antenna cable.
  9. A wiring harness/adapter removes the need to splice/solder the new stereo in.
  10. Earth the new stereo to the metal chassis as the factory model is earthed through its case.

Friday, December 4, 2009

Salesforce Cheat Sheets - Web Services API

Cheat Sheets

Integration

Web Services API Cheat Sheet

Overview

Use the Force.com Web Services API to create, retrieve, update, or delete records in Force.com from any external system that supports SOAP-based Web services, such as Java, .NET, or PHP client applications. With more than 20 different calls, the API also allows you to maintain passwords, perform searches, retrieve metadata information about objects and more.

Getting Started

To generate the WSDL representing your environment, go to Setup | Developer | API. The Enterprise WSDL is a strongly typed representation of your data, while the Partner WSDL is a loosely typed representation, ideal for writing generic clients that work across different organizations.


Core Calls

These are the fundamental calls needed for logging in, querying, creating and deleting data:

login()Logs in and starts a client session.
logout() Ends the session of the logged-in user.
invalidateSessions() Ends one or more sessions.
create() Adds one or more new individual records.
delete() Deletes one or more individual records.
update() Updates one or more existing records.
upsert() Creates new records and updates existing records; matches on a custom field to determine the presence of existing records.
undelete() Undeletes records from the Recycle Bin.
emptyRecycleBin() Deletes records from the Recycle Bin immediately.
getDeleted() Retrieves the IDs of individual deleted records for a specified timespan.
getUpdated() Retrieves the IDs of individual updated records for a specified timespan.
convertLead() Converts a Lead into an Account, Contact, or (optionally) an Opportunity.
merge() Merges records of the same object type.
process() Submits an array of approval process instances for approval, or processes an array of approval process instances to be approved, rejected, or removed.
query() Executes a query against the specified object and returns data that matches the specified criteria.
queryAll() Same as query(), but includes deleted and archived items.
queryMore() Retrieves the next batch of records from a query.
retrieve() Retrieves one or more records based on the specified record IDs.
search() Executes a text search in your organization’s data.

Describe Calls

These calls let you dynamically retrieve information about objects and other aspects of an organization:

describeGlobal() Retrieves a list of available objects for your organization’s data.
describeLayout() Retrieves metadata about page layouts for the specified object type.
describeSObject() Superseded. Use describeSObjects() instead.
describeSObjects() Retrieves metadata (field list and object properties) for the specified object types.
describeSoftphoneLayout() Describes the SoftPhone layout(s) created for an organization.
describeTabs() Describes the apps and tabs that have been configured for the user.

Utility Calls

These calls let you retrieve user information, send emails and reset passwords:

getServerTimestamp() Retrieves the current system timestamp from the API.
getUserInfo() Retrieves personal information for the user associated with the current session.
resetPassword() Changes a user’s password to a system-generated value.
sendEmail() Immediately sends an email message.
setPassword() Sets the specified user’s password to the specified value.

Primitive Data Types

The API uses the following primitive data types:

base64 Base 64-encoded binary data.
boolean Boolean fields have one of these values: true (or 1), or false (or 0).
byte A set of bits.
date Date data. Unlike dateTime fields, date fields contain no time value.
dateTime Date/time values (timestamps)
double Double values.
int Fields of this type contain numbers with no fractional portion.
string Character strings.
time Time values.

Field Types

In addition to the primitive data types, the API defines the following data types for object fields:

anyType Polymorphic data type that represents a particular type depending on the kind of field involved.
calculated Fields that are defined by a formula.
combobox A combobox, which includes a set of enumerated values and allows the user to specify a value not in the list.
currency Currency values.
email Email addresses.
encryptedstring Encrypted text fields contain any combination of letters, numbers, or symbols that are stored in encrypted form.
ID Primary key field for the object.
masterrecord When records are merged, the ID of the record that is saved (the other records are deleted).
multipicklist Multi-select picklists, which include a set of enumerated values from which multiple values can be selected.
percent Percentage values.
phone Phone numbers. Values can include alphabetic characters.
picklist Picklists, which include a set of enumerated values from which one value can be selected.
reference Cross-references to a different object. Analogous to a foreign key field in SQL.
textarea String that is displayed as a multiline text field.
url URL values.

API Fault Element

The following table lists the API fault elements that the API returns if an error occurs when processing a service request.

ApiQueryFault The row and column numbers where the problem occurred.
LoginFault An error occurred during the login() call.
InvalidSObjectFault An invalid sObject in a describeSObject(), describeSObjects(), create(), update(), retrieve(), or query() call.
InvalidFieldFault An invalid field in a retrieve() or query() call.
MalformedQueryFault A problem in the queryString passed in a query() call.
InvalidQueryLocatorFault A problem in the queryLocator passed in a queryMore() call.
MalformedSearchFault A problem in the search passed in a search() call.
InvalidIdFault A specified ID was invalid in a setPassword() or resetPassword() call.
UnexpectedErrorFault An unexpected error occurred. The error is not associated with any other API fault.

Monday, November 23, 2009

Adding a cancel event to the Telerik RadUpload control

I came up with the following to fire a function when the RadUpload cancel button is pressed. Replace alert('cancel pressed') as required.

<telerik:radprogressarea id="progressArea1" runat="server" />
    var cancelButton = $find('<%= progressArea1.ClientID %>')._findElement("CancelButton");
    $addHandlers(cancelButton, { "click": function() { alert('cancel pressed') } }, this);

Friday, November 20, 2009

Konami Code for lens flare in Facebook

This may be old news to some, but it's new to me.

Entering the Konami Code (UP UP DOWN DOWN LEFT RIGHT LEFT RIGHT B A ENTER) into Facebook creates a lens flare effect on scrolling and mouse clicks.

The keystrokes appear in the javascript file as var secrets=[38,38,40,40,37,39,37,39,66,65,13].

Tuesday, November 17, 2009

PS3 Gamer Tag / Portable ID from PSN

Discovered that I can use the US rather than EU Portable Id simply by logging into the us site. The US version is easier to throw in the sidebar of my layout. I did notice that the EU version shows games you were playing recently. Annoyingly is forces you to specify a Favourite game and mood.

EU

US


Get your Portable ID!

ASP.NET Property Parameter for Data Sources

When reworking an ASP.NET page I wanted to swap out a QueryStringParameter and use the value from a property instead.

I came across Joe Stevens' ASP.NET Property Parameter for Data Sources which works well.

Wednesday, November 11, 2009

Working around Adobe Download Manager issues in Firefox

With the Firefox 3.5.5 update I was prompted to update my Adobe Flash Player to version 10. I went through the process with the Adobe Download Manager but only got as far as Status "Connecting..." and then "Network error".

I found a complete download at the Adobe Flash Player Support Center. Under the Flash Player 10 section I downloaded the Adobe Flash Player 10 Update for Windows and Macintosh (ZIP, 44 MB) and installed the plugin from flash_player_update3_flash10.zip\Players\Release\Install Flash Player 10 Plugin.exe.

Tuesday, November 10, 2009

Working with Salesforce Datetimes via DBAmp

I came across some SQL today that was setting Salesforce Date/Time fields with UTC values using DBAmp, which shows Salesforce as a linked Server.

When the results were viewed in Salesforce the values were off by 8 hours when the user was set to the UTC time zone.
NOTE: I found it much easier to work with a Salesforce user where the Time Zone was set to "(GMT+00:00) Greenwich Mean Time (Europe/Dublin)". This avoided any extra conversion occuring in the Salesforce GUI.

It turns out by default (as clearly stated in the Installation Guide) that DBAmp will convert the UTC values from Salesforce to the "local timezone". This conversion is occuring for inserts as well as queries.

After setting the HKEY_LOCAL_MACHINE / Software / DBAmp/ NoTimeZoneConversion registry key to 1 and restarting SQL Server the issue went away.

Friday, November 6, 2009

Finding the largest tables in a SQL Server database

I came across this handy script by Bill Graziano - Finding the biggest tables in a database. The following is the modified version from the forums.

Run DBCC UPDATEUSAGE('database') first if things seem a bit off with the results.

/**************************************************************************************
*
*  BigTables.sql
*  Bill Graziano (SQLTeam.com)
*  graz@sqlteam.com
*  v1.11
*
**************************************************************************************/

declare @id int   
declare @type character(2)   
declare @pages int   
declare @dbname sysname
declare @dbsize dec(15,0)
declare @bytesperpage dec(15,0)
declare @pagesperMB  dec(15,0)


create table #spt_space
(
 objid  int null,
 rows  int null,
 reserved dec(15) null,
 data  dec(15) null,
 indexp  dec(15) null,
 unused  dec(15) null
)

set nocount on

-- Create a cursor to loop through the user tables
declare c_tables cursor for
select id
from sysobjects
where xtype = 'U'

open c_tables

fetch next from c_tables
into @id

while @@fetch_status = 0
begin

 /* Code from sp_spaceused */
 insert into #spt_space (objid, reserved)
  select objid = @id, sum(reserved)
   from sysindexes
    where indid in (0, 1, 255)
     and id = @id

 select @pages = sum(dpages)
   from sysindexes
    where indid < 2
     and id = @id
 select @pages = @pages + isnull(sum(used), 0)
  from sysindexes
   where indid = 255
    and id = @id
 update #spt_space
  set data = @pages
 where objid = @id


 /* index: sum(used) where indid in (0, 1, 255) - data */
 update #spt_space
  set indexp = (select sum(used)
    from sysindexes
    where indid in (0, 1, 255)
    and id = @id)
       - data
  where objid = @id

 /* unused: sum(reserved) - sum(used) where indid in (0, 1, 255) */
 update #spt_space
  set unused = reserved
    - (select sum(used)
     from sysindexes
      where indid in (0, 1, 255)
      and id = @id)
  where objid = @id

 update #spt_space
  set rows = i.rows
   from sysindexes i
    where i.indid < 2
    and i.id = @id
    and objid = @id

 fetch next from c_tables
 into @id
end


select top 25
 Table_Name = (select left(name,25) from sysobjects where id = objid),
 rows = convert(char(11), rows),
 reserved_KB = ltrim(str(reserved * d.low / 1024.,15,0) + ' ' + 'KB'),
 data_KB = ltrim(str(data * d.low / 1024.,15,0) + ' ' + 'KB'),
 index_size_KB = ltrim(str(indexp * d.low / 1024.,15,0) + ' ' + 'KB'),
 unused_KB = ltrim(str(unused * d.low / 1024.,15,0) + ' ' + 'KB'),
 idx_data_ratio = (case data when 0 then '100%' else ltrim(str(indexp*100 /data) + '%') end),
 unused_pct = (case reserved when 0 then '0%' else ltrim(str(unused * 100 /reserved) + '%') end)
  
from  #spt_space, master.dbo.spt_values d
where  d.number = 1
and  d.type = 'E'
order by reserved desc

drop table #spt_space
close c_tables
deallocate c_tables

Tuesday, November 3, 2009

Forcing the ASP.NET Web Application Designer class to update

Every now and then a designer class will not update to reflect that changes occurring in the markup (aspx/ascx). The first sign is typically the loss of IntelliSense for controls you know are on the page/control followed by "The name 'your control here' does not exist in the current context" compile errors if you progress as far as a build.

Cutting and pasting markup seems to be a fairly reliable way of breaking the designer classes.

My current preferred solution is to delete the offending designer class and then use the "Convert To Web Application" option that becomes available in solution explorer. Note that this option isn't available until you remove the out of date designer file.

Useful links:

Friday, October 30, 2009

Calculating UPS Shipping Rate in C#

Some useful links for doing UPS shipping rate calculations.

  • UPS Rates & Service Selection
  • https://www.ups.com/ups.app/xml/Rate
    The XML service for retrieving the rate data
  • dtk_RateXML_V1.zip
    Look for
    /Developers Guide/RSS_Tool_06_10_09.pdf
    in the zip.
  • Use the Xml Schemas/DataTypes support utility (xsd.exe) from the Visual Studio command prompt to generate classes that you can pass through the System.Xml.Serialization.XmlSerializer class straight to a HttpWebRequest RequestStream or from the corresponding ResponseStream.
    • xsd.exe RateRequest_external.xsd /classes /l:cs /n:FishOfPrey.SampleNamespace
    • xsd.exe RateResponse_external.xsd /classes /l:cs /n:FishOfPrey.SampleNamespace

Friday, October 23, 2009

SOQL Pagination for Salesforce API queries with the S4S Data Connector

At the start of the month I asked a question of Stack Overflow about how to efficiently page through SOQL results. See Stack Overflow : SOQL Pagination for Salesforce API queries. As at today I haven't received any answers. There is Salesforce Ideas : SOQL Request: allow greater-than, less-than WHERE conditions for Id fields, but it requires starting a new query for each page.

I've taken a different approach with the Sitecore for Salesforce Data Connector (DC). Using the QueryResultPager I can jump to any page in the query results with minimal overhead.

The following is an example of paging over 10,000 Salesforce Leads from C#. In real world ASP.NET examples the QueryResultPager would be persisted between requests. The performance is a respectable 10 seconds in the debug build, which I think is pretty good considering I'm jumping all over the place with the page index and page size. More linear paging performs better.

using Microsoft.VisualStudio.TestTools.UnitTesting; //Only used for the Assert in the example
using FuseIT.Sitecore.SalesforceConnector.Entities;
using FuseIT.Sitecore.SalesforceConnector.Services;

//...

[TestMethod]
public void LeadsPagination()
{
    SalesforceSession salesforceSession = new SalesforceSession(
        new LoginDetails("username@example.com", "salesforcePassword"));
    LeadDataSource leadDataSource = new LeadDataSource(salesforceSession);
    string[] requiredLeadFields = new string[] { "Id", "Name", "Email", "Description" };
    QueryResultPager pager = leadDataSource.GetPager(requiredLeadFields);

    Assert.IsTrue(pager.TotalRecordCount > 10000, "The total number of records");

    var firstPage = pager.GetPage(salesforceSession, 0, 10); //Records 0 to 9
    List leads = leadDataSource.EntitysFromQueryResult(firstPage);

    var randomPage = pager.GetPage(salesforceSession, 30, 100); //Records 3000 to 3099
    leads = leadDataSource.EntitysFromQueryResult(randomPage);

    randomPage = pager.GetPage(salesforceSession, 1, 150); //Records 150 to 299
    leads = leadDataSource.EntitysFromQueryResult(randomPage);

    randomPage = pager.GetPage(salesforceSession, 17, 15); //Records 255 to 269
    leads = leadDataSource.EntitysFromQueryResult(randomPage);

    randomPage = pager.GetRecords(salesforceSession, pager.TotalRecordCount - 101, pager.TotalRecordCount - 1); //The last 100 leads
    leads = leadDataSource.EntitysFromQueryResult(randomPage);
}

Behind the scenes this is sending the SOQL query select Id, Name, Email, Description from Lead and then utilizing the QueryLocator.

For Salesforce Organizations with customizations, like fields, you would use the T4 generated classes that match your org rather than those in FuseIT.Sitecore.SalesforceConnector.Entities.
NB: These code snippets are applicable to the current 1.0.5 release.

Finding an existing Salesforce Account using the S4S Data Connector

The following is an example of creating a new Salesforce Account from C# using the Sitecore for Salesforce Data Connector (DC).

using Microsoft.VisualStudio.TestTools.UnitTesting; //Only used for the Assert in the example
using FuseIT.Sitecore.SalesforceConnector.Entities;
using FuseIT.Sitecore.SalesforceConnector.Services;

//...

[TestMethod]
public void CreateAccount()
{
    SalesforceSession salesforceSession = new SalesforceSession(
        new LoginDetails("username@example.com", "salesforcePassword"));
    AccountService accountService = new AccountService(salesforceSession);
    string accountNameToFind = "FuseIT";
    Account matchingAccount = accountService.GetSingleByFieldEquals("Name", accountNameToFind);
    Assert.IsNotNull(matchingAccount);
    Assert.AreEqual(accountNameToFind, matchingAccount.Name);
}

For Salesforce Organizations with customizations, like fields, you would use the T4 generated classes that match your org rather than those in FuseIT.Sitecore.SalesforceConnector.Entities.
NB: These code snippets are applicable to the current 1.0.5 release.

Creating a new Salesforce Account using the S4S Data Connector

The following is an example of creating a new Salesforce Account from C# using the Sitecore for Salesforce Data Connector (DC).
NB: These code snippets are applicable to the current 1.0.5 release.

For Salesforce Organizations with customizations, like fields, you would use the T4 generated classes that match your org rather than those in FuseIT.Sitecore.SalesforceConnector.Entities.

using Microsoft.VisualStudio.TestTools.UnitTesting; //Only used for the Assert in the example
using FuseIT.Sitecore.SalesforceConnector.Entities;
using FuseIT.Sitecore.SalesforceConnector.Services;

//...

[TestMethod]
public void CreateAccount()
{
    Account account = new Account();
    account.Name = "FuseIT";

    SalesforceSession salesforceSession = new SalesforceSession(
        new LoginDetails("username@example.com", "salesforcePassword"));
    AccountService accountService = new AccountService(salesforceSession);

    var saveResult = accountService.Insert(account);
    string newAccountId = null;
    if (saveResult.success)
    {
         newAccountId = account.Id;
    }
    Assert.IsTrue(accountService.ValidEntityId(newAccountId));
}

Thursday, October 22, 2009

Monday, October 19, 2009

Sitecore CMS 6 - Pagination from the shell User Manager with a custom Membership Provider

When I connected a custom Membership Provider to Sitecore.NET 6.1.0 (rev. 090821) I noticed the performance was degrading badly as the number of custom members increased. Some investigation into the calls coming out of Sitecore showed that pageIndex was always 0 and the pageSize always int.MaxValue regardless of the actual page required. I.e.:

//Sitecore requests the first 15 records - expected pageIndex = 0, pageSize = 15
CustomMembershipProvider.GetAllUsers(int pageIndex = 0, int pageSize = int.MaxValue, out int totalRecords);

//Sitecore requests the second 15 records - expected pageIndex = 1, pageSize = 15
CustomMembershipProvider.GetAllUsers(int pageIndex = 0, int pageSize = int.MaxValue, out int totalRecords); 

//Sitecore requests the third 15 records  - expected pageIndex = 2, pageSize = 15
CustomMembershipProvider.GetAllUsers(int pageIndex = 0, int pageSize = int.MaxValue, out int totalRecords); 

//...

I checked with Sitecore and this is a known issue that will be resolved in an upcoming release. In the short term it can be patched by adding the ManualPaging="true" attribute to the ComponentArt Grid in \sitecore\shell\Applications\Security\UserManager\UserManager.aspx.

Friday, October 16, 2009

ASP.NET Enumerate profile properties

    var propertyNames = new Dictionary<string, string>();

    var profileSection = (System.Web.Configuration.ProfileSection)ConfigurationManager.GetSection("system.web/profile");
    foreach (ProfilePropertySettings propertyDefinition in profileSection.PropertySettings)
    {
        var customProviderData = propertyDefinition.CustomProviderData;
        //...
    }

Sys.WebForms.PageRequestManagerParserErrorException and HttpContext Trace

Recently I started getting the following error from UpdatePanels in a ASP.NET site.

Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.
Details: Error parsing near '[initialvalue']|""|<div id="__asptrace"'.

Happily the error message pretty much points me straight at the issue: "server trace is enabled" and "__asptrace".

Turning the ASP.NET server trace off resolves the issue.

<trace enabled="true" pageOutput="false" requestLimit="40" traceMode="SortByTime" localOnly="true"/>

Monday, October 12, 2009

Accessing Session values for a HTTP Handler (ASHX)

When converting from a ASPX to ASHX for returning image data I ran into an issue with the HttpContext session being null. Turns out I just needed to add the IReadOnlySessionState interface to my IHttpHandler implementation.

An HTTP handler that needs to access session-state values must implement the IRequiresSessionState interface. Like INamingContainer, it's a marker interface and requires no method implementation. Note that the IRequiresSessionState interface indicates that the HTTP handler requires read and write access to the session state. If read-only access is needed, use the IReadOnlySessionState interface instead.
MSDN: Writing HTTP Handlers

Scott Hanselman: Getting Session State in HttpHandlers (ASHX files)

Thursday, October 8, 2009

Blog Disclaimer

After reading Computer World - Opinion: Website disclaimers – yes, they do work I thought it might be sensible to add a disclaimer that states users should "make their own, independent inquiries before acting on any information" [on this site] and that "all information is of a general nature only and must not be taken as advice.". A quick Google however turned up a much more thorough definition. Full credit goes to Herche’s Blog Disclaimer


http://www.fishofprey.com (hereafter “this website”) will abide by, adhere to, accept responsibility for, endure under and act with respect toward the following weblog disclaimer:

By accessing this website, a web browser (hereafter user) is consents that s/he is familiar with, understands and absolutely accepts the following weblog disclaimer:

The views expressed by the authors on this website do not necessarily reflect the views of this website, those who link to this website, the author's mother, father, sister, brother, uncle, aunt, grandparents, cousins, step relations, any other blood relative and the author himself, this website’s web host, template designer, or any other organization, service, motto, logo, insignia or avatar in any way connected with this website.

Comments on this website are the sole responsibility of their writers and the writer will take full responsibility, liability, and blame for any libel or litigation that results from something written in or as a direct result of something written in a comment. The accuracy, completeness, veracity, honesty, exactitude, factuality and politeness of comments are not guaranteed.

All trademarks, service marks, collective marks, design rights, personality rights, copyrights, registered names, mottos, logos, avatars, insignias and marks used or cited by this website are the property of their respective owners and this website in no way accepts any responsibility for an infringement on one of the above.

Although it may claim otherwise, this website does not offer legal, medical, psychiatric, veterinary, gynecological, archaeological, astronomical, astrological, ontological, paleontological, philosophical, axiological, audiological, bacteriological, mineralogical, criminological, terminological, dermatological, ecclesiastical, campanological, phrenological, phonological, technological, hematological, campanological, neurological, psychobiological, urological, ufological, typological,, mythological, hydrological, xylological, zoological, logical or any other kind of professional advice. Nothing on this website should be construed as professional advice including, but not limited to, the above list.

The information provided on this website is of a general, broad, sweeping, large, wide-ranging, wide-reaching and wide-spread nature and cannot substitute for the advice of a licensed professional or chiropractor. A competent authority with specialized knowledge is the only one who can address the specific circumstances of your predicament. We can try, but this disclaimer frees us of any liability if negative consequences result from our efforts.

Please contact your local bar association, law society, neighborhood association of jurists, medical board, county hospital, phone book, online directory, local emergency number in your jurisdiction, mother or Google to find a or obtain a referral to a competent professional. If you do not have reasonable means of contacting an attorney-at-law, lawyer, civil law notary, barrister, solicitor, medical professional, coroner or any other professional in the area of your inquiry, meaning you are an orphaned, computer-illiterate social hazard, please exit this window and get your life in order.

This website has no control over the information you access via outbound link(s) in the post text, sidebar, header, footer or comment sections. This website does not endorse linked website(s), cannot guarantee the accuracy of any information found by following said links or the correctness of any analysis found therein and should not be held responsible for it or the consequences of a user’s use of that information. If you are curious about the veracity of something you find, please follow the directions in the above paragraph and consult the appropriate experts.

This website may inadvertently link to content that is obscene, prurient, useless, hate-filled, poisonous, pornographic, frivolous, empty, rotten, bad, disgusting, hostile, repulsive, virulent, infectious, malignant, antagonistic, irritating, obnoxious, harsh, embittered, rancorous, resentful, acrimonious, pestilential, baneful, noxious, toxic, venomous, pernicious or repetitive. This website in no way condones, endorses or takes responsibility for such content.

This website publishes content regularly and said content is maintained in reference to the protections afforded it under local, provincial, state, martial, federal, international and mafia law. Publication of information found on this website may be in violation of the laws of the city, county, state, country or other jurisdiction from where you are viewing this website’s content and laws in your jurisdiction may not protect or allow the same kinds of speech or distribution. In the case that the laws of the jurisdiction where this website's content is maintained and those of yours conflict, this website does not encourage, condone, facilitate, recommend or protect the violation of any laws and cannot be responsible for any violations of such laws.

Because the World Wide Web is an integrated net of communication, discussion and litigation, this website encourages the distribution of its content. Cross, reciprocal or just plain friendly hyperlinking is consistent with this information sharing and this disclaimer should not be construed as a condemnation of any linking practices. That said, any reproduction of this website’s content must credit the website by name and Uniform Resource Locator (URL). Should you link to this domain or use, reproduce, republish, regurgitate, repeat, reiterate, rebound, reecho, reverberate, mimic, imitate, parrot or duplicate the information contained on this website, you alone are responsible for that action and should, under threat of litigation, credit this website by name and URL.

This website is not recommended for inmates, ingrates or anyone professing an irrational fear of cats or any other mammal, those who have a penchant for time wasting, illiterates and lawyers. Women who are pregnant or may become pregnant or are nursing are advised to consult their husband and physician before reading this website. Eating before reading may result in unhealthy indigestion. Not recommended for people over the age of 120.

Wednesday, September 30, 2009

Tech Ed 2009 - Visual Studio 2008 IDE Tips [DEV301]

I was watching Tech Ed Online - Visual Studio 2008 IDE Tips [DEV301] and thought I'd record the tips I was interested in.

Extracted from http://blogs.msdn.com/saraford/archive/2009/09/09/teched-australia-25-visual-studio-2008-ide-tips.aspx

5. You can use a reg hack for customizing search results
HKCU\Software\Microsoft\VisualStudio\9.0\Find String Find=$f$e($l,$c):$t\r\n

6. How not to accidentally copy a blank line
Tools – Options – Text Editor – All Languages – General, Uncheck Apply cut or copy to blank lines

22. You can use tracepoints to log stuff in your code
Right-click in indicator margin, select breakpoints, select Insert Tracepoint

Bidirectional data connection between Sitecore (CMS) and Salesforce (CRM)

I've been working a product called (S4S) that provides a number of ways to integrate Sitecore and Salesforce. This parallels the Sitecore (Microsoft) CRM Membership Provider but with Salesforce rather than Microsoft CRM.

One part (the Security Connector) provides custom ASP.NET Security Providers (Membership, Role and Profile) that uses the Salesforce Partner API to expose Contacts and Accounts as Users and Roles in Sitecore respectively. This is useful for companies who want to provide extranet logins in Sitecore for their Salesforce contacts. Using the standard ASP.NET providers makes the integration seemless for sitecore admins. I.e. You can use Salesforce derived users and roles within Sitecore in the same way you would use a native user and/or role. You use the same admin tools from the Sitecore Shell. The profile connection can be used to expose Salesforce Contact fields to Sitecore.

Some other goodness (the Data connector) makes bidirectional data transfer easier. At its core it is a entity based .NET wrapper around the Salesforce partner and metadata API's. This makes strongly typed CRUD (Create, Retrieve, Update, Delete) operations really simple. Internally we handles compressing the outbound and inbound API calls and caching the meta data to improve performance. There is also query building to produce dynamic SOQL. I've done some quick demos with standard ASP.NET GridViews and FormViews round tripping Salesforce data through Sitecore (remember to set typesThatShouldNotBeExpanded in the web.config first!). The is also a Generic entity service that can accommodate any Salesforce object. This is useful if the field definition is unknown or likely to change.

Finally there is the Report and Dashboard Connector that provides .NET API access to Salesforce Reports and Dashboards. With reports you can pull out the HTML or have it converted to a DataSet. By surfacing the graphs and charts from Salesforce rather than using static content keeps everything up to date with changes in the Salesforce data.

Tuesday, September 29, 2009

Sitecore CMS 6 - IndexOutOfRangeException from Sitecore.Shell.Applications.Security.UserManager

Something odd has occurred to my Sitecore 6 install. I've started getting the following exception from the Security Tools > User Manager:

Exception: System.IndexOutOfRangeException
Message: Index was outside the bounds of the array.
Source: ComponentArt.Web.UI
   at ComponentArt.Web.UI.GridItem.set_Item(Object obj, Object value)
   at Sitecore.Shell.Applications.Security.UserManager.UserManager.Users_ItemDataBound(Object sender, GridItemDataBoundEventArgs e)
   at ComponentArt.Web.UI.Grid.ItemDataBoundEventHandler.Invoke(Object sender, GridItemDataBoundEventArgs e)
   at ComponentArt.Web.UI.Grid.LoadGridItems(GridItemCollection arItems, Object[] arObjects, Int32 iLevel)
   at ComponentArt.Web.UI.Grid.DataBindToEnumerable(IEnumerable arList)
   at ComponentArt.Web.UI.Grid.DataBind()
   at Sitecore.Web.UI.Grids.ComponentArtGridHandler`1.DataBind()
   at Sitecore.Web.UI.Grids.ComponentArtGridHandler`1.InitializeGrid(Boolean dataBind)
   at Sitecore.Web.UI.Grids.ComponentArtGridHandler`1..ctor(Grid grid, IGridSource`1 source, Boolean dataBind)
   at Sitecore.Web.UI.Grids.ComponentArtGridHandler`1.Manage(Grid grid, IGridSource`1 source, Boolean dataBind)
   at Sitecore.Shell.Applications.Security.UserManager.UserManager.OnLoad(EventArgs e)
   at System.Web.UI.Control.LoadRecursive()
   at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

Using reflector I found the code in question:

private void Users_ItemDataBound(object sender, GridItemDataBoundEventArgs e)
{
    e.Item["Profile.PortraitFullPath"] = Themes.MapTheme((e.DataItem as User).Profile.Portrait);
}

There appears to be an issue with the Portrait profile profile property not existing for the GridItem.


Turns out the issue was caused by the aspx files under \sitecore\ in the web site being out of sync with the Sitecore DLLS. In particular, I had Sitecore.Client.dll (6.0.1.2906) and an out of date version of \sitecore\shell\Applications\Security\UserManager\UserManager.aspx. I believe this can about by trying to keep Sitecore under TFS source control. At some point an update package was installed but the changes to the sitecore folder never found their way into source control.

Monday, September 28, 2009

Adding FLV files to the TFS build output

I have several flash videos that I need to include in an ASP.NET web site. From the build servers perspective they should be static content.

For some reason the TFS build server wasn't copying them over to the Binary drop location as part of the _CopyWebApplication target but the SWF files were.

Turned out to be a simple fix. The SWF files were set as "Build Action" "Content" while the FLV files were "Build Action" "None". Changing them to content fixed the problem.


On a side note. I needed to add the MIME type for FLV files to IIS.
.flv   video/x-flv

Friday, September 25, 2009

Arggg - websites with broken email address validation

Gmail offers a handy feature for adding email address aliases by appending a '+' and a term to the end of your email address. See Using an address alias and Wikipedia:Sub-addressing

The issue I keep running into is incorrect email address validation preventing the use of characters that should be valid according to RFC 5322 and RFC 3696.

What annoys me most is that there often isn't an easy way to tell a site that there is a bug. Some problematic sites I've come across:

From some quick Googling it may be that RegEx just won't cut it for email address validation.

Monday, September 21, 2009

Finding missing SATA hard drive after upgrading to Windows 7

I recently upgraded to Windows 7 buy adding an additional SATA hard drive and doing a fresh install. After logging in I found the older existing drive wasn't appearing in Windows 7.

I found I needed to bring up Disk manager (diskmgmt.msc) and map it to a drive letter.

Lake Wanaka - Island within an island within an island (Mou Waho Island)

Caught a bit of South on TV last night and thought I'd find the location they found with a small island sitting inside a larger island in Lake Wanaka. Turns out the island is called Mou Waho Island and the smaller "island" is in the "Arethusa Pool".


View Larger Map

Thursday, September 17, 2009

Microsoft launches hosted JavaScript libraries

Microsoft has launched the Microsoft Ajax CDN (Content Delivery Network) service that provides caching support for AJAX libraries. In addition to being an alternative to the Google AJAX Libraries API it looks like it will add support for the ASP.NET JavaScript libraries.

See Also:

Visual Studio Macros to clean up HTML tables from Microsoft Word

The following Visual Studio Macros can be used to strip an HTML table copied from word down to it's core tags.
It's a little(very) rough around the edges but it seems to get the job done.

Sub CleanWordTable()

        FlattenStyles()

        DTE.ExecuteCommand("Edit.Replace")
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.FindWhat = "<o:p></o:p>"
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.Execute()

        DTE.ExecuteCommand("Edit.Replace")
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.FindWhat = "class=""MsoNormal"""
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.Execute()

        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.FindWhat = "\<span.@\>"
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.Execute()

        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.FindWhat = "\</span\>"
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.Execute()

        'DTE.ExecuteCommand("Edit.FormatSelection")

        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.FindWhat = "style="".@"""
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.Execute()

        ''DTE.ExecuteCommand("Edit.FormatSelection")

        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.FindWhat = "style="".@(\n.@)"""
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.Execute()

        DTE.ExecuteCommand("Edit.FormatSelection")

        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.ReplaceWith = "<td>"
        DTE.Find.FindWhat = "\<td.@\>"
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Execute()

        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.FindWhat = "align=""right"""
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Execute()

        DTE.ExecuteCommand("Edit.FormatSelection")

        DTE.Find.FindWhat = "<p>"
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
            Throw New System.Exception("vsFindResultNotFound")
        End If

        DTE.Find.FindWhat = "</p>"
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
            Throw New System.Exception("vsFindResultNotFound")
        End If

        DTE.Find.FindWhat = "</b>"
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
            Throw New System.Exception("vsFindResultNotFound")
        End If

        DTE.Find.FindWhat = "<b>"
        DTE.Find.ReplaceWith = ""
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
        DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        If (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
            Throw New System.Exception("vsFindResultNotFound")
        End If

        DTE.ExecuteCommand("Edit.FormatSelection")
    End Sub

    Sub FlattenStyles()
        ' Recursively Flatten style tags that span multiple lines
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocumentSelection
        DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
        DTE.Find.FindWhat = "{style=""[^""]@}\n"
        DTE.Find.ReplaceWith = "\1"
        DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
        DTE.Find.MatchCase = True
        DTE.Find.MatchWholeWord = False
        DTE.Find.Backwards = False
        DTE.Find.MatchInHiddenText = True
        DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxRegExpr
        If Not (DTE.Find.Execute() = vsFindResult.vsFindResultNotFound) Then
            FlattenStyles()
        End If
    End Sub

Friday, September 11, 2009

Salesforce WebServices SOAP Compression - Response is not well-formed XML. - '', hexadecimal value 0x1F, is an invalid character

I've been getting the following error when trying to login to the Salesforce Partner API with invalid credentials and SOAP Compression enabled. Valid credentials and subsequent API calls have been working fine.

System.InvalidOperationException: Response is not well-formed XML.
--->  System.Xml.XmlException: '', hexadecimal value 0x1F, is an invalid character.
Line 1, position 1..

The exception was coming out of the following method from the GzipWebRequest as defined here:

    public override WebResponse GetResponse()
    {
        return new GzipWebResponse(wr.GetResponse ());
    }

It turns out this is truly ancient .NET 1.1 code that is no longer applicable for .NET 2.0 and later projects. This has been the case for some time, I've just never gone into this part of the code base as it hasn't been failing until now.

Rather than manually accepting and decompressing the response stream we now just set EnableDecompression to true when inheriting the generated HttpWebClientProtocol (the Generated Proxy Client).

There is still the need to manually compress the outbound request. Improvements can be made here too by ditching the third party ICSharpCode.SharpZipLib.dll and using the built in System.IO.Compression.GZipStream class. There isn't anything wrong with the SharpZipLib but it's one less DLL to cart around.

See also:

Privacy Preferences Project (P3P) header for setting session cookies when nested in an iframe

This can be setup in IIS as CP="CAO DSP IVAa IVDa OUR BUS UNI OTC". Alternatively, I've added the following code to the Page_Load method of the master page.

        string p3pHeader = "CP=\"CAO DSP IVAa IVDa OUR BUS UNI OTC\"";
        if (!string.IsNullOrEmpty(p3pHeader))
        {
            //P3P headers will typically be set in IIS. This is more useful when running locally from Visual Studio.
            HttpContext.Current.Response.AddHeader("p3p", p3pHeader);
        }

Wednesday, September 9, 2009

WCF - Custom tool error: Failed to generate code for the service reference

Error 240 Custom tool error: Failed to generate code for the service reference 'FooService'.  
Please check other error and warning messages for details. 
C:\Development\SolutionName\ProjectName\Service References\FooService\Reference.svcmap
  • Try "Update Service Reference"
  • Try "Configure Service Reference" and unchecking "Reuse types in referenced assemblies"
  • Try searching through the generated files (.disco, .wsdl, .xsd) for path references that differ from the address set in the step above.
  • Try dropping the entire service reference and adding it again.

Tuesday, September 8, 2009

TF20017: The area or iteration provided for field 'Iteration Path' could not be found.

When attempting to update a TFS work item I got the following error prompt:

To add the new iteration from the menu: Team > Team Project Settings > Areas and Iterations...

Then jump to the Iteration tab and use the "Add a child node" button.

Refreshing the TFS cached iteration list

  1. Close Visual Studio to release any file locks in the cache.
  2. Delete all the files in:
    C:\Documents and Settings\[USER]\Local Settings\Application Data\Microsoft\Team Foundation\2.0\Cache
    or
    C:\Users\[USER]\AppData\Local\Microsoft\Team Foundation\2.0\Cache
    depending on your OS.

Tuesday, August 18, 2009

Detecting when .netTiers is interrogating a stored procedures

During code generation using the .netTiers templates the stored procedures will be called to determine their outputs. If the stored procedures don't return the correct results netTiers will only provide IDataReader as a return type rather than a strongly typed object.

To resolve this use the SQL user_name() function to determine if netTiers is calling the stored procedure and return data that will generate the correct structure.

-- Check if netTiers is trying to discover how this stored proc works and return a dummy record with the expected result set.
 IF user_name() IS null
 BEGIN
  -- Return dummy data to netTiers with the expected data types
  SELECT TOP 1 * FROM [dbo].[SampleView]

  SELECT 1 as TotalRowCount

  RETURN
 END

It will pay to check your custom stored procedure works with FMTONLY. NetTiers will call the stored procedure with FMTONLY on and null for all of the parameters.

SET FMTONLY ON;

EXEC dbo.custom_sp_MyTestSproc @param1 = NULL, @param2 = NULL, @param3 = NULL

SET FMTONLY OFF;

I found this caused issues when using temporary tables. I.e.:

Msg 208, Level 16, State 0, Procedure custom_sp_StoredProcedureName, Line 160
Invalid object name '#TheTempTable'.

If this is the case you can turn FMTONLY OFF in the in stored procedure before creating the temporary table and then back on again afterward. E.g.

-- ...
DECLARE @fmtonlyon bit
SELECT @fmtonlyon = 0
IF 1 = 0 
BEGIN
 --This block will only be reached when FMTONLY is ON
 SELECT @fmtonlyon = 1
 SET FMTONLY OFF
END
    
CREATE TABLE #TheTempTable
  ( 
   -- ...
  )

--USE test here as FMTONLY will be disabled
IF @fmtonlyon = 1 SET FMTONLY ON

Cannot insert an explicit value into a timestamp column.

When creating a temporary table variable I needed to store a timestamp that was being sourced from another table. When attempting to insert that values into the timestamp column to following error message appeared:

Cannot insert an explicit value into a timestamp column. Use INSERT with a column list to exclude the timestamp column, or insert a DEFAULT into the timestamp column.

According to books online:

A nonnullable timestamp column is semantically equivalent to a binary(8) column. A nullable timestamp column is semantically equivalent to a varbinary(8) column.

So changing the column in the temporary table from timestamp to binary(8) resolved the issue.

Tuesday, August 11, 2009

Sitecore 6 - CryptographicException: Padding is invalid and cannot be removed.

I've been getting the following exception from sitecore fairly frequently from my local IIS.

[CryptographicException: Padding is invalid and cannot be removed.]
   System.Security.Cryptography.RijndaelManagedTransform.DecryptData(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount, Byte[]& outputBuffer, Int32 outputOffset, PaddingMode paddingMode, Boolean fLast) +2910
   System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock(Byte[] inputBuffer, Int32 inputOffset, Int32 inputCount) +286
   System.Security.Cryptography.CryptoStream.FlushFinalBlock() +51
   System.Web.Configuration.MachineKeySection.EncryptOrDecryptData(Boolean fEncrypt, Byte[] buf, Byte[] modifier, Int32 start, Int32 length, IVType ivType, Boolean useValidationSymAlgo) +318
   System.Web.Security.FormsAuthentication.Decrypt(String encryptedTicket) +290
   Sitecore.Security.Authentication.AuthenticationHelper.GetCurrentUser() +473
   Sitecore.Security.Authentication.AuthenticationHelper.GetActiveUser() +17
   Sitecore.Security.Authentication.AuthenticationProvider.GetActiveUser() +21
   Sitecore.Security.Authentication.AuthenticationManager.GetActiveUser() +39
   Sitecore.Context.get_User() +17
   Sitecore.DateUtil.ParseTimeSpan(String time) +51
   Sitecore.Configuration.Settings.GetTimeSpanSetting(String name, TimeSpan defaultValue) +106
   Sitecore.Caching.CacheManager.InitializeScavenging() +70
   Sitecore.Caching.CacheManager..cctor() +92

I've tracked it back to the following sitecore code in Sitecore.Security.Authentication.AuthenticationHelper.GetCurrentUser():

protected User GetCurrentUser()
{
    HttpContext current = HttpContext.Current;
    if (current != null)
    {
        IPrincipal user = HttpContext.Current.User;
        if (user == null)
        {
            HttpCookie cookie = current.Request.Cookies[FormsAuthentication.FormsCookieName];
            if ((cookie != null) && !string.IsNullOrEmpty(cookie.Value))
            {
                FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value);
                if (!string.IsNullOrEmpty(ticket.Name))
                {
                    return this.GetUser(ticket.Name, true);
                }
            }
            return null;
        }
        // omitted code
        return null;
    }
    // omitted code
    return null;
}

The issue around line 473 (12 in the sample above) is in decrypting the value of the Forms Authentication cookie.

Possible Solution 0

Install Sitecore CMS 6.3.0 rev.100716 or higher. From the release notes:

When receiving an invalid authentication cookie (which may occur when switching between Sitecore instances in the IIS or when running a load balanced setup where each web application uses different machine keys), Sitecore would sometimes throw a "CryptographicException: Padding is invalid and cannot be removed" error and return a "500 Server Error" response to all requests hitting the web server. The CryptographicException will still be written to the log file to inform about improper configuration, while all requests passing invalid cookies are processed under the context of Anonymous user. (320777)

Possible Solution 1

If you are hosting with IIS7 try making adding the following to the global.asax (found at Running Sitecore 6 on Windows 7’s IIS but equally applies to Windows Server 2008):

public void Application_Start() {
      System.Security.Cryptography.RSACryptoServiceProvider.UseMachineKeyStore = true;
      System.Security.Cryptography.DSACryptoServiceProvider.UseMachineKeyStore = true;
  }

Possible Solution 2

Try clearing out the browsers cookies - in particular the Forms Authentication Cookie that ASP.NET creates.

Possible Solution 3

Try restarting/recycling the application pool in IIS.

Possible Solution 4

This may be applicable if you are running multiple versions of the site locally on different ports. Change these settings so that they are unique for each of your local sites.

Change your web.config Authentication Form name, which is used for the cookie name. Below the ".XXXXXXXX" is usually defaults to ".ASPXAUTH".

<authentication mode="None">
   <forms name=".XXXXXXXX" cookieless="UseCookies"/>
</authentication>

Add a unique machineKey in the web.config system.web node.

<system.web>
  <!-- Machine key attributes -->
  <machineKey validationKey="3SAMPLE122EA7A3F74BBEAD8450366243D982677F5243660BBAE3281839E013C2CEE7CF18BB85DC00C207E861B309FB075C51F74DFEB5FC4B72E01277610SAMPLE" 
       decryptionKey="060B29B5E05ESAMPLE42884148138D39B2ED861755A5E64AE370F6473E8SAMPLE" 
       validation="SHA1" decryption="AES" />

</system.web>

Possible Solution 5

Day to day I may have multiple separate instances of Sitecore running on my local IIS for developing for different clients. I'll typically be accessing them as http://localhost:8080 or http://127.0.0.1:8080 with different ports depending on the instance.

I often seem to come across the padding exception when switching between the multiple sites (by just changing the port).

My new theory is to setup a mock domain in my hosts file and change the site binding in IIS to use this new binding on port 80. I then use the new mock domain to access Sitecore from the browser.

See Also

Monday, August 10, 2009

Obsolete - HttpContext.Current.Request.Browser.JavaScript

'System.Web.Configuration.HttpCapabilitiesBase.JavaScript' is obsolete: 'The recommended alternative is the EcmaScriptVersion property. A Major version value greater than or equal to 1 implies JavaScript support. http://go.microsoft.com/fwlink/?linkid=14202'

Try using the following instead:

(HttpContext.Current.Request.Browser.EcmaScriptVersion.Major > 1)

See also: HttpBrowserCapabilities.EcmaScriptVersion

Thursday, August 6, 2009

Salesforce APEX "System.QueryException: List has no rows for assignment to SObject"

The assignment to a single object requires the query to return a single row, if the query may not return a row, you should assign it to an array, then examine the size of the array to determine if you got a row or not, and take the appropriate action.
http://forums.sforce.com/sforce/board/message?board.id=apex&thread.id=3470

Salesforce APEX "Log filter in effect"

When trying to debug a managed package I'm seeing the following message in the Debug Logs.

Log filter in effect. Executing code from installed managed application

This is "by design" and means you can't see debug level log messages - http://community.salesforce.com/sforce/board/message?board.id=apex&message.id=12460#M12460

Update 13/02/2012
The idea Display managed package logs in subscriber orgs is marked as delivered but doesn't actually say what was delivered. I've raised the following question on the forum to try and find out - Logging from a deployed managed package.

Thursday, July 23, 2009

Salesforce APEX "Invalid bind expression type of Datetime for column of type Date"

When trying to compare a Date field with a DateTime field in an APEX SOQL query I got the following error:

Invalid bind expression type of Datetime for column of type Date

The solution was to use date.newinstance(year, month, day) rather than the raw DateTime value in the comparison. Year, month, and day are found using the methods of the same name on the DateTime object.

Tuesday, July 21, 2009

Nelson .NET User Group Presentation - Best practice - Caching - 19th of August

Upcoming presentation

Kirk Jackson will be giving a presentation on Wednesday the 19th of August.

Title:
Best practice - Caching

Abstract:
At some point in your application development, you'll need to start caching data or the output of your program. Scalability, performance, bytes transferred, speed, reliability and robustness can all be improved through judicious use of caching. The first half of the talk will cover the types of caching and motivations for using them -- through all layers of your app: SQL, C#, ASP.NET and through to the browser cache. The second half of the talk will briefly cover memcached, which is used by all the cool kids (YouTube, Facebook, Trade Me), and then introduce the Microsoft project code named Velocity (currently CTP), which brings all that coolness and more to the .NET platform.

Useful links:

When:
Wednesday 19th August 2009
Gather at 11:50 am, starting at 12:00 pm.
Approximately 1 hour plus pizza afterward.

Where:
FuseIT Ltd,
Ground Floor,
7 Forests Rd,
Stoke,
Nelson
(Off Nayland Rd and behind Carters)

http://local.live.com/default.aspx?v=2&cp=-41.299774~173.236231&style=r&lvl=16&alt=-1000
or
http://maps.google.com/?ie=UTF8&om=1&z=17&ll=-41.299774,173.236231&spn=0.005239,0.010042&t=h

If you are parking on site, please use the parks marked FuseIT that are at the back of the site.

Giveaways:
A Windows Game

Catering: Pizza & Drinks
Door Charge: Free

RSVP to me if you are going to attend so I can guesstimate the food and drink requirements.

However, feel free to turn up on the day though if you can't commit at the moment.

Please feel free to invite anyone who may be interested in attending.

Monday, July 6, 2009

Sitecore CMS 6 - Error 27506 during installation

Error 27506.Error executing SQL script attach. Line 5. Directory lookup for the file "D:\websites\instance name\Databases\\Sitecore.Core.MDF" failed with the operating system error 3(error not found). (5133)

The Sitecore installation wizard kept failing at the Executing SQL Install Script step when installing to a remote database. Running the install from the database server avoided the issue.

I've also seen the error appear as:

Error -2147217900: failed to execute SQL string, error detail: Directory lookup for the file "D:\websites\instance name\Databases\MDF\...\Sitecore.Core.ldf') FOR ATTACH"

Friday, June 26, 2009

Tuesday, June 23, 2009

Postback issues with Sitecore 6 - typesThatShouldNotBeExpanded

I've run into this a couple of times now. An ASP.NET control, say the FormView, posts back to the server to change the page contents, say transitioning from the view to edit mode. The page loads, but some of the state or the postback event get lost along the way.

If the same control/code works just fine outside of Sitecore start looking for a solution with the rendering/typesThatShouldNotBeExpanded element in the web.config.

According to ListView and DataPager under Sitecore context"

As Sitecore runs [the] "layout pipeline" during [the] page rendering process, some of the complex ASP.NET controls may fail due to changes in the control tree. Adding type to the mentioned list allows affected ASP.NET controls to work the way they were initially designed.
<!-- RENDERING -->
  <rendering>
      <typesThatShouldNotBeExpanded>
        <type>System.Web.UI.WebControls.DropDownList</type>
        <type>System.Web.UI.WebControls.GridView</type>
        <type>System.Web.UI.WebControls.Repeater</type>
        <type>System.Web.UI.WebControls.DataList</type>
        <type>System.Web.UI.WebControls.FormView</type>
        <!-- Fix issue with the LoginStatus logout link not working when nested in a LoginView -- >
        <type>System.Web.UI.WebControls.LoginView</type>
      </typesThatShouldNotBeExpanded>
  </rendering>

See also:

Thursday, June 18, 2009

Extending System.Web.UI.WebControls.Style to handle all style attributes. E.g. text-align

When using the Page.Header.StyleSheet.CreateStyleRule method I ran into a limitation of the System.Web.UI.WebControls.Style class. I could only set a small subset of the available CSS attributes. In particular, I couldn't set text-align using the WebControls.Style class.

By overriding the Style.FillStyleAttributes method I can add additional CSS attributes to the CssStyleCollection.

class ExtendedStyle : Style
{
    private Dictionary<System.Web.UI.HtmlTextWriterStyle, string> extendedAttributes;

    public string TextAlign
    {
        get
        {
            return GetAttribute(System.Web.UI.HtmlTextWriterStyle.TextAlign);
        }
        set
        {
            SetAttribute(System.Web.UI.HtmlTextWriterStyle.TextAlign, value);
        }
    }

    private string GetAttribute(System.Web.UI.HtmlTextWriterStyle attribute)
    {
        return (extendedAttributes.ContainsKey(attribute))?extendedAttributes[attribute]:null;
    }

    private void SetAttribute(System.Web.UI.HtmlTextWriterStyle attribute, string value)
    {
        if (value == null)
        {
            extendedAttributes.Remove(attribute);
        }
        else
        {
            extendedAttributes[attribute] = value;
        }
    }

    public ExtendedStyle()
    {
        extendedAttributes = new Dictionary();
    }

    protected override void FillStyleAttributes(System.Web.UI.CssStyleCollection attributes, System.Web.UI.IUrlResolutionService urlResolver)
    {
        base.FillStyleAttributes(attributes, urlResolver);

        foreach (System.Web.UI.HtmlTextWriterStyle attribute in this.extendedAttributes.Keys)
        {
            attributes[attribute] = extendedAttributes[attribute];
        }
    }
}

Which allows me to do the following:

Page.Header.StyleSheet.CreateStyleRule(new ExtendedStyle() { TextAlign = "right" }, this, ".foobar");

Converting between HTML Hex color, int, System.Drawing.Color

using System.Drawing;
//...

        [TestMethod]
        public void ColorTest()
        {
            //HTML hex color values and expected int conversions
            Dictionary<string, int> testValues = new Dictionary<string, int> {
            { "#000000", 0 },
            { "#000001", 1 },
            { "#000100", 256 },
            { "#010000", 65536 },
            { "#666666", 6710886 },
            { "#AAAAAA", 11184810 },
            { "#FFFFFF", 16777215 },
            { "#C25454", 12735572 },
            { "#54C254", 5554772 }
            };

            foreach (string htmlColor in testValues.Keys)
            {
                //Drop the #
                string hexString = htmlColor.Substring(1);

                //convert the 6 char hex color value to an int
                //similar to the JavaScript parseInt(hexString, 16) function
                int convertedValue = Convert.ToInt32(hexString, 16);
                int convertedValueAlternative = int.Parse(hexString, System.Globalization.NumberStyles.HexNumber);
                Assert.AreEqual(convertedValue, convertedValueAlternative);

                //convert the int to the 6 char hex color value
                string hexStringFromInt = convertedValue.ToString("X").PadLeft(6, '0');
                Assert.AreEqual(hexString, hexStringFromInt);

                //Check that the converted value matches the expected value
                int expectedValue = testValues[htmlColor];
                Assert.AreEqual(expectedValue, convertedValue);

                //Get the System.Drawing.Color from the hex color value
                Color c = ColorTranslator.FromHtml(htmlColor);
                //Get the hex color value from the System.Drawing.Color
                string htmlHexColor = ColorTranslator.ToHtml(c);
                Assert.AreEqual(htmlColor, htmlHexColor);
            }
        }

Friday, June 12, 2009

Determining your Sitecore CMS6 Version

The Sitecore version appears on the shell Login page. Which is typcally at http://domain/sitecore/login/

Sitecore CMS6 Version

It may also pay to check the DLL versions as the interface might be lying about the actual version.

From code try:

  • Sitecore.Configuration.About.GetVersionNumber
  • Sitecore.Configuration.About.VersionInformation
  • ~/sitecore/shell/sitecore.version.xml

Wednesday, June 10, 2009

Enabling Sitecore CMS6 logging in tests

I ran into an issue when writing unit tests code that used the Sitecore CMS6 Sitecore.Disagnostics.Log class. My App.config had a log4net section but nothing was being logged to the appenders.
Using reflector I found out the Sitecore.Diagnostics.Log.Enabled returns Sitecore.Configuration.Settings.ConfigurationIsSet. So I added the configSection:

<section name="sitecore" type="Sitecore.Configuration.ConfigReader, Sitecore.Kernel" />

and a stub sitecore section to get things working.

Tuesday, June 9, 2009

SettingsPropertyValue.PropertyValue throws a NullReferenceException

    SettingsPropertyValue settingsPropertyValue = new SettingsPropertyValue(settingsProperty);
    settingsProperty.PropertyType = typeof(string); //Must set the type to use the PropertyValue
    settingsPropertyValue.PropertyValue = "FooBar";
    Assert.AreEqual("FooBar", settingsPropertyValue.PropertyValue);

If the SettingsProperty.PropertyType isn't set you will get a NullReferenceException when trying to access the PropertyValue.

See Also: Profile Provider Exception

Friday, June 5, 2009

Nelson .NET User Group Presentation - ASP.NET MVC - 24th of June

Upcoming presentation

Owen Evans will be giving a presentation on Wednesday the 24th of June.

Title:
ASP.NET MVC

Abstract:
This is TBC. In general it will be covering -

  • What is MVC?
  • What is the ASP.NET MVC Framework?
  • How to get started in MVC?
  • What are the main concerns moving from WebForms to MVC?
  • Will MVC suit me?
  • Q&A?

Useful links:

When:
Wednesday 24th June 2009
Gather at 11:50 am, starting at 12:00 pm.
Approximately 1 hour plus pizza afterward.

Where:
FuseIT Ltd,
Ground Floor,
7 Forests Rd,
Stoke,
Nelson
(Off Nayland Rd and behind Carters)

http://local.live.com/default.aspx?v=2&cp=-41.299774~173.236231&style=r&lvl=16&alt=-1000
or
http://maps.google.com/?ie=UTF8&om=1&z=17&ll=-41.299774,173.236231&spn=0.005239,0.010042&t=h

If you are parking on site, please use the parks marked FuseIT that are at the back of the site.

Giveaways:
A Windows Game

Catering: Pizza & Drinks
Door Charge: Free

RSVP to me if you are going to attend so I can guesstimate the food and drink requirements.

However, feel free to turn up on the day though if you can't commit at the moment.

Please feel free to invite anyone who may be interested in attending.