more snippet sharing

master
glmdev 5 years ago
parent ee1c2c2722
commit 5bff5407a5

@ -169,6 +169,21 @@ if ( window ) window.out = out; window.breakpoint = breakpoint;
owns(project, target_user){
return (project.user_id === target_user.uuid)
}
},
snippet: {
edit(snippet, target_user){
if ( snippet.user_id === target_user.uuid ) return true
else if ( snippet.edit_user_ids.includes(target_user.uuid) ) return true
else return false
},
view(snippet, target_user){
if ( this.edit(snippet, target_user) ) return true
else if ( project.shared_user_ids.includes(target_user.uuid) ) return true
else return false
},
owns(snippet, target_user){
return snippet.user_id === target_user.uuid
}
}
},
}

@ -7,6 +7,10 @@ const Project = _flitter.model('v1:Project')
const Out = _flitter.model('v1:Out')
const Invite = _flitter.model('v1:Invite')
const Snippet = _flitter.model('v1:Snippet')
const share_api = {
project: Project,
snippet: Snippet,
}
class v1 {
/*
@ -199,11 +203,13 @@ class v1 {
}
async project_share_show(req, res, next){
const project = await Project.findById(req.params.id)
const share_model = share_api[req.params.api]
if ( !share_model ) return _flitter.error(res, 400, {reason: 'Invalid Share API endpoint.'})
const project = await share_model.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: req.params.api+' 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 ( !devbug.permission.project.owns(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: 'You do not have permission to edit this '+req.params.api+'.'})
// Find read-only users
const read_find = {
@ -240,34 +246,38 @@ class v1 {
current_owns: (project.user_id === 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 })
return _flitter.view(res, 'dash_v1:share', { user: req.session.auth.user, sharing, item: project, api: req.params.api, title: 'Share '+req.params.api+': '+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 share_model = share_api[req.params.api]
if ( !share_model ) return _flitter.error(res, 400, {reason: 'Invalid Share API endpoint.'})
const project = await share_model.findById(req.params.id)
if ( !project ) return _flitter.error(res, 404, {reason: req.params.api+' 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 ( !devbug.permission.project.owns(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: "You do not have permission to edit this "+req.params.api+"."})
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)
return res.redirect('/dash/v1/'+req.params.api+'/share/'+project.id)
}
async project_share_edit_do(req, res, next){
const project = await Project.findById(req.params.id)
const share_model = share_api[req.params.api]
if ( !share_model ) return _flitter.error(res, 400, {reason: 'Invalid Share API endpoint.'})
const project = await share_model.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 ( !devbug.permission.project.owns(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: "You do not have permission to edit this "+req.params.api+"."})
if ( !(project.user_id === target_user.uuid) && !(project.edit_user_ids.includes(target_user.uuid)) ){
// check if read access. If so, revoke.
@ -279,17 +289,19 @@ class v1 {
await project.save()
}
return res.redirect('/dash/v1/project/share/'+project.id)
return res.redirect('/dash/v1/'+req.params.api+'/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 share_model = share_api[req.params.api]
if ( !share_model ) return _flitter.error(res, 400, {reason: 'Invalid Share API endpoint.'})
const project = await share_model.findById(req.params.id)
if ( !project ) return _flitter.error(res, 404, {reason: req.params.api+' 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."})
if ( !devbug.permission.project.view(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: "You do not have permission to edit this "+req.params.api+"."})
const to_dash = project.shared_user_ids.includes(req.session.auth.uuid)
@ -300,17 +312,19 @@ class v1 {
if ( to_dash ) return res.redirect('/dash/v1')
return res.redirect('/dash/v1/project/share/'+project.id)
return res.redirect('/dash/v1/'+req.params.api+'/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 share_model = share_api[req.params.api]
if ( !share_model ) return _flitter.error(res, 400, {reason: 'Invalid Share API endpoint.'})
const project = await share_model.findById(req.params.id)
if ( !project ) return _flitter.error(res, 404, {reason: req.params.api+' 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."})
if ( !devbug.permission.project.view(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: "You do not have permission to edit this "+req.params.api+"."})
const to_dash = project.edit_user_ids.includes(req.session.auth.uuid)
@ -321,17 +335,19 @@ class v1 {
if ( to_dash ) return res.redirect('/dash/v1')
return res.redirect('/dash/v1/project/share/'+project.id)
return res.redirect('/dash/v1/'+req.params.api+'/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 share_model = share_api[req.params.api]
if ( !share_model ) return _flitter.error(res, 400, {reason: 'Invalid Share API endpoint.'})
const project = await share_model.findById(req.params.id)
if ( !project ) return _flitter.error(res, 404, {reason: req.params.api+' 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 ( !devbug.permission.project.owns(project, req.session.auth.user) ) return _flitter.error(res, 401, {reason: 'You do not have permission to edit this '+req.params.api+'.'})
project.user_id = target_user.uuid
project.shared_user_ids.push(req.session.auth.uuid)
@ -404,21 +420,37 @@ class v1 {
if ( !project ) return _flitter.error(res, 404, {reason: 'The specified project does not exist.'})
if ( !req.body.title || !req.body.data ){
return _flitter.view(res, 'dash_v1:snippet', {project, user: req.session.auth.user, title: 'Create Snippet', show_back: true});
return _flitter.view(res, 'dash_v1:snippet', {project, user: req.session.auth.user, title: 'Create Snippet', show_back: true})
}
// check required fields: title, data, mode
let fail = false
if ( !req.body.title ) fail = 'Snippet title is required.'
else if ( !req.body.data ) fail = 'Snippet data is required.'
else if ( !req.body.mode ) fail = 'Snippet mode is required.'
if ( fail ){
return _flitter.view(res, 'dash_v1:snippet', {project, user: req.session.auth.user, title: 'Create Snippet', show_back: true, errors:[fail]})
}
const snippet_data = {
title: req.body.title,
name: req.body.title,
data: req.body.data,
mode: req.body.mode,
user_id: req.session.auth.uuid,
};
project_id: project.id,
}
const snippet = new Snippet(snippet_data);
await snippet.save();
console.log({snippet_data})
const snippet = new Snippet(snippet_data)
await snippet.save()
return res.redirect('/dash/v1/project/snippet/'+req.params.id+'/view/'+snippet.uuid)
}
// TODO access checks
async project_snippet_view(req, res, next){
const project = await Project.findById(req.params.id)
if ( !project ) return _flitter.error(res, 404, {reason: 'The specified project does not exist.'})
@ -426,7 +458,9 @@ class v1 {
const snippet = await Snippet.findOne({uuid: req.params.snippet})
if ( !snippet ) return _flitter.error(res, 404, {reason: 'The specified snippet does not exist.'})
return _flitter.view(res, 'dash_v1:snippet', {snippet, project, user: req.session.auth.user, title: snippet.title, show_back: true, readonly: true})
console.log('snippet mode', snippet.mode)
return _flitter.view(res, 'dash_v1:snippet', {snippet, project, user: req.session.auth.user, title: 'Snippet: '+snippet.name, show_back: true, readonly: true})
}
}

@ -5,7 +5,7 @@
*/
const uuid = require('uuid/v4')
const Snippet = {
title: String,
name: String,
user_id: String,
archived: { type: Boolean, default: false },
data: String,
@ -13,6 +13,8 @@ const Snippet = {
edit_user_ids: [String],
uuid: { type: String, default: uuid },
public_share: String,
mode: String,
project_id: String,
}
module.exports = exports = Snippet

@ -41,13 +41,13 @@ const v1 = {
'/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/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 ],
'/:api/share/:id': [ _flitter.controller('dash:v1').project_share_show ],
'/:api/share/:id/share/:user': [ _flitter.controller('dash:v1').project_share_do ],
'/:api/share/:id/share/:user/edit': [ _flitter.controller('dash:v1').project_share_edit_do ],
'/:api/share/:id/revoke/:user': [ _flitter.controller('dash:v1').project_share_revoke ],
'/:api/share/:id/revoke/:user/edit': [ _flitter.controller('dash:v1').project_share_revoke_edit ],
'/:api/share/:id/transfer/:user': [ _flitter.controller('dash:v1').project_share_transfer ],
'/:api/share/:id/invite': [ _flitter.controller('dash:v1').project_share_invite ],
'/project/snippet/:id/new': [ _flitter.controller('dash:v1').project_snippet_new ],
'/project/snippet/:id/view/:snippet': [ _flitter.controller('dash:v1').project_snippet_view ],

@ -20,14 +20,14 @@ block content
tbody
each user in sharing.read
tr
td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)}
td #{(user.uuid === item.user_id ? user.username + " (Owner)" : user.username)}
td
ul(style='list-style-type: none; margin: 0; padding: 0;')
if !(user.uuid === project.user_id)
if !(user.uuid === item.user_id)
li.action-li
a.action(href='/dash/v1/project/share/'+project.id+'/revoke/'+user.uuid) Revoke
a.action(href='/dash/v1/'+api+'/share/'+item.id+'/revoke/'+user.uuid) Revoke
li
a.action(href='/dash/v1/project/share/'+project.id+'/transfer/'+user.uuid) Transfer Ownership
a.action(href='/dash/v1/'+api+'/share/'+item.id+'/transfer/'+user.uuid) Transfer Ownership
else
li.action-li
strike Revoke
@ -45,14 +45,14 @@ block content
tbody
each user in sharing.edit
tr
td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)}
td #{(user.uuid === item.user_id ? user.username + " (Owner)" : user.username)}
td
ul(style='list-style-type: none; margin: 0; padding: 0;')
if !(user.uuid === project.user_id)
if !(user.uuid === item.user_id)
li.action-li
a.action(href='/dash/v1/project/share/'+project.id+'/revoke/'+user.uuid+'/edit') Revoke
a.action(href='/dash/v1/'+api+'/share/'+item.id+'/revoke/'+user.uuid+'/edit') Revoke
li
a.action(href='/dash/v1/project/share/'+project.id+'/transfer/'+user.uuid) Transfer Ownership
a.action(href='/dash/v1/'+api+'/share/'+item.id+'/transfer/'+user.uuid) Transfer Ownership
else
li.action-li
strike Revoke
@ -60,7 +60,7 @@ block content
strike Transfer Ownership
br
h2 Share With New User
a.btn(href='/dash/v1/project/share/'+project.id+'/invite') Generate Sharing Link
a.btn(href='/dash/v1/'+api+'/share/'+item.id+'/invite') Generate Sharing Link
if sharing.other.length > 0
br
@ -73,16 +73,16 @@ block content
tbody
each user in sharing.other
tr
td #{(user.uuid === project.user_id ? user.username + " (Owner)" : user.username)}
td #{(user.uuid === item.user_id ? user.username + " (Owner)" : user.username)}
td
ul(style='list-style-type: none; margin: 0; padding: 0;')
if !(user.uuid === project.user_id)
if !(user.uuid === item.user_id)
li.action-li
a.action(href='/dash/v1/project/share/' + project.id + '/share/'+user.uuid) Share (View)
a.action(href='/dash/v1/'+api+'/share/' + item.id + '/share/'+user.uuid) Share (View)
li.action-li
a.action(href='/dash/v1/project/share/' + project.id + '/share/'+user.uuid+'/edit') Share (Edit)
a.action(href='/dash/v1/'+api+'/share/' + item.id + '/share/'+user.uuid+'/edit') Share (Edit)
else
li.action-li
strike Share
li.action-li
a.action(href='/dash/v1/project/share/' + project.id + '/transfer/' + user.uuid) Transfer Ownership
a.action(href='/dash/v1/'+api+'/share/' + item.id + '/transfer/' + user.uuid) Transfer Ownership

@ -3,21 +3,103 @@ block content
if errors
each error in errors
p(style='color: red; font-weight: bold;') #{error}
if (!readonly)
form#snippet_form(method='post' enctype='multipart/form-data')
label(for='snippet_name') Snippet Title:
input#snippet_name(type='text' name='title' autofocus required)
input#snippet_name(type='text' name='title' placeholder='required' value=snippet ? snippet.name : '' autofocus required readonly=!!readonly)
label(for='snippet_mode' style='margin-left: 10px;') Language:
select#snippet_mode(name='mode' onchange='selectonchange()' required disabled=!!readonly)
option(value='ace/mode/c_cpp') C/C++
option(value='ace/mode/css') CSS
option(value='ace/mode/gitignore') .gitignore
option(value='ace/mode/go') Go
option(value='ace/mode/html') HTML
option(value='ace/mode/java') Java
option(value='ace/mode/javascript') JavaScript
option(value='ace/mode/json') JSON
option(value='ace/mode/mysql') MySQL
option(value='ace/mode/perl') Perl
option(value='ace/mode/pgsql') Postgres
option(value='ace/mode/php') PHP
option(value='ace/mode/python') Python
option(value='ace/mode/ruby') Ruby
option(value='ace/mode/rust') Rust
option(value='ace/mode/sql') SQL
option(value='ace/mode/text') Text
option(value='ace/mode/typescript') TypeScript
option(value='ace/mode/apache_conf') Apache Configuration
option(value='ace/mode/asl') ASL
option(value='ace/mode/assembly_x86') Assembly (x86)
option(value='ace/mode/autohotkey') AutoHotKey
option(value='ace/mode/crystal') Crystal
option(value='ace/mode/clojure') Clojure
option(value='ace/mode/cobol') COBOL
option(value='ace/mode/coffee') CoffeeScript
option(value='ace/mode/csharp') C#
option(value='ace/mode/d') D
option(value='ace/mode/django') django
option(value='ace/mode/dockerfile') Dockerfile
option(value='ace/mode/ejs') EJS
option(value='ace/mode/elixir') Elixir
option(value='ace/mode/fortran') Fortran
option(value='ace/mode/haml') HAML
option(value='ace/mode/handlebars') Handlebars
option(value='ace/mode/html_elixir') HTML (Elixir)
option(value='ace/mode/html_ruby') HTML (Ruby)
option(value='ace/mode/ini') INI
option(value='ace/mode/jade') Jade
option(value='ace/mode/julia') Julia
option(value='ace/mode/kotlin') Kotlin
option(value='ace/mode/latex') LaTeX
option(value='ace/mode/less') Less
option(value='ace/mode/lisp') Lisp
option(value='ace/mode/lua') Lua
option(value='ace/mode/makefile') Makefile
option(value='ace/mode/markdown') Markdown
option(value='ace/mode/matlab') MATLAB
option(value='ace/mode/nginx') NGINX
option(value='ace/mode/objectivec') Objective-C
option(value='ace/mode/pascal') Pascal
option(value='ace/mode/perl6') Perl 6
option(value='ace/mode/php_laravel_blade') PHP (blade template)
option(value='ace/mode/puppet') Puppet
option(value='ace/mode/powershell') PowerShell
option(value='ace/mode/r') R
option(value='ace/mode/sass') SASS
option(value='ace/mode/scala') Scala
option(value='ace/mode/scss') SCSS
option(value='ace/mode/sh') /bin/sh
option(value='ace/mode/slim') Slim
option(value='ace/mode/snippets') Snippets
option(value='ace/mode/sqlserver') SQL Server
option(value='ace/mode/svg') SVG
option(value='ace/mode/swift') Swift
option(value='ace/mode/tcl') TCL
option(value='ace/mode/tex') Tex
option(value='ace/mode/vbscript') VBScript
option(value='ace/mode/xml') XML
option(value='ace/mode/yaml') YAML
input#snippet_value(type='hidden' name='data' required)
if snippet && user.uuid === snippet.user_id
a.btn(href='/dash/v1/snippet/share/'+snippet.id style='margin-left: 20px') Share Snippet
pre#editor #{ snippet ? snippet.data : '' }
if (!readonly)
button(onclick='submitSnippet()') #{ snippet ? 'Update Snippet' : 'Create Snippet' }
input#preset_mode(type='hidden' value=snippet ? snippet.mode : 'ace/mode/javascript')
block scripts
script(src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.5/ace.js")
script.
const readonly = #{!!readonly}
const preset_mode = document.getElementById('preset_mode').value
const editor = ace.edit('editor');
editor.setTheme('ace/theme/cobalt');
//- editor.session.setMode('ace/mode/javascript');
console.log('preset mode: ', preset_mode);
editor.session.setMode(preset_mode);
document.getElementById('snippet_mode').value = preset_mode
if ( readonly ){
editor.setOption('readOnly', true)
}
function trim(s){
return ( s || '' ).replace( /^\s+|\s+$/g, '' );
@ -27,10 +109,14 @@ block scripts
const form = document.getElementById('snippet_form');
const value = document.getElementById('snippet_value');
const name = document.getElementById('snippet_name');
const mode = document.getElementById('snippet_mode');
value.value = editor.getValue();
if ( trim(value.value) && trim(name.value) ) form.submit();
if ( trim(value.value) && trim(name.value) && trim(mode.value) ) form.submit();
}
function selectonchange() {
const mode = document.getElementById('snippet_mode');
console.log('set mode: ', mode.value);
editor.session.setMode(mode.value);
}
if readonly
script.
editor.setOption('readOnly', true)
Loading…
Cancel
Save