From a078b768da49ea627a8b657abdfcdec3a831ed64 Mon Sep 17 00:00:00 2001 From: Garrett Mills Date: Wed, 10 Jul 2019 14:10:36 -0500 Subject: [PATCH] cyclic structure resolution & edit sharing --- app/MiscUnit.js | 21 +++- app/assets/agents/web.js | 52 ++++++++++ app/assets/cycle.js | 118 ++++++++++++++++++++++ app/assets/dash_v1.js | 1 + app/controllers/dash/v1.controller.js | 138 ++++++++++++++++++++------ app/models/v1/Project.model.js | 1 + app/routing/routers/dash/v1.routes.js | 2 + app/views/dash_v1/code.pug | 7 +- app/views/dash_v1/main.pug | 15 ++- app/views/dash_v1/share.pug | 110 +++++++++++++------- app/views/dash_v1/view.pug | 5 +- 11 files changed, 394 insertions(+), 76 deletions(-) create mode 100644 app/assets/agents/web.js create mode 100644 app/assets/cycle.js diff --git a/app/MiscUnit.js b/app/MiscUnit.js index f2b24dd..55f0b5b 100644 --- a/app/MiscUnit.js +++ b/app/MiscUnit.js @@ -152,9 +152,26 @@ const breakpoint = (html = false, name = null) => { } if ( window ) window.out = out; window.breakpoint = breakpoint; // ===========================================================` - } + }, + permission: { + project: { + edit(project, target_user){ + if ( project.user_id === target_user.uuid ) return true + else if ( project.edit_user_ids.includes(target_user.uuid) ) return true + else return false + }, + view(project, target_user){ + if ( project.user_id === target_user.uuid ) return true + else if ( project.shared_user_ids.includes(target_user.uuid) ) return true + else if ( project.edit_user_ids.includes(target_user.uuid) ) return true + else return false + }, + owns(project, target_user){ + return (project.user_id === target_user.uuid) + } + } + }, } - } name(){ diff --git a/app/assets/agents/web.js b/app/assets/agents/web.js new file mode 100644 index 0000000..6de9c7e --- /dev/null +++ b/app/assets/agents/web.js @@ -0,0 +1,52 @@ +// =========================================================== +// DEVBUG INLINE DEBUGGING HELPER - FOR USE WITH DEVBUG SERVER +// TODO: REMOVE BEFORE COMMITTING +let outs = {} +let devbug_url = 'http://localhost:8000/' +let project_api_key = 'CHANGEME' +const out = (key, what, group="") => { + if ( group ){ + if ( Object.keys(outs).includes(group) ) outs[group][key] = what + else outs[group] = {}; outs[group][key] = what + } + else { + outs[key] = what + } +} +const breakpoint = (html = false, name = null) => { + var e = new Error(); + (function() { + // Load better json + var js_decycle = document.createElement("script"); + js_decycle.src = devbug_url+"assets/cycle.js"; + js_decycle.type = 'text/javascript'; + js_decycle.onload = () => { + // Load the script + var script = document.createElement("script"); + script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js'; + script.type = 'text/javascript'; + script.onload = () => { + var $ = window.jQuery; + $(() => { + var s = e.stack.split('at'); var caller = ''; + if ( s.length < 3 ) s = e.stack.split('@') + if ( s.length > 2 ) caller = s[2].trim() + else if ( s.length > 1 ) caller = s[1] + else caller = 'Unable to determine stacktrace' + var data = new FormData(); + console.log(JSON.decycle({brief: (name ? name : 'Breakpoint: ')+caller,data: outs})) + data.append('data', JSON.stringify(JSON.rmref(JSON.decycle({brief: (name ? name : 'Breakpoint: ')+caller,data: outs})))) + $.ajax({ + url: devbug_url+'api/v1/out/'+project_api_key, + data: data, cache: false, contentType: false, processData: false, method: 'POST', type: 'POST', + success: (res) => { console.log('DevBug POST Completed'); console.log(res) } + }) + }); + }; + document.getElementsByTagName("head")[0].appendChild(script); + } + document.getElementsByTagName("head")[0].appendChild(js_decycle); + })(); +} +window.out = out; window.breakpoint = breakpoint; +// =========================================================== \ No newline at end of file diff --git a/app/assets/cycle.js b/app/assets/cycle.js new file mode 100644 index 0000000..2e980f6 --- /dev/null +++ b/app/assets/cycle.js @@ -0,0 +1,118 @@ +if (typeof JSON.rmref !== "function") { + JSON.rmref = function rmref(o){ + function eachRecursive(obj) { + for (var k in obj) + { + if (typeof obj[k] == "object" && obj[k] !== null) + eachRecursive(obj[k]); + else { + for ( var key in obj ){ + if ( key === "$ref" ){ + obj[k] = "cyclic structure removed"; + break; + } + } + } + } + } + eachRecursive(o) + return o + } +} + +if (typeof JSON.decycle !== "function") { + JSON.decycle = function decycle(object, replacer) { + "use strict"; + + var objects = new WeakMap(); // object to path mappings + + return (function derez(value, path) { + + var old_path; // The path of an earlier occurance of value + var nu; // The new object or array + + if (replacer !== undefined) { + value = replacer(value); + } + + if ( + typeof value === "object" + && value !== null + && !(value instanceof Boolean) + && !(value instanceof Date) + && !(value instanceof Number) + && !(value instanceof RegExp) + && !(value instanceof String) + ) { + + + old_path = objects.get(value); + if (old_path !== undefined) { + return {$ref: old_path}; + } + + + objects.set(value, path); + + if (Array.isArray(value)) { + nu = []; + value.forEach(function (element, i) { + nu[i] = derez(element, path + "[" + i + "]"); + }); + } else { + + nu = {}; + Object.keys(value).forEach(function (name) { + nu[name] = derez( + value[name], + path + "[" + JSON.stringify(name) + "]" + ); + }); + } + return nu; + } + return value; + }(object, "$")); + }; +} + + +if (typeof JSON.retrocycle !== "function") { + JSON.retrocycle = function retrocycle($) { + "use strict"; + + var px = /^\$(?:\[(?:\d+|"(?:[^\\"\u0000-\u001f]|\\(?:[\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*")\])*$/; + + (function rez(value) { + + + if (value && typeof value === "object") { + if (Array.isArray(value)) { + value.forEach(function (element, i) { + if (typeof element === "object" && element !== null) { + var path = element.$ref; + if (typeof path === "string" && px.test(path)) { + value[i] = eval(path); + } else { + rez(element); + } + } + }); + } else { + Object.keys(value).forEach(function (name) { + var item = value[name]; + if (typeof item === "object" && item !== null) { + var path = item.$ref; + if (typeof path === "string" && px.test(path)) { + value[name] = eval(path); + } else { + rez(item); + } + } + }); + } + } + }($)); + return $; + }; +} diff --git a/app/assets/dash_v1.js b/app/assets/dash_v1.js index e5daf25..665d8e3 100644 --- a/app/assets/dash_v1.js +++ b/app/assets/dash_v1.js @@ -3,6 +3,7 @@ function output(inp) { } function syntaxHighlight(json) { + console.log(json) json = JSON.stringify(JSON.parse(json.replace(/"/g, '"')), undefined, 4) json = json.replace(/&/g, '&').replace(//g, '>'); return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { diff --git a/app/controllers/dash/v1.controller.js b/app/controllers/dash/v1.controller.js index 6289a9b..dba814a 100644 --- a/app/controllers/dash/v1.controller.js +++ b/app/controllers/dash/v1.controller.js @@ -15,15 +15,26 @@ class v1 { const projects = await Project.find({ archived: false, user_id: req.session.auth.uuid }) - let find = { + let view_find = { shared_user_ids: { $elemMatch: { $eq: req.session.auth.uuid } } } + + let edit_find = { + edit_user_ids: { + $elemMatch: { + $eq: req.session.auth.uuid + } + } + } - const shared_projects = await Project.find(find) + const shared_projects = { + view: await Project.find(view_find), + edit: await Project.find(edit_find), + } /* * Return the main view. @@ -45,7 +56,7 @@ class v1 { } // check access perms - if ( !(project.user_id === req.session.auth.uuid) ){ + if ( !devbug.permission.project.edit(project, req.session.auth.user) ){ return _flitter.error(res, 401, {reason: 'You do not have permissions to edit this project.'}) } @@ -63,8 +74,8 @@ class v1 { } // check access perms - if ( !(project.user_id === req.session.auth.uuid) ){ - return _flitter.error(res, 401, {reason: 'Project not found with the specified ID.'}) + if ( !devbug.permission.project.edit(project, req.session.auth.user) ){ + return _flitter.error(res, 401, {reason: 'You do not have permissions to edit this project.'}) } project.name = req.body.name @@ -102,7 +113,7 @@ class v1 { const outs = await Out.find({ project_id: project.id }).sort('-created') - if ( !(project.user_id === req.session.auth.uuid) && !(project.shared_user_ids.includes(req.session.auth.uuid)) ){ + if ( !devbug.permission.project.view(project, req.session.auth.user) ){ return _flitter.error(res, 401, {reason: 'You do not have permission to view this project.'}) } @@ -119,6 +130,7 @@ class v1 { let pretty try { pretty = JSON.stringify(JSON.parse(out.data), null, 4) + console.log('Pretty out: ', pretty) } catch (e){ return _flitter.error(res, 500, {reason: 'Unable to parse output data. Data contains invalid JSON.'}) @@ -126,11 +138,10 @@ class v1 { const project = await Project.findById(out.project_id) - if ( !project || (!(project.user_id === req.session.auth.uuid) && !(project.shared_user_ids.includes(req.session.auth.uuid))) ){ + if ( !project || (!devbug.permission.project.view(project, req.session.auth.user)) ){ return _flitter.error(res, 401, {reason: 'You do not have permission to view this project.'}) } - // TODO permission access check return _flitter.view(res, 'dash_v1:out', {project, user: req.session.auth.user, out, prettyd:pretty, show_back: true, title: out.brief, title_small: true }); } @@ -138,7 +149,7 @@ class v1 { const out = await Out.findById(req.params.id) const project = await Project.findById(req.params.project) - if ( !project || ( !(project.user_id === req.session.auth.uuid) ) ){ + if ( !project || ( !devbug.permission.project.edit(project, req.session.auth.user) ) ){ return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'}) } @@ -155,7 +166,7 @@ class v1 { return _flitter.error(res, 404, {reason: 'Project not found with the specified ID.'}) } - if ( !(project.user_id === req.session.auth.uuid) ){ + if ( !devbug.permission.project.owns(project, req.session.auth.user) ){ return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'}) } @@ -165,7 +176,7 @@ class v1 { async project_delete_do(req, res, next){ const project = await Project.findById(req.params.id) - if ( project && ( !(project.user_id === req.session.auth.uuid) ) ){ + if ( project && ( !devbug.permission.project.owns(project, req.session.auth.user) ) ){ return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'}) } @@ -191,24 +202,44 @@ class v1 { if ( !project ) return _flitter.error(res, 404, {reason: 'Project not found with the specified ID.'}) - if ( !(project.user_id === req.session.auth.uuid) ) return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'}) + if ( !devbug.permission.project.owns(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'}) - let find = { - uuid: { $nin: [] } + // Find read-only users + const read_find = { + uuid: { $in: [] } } - - find.uuid.$nin.push(req.session.auth.uuid) - find.uuid.$nin = find.uuid.$nin.concat(project.shared_user_ids) - - const to_share = await _flitter.model('User').find(find) - - find = { - uuid: { $in: find.uuid.$nin } + read_find.uuid.$in = read_find.uuid.$in.concat(project.shared_user_ids) + + const read = await _flitter.model('User').find(read_find) + + // Find edit users + const edit_find = { + uuid: { $in : [] } + } + edit_find.uuid.$in = edit_find.uuid.$in.concat(project.edit_user_ids) + + const edit = await _flitter.model('User').find(edit_find) + + // Find other users + const other_find = { + uuid: { $nin: [ project.user_id ] } + } + other_find.uuid.$nin = other_find.uuid.$nin.concat(project.edit_user_ids).concat(project.shared_user_ids) + + const other = await _flitter.model('User').find(other_find) + + // Get the owner user + const owner = await _flitter.model('User').findOne({ uuid: project.user_id }) + + const sharing = { + read, + edit, + other, + owner, + current_owns: (project.user_id === req.session.auth.uuid) } - const shared = await _flitter.model('User').find(find) - - return _flitter.view(res, 'dash_v1:share', { user: req.session.auth.user, sharing: { to_share, shared }, project, title: 'Share Project: '+project.name, show_back: true }) + return _flitter.view(res, 'dash_v1:share', { user: req.session.auth.user, sharing, project, title: 'Share Project: '+project.name, show_back: true }) } async project_share_do(req, res, next){ @@ -218,7 +249,7 @@ class v1 { const target_user = await _flitter.model('User').findOne({uuid: req.params.user}) if ( !target_user ) return _flitter.error(res, 404, {reason: 'User not found with the specified ID.'}) - if ( !(project.user_id === req.session.auth.uuid) ) return _flitter.error(res, 401, {reason: "You do not have permission to edit this project."}) + if ( !devbug.permission.project.owns(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: "You do not have permission to edit this project."}) if ( !(project.user_id === target_user.uuid) && !(project.shared_user_ids.includes(target_user.uuid)) ){ project.shared_user_ids.push(target_user.uuid) @@ -227,6 +258,28 @@ class v1 { return res.redirect('/dash/v1/project/share/'+project.id) } + + async project_share_edit_do(req, res, next){ + const project = await Project.findById(req.params.id) + if ( !project ) return _flitter.error(res, 404, {reason: 'Project not found with the specified ID.'}) + + const target_user = await _flitter.model('User').findOne({uuid: req.params.user}) + if ( !target_user ) return _flitter.error(res, 404, {reason: 'User not found with the specified ID.'}) + + if ( !devbug.permission.project.owns(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: "You do not have permission to edit this project."}) + + if ( !(project.user_id === target_user.uuid) && !(project.edit_user_ids.includes(target_user.uuid)) ){ + // check if read access. If so, revoke. + if ( project.shared_user_ids.includes(target_user.uuid) ){ + project.shared_user_ids.splice(project.shared_user_ids.indexOf(target_user.uuid), 1) + } + + project.edit_user_ids.push(target_user.uuid) + await project.save() + } + + return res.redirect('/dash/v1/project/share/'+project.id) + } async project_share_revoke(req, res, next){ const project = await Project.findById(req.params.id) @@ -235,7 +288,7 @@ class v1 { const target_user = await _flitter.model('User').findOne({uuid: req.params.user}) if ( !target_user ) return _flitter.error(res, 404, {reason: 'User not found with the specified ID.'}) - if ( !(project.user_id === req.session.auth.uuid || project.shared_user_ids.includes(req.session.auth.uuid)) ) return _flitter.error(res, 401, {reason: "You do not have permission to edit this project."}) + if ( !devbug.permission.project.view(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: "You do not have permission to edit this project."}) const to_dash = project.shared_user_ids.includes(req.session.auth.uuid) @@ -248,6 +301,27 @@ class v1 { return res.redirect('/dash/v1/project/share/'+project.id) } + + async project_share_revoke_edit(req, res, next){ + const project = await Project.findById(req.params.id) + if ( !project ) return _flitter.error(res, 404, {reason: 'Project not found with the specified ID.'}) + + const target_user = await _flitter.model('User').findOne({uuid: req.params.user}) + if ( !target_user ) return _flitter.error(res, 404, {reason: 'User not found with the specified ID.'}) + + if ( !devbug.permission.project.view(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: "You do not have permission to edit this project."}) + + const to_dash = project.edit_user_ids.includes(req.session.auth.uuid) + + if ( !(target_user.uuid === project.user_id) && (project.edit_user_ids.includes(target_user.uuid)) ){ + project.edit_user_ids.splice(project.edit_user_ids.indexOf(target_user.uuid), 1) + await project.save() + } + + if ( to_dash ) return res.redirect('/dash/v1') + + return res.redirect('/dash/v1/project/share/'+project.id) + } async project_share_transfer(req, res, next){ const project = await Project.findById(req.params.id) @@ -256,14 +330,18 @@ class v1 { const target_user = await _flitter.model('User').findOne({uuid: req.params.user}) if ( !target_user ) return _flitter.error(res, 404, {reason: 'User not found with the specified ID.'}) - if ( !project.user_id === req.session.auth.uuid ) return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'}) + if ( !devbug.permission.project.owns(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'}) project.user_id = target_user.uuid project.shared_user_ids.push(req.session.auth.uuid) - if ( project.shared_user_ids.includes(req.session.auth.uuid) ){ + if ( project.shared_user_ids.includes(target_user.uuid) ){ project.shared_user_ids.splice(project.shared_user_ids.indexOf(target_user.uuid), 1) } + + if ( project.edit_user_ids.includes(target_user.uuid) ){ + project.edit_user_ids.splice(project.edit_user_ids.indexOf(target_user.uuid), 1) + } await project.save() @@ -274,7 +352,7 @@ class v1 { const project = await Project.findById(req.params.id) if ( !project ) return _flitter.error(res, 404, {reason: 'Project not found with the specified ID.'}) - if ( !project.user_id === req.session.auth.uuid ) return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'}) + if ( !devbug.permission.project.owns(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'}) let share_data = { project_id: project.id, diff --git a/app/models/v1/Project.model.js b/app/models/v1/Project.model.js index 64f4347..2a1458b 100644 --- a/app/models/v1/Project.model.js +++ b/app/models/v1/Project.model.js @@ -10,6 +10,7 @@ const Project = { archived: { type: Boolean, default: false }, data: String, shared_user_ids: [String], + edit_user_ids: [String], uuid: { type: String, default: uuid } } diff --git a/app/routing/routers/dash/v1.routes.js b/app/routing/routers/dash/v1.routes.js index 5ee7752..5eed586 100644 --- a/app/routing/routers/dash/v1.routes.js +++ b/app/routing/routers/dash/v1.routes.js @@ -43,7 +43,9 @@ const v1 = { '/project/edit/:id': [ _flitter.controller('dash:v1').project_edit_show ], '/project/share/:id': [ _flitter.controller('dash:v1').project_share_show ], '/project/share/:id/share/:user': [ _flitter.controller('dash:v1').project_share_do ], + '/project/share/:id/share/:user/edit': [ _flitter.controller('dash:v1').project_share_edit_do ], '/project/share/:id/revoke/:user': [ _flitter.controller('dash:v1').project_share_revoke ], + '/project/share/:id/revoke/:user/edit': [ _flitter.controller('dash:v1').project_share_revoke_edit ], '/project/share/:id/transfer/:user': [ _flitter.controller('dash:v1').project_share_transfer ], '/project/share/:id/invite': [ _flitter.controller('dash:v1').project_share_invite ], diff --git a/app/views/dash_v1/code.pug b/app/views/dash_v1/code.pug index 32cb458..43180a8 100644 --- a/app/views/dash_v1/code.pug +++ b/app/views/dash_v1/code.pug @@ -17,11 +17,8 @@ block content | function. pre code #{devbug.code.php} - h2 JavaScript (Web) - p - | This snippet works by loading jQuery via a script tag when a breakpoint is triggered. - pre - code #{devbug.code.js} + h2 + a(href="/assets/agents/web.js" target="_blank") JavaScript (Web) h2 Using the API p You can post output to DevBug projects from anywhere using the DevBug API. Here's how: pre diff --git a/app/views/dash_v1/main.pug b/app/views/dash_v1/main.pug index 30a26dd..5e1132d 100644 --- a/app/views/dash_v1/main.pug +++ b/app/views/dash_v1/main.pug @@ -24,7 +24,7 @@ block content li a.action(href='/dash/v1/project/edit/'+project.id) Edit - if shared_projects + if shared_projects.view || shared_projects.edit h3 Projects Shared With Me table thead @@ -32,7 +32,18 @@ block content th(scope='col' style='min-width: 250px') Name th(scope='col') Actions tbody - each project in shared_projects + each project in shared_projects.edit + tr + td #{project.name} + td + ul(style='list-style-type: none; margin: 0; padding: 0;') + li + a.action(href='/dash/v1/project/view/'+project.id) View + li + a.action(href='/dash/v1/project/share/'+project.id+'/revoke/'+user.uuid+'/edit') Remove + li + a.action(href='/dash/v1/project/edit/'+project.id) Edit + each project in shared_projects.view tr td #{project.name} td diff --git a/app/views/dash_v1/share.pug b/app/views/dash_v1/share.pug index a54821f..ac00146 100644 --- a/app/views/dash_v1/share.pug +++ b/app/views/dash_v1/share.pug @@ -1,48 +1,88 @@ extends ./template block content - h2 Shared With + h2 Owned By table thead tr th(scope='col' style='min-width: 250px') Username - th(scope='col') Actions tbody - each user in sharing.shared + tr + td #{(sharing.current_owns ? `You (${sharing.owner.username})` : sharing.owner.username)} + + if sharing.read.length > 0 + br + h2 Shared With (Read-Only) + table + thead tr - td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)} - td - ul(style='list-style-type: none; margin: 0; padding: 0;') - if !(user.uuid === project.user_id) - li - a.action(href='/dash/v1/project/share/'+project.id+'/revoke/'+user.uuid) Revoke - li - a.action(href='/dash/v1/project/share/'+project.id+'/transfer/'+user.uuid) Transfer Ownership - else - li - strike Revoke - li - strike Transfer Ownership + th(scope='col' style='min-width: 250px') Username + th(scope='col') Actions + tbody + each user in sharing.read + tr + td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)} + td + ul(style='list-style-type: none; margin: 0; padding: 0;') + if !(user.uuid === project.user_id) + li + a.action(href='/dash/v1/project/share/'+project.id+'/revoke/'+user.uuid) Revoke + li + a.action(href='/dash/v1/project/share/'+project.id+'/transfer/'+user.uuid) Transfer Ownership + else + li + strike Revoke + li + strike Transfer Ownership + + if sharing.edit.length > 0 + br + h2 Shared With (Edit) + table + thead + tr + th(scope='col' style='min-width: 250px') Username + th(scope='col') Actions + tbody + each user in sharing.edit + tr + td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)} + td + ul(style='list-style-type: none; margin: 0; padding: 0;') + if !(user.uuid === project.user_id) + li + a.action(href='/dash/v1/project/share/'+project.id+'/revoke/'+user.uuid+'/edit') Revoke + li + a.action(href='/dash/v1/project/share/'+project.id+'/transfer/'+user.uuid) Transfer Ownership + else + li + strike Revoke + li + strike Transfer Ownership br h2 Share With New User a.btn(href='/dash/v1/project/share/'+project.id+'/invite') Generate Sharing Link - br - br - table - thead - tr - th(scope='col' style='min-width: 250px') Username - th(scope='col') Actions - tbody - each user in sharing.to_share + + if sharing.other.length > 0 + br + br + table + thead tr - td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)} - td - ul(style='list-style-type: none; margin: 0; padding: 0;') - if !(user.uuid === project.user_id) - li - a.action(href='/dash/v1/project/share/' + project.id + '/share/'+user.uuid) Share - else + th(scope='col' style='min-width: 250px') Username + th(scope='col') Actions + tbody + each user in sharing.other + tr + td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)} + td + ul(style='list-style-type: none; margin: 0; padding: 0;') + if !(user.uuid === project.user_id) + li + a.action(href='/dash/v1/project/share/' + project.id + '/share/'+user.uuid) Share (View) + li + a.action(href='/dash/v1/project/share/' + project.id + '/share/'+user.uuid+'/edit') Share (Edit) + else + li + strike Share li - strike Share - li - a.action(href='/dash/v1/project/share/' + project.id + '/transfer/' + user.uuid) Transfer Ownership + a.action(href='/dash/v1/project/share/' + project.id + '/transfer/' + user.uuid) Transfer Ownership diff --git a/app/views/dash_v1/view.pug b/app/views/dash_v1/view.pug index 3a60f3b..958951c 100644 --- a/app/views/dash_v1/view.pug +++ b/app/views/dash_v1/view.pug @@ -16,5 +16,6 @@ block content ul(style='list-style-type: none; margin: 0; padding: 0;') li a.action(href='/dash/v1/out/view/'+out.id) View - li - a.action(href='/dash/v1/out/delete/'+out.id+'/'+project.id) Delete + if ( devbug.permission.project.edit(project, user) ) + li + a.action(href='/dash/v1/out/delete/'+out.id+'/'+project.id) Delete