Jun 26, 2005

And now for something completely different...

So, no newsflash here, but programmers love unix.  There are of course exceptions to the rule, but in general people I respect as programmers have a high degree of appreciation for the various flavors of desktop Unix (Linux, BSD, MAC OS X?) as productive programming environments.

One reason for this is the immense value of the command line for eliminating repetitive tasks. (Doesn't "repetitive" strike you as an onomatopoeic word?)  I am in the middle of creating image galleries for a friend's website.  I've got five or six folders full of images and find myself having to do repetitive tasks across multiple folders.  I won't bore you with my ability to resize 300 photos from the command line with a single command that specifies jpeg compression and maximum dimensions.  Instead I'll present you with the simpler problem I solved a moment ago.

The way the "files only, no db" version of my image gallery works, is by filename convention.  For instance I expect to find a file named "index" (ignoring extension) in a directory that represents an image gallery.  Index will not be displayed in the gallery layout and won't have a thumbnail generated but it will represent the gallery on the page that lists the different photo galleries.  Currently "index" has to be all lower case. I'll fix the case insensitivity later, but right now I'm trying to get the site up as fast as possible.  Stephanie has been helping me categorize the images and picked the Index for each gallery, but named it "Index".  So my problem is this: how do I rename to lower case half a dozen files in seperate directories?

If I was on windows, I'd navigate to each folder and manually rename the file.  If I had a hundred files on Windows I would write a script (python tho, not WSH VBScript).  On unix the shell hits the sweet spot for tasks that are irritating to do manually but too small or one-off to break out an editor and write a script to solve. I know I can use the find command to find all the files named "Index" so

find . -name Index.???

generates a listing like

./In_the_Studio/Index.JPG
./Memorial_Day_2005/Index.JPG
./Songfest_2000/Index.JPG
./Songfest_2001/Index.jpg
./Songfest_2002/Index.JPG
./Songfest_2003/Index.JPG
./Songfest_2004/Index.jpg

The find command, btw, takes a directory to work on (. in this case refers to the current directory I am executing the command in) and optionally a flag. I used the -name flag and supplied it with a filename pattern: I'm looking for files named Index with a 3 character extension.

Now I just need to rename those files. I was initially thinking of using the mv command by way of xargs. But just saying rename makes me think there should be a command named rename... And there is! A quick visit to a man page and I generate the following command

find . -name Index.??? | rename s/Index/index/
This command pipes the output of my find command to the rename command. Then on each filename it gets, rename runs the regular expression "s/Index/index/". The first "s" says replace the next part "Index" with the third part ("index"). One command and thirty seconds of thought... For those who can figure it out, Bash is a swiss-army-chainsaw of functionality.

Jun 03, 2005

Greasemonkey

So lately I've discovered the power of the Greasemonkey extension.  I'm not sure it offers much to the normal user, but if you're a tech savvy internet denizen not afraid to hack a little, (you are using Firefox, right?) then Greasemonkey can improve your browsing experience.  Essentially greasemonkey allows you to add your own custom javascript to a site.  Why would you want to do this?  Glad you asked.

I have a big monitor.  In fact I have two monitors attached to my primary computer, one 19" and one 17".  I rationalize that this lets me keep both my editor and a console window open and visible at the same time, but you and I both know that its really so I can browse the web and watch movies at the same time.  Unfortunately many websites are still using table based layouts with fixed widths.  This means on a big monitor like mine a website might look like this:

Notice all the whitespace on the right?  Only about 1/2 of my browser window actually contains the text that I want to read.  What I'd really prefer (since I'm using tabbed browsing) is to see the "printable" version.  No navigation or ads, and my whole screen is filled with the text I actually want to read.

So it's only one extra click to get to the printable version, but it breaks my flow for reading "article" style sites: go to the index page and open in tabs all the stories I wat to read.  So this script automatically parses the url and navigates to the printable version of the same page when installed as a greasemonkey script.  You have to read the greasemonkyey site to see how to do that, but short story is install greasemonkey in firefox, navigate to the script you want to install, and select "Install user script..." from the tools menu.  Type "http://frontpagemag.com/*" to apply the script to pages at frontpagemag.com.

