module.exports = function()

in routes/onedoc.js [20:388]


module.exports = function (Document, opts) {
    var checkID = check(opts.jsonidpath)
        .exists()
        .custom((val, {
            req
        }) => {
            if (validator.matches(val, '^' + opts.idpattern + '$')) {
                return true;
            }
            return false;
        })
        .withMessage('Document ID not valid. Expecting ' + opts.idpattern);

    var router = module.router = express.Router();

    // GET docuemnt
    router.get('/:id', csrfProtection, [checkID], function (req, res) {
        var q = {};
        q[opts.idpath] = req.params.id;
        Document.findOne(q, async function (err, doc) {
            var ucomments = undefined;
            if (!doc) {
                if(req.params.id != 'new') {
                    req.flash('error', 'ID not found: ' + req.params.id);
                }
            } else {
                ucomments = doc.comments;
            }
            // ASF
            if (!asf.asfhookshowcveacl(doc, req, res)) {
                module.router = router;
                return module;                                
            }
            // END ASF
            res.locals.renderStartTime = Date.now();
            if (opts.conf.readonly) {
                if (doc && doc._doc) {
                    delete doc._doc._id;
                }
                res.setHeader("Content-Security-Policy", "default-src 'self'; connect-src 'none'; font-src 'none'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'");
                res.render((opts.render == 'render' ? 'readonly' : opts.render), {
                    title: req.params.id,
                    doc: doc ? doc._doc : {},
                    textUtil: textUtil,
                    doc_id: req.params.id,
                    csrfToken: req.csrfToken(),
                    renderTemplate: 'default',
                    ucomments: ucomments
                });
            } else {
                res.render(opts.edit, {
                    title: req.params.id,
                    opts: opts,
                    doc_id: req.params.id,
                    idpath: opts.jsonidpath,
                    doc: doc,
                    textUtil: textUtil,
                    csrfToken: req.csrfToken(),
                    allowAjax: true,
                    ucomments: ucomments
                });
            }
        });
    });

    if (opts.conf.readonly) {
        return module;
    }

    var existCheck = check(opts.jsonidpath)
        .exists()
        .custom((val, {
            req
        }) => {
            var q = {};
            q[opts.idpath] = val;
            return Document.findOne(q).then((doc) => {
                if (doc) {
                    throw new Error('Document ' + val + ' exists. Save with a different ID or Update the existing one');
                    return false;
                } else {
                    return true;
                }
            });
        });

    var queryMW = querymw(opts.facet);

    // Render a NEW editable document
    router.get('/new', csrfProtection, queryMW, async function (req, res) {
        var doc = null;
        if (req.querymen.query[opts.idpath]) {
            var fq = {};
            fq[opts.idpath] = req.querymen.query[opts.idpath];
            var doc = await Document.findOne(fq);
        }
        if (doc) {
            res.redirect(req.querymen.query[opts.idpath]);
        } else {
            var doc = {};
            for (a in req.querymen.query) {
                _.set(doc, a, req.querymen.query[a]);
            };
            //console.log(JSON.stringify(req.querymen.query));
            res.render(opts.edit, {
                title: 'New',
                doc: doc,
                opts: opts,
                idpath: opts.jsonidpath,
                textUtil: textUtil,
                csrfToken: req.csrfToken(),
                allowAjax: true
            });
        }
    });


    module.addModelHistory = function (model, oldDoc, newDoc) {
        // ASF
        asf.asfhookaddhistory(oldDoc, newDoc);
        // END ASF
        if (oldDoc === null) {
            oldDoc = {
                __v: -1,
                _id: newDoc._id,
                author: newDoc.author,
                updatedAt: newDoc.updatedAt,
                body: {}
            }
        }
        var auditTrail = {
            parent_id: oldDoc._id,
            updatedAt: newDoc.updatedAt,
            author: newDoc.author,
            __v: oldDoc.__v + 1,
            body: {
                old_version: oldDoc.__v,
                old_author: oldDoc.author,
                old_date: oldDoc.updatedAt,
                patch: jsonpatch.compare(oldDoc.body, newDoc.body),
            },
        };
        //todo: eliminate mongoose and call InsertOne directly
        if (auditTrail.body.patch.length > 0) {
            model.bulkWrite([{
                insertOne: {
                    document: auditTrail
                }
            }], function (err, d) {
                if (err) {
                    console.log('Error: saving history ' + err);
                } else {
                }
            });
            return auditTrail;
        } else {
            return null;
        }
    }
    var History = docModel(opts.schemaName + '_history');
    var addHistory = function(oldDoc, newDoc) {
        return module.addModelHistory(History, oldDoc, newDoc);
    }

    // Creat a new document
    router.post(/\/(new)$/, csrfProtection, [checkID, existCheck], function (req, res) {
        let errors = validationResult(req).array();
        if (errors.length > 0) {
            var msg = 'Error: ';
            for (var e of errors) {
                msg += e.param + ': ' + e.msg + ' ';
            }
            res.json({
                type: 'err',
                msg: msg
            });
            return;
        }

        let entry = new Document({
            "body": req.body,
            "author": req.user.username
        });
        entry.save(function (err, doc) {
            if (err) {
                res.json({
                    type: 'err',
                    msg: 'Error ' + err
                });
                return;
            } else {
                addHistory(null, doc);
                res.json({
                    type: 'go',
                    to: _.get(doc, opts.idpath)
                });
                return;
            }
        });
        return;
    });

    // Update or insert existing Document ID 
    router.post('/:id(' + opts.idpattern + ')', csrfProtection, [checkID], function (req, res) {
        let errors = validationResult(req).array();
        if (errors.length > 0) {
            var msg = 'Error: ';
            for (var e of errors) {
                msg += e.param + ': ' + e.msg + ' ';
            }
            res.json({
                type: 'err',
                msg: msg
            });
            return;
        }

        //let doc = req.body;
        let inputID = _.get(req, opts.idpath);
        let entry = {
            "body": req.body,
            "author": req.user.username
        };
        let queryNewID = {};
        let queryOldID = {};
        queryNewID[opts.idpath] = inputID;
        queryOldID[opts.idpath] = req.params.id;
        var renaming = (req.params.id != inputID);
        // ASF
        var dorefresh = false;
        // END ASF
        Document.findOne(queryNewID).then((existingDoc) => {
            if (existingDoc) {
                // check Document ID is being renamed.
                if (renaming) {
                    res.json({
                        type: 'err',
                        msg: 'Not saved. Document ' + inputID + ' exists. Save with a different ID or update the existing one.'
                    });
                    return;
                }
            }
            // ASF
            asf.asfhookupsertdoc(req,dorefresh);
            // END ASF
            var d = new Date();
            newDoc = {
                body: req.body,
                author: req.user.username,
                updatedAt: d
            };
            Document.findOneAndUpdate(
                queryOldID,
                {
                    "$set": newDoc,
                    "$inc": {
                        __v: 1
                    },
                    "$setOnInsert": {
                        createdAt: d
                    }
                }, {
                "upsert": true
            },
                function (err, doc) {
                    if (doc) {
                        addHistory(doc, newDoc);
                    } else {
                        addHistory(null, newDoc);
                    }
                    if (err) {
                        res.json({
                            type: 'err',
                            msg: 'Error! Document not Updated, ' + err
                        });
                    } else {
                        // ASF
                        if (renaming || dorefresh) {
                        // END ASF
                            res.json({
                                type: 'go',
                                to: inputID
                            });
                        } else {
                            res.json({
                                type: 'saved'
                            });
                        }
                    }
                    return;
                });
        });
        return;
    });

    //Delete Document
    router.delete('/:id(' + opts.idpattern + ')', csrfProtection, function (req, res) {
        let query = {};
        query[opts.idpath] = req.params.id;

        // ASF
        console.log("ASF1 remove document",query);
        if (!asf.asfallowedtodelete(req)) {
            console.log("ASF1 no delete as "+req.user.pmcs + " is not in "+ opts.conf.admingroupname)
            res.send('not authorized');                                                   
            return;                                                                       
        }     
        // END ASF
        Document.deleteOne(query, function (err) {
            if (err) {
                res.send('Error Deleting');
                return;
            } else {
                res.send('Deleted');
            }
        });
    });

    // ASF
    // fetch either logs or comments
    var getSubDocs = async function (subSchema, doc_id, mypmcs) {
    // END ASF
        var q = {}
        q[opts.idpath] = doc_id;
        parentDoc = await Document.findOne(q).exec();
        if (parentDoc) {
            // ASF
            if (parentDoc.body && parentDoc.body.CNA_private && !asf.asfgroupacls(parentDoc.body.CNA_private.owner, mypmcs)) {
                return { 'message': 'Access Denied' };
            }
            // END ASF
            var subq = {
                parent_id: parentDoc._id
            }
            var ret = await subSchema.find(subq, {
                _id: 0,
                parent_id: 0
            }).sort({
                updatedAt: -1
            }).exec();
            return (ret);
        } else {
            return {
                'message': 'No parent document'
            };
        }
    }

    // Get document chage history (JSON patches)
    router.get('/log/:id', [checkID], function (req, res) {
        // ASF
        console.log(History, opts.schemaName);
        getSubDocs(History, req.params.id, req.user.pmcs).then(r => {
            res.json(r);
        });
        // END ASF
    });

    // Get document comments
    router.get('/comment/:id', [checkID], function (req, res) {
        // ASF
        getSubDocs(History, req.params.id, req.user.pmcs).then(r => {
            res.json(r);
        });
        // END ASF
    });

    return module;
}