<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Programming blog</title>
  <link rel="alternate" type="text/html" href="http://davidwees.com/myblog"/>
  <link rel="self" type="application/atom+xml" href="http://davidwees.com/myblog/atom.xml"/>
  <id>http://davidwees.com/myblog/atom.xml</id>
  <updated>2008-08-29T20:11:10-04:00</updated>
  <entry>
    <title>Using Red 5 Flash media server with Drupal</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/317" />
    <id>http://davidwees.com/myblog/node/317</id>
    <published>2009-02-20T23:03:50-05:00</published>
    <updated>2009-02-23T18:00:15-05:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="drupal" />
    <category term="flash" />
    <summary type="html"><![CDATA[<p>I am working on a project where I wanted to be able to record audio through a web browser.  Not having the $5000 to pay for Adobe Flash Media Server, I decided to try to use Red 5 instead.</p>
<p>My first step was to download <a href="http://osflash.org/red5">Red 5</a> and get it installed locally.  As I'm on a shared computer, I'm developing in Windows so I used the Windows version.  It installs relatively easily, and you can check to make sure it is running smoothly by visiting <a href="http://localhost:5080" title="http://localhost:5080">http://localhost:5080</a>, assuming you install it with the default port.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>I am working on a project where I wanted to be able to record audio through a web browser.  Not having the $5000 to pay for Adobe Flash Media Server, I decided to try to use Red 5 instead.</p>
<p>My first step was to download <a href="http://osflash.org/red5">Red 5</a> and get it installed locally.  As I'm on a shared computer, I'm developing in Windows so I used the Windows version.  It installs relatively easily, and you can check to make sure it is running smoothly by visiting <a href="http://localhost:5080" title="http://localhost:5080">http://localhost:5080</a>, assuming you install it with the default port.</p>
<p>One problem I discovered here was that if you shut down the Red 5 server improperly, ie. not using the batch script provided, you have to re-install Red 5 to get it up and running properly.  That one was a big pain until I figured it out.  Noticeably, the Red 5 install on Windows did not create a shortcut in my Start menu for shutting down the Red 5 server, so I suggest you set one up yourself.   Another issue was that I needed to set the JAVA_HOME and JAVA_VERSION environmental variables, or rather I did, and ended up having fewer problems afterward...</p>
<p>Once I had this all set up and tested, I verified that I could actually record audio + video using the Red 5 demos, available at <a href="http://localhost:5080" title="http://localhost:5080">http://localhost:5080</a>.  I checked to see what directory the RTMP stream was being recorded to, and verified that a file was being created when I was recording the demo.  This ended up being inside  C:\Red5\webapps\oflaDemo\streams as I chose to install Red 5 to the root directory of my Windows installation.  The next step was to try and get the audio recorder embedded within a Drupal node.</p>
<p>At first I tried out a module which claims to allow users to record audio and video from a webcam, and although the structure of the module was sound, I discovered a major problem.  The URL for the Red 5 server was hard-coded into the SWF which I discovered after using a free tool to decompile the SWF as the module author did not include the source code for his web cam recorder.</p>
<p>So I looked around to see if anyone else had tackled this issue, and sure enough <a href="http://fms.denniehoopingarner.com/">Dennie Hoopingarner</a> had figured it out.   He provides some advice, tutorials, and the license to use his scripts for free.  Very nice guy!  Also, his SWF audio and video recorders allow you to define the location of your Red 5 server using Flashvars, which made it relatively easy to create a module to handle the creation of audio files.</p>
<p>I created a module to handle the creation and display of the audio files.  Basically what I do is, on node creation I move the newly created audio.flv file into the Drupal file system (if it exists) and I rely on the SWFTools module (with the JW Mediaplayer v4 installed) to display an audio player for the file when the node is being viewed.  It's pretty bare boned without any significant features, but it works.</p>
<p>I used the Audio recorder available from <a href="http://fms.denniehoopingarner.com/">Dennie Hoopingarner's website</a> to provide the Flash ability to create RTMP streams back to my Red 5 media server.</p>
<p>On Linux, you'll have to make sure you set your permissions correctly in your Red 5 application folder so that this module can move the created file over.  You may also have to fiddle with the settings initially to get it to work.  For example, "Red5 Flash Server IP Address" should really be named "where do you access your Red 5 OFLADemo through a browser" but I just took a bunch of the code from the original module (which remember didn't work) and adapted it to my use case.</p>
<p>Let me know if there are any other issues.  This is still a work in progress, but I thought some people might be interested in hearing about using Red 5 with Drupal.</p>
    ]]></content>
  </entry>
  <entry>
    <title>Creating a module to allow a user to upload multiple files at once in a ZIP Archive</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/316" />
    <id>http://davidwees.com/myblog/node/316</id>
    <published>2008-12-11T01:21:37-05:00</published>
    <updated>2008-12-11T02:28:41-05:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="code" />
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>I am working on a website called <a href="http://pedagogle.com">Pedagogle.com</a> which is intended to be a file sharing website for educators.  The plan is, upload and categorize resources, and if enough people do this, and we have enough resources, then we end up with a website where teachers can go to find resources easily.   The problem is that it takes a long time to upload and categorize each file, which leads to frustration from users.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>I am working on a website called <a href="http://pedagogle.com">Pedagogle.com</a> which is intended to be a file sharing website for educators.  The plan is, upload and categorize resources, and if enough people do this, and we have enough resources, then we end up with a website where teachers can go to find resources easily.   The problem is that it takes a long time to upload and categorize each file, which leads to frustration from users.  So far hardly any files have been uploaded, mostly because I think the process is a bit too difficult for the average teachers, and is definitely too time-consuming.</p>
<p>So I'm developing a module which will cut down the time it takes to share resources on my site.  The basic work-flow I envisioned is, a teacher creates a ZIP archive for a folder of digital resources they have created, and uploads the ZIP.  They then are presented with a form where they categorize all of the resources and save them to the database, with the option of deleting any files from our server right then that were included in the ZIP archive by error.</p>
<p>The first step in writing this module was to decide on how this was going to work conceptually for the user.  It occurred to me that some users might not want to complete the categorization all at the same time, so that the files should be unarchived and stored in a temporary folder on the computer while the user is in the middle of the categorization, and that the process should be one that the user can come back to later if they like.  This meant that the URL for the uploading of the ZIP should be different than the URL for managing the ZIP files to make the UI a bit easier to manage.</p>
<p>I implemented hook_perm() and hook_menu() in my module first like so:<br />
<div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/**<br /> * Implementation of hook_perm().<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">zip_upload_perm</span><span style="color: #007700">() {<br />&nbsp; return array(</span><span style="color: #DD0000">'upload zip files'</span><span style="color: #007700">);<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
<p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/**<br /> * Implementation of hook_menu().<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">zip_upload_menu</span><span style="color: #007700">() {<br />&nbsp; </span><span style="color: #0000BB">$items </span><span style="color: #007700">= array();<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #DD0000">'zip_upload'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'title' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'Upload ZIP'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'description' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'Allows you to upload a zip of your resources and then categorize and save the resources'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'page callback' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'drupal_get_form'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'page arguments' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'zip_upload'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'access callback' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'user_access'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'access arguments' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'upload zip files'</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #DD0000">'zip_upload/my'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'title' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'My ZIP Uploads'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'description' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'Categorize your uploaded files here and save them permanently'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'page callback' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'drupal_get_form'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'page arguments' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'zip_upload_recent'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'access callback' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'user_access'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'access arguments' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'upload zip files'</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #DD0000">'admin/settings/zip_upload'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'title' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'Zip Uploads'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'description' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'Configure zip uploads for your site.'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'page callback' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'drupal_get_form'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'page arguments' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'zip_upload_settings_form'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'access callback' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'user_access'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'access arguments' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'administer site configuration'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">MENU_NORMAL_ITEM</span><span style="color: #007700">,<br />&nbsp; );<br />&nbsp; <br />&nbsp; return </span><span style="color: #0000BB">$items</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
<p>I decided that even though my module was for a specific website, I should build it with <em>some</em> generalization in mind, just to make it easier to use the module for a different site.  This is why I have a menu entry in my hook_menu() for a settings page.</p>
<p>Once I had my menu entries sorted out, the next step was to build both the forms for each path, but also the submit functions for these entries, which are the most difficult part of this module.  The form for the settings page was extremely easy, especially as I could let Drupal handle the submission of the form by running it through system_settings_form.</p>
<p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/**<br /> * Custom settings form for zip uploads<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">zip_upload_settings_form</span><span style="color: #007700">() {<br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'zip_upload_import_path'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'textfield'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Zip Uploads path'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#description' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Choose to which directory inside your default files directory your zip uploads will be saved temporarily.&nbsp; Note that once the user finishes categorizing their resources, the zip files and the extracted files will be deleted automatically.'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">variable_get</span><span style="color: #007700">(</span><span style="color: #DD0000">'zip_upload_import_path'</span><span style="color: #007700">, </span><span style="color: #DD0000">'zip_upload'</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'zip_upload_allowed_file_types'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'textfield'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Allowed extensions'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#description' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Choose which extensions of files are allowed to be contained within the zip archives.&nbsp; Separate each extension with a space.'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">variable_get</span><span style="color: #007700">(</span><span style="color: #DD0000">'zip_upload_allowed_file_types'</span><span style="color: #007700">, </span><span style="color: #DD0000">'jpeg jpg png gif'</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$types </span><span style="color: #007700">= </span><span style="color: #0000BB">node_get_types</span><span style="color: #007700">();<br />&nbsp; <br />&nbsp; foreach (</span><span style="color: #0000BB">$types </span><span style="color: #007700">as </span><span style="color: #0000BB">$name </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$type</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$options</span><span style="color: #007700">[</span><span style="color: #0000BB">$name</span><span style="color: #007700">] = </span><span style="color: #0000BB">$name</span><span style="color: #007700">;<br />&nbsp; }<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'zip_upload_node_type'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'select'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Choose node type'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#description' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'You must associate each file uploaded with a node type that has a either an upload field or a filefield attached to it.'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#options' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$options</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">variable_get</span><span style="color: #007700">(</span><span style="color: #DD0000">'zip_upload_node_type'</span><span style="color: #007700">, </span><span style="color: #DD0000">'page'</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; <br />&nbsp; return </span><span style="color: #0000BB">system_settings_form</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">);<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
<p>The upload ZIP form was also very easy, I just had to remember to make the form have the right information initially, so hence  $form['#attributes'] = array("enctype" => "multipart/form-data");.</p>
<p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/**<br /> * Zip Upload form<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">zip_upload</span><span style="color: #007700">() {<br />&nbsp; </span><span style="color: #0000BB">$form </span><span style="color: #007700">= array();<br /><br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'#attributes'</span><span style="color: #007700">] = array(</span><span style="color: #DD0000">"enctype" </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">"multipart/form-data"</span><span style="color: #007700">);<br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'zipfile'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'file'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Zip'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#size' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">40</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#description' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Click "Browse..." to select a Zip file to upload.'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#weight' </span><span style="color: #007700">=&gt; -</span><span style="color: #0000BB">3</span><span style="color: #007700">,<br />&nbsp; );<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'hash_directory'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'value'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">md5</span><span style="color: #007700">(</span><span style="color: #0000BB">mktime</span><span style="color: #007700">()),<br />&nbsp; );<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'file'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'value'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">''</span><span style="color: #007700">,<br />&nbsp; );<br /><br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'buttons'</span><span style="color: #007700">][</span><span style="color: #DD0000">'submit'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'submit'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Import zip'</span><span style="color: #007700">),<br />&nbsp; );<br /><br />&nbsp; return </span><span style="color: #0000BB">$form</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
<p>The hard part about the ZIP upload form was the validating function and the submit function.  I decided the best way of handling this section would be to extract the ZIP during validation (after verifying it was a legitimate ZIP archive) into a temporary folder and then go through each file and check the extension, and remove all files with improper file extensions immediately.  In the submit handler, I save the information about the directory for later, so that the user can come back to the next step, and then redirect the user to the next form.</p>
<p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/**<br /> * Validate upload form<br /> *<br /> * @function<br /> * Confirm the user has uploaded a zipped file first<br /> * then we extract the file to a temporary folder and remove any undesired files.<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">zip_upload_validate</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #0000BB">$dirpath </span><span style="color: #007700">= </span><span style="color: #0000BB">file_create_path</span><span style="color: #007700">(</span><span style="color: #0000BB">variable_get</span><span style="color: #007700">(</span><span style="color: #DD0000">'zip_upload_import_path'</span><span style="color: #007700">, </span><span style="color: #DD0000">'zip_upload'</span><span style="color: #007700">));<br />&nbsp; if (</span><span style="color: #0000BB">file_check_directory</span><span style="color: #007700">(</span><span style="color: #0000BB">$dirpath</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp; if (</span><span style="color: #0000BB">$file </span><span style="color: #007700">= </span><span style="color: #0000BB">file_save_upload</span><span style="color: #007700">(</span><span style="color: #DD0000">'zipfile'</span><span style="color: #007700">, array(</span><span style="color: #DD0000">'file_validate_extensions' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'zip'</span><span style="color: #007700">)),</span><span style="color: #0000BB">$dirpath </span><span style="color: #007700">. </span><span style="color: #0000BB">$file</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">filename</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!</span><span style="color: #0000BB">$file</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">form_set_error</span><span style="color: #007700">(</span><span style="color: #DD0000">'zipfile'</span><span style="color: #007700">, </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Zip file not uploaded'</span><span style="color: #007700">));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// try to avoid php's script timeout with large files or a slow machine<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if (!</span><span style="color: #0000BB">ini_get</span><span style="color: #007700">(</span><span style="color: #DD0000">'safe_mode'</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">set_time_limit</span><span style="color: #007700">(</span><span style="color: #0000BB">0</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// extract the zip file into the subdirectory<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$zip </span><span style="color: #007700">= new </span><span style="color: #0000BB">ZipArchive</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (</span><span style="color: #0000BB">$zip</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">open</span><span style="color: #007700">(</span><span style="color: #0000BB">$dirpath </span><span style="color: #007700">. </span><span style="color: #DD0000">'/' </span><span style="color: #007700">. </span><span style="color: #0000BB">$file</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">filename</span><span style="color: #007700">) === </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$directory </span><span style="color: #007700">= </span><span style="color: #0000BB">file_create_path</span><span style="color: #007700">(</span><span style="color: #0000BB">$dirpath </span><span style="color: #007700">. </span><span style="color: #DD0000">'/' </span><span style="color: #007700">. </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">][</span><span style="color: #DD0000">'hash_directory'</span><span style="color: #007700">]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$zip</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">extractTo</span><span style="color: #007700">(</span><span style="color: #0000BB">$directory</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$zip</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">close</span><span style="color: #007700">();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">file_delete</span><span style="color: #007700">(</span><span style="color: #0000BB">$dirpath </span><span style="color: #007700">.</span><span style="color: #DD0000">'/'</span><span style="color: #007700">. </span><span style="color: #0000BB">$file</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">filename</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// check here for bad files and remove them?<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$allowed_extensions </span><span style="color: #007700">= </span><span style="color: #0000BB">drupal_strtolower</span><span style="color: #007700">(</span><span style="color: #0000BB">variable_get</span><span style="color: #007700">(</span><span style="color: #DD0000">'zip_upload_allowed_file_types'</span><span style="color: #007700">, </span><span style="color: #DD0000">'jpeg jpg png gif'</span><span style="color: #007700">));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$files </span><span style="color: #007700">= </span><span style="color: #0000BB">file_scan_directory</span><span style="color: #007700">(</span><span style="color: #0000BB">$directory</span><span style="color: #007700">, </span><span style="color: #DD0000">'.*'</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// This is crucial.&nbsp; We need to remove disallowed files.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$invalid </span><span style="color: #007700">= </span><span style="color: #0000BB">0</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (</span><span style="color: #0000BB">$files </span><span style="color: #007700">as </span><span style="color: #0000BB">$filename </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$file</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$errors </span><span style="color: #007700">= </span><span style="color: #0000BB">file_validate_extensions</span><span style="color: #007700">(</span><span style="color: #0000BB">$files</span><span style="color: #007700">[</span><span style="color: #0000BB">$filename</span><span style="color: #007700">], </span><span style="color: #0000BB">$allowed_extensions</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (</span><span style="color: #0000BB">$errors</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$name </span><span style="color: #007700">= </span><span style="color: #0000BB">array_pop</span><span style="color: #007700">(</span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">"/"</span><span style="color: #007700">, </span><span style="color: #0000BB">$filename</span><span style="color: #007700">));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// warn the user<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">drupal_set_message</span><span style="color: #007700">(</span><span style="color: #DD0000">"Skipping " </span><span style="color: #007700">. </span><span style="color: #0000BB">check_plain</span><span style="color: #007700">(</span><span style="color: #0000BB">$name</span><span style="color: #007700">) .</span><span style="color: #DD0000">" because extension isn't allowed"</span><span style="color: #007700">, </span><span style="color: #DD0000">'error'</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// remove the file<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">file_delete</span><span style="color: #007700">(</span><span style="color: #0000BB">$filename</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unset(</span><span style="color: #0000BB">$files</span><span style="color: #007700">[</span><span style="color: #0000BB">$filename</span><span style="color: #007700">]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$invalid</span><span style="color: #007700">++;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (</span><span style="color: #0000BB">$invalid</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">drupal_set_message</span><span style="color: #007700">(</span><span style="color: #DD0000">"There were $invalid errors while extracting your archive."</span><span style="color: #007700">, </span><span style="color: #DD0000">'error'</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; elseif (!empty(</span><span style="color: #0000BB">$files</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">drupal_set_message</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Zip file uploaded and extracted.'</span><span style="color: #007700">), </span><span style="color: #DD0000">'success'</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (empty(</span><span style="color: #0000BB">$files</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// remove empty subdirectories<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">_zip_upload_rmdir_recurse</span><span style="color: #007700">(</span><span style="color: #0000BB">$directory</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// remove the now empty top directory<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">rmdir</span><span style="color: #007700">(</span><span style="color: #0000BB">$directory</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">form_set_error</span><span style="color: #007700">(</span><span style="color: #DD0000">'zipfile'</span><span style="color: #007700">, </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Your archive has no files with valid extensions in it.&nbsp; Please upload a different archive and try again.'</span><span style="color: #007700">));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">drupal_set_message</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'There was a problem trying to extract the zip archive to the temporary folder.&nbsp; Report this issue to your system administrator.'</span><span style="color: #007700">), </span><span style="color: #DD0000">'error'</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }<br />}<br /><br /></span><span style="color: #FF8000">/**<br /> * Submit upload form<br /> *<br /> * @function<br /> * Here we store the information about the temporary folder and send the user to a<br /> * form to categorize and give a description to each file.<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">zip_upload_submit</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #FF8000">// save the zip file location and current user to the database for the next step<br />&nbsp; </span><span style="color: #007700">global </span><span style="color: #0000BB">$user</span><span style="color: #007700">;<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">db_query</span><span style="color: #007700">(</span><span style="color: #DD0000">"INSERT INTO {zip_upload} (uid, hash_directory) VALUES (%d, '%s')"</span><span style="color: #007700">, </span><span style="color: #0000BB">$user</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">uid</span><span style="color: #007700">, </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">][</span><span style="color: #DD0000">'hash_directory'</span><span style="color: #007700">]);<br />&nbsp; <br />&nbsp; return </span><span style="color: #0000BB">drupal_goto</span><span style="color: #007700">(</span><span style="color: #DD0000">'zip_upload/my'</span><span style="color: #007700">);<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
<p>Once the files are sorted onto the server and stored in a location which is difficult for the user to access, we then want the user to decide on a categorization for each file and give each file a quick description.   Since I've stored the names of the temporary directories in my database at the previous step on a per user basis, this allows me to keep each set of uploaded files separate, and allows users to come back to the final form whenever they want.  I decided on a gigantic table for my form with the vocabularies as select elements, the description for each file as a textfield, and then a checkbox next to each file name in each table so that the user could choose which files to either save permanently or delete.</p>
<p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/**<br /> * Categorize recent resources form<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">zip_upload_recent</span><span style="color: #007700">() {<br />&nbsp; </span><span style="color: #0000BB">$form </span><span style="color: #007700">= array();<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$vocabularies </span><span style="color: #007700">= </span><span style="color: #0000BB">_zip_upload_get_vocabularies</span><span style="color: #007700">();<br />&nbsp; <br />&nbsp; </span><span style="color: #FF8000">// get the current user<br />&nbsp; </span><span style="color: #007700">global </span><span style="color: #0000BB">$user</span><span style="color: #007700">;<br />&nbsp; <br />&nbsp; </span><span style="color: #FF8000">// look up any folders the user has created because of a zip upload<br />&nbsp; </span><span style="color: #0000BB">$result </span><span style="color: #007700">= </span><span style="color: #0000BB">db_query</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT hash_directory FROM {zip_upload} WHERE uid = %d"</span><span style="color: #007700">, </span><span style="color: #0000BB">$user</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">uid</span><span style="color: #007700">);<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'markup'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Uploaded files'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#description' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Categorize each file, then click on Save'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#tree' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">,<br />&nbsp; );<br />&nbsp; <br />&nbsp; </span><span style="color: #FF8000">// at this stage files with disallowed extensions should be removed already.<br />&nbsp; </span><span style="color: #007700">while (</span><span style="color: #0000BB">$row </span><span style="color: #007700">= </span><span style="color: #0000BB">db_fetch_array</span><span style="color: #007700">(</span><span style="color: #0000BB">$result</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$dirpath </span><span style="color: #007700">= </span><span style="color: #0000BB">file_directory_path</span><span style="color: #007700">() .</span><span style="color: #DD0000">'/'</span><span style="color: #007700">. </span><span style="color: #0000BB">variable_get</span><span style="color: #007700">(</span><span style="color: #DD0000">'zip_upload_import_path'</span><span style="color: #007700">, </span><span style="color: #DD0000">'zip_upload'</span><span style="color: #007700">) .</span><span style="color: #DD0000">'/'</span><span style="color: #007700">. </span><span style="color: #0000BB">$row</span><span style="color: #007700">[</span><span style="color: #DD0000">'hash_directory'</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp; if (</span><span style="color: #0000BB">file_check_directory</span><span style="color: #007700">(</span><span style="color: #0000BB">$dirpath</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$directory </span><span style="color: #007700">= </span><span style="color: #0000BB">$dirpath</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp; };<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$files </span><span style="color: #007700">= </span><span style="color: #0000BB">file_scan_directory</span><span style="color: #007700">(</span><span style="color: #0000BB">$directory</span><span style="color: #007700">, </span><span style="color: #DD0000">'.*'</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// add an index to each textfield<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$index </span><span style="color: #007700">= </span><span style="color: #0000BB">0</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; foreach (</span><span style="color: #0000BB">$files </span><span style="color: #007700">as </span><span style="color: #0000BB">$filename </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$file</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">][</span><span style="color: #0000BB">$index</span><span style="color: #007700">][</span><span style="color: #DD0000">'checked'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'checkbox'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#default_value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">0</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#attributes' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'class' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'checked'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">][</span><span style="color: #0000BB">$index</span><span style="color: #007700">][</span><span style="color: #DD0000">'filename'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'value'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$filename</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// add a description<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">][</span><span style="color: #0000BB">$index</span><span style="color: #007700">][</span><span style="color: #DD0000">'description'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'textfield'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#size' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">20</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#maxlength' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">255</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">''</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#attributes' </span><span style="color: #007700">=&gt; array(</span><span style="color: #DD0000">'class' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'textfield-' </span><span style="color: #007700">. </span><span style="color: #0000BB">$index</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// now we include the taxonomy terms, bulk of this copied from taxonomy.module<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">foreach (</span><span style="color: #0000BB">$vocabularies </span><span style="color: #007700">as </span><span style="color: #0000BB">$vid </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$name</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// add a taxonomy select<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">][</span><span style="color: #0000BB">$index</span><span style="color: #007700">][</span><span style="color: #DD0000">'taxonomy'</span><span style="color: #007700">][</span><span style="color: #0000BB">$vid</span><span style="color: #007700">] = </span><span style="color: #0000BB">taxonomy_form</span><span style="color: #007700">(</span><span style="color: #0000BB">$vid</span><span style="color: #007700">, </span><span style="color: #0000BB">0</span><span style="color: #007700">, </span><span style="color: #DD0000">' '</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// add a class to each taxonomy select to make it easier to grab with jQuery<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">][</span><span style="color: #0000BB">$index</span><span style="color: #007700">][</span><span style="color: #DD0000">'taxonomy'</span><span style="color: #007700">][</span><span style="color: #0000BB">$vid</span><span style="color: #007700">][</span><span style="color: #DD0000">'#attributes'</span><span style="color: #007700">] = array(</span><span style="color: #DD0000">'class' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'taxonomy-' </span><span style="color: #007700">. </span><span style="color: #0000BB">$vid</span><span style="color: #007700">); <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// remove the title from each field<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">unset(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">][</span><span style="color: #0000BB">$index</span><span style="color: #007700">][</span><span style="color: #DD0000">'taxonomy'</span><span style="color: #007700">][</span><span style="color: #0000BB">$vid</span><span style="color: #007700">][</span><span style="color: #DD0000">'#title'</span><span style="color: #007700">]);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$index</span><span style="color: #007700">++;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// remove this directory to help keep the folder clean.<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if (</span><span style="color: #0000BB">$index </span><span style="color: #007700">=== </span><span style="color: #0000BB">0</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (</span><span style="color: #0000BB">file_check_directory</span><span style="color: #007700">(</span><span style="color: #0000BB">$directory</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">//_zip_upload_rmdir_recurse($directory);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">rmdir</span><span style="color: #007700">(</span><span style="color: #0000BB">$directory</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">db_query</span><span style="color: #007700">(</span><span style="color: #DD0000">"DELETE FROM {zip_upload} WHERE hash_directory = '%s'"</span><span style="color: #007700">, </span><span style="color: #0000BB">$row</span><span style="color: #007700">[</span><span style="color: #DD0000">'hash_directory'</span><span style="color: #007700">]);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'number_of_files'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'value'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$index</span><span style="color: #007700">,<br />&nbsp; );<br /><br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'submit'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'submit'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Save'</span><span style="color: #007700">),<br />&nbsp; );<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'delete'</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'submit'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Delete'</span><span style="color: #007700">),<br />&nbsp; );<br /><br />&nbsp; return </span><span style="color: #0000BB">$form</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
<p>Theming this form was relatively straight forward, I just converted by fields into individual rows and sent off most of the form to be themed as a table.  At this stage I also added a CSS file and a JS file for later customization which I suspected was going to be necessary.</p>
<p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/**<br /> * Theme the upload categorization form<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">theme_zip_upload_recent</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #0000BB">$path </span><span style="color: #007700">= </span><span style="color: #0000BB">drupal_get_path</span><span style="color: #007700">(</span><span style="color: #DD0000">'module'</span><span style="color: #007700">, </span><span style="color: #DD0000">'zip_upload'</span><span style="color: #007700">);<br />&nbsp; </span><span style="color: #0000BB">drupal_add_js</span><span style="color: #007700">(</span><span style="color: #0000BB">$path </span><span style="color: #007700">. </span><span style="color: #DD0000">'/js/zip_upload.js'</span><span style="color: #007700">);<br />&nbsp; </span><span style="color: #0000BB">drupal_add_css</span><span style="color: #007700">(</span><span style="color: #0000BB">$path </span><span style="color: #007700">. </span><span style="color: #DD0000">'/css/zip_upload.css'</span><span style="color: #007700">);<br />&nbsp; </span><span style="color: #0000BB">$vocabularies </span><span style="color: #007700">= </span><span style="color: #0000BB">_zip_upload_get_vocabularies</span><span style="color: #007700">();<br /><br />&nbsp; </span><span style="color: #FF8000">// build the header of the table<br />&nbsp; </span><span style="color: #0000BB">$header </span><span style="color: #007700">= array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Select'</span><span style="color: #007700">), <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'File name'</span><span style="color: #007700">)<br />&nbsp; );<br />&nbsp; foreach (</span><span style="color: #0000BB">$vocabularies </span><span style="color: #007700">as </span><span style="color: #0000BB">$vid </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$vocabulary</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$header</span><span style="color: #007700">[] = </span><span style="color: #0000BB">check_plain</span><span style="color: #007700">(</span><span style="color: #0000BB">$vocabulary</span><span style="color: #007700">);<br />&nbsp; }<br />&nbsp; </span><span style="color: #FF8000">// unlikely we'll have more than 1000 vocubularies created on the same site<br />&nbsp; </span><span style="color: #0000BB">$header</span><span style="color: #007700">[] = </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'Description'</span><span style="color: #007700">);<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$rows </span><span style="color: #007700">= array();<br />&nbsp; <br />&nbsp; for (</span><span style="color: #0000BB">$index </span><span style="color: #007700">= </span><span style="color: #0000BB">0</span><span style="color: #007700">; </span><span style="color: #0000BB">$index </span><span style="color: #007700">&lt; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'number_of_files'</span><span style="color: #007700">][</span><span style="color: #DD0000">'#value'</span><span style="color: #007700">]; </span><span style="color: #0000BB">$index</span><span style="color: #007700">++) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$row </span><span style="color: #007700">= array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'checked' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">drupal_render</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">][</span><span style="color: #0000BB">$index</span><span style="color: #007700">][</span><span style="color: #DD0000">'checked'</span><span style="color: #007700">]),<br />&nbsp;&nbsp;&nbsp; );<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$name </span><span style="color: #007700">= </span><span style="color: #0000BB">array_pop</span><span style="color: #007700">(</span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">"/"</span><span style="color: #007700">, </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">][</span><span style="color: #0000BB">$index</span><span style="color: #007700">][</span><span style="color: #DD0000">'filename'</span><span style="color: #007700">][</span><span style="color: #DD0000">'#value'</span><span style="color: #007700">]));<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// already filtered<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$row</span><span style="color: #007700">[</span><span style="color: #DD0000">'filename'</span><span style="color: #007700">] = </span><span style="color: #0000BB">check_plain</span><span style="color: #007700">(</span><span style="color: #0000BB">$name</span><span style="color: #007700">);<br /><br />&nbsp;&nbsp;&nbsp; foreach (</span><span style="color: #0000BB">$vocabularies </span><span style="color: #007700">as </span><span style="color: #0000BB">$vid </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$vocabulary</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$row</span><span style="color: #007700">[</span><span style="color: #0000BB">$vid</span><span style="color: #007700">] = </span><span style="color: #0000BB">drupal_render</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">][</span><span style="color: #0000BB">$index</span><span style="color: #007700">][</span><span style="color: #DD0000">'taxonomy'</span><span style="color: #007700">][</span><span style="color: #0000BB">$vid</span><span style="color: #007700">]);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$row</span><span style="color: #007700">[</span><span style="color: #DD0000">'description'</span><span style="color: #007700">] = </span><span style="color: #0000BB">drupal_render</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">][</span><span style="color: #0000BB">$index</span><span style="color: #007700">][</span><span style="color: #DD0000">'description'</span><span style="color: #007700">]);<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$rows</span><span style="color: #007700">[] = </span><span style="color: #0000BB">$row</span><span style="color: #007700">;<br />&nbsp; }<br />&nbsp; <br />&nbsp; <br />&nbsp; if (empty(</span><span style="color: #0000BB">$rows</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp; return </span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'You do not have any files left to perform operations on.'</span><span style="color: #007700">);<br />&nbsp; }<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$output </span><span style="color: #007700">.= </span><span style="color: #0000BB">theme</span><span style="color: #007700">(</span><span style="color: #DD0000">'table'</span><span style="color: #007700">, </span><span style="color: #0000BB">$header</span><span style="color: #007700">, </span><span style="color: #0000BB">$rows</span><span style="color: #007700">, array(</span><span style="color: #DD0000">'class' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'zip-upload-table'</span><span style="color: #007700">));<br />&nbsp; </span><span style="color: #0000BB">$output </span><span style="color: #007700">.= </span><span style="color: #0000BB">drupal_render</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">);<br /><br />&nbsp; return </span><span style="color: #0000BB">$output</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
<p>The next step was to build the submit function for the form.  A validation function was unnecessary in this case since I had already validated the file extensions, and my site fixes the input filter for the description at the default filter.  Also, the user doesn't actually have access to the file locations, so I wasn't worried about the files being switched somehow at this stage.  My submit function basically splits into two cases, one where the user wants to save the file permanently, and one where the user wants to delete the file.  In both cases, the final step is to check if the file is in an now empty sub-directory and delete that directory.  One issue I am still having is creating a function to check if the entire temporary directory is empty, and removing it if it is.</p>
<p>I decided to use node_submit and node_save here to save the node information rather than drupal_execute because I have found the first two functions easier to work with, and they serve my purpose well.  Finally, I was unsure if the validation on the file field would work properly given that the uploaded file being attached to the node wasn't in the official temporary directory.  In any case all of the right hooks seem to fire using this code,  and my users get points (using the User Points module) as expected.</p>
<p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/**<br /> * Submit function<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">zip_upload_recent_submit</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &amp;</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">) {<br />&nbsp; </span><span style="color: #FF8000">// aliased for readability<br />&nbsp; </span><span style="color: #0000BB">$form_values </span><span style="color: #007700">= </span><span style="color: #0000BB">$form_state</span><span style="color: #007700">[</span><span style="color: #DD0000">'values'</span><span style="color: #007700">];<br />&nbsp; <br />&nbsp; if (</span><span style="color: #0000BB">$form_values</span><span style="color: #007700">[</span><span style="color: #DD0000">'op'</span><span style="color: #007700">] == </span><span style="color: #DD0000">'Save'</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp; global </span><span style="color: #0000BB">$user</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// figure out what the proper destination is for this file<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if (</span><span style="color: #0000BB">module_exists</span><span style="color: #007700">(</span><span style="color: #DD0000">'token'</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$types </span><span style="color: #007700">= array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'user' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$user</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (</span><span style="color: #0000BB">module_exists</span><span style="color: #007700">(</span><span style="color: #DD0000">'date'</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$types</span><span style="color: #007700">[</span><span style="color: #DD0000">'field'</span><span style="color: #007700">] = array(</span><span style="color: #0000BB">0 </span><span style="color: #007700">=&gt; array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'value2' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'now'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// do I need to hard-code this?&nbsp; I suspect not...<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$dest </span><span style="color: #007700">= </span><span style="color: #0000BB">token_replace_multiple</span><span style="color: #007700">(</span><span style="color: #DD0000">'[yyyy]/[mm]/[dd]/[uid]'</span><span style="color: #007700">, </span><span style="color: #0000BB">$types</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; foreach (</span><span style="color: #0000BB">$form_values</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">] as </span><span style="color: #0000BB">$index </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$file</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$filename </span><span style="color: #007700">= </span><span style="color: #0000BB">$file</span><span style="color: #007700">[</span><span style="color: #DD0000">'filename'</span><span style="color: #007700">];<br /> <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (</span><span style="color: #0000BB">$file</span><span style="color: #007700">[</span><span style="color: #DD0000">'checked'</span><span style="color: #007700">]) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// get the name of the file<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$name </span><span style="color: #007700">= </span><span style="color: #0000BB">array_pop</span><span style="color: #007700">(</span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">"/"</span><span style="color: #007700">, </span><span style="color: #0000BB">$filename</span><span style="color: #007700">));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// create a title for the node<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$title </span><span style="color: #007700">= </span><span style="color: #0000BB">str_replace</span><span style="color: #007700">(</span><span style="color: #DD0000">"-"</span><span style="color: #007700">, </span><span style="color: #DD0000">" "</span><span style="color: #007700">, </span><span style="color: #0000BB">check_plain</span><span style="color: #007700">(</span><span style="color: #0000BB">array_shift</span><span style="color: #007700">(</span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">"."</span><span style="color: #007700">, </span><span style="color: #0000BB">$name</span><span style="color: #007700">))));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$destination </span><span style="color: #007700">= </span><span style="color: #0000BB">file_directory_path</span><span style="color: #007700">() .</span><span style="color: #DD0000">'/'</span><span style="color: #007700">. </span><span style="color: #0000BB">$dest </span><span style="color: #007700">.</span><span style="color: #DD0000">'/'</span><span style="color: #007700">. </span><span style="color: #0000BB">$name</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$path </span><span style="color: #007700">= </span><span style="color: #0000BB">file_directory_path</span><span style="color: #007700">() .</span><span style="color: #DD0000">'/'</span><span style="color: #007700">. </span><span style="color: #0000BB">$dest</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// get some information about the file<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$filemime </span><span style="color: #007700">= </span><span style="color: #0000BB">file_get_mimetype</span><span style="color: #007700">(</span><span style="color: #0000BB">$filename</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$filesize </span><span style="color: #007700">= </span><span style="color: #0000BB">filesize</span><span style="color: #007700">(</span><span style="color: #0000BB">$filename</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// create the destination directory, if it does not already exist<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if (</span><span style="color: #0000BB">file_check_directory</span><span style="color: #007700">(</span><span style="color: #0000BB">$path</span><span style="color: #007700">) || </span><span style="color: #0000BB">mkdir</span><span style="color: #007700">(</span><span style="color: #0000BB">$path</span><span style="color: #007700">, </span><span style="color: #0000BB">0775</span><span style="color: #007700">, </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// TO DO:&nbsp; Fix this so that empty directories are removed.&nbsp; Maybe a call to a single function <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; at the end instead?<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // check to see if the directory is now empty<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$source </span><span style="color: #007700">= </span><span style="color: #0000BB">rtrim</span><span style="color: #007700">(</span><span style="color: #0000BB">str_replace</span><span style="color: #007700">(</span><span style="color: #0000BB">$name</span><span style="color: #007700">, </span><span style="color: #DD0000">""</span><span style="color: #007700">, </span><span style="color: #0000BB">$filename</span><span style="color: #007700">), </span><span style="color: #DD0000">"/"</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// move the file<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if(</span><span style="color: #0000BB">file_move</span><span style="color: #007700">(</span><span style="color: #0000BB">$filename</span><span style="color: #007700">, </span><span style="color: #0000BB">$destination</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$files </span><span style="color: #007700">= </span><span style="color: #0000BB">file_scan_directory</span><span style="color: #007700">(</span><span style="color: #0000BB">$source</span><span style="color: #007700">, </span><span style="color: #DD0000">".*"</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// if it is empty, remove the directory<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if (empty(</span><span style="color: #0000BB">$files</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">rmdir</span><span style="color: #007700">(</span><span style="color: #0000BB">$source</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// the query will be the same for each file, just the placeholders will be different<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$query </span><span style="color: #007700">= </span><span style="color: #DD0000">"INSERT INTO {files} (uid, filename, filepath, filemime, filesize, status, timestamp) VALUES (%d, '%s', '%s', '%s', '%s', 1, %d)"</span><span style="color: #007700">;<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// insert an entry into the files table<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">db_query</span><span style="color: #007700">(</span><span style="color: #0000BB">$query</span><span style="color: #007700">, </span><span style="color: #0000BB">$user</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">uid</span><span style="color: #007700">, </span><span style="color: #0000BB">$name</span><span style="color: #007700">, </span><span style="color: #0000BB">$destination</span><span style="color: #007700">, </span><span style="color: #0000BB">$filemime</span><span style="color: #007700">, </span><span style="color: #0000BB">$filesize</span><span style="color: #007700">, </span><span style="color: #0000BB">time</span><span style="color: #007700">());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// get the fid, since we know the full filepath will be unique<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$fid </span><span style="color: #007700">= </span><span style="color: #0000BB">db_result</span><span style="color: #007700">(</span><span style="color: #0000BB">db_query</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT fid FROM {files} WHERE filepath = '%s'"</span><span style="color: #007700">, </span><span style="color: #0000BB">$destination</span><span style="color: #007700">));<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// build the node here<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$node </span><span style="color: #007700">= array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'title' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$title</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'type' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">variable_get</span><span style="color: #007700">(</span><span style="color: #DD0000">'zip_upload_node_type'</span><span style="color: #007700">, </span><span style="color: #DD0000">'page'</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'uid' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$user</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">uid</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'body' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$file</span><span style="color: #007700">[</span><span style="color: #DD0000">'description'</span><span style="color: #007700">],<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'promote' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">0</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'status' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">1</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'pathauto_perform_alias' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">1</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'field_upload' </span><span style="color: #007700">=&gt; array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">0 </span><span style="color: #007700">=&gt; array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'fid' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$fid</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'list' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">1</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'data' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">0</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'filename' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$name</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'filepath' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$destination</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'filemime' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$filemime</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'source' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'field_upload_0'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'destination' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$destination</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'taxonomy' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$file</span><span style="color: #007700">[</span><span style="color: #DD0000">'taxonomy'</span><span style="color: #007700">],<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'filesize' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$filesize</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'uid' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$user</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">uid</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'status' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">1</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ),<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (</span><span style="color: #0000BB">$node </span><span style="color: #007700">= </span><span style="color: #0000BB">node_submit</span><span style="color: #007700">(</span><span style="color: #0000BB">$node</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$node</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">uid </span><span style="color: #007700">= </span><span style="color: #0000BB">$user</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">uid</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$node</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">taxonomy </span><span style="color: #007700">= </span><span style="color: #0000BB">$file</span><span style="color: #007700">[</span><span style="color: #DD0000">'taxonomy'</span><span style="color: #007700">];<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">node_save</span><span style="color: #007700">(</span><span style="color: #0000BB">$node</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">drupal_set_message</span><span style="color: #007700">(</span><span style="color: #0000BB">check_plain</span><span style="color: #007700">(</span><span style="color: #0000BB">$title</span><span style="color: #007700">) . </span><span style="color: #DD0000">" has been saved."</span><span style="color: #007700">, </span><span style="color: #DD0000">"success"</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">drupal_set_message</span><span style="color: #007700">(</span><span style="color: #DD0000">"There was an error saving " </span><span style="color: #007700">. </span><span style="color: #0000BB">check_plain</span><span style="color: #007700">(</span><span style="color: #0000BB">$title</span><span style="color: #007700">) . </span><span style="color: #DD0000">"."</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">drupal_set_message</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'There was a problem moving your file.&nbsp; Talk to your system administrators about fixing this problem.'</span><span style="color: #007700">), </span><span style="color: #DD0000">'error'</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">drupal_set_message</span><span style="color: #007700">(</span><span style="color: #0000BB">t</span><span style="color: #007700">(</span><span style="color: #DD0000">'There was a problem moving your file.&nbsp; Talk to your system administrators about fixing this problem.'</span><span style="color: #007700">), </span><span style="color: #DD0000">'error'</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }<br />&nbsp; elseif (</span><span style="color: #0000BB">$form_values</span><span style="color: #007700">[</span><span style="color: #DD0000">'op'</span><span style="color: #007700">] == </span><span style="color: #DD0000">'Delete'</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp; foreach (</span><span style="color: #0000BB">$form_values</span><span style="color: #007700">[</span><span style="color: #DD0000">'files'</span><span style="color: #007700">] as </span><span style="color: #0000BB">$filename </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$file</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (</span><span style="color: #0000BB">$file</span><span style="color: #007700">[</span><span style="color: #DD0000">'checked'</span><span style="color: #007700">]) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// delete the file in question<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">file_delete</span><span style="color: #007700">(</span><span style="color: #0000BB">$filename</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// check to see if the directory is now empty<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$fileArray </span><span style="color: #007700">= </span><span style="color: #0000BB">explode</span><span style="color: #007700">(</span><span style="color: #DD0000">"/"</span><span style="color: #007700">, </span><span style="color: #0000BB">$filename</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">array_pop</span><span style="color: #007700">(</span><span style="color: #0000BB">$fileArray</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$directory </span><span style="color: #007700">= </span><span style="color: #0000BB">implode</span><span style="color: #007700">(</span><span style="color: #0000BB">$fileArray</span><span style="color: #007700">, </span><span style="color: #DD0000">"/"</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$files </span><span style="color: #007700">= </span><span style="color: #0000BB">file_scan_directory</span><span style="color: #007700">(</span><span style="color: #0000BB">$directory</span><span style="color: #007700">, </span><span style="color: #DD0000">".*"</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #FF8000">// if it is empty, remove the directory<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #007700">if (empty(</span><span style="color: #0000BB">$files</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">rmdir</span><span style="color: #007700">(</span><span style="color: #0000BB">$directory</span><span style="color: #007700">);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp; }<br />&nbsp; return;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
<p>There is one additional helper function I use to return the vocabularies for a given node type, which I suspect already exists in the Taxonomy module, but I didn't find it.  This helper function was just a bit easier to use and customized for my module.</p>
<p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/**<br /> * Internal function to return the categories for our fixed node type.<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">_zip_upload_get_vocabularies</span><span style="color: #007700">() {<br />&nbsp; </span><span style="color: #0000BB">$type </span><span style="color: #007700">= </span><span style="color: #0000BB">variable_get</span><span style="color: #007700">(</span><span style="color: #DD0000">'zip_upload_node_type'</span><span style="color: #007700">, </span><span style="color: #DD0000">'page'</span><span style="color: #007700">);<br />&nbsp; <br />&nbsp; </span><span style="color: #0000BB">$types </span><span style="color: #007700">= </span><span style="color: #0000BB">node_get_types</span><span style="color: #007700">();<br />&nbsp; <br />&nbsp; foreach (</span><span style="color: #0000BB">$types </span><span style="color: #007700">as </span><span style="color: #0000BB">$name </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$value</span><span style="color: #007700">) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$options</span><span style="color: #007700">[</span><span style="color: #0000BB">$name</span><span style="color: #007700">] = </span><span style="color: #0000BB">$name</span><span style="color: #007700">;<br />&nbsp; }<br />&nbsp; <br />&nbsp; </span><span style="color: #FF8000">// get the vocabularies associated with this node type<br />&nbsp; </span><span style="color: #0000BB">$c </span><span style="color: #007700">= </span><span style="color: #0000BB">db_query</span><span style="color: #007700">(</span><span style="color: #0000BB">db_rewrite_sql</span><span style="color: #007700">(</span><span style="color: #DD0000">"SELECT v.* FROM {vocabulary} v INNER JOIN {vocabulary_node_types} n ON v.vid = n.vid WHERE n.type = '%s' ORDER BY v.weight, v.name"</span><span style="color: #007700">, </span><span style="color: #DD0000">'v'</span><span style="color: #007700">, </span><span style="color: #DD0000">'vid'</span><span style="color: #007700">), </span><span style="color: #0000BB">$options</span><span style="color: #007700">[</span><span style="color: #0000BB">$type</span><span style="color: #007700">]);<br /><br />&nbsp; while (</span><span style="color: #0000BB">$vocabulary </span><span style="color: #007700">= </span><span style="color: #0000BB">db_fetch_object</span><span style="color: #007700">(</span><span style="color: #0000BB">$c</span><span style="color: #007700">)) {<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$vocabularies</span><span style="color: #007700">[</span><span style="color: #0000BB">$vocabulary</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">vid</span><span style="color: #007700">] = </span><span style="color: #0000BB">$vocabulary</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">name</span><span style="color: #007700">;<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'vocabularies'</span><span style="color: #007700">][</span><span style="color: #0000BB">$vocabulary</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">vid</span><span style="color: #007700">] = array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'value'</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">check_plain</span><span style="color: #007700">(</span><span style="color: #0000BB">$vocabulary</span><span style="color: #007700">-&gt;</span><span style="color: #0000BB">name</span><span style="color: #007700">),<br />&nbsp;&nbsp;&nbsp; );<br />&nbsp; }<br />&nbsp; return </span><span style="color: #0000BB">$vocabularies</span><span style="color: #007700">;<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
<p>I was having problems with the theme function being recognized, until I realized that contrary to the instructions in the FAPI documentation on <a href="http://api.drupal.org" title="http://api.drupal.org">http://api.drupal.org</a> , theme functions are not automatically discovered for functions which define Drupal forms, so I had to implement hook_theme as well.</p>
<p><div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /></span><span style="color: #FF8000">/**<br /> * Implementation of hook_theme().<br /> */<br /></span><span style="color: #007700">function </span><span style="color: #0000BB">zip_upload_theme</span><span style="color: #007700">(</span><span style="color: #0000BB">$existing</span><span style="color: #007700">, </span><span style="color: #0000BB">$type</span><span style="color: #007700">, </span><span style="color: #0000BB">$theme</span><span style="color: #007700">, </span><span style="color: #0000BB">$path</span><span style="color: #007700">) {<br />&nbsp; return array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'zip_upload_recent' </span><span style="color: #007700">=&gt; array(<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'arguments' </span><span style="color: #007700">=&gt; array(),<br />&nbsp;&nbsp;&nbsp; ),<br />&nbsp; );<br />}<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
<p>Once I had my form working, I decided I needed to add some JavaScript to make it a bit easier to use.  Categorizing 100 files, all of which are in the same categories could be a bit tedious, so I added some JS to make it easier to apply the same categorization of Topic, Type and Age level (the three vocabularies used to categorize resources on <a href="http://pedagogle.com">Pedagogle</a>.  The JS was relatively easy to build but I had to refer to the documentation over at <a href="http://jquery.com">JQuery</a> a few times in order to get it right.</p>
<p>I also found myself going back to the PHP code a few times in order to add class and id attributes where appropriate to make it easier to select elements to manipulate using jQuery.  </p>
<p><div class="codeblock"><code>// $Id$<br /><br />// check to make sure JS is enabled and functional<br />if (Drupal.jsEnabled) {<br /><br />&nbsp; // add our stuff inside the header of the table<br />&nbsp; $(document).ready(function() {<br />&nbsp; <br />&nbsp;&nbsp;&nbsp; // first add a checkbox to the top of the first column<br />&nbsp;&nbsp;&nbsp; $(&quot;table.zip-upload-table th:first&quot;).each(function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // set a width on the first column<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(this).css({&#039;width&#039; : &#039;100px&#039;});<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // add a checkbox<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(this).prepend(&#039;&lt;input type=&quot;checkbox&quot; id=&quot;check-all&quot; /&gt; &#039;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // make the checkbox select and deselect all other checkboxes in this table<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&quot;#check-all&quot;).click(function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&quot;input.checked&quot;).attr(&quot;checked&quot;, $(this).attr(&quot;checked&quot;));<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />&nbsp;&nbsp;&nbsp; });<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // now add a taxonomy select to the top of the 3rd, 4th, and 5th columns<br />&nbsp;&nbsp;&nbsp; for (var i = 2; i &lt;= 4; i++) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&quot;table.zip-upload-table th:eq(&quot; + i + &quot;)&quot;).each(function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var text = $(this).text();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var tableHeader = this;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(this).html($(&quot;table.zip-upload-table td:eq(&quot; + i + &quot;)&quot;).html());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(this).find(&quot;option:eq(0)&quot;).css({&quot;font-weight&quot; : &quot;bold&quot;}).text(&quot;Choose &quot; + text);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(this).find(&quot;select:eq(0)&quot;).change(function() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var index = 1*$(this).attr(&quot;class&quot;).replace(&quot;form-select taxonomy-&quot;, &quot;&quot;) + 1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&quot;table.zip-upload-table tr&quot;).find(&quot;td:eq(&quot; + index + &quot;) select:eq(0)&quot;).val($(this).val());<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // now make the textfield&#039;s easier to use by adding up, down arrow and tab navigation<br />&nbsp;&nbsp;&nbsp; $(&quot;table.zip-upload-table input.form-text&quot;).focus(function () {<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // add a keydown event on focus<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(this).keydown(function (event) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // get the current index<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var thisIndex = 1*$(this).attr(&quot;class&quot;).replace(&quot;form-text textfield-&quot;, &quot;&quot;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // get the total number of textfields<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var size = $(&quot;table.zip-upload-table input.form-text&quot;).size();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // get the event which is browser dependent<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var e = event.which || event.keyCode;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // which key has been hit<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; switch (e) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // enter key<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case 13:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // tab key<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case 9:<br />&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // down arrow<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case 40:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // increase the index by 1, then use modulo arithmetic to prevent potential over-run<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thisIndex = (thisIndex + 1) % size;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // focus on the new textfield<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&quot;table.zip-upload-table input.form-text&quot;).get(thisIndex).focus();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // up arrow<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; case 38:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // decrease the index by 1, then make sure that negative values jump up to the other end<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thisIndex = ((thisIndex - 1) &lt; 0) ? thisIndex + size - 1: thisIndex - 1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // focus on the new textfield<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(&quot;table.zip-upload-table input.form-text&quot;).get(thisIndex).focus();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // nothing to do unless the event is receiving an up or down arrow<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // remove the keydown until the next time this textfield has focus<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(this).unbind(&#039;keydown&#039;);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // prevent bubbling<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br />&nbsp;&nbsp;&nbsp; });<br />&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // just in case the user clicks between boxes, remove the keydown from before<br />&nbsp;&nbsp;&nbsp; $(&quot;table.zip-upload-table input.form-text&quot;).blur(function () {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $(this).unbind(&#039;keydown&#039;);<br />&nbsp;&nbsp;&nbsp; });<br />&nbsp; });<br />}</code></div></p>
<p>The total development time for this module was about a day, but it still has a couple of small things I need to fix.  For example, I'd like to be able to clean up the empty temporary directories at some stage, either through hook_cron, or as soon as it is emptied.  On a Mac, when you ZIP up a directory using most of the tools available (including the default archiving tool provided), it adds other directories not present in a Windows archive, like __MASOSX and _trashes, etc... So my code needs to ignore files in those directories, which I'll probably do using regular expressions, quietly during the validation stage, since these are hidden directories in the Mac OS X, and most users are blissfully unaware of their existence.</p>
<p>Anyway, if anyone can help me with the recursive checker for empty sub-directories, which also then deletes those sub-directories, that would be great.  If there are any other OS specific problems I should be aware of in Zip archives, that would also be handy.  For instance, I'm not sure what happens if a user sets permissions on the files in their archive in Linux, and then uploads the Zip.  Do the permissions stick using my current code?  I'll probably have to check before I let everyone have access to this form.  For now, I've set up a "trusted user" role, and assigned the "upload zip archives" permission to this role, and allow individual users to access this form as I see fit.</p>
<p>Finally, the file doesn't include any embedded help text right now because I'm still working on it, and because I've got a module I created a while ago which allows me to edit help text for any path on the fly.  Once I've got some good solid help text written, I'll include it in this module as well.</p>
<p>Hope you find this tutorial useful, and let me know where I can make some improvements in my code, as I am most definitely not a professional PHP programmer.</p>
    ]]></content>
  </entry>
  <entry>
    <title>SWF Playlist module</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/315" />
    <id>http://davidwees.com/myblog/node/315</id>
    <published>2008-11-01T01:23:08-04:00</published>
    <updated>2008-11-01T01:32:40-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="code" />
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>This module let's you take a Podcast view you have created (or probably ANY audio podcast) and allow the JW Media Player 4 module (from the <a href="http://drupal.org/project/swftools">SWF tools</a> module ) to play the audio feed as a playlist, automatically embedding the player as a block.  Note that this is built for Drupal 6.</p>
<p>It does not have a lot of configuration options yet, you pretty much need to open up swf_playlist.tpl.php and modify it to suit the location of your feed.</p>
<p>swf_playlist.tpl.php<br />
<?php<br />
  // get the location of the JW Media Player</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>This module let's you take a Podcast view you have created (or probably ANY audio podcast) and allow the JW Media Player 4 module (from the <a href="http://drupal.org/project/swftools">SWF tools</a> module ) to play the audio feed as a playlist, automatically embedding the player as a block.  Note that this is built for Drupal 6.</p>
