Thursday, December 18, 2008

A tricky side effect of Firefox's "remember password" feature

I think this is worth sharing.

I have a page that allows customer to modify their account password. The password field and 'password again' field are pre-filled with user's password (encrypted). (I know this is not the best practice and they should be left blank - this is a purchased software package).

What happens is that if no change made to the password, the client script that verifies password match always report unmatched password. The HTML source code does show both password fields are the same. But somehow this code
document.getElementById("password").value
always gets user's real password, not the encrypted one in the HTML source code. It turns out that it's the Firefox's "Remember Password" feature automatically puts in the remembered password, regardless the specified value in the source.

Lesson learned: use different ID for login and password change screen. However the best practice is to leave it blank and not to update it if password is blank.

Friday, December 12, 2008

Cache with HttpContext.Current.Items

This is a often overlooked yet very powerful cache option. In my BlogEngine multiple user project, multiple components of the page need to resolve blogName/ID, user roles etc. Since each time same user may hit different blog and this user will have different role for each blog, this is a ever-changing value. This per-request cache is a perfect solution.
if (HttpContext.Current.Items["blogName"] != null)

return HttpContext.Current.Items["blogName"].ToString();
else
// parse the blogName

SLOX RecordChanges not working

It's confirmed that "RecordChanges" checkbox on a controls property doesn't always work (SLXDev forum). Today I testified it too:
select * from sysdba.history where datediff( hour, startdate, GetUTCDate()) < 48
It only gives me a handful records while there are so many updates. The returned history are all in default SLOX objects. So I guess the custom objects/tables (e.g. Policy etc) have problems. I was thinking to put change logging in database triggers, but a few concerns:
  • it may not always have valid ModifyUser value
  • to add a new field to be logged is tedious and error-prone
  • when we remove a database field, we have to remember to remove it from the trigger
  • any unhandeled error will prevent saving.
That said, this isn't a very practical approach.

Redirect any pages in a old site to corresponding pages in new site

This mostly involves moving a site to another hosting company, since we can't simply change the IP address of the new server to be the DNS-registered IP. DNS will be changed to the new IP but it takes hours for it to populate to all ISPs. We can blindly redirect all request to the home page of the new server, but that means no matter what page/querystring user requests, he/she will see the home page. Not very user friendly.

Step 1: register a new sub domain (e.g. new.myblog.com), so that user's ISP will get the brand new IP address.

Step 2: in old server, add a Wildcard application maps to have ASP.NET engine handle all requests (html, gif etc)

Step 3: in global.asax, redirect user in Application_BegineRequest:
string url = HttpContext.Current.Request.Url.AbsolutePath;
string QueryParams = HttpContext.Current.Request.QueryString.ToString();
if ( QueryParams != "")
{ Response.Redirect("http://new.myblog.com" + url + "?"+ QueryParams); }
else
{ Response.Redirect("http://new.myblog.com" + url ); }

Tuesday, December 2, 2008

DateTimeEdit Readonly and UTC conversion

If a DateTimeEdit control is set to ReadOnly, user can still modifiy it through the popup date picker and the change WILL be saved. So uncheck "Enabled" is safer.

=============================================

SLOX DB provider has Date and DateTime 2 different data types - even though in SQL server they are all created as DateTime. However SLOX DB provider treats them differently. If it's a DateTime type, it converts it to UTC time and convert back to local time when getting it. However if it's a Date type, when saving the data, SLOX DB provider will always use the local date value and either 1) use current local time as time 2) use 00:00:00. (Among other scenarios, if the editing is done directly in a grid view control, 00:00:00 will be used) When retrieving the data, it doesn't do any conversion - except the Crystal Report, in which case it always try to convert to local time. This is annoying.

Some of the fields should be date only but were created as DateTime type. It created problem when I try to copy that field to another table which I created the field as Date type. The copying was done in SQL so SLOX doesn't get a chance to do the appropriate conversion. The outcome is some dates are displayed one day ealier in UI. I had to do the UTC to local conversion in SQL query to fix it.

However since it always converts to local time regardless of date type in Crystal Report, I would recommend to always use DateTime type.

===========================================

Internally, SLX stores it's schema information in SECTableDefs table. This command will list all fields that has Date type:
select * from sysdba.sectabledefs where datetimetype='D'
'U' in above field indicates a DateTime type.

The Architech interface does not allow you to change the data type of an existing field. However here is a workaround in database level:
  1. UPDATE sysdba.sectabledefs SET datetimetype='U' WHERE TableName='[my-table]' AND FieldName='[my-field]' AND datetimetype='D' -- this changes the date type
  2. UPDATE [my-table] SET [my-field] = DateAdd(hour, 8, [my-field]) WHERE DatePart(hour, [my-field]) < 8 -- this fixes the existing data so that it works for conversion.

Monday, November 24, 2008

GetSubDomain

private static string GetSubDomain(Uri url)
{
if (url.HostNameType == UriHostNameType.Dns)
{
string host = url.Host;
if (host.Split('.').Length > 2)
{
int lastIndex = host.LastIndexOf(".");
int index = host.LastIndexOf(".", lastIndex - 1);
return host.Substring(0, index);
}
}

return null;
}

From: http://www.webpronews.com/expertarticles/2006/11/30/retrieve-subdomain-from-a-url-in-c

Tuesday, November 18, 2008

script to shink log

First switch to that database,then run this:

==================================

