Search

Wednesday, September 26, 2007

SVN4DW - Hard to Find SVN Extension for Dreamweaver


I've been using this extension for a while and it's pretty handy for updating/committing code from/to your SVN repository. I tried to find it on the net tonight so I can install it at home but it seems to have vanished. So I looked through my work PC and there it was. Awesome! For those folks that like to use it, I've put it up on my personal site. The link is http://tech-cats.net/blog/downloads/SVN4DW.zip

Querying Active Directory Through SQL Server

Integrating your application with Active Directory (or another LDAP based directory) is a common requirement in many business applications. Almost every language has a way to query LDAP but little known is the approach of integrating SQL Server with your LDAP controller. This quick article will guide you through setting up and using LDAP queries through SQL Server 2000 with Active Directory as the LDAP controller.

First, you need to create a SQL Server linked server. Set it up through SQL Server Enterprise Manager:
  1. Open SQL Server Enterprise Manager
  2. Go to the database server to which you will be adding the linked server
  3. Expend "Security"
  4. Right-click on "Linked Servers" and click on "New Linked Server..."
  5. Fill in the following:
    1. Under the General tab:
      1. Linked Server: adsi (or whatever you want to call it)
      2. Server type: select Other Data Source
      3. Provider name:select OLE DB Provider for Microsoft Directory Services
      4. Under Provider Options: check that Allow InProcess is checked
      5. Leave the rest of the fields blank
    2. Under the Security tab:
      1. Local Login: sqlServerUser
      2. Remote User: ntaccount@domain.com (such as bkostadinov@ica.com or ica.com\bkostadinov)
      3. Remote Password: userPassword for the above domain account
Through Query Analyzer:
  1. Change the "AllowInProcess" registry key under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Providers\ADSDSOObject to "dword:00000001"
    1. Edit the registry manually or put the following in a .reg file and execute it:
      Windows Registry Editor Version 5.00
      
      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Providers\ADSDSOObject]
      
      "AllowInProcess"=dword:00000001
            
  2. Open Query Analyzer (or your choice of query tool)
  3. Connect to the server to which you will be adding the linked server
  4. Change the provided values and execute the following code:
-- Change 'adsi' to the desired name of the linked server
exec sp_addlinkedserver
  'adsi',
  '',
  'ADSDSOObject',
  ''
go

-- Change 'adsi' to the desired name of the linked server
-- Change 'sqlUser' to the username of local sql server user
-- Change 'domainName\userName' to a domain account
-- (the format can be 'domainName\userName' or 'userName@domainName')
-- Change 'domainUserPassword' to the password of the domain account
exec sp_addlinkedsrvlogin
  'adsi',
  false,
  'sqlServerUser',
  'domainName\userName',
  'domainUserPassword'
go

Or you can just fill in the values on top of the following script and run that:

declare @linkedServerSql nvarchar(4000),
  @linkedServerName varchar(100),
  @localSqlUsername varchar(100),
  @domainUsername varchar(100),
  @domainUserPassword varchar(100)

-- Set the local sql server user
set @localSqlUsername = 'sqlUser'
-- format can be 'domainName\userName' or 'userName@domainName'
set @domainUsername = 'domainName\userName'
set @domainUserPassword = 'domainUserPassword'

set @linkedServerName = 'adsi'

set @linkedServerSql = '
  exec sp_addlinkedserver
  ''' + @linkedServerName + ''',
  '''',
  ''ADSDSOObject'',
  '''' 

exec sp_addlinkedsrvlogin
  ''' + @linkedServerName + ''',
  false,
  ''' + @localSqlUsername + ''',
  ''' + @domainUsername + ''',
  ''' + @domainUserPassword + ''''