<p>It does not have a lot of configuration options yet, you pretty much need to open up swf_playlist.tpl.php and modify it to suit the location of your feed.</p>
<p>swf_playlist.tpl.php<br />
<div class="codeblock"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br />&nbsp; </span><span style="color: #FF8000">// get the location of the JW Media Player<br /><br />&nbsp; // this doesn't work, I'm not sure why<br />&nbsp; // $player = url(drupal_get_path('module', 'swftools'). '/shared/flash_media_player/player.swf', array('absolute', TRUE));<br /><br />&nbsp; // you have to set this manually for now.&nbsp; yech.<br />&nbsp; </span><span style="color: #0000BB">$player </span><span style="color: #007700">= </span><span style="color: #DD0000">"http://localhost/skywyatt/sites/all/modules/swftools/shared/flash_media_player/player.swf"</span><span style="color: #007700">;<br /><br />&nbsp; </span><span style="color: #FF8000">// get the default settings<br />&nbsp; </span><span style="color: #0000BB">$flashvars </span><span style="color: #007700">= </span><span style="color: #0000BB">_wijering4_flashvars</span><span style="color: #007700">(</span><span style="color: #0000BB">$settings</span><span style="color: #007700">[</span><span style="color: #DD0000">'name'</span><span style="color: #007700">]);<br /><br />&nbsp; </span><span style="color: #FF8000">// set the flashvars<br />&nbsp; </span><span style="color: #0000BB">$flashvars</span><span style="color: #007700">[</span><span style="color: #DD0000">'file'</span><span style="color: #007700">] = </span><span style="color: #0000BB">url</span><span style="color: #007700">(</span><span style="color: #0000BB">$settings</span><span style="color: #007700">[</span><span style="color: #DD0000">'path'</span><span style="color: #007700">], array(</span><span style="color: #DD0000">'absolute'</span><span style="color: #007700">, </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">));<br />&nbsp; </span><span style="color: #0000BB">$flashvars</span><span style="color: #007700">[</span><span style="color: #DD0000">'autostart'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$settings</span><span style="color: #007700">[</span><span style="color: #DD0000">'autostart'</span><span style="color: #007700">] ? </span><span style="color: #DD0000">'true' </span><span style="color: #007700">: </span><span style="color: #DD0000">'false'</span><span style="color: #007700">;<br />&nbsp; </span><span style="color: #0000BB">$flashvars</span><span style="color: #007700">[</span><span style="color: #DD0000">'repeat'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$settings</span><span style="color: #007700">[</span><span style="color: #DD0000">'repeats'</span><span style="color: #007700">] ? </span><span style="color: #DD0000">'true' </span><span style="color: #007700">: </span><span style="color: #DD0000">'false'</span><span style="color: #007700">;<br />&nbsp; </span><span style="color: #0000BB">$flashvars</span><span style="color: #007700">[</span><span style="color: #DD0000">'playlist'</span><span style="color: #007700">] = </span><span style="color: #DD0000">'over'</span><span style="color: #007700">;<br />&nbsp; </span><span style="color: #0000BB">$flashvars</span><span style="color: #007700">[</span><span style="color: #DD0000">'playlistsize'</span><span style="color: #007700">] = </span><span style="color: #DD0000">'40'</span><span style="color: #007700">;<br /><br />&nbsp; </span><span style="color: #FF8000">// add the fancy random image stuff while the audio file is playing<br />&nbsp; </span><span style="color: #0000BB">$flashvars</span><span style="color: #007700">[</span><span style="color: #DD0000">'plugins'</span><span style="color: #007700">] = </span><span style="color: #DD0000">'revolt-1'</span><span style="color: #007700">;<br /><br />&nbsp; </span><span style="color: #FF8000">// unset the name of the playlist<br />&nbsp; // we don't want to confuse the JW Media player<br />&nbsp; </span><span style="color: #0000BB">$name </span><span style="color: #007700">= </span><span style="color: #0000BB">$settings</span><span style="color: #007700">[</span><span style="color: #DD0000">'name'</span><span style="color: #007700">];<br />&nbsp; unset(</span><span style="color: #0000BB">$settings</span><span style="color: #007700">[</span><span style="color: #DD0000">'name'</span><span style="color: #007700">]);<br />&nbsp; unset(</span><span style="color: #0000BB">$settings</span><span style="color: #007700">[</span><span style="color: #DD0000">'path'</span><span style="color: #007700">]);<br /><br />&nbsp; </span><span style="color: #FF8000">// change some default options<br />&nbsp; </span><span style="color: #0000BB">$settings</span><span style="color: #007700">[</span><span style="color: #DD0000">'base'</span><span style="color: #007700">] = </span><span style="color: #0000BB">base_path</span><span style="color: #007700">();<br /><br />&nbsp; </span><span style="color: #0000BB">$options </span><span style="color: #007700">= array(<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'params' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$settings</span><span style="color: #007700">,<br />&nbsp;&nbsp;&nbsp; </span><span style="color: #DD0000">'flashvars' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">$flashvars</span><span style="color: #007700">,<br />&nbsp; );<br /><br />&nbsp; </span><span style="color: #FF8000">// requires SWF tools module and JW Media Player 4 modules<br />&nbsp; </span><span style="color: #007700">print </span><span style="color: #0000BB">swf</span><span style="color: #007700">(</span><span style="color: #0000BB">$player</span><span style="color: #007700">, </span><span style="color: #0000BB">$options</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">?&gt;</span></span></code></div></p>
    ]]></content>
  </entry>
  <entry>
    <title>Add Help module</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/314" />
    <id>http://davidwees.com/myblog/node/314</id>
    <published>2008-11-01T00:42:18-04:00</published>
    <updated>2008-11-01T00:58:48-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="code" />
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>This is a module to help non-coders add help text to the top of any path, much like the core Help module does.  One problem with the core Help module is that it relies on using hook_help to add the help text to the top of the page.  This means that if you want to add help to a page which does not already have it, you need to create a custom module.  This means that only the developers of the site can easily add help text, although once the text is added, a user with sufficient privileges could 'translate' the help text string and reword it however they like.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>This is a module to help non-coders add help text to the top of any path, much like the core Help module does.  One problem with the core Help module is that it relies on using hook_help to add the help text to the top of the page.  This means that if you want to add help to a page which does not already have it, you need to create a custom module.  This means that only the developers of the site can easily add help text, although once the text is added, a user with sufficient privileges could 'translate' the help text string and reword it however they like.</p>
<p>This is a barrier of use for non-coders.  My wife wanted to be able to add help text to pages, so I created  a module which I call 'Add Help' which allows her to search for a path, and then add help text to that path.</p>
<p>Here is a screen-shot of the "Search for Path" form:</p>
<p><img src="/myblog/files/u1/add-help-search.png" width="500" height="166" alt="add-help-search.png" /></p>
<p>Here is a screen-shot of the "Add Help to Path" form:</p>
<p><img src="/myblog/files/u1/add-help-edit-help.png" width="500" height="287" alt="add-help-edit-help.png" /></p>
<p>There are some minor problems with the implementation, for one there is no way currently to keep track of which pages to which you have added help text so you have to keep track of this yourself.  A second problem is that I haven't figured out how to execute php code properly in case the user has chosen the php filter.</p>
<p>Anyway I'm happy to release my code under the GPL, hopefully I'll find time to upload it to Drupal.org as well.</p>
    ]]></content>
  </entry>
  <entry>
    <title>Creating a better administrative interface for Drupal</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/313" />
    <id>http://davidwees.com/myblog/node/313</id>
    <published>2008-09-26T20:35:31-04:00</published>
    <updated>2008-09-26T20:41:15-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>One problem with Drupal is that the UI sucks for beginner users.  They have great difficulty finding all of the most important administrative menu items they want to use.  Since Drupal tends to group administrative items by general functionality and by module, it can tricky working where something is located for a beginner.  Not to mention the fact, many beginning CMS users are terrified to death of 'clicking the auto-destruct link' by mistake and screwing up their work.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>One problem with Drupal is that the UI sucks for beginner users.  They have great difficulty finding all of the most important administrative menu items they want to use.  Since Drupal tends to group administrative items by general functionality and by module, it can tricky working where something is located for a beginner.  Not to mention the fact, many beginning CMS users are terrified to death of 'clicking the auto-destruct link' by mistake and screwing up their work.  This fear is not ungrounded, as many Desktop applications have a strange red X in the top right corner of the page that is terribly destructive when accidentally clicked (assuming one is not used to reading dialogs carefully before clicking Yes).</p>