DECLARE @DB varchar(100)
SET @DB = DB_NAME()
BACKUP LOG @DB WITH TRUNCATE_ONLY
DBCC SHRINKDATABASE (@DB, TRUNCATEONLY )


==================================
It worked very well for me in SQL 2005.

This works too but not very convenient:

alter database <mydb> set recovery simple
go

checkpoint
go

alter database <mydb> set recovery full
go

backup database pubs to disk = 'c:\mydb.bak' with init
go

dbcc shrinkfile (N'mydb_log' , 1)
g

Wednesday, October 29, 2008

Fix user mapping after restoring database to another server instance

Often when you restore a backup from SQL server you run into funky
problems with users. Suppose you have login calvin and you restore a
database from another server that already has user called calvin. When
you try to map the server calvin to the database calvin, you might
get the error:

Error 15023: User or role 'calvin' already exists in the current database.

To fix this:

sp_change_users_login 'update_one', 'calvin', 'calvin', 'password'
-- this command will link the server user to the database level user.

Thursday, October 23, 2008

xcopy

echo ---------------- >> c:\backup_log.log
echo \calvin\*.* >> c:\backup_log.log
xcopy d:\calvin\*.* "\\192.168.1.99\F$\calvin-backup\calvin\*.*" /H /D
/E /C /R /Y>> c:\backup_log.log
time /t >> "c:\backup_log.log"

Friday, September 26, 2008

truncate SQL 2005 Log file

Unlike SQL 2000, you can't conveniently just truncate the transaction
log. But you can do this:

1. Highlight the database-> Tasks->Detach..-> Click OK
2. Go to log file folder -> rename the xxx_log.ldf to something else
3. Highlight Databases->Attach…-> Click Add -> add the database MDF
file, highlight the log file in the lower part of the window and click
the 'Remove' button.
4. After this is done, you can verify the contents of the attached
database and then delete the log file.

Monday, August 18, 2008

AJAX Calendar Extender only shows partial weekdays

The Calendar extender in my page only shows 5 days in a week, instead
of a full 7 days. There is no parameters controlling this behavior.
After some investigation I found that's because I have defined padding
in my stylesheet for TD:

TD {padding-left:5px;padding-right:5px;}

Generally it's not a good idea to change the general TD style,
especially third-party control will be used. I put the padding in my
own table class then problem solved!

Wednesday, August 6, 2008

Call server postback from ModalPopup extender

The Atlas AJAX Toolkit has a very convenient ModalPopup extender. However by default it doesn't do post back on button clicks, even the event is wired with the button:
< runat="server" id="btnAddExistingPart" text="Add" cssclass="SplashButton" onclick="btnAddExistingPart_Click">
Upon click it only dismiss the modal popup.

You have to explicitly call the post back client method to initiate the post back:
protected void Page_Load(object sender, EventArgs e)
{
...
btnAddExistingPart.OnClientClick = "__doPostBack('" + btnAddExistingPart.UniqueID + "','')";
...
}

Monday, August 4, 2008

System admin script

To lock the workstation (tested on Win2K/XP): rundll32 user32.dll,LockWorkStation

Thursday, July 17, 2008

OleDbCommand doesn't support named parameters

When calling a parameterized stored procedure, if you pass parameters to a SqlCommand object

(i.e.
cmd.Parameters.AddWithValue("@productID", strProductID);
cmd.Parameters.AddWithValue("@productName", strName);
...
)

SqlCommand will correctly map the value with the parameter regardless of the order these AddWithValue methods are called. However, OleDbCommand doesn't support named parameters (MS confirmed it: http://support.microsoft.com/kb/316744). You have to make sure the order you call the AddWithValue method matches the order of your SP parameters. Obviously the SqlCommand is much more convinient because you can simply skip some parameters that have default values defined in SP. In my database utility class for SqlClient, I can pack the parameters in a HashTable and pass them in:

public DataSet ExecuteSPQuery(string spName, Hashtable param)
{
SqlConnection myConnection = null;
DataSet dataSet = new DataSet();
try
{
myConnection = ConnectToDB();
SqlCommand cmd = new SqlCommand();
cmd.Connection = myConnection;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = spName;

// a different way of looping a hashtable
if (param != null)
{
foreach (DictionaryEntry de in param)
{
cmd.Parameters.AddWithValue("@" + de.Key.ToString(), de.Value.ToString());
}
}

SqlDataAdapter myAdapter = new SqlDataAdapter();
myAdapter.SelectCommand = cmd;
myAdapter.Fill(dataSet);

}
finally
{
if (myConnection != null)
myConnection.Close();
}

if (dataSet != null && dataSet.Tables.Count == 0)
return null;
else
return dataSet;
}

However for OldDb client, I have to use a queue to do that, and anytime there is a change to the SP, I have to change everywhere that queue is populated, or to define a method for each SP I call. How nice...

Tuesday, July 15, 2008

Sorted view in SQL 2005

It looks like something new in SQL 2005. If you try to add a "order by" in the view, the management studio will automatically add a "TOP (100) PERCENT" after the SELECT clause. However this only works in "preview" mode. If you query "select * from [view_name]", you still get the records unsorted.

To get the sorted records from a view, a workaround is to use
SELECT top 1000000000 * FROM

This is not ideal but it works if you know your data count range. Microsoft has a hotfix for this. I can't believe MS let SQL 2005 out of the door with such a bug.