Apache Mod_AutoIndex - IIS/ASP Implementation

This is a simple directory listing script for IIS, using ASP.

Features

  • Custom CSS for each folder, if available It reads a css directory and if there is a .css file with the requested directory name, it will include a <style> tag for it. For example, if the requested directory listing is forms, it will create a style tag for css/forms.css.
  • Single instance One copy of this code can reside at the top of a directory tree and allow navigation throughout sub folders.
  • Drag and drop content posting The administrator or person who is posting content can drag and drop the files using Windows Explorer, without modifying the script, or asking for help. New content will be displayed as copied.
  • Hidden aware Hidden directories and files aren’t listed, so content can be placed on the server, but not displayed. This includes files named with a leading underscore which are usually not content.
  • Sample CSS A sample CSS file is included

<!DOCTYPE HTML>
<html>
<head>
<%
Const ReadOnly=1
Const Hidden=2

Dim fs,fo,x
Set fs=Server.CreateObject("Scripting.FileSystemObject")

Dim strPage,strPageHTML,strPageEncode,i
strPage=Request.ServerVariables("QUERY_STRING")
If (strPage = Null) Then strPage = ""
If (InStr(strPage,".")) Then strPage = ""
strPage=Replace(strPage,"+"," ")
strPageHTML=Server.HTMLEncode(strPage)
strPageEscape=Server.URLEncode(strPage)
%>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<META http-equiv="Content-Style-Type" content="text/css">
<title>Page Title</title>
<base href="http://domain.com/top/" />
<link href="css/style.css" rel="stylesheet" type="text/css" />
<%
If (strPage <> "") Then
	Set fo=fs.GetFolder("D:\docs\top\css")
	For Each x In fo.Files
		i=InStr(LCase(x.Name),LCase(strPage))
		If (i <> 0) Then Response.Write("<link href=""css/" & x.Name & """ rel=""stylesheet"" type=""text/css"" />")
	Next
End If
%>
</head>
<body>
<div id="divHeader">
<a href="#" title="Go to Home Page"><h1 id="title">Page Title</h1></a>
<h2><% Response.write(strPageHTML) %></h2>
</div>
<div id="divMain">
<div id="divFolders">
<ul>
<li><a href="http://docs/">Intranet Home</a></li>
<li><a href="http://docs:8088/default.aspx">Search Intranet</a></li>
</ul>
<ul>
<%
Set fo=fs.GetFolder("D:\docs\top\")

For Each x In fo.SubFolders
  'Print the name of all files in the test folder
  If (((x.Attributes And Hidden) = 0) And _
	(Left(x.Name,1) <> "_")) Then
	Response.write("<li><a href='" & Request.ServerVariables("SCRIPT_NAME") & "?" & Server.URLEncode(x.Name) & "'>" & Server.HTMLEncode(x.Name) & "</a></li>")
  End If
Next
%>
</ul>
</div>
<div id="divContent">
<% If (strPage = "") Then %>
<p>Home or top page text.</p>
<% Else %>
<%
Set fo=fs.GetFolder("D:\docs\top\" & strPage)
If (fo.Files.Count > 0) Then
	Response.Write("<ul>")
	For Each x In fo.Files
		If (((x.Attributes And Hidden) = 0) And _
			(Left(x.Name,1) <> "_")) Then
			Response.write("<li><a href='" & strPageEscape & "/" & x.Name & "'>" & Server.HTMLEncode(x.Name) & "</a></li>")
		End If
	Next
	Response.Write("</ul>")
Else
	Response.Write("No files found")
End If
%>
<% End If %>
</div>
</div>
<div id="divFooter">
<span class="float-left bolder">&copy; 2008-<% Response.Write(Year(Now)) %>&nbsp;Company, Inc.</span>
<span class="float-right">This information is for private internal use only.</span>
</div>
</body>
</html>
<%
set fo=nothing
set fs=nothing
%>

Base CSS file

Name this file style.css and place it in the css directory. You may add additional CSS files for each page. The CSS filenames are case-insensitive.

*
{
font-family: "Trebuchet MS","sans-serif";
margin:0;
padding:0;
}
body
{
width:1000px;
}
#divHeader
{
height:105px;
width:950px;
padding:25px 25px 0;
background:#fff url(../images/logo.jpg) no-repeat top right;
}
h1 
{
color:#000;
}
#divMain
{
border-style:solid;
border-width:1px 0;
border-color:#000;
height:450px;
width:1000px;
}
#divFolders
{
width:200px;
float:left;
background-color:yellow;
height:450px;
overflow:auto;
}
ul
{
list-style-type:none;
padding:5px;
margin:10px;
}
#divContent
{
padding:10px;
width:780px;
float:left;
}
#divFooter
{
background-color:#fff;
padding:15px;
}
.break
{
float:none;
clear:both;
}
.float-left
{
float:left;
}
.float-right
{
float:right;
}
.center
{
margin:0 auto;
}
.bolder
{
font-weight:bolder;
}
p
{
padding:5px;
margin:5px;
}
a
{
color:#000;
text-decoration:none;
}
a:hover
{
text-decoration:underline;
}
a:visited
{
color:#000;
}

Browser Cache Management - Ensure Updates

This post describes one way to ensure the javascript and CSS are requested by the browser when a new release is distributed.

The first approach is to prefix the file names with a version-release string, and create symlinks to the files during installation or the first execution. Many systems have a version identification mechanism.

To manage the symlinks, the following could be used:


#!/bin/bash

fclearold()
{
        echo 'fclearold'
        for g in $(find *.$1 -maxdepth 1 -type l);do
                echo $g
                rm -f $g
        done
}

fcreatenew()
{
        echo 'fcreatenew'
        for g in $(ls *.$1); do
                ln -s $g $2.$g
        done

}

version=`cat "$BASE/config/version`;
for f in 'js' 'css'; do
        echo $f
        pushd $f > /dev/null
        fclearold $f
        echo $version;
        fcreatenew $f $version
        popd > /dev/null
done

fclearold removes the old symlinks, fcreatenew makes new ones. It is assumed the javascript is in the js directory and all javascript files have a .js extension, and the CSS is in the css directory and all CSS files have a .css extension.

httpd.conf (or equivalent)

# Cache js and CSS files for 6 months - with a timestamp
<FilesMatch "\.(js|css)$">
  ExpiresActive On
  ExpiresDefault "access plus 6 months"
  Header set Cache-Control "max-age=15552000"
  RewriteEngine On
  RewriteRule (.*)/[0-9]+\.(.*)$ $1/$2
  FileETag MTime
</FilesMatch>

Timestamp Management Code (PHP)


        function sTimestamp()
        {
                $sTimestampFile = 'cache/timestamp';
                if (is_file($sTimestampFile))
                        $sRetVal = file_get_contents($sTimestampFile);
                else
                {
                        $sRetVal = time();
                        file_put_contents($sTimestampFile,$sRetVal);
                }
                return $sRetVal;
        }

Once the timestamp is set, it is cached in the cache/timestamp file. In this system, the cache is cleared when new releases are installed, so the absence of the timestamp file ensures an update and a new set of requested files.

The timestamp can applied to .js and .css file requests like so:

<script type="text/javascript" src="js/<?php echo $timestamp ?>.code.js"></script>

Zend Framework - Oauth - Get LinkedInContacts

Get a request token

            
            /* Set up the Oauth consumer */
            $oauth = new Zend_Oauth_Consumer(array(
                'consumerKey' => $this->configs->connections->consumerKey,
                'consumerSecret' => $this->configs->connections->consumerSecret));
            $oauth->setRequestTokenUrl($this->configs->token_uri);

            /* Get the request token */
            $token = $oauth->getRequestToken();

            /* This is the URI to allow the user to authorize access */
            $auth_uri = $token->xoauth_request_auth_url.'?oauth_token='.urlencode($token->oauth_token);

            /* Save the request token */
            $session=new Zend_Session_Namespace('linkedin_connections');
            $session->token = serialize($token);

Get the OAuth token and token secret for the user

              
                /* Set up the OAuth consumer */
                $oauth = new Zend_Oauth_Consumer(array(
                    'consumerKey' => $this->configs->connections->consumerKey,
                    'consumerSecret' => $this->configs->connections->consumerSecret));
                $oauth->setAccessTokenUrl($this->configs->access_uri);

                /* Get the request token */
                $session=new Zend_Session_Namespace('linkedin_connections');
                $token = unserialize($session->token);

                /* Get the access token and token secret */
                $token = $oauth->getAccessToken(
                    array('oauth_token' => $token->oauth_token,
                        'oauth_token_secret' => $token->oauth_token_secret,
                        'oauth_verifier' => $data['auth_code']),
                    $token,Zend_Oauth_Consumer::POST);

                $response = $token->getResponse();
                $this->linkedinconnections_data->oauth_token = $token->oauth_token;
                $this->linkedinconnections_data->oauth_token_secret = $token->oauth_token_secret;
                $this->linkedinconnections_data->oauth_expires_in = $token->oauth_expires_in;
                $this->linkedinconnections_data->oauth_authorization_expires_in = $token->oauth_authorization_expires_in;

Get the connections

            
            /* Set up the Oauth client */
            $oauth = new Zend_Oauth_Client(array(
                'consumerKey' => $this->configs->connections->consumerKey,
                'consumerSecret' => $this->configs->connections->consumerSecret),$this->configs->connections_uri);

            /* Set up the tokens for the user */
            $token = new Zend_Oauth_Token_Access();
            $token->setToken($this->linkedinconnections_data->oauth_token);
            $token->setTokenSecret($this->linkedinconnections_data->oauth_token_secret);

            /* Set the tokens for the client */
            $oauth->setToken($token);
            $oauth->setMethod(Zend_Oauth_Client::GET);

            /* Make the request */
            $response = $oauth->request();

Zend Framework Version 1.11

Zend Framework - Navigation View Helper - Active Style Rules

The objective of these rules are to ensure only the active link is highlighted.

The key is the ‘element>element’ selector, which indicates that only a tags which have a parent of li.active will be affected by the rule.

#mainNav a
{
color:#888;
}
#mainNav li.active>a
{
color:#000;
}
#mainNav a:hover
{
color:#444;
}

Token Handling - Zend Framework - OAuth 2.0 - Google

Sample code to get a request token from Google through OAuth 2.0. These are snippets of code to show the request and response interaction.

This is the link to allow a user to authorize application access.

        $auth_uri='https://accounts.google.com/o/oauth2/auth?'.
                'client_id='.$this->configs->contacts->client->id.'&'.
                'redirect_uri='.$this->configs->contacts->redirect_uri.'&'.
                'scope='.$this->configs->contacts->scope.'&'.
                'response_type=code';

If the user authorizes access, Google gives them a token, which is referred to as an auth_code in the following code. They paste the token in the auth_code input and click a button to initiate this action.

            if ($form->getElement('auth_code')->isValid($data['auth_code']))
            {
                $client = new Zend_Http_Client($this->configs->oauth_uri,
                    array( 'maxredirects' => 0, 'timeout'      => 30));
                $client->setMethod(Zend_Http_Client::POST);
                $client->setHeaders('Content-Type: application/x-www-form-urlencoded');
                $client->setParameterPost(array(
                    'code' => $data['auth_code'],
                    'client_id' => $this->configs->contacts->client->id,
                    'client_secret' => $this->configs->contacts->client->secret,
                    'redirect_uri' => $this->configs->contacts->redirect_uri,
                    'grant_type' => 'authorization_code'));
                $response = $client->request();
                $this->googlecontacts_data->last_status = $response->getStatus();
                if ($response->isSuccessful())
                {
                    $response_data = Zend_Json::decode($response->getBody());
                    $this->googlecontacts_data->last_auth = new Doctrine_Expression('NOW()');
                    $this->googlecontacts_data->access_token = $response_data['access_token'];
                    $this->googlecontacts_data->expires_in = $response_data['expires_in'];
                    $this->googlecontacts_data->token_type = $response_data['token_type'];
                    $this->googlecontacts_data->refresh_token = $response_data['refresh_token'];
                    $this->googlecontacts_data->status = 'authorized';
                }
                else
                {
                    $this->googlecontacts_data->access_token =
                    $this->googlecontacts_data->expires_in =
                    $this->googlecontacts_data->token_type =  
                    $this->googlecontacts_data->refresh_token = null;
                    $this->googlecontacts_data->status = 'not_authorized';
                }
                $this->save_googlecontacts_data(); 

This code uses the access or refresh token to retrieve the contacts.

        if ($this->googlecontacts_data->status == 'authorized')
        {
            $client = new Zend_Http_Client($this->configs->contacts->userinfo,
                array( 'maxredirects' => 0, 'timeout' => 30));
            $client->setMethod(Zend_Http_Client::GET);
            $client->setHeaders('Authorization: Bearer '.$this->googlecontacts_data->access_token);
            $response = $client->request();
            if (!$response->isSuccessful())
            {
                $client = new Zend_Http_Client($this->configs->oauth_uri,
                    array( 'maxredirects' => 0, 'timeout'      => 30));
                $client->setMethod(Zend_Http_Client::POST);
                $client->setHeaders('Content-Type: application/x-www-form-urlencoded');
                $client->setParameterPost(array(
                    'client_id' => $this->configs->contacts->client->id,
                    'client_secret' => $this->configs->contacts->client->secret,
                    'refresh_token' => $this->googlecontacts_data->refresh_token,
                    'grant_type' => 'refresh_token'));
                $response = $client->request();
            }
            $this->googlecontacts_data->last_status = $response->getStatus();
            if ($response->isSuccessful())
            {
                $response_data = Zend_Json::decode($response->getBody());
                $this->googlecontacts_data->last_auth = new Doctrine_Expression('NOW()');
                $this->googlecontacts_data->access_token = $response_data['access_token'];
                $this->googlecontacts_data->expires_in = $response_data['expires_in'];
                $this->googlecontacts_data->token_type = $response_data['token_type'];
                $this->googlecontacts_data->auto = null;
                $this->googlecontacts_data->deleted_at = null;
                $this->googlecontacts_data->status = 'authorized';
            }
            else
            {
                $this->googlecontacts_data->access_token =
                $this->googlecontacts_data->expires_in =
                $this->googlecontacts_data->token_type =
                $this->googlecontacts_data->refresh_token =
                $this->googlecontacts_data->auto = null;
                $this->googlecontacts_data->status = 'not_authorized';
            }
            $return = $this->save_googlecontacts_data();
            if (!isset($return['error']))
            {
                $this->view->results = $this->get_contacts();
                $this->return['success'] = true;
            }
        }
        else
            $this->return['error'] = $this->status();

Some of the config values (other omitted for security):

oauth_uri = “https://accounts.google.com/o/oauth2/token”
contacts.uri = “https://www.google.com/m8/feeds/contacts/default/full”
contacts.scope = “https://www.google.com/m8/feeds/”
contacts.userinfo = “https://www.googleapis.com/oauth2/v1/userinfo”

.ini file settings for auth_code input. This application forces the user to cut and paste the token into the browser.

[production]
action="/contacts/google”
method="post”

disableTranslator = 0
; code element
elements.auth_code.type = “ValidationTextBox”
elements.auth_code.options.label = “Authorization Code”
elements.auth_code.options.required = true
elements.auth_code.options.trim = “true”
elements.auth_code.options.class = “long”
elements.auth_code.options.validators.strlen.validator = “StringLength”
elements.auth_code.options.validators.strlen.options.min = “8″
elements.auth_code.options.validators.strlen.options.max = “100″
elements.auth_code.options.validators.regex.validator = “regex”
elements.auth_code.options.validators.regex.options.pattern = “/^[\w\/\-]{8,100}$/”
elements.auth_code.options.validators.regex.options.messages.regexInvalid = “Invalid code”
elements.auth_code.options.filters[] = “StringTrim”
elements.auth_code.options.filters[] = “StripTags”
elements.auth_code.options.filters[] = “StripNewlines”

displayGroups.gcode.options.order = 10
displayGroups.gcode.options.class = “auth_code”
displayGroups.gcode.elements[] = “auth_code”