Compare commits

..

6 commits

Author SHA1 Message Date
Devin Howard e0eb3e6333 fix pathname 2017-09-23 11:57:57 -07:00
Devin Howard 6bbd35d644 cache path name to avoid race conditions when updating url 2017-09-23 11:57:57 -07:00
Devin Howard d219bde48d rework debouncing and lodash in Visualize.js 2017-09-23 11:57:57 -07:00
Devin Howard e30d05f71d fix initial scale/translate algorithm 2017-09-23 11:57:57 -07:00
Devin Howard 7af5972765 monkey patch scale/translate functions so they update the URL 2017-09-23 11:57:57 -07:00
Devin Howard 75e41619eb enable specifying scale/translate in query params for a map
e.g. https://metamaps.cc/maps/12?scale=0.5&translate=-350,300
2017-09-23 11:57:57 -07:00
294 changed files with 1189 additions and 2278 deletions

View file

@ -12,7 +12,7 @@ Rails:
Enabled: true
Metrics/LineLength:
Max: 120
Max: 100
Metrics/AbcSize:
Max: 16
@ -22,8 +22,3 @@ Style/Documentation:
Style/EmptyMethod:
EnforcedStyle: expanded
# I like this cop, but occasionally code is more readable without a guard clause,
# and I don't want to write rubocop:disable comments every time that happens
Style/GuardClause:
Enabled: false

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
source 'https://rubygems.org'
ruby '2.3.0'
@ -38,7 +37,7 @@ gem 'uglifier'
group :test do
gem 'brakeman', require: false
gem 'factory_bot_rails'
gem 'factory_girl_rails'
gem 'json-schema'
gem 'rspec-rails'
gem 'shoulda-matchers'
@ -48,10 +47,10 @@ end
group :development, :test do
gem 'better_errors'
gem 'binding_of_caller'
gem 'faker'
gem 'pry-byebug'
gem 'pry-rails'
gem 'rubocop', '~> 0.48.1' # match code climate https://github.com/tootsuite/mastodon/issues/1758
gem 'timecop'
gem 'rubocop'
gem 'tunemygc'
gem 'faker'
gem 'timecop'
end

View file

@ -105,10 +105,10 @@ GEM
actionmailer (>= 4.0, < 6)
activesupport (>= 4.0, < 6)
execjs (2.7.0)
factory_bot (4.8.2)
factory_girl (4.8.0)
activesupport (>= 3.0.0)
factory_bot_rails (4.8.2)
factory_bot (~> 4.8.2)
factory_girl_rails (4.8.0)
factory_girl (~> 4.8.0)
railties (>= 3.0.0)
faker (1.8.4)
i18n (~> 0.5)
@ -117,8 +117,7 @@ GEM
activesupport (>= 4.2.0)
httparty (0.15.6)
multi_xml (>= 0.5.2)
i18n (0.9.3)
concurrent-ruby (~> 1.0)
i18n (0.8.6)
jmespath (1.3.1)
jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3)
@ -154,21 +153,22 @@ GEM
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mimemagic (0.3.2)
mini_portile2 (2.3.0)
minitest (5.11.1)
mini_portile2 (2.2.0)
minitest (5.10.3)
multi_xml (0.6.0)
nio4r (2.1.0)
nokogiri (1.8.1)
mini_portile2 (~> 2.3.0)
nokogiri (1.8.0)
mini_portile2 (~> 2.2.0)
orm_adapter (0.5.0)
paperclip (5.2.0)
paperclip (5.1.0)
activemodel (>= 4.2.0)
activesupport (>= 4.2.0)
cocaine (~> 0.5.5)
mime-types
mimemagic (~> 0.3.0)
parser (2.4.0.2)
ast (~> 2.3)
parallel (1.12.0)
parser (2.4.0.0)
ast (~> 2.2)
pg (0.21.0)
powerpack (0.1.1)
pry (0.10.4)
@ -216,7 +216,7 @@ GEM
thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
rake (12.3.0)
rake (12.0.0)
rb-fsevent (0.10.2)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
@ -241,13 +241,14 @@ GEM
rspec-mocks (~> 3.6.0)
rspec-support (~> 3.6.0)
rspec-support (3.6.0)
rubocop (0.48.1)
rubocop (0.49.1)
parallel (~> 1.10)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-progressbar (1.9.0)
ruby-progressbar (1.8.1)
sass (3.5.1)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
@ -284,7 +285,7 @@ GEM
tilt (2.0.8)
timecop (0.9.1)
tunemygc (1.0.69)
tzinfo (1.2.4)
tzinfo (1.2.3)
thread_safe (~> 0.1)
uglifier (3.2.0)
execjs (>= 0.3.0, < 3)
@ -311,7 +312,7 @@ DEPENDENCIES
doorkeeper
dotenv-rails
exception_notification
factory_bot_rails
factory_girl_rails
faker
httparty
jquery-rails
@ -332,7 +333,7 @@ DEPENDENCIES
rails (~> 5.0.0)
redis (~> 3.3.3)
rspec-rails
rubocop (~> 0.48.1)
rubocop
sass-rails
shoulda-matchers
simplecov
@ -347,4 +348,4 @@ RUBY VERSION
ruby 2.3.0p0
BUNDLED WITH
1.16.1
1.15.4

1
Rakefile Executable file → Normal file
View file

@ -1,6 +1,5 @@
#!/usr/bin/env rake
# frozen_string_literal: true
# Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.

View file

