Sunday, 31 August 2008

Googlebot, fdfdkll.html and Configuring 404 Page Not Found with PHP

After checking the logs for a website, I noticed Googlebot trying to access fdfdkll.html, which definately does not exist.

I assumed Googlebot hadn't gone mad so I did some investigations and found this is Googlebot trying to establish how the site handles invalid urls.

Thinking from the search engine perspective, this is important, as if a default page is served with a status of 200, page found, the invalid url would be listed.

This is exactly what I had done. Ooops. I had set htaccess to display the homepage if the requested page didn't exist. The site is small so a specifc error page is not really needed.

It is most likely this would effect the page rank so I really needed to change the return status to 404 if the page has been reached due to an invalid url.

The homepage is PHP, so I made the following changes:

Initial .htaccess extract

ErrorDocument 404 /home.php

I changed this to

ErrorDocument 404 /home.php?error=true

And added the following code to the start of the PHP homepage.

if($_GET['error']=='true')
header("HTTP/1.0 404 Not Found");

The header command needs to come before any output is written to the page.

So now if the homepage is accessed because the url in invalid, the error parameter is set and the status returned is 404, otherwise it is 200 as normal. From the user perspective, there is no difference, the homepage is displayed as normal.

fdfdkll.html ...Done.

Thursday, 28 August 2008

PHP: Read File of Name Value Pair Parameters into an Array

This is a simple code snippet, that can be dropped into any code, to read name value pair parameters from a file into an array, so that the parameter name is the array key.

Example params.txt file extract:
height=100
width=50

PHP code snippet:
$param = array();
$paramsFile = fopen('params.txt','r');
while(!feof($paramsFile))
{
$buffer = fgets($paramsFile);
list($name,$value) = split('=',trim($buffer));
$param[$name] = $value;
}
fclose ($paramsFile);

The array can then be referenced as:
echo $param['height'];

To use the tab character as the seperator instead of (=) equals, change the list line to:
list($name,$value) = split("\t",trim($buffer));

BlogEntry=Done

Tuesday, 19 August 2008

Java Node to String Conversion

When trying to convert a org.w3c.dom.Node to a string, I initial tried using toString(). I assumed toString() would give me text content of the XML contained in the node but it was not going to be that easy today, it gave me some kind of pointer gobbledeguck back.

OK then, with some investigation, I found that toString() shouldn't really be relied upon, so I have written the following method using StringWriter and Transform methods.

import org.w3c.dom.Node;

import java.io.StringWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;


private String nodeToString(Node node) {
StringWriter sw = new StringWriter();
try {
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.transform(new DOMSource(node), new StreamResult(sw));
} catch (TransformerException te) {
System.out.println("nodeToString Transformer Exception");
}
return sw.toString();
}

Node to String...Converted.

Monday, 18 August 2008

JSP Printing Escape Characters.

During some recent developing in Java/JSP, using the c taglib, I had an issue when I wanted to print characters that JSP interprited as escape characters.

To output an attribute stored in the request I was simply using

{$myAttribute}

This worked well until the escape character came into play.

After a bit of research, I found some instructions to change the code as follows.

<c:out escapeXml='false' value='$myAttribute'/>

This now outputs the attribute exactly as it was stored.

Done.

Thursday, 14 August 2008

Javascript Simple Image Slideshow; Take 2

This Javascript image slideshow expands on the previous version, See Here.
As in the previous version, the user navigates through the slideshow using previous and next buttons, however the previous version required images to be numbered in sequence, image1.jpg image2.jpg etc, this version allows any filename. It also makes use of the image preload code, See Here, so that the images are loaded with the page, so there is no waiting for them to during the slideshow.

<html>

<head>

<script type="text/javascript">

var imageFiles=[];
var imageIndex=0;

function imageSwitch(i,d)
{
imageIndex += d;
if(imageIndex >= imageFiles.length){imageIndex=0;}
else if(imageIndex < 0){imageIndex=imageFiles.length-1}
i.src = imageFiles[imageIndex].src;
}

function imagePreload()
{
for(i=0; i<arguments.length; i++)
{
imageFiles[imageFiles.length]=new Image();
imageFiles[imageFiles.length-1].src=arguments[i];
}
}

imagePreload('your.jpg','list.jpg','of.jpg','images.jpg','goes.jpg','here.jpg');


</script>

</head>

<body>

<h1>Switch Image Source 2</h1>