exec sp_executesql @linkedServerSql
Run a query to verify that the linked server works. The query below will give you all the users in the dc=ica,dc=com (change that to match your own domain):
select  *
from  openquery(adsi, '
select  givenName,
    sn,
    sAMAccountName,
    displayName,
    mail,
    telephoneNumber,
    mobile,
    physicalDeliveryOfficeName,
    department,
    division
from    ''LDAP://dc=ica,dc=com''
where   objectCategory = ''Person''
        and
        objectClass = ''user''
')
Below, is an alternate syntax which you can use to apply ldap filters with almost universal syntax. The following will get all the users in LDAP but limit the result set to those users who's "given" & "sn" names are not empty. It will also apply a filter to the "division" attribute and exclude any records that match "system" and "generic".
declare @ldapFilter nvarchar(1000), @ldapSQL nvarchar(4000)

-- Set the filter to exlude objects that have a division of "System" and "Generic"
set @ldapFilter = '(!division=System*)(!division=Generic)'

-- Create an ldap query to get all users under dc=ica,dc=com
set @ldapSQL = '
select  givenName as firstName,
        sn as lastName,
        displayName,
        lower(sAMAccountName) as accountName,
        telephoneNumber as phoneNumber,
        mobile as cellPhoneNumber,
        mail as emailAddress,
        department,
        physicalDeliveryOfficeName as siteName
from    openquery(adsi, ''<LDAP://dc=ica,dc=com>
    (&(objectCategory=Person)(objectClass=user)'+ @ldapFilter + ');
        givenName,
    sn,
    sAMAccountName,
    displayName,
    mail,
    telephoneNumber,
    mobile,
    physicalDeliveryOfficeName,
    department,
    division;
    subtree'')
where   givenName is not null
    and
    sn is not null'

exec sp_executesql @ldapSQL

Some things to note:

"LDAP" is case sensitive, if you try using "ldap", the query will throw an error.

Querying the Active Directory server will work fine from Query Analyzer even if you do not run sp_addlinkedserverlogin. However, if you try to execute the query from the web (with something like ColdFusion, you will get an error similar to:

[Macromedia][SQLServer JDBC Driver][SQLServer]OLE DB provider 'ADSDSOObject' reported an error. The provider indicates that the user did not have the permission to perform the operation.

Tuesday, September 25, 2007

Web Application Deadly Sin

I have ran across this situation more than a couple of times recently. First it was my hosting provider, than the new web application I tried today (called Fuser). When signing up, they let you pick your password and I use a totally random password each time somewhere around 16-20 characters. So it happens that the applications above, cut my password to their allowed length but do not let me know they are doing so. Nowhere does it say that the password should be 10 or 15 characters. So end up with a newly created account that I cannot log into only to find out (after I go through the "forgot password" procedure) that my password was chopped. That is annoying and enough to turn me off using the application. Common sense, where are you?

Reactor Oops - Case Sensitive Dictionary XML

Here is the message I got today while trying to do some work with reactor and model-glue.

Message An error occurred while Transforming an XML document.

Detail Empty expression!

Extended Info

Tag Context D:\_Webroot\reactor\core\objectTranslator.cfc (149) D:\_Webroot\reactor\core\objectTranslator.cfc (164) D:\_Webroot\reactor\core\objectTranslator.cfc (164) D:\_Webroot\reactor\core\objectTranslator.cfc (101) D:\_Webroot\reactor\core\objectFactory.cfc (223) D:\_Webroot\reactor\reactorFactory.cfc (80) D:\_Webroot\reactor\base\abstractRecord.cfc (393) D:\_Webroot\reactor\project\myproject\Validator\usersValidator.cfc (10)

It turned out that the dictionary file that reactor generated is case sensitive. I need to change "<Users>" to "<users>". The table is defined as "users" but I had to change it to "<Users>" on my home machine. It seems that reactor can't make up it's mind on how it wont's the table to be called. Why is it "Users" at home and "users" at work? I'm using Apache on Windows with the same version of MySQL and reactor on both machines.

Update: So I just learned that:

<cfset usersRecord = variables.reactor.createRecord("users") />
is not the same as:
<cfset usersRecord = variables.reactor.createRecord("Users") />
The dictionary file was created with "Users" as the table name so reactor was looking for the same case. I think this is only an issue with MySQL.

Updating Web Applications in Production

Disclaimer: This is just an idea so far, but it is worth documenting so I remember to do it. It also assumes that you are using some kind of source control system such as Subversion.

Wouldn't it be cool to know when your production web application is out of date? By that I mean there is a newer version of it available in your source code repository. So instead of the usual steps:

  1. Check-in latest changes to the application
  2. Go to the production sever and update the code from the repository
  3. Reinitialize the application (I use Model-Glue)

You can simply:

  1. Check-in latest changes to the application
  2. Go to the web application in production
  3. Click a button to updated to the latest revision

Same number of steps I guess but doesn't the second solution seem so much nicer. I will have to implement this across all the web applications I maintain/develop.

Thursday, September 20, 2007

CSSVista - Edit your CSS code live on Internet Explorer and Firefox

I came across this pretty nice tool called CSSVista. It lets you edit css on the fly. Sure, the web developer toolbar and Firebug already does this for Firefox but it's still cool.

JSValidation Kicks Butt - Validation Library for Prototype.js

to get it to work, simply include the library like so:
<script language="javascript" type="text/javascript" src="js/jsvalidate/jsvalidate.js"></script>
Add some css styles:
.jsvalidation { color: #ff0000; font-size: 14px; }
add "jsrequired" (for a required field) and the jsvalidate type (such as "jsvalidate_number") to the "class" attribute of your text element.
<input type="text" name="year" id="year" size="4" class="jsrequired jsvalidate_number" />
and finally set the message to be displayed when the validation fails in the "alt" attribute of the element:
<input type="text" name="year" id="year" size="4" class="jsrequired jsvalidate_number" alt="Please enter a Year" />
More on the jsvalidation types at the docs

Monday, September 17, 2007

Quake Style Command Prompt - Cool Stuff

This is based on the guide provided on Instructables. I'm posting this here because I finally got around trying this out. I have to say it's pretty nice. The idea is simple. You press the Windows key + '~' (tilda) and get a semi-transparent console window in the top left corner of your screen. You press the same combination key again and the window disappears. Very handy for those of us that use the command prompt often. Here is the end result. Click on the thumbnail to see a larger picture: To get started: 1. Get AutoHotkey 2. Download the last stable version of Console (as of 09.17.2007 that is 1.5 so get "Console-1.5.zip" 3. Get the customized "console.xml" file from the bottom of the Instructable or from here directly 4. Get the AutoHotkey script from the bottom of the Instructable or from here directly 5. Install AutoHotkey 6. Extract Console-1.5.zip to "c:\program files\console" or wherever you desire 7. Create a shortcut to "console.exe" in your "c:\windows" directory 8. Copy the customized "console.xml" to "c:\program files\console" (or wherever you installed Console) and overwrite the file that's there 9. Start the AutoHotkey script you got in step 4. You can also put a shortcut to the script in the "Startup" folder on your Start Menu.

Using Regular Expression in SQL Server

Who doesn’t love regular expressions?! If you don’t know what they are, you should take some time to learn. Regular expressions or from here on RegEx are a very powerful way to search/replace string within strings. That being said, almost any language supports RegEx and for a while I’ve been using them with SQL Server thanks to xp_pcre - Regular Expressions in T-SQL Here is an example use of two of the functions that come with the above extended sql server library:
declare @out varchar(8000)
declare @str varchar(500)

-- Create a string containing an ldap common name

set @str = lower(’LDAP://CN=APO Conference Room,OU=Locations,OU=APO,OU=Sites,DC=ica,DC=com’)

-- Strip everything from the string and replace with the ’ou’ that comes after ’ou=locations’

exec master.dbo.xp_pcre_replace @str, ’.*ou=locations,ou=(’w+).*’, ’$1’, @out out

-- Print the results
print @out

-- Create a string containing an ldap common name
set @str = lower(’LDAP://OU=SMD,OU=Sites,DC=ica,DC=com’)

-- Return 1 if there is a match of ’ou=’ followed by ’ou=sites’
select master.dbo.fn_pcre_match(@str, ’^ldap://ou=’w+,ou=sites’)
More information here: http://www.codeproject.com/database/xp_pcre.asp?df=100&forumid=16452&exp=0&select=1191266

Thursday, September 13, 2007

Draw Flow Charts Online

Download Squad just had a post about this nifty online tool. Anybody that draws diagrams (that means you Doug), will benefit from this. Draw Anywhere - easy online diagramming, flow chart

Disabling Forms with Prototype - Gotcha

I love using Prototype for all my JavaScript needs. Ajax calls are real easy with or without parameters. To get some form parameters to pass with your request, you would simply do:
var params = $('myForm').serialize();
and then you would disable the form like so:
$('myForm').disable();
However, here is a little gotcha I always seem to forget:
  • DO NOT disable the form before you read your form values
If you do, code such as:
$F('myFormFiled');
or
$('myForm').serialize();
will no longer work. By not working, I mean will just return empty and you will be beating your head against the desk.

Chad Vader - Funny Stuff

Whoever hasn't seen the Chad Vader creation of Matt Sloan and Aeron Yonda should. It's pretty amusing. You can find it at
Chad Vader: Day Shift Manager - Blame Society Productions - Matt Sloan and Aaron Yonda. The tag line is "Life is hard for Chad Vader, the younger, less charismatic brother of Darth Vader, who is the day shift manager of a grocery store."

Tuesday, September 11, 2007

Cats love to sleep, eat and sneak out

This is one passed out cat. She had to eat first however. This is the same cat that managed to get out and spend the night outdoors when we took our overnight trip to Old Forge. I am glad she is ok. To our surprise, we came home to a dead mole by the back door and it was a mystery until we found the cat outside of the house.DSCF4856

Monday, September 10, 2007

Common Model-Glue "Newbie" Mistake

In short: forgetting to "wire" the message broadcasted by your event to the function in your controller. I still do this on occasion which prompted me to post it. The longer version: You have a an event defined as follows:
<event-handler name="GetUsersForm">
 <broadcasts>
  <message name="getRolesList" />
  <message name="getSytelineUsersBySiteID" />
  <message name="getUsersBySiteID" />
 </broadcasts>

 <views>
  <view name="body" template="frmUsers.cfm" />
 </views>
</event-handler>
You have "getRolesList", "getSytelineUsersBySiteID" and "getUsersBySiteID" defined in your controller. However, when you call the event, you don't get any of the data you expect to see there. The gotcha is "wiring" the broadcasted message to the function in the controller as follows:
<controller name="ApplicationController" type="LabelGeneration.controller.ApplicationController">
 <message-listener message="OnRequestStart" function="OnRequestStart" />
 <message-listener message="OnQueueComplete" function="OnQueueComplete" />
 <message-listener message="OnRequestEnd" function="OnRequestEnd" />

 <message-listener message="getSitesList" function="getSitesList" />
 <message-listener message="getRolesList" function="getRolesList" />

 <message-listener message="getSytelineUsersBySiteID" function="getSytelineUsersBySiteID" />
 <message-listener message="getUsersBySiteID" function="getUsersBySiteID" />
 <message-listener message="addUser" function="addUser" />
 <message-listener message="deleteUser" function="deleteUser" />
</controller>

Friday, September 07, 2007

Howto Reload Model-Glue Applications

There are a few different way to reload a Model-Glue application. Before you start, please Note the "reloadKey" and "reloadPassword" in your ColdSpring.xml file. If the "reload" value is set to true in your ColdSpring.xml file, you do not need to do anything as the application is reloaded every time you load/refresh the page. While this can be helpful for development, it will kill your application in production, so beware. If the "reload" value is set to false, here is how to reload your application: If you want your application to reload on every request: 1. Edit the ColdSpring.xml file and set the "reload" value to true 2. Invoke the application by going to "http://yourhost/yourapplication/?init=true", where "init" is the value of the "reloadKey" and "true" is the value of the "reloadPassword" If you don't want the application to reload on every request: 1. Invoke the application by going to "http://yourhost/yourapplication/?init=true", where "init" is the value of the "reloadKey" and "true" is the value of the "reloadPassword" The alternative solution I use while always leaving the reload key to "false" is to write your own re-init method. For some reason I find that invoking the application with "http://yourhost/yourapplication/?init=true" is a pain and I'm not sure that it clears application or session scope variables. So instead, I have my own method. In your "application.cfc", create the onRequestStart function as follows. If it already exists, just add the code shown here.
<cffunction name="OnRequestStart" output="no">
  <cfif structKeyExists(url, "init")>
    <cfset structclear(Application) />
    <cfset structclear(Session) />

    <cfset onApplicationStart() />
  </cfif>
</cffunction>
Now, when you invoke the url "http://yourhost/yourapplication/?init", your application will re-init by deleting the application and session scopes. The concept is based on some code I say Ray Camden use. Maybe it's reinventing the wheel and ColdSpring already does that but I'm not sure.

Apt-Get for Windows = Win-Get (auto download and install applications)

If you ever installed anything under Ubuntu or Debian Linux, you know how nice it is to just type "apt-get install apache" and the operating system will download apache and install it for you. This feature has forever been lacking in Windows based platforms. The guys at Win-Get have decided to remedy that by implementing similar functionality for Windows. All you need to do (right from the win-get web page) is:

1. Download wget.exe
2. Download win-get.exe (version 1.01)
3. Put the 2 files some where on your system (I like in the c:\windows so they are system wide accessible).

Then to install an application from the supported application list, you open a command prompt and type:
"win-get install paint.net" or "win-get sinstall paint.net" ("sinstall" is for silent install but not all applications support it).

Here is a list of commands I ran for a new install:

win-get sinstall paint.net
win-get sinstall spybot
win-get sinstall firefox
win-get sinstall avg_antivirus
win-get sinstall notepad++
win-get sinstall openoffice
win-get sinstall winrar

So nice and easy! I love it.
// //]]>