Posts tagged how to

Configuring IIS 6 and ASP.NET MVC

By default, IIS 6 does not work with ASP.NET MVC and needs to be configured to use wild-card mapping to get MVC’s routing and clean URLs to work correctly. Unfortunately, IIS 6 does take a performance hit because all requests are processed by ASP.NET. Static files, such as images, CSS, and JavaScript are processed as a dynamic page instead of a static one.

Install ASP.NET MVC
Follow the first two steps of the instructions for installing ASP.NET MVC. The third step can be ignored since Visual Studio will not be installed on the server.

Configuring ASP.NET 2.0
First thing to do is open the IIS Manager ->expand your server ->Web Service Extensions folder. Right-click on the white space under the list of Web Service Extensions and select to Add a new Web service extension…

The New Web Service Extension window will pop open. Enter “ASP.NET v2.0.50727″ as the Extension Name and click the Add button.  Browse to the C:\Windows\Microsoft.Net\Framework\v2.0.50727 folder and select the aspnet_isapi.dll file. Make sure to check the “Set extension status is Allowed” button before clicking OK.

This will allow your programs to run ASP.NET 2.0, 3.0, and 3.5 versions of the .NET framework.

Configuring Your Web Application
The next thing to do is open the properties of the Web Site where your web application is. Select the ASP.NET tab and select 2.0.50717 in the ASP.NET version drop down box.

Next, click on the Home Directory tab. Click on the Configuration button. Under the Wild-card application maps area, click the Insert button. Browse to the C:\Windows\Microsoft.Net\Framework\v2.0.50727 folder and select the aspnet_isapi.dll file. Uncheck the “Verify that file exists” before clicking OK. Save your changes and your site should now be able to run ASP.NET MVC and use the routes setup by your application.

Clearing the Client Version from the Lotus Notes Directory

With the latest version of Lotus Notes (8.5.1), you can now view the version of your clients by looking in the People -> by Client Version view. One problem with this, however, is that your users will show each client they have logged in as. Over the years, you may accumulate many versions for each user.

During an upgrade, you may wish to see what users are using the previous version of client against the ones who have the new version installed. But first, we’ll need to clean up the directory so we don’t see all this old version history.

To clean up these fields, you need to write an agent that will empty them for each selected person. This will allow you to run the clean up agent on only the users you wish to run it on.

Writing the agent
Open up the pubnames.ntf template file in your Notes Designer. You’ll need to go to Code and double-click on Agents to see the current agents for the template.

We’ll create a new agent and give it a name. Below is the code for the agent. Copy and paste it in the Designer.

Option Public
Option Declare

Sub Initialize()

  'Declare.
  Dim s As New NotesSession
  Dim db  As NotesDatabase
  Dim dc As NotesDocumentCollection
  Dim doc As NotesDocument

  'Initialize.
  Set db = s.Currentdatabase
  Set dc  = db.Unprocesseddocuments
  Set doc = dc.Getfirstdocument()

  While(Not(doc Is Nothing))

    If (doc.Form(0) = "Person") Then

      Call doc.Removeitem("ClntBld")
      Call doc.Removeitem("ClntDate")
      Call doc.Removeitem("ClntDgst")
      Call doc.Removeitem("ClntMachine")
      Call doc.Removeitem("ClntPltfrm")
      Call doc.Save(False, False, False)

    End If

    Set doc = dc.Getnextdocument(doc)

  Wend

End Sub

Now, after you refresh your names.nsf file, you can go to the Action menu and find your agent. Running the agent will only process those Person documents that you have selected.

Scheduling A Task In A Web Application On A Windows Server

When writting web applications, there may be times when you would like to perform some task automatically at the same time or every X number of hours. Web applications only execute their code when a request is made from a client’s browser. Because of this, scheduled tasks within the web application will not work.

There are a few different options:

  1. Build a separate Windows Service application that can connect to the assemblies your web application uses to execute the scheduled task.
  2. Build a separate Windows application that communicates with a web service that your web application also uses to perform the scheduled task.
  3. Use the Windows Scheduler to call the URL of the action to perform the task.

The first option might sound good at first, but creating a separate program that talks to your web application’s library files is a bad idea. Maintenance will become a problem as you change your web application. Since the code is in two places, unit testing will not tell you that the class library you just changed has now broken your scheduled tasks in your windows service. 

