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:
parent
3b8a5d0c2e
commit
d1680c1895
7 changed files with 23 additions and 195 deletions
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue