diff --git a/API/Backend/Draw/models/filehistories.js b/API/Backend/Draw/models/filehistories.js index 4640e30b..034d20b2 100644 --- a/API/Backend/Draw/models/filehistories.js +++ b/API/Backend/Draw/models/filehistories.js @@ -43,6 +43,11 @@ const attributes = { type: Sequelize.DataTypes.ARRAY(Sequelize.DataTypes.INTEGER), allowNull: true, }, + author: { + type: Sequelize.STRING, + unique: false, + allowNull: true, + }, }; const options = { @@ -57,5 +62,27 @@ var FilehistoriesTEST = sequelize.define( options ); +// Adds to the table, never removes +const up = async () => { + // author column + await sequelize + .query( + `ALTER TABLE file_histories ADD COLUMN IF NOT EXISTS author varchar(255) NULL;` + ) + .then(() => { + return null; + }) + .catch((err) => { + logger( + "error", + `Failed to adding file_histories.author column. DB tables may be out of sync!`, + "file_histories", + null, + err + ); + return null; + }); +}; + // export Filehistories model for use in other files. -module.exports = { Filehistories, FilehistoriesTEST }; +module.exports = { Filehistories, FilehistoriesTEST, up }; diff --git a/API/Backend/Draw/models/userfiles.js b/API/Backend/Draw/models/userfiles.js index e6dc8c8d..f701f743 100644 --- a/API/Backend/Draw/models/userfiles.js +++ b/API/Backend/Draw/models/userfiles.js @@ -84,6 +84,16 @@ const attributes = { allowNull: true, defaultValue: null, }, + publicity_type: { + type: Sequelize.STRING, + unique: false, + allowNull: true, + }, + public_editors: { + type: Sequelize.ARRAY(Sequelize.TEXT), + unique: false, + allowNull: true, + }, }; const options = { @@ -153,6 +163,44 @@ const up = async () => { ); return null; }); + + // publicity_type column + await sequelize + .query( + `ALTER TABLE user_files ADD COLUMN IF NOT EXISTS publicity_type varchar(255) NULL;` + ) + .then(() => { + return null; + }) + .catch((err) => { + logger( + "error", + `Failed to adding user_files.publicity_type column. DB tables may be out of sync!`, + "user_files", + null, + err + ); + return null; + }); + + // public_editors column + await sequelize + .query( + `ALTER TABLE user_files ADD COLUMN IF NOT EXISTS public_editors text[] NULL;` + ) + .then(() => { + return null; + }) + .catch((err) => { + logger( + "error", + `Failed to adding user_files.public_editors column. DB tables may be out of sync!`, + "user_files", + null, + err + ); + return null; + }); }; // export User model for use in other files. diff --git a/API/Backend/Draw/routes/draw.js b/API/Backend/Draw/routes/draw.js index bab38b8b..703d0bba 100644 --- a/API/Backend/Draw/routes/draw.js +++ b/API/Backend/Draw/routes/draw.js @@ -49,6 +49,7 @@ const pushToHistory = ( time, undoToTime, action_index, + user, successCallback, failureCallback ) => { @@ -85,6 +86,7 @@ const pushToHistory = ( time: time, action_index: action_index, history: h, + author: user, }; // Insert new entry into the history table Table.create(newHistoryEntry) @@ -252,6 +254,7 @@ const clipOver = function ( time, null, 5, + req.user, () => { if (typeof successCallback === "function") successCallback(); }, @@ -392,6 +395,7 @@ const clipUnder = function ( time, null, 7, + req.user, () => { if (typeof successCallback === "function") successCallback(); }, @@ -475,13 +479,28 @@ const add = function ( Files.findOne({ where: { id: req.body.file_id, - [Sequelize.Op.or]: { - file_owner: req.user, - [Sequelize.Op.and]: { - file_owner: "group", - file_owner_group: { [Sequelize.Op.overlap]: groups }, + [Sequelize.Op.or]: [ + { file_owner: req.user }, + { + [Sequelize.Op.and]: { + file_owner: "group", + file_owner_group: { [Sequelize.Op.overlap]: groups }, + }, }, - }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "list_edit", + public_editors: { [Sequelize.Op.contains]: [req.user] }, + }, + }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "all_edit", + }, + }, + ], }, }).then((file) => { if (!file) { @@ -581,6 +600,7 @@ const add = function ( time, null, 0, + req.user, () => { if (typeof successCallback === "function") successCallback(created.id, created.intent); @@ -665,13 +685,28 @@ const edit = function (req, res, successCallback, failureCallback) { Files.findOne({ where: { id: req.body.file_id, - [Sequelize.Op.or]: { - file_owner: req.user, - [Sequelize.Op.and]: { - file_owner: "group", - file_owner_group: { [Sequelize.Op.overlap]: groups }, + [Sequelize.Op.or]: [ + { file_owner: req.user }, + { + [Sequelize.Op.and]: { + file_owner: "group", + file_owner_group: { [Sequelize.Op.overlap]: groups }, + }, }, - }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "list_edit", + public_editors: { [Sequelize.Op.contains]: [req.user] }, + }, + }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "all_edit", + }, + }, + ], }, }) .then((file) => { @@ -749,6 +784,7 @@ const edit = function (req, res, successCallback, failureCallback) { time, null, 1, + req.user, () => { successCallback(createdId, createdUUID, createdIntent); }, @@ -822,13 +858,28 @@ router.post("/remove", function (req, res, next) { Files.findOne({ where: { id: req.body.file_id, - [Sequelize.Op.or]: { - file_owner: req.user, - [Sequelize.Op.and]: { - file_owner: "group", - file_owner_group: { [Sequelize.Op.overlap]: groups }, + [Sequelize.Op.or]: [ + { file_owner: req.user }, + { + [Sequelize.Op.and]: { + file_owner: "group", + file_owner_group: { [Sequelize.Op.overlap]: groups }, + }, }, - }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "list_edit", + public_editors: { [Sequelize.Op.contains]: [req.user] }, + }, + }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "all_edit", + }, + }, + ], }, }).then((file) => { if (!file) { @@ -861,6 +912,7 @@ router.post("/remove", function (req, res, next) { time, null, 2, + req.user, () => { logger("info", "Feature removed.", req.originalUrl, req); res.send({ @@ -927,13 +979,28 @@ router.post("/undo", function (req, res, next) { Files.findOne({ where: { id: req.body.file_id, - [Sequelize.Op.or]: { - file_owner: req.user, - [Sequelize.Op.and]: { - file_owner: "group", - file_owner_group: { [Sequelize.Op.overlap]: groups }, + [Sequelize.Op.or]: [ + { file_owner: req.user }, + { + [Sequelize.Op.and]: { + file_owner: "group", + file_owner_group: { [Sequelize.Op.overlap]: groups }, + }, }, - }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "list_edit", + public_editors: { [Sequelize.Op.contains]: [req.user] }, + }, + }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "all_edit", + }, + }, + ], }, }).then((file) => { if (!file) { @@ -992,6 +1059,7 @@ router.post("/undo", function (req, res, next) { time, req.body.undo_time, 3, + req.user, () => { logger("info", "Undo successful.", req.originalUrl, req); res.send({ @@ -1052,13 +1120,28 @@ router.post("/merge", function (req, res, next) { Files.findOne({ where: { id: req.body.file_id, - [Sequelize.Op.or]: { - file_owner: req.user, - [Sequelize.Op.and]: { - file_owner: "group", - file_owner_group: { [Sequelize.Op.overlap]: groups }, + [Sequelize.Op.or]: [ + { file_owner: req.user }, + { + [Sequelize.Op.and]: { + file_owner: "group", + file_owner_group: { [Sequelize.Op.overlap]: groups }, + }, }, - }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "list_edit", + public_editors: { [Sequelize.Op.contains]: [req.user] }, + }, + }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "all_edit", + }, + }, + ], }, }).then((file) => { if (!file) { @@ -1131,6 +1214,7 @@ router.post("/merge", function (req, res, next) { time, null, 6, + req.user, () => { logger( "info", @@ -1216,13 +1300,28 @@ router.post("/split", function (req, res, next) { Files.findOne({ where: { id: req.body.file_id, - [Sequelize.Op.or]: { - file_owner: req.user, - [Sequelize.Op.and]: { - file_owner: "group", - file_owner_group: { [Sequelize.Op.overlap]: groups }, + [Sequelize.Op.or]: [ + { file_owner: req.user }, + { + [Sequelize.Op.and]: { + file_owner: "group", + file_owner_group: { [Sequelize.Op.overlap]: groups }, + }, }, - }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "list_edit", + public_editors: { [Sequelize.Op.contains]: [req.user] }, + }, + }, + { + [Sequelize.Op.and]: { + public: "1", + publicity_type: "all_edit", + }, + }, + ], }, }) .then((file) => { @@ -1290,6 +1389,7 @@ router.post("/split", function (req, res, next) { time, null, 8, + req.user, () => { res.send({ status: "success", diff --git a/API/Backend/Draw/routes/files.js b/API/Backend/Draw/routes/files.js index 0ec36f34..f0d4f4cf 100644 --- a/API/Backend/Draw/routes/files.js +++ b/API/Backend/Draw/routes/files.js @@ -48,14 +48,25 @@ router.post("/", function (req, res, next) { router.post("/getfiles", function (req, res, next) { let Table = req.body.test === "true" ? UserfilesTEST : Userfiles; + const orWhere = [ + { + file_owner: req.user, + }, + { public: "1" }, + { + public: + req.leadGroupName != null && + req.groups != null && + req.groups[req.leadGroupName] === true + ? "0" + : "1", + }, + ]; Table.findAll({ where: { //file_owner is req.user or public is '0' hidden: "0", - [Sequelize.Op.or]: { - file_owner: req.user, - public: "1", - }, + [Sequelize.Op.or]: orWhere, }, }) .then((files) => { @@ -131,6 +142,7 @@ router.post("/make", function (req, res, next) { file_description: req.body.file_description, intent: req.body.intent, public: "1", + publicity_type: "read_only", hidden: "0", template: req.body.template ? JSON.parse(req.body.template) : null, }; @@ -403,6 +415,9 @@ router.post("/restore", function (req, res, next) { * file_name: (optional) * file_description: (optional) * public: <0|1> (optional) + * template: (optional) + * publicity_type: (optional) + * public_editors: (optional) * } */ router.post("/change", function (req, res, next) { @@ -430,6 +445,24 @@ router.post("/change", function (req, res, next) { toUpdateTo.template = JSON.parse(req.body.template); } catch (err) {} } + if ( + req.body.hasOwnProperty("publicity_type") && + [null, "read_only", "list_edit", "all_edit"].includes( + req.body.publicity_type + ) + ) { + toUpdateTo.publicity_type = req.body.publicity_type; + } + if (req.body.hasOwnProperty("public_editors")) { + try { + let public_editors = null; + if (typeof req.body.public_editors === "string") + public_editors = req.body.public_editors + .split(",") + .map((e) => e.trim()); + toUpdateTo.public_editors = public_editors; + } catch (err) {} + } let updateObj = { where: { diff --git a/API/Backend/Draw/setup.js b/API/Backend/Draw/setup.js index 23901cf6..d74efb19 100644 --- a/API/Backend/Draw/setup.js +++ b/API/Backend/Draw/setup.js @@ -2,6 +2,7 @@ const routeFiles = require("./routes/files"); const routerFiles = routeFiles.router; const routerDraw = require("./routes/draw").router; const ufiles = require("./models/userfiles"); +const file_histories = require("./models/filehistories"); let setup = { //Once the app initializes @@ -28,6 +29,9 @@ let setup = { onceStarted: (s) => {}, //Once all tables sync onceSynced: (s) => { + if (typeof file_histories.up === "function") { + file_histories.up(); + } if (typeof ufiles.up === "function") { ufiles.up(); } diff --git a/src/essence/Basics/Formulae_/Formulae_.js b/src/essence/Basics/Formulae_/Formulae_.js index 3a604ab9..28dceb1f 100644 --- a/src/essence/Basics/Formulae_/Formulae_.js +++ b/src/essence/Basics/Formulae_/Formulae_.js @@ -1757,7 +1757,7 @@ var Formulae_ = { image.style.color = stringToTest return image.style.color !== 'rgb(255, 255, 255)' }, - timestampToDate(timestamp) { + timestampToDate(timestamp, small) { var a = new Date(timestamp * 1000) var months = [ 'Jan', @@ -1784,6 +1784,19 @@ var Formulae_ = { var sec = a.getUTCSeconds() < 10 ? '0' + a.getUTCSeconds() : a.getUTCSeconds() + if (small) { + return ( + month + + '/' + + date + + '/' + + (year + '').slice(-2) + + ' ' + + hour + + ':' + + min + ) + } return ( monthName + ' ' +