The second option is slightly better, but you still may have issues with changing your class library.

The third option is the one that I will show you how to configure. We will setup a VBS script that will perform a call to any web address we pass to it. Then, we’ll setup a batch file that calls the VBS script and passes in one or more URLs to execute. And finally, we’ll go through using the Windows Scheduler to create the scheduled task when the batch file should execute.

The primary advantage to this method is that you get to keep all of your code together in your web application. You simply setup an action that performs whatever task you wish to schedule and make sure that action is callable from an anonymous user.

WebRequest.vbs
You may copy the following code and paste into a text file. Then, rename the file to WebRequest.vbs. The script basically takes an argument that is passed into it as the URL. The HTTP object is setup and then the URL is called using a GET request. Then, the returned status is checked to see if the request was successful or not.

url = WScript.Arguments.Item(0)
'WScript.echo url

set WshShell = WScript.CreateObject("WScript.Shell")
set http = CreateObject("Microsoft.XmlHttp")

http.open "GET", url, FALSE
http.send ""

if (http.Status = 200) then

    WScript.echo "Request successful."

else

    WScript.echo "Error: " & http.Status

end if

set WshShell = nothing
set http = nothing

The Batch File
Calling the WebRequest.vbs file from a batch file is very easy. Simply create a new text file, calling it whatever you’d like, and paste in the following line of text. Rename the file to .BAT when finished.

cscript WebRequest.vbs "http://www.domain.com/controller/action"

Scheduling The Task
From your Windows server, open the Control Panel and then open the Scheduled Tasks item. Next, click on Add Scheduled Task. Choose any application to schedule.  This will be changed later on. Configure how often you wish the schedule to run and enter a user name and password to run the schedule as. This users should have access to running the web browser from the server.

After your new scheduled task has been created, open it. In the Run text box, type in the full path to your batch file. Then, in the Start In text box, type in the full path without the batch file name. You can also adjust your scheduled time from within here. Click OK to save the changes and your scheduled task should be ready.

You can test the Windows Scheduler by setting the scheduled time to a minute or two in the future and then waiting for it to execute. If you just wish to test your action, or manually run it, you can simply run the batch file.

ASP.NET MVC Using Forms Authentication With LDAP

If you are using ASP.NET MVC and you want to authenticate your users against Active Directory using LDAP, you need to do a little work to get everything set up. It is pretty easy to authenticate users with Active Directory using the <Authorize()> attribute, but I ran into some problems when I wanted to authorize a user based on a Windows Group.

Here are the steps I took to be able to authenticate active directory users and authorize their use of actions based on being members to user groups.

Web.Config

Find the authentication tag and change it to the following:

<authentication mode="Forms">
  <forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>

The LoginUrl points to the Controller/Action where the login function is.

UserRepository

This is the class object that does the communication with Active Directory via LDAP. The project where this class resides should also add a reference to the System.DirectoryServices DLL.

Imports System.DirectoryServices