<p>So the solution in Drupal is simple.  Create a page where all of the most useful administrative items are linked, and make sure that each type of functionality is in blocks which are displayed only to the user who has access to do that item.</p>
<p><img src="/myblog/files/u1/admin_menu.jpg" width="728" height="398" alt="admin_menu.jpg" /></p>
    ]]></content>
  </entry>
  <entry>
    <title>Updated BookGUI</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/312" />
    <id>http://davidwees.com/myblog/node/312</id>
    <published>2008-09-18T19:30:19-04:00</published>
    <updated>2008-09-18T19:33:38-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="code" />
    <category term="drupal" />
    <category term="javascript" />
    <summary type="html"><![CDATA[<p>This is my BookGUI module, including the most recent updates (which are actually almost a year old).  I don't consider it stable for a except for a development environment, but maybe someone can iron out a couple of the kinks I gave up on.<br />
This allows you to edit the structure of a book (AKA the Book module) as well as add/delete pages to the book on the fly, all via an Ajax management page.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>This is my BookGUI module, including the most recent updates (which are actually almost a year old).  I don't consider it stable for a except for a development environment, but maybe someone can iron out a couple of the kinks I gave up on.</p>
<p>This allows you to edit the structure of a book (AKA the Book module) as well as add/delete pages to the book on the fly, all via an Ajax management page.</p>
<p>It's huge flaw is that it doesn't check for the number of pages that it's going to load.  I really need to build some kind of paging system or something similar into this structure.  Maybe only load the structure of the book, and none of the content?  Content could be loaded on the fly via Ajax...</p>
<p>Anyway, check it out if you like, it's released under the GPL.</p>
    ]]></content>
  </entry>
  <entry>
    <title>Search Statistics for Drupal</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/311" />
    <id>http://davidwees.com/myblog/node/311</id>
    <published>2008-09-18T19:27:22-04:00</published>
    <updated>2008-09-18T19:30:03-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="code" />
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>Someone mentioned in one of the Drupal groups that they had created a very simple script to check how popular search terms, unique for IP address.  In other words, how many different unique visitors have searched for a particular word.  I just took their script and converted it to a simple module.  Can't take much credit for this one.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>Someone mentioned in one of the Drupal groups that they had created a very simple script to check how popular search terms, unique for IP address.  In other words, how many different unique visitors have searched for a particular word.  I just took their script and converted it to a simple module.  Can't take much credit for this one.</p>
    ]]></content>
  </entry>
  <entry>
    <title>Rotating cube in Flash</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/310" />
    <id>http://davidwees.com/myblog/node/310</id>
    <published>2008-09-16T21:12:53-04:00</published>
    <updated>2008-09-16T21:38:47-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <summary type="html"><![CDATA[<p>Check this out!</p>    ]]></summary>
    <content type="html"><![CDATA[<p>Check this out!</p>

<p>
<object type="application/x-shockwave-flash" data="http://unitorganizer.com/flash/rotatingcube/45-picturecube.swf?im0=http://unitorganizer.com/flash/rotatingcube/images/face01.gif&amp;im1=http://unitorganizer.com/flash/rotatingcube/images/face02.gif&amp;im2=http://unitorganizer.com/flash/rotatingcube/images/face031.gif&amp;im3=http://unitorganizer.com/flash/rotatingcube/images/face04.gif&amp;im4=http://unitorganizer.com/flash/rotatingcube/images/face05.gif&amp;im5=http://unitorganizer.com/flash/rotatingcube/images/face06.gif&amp;cubesize=300" width="600" height="450"><param name="movie" value="http://unitorganizer.com/flash/rotatingcube/45-picturecube.swf?im0=http://unitorganizer.com/flash/rotatingcube/images/face01.gif&amp;im1=http://unitorganizer.com/flash/rotatingcube/images/face02.gif&amp;im2=http://unitorganizer.com/flash/rotatingcube/images/face031.gif&amp;im3=http://unitorganizer.com/flash/rotatingcube/images/face04.gif&amp;im4=http://unitorganizer.com/flash/rotatingcube/images/face05.gif&amp;im5=http://unitorganizer.com/flash/rotatingcube/images/face06.gif&amp;cubesize=300" width="600" height="450&amp;refresh=1221605720995"/></object>
</p>

<p>This is a script written by <a href="http://www.bezzmedia.com">John Bezanis</a> which takes 6 images, specified by GET parameters (his own words) and turns them into a spinning cube.  You can click on a face of the cube and it will spin around into focus.  Click again and the cube continues spinning.</p>

<p>It's currently displaying 6 images from <a href="http://blogs.ubc.ca/etec522">a course</a> in my online MA where we have to analyze an e-Learning platform using these 6 facets.</p>    ]]></content>
  </entry>
  <entry>
    <title>Basic templates for themes</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/309" />
    <id>http://davidwees.com/myblog/node/309</id>
    <published>2008-09-15T20:59:19-04:00</published>
    <updated>2008-09-15T21:31:28-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="code" />
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>There are 8 different basic usable website themes I can think of.  I've listed them below.  My intention is to find minimal mark-up that will produce these themes, with as wide a range of browsers as possible support.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>There are 8 different basic usable website themes I can think of.  I've listed them below.  My intention is to find minimal mark-up that will produce these themes, with as wide a range of browsers as possible support.</p>
<p><!--break--></p>
<table>
<tbody>
<tr>
<td><img src="http://www.unitorganizer.com/myblog/files/u1/one_col.png" alt="one_col.png" height="90" width="100"></td>
<td><img src="http://www.unitorganizer.com/myblog/files/u1/three_col_fixed.png" alt="three_col_fixed.png" height="90" width="100"></td>
</tr>
<tr>
<td>One column</td>
<td>Three fixed columns</td>
</tr>
<tr>
<td><img src="http://www.unitorganizer.com/myblog/files/u1/three_col_fluid_center.png" alt="three_col_fluid_center.png" height="90" width="100"></td>
<td><img src="http://www.unitorganizer.com/myblog/files/u1/three_col_fluid_left.png" alt="three_col_fluid_left.png" height="90" width="100"></td>
</tr>
<tr>
<td>Three column fluid center</td>
<td>Three columns - fluid left</td>
</tr>
<tr>
<td><img src="http://www.unitorganizer.com/myblog/files/u1/three_col_fluid_right.png" alt="three_col_fluid_right.png" height="90" width="100"></td>
<td><img src="http://www.unitorganizer.com/myblog/files/u1/two_col_fixed.png" alt="two_col_fixed.png" height="90" width="100"></td>
</tr>
<tr>
<td>Three columns fluid right</td>
<td>Two fixed columns</td>
</tr>
<tr>
<td><img src="http://www.unitorganizer.com/myblog/files/u1/two_col_fluid_left.png" alt="two_col_fluid_left.png" height="90" width="100"></td>
<td><img src="http://www.unitorganizer.com/myblog/files/u1/two_col_fluid_right.png" alt="two_col_fluid_right.png" height="90" width="100"></td>
</tr>
<tr>
<td>Two columns - fluid left</td>
<td>Two columns - fluid right</td>
</tr>
</tbody>
</table>
<p>Does anyone have any suggestions of an Open Source solution to these themes?  I'm sure they have all been created before.  Why re-invent the wheel?</p>
    ]]></content>
  </entry>
  <entry>
    <title>Simplifying pane creation using the Panels module</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/308" />
    <id>http://davidwees.com/myblog/node/308</id>
    <published>2008-09-11T22:54:06-04:00</published>
    <updated>2008-09-13T02:38:56-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="code" />
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>So I love the Panels module.  It gives me much, much greater control over where I want to place my content, and how I create my blocks.  It's stupendous.</p>
<p>It has a serious problem though, the actual construction of content is not meant to be done by a beginner.  The form includes many unnecessary elements for a novice user and the process is confusing.  Worse still, you can't use either the IMCE module or any WYSIWYG editor when creating custom panes.  This is a serious issue for many of my users, most of whom will be publishing to the web for the first time.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>So I love the Panels module.  It gives me much, much greater control over where I want to place my content, and how I create my blocks.  It's stupendous.</p>
<p>It has a serious problem though, the actual construction of content is not meant to be done by a beginner.  The form includes many unnecessary elements for a novice user and the process is confusing.  Worse still, you can't use either the IMCE module or any WYSIWYG editor when creating custom panes.  This is a serious issue for many of my users, most of whom will be publishing to the web for the first time.</p>
<p>So I wrote a module which takes the process of custom pane construction, and tries to simplify it as much as possible.  The basic idea is, people navigate to a Panes tab, where they see all of the displays associated with this group (or one display in the case of a panels node).  They can then choose to add panes to the display, and be sent to a separate form, or edit the custom panes that have already been built.  Since this is a separate form, the users have access to a WYSIWYG editor and the nice IMCE pop-up for adding images.</p>
<p>I think this will make managing custom pane content much easier for most of my users.  We'll see, I'm going to let my partner know about it, and see what he thinks.</p>
<p>Screen shots:</p>
<p><img src="/myblog/files/u1/simple_panes.jpg" width="500" height="157" alt="simple_panes.jpg" /><br />
Main pane edit window</p>
<p><img src="/myblog/files/u1/simple_panes_add.jpg" width="500" height="287" alt="simple_panes_add.jpg" /><br />
Add pane window</p>
<p><img src="/myblog/files/u1/simple_panes_edit.jpg" width="500" height="287" alt="simple_panes_edit.jpg" /><br />
Edit pane window</p>
<p>Notice the use of a WYSIWYG editor in both the 'Add pane' and 'Edit pane' forms (and the link to the IMCE image uploader).  This is what really lowers the difficulty of adding nice custom panes to a panels node or an Og panels page.</p>
    ]]></content>
  </entry>
  <entry>
    <title>Modified main theme for our new skin drupal site</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/307" />
    <id>http://davidwees.com/myblog/node/307</id>
    <published>2008-09-06T23:36:08-04:00</published>
    <updated>2008-09-06T23:46:32-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="code" />
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>So our new site already has a new theme, which is just a modified version of the old theme.  It looks pretty good I think.</p>
<p><img src="/myblog/files/u1/skindrupal_1.jpg" width="641" height="276" alt="skindrupal_1.jpg" /></p>
<p>The idea is, instead of fading out the backgrounds of the preview images, we leave them in place, and fade out the images.  We also reduced the number of images previewed to 3, and arranged them in a column.  Finally, my wife had a great idea of moving the menu to beneath the images, so now our menu block is at the bottom of the page.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>So our new site already has a new theme, which is just a modified version of the old theme.  It looks pretty good I think.</p>
<p><img src="/myblog/files/u1/skindrupal_1.jpg" width="641" height="276" alt="skindrupal_1.jpg" /></p>
<p>The idea is, instead of fading out the backgrounds of the preview images, we leave them in place, and fade out the images.  We also reduced the number of images previewed to 3, and arranged them in a column.  Finally, my wife had a great idea of moving the menu to beneath the images, so now our menu block is at the bottom of the page.</p>
<p>This is an extremely easy theme, and it even works in IE 5.5 (with a minor adjustment for the logo, which I haven't done yet).</p>
<p>One column, centered horizontally, with header, main, and footer sections divided vertically.  No sidebars at all, and extremely easy to retheme later.  The JS had to be rewritten, nearly from scratch, but I think it works a bit smoother now, less jerkiness because of the fewer number of elements involved.</p>
<p>Check it out at <a href="http://unitorganizer.com/skindrupal" title="http://unitorganizer.com/skindrupal">http://unitorganizer.com/skindrupal</a></p>
<p>Dave</p>
    ]]></content>
  </entry>
  <entry>
    <title>More detail on theme creation for Drupal 5</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/306" />
    <id>http://davidwees.com/myblog/node/306</id>
    <published>2008-09-05T21:53:50-04:00</published>
    <updated>2008-09-05T22:36:46-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="code" />
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>So I thought I should give more detail on how to create a theme for Drupal 5.</p>
<p>What I basically did was copy page.tpl.php, node.tpl.php, block.tpl.php, box.tpl.php, comment.tpl.php and template.php from the Garland theme.  I then also created a file called page-front.tpl.php.  This gave me a basic framework with which to work.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>So I thought I should give more detail on how to create a theme for Drupal 5.</p>
<p>What I basically did was copy page.tpl.php, node.tpl.php, block.tpl.php, box.tpl.php, comment.tpl.php and template.php from the Garland theme.  I then also created a file called page-front.tpl.php.  This gave me a basic framework with which to work.</p>
<p>For the creation of the theme for the <a href="http://unitorganizer.com/skindrupal">Skin Drupal</a> site, the first thing I did was rip out all of the html between the body tags in the page.tpl.php file.  I then looked at the picture my wife gave me, and mentally divided it into regions.</p>
<p><img src="/myblog/files/u1/SKIN2.png" width="500" height="260" alt="Theme" /></p>
<p>From this picture, you can see it could be easily divided into 4 blocks, which is essentially how I created the html.</p>
<p><img src="/myblog/files/u1/example1.png" width="500" height="260" alt="Theme cut into blocks" /></p>
<p>First I inserted a wrapper div, to make it easy to center the display of the site for people with browsers with resolution greater than 1024x768.  We decided, by doing some research online, that a theme that was 960px wide by about 500px tall was ideal for display in most people's monitors.  We'd be leaving those poor 800x600 people out of the loop, but that's only 10% of the market, and not likely to be the people looking for themes at our site (developers tend to try and use the best tools at their disposal).</p>
<p>Inside the wrapper div, I inserted two sibling divs to divide the site into the header and main regions.  Since the content of the header was so simple, I just inserted 2 divs for the right and left portions of the header and the same for the main portion.</p>
<p>The 5 rotating image blocks, I quickly realized I would have to position absolutely on the page in order to make the fade in and fade out work.  This presented problems later and led us to eventually scrap this theme, but more about that in a future post.  Anyway, to allow for this, I opened up my blank template.php file and created a themename_regions function, and created 5 more regions for my site, calling them easy to remember things like box 1, box 2, box 3, box 4 and box 5.  Before I went any further, I created 5 views to match these blocks and used the block administration page to match the blocks to their position on the front page.  I went ahead and created some filler content, so we'd be able to see these blocks at work.</p>
<p>It was pretty simple to set some default blocks for the right column of the main page, and then I went ahead and worked on the css for the theme, which is remarkably simple since our layout is so clean and easy.</p>
<p>Fix the width of all of the blocks, fix their height, add float left to most of them, and float right to the right column, and we were set.  Then I used absolute positioning on the blocks and wrote the JS script to fade them in and out.  jQuery is amazing, I have to say, this script was a breeze to write.  If you examine the source of the page, or use JSview, you can see the name of the JS file, which you can safely view in your browser.  You'll see that it's pretty short.</p>
<p>I kept the default html for the box, block, comment, and node files, but used a views theming function inside my template.php file to greatly simplify the html presented by the views (since all I really wanted to see was the thumbnails).  Then Tada! I had a really simple plain theme.  The idea of the theme is that it looks essentially unthemed, and that our themes will be ways to spruce up the site.</p>
    ]]></content>
  </entry>
  <entry>
    <title>Creating rotating image blocks in Drupal using jQuery</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/305" />
    <id>http://davidwees.com/myblog/node/305</id>
    <published>2008-09-04T21:17:54-04:00</published>
    <updated>2008-09-05T01:55:40-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="code" />
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>So my wife and I have decided that we are going to try and be partners in a new business.  We'll see how well we work together, we really haven't tried very hard to work on any serious projects (except of course for our son) together before this.</p>
<p>My wife knows a lot about graphic design and what looks good as a result of her training as a landscape arctitect, but knows very little about building websites.  I know a fair bit about building websites, but my design sense is horrible.  So we combine together these two talents, and we should make a pretty good team.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>So my wife and I have decided that we are going to try and be partners in a new business.  We'll see how well we work together, we really haven't tried very hard to work on any serious projects (except of course for our son) together before this.</p>
<p>My wife knows a lot about graphic design and what looks good as a result of her training as a landscape arctitect, but knows very little about building websites.  I know a fair bit about building websites, but my design sense is horrible.  So we combine together these two talents, and we should make a pretty good team.</p>
<p>Instead of trying to build entire websites right now, our plan is to focus on building themes for Drupal 5 and Drupal 6.  We'll create a showcase site for our themes, and charge people either a license to use the theme, or the complete copyright to a theme at a much steeper price.</p>
<p>So yesterday we started our collaboration by building a simple theme for our theme shop.</p>
<p>My wife and I each brainstormed a few ideas, and we settled on one of my ideas, with some touch-ups from my wife.  I went to work and started installing and setting up the modules I thought we would need.</p>
<p>Installed modules:</p>
<ul>
<li>CCK + all default field types</li>
<li>Imagefield</li>
<li>Project + Project Release + Project Issue tracking</li>
<li>Token</li>
<li>Views + Views Bonus</li>
<li>Panels</li>
<li>Path</li>
<li>E-commerce suit</li>
<li>Poormanscron</li>
</ul>
<p>The website portion isn't yet totally complete, but my wife created a layout, which I have turned into a theme.  One of the fun aspects of this theme is the front page rotating image preview, done by customizing the html displayed by the view (essentially minimizing it as much as possible) and some jQuery fun.</p>
<p>Check out the site at:</p>
<p><a href="http://unitorganizer.com/skindrupal/" title="http://unitorganizer.com/skindrupal/">http://unitorganizer.com/skindrupal/</a></p>
    ]]></content>
  </entry>
  <entry>
    <title>OpenWYSIWYG module - yet another textarea editor</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/304" />
    <id>http://davidwees.com/myblog/node/304</id>
    <published>2008-08-29T20:17:04-04:00</published>
    <updated>2008-08-29T21:38:49-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>This is yet another WYSIWYG module.    It has a very simple configuration page, and will currently only show up on node/add or node/edit pages, of the content types one wants.  It has a couple of advantages I see over TinyMCE.<br />
