cyclic structure resolution & edit sharing
This commit is contained in:
parent
2dafb07cea
commit
a078b768da
@ -152,9 +152,26 @@ const breakpoint = (html = false, name = null) => {
|
|||||||
}
|
}
|
||||||
if ( window ) window.out = out; window.breakpoint = breakpoint;
|
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(){
|
name(){
|
||||||
|
52
app/assets/agents/web.js
Normal file
52
app/assets/agents/web.js
Normal file
@ -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;
|
||||||
|
// ===========================================================
|
118
app/assets/cycle.js
Normal file
118
app/assets/cycle.js
Normal file
@ -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 $;
|
||||||
|
};
|
||||||
|
}
|
@ -3,6 +3,7 @@ function output(inp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function syntaxHighlight(json) {
|
function syntaxHighlight(json) {
|
||||||
|
console.log(json)
|
||||||
json = JSON.stringify(JSON.parse(json.replace(/"/g, '"')), undefined, 4)
|
json = JSON.stringify(JSON.parse(json.replace(/"/g, '"')), undefined, 4)
|
||||||
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
json = json.replace(/&/g, '&').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) {
|
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
|
||||||
|
@ -15,15 +15,26 @@ class v1 {
|
|||||||
|
|
||||||
const projects = await Project.find({ archived: false, user_id: req.session.auth.uuid })
|
const projects = await Project.find({ archived: false, user_id: req.session.auth.uuid })
|
||||||
|
|
||||||
let find = {
|
let view_find = {
|
||||||
shared_user_ids: {
|
shared_user_ids: {
|
||||||
$elemMatch: {
|
$elemMatch: {
|
||||||
$eq: req.session.auth.uuid
|
$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.
|
* Return the main view.
|
||||||
@ -45,7 +56,7 @@ class v1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check access perms
|
// 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.'})
|
return _flitter.error(res, 401, {reason: 'You do not have permissions to edit this project.'})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,8 +74,8 @@ class v1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check access perms
|
// 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: 'Project not found with the specified ID.'})
|
return _flitter.error(res, 401, {reason: 'You do not have permissions to edit this project.'})
|
||||||
}
|
}
|
||||||
|
|
||||||
project.name = req.body.name
|
project.name = req.body.name
|
||||||
@ -102,7 +113,7 @@ class v1 {
|
|||||||
|
|
||||||
const outs = await Out.find({ project_id: project.id }).sort('-created')
|
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.'})
|
return _flitter.error(res, 401, {reason: 'You do not have permission to view this project.'})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +130,7 @@ class v1 {
|
|||||||
let pretty
|
let pretty
|
||||||
try {
|
try {
|
||||||
pretty = JSON.stringify(JSON.parse(out.data), null, 4)
|
pretty = JSON.stringify(JSON.parse(out.data), null, 4)
|
||||||
|
console.log('Pretty out: ', pretty)
|
||||||
}
|
}
|
||||||
catch (e){
|
catch (e){
|
||||||
return _flitter.error(res, 500, {reason: 'Unable to parse output data. Data contains invalid JSON.'})
|
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)
|
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.'})
|
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 });
|
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 out = await Out.findById(req.params.id)
|
||||||
|
|
||||||
const project = await Project.findById(req.params.project)
|
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.'})
|
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.'})
|
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.'})
|
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){
|
async project_delete_do(req, res, next){
|
||||||
const project = await Project.findById(req.params.id)
|
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.'})
|
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 ) 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 = {
|
// Find read-only users
|
||||||
uuid: { $nin: [] }
|
const read_find = {
|
||||||
|
uuid: { $in: [] }
|
||||||
|
}
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
find.uuid.$nin.push(req.session.auth.uuid)
|
return _flitter.view(res, 'dash_v1:share', { user: req.session.auth.user, sharing, project, title: 'Share Project: '+project.name, show_back: true })
|
||||||
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 }
|
|
||||||
}
|
|
||||||
|
|
||||||
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 })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async project_share_do(req, res, next){
|
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})
|
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 ( !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)) ){
|
if ( !(project.user_id === target_user.uuid) && !(project.shared_user_ids.includes(target_user.uuid)) ){
|
||||||
project.shared_user_ids.push(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)
|
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){
|
async project_share_revoke(req, res, next){
|
||||||
const project = await Project.findById(req.params.id)
|
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})
|
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 ( !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)
|
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)
|
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){
|
async project_share_transfer(req, res, next){
|
||||||
const project = await Project.findById(req.params.id)
|
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})
|
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 ( !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.user_id = target_user.uuid
|
||||||
project.shared_user_ids.push(req.session.auth.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)
|
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()
|
await project.save()
|
||||||
|
|
||||||
@ -274,7 +352,7 @@ class v1 {
|
|||||||
const project = await Project.findById(req.params.id)
|
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 ) 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 = {
|
let share_data = {
|
||||||
project_id: project.id,
|
project_id: project.id,
|
||||||
|
@ -10,6 +10,7 @@ const Project = {
|
|||||||
archived: { type: Boolean, default: false },
|
archived: { type: Boolean, default: false },
|
||||||
data: String,
|
data: String,
|
||||||
shared_user_ids: [String],
|
shared_user_ids: [String],
|
||||||
|
edit_user_ids: [String],
|
||||||
uuid: { type: String, default: uuid }
|
uuid: { type: String, default: uuid }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,9 @@ const v1 = {
|
|||||||
'/project/edit/:id': [ _flitter.controller('dash:v1').project_edit_show ],
|
'/project/edit/:id': [ _flitter.controller('dash:v1').project_edit_show ],
|
||||||
'/project/share/:id': [ _flitter.controller('dash:v1').project_share_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': [ _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': [ _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/transfer/:user': [ _flitter.controller('dash:v1').project_share_transfer ],
|
||||||
'/project/share/:id/invite': [ _flitter.controller('dash:v1').project_share_invite ],
|
'/project/share/:id/invite': [ _flitter.controller('dash:v1').project_share_invite ],
|
||||||
|
|
||||||
|
@ -17,11 +17,8 @@ block content
|
|||||||
| function.
|
| function.
|
||||||
pre
|
pre
|
||||||
code #{devbug.code.php}
|
code #{devbug.code.php}
|
||||||
h2 JavaScript (Web)
|
h2
|
||||||
p
|
a(href="/assets/agents/web.js" target="_blank") JavaScript (Web)
|
||||||
| This snippet works by loading jQuery via a script tag when a breakpoint is triggered.
|
|
||||||
pre
|
|
||||||
code #{devbug.code.js}
|
|
||||||
h2 Using the API
|
h2 Using the API
|
||||||
p You can post output to DevBug projects from anywhere using the DevBug API. Here's how:
|
p You can post output to DevBug projects from anywhere using the DevBug API. Here's how:
|
||||||
pre
|
pre
|
||||||
|
@ -24,7 +24,7 @@ block content
|
|||||||
li
|
li
|
||||||
a.action(href='/dash/v1/project/edit/'+project.id) Edit
|
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
|
h3 Projects Shared With Me
|
||||||
table
|
table
|
||||||
thead
|
thead
|
||||||
@ -32,7 +32,18 @@ block content
|
|||||||
th(scope='col' style='min-width: 250px') Name
|
th(scope='col' style='min-width: 250px') Name
|
||||||
th(scope='col') Actions
|
th(scope='col') Actions
|
||||||
tbody
|
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
|
tr
|
||||||
td #{project.name}
|
td #{project.name}
|
||||||
td
|
td
|
||||||
|
@ -1,48 +1,88 @@
|
|||||||
extends ./template
|
extends ./template
|
||||||
block content
|
block content
|
||||||
h2 Shared With
|
h2 Owned By
|
||||||
table
|
table
|
||||||
thead
|
thead
|
||||||
tr
|
tr
|
||||||
th(scope='col' style='min-width: 250px') Username
|
th(scope='col' style='min-width: 250px') Username
|
||||||
th(scope='col') Actions
|
|
||||||
tbody
|
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
|
tr
|
||||||
td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)}
|
th(scope='col' style='min-width: 250px') Username
|
||||||
td
|
th(scope='col') Actions
|
||||||
ul(style='list-style-type: none; margin: 0; padding: 0;')
|
tbody
|
||||||
if !(user.uuid === project.user_id)
|
each user in sharing.read
|
||||||
li
|
tr
|
||||||
a.action(href='/dash/v1/project/share/'+project.id+'/revoke/'+user.uuid) Revoke
|
td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)}
|
||||||
li
|
td
|
||||||
a.action(href='/dash/v1/project/share/'+project.id+'/transfer/'+user.uuid) Transfer Ownership
|
ul(style='list-style-type: none; margin: 0; padding: 0;')
|
||||||
else
|
if !(user.uuid === project.user_id)
|
||||||
li
|
li
|
||||||
strike Revoke
|
a.action(href='/dash/v1/project/share/'+project.id+'/revoke/'+user.uuid) Revoke
|
||||||
li
|
li
|
||||||
strike Transfer Ownership
|
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
|
br
|
||||||
h2 Share With New User
|
h2 Share With New User
|
||||||
a.btn(href='/dash/v1/project/share/'+project.id+'/invite') Generate Sharing Link
|
a.btn(href='/dash/v1/project/share/'+project.id+'/invite') Generate Sharing Link
|
||||||
br
|
|
||||||
br
|
if sharing.other.length > 0
|
||||||
table
|
br
|
||||||
thead
|
br
|
||||||
tr
|
table
|
||||||
th(scope='col' style='min-width: 250px') Username
|
thead
|
||||||
th(scope='col') Actions
|
|
||||||
tbody
|
|
||||||
each user in sharing.to_share
|
|
||||||
tr
|
tr
|
||||||
td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)}
|
th(scope='col' style='min-width: 250px') Username
|
||||||
td
|
th(scope='col') Actions
|
||||||
ul(style='list-style-type: none; margin: 0; padding: 0;')
|
tbody
|
||||||
if !(user.uuid === project.user_id)
|
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
|
li
|
||||||
a.action(href='/dash/v1/project/share/' + project.id + '/share/'+user.uuid) Share
|
a.action(href='/dash/v1/project/share/' + project.id + '/transfer/' + user.uuid) Transfer Ownership
|
||||||
else
|
|
||||||
li
|
|
||||||
strike Share
|
|
||||||
li
|
|
||||||
a.action(href='/dash/v1/project/share/' + project.id + '/transfer/' + user.uuid) Transfer Ownership
|
|
||||||
|
@ -16,5 +16,6 @@ block content
|
|||||||
ul(style='list-style-type: none; margin: 0; padding: 0;')
|
ul(style='list-style-type: none; margin: 0; padding: 0;')
|
||||||
li
|
li
|
||||||
a.action(href='/dash/v1/out/view/'+out.id) View
|
a.action(href='/dash/v1/out/view/'+out.id) View
|
||||||
li
|
if ( devbug.permission.project.edit(project, user) )
|
||||||
a.action(href='/dash/v1/out/delete/'+out.id+'/'+project.id) Delete
|
li
|
||||||
|
a.action(href='/dash/v1/out/delete/'+out.id+'/'+project.id) Delete
|
||||||
|
Loading…
Reference in New Issue
Block a user