Public Class UserRepository

  Private _server As String

  Public Sub New(ByVal server As String)

    _server = server

  End Sub

  Public Function GetUser(ByVal userName As String) As Data.User

    Dim root As DirectoryEntry = New DirectoryEntry("LDAP://" + _server)
    Dim search As DirectorySearcher = New DirectorySearcher(root)

    search.SearchScope = SearchScope.Subtree
    search.Filter = "(sAMAccountName=" + userName.Substring(userName.IndexOf("\") + 1) + ")"

    Dim results As SearchResultCollection = search.FindAll()

    Return _GetUser(results(0).Path)

  End Function

  Public Function GetUserByFullName(ByVal fullName As String) As Data.User

    Dim root As DirectoryEntry = New DirectoryEntry("LDAP://" + _server)
    Dim search As DirectorySearcher = New DirectorySearcher(root)

    search.SearchScope = SearchScope.Subtree
    search.Filter = "(displayName=" + fullName + ")"

    Dim results As SearchResultCollection = search.FindAll()

    Return _GetUser(results(0).Path)

  End Function

  Public Function GetMembers(ByVal groupPath As String) As IQueryable(Of Data.User)

    Dim root As DirectoryEntry = New DirectoryEntry("LDAP://" + _server)
    Dim search As DirectorySearcher = New DirectorySearcher(root)
    Dim members As List(Of Data.User) = New List(Of Data.User)()

    search.SearchScope = SearchScope.Subtree
    search.Filter = "(memberOf=" + groupPath + ")"

    Dim results As SearchResultCollection = search.FindAll()

    For Each result As SearchResult In results
      members.Add(_GetUser(result.Path))
    Next

    root.Close()

    Return members.AsQueryable

  End Function

  Public Function Authenticate(ByVal userName As String, ByVal password As String) As Data.User

    Dim root As DirectoryEntry = New DirectoryEntry("LDAP://" + _server, userName, password)
    Dim search As DirectorySearcher = New DirectorySearcher(root)
    Dim user As User = Nothing

    search.SearchScope = SearchScope.Subtree
    search.Filter = "(sAMAccountName=" + userName + ")"

    Dim results As SearchResultCollection = search.FindAll()

    If (Not (results Is Nothing)) Then

        user = _GetUser(results(0).Path)

    End If

    Return user

  End Function

  Private Function _GetUser(ByVal userPath As String) As Data.User

    Dim entry As DirectoryEntry = New DirectoryEntry(userPath)
    Dim user As Data.User = New Data.User()

    user.UserName = entry.Properties("sAMAccountName").Value
    user.FirstName = entry.Properties("givenname").Value
    user.LastName = entry.Properties("sn").Value
    user.Email = entry.Properties("mail").Value

    For Each group In entry.Properties("memberOf")

        user.Groups.Add(_GetGroup(group))

    Next

    entry.Close()

    Return user

  End Function

  Private Function _GetGroup(ByVal path As String) As String

    Dim value As String = ""
    Dim index1 As Integer = path.IndexOf("=", 1)
    Dim index2 As Integer = path.IndexOf(",", 1)

    If (Not (index1 = -1)) Then

        value = path.Substring((index1 + 1), (index2 - index1) - 1)

    End If

    Return value

  End Function

End Class

User

This class hold the user information that we want to work with from Active Directory. Here I am storing the username, first and last names, email address, and the list of groups the user is a member of.

This class is in my Data project and that is why you will see Data.User in the UserRepository. If you want everything to be in one project, you could create the Data namespace around the User class. I did run into problems with MVC getting confused between my User class and a built in one if I did not specify the namespace. You could also simply rename the User class to something else.

Public Class User

    Private _userName As String
    Private _firstName As String
    Private _lastName As String
    Private _email As String
    Private _groups As List(Of String)

    Public Property UserName() As String
        Get
            Return _userName
        End Get
        Set(ByVal value As String)
            _userName = value
        End Set
    End Property

    Public Property FirstName() As String
        Get
            Return _firstName
        End Get
        Set(ByVal value As String)
            _firstName = value
        End Set
    End Property

    Public Property LastName() As String
        Get
            Return _lastName
        End Get
        Set(ByVal value As String)
            _lastName = value
        End Set
    End Property

    Public Property Email() As String
        Get
            Return _email
        End Get
        Set(ByVal value As String)
            _email = value
        End Set
    End Property

    Public Property Groups() As List(Of String)
        Get
            Return _groups
        End Get
        Set(ByVal value As List(Of String))
            _groups = value
        End Set
    End Property

    Public Sub New()

        Me.UserName = ""
        Me.FirstName = ""
        Me.LastName = ""
        Me.Email = ""

        Me.Groups = New List(Of String)()

    End Sub

    Public Function GetFullName() As String

        Dim s As System.Text.StringBuilder = New System.Text.StringBuilder()

        If (Not (Me.FirstName = "")) Then

            s.Append(Me.FirstName)
            s.Append(" ")

        End If

        s.Append(Me.LastName)

        Return s.ToString()

    End Function

    Public Function SerializeGroups() As String

        Dim text As String = ""

        For Each item In Groups

            If (text = "") Then

                text = item

            Else

                text = text + "|" + item

            End If

        Next

        Return text

    End Function

End Class

AccountController

Next is the controller that handles signing in and out of the application. I’ve simplified this controller code for the sake of simplicity. Under normal circumstances, I would have the controller communicate to a service class that then interacts with the repository. The service class could do validation on the login form before attempting to authenticate empty user and password strings.

Imports System.Globalization
Imports System.Security.Principal
Imports S3.Data

<HandleError()> _
Public Class AccountController
    Inherits System.Web.Mvc.Controller

    'data members.
    Private _userRepository as UserRepository
    Private _formsAuthentication As IFormsAuthentication

    Public Sub New()

        _userRepository = New UserRepository(ConfigurationManager.AppSettings("LDAPServer")
        _formsAuthentication = New FormsAuthentication()

    End Sub

    'GET: /Account/LogOn
    Public Function LogOn() As ActionResult

        Return View("LogOn")

    End Function

    'POST: /Account/LogOn
    <AcceptVerbs(HttpVerbs.Post)> _
    Public Function LogOn( _
        ByVal userName As String, _
        ByVal password As String, _
        ByVal rememberMe As Boolean, _
        ByVal returnUrl As String) As ActionResult

        Dim result As ActionResult = View("LogOn")
        Dim user As User = _userRepository.Authenticate(userName, password)

        If (Not (user Is Nothing)) Then

            _formsAuthentication.SignIn(user, rememberMe)

            If (returnUrl Is Nothing) Then

                result = RedirectToAction("Index", "Home")

            Else

                result = Redirect(returnUrl)

            End If

        End If

        Return result

    End Function

    Public Function LogOff() As ActionResult

        _formsAuthentication.SignOut()
        Return RedirectToAction("Index", "Home")

    End Function

End Class

FormsAuthentication

The next class is the FormsAuthentication class. The main reason that I made this code a class of its own is for the sake of unit testing my project. During unit testing, the commands that deal with writing and clearing the cookie would error out. To get around this, I created an empty FormsAuthenticationTest class that does nothing for the SignIn and SignOut methods. Note: The example code does not show the interfaces. Check out the repository pattern and factory pattern to see how to setup services and repositories for unit testing.

After a user has been authenticated by the UserRepository, the controller passes the returned user into the SignIn method of the FormsAuthentication. This method then stores the user’s full name and serialized groups in a cookie. The list of groups is serialized by creating a string with each group name separated by a “|” character. This serialization takes place in the User object.

Imports System.Web.Security

Public Class FormsAuthentication

    Public Sub SignIn(ByVal user As Data.User, ByVal createPersistentCookie As Boolean)

        Dim authTicket As System.Web.Security.FormsAuthenticationTicket = _
            New System.Web.Security.FormsAuthenticationTicket( _
            1, _
            user.GetFullName(), _
            Now, _
            Now.AddMinutes(60), _
            createPersistentCookie, _
            user.SerializeGroups())

        Dim encryptedTicket As String = System.Web.Security.FormsAuthentication.Encrypt(authTicket)

        Dim authCookie As HttpCookie = New HttpCookie( _
            System.Web.Security.FormsAuthentication.FormsCookieName, _
            encryptedTicket)

        If (createPersistentCookie) Then

            authCookie.Expires = authTicket.Expiration

        End If

        HttpContext.Current.Response.Cookies.Add(authCookie)

    End Sub

    Public Sub SignOut()

        System.Web.Security.FormsAuthentication.SignOut()

    End Sub

End Class

Global.asax

With the previous code, you should be able to use Forms Authentication to log into your application and authenticate a user against the Active Directory via LDAP. The <Authorize()> attribute will allow you to secure your controller or actions. There’s just one problem. Now, if you want to use <Authorize(Roles:=”GroupName”),  your user does not get authorization to the action or controller.

There’s just one change left that we need to make. ASP MVC makes the current logged in user available by accessing the Context.User. When you log into the application MVC is setting the user as authenticated, but no roles or groups have been added to that user. We need to modify the Global.asax file and use the AuthenticationRequest event.

The following code will fire each time an authentication request is triggered. The code will check if the authentication cookie exists and if it does, reads the user’s name and groups from it and stores them in the Context.User so MVC can access the groups.

    Private Sub MvcApplication_AuthenticateRequest(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.AuthenticateRequest

        Dim cookieName As String = System.Web.Security.FormsAuthentication.FormsCookieName
        Dim authCookie As HttpCookie = Context.Request.Cookies(cookieName)

        If (Not (authCookie Is Nothing)) Then

            Dim authTicket As System.Web.Security.FormsAuthenticationTicket = Nothing

            Try

                authTicket = System.Web.Security.FormsAuthentication.Decrypt(authCookie.Value)

                If (Not (authTicket Is Nothing)) Then

                    Dim groups As String() = authTicket.UserData.Split(New Char() {"|"})
                    Dim id As System.Security.Principal.GenericIdentity = _
                        New System.Security.Principal.GenericIdentity(authTicket.Name, "LdapAuthentication")
                    Dim principal As System.Security.Principal.GenericPrincipal = _
                        New System.Security.Principal.GenericPrincipal(id, groups)

                    Context.User = principal

                End If

            Catch ex As Exception

                'Do nothing.

            End Try

        End If

    End Sub

And that should be all you need to use FormsAuthentication with LDAP and group authorization.

ASP.NET MVC Full URL From A Controller

In working with ASP.NET MVC, I came across the need to send an email with the URL of a document that is to be approved.  There are helper functions accessible from the View to build an ActionLink or get the URL of an action, but I had problems finding how to build this URL from within a controller.  Here is the simple code that I finally pieced together to do what I wanted:

Dim link As String = HttpContext.Request.Url.Scheme + _
    "://" + HttpContext.Request.Url.Authority + _
    Url.Action("ActionName", "ControllerName", New With {.id = idOfDocument})

HttpContext.Request.Url.Scheme returns “http” or “https”, which ever one you’re using.
HttpContext.Request.Url.Authority returns the Base Url of your application.
And, the Url.Action method creates the Url to the Controller, Action, and ID of the document that is being sent out for approval.

How to setup CodeIgniter

Introduction

This article explains the process I go through to setup the CodeIgniter framework and how to configure it so that I can start developing an application.

What is CodeIgniter?

If you don’t already know, CodeIgniter is an MVC framework built on PHP.  There are many features and built in functions that make building a web application fairly easy.  You can download the current version, 1.7.1, from CodeIgniter.com.  Also, check out the very well documented User Guide.

If you are interested in using the Zend library of tools with CodeIgniter, please check out How to use Zend_Search_Lucene with CodeIgniter.

Step-By-Step

For this tutorial, I’m using a WAMP Server running on my local Windows computer.  You could just as easily perform these same actions on a web server running PHP, although the paths may be different depending on if you are using Windows or a Linux based server.

Step 1

Download the latest version of CodeIgniter (1.7.1).

Before extracting the files to your server, let’s talk about where to put the files.  For security purposes, it is recommended to place the CodeIgniter files outside the path of your web server so those files cannot be accessed by typing in a URL.  So, if I access my web server by typing in http://localhost and the web server loads the website at C:\wamp\www\, then I want to place the System folder of CodeIgniter inside the C:\wamp\ folder.  Go ahead and extract the System folder from the download package to the C:\wamp\ folder.  You can place the System folder in the C:\wamp\www\ folder if you desire.  Just make sure to adjust the path name later on.

Step 2

Next, I have decided that I may want to use CodeIgniter for several applications that I develop.  To save on hosting space, I can setup all CodeIgniter applications to use the same core framework.  First, we need to move some files around from their default locations.

Open the C:\wamp\system\application\ folder.  You should see several folders listed here.  The default CodeIgniter application is setup for a single application.  We will be changing it to run multiple applications.  Create a new folder called baseApplication_1234 or something unique.  Make a copy of the index.html file and paste it inside baseApplication_1234.  Next, move all of the folders located at C:\wamp\system\application\ into C:\wamp\system\application\baseApplication_1234\.

We will configure this base application with all of our default settings that we want to use for all CodeIgniter applications.  Then, when you want to make a new application, you can simply copy the baseApplication_1234 and rename it.

Step 3

Next, we will configure the CodeIgniter application files.

Browse to C:\wamp\system\application\baseApplication_1234\config\ and open the following:

autoload.php

Modify line 42 to show:

$autoload['libraries'] = array('database', 'session');

 config.php

Modify line 14 to show (change localhost to your domain name):

$config['base_url'] = "http://localhost/";

Modify line 26 to show:

$config['index_page'] = "index.php?";

This line will be very important for the way our URLs are displayed later on.

Modify line 44 to show:

$config['uri_protocol'] = "QUERY_STRING";

Modify line 57 to show (this is optional if you want a .html to be shown on the end of your URLs):

$config['url_suffix'] = ".html";

Modify line 220 to show (add your own unique value within the quotes):

$config['encryption_key'] = "12345";

Modify lines 234 – 241 to show:

$config['sess_cookie_name']  = 'ci_session';
$config['sess_expiration']  = 7200;
$config['sess_encrypt_cookie'] = TRUE;
$config['sess_use_database'] = TRUE;
$config['sess_table_name']  = 'ci_sessions';
$config['sess_match_ip']  = TRUE;
$config['sess_match_useragent'] = TRUE;
$config['sess_time_to_update']  = 300;

This will require that we have a database and a table within called ci_sessions.  This table will need to have certain fields that CodeIgniter will be attempting to write session data to for each users who visits your site.  I’ll explain more later on.

database.php

Modify lines 41 – 45 to show:

$db['default']['username'] = "dbuser";
$db['default']['password'] = "mypassword";
$db['default']['database'] = "mydbname";
$db['default']['dbdriver'] = "mysql";
$db['default']['dbprefix'] = "dev_";

You will need to enter your correct username, password, database name, and prefix if you wish to use one.  If you use a prefix, you will need to have a table called dev_ci_session instead of ci_session.

routes.php

For the base application, the routes.php file is probably OK.  There are two lines that need to be modified when you build an application. 

$route['default_controller'] = "welcome";
$route['scaffolding_trigger'] = "12345";

Again, use some unique value for the scaffolding_trigger.  If you use a value that is hackable or nothing at all, your application will have a possible security hole.  The default controller name can be changed here as well.

Step 4

There are two helper files that I add to my projects that help translate the URLs the way I like them (http://localhost/controller/action/id.html).

MY_form_helper.php


<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
if ( ! function_exists('form_open'))
{
    function form_open($action = '', $attributes = '', $hidden = array())
    {
        $CI =& get_instance();

        if ($attributes == '')
        {
            $attributes = 'method="post"';
        }

        //Modify ->site_url to ->item('base_url').$action
        $action = ( strpos($action, '://') === FALSE) ? $CI->config->item('base_url').$action : $action;

        $form = '<form action="'.$action.'"';
 
        $form .= _attributes_to_string($attributes, TRUE);
 
        $form .= '>';

        if (is_array($hidden) AND count($hidden) > 0)
        {
            $form .= form_hidden($hidden);
        }

        return $form;
    }
}
?>

MY_url_helper.php


<?php
function redirect($uri = '', $method = 'location', $http_response_code = 302)
{
    $CI =& get_instance();

    switch($method)
    {
        case 'refresh' : header("Refresh:0;url=".site_url($uri));
        break;
        default   : header("Location: ".$CI->config->item('base_url').$uri, TRUE, $http_response_code);
        break;
    }
    exit;
}
?>

Create these files and save them to C:\wamp\system\application\baseApplication_1234\helpers\.  These functions will override the original CodeIgniter functions and fix some URL rewriting issues.

Step 5

Enabling URL rewriting may depend if your server supports it.  Apache has a rewrite_module that must be enabled before this will work.  Most hosting providers should already have enabled URL rewriting.

Create a new text file called .htaccess and paste the following code into it:

RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php?$1 [L]

This will redirect all requests on your domain back to the index.php file, unless the actual path does happen to exist, then the server will serve up whatever file/directory is at that location.

Notice the ? behind index.php.  The web server is rewriting the pretty URL into a QUERY_STRING that CodeIgniter is expecting and passes in the controller and action as variables such as index.php?var1=this&var2=that. 

The .htaccess file should be stored in the C:\wamp\www\ folder or the root of your web site.

Step 6

Next, the database will need to be created and the ci_sessions table created to store the user sessions to.

After you create the MySQL database, run the following SQL and it will create the table for you:

CREATE TABLE `dev_ci_sessions` (
  `session_id` varchar(32) NOT NULL,
  `ip_address` varchar(15) NOT NULL,
  `user_agent` text NOT NULL,
  `last_activity` datetime NOT NULL,
  PRIMARY KEY  (`session_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Make sure to remove the “dev_” if you did not setup a dbprefix.

Step 7

Next, for testing purposes, we’ll setup the baseApplication_1234 web application and attempt to run it.

Find the index.php file that came with the CodeIgniter 1.7.1 download.  It should be located in the same folder as the system folder and user_guide folder.  Copy this file into your web site root at C:\wamp\www\.

Modify line 26 as shown:

$system_folder = "c:/wamp/system";

Modify line 43 as shown:

$application_folder = "application/baseApplication_1234";

This is the line that specifies which application to load.

This index.php file is the main entry point for the application.  It will use these modifications to find the C:\wamp\system\ folder and run the correct web application.  The only other files that need to be in the C:\wamp\www\ folder are files that HTML needs to be able to access via URLs such as images and JavaScript.

Conclusion

That should be everything.  You can now try to open your web application by going to the URL, http://localhost.  The default controller is Welcome and the default action is Index, so you can also test http://localhost/welcome/index.html to verify URL rewriting works.  Remember the “.html” can be used to hide that PHP is running your web site.  In reality, index is an action of the welcome controller and doesn’t need the “.html” to be there at all.

If you run into problems, you can add this line to an action or the controller’s constructor for further information:

$this->output->enable_profiler(TRUE);

Be sure to shut this down when not needed, or it could give your users information you may not wish them to have.

Audiobooks On Your IPod

CD Case

If you like to listen to audio books on your IPod, you may be wondering how you can load your own audio books from MP3 files and have them show up under the “Audiobooks” category on your IPod.

If you load your audiobook files onto your IPod as MP3s, they will show up under Music and not Audiobooks. The MP3 files need to be converted to the M4B format.

There are a couple different ways to make M4B files. I’ve listed two methods below. Depending on if you want to combine your files into fewer files will determine which method you should probably use.

Using Itunes to convert your files

The easiest method is to use Itunes to do the converting. To do this, you need to import your files into Itunes as MP3 files. Once they are imported, find them in the list of Music (Hopefully they are tagged correctly to make it easy to find them). Next, select all the files you wish to convert and right-click on your selection and select the option “Convert selection to AAC”. This will convert your audio files to M4P files. Then, once they have all been converted, just remove the MP3 and M4P versions from your music list, rename all the converted M4P files to M4B and reimport them into Itunes. Now, your audiobook files should be under the “Audiobooks” category.

Using a third-party program

I have also found the “MP3 to iPod Audio Book Convert” useful when I want to combine multiple files into one M4B. You can add all the MP3 files you want and it will process them all into one M4B for you. This program allows you to do some limited tagging on the newly created file as well.

One note on using this converter: I’ve found that my Ipod sometimes chokes on audiobooks that are only one large file. If you stop listening to the audiobook and switch over to music and then go back, the marker that holds your place sometimes resets to the beginning. This can be annoying when you are near the end and have to fast-forward for several minutes to get back to your place. I’ve found that combining your files works best if you limit the size to around the length of a CD or if you break your audiobooks up by making each chapter its own file.

Photoshop CS3

Feather

I thought I’d post this little note about the hassles I’ve had recently with the installation of Photoshop CS3, which my company purchased as an upgrade from Photoshop 7.

I was pretty excited to get the new version since we were 3 versions behind on the latest release. After I got approval to purchase the software, I went through the online ordering process on Adobe’s website. Everything went well with this part of the process, but soon I would end up in the installation hell that is Photoshop CS3 (and apparently the other CS3 products from Adobe).

Once the download was finished, I started the “setup.exe” program and watched as the progress bar went to 100%. Then, nothing. No installer. So, I tried running the installer again. This time I got an error saying that I could only install one Adobe product at a time. So, after searching the Internet for solutions to the problem, I found that I wasn’t the only one having issues with the Adobe software.

I ran through some of the procedures on a test computer which led me to downloading a program called WinCS3Clean that removes Adobe software from the registry as well as from the file system. This program also required having the Microsoft Windows Installer Clean Up utility installed.

To get the Clean program to work, I had to instruct it to remove all Adobe and Macromedia products. Also, you must run it multiple times to make sure to remove everything. Then, finally the installer worked on my test computer.

However, this was not the end of my troubles. I called Adobe’s tech support to find out if there was an easier way to get CS3 installed on my production computer. We walked through pretty much the same steps I had done based on the support documents I found. Eventually, we had to remove all of the Adobe and Macromedia products once again. This time, though, the installer did not run. We had to go one step further and create a fresh local admin account on the computer before the installer would work. Then, I had to reinstall my Adobe and Macromedia products.

Finally, I did get the new version installed on my computer. So, if you want Photoshop CS3, be prepared to remove all of your Adobe and Macromedia software and, in my case, spend about two hours on the phone talking to a technician. When did Adobe’s quality drop to this level? They use to be very reliable. Well, those days seem to be gone.