sharing and UX improvements
This commit is contained in:
parent
1bd6ad1830
commit
2a80e65c35
@ -34,6 +34,66 @@ class MiscUnit extends Unit {
|
||||
async go(app, context){
|
||||
|
||||
// do stuff here
|
||||
global.devbug = {
|
||||
version: '0.2.0',
|
||||
code: {
|
||||
php: `<?php
|
||||
// ===========================================================
|
||||
// DEVBUG INLINE DEBUGGING HELPER - FOR USE WITH DEVBUG SERVER
|
||||
// TODO: REMOVE BEFORE COMMITTING
|
||||
$dev_outs = [];
|
||||
function out($key, $what, $group = null){
|
||||
\tglobal $dev_outs;
|
||||
\tif ( $group ){
|
||||
\t\tif ( !array_key_exists($group, $dev_outs) ){
|
||||
\t\t\t$dev_outs[$group] = [ $key => $what ];
|
||||
\t\t}
|
||||
\t\telse {
|
||||
\t\t\t$dev_outs[$group][$key] = $what;
|
||||
\t\t}
|
||||
\t}
|
||||
\telse {
|
||||
\t\t$dev_outs[$key] = $what;
|
||||
\t}
|
||||
}
|
||||
function breakpoint($html = false, $name = null){
|
||||
\tglobal $dev_outs;
|
||||
\t$devbug = "http://CHANGEME:8000/";
|
||||
\t$project_api_key = "CHANGEME";
|
||||
\t$bt = debug_backtrace();
|
||||
\t$caller = array_shift($bt);
|
||||
|
||||
\tif ( !$html ){
|
||||
\t\tvar_dump([($item ? $item : $caller), 'outs' => $dev_outs]);
|
||||
\t}
|
||||
\telse {
|
||||
\t\techo "<pre><code>";
|
||||
\t\tvar_dump([($item ? $item : $caller), 'outs' => $dev_outs]);
|
||||
\t\techo "</code></pre>";
|
||||
\t}
|
||||
|
||||
\t// Send to devbug server
|
||||
\t$ch = curl_init();
|
||||
\t$url = $devbug.'api/v1/out/'.$project_api_key;
|
||||
\tvar_dump($url);
|
||||
\tcurl_setopt($ch, CURLOPT_URL, $url);
|
||||
\tcurl_setopt($ch, CURLOPT_POST, 1);
|
||||
\tcurl_setopt($ch, CURLOPT_POSTFIELDS, [
|
||||
\t 'data' => json_encode([
|
||||
\t 'brief' => ($name ? $name : 'Breakpoint').': '.$caller['file'].': '.$caller['line'],
|
||||
\t 'data' => $dev_outs,
|
||||
\t ])
|
||||
\t]);
|
||||
|
||||
\tcurl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
\t
|
||||
\t$odata = curl_exec($ch);
|
||||
|
||||
\texit();
|
||||
}
|
||||
// ===========================================================`
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
@import url('https://fonts.googleapis.com/css?family=Source+Code+Pro');
|
||||
|
||||
html {
|
||||
font-family: "Source Sans Pro";
|
||||
font-family: "Source Sans Pro", sans-serif;
|
||||
}
|
||||
|
||||
table, th, td {
|
||||
@ -17,7 +17,7 @@ th, td {
|
||||
}
|
||||
|
||||
pre, code {
|
||||
font-family: "Source Code Pro";
|
||||
font-family: "Source Code Pro", monospace;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
@ -25,11 +25,44 @@ a {
|
||||
color: #004d4d;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
background: #ccdddd;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.devbug-header {
|
||||
background: #509d9d;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
padding-left: 20px;
|
||||
margin-left: -20px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
min-height: 165px;
|
||||
}
|
||||
|
||||
.navul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 5;
|
||||
margin-bottom: 20px;
|
||||
padding: 0;
|
||||
padding-left: 20px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin-left: -20px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background: #004d4d;
|
||||
}
|
||||
|
||||
.navli {
|
||||
@ -41,7 +74,7 @@ a {
|
||||
}
|
||||
|
||||
.navli:hover {
|
||||
background: #004d4d;
|
||||
background: #509d9d;
|
||||
}
|
||||
|
||||
.nava {
|
||||
@ -53,3 +86,10 @@ a {
|
||||
.nava:hover {
|
||||
color: #eee;
|
||||
}
|
||||
|
||||
pre {outline: 1px solid #ccc; padding: 5px; margin: 5px; }
|
||||
.string { color: darkslateblue; }
|
||||
.number { color: darkorange; }
|
||||
.boolean { color: blue; }
|
||||
.null { color: magenta; }
|
||||
.key { color: green; }
|
||||
|
29
app/assets/dash_v1.js
Normal file
29
app/assets/dash_v1.js
Normal file
@ -0,0 +1,29 @@
|
||||
function output(inp) {
|
||||
document.body.appendChild(document.createElement('pre')).innerHTML = inp;
|
||||
}
|
||||
|
||||
function syntaxHighlight(json) {
|
||||
json = JSON.stringify(JSON.parse(json.replace(/"/g, '"')), undefined, 4)
|
||||
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) {
|
||||
var cls = 'number';
|
||||
if (/^"/.test(match)) {
|
||||
if (/:$/.test(match)) {
|
||||
cls = 'key';
|
||||
} else {
|
||||
cls = 'string';
|
||||
}
|
||||
} else if (/true|false/.test(match)) {
|
||||
cls = 'boolean';
|
||||
} else if (/null/.test(match)) {
|
||||
cls = 'null';
|
||||
}
|
||||
return '<span class="' + cls + '">' + match + '</span>';
|
||||
});
|
||||
}
|
||||
|
||||
function from_server(json_string){
|
||||
console.log(json_string)
|
||||
json_string = JSON.parse(json_string.replace(/"/g, '"'))
|
||||
window.devbug = json_string
|
||||
}
|
@ -39,7 +39,20 @@ class v1 {
|
||||
})
|
||||
}
|
||||
|
||||
const data = JSON.parse(req.body.data)
|
||||
let data
|
||||
try {
|
||||
data = JSON.parse(req.body.data)
|
||||
}
|
||||
catch (e){
|
||||
return res.status(400).send({
|
||||
success: false,
|
||||
error: 'Invalid JSON.'
|
||||
})
|
||||
}
|
||||
|
||||
if ( !data.brief ){
|
||||
data.brief = 'Breakpoint. (No Message.)'
|
||||
}
|
||||
|
||||
const out = new Out({
|
||||
brief: data.brief,
|
||||
|
@ -14,13 +14,23 @@ class v1 {
|
||||
|
||||
const projects = await Project.find({ archived: false, user_id: req.session.auth.uuid })
|
||||
|
||||
let find = {
|
||||
shared_user_ids: {
|
||||
$elemMatch: {
|
||||
$eq: req.session.auth.uuid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const shared_projects = await Project.find(find)
|
||||
|
||||
/*
|
||||
* Return the main view.
|
||||
* It must be passed the response.
|
||||
* View parameters can be passed as an optional third
|
||||
* argument to the view() method.
|
||||
*/
|
||||
return _flitter.view(res, 'dash_v1:main', { projects })
|
||||
return _flitter.view(res, 'dash_v1:main', { projects, shared_projects, user: req.session.auth.user })
|
||||
}
|
||||
|
||||
new_project_show(req, res, next){
|
||||
@ -30,20 +40,30 @@ class v1 {
|
||||
async project_edit_show(req, res, next){
|
||||
const project = await Project.findById(req.params.id)
|
||||
if ( !project ){
|
||||
return _flitter.error(res, 404, 'Project not found with the specified ID.')
|
||||
return _flitter.error(res, 404, {reason: 'Project not found with the specified ID.'})
|
||||
}
|
||||
|
||||
return _flitter.view(res, 'dash_v1:project', { show_back: true, title: 'Update Project', project_name: project.name})
|
||||
// check access perms
|
||||
if ( !(project.user_id === req.session.auth.uuid) ){
|
||||
return _flitter.error(res, 401, {reason: 'You do not have permissions to edit this project.'})
|
||||
}
|
||||
|
||||
return _flitter.view(res, 'dash_v1:project', { show_back: true, title: 'Update Project', project_name: project.name, user: req.session.auth.user })
|
||||
}
|
||||
|
||||
async project_edit_do(req, res, next){
|
||||
const project = await Project.findById(req.params.id)
|
||||
if ( !project ){
|
||||
return _flitter.error(res, 404, 'Project not found with the specified ID.')
|
||||
return _flitter.error(res, 404, {reason: 'Project not found with the specified ID.'})
|
||||
}
|
||||
|
||||
if ( !req.body || !req.body.name ){
|
||||
return _flitter.view(res, 'dash_v1:project', {show_back: true, title: 'Update Project', project_name: project.name, errors: ['Project name is required.']})
|
||||
return _flitter.view(res, 'dash_v1:project', {user: req.session.auth.user, show_back: true, title: 'Update Project', project_name: project.name, errors: ['Project name is required.']})
|
||||
}
|
||||
|
||||
// check access perms
|
||||
if ( !(project.user_id === req.session.auth.uuid) ){
|
||||
return _flitter.error(res, 401, {reason: 'Project not found with the specified ID.'})
|
||||
}
|
||||
|
||||
project.name = req.body.name
|
||||
@ -54,7 +74,7 @@ class v1 {
|
||||
|
||||
async new_project_do(req, res, next){
|
||||
if ( !req.body.name ){
|
||||
return _flitter.view(res, 'dash_v1:project', {show_back: true, title: 'Create Project', errors: ['Project name is required.']})
|
||||
return _flitter.view(res, 'dash_v1:project', {user: req.session.auth.user, show_back: true, title: 'Create Project', errors: ['Project name is required.']})
|
||||
}
|
||||
|
||||
const project = new Project({
|
||||
@ -76,31 +96,78 @@ class v1 {
|
||||
const project = await Project.findById(req.params.id)
|
||||
|
||||
if ( !project ){
|
||||
_flitter.error(res, 404, 'Project not found.')
|
||||
return _flitter.error(res, 404, {reason: 'Project not found with the specified ID.'})
|
||||
}
|
||||
|
||||
const outs = await Out.find({ project_id: project.id }).sort('-created')
|
||||
|
||||
return _flitter.view(res, 'dash_v1:view', { project, outs, show_back: true, title: 'View: '+project.name })
|
||||
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 view this project.'})
|
||||
}
|
||||
|
||||
return _flitter.view(res, 'dash_v1:view', {user: req.session.auth.user, project, outs, show_back: true, title: 'View: '+project.name })
|
||||
}
|
||||
|
||||
async out_view(req, res, next){
|
||||
const out = await Out.findById(req.params.id)
|
||||
|
||||
console.log(out.data)
|
||||
if ( !out ){
|
||||
return _flitter.error(res, 404, {reason: 'Output not found with the specified ID.'})
|
||||
}
|
||||
|
||||
const pretty = JSON.stringify(JSON.parse(out.data), null, 4)
|
||||
let pretty
|
||||
try {
|
||||
pretty = JSON.stringify(JSON.parse(out.data), null, 4)
|
||||
}
|
||||
catch (e){
|
||||
return _flitter.error(res, 500, {reason: 'Unable to parse output data. Data contains invalid JSON.'})
|
||||
}
|
||||
|
||||
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))) ){
|
||||
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', {out, prettyd:pretty, show_back: true, title: out.brief, title_small: true });
|
||||
return _flitter.view(res, 'dash_v1:out', {user: req.session.auth.user, out, prettyd:pretty, show_back: true, title: out.brief, title_small: true });
|
||||
}
|
||||
|
||||
project_delete_show(req, res, next){
|
||||
return _flitter.view(res, 'dash_v1:confirm', {show_back: true, title: 'Are you sure?', text: 'Deleting this project will remove all stored breakpoint data. This action cannot be undone.', destination: '/dash/v1/project/delete/'+req.params.id})
|
||||
async out_delete(req, res, next){
|
||||
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) ) ){
|
||||
return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'})
|
||||
}
|
||||
|
||||
if ( out ){
|
||||
await out.delete()
|
||||
}
|
||||
|
||||
return res.redirect('/dash/v1/project/view/'+req.params.project)
|
||||
}
|
||||
|
||||
async project_delete_show(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.'})
|
||||
}
|
||||
|
||||
if ( !(project.user_id === req.session.auth.uuid) ){
|
||||
return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'})
|
||||
}
|
||||
|
||||
return _flitter.view(res, 'dash_v1:confirm', {user: req.session.auth.user, project, show_back: true, title: 'Are you sure?', text: 'Deleting this project will remove all stored breakpoint data. This action cannot be undone.', destination: '/dash/v1/project/delete/'+req.params.id})
|
||||
}
|
||||
|
||||
async project_delete_do(req, res, next){
|
||||
const project = await Project.findById(req.params.id)
|
||||
|
||||
if ( project && ( !(project.user_id === req.session.auth.uuid) ) ){
|
||||
return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'})
|
||||
}
|
||||
|
||||
if ( project ){
|
||||
const outs = await Out.find({project_id: project.id})
|
||||
|
||||
@ -113,6 +180,90 @@ class v1 {
|
||||
|
||||
return res.redirect('/dash/v1')
|
||||
}
|
||||
|
||||
view_code(req, res, next){
|
||||
return _flitter.view(res, 'dash_v1:code', { user: req.session.auth.user, title: 'Inline Code Snippets' })
|
||||
}
|
||||
|
||||
async project_share_show(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.'})
|
||||
|
||||
if ( !(project.user_id === req.session.auth.uuid) ) return _flitter.error(res, 401, {reason: 'You do not have permission to edit this project.'})
|
||||
|
||||
let find = {
|
||||
uuid: { $nin: [] }
|
||||
}
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
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){
|
||||
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 ( !(project.user_id === req.session.auth.uuid) ) 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)
|
||||
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)
|
||||
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 ( !(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."})
|
||||
|
||||
const to_dash = project.shared_user_ids.includes(req.session.auth.uuid)
|
||||
|
||||
if ( !(target_user.uuid === project.user_id) && (project.shared_user_ids.includes(target_user.uuid)) ){
|
||||
project.shared_user_ids.splice(project.shared_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)
|
||||
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 ( !project.user_id === req.session.auth.uuid ) 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)
|
||||
|
||||
await project.save()
|
||||
|
||||
return res.redirect('/dash/v1')
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = v1
|
@ -34,15 +34,21 @@ const v1 = {
|
||||
* controller() calls get methods in Flitter controllers
|
||||
*/
|
||||
get: {
|
||||
// '/': [ controller('Controller_Name').handler_name ],
|
||||
'/': [ _flitter.controller('dash:v1').main ],
|
||||
|
||||
'/project/new': [ _flitter.controller('dash:v1').new_project_show ],
|
||||
'/project/view/:id': [ _flitter.controller('dash:v1').project_view ],
|
||||
'/project/delete/:id': [ _flitter.controller('dash:v1').project_delete_show ],
|
||||
'/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/revoke/:user': [ _flitter.controller('dash:v1').project_share_revoke ],
|
||||
'/project/share/:id/transfer/:user': [ _flitter.controller('dash:v1').project_share_transfer ],
|
||||
|
||||
'/out/view/:id': [ _flitter.controller('dash:v1').out_view ],
|
||||
'/out/delete/:id/:project': [ _flitter.controller('dash:v1').out_delete ],
|
||||
|
||||
'/code': [ _flitter.controller('dash:v1').view_code ],
|
||||
},
|
||||
|
||||
/*
|
||||
|
15
app/views/dash_v1/code.pug
Normal file
15
app/views/dash_v1/code.pug
Normal file
@ -0,0 +1,15 @@
|
||||
extends ./template
|
||||
block content
|
||||
p
|
||||
| These code snippets are designed to be included in-line. You can store outputs using the
|
||||
code out()
|
||||
| function. Then, call the
|
||||
code breakpoint()
|
||||
| function to send those outputs to DevBug.
|
||||
p
|
||||
| You'll need to ensure that the DevBug server URL and Project API Key are correct. These are local variables in the
|
||||
code breakpoint()
|
||||
| function.
|
||||
h2 PHP
|
||||
pre
|
||||
code #{devbug.code.php}
|
@ -17,7 +17,27 @@ block content
|
||||
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) Share
|
||||
li
|
||||
a.action(href='/dash/v1/project/delete/'+project.id) Delete
|
||||
li
|
||||
a.action(href='/dash/v1/project/edit/'+project.id) Edit
|
||||
|
||||
if shared_projects
|
||||
h3 Projects Shared With Me
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th(scope='col' style='min-width: 250px') Name
|
||||
th(scope='col') Actions
|
||||
tbody
|
||||
each project in shared_projects
|
||||
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) Remove
|
||||
|
@ -1,5 +1,4 @@
|
||||
extends ./template
|
||||
block content
|
||||
pre
|
||||
code
|
||||
div #{prettyd}
|
||||
script(src='/assets/dash_v1.js')
|
||||
script output(syntaxHighlight(`#{prettyd}`));
|
||||
|
45
app/views/dash_v1/share.pug
Normal file
45
app/views/dash_v1/share.pug
Normal file
@ -0,0 +1,45 @@
|
||||
extends ./template
|
||||
block content
|
||||
h2 Shared With
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th(scope='col' style='min-width: 250px') Username
|
||||
th(scope='col') Actions
|
||||
tbody
|
||||
each user in sharing.shared
|
||||
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
|
||||
br
|
||||
h2 Share With New User
|
||||
table
|
||||
thead
|
||||
tr
|
||||
th(scope='col' style='min-width: 250px') Username
|
||||
th(scope='col') Actions
|
||||
tbody
|
||||
each user in sharing.to_share
|
||||
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
|
||||
li
|
||||
strike Share
|
||||
li
|
||||
a.action(href='/dash/v1/project/share/' + project.id + '/transfer/' + user.uuid) Transfer Ownership
|
@ -2,17 +2,23 @@ html
|
||||
head
|
||||
title #{(title ? title+' | DevBug' : 'DevBug Dashboard')}
|
||||
link(rel='stylesheet' href='/assets/dash_v1.css')
|
||||
script(src='/assets/dash_v1.js')
|
||||
body
|
||||
if title_small
|
||||
h3 #{(title ? title+' | DevBug' : 'DevBug Dashboard')}
|
||||
else
|
||||
h1 #{(title ? title+' | DevBug' : 'DevBug Dashboard')}
|
||||
ul.navul
|
||||
li.navli
|
||||
a.nava(href='/dash/v1') Home
|
||||
li.navli
|
||||
a.nava(href='/auth/logout') Logout
|
||||
if show_back
|
||||
.page-header
|
||||
.devbug-header DevBug | v#{devbug.version} #{(user ? " | User: "+user.username : "")} #{(project ? " | Project: "+project.name+" | API: "+project.uuid : "")} #{((_flitter.config('server.environment') === 'development') ? " | Development" : "" )}
|
||||
if title_small
|
||||
h3 #{(title ? title : 'Dashboard')}
|
||||
else
|
||||
h1 #{(title ? title : 'Dashboard')}
|
||||
ul.navul
|
||||
li.navli
|
||||
a.nava(href='javascript:window.history.back()') Back
|
||||
a.nava(href='/dash/v1') Home
|
||||
li.navli
|
||||
a.nava(href='/dash/v1/code') Code Snippets
|
||||
li.navli
|
||||
a.nava(href='/auth/logout') Logout
|
||||
if show_back
|
||||
li.navli
|
||||
a.nava(href='javascript:window.history.back()') Back
|
||||
.spacer
|
||||
block content
|
@ -17,4 +17,4 @@ block content
|
||||
li
|
||||
a.action(href='/dash/v1/out/view/'+out.id) View
|
||||
li
|
||||
a.action(href='/dash/v1/out/delete/'+out.id) Delete
|
||||
a.action(href='/dash/v1/out/delete/'+out.id+'/'+project.id) Delete
|
||||
|
33
app/views/errors/401.pug
Normal file
33
app/views/errors/401.pug
Normal file
@ -0,0 +1,33 @@
|
||||
html
|
||||
head
|
||||
title Access Denied | Flitter
|
||||
style(type="text/css").
|
||||
@import url('https://fonts.googleapis.com/css?family=Rajdhani');
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
overflow-y: hidden;
|
||||
background-color: #c7dbdf;
|
||||
}
|
||||
|
||||
.flitter-container {
|
||||
height: 60%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flitter-image {
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
.flitter-name {
|
||||
font-family: "Rajdhani";
|
||||
font-size: 36pt;
|
||||
margin-left: 35px;
|
||||
color: #00323d;
|
||||
}
|
||||
body
|
||||
.flitter-container
|
||||
img.flitter-image(src="/assets/flitter.png")
|
||||
p.flitter-name Access Denied: #{(reason ? reason : "Resource Not Found.")}
|
@ -23,11 +23,11 @@ html
|
||||
|
||||
.flitter-name {
|
||||
font-family: "Rajdhani";
|
||||
font-size: 50pt;
|
||||
font-size: 36pt;
|
||||
margin-left: 35px;
|
||||
color: #00323d;
|
||||
}
|
||||
body
|
||||
.flitter-container
|
||||
img.flitter-image(src="/assets/flitter.png")
|
||||
p.flitter-name 404: Page Not Found
|
||||
p.flitter-name 404: #{(reason ? reason : "Resource Not Found.")}
|
||||
|
Loading…
Reference in New Issue
Block a user