UserLand Software
Powerful, cross-platform web scripting.
 

Manipulating Files and Folders

Frontier Scripting Tutorial

About This Tutorial

What Does Frontier Do?

Keywords, Handlers, Verbs, and Calls

Loops, Variables, Parameters, and Conditionals

The Handler Rule

Returns, Addresses, and Dereferencing

Scope and With

Strings and Files

Outlines and Tables

Running, Debugging, and Getting Help

Datatypes

A Real-Life Problem

String Parsing and Substitution

Manipulating Files and Folders

Final Touches

AmelioWeb, part two

We left off in the previous chapter with our routine, workspace.BuildSite, worked out to the point where we were ready to write each piece of text out to disk as new HTML file.

The routine so far looks like this:


local (folder)
if not file.getFolderDialog ("Where is the AmelioWeb folder?", @folder) {return}
local (templatetext = string (file.readWholeFile (folder + "template.html")))
templatetext = string.replaceAll (templatetext, cr + lf, cr)
local (infolder = folder + "Source Files" + file.getPathChar ())
local (infile, filetext, title, subtitle)
on popline()
   local (s)
   s = string.NthField (filetext, cr, 1)
   filetext = string.delete (filetext, 1, sizeOf(s) + 1)
   return (s)
fileloop (infile in infolder, 1)
   filetext = string (file.readWholeFile (infile))
   filetext = string.replaceAll (filetext, cr + lf, cr)
   title = popline (); popline (); popline (); popline ()
   subtitle = popline (); popline ()
   local (s = templatetext)
   s = string.replaceAll (s, "<<title>>", title)
   s = string.replaceAll (s, "<<subtitle>>", subtitle)
   s = string.replaceAll (s, "<<bodytext>>", filetext)
   local (adrTable = @websites.["#data"])
   html.data.adrPageTable = adrTable
   adrTable^.activeURLs = true
   adrTable^.clayCompatibility = false
   adrTable^.autoParagraphs = true
   s = html.processMacros (s)
   wp.newTextObject (s, @workspace.tempText)
   edit (@workspace.tempText)

Those last two lines are only for testing purposes, so remove them. In their place we want to write out "s" as a new textfile. We know how to do this, using file.writeWholeFile; the question is where to do it. We need a place to put the files, and we need a name for each one.

Working with folders

As for the place, let's create a new folder inside the AmelioWeb folder. We'll call it "Website."

We can calculate its pathname in the same way we calculated the pathname for "infolder"; and, to go with it, let's call the variable in which we store the result of the calculation "outfolder." Right after the "local" line where we declared "infolder," we'll insert a line:


local (outfolder = folder + "Website" + file.getPathChar ())

We need to make sure that the folder designated by "outfolder" really does exist; if it doesn't, we need to create it.

There's a verb file.sureFolder, which the DocServer website tells us "creates it if it doesn't exist."


file.sureFolder (outfolder)

Also, in case the folder does already exist, it might be because we've done the demo before, perhaps during testing.

In that case we'd like to clean it out fresh, so that only the files we add during the present run of the routine will be there.

There's a verb, file.emptyFolder, which empties out a named folder. (You can control-double-click this to study its script in the database; it's very easy to understand.)

So we'll call it:


file.emptyFolder (outfolder)

File names

So much for the place to put the files. What about a name for each file?

Well, each file in "Source Files" is named with a suffix ".txt". On the other hand, we will be creating HTML files, whose names it makes sense to end with a suffix ".html" so they can be served over the Web. Our most economical course is to change the names by replacing ".txt" with ".html". For each HTML file we make, let's call the filename "fname" and the full pathname for that file "outfile."

Clearly, "outfile" is simply "outfolder" plus "fname." Now, how will we get "fname?"

What we're starting with is "infile"; but "infile" includes the whole pathname, leading into the "Source File" folder. We want to strip this off, leaving just the name of the file itself; then we want to transform the name by replacing ".txt" with ".html." That's "fname."

We know how to perform the replacement; we can use string.replaceAll, or perhaps just string.replace (which replaces just one occurrence), it makes no difference since there's just one occurrence to replace.

How will we strip off the pathname leaving just the name of the file? There's a verb file.fileFromPath.

We need to do all this for every file we read in, so it goes in the "fileloop" construct; let's put it right after the line where we read in the file and assign it to "filetext." We can say:


local (fname = file.filefrompath (infile))
fname = string.replace (fname, ".txt", ".html")
outfile = outfolder + fname

Writing files

Now we are ready to write out "s" with a call to file.writeWholeFile.

This takes a lot of parameters so let's be sure we have them all straight. We need the pathname to write to: that's "outfile." We need the text to be written; that's "s." We need a file type code to be given to the new file; this is a textfile, so it's the string4 'TEXT'.

On a Mac we need a creator code for the new file too; what application should "own" this file? It could be any application, really; neither our browser nor a Web server is going to care who created the file, provided it's a textfile. But for our own convenience, it might be nice to have the creator be our browser so we can double-click the file and inspect it in the browser.

Finally, we need a creation date-time; there's no need for this to be anything but the actual moment of creation, which we can determine with a call to clock.now.


file.writeWholeFile (outfile, s, 'TEXT', 'MSIE', clock.now ())

The 'MSIE' part is a bit of Mac-specific functionality: it determines the creator code of the file. In the above example that's Microsoft Internet Explorer. For Netscape you'd use 'MOSS.' (If you're using a Mac, jump to system.verbs.apps.Netscape.id and system.verbs.apps.msExplorer.id. You'll see these codes listed there.) Including the creator code on Windows doesn't do anything: it's harmless. Might as well leave it in there.