Now my script is a crude little toy.  That's because my javascript skills range from nonexistent to laughable.  People (and by "people" I mean people who grok javascript and the dom a lot better than I do) are doing amazing things with greasemonkey scripts.  Among other gems are an ajax logging console and a collapsible tree interface to slashdot comments.  There is even an extension to generate a greasemonkey script from your in-the-browser edits of a web page.


May 14, 2005

PHP and XML editing

So I realise that PHP5 and simpleXML are the promised land for PHP devs in dealing with XMl.  The problem, for me and probably for you, is that I'm still stuck in the PHP 4.3.x series.  This is for a variety of reasons; I'm writing a relatively small and simple CMS that is targeting the shared hosting market segment (ie you don't have root, you can't install reallycoollibversion4.23.14b to make my cms work, and you don't have control over your PHP version.)

Now even PHP 4.3 boasts some nice integrated XML libraries. XSLT and DOM support etc. All not turned on by default of course. I am open to better solutions, but for PHP 4.3, it looks like the PEAR XML_Transformer class is the best bet for editing existing XML. This of course brings up the hideous lack of documentation online. Via PHP Kitchen's list of PEAR tutorials I found this tutorial wherein the author touts the ease of use of XML_Transform, demonstrates the settings for case folding, and never actually transforms any XML. So, the following brief snippet demonstrates a simple transformation.

What I want to do is have the equivalent of the domNode.innerHTMl = string way of editing html.  Not theoretically pure I konw, bu quick n easy!  My CMS has little snippets of XML that describe a page's configuration and in editing them I need to find a named BLOCK tag (which corresponds to a Template_Sigma variable) and replace it's current content (which may contain TEMPLATE, BLOCK, and OBJECT tages) with a new snippet of XML (which may contain new TEMPLATE, BLOCK, and OBJECT tags).

Basically, I write a class (replaceBlockContent) that extends the XML_Transformer_Namespace class. The base class handles all the opening and closing tag events, but calls methods in the child class if the tag type matches a defined function. For my class I have defined start_block and end_block methods, so they get called when a block tag (opening and closing) is found. I copied the default return statements from the base class. The block_start functions watches for a block tag to come along with the right name. It then sets the state of the class ($this->_capture=true) or increases the depth count if it is already in "capture mode". The block_end method usually just returns the closing tag and any cdata but if it returns an array of ($string, true) than $string overwrites all the returns back up to the opening tag. This is the behaviour we are looking for. The depth indicator is checked to account for the nested recursive nature of my XML schema. The following code would change this fragment of XML

<template name="index.html">
   <block name="menu">
       <data_object name="menu"></data_object>
   </block>
   <block name="middle_content">
   <template name="blog.html">
       <block name="blog">
               <data_object name="blog" factory="1" />
       </block>
       <block name="tags">
           <data_object name="blog" factory="1" tags="1" />
       </block>            
   </template>
   </block>
</template>

to this

<template name="index.html">
   <block name="menu">
       <data_object name="menu"></data_object>
   </block>
   <block name="middle_content">
       <data_object name="text"></data_object>
   </block>
</template>

Here is the code:
<?php

require_once 'XML/Transformer.php';
require_once 'XML/Transformer/Namespace.php';
class replaceBlockContent extends XML_Transformer_Namespace {
    var $_blockname ='';
    var $_data = '';
    var $_capture = false;
    var $_depth = 0;
    function replaceBlockContent($blockname, $xml)
    {
        $this->blockname = strtoupper($blockname);
        $this->xml = $xml;
    }
    function start_block($attributes)
    {
        if($this->_capture==true)
            $this->_depth++;
        if(strtoupper($attributes['name']) == $this->blockname)
        {
            $this->_blockname = $attributes['name'];
            $this->_capture=true;
        }
        return sprintf("<block %s>", XML_Util::attributesToString($attributes));
    }

    function end_block($cdata)
    {
        if($this->_capture==true and $this->_depth==0)
        {
            $this->_capture = false;
            return array('<block name="' . $this->_blockname . '">' . $this->xml . '</block>', true);
        }
        elseif($this->_capture==true)
        {
            $this->_depth--;
        }
        return array( sprintf('%s</block>', $cdata), FALSE);
    }
}

$name="middle_content"; $t=new XML_Transformer();
$t->overloadNamespace('', new replaceBlockContent($name, "<data_object name='text' />"));
$newschema = $t->transform($schema);

?>


Archive: [1]      «      14   |   15   |   16