@ -56,15 +56,16 @@
}
li.toggledOff {
opacity: 0.6;
opacity: 0.4;
}
}
.centerContent {
.blackBox {
width: 760px;
margin: 0 auto;
padding: 80px 0 60px 20px;
background: rgba(125, 125, 125, 0.4);
background: rgba(0, 0, 0, 0.4);
color: white;
overflow: hidden;
position: relative;
@ -84,10 +85,10 @@
display: table-row;
}
tr:nth-child(odd) {
background: rgba(125, 125, 125, 0.2);
background: rgba(0, 0, 0, 0.2);
}
tr:nth-child(even) {
background: rgba(125, 125, 125, 0.3);
background: rgba(0, 0, 0, 0.3);
}
th,
td {

View file

@ -826,7 +826,6 @@ label {
font-size: 14px;
line-height: 14px;
color: #757575;
cursor: pointer;
}
.accountListItem:hover {
color: #424242;
@ -2930,6 +2929,136 @@ and it won't be important on password protected instances */
color: #424242;
}
/* Admin Pages */
.blackBox {
width: 760px;
margin: 0 auto;
padding: 20px 0 60px 20px;
background: rgba(0, 0, 0, 0.4);
color: white;
overflow: hidden;
position: relative;
}
.blackBox .metacodeSetsDescription {
width: 314px;
}
.blackBox td.metacodeSetDesc {
width: 314px;
word-wrap: break-word;
}
.blackBox .metacodeSetImage {
width: 36px;
height: 36px;
float: left;
}
.blackBox tr {
display: table-row;
}
.blackBox tr:nth-child(odd) {
background: rgba(0, 0, 0, 0.2);
}
.blackBox tr:nth-child(even) {
background: rgba(0, 0, 0, 0.3);
}
.blackBox th,
.blackBox td {
padding: 10px;
}
.blackBox td.iconURL {
max-width: 415px;
word-wrap: break-word;
}
.blackBox td.iconColor {
}
.blackBox .field {
margin: 15px 0 5px;
}
.blackBox label {
float: left;
width: 100px;
margin-right: 15px;
}
.blackBox 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;
}
.blackBox input[type="text"]:hover,
.blackBox textarea: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);
}
.blackBox 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;
}
.blackBox .allMetacodes {
padding: 5px 0;
}
.blackBox a.button {
margin-right: 20px;
line-height: 40px;
}
.blackBox a.button,
.blackBox input.add {
float: left;
margin-top: 5px;
height: 40px;
font-size: 17px;
width: auto;
padding: 0 30px;
cursor: pointer;
font-weight: normal;
}
.blackBox a.button:hover,
.blackBox input.add:hover {
-webkit-box-shadow: none;
box-shadow: none;
}
/* request */
.requestInvite {

View file

@ -127,10 +127,6 @@ $mid-gray-opacity: rgba(66, 66, 66, 0.6);
a.mdSupport {
color: #4fb5c0;
font-size: 11px;
display: none;
}
.riek-editing + .mdSupport {
display: block;
}
}
.CardOnGraph.hasAttachment .scroll {

View file

@ -668,19 +668,19 @@
box-shadow: 0px 3px 3px rgba(0,0,0,0.23), 0 3px 3px rgba(0,0,0,0.16);
}
#navBar {
#exploreMapsHeader {
position: absolute;
width: 100%;
}
.navBarContainer {
.exploreMapsBar {
z-index:2;
background-color:#FAFAFA;
height: 42px;
padding-top: 52px;
}
.navBarMenu {
.exploreMapsMenu {
display: block;
width: 100%;
height:42px;
@ -689,29 +689,30 @@
text-align: center;
}
.navBarCenter {
.exploreMapsCenter {
display: block;
}
.navBarButton {
color: #757575;
.exploreMapsButton {
color: #757575;
cursor: default;
font-weight: normal;
font-family: 'din-medium';
font-size: 14px;
padding: 0 8px;
border-bottom: 2px solid rgba(0,0,0,0);
height: 14px;
padding: 14px 8px 12px 40px;
border-bottom: 2px solid rgba(0,0,0,0);
display: inline-block;
cursor: pointer;
position:relative;
cursor: pointer;
position:relative;
}
.navBarButton:hover, .navBarButton.active {
.exploreMapsButton:hover, .exploreMapsButton.active {
text-decoration: none;
color: #424242;
border-bottom: 2px solid #00BCD4;
}
.navBarButton.mapperButton {
.exploreMapsButton.mapperButton {
height: 40px;
padding: 0;
}
@ -728,69 +729,62 @@
}
.navBarButton .navBarIcon {
.exploreMapsButton .exploreMapsIcon {
background-repeat: no-repeat;
width:32px;
height:32px;
margin-top:5px;
margin-left:5px;
margin-right: 5px;
display: inline-block;
vertical-align: top;
position:absolute;
top:5px;
left:5px;
}
.navBarLinkText {
padding: 11px 0 12px 0;
display: inline-block;
}
.navBarCenter .authedApps .navBarIcon {
.exploreMapsCenter .authedApps .exploreMapsIcon {
background-image: url(<%= asset_path('user_sprite.png') %>);
background-position: 0 -32px;
}
.navBarCenter .myMaps .navBarIcon {
.exploreMapsCenter .myMaps .exploreMapsIcon {
background-image: url(<%= asset_path 'exploremaps_sprite.png' %>);
background-position: -32px 0;
}
.navBarCenter .sharedMaps .navBarIcon {
.exploreMapsCenter .sharedMaps .exploreMapsIcon {
background-image: url(<%= asset_path 'exploremaps_sprite.png' %>);
background-position: -128px 0;
}
.navBarCenter .activeMaps .navBarIcon {
.exploreMapsCenter .activeMaps .exploreMapsIcon {
background-image: url(<%= asset_path 'exploremaps_sprite.png' %>);
background-position: 0 0;
}
.navBarCenter .featuredMaps .navBarIcon {
.exploreMapsCenter .featuredMaps .exploreMapsIcon {
background-image: url(<%= asset_path 'exploremaps_sprite.png' %>);
background-position: -96px 0;
}
.navBarCenter .starredMaps .navBarIcon {
.exploreMapsCenter .starredMaps .exploreMapsIcon {
background-image: url(<%= asset_path 'exploremaps_sprite.png' %>);
background-position: -96px 0;
}
.navBarCenter .notificationsLink .navBarIcon {
.exploreMapsCenter .notificationsLink .exploreMapsIcon {
background-image: url(<%= asset_path 'topright_sprite.png' %>);
background-position: -128px 0;
}
.authedApps:hover .navBarIcon, .authedApps.active .navBarIcon {
.authedApps:hover .exploreMapsIcon, .authedApps.active .exploreMapsIcon {
background-position-x: -32px;
}
.myMaps:hover .navBarIcon, .myMaps.active .navBarIcon {
.myMaps:hover .exploreMapsIcon, .myMaps.active .exploreMapsIcon {
background-position: -32px -32px;
}
.activeMaps:hover .navBarIcon, .activeMaps.active .navBarIcon {
.activeMaps:hover .exploreMapsIcon, .activeMaps.active .exploreMapsIcon {
background-position: 0 -32px;
}
.featuredMaps:hover .navBarIcon, .featuredMaps.active .navBarIcon {
.featuredMaps:hover .exploreMapsIcon, .featuredMaps.active .exploreMapsIcon {
background-position: -96px -32px;
}
.starredMaps:hover .navBarIcon, .starredMaps.active .navBarIcon {
.starredMaps:hover .exploreMapsIcon, .starredMaps.active .exploreMapsIcon {
background-position: -96px -32px;
}
.sharedMaps:hover .navBarIcon, .sharedMaps.active .navBarIcon {
.sharedMaps:hover .exploreMapsIcon, .sharedMaps.active .exploreMapsIcon {
background-position: -128px -32px;
}
.notificationsLink:hover .navBarIcon, .notificationsLink.active .navBarIcon {
.notificationsLink:hover .exploreMapsIcon, .notificationsLink.active .exploreMapsIcon {
background-position-y: -32px;
}

View file

@ -32,7 +32,7 @@
/* Smartphones (portrait and landscape) ----------- the minimum space that two map cards can fit side by side */
@media only screen and (max-width : 504px) {
.upperLeftUI, .upperRightUI, .openCheatsheet, .mapInfoIcon, .feedback-icon, .chat-box, #navBar {
.upperLeftUI, .upperRightUI, .openCheatsheet, .mapInfoIcon, .feedback-icon, .chat-box, #exploreMapsHeader {
display: none !important;
}

View file

@ -1,7 +1,4 @@
$notifications-border-color: #DDDDDD;
$notifications-hover-color: #F6F6F6;
$unread_notifications_dot_size: 8px;
.unread-notifications-dot {
width: $unread_notifications_dot_size;
height: $unread_notifications_dot_size;
@ -16,72 +13,13 @@ $unread_notifications_dot_size: 8px;
.notificationsIcon {
position: relative;
}
.notificationsBox {
position: absolute;
background: #FFFFFF;
border-radius: 2px;
width: 350px;
right: 0;
top: 50px;
box-shadow: 0 3px 6px rgba(0,0,0,0.16);
border: 1px solid $notifications-border-color;
.notificationsBoxTriangle {
min-width: 0 !important;
display: block;
position: absolute;
right: 48px;
width: 20px !important;
height: 20px !important;
margin-left: -10px;
top: -11px;
border-left: 1px solid $notifications-border-color;
border-top: 1px solid $notifications-border-color;
border-bottom: 0 !important;
border-right: 0 !important;
background-color: #fff;
transform: rotate(45deg);
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
}
ul.notifications {
max-height: 500px;
overflow-y: auto;
.notification {
font-size: 13px;
.notification-body {
border-bottom: 1px solid $notifications-border-color;
}
}
.notificationsEmpty {
font-family: din-regular, helvetica, sans-serif;
margin: 50px 10px;
text-align: center;
}
}
.notificationsBoxSeeAll {
display: block;
width: 100%;
text-align: center;
padding: 6px 0;
font-family: din-regular, helvetica, sans-serif;
border-top: 1px solid rgba(0, 0, 0, 0.1);
&:hover {
color: #333;
background: $notifications-hover-color;
}
}
}
}
.controller-notifications {
ul.notifications {
list-style: none;
}
.notificationPage,
.notificationsPage {
font-family: 'din-regular', Sans-Serif;
@ -109,9 +47,89 @@ $unread_notifications_dot_size: 8px;
.emptyInbox {
padding-top: 15px;
}
.notification {
padding: 10px;
position: relative;
&:hover {
background: #F6F6F6;
.notification-read-unread {
display:block;
}
.notification-date {
display: none;
}
}
& > a {
float: left;
width: 85%;
box-sizing: border-box;
padding-right: 10px;
}
.notification-actor {
float: left;
img {
width: 32px;
height: 32px;
border-radius: 16px;
}
}
.notification-body {
margin-left: 50px;
line-height: 20px;
.in-bold {
font-family: 'din-medium', Sans-Serif;
}
.action {
background: #4fb5c0;
color: #FFF;
padding: 2px 6px;
border-radius: 3px;
display: inline-block;
margin: 5px 0;
}
}
.notification-date {
position: absolute;
top: 50%;
right: 10px;
color: #607d8b;
font-size: 13px;
line-height: 13px;
margin-top: -6px;
}
.notification-read-unread {
display: none;
float: left;
width: 15%;
a {
position: absolute;
top: 50%;
margin-top: -10px;
text-align: center;
}
}
&.unread {
background: #EEE;
}
}
}
.notificationPage {
.thirty-two-avatar {
@ -121,14 +139,14 @@ $unread_notifications_dot_size: 8px;
border-radius: 16px;
vertical-align: middle;
}
.button {
line-height: 32px;
img {
margin-top: 8px;
}
&.decline {
background: #DB5D5D;
&:hover {
@ -136,7 +154,7 @@ $unread_notifications_dot_size: 8px;
}
}
}
.notification-body {
p, div {
margin: 1em auto;
@ -145,93 +163,3 @@ $unread_notifications_dot_size: 8px;
}
}
}
ul.notifications {
list-style: none;
li:nth-last-child(2) {
.notification-body {
border-bottom: none !important;
}
}
}
.notification {
padding: 10px 10px 0 10px;
position: relative;
font-family: 'din-regular', Sans-Serif;
&.unread {
background: #EEE;
}
&:hover {
background: $notifications-hover-color;
.notification-read-unread {
display:block;
}
.notification-date {
display: none;
}
}
& > a {
float: left;
width: 85%;
box-sizing: border-box;
padding-right: 10px;
}
.notification-actor {
float: left;
img {
width: 32px;
height: 32px;
border-radius: 16px;
}
}
.notification-body {
margin-left: 50px;
line-height: 20px;
padding-bottom: 10px;
.in-bold {
font-family: 'din-medium', Sans-Serif;
}
.action {
background: #4fb5c0;
color: #FFF;
padding: 2px 6px;
border-radius: 3px;
display: inline-block;
margin: 5px 0;
}
}
.notification-date {
position: absolute;
top: 50%;
right: 10px;
color: #607d8b;
margin-top: -6px;
}
.notification-read-unread {
display: none;
float: left;
width: 15%;
a, div {
position: absolute;
top: 50%;
margin-top: -10px;
text-align: center;
cursor: pointer;
}
}
}

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module ApplicationCable
class Channel < ActionCable::Channel::Base
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class MapChannel < ApplicationCable::Channel
# Called when the consumer has successfully
# become a subscriber of this channel.

View file

@ -1,12 +1,11 @@
# frozen_string_literal: true
class AccessController < ApplicationController
before_action :require_user, only: %i[access access_request
approve_access approve_access_post
deny_access deny_access_post request_access]
before_action :set_map, only: %i[access access_request
approve_access approve_access_post
deny_access deny_access_post request_access]
before_action :require_user, only: [:access, :access_request,
:approve_access, :approve_access_post,
:deny_access, :deny_access_post, :request_access]
before_action :set_map, only: [:access, :access_request,
:approve_access, :approve_access_post,
:deny_access, :deny_access_post, :request_access]
after_action :verify_authorized
# GET maps/:id/request_access

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Api
module V1
class DeprecatedController < ApplicationController

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Api
module V2
class MappingsController < WithUpdatesController

View file

@ -1,10 +1,9 @@
# frozen_string_literal: true
module Api
module V2
class MapsController < WithUpdatesController
def searchable_columns
%i[name desc]
[:name, :desc]
end
def apply_filters(collection)

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Api
module V2
class MetacodesController < RestfulController

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Api
module V2
class RestfulController < ActionController::Base
@ -8,7 +7,7 @@ module Api
snorlax_used_rest!
before_action :load_resource, only: %i[show update destroy]
before_action :load_resource, only: [:show, :update, :destroy]
after_action :verify_authorized
def index
@ -92,7 +91,7 @@ module Api
end
def doorkeeper_user
return if doorkeeper_token.blank?
return unless doorkeeper_token.present?
doorkeeper_render_error unless valid_doorkeeper_token?
@doorkeeper_user ||= User.find(doorkeeper_token.resource_owner_id)
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Api
module V2
class SessionsController < ApplicationController

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Api
module V2
class StarsController < RestfulController

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Api
module V2
class SynapsesController < WithUpdatesController

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Api
module V2
class TokensController < RestfulController

View file

@ -1,10 +1,9 @@
# frozen_string_literal: true
module Api
module V2
class TopicsController < WithUpdatesController
def searchable_columns
%i[name desc link]
[:name, :desc, :link]
end
end
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Api
module V2
class UsersController < RestfulController

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Api
module V2
class WithUpdatesController < RestfulController

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class ApplicationController < ActionController::Base
include ApplicationHelper
include Pundit

View file

@ -1,7 +1,6 @@
# frozen_string_literal: true
class ExploreController < ApplicationController
before_action :require_authentication, only: %i[mine shared starred]
before_action :require_authentication, only: [:mine, :shared, :starred]
before_action :authorize_explore
after_action :verify_authorized
after_action :verify_policy_scoped

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
# bad code that should be checked over before entering one of the
# nice files from the right side of this repo
class HacksController < ApplicationController

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class MainController < ApplicationController
before_action :authorize_main
after_action :verify_authorized

View file

@ -1,7 +1,6 @@
# frozen_string_literal: true
class MappingsController < ApplicationController
before_action :require_user, only: %i[create update destroy]
before_action :require_user, only: [:create, :update, :destroy]
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :index

View file

@ -1,10 +1,7 @@
# frozen_string_literal: true
class MapsController < ApplicationController
before_action :require_user, only: %i[create update destroy events follow unfollow]
before_action :set_map, only: %i[show conversation update destroy
contains events export
follow unfollow unfollow_from_email]
before_action :require_user, only: [:create, :update, :destroy, :events, :follow, :unfollow]
before_action :set_map, only: [:show, :conversation, :update, :destroy, :contains, :events, :export, :follow, :unfollow, :unfollow_from_email]
after_action :verify_authorized
# GET maps/:id
@ -26,7 +23,7 @@ class MapsController < ApplicationController
format.ttl { redirect_to action: :export, format: :ttl }
end
end
# GET maps/:id/conversation
def conversation
respond_to do |format|
@ -143,7 +140,7 @@ class MapsController < ApplicationController
# POST maps/:id/follow
def follow
follow = FollowService.follow(@map, current_user, 'followed')
follow = FollowService.follow(@map, current_user, 'followed')
respond_to do |format|
format.json do
@ -158,7 +155,7 @@ class MapsController < ApplicationController
# POST maps/:id/unfollow
def unfollow
FollowService.unfollow(@map, current_user)
FollowService.unfollow(@map, current_user)
respond_to do |format|
format.json do

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class MessagesController < ApplicationController
before_action :require_user, except: [:show]
after_action :verify_authorized

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class MetacodeSetsController < ApplicationController
before_action :require_admin

View file

@ -1,8 +1,7 @@
# frozen_string_literal: true
class MetacodesController < ApplicationController
before_action :require_admin, except: %i[index show]
before_action :set_metacode, only: %i[edit update]
before_action :require_admin, except: [:index, :show]
before_action :set_metacode, only: [:edit, :update]
# GET /metacodes
# GET /metacodes.json

View file

@ -1,23 +1,18 @@
# frozen_string_literal: true
class NotificationsController < ApplicationController
before_action :set_receipts, only: %i[index show mark_read mark_unread]
before_action :set_notification, only: %i[show mark_read mark_unread]
before_action :set_receipt, only: %i[show mark_read mark_unread]
before_action :set_receipts, only: [:index, :show, :mark_read, :mark_unread]
before_action :set_notification, only: [:show, :mark_read, :mark_unread]
before_action :set_receipt, only: [:show, :mark_read, :mark_unread]
def index
@notifications = current_user.mailbox.notifications.page(params[:page]).per(25)
respond_to do |format|
format.html
format.json do
notifications = @notifications.map do |notification|
render json: @notifications.map do |notification|
receipt = @receipts.find_by(notification_id: notification.id)
NotificationDecorator.decorate(notification, receipt)
end
if !notifications.empty?
render json: notifications
else
render json: [].to_json
notification.as_json.merge(is_read: receipt.is_read)
end
end
end
@ -28,18 +23,20 @@ class NotificationsController < ApplicationController
respond_to do |format|
format.html do
case @notification.notification_code
when MAP_ACCESS_APPROVED, MAP_INVITE_TO_EDIT
redirect_to map_path(@notification.notified_object.map)
when TOPIC_ADDED_TO_MAP
redirect_to map_path(@notification.notified_object.map)
when TOPIC_CONNECTED_1
redirect_to topic_path(@notification.notified_object.topic1)
when TOPIC_CONNECTED_2
redirect_to topic_path(@notification.notified_object.topic2)
when MAP_ACCESS_APPROVED, MAP_INVITE_TO_EDIT
redirect_to map_path(@notification.notified_object.map)
when TOPIC_ADDED_TO_MAP
redirect_to map_path(@notification.notified_object.map)
when TOPIC_CONNECTED_1
redirect_to topic_path(@notification.notified_object.topic1)
when TOPIC_CONNECTED_2
redirect_to topic_path(@notification.notified_object.topic2)
end
end
format.json do
render json: NotificationDecorator.decorate(@notification, @receipt)
render json: @notification.as_json.merge(
is_read: @receipt.is_read
)
end
end
end
@ -49,7 +46,9 @@ class NotificationsController < ApplicationController
respond_to do |format|
format.js
format.json do
render json: NotificationDecorator.decorate(@notification, @receipt)
render json: @notification.as_json.merge(
is_read: @receipt.is_read
)
end
end
end
@ -59,7 +58,9 @@ class NotificationsController < ApplicationController
respond_to do |format|
format.js
format.json do
render json: NotificationDecorator.decorate(@notification, @receipt)
render json: @notification.as_json.merge(
is_read: @receipt.is_read
)
end
end
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class SearchController < ApplicationController
include TopicsHelper
include MapsHelper
@ -8,7 +7,7 @@ class SearchController < ApplicationController
before_action :authorize_search
after_action :verify_authorized
after_action :verify_policy_scoped, only: %i[maps mappers synapses topics]
after_action :verify_policy_scoped, only: [:maps, :mappers, :synapses, :topics]
# get /search/topics?term=SOMETERM
def topics
@ -141,13 +140,13 @@ class SearchController < ApplicationController
topic1id = params[:topic1id]
topic2id = params[:topic2id]
if term.present?
if term && !term.empty?
@synapses = policy_scope(Synapse)
.where('LOWER("desc") like ?', '%' + term.downcase.strip + '%')
.order('"desc"')
@synapses = @synapses.uniq(&:desc)
elsif topic1id.present?
elsif topic1id && !topic1id.empty?
one = policy_scope(Synapse).where(topic1_id: topic1id, topic2_id: topic2id)
two = policy_scope(Synapse).where(topic2_id: topic1id, topic1_id: topic2id)
@synapses = one + two

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class StarsController < ApplicationController
before_action :require_user
before_action :set_map

View file

@ -1,9 +1,8 @@
# frozen_string_literal: true
class SynapsesController < ApplicationController
include TopicsHelper
before_action :require_user, only: %i[create update destroy]
before_action :require_user, only: [:create, :update, :destroy]
after_action :verify_authorized, except: :index
after_action :verify_policy_scoped, only: :index

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class TokensController < ApplicationController
before_action :require_user, only: [:new]

View file

@ -1,12 +1,11 @@
# frozen_string_literal: true
class TopicsController < ApplicationController
include TopicsHelper
before_action :require_user, only: %i[create update destroy follow unfollow]
before_action :set_topic, only: %i[show update relative_numbers
relatives network destroy
follow unfollow unfollow_from_email]
before_action :require_user, only: [:create, :update, :destroy, :follow, :unfollow]
before_action :set_topic, only: [:show, :update, :relative_numbers,
:relatives, :network, :destroy,
:follow, :unfollow, :unfollow_from_email]
after_action :verify_authorized, except: :autocomplete_topic
respond_to :html, :js, :json
@ -14,7 +13,7 @@ class TopicsController < ApplicationController
# GET /topics/autocomplete_topic
def autocomplete_topic
term = params[:term]
if term.present?
if term && !term.empty?
topics = policy_scope(Topic)
.where('LOWER("name") like ?', term.downcase + '%')
.order('"name"')

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Users
class PasswordsController < Devise::PasswordsController
protected

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Users
class RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
@ -30,7 +29,7 @@ module Users
end
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: %i[name joinedwithcode])
devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :joinedwithcode])
end
def configure_account_update_params

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Users
class SessionsController < Devise::SessionsController
after_action :store_location, only: [:new]

View file

@ -1,7 +1,6 @@
# frozen_string_literal: true
class UsersController < ApplicationController
before_action :require_user, only: %i[edit update updatemetacodes update_metacode_focus]
before_action :require_user, only: [:edit, :update, :updatemetacodes, :update_metacode_focus]
respond_to :html, :json
@ -42,7 +41,7 @@ class UsersController < ApplicationController
correct_pass = @user.valid_password?(params[:current_password])
if correct_pass && @user.update_attributes(user_params)
update_follow_settings(@user, params[:settings])
update_follow_settings(@user, params[:settings]) if is_tester(@user)
@user.image = nil if params[:remove_image] == '1'
@user.save
sign_in(@user, bypass: true)
@ -101,7 +100,7 @@ class UsersController < ApplicationController
@user.settings.metacode_focus = params[:value]
@user.save
respond_to do |format|
format.json { render json: { success: 'success' } }
format.json { render json: { success: "success" }}
end
end

View file

@ -1,51 +0,0 @@
# frozen_string_literal: true
class NotificationDecorator
class << self
def decorate(notification, receipt)
result = {
id: notification.id,
type: notification.notification_code,
subject: notification.subject,
is_read: receipt.is_read,
created_at: notification.created_at,
actor: notification.sender,
data: {
object: notification.notified_object
}
}
case notification.notification_code
when MAP_ACCESS_APPROVED, MAP_ACCESS_REQUEST, MAP_INVITE_TO_EDIT
map = notification.notified_object&.map
result[:data][:map] = {
id: map&.id,
name: map&.name
}
when TOPIC_ADDED_TO_MAP
topic = notification.notified_object&.eventable
map = notification.notified_object&.map
result[:data][:topic] = {
id: topic&.id,
name: topic&.name
}
result[:data][:map] = {
id: map&.id,
name: map&.name
}
when TOPIC_CONNECTED_1, TOPIC_CONNECTED_2
topic1 = notification.notified_object&.topic1
topic2 = notification.notified_object&.topic2
result[:data][:topic1] = {
id: topic1&.id,
name: topic1&.name
}
result[:data][:topic2] = {
id: topic2&.id,
name: topic2&.name
}
end
result
end
end
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module ApplicationHelper
def invite_link
"#{request.base_url}/join" + (current_user ? "?code=#{current_user.code}" : '')

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module ContentHelper
def resource_name
:user

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module DeviseHelper
def devise_error_messages!
resource.errors.to_a[0]

View file

@ -1,4 +1,3 @@
# frozen_string_literal: true
module InMetacodeSetsHelper
end

View file

@ -1,4 +1,3 @@
# frozen_string_literal: true
module MainHelper
end

View file

@ -1,5 +1,3 @@
# frozen_string_literal: true
module MapMailerHelper
def access_approved_subject(map)
map.name + ' - access approved'
@ -7,9 +5,9 @@ module MapMailerHelper
def access_request_subject(map)
map.name + ' - request to edit'
end
end
def invite_to_edit_subject(map)
map.name + ' - invited to edit'
end
end
end

View file

@ -1,4 +1,3 @@
# frozen_string_literal: true
module MappingHelper
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module MapsHelper
# JSON autocomplete format for typeahead
def autocomplete_map_array_json(maps)

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module MetacodesHelper
def metacodeset
metacodes = current_user.settings.metacodes

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module SynapsesHelper
## this one is for building our custom JSON autocomplete format for typeahead
def autocomplete_synapse_generic_json(unique)

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module TopicMailerHelper
def added_to_map_subject(topic, map)
topic.name + ' was added to map ' + map.name
@ -8,4 +7,4 @@ module TopicMailerHelper
def connected_subject(topic)
'new synapse to topic ' + topic.name
end
end
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module TopicsHelper
## this one is for building our custom JSON autocomplete format for typeahead
def autocomplete_array_json(topics)
@ -17,7 +16,7 @@ module TopicsHelper
rtype: is_map ? 'map' : 'topic',
inmaps: is_map ? [] : t.inmaps(current_user),
inmapsLinks: is_map ? [] : t.inmaps_links(current_user),
inmapsLinks: is_map ? [] : t.inmapsLinks(current_user),
type: is_map ? metamap_metacode.name : t.metacode.name,
typeImageURL: is_map ? metamap_metacode.icon : t.metacode.icon,
mapCount: is_map ? 0 : t.maps.count,

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module UsersHelper
# build custom json autocomplete for typeahead
def autocomplete_user_array_json(users)

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class ApplicationMailer < ActionMailer::Base
default from: 'team@metamaps.cc'
layout 'mailer'
@ -7,24 +6,24 @@ class ApplicationMailer < ActionMailer::Base
class << self
def mail_for_notification(notification)
case notification.notification_code
when MAP_ACCESS_REQUEST
request = notification.notified_object
MapMailer.access_request(request)
when MAP_ACCESS_APPROVED
request = notification.notified_object
MapMailer.access_approved(request)
when MAP_INVITE_TO_EDIT
user_map = notification.notified_object
MapMailer.invite_to_edit(user_map)
when TOPIC_ADDED_TO_MAP
event = notification.notified_object
TopicMailer.added_to_map(event, notification.recipients[0])
when TOPIC_CONNECTED_1
synapse = notification.notified_object
TopicMailer.connected(synapse, synapse.topic1, notification.recipients[0])
when TOPIC_CONNECTED_2
synapse = notification.notified_object
TopicMailer.connected(synapse, synapse.topic2, notification.recipients[0])
when MAP_ACCESS_REQUEST
request = notification.notified_object
MapMailer.access_request(request)
when MAP_ACCESS_APPROVED
request = notification.notified_object
MapMailer.access_approved(request)
when MAP_INVITE_TO_EDIT
user_map = notification.notified_object
MapMailer.invite_to_edit(user_map)
when TOPIC_ADDED_TO_MAP
event = notification.notified_object
TopicMailer.added_to_map(event, notification.recipients[0])
when TOPIC_CONNECTED_1
synapse = notification.notified_object
TopicMailer.connected(synapse, synapse.topic1, notification.recipients[0])
when TOPIC_CONNECTED_2
synapse = notification.notified_object
TopicMailer.connected(synapse, synapse.topic2, notification.recipients[0])
end
end
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class MapActivityMailer < ApplicationMailer
default from: 'team@metamaps.cc'

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class MapMailer < ApplicationMailer
include MapMailerHelper
default from: 'team@metamaps.cc'

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class TopicMailer < ApplicationMailer
include TopicMailerHelper
default from: 'team@metamaps.cc'

View file

@ -1,10 +1,9 @@
# frozen_string_literal: true
class AccessRequest < ApplicationRecord
belongs_to :user
belongs_to :map
has_one :user_map
after_create :after_created_async
def approve
@ -28,9 +27,9 @@ class AccessRequest < ApplicationRecord
Mailboxer::Receipt.where(notification: notification).update_all(is_read: true)
end
end
protected
def after_created_async
NotificationService.access_request(self)
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class Attachment < ApplicationRecord
belongs_to :attachable, polymorphic: true

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Attachable
extend ActiveSupport::Concern

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Routing
extend ActiveSupport::Concern
include Rails.application.routes.url_helpers

View file

@ -1,17 +1,16 @@
# frozen_string_literal: true
class Event < ApplicationRecord
KINDS = %w[user_present_on_map user_not_present_on_map
KINDS = %w(user_present_on_map user_not_present_on_map
conversation_started_on_map
topic_added_to_map topic_moved_on_map topic_removed_from_map
synapse_added_to_map synapse_removed_from_map
topic_updated synapse_updated].freeze
topic_updated synapse_updated).freeze
belongs_to :eventable, polymorphic: true
belongs_to :map
belongs_to :user
scope :chronologically, (-> { order('created_at asc') })
scope :chronologically, -> { order('created_at asc') }
after_create :notify_webhooks!, if: :map

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Events
class ConversationStartedOnMap < Event
# after_create :notify_users!

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Events
class SynapseAddedToMap < Event
# after_create :notify_users!

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Events
class SynapseRemovedFromMap < Event
# after_create :notify_users!

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Events
class SynapseUpdated < Event
# after_create :notify_users!

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Events
class TopicAddedToMap < Event
after_create :notify_users!
@ -11,7 +10,7 @@ module Events
user: user,
meta: meta)
end
def notify_users!
# in the future, notify followers of both the topic, and the map
NotificationService.notify_followers(eventable, TOPIC_ADDED_TO_MAP, self)

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Events
class TopicMovedOnMap < Event
# after_create :notify_users!

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Events
class TopicRemovedFromMap < Event
# after_create :notify_users!

View file

@ -1,8 +1,7 @@
# frozen_string_literal: true
module Events
class TopicUpdated < Event
# after_create :notify_users!
#after_create :notify_users!
def self.publish!(topic, user, meta)
create!(kind: 'topic_updated',
@ -10,7 +9,7 @@ module Events
user: user,
meta: meta)
end
def notify_users!
NotificationService.notify_followers(eventable, 'topic_updated', self)
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Events
class UserNotPresentOnMap < Event
# after_create :notify_users!

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
module Events
class UserPresentOnMap < Event
# after_create :notify_users!

View file

@ -1,21 +1,23 @@
# frozen_string_literal: true
class Follow < ApplicationRecord
belongs_to :user
belongs_to :followed, polymorphic: true
has_one :follow_reason, dependent: :destroy
validates :user, presence: true
validates :followed, presence: true
validates :user, uniqueness: { scope: :followed, message: 'This entity is already followed by this user' }
after_create :add_subsetting
scope :active, (-> { where(muted: false) })
scope :active, -> {
where(muted: false)
}
private
def add_subsetting
FollowReason.create!(follow: self)
follow_reason = FollowReason.create!(follow: self)
end
end

View file

@ -1,13 +1,12 @@
# frozen_string_literal: true
class FollowReason < ApplicationRecord
REASONS = %w[created commented contributed followed shared_on starred].freeze
REASONS = %w(created commented contributed followed shared_on starred).freeze
belongs_to :follow
validates :follow, presence: true
def has_reason
created || commented || contributed || followed || shared_on || starred
end
end
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class InMetacodeSet < ApplicationRecord
belongs_to :metacode, class_name: 'Metacode', foreign_key: 'metacode_id'
belongs_to :metacode_set, class_name: 'MetacodeSet', foreign_key: 'metacode_set_id'

View file

@ -1,7 +1,6 @@
# frozen_string_literal: true
class Map < ApplicationRecord
ATTRS_TO_WATCH = %w[name desc permission].freeze
ATTRS_TO_WATCH = %w(name desc permission).freeze
belongs_to :user
belongs_to :source, class_name: :Map
@ -16,7 +15,7 @@ class Map < ApplicationRecord
has_many :messages, as: :resource, dependent: :destroy
has_many :stars, dependent: :destroy
has_many :follows, as: :followed, dependent: :destroy
has_many :followers, through: :follows, source: :user
has_many :followers, :through => :follows, source: :user
has_many :access_requests, dependent: :destroy
has_many :user_maps, dependent: :destroy
@ -90,10 +89,10 @@ class Map < ApplicationRecord
def as_json(_options = {})
json = super(
methods: %i[user_name user_image star_count topic_count synapse_count
contributor_count collaborator_ids screenshot_url],
except: %i[screenshot_content_type screenshot_file_size screenshot_file_name
screenshot_updated_at]
methods: [:user_name, :user_image, :star_count, :topic_count, :synapse_count,
:contributor_count, :collaborator_ids, :screenshot_url],
except: [:screenshot_content_type, :screenshot_file_size, :screenshot_file_name,
:screenshot_updated_at]
)
json[:created_at_clean] = created_at_str
json[:updated_at_clean] = updated_at_str
@ -134,16 +133,16 @@ class Map < ApplicationRecord
end
removed.compact
end
def update_deferring_topics_and_synapses
Topic.where(defer_to_map_id: id).update(permission: permission)
Synapse.where(defer_to_map_id: id).update(permission: permission)
end
protected
def after_created
FollowService.follow(self, user, 'created')
FollowService.follow(self, self.user, 'created')
# notify users following the map creator
end
@ -151,12 +150,13 @@ class Map < ApplicationRecord
return unless ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
ActionCable.server.broadcast 'map_' + id.to_s, type: 'mapUpdated'
end
def after_updated_async
return unless ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
FollowService.follow(self, updated_by, 'contributed')
# NotificationService.notify_followers(self, 'map_updated', changed_attributes)
# or better yet publish an event
if ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
FollowService.follow(self, updated_by, 'contributed')
# NotificationService.notify_followers(self, 'map_updated', changed_attributes)
# or better yet publish an event
end
end
handle_asynchronously :after_updated_async

View file

@ -1,8 +1,7 @@
# frozen_string_literal: true
class Mapping < ApplicationRecord
scope :topicmapping, (-> { where(mappable_type: :Topic) })
scope :synapsemapping, (-> { where(mappable_type: :Synapse) })
scope :topicmapping, -> { where(mappable_type: :Topic) }
scope :synapsemapping, -> { where(mappable_type: :Synapse) }
belongs_to :mappable, polymorphic: true
belongs_to :map, class_name: 'Map', foreign_key: 'map_id', touch: true
@ -25,7 +24,7 @@ class Mapping < ApplicationRecord
end
def as_json(_options = {})
super(methods: %i[user_name user_image])
super(methods: [:user_name, :user_image])
end
def after_created
@ -53,17 +52,17 @@ class Mapping < ApplicationRecord
handle_asynchronously :after_created_async
def after_updated
return unless (mappable_type == 'Topic') && (xloc_changed? || yloc_changed?)
meta = { 'x': xloc, 'y': yloc, 'mapping_id': id }
Events::TopicMovedOnMap.publish!(mappable, map, updated_by, meta)
ActionCable.server.broadcast('map_' + map.id.to_s, type: 'topicMoved',
id: mappable.id, mapping_id: id,
x: xloc, y: yloc)
if (mappable_type == 'Topic') && (xloc_changed? || yloc_changed?)
meta = { 'x': xloc, 'y': yloc, 'mapping_id': id }
Events::TopicMovedOnMap.publish!(mappable, map, updated_by, meta)
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicMoved', id: mappable.id, mapping_id: id, x: xloc, y: yloc
end
end
def after_updated_async
return unless (mappable_type == 'Topic') && (xloc_changed? || yloc_changed?)
FollowService.follow(map, updated_by, 'contributed')
if (mappable_type == 'Topic') && (xloc_changed? || yloc_changed?)
FollowService.follow(map, updated_by, 'contributed')
end
end
handle_asynchronously :after_updated_async

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class Message < ApplicationRecord
belongs_to :user
belongs_to :resource, polymorphic: true
@ -7,14 +6,15 @@ class Message < ApplicationRecord
delegate :name, to: :user, prefix: true
after_create :after_created
# after_create :after_created_async
#after_create :after_created_async
def user_image
user.image.url
end
def as_json(_options = {})
json = super(methods: %i[user_name user_image])
json = super(methods: [:user_name, :user_image])
json
end

View file

@ -1,16 +1,17 @@
# frozen_string_literal: true
class Metacode < ApplicationRecord
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] },
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: %r{\Aimage/.*\Z}
validates_attachment_content_type :aws_icon, content_type: /\Aimage\/.*\Z/
validate :aws_xor_manual_icon
validate :manual_icon_https
@ -29,13 +30,15 @@ class Metacode < ApplicationRecord
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'
)
default.except('aws_icon_file_name', 'aws_icon_content_type', 'aws_icon_file_size', 'aws_icon_updated_at', 'manual_icon')
end
def in_metacode_set(metacode_set)
def hasSelected(user)
return true if user.settings.metacodes.include? id.to_s
false
end
def inMetacodeSet(metacode_set)
return true if metacode_sets.include? metacode_set
false
end
@ -52,9 +55,10 @@ class Metacode < ApplicationRecord
end
def manual_icon_https
return if manual_icon.blank?
unless manual_icon.starts_with? 'https'
errors.add(:base, 'Manual icon must begin with https')
if manual_icon.present?
unless manual_icon.starts_with? 'https'
errors.add(:base, 'Manual icon must begin with https')
end
end
end
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class MetacodeSet < ApplicationRecord
belongs_to :user
has_many :in_metacode_sets

View file

@ -1,18 +1,13 @@
# frozen_string_literal: true
class PermittedParams
%w[map synapse topic mapping token].each do |kind|
class PermittedParams < Struct.new(:params)
%w(map synapse topic mapping token).each do |kind|
define_method(kind) do
permitted_attributes = send("#{kind}_attributes")
@params.require(kind).permit(*permitted_attributes)
params.require(kind).permit(*permitted_attributes)
end
alias_method :"api_#{kind}", kind.to_sym
end
def initialize(params)
@params = params
end
alias read_attribute_for_serialization send
def token_attributes
@ -20,18 +15,18 @@ class PermittedParams
end
def map_attributes
%i[name desc permission arranged]
[:name, :desc, :permission, :arranged]
end
def synapse_attributes
%i[desc category weight permission topic1_id topic2_id]
[:desc, :category, :weight, :permission, :topic1_id, :topic2_id]
end
def topic_attributes
%i[name desc link permission metacode_id]
[:name, :desc, :link, :permission, :metacode_id]
end
def mapping_attributes
%i[xloc yloc map_id mappable_type mappable_id]
[:xloc, :yloc, :map_id, :mappable_type, :mappable_id]
end
end

View file

@ -1,21 +1,20 @@
# frozen_string_literal: true
class Star < ApplicationRecord
class Star < ActiveRecord::Base
belongs_to :user
belongs_to :map
validates :map, uniqueness: { scope: :user, message: 'You have already starred this map' }
# after_create :after_created_async
# before_destroy :before_destroyed
#after_create :after_created_async
#before_destroy :before_destroyed
protected
def after_created_async
FollowService.follow(map, user, 'starred')
NotificationService.notify_followers(map, 'map_starred', self, 'created')
end
handle_asynchronously :after_created_async
def before_destroyed
FollowService.remove_reason(map, user, 'starred')
end

View file

@ -1,7 +1,6 @@
# frozen_string_literal: true
class Synapse < ApplicationRecord
ATTRS_TO_WATCH = %w[desc category permission defer_to_map_id].freeze
ATTRS_TO_WATCH = %w(desc category permission defer_to_map_id).freeze
belongs_to :user
belongs_to :defer_to_map, class_name: 'Map', foreign_key: 'defer_to_map_id'
@ -22,9 +21,9 @@ class Synapse < ApplicationRecord
validates :category, inclusion: { in: ['from-to', 'both'], allow_nil: true }
scope :for_topic, (lambda do |topic_id = nil|
scope :for_topic, ->(topic_id = nil) {
where(topic1_id: topic_id).or(where(topic2_id: topic_id))
end)
}
before_create :set_perm_by_defer
after_create :after_created_async
@ -39,7 +38,7 @@ class Synapse < ApplicationRecord
def collaborator_ids
if defer_to_map
defer_to_map.editors.reject { |mapper| mapper == user }.map(&:id)
defer_to_map.editors.select { |mapper| mapper != user }.map(&:id)
else
[]
end
@ -55,7 +54,7 @@ class Synapse < ApplicationRecord
end
def as_json(_options = {})
super(methods: %i[user_name user_image collaborator_ids])
super(methods: [:user_name, :user_image, :collaborator_ids])
end
def as_rdf
@ -73,7 +72,7 @@ class Synapse < ApplicationRecord
protected
def set_perm_by_defer
defer_to_map&.permission
permission = defer_to_map.permission if defer_to_map
end
def after_created_async
@ -83,21 +82,21 @@ class Synapse < ApplicationRecord
handle_asynchronously :after_created_async
def after_updated
return unless ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
new = attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
old = changed_attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
meta = new.merge(old) # we are prioritizing the old values, keeping them
meta['changed'] = changed_attributes.keys.select { |k| ATTRS_TO_WATCH.include?(k) }
Events::SynapseUpdated.publish!(self, updated_by, meta)
maps.each do |map|
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'synapseUpdated', id: id
if ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
new = attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
old = changed_attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
meta = new.merge(old) # we are prioritizing the old values, keeping them
meta['changed'] = changed_attributes.keys.select { |k| ATTRS_TO_WATCH.include?(k) }
Events::SynapseUpdated.publish!(self, updated_by, meta)
maps.each do |map|
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'synapseUpdated', id: id
end
end
end
def before_destroyed
# hard to know how to do this yet, because the synapse actually gets destroyed
# NotificationService.notify_followers(topic1, 'topic_disconnected', self)
# NotificationService.notify_followers(topic2, 'topic_disconnected', self)
#NotificationService.notify_followers(topic1, 'topic_disconnected', self)
#NotificationService.notify_followers(topic2, 'topic_disconnected', self)
end
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class Token < ApplicationRecord
belongs_to :user

View file

@ -1,7 +1,6 @@
# frozen_string_literal: true
class Topic < ApplicationRecord
ATTRS_TO_WATCH = %w[name desc link metacode_id permission defer_to_map_id].freeze
ATTRS_TO_WATCH = %w(name desc link metacode_id permission defer_to_map_id).freeze
include TopicsHelper
include Attachable
@ -17,7 +16,7 @@ class Topic < ApplicationRecord
has_many :mappings, as: :mappable, dependent: :destroy
has_many :maps, through: :mappings
has_many :follows, as: :followed, dependent: :destroy
has_many :followers, through: :follows, source: :user
has_many :followers, :through => :follows, source: :user
belongs_to :metacode
@ -26,7 +25,7 @@ class Topic < ApplicationRecord
after_create :after_created_async
after_update :after_updated
after_update :after_updated_async
# before_destroy :before_destroyed
#before_destroy :before_destroyed
validates :permission, presence: true
validates :permission, inclusion: { in: Perm::ISSIONS.map(&:to_s) }
@ -39,13 +38,13 @@ class Topic < ApplicationRecord
topics1.or(topics2)
end
scope :relatives, (lambda do |topic_id = nil, user = nil|
scope :relatives, ->(topic_id = nil, user = nil) {
# should only see topics through *visible* synapses
# e.g. Topic A (commons) -> synapse (private) -> Topic B (commons) must be filtered out
topic_ids = Pundit.policy_scope(user, Synapse.where(topic1_id: topic_id)).pluck(:topic2_id)
topic_ids += Pundit.policy_scope(user, Synapse.where(topic2_id: topic_id)).pluck(:topic1_id)
where(id: topic_ids.uniq)
end)
}
delegate :name, to: :user, prefix: true
@ -65,13 +64,13 @@ class Topic < ApplicationRecord
Pundit.policy_scope(user, maps).map(&:name)
end
def inmaps_links(user)
def inmapsLinks(user)
Pundit.policy_scope(user, maps).map(&:id)
end
def as_json(options = {})
super(methods: %i[user_name user_image collaborator_ids])
.merge(inmaps: inmaps(options[:user]), inmapsLinks: inmaps_links(options[:user]),
super(methods: [:user_name, :user_image, :collaborator_ids])
.merge(inmaps: inmaps(options[:user]), inmapsLinks: inmapsLinks(options[:user]),
map_count: map_count(options[:user]), synapse_count: synapse_count(options[:user]))
end
@ -90,7 +89,7 @@ class Topic < ApplicationRecord
def collaborator_ids
if defer_to_map
defer_to_map.editors.reject { |mapper| mapper == user }.map(&:id)
defer_to_map.editors.select { |mapper| mapper != user }.map(&:id)
else
[]
end
@ -127,9 +126,14 @@ class Topic < ApplicationRecord
end
end
end
return output if output_format == 'array'
return output.join('; ') if output_format == 'text'
raise 'invalid argument to synapses_csv'
if output_format == 'array'
return output
elsif output_format == 'text'
return output.join('; ')
else
raise 'invalid argument to synapses_csv'
end
output
end
def topic_autocomplete_method
@ -139,7 +143,7 @@ class Topic < ApplicationRecord
protected
def set_perm_by_defer
self.permission = defer_to_map.permission if defer_to_map
permission = defer_to_map.permission if defer_to_map
end
def create_metamap?
@ -150,35 +154,35 @@ class Topic < ApplicationRecord
self.link = Rails.application.routes.url_helpers
.map_url(host: ENV['MAILER_DEFAULT_URL'], id: @map.id)
end
def after_created_async
FollowService.follow(self, user, 'created')
FollowService.follow(self, self.user, 'created')
# notify users following the topic creator
end
handle_asynchronously :after_created_async
def after_updated
return unless ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
new = attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
old = changed_attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
meta = new.merge(old) # we are prioritizing the old values, keeping them
meta['changed'] = changed_attributes.keys.select { |k| ATTRS_TO_WATCH.include?(k) }
Events::TopicUpdated.publish!(self, updated_by, meta)
maps.each do |map|
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicUpdated', id: id
if ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
new = attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
old = changed_attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
meta = new.merge(old) # we are prioritizing the old values, keeping them
meta['changed'] = changed_attributes.keys.select { |k| ATTRS_TO_WATCH.include?(k) }
Events::TopicUpdated.publish!(self, updated_by, meta)
maps.each do |map|
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicUpdated', id: id
end
end
end
def after_updated_async
return unless ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
FollowService.follow(self, updated_by, 'contributed')
if ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
FollowService.follow(self, updated_by, 'contributed')
end
end
handle_asynchronously :after_updated_async
def before_destroyed
# hard to know how to do this yet, because the topic actually gets destroyed
# NotificationService.notify_followers(self, 'topic_deleted', ?)
#NotificationService.notify_followers(self, 'topic_deleted', ?)
end
end

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
require 'open-uri'
class User < ApplicationRecord
@ -52,24 +51,26 @@ class User < ApplicationRecord
validates_attachment_content_type :image, content_type: %r{\Aimage/.*\Z}
# override default as_json
def as_json(options = {})
def as_json(_options = {})
json = { id: id,
name: name,
image: image.url(:sixtyfour),
admin: admin }
if options[:follows]
name: name,
image: image.url(:sixtyfour),
admin: admin }
if (_options[:follows])
json['follows'] = {
topics: following.active.where(followed_type: 'Topic').to_a.map(&:followed_id),
maps: following.active.where(followed_type: 'Map').to_a.map(&:followed_id)
}
end
if options[:follow_settings]
json['follow_topic_on_created'] = settings.follow_topic_on_created == '1'
json['follow_topic_on_contributed'] = settings.follow_topic_on_contributed == '1'
json['follow_map_on_created'] = settings.follow_map_on_created == '1'
json['follow_map_on_contributed'] = settings.follow_map_on_contributed == '1'
if (_options[:follow_settings])
json['follow_topic_on_created'] = settings.follow_topic_on_created == "1"
json['follow_topic_on_contributed'] = settings.follow_topic_on_contributed == "1"
json['follow_map_on_created'] = settings.follow_map_on_created == "1"
json['follow_map_on_contributed'] = settings.follow_map_on_contributed == "1"
end
if (_options[:email])
json['email'] = email
end
json['email'] = email if options[:email]
json
end
@ -106,14 +107,10 @@ class User < ApplicationRecord
end
def most_used_metacodes
metacode_counts = topics.to_a.each_with_object(Hash.new(0)) do |topic, list_so_far|
list_so_far[topic.metacode_id] += 1
list_so_far
end
id_count_pairs = metacode_counts.to_a
id_count_pairs.sort! { |a, b| b[1] <=> a[1] }
metacode_ids = id_count_pairs.map { |i| i[0] }
metacode_ids.slice(0, 5)
topics.to_a.each_with_object(Hash.new(0)) do |topic, memo|
memo[topic.metacode_id] += 1
memo
end.to_a.sort { |a, b| b[1] <=> a[1] }.map { |i| i[0] }.slice(0, 5)
end
# generate a random 8 letter/digit code that they can use to invite people
@ -136,20 +133,20 @@ class User < ApplicationRecord
end
def has_map_open(map)
latest_event = Event.where(map: map, user: self)
.where(kind: %w[user_present_on_map user_not_present_on_map])
.order(:created_at)
.last
latest_event && latest_event.kind == 'user_present_on_map'
latestEvent = Event.where(map: map, user: self)
.where(kind: ['user_present_on_map', 'user_not_present_on_map'])
.order(:created_at)
.last
latestEvent && latestEvent.kind == 'user_present_on_map'
end
def has_map_with_synapse_open(synapse)
synapse.maps.any? { |map| has_map_open(map) }
synapse.maps.any?{|map| has_map_open(map)}
end
def settings
self[:settings] = UserPreference.new if self[:settings].nil?
unless self[:settings].respond_to?(:follow_topic_on_created)
if not self[:settings].respond_to?(:follow_topic_on_created)
self[:settings].initialize_follow_settings
end
self[:settings]

View file

@ -1,10 +1,9 @@
# frozen_string_literal: true
class UserMap < ApplicationRecord
belongs_to :map
belongs_to :user
belongs_to :access_request
after_create :after_created_async
before_destroy :before_destroyed
@ -13,20 +12,20 @@ class UserMap < ApplicationRecord
Mailboxer::Receipt.where(notification: notification).update_all(is_read: true)
end
end
protected
def after_created_async
FollowService.follow(map, user, 'shared_on')
if access_request
NotificationService.access_approved(access_request)
NotificationService.access_approved(self.access_request)
else
NotificationService.invite_to_edit(self)
end
# NotificationService.notify_followers(map, 'map_collaborator_added', self, 'shared_on')
end
handle_asynchronously :after_created_async
def before_destroyed
FollowService.remove_reason(map, user, 'shared_on')
end

View file

@ -1,34 +1,29 @@
# frozen_string_literal: true
class UserPreference
attr_accessor :metacodes, :metacode_focus, :follow_topic_on_created, :follow_topic_on_contributed,
:follow_map_on_created, :follow_map_on_contributed
def initialize
@metacodes = init_metacodes.compact
@metacode_focus = @metacodes[0]
initialize_follow_settings
end
private
def init_metacodes
%w[Action Aim Idea Question Note Wildcard Subject].map do |m|
array = []
%w(Action Aim Idea Question Note Wildcard Subject).each do |m|
begin
metacode = Metacode.find_by(name: m)
metacode.id.to_s if metacode
array.push(metacode.id.to_s) if metacode
rescue ActiveRecord::StatementInvalid
if m == 'Action'
Rails.logger.warn('TODO: remove this travis workaround in user_preference.rb')
end
end
end.compact
end
@metacodes = array
@metacode_focus = array[0]
initialize_follow_settings
end
def initialize_follow_settings
@follow_topic_on_created = false
@follow_topic_on_contributed = false
@follow_map_on_created = true
@follow_map_on_created = false
@follow_map_on_contributed = false
end
end

View file

@ -1,11 +1,10 @@
# frozen_string_literal: true
class Webhook < ApplicationRecord
belongs_to :hookable, polymorphic: true
validates :uri, presence: true
validates :hookable, presence: true
validates :kind, inclusion: { in: %w[slack] }
validates :kind, inclusion: { in: %w(slack) }
validates :event_types, length: { minimum: 1 }
def headers

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
Webhooks::Slack::Base = Struct.new(:webhook, :event) do
include Routing

View file

@ -1,5 +1,4 @@
# frozen_string_literal: true
class Webhooks::Slack::ConversationStartedOnMap < Webhooks::Slack::Base
def text
"There is a live conversation starting on map *#{event.map.name}*. #{view_map_on_metamaps('Join in!')}"

View file

@ -1,10 +1,7 @@
# frozen_string_literal: true
class Webhooks::Slack::SynapseAddedToMap < Webhooks::Slack::Base
def text
connector = eventable.desc.empty? ? '->' : eventable.desc
"\"*#{eventable.topic1.name}* #{connector} *#{eventable.topic2.name}*\"" \
" was added as a connection by *#{event.user.name}*" \
" to the map *#{view_map_on_metamaps}*"
"\"*#{eventable.topic1.name}* #{connector} *#{eventable.topic2.name}*\" was added as a connection by *#{event.user.name}* to the map *#{view_map_on_metamaps}*"
end
end

View file

@ -1,13 +1,8 @@
# frozen_string_literal: true
class Webhooks::Slack::SynapseRemovedFromMap < Webhooks::Slack::Base
def text
connector = eventable.desc.empty? ? '->' : eventable.desc
# TODO: express correct directionality of arrows when desc is empty
"\"*#{eventable.topic1.name}* #{connector} *#{eventable.topic2.name}*\"" \
" was removed by *#{event.user.name}* as a connection" \
" from the map *#{view_map_on_metamaps}*"
"\"*#{eventable.topic1.name}* #{connector} *#{eventable.topic2.name}*\" was removed by *#{event.user.name}* as a connection from the map *#{view_map_on_metamaps}*"
end
end

Some files were not shown because too many files have changed in this diff Show more