{
  "translatorID": "86ffd88b-6f4e-4bec-a5be-839c1034beb2",
  "label": "File Hierarchy",
  "description": "Export files according to collection organisation",
  "creator": "Emiliano Heyns",
  "target": "txt",
  "minVersion": "4.0.27",
  "maxVersion": "",
  "configOptions": {
    "getCollections": true
  },
  "displayOptions": {
    "exportFileData": true
  },
  "translatorType": 2,
  "browserSupport": "gcsv",
  "priority": 100,
  "inRepository": false,
  "lastUpdated": "2021-12-30 13:05:34"
}

function debug(msg) {
    Zotero.debug(`File hierarchy: ${msg}`);
}
class Collections {
    constructor() {
        this.path = {};
        this.saved = {};
        let coll;
        while (coll = Zotero.nextCollection()) {
            this.register(coll);
        }
        debug('collections: ' + JSON.stringify(this.path));
    }
    join(...p) {
        return p.filter(_ => _).join('/');
    }
    register(collection, path) {
        const key = (collection.primary ? collection.primary : collection).key;
        const children = collection.children || collection.descendents || [];
        const collections = children.filter(coll => coll.type === 'collection');
        const name = this.clean(collection.name);
        this.path[key] = this.join(path, name);
        for (collection of collections) {
            this.register(collection, this.path[key]);
        }
    }
    clean(filename) {
        return filename.replace(/[\x00-\x1F\x7F\/\\:*?"<>|$%]/g, encodeURIComponent);
    }
    split(filename) {
        const dot = filename.lastIndexOf('.');
        return (dot < 1 || dot === (filename.length - 1)) ? [filename, ''] : [filename.substring(0, dot), filename.substring(dot)];
    }
    save(item) {
        const attachments = (item.itemType === 'attachment') ? [item] : (item.attachments || []);
        let collections = (item.collections || []).map(key => this.path[key]).filter(coll => coll);
        if (!collections.length)
            collections = ['']; // if the item is not in a collection, save it in the root.
        for (const att of attachments) {
            if (!att.defaultPath)
                continue;
            const [base, ext] = this.split(this.clean(att.filename));
            const subdir = att.contentType === 'text/html' ? base : '';
            for (const coll of collections) {
                const path = this.join(coll, subdir, base);
                let filename = `${path}${ext}`;
                let postfix = 0;
                while (this.saved[filename.toLowerCase()]) {
                    filename = `${path}_${++postfix}${ext}`;
                }
                this.saved[filename.toLowerCase()] = true;
                debug(JSON.stringify(filename));
                att.saveFile(filename, true);
                Zotero.write(`${filename}\n`);
            }
        }
    }
}
function doExport() {
    if (!Zotero.getOption('exportFileData'))
        throw new Error('File Hierarchy needs "Export File Data" to be on');
    const collections = new Collections;
    let item;
    while ((item = Zotero.nextItem())) {
        collections.save(item);
    }
}