diff --git a/app/controllers/maps_controller.rb b/app/controllers/maps_controller.rb index f253efd5..3d785e91 100644 --- a/app/controllers/maps_controller.rb +++ b/app/controllers/maps_controller.rb @@ -116,8 +116,12 @@ class MapsController < ApplicationController # GET maps/:id/export def export - exporter = MapExportService.new(current_user, @map, base_url: request.base_url) - + topic_ids = params[:topic_ids].split(',').map(&:to_i) + synapse_ids = params[:synapse_ids].split(',').map(&:to_i) + exporter = MapExportService.new(current_user, @map, + topic_ids: topic_ids, + synapse_ids: synapse_ids, + base_url: request.base_url) respond_to do |format| format.json { render json: exporter.json } format.csv { send_data exporter.csv } diff --git a/app/services/map_export_service.rb b/app/services/map_export_service.rb index 4e180bf2..5bd23b65 100644 --- a/app/services/map_export_service.rb +++ b/app/services/map_export_service.rb @@ -6,6 +6,8 @@ class MapExportService def initialize(user, map, opts = {}) @user = user @map = map + @topic_ids = opts[:topic_ids] if opts[:topic_ids] + @synapse_ids = opts[:synapse_ids] if opts[:synapse_ids] @base_url = opts[:base_url] || 'https://metamaps.cc' end @@ -61,6 +63,7 @@ class MapExportService topic_mappings.map do |mapping| topic = mapping.mappable next nil if topic.nil? + next nil if @topic_ids && !@topic_ids.include?(topic.id) OpenStruct.new( id: topic.id, name: topic.name, @@ -72,13 +75,14 @@ class MapExportService user: topic.user.name, permission: topic.permission ) - end.compact + end.compact.uniq(&:id) end def exportable_synapses - visible_synapses = Pundit.policy_scope!(user, map.synapses) + visible_synapses = Pundit.policy_scope!(user, map.synapses).uniq visible_synapses.map do |synapse| next nil if synapse.nil? + next nil if @synapse_ids && !@synapse_ids.include?(synapse.id) OpenStruct.new( topic1: synapse.topic1_id, topic2: synapse.topic2_id, diff --git a/frontend/src/Metamaps/Export.js b/frontend/src/Metamaps/Export.js new file mode 100644 index 00000000..b0cf429c --- /dev/null +++ b/frontend/src/Metamaps/Export.js @@ -0,0 +1,32 @@ +/* global $ */ +import Active from './Active' +import GlobalUI from './GlobalUI' +import Selected from './Selected' + +const Export = { + // simple hack to use the existing ruby export code + // someday we can build a real export function here + copySelection: function() { + if (!Active.Map) return // someday we can expand this + const topic_ids = Selected.Nodes.map(node => node.id).join(',') + const synapse_ids = Selected.Edges.map(edge => { + return edge.getData('synapses')[edge.getData('displayIndex')].id + }).join(',') + const url = `/maps/${Active.Map.id}/export.json` + const query = `topic_ids=${topic_ids}&synapse_ids=${synapse_ids}` + $.ajax(`${url}?${query}`, { + success: data => { + $('body').append($('').select()) + const copied = document.execCommand('copy') + $('#clipboard-text').remove() + if (copied) { + GlobalUI.notifyUser(`${Selected.Nodes.length} topics and ${Selected.Edges.length} synapses were copied to the clipboard`) + } else { + GlobalUI.notifyUser(`Copy-paste failed, try manually exporting the map at ${url}.`) + } + } + }) + } +} + +export default Export diff --git a/frontend/src/Metamaps/Listeners.js b/frontend/src/Metamaps/Listeners.js index a5dc630f..b5670e1e 100644 --- a/frontend/src/Metamaps/Listeners.js +++ b/frontend/src/Metamaps/Listeners.js @@ -4,6 +4,7 @@ import Active from './Active' import Create from './Create' import Control from './Control' import DataModel from './DataModel' +import Export from './Export' import JIT from './JIT' import Realtime from './Realtime' import Selected from './Selected' @@ -72,6 +73,11 @@ const Listeners = { Visualize.mGraph.plot() } + break + case 67: // if c or C is pressed + if (e.ctrlKey && e.target.tagName === 'BODY') { + Export.copySelection() + } break case 68: // if d or D is pressed if (e.ctrlKey || e.metaKey) { diff --git a/frontend/src/Metamaps/index.js b/frontend/src/Metamaps/index.js index 747cb643..2f28ee55 100644 --- a/frontend/src/Metamaps/index.js +++ b/frontend/src/Metamaps/index.js @@ -7,6 +7,7 @@ import Control from './Control' import Create from './Create' import DataModel from './DataModel' import Debug from './Debug' +import Export from './Export' import Filter from './Filter' import GlobalUI, { Notifications, ReactApp, Search, CreateMap, ImportDialog @@ -40,6 +41,7 @@ Metamaps.Control = Control Metamaps.Create = Create Metamaps.DataModel = DataModel Metamaps.Debug = Debug +Metamaps.Export = Export Metamaps.Filter = Filter Metamaps.GlobalUI = GlobalUI Metamaps.GlobalUI.Notifications = Notifications