1.  The code for the editor is open-source.<br />
2.  It is a lot, lot smaller in size than many of the other editors out there, which makes it load faster than other editors.<br />
3.  The JS only loads on pages that have the editor present, rather than on all pages.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>This is yet another WYSIWYG module.    It has a very simple configuration page, and will currently only show up on node/add or node/edit pages, of the content types one wants.  It has a couple of advantages I see over TinyMCE.</p>
<p>1.  The code for the editor is open-source.<br />
2.  It is a lot, lot smaller in size than many of the other editors out there, which makes it load faster than other editors.<br />
3.  The JS only loads on pages that have the editor present, rather than on all pages.</p>
<p>The major disadvantage of course is a much smaller developer team working on it, so bugs and feature requests will presumably take a long time to get implemented.</p>
<p>Anyway, I don't want to maintain this module, so I'm releasing it here, feature frozen.</p>
    ]]></content>
  </entry>
  <entry>
    <title>Review module - simple node reviews attached to other nodes</title>
    <link rel="alternate" type="text/html" href="http://davidwees.com/myblog/node/303" />
    <id>http://davidwees.com/myblog/node/303</id>
    <published>2008-08-29T19:58:56-04:00</published>
    <updated>2008-08-29T20:11:10-04:00</updated>
    <author>
      <name>David Wees</name>
    </author>
    <category term="drupal" />
    <summary type="html"><![CDATA[<p>This is a simple module that allows users to post responses/reviews of other people's posts.  It also includes a simple 5 star voting widget, which degrades nicely when JavaScript is disabled.  The posts are limited to the default filter input and are carefully screened for possible xss attacks since it is likely this kind of module could be used for allowing anonymous users to post.</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>This is a simple module that allows users to post responses/reviews of other people's posts.  It also includes a simple 5 star voting widget, which degrades nicely when JavaScript is disabled.  The posts are limited to the default filter input and are carefully screened for possible xss attacks since it is likely this kind of module could be used for allowing anonymous users to post.</p>
<p>Another feature of this module is that you can use one of your site vocabularies to define in what categories people can vote on.  The voting unfortunately does not currently use the Voting Api module.  It really should, because then one could define Views of the votes much more easily, and it would allow for a greater generalization of the voting process.</p>
<p>This module has actually already seen use in a couple of places, one a public library (they are using it to review books) and another site is using it to review cars.</p>
<p>Both of the sites have given me permission to release the code.  Again, I'm too busy to maintain a module like this, but if someone wants to take up the torch...</p>
    ]]></content>
  </entry>
</feed>