<input type="button" value="Prev" onClick="imageSwitch(document.getElementById('myImg'),-1);"/>
<input type="button" value="Next" onClick="imageSwitch(document.getElementById('myImg'),1);"/>
<br/><br/>
<img id="myImg" src="image0.jpg"/>

</body>

</html>

Much better version...Done.

Wednesday, 13 August 2008

PHP Copyright Year Trick

I found this snippet on a forum recently, it's so simple and obvious I wish I'd thought of it!

A lot of web pages have a copyright notice in the footer of the page with the year included. For server side scripts, instead of just writing the year in, you can replace it with code to dynamically write the year, so it's always up to date.

For PHP:

&copy;<?php print date("Y"); ?> My Company Ltd

You can use the same idea for other server side code, such as Java or Perl.

How Simple?

Tuesday, 12 August 2008

Generating Java Map List and Displaying with JSP

This example is to demonstrate recent investigations into outputting xml data to the page using a Java HTTPServlet and JSP with a Map List.

We want to output the id, description and size for each widget from the xml, to a form in html.
The id is stored in its own element but the description and size are name value pair properties.

<?xml version="1.0" encoding="UTF-8"?>
<widgetNode>
<widget>
<id>101</id>
<properties>
<property>
<name>description</name>
<value>Thingy Bolt</value>
</property>
<property>
<name>size</name>
<value>27</value>
</property>
</widget>
<widget>
<id>102</id>
<properties>
<property>
<name>description</name>
<value>Wotsit Nut</value>
</property>
<property>
<name>size</name>
<value>11</value>
</property>
</widget>
</widgetNode>


The Java extract here loops through each widget node, adding the values we want to Map, then adds that Map to a List.
The Map List is then added, as an attribute, to the request.

(Although unintended, this is also an example for XPath, which took a little investigation too)

...
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
...

private void processWidgetXml(HttpServletRequest request, String xml) {
List<Map<String,String>> widgets = new ArrayList<Map<String,String>>();
String expression = "/widgets/widget";
try {
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodes = (NodeList)xPath.evaluate(expression, new InputSource(new StringReader(xml)),XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
Map<String,String> widgetMap = new HashMap<String,String>();
widgetMap.put("id", (String)xPath.evaluate("id", node));
widgetMap.put("description", (String)xPath.evaluate("properties/property[name='description']/value", node));
widgetMap.put("size", (String)xPath.evaluate("properties/property[name='size']/value", node));
widgets.add(widgetMap);
}
request.setAttribute("widgets", widgets);
} catch (XPathExpressionException e) {
System.err.println("XPath expression invalid: "+e.getMessage());
e.printStackTrace();
}
}

Coming to the JSP, the following extract highlights the syntax for the Map List. It loops through all of the widgets, creating a form, with unique name, for each widget.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<% int lc = 0; %>
<c:forEach var="widget" items="${widgets}">
<form name="myWidget<%= lc %>" action="/myActionPage" method="post">
<input type="hidden" name="widgetId" value="${widget['id']}"/>
<input type="submit" value="Select"/>
${widget['description']} : ${widget['size']}
</form>
<% lc++; %>
</c:forEach>


Done... (finally)

Monday, 11 August 2008

Javascript to Pre Load Images

Normal behaviour for images on web pages is to load them when they are required. This can sometimes create an undesirable delay whilst the user navigates around the page. Not good, and annoying for the user. To get around this problem you can load the images you know you'll use as the page loads, giving the user a smoother experience.

The following Javascript loads images into an array. This forces the images to be downloaded, so that when the page requires them the browser will automatically get them from the cache. Add this code the head section.

<script type="text/javascript">

function preloadImages()
{
var images=[];
for(i=0; i<arguments.length; i++)
{
images[images.length]=new Image();
images[images.length-1].src=arguments[i];
}
}

preloadImages('your.jpg','list.jpg','of.jpg','images.jpg','goes.jpg','here.jpg');

</script>

Obviously (I hope), change the list of images passed in the call to the preloadImages function, to your own list of images.

Image Preload... Done.

Example: Preloaded images used for a slideshow

Friday, 8 August 2008

Javascript Simple Image Slideshow

This is a simple web page using HTML and Javascript. It displays an image with Next and Previous buttons.

When one of the buttons is pressed, the image is switched with the next or previous image.

As this is a simple example, the filenames for the images are expected to be the same but with incremental numbering. EG. image0.jpg, image1.jpg ... imageN.jpg.

<html>

<head>

<script type="text/javascript">
var img = 0; // Current image

function switchImage(i,d)
{
const imgs = 5; // Total number of images
img+=d;
if(img>=imgs){img=0;}
else if(img<0){img=imgs-1}
i.src = "image"+img+".jpg";
}
</script>

</head>

<body>

<h1>Switch Image Source</h1>

<input type="button" value="Prev" onClick="switchImage(document.getElementById('myImg'),-1);"/>
<input type="button" value="Next" onClick="switchImage(document.getElementById('myImg'),1);"/>
<br/><br/>
<img id="myImg" src="image0.jpg"/>

</body>

</html>

Change the imgs constant value to the total number of images you have in you slideshow.

Done.

See also: Progressed version preloading images and using any filename

Thursday, 7 August 2008

OOBasic Export Macro Part 2: CSV

Expanding on the last blog entry, See here, using a macro to create tab delimited text files, we can modify the code easily to create csv files instead.

Replace the following two lines in the code

FileProperties(2).Value = "9,0,ANSI,1"
sExt = ".txt"

With these

FileProperties(2).Value = "44,34,ANSI"
sExt = ".csv"

This then creates a comma seperated file with .csv extention for each tab within the spreadsheet.

CSV macro... Done

Wednesday, 6 August 2008

OpenOffice Basic Macro to Create Tab Delimited Text Files

This piece of code has proven most useful today...

It's a OpenOffice Basic macro that creates a tab delimited text file for each sheet in the document. Each sheet tab name is used to name the file.

Sub CreateText

  '----------------------------------------------------------------------
  ' Save document

  Dim document As Object
  Dim dispatcher As Object
  document = ThisComponent.CurrentController.Frame
  dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
  dispatcher.executeDispatch(document, ".uno:Save", "", 0, Array())

  '----------------------------------------------------------------------
  ' Export to text files

  Dim oDoc As Object ' The current Document
  Dim oSheets As Object ' A collection of all sheets
  Dim oSheet As Object ' One specific Sheet
  Dim sNewFilename As String ' New Filename
  Dim sExt As String ' The new extension to be applied
  Dim sURL As String ' Existing URL of file
  Dim sCurrDir As String ' Current Directory
  Dim oSaveSheet As Object
  Dim FileProperties(2) As New com.sun.star.beans.PropertyValue

  oDoc = thisComponent
  oSaveSheet = oDoc.CurrentController.getActiveSheet()
  oSheets = oDoc.Sheets()
  sURL = ConvertFromURL(oDoc.getLocation())
  sCurrDir = Mid(sURL,1,Len(sURL)-Len(Dir(sURL)))

  FileProperties(0).Name = "Overwrite"
  FileProperties(0).Value = True
  FileProperties(1).Name = "FilterName"
  FileProperties(1).Value = "Text - txt - csv (StarCalc)"
  FileProperties(2).Name = "FilterOptions"
  FileProperties(2).Value = "9,0,ANSI,1"
  sExt = ".txt"

  For i = 0 to (oSheets.getCount()-1)
    oSheet = oSheets.getByIndex(i)
    sNewFilename = oSheet.getName() & sExt
    oDoc.CurrentController.setActiveSheet(oSheet)
    oDoc.storeToURL(ConvertToURL(sCurrDir & sNewFilename),FileProperties())
  Next i

  oDoc.CurrentController.setActiveSheet(oSaveSheet) ' restores the original view

  MsgBox("Text Files Created")

End Sub


OOBasic Text File Export...Done

Friday, 1 August 2008

VBScript to Delete Files Older Than 14 Days

The VBScript can be run from the Windows command line or added to a windows script. It accepts a start directory as a parameter and searches through the directory and subdirectories deleting files older than 14 days.

(You can change the 14 in the code to any number of days)

Save this code in a file called DeleteOldFiles.vbs

Sub processFolder(ByRef folder, ByVal path)
  For Each file In folder.Files
    If DateDiff("d",file.datelastmodified,Date) > 14 Then
      Wscript.Echo "Deleting: "&path&"/"&file.Name
      file.Delete
    End If
  Next
  For Each subfolder In folder.SubFolders
    processFolder subfolder, path&"/"&folder.Name
  Next
End Sub
processFolder CreateObject("Scripting.FileSystemObject").GetFolder(WScript.Arguments(0)), WScript.Arguments(0)

To run the script from the command line issue the following statement, changing c:\temp to your starting directory:

cscript.exe DeleteOldFiles.vbs c:\temp


Or for the current directory

cscript.exe DeleteOldFiles.vbs .


Files Deleted Successfully.