Serious First Steps In UserTalk Scripting
Prev |
Table Of Contents |
Next
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(toys.readWholeFile (folder + "template.html"))) local (infolder = folder + "Source Files:") 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 (toys.readWholeFile (infile)) 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) s = html.processMacros (s) new (wptextType, @workspace.tempText) edit (@workspace.tempText) wp.setText(s)
Those last three 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 toys.writeWholeFile; the question is where to do it. We need a place to put the files, and we need a name for each one.
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:")
We need to make sure that the folder designated by "outfolder" really does exist; if it doesn't, we need to create it. Consulting ALittleHelp (under "controlling and querying the system/Finder: file moving, creating and destruction, existence, etc.: existence..."), we discover a verb file.sureFolder, which we are told "creates it if it doesn't exist. This seems a good bet, and sure enough, checking in DocServer, we find that this verb is just made for our situation. So we'll say:
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. Looking under "delete:" in the same part of ALittleHelp we find that there's a verb, suites.toys.emptyFolder, which empties out a named folder. (You can command-click this to study its script in the database; it's very easy to understand.) So we'll call it:
toys.emptyFolder (outfolder)
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? This is a string operation, so we look in ALittleHelp, under "language basics: strings, lists and records: other string operations: parse:", and lo and behold, there's an entry about parsing as a pathname, and it lists a verb file.filefrompath; this sounds like what we want, and checking in DocServer we find it is. UserTalk certainly includes some convenient verbs, doesn't it?
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
Now we are ready to write out "s" with a call to toys.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'. 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. Frontier saves us from having to remember creator codes for the applications it knows about in system.verbs.apps; every one of those apps, as you'll see if you examine their tables, has an entry called "id" containing its creator code as a string4. So if you have Netscape as your browser, it suffices to say "Netscape.id" to get the creator code (we get an automatic "with" to system.verbs.apps, remember?); if it's Internet Explorer, then say "msExplorer.id". 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. Thus, if you use Netscape:
toys.writeWholeFile (outfile, s, 'TEXT', Netscape.id, clock.now ())
Our program now looks like this:
local (folder) if not file.getFolderDialog ("Where is the AmelioWeb folder?", @folder) {return} local (templatetext = string(toys.readWholeFile (folder + "template.html"))) local (infolder = folder + "Source Files:") local (outfolder = folder + "Website:") file.sureFolder (outfolder) toys.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 (toys.readWholeFile (infile)) 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) s = html.processMacros (s) toys.writeWholeFile (outfile, s, 'TEXT', Netscape.id, clock.now ())
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" (explained by ALittleHelp, under "language basics: control structures: looping constructs:") 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. Then, as we find from ALittleHelp (under "controlling and querying the system/Finder: file moving, creating and destruction, existence, etc.: existence, kind of entity, busy:"), 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.
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 (toys.readWholeFile (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) s = html.processMacros (s) toys.writeWholeFile (outfile, s, 'TEXT', Netscape.id, clock.now ())
We run the revised routine, and double-click an HTML file to examine it. The images are there.
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)
If we make sure the browser is running and then run our routine, we get a nice, impressive routine. 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.
Prev |
Table Of Contents |
Next
Text © Matt Neuburg 1997 ALL RIGHTS RESERVED
You can download a copy of this tutorial.
This Web document scripted with Frontier. Last build at 4/18/97; 10:50:08 PM.