diff --git a/.gitignore b/.gitignore index a2b03a61..43009ea4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,8 @@ vendor/ log/*.log tmp +coverage + .DS_Store */.DS_Store .DS_Store? diff --git a/.simplecov b/.simplecov new file mode 100644 index 00000000..b81ebfeb --- /dev/null +++ b/.simplecov @@ -0,0 +1,3 @@ +if ENV['COVERAGE'] == 'on' + SimpleCov.start 'rails' +end diff --git a/Gemfile b/Gemfile index b4e3bdf6..8379a7db 100644 --- a/Gemfile +++ b/Gemfile @@ -42,6 +42,8 @@ group :test do gem 'rspec-rails' gem 'factory_girl_rails' gem 'shoulda-matchers' + gem 'simplecov', require: false + gem 'json-schema' end group :production do #this is used on heroku diff --git a/Gemfile.lock b/Gemfile.lock index be8637de..30817e66 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -36,6 +36,7 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) + addressable (2.3.8) arel (6.0.3) aws-sdk (1.66.0) aws-sdk-v1 (= 1.66.0) @@ -78,6 +79,7 @@ GEM thread_safe (~> 0.1) warden (~> 1.2.3) diff-lcs (1.2.5) + docile (1.1.5) dotenv (2.0.2) erubis (2.7.0) execjs (2.6.0) @@ -104,6 +106,8 @@ GEM jquery-ui-rails (5.0.5) railties (>= 3.2.16) json (1.8.3) + json-schema (2.6.0) + addressable (~> 2.3.8) kaminari (0.16.3) actionpack (>= 3.0.0) activesupport (>= 3.0.0) @@ -203,6 +207,11 @@ GEM tilt (>= 1.1, < 3) shoulda-matchers (3.0.1) activesupport (>= 4.0.0) + simplecov (0.11.1) + docile (~> 1.1.0) + json (~> 1.8) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) slop (3.6.0) sprockets (3.4.0) rack (> 1, < 3) @@ -245,6 +254,7 @@ DEPENDENCIES jquery-rails jquery-ui-rails json + json-schema kaminari paperclip pg @@ -258,9 +268,7 @@ DEPENDENCIES rspec-rails sass-rails shoulda-matchers + simplecov tunemygc uglifier uservoice-ruby - -BUNDLED WITH - 1.11.2 diff --git a/README.md b/README.md index 5cfd3a37..293265ac 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Metamaps ======= [![Join the chat at https://gitter.im/metamaps/metamaps_gen002](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/metamaps/metamaps_gen002?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -![Build Status](https://jenkins.devinhoward.ca/job/metamaps_gen002.develop/badge/icon) +[![Build Status](https://jenkins.devinhoward.ca/job/metamaps_gen002.develop/badge/icon)](https://jenkins.devinhoward.ca/job/metamaps_gen002.develop/) Welcome to the Metamaps GitHub repo. diff --git a/app/assets/javascripts/src/Metamaps.JIT.js.erb b/app/assets/javascripts/src/Metamaps.JIT.js.erb index 6cd0fa88..74e6c64b 100644 --- a/app/assets/javascripts/src/Metamaps.JIT.js.erb +++ b/app/assets/javascripts/src/Metamaps.JIT.js.erb @@ -869,9 +869,9 @@ Metamaps.JIT = { } // if it's a right click or holding down alt, start synapse creation ->third option is for firefox else if ((e.button == 2 || (e.button == 0 && e.altKey) || e.buttons == 2) && authorized) { - if (tempInit == false) { - tempNode = node; - tempInit = true; + if (Metamaps.tempInit == false) { + Metamaps.tempNode = node; + Metamaps.tempInit = true; Metamaps.Create.newTopic.hide(); Metamaps.Create.newSynapse.hide(); @@ -887,8 +887,8 @@ Metamaps.JIT = { } } else { Metamaps.Mouse.synapseStartCoordinates = [{ - x: tempNode.pos.getc().x, - y: tempNode.pos.getc().y + x: Metamaps.tempNode.pos.getc().x, + y: Metamaps.tempNode.pos.getc().y }]; } Metamaps.Mouse.synapseEndCoordinates = { @@ -899,11 +899,11 @@ Metamaps.JIT = { // temp = eventInfo.getNode(); if (temp != false && temp.id != node.id && Metamaps.Selected.Nodes.indexOf(temp) == -1) { // this means a Node has been returned - tempNode2 = temp; + Metamaps.tempNode2 = temp; Metamaps.Mouse.synapseEndCoordinates = { - x: tempNode2.pos.getc().x, - y: tempNode2.pos.getc().y + x: Metamaps.tempNode2.pos.getc().x, + y: Metamaps.tempNode2.pos.getc().y }; // before making the highlighted one bigger, make sure all the others are regular size @@ -913,7 +913,7 @@ Metamaps.JIT = { temp.setData('dim', 35, 'current'); Metamaps.Visualize.mGraph.plot(); } else if (!temp) { - tempNode2 = null; + Metamaps.tempNode2 = null; Metamaps.Visualize.mGraph.graph.eachNode(function (n) { n.setData('dim', 25, 'current'); }); @@ -941,10 +941,10 @@ Metamaps.JIT = { } }, // onDragMoveTopicHandler onDragCancelHandler: function (node, eventInfo, e) { - tempNode = null; - if (tempNode2) tempNode2.setData('dim', 25, 'current'); - tempNode2 = null; - tempInit = false; + Metamaps.tempNode = null; + if (Metamaps.tempNode2) Metamaps.tempNode2.setData('dim', 25, 'current'); + Metamaps.tempNode2 = null; + Metamaps.tempInit = false; // reset the draw synapse positions to false Metamaps.Mouse.synapseStartCoordinates = []; Metamaps.Mouse.synapseEndCoordinates = null; @@ -953,27 +953,27 @@ Metamaps.JIT = { onDragEndTopicHandler: function (node, eventInfo, e) { var midpoint = {}, pixelPos, mapping; - if (tempInit && tempNode2 == null) { + if (Metamaps.tempInit && Metamaps.tempNode2 == null) { // this means you want to add a new topic, and then a synapse Metamaps.Create.newTopic.addSynapse = true; Metamaps.Create.newTopic.open(); - } else if (tempInit && tempNode2 != null) { + } else if (Metamaps.tempInit && Metamaps.tempNode2 != null) { // this means you want to create a synapse between two existing topics Metamaps.Create.newTopic.addSynapse = false; - Metamaps.Create.newSynapse.topic1id = tempNode.getData('topic').id; - Metamaps.Create.newSynapse.topic2id = tempNode2.getData('topic').id; - tempNode2.setData('dim', 25, 'current'); + Metamaps.Create.newSynapse.topic1id = Metamaps.tempNode.getData('topic').id; + Metamaps.Create.newSynapse.topic2id = Metamaps.tempNode2.getData('topic').id; + Metamaps.tempNode2.setData('dim', 25, 'current'); Metamaps.Visualize.mGraph.plot(); - midpoint.x = tempNode.pos.getc().x + (tempNode2.pos.getc().x - tempNode.pos.getc().x) / 2; - midpoint.y = tempNode.pos.getc().y + (tempNode2.pos.getc().y - tempNode.pos.getc().y) / 2; + midpoint.x = Metamaps.tempNode.pos.getc().x + (Metamaps.tempNode2.pos.getc().x - Metamaps.tempNode.pos.getc().x) / 2; + midpoint.y = Metamaps.tempNode.pos.getc().y + (Metamaps.tempNode2.pos.getc().y - Metamaps.tempNode.pos.getc().y) / 2; pixelPos = Metamaps.Util.coordsToPixels(midpoint); $('#new_synapse').css('left', pixelPos.x + "px"); $('#new_synapse').css('top', pixelPos.y + "px"); Metamaps.Create.newSynapse.open(); - tempNode = null; - tempNode2 = null; - tempInit = false; - } else if (!tempInit && node && !node.nodeFrom) { + Metamaps.tempNode = null; + Metamaps.tempNode2 = null; + Metamaps.tempInit = false; + } else if (!Metamaps.tempInit && node && !node.nodeFrom) { // this means you dragged an existing node, autosave that to the database // check whether to save mappings @@ -1047,18 +1047,13 @@ Metamaps.JIT = { Metamaps.TopicCard.hideCard(); Metamaps.SynapseCard.hideCard(); Metamaps.Create.newTopic.hide(); - if ($('#new_synapse').is(":visible")) { - // Hide the new_synapse form and create the synapse! - Metamaps.Synapse.createSynapseLocally(); - }//if - $('.rightclickmenu').remove(); // reset the draw synapse positions to false Metamaps.Mouse.synapseStartCoordinates = []; Metamaps.Mouse.synapseEndCoordinates = null; - tempInit = false; - tempNode = null; - tempNode2 = null; + Metamaps.tempInit = false; + Metamaps.tempNode = null; + Metamaps.tempNode2 = null; if (!e.ctrlKey && !e.shiftKey) { Metamaps.Control.deselectAllEdges(); Metamaps.Control.deselectAllNodes(); @@ -1697,8 +1692,8 @@ Metamaps.JIT = { easing = 1; // frictional value easing = 1; - window.clearInterval(panningInt) - panningInt = setInterval(function () { + window.clearInterval(Metamaps.panningInt) + Metamaps.panningInt = setInterval(function () { myTimer() }, 1); @@ -1707,7 +1702,7 @@ Metamaps.JIT = { $(document).trigger(Metamaps.JIT.events.pan); easing = easing * 0.75; - if (easing < 0.1) window.clearInterval(panningInt); + if (easing < 0.1) window.clearInterval(Metamaps.panningInt); } }, // SmoothPanning renderMidArrow: function (from, to, dim, swap, canvas, placement, newSynapse) { diff --git a/app/assets/javascripts/src/Metamaps.js.erb b/app/assets/javascripts/src/Metamaps.js.erb index 90ab4a8e..4744105c 100644 --- a/app/assets/javascripts/src/Metamaps.js.erb +++ b/app/assets/javascripts/src/Metamaps.js.erb @@ -1,3 +1,5 @@ +// TODO document this user agent function + var labelType, useGradients, nativeTextSupport, animate; (function () { @@ -14,11 +16,13 @@ var labelType, useGradients, nativeTextSupport, animate; animate = !(iStuff || !nativeCanvasSupport); })(); -// TODO eliminate these 4 global variables -var panningInt; // this variable is used to store a 'setInterval' for the Metamaps.JIT.SmoothPanning() function, so that it can be cleared with window.clearInterval -var tempNode = null, - tempInit = false, - tempNode2 = null; +// TODO eliminate these 4 top-level variables +Metamaps = { + panningInt: null, + tempNode: null, + tempInit: false, + tempNode2: null +} Metamaps.Settings = { embed: false, // indicates that the app is on a page that is optimized for embedding in iFrames on other web pages @@ -736,10 +740,6 @@ Metamaps.Create = { init: function () { var self = Metamaps.Create.newSynapse; - $('#synapse_desc').keyup(function () { - Metamaps.Create.newSynapse.description = $(this).val(); - }); - var synapseBloodhound = new Bloodhound({ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), queryTokenizer: Bloodhound.tokenizers.whitespace, @@ -795,6 +795,22 @@ Metamaps.Create = { }] ); + $('#synapse_desc').keyup(function (e) { + var ESC = 27, BACKSPACE = 8, DELETE = 46; + if (e.keyCode === BACKSPACE && $(this).val() === "" || + e.keyCode === DELETE && $(this).val() === "" || + e.keyCode === ESC) { + Metamaps.Create.newSynapse.hide(); + }//if + Metamaps.Create.newSynapse.description = $(this).val(); + }); + + $('#synapse_desc').focusout(function() { + if (Metamaps.Create.newSynapse.beingCreated) { + Metamaps.Synapse.createSynapseLocally(); + } + }); + $('#synapse_desc').bind('typeahead:select', function (event, datum, dataset) { if (datum.id) { // if they clicked on an existing synapse get it Metamaps.Synapse.getSynapseFromAutocomplete(datum.id); @@ -811,7 +827,7 @@ Metamaps.Create = { topic2id: null, newSynapseId: null, open: function () { - $('#new_synapse').fadeIn('fast', function () { + $('#new_synapse').fadeIn(100, function () { $('#synapse_desc').focus(); }); Metamaps.Create.newSynapse.beingCreated = true; diff --git a/app/assets/stylesheets/admin.css b/app/assets/stylesheets/admin.css deleted file mode 100644 index 70e68127..00000000 --- a/app/assets/stylesheets/admin.css +++ /dev/null @@ -1,63 +0,0 @@ - - -.allMetacodes { - float:left; -} - -.allMetacodes span { - margin:4px 8px; - color:#67AF9F; -} - -.editMetacodes { - z-index:12; - width:auto; - color: #67AF9F; - padding:10px; - float:left; -} - -.editMetacodes ul { - display:block; -} - -.editMetacodes ul li { - clear:both; - list-style-type:none; - display:block; - padding:3px; -} - -.editMetacodes ul img { - width:40px; - height:40px; - float:left; -} - -.editMetacodes ul p { - float:left; - display: block; - margin: 0; - background: none; - padding: 10px 4px 2px 4px; -} - -.editMetacodes #filters-one { - float:left; -} - -.editMetacodes #filters-two { - float:left; -} - -.editMetacodes #filters-three { - float:left; -} - -.editMetacodes #filters-four { - float:left; -} - -.editMetacodes li.toggledOff { - opacity: 0.4; -} diff --git a/app/assets/stylesheets/admin.scss.erb b/app/assets/stylesheets/admin.scss.erb new file mode 100644 index 00000000..8fe47ae1 --- /dev/null +++ b/app/assets/stylesheets/admin.scss.erb @@ -0,0 +1,188 @@ +.allMetacodes { + float:left; + + span { + margin:4px 8px; + color:#67AF9F; + } +} + +.editMetacodes { + z-index:12; + width:auto; + color: #67AF9F; + padding:10px; + float:left; + + ul { + display:block; + } + + ul li { + clear:both; + list-style-type:none; + display:block; + padding:3px; + } + + ul img { + width:40px; + height:40px; + float:left; + } + + ul p { + float:left; + display: block; + margin: 0; + background: none; + padding: 10px 4px 2px 4px; + } + + #filters-one { + float:left; + } + + #filters-two { + float:left; + } + + #filters-three { + float:left; + } + + #filters-four { + float:left; + } + + li.toggledOff { + opacity: 0.4; + } +} + +.blackBox { + width: 760px; + margin: 0 auto; + padding: 20px 0 60px 20px; + background: rgba(0, 0, 0, 0.4); + color: white; + overflow: hidden; + position: relative; + + .metacodeSetsDescription { + width: 314px; + } + td.metacodeSetDesc { + width: 314px; + word-wrap: break-word; + } + .metacodeSetImage { + width: 36px; + height: 36px; + float: left; + } + tr { + display: table-row; + } + tr:nth-child(odd) { + background: rgba(0, 0, 0, 0.2); + } + tr:nth-child(even) { + background: rgba(0, 0, 0, 0.3); + } + th, + td { + padding: 10px; + } + td.iconURL { + max-width: 415px; + word-wrap: break-word; + } + .field { + margin: 15px 0 5px; + } + label { + float: left; + width: 100px; + margin-right: 15px; + margin-top: 0px; + } + input[type="text"] { + width: 336px; + height: 32px; + font-size: 15px; + direction: ltr; + -webkit-appearance: none; + appearance: none; + display: inline-block; + margin: 0; + padding: 0 8px; + background: #fff; + border: 1px solid #d9d9d9; + border-top: 1px solid #c0c0c0; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + -webkit-border-radius: 1px; + -moz-border-radius: 1px; + border-radius: 1px; + font: -webkit-small-control; + color: initial; + letter-spacing: normal; + word-spacing: normal; + text-transform: none; + text-indent: 0px; + text-shadow: none; + display: inline-block; + text-align: start; + font-family: arial; + } + textarea:hover, + input[type="text"]:hover { + border: 1px solid #b9b9b9; + border-top: 1px solid #a0a0a0; + -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + } + textarea { + padding: 8px; + border: 1px solid #d9d9d9; + border-top: 1px solid #c0c0c0; + resize: none; + font: -webkit-small-control; + letter-spacing: normal; + word-spacing: normal; + text-transform: none; + text-indent: 0px; + text-shadow: none; + text-align: start; + font-family: arial; + font-size: 15px; + line-height: 17px; + width: 318px; + } + .allMetacodes { + padding: 5px 0; + } + a.button { + margin-right: 20px; + line-height: 40px; + } + a.button, + input.add { + float: left; + margin-top: 5px; + height: 40px; + font-size: 17px; + width: auto; + padding: 0 30px; + cursor: pointer; + font-weight: normal; + } + a.button:hover, + input.add:hover { + -webkit-box-shadow: none; + box-shadow: none; + } +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 79beb773..5a32742d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,7 +2,8 @@ class ApplicationController < ActionController::Base protect_from_forgery before_filter :get_invite_link - + after_action :allow_embedding + # this is for global login include ContentHelper @@ -64,9 +65,13 @@ private end def get_invite_link - unsafe_uri = request.env["REQUEST_URI"] || 'https://metamaps.cc' - valid_url = /^https?:\/\/([\w\.-]+)(:\d{1,5})?\/?$/ - safe_uri = (unsafe_uri.match(valid_url)) ? unsafe_uri : '//metamaps.cc/' - @invite_link = "#{safe_uri}join" + (current_user ? "?code=#{current_user.code}" : "") + @invite_link = "#{request.base_url}/join" + (current_user ? "?code=#{current_user.code}" : "") + end + + def allow_embedding + #allow all + response.headers.except! 'X-Frame-Options' + # or allow a whitelist + # response.headers['X-Frame-Options'] = 'ALLOW-FROM http://blog.metamaps.cc' end end diff --git a/app/controllers/mappings_controller.rb b/app/controllers/mappings_controller.rb index 27567eb4..c20b0153 100644 --- a/app/controllers/mappings_controller.rb +++ b/app/controllers/mappings_controller.rb @@ -15,8 +15,6 @@ class MappingsController < ApplicationController def create @mapping = Mapping.new(mapping_params) - @mapping.map.touch(:updated_at) - if @mapping.save render json: @mapping, status: :created else @@ -28,8 +26,6 @@ class MappingsController < ApplicationController def update @mapping = Mapping.find(params[:id]) - @mapping.map.touch(:updated_at) - if @mapping.update_attributes(mapping_params) head :no_content else @@ -44,8 +40,6 @@ class MappingsController < ApplicationController @mapping.destroy - @map.touch(:updated_at) - head :no_content end diff --git a/app/controllers/maps_controller.rb b/app/controllers/maps_controller.rb index e539ac08..3bc351d8 100644 --- a/app/controllers/maps_controller.rb +++ b/app/controllers/maps_controller.rb @@ -1,5 +1,4 @@ class MapsController < ApplicationController - before_filter :require_user, only: [:create, :update, :screenshot, :destroy] respond_to :html, :json, :csv @@ -10,38 +9,24 @@ class MapsController < ApplicationController # GET /explore/featured # GET /explore/mapper/:id def index - - if request.path == "/explore" - redirect_to activemaps_url and return - end + return redirect_to activemaps_url if request.path == "/explore" @current = current_user - @user = nil @maps = [] - @mapperId = nil - - if !params[:page] - page = 1 - else - page = params[:page] - end + page = params[:page].present? ? params[:page] : 1 if request.path.index("/explore/active") != nil @maps = Map.where("maps.permission != ?", "private").order("updated_at DESC").page(page).per(20) @request = "active" - elsif request.path.index("/explore/featured") != nil @maps = Map.where("maps.featured = ? AND maps.permission != ?", true, "private").order("updated_at DESC").page(page).per(20) @request = "featured" - elsif request.path.index('/explore/mine') != nil # looking for maps by me - if !authenticated? - redirect_to activemaps_url and return - end + return redirect_to activemaps_url if !authenticated? + # don't need to exclude private maps because they all belong to you @maps = Map.where("maps.user_id = ?", @current.id).order("updated_at DESC").page(page).per(20) @request = "you" - elsif request.path.index('/explore/mapper/') != nil # looking for maps by a mapper @user = User.find(params[:id]) @maps = Map.where("maps.user_id = ? AND maps.permission != ?", @user.id, "private").order("updated_at DESC").page(page).per(20) @@ -121,7 +106,6 @@ class MapsController < ApplicationController # POST maps def create - @user = current_user @map = Map.new() @map.name = params[:name] @@ -129,40 +113,45 @@ class MapsController < ApplicationController @map.permission = params[:permission] @map.user = @user @map.arranged = false - @map.save if params[:topicsToMap] @all = params[:topicsToMap] @all = @all.split(',') @all.each do |topic| topic = topic.split('/') - @mapping = Mapping.new() - @mapping.user = @user - @mapping.map = @map - @mapping.mappable = Topic.find(topic[0]) - @mapping.xloc = topic[1] - @mapping.yloc = topic[2] - @mapping.save + mapping = Mapping.new() + mapping.user = @user + mapping.mappable = Topic.find(topic[0]) + mapping.xloc = topic[1] + mapping.yloc = topic[2] + @map.topicmappings << mapping + mapping.save end if params[:synapsesToMap] @synAll = params[:synapsesToMap] @synAll = @synAll.split(',') @synAll.each do |synapse_id| - @mapping = Mapping.new() - @mapping.user = @user - @mapping.map = @map - @mapping.mappable = Synapse.find(synapse_id) - @mapping.save + mapping = Mapping.new() + mapping.user = @user + mapping.map = @map + mapping.mappable = Synapse.find(synapse_id) + @map.synapsemappings << mapping + mapping.save end end @map.arranged = true - @map.save end - respond_to do |format| + if @map.save + respond_to do |format| format.json { render :json => @map } + end + else + respond_to do |format| + format.json { render :json => "invalid params" } + end end end diff --git a/app/controllers/metacodes_controller.rb b/app/controllers/metacodes_controller.rb index 5d1e1367..54956c60 100644 --- a/app/controllers/metacodes_controller.rb +++ b/app/controllers/metacodes_controller.rb @@ -5,9 +5,6 @@ class MetacodesController < ApplicationController # GET /metacodes.json def index @metacodes = Metacode.order("name").all - @metacodes.map do |metacode| - metacode.icon = ActionController::Base.helpers.asset_path(metacode.icon) - end respond_to do |format| format.html { @@ -15,24 +12,12 @@ class MetacodesController < ApplicationController redirect_to root_url, notice: "You need to be an admin for that." return false end - render action: "index" + render :index } format.json { render json: @metacodes } end end - ### SHOW IS CURRENTLY DISABLED - # GET /metacodes/1 - # GET /metacodes/1.json -# def show -# @metacode = Metacode.find(params[:id]) -# -# respond_to do |format| -# format.html # show.html.erb -# format.json { render json: @metacode } -# end -# end - # GET /metacodes/new # GET /metacodes/new.json def new @@ -59,7 +44,7 @@ class MetacodesController < ApplicationController format.html { redirect_to metacodes_url, notice: 'Metacode was successfully created.' } format.json { render json: @metacode, status: :created, location: metacodes_url } else - format.html { render action: "new" } + format.html { render :new } format.json { render json: @metacode.errors, status: :unprocessable_entity } end end @@ -71,34 +56,20 @@ class MetacodesController < ApplicationController @metacode = Metacode.find(params[:id]) respond_to do |format| - if @metacode.update_attributes(metacode_params) + if @metacode.update(metacode_params) format.html { redirect_to metacodes_url, notice: 'Metacode was successfully updated.' } format.json { head :no_content } else - format.html { render action: "edit" } + format.html { render :edit } format.json { render json: @metacode.errors, status: :unprocessable_entity } end end end - - ### DESTROY IS CURRENTLY DISABLED - # DELETE /metacodes/1 - # DELETE /metacodes/1.json -# def destroy -# @metacode = Metacode.find(params[:id]) -# @metacode.destroy -# -# respond_to do |format| -# format.html { redirect_to metacodes_url } -# format.json { head :no_content } -# end -# end - private - # Never trust parameters from the scary internet, only allow the white list through. - def metacode_params - params.require(:metacode).permit(:id, :name, :icon, :color) - end + # Never trust parameters from the scary internet, only allow the white list through. + def metacode_params + params.require(:metacode).permit(:id, :name, :aws_icon, :manual_icon, :color) + end end diff --git a/app/controllers/synapses_controller.rb b/app/controllers/synapses_controller.rb index 2c3c08d0..f19dc053 100644 --- a/app/controllers/synapses_controller.rb +++ b/app/controllers/synapses_controller.rb @@ -22,7 +22,7 @@ class SynapsesController < ApplicationController # POST /synapses.json def create @synapse = Synapse.new(synapse_params) - @synapse.update_attribute :desc, "" if @synapse.desc.nil? + @synapse.desc = "" if @synapse.desc.nil? respond_to do |format| if @synapse.save @@ -37,7 +37,7 @@ class SynapsesController < ApplicationController # PUT /synapses/1.json def update @synapse = Synapse.find(params[:id]) - @synapse.update_attribute :desc, "" if @synapse.desc.nil? + @synapse.desc = "" if @synapse.desc.nil? respond_to do |format| if @synapse.update_attributes(synapse_params) diff --git a/app/models/map.rb b/app/models/map.rb index 2ec831bf..d1ab0cfb 100644 --- a/app/models/map.rb +++ b/app/models/map.rb @@ -43,41 +43,41 @@ class Map < ActiveRecord::Base end def topic_count - self.topics.length + topics.length end def synapse_count - self.synapses.length + synapses.length end def user_name - self.user.name + user.name end def user_image - self.user.image.url + user.image.url end - def contributor_count - self.contributors.length + def contributor_count + contributors.length end def screenshot_url - self.screenshot.url(:thumb) + screenshot.url(:thumb) end def created_at_str - self.created_at.strftime("%m/%d/%Y") + created_at.strftime("%m/%d/%Y") end def updated_at_str - self.updated_at.strftime("%m/%d/%Y") + updated_at.strftime("%m/%d/%Y") end def as_json(options={}) json = super(:methods =>[:user_name, :user_image, :topic_count, :synapse_count, :contributor_count, :screenshot_url], :except => [:screenshot_content_type, :screenshot_file_size, :screenshot_file_name, :screenshot_updated_at]) - json[:created_at_clean] = self.created_at_str - json[:updated_at_clean] = self.updated_at_str + json[:created_at_clean] = created_at_str + json[:updated_at_clean] = updated_at_str json end diff --git a/app/models/mapping.rb b/app/models/mapping.rb index 318aa5cf..d034de1c 100644 --- a/app/models/mapping.rb +++ b/app/models/mapping.rb @@ -4,10 +4,15 @@ class Mapping < ActiveRecord::Base scope :synapsemapping, -> { where(mappable_type: :Synapse) } belongs_to :mappable, polymorphic: true - - belongs_to :map, :class_name => "Map", :foreign_key => "map_id" - + belongs_to :map, :class_name => "Map", :foreign_key => "map_id", touch: true belongs_to :user + + validates :xloc, presence: true, + unless: Proc.new { |m| m.mappable_type == 'Synapse' } + validates :yloc, presence: true, + unless: Proc.new { |m| m.mappable_type == 'Synapse' } + validates :map, presence: true + validates :mappable, presence: true def user_name self.user.name diff --git a/app/models/metacode.rb b/app/models/metacode.rb index ff9591db..d3a8160b 100644 --- a/app/models/metacode.rb +++ b/app/models/metacode.rb @@ -1,9 +1,37 @@ class Metacode < ActiveRecord::Base - has_many :in_metacode_sets has_many :metacode_sets, :through => :in_metacode_sets has_many :topics + # This method associates the attribute ":aws_icon" with a file attachment + has_attached_file :aws_icon, :styles => { + :ninetysix => ['96x96#', :png], + }, + :default_url => 'https://s3.amazonaws.com/metamaps-assets/metacodes/generics/96px/gen_wildcard.png' + + # Validate the attached icon is image/jpg, image/png, etc + validates_attachment_content_type :aws_icon, :content_type => /\Aimage\/.*\Z/ + + validate :aws_xor_manual_icon + validate :manual_icon_https + before_create do + self.manual_icon = nil if self.manual_icon == "" + end + + def icon(*args) + if manual_icon.present? + manual_icon + else + aws_icon(*args) + end + end + + def as_json(options={}) + default = super(options) + default[:icon] = icon + default.except('aws_icon_file_name', 'aws_icon_content_type', 'aws_icon_file_size', 'aws_icon_updated_at', 'manual_icon') + end + def hasSelected(user) return true if user.settings.metacodes.include? self.id.to_s return false @@ -13,4 +41,23 @@ class Metacode < ActiveRecord::Base return true if self.metacode_sets.include? metacode_set return false end + + private + + def aws_xor_manual_icon + if aws_icon.blank? && manual_icon.blank? + errors.add(:base, "Either aws_icon or manual_icon is required") + end + if aws_icon.present? && manual_icon.present? + errors.add(:base, "Specify aws_icon or manual_icon, not both") + end + end + + def manual_icon_https + if manual_icon.present? + unless manual_icon.starts_with? 'https' + errors.add(:base, "Manual icon must begin with https") + end + end + end end diff --git a/app/models/synapse.rb b/app/models/synapse.rb index 225485e5..ea5889cc 100644 --- a/app/models/synapse.rb +++ b/app/models/synapse.rb @@ -1,5 +1,4 @@ class Synapse < ActiveRecord::Base - belongs_to :user belongs_to :topic1, :class_name => "Topic", :foreign_key => "node1_id" @@ -13,17 +12,25 @@ class Synapse < ActiveRecord::Base validates :permission, presence: true validates :permission, inclusion: { in: Perm::ISSIONS.map(&:to_s) } + validates :category, inclusion: { in: ['from-to', 'both'], allow_nil: true } + + # :nocov: def user_name - self.user.name + user.name end + # :nocov: + # :nocov: def user_image - self.user.image.url + user.image.url end + # :nocov: + # :nocov: def as_json(options={}) super(:methods =>[:user_name, :user_image]) end + # :nocov: ##### PERMISSIONS ###### diff --git a/app/views/layouts/_lightboxes.html.erb b/app/views/layouts/_lightboxes.html.erb index e2c9de50..f9bb78ff 100644 --- a/app/views/layouts/_lightboxes.html.erb +++ b/app/views/layouts/_lightboxes.html.erb @@ -46,7 +46,6 @@
While it's downloading, explore our blog,
watch the tutorials, or visit our knowledge base!
@@ -171,7 +168,7 @@
If you'd like to know what your money is going towards, we publish our financials transparently - everything is recorded through our value accounting system.
- +Want to help with design, code, community building, or communications for Metamaps? We're an open value network, which for us means we want to invite and empower peers to participate in creating value together. - <% # TODO change this link to https once it works %> -
To be a USER, request an invite! We'll expect you to abide by our terms of service.
+To be a USER, request an invite! We'll expect you to abide by our terms of service.
To be a CONTRIBUTOR, simply enter our spaces and join the conversation! We'll expect you to follow some guidelines.
diff --git a/app/views/maps/index.html.erb b/app/views/maps/index.html.erb index cd9661e0..0e067a63 100644 --- a/app/views/maps/index.html.erb +++ b/app/views/maps/index.html.erb @@ -1,10 +1,12 @@ -<%# - # @file - # Shows a list of all maps, or just a user's maps. - # TODO: What url is this accessible at? - #%> - - +<% # + # @file + # Shows a list of all maps, or just a user's maps. + # GET /explore/active(.:format) + # GET /explore/featured(.:format) + # GET /explore/mine(.:format) + # GET /explore/mapper/:id(.:format) + # GET /maps(.:format) + # %>