Our program now looks like this:


local (folder)
if not file.getFolderDialog ("Where is the AmelioWeb folder?", @folder) {return}
local (templatetext = string (file.readWholeFile (folder + "template.html")))
templatetext = string.replaceAll (templatetext, cr + lf, cr)
local (infolder = folder + "Source Files" + file.getPathChar ())
local (outfolder = folder + "Website" + file.getPathChar ())
file.sureFolder (outfolder)
file.emptyFolder (outfolder)
local (infile, filetext, title, subtitle)
on popline()
   local (s)
   s = string.NthField (filetext, cr, 1)
   filetext = string.delete (filetext, 1, sizeOf (s) + 1)
   return (s)
fileloop (infile in infolder, 1)
   filetext = string (file.readWholeFile (infile))
   filetext = string.replaceAll (filetext, cr + lf, cr)
   local (fname = file.filefrompath (infile))
   fname = string.replace (fname, ".txt", ".html")
   outfile = outfolder + fname
   title = popline (); popline (); popline (); popline ()
   subtitle = popline (); popline ()
   local (s = templatetext)
   s = string.replaceAll (s, "<<title>>", title)
   s = string.replaceAll (s, "<<subtitle>>", subtitle)
   s = string.replaceAll (s, "<<bodytext>>", filetext)
   local (adrTable = @websites.["#data"])
   html.data.adrPageTable = adrTable
   adrTable^.activeURLs = true
   adrTable^.clayCompatibility = false
   adrTable^.autoParagraphs = true
   s = html.processMacros (s)
   file.writeWholeFile (outfile, s, 'TEXT', 'MSIE', clock.now ())

Trying it out

The moment of truth has come. Let's run the program and see what we get. (Hmm, it's sort of dull waiting around for it to process the files and not knowing what stage it's at; we really must put in some feedback later on.)

When it's all finished, go in the Finder to the AmelioWeb folder, open the Website folder, pick a file, any file, and double-click it to inspect it in the browser.

Gee, it's beautiful! There's only one minor problem; there are three pictures that are supposed to appear, and they don't.

Examining the "Website" and "Source Files" folders, and looking at one of the HTML files with a word-processor so we can see the actual HTML, we see why this is. The IMG tags use relative URLs; they assume a folder "images" in the same folder as the Web page. That folder is in "Source Files," not in "Website." Clearly, as we create the HTML files, we also need to copy the "images" folder and its contents across from "Source Files" to "Website."

We could do this explicitly as a one-time copy, but let's create a more general solution so that our program remains a powerful, flexible utility. We'll make a rule that any files in "Source Files" are assumed to be email textfiles and will be transformed into HTML, but any folders in "Source Files" are assumed to be other supplementary material and will be copied across unchanged.

To implement this, we need our "fileloop" to include both files and folders as candidates for "infile," deciding what to do based on what type it encounters.

At the moment, though, our "fileloop" is ignoring folders entirely; "infile" can never be a folder. A careful reading of the DocServer entry for "fileloop" shows why this is: the way to get "fileloop" to include folders among the pathnames fed to the loop is to omit the second parameter altogether.

There's a verb file.isFolder which will tell us whether we're seeing a file or a folder, and a verb file.copy that will copy a folder and its contents.

Adding feedback

While we're revising, let's also put in some feedback so that our routine is a little more interesting and informative for the user to watch.

We'll put each "fname" value into the Main Window, so we can observe our progress through the input files.

So, now the "fileloop," with the order of things slightly revised, looks like this:


fileloop (infile in infolder)
local (fname = file.filefrompath (infile))
msg (fname)
if file.isFolder (infile)
   file.copy (infile, outfolder + fname)
else
   filetext = string (file.readWholeFile (infile))
   filetext = string.replaceAll (filetext, cr + lf, cr)
   fname = string.replace (fname, ".txt", ".html")
   outfile = outfolder + fname
   title = popline (); popline (); popline (); popline ()
   subtitle = popline (); popline ()
   local (s = templatetext)
   s = string.replaceAll (s, "<<title>>", title)
   s = string.replaceAll (s, "<<subtitle>>", subtitle)
   s = string.replaceAll (s, "<<bodytext>>", filetext)
   local (adrTable = @websites.["#data"])
   html.data.adrPageTable = adrTable
   adrTable^.activeURLs = true
   adrTable^.clayCompatibility = false
   adrTable^.autoParagraphs = true
   s = html.processMacros (s)
   file.writeWholeFile (outfile, s, 'TEXT', 'MSIE', clock.now ())

We run the revised routine, and double-click an HTML file to examine it. The images are there.

Adding flash

Since this should be a flashy routine such as to amaze our friends and astound our enemies, let's add even more feedback to show its progress: we'll use Frontier's ability to drive other applications to get our browser to show each HTML file as it is created.

The simplest way to do this is with the verbs in suites.webbrowser, because they don't care which browser we're using (plus, their names and syntax are fairly human). The verb we want is webbrowser.openDocument. To the end of the "fileloop" we'll simply add:


webBrowser.openDocument (outfile)

It remains only to add the code that creates a table-of-contents page linking to each HTML file. That's for our next (and final!) chapter.

PreviousNext

   

Site Scripted By Frontier © Copyright 1996-98 UserLand Software. This page was last built on 2/10/98; 1:26:22 AM. It was originally posted on 4/15/97; 8:53:46 PM. Webmaster: brent@scripting.com.

 
This tutorial was adapted for Frontier 5 by Brent Simmons, from the Frontier 4 scripting tutorial written by Matt Neuburg.