mongaku/mongaku

View on GitHub
src/logic/edit.js

Summary

Maintainability
D
2 days
Test Coverage
// @flow

const async = require("async");
const formidable = require("formidable");

const db = require("../lib/db");
const {cloneModel} = require("../lib/clone");
const record = require("../lib/record");
const models = require("../lib/models");
const options = require("../lib/options");
const metadata = require("../lib/metadata");
const urls = require("../lib/urls")(options);

module.exports = function(app: express$Application) {
    const Image = models("Image");

    const {auth, canEdit} = require("./shared/auth");

    const removeRecord = (req: express$Request, res, next) => {
        const {params, i18n, lang} = req;
        const {type} = params;
        const Record = record(type);
        const id = `${params.source}/${params.recordName}`;

        Record.findById(id, (err, record) => {
            if (err || !record) {
                return next(new Error(i18n.gettext("Not found.")));
            }

            record.remove(err => {
                if (err) {
                    return next(
                        new Error(i18n.gettext("Error removing record.")),
                    );
                }

                res.redirect(urls.gen(lang, "/"));
            });
        });
    };

    const editView = (
        {params: {type, source, recordName}, i18n}: express$Request,
        res,
    ) => {
        const Record = record(type);
        const id = `${source}/${recordName}`;

        Record.findById(id, (err, record) => {
            if (err || !record) {
                return res.status(404).render("Error", {
                    title: i18n.gettext("Page not found."),
                });
            }

            const recordTitle = record.getTitle(i18n);
            const title = i18n.format(
                i18n.gettext("Updating '%(recordTitle)s'"),
                {recordTitle},
            );

            record.loadImages(true, () => {
                Record.getFacets(i18n, (err, globalFacets) => {
                    record.getDynamicValues(i18n, (err, dynamicValues) => {
                        const clonedRecord = cloneModel(record, i18n);

                        clonedRecord.imageModels = record.images.map(image =>
                            cloneModel(image, i18n),
                        );

                        res.render("EditRecord", {
                            title,
                            mode: "edit",
                            record: clonedRecord,
                            source,
                            globalFacets,
                            dynamicValues,
                            type,
                        });
                    });
                });
            });
        });
    };

    const edit = (req: express$Request, res, next) => {
        const {
            params: {type, recordName, source},
            i18n,
            lang,
        } = req;
        const props = {};
        const model = metadata.model(type);
        const hasImageSearch = options.types[type].hasImageSearch();
        const _id = `${source}/${recordName}`;

        const form = new formidable.IncomingForm();
        form.encoding = "utf-8";
        form.maxFieldsSize = options.maxUploadSize;
        form.multiples = true;

        form.parse(req, (err, fields, files) => {
            /* istanbul ignore if */
            if (err) {
                return next(
                    new Error(i18n.gettext("Error processing upload.")),
                );
            }

            if (fields.removeRecord) {
                return removeRecord(req, res, next);
            }

            for (const prop in model) {
                props[prop] = fields[prop];
            }

            Object.assign(props, {
                id: recordName,
                lang: lang,
                source,
                type,
            });

            const Record = record(type);

            const {data, error} = Record.lintData(props, i18n);

            if (error) {
                return next(new Error(error));
            }

            const mockBatch = {
                _id: db.mongoose.Types.ObjectId().toString(),
                source,
            };

            const images = Array.isArray(files.images)
                ? files.images
                : files.images
                    ? [files.images]
                    : [];

            async.mapSeries(
                images,
                (file, callback) => {
                    if (!file.path || file.size <= 0) {
                        return process.nextTick(callback);
                    }

                    Image.fromFile(mockBatch, file, (err, image) => {
                        // TODO: Display better error message
                        if (err) {
                            return callback(
                                new Error(
                                    i18n.gettext("Error processing image."),
                                ),
                            );
                        }

                        image.save(err => {
                            /* istanbul ignore if */
                            if (err) {
                                return callback(err);
                            }

                            callback(null, image);
                        });
                    });
                },
                (err, unfilteredImages) => {
                    if (err) {
                        return next(err);
                    }

                    Record.findById(_id, (err, record) => {
                        if (err || !record) {
                            return res.status(404).render("Error", {
                                title: i18n.gettext("Not found."),
                            });
                        }

                        record.set(data);

                        for (const prop in model) {
                            if (!fields[prop] && !data[prop]) {
                                record[prop] = undefined;
                            }
                        }

                        record.images = record.images.concat(
                            unfilteredImages
                                .filter(image => image)
                                .map(image => image._id),
                        );

                        record.save(err => {
                            if (err) {
                                return next(
                                    new Error(
                                        i18n.gettext("Error saving record."),
                                    ),
                                );
                            }

                            const finish = () =>
                                res.redirect(record.getURL(lang));

                            if (record.images.length === 0 || !hasImageSearch) {
                                return finish();
                            }

                            // If new images were added then we need to update
                            // their similarity and the similarity of all other
                            // images, as well.
                            Image.queueBatchSimilarityIndex(
                                mockBatch._id,
                                finish,
                            );
                        });
                    });
                },
            );
        });
    };

    const removeImage = (req: express$Request, res, next) => {
        const {
            params: {type, source, recordName},
            i18n,
            lang,
        } = req;
        const Record = record(type);
        const hasImageSearch = options.types[type].hasImageSearch();
        const id = `${source}/${recordName}`;

        const form = new formidable.IncomingForm();
        form.encoding = "utf-8";

        form.parse(req, (err, fields) => {
            /* istanbul ignore if */
            if (err) {
                return next(
                    new Error(i18n.gettext("Error processing request.")),
                );
            }

            const imageID = fields.image;

            Record.findById(id, (err, record) => {
                if (err || !record) {
                    return next(new Error(i18n.gettext("Not found.")));
                }

                record.images = record.images.filter(
                    image => image !== imageID,
                );

                record.save(err => {
                    if (err) {
                        return next(
                            new Error(i18n.gettext("Error saving record.")),
                        );
                    }

                    const finish = () => res.redirect(record.getURL(lang));

                    if (!hasImageSearch) {
                        return finish();
                    }

                    record.updateSimilarity(finish);
                });
            });
        });
    };

    return {
        routes() {
            // Handle these last as they'll catch almost anything
            app.get("/:type/:source/:recordName/edit", auth, canEdit, editView);
            app.post("/:type/:source/:recordName/edit", auth, canEdit, edit);
            app.post(
                "/:type/:source/:recordName/remove-image",
                auth,
                canEdit,
                removeImage,
            );
        },
    };
};