0

I'm a fairly new programmer. I'm attempting something pretty advanced, but I enjoy a good challenge. :~) I'm using Adobe's ESTK (ExtendScript Toolkit) to write a complex script for InDesign CS6. I've gone through most of the tutorials and have learned quite a bit, but I have run into a wall now.

I need the script to detect whether a certain folder meets a certain criteria, and if it does, to delve into that folder, count all of its subfolders, and open each of the .indd files in each of those subfolders, in turn, performing tasks on each one. I have only just started the script today and this is what I've got so far:

var getDataDialog = app.dialogs.add({name:"Job Info"});
with(getDataDialog){
    // Add a dialog column.
    with(dialogColumns.add()){
        // Create the order number text edit box.
        var orderNumTextEditBox = textEditboxes.add({minWidth:80});
        }
    }
// Show the dialog box.
var dialogResult = getDataDialog.show();
if(dialogResult == true){
    // Get the order number and put it in the variable "orderNum".
    var orderNum = orderNumTextEditBox.editContents;
    // Get the first three numbers of the order number and put it in "orderPrefix".
    var orderPrefix = orderNum.substr(0,3);
    // Remove the dialog box from memory.
    getDataDialog.destroy();
    // Store the folder prefix into "folderPrefix".
    var folderPrefix = "/g/ ArtDept/JOBS/" + orderPrefix + "000-" + orderPrefix + "999/"
    // Open the document with the order number.
    var myDoc = app.open(File(folderPrefix + orderNum + " Folder/" + orderNum + ".indd"), true);
    }
else{
    getDataDialog.destroy();
    }

So, if the order number entered is "405042", it will look in the "405000-405999" folder, then into the packaged folder called "405042 Folder", then open the .indd file inside that. Trouble is, we sometimes have several packages within a folder. For example, we might have:

405000-405999/405007/405007_N10/405007_N10.indd
405000-405999/405007/405007_C12/405007_C12.indd
405000-405999/405007/405007_Orange/405007_Orange.indd

I would want the script to open up each of those files, in turn, and perform some tasks on them. I'm certain that it's possible, but I just need to know how to code it.

halfer
  • 19,824
  • 17
  • 99
  • 186
Sturm
  • 689
  • 2
  • 23
  • 52
  • 1
    Your topic is already pretty complex. So there is no simple answer to that. You should have a look into these things: Recursion in JavaScript http://www.codecademy.com/courses/javascript-lesson-205 Files in ExtendScript http://jongware.mit.edu/Js/pc_File.html Folders in ExtendScript http://jongware.mit.edu/Js/pc_Folder.html Hope that helps a bit. – fabianmoronzirfas May 14 '13 at 06:30
  • Can you possibly draw your directory structure out a little more? – Josh Voigts May 14 '13 at 13:15
  • Thank you for the links, @fabiantheblind; I will check them out as soon as I have time and hopefully learn more. Yes, this is definitely going to wind up being a complex script, so it's my trial by fire. – Sturm May 15 '13 at 14:24
  • @JoshVoigts--Hopefully these two images can make things more clear. If you are familiar with InDesign, you know that when you package a project, it saves the .indd file, along with font files, links, etc. in a folder called " Folder". (Curse these comment limits...) – Sturm May 15 '13 at 14:25
  • @JoshVoigts (continued...)--With that in mind, [this first image](http://i.imgur.com/R8ESvMQ) shows what it looks like when we package a file called 405006, for example. If we have a project with a bunch of packages, then they are saved in a parent folder with that project's number. [This second image](http://i.imgur.com/d3AS3ee.jpg) shows just such a project, number 405007. I hope these clear things up for you a little better than my incorrect file structure I posted in the original question. – Sturm May 15 '13 at 14:26

1 Answers1

2

If I understand your problem correctly, there are two parts:

Part 1: Find a certain folder meeting certain criteria. (Looks like you've got this covered.)

Part 2: For each InDesign document that is a descendant of this folder, open it and do some processing to it.

In the sample below, I've labeled where you should add the code that finds the top folder and the code that manipulates each document. If you run the sample as-is, it will use the script's parent folder as the top folder, and for each descendant document it will simply log its name.

// TODO: (Part 1) Put code here that determines the top folder.
var topFolder = (new File($.fileName)).parent; // Change me. Currently the script's folder.
forEachDescendantFile(topFolder, doStuffIfdocument); // Don't change this line.

/**
 * Does stuff to the document.
 */
function doStuff(document) {
    // TODO: (Part 2) Put code here to manipulate the document.
    $.writeln("Found document " + document.name);
}

/**
 * Opens the given file if it ends in ".indd". Then calls doStuff().
 * 
 * @param {File} oFile
 */
function doStuffIfdocument(oFile) {
    if (matchExtension(oFile, "indd")) {
        var document = app.open(oFile);
        try {
            doStuff(document);
        }
        finally {
            document.close(SaveOptions.YES);
        }
    }
}

/**
 * Calls the callback function for each descendant file of the specified folder.
 * The callback should accept a single argument that is a File object.
 * 
 * @param {Folder} folder The folder to search in.
 * @param {Function} callback The function to call for each file.
 */
function forEachDescendantFile(folder, callback) {
    var aChildren = folder.getFiles();
    for (var i = 0; i < aChildren.length; i++) {
        var child = aChildren[i];
        if (child instanceof File) {
            callback(child);
        }
        else if (child instanceof Folder) {
            this.forEachDescendantFile(child, callback);
        }
        else {
            throw new Error("The object at \"" + child.fullName + "\" is a child of a folder and yet is not a file or folder.");
        }
    }
}

/**
 * Returns true if the name of the given file ends in the given extension. Case insensitive.
 *
 * @param {File} iFile
 * @param {String} sExtension The extension to match, not including the dot. Case insensitive.
 * @return {boolean}
 */
function matchExtension(iFile, sExtension) {
    sExtension = "." + sExtension.toLowerCase();
    var displayName = iFile.displayName.toLowerCase();
    if (displayName.length < sExtension.length) {
        return false;
    }
    return displayName.slice(-sExtension.length) === sExtension;
}
dln385
  • 11,630
  • 12
  • 48
  • 58
  • Being a complete newcomer to JavaScript/ExtendScript, I'm going to need to mull over your code to see if I can grok it before I just blindly paste it into my program. – Sturm May 15 '13 at 13:52
  • @Sturm Always a wise decision. A recommendation: instead of trying to follow the code path start-to-finish, just look at each function individually and try to verify that it does what the comment says. It's much simpler that way. – dln385 May 15 '13 at 14:15
  • Although I have a myriad of questions, I'll just start with one for now: In line 3, the second argument when you call the function `forEachDescendantFile` is a function. That's fine, but shouldn't you pass an argument to _that_ function as well? It's expecting a `{File}` type argument, if I'm not mistaken. – Sturm May 16 '13 at 17:34
  • @Sturm A function is itself an object, and it can be passed around or stored to a variable just like any other object. The `forEachDescendantFile` function takes a function as its second argument, and stores it to the variable `callback`. The line `callback(child);` is actually calling the `forEachDescendantFile` function which is stored in the variable `callback`. It isn't calling a function named `callback` because there is no function named `callback`. – dln385 May 16 '13 at 17:42
  • @Sturm To answer your question more directly: If you use parentheses after a function name, then you are ***calling*** the function. After the function completes, the call is replaced with the function's ***return value***. If you don't ***call*** the function, then the function doesn't run and the call doesn't get replaced by a return value. Instead, you just have a reference to a function object. – dln385 May 16 '13 at 17:53
  • Okay, @dln385, it seems this whole `callback` thing is what's throwing me off. I'm still not nearly advanced enough to understand it. I've gone through a lot of tutorials so far, taking me from an absolute beginner to getting the basics down pat. I've even done the recursion tutorial that @fabiantheblind recommended in the comments to my original question. It took a bit to wrap my head around that one, but I did finally make it. Okay, your second comment showed up and that helps me some more, now, thank you. I also plugged your code in and it worked brilliantly. Still trying to grok it. – Sturm May 16 '13 at 18:28
  • @Sturm If the code works, can you please click the green checkmark at the top-left of the answer to accept it? As others have mentioned, this is a complex problem that requires an advanced answer. Just keep coding and these things will become easier to understand. I don't have a specific tutorial for you, but as long as you're scripting in InDesign I highly recommend using [this page](http://jongware.mit.edu/idcs6js/) for reference. – dln385 May 16 '13 at 18:52
  • Thanks, @dln385, I'll keep that site bookmarked—it certainly looks more useful than ESTK's built-in Object Model Viewer. And yes, I'll give you that checkmark now. Thank you for all of your help! – Sturm May 16 '13 at 19:05