The Motivation:

I'm big on automation. When I can make something that'll do something else for me, I probably will. Thus, a rudimentary content management system is born. For the projects index page and all the category index pages on this site, I created a custom PHP script that generates the index pages based on the directory structure and the files therein. Also, it was a good excuse to start learning PHP.

The Thinking:

I'm a MovableType user and I know that I could set that up as my interface for this projects section, but I'm not 100% happy with that input interface and a MySQL backed CMS is a little bit of overkill when directory structure would do it. I use the directories as categories, and assume all .html and .php files are articles filed under those categories. So all it takes to publish an article is to upload the file. Listing on index pages happens automatically. What about article title and article description? One possibility is to have that stored in the document name and parse it to a more readable form. That's a little cumbersome, though. A mapping between filename and article title/description in a MySQL db? Then I'd have to create an interface to create that mapping. A mapping that's intrinsically tied to the files and that has the same interface would be ideal. Article title and description information embedded directly in the files is the solution for me for now. It's directly tied to the file, and it's entered in the same way at the same time as the rest of the contents of the file.

The Execution:

Directories are created for the categories. The index file is dumped into each of these directories. Each directory also has its own images directory (which is ignored and not treated as a sub-category). This way, each category is a complete, self-contained entity. Each directory could be moved just by copying the contents without any other setup.

Index file:

<?php
include_once '/blah/page_functions.php';
?>
<html>
<head>
<title>KStroke: Projects</title>
<link rel="stylesheet" href="<?php print get_style(); ?>" type="text/css">
</head>

<body bgcolor="#FFFFFF">
<div class='page_header'>
  <span class='spacer'>&nbsp</span><?php print basename(getcwd()); ?>
</div>
<br/>
<?php
print make_page();
?>

</body>

</html>

Page functions file:

<?php
// page functions file
// index page prints result of make_page function in the body

// generates html page
function make_page() {
  
$listings = &get_listings(getcwd());
  foreach(
$listings as $listing) {
    
$result $result."\n".make_listing($listing"\t");
  }
  
$categories = &get_categories(getcwd());
  foreach(
$categories as $category) {
    
$result $result."\n".make_category($category0"\t");
  }
  return 
$result;
}

// generates html for category (including listings)
function make_category$category$level$prefix='' ) {
  
$categoryBasename substr($categorystrlen(getcwd())+1);
  
$categoryBasename str_replace('/''.'$categoryBasename);
  
$result "$prefix<div class='category_level_$level'>
\t
$prefix<a href='".substr($categorystrlen(getcwd())+1)."/'><div class='category_header'>$categoryBasename</div></a><br/>";
  
$listings = &get_listings($category);
  foreach( 
$listings as $listing ) {
    
$result $result."\n".make_listing($listing$prefix."\t");
  }
  
$sub_categories = &get_categories($category);
  foreach(
$sub_categories as $sub_category) {
    
$result $result."\n".make_category($sub_category$level+1$prefix."\t");
  }
  
$result $result."\n$prefix</div>\n";
  return 
$result;
}

// generates html for listing
function make_listing$listing$prefix='' ) {
  
$result "$prefix<div class='listing'>";
  
$file fopen($listing'r');
  
$header get_listing_header($file);
  
// assumed to be subdirectory + 1 to account for /
  
$link substr($listingstrlen(getcwd())+1);  
  if (!empty(
$header)) {
    
$result $result."\n\t$prefix<a href='$link'><div class='listing_header'><span class='spacer'>&nbsp</span>$header</div></a>";
  }
  
rewind($file);
  
$description get_listing_description($file);
  if (!empty(
$description)) {
    
$result $result."\n\t$prefix<div class='listing_description'>$description</div><br/>";
  }
  if (empty(
$description) && empty($header)) {
    
$filename basename($listing);
    
$filename preg_replace('!(.*)\..+$!'"\1"$filename);
    
$result $result."\n\t$prefix<a href='$link'><div class='listing_header'><span class='spacer'>&nbsp</span>$filename</div></a><br/>";
  }
  
$result $result."\n$prefix</div>";
  return 
$result;
}

// generates listing header
// searches for <kstroke_listing_header></kstroke_listing_header>
function get_listing_header$file ) {
  
$tagStart '<kstroke_listing_header>';
  
$tagStartLen strlen($tagStart);
  
$foundStart false;
  
$tagStartPos = -1;
  while(!
feof($file)) {
    
$line fgets($file999);
    
$tagStartPos strpos($line$tagStart);
    if(!(
$tagStartPos === false)) {
      
$result substr($line$tagStartPos $tagStartLen);
      
$foundStart true;
      break;
    }
  }
  if(
$foundStart) {
    
$tagEnd '</kstroke_listing_header>';
    while(!
feof($file)) {
      
$tagEndPos strpos($result$tagEnd);
      if(!(
$tagEndPos === false)) {
    
$result substr($result0$tagEndPos);
    break;
      }
      
$result $result.' '.fgets($file999);
    }
  }
  return 
$result;
}

// generates listing description
// searches for <kstroke_listing_description></kstroke_listing_description>
function get_listing_description$file ) {
  
$tagStart '<kstroke_listing_description>';
  
$tagStartLen strlen($tagStart);
  
$foundStart false;
  
$tagStartPos = -1;
  while(!
feof($file)) {
    
$line fgets($file999);
    
$tagStartPos strpos($line$tagStart);
    if(!(
$tagStartPos === false)) {
      
$result substr($line$tagStartPos $tagStartLen);
      
$foundStart true;
      break;
    }
  }
  if(
$foundStart) {
    
$tagEnd '</kstroke_listing_description>';
    while(!
feof($file)) {
      
$tagEndPos strpos($result$tagEnd);
      if(!(
$tagEndPos === false)) {
    
$result substr($result0$tagEndPos);
    break;
      }
      
$result $result.' '.fgets($file999);
    }
  }
  return 
$result;
}

// returns ref to array of categories within given category
function &get_categories$category ) {
  
$result = array();
  
$directory =  $category;
  
$dir_object = @ dir ($directory) or die ("Could not open a directory stream for <i>$directory</i>");
  
$idx 0;
  while( 
$entry $dir_object->read() ) {
    if( 
is_dir($category.'/'.$entry) ) {
      
// exclude ".", "..", and "images" directories
      
if( !ereg('^((\.)|(images))'$entry) ) {  
    
$result[$idx] = $directory.'/'.$entry;
    
$idx++;
      }
    }
  }
  
$dir_object->close();
  return 
$result;
}

// returns ref to array of listings within given category
function &get_listings$category ) {
  
$result = array();
  
$directory =  $category;
  
$dir_object = @ dir ($directory) or die ("Could not open a directory stream for <i>$directory</i>");
  
$idx 0;
  while( 
$entry $dir_object->read() ) {
    if( 
is_file($category.'/'.$entry) ) {
      
// exclude index.html and index.php
      
if( !eregi'^index\.(html|php)$'$entry ) ) {
    
// include html and php files
    
if( eregi'\.(html|php)$'$entry ) ) {
      
$result[$idx] = $category.'/'.$entry;
      
$idx++;
    }
      }
    }
  }
  
$dir_object->close();
  return 
$result;
}

function 
get_style() {
  
$result 'style.css';
  while(!
is_file($result)) {
    
$result '../'.$result;
  }
  return 
$result;
}
?>

Articles are created with PHP comments containing the title and description information at the beginning of the file.

<?php
/*
commented out in php to keep from showing up in final output
<kstroke_listing_header>This Projects Page</kstroke_listing_header>
<kstroke_listing_description>How and why this Projects page is made the way it is.</kstroke_listing_description>
 */
?>

Other:

Higlighting PHP for HTML using PHP?

<?php
$temp 
"
<?php
code code code don't forget to escape quotes code code code
?>
"
;
highlight_string($temp);
?>

I consider dates critically important to written documents. You cannot get a complete context in which to understand documents without knowing when they were written. Let's say I pick up a book that says Advanced Java. I flip through and it seems ridiculously simple and stupid. I look at the publishing date in the front, see 1997, and better understand how they were allowed to put "advanced" on the cover. Lots of articles on the web have no date. That's bad. Especially for tutorial-type sites. I see tutorials with outdated techniques all over the place, and there's no way to tell they're outdated without continuing your search and seeing the new techniques and the new tutorials that say what you read before is deprecated. How do you add the date a file/article was last modified in PHP?

<?php 
print date("Y.m.d H:i"getlastmod());
?>


Stuff to do later if so inclined:
- control ordering of categories and articles based on something other than what was returned by the directory/file listing operation
- create an index page that's not list oriented

last modified // 2005.11.27 // 12:09

kstroke // projects // nerdy