disable permanent deletion of topics and synapses, except...

when the topic or synapse no longer appears on any maps, and this can currently only be performed over the api
This commit is contained in:
Connor Turland 2016-12-14 14:15:11 -05:00
parent 3b8a5d0c2e
commit d1680c1895
7 changed files with 23 additions and 195 deletions

View file

@ -38,7 +38,7 @@ class SynapsePolicy < ApplicationPolicy
end
def destroy?
record.user == user || admin_override
record.mappings.empty? && (record.user == user || admin_override)
end
# Helpers

View file

@ -36,7 +36,7 @@ class TopicPolicy < ApplicationPolicy
end
def destroy?
record.user == user || admin_override
record.mappings.empty? && (record.user == user || admin_override)
end
def autocomplete_topic?

View file

@ -39,76 +39,13 @@ const Control = {
Selected.Nodes.splice(
Selected.Nodes.indexOf(node), 1)
},
deleteSelected: function() {
if (!Active.Map) return
var n = Selected.Nodes.length
var e = Selected.Edges.length
var ntext = n === 1 ? '1 topic' : n + ' topics'
var etext = e === 1 ? '1 synapse' : e + ' synapses'
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
var r = window.confirm(outdent`
You have ${ntext} and ${etext} selected. Are you sure you want
to permanently delete them all? This will remove them from all
maps they appear on.`)
if (r) {
Control.deleteSelectedEdges()
Control.deleteSelectedNodes()
}
if (DataModel.Topics.length === 0) {
GlobalUI.showDiv('#instructions')
}
removeSelected: function() {
Control.removeSelectedEdges()
Control.removeSelectedNodes()
},
deleteSelectedNodes: function() { // refers to deleting topics permanently
if (!Active.Map) return
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
var l = Selected.Nodes.length
for (var i = l - 1; i >= 0; i -= 1) {
var node = Selected.Nodes[i]
Control.deleteNode(node.id)
}
},
deleteNode: function(nodeid) { // refers to deleting topics permanently
if (!Active.Map) return
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
var node = Visualize.mGraph.graph.getNode(nodeid)
var topic = node.getData('topic')
var permToDelete = Active.Mapper.id === topic.get('user_id') || Active.Mapper.get('admin')
if (permToDelete) {
var mappableid = topic.id
var mapping = node.getData('mapping')
topic.destroy()
DataModel.Mappings.remove(mapping)
$(document).trigger(JIT.events.deleteTopic, [{
mappableid: mappableid
}])
Control.hideNode(nodeid)
} else {
GlobalUI.notifyUser('Only topics you created can be deleted')
}
hideSelected: function() {
Control.hideSelectedEdges()
Control.hideSelectedNodes()
},
removeSelectedNodes: function() { // refers to removing topics permanently from a map
if (Active.Topic) {
@ -237,59 +174,6 @@ const Control = {
Selected.Edges.splice(
Selected.Edges.indexOf(edge), 1)
},
deleteSelectedEdges: function() { // refers to deleting topics permanently
if (!Active.Map) return
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
const l = Selected.Edges.length
for (let i = l - 1; i >= 0; i -= 1) {
const edge = Selected.Edges[i]
Control.deleteEdge(edge)
}
},
deleteEdge: function(edge) {
if (!Active.Map) return
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
GlobalUI.notifyUser('Cannot edit Public map.')
return
}
var index = edge.getData('displayIndex') ? edge.getData('displayIndex') : 0
var synapse = edge.getData('synapses')[index]
var mapping = edge.getData('mappings')[index]
var permToDelete = Active.Mapper.id === synapse.get('user_id') || Active.Mapper.get('admin')
if (permToDelete) {
if (edge.getData('synapses').length - 1 === 0) {
Control.hideEdge(edge)
}
var mappableid = synapse.id
synapse.destroy()
// the server will destroy the mapping, we just need to remove it here
DataModel.Mappings.remove(mapping)
edge.getData('mappings').splice(index, 1)
edge.getData('synapses').splice(index, 1)
if (edge.getData('displayIndex')) {
delete edge.data.$displayIndex
}
$(document).trigger(JIT.events.deleteSynapse, [{
mappableid: mappableid
}])
} else {
GlobalUI.notifyUser('Only synapses you created can be deleted')
}
},
removeSelectedEdges: function() {
// Topic view is handled by removeSelectedNodes
if (!Active.Map) return
@ -299,7 +183,7 @@ const Control = {
var authorized = Active.Map.authorizeToEdit(Active.Mapper)
if (!authorized) {
GlobalUI.notifyUser('Cannot edit Public map.')
GlobalUI.notifyUser('Cannot edit this map.')
return
}

View file

@ -37,10 +37,8 @@ const JIT = {
events: {
topicDrag: 'Metamaps:JIT:events:topicDrag',
newTopic: 'Metamaps:JIT:events:newTopic',
deleteTopic: 'Metamaps:JIT:events:deleteTopic',
removeTopic: 'Metamaps:JIT:events:removeTopic',
newSynapse: 'Metamaps:JIT:events:newSynapse',
deleteSynapse: 'Metamaps:JIT:events:deleteSynapse',
removeSynapse: 'Metamaps:JIT:events:removeSynapse',
pan: 'Metamaps:JIT:events:pan',
zoom: 'Metamaps:JIT:events:zoom',
@ -1396,9 +1394,8 @@ const JIT = {
const disabled = authorized ? '' : 'disabled'
if (Active.Map) menustring += '<li class="rc-hide"><div class="rc-icon"></div>Hide until refresh<div class="rc-keyboard">Ctrl+H</div></li>'
if (Active.Map && Active.Mapper) menustring += '<li class="rc-remove ' + disabled + '"><div class="rc-icon"></div>Remove from map<div class="rc-keyboard">Ctrl+M</div></li>'
if (Active.Topic) menustring += '<li class="rc-remove"><div class="rc-icon"></div>Remove from view<div class="rc-keyboard">Ctrl+M</div></li>'
if (Active.Map && Active.Mapper) menustring += '<li class="rc-delete ' + disabled + '"><div class="rc-icon"></div>Delete<div class="rc-keyboard">Ctrl+D</div></li>'
if (Active.Map && Active.Mapper) menustring += '<li class="rc-remove ' + disabled + '"><div class="rc-icon"></div>Remove<div class="rc-keyboard">Ctrl+D</div></li>'
if (Active.Topic) menustring += '<li class="rc-remove"><div class="rc-icon"></div>Remove from view<div class="rc-keyboard">Ctrl+D</div></li>'
if (Active.Topic) {
menustring += '<li class="rc-center"><div class="rc-icon"></div>Center this topic<div class="rc-keyboard">Alt+E</div></li>'
@ -1483,28 +1480,18 @@ const JIT = {
// attach events to clicks on the list items
// delete the selected things from the database
if (authorized) {
$('.rc-delete').click(function() {
$('.rightclickmenu').remove()
Control.deleteSelected()
})
}
// remove the selected things from the map
if (Active.Topic || authorized) {
$('.rc-remove').click(function() {
$('.rightclickmenu').remove()
Control.removeSelectedEdges()
Control.removeSelectedNodes()
Control.removeSelected()
})
}
// hide selected nodes and synapses until refresh
$('.rc-hide').click(function() {
$('.rightclickmenu').remove()
Control.hideSelectedEdges()
Control.hideSelectedNodes()
Control.hideSelected()
})
// when in radial, center on the topic you picked
@ -1654,9 +1641,8 @@ const JIT = {
const disabled = authorized ? '' : 'disabled'
if (Active.Map) menustring += '<li class="rc-hide"><div class="rc-icon"></div>Hide until refresh<div class="rc-keyboard">Ctrl+H</div></li>'
if (Active.Map && Active.Mapper) menustring += '<li class="rc-remove ' + disabled + '"><div class="rc-icon"></div>Remove from map<div class="rc-keyboard">Ctrl+M</div></li>'
if (Active.Topic) menustring += '<li class="rc-remove"><div class="rc-icon"></div>Remove from view<div class="rc-keyboard">Ctrl+M</div></li>'
if (Active.Map && Active.Mapper) menustring += '<li class="rc-delete ' + disabled + '"><div class="rc-icon"></div>Delete<div class="rc-keyboard">Ctrl+D</div></li>'
if (Active.Map && Active.Mapper) menustring += '<li class="rc-remove ' + disabled + '"><div class="rc-icon"></div>Remove<div class="rc-keyboard">Ctrl+D</div></li>'
if (Active.Topic) menustring += '<li class="rc-remove"><div class="rc-icon"></div>Remove from view<div class="rc-keyboard">Ctrl+D</div></li>'
if (Active.Map && Active.Mapper) menustring += '<li class="rc-spacer"></li>'
@ -1703,28 +1689,18 @@ const JIT = {
// attach events to clicks on the list items
// delete the selected things from the database
if (authorized) {
$('.rc-delete').click(function() {
$('.rightclickmenu').remove()
Control.deleteSelected()
})
}
// remove the selected things from the map
if (authorized) {
$('.rc-remove').click(function() {
$('.rightclickmenu').remove()
Control.removeSelectedEdges()
Control.removeSelectedNodes()
Control.removeSelected()
})
}
// hide selected nodes and synapses until refresh
$('.rc-hide').click(function() {
$('.rightclickmenu').remove()
Control.hideSelectedEdges()
Control.hideSelectedNodes()
Control.hideSelected()
})
// change the permission of all the selected nodes and synapses that you were the originator of

View file

@ -60,7 +60,7 @@ const Listeners = {
case 68: // if d or D is pressed
if (e.ctrlKey || e.metaKey) {
e.preventDefault()
Control.deleteSelected()
Control.removeSelected()
}
break
case 69: // if e or E is pressed
@ -84,15 +84,16 @@ const Listeners = {
case 72: // if h or H is pressed
if (e.ctrlKey || e.metaKey) {
e.preventDefault()
Control.hideSelectedNodes()
Control.hideSelectedEdges()
Control.hideSelected()
}
break
case 77: // if m or M is pressed
// since we're removing 'deletion', we have Ctrl-D available for this now, but leave this in
// as a deprecated function, just in case its what people got used to
// or should we do a GlobalUI.notifyUser to ask them to switch?
if (e.ctrlKey || e.metaKey) {
e.preventDefault()
Control.removeSelectedNodes()
Control.removeSelectedEdges()
Control.removeSelected()
}
break
case 82: // if r or R is pressed

View file

@ -29,11 +29,9 @@ import {
TOPIC_CREATED,
TOPIC_UPDATED,
TOPIC_REMOVED,
TOPIC_DELETED,
SYNAPSE_CREATED,
SYNAPSE_UPDATED,
SYNAPSE_REMOVED,
SYNAPSE_DELETED,
PEER_COORDS_UPDATED,
MAP_UPDATED
} from './events'
@ -58,11 +56,9 @@ import {
topicCreated,
topicUpdated,
topicRemoved,
topicDeleted,
synapseCreated,
synapseUpdated,
synapseRemoved,
synapseDeleted,
mapUpdated
} from './receivable'
@ -84,11 +80,9 @@ import {
createTopic,
updateTopic,
removeTopic,
deleteTopic,
createSynapse,
updateSynapse,
removeSynapse,
deleteSynapse,
updateMap
} from './sendable'
@ -304,11 +298,6 @@ let Realtime = {
}
$(document).on(JIT.events.newTopic + '.map', createTopic)
var deleteTopic = function(event, data) {
self.deleteTopic(data)
}
$(document).on(JIT.events.deleteTopic + '.map', deleteTopic)
var removeTopic = function(event, data) {
self.removeTopic(data)
}
@ -319,11 +308,6 @@ let Realtime = {
}
$(document).on(JIT.events.newSynapse + '.map', createSynapse)
var deleteSynapse = function(event, data) {
self.deleteSynapse(data)
}
$(document).on(JIT.events.deleteSynapse + '.map', deleteSynapse)
var removeSynapse = function(event, data) {
self.removeSynapse(data)
}
@ -500,11 +484,9 @@ const sendables = [
['createTopic', createTopic],
['updateTopic', updateTopic],
['removeTopic', removeTopic],
['deleteTopic', deleteTopic],
['createSynapse', createSynapse],
['updateSynapse', updateSynapse],
['removeSynapse', removeSynapse],
['deleteSynapse', deleteSynapse],
['updateMap', updateMap]
]
sendables.forEach(sendable => {
@ -531,11 +513,9 @@ const subscribeToEvents = (Realtime, socket) => {
socket.on(TOPIC_CREATED, topicCreated(Realtime))
socket.on(TOPIC_UPDATED, topicUpdated(Realtime))
socket.on(TOPIC_REMOVED, topicRemoved(Realtime))
socket.on(TOPIC_DELETED, topicDeleted(Realtime))
socket.on(SYNAPSE_CREATED, synapseCreated(Realtime))
socket.on(SYNAPSE_UPDATED, synapseUpdated(Realtime))
socket.on(SYNAPSE_REMOVED, synapseRemoved(Realtime))
socket.on(SYNAPSE_DELETED, synapseDeleted(Realtime))
socket.on(MAP_UPDATED, mapUpdated(Realtime))
}

View file

@ -1,9 +1,7 @@
const {
// server sendable, client receivable
TOPIC_UPDATED,
TOPIC_DELETED,
SYNAPSE_UPDATED,
SYNAPSE_DELETED,
MAP_UPDATED,
JUNTO_UPDATED,
@ -13,15 +11,12 @@ const {
JOIN_MAP,
LEAVE_MAP,
UPDATE_TOPIC,
DELETE_TOPIC,
UPDATE_SYNAPSE,
DELETE_SYNAPSE,
UPDATE_MAP
} = require('../frontend/src/Metamaps/Realtime/events')
module.exports = function(io, store) {
store.subscribe(() => {
console.log(store.getState())
io.sockets.emit(JUNTO_UPDATED, store.getState())
})
@ -38,18 +33,10 @@ module.exports = function(io, store) {
socket.broadcast.emit(TOPIC_UPDATED, data)
})
socket.on(DELETE_TOPIC, function(data) {
socket.broadcast.emit(TOPIC_DELETED, data)
})
socket.on(UPDATE_SYNAPSE, function(data) {
socket.broadcast.emit(SYNAPSE_UPDATED, data)
})
socket.on(DELETE_SYNAPSE, function(data) {
socket.broadcast.emit(SYNAPSE_DELETED, data)
})
socket.on(UPDATE_MAP, function(data) {
socket.broadcast.emit(MAP_UPDATED, data)
})