Merge branch 'develop'
This commit is contained in:
commit
50639e8a0a
164 changed files with 6496 additions and 732 deletions
|
@ -6,7 +6,7 @@ export DB_USERNAME='postgres'
|
||||||
export DB_PASSWORD='3112'
|
export DB_PASSWORD='3112'
|
||||||
export DB_HOST='localhost'
|
export DB_HOST='localhost'
|
||||||
export DB_PORT='5432'
|
export DB_PORT='5432'
|
||||||
export DB_NAME='metamap002'
|
export DB_NAME='metamaps'
|
||||||
|
|
||||||
export REALTIME_SERVER='http://localhost:5000'
|
export REALTIME_SERVER='http://localhost:5000'
|
||||||
export MAILER_DEFAULT_URL='localhost:3000'
|
export MAILER_DEFAULT_URL='localhost:3000'
|
||||||
|
|
9
.github/ISSUE_TEMPLATE.md
vendored
9
.github/ISSUE_TEMPLATE.md
vendored
|
@ -1,12 +1,3 @@
|
||||||
please link to related trello cards, if they exist, from the following two boards respectively
|
|
||||||
|
|
||||||
https://trello.com/b/8HlCikOX/metamaps-design
|
|
||||||
|
|
||||||
https://trello.com/b/uFOA6a2x/metamaps-feedback-feature-ideas-requests
|
|
||||||
|
|
||||||
[the issue as framed for design]()
|
|
||||||
|
|
||||||
[the issue as framed from the users perspective]()
|
|
||||||
|
|
||||||
|
|
||||||
============
|
============
|
||||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -29,3 +29,5 @@ coverage
|
||||||
*/.DS_Store
|
*/.DS_Store
|
||||||
.DS_Store?
|
.DS_Store?
|
||||||
.vagrant
|
.vagrant
|
||||||
|
gentle/
|
||||||
|
startserver.sh
|
||||||
|
|
|
@ -19,3 +19,6 @@ Metrics/AbcSize:
|
||||||
|
|
||||||
Style/Documentation:
|
Style/Documentation:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
Style/EmptyMethod:
|
||||||
|
EnforcedStyle: expanded
|
||||||
|
|
8
Gemfile
8
Gemfile
|
@ -9,7 +9,6 @@ gem 'aws-sdk'
|
||||||
gem 'best_in_place'
|
gem 'best_in_place'
|
||||||
gem 'delayed_job'
|
gem 'delayed_job'
|
||||||
gem 'delayed_job_active_record'
|
gem 'delayed_job_active_record'
|
||||||
gem 'sucker_punch'
|
|
||||||
gem 'devise'
|
gem 'devise'
|
||||||
gem 'doorkeeper'
|
gem 'doorkeeper'
|
||||||
gem 'dotenv-rails'
|
gem 'dotenv-rails'
|
||||||
|
@ -20,6 +19,7 @@ gem 'kaminari'
|
||||||
gem 'mailboxer'
|
gem 'mailboxer'
|
||||||
gem 'paperclip'
|
gem 'paperclip'
|
||||||
gem 'pg'
|
gem 'pg'
|
||||||
|
gem 'puma'
|
||||||
gem 'pundit'
|
gem 'pundit'
|
||||||
gem 'pundit_extra'
|
gem 'pundit_extra'
|
||||||
gem 'rack-attack'
|
gem 'rack-attack'
|
||||||
|
@ -27,7 +27,7 @@ gem 'rack-cors'
|
||||||
gem 'redis'
|
gem 'redis'
|
||||||
gem 'slack-notifier'
|
gem 'slack-notifier'
|
||||||
gem 'snorlax'
|
gem 'snorlax'
|
||||||
gem 'puma'
|
gem 'sucker_punch'
|
||||||
|
|
||||||
# asset stuff
|
# asset stuff
|
||||||
gem 'jquery-rails'
|
gem 'jquery-rails'
|
||||||
|
@ -36,12 +36,12 @@ gem 'sass-rails'
|
||||||
gem 'uglifier'
|
gem 'uglifier'
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
|
gem 'brakeman', require: false
|
||||||
gem 'factory_girl_rails'
|
gem 'factory_girl_rails'
|
||||||
gem 'json-schema'
|
gem 'json-schema'
|
||||||
gem 'rspec-rails'
|
gem 'rspec-rails'
|
||||||
gem 'shoulda-matchers'
|
gem 'shoulda-matchers'
|
||||||
gem 'simplecov', require: false
|
gem 'simplecov', require: false
|
||||||
gem 'brakeman', require: false
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
|
@ -49,6 +49,6 @@ group :development, :test do
|
||||||
gem 'binding_of_caller'
|
gem 'binding_of_caller'
|
||||||
gem 'pry-byebug'
|
gem 'pry-byebug'
|
||||||
gem 'pry-rails'
|
gem 'pry-rails'
|
||||||
gem 'tunemygc'
|
|
||||||
gem 'rubocop'
|
gem 'rubocop'
|
||||||
|
gem 'tunemygc'
|
||||||
end
|
end
|
||||||
|
|
201
Gemfile.lock
201
Gemfile.lock
|
@ -1,57 +1,60 @@
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (5.0.0.1)
|
actioncable (5.0.1)
|
||||||
actionpack (= 5.0.0.1)
|
actionpack (= 5.0.1)
|
||||||
nio4r (~> 1.2)
|
nio4r (~> 1.2)
|
||||||
websocket-driver (~> 0.6.1)
|
websocket-driver (~> 0.6.1)
|
||||||
actionmailer (5.0.0.1)
|
actionmailer (5.0.1)
|
||||||
actionpack (= 5.0.0.1)
|
actionpack (= 5.0.1)
|
||||||
actionview (= 5.0.0.1)
|
actionview (= 5.0.1)
|
||||||
activejob (= 5.0.0.1)
|
activejob (= 5.0.1)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (5.0.0.1)
|
actionpack (5.0.1)
|
||||||
actionview (= 5.0.0.1)
|
actionview (= 5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
rack (~> 2.0)
|
rack (~> 2.0)
|
||||||
rack-test (~> 0.6.3)
|
rack-test (~> 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
actionview (5.0.0.1)
|
actionview (5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubis (~> 2.7.0)
|
erubis (~> 2.7.0)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
active_model_serializers (0.10.2)
|
active_model_serializers (0.10.4)
|
||||||
actionpack (>= 4.1, < 6)
|
actionpack (>= 4.1, < 6)
|
||||||
activemodel (>= 4.1, < 6)
|
activemodel (>= 4.1, < 6)
|
||||||
jsonapi (~> 0.1.1.beta2)
|
case_transform (>= 0.2)
|
||||||
railties (>= 4.1, < 6)
|
jsonapi (= 0.1.1.beta6)
|
||||||
activejob (5.0.0.1)
|
activejob (5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (5.0.0.1)
|
activemodel (5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
activerecord (5.0.0.1)
|
activerecord (5.0.1)
|
||||||
activemodel (= 5.0.0.1)
|
activemodel (= 5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
arel (~> 7.0)
|
arel (~> 7.0)
|
||||||
activesupport (5.0.0.1)
|
activesupport (5.0.1)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (~> 0.7)
|
i18n (~> 0.7)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
addressable (2.3.8)
|
addressable (2.5.0)
|
||||||
arel (7.1.2)
|
public_suffix (~> 2.0, >= 2.0.2)
|
||||||
|
arel (7.1.4)
|
||||||
ast (2.3.0)
|
ast (2.3.0)
|
||||||
aws-sdk (2.6.3)
|
aws-sdk (2.7.0)
|
||||||
aws-sdk-resources (= 2.6.3)
|
aws-sdk-resources (= 2.7.0)
|
||||||
aws-sdk-core (2.6.3)
|
aws-sdk-core (2.7.0)
|
||||||
|
aws-sigv4 (~> 1.0)
|
||||||
jmespath (~> 1.0)
|
jmespath (~> 1.0)
|
||||||
aws-sdk-resources (2.6.3)
|
aws-sdk-resources (2.7.0)
|
||||||
aws-sdk-core (= 2.6.3)
|
aws-sdk-core (= 2.7.0)
|
||||||
|
aws-sigv4 (1.0.0)
|
||||||
bcrypt (3.1.11)
|
bcrypt (3.1.11)
|
||||||
best_in_place (3.1.0)
|
best_in_place (3.1.0)
|
||||||
actionpack (>= 3.2)
|
actionpack (>= 3.2)
|
||||||
|
@ -62,21 +65,20 @@ GEM
|
||||||
rack (>= 0.9.0)
|
rack (>= 0.9.0)
|
||||||
binding_of_caller (0.7.2)
|
binding_of_caller (0.7.2)
|
||||||
debug_inspector (>= 0.0.1)
|
debug_inspector (>= 0.0.1)
|
||||||
brakeman (3.4.0)
|
brakeman (3.4.1)
|
||||||
builder (3.2.2)
|
builder (3.2.3)
|
||||||
byebug (9.0.5)
|
byebug (9.0.6)
|
||||||
carrierwave (0.11.2)
|
carrierwave (1.0.0)
|
||||||
activemodel (>= 3.2.0)
|
activemodel (>= 4.0.0)
|
||||||
activesupport (>= 3.2.0)
|
activesupport (>= 4.0.0)
|
||||||
json (>= 1.7)
|
|
||||||
mime-types (>= 1.16)
|
mime-types (>= 1.16)
|
||||||
mimemagic (>= 0.3.0)
|
case_transform (0.2)
|
||||||
climate_control (0.0.3)
|
activesupport
|
||||||
activesupport (>= 3.0)
|
climate_control (0.1.0)
|
||||||
cocaine (0.5.8)
|
cocaine (0.5.8)
|
||||||
climate_control (>= 0.0.3, < 1.0)
|
climate_control (>= 0.0.3, < 1.0)
|
||||||
coderay (1.1.1)
|
coderay (1.1.1)
|
||||||
concurrent-ruby (1.0.2)
|
concurrent-ruby (1.0.4)
|
||||||
debug_inspector (0.0.2)
|
debug_inspector (0.0.2)
|
||||||
delayed_job (4.1.2)
|
delayed_job (4.1.2)
|
||||||
activesupport (>= 3.0, < 5.1)
|
activesupport (>= 3.0, < 5.1)
|
||||||
|
@ -89,23 +91,23 @@ GEM
|
||||||
railties (>= 4.1.0, < 5.1)
|
railties (>= 4.1.0, < 5.1)
|
||||||
responders
|
responders
|
||||||
warden (~> 1.2.3)
|
warden (~> 1.2.3)
|
||||||
diff-lcs (1.2.5)
|
diff-lcs (1.3)
|
||||||
docile (1.1.5)
|
docile (1.1.5)
|
||||||
doorkeeper (4.2.0)
|
doorkeeper (4.2.0)
|
||||||
railties (>= 4.2)
|
railties (>= 4.2)
|
||||||
dotenv (2.1.1)
|
dotenv (2.1.2)
|
||||||
dotenv-rails (2.1.1)
|
dotenv-rails (2.1.2)
|
||||||
dotenv (= 2.1.1)
|
dotenv (= 2.1.2)
|
||||||
railties (>= 4.0, < 5.1)
|
railties (>= 3.2, < 5.1)
|
||||||
erubis (2.7.0)
|
erubis (2.7.0)
|
||||||
exception_notification (4.2.1)
|
exception_notification (4.2.1)
|
||||||
actionmailer (>= 4.0, < 6)
|
actionmailer (>= 4.0, < 6)
|
||||||
activesupport (>= 4.0, < 6)
|
activesupport (>= 4.0, < 6)
|
||||||
execjs (2.7.0)
|
execjs (2.7.0)
|
||||||
factory_girl (4.7.0)
|
factory_girl (4.8.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
factory_girl_rails (4.7.0)
|
factory_girl_rails (4.8.0)
|
||||||
factory_girl (~> 4.7.0)
|
factory_girl (~> 4.8.0)
|
||||||
railties (>= 3.0.0)
|
railties (>= 3.0.0)
|
||||||
globalid (0.3.7)
|
globalid (0.3.7)
|
||||||
activesupport (>= 4.1.0)
|
activesupport (>= 4.1.0)
|
||||||
|
@ -113,20 +115,32 @@ GEM
|
||||||
multi_xml (>= 0.5.2)
|
multi_xml (>= 0.5.2)
|
||||||
i18n (0.7.0)
|
i18n (0.7.0)
|
||||||
jmespath (1.3.1)
|
jmespath (1.3.1)
|
||||||
jquery-rails (4.2.1)
|
jquery-rails (4.2.2)
|
||||||
rails-dom-testing (>= 1, < 3)
|
rails-dom-testing (>= 1, < 3)
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
jquery-ui-rails (5.0.5)
|
jquery-ui-rails (6.0.1)
|
||||||
railties (>= 3.2.16)
|
railties (>= 3.2.16)
|
||||||
json (1.8.3)
|
json (2.0.3)
|
||||||
json-schema (2.6.2)
|
json-schema (2.7.0)
|
||||||
addressable (~> 2.3.8)
|
addressable (>= 2.4)
|
||||||
jsonapi (0.1.1.beta2)
|
jsonapi (0.1.1.beta6)
|
||||||
json (~> 1.8)
|
jsonapi-parser (= 0.1.1.beta3)
|
||||||
kaminari (0.17.0)
|
jsonapi-renderer (= 0.1.1.beta1)
|
||||||
actionpack (>= 3.0.0)
|
jsonapi-parser (0.1.1.beta3)
|
||||||
activesupport (>= 3.0.0)
|
jsonapi-renderer (0.1.1.beta1)
|
||||||
|
kaminari (1.0.1)
|
||||||
|
activesupport (>= 4.1.0)
|
||||||
|
kaminari-actionview (= 1.0.1)
|
||||||
|
kaminari-activerecord (= 1.0.1)
|
||||||
|
kaminari-core (= 1.0.1)
|
||||||
|
kaminari-actionview (1.0.1)
|
||||||
|
actionview
|
||||||
|
kaminari-core (= 1.0.1)
|
||||||
|
kaminari-activerecord (1.0.1)
|
||||||
|
activerecord
|
||||||
|
kaminari-core (= 1.0.1)
|
||||||
|
kaminari-core (1.0.1)
|
||||||
loofah (2.0.3)
|
loofah (2.0.3)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
mail (2.6.4)
|
mail (2.6.4)
|
||||||
|
@ -140,12 +154,11 @@ GEM
|
||||||
mime-types-data (3.2016.0521)
|
mime-types-data (3.2016.0521)
|
||||||
mimemagic (0.3.2)
|
mimemagic (0.3.2)
|
||||||
mini_portile2 (2.1.0)
|
mini_portile2 (2.1.0)
|
||||||
minitest (5.9.1)
|
minitest (5.10.1)
|
||||||
multi_xml (0.5.5)
|
multi_xml (0.6.0)
|
||||||
nio4r (1.2.1)
|
nio4r (1.2.1)
|
||||||
nokogiri (1.6.8)
|
nokogiri (1.7.0.1)
|
||||||
mini_portile2 (~> 2.1.0)
|
mini_portile2 (~> 2.1.0)
|
||||||
pkg-config (~> 1.1.7)
|
|
||||||
orm_adapter (0.5.0)
|
orm_adapter (0.5.0)
|
||||||
paperclip (5.1.0)
|
paperclip (5.1.0)
|
||||||
activemodel (>= 4.2.0)
|
activemodel (>= 4.2.0)
|
||||||
|
@ -153,20 +166,20 @@ GEM
|
||||||
cocaine (~> 0.5.5)
|
cocaine (~> 0.5.5)
|
||||||
mime-types
|
mime-types
|
||||||
mimemagic (~> 0.3.0)
|
mimemagic (~> 0.3.0)
|
||||||
parser (2.3.1.4)
|
parser (2.3.3.1)
|
||||||
ast (~> 2.2)
|
ast (~> 2.2)
|
||||||
pg (0.19.0)
|
pg (0.19.0)
|
||||||
pkg-config (1.1.7)
|
|
||||||
powerpack (0.1.1)
|
powerpack (0.1.1)
|
||||||
pry (0.10.4)
|
pry (0.10.4)
|
||||||
coderay (~> 1.1.0)
|
coderay (~> 1.1.0)
|
||||||
method_source (~> 0.8.1)
|
method_source (~> 0.8.1)
|
||||||
slop (~> 3.4)
|
slop (~> 3.4)
|
||||||
pry-byebug (3.4.0)
|
pry-byebug (3.4.2)
|
||||||
byebug (~> 9.0)
|
byebug (~> 9.0)
|
||||||
pry (~> 0.10)
|
pry (~> 0.10)
|
||||||
pry-rails (0.3.4)
|
pry-rails (0.3.4)
|
||||||
pry (>= 0.9.10)
|
pry (>= 0.9.10)
|
||||||
|
public_suffix (2.0.5)
|
||||||
puma (3.6.2)
|
puma (3.6.2)
|
||||||
pundit (1.1.0)
|
pundit (1.1.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
|
@ -177,35 +190,35 @@ GEM
|
||||||
rack-cors (0.4.0)
|
rack-cors (0.4.0)
|
||||||
rack-test (0.6.3)
|
rack-test (0.6.3)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
rails (5.0.0.1)
|
rails (5.0.1)
|
||||||
actioncable (= 5.0.0.1)
|
actioncable (= 5.0.1)
|
||||||
actionmailer (= 5.0.0.1)
|
actionmailer (= 5.0.1)
|
||||||
actionpack (= 5.0.0.1)
|
actionpack (= 5.0.1)
|
||||||
actionview (= 5.0.0.1)
|
actionview (= 5.0.1)
|
||||||
activejob (= 5.0.0.1)
|
activejob (= 5.0.1)
|
||||||
activemodel (= 5.0.0.1)
|
activemodel (= 5.0.1)
|
||||||
activerecord (= 5.0.0.1)
|
activerecord (= 5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
bundler (>= 1.3.0, < 2.0)
|
bundler (>= 1.3.0, < 2.0)
|
||||||
railties (= 5.0.0.1)
|
railties (= 5.0.1)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
rails-dom-testing (2.0.1)
|
rails-dom-testing (2.0.2)
|
||||||
activesupport (>= 4.2.0, < 6.0)
|
activesupport (>= 4.2.0, < 6.0)
|
||||||
nokogiri (~> 1.6.0)
|
nokogiri (~> 1.6)
|
||||||
rails-html-sanitizer (1.0.3)
|
rails-html-sanitizer (1.0.3)
|
||||||
loofah (~> 2.0)
|
loofah (~> 2.0)
|
||||||
railties (5.0.0.1)
|
railties (5.0.1)
|
||||||
actionpack (= 5.0.0.1)
|
actionpack (= 5.0.1)
|
||||||
activesupport (= 5.0.0.1)
|
activesupport (= 5.0.1)
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.18.1, < 2.0)
|
thor (>= 0.18.1, < 2.0)
|
||||||
rainbow (2.1.0)
|
rainbow (2.2.1)
|
||||||
rake (11.3.0)
|
rake (12.0.0)
|
||||||
redis (3.3.1)
|
redis (3.3.2)
|
||||||
responders (2.3.0)
|
responders (2.3.0)
|
||||||
railties (>= 4.2.0, < 5.1)
|
railties (>= 4.2.0, < 5.1)
|
||||||
rspec-core (3.5.3)
|
rspec-core (3.5.4)
|
||||||
rspec-support (~> 3.5.0)
|
rspec-support (~> 3.5.0)
|
||||||
rspec-expectations (3.5.0)
|
rspec-expectations (3.5.0)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
|
@ -222,14 +235,14 @@ GEM
|
||||||
rspec-mocks (~> 3.5.0)
|
rspec-mocks (~> 3.5.0)
|
||||||
rspec-support (~> 3.5.0)
|
rspec-support (~> 3.5.0)
|
||||||
rspec-support (3.5.0)
|
rspec-support (3.5.0)
|
||||||
rubocop (0.43.0)
|
rubocop (0.47.1)
|
||||||
parser (>= 2.3.1.1, < 3.0)
|
parser (>= 2.3.3.1, < 3.0)
|
||||||
powerpack (~> 0.1)
|
powerpack (~> 0.1)
|
||||||
rainbow (>= 1.99.1, < 3.0)
|
rainbow (>= 1.99.1, < 3.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||||
ruby-progressbar (1.8.1)
|
ruby-progressbar (1.8.1)
|
||||||
sass (3.4.22)
|
sass (3.4.23)
|
||||||
sass-rails (5.0.6)
|
sass-rails (5.0.6)
|
||||||
railties (>= 4.0.0, < 6)
|
railties (>= 4.0.0, < 6)
|
||||||
sass (~> 3.1)
|
sass (~> 3.1)
|
||||||
|
@ -243,11 +256,11 @@ GEM
|
||||||
json (>= 1.8, < 3)
|
json (>= 1.8, < 3)
|
||||||
simplecov-html (~> 0.10.0)
|
simplecov-html (~> 0.10.0)
|
||||||
simplecov-html (0.10.0)
|
simplecov-html (0.10.0)
|
||||||
slack-notifier (1.5.1)
|
slack-notifier (2.0.0)
|
||||||
slop (3.6.0)
|
slop (3.6.0)
|
||||||
snorlax (0.1.6)
|
snorlax (0.1.6)
|
||||||
rails (> 4.1)
|
rails (> 4.1)
|
||||||
sprockets (3.7.0)
|
sprockets (3.7.1)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (> 1, < 3)
|
rack (> 1, < 3)
|
||||||
sprockets-rails (3.2.0)
|
sprockets-rails (3.2.0)
|
||||||
|
@ -256,15 +269,15 @@ GEM
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sucker_punch (2.0.2)
|
sucker_punch (2.0.2)
|
||||||
concurrent-ruby (~> 1.0.0)
|
concurrent-ruby (~> 1.0.0)
|
||||||
thor (0.19.1)
|
thor (0.19.4)
|
||||||
thread_safe (0.3.5)
|
thread_safe (0.3.5)
|
||||||
tilt (2.0.5)
|
tilt (2.0.5)
|
||||||
tunemygc (1.0.68)
|
tunemygc (1.0.69)
|
||||||
tzinfo (1.2.2)
|
tzinfo (1.2.2)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
uglifier (3.0.2)
|
uglifier (3.0.4)
|
||||||
execjs (>= 0.3.0, < 3)
|
execjs (>= 0.3.0, < 3)
|
||||||
unicode-display_width (1.1.1)
|
unicode-display_width (1.1.3)
|
||||||
warden (1.2.6)
|
warden (1.2.6)
|
||||||
rack (>= 1.0)
|
rack (>= 1.0)
|
||||||
websocket-driver (0.6.4)
|
websocket-driver (0.6.4)
|
||||||
|
@ -321,4 +334,4 @@ RUBY VERSION
|
||||||
ruby 2.3.0p0
|
ruby 2.3.0p0
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.13.6
|
1.13.7
|
||||||
|
|
|
@ -22,6 +22,8 @@ Metamaps is developed and maintained by a distributed, nomadic community compris
|
||||||
- To follow along with, or contribute to, our development process, see our [Github Issues and Pull Requests](https://github.com/metamaps/metamaps/issues)
|
- To follow along with, or contribute to, our development process, see our [Github Issues and Pull Requests](https://github.com/metamaps/metamaps/issues)
|
||||||
- Request an invite to the open beta [here](https://metamaps.cc/request)
|
- Request an invite to the open beta [here](https://metamaps.cc/request)
|
||||||
|
|
||||||
|
<!-- markdown hack to split two lists -->
|
||||||
|
|
||||||
- To send us a personal message get in touch with us via email, Twitter, or Hylo
|
- To send us a personal message get in touch with us via email, Twitter, or Hylo
|
||||||
- If you would like to report a bug, please check the [issues][contributing-issues] section in our [contributing instructions][contributing].
|
- If you would like to report a bug, please check the [issues][contributing-issues] section in our [contributing instructions][contributing].
|
||||||
- If you would like to get set up as a developer, that's great! Read on for help getting your development environment set up.
|
- If you would like to get set up as a developer, that's great! Read on for help getting your development environment set up.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// eslint-disable spaced-comment
|
||||||
// JS and CSS bundles
|
// JS and CSS bundles
|
||||||
//= link_directory ../javascripts .js
|
//= link_directory ../javascripts .js
|
||||||
//= link_directory ../stylesheets .css
|
//= link_directory ../stylesheets .css
|
||||||
|
|
23
app/assets/javascripts/application-secret.js
Normal file
23
app/assets/javascripts/application-secret.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
||||||
|
// listed below.
|
||||||
|
//
|
||||||
|
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
||||||
|
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
|
||||||
|
//
|
||||||
|
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||||
|
// the compiled file.
|
||||||
|
//
|
||||||
|
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
||||||
|
// GO AFTER THE REQUIRES BELOW.
|
||||||
|
//
|
||||||
|
/* eslint-disable spaced-comment */
|
||||||
|
//= require jquery
|
||||||
|
//= require jquery-ui
|
||||||
|
//= require jquery_ujs
|
||||||
|
//= require action_cable
|
||||||
|
//= require_directory ./lib
|
||||||
|
//= require ./cloudcarousel-secret
|
||||||
|
//= require ./metamaps.secret.bundle
|
||||||
|
//= require ./Metamaps.ServerData
|
||||||
|
//= require homepageVimeoFallback
|
||||||
|
/* eslint-enable spaced-comment */
|
|
@ -1,4 +1,3 @@
|
||||||
// eslint-disable spaced-comment
|
|
||||||
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
||||||
// listed below.
|
// listed below.
|
||||||
//
|
//
|
||||||
|
@ -10,7 +9,8 @@
|
||||||
//
|
//
|
||||||
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
|
||||||
// GO AFTER THE REQUIRES BELOW.
|
// GO AFTER THE REQUIRES BELOW.
|
||||||
//
|
//
|
||||||
|
/* eslint-disable spaced-comment */
|
||||||
//= require jquery
|
//= require jquery
|
||||||
//= require jquery-ui
|
//= require jquery-ui
|
||||||
//= require jquery_ujs
|
//= require jquery_ujs
|
||||||
|
@ -19,3 +19,4 @@
|
||||||
//= require ./webpacked/metamaps.bundle
|
//= require ./webpacked/metamaps.bundle
|
||||||
//= require ./Metamaps.ServerData
|
//= require ./Metamaps.ServerData
|
||||||
//= require homepageVimeoFallback
|
//= require homepageVimeoFallback
|
||||||
|
/* eslint-enable spaced-comment */
|
||||||
|
|
438
app/assets/javascripts/cloudcarousel-secret.js
Normal file
438
app/assets/javascripts/cloudcarousel-secret.js
Normal file
|
@ -0,0 +1,438 @@
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CloudCarousel V1.0.5
|
||||||
|
// (c) 2011 by R Cecco. <http://www.professorcloud.com>
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
// Reflection code based on plugin by Christophe Beyls <http://www.digitalia.be>
|
||||||
|
//
|
||||||
|
// Please retain this copyright header in all versions of the software
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
var matched, browser;
|
||||||
|
|
||||||
|
jQuery.uaMatch = function( ua ) {
|
||||||
|
ua = ua.toLowerCase();
|
||||||
|
|
||||||
|
var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
|
||||||
|
/(webkit)[ \/]([\w.]+)/.exec( ua ) ||
|
||||||
|
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
|
||||||
|
/(msie) ([\w.]+)/.exec( ua ) ||
|
||||||
|
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
|
||||||
|
[];
|
||||||
|
|
||||||
|
return {
|
||||||
|
browser: match[ 1 ] || "",
|
||||||
|
version: match[ 2 ] || "0"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
matched = jQuery.uaMatch( navigator.userAgent );
|
||||||
|
browser = {};
|
||||||
|
|
||||||
|
if ( matched.browser ) {
|
||||||
|
browser[ matched.browser ] = true;
|
||||||
|
browser.version = matched.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chrome is Webkit, but Webkit is also Safari.
|
||||||
|
if ( browser.chrome ) {
|
||||||
|
browser.webkit = true;
|
||||||
|
} else if ( browser.webkit ) {
|
||||||
|
browser.safari = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
jQuery.browser = browser;
|
||||||
|
|
||||||
|
(function($) {
|
||||||
|
|
||||||
|
// START Reflection object.
|
||||||
|
// Creates a reflection for underneath an image.
|
||||||
|
// IE uses an image with IE specific filter properties, other browsers use the Canvas tag.
|
||||||
|
// The position and size of the reflection gets updated by updateAll() in Controller.
|
||||||
|
function Reflection(img, reflHeight, opacity) {
|
||||||
|
|
||||||
|
var reflection, cntx, imageWidth = img.width, imageHeight = img.width, gradient, parent;
|
||||||
|
|
||||||
|
parent = $(img.parentNode);
|
||||||
|
this.element = reflection = parent.append("<canvas class='reflection' style='position:absolute'/>").find(':last')[0];
|
||||||
|
if ( !reflection.getContext && $.browser.msie) {
|
||||||
|
this.element = reflection = parent.append("<img class='reflection' style='position:absolute'/>").find(':last')[0];
|
||||||
|
reflection.src = img.src;
|
||||||
|
reflection.style.filter = "flipv progid:DXImageTransform.Microsoft.Alpha(opacity=" + (opacity * 100) + ", style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=" + (reflHeight / imageHeight * 100) + ")";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
cntx = reflection.getContext("2d");
|
||||||
|
try {
|
||||||
|
|
||||||
|
|
||||||
|
$(reflection).attr({width: imageWidth, height: reflHeight});
|
||||||
|
cntx.save();
|
||||||
|
cntx.translate(0, imageHeight-1);
|
||||||
|
cntx.scale(1, -1);
|
||||||
|
cntx.drawImage(img, 0, 0, imageWidth, imageHeight);
|
||||||
|
cntx.restore();
|
||||||
|
cntx.globalCompositeOperation = "destination-out";
|
||||||
|
gradient = cntx.createLinearGradient(0, 0, 0, reflHeight);
|
||||||
|
gradient.addColorStop(0, "rgba(255, 255, 255, " + (1 - opacity) + ")");
|
||||||
|
gradient.addColorStop(1, "rgba(255, 255, 255, 1.0)");
|
||||||
|
cntx.fillStyle = gradient;
|
||||||
|
cntx.fillRect(0, 0, imageWidth, reflHeight);
|
||||||
|
} catch(e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Store a copy of the alt and title attrs into the reflection
|
||||||
|
$(reflection).attr({ 'alt': $(img).attr('alt'), title: $(img).attr('title')} );
|
||||||
|
|
||||||
|
} //END Reflection object
|
||||||
|
|
||||||
|
// START Item object.
|
||||||
|
// A wrapper object for items within the carousel.
|
||||||
|
var Item = function(imgIn, options)
|
||||||
|
{
|
||||||
|
this.orgWidth = imgIn.width;
|
||||||
|
this.orgHeight = imgIn.height;
|
||||||
|
this.image = imgIn;
|
||||||
|
this.reflection = null;
|
||||||
|
this.alt = imgIn.alt;
|
||||||
|
this.title = imgIn.title;
|
||||||
|
this.imageOK = false;
|
||||||
|
this.options = options;
|
||||||
|
|
||||||
|
this.imageOK = true;
|
||||||
|
|
||||||
|
if (this.options.reflHeight > 0)
|
||||||
|
{
|
||||||
|
this.reflection = new Reflection(this.image, this.options.reflHeight, this.options.reflOpacity);
|
||||||
|
}
|
||||||
|
$(this.image).css('position','absolute'); // Bizarre. This seems to reset image width to 0 on webkit!
|
||||||
|
};// END Item object
|
||||||
|
|
||||||
|
|
||||||
|
// Controller object.
|
||||||
|
// This handles moving all the items, dealing with mouse clicks etc.
|
||||||
|
var Controller = function(container, images, options)
|
||||||
|
{
|
||||||
|
var items = [], funcSin = Math.sin, funcCos = Math.cos, ctx=this;
|
||||||
|
this.controlTimer = 0;
|
||||||
|
this.stopped = false;
|
||||||
|
//this.imagesLoaded = 0;
|
||||||
|
this.container = container;
|
||||||
|
this.xRadius = options.xRadius;
|
||||||
|
this.yRadius = options.yRadius;
|
||||||
|
this.showFrontTextTimer = 0;
|
||||||
|
this.autoRotateTimer = 0;
|
||||||
|
if (options.xRadius === 0)
|
||||||
|
{
|
||||||
|
this.xRadius = ($(container).width()/2.3);
|
||||||
|
}
|
||||||
|
if (options.yRadius === 0)
|
||||||
|
{
|
||||||
|
this.yRadius = ($(container).height()/6);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.xCentre = options.xPos;
|
||||||
|
this.yCentre = options.yPos;
|
||||||
|
this.frontIndex = 0; // Index of the item at the front
|
||||||
|
|
||||||
|
// Start with the first item at the front.
|
||||||
|
this.rotation = this.destRotation = Math.PI/2;
|
||||||
|
this.timeDelay = 1000/options.FPS;
|
||||||
|
|
||||||
|
// Turn on the infoBox
|
||||||
|
if(options.altBox !== null)
|
||||||
|
{
|
||||||
|
$(options.altBox).css('display','block');
|
||||||
|
$(options.titleBox).css('display','block');
|
||||||
|
}
|
||||||
|
// Turn on relative position for container to allow absolutely positioned elements
|
||||||
|
// within it to work.
|
||||||
|
$(container).css({ position:'relative', overflow:'hidden'} );
|
||||||
|
|
||||||
|
$(options.buttonLeft).css('display','inline');
|
||||||
|
$(options.buttonRight).css('display','inline');
|
||||||
|
|
||||||
|
// Setup the buttons.
|
||||||
|
$(options.buttonLeft).bind('mouseup',this,function(event){
|
||||||
|
event.data.rotate(-1);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$(options.buttonRight).bind('mouseup',this,function(event){
|
||||||
|
event.data.rotate(1);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// START METAMAPS CODE
|
||||||
|
// Add code that makes tab and shift+tab scroll through metacodes
|
||||||
|
$('.new_topic').bind('keydown',this,function(event){
|
||||||
|
if (event.keyCode == 9 && event.shiftKey) {
|
||||||
|
$(container).show()
|
||||||
|
event.data.rotate(-1);
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
} else if (event.keyCode == 9) {
|
||||||
|
$(container).show()
|
||||||
|
event.data.rotate(1);
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// END METAMAPS CODE
|
||||||
|
|
||||||
|
// You will need this plugin for the mousewheel to work: http://plugins.jquery.com/project/mousewheel
|
||||||
|
if (options.mouseWheel)
|
||||||
|
{
|
||||||
|
// START METAMAPS CODE
|
||||||
|
/*$('body').bind('mousewheel',this,function(event, delta) {
|
||||||
|
if (Metamaps.Create.newTopic.beingCreated &&
|
||||||
|
!Metamaps.Create.isSwitchingSet &&
|
||||||
|
!Metamaps.Create.newTopic.pinned) {
|
||||||
|
event.data.rotate(delta);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});*/
|
||||||
|
// END METAMAPS CODE
|
||||||
|
// ORIGINAL CODE
|
||||||
|
// $(container).bind('mousewheel',this,function(event, delta) {
|
||||||
|
// event.data.rotate(delta);
|
||||||
|
// return false;
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
}
|
||||||
|
$(container).unbind('mouseover click').bind('mouseover click',this,function(event){
|
||||||
|
|
||||||
|
clearInterval(event.data.autoRotateTimer); // Stop auto rotation if mouse over.
|
||||||
|
var text = $(event.target).attr('alt');
|
||||||
|
// If we have moved over a carousel item, then show the alt and title text.
|
||||||
|
|
||||||
|
if ( text !== undefined && text !== null )
|
||||||
|
{
|
||||||
|
|
||||||
|
clearTimeout(event.data.showFrontTextTimer);
|
||||||
|
$(options.altBox).html( ($(event.target).attr('alt') ));
|
||||||
|
//$(options.titleBox).html( ($(event.target).attr('title') ));
|
||||||
|
if ( options.bringToFront && event.type == 'click' )
|
||||||
|
{
|
||||||
|
$(options.titleBox).html( ($(event.target).attr('title') ));
|
||||||
|
// START METAMAPS CODE
|
||||||
|
Metamaps.Create.newTopic.metacode = $(event.target).attr('data-id');
|
||||||
|
// END METAMAPS CODE
|
||||||
|
var idx = $(event.target).data('itemIndex');
|
||||||
|
var frontIndex = event.data.frontIndex;
|
||||||
|
//var diff = idx - frontIndex;
|
||||||
|
var diff = (idx - frontIndex) % images.length;
|
||||||
|
if (Math.abs(diff) > images.length / 2) {
|
||||||
|
diff += (diff > 0 ? -images.length : images.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
event.data.rotate(-diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// START METAMAPS CODE - initialize newTopic.metacode
|
||||||
|
var first = $(this.container).find('img').get(0)
|
||||||
|
Metamaps.Create.newTopic.metacode = $(first).data('id')
|
||||||
|
// END METAMAPS CODE
|
||||||
|
|
||||||
|
// If we have moved out of a carousel item (or the container itself),
|
||||||
|
// restore the text of the front item in 1 second.
|
||||||
|
$(container).bind('mouseout',this,function(event){
|
||||||
|
var context = event.data;
|
||||||
|
clearTimeout(context.showFrontTextTimer);
|
||||||
|
context.showFrontTextTimer = setTimeout( function(){context.showFrontText();},1000);
|
||||||
|
context.autoRotate(); // Start auto rotation.
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prevent items from being selected as mouse is moved and clicked in the container.
|
||||||
|
$(container).bind('mousedown',this,function(event){
|
||||||
|
|
||||||
|
event.data.container.focus();
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
container.onselectstart = function () { return false; }; // For IE.
|
||||||
|
|
||||||
|
this.innerWrapper = $(container).wrapInner('<div style="position:absolute;width:100%;height:100%;"/>').children()[0];
|
||||||
|
|
||||||
|
// Shows the text from the front most item.
|
||||||
|
this.showFrontText = function()
|
||||||
|
{
|
||||||
|
if ( items[this.frontIndex] === undefined ) { return; } // Images might not have loaded yet.
|
||||||
|
// METAMAPS CODE
|
||||||
|
Metamaps.Create.newTopic.setMetacode($(items[this.frontIndex].image).attr('data-id'))
|
||||||
|
// NOT METAMAPS CODE
|
||||||
|
//$(options.titleBox).html( $(items[this.frontIndex].image).attr('title'));
|
||||||
|
//$(options.altBox).html( $(items[this.frontIndex].image).attr('alt'));
|
||||||
|
};
|
||||||
|
|
||||||
|
this.go = function()
|
||||||
|
{
|
||||||
|
if(this.controlTimer !== 0) { return; }
|
||||||
|
var context = this;
|
||||||
|
this.controlTimer = setTimeout( function(){context.updateAll();},this.timeDelay);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.stop = function()
|
||||||
|
{
|
||||||
|
clearTimeout(this.controlTimer);
|
||||||
|
this.controlTimer = 0;
|
||||||
|
// METAMAPS CODE
|
||||||
|
$(container).hide()
|
||||||
|
// END METAMAPS CODE
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Starts the rotation of the carousel. Direction is the number (+-) of carousel items to rotate by.
|
||||||
|
this.rotate = function(direction)
|
||||||
|
{
|
||||||
|
this.frontIndex -= direction;
|
||||||
|
if (this.frontIndex == -1) this.frontIndex = items.length - 1;
|
||||||
|
this.frontIndex %= items.length;
|
||||||
|
this.destRotation += ( Math.PI / items.length ) * ( 2*direction );
|
||||||
|
this.showFrontText();
|
||||||
|
this.go();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.autoRotate = function()
|
||||||
|
{
|
||||||
|
if ( options.autoRotate !== 'no' )
|
||||||
|
{
|
||||||
|
var dir = (options.autoRotate === 'right')? 1 : -1;
|
||||||
|
this.autoRotateTimer = setInterval( function(){ctx.rotate(dir); }, options.autoRotateDelay );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is the main loop function that moves everything.
|
||||||
|
this.updateAll = function()
|
||||||
|
{
|
||||||
|
var minScale = options.minScale; // This is the smallest scale applied to the furthest item.
|
||||||
|
var smallRange = (1-minScale) * 0.5;
|
||||||
|
var w,h,x,y,scale,item,sinVal;
|
||||||
|
|
||||||
|
var change = (this.destRotation - this.rotation);
|
||||||
|
var absChange = Math.abs(change);
|
||||||
|
|
||||||
|
this.rotation += change * options.speed;
|
||||||
|
if ( absChange < 0.001 ) { this.rotation = this.destRotation; }
|
||||||
|
var itemsLen = items.length;
|
||||||
|
var spacing = (Math.PI / itemsLen) * 2;
|
||||||
|
//var wrapStyle = null;
|
||||||
|
var radians = this.rotation;
|
||||||
|
var isMSIE = $.browser.msie;
|
||||||
|
|
||||||
|
// Turn off display. This can reduce repaints/reflows when making style and position changes in the loop.
|
||||||
|
// See http://dev.opera.com/articles/view/efficient-javascript/?page=3
|
||||||
|
this.innerWrapper.style.display = 'none';
|
||||||
|
|
||||||
|
var style;
|
||||||
|
var px = 'px', reflHeight;
|
||||||
|
var context = this;
|
||||||
|
for (var i = 0; i<itemsLen ;i++)
|
||||||
|
{
|
||||||
|
item = items[i];
|
||||||
|
|
||||||
|
sinVal = funcSin(radians);
|
||||||
|
|
||||||
|
scale = ((sinVal+1) * smallRange) + minScale;
|
||||||
|
|
||||||
|
x = this.xCentre + (( (funcCos(radians) * this.xRadius) - (item.orgWidth*0.5)) * scale);
|
||||||
|
y = this.yCentre + (( (sinVal * this.yRadius) ) * scale);
|
||||||
|
|
||||||
|
if (item.imageOK)
|
||||||
|
{
|
||||||
|
var img = item.image;
|
||||||
|
|
||||||
|
img.style.zIndex = "" + (scale * 100)>>0; // >>0 = Math.foor(). Firefox doesn't like fractional decimals in z-index.
|
||||||
|
w = img.width = item.orgWidth * scale;
|
||||||
|
h = img.height = item.orgHeight * scale;
|
||||||
|
img.style.left = x + px ;
|
||||||
|
img.style.top = y + px;
|
||||||
|
if (item.reflection !== null)
|
||||||
|
{
|
||||||
|
reflHeight = options.reflHeight * scale;
|
||||||
|
style = item.reflection.element.style;
|
||||||
|
style.left = x + px;
|
||||||
|
style.top = y + h + options.reflGap * scale + px;
|
||||||
|
style.width = w + px;
|
||||||
|
if (isMSIE)
|
||||||
|
{
|
||||||
|
style.filter.finishy = (reflHeight / h * 100);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
style.height = reflHeight + px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
radians += spacing;
|
||||||
|
}
|
||||||
|
// Turn display back on.
|
||||||
|
this.innerWrapper.style.display = 'block';
|
||||||
|
|
||||||
|
// If we have a preceptable change in rotation then loop again next frame.
|
||||||
|
if ( absChange >= 0.001 )
|
||||||
|
{
|
||||||
|
this.controlTimer = setTimeout( function(){context.updateAll();},this.timeDelay);
|
||||||
|
}else
|
||||||
|
{
|
||||||
|
// Otherwise just stop completely.
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
}; // END updateAll
|
||||||
|
|
||||||
|
// Create an Item object for each image
|
||||||
|
// func = function(){return;ctx.updateAll();} ;
|
||||||
|
|
||||||
|
// Check if images have loaded. We need valid widths and heights for the reflections.
|
||||||
|
this.checkImagesLoaded = function()
|
||||||
|
{
|
||||||
|
var i;
|
||||||
|
for(i=0;i<images.length;i++) {
|
||||||
|
if ( (images[i].width === undefined) || ( (images[i].complete !== undefined) && (!images[i].complete) ))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i=0;i<images.length;i++) {
|
||||||
|
items.push( new Item( images[i], options ) );
|
||||||
|
$(images[i]).data('itemIndex',i);
|
||||||
|
}
|
||||||
|
// If all images have valid widths and heights, we can stop checking.
|
||||||
|
clearInterval(this.tt);
|
||||||
|
// METAMAPS COMMENT this.showFrontText();
|
||||||
|
this.autoRotate();
|
||||||
|
this.updateAll();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
this.tt = setInterval( function(){ctx.checkImagesLoaded();},50);
|
||||||
|
}; // END Controller object
|
||||||
|
|
||||||
|
// The jQuery plugin part. Iterates through items specified in selector and inits a Controller class for each one.
|
||||||
|
$.fn.CloudCarousel = function(options) {
|
||||||
|
|
||||||
|
this.each( function() {
|
||||||
|
|
||||||
|
options = $.extend({}, {
|
||||||
|
reflHeight:0,
|
||||||
|
reflOpacity:0.5,
|
||||||
|
reflGap:0,
|
||||||
|
minScale:0.5,
|
||||||
|
xPos:0,
|
||||||
|
yPos:0,
|
||||||
|
xRadius:0,
|
||||||
|
yRadius:0,
|
||||||
|
altBox:null,
|
||||||
|
titleBox:null,
|
||||||
|
FPS: 30,
|
||||||
|
autoRotate: 'no',
|
||||||
|
autoRotateDelay: 1500,
|
||||||
|
speed:0.2,
|
||||||
|
mouseWheel: false,
|
||||||
|
bringToFront: false
|
||||||
|
},options );
|
||||||
|
// Create a Controller for each carousel.
|
||||||
|
$(this).data('cloudcarousel', new Controller( this, $('.cloudcarousel',$(this)), options) );
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
})(jQuery);
|
|
@ -1,11 +1,11 @@
|
||||||
/* global $ */
|
/* global $ */
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function() {
|
||||||
if (window.location.pathname === '/') {
|
if (window.location.pathname === '/') {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'GET',
|
type: 'GET',
|
||||||
url: 'https://player.vimeo.com',
|
url: 'https://player.vimeo.com',
|
||||||
error: function (e) {
|
error: function(e) {
|
||||||
$('.homeVideo').hide()
|
$('.homeVideo').hide()
|
||||||
$('.homeVideo').replaceWith($('<video/>', {
|
$('.homeVideo').replaceWith($('<video/>', {
|
||||||
poster: '/assets/metamaps-intro-poster.webp',
|
poster: '/assets/metamaps-intro-poster.webp',
|
||||||
|
|
161
app/assets/javascripts/lib/ajaxq.js
Normal file
161
app/assets/javascripts/lib/ajaxq.js
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
// AjaxQ jQuery Plugin
|
||||||
|
// Copyright (c) 2012 Foliotek Inc.
|
||||||
|
// MIT License
|
||||||
|
// https://github.com/Foliotek/ajaxq
|
||||||
|
// Uses CommonJS, AMD or browser globals to create a jQuery plugin.
|
||||||
|
|
||||||
|
(function (factory) {
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
// AMD. Register as an anonymous module.
|
||||||
|
define(['jquery'], factory);
|
||||||
|
} else if (typeof module === 'object' && module.exports) {
|
||||||
|
// Node/CommonJS
|
||||||
|
module.exports = factory(require('jquery'));
|
||||||
|
} else {
|
||||||
|
// Browser globals
|
||||||
|
factory(jQuery);
|
||||||
|
}
|
||||||
|
}(function ($) {
|
||||||
|
var queues = {};
|
||||||
|
var activeReqs = {};
|
||||||
|
|
||||||
|
// Register an $.ajaxq function, which follows the $.ajax interface, but allows a queue name which will force only one request per queue to fire.
|
||||||
|
// opts can be the regular $.ajax settings plainObject, or a callback returning the settings object, to be evaluated just prior to the actual call to $.ajax.
|
||||||
|
$.ajaxq = function(qname, opts) {
|
||||||
|
|
||||||
|
if (typeof opts === "undefined") {
|
||||||
|
throw ("AjaxQ: queue name is not provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will return a Deferred promise object extended with success/error/callback, so that this function matches the interface of $.ajax
|
||||||
|
var deferred = $.Deferred(),
|
||||||
|
promise = deferred.promise();
|
||||||
|
|
||||||
|
promise.success = promise.done;
|
||||||
|
promise.error = promise.fail;
|
||||||
|
promise.complete = promise.always;
|
||||||
|
|
||||||
|
// Check whether options are to be evaluated at call time or not.
|
||||||
|
var deferredOpts = typeof opts === 'function';
|
||||||
|
// Create a deep copy of the arguments, and enqueue this request.
|
||||||
|
var clonedOptions = !deferredOpts ? $.extend(true, {}, opts) : null;
|
||||||
|
enqueue(function() {
|
||||||
|
// Send off the ajax request now that the item has been removed from the queue
|
||||||
|
var jqXHR = $.ajax.apply(window, [deferredOpts ? opts() : clonedOptions]);
|
||||||
|
|
||||||
|
// Notify the returned deferred object with the correct context when the jqXHR is done or fails
|
||||||
|
// Note that 'always' will automatically be fired once one of these are called: http://api.jquery.com/category/deferred-object/.
|
||||||
|
jqXHR.done(function() {
|
||||||
|
deferred.resolve.apply(this, arguments);
|
||||||
|
});
|
||||||
|
jqXHR.fail(function() {
|
||||||
|
deferred.reject.apply(this, arguments);
|
||||||
|
});
|
||||||
|
|
||||||
|
jqXHR.always(dequeue); // make sure to dequeue the next request AFTER the done and fail callbacks are fired
|
||||||
|
|
||||||
|
return jqXHR;
|
||||||
|
});
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
|
||||||
|
|
||||||
|
// If there is no queue, create an empty one and instantly process this item.
|
||||||
|
// Otherwise, just add this item onto it for later processing.
|
||||||
|
function enqueue(cb) {
|
||||||
|
if (!queues[qname]) {
|
||||||
|
queues[qname] = [];
|
||||||
|
var xhr = cb();
|
||||||
|
activeReqs[qname] = xhr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
queues[qname].push(cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the next callback from the queue and fire it off.
|
||||||
|
// If the queue was empty (this was the last item), delete it from memory so the next one can be instantly processed.
|
||||||
|
function dequeue() {
|
||||||
|
if (!queues[qname]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var nextCallback = queues[qname].shift();
|
||||||
|
if (nextCallback) {
|
||||||
|
var xhr = nextCallback();
|
||||||
|
activeReqs[qname] = xhr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete queues[qname];
|
||||||
|
delete activeReqs[qname];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Register a $.postq and $.getq method to provide shortcuts for $.get and $.post
|
||||||
|
// Copied from jQuery source to make sure the functions share the same defaults as $.get and $.post.
|
||||||
|
$.each( [ "getq", "postq" ], function( i, method ) {
|
||||||
|
$[ method ] = function( qname, url, data, callback, type ) {
|
||||||
|
|
||||||
|
if ( $.isFunction( data ) ) {
|
||||||
|
type = type || callback;
|
||||||
|
callback = data;
|
||||||
|
data = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $.ajaxq(qname, {
|
||||||
|
type: method === "postq" ? "post" : "get",
|
||||||
|
url: url,
|
||||||
|
data: data,
|
||||||
|
success: callback,
|
||||||
|
dataType: type
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
var isQueueRunning = function(qname) {
|
||||||
|
return (queues.hasOwnProperty(qname) && queues[qname].length > 0) || activeReqs.hasOwnProperty(qname);
|
||||||
|
};
|
||||||
|
|
||||||
|
var isAnyQueueRunning = function() {
|
||||||
|
for (var i in queues) {
|
||||||
|
if (isQueueRunning(i)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajaxq.isRunning = function(qname) {
|
||||||
|
if (qname) return isQueueRunning(qname);
|
||||||
|
else return isAnyQueueRunning();
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajaxq.getActiveRequest = function(qname) {
|
||||||
|
if (!qname) throw ("AjaxQ: queue name is required");
|
||||||
|
|
||||||
|
return activeReqs[qname];
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajaxq.abort = function(qname) {
|
||||||
|
if (!qname) throw ("AjaxQ: queue name is required");
|
||||||
|
|
||||||
|
var current = $.ajaxq.getActiveRequest(qname);
|
||||||
|
delete queues[qname];
|
||||||
|
delete activeReqs[qname];
|
||||||
|
if (current) current.abort();
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajaxq.clear = function(qname) {
|
||||||
|
if (!qname) {
|
||||||
|
for (var i in queues) {
|
||||||
|
if (queues.hasOwnProperty(i)) {
|
||||||
|
queues[i] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (queues[qname]) {
|
||||||
|
queues[qname] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}));
|
3836
app/assets/javascripts/metamaps.secret.bundle.js
Normal file
3836
app/assets/javascripts/metamaps.secret.bundle.js
Normal file
File diff suppressed because one or more lines are too long
12
app/assets/secret_stylesheets/application-secret.scss.erb
Normal file
12
app/assets/secret_stylesheets/application-secret.scss.erb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||||
|
* listed below.
|
||||||
|
*
|
||||||
|
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
||||||
|
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
||||||
|
*
|
||||||
|
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
||||||
|
* compiled file, but it's generally better to create a new file per style scope.
|
||||||
|
*
|
||||||
|
*= require ./special
|
||||||
|
*/
|
109
app/assets/secret_stylesheets/special.scss.erb
Normal file
109
app/assets/secret_stylesheets/special.scss.erb
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
#metacodeSelector {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.metacodeSelect {
|
||||||
|
border-top: 1px solid #DDD;
|
||||||
|
padding: 0;
|
||||||
|
background: #FFF;
|
||||||
|
|
||||||
|
.metacodeFilterInput {
|
||||||
|
width: 100px;
|
||||||
|
outline: none;
|
||||||
|
border: 0;
|
||||||
|
padding: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 14px;
|
||||||
|
color: #424242;
|
||||||
|
font-family: 'din-medium', helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.metacodeList {
|
||||||
|
list-style: none;
|
||||||
|
background: #FFF;
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover, &.keySelect {
|
||||||
|
background: #4CAF50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding-right: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.selectedMetacode {
|
||||||
|
float: left;
|
||||||
|
background: #FFF;
|
||||||
|
border-top-left-radius: 2px;
|
||||||
|
border-bottom-left-radius: 2px;
|
||||||
|
padding: 5px 10px 5px 6px;
|
||||||
|
vertical-align: top;
|
||||||
|
border-right: 1px solid #DDD;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.selectedMetacode:hover, .selectedMetacode.isBeingSelected {
|
||||||
|
background: #EDEDED;
|
||||||
|
}
|
||||||
|
.selectedMetacode img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.selectedMetacode span {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
.selectedMetacode .downArrow {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 8px 6px 0 6px;
|
||||||
|
border-color: #777 transparent transparent transparent;
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
.new_topic {
|
||||||
|
margin: 0;
|
||||||
|
margin-top: -17px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
#new_topic .twitter-typeahead {
|
||||||
|
position: relative !important;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.new_topic #topic_name,
|
||||||
|
.new_topic .tt-hint {
|
||||||
|
border-radius: none;
|
||||||
|
border-top-right-radius: 2px;
|
||||||
|
border-bottom-right-radius: 2px;
|
||||||
|
}
|
||||||
|
.openMetacodeSwitcher {
|
||||||
|
top: -16px;
|
||||||
|
left: -16px;
|
||||||
|
}
|
||||||
|
#metacodeImg {
|
||||||
|
height: 120px;
|
||||||
|
width: 380px;
|
||||||
|
display: none;
|
||||||
|
position: absolute !important;
|
||||||
|
top: -30px;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
#metacodeImgTitle {
|
||||||
|
display: none;
|
||||||
|
float: left;
|
||||||
|
width: 120px;
|
||||||
|
text-align: center;
|
||||||
|
margin-left: 110px;
|
||||||
|
}
|
|
@ -531,10 +531,12 @@ button.button.btn-no:hover {
|
||||||
left: -1000px;
|
left: -1000px;
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 340px;
|
|
||||||
margin: -40px 0 0 -35px;
|
margin: -40px 0 0 -35px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
body:not(.action-conversation) .new_topic {
|
||||||
|
width: 340px;
|
||||||
|
}
|
||||||
|
|
||||||
#new_topic .twitter-typeahead {
|
#new_topic .twitter-typeahead {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
|
@ -1558,6 +1560,7 @@ h3.filterBox {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0.75em;
|
margin: 0.75em;
|
||||||
padding: 0.75em;
|
padding: 0.75em;
|
||||||
|
padding-top: 0.85em;
|
||||||
height: 3em;
|
height: 3em;
|
||||||
background-color: #AAB0FB;
|
background-color: #AAB0FB;
|
||||||
border-radius: 0.3em;
|
border-radius: 0.3em;
|
||||||
|
@ -2012,6 +2015,7 @@ input.collaboratorSearchField {
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: none;
|
display: none;
|
||||||
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
.mapInfoShareIcon {
|
.mapInfoShareIcon {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
|
@ -2051,6 +2055,43 @@ and it won't be important on password protected instances */
|
||||||
.yourMap .mapInfoDelete {
|
.yourMap .mapInfoDelete {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mapInfoButtonsWrapper .mapInfoThumbnail {
|
||||||
|
display: block;
|
||||||
|
background-image: url(<%= asset_path('screenshot_sprite.png') %>);
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
bottom: -8px;
|
||||||
|
right: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #e0e0e0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-position: -32px 0;
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
background: black;
|
||||||
|
color: white;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 3px 5px 2px 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.mapInfoButtonsWrapper span {
|
.mapInfoButtonsWrapper span {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
@ -83,6 +83,7 @@ $unread_notifications_dot_size: 8px;
|
||||||
|
|
||||||
.notification-body {
|
.notification-body {
|
||||||
margin-left: 50px;
|
margin-left: 50px;
|
||||||
|
line-height: 20px;
|
||||||
|
|
||||||
.in-bold {
|
.in-bold {
|
||||||
font-family: 'din-medium', Sans-Serif;
|
font-family: 'din-medium', Sans-Serif;
|
||||||
|
@ -130,9 +131,35 @@ $unread_notifications_dot_size: 8px;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.notificationPage .notification-body {
|
.notificationPage {
|
||||||
p, div {
|
.thirty-two-avatar {
|
||||||
margin: 1em auto;
|
display: inline-block;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 16px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
line-height: 32px;
|
||||||
|
|
||||||
|
img {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.decline {
|
||||||
|
background: #DB5D5D;
|
||||||
|
&:hover {
|
||||||
|
background: #DC4B4B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-body {
|
||||||
|
p, div {
|
||||||
|
margin: 1em auto;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
module ApplicationCable
|
module ApplicationCable
|
||||||
class Channel < ActionCable::Channel::Base
|
class Channel < ActionCable::Channel::Base
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
class MapChannel < ApplicationCable::Channel
|
class MapChannel < ApplicationCable::Channel
|
||||||
# Called when the consumer has successfully
|
# Called when the consumer has successfully
|
||||||
# become a subscriber of this channel.
|
# become a subscriber of this channel.
|
||||||
def subscribed
|
def subscribed
|
||||||
return unless Pundit.policy(current_user, Map.find(params[:id])).show?
|
map = Map.find(params[:id])
|
||||||
|
return unless Pundit.policy(current_user, map).show?
|
||||||
stream_from "map_#{params[:id]}"
|
stream_from "map_#{params[:id]}"
|
||||||
|
Events::UserPresentOnMap.publish!(map, current_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribed
|
||||||
|
map = Map.find(params[:id])
|
||||||
|
return unless Pundit.policy(current_user, map).show?
|
||||||
|
Events::UserNotPresentOnMap.publish!(map, current_user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class AccessController < ApplicationController
|
class AccessController < ApplicationController
|
||||||
before_action :require_user, only: [:access, :access_request, :approve_access, :approve_access_post,
|
before_action :require_user, only: [:access, :access_request,
|
||||||
|
:approve_access, :approve_access_post,
|
||||||
:deny_access, :deny_access_post, :request_access]
|
:deny_access, :deny_access_post, :request_access]
|
||||||
before_action :set_map, only: [:access, :access_request, :approve_access, :approve_access_post,
|
before_action :set_map, only: [:access, :access_request,
|
||||||
|
:approve_access, :approve_access_post,
|
||||||
:deny_access, :deny_access_post, :request_access]
|
:deny_access, :deny_access_post, :request_access]
|
||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
|
|
||||||
|
@ -18,9 +20,7 @@ class AccessController < ApplicationController
|
||||||
|
|
||||||
# POST maps/:id/access_request
|
# POST maps/:id/access_request
|
||||||
def access_request
|
def access_request
|
||||||
request = AccessRequest.create(user: current_user, map: @map)
|
AccessRequest.create(user: current_user, map: @map)
|
||||||
NotificationService.access_request(request)
|
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.json { head :ok }
|
format.json { head :ok }
|
||||||
end
|
end
|
||||||
|
@ -30,12 +30,7 @@ class AccessController < ApplicationController
|
||||||
def access
|
def access
|
||||||
user_ids = params[:access].to_a.map(&:to_i) || []
|
user_ids = params[:access].to_a.map(&:to_i) || []
|
||||||
|
|
||||||
@map.add_new_collaborators(user_ids).each do |user_id|
|
@map.add_new_collaborators(user_ids)
|
||||||
# add_new_collaborators returns array of added users,
|
|
||||||
# who we then send a notification to
|
|
||||||
user = User.find(user_id)
|
|
||||||
NotificationService.invite_to_edit(@map, current_user, user)
|
|
||||||
end
|
|
||||||
@map.remove_old_collaborators(user_ids)
|
@map.remove_old_collaborators(user_ids)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
@ -66,6 +61,7 @@ class AccessController < ApplicationController
|
||||||
request = AccessRequest.find(params[:request_id])
|
request = AccessRequest.find(params[:request_id])
|
||||||
request.approve
|
request.approve
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
format.js
|
||||||
format.json do
|
format.json do
|
||||||
head :ok
|
head :ok
|
||||||
end
|
end
|
||||||
|
@ -77,6 +73,7 @@ class AccessController < ApplicationController
|
||||||
request = AccessRequest.find(params[:request_id])
|
request = AccessRequest.find(params[:request_id])
|
||||||
request.deny
|
request.deny
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
format.js
|
||||||
format.json do
|
format.json do
|
||||||
head :ok
|
head :ok
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,31 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
module Api
|
module Api
|
||||||
module V2
|
module V2
|
||||||
class MappingsController < RestfulController
|
class MappingsController < WithUpdatesController
|
||||||
def searchable_columns
|
def searchable_columns
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
|
||||||
instantiate_resource
|
|
||||||
resource.user = current_user if current_user.present?
|
|
||||||
resource.updated_by = current_user if current_user.present?
|
|
||||||
authorize resource
|
|
||||||
create_action
|
|
||||||
respond_with_resource
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
resource.updated_by = current_user if current_user.present?
|
|
||||||
update_action
|
|
||||||
respond_with_resource
|
|
||||||
end
|
|
||||||
|
|
||||||
def destroy
|
|
||||||
resource.updated_by = current_user if current_user.present?
|
|
||||||
destroy_action
|
|
||||||
head :no_content
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
module Api
|
module Api
|
||||||
module V2
|
module V2
|
||||||
class MapsController < RestfulController
|
class MapsController < WithUpdatesController
|
||||||
def searchable_columns
|
def searchable_columns
|
||||||
[:name, :desc]
|
[:name, :desc]
|
||||||
end
|
end
|
||||||
|
|
|
@ -45,7 +45,7 @@ module Api
|
||||||
end
|
end
|
||||||
|
|
||||||
def current_user
|
def current_user
|
||||||
token_user || doorkeeper_user || super
|
token_user || doorkeeper_user
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_resource
|
def load_resource
|
||||||
|
@ -86,7 +86,7 @@ module Api
|
||||||
|
|
||||||
def token_user
|
def token_user
|
||||||
token = params[:access_token]
|
token = params[:access_token]
|
||||||
access_token = Token.find_by_token(token)
|
access_token = Token.find_by(token: token)
|
||||||
@token_user ||= access_token.user if access_token
|
@token_user ||= access_token.user if access_token
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -149,19 +149,30 @@ module Api
|
||||||
|
|
||||||
# override this method to explicitly set searchable columns
|
# override this method to explicitly set searchable columns
|
||||||
def searchable_columns
|
def searchable_columns
|
||||||
|
return @searchable_columns unless @searchable_columns.nil?
|
||||||
|
|
||||||
columns = resource_class.columns.select do |column|
|
columns = resource_class.columns.select do |column|
|
||||||
column.type == :text || column.type == :string
|
column.type == :text || column.type == :string
|
||||||
end
|
end
|
||||||
columns.map(&:name)
|
@searchable_columns = columns.map(&:name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# e.g. ?q=test&searchfields=name,desc
|
||||||
|
def searchfields
|
||||||
|
return searchable_columns if params[:searchfields].blank?
|
||||||
|
|
||||||
|
searchfields = params[:searchfields].split(',')
|
||||||
|
searchfields.select! { |f| searchable_columns.include?(f.to_sym) }
|
||||||
|
searchfields.empty? ? searchable_columns : searchfields
|
||||||
end
|
end
|
||||||
|
|
||||||
# thanks to http://stackoverflow.com/questions/4430578
|
# thanks to http://stackoverflow.com/questions/4430578
|
||||||
def search_by_q(collection)
|
def search_by_q(collection)
|
||||||
table = resource_class.arel_table
|
table = resource_class.arel_table
|
||||||
safe_query = "%#{params[:q].gsub(/[%_]/, '\\\\\0')}%"
|
safe_query = "%#{params[:q].gsub(/[%_]/, '\\\\\0')}%"
|
||||||
search_column = -> (column) { table[column].matches(safe_query) }
|
search_column = ->(column) { table[column].matches(safe_query) }
|
||||||
|
|
||||||
condition = searchable_columns.reduce(nil) do |prev, column|
|
condition = searchfields.reduce(nil) do |prev, column|
|
||||||
next search_column.call(column) if prev.nil?
|
next search_column.call(column) if prev.nil?
|
||||||
search_column.call(column).or(prev)
|
search_column.call(column).or(prev)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
module Api
|
module Api
|
||||||
module V2
|
module V2
|
||||||
class SynapsesController < RestfulController
|
class SynapsesController < WithUpdatesController
|
||||||
def searchable_columns
|
def searchable_columns
|
||||||
[:desc]
|
[:desc]
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
module Api
|
module Api
|
||||||
module V2
|
module V2
|
||||||
class TokensController < RestfulController
|
class TokensController < RestfulController
|
||||||
|
protect_from_forgery
|
||||||
|
|
||||||
def searchable_columns
|
def searchable_columns
|
||||||
[:description]
|
[:description]
|
||||||
end
|
end
|
||||||
|
@ -18,6 +20,12 @@ module Api
|
||||||
create_action
|
create_action
|
||||||
respond_with_resource
|
respond_with_resource
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def current_user
|
||||||
|
token_user || doorkeeper_user || method(:current_user).super_method.super_method.call
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
module Api
|
module Api
|
||||||
module V2
|
module V2
|
||||||
class TopicsController < RestfulController
|
class TopicsController < WithUpdatesController
|
||||||
def searchable_columns
|
def searchable_columns
|
||||||
[:name, :desc, :link]
|
[:name, :desc, :link]
|
||||||
end
|
end
|
||||||
|
|
27
app/controllers/api/v2/with_updates_controller.rb
Normal file
27
app/controllers/api/v2/with_updates_controller.rb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module Api
|
||||||
|
module V2
|
||||||
|
class WithUpdatesController < RestfulController
|
||||||
|
def create
|
||||||
|
instantiate_resource
|
||||||
|
resource.user = current_user if current_user.present?
|
||||||
|
resource.updated_by = current_user if current_user.present?
|
||||||
|
authorize resource
|
||||||
|
create_action
|
||||||
|
respond_with_resource
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
resource.updated_by = current_user if current_user.present?
|
||||||
|
update_action
|
||||||
|
respond_with_resource
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
resource.updated_by = current_user if current_user.present?
|
||||||
|
destroy_action
|
||||||
|
head :no_content
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,7 +5,6 @@ class HacksController < ApplicationController
|
||||||
include ActionView::Helpers::TextHelper # string truncate method
|
include ActionView::Helpers::TextHelper # string truncate method
|
||||||
|
|
||||||
# rate limited by rack-attack - currently 5r/s
|
# rate limited by rack-attack - currently 5r/s
|
||||||
# TODO: what else can we do to make get_with_redirects safer?
|
|
||||||
def load_url_title
|
def load_url_title
|
||||||
authorize :Hack
|
authorize :Hack
|
||||||
url = params[:url]
|
url = params[:url]
|
||||||
|
|
|
@ -33,6 +33,7 @@ class MappingsController < ApplicationController
|
||||||
@mapping = Mapping.find(params[:id])
|
@mapping = Mapping.find(params[:id])
|
||||||
authorize @mapping
|
authorize @mapping
|
||||||
@mapping.updated_by = current_user
|
@mapping.updated_by = current_user
|
||||||
|
@mapping.map.updated_by = current_user
|
||||||
@mapping.assign_attributes(mapping_params)
|
@mapping.assign_attributes(mapping_params)
|
||||||
|
|
||||||
if @mapping.save
|
if @mapping.save
|
||||||
|
@ -47,6 +48,7 @@ class MappingsController < ApplicationController
|
||||||
@mapping = Mapping.find(params[:id])
|
@mapping = Mapping.find(params[:id])
|
||||||
authorize @mapping
|
authorize @mapping
|
||||||
@mapping.updated_by = current_user
|
@mapping.updated_by = current_user
|
||||||
|
@mapping.map.updated_by = current_user
|
||||||
@mapping.destroy
|
@mapping.destroy
|
||||||
|
|
||||||
head :no_content
|
head :no_content
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class MapsController < ApplicationController
|
class MapsController < ApplicationController
|
||||||
before_action :require_user, only: [:create, :update, :destroy, :events]
|
before_action :require_user, only: [:create, :update, :destroy, :events]
|
||||||
before_action :set_map, only: [:show, :update, :destroy, :contains, :events, :export]
|
before_action :set_map, only: [:show, :conversation, :update, :destroy, :contains, :events, :export]
|
||||||
after_action :verify_authorized
|
after_action :verify_authorized
|
||||||
|
|
||||||
# GET maps/:id
|
# GET maps/:id
|
||||||
|
@ -20,6 +20,24 @@ class MapsController < ApplicationController
|
||||||
end
|
end
|
||||||
format.json { render json: @map }
|
format.json { render json: @map }
|
||||||
format.csv { redirect_to action: :export, format: :csv }
|
format.csv { redirect_to action: :export, format: :csv }
|
||||||
|
format.ttl { redirect_to action: :export, format: :ttl }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET maps/:id/conversation
|
||||||
|
def conversation
|
||||||
|
respond_to do |format|
|
||||||
|
format.html do
|
||||||
|
UserMap.where(map: @map, user: current_user).map(&:mark_invite_notifications_as_read)
|
||||||
|
@allmappers = @map.contributors
|
||||||
|
@allcollaborators = @map.editors
|
||||||
|
@alltopics = policy_scope(@map.topics)
|
||||||
|
@allsynapses = policy_scope(@map.synapses)
|
||||||
|
@allmappings = policy_scope(@map.mappings)
|
||||||
|
@allmessages = @map.messages.sort_by(&:created_at)
|
||||||
|
@allstars = @map.stars
|
||||||
|
@allrequests = @map.access_requests
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -41,6 +59,7 @@ class MapsController < ApplicationController
|
||||||
def create
|
def create
|
||||||
@map = Map.new(create_map_params)
|
@map = Map.new(create_map_params)
|
||||||
@map.user = current_user
|
@map.user = current_user
|
||||||
|
@map.updated_by = current_user
|
||||||
@map.arranged = false
|
@map.arranged = false
|
||||||
authorize @map
|
authorize @map
|
||||||
|
|
||||||
|
@ -61,8 +80,11 @@ class MapsController < ApplicationController
|
||||||
|
|
||||||
# PUT maps/:id
|
# PUT maps/:id
|
||||||
def update
|
def update
|
||||||
|
@map.updated_by = current_user
|
||||||
|
@map.assign_attributes(update_map_params)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @map.update_attributes(update_map_params)
|
if @map.save
|
||||||
format.json { head :no_content }
|
format.json { head :no_content }
|
||||||
else
|
else
|
||||||
format.json { render json: @map.errors, status: :unprocessable_entity }
|
format.json { render json: @map.errors, status: :unprocessable_entity }
|
||||||
|
@ -72,7 +94,8 @@ class MapsController < ApplicationController
|
||||||
|
|
||||||
# DELETE maps/:id
|
# DELETE maps/:id
|
||||||
def destroy
|
def destroy
|
||||||
@map.delete
|
@map.updated_by = current_user
|
||||||
|
@map.destroy
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.json do
|
format.json do
|
||||||
|
@ -90,10 +113,12 @@ class MapsController < ApplicationController
|
||||||
|
|
||||||
# GET maps/:id/export
|
# GET maps/:id/export
|
||||||
def export
|
def export
|
||||||
exporter = MapExportService.new(current_user, @map)
|
exporter = MapExportService.new(current_user, @map, base_url: request.base_url)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.json { render json: exporter.json }
|
format.json { render json: exporter.json }
|
||||||
format.csv { send_data exporter.csv }
|
format.csv { send_data exporter.csv }
|
||||||
|
format.ttl { render text: exporter.rdf }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -103,9 +128,6 @@ class MapsController < ApplicationController
|
||||||
if params[:event] == 'conversation'
|
if params[:event] == 'conversation'
|
||||||
Events::ConversationStartedOnMap.publish!(@map, current_user)
|
Events::ConversationStartedOnMap.publish!(@map, current_user)
|
||||||
valid_event = true
|
valid_event = true
|
||||||
elsif params[:event] == 'user_presence'
|
|
||||||
Events::UserPresentOnMap.publish!(@map, current_user)
|
|
||||||
valid_event = true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
|
|
@ -55,8 +55,13 @@ class MetacodeSetsController < ApplicationController
|
||||||
@metacodes.each do |m|
|
@metacodes.each do |m|
|
||||||
InMetacodeSet.create(metacode_id: m, metacode_set_id: @metacode_set.id)
|
InMetacodeSet.create(metacode_id: m, metacode_set_id: @metacode_set.id)
|
||||||
end
|
end
|
||||||
format.html { redirect_to metacode_sets_url, notice: 'Metacode set was successfully created.' }
|
format.html do
|
||||||
format.json { render json: @metacode_set, status: :created, location: metacode_sets_url }
|
redirect_to metacode_sets_url,
|
||||||
|
notice: 'Metacode set was successfully created.'
|
||||||
|
end
|
||||||
|
format.json do
|
||||||
|
render json: @metacode_set, status: :created, location: metacode_sets_url
|
||||||
|
end
|
||||||
else
|
else
|
||||||
format.html { render action: 'new' }
|
format.html { render action: 'new' }
|
||||||
format.json { render json: @metacode_set.errors, status: :unprocessable_entity }
|
format.json { render json: @metacode_set.errors, status: :unprocessable_entity }
|
||||||
|
@ -73,20 +78,20 @@ class MetacodeSetsController < ApplicationController
|
||||||
if @metacode_set.update_attributes(metacode_set_params)
|
if @metacode_set.update_attributes(metacode_set_params)
|
||||||
|
|
||||||
# build an array of the IDs of the metacodes currently in the set
|
# build an array of the IDs of the metacodes currently in the set
|
||||||
@currentMetacodes = @metacode_set.metacodes.map { |m| m.id.to_s }
|
current_metacodes = @metacode_set.metacodes.map { |m| m.id.to_s }
|
||||||
# get the list of desired metacodes for the set from the user input and build an array out of it
|
# get the list of desired metacodes for the set from the user input and build an array out of it
|
||||||
@newMetacodes = params[:metacodes][:value].split(',')
|
new_metacodes = params[:metacodes][:value].split(',')
|
||||||
|
|
||||||
# remove the metacodes that were in it, but now aren't
|
# remove the metacodes that were in it, but now aren't
|
||||||
@removedMetacodes = @currentMetacodes - @newMetacodes
|
removed_metacodes = current_metacodes - new_metacodes
|
||||||
@removedMetacodes.each do |m|
|
removed_metacodes.each do |m|
|
||||||
@inmetacodeset = InMetacodeSet.find_by_metacode_id_and_metacode_set_id(m, @metacode_set.id)
|
inmetacodeset = InMetacodeSet.find_by(metacode_id: m, metacode_set_id: @metacode_set.id)
|
||||||
@inmetacodeset.destroy
|
inmetacodeset.destroy
|
||||||
end
|
end
|
||||||
|
|
||||||
# add the new metacodes
|
# add the new metacodes
|
||||||
@addedMetacodes = @newMetacodes - @currentMetacodes
|
added_metacodes = new_metacodes - current_metacodes
|
||||||
@addedMetacodes.each do |m|
|
added_metacodes.each do |m|
|
||||||
InMetacodeSet.create(metacode_id: m, metacode_set_id: @metacode_set.id)
|
InMetacodeSet.create(metacode_id: m, metacode_set_id: @metacode_set.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,18 @@ class NotificationsController < ApplicationController
|
||||||
def show
|
def show
|
||||||
@receipt.update(is_read: true)
|
@receipt.update(is_read: true)
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html
|
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)
|
||||||
|
end
|
||||||
|
end
|
||||||
format.json do
|
format.json do
|
||||||
render json: @notification.as_json.merge(
|
render json: @notification.as_json.merge(
|
||||||
is_read: @receipt.is_read
|
is_read: @receipt.is_read
|
||||||
|
|
|
@ -14,7 +14,8 @@ class SearchController < ApplicationController
|
||||||
term = params[:term]
|
term = params[:term]
|
||||||
user = params[:user] ? params[:user] : false
|
user = params[:user] ? params[:user] : false
|
||||||
|
|
||||||
if term && !term.empty? && term.downcase[0..3] != 'map:' && term.downcase[0..6] != 'mapper:' && !term.casecmp('topic:').zero?
|
if term.present? && term.downcase[0..3] != 'map:' &&
|
||||||
|
term.downcase[0..6] != 'mapper:' && !term.casecmp('topic:').zero?
|
||||||
|
|
||||||
# remove "topic:" if appended at beginning
|
# remove "topic:" if appended at beginning
|
||||||
term = term[6..-1] if term.downcase[0..5] == 'topic:'
|
term = term[6..-1] if term.downcase[0..5] == 'topic:'
|
||||||
|
@ -34,28 +35,28 @@ class SearchController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
# check whether there's a filter by metacode as part of the query
|
# check whether there's a filter by metacode as part of the query
|
||||||
filterByMetacode = false
|
filter_by_metacode = false
|
||||||
Metacode.all.each do |m|
|
Metacode.all.each do |m|
|
||||||
lOne = m.name.length + 1
|
length_one = m.name.length + 1
|
||||||
lTwo = m.name.length
|
length_two = m.name.length
|
||||||
|
|
||||||
if term.downcase[0..lTwo] == m.name.downcase + ':'
|
if term.downcase[0..length_two] == m.name.downcase + ':'
|
||||||
term = term[lOne..-1]
|
term = term[length_one..-1]
|
||||||
filterByMetacode = m
|
filter_by_metacode = m
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
search = '%' + term.downcase.strip + '%'
|
search = '%' + term.downcase.strip + '%'
|
||||||
builder = policy_scope(Topic)
|
builder = policy_scope(Topic)
|
||||||
|
|
||||||
if filterByMetacode
|
if filter_by_metacode
|
||||||
if term == ''
|
if term == ''
|
||||||
builder = builder.none
|
builder = builder.none
|
||||||
else
|
else
|
||||||
builder = builder.where('LOWER("name") like ? OR
|
builder = builder.where('LOWER("name") like ? OR
|
||||||
LOWER("desc") like ? OR
|
LOWER("desc") like ? OR
|
||||||
LOWER("link") like ?', search, search, search)
|
LOWER("link") like ?', search, search, search)
|
||||||
builder = builder.where(metacode_id: filterByMetacode.id)
|
builder = builder.where(metacode_id: filter_by_metacode.id)
|
||||||
end
|
end
|
||||||
elsif desc
|
elsif desc
|
||||||
builder = builder.where('LOWER("desc") like ?', search)
|
builder = builder.where('LOWER("desc") like ?', search)
|
||||||
|
@ -82,7 +83,8 @@ class SearchController < ApplicationController
|
||||||
term = params[:term]
|
term = params[:term]
|
||||||
user = params[:user] ? params[:user] : nil
|
user = params[:user] ? params[:user] : nil
|
||||||
|
|
||||||
if term && !term.empty? && term.downcase[0..5] != 'topic:' && term.downcase[0..6] != 'mapper:' && !term.casecmp('map:').zero?
|
if term.present? && term.downcase[0..5] != 'topic:' &&
|
||||||
|
term.downcase[0..6] != 'mapper:' && !term.casecmp('map:').zero?
|
||||||
|
|
||||||
# remove "map:" if appended at beginning
|
# remove "map:" if appended at beginning
|
||||||
term = term[4..-1] if term.downcase[0..3] == 'map:'
|
term = term[4..-1] if term.downcase[0..3] == 'map:'
|
||||||
|
@ -115,7 +117,8 @@ class SearchController < ApplicationController
|
||||||
# get /search/mappers?term=SOMETERM
|
# get /search/mappers?term=SOMETERM
|
||||||
def mappers
|
def mappers
|
||||||
term = params[:term]
|
term = params[:term]
|
||||||
if term && !term.empty? && term.downcase[0..3] != 'map:' && term.downcase[0..5] != 'topic:' && !term.casecmp('mapper:').zero?
|
if term.present? && term.downcase[0..3] != 'map:' &&
|
||||||
|
term.downcase[0..5] != 'topic:' && !term.casecmp('mapper:').zero?
|
||||||
|
|
||||||
# remove "mapper:" if appended at beginning
|
# remove "mapper:" if appended at beginning
|
||||||
term = term[7..-1] if term.downcase[0..6] == 'mapper:'
|
term = term[7..-1] if term.downcase[0..6] == 'mapper:'
|
||||||
|
@ -138,13 +141,15 @@ class SearchController < ApplicationController
|
||||||
topic2id = params[:topic2id]
|
topic2id = params[:topic2id]
|
||||||
|
|
||||||
if term && !term.empty?
|
if term && !term.empty?
|
||||||
@synapses = policy_scope(Synapse).where('LOWER("desc") like ?', '%' + term.downcase.strip + '%').order('"desc"')
|
@synapses = policy_scope(Synapse)
|
||||||
|
.where('LOWER("desc") like ?', '%' + term.downcase.strip + '%')
|
||||||
|
.order('"desc"')
|
||||||
|
|
||||||
@synapses = @synapses.uniq(&:desc)
|
@synapses = @synapses.uniq(&:desc)
|
||||||
elsif topic1id && !topic1id.empty?
|
elsif topic1id && !topic1id.empty?
|
||||||
@one = policy_scope(Synapse).where(topic1_id: topic1id, topic2_id: topic2id)
|
one = policy_scope(Synapse).where(topic1_id: topic1id, topic2_id: topic2id)
|
||||||
@two = policy_scope(Synapse).where(topic2_id: topic1id, topic1_id: topic2id)
|
two = policy_scope(Synapse).where(topic2_id: topic1id, topic1_id: topic2id)
|
||||||
@synapses = @one + @two
|
@synapses = one + two
|
||||||
@synapses.sort! { |s1, s2| s1.desc <=> s2.desc }.to_a
|
@synapses.sort! { |s1, s2| s1.desc <=> s2.desc }.to_a
|
||||||
else
|
else
|
||||||
skip_policy_scope
|
skip_policy_scope
|
||||||
|
|
|
@ -22,6 +22,8 @@ class SynapsesController < ApplicationController
|
||||||
@synapse = Synapse.new(synapse_params)
|
@synapse = Synapse.new(synapse_params)
|
||||||
@synapse.desc = '' if @synapse.desc.nil?
|
@synapse.desc = '' if @synapse.desc.nil?
|
||||||
@synapse.desc.strip! # no trailing/leading whitespace
|
@synapse.desc.strip! # no trailing/leading whitespace
|
||||||
|
@synapse.user = current_user
|
||||||
|
@synapse.updated_by = current_user
|
||||||
|
|
||||||
# we want invalid params to return :unprocessable_entity
|
# we want invalid params to return :unprocessable_entity
|
||||||
# so we have to authorize AFTER saving. But if authorize
|
# so we have to authorize AFTER saving. But if authorize
|
||||||
|
@ -47,9 +49,11 @@ class SynapsesController < ApplicationController
|
||||||
@synapse = Synapse.find(params[:id])
|
@synapse = Synapse.find(params[:id])
|
||||||
@synapse.desc = '' if @synapse.desc.nil?
|
@synapse.desc = '' if @synapse.desc.nil?
|
||||||
authorize @synapse
|
authorize @synapse
|
||||||
|
@synapse.updated_by = current_user
|
||||||
|
@synapse.assign_attributes(synapse_params)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @synapse.update_attributes(synapse_params)
|
if @synapse.save
|
||||||
format.json { head :no_content }
|
format.json { head :no_content }
|
||||||
else
|
else
|
||||||
format.json { render json: @synapse.errors, status: :unprocessable_entity }
|
format.json { render json: @synapse.errors, status: :unprocessable_entity }
|
||||||
|
@ -61,6 +65,7 @@ class SynapsesController < ApplicationController
|
||||||
def destroy
|
def destroy
|
||||||
@synapse = Synapse.find(params[:id])
|
@synapse = Synapse.find(params[:id])
|
||||||
authorize @synapse
|
authorize @synapse
|
||||||
|
@synapse.updated_by = current_user
|
||||||
@synapse.destroy
|
@synapse.destroy
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
|
@ -71,6 +76,8 @@ class SynapsesController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def synapse_params
|
def synapse_params
|
||||||
params.require(:synapse).permit(:id, :desc, :category, :weight, :permission, :topic1_id, :topic2_id, :user_id)
|
params.require(:synapse).permit(
|
||||||
|
:id, :desc, :category, :weight, :permission, :topic1_id, :topic2_id
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
9
app/controllers/tokens_controller.rb
Normal file
9
app/controllers/tokens_controller.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class TokensController < ApplicationController
|
||||||
|
before_action :require_user, only: [:new]
|
||||||
|
|
||||||
|
def new
|
||||||
|
@token = Token.new(user: current_user)
|
||||||
|
render :new, layout: false
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,16 +11,20 @@ class TopicsController < ApplicationController
|
||||||
def autocomplete_topic
|
def autocomplete_topic
|
||||||
term = params[:term]
|
term = params[:term]
|
||||||
if term && !term.empty?
|
if term && !term.empty?
|
||||||
@topics = policy_scope(Topic).where('LOWER("name") like ?', term.downcase + '%').order('"name"')
|
topics = policy_scope(Topic)
|
||||||
@mapTopics = @topics.select { |t| t&.metacode&.name == 'Metamap' }
|
.where('LOWER("name") like ?', term.downcase + '%')
|
||||||
|
.order('"name"')
|
||||||
|
map_topics = topics.select { |t| t&.metacode&.name == 'Metamap' }
|
||||||
# prioritize topics which point to maps, over maps
|
# prioritize topics which point to maps, over maps
|
||||||
@exclude = @mapTopics.length.positive? ? @mapTopics.map(&:name) : ['']
|
exclude = map_topics.length.positive? ? map_topics.map(&:name) : ['']
|
||||||
@maps = policy_scope(Map).where('LOWER("name") like ? AND name NOT IN (?)', term.downcase + '%', @exclude).order('"name"')
|
maps = policy_scope(Map)
|
||||||
|
.where('LOWER("name") like ? AND name NOT IN (?)', term.downcase + '%', exclude)
|
||||||
|
.order('"name"')
|
||||||
else
|
else
|
||||||
@topics = []
|
topics = []
|
||||||
@maps = []
|
maps = []
|
||||||
end
|
end
|
||||||
@all = @topics.to_a.concat(@maps.to_a).sort_by(&:name)
|
@all = topics.to_a.concat(maps.to_a).sort_by(&:name)
|
||||||
|
|
||||||
render json: autocomplete_array_json(@all).to_json
|
render json: autocomplete_array_json(@all).to_json
|
||||||
end
|
end
|
||||||
|
@ -70,13 +74,13 @@ class TopicsController < ApplicationController
|
||||||
@topic = Topic.find(params[:id])
|
@topic = Topic.find(params[:id])
|
||||||
authorize @topic
|
authorize @topic
|
||||||
|
|
||||||
topicsAlreadyHas = params[:network] ? params[:network].split(',').map(&:to_i) : []
|
topics_already_has = params[:network] ? params[:network].split(',').map(&:to_i) : []
|
||||||
|
|
||||||
alltopics = policy_scope(Topic.relatives(@topic.id, current_user)).to_a
|
alltopics = policy_scope(Topic.relatives(@topic.id, current_user)).to_a
|
||||||
alltopics.delete_if { |topic| topic.metacode_id != params[:metacode].to_i } if params[:metacode].present?
|
if params[:metacode].present?
|
||||||
alltopics.delete_if do |topic|
|
alltopics.delete_if { |topic| topic.metacode_id != params[:metacode].to_i }
|
||||||
!topicsAlreadyHas.index(topic.id).nil?
|
|
||||||
end
|
end
|
||||||
|
alltopics.delete_if { |topic| !topics_already_has.index(topic.id).nil? }
|
||||||
|
|
||||||
@json = Hash.new(0)
|
@json = Hash.new(0)
|
||||||
alltopics.each do |t|
|
alltopics.each do |t|
|
||||||
|
@ -93,12 +97,14 @@ class TopicsController < ApplicationController
|
||||||
@topic = Topic.find(params[:id])
|
@topic = Topic.find(params[:id])
|
||||||
authorize @topic
|
authorize @topic
|
||||||
|
|
||||||
topicsAlreadyHas = params[:network] ? params[:network].split(',').map(&:to_i) : []
|
topics_already_has = params[:network] ? params[:network].split(',').map(&:to_i) : []
|
||||||
|
|
||||||
alltopics = policy_scope(Topic.relatives(@topic.id, current_user)).to_a
|
alltopics = policy_scope(Topic.relatives(@topic.id, current_user)).to_a
|
||||||
alltopics.delete_if { |topic| topic.metacode_id != params[:metacode].to_i } if params[:metacode].present?
|
if params[:metacode].present?
|
||||||
|
alltopics.delete_if { |topic| topic.metacode_id != params[:metacode].to_i }
|
||||||
|
end
|
||||||
alltopics.delete_if do |topic|
|
alltopics.delete_if do |topic|
|
||||||
!topicsAlreadyHas.index(topic.id.to_s).nil?
|
!topics_already_has.index(topic.id.to_s).nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
# find synapses between topics in alltopics array
|
# find synapses between topics in alltopics array
|
||||||
|
@ -108,9 +114,9 @@ class TopicsController < ApplicationController
|
||||||
!synapse_ids.index(synapse.id).nil?
|
!synapse_ids.index(synapse.id).nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
creatorsAlreadyHas = params[:creators] ? params[:creators].split(',').map(&:to_i) : []
|
creators_already_has = params[:creators] ? params[:creators].split(',').map(&:to_i) : []
|
||||||
allcreators = (alltopics.map(&:user) + allsynapses.map(&:user)).uniq.delete_if do |user|
|
allcreators = (alltopics.map(&:user) + allsynapses.map(&:user)).uniq.delete_if do |user|
|
||||||
!creatorsAlreadyHas.index(user.id).nil?
|
!creators_already_has.index(user.id).nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
@json = {}
|
@json = {}
|
||||||
|
@ -128,6 +134,8 @@ class TopicsController < ApplicationController
|
||||||
def create
|
def create
|
||||||
@topic = Topic.new(topic_params)
|
@topic = Topic.new(topic_params)
|
||||||
authorize @topic
|
authorize @topic
|
||||||
|
@topic.user = current_user
|
||||||
|
@topic.updated_by = current_user
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @topic.save
|
if @topic.save
|
||||||
|
@ -143,9 +151,11 @@ class TopicsController < ApplicationController
|
||||||
def update
|
def update
|
||||||
@topic = Topic.find(params[:id])
|
@topic = Topic.find(params[:id])
|
||||||
authorize @topic
|
authorize @topic
|
||||||
|
@topic.updated_by = current_user
|
||||||
|
@topic.assign_attributes(topic_params)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
if @topic.update_attributes(topic_params)
|
if @topic.save
|
||||||
format.json { head :no_content }
|
format.json { head :no_content }
|
||||||
else
|
else
|
||||||
format.json { render json: @topic.errors, status: :unprocessable_entity }
|
format.json { render json: @topic.errors, status: :unprocessable_entity }
|
||||||
|
@ -157,7 +167,7 @@ class TopicsController < ApplicationController
|
||||||
def destroy
|
def destroy
|
||||||
@topic = Topic.find(params[:id])
|
@topic = Topic.find(params[:id])
|
||||||
authorize @topic
|
authorize @topic
|
||||||
|
@topic.updated_by = current_user
|
||||||
@topic.destroy
|
@topic.destroy
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.json { head :no_content }
|
format.json { head :no_content }
|
||||||
|
@ -167,6 +177,6 @@ class TopicsController < ApplicationController
|
||||||
private
|
private
|
||||||
|
|
||||||
def topic_params
|
def topic_params
|
||||||
params.require(:topic).permit(:id, :name, :desc, :link, :permission, :user_id, :metacode_id, :defer_to_map_id)
|
params.require(:topic).permit(:id, :name, :desc, :link, :permission, :metacode_id, :defer_to_map_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Users::PasswordsController < Devise::PasswordsController
|
module Users
|
||||||
protected
|
class PasswordsController < Devise::PasswordsController
|
||||||
|
protected
|
||||||
|
|
||||||
def after_resetting_password_path_for(resource)
|
def after_resetting_password_path_for(resource)
|
||||||
signed_in_root_path(resource)
|
signed_in_root_path(resource)
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_sending_reset_password_instructions_path_for(_resource_name)
|
def after_sending_reset_password_instructions_path_for(_resource_name)
|
||||||
sign_in_path if is_navigational_format?
|
sign_in_path if is_navigational_format?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,37 +1,39 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Users::RegistrationsController < Devise::RegistrationsController
|
module Users
|
||||||
before_action :configure_sign_up_params, only: [:create]
|
class RegistrationsController < Devise::RegistrationsController
|
||||||
before_action :configure_account_update_params, only: [:update]
|
before_action :configure_sign_up_params, only: [:create]
|
||||||
after_action :store_location, only: [:new]
|
before_action :configure_account_update_params, only: [:update]
|
||||||
|
after_action :store_location, only: [:new]
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def after_update_path_for(resource)
|
def after_update_path_for(resource)
|
||||||
signed_in_root_path(resource)
|
signed_in_root_path(resource)
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_sign_in_path_for(resource)
|
def after_sign_in_path_for(resource)
|
||||||
stored = stored_location_for(User)
|
stored = stored_location_for(User)
|
||||||
return stored if stored
|
return stored if stored
|
||||||
|
|
||||||
if request.referer&.match(sign_in_url) || request.referer&.match(sign_up_url)
|
if request.referer&.match(sign_in_url) || request.referer&.match(sign_up_url)
|
||||||
super
|
super
|
||||||
else
|
else
|
||||||
request.referer || root_path
|
request.referer || root_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def store_location
|
||||||
|
store_location_for(User, params[:redirect_to]) if params[:redirect_to]
|
||||||
|
end
|
||||||
|
|
||||||
|
def configure_sign_up_params
|
||||||
|
devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :joinedwithcode])
|
||||||
|
end
|
||||||
|
|
||||||
|
def configure_account_update_params
|
||||||
|
devise_parameter_sanitizer.permit(:account_update, keys: [:image])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def store_location
|
|
||||||
store_location_for(User, params[:redirect_to]) if params[:redirect_to]
|
|
||||||
end
|
|
||||||
|
|
||||||
def configure_sign_up_params
|
|
||||||
devise_parameter_sanitizer.permit(:sign_up, keys: [:name, :joinedwithcode])
|
|
||||||
end
|
|
||||||
|
|
||||||
def configure_account_update_params
|
|
||||||
devise_parameter_sanitizer.permit(:account_update, keys: [:image])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class UsersController < ApplicationController
|
class UsersController < ApplicationController
|
||||||
before_action :require_user, only: [:edit, :update, :updatemetacodes]
|
before_action :require_user, only: [:edit, :update, :updatemetacodes, :update_metacode_focus]
|
||||||
|
|
||||||
respond_to :html, :json
|
respond_to :html, :json
|
||||||
|
|
||||||
|
@ -92,6 +92,16 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# PUT /user/update_metacode_focus
|
||||||
|
def update_metacode_focus
|
||||||
|
@user = current_user
|
||||||
|
@user.settings.metacode_focus = params[:value]
|
||||||
|
@user.save
|
||||||
|
respond_to do |format|
|
||||||
|
format.json { render json: { success: "success" }}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def user_params
|
def user_params
|
||||||
|
|
|
@ -2,12 +2,11 @@
|
||||||
module ApplicationHelper
|
module ApplicationHelper
|
||||||
def metacodeset
|
def metacodeset
|
||||||
metacodes = current_user.settings.metacodes
|
metacodes = current_user.settings.metacodes
|
||||||
|
|
||||||
return false unless metacodes[0].include?('metacodeset')
|
return false unless metacodes[0].include?('metacodeset')
|
||||||
if metacodes[0].sub('metacodeset-', '') == 'Most'
|
return 'Most' if metacodes[0].sub('metacodeset-', '') == 'Most'
|
||||||
return 'Most'
|
return 'Recent' if metacodes[0].sub('metacodeset-', '') == 'Recent'
|
||||||
elsif metacodes[0].sub('metacodeset-', '') == 'Recent'
|
|
||||||
return 'Recent'
|
|
||||||
end
|
|
||||||
MetacodeSet.find(metacodes[0].sub('metacodeset-', '').to_i)
|
MetacodeSet.find(metacodes[0].sub('metacodeset-', '').to_i)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -23,7 +22,24 @@ module ApplicationHelper
|
||||||
else
|
else
|
||||||
Metacode.where(id: @m).to_a
|
Metacode.where(id: @m).to_a
|
||||||
end
|
end
|
||||||
@metacodes.sort! { |m1, m2| m2.name.downcase <=> m1.name.downcase }.rotate!(-1)
|
|
||||||
|
focus_code = user_metacode()
|
||||||
|
if focus_code != nil && @metacodes.index{|m| m.id == focus_code.id} == nil
|
||||||
|
@metacodes.push(focus_code)
|
||||||
|
end
|
||||||
|
|
||||||
|
@metacodes
|
||||||
|
.sort! { |m1, m2| m2.name.downcase <=> m1.name.downcase }
|
||||||
|
|
||||||
|
if focus_code != nil
|
||||||
|
@metacodes.rotate!(@metacodes.index{|m| m.id == focus_code.id})
|
||||||
|
else
|
||||||
|
@metacodes.rotate!(-1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_metacode
|
||||||
|
current_user.settings.metacode_focus ? Metacode.find(current_user.settings.metacode_focus.to_i) : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_most_used_metacodes
|
def user_most_used_metacodes
|
||||||
|
|
13
app/helpers/map_mailer_helper.rb
Normal file
13
app/helpers/map_mailer_helper.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
module MapMailerHelper
|
||||||
|
def access_approved_subject(map)
|
||||||
|
map.name + ' - access approved'
|
||||||
|
end
|
||||||
|
|
||||||
|
def access_request_subject(map)
|
||||||
|
map.name + ' - request to edit'
|
||||||
|
end
|
||||||
|
|
||||||
|
def invite_to_edit_subject(map)
|
||||||
|
map.name + ' - invited to edit'
|
||||||
|
end
|
||||||
|
end
|
10
app/helpers/topic_mailer_helper.rb
Normal file
10
app/helpers/topic_mailer_helper.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module TopicMailerHelper
|
||||||
|
def added_to_map_subject(topic, map)
|
||||||
|
topic.name + ' was added to map ' + map.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def connected_subject(topic)
|
||||||
|
'new synapse to topic ' + topic.name
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,7 +4,7 @@ module TopicsHelper
|
||||||
def autocomplete_array_json(topics)
|
def autocomplete_array_json(topics)
|
||||||
topics.map do |t|
|
topics.map do |t|
|
||||||
is_map = t.is_a?(Map)
|
is_map = t.is_a?(Map)
|
||||||
metamapMetacode = Metacode.find_by_name('Metamap')
|
metamap_metacode = Metacode.find_by(name: 'Metamap')
|
||||||
{
|
{
|
||||||
id: t.id,
|
id: t.id,
|
||||||
label: t.name,
|
label: t.name,
|
||||||
|
@ -17,8 +17,8 @@ module TopicsHelper
|
||||||
rtype: is_map ? 'map' : 'topic',
|
rtype: is_map ? 'map' : 'topic',
|
||||||
inmaps: is_map ? [] : t.inmaps(current_user),
|
inmaps: is_map ? [] : t.inmaps(current_user),
|
||||||
inmapsLinks: is_map ? [] : t.inmapsLinks(current_user),
|
inmapsLinks: is_map ? [] : t.inmapsLinks(current_user),
|
||||||
type: is_map ? metamapMetacode.name : t.metacode.name,
|
type: is_map ? metamap_metacode.name : t.metacode.name,
|
||||||
typeImageURL: is_map ? metamapMetacode.icon : t.metacode.icon,
|
typeImageURL: is_map ? metamap_metacode.icon : t.metacode.icon,
|
||||||
mapCount: is_map ? 0 : t.maps.count,
|
mapCount: is_map ? 0 : t.maps.count,
|
||||||
synapseCount: is_map ? 0 : t.synapses.count
|
synapseCount: is_map ? 0 : t.synapses.count
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,25 @@ class ApplicationMailer < ActionMailer::Base
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def mail_for_notification(notification)
|
def mail_for_notification(notification)
|
||||||
if notification.notification_code == MAILBOXER_CODE_ACCESS_REQUEST
|
case notification.notification_code
|
||||||
request = notification.notified_object
|
when MAP_ACCESS_REQUEST
|
||||||
MapMailer.access_request_email(request)
|
request = notification.notified_object
|
||||||
elsif notification.notification_code == MAILBOXER_CODE_ACCESS_APPROVED
|
MapMailer.access_request(request)
|
||||||
request = notification.notified_object
|
when MAP_ACCESS_APPROVED
|
||||||
MapMailer.access_approved_email(request)
|
request = notification.notified_object
|
||||||
elsif notification.notification_code == MAILBOXER_CODE_INVITE_TO_EDIT
|
MapMailer.access_approved(request)
|
||||||
user_map = notification.notified_object
|
when MAP_INVITE_TO_EDIT
|
||||||
MapMailer.invite_to_edit_email(user_map.map, user_map.map.user, user_map.user)
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class MapMailer < ApplicationMailer
|
class MapMailer < ApplicationMailer
|
||||||
|
include MapMailerHelper
|
||||||
default from: 'team@metamaps.cc'
|
default from: 'team@metamaps.cc'
|
||||||
|
|
||||||
def access_request_email(request)
|
def access_approved(request)
|
||||||
@request = request
|
@request = request
|
||||||
@map = request.map
|
@map = request.map
|
||||||
mail(to: @map.user.email, subject: request.requested_text)
|
mail(to: request.user.email, subject: access_approved_subject(@map))
|
||||||
end
|
end
|
||||||
|
|
||||||
def access_approved_email(request)
|
def access_request(request)
|
||||||
@request = request
|
@request = request
|
||||||
@map = request.map
|
@map = request.map
|
||||||
mail(to: request.user, subject: request.approved_text)
|
mail(to: @map.user.email, subject: access_request_subject(@map))
|
||||||
end
|
end
|
||||||
|
|
||||||
def invite_to_edit_email(map, inviter, invitee)
|
def invite_to_edit(user_map)
|
||||||
@inviter = inviter
|
@inviter = user_map.map.user
|
||||||
@map = map
|
@map = user_map.map
|
||||||
mail(to: invitee.email, subject: map.invited_text)
|
mail(to: user_map.user.email, subject: invite_to_edit_subject(@map))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
17
app/mailers/topic_mailer.rb
Normal file
17
app/mailers/topic_mailer.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class TopicMailer < ApplicationMailer
|
||||||
|
include TopicMailerHelper
|
||||||
|
default from: 'team@metamaps.cc'
|
||||||
|
|
||||||
|
def added_to_map(event, user)
|
||||||
|
@entity = event.eventable
|
||||||
|
@event = event
|
||||||
|
mail(to: user.email, subject: added_to_map_subject(@entity, event.map))
|
||||||
|
end
|
||||||
|
|
||||||
|
def connected(synapse, topic, user)
|
||||||
|
@entity = topic
|
||||||
|
@event = synapse
|
||||||
|
mail(to: user.email, subject: connected_subject(topic))
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,9 @@
|
||||||
class AccessRequest < ApplicationRecord
|
class AccessRequest < ApplicationRecord
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :map
|
belongs_to :map
|
||||||
|
has_one :user_map
|
||||||
|
|
||||||
|
after_create :after_created_async
|
||||||
|
|
||||||
def approve
|
def approve
|
||||||
self.approved = true
|
self.approved = true
|
||||||
|
@ -12,8 +15,7 @@ class AccessRequest < ApplicationRecord
|
||||||
Mailboxer::Receipt.where(notification: notification).update_all(is_read: true)
|
Mailboxer::Receipt.where(notification: notification).update_all(is_read: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
user_map = UserMap.create(user: user, map: map)
|
UserMap.create(user: user, map: map, access_request: self)
|
||||||
NotificationService.access_approved(self)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def deny
|
def deny
|
||||||
|
@ -25,12 +27,11 @@ class AccessRequest < ApplicationRecord
|
||||||
Mailboxer::Receipt.where(notification: notification).update_all(is_read: true)
|
Mailboxer::Receipt.where(notification: notification).update_all(is_read: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def requested_text
|
protected
|
||||||
map.name + ' - request to edit'
|
|
||||||
end
|
def after_created_async
|
||||||
|
NotificationService.access_request(self)
|
||||||
def approved_text
|
|
||||||
map.name + ' - access approved'
|
|
||||||
end
|
end
|
||||||
|
handle_asynchronously :after_created_async
|
||||||
end
|
end
|
||||||
|
|
38
app/models/attachment.rb
Normal file
38
app/models/attachment.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class Attachment < ApplicationRecord
|
||||||
|
belongs_to :attachable, polymorphic: true
|
||||||
|
|
||||||
|
has_attached_file :file,
|
||||||
|
styles: lambda { |a|
|
||||||
|
if a.instance.image?
|
||||||
|
{
|
||||||
|
thumb: 'x128#',
|
||||||
|
medium: 'x320>'
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{}
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
validates_attachment_content_type :file, content_type: Attachable.allowed_types
|
||||||
|
|
||||||
|
def image?
|
||||||
|
Attachable.image_types.include?(file.instance.file_content_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def audio?
|
||||||
|
Attachable.audio_types.include?(file.instance.file_content_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def text?
|
||||||
|
Attachable.text_types.include?(file.instance.file_content_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pdf?
|
||||||
|
Attachable.pdf_types.include?(file.instance.file_content_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def document?
|
||||||
|
text? || pdf?
|
||||||
|
end
|
||||||
|
end
|
50
app/models/concerns/attachable.rb
Normal file
50
app/models/concerns/attachable.rb
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module Attachable
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
has_many :attachments, as: :attachable, dependent: :destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
def images
|
||||||
|
attachments.where(file_content_type: image_types)
|
||||||
|
end
|
||||||
|
|
||||||
|
def audios
|
||||||
|
attachments.where(file_content_type: audio_types)
|
||||||
|
end
|
||||||
|
|
||||||
|
def texts
|
||||||
|
attachments.where(file_content_type: text_types)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pdfs
|
||||||
|
attachments.where(file_content_type: pdf_types)
|
||||||
|
end
|
||||||
|
|
||||||
|
def documents
|
||||||
|
attachments.where(file_content_type: text_types + pdf_types)
|
||||||
|
end
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def image_types
|
||||||
|
['image/png', 'image/gif', 'image/jpeg']
|
||||||
|
end
|
||||||
|
|
||||||
|
def audio_types
|
||||||
|
['audio/ogg', 'audio/mp3']
|
||||||
|
end
|
||||||
|
|
||||||
|
def text_types
|
||||||
|
['text/plain']
|
||||||
|
end
|
||||||
|
|
||||||
|
def pdf_types
|
||||||
|
['application/pdf']
|
||||||
|
end
|
||||||
|
|
||||||
|
def allowed_types
|
||||||
|
image_types + audio_types + text_types + pdf_types
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,9 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Event < ApplicationRecord
|
class Event < ApplicationRecord
|
||||||
KINDS = %w(user_present_on_map conversation_started_on_map
|
KINDS = %w(user_present_on_map user_not_present_on_map
|
||||||
topic_added_to_map topic_moved_on_map topic_removed_from_map
|
conversation_started_on_map
|
||||||
synapse_added_to_map synapse_removed_from_map
|
topic_added_to_map topic_moved_on_map topic_removed_from_map
|
||||||
topic_updated synapse_updated).freeze
|
synapse_added_to_map synapse_removed_from_map
|
||||||
|
topic_updated synapse_updated).freeze
|
||||||
|
|
||||||
belongs_to :eventable, polymorphic: true
|
belongs_to :eventable, polymorphic: true
|
||||||
belongs_to :map
|
belongs_to :map
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Events::ConversationStartedOnMap < Event
|
module Events
|
||||||
# after_create :notify_users!
|
class ConversationStartedOnMap < Event
|
||||||
|
# after_create :notify_users!
|
||||||
|
|
||||||
def self.publish!(map, user)
|
def self.publish!(map, user)
|
||||||
create!(kind: 'conversation_started_on_map',
|
create!(kind: 'conversation_started_on_map',
|
||||||
eventable: map,
|
eventable: map,
|
||||||
map: map,
|
map: map,
|
||||||
user: user)
|
user: user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Events::SynapseAddedToMap < Event
|
module Events
|
||||||
# after_create :notify_users!
|
class SynapseAddedToMap < Event
|
||||||
|
# after_create :notify_users!
|
||||||
|
|
||||||
def self.publish!(synapse, map, user, meta)
|
def self.publish!(synapse, map, user, meta)
|
||||||
create!(kind: 'synapse_added_to_map',
|
create!(kind: 'synapse_added_to_map',
|
||||||
eventable: synapse,
|
eventable: synapse,
|
||||||
map: map,
|
map: map,
|
||||||
user: user,
|
user: user,
|
||||||
meta: meta)
|
meta: meta)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Events::SynapseRemovedFromMap < Event
|
module Events
|
||||||
# after_create :notify_users!
|
class SynapseRemovedFromMap < Event
|
||||||
|
# after_create :notify_users!
|
||||||
|
|
||||||
def self.publish!(synapse, map, user, meta)
|
def self.publish!(synapse, map, user, meta)
|
||||||
create!(kind: 'synapse_removed_from_map',
|
create!(kind: 'synapse_removed_from_map',
|
||||||
eventable: synapse,
|
eventable: synapse,
|
||||||
map: map,
|
map: map,
|
||||||
user: user,
|
user: user,
|
||||||
meta: meta)
|
meta: meta)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Events::SynapseUpdated < Event
|
module Events
|
||||||
# after_create :notify_users!
|
class SynapseUpdated < Event
|
||||||
|
# after_create :notify_users!
|
||||||
|
|
||||||
def self.publish!(synapse, user, meta)
|
def self.publish!(synapse, user, meta)
|
||||||
create!(kind: 'synapse_updated',
|
create!(kind: 'synapse_updated',
|
||||||
eventable: synapse,
|
eventable: synapse,
|
||||||
user: user,
|
user: user,
|
||||||
meta: meta)
|
meta: meta)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Events::TopicAddedToMap < Event
|
module Events
|
||||||
# after_create :notify_users!
|
class TopicAddedToMap < Event
|
||||||
|
after_create :notify_users!
|
||||||
|
|
||||||
def self.publish!(topic, map, user, meta)
|
def self.publish!(topic, map, user, meta)
|
||||||
create!(kind: 'topic_added_to_map',
|
create!(kind: 'topic_added_to_map',
|
||||||
eventable: topic,
|
eventable: topic,
|
||||||
map: map,
|
map: map,
|
||||||
user: user,
|
user: user,
|
||||||
meta: meta)
|
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)
|
||||||
|
# NotificationService.notify_followers(map, MAP_RECEIVED_TOPIC, self)
|
||||||
|
end
|
||||||
|
handle_asynchronously :notify_users!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Events::TopicMovedOnMap < Event
|
module Events
|
||||||
# after_create :notify_users!
|
class TopicMovedOnMap < Event
|
||||||
|
# after_create :notify_users!
|
||||||
|
|
||||||
def self.publish!(topic, map, user, meta)
|
def self.publish!(topic, map, user, meta)
|
||||||
create!(kind: 'topic_moved_on_map',
|
create!(kind: 'topic_moved_on_map',
|
||||||
eventable: topic,
|
eventable: topic,
|
||||||
map: map,
|
map: map,
|
||||||
user: user,
|
user: user,
|
||||||
meta: meta)
|
meta: meta)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Events::TopicRemovedFromMap < Event
|
module Events
|
||||||
# after_create :notify_users!
|
class TopicRemovedFromMap < Event
|
||||||
|
# after_create :notify_users!
|
||||||
|
|
||||||
def self.publish!(topic, map, user, meta)
|
def self.publish!(topic, map, user, meta)
|
||||||
create!(kind: 'topic_removed_from_map',
|
create!(kind: 'topic_removed_from_map',
|
||||||
eventable: topic,
|
eventable: topic,
|
||||||
map: map,
|
map: map,
|
||||||
user: user,
|
user: user,
|
||||||
meta: meta)
|
meta: meta)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Events::TopicUpdated < Event
|
module Events
|
||||||
# after_create :notify_users!
|
class TopicUpdated < Event
|
||||||
|
#after_create :notify_users!
|
||||||
|
|
||||||
def self.publish!(topic, user, meta)
|
def self.publish!(topic, user, meta)
|
||||||
create!(kind: 'topic_updated',
|
create!(kind: 'topic_updated',
|
||||||
eventable: topic,
|
eventable: topic,
|
||||||
user: user,
|
user: user,
|
||||||
meta: meta)
|
meta: meta)
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_users!
|
||||||
|
NotificationService.notify_followers(eventable, 'topic_updated', self)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
13
app/models/events/user_not_present_on_map.rb
Normal file
13
app/models/events/user_not_present_on_map.rb
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
module Events
|
||||||
|
class UserNotPresentOnMap < Event
|
||||||
|
# after_create :notify_users!
|
||||||
|
|
||||||
|
def self.publish!(map, user)
|
||||||
|
create!(kind: 'user_not_present_on_map',
|
||||||
|
eventable: map,
|
||||||
|
map: map,
|
||||||
|
user: user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,11 +1,13 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Events::UserPresentOnMap < Event
|
module Events
|
||||||
# after_create :notify_users!
|
class UserPresentOnMap < Event
|
||||||
|
# after_create :notify_users!
|
||||||
|
|
||||||
def self.publish!(map, user)
|
def self.publish!(map, user)
|
||||||
create!(kind: 'user_present_on_map',
|
create!(kind: 'user_present_on_map',
|
||||||
eventable: map,
|
eventable: map,
|
||||||
map: map,
|
map: map,
|
||||||
user: user)
|
user: user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
19
app/models/follow.rb
Normal file
19
app/models/follow.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# 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
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def add_subsetting
|
||||||
|
follow_reason = FollowReason.create!(follow: self)
|
||||||
|
end
|
||||||
|
end
|
12
app/models/follow_reason.rb
Normal file
12
app/models/follow_reason.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class FollowReason < ApplicationRecord
|
||||||
|
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
|
|
@ -1,14 +1,21 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Map < ApplicationRecord
|
class Map < ApplicationRecord
|
||||||
|
ATTRS_TO_WATCH = %w(name desc permission).freeze
|
||||||
|
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :source, class_name: :Map
|
belongs_to :source, class_name: :Map
|
||||||
|
belongs_to :updated_by, class_name: 'User'
|
||||||
|
|
||||||
has_many :topicmappings, -> { Mapping.topicmapping }, class_name: :Mapping, dependent: :destroy
|
has_many :topicmappings, -> { Mapping.topicmapping },
|
||||||
has_many :synapsemappings, -> { Mapping.synapsemapping }, class_name: :Mapping, dependent: :destroy
|
class_name: :Mapping, dependent: :destroy
|
||||||
|
has_many :synapsemappings, -> { Mapping.synapsemapping },
|
||||||
|
class_name: :Mapping, dependent: :destroy
|
||||||
has_many :topics, through: :topicmappings, source: :mappable, source_type: 'Topic'
|
has_many :topics, through: :topicmappings, source: :mappable, source_type: 'Topic'
|
||||||
has_many :synapses, through: :synapsemappings, source: :mappable, source_type: 'Synapse'
|
has_many :synapses, through: :synapsemappings, source: :mappable, source_type: 'Synapse'
|
||||||
has_many :messages, as: :resource, dependent: :destroy
|
has_many :messages, as: :resource, dependent: :destroy
|
||||||
has_many :stars
|
has_many :stars, dependent: :destroy
|
||||||
|
has_many :follows, as: :followed, dependent: :destroy
|
||||||
|
has_many :followers, :through => :follows, source: :user
|
||||||
|
|
||||||
has_many :access_requests, dependent: :destroy
|
has_many :access_requests, dependent: :destroy
|
||||||
has_many :user_maps, dependent: :destroy
|
has_many :user_maps, dependent: :destroy
|
||||||
|
@ -21,7 +28,6 @@ class Map < ApplicationRecord
|
||||||
has_attached_file :screenshot,
|
has_attached_file :screenshot,
|
||||||
styles: {
|
styles: {
|
||||||
thumb: ['220x220#', :png]
|
thumb: ['220x220#', :png]
|
||||||
#:full => ['940x630#', :png]
|
|
||||||
},
|
},
|
||||||
default_url: 'https://s3.amazonaws.com/metamaps-assets/site/missing-map-square.png'
|
default_url: 'https://s3.amazonaws.com/metamaps-assets/site/missing-map-square.png'
|
||||||
|
|
||||||
|
@ -31,8 +37,9 @@ class Map < ApplicationRecord
|
||||||
validates :permission, inclusion: { in: Perm::ISSIONS.map(&:to_s) }
|
validates :permission, inclusion: { in: Perm::ISSIONS.map(&:to_s) }
|
||||||
|
|
||||||
# Validate the attached image is image/jpg, image/png, etc
|
# Validate the attached image is image/jpg, image/png, etc
|
||||||
validates_attachment_content_type :screenshot, content_type: /\Aimage\/.*\Z/
|
validates_attachment_content_type :screenshot, content_type: %r{\Aimage/.*\Z}
|
||||||
|
|
||||||
|
after_create :after_created_async
|
||||||
after_update :after_updated
|
after_update :after_updated
|
||||||
after_save :update_deferring_topics_and_synapses, if: :permission_changed?
|
after_save :update_deferring_topics_and_synapses, if: :permission_changed?
|
||||||
|
|
||||||
|
@ -80,7 +87,12 @@ class Map < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def as_json(_options = {})
|
def as_json(_options = {})
|
||||||
json = super(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 = super(
|
||||||
|
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[:created_at_clean] = created_at_str
|
||||||
json[:updated_at_clean] = updated_at_str
|
json[:updated_at_clean] = updated_at_str
|
||||||
json
|
json
|
||||||
|
@ -121,19 +133,30 @@ class Map < ApplicationRecord
|
||||||
removed.compact
|
removed.compact
|
||||||
end
|
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_async
|
||||||
|
FollowService.follow(self, self.user, 'created')
|
||||||
|
# notify users following the map creator
|
||||||
|
end
|
||||||
|
handle_asynchronously :after_created_async
|
||||||
|
|
||||||
def after_updated
|
def after_updated
|
||||||
attrs = ['name', 'desc', 'permission']
|
return unless ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
|
||||||
if attrs.any? {|k| changed_attributes.key?(k)}
|
ActionCable.server.broadcast 'map_' + id.to_s, type: 'mapUpdated'
|
||||||
ActionCable.server.broadcast 'map_' + id.to_s, type: 'mapUpdated'
|
end
|
||||||
|
|
||||||
|
def after_updated_async
|
||||||
|
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
|
||||||
end
|
end
|
||||||
|
handle_asynchronously :after_updated_async
|
||||||
def update_deferring_topics_and_synapses
|
|
||||||
Topic.where(defer_to_map_id: id).update_all(permission: permission)
|
|
||||||
Synapse.where(defer_to_map_id: id).update_all(permission: permission)
|
|
||||||
end
|
|
||||||
|
|
||||||
def invited_text
|
|
||||||
name + ' - invited to edit'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,17 +8,15 @@ class Mapping < ApplicationRecord
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :updated_by, class_name: 'User'
|
belongs_to :updated_by, class_name: 'User'
|
||||||
|
|
||||||
validates :xloc, presence: true,
|
|
||||||
unless: proc { |m| m.mappable_type == 'Synapse' }
|
|
||||||
validates :yloc, presence: true,
|
|
||||||
unless: proc { |m| m.mappable_type == 'Synapse' }
|
|
||||||
validates :map, presence: true
|
validates :map, presence: true
|
||||||
validates :mappable, presence: true
|
validates :mappable, presence: true
|
||||||
|
|
||||||
delegate :name, to: :user, prefix: true
|
delegate :name, to: :user, prefix: true
|
||||||
|
|
||||||
after_create :after_created
|
after_create :after_created
|
||||||
|
after_create :after_created_async
|
||||||
after_update :after_updated
|
after_update :after_updated
|
||||||
|
after_update :after_updated_async
|
||||||
before_destroy :before_destroyed
|
before_destroy :before_destroyed
|
||||||
|
|
||||||
def user_image
|
def user_image
|
||||||
|
@ -31,28 +29,41 @@ class Mapping < ApplicationRecord
|
||||||
|
|
||||||
def after_created
|
def after_created
|
||||||
if mappable_type == 'Topic'
|
if mappable_type == 'Topic'
|
||||||
meta = {'x': xloc, 'y': yloc, 'mapping_id': id}
|
|
||||||
Events::TopicAddedToMap.publish!(mappable, map, user, meta)
|
|
||||||
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicAdded', topic: mappable.filtered, mapping_id: id
|
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicAdded', topic: mappable.filtered, mapping_id: id
|
||||||
|
meta = { 'x': xloc, 'y': yloc, 'mapping_id': id }
|
||||||
|
Events::TopicAddedToMap.publish!(mappable, map, user, meta)
|
||||||
elsif mappable_type == 'Synapse'
|
elsif mappable_type == 'Synapse'
|
||||||
Events::SynapseAddedToMap.publish!(mappable, map, user, meta)
|
|
||||||
ActionCable.server.broadcast(
|
ActionCable.server.broadcast(
|
||||||
'map_' + map.id.to_s,
|
'map_' + map.id.to_s,
|
||||||
type: 'synapseAdded',
|
type: 'synapseAdded',
|
||||||
synapse: mappable.filtered,
|
synapse: mappable.filtered,
|
||||||
topic1: mappable.topic1.filtered,
|
topic1: mappable.topic1.filtered,
|
||||||
topic2: mappable.topic2.filtered,
|
topic2: mappable.topic2.filtered,
|
||||||
mapping_id: id)
|
mapping_id: id
|
||||||
|
)
|
||||||
|
Events::SynapseAddedToMap.publish!(mappable, map, user, nil)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def after_created_async
|
||||||
|
FollowService.follow(map, user, 'contributed')
|
||||||
|
end
|
||||||
|
handle_asynchronously :after_created_async
|
||||||
|
|
||||||
def after_updated
|
def after_updated
|
||||||
if mappable_type == 'Topic' and (xloc_changed? or yloc_changed?)
|
if (mappable_type == 'Topic') && (xloc_changed? || yloc_changed?)
|
||||||
meta = {'x': xloc, 'y': yloc, 'mapping_id': id}
|
meta = { 'x': xloc, 'y': yloc, 'mapping_id': id }
|
||||||
Events::TopicMovedOnMap.publish!(mappable, map, updated_by, meta)
|
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
|
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicMoved', id: mappable.id, mapping_id: id, x: xloc, y: yloc
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def after_updated_async
|
||||||
|
if (mappable_type == 'Topic') && (xloc_changed? || yloc_changed?)
|
||||||
|
FollowService.follow(map, updated_by, 'contributed')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
handle_asynchronously :after_updated_async
|
||||||
|
|
||||||
def before_destroyed
|
def before_destroyed
|
||||||
if mappable.defer_to_map
|
if mappable.defer_to_map
|
||||||
|
@ -61,7 +72,7 @@ class Mapping < ApplicationRecord
|
||||||
mappable.save
|
mappable.save
|
||||||
end
|
end
|
||||||
|
|
||||||
meta = {'mapping_id': id}
|
meta = { 'mapping_id': id }
|
||||||
if mappable_type == 'Topic'
|
if mappable_type == 'Topic'
|
||||||
Events::TopicRemovedFromMap.publish!(mappable, map, updated_by, meta)
|
Events::TopicRemovedFromMap.publish!(mappable, map, updated_by, meta)
|
||||||
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicRemoved', id: mappable.id, mapping_id: id
|
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicRemoved', id: mappable.id, mapping_id: id
|
||||||
|
@ -69,5 +80,6 @@ class Mapping < ApplicationRecord
|
||||||
Events::SynapseRemovedFromMap.publish!(mappable, map, updated_by, meta)
|
Events::SynapseRemovedFromMap.publish!(mappable, map, updated_by, meta)
|
||||||
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'synapseRemoved', id: mappable.id, mapping_id: id
|
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'synapseRemoved', id: mappable.id, mapping_id: id
|
||||||
end
|
end
|
||||||
|
FollowService.follow(map, updated_by, 'contributed')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,8 +4,10 @@ class Message < ApplicationRecord
|
||||||
belongs_to :resource, polymorphic: true
|
belongs_to :resource, polymorphic: true
|
||||||
|
|
||||||
delegate :name, to: :user, prefix: true
|
delegate :name, to: :user, prefix: true
|
||||||
|
|
||||||
after_create :after_created
|
after_create :after_created
|
||||||
|
#after_create :after_created_async
|
||||||
|
|
||||||
|
|
||||||
def user_image
|
def user_image
|
||||||
user.image.url
|
user.image.url
|
||||||
|
@ -15,8 +17,14 @@ class Message < ApplicationRecord
|
||||||
json = super(methods: [:user_name, :user_image])
|
json = super(methods: [:user_name, :user_image])
|
||||||
json
|
json
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_created
|
def after_created
|
||||||
ActionCable.server.broadcast 'map_' + resource.id.to_s, type: 'messageCreated', message: self.as_json
|
ActionCable.server.broadcast 'map_' + resource.id.to_s, type: 'messageCreated', message: as_json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def after_created_async
|
||||||
|
FollowService.follow(resource, user, 'commented')
|
||||||
|
NotificationService.notify_followers(resource, 'map_message', self)
|
||||||
|
end
|
||||||
|
handle_asynchronously :after_created_async
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,4 +3,19 @@ class Star < ActiveRecord::Base
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :map
|
belongs_to :map
|
||||||
validates :map, uniqueness: { scope: :user, message: 'You have already starred this map' }
|
validates :map, uniqueness: { scope: :user, message: 'You have already starred this map' }
|
||||||
|
|
||||||
|
#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
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Synapse < ApplicationRecord
|
class Synapse < ApplicationRecord
|
||||||
|
ATTRS_TO_WATCH = %w(desc category permission defer_to_map_id).freeze
|
||||||
|
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :defer_to_map, class_name: 'Map', foreign_key: 'defer_to_map_id'
|
belongs_to :defer_to_map, class_name: 'Map', foreign_key: 'defer_to_map_id'
|
||||||
|
belongs_to :updated_by, class_name: 'User'
|
||||||
|
|
||||||
belongs_to :topic1, class_name: 'Topic', foreign_key: 'topic1_id'
|
belongs_to :topic1, class_name: 'Topic', foreign_key: 'topic1_id'
|
||||||
belongs_to :topic2, class_name: 'Topic', foreign_key: 'topic2_id'
|
belongs_to :topic2, class_name: 'Topic', foreign_key: 'topic2_id'
|
||||||
|
@ -22,7 +25,10 @@ class Synapse < ApplicationRecord
|
||||||
where(topic1_id: topic_id).or(where(topic2_id: topic_id))
|
where(topic1_id: topic_id).or(where(topic2_id: topic_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
before_create :set_perm_by_defer
|
||||||
|
after_create :after_created_async
|
||||||
after_update :after_updated
|
after_update :after_updated
|
||||||
|
before_destroy :before_destroyed
|
||||||
|
|
||||||
delegate :name, to: :user, prefix: true
|
delegate :name, to: :user, prefix: true
|
||||||
|
|
||||||
|
@ -51,17 +57,46 @@ class Synapse < ApplicationRecord
|
||||||
super(methods: [:user_name, :user_image, :collaborator_ids])
|
super(methods: [:user_name, :user_image, :collaborator_ids])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def as_rdf
|
||||||
|
output = ''
|
||||||
|
output += %(d:synapse_#{id} a mm:Synapse ;\n)
|
||||||
|
output += %( mm:topic1 d:topic_#{topic1_id} ;\n)
|
||||||
|
output += %( mm:topic2 d:topic_#{topic2_id} ;\n)
|
||||||
|
output += %( mm:direction "#{category}" ;\n)
|
||||||
|
output += %( rdfs:comment "#{desc}" ;\n) if desc.present?
|
||||||
|
output[-2] = '.'
|
||||||
|
output += %(\n)
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def set_perm_by_defer
|
||||||
|
permission = defer_to_map.permission if defer_to_map
|
||||||
|
end
|
||||||
|
|
||||||
|
def after_created_async
|
||||||
|
follow_ids = NotificationService.notify_followers(topic1, TOPIC_CONNECTED_1, self)
|
||||||
|
NotificationService.notify_followers(topic2, TOPIC_CONNECTED_2, self, nil, follow_ids)
|
||||||
|
end
|
||||||
|
handle_asynchronously :after_created_async
|
||||||
|
|
||||||
def after_updated
|
def after_updated
|
||||||
attrs = ['desc', 'category', 'permission', 'defer_to_map_id']
|
if ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
|
||||||
if attrs.any? {|k| changed_attributes.key?(k)}
|
new = attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
|
||||||
new = self.attributes.select {|k| attrs.include?(k) }
|
old = changed_attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
|
||||||
old = changed_attributes.select {|k| attrs.include?(k) }
|
meta = new.merge(old) # we are prioritizing the old values, keeping them
|
||||||
meta = new.merge(old) # we are prioritizing the old values, keeping them
|
meta['changed'] = changed_attributes.keys.select { |k| ATTRS_TO_WATCH.include?(k) }
|
||||||
meta['changed'] = changed_attributes.keys.select {|k| attrs.include?(k) }
|
Events::SynapseUpdated.publish!(self, updated_by, meta)
|
||||||
Events::SynapseUpdated.publish!(self, user, meta)
|
maps.each do |map|
|
||||||
maps.each {|map|
|
|
||||||
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'synapseUpdated', id: id
|
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'synapseUpdated', id: id
|
||||||
}
|
end
|
||||||
end
|
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)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class Topic < ApplicationRecord
|
class Topic < ApplicationRecord
|
||||||
|
ATTRS_TO_WATCH = %w(name desc link metacode_id permission defer_to_map_id).freeze
|
||||||
include TopicsHelper
|
include TopicsHelper
|
||||||
|
include Attachable
|
||||||
|
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
belongs_to :defer_to_map, class_name: 'Map', foreign_key: 'defer_to_map_id'
|
belongs_to :defer_to_map, class_name: 'Map', foreign_key: 'defer_to_map_id'
|
||||||
|
belongs_to :updated_by, class_name: 'User'
|
||||||
|
|
||||||
has_many :synapses1, class_name: 'Synapse', foreign_key: 'topic1_id', dependent: :destroy
|
has_many :synapses1, class_name: 'Synapse', foreign_key: 'topic1_id', dependent: :destroy
|
||||||
has_many :synapses2, class_name: 'Synapse', foreign_key: 'topic2_id', dependent: :destroy
|
has_many :synapses2, class_name: 'Synapse', foreign_key: 'topic2_id', dependent: :destroy
|
||||||
|
@ -12,32 +15,21 @@ class Topic < ApplicationRecord
|
||||||
|
|
||||||
has_many :mappings, as: :mappable, dependent: :destroy
|
has_many :mappings, as: :mappable, dependent: :destroy
|
||||||
has_many :maps, through: :mappings
|
has_many :maps, through: :mappings
|
||||||
|
has_many :follows, as: :followed, dependent: :destroy
|
||||||
|
has_many :followers, :through => :follows, source: :user
|
||||||
|
|
||||||
belongs_to :metacode
|
belongs_to :metacode
|
||||||
|
|
||||||
|
before_create :set_perm_by_defer
|
||||||
before_create :create_metamap?
|
before_create :create_metamap?
|
||||||
|
after_create :after_created_async
|
||||||
after_update :after_updated
|
after_update :after_updated
|
||||||
|
after_update :after_updated_async
|
||||||
|
#before_destroy :before_destroyed
|
||||||
|
|
||||||
validates :permission, presence: true
|
validates :permission, presence: true
|
||||||
validates :permission, inclusion: { in: Perm::ISSIONS.map(&:to_s) }
|
validates :permission, inclusion: { in: Perm::ISSIONS.map(&:to_s) }
|
||||||
|
|
||||||
# This method associates the attribute ":image" with a file attachment
|
|
||||||
has_attached_file :image
|
|
||||||
|
|
||||||
# , styles: {
|
|
||||||
# thumb: '100x100>',
|
|
||||||
# square: '200x200#',
|
|
||||||
# medium: '300x300>'
|
|
||||||
# }
|
|
||||||
|
|
||||||
# Validate the attached image is image/jpg, image/png, etc
|
|
||||||
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
|
|
||||||
|
|
||||||
# This method associates the attribute ":image" with a file attachment
|
|
||||||
has_attached_file :audio
|
|
||||||
# Validate the attached audio is audio/wav, audio/mp3, etc
|
|
||||||
validates_attachment_content_type :audio, content_type: /\Aaudio\/.*\Z/
|
|
||||||
|
|
||||||
def synapses
|
def synapses
|
||||||
synapses1.or(synapses2)
|
synapses1.or(synapses2)
|
||||||
end
|
end
|
||||||
|
@ -82,6 +74,19 @@ class Topic < ApplicationRecord
|
||||||
map_count: map_count(options[:user]), synapse_count: synapse_count(options[:user]))
|
map_count: map_count(options[:user]), synapse_count: synapse_count(options[:user]))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def as_rdf
|
||||||
|
output = ''
|
||||||
|
output += %(d:topic_#{id} a mm:Topic ;\n)
|
||||||
|
output += %( rdfs:label "#{name}" ;\n)
|
||||||
|
output += %( rdfs:comment "#{desc}" ;\n) if desc.present?
|
||||||
|
output += %( foaf:homepage <#{link}> ;\n) if link.present?
|
||||||
|
output += %( mm:mapper d:mapper_#{user_id} ;\n)
|
||||||
|
output += %( mm:metacode "#{metacode.name}" ;\n)
|
||||||
|
output[-2] = '.' # change last ; to a .
|
||||||
|
output += %(\n)
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
def collaborator_ids
|
def collaborator_ids
|
||||||
if defer_to_map
|
if defer_to_map
|
||||||
defer_to_map.editors.select { |mapper| mapper != user }.map(&:id)
|
defer_to_map.editors.select { |mapper| mapper != user }.map(&:id)
|
||||||
|
@ -137,6 +142,10 @@ class Topic < ApplicationRecord
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
def set_perm_by_defer
|
||||||
|
permission = defer_to_map.permission if defer_to_map
|
||||||
|
end
|
||||||
|
|
||||||
def create_metamap?
|
def create_metamap?
|
||||||
return unless (link == '') && (metacode.name == 'Metamap')
|
return unless (link == '') && (metacode.name == 'Metamap')
|
||||||
|
|
||||||
|
@ -145,18 +154,35 @@ class Topic < ApplicationRecord
|
||||||
self.link = Rails.application.routes.url_helpers
|
self.link = Rails.application.routes.url_helpers
|
||||||
.map_url(host: ENV['MAILER_DEFAULT_URL'], id: @map.id)
|
.map_url(host: ENV['MAILER_DEFAULT_URL'], id: @map.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def after_created_async
|
||||||
|
FollowService.follow(self, self.user, 'created')
|
||||||
|
# notify users following the topic creator
|
||||||
|
end
|
||||||
|
handle_asynchronously :after_created_async
|
||||||
|
|
||||||
def after_updated
|
def after_updated
|
||||||
attrs = ['name', 'desc', 'link', 'metacode_id', 'permission', 'defer_to_map_id']
|
if ATTRS_TO_WATCH.any? { |k| changed_attributes.key?(k) }
|
||||||
if attrs.any? {|k| changed_attributes.key?(k)}
|
new = attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
|
||||||
new = self.attributes.select {|k| attrs.include?(k) }
|
old = changed_attributes.select { |k| ATTRS_TO_WATCH.include?(k) }
|
||||||
old = changed_attributes.select {|k| attrs.include?(k) }
|
meta = new.merge(old) # we are prioritizing the old values, keeping them
|
||||||
meta = new.merge(old) # we are prioritizing the old values, keeping them
|
meta['changed'] = changed_attributes.keys.select { |k| ATTRS_TO_WATCH.include?(k) }
|
||||||
meta['changed'] = changed_attributes.keys.select {|k| attrs.include?(k) }
|
Events::TopicUpdated.publish!(self, updated_by, meta)
|
||||||
Events::TopicUpdated.publish!(self, user, meta)
|
maps.each do |map|
|
||||||
maps.each {|map|
|
|
||||||
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicUpdated', id: id
|
ActionCable.server.broadcast 'map_' + map.id.to_s, type: 'topicUpdated', id: id
|
||||||
}
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def after_updated_async
|
||||||
|
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', ?)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,6 +12,10 @@ class User < ApplicationRecord
|
||||||
has_many :stars
|
has_many :stars
|
||||||
has_many :user_maps, dependent: :destroy
|
has_many :user_maps, dependent: :destroy
|
||||||
has_many :shared_maps, through: :user_maps, source: :map
|
has_many :shared_maps, through: :user_maps, source: :map
|
||||||
|
has_many :follows, as: :followed
|
||||||
|
has_many :followers, through: :follows, source: :user
|
||||||
|
|
||||||
|
has_many :following, class_name: 'Follow'
|
||||||
|
|
||||||
after_create :generate_code
|
after_create :generate_code
|
||||||
|
|
||||||
|
@ -67,6 +71,17 @@ class User < ApplicationRecord
|
||||||
json
|
json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def as_rdf(opts = {})
|
||||||
|
base_url = opts[:base_url] || 'https://metamaps.cc'
|
||||||
|
output = ''
|
||||||
|
output += %(d:mapper_#{id} a foaf:OnlineAccount ;\n)
|
||||||
|
output += %( foaf:accountName "#{name}" ;\n)
|
||||||
|
output += %( foaf:accountServiceHomepage "#{base_url}/mapper/#{id}" ;\n)
|
||||||
|
output[-2] = '.' # change last ; to a .
|
||||||
|
output += %(\n)
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
def all_accessible_maps
|
def all_accessible_maps
|
||||||
maps + shared_maps
|
maps + shared_maps
|
||||||
end
|
end
|
||||||
|
@ -93,7 +108,7 @@ class User < ApplicationRecord
|
||||||
if code == joinedwithcode
|
if code == joinedwithcode
|
||||||
update(generation: 0)
|
update(generation: 0)
|
||||||
else
|
else
|
||||||
update(generation: User.find_by_code(joinedwithcode).generation + 1)
|
update(generation: User.find_by(code: joinedwithcode).generation + 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,31 @@
|
||||||
class UserMap < ApplicationRecord
|
class UserMap < ApplicationRecord
|
||||||
belongs_to :map
|
belongs_to :map
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
belongs_to :access_request
|
||||||
|
|
||||||
|
after_create :after_created_async
|
||||||
|
before_destroy :before_destroyed
|
||||||
|
|
||||||
def mark_invite_notifications_as_read
|
def mark_invite_notifications_as_read
|
||||||
Mailboxer::Notification.where(notified_object: self).find_each do |notification|
|
Mailboxer::Notification.where(notified_object: self).find_each do |notification|
|
||||||
Mailboxer::Receipt.where(notification: notification).update_all(is_read: true)
|
Mailboxer::Receipt.where(notification: notification).update_all(is_read: true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def after_created_async
|
||||||
|
FollowService.follow(map, user, 'shared_on')
|
||||||
|
if 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
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class UserPreference
|
class UserPreference
|
||||||
attr_accessor :metacodes
|
attr_accessor :metacodes, :metacode_focus
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
array = []
|
array = []
|
||||||
%w(Action Aim Idea Question Note Wildcard Subject).each do |m|
|
%w(Action Aim Idea Question Note Wildcard Subject).each do |m|
|
||||||
begin
|
begin
|
||||||
metacode = Metacode.find_by_name(m)
|
metacode = Metacode.find_by(name: m)
|
||||||
array.push(metacode.id.to_s) if metacode
|
array.push(metacode.id.to_s) if metacode
|
||||||
rescue ActiveRecord::StatementInvalid
|
rescue ActiveRecord::StatementInvalid
|
||||||
if m == 'Action'
|
if m == 'Action'
|
||||||
|
@ -15,5 +15,6 @@ class UserPreference
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@metacodes = array
|
@metacodes = array
|
||||||
|
@metacode_focus = array[0]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
class Webhooks::Slack::SynapseRemovedFromMap < Webhooks::Slack::Base
|
class Webhooks::Slack::SynapseRemovedFromMap < Webhooks::Slack::Base
|
||||||
def text
|
def text
|
||||||
connector = eventable.desc.empty? ? '->' : eventable.desc
|
connector = eventable.desc.empty? ? '->' : eventable.desc
|
||||||
# todo express correct directionality of arrows when desc is empty
|
# 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
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,6 +20,10 @@ class MapPolicy < ApplicationPolicy
|
||||||
record.collaborators.include?(user) ||
|
record.collaborators.include?(user) ||
|
||||||
record.user == user
|
record.user == user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def conversation?
|
||||||
|
show? && %w(connorturland@gmail.com devin@callysto.com chessscholar@gmail.com solaureum@gmail.com ishanshapiro@gmail.com).include?(user.email)
|
||||||
|
end
|
||||||
|
|
||||||
def create?
|
def create?
|
||||||
user.present?
|
user.present?
|
||||||
|
|
|
@ -28,6 +28,10 @@ class UserPolicy < ApplicationPolicy
|
||||||
update?
|
update?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update_metacode_focus?
|
||||||
|
update?
|
||||||
|
end
|
||||||
|
|
||||||
# API action
|
# API action
|
||||||
def current?
|
def current?
|
||||||
user == record
|
user == record
|
||||||
|
|
|
@ -35,13 +35,29 @@ module Api
|
||||||
Pundit.policy_scope(scope[:current_user], object.send(attr))&.map(&:id) || []
|
Pundit.policy_scope(scope[:current_user], object.send(attr))&.map(&:id) || []
|
||||||
end
|
end
|
||||||
has_many(attr, opts.merge(if: -> { embeds.include?(key) })) do
|
has_many(attr, opts.merge(if: -> { embeds.include?(key) })) do
|
||||||
Pundit.policy_scope(scope[:current_user], object.send(attr)) || []
|
list = Pundit.policy_scope(scope[:current_user], object.send(attr)) || []
|
||||||
|
child_serializer = "Api::V2::#{attr.to_s.singularize.camelize}Serializer".constantize
|
||||||
|
resource = ActiveModelSerializers::SerializableResource.new(
|
||||||
|
list,
|
||||||
|
each_serializer: child_serializer,
|
||||||
|
scope: scope.merge(embeds: [])
|
||||||
|
)
|
||||||
|
resource.as_json
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
id_opts = opts.merge(key: "#{key}_id")
|
id_opts = opts.merge(key: "#{key}_id")
|
||||||
attribute("#{attr}_id".to_sym,
|
attribute("#{attr}_id".to_sym,
|
||||||
id_opts.merge(unless: -> { embeds.include?(key) }))
|
id_opts.merge(unless: -> { embeds.include?(key) }))
|
||||||
attribute(key, opts.merge(if: -> { embeds.include?(key) }))
|
attribute(key, opts.merge(if: -> { embeds.include?(key) })) do |serializer|
|
||||||
|
object = serializer.object.send(key)
|
||||||
|
child_serializer = "Api::V2::#{object.class.name}Serializer".constantize
|
||||||
|
resource = ActiveModelSerializers::SerializableResource.new(
|
||||||
|
object,
|
||||||
|
serializer: child_serializer,
|
||||||
|
scope: scope.merge(embeds: [])
|
||||||
|
)
|
||||||
|
resource.as_json
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
35
app/services/follow_service.rb
Normal file
35
app/services/follow_service.rb
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
class FollowService
|
||||||
|
class << self
|
||||||
|
def follow(entity, user, reason)
|
||||||
|
|
||||||
|
return unless is_tester(user)
|
||||||
|
|
||||||
|
follow = Follow.where(followed: entity, user: user).first_or_create
|
||||||
|
if FollowReason::REASONS.include?(reason) && !follow.follow_reason.read_attribute(reason)
|
||||||
|
follow.follow_reason.update_attribute(reason, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unfollow(entity, user)
|
||||||
|
Follow.where(followed: entity, user: user).destroy_all
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_reason(entity, user, reason)
|
||||||
|
return unless FollowReason::REASONS.include?(reason)
|
||||||
|
follow = Follow.where(followed: entity, user: user).first
|
||||||
|
if follow
|
||||||
|
follow.follow_reason.update_attribute(reason, false)
|
||||||
|
if !follow.follow_reason.has_reason
|
||||||
|
follow.destroy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def is_tester(user)
|
||||||
|
%w(connorturland@gmail.com devin@callysto.com chessscholar@gmail.com solaureum@gmail.com ishanshapiro@gmail.com).include?(user.email)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,9 +1,11 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class MapExportService
|
class MapExportService
|
||||||
attr_reader :user, :map
|
attr_reader :user, :map, :base_url
|
||||||
def initialize(user, map)
|
|
||||||
|
def initialize(user, map, opts = {})
|
||||||
@user = user
|
@user = user
|
||||||
@map = map
|
@map = map
|
||||||
|
@base_url = opts[:base_url] || 'https://metamaps.cc'
|
||||||
end
|
end
|
||||||
|
|
||||||
def json
|
def json
|
||||||
|
@ -22,6 +24,25 @@ class MapExportService
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rdf
|
||||||
|
output = ''
|
||||||
|
output += "PREFIX d: <#{base_url}/maps/#{map.id}>\n"
|
||||||
|
output += "PREFIX mm: <#{base_url}/owl/map.owl.ttl>\n"
|
||||||
|
output += "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n"
|
||||||
|
output += "PREFIX foaf: <http://xmlns.com/foaf/0.1/>\n"
|
||||||
|
output += "\n"
|
||||||
|
map.contributors.each do |mapper|
|
||||||
|
output += mapper.as_rdf(base_url: base_url)
|
||||||
|
end
|
||||||
|
map.topics.each do |topic|
|
||||||
|
output += topic.as_rdf
|
||||||
|
end
|
||||||
|
map.synapses.each do |synapse|
|
||||||
|
output += synapse.as_rdf
|
||||||
|
end
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def topic_headings
|
def topic_headings
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
class NotificationService
|
class NotificationService
|
||||||
|
extend TopicMailerHelper
|
||||||
|
extend MapMailerHelper
|
||||||
|
# for strip_tags
|
||||||
|
include ActionView::Helpers::SanitizeHelper
|
||||||
|
|
||||||
def self.renderer
|
def self.renderer
|
||||||
renderer ||= ApplicationController.renderer.new(
|
renderer ||= ApplicationController.renderer.new(
|
||||||
http_host: ENV['MAILER_DEFAULT_URL'],
|
http_host: ENV['MAILER_DEFAULT_URL'],
|
||||||
|
@ -7,32 +12,78 @@ class NotificationService
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.get_settings_for_event(entity, event_type, event)
|
||||||
|
case event_type
|
||||||
|
when TOPIC_ADDED_TO_MAP
|
||||||
|
subject = added_to_map_subject(entity, event.map)
|
||||||
|
template = 'topic_mailer/added_to_map'
|
||||||
|
when TOPIC_CONNECTED_1
|
||||||
|
subject = connected_subject(event.topic1)
|
||||||
|
template = 'topic_mailer/connected'
|
||||||
|
when TOPIC_CONNECTED_2
|
||||||
|
subject = connected_subject(event.topic2)
|
||||||
|
template = 'topic_mailer/connected'
|
||||||
|
end
|
||||||
|
|
||||||
|
{template: template, subject: subject}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.send_for_follows(follows, entity, event_type, event)
|
||||||
|
return if follows.length == 0
|
||||||
|
settings = get_settings_for_event(entity, event_type, event)
|
||||||
|
# we'll prbly want to put the body into the actual loop so we can pass the current user in as a local
|
||||||
|
body = renderer.render(template: settings[:template], locals: { entity: entity, event: event }, layout: false)
|
||||||
|
follows.each{|follow|
|
||||||
|
# this handles email and in-app notifications, in the future, include push
|
||||||
|
follow.user.notify(settings[:subject], body, event, false, event_type, follow.user.emails_allowed, event.user)
|
||||||
|
# push could be handled with Actioncable to send transient notifications to the UI
|
||||||
|
# the receipt from the notify call could be used to link to the full notification
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.notify_followers(entity, event_type, event, reason_filter = nil, exclude_follows = nil)
|
||||||
|
follows = entity.follows.where.not(user_id: event.user.id)
|
||||||
|
|
||||||
|
if !exclude_follows.nil?
|
||||||
|
follows = follows.where.not(id: exclude_follows)
|
||||||
|
end
|
||||||
|
|
||||||
|
if reason_filter.class == String && FollowReason::REASONS.include?(reason_filter)
|
||||||
|
follows = follows.joins(:follow_reason).where('follow_reasons.' + reason_filter => true)
|
||||||
|
elsif reason_filter.class == Array
|
||||||
|
# TODO: throw an error here if all the reasons aren't valid
|
||||||
|
follows = follows.joins(:follow_reason).where(reason_filter.map{|r| "follow_reasons.#{r} = 't'"}.join(' OR '))
|
||||||
|
end
|
||||||
|
send_for_follows(follows, entity, event_type, event)
|
||||||
|
return follows.map(&:id)
|
||||||
|
end
|
||||||
|
|
||||||
def self.access_request(request)
|
def self.access_request(request)
|
||||||
body = renderer.render(template: 'map_mailer/access_request_email', locals: { map: request.map, request: request }, layout: false)
|
subject = access_request_subject(request.map)
|
||||||
request.map.user.notify(request.requested_text, body, request, false, MAILBOXER_CODE_ACCESS_REQUEST, true, request.user)
|
body = renderer.render(template: 'map_mailer/access_request', locals: { map: request.map, request: request }, layout: false)
|
||||||
|
request.map.user.notify(subject, body, request, false, MAP_ACCESS_REQUEST, true, request.user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.access_approved(request)
|
def self.access_approved(request)
|
||||||
body = renderer.render(template: 'map_mailer/access_approved_email', locals: { map: request.map }, layout: false)
|
subject = access_approved_subject(request.map)
|
||||||
receipt = request.user.notify(request.approved_text, body, request, false, MAILBOXER_CODE_ACCESS_APPROVED, true, request.map.user)
|
body = renderer.render(template: 'map_mailer/access_approved', locals: { map: request.map }, layout: false)
|
||||||
|
request.user.notify(subject, body, request, false, MAP_ACCESS_APPROVED, true, request.map.user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.invite_to_edit(map, inviter, invited)
|
def self.invite_to_edit(user_map)
|
||||||
user_map = UserMap.find_by(user: invited, map: map)
|
subject = invite_to_edit_subject(user_map.map)
|
||||||
body = renderer.render(template: 'map_mailer/invite_to_edit_email', locals: { map: map, inviter: inviter }, layout: false)
|
body = renderer.render(template: 'map_mailer/invite_to_edit', locals: { map: user_map.map, inviter: user_map.map.user }, layout: false)
|
||||||
invited.notify(map.invited_text, body, user_map, false, MAILBOXER_CODE_INVITE_TO_EDIT, true, inviter)
|
user_map.user.notify(subject, body, user_map, false, MAP_INVITE_TO_EDIT, true, user_map.map.user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.text_for_notification(notification)
|
# note: this is a global function, probably called from the rails console with some html body
|
||||||
if notification.notification_code == MAILBOXER_CODE_ACCESS_REQUEST
|
def self.message_from_devs(subject, body, opts = {})
|
||||||
map = notification.notified_object.map
|
users = opts[:users] || User.all
|
||||||
'wants permission to map with you on <span class="in-bold">' + map.name + '</span> <div class="action">Offer a response</div>'
|
obj = opts[:obj] || nil
|
||||||
elsif notification.notification_code == MAILBOXER_CODE_ACCESS_APPROVED
|
sanitize_text = opts[:sanitize_text] || false
|
||||||
map = notification.notified_object.map
|
notification_code = opts[:notification_code] || MESSAGE_FROM_DEVS
|
||||||
'granted your request to edit map <span class="in-bold">' + map.name + '</span>'
|
send_mail = opts[:send_mail] ? true : false
|
||||||
elsif notification.notification_code == MAILBOXER_CODE_INVITE_TO_EDIT
|
sender = opts[:sender] || User.find_by(email: 'ishanshapiro@gmail.com')
|
||||||
map = notification.notified_object.map
|
Mailboxer::Notification.notify_all(users, subject, body, obj, sanitize_text, notification_code, send_mail, sender)
|
||||||
'gave you edit access to map <span class="in-bold">' + map.name + '</span>'
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
1
app/views/access/approve_access_post.js.erb
Normal file
1
app/views/access/approve_access_post.js.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
$('.main-text').text($('.requesterName').text() + ' has been shared on the map and notified.')
|
1
app/views/access/deny_access_post.js.erb
Normal file
1
app/views/access/deny_access_post.js.erb
Normal file
|
@ -0,0 +1 @@
|
||||||
|
$('.main-text').text('Fair enough.')
|
|
@ -12,7 +12,7 @@
|
||||||
Metamaps.currentPage = "mapper";
|
Metamaps.currentPage = "mapper";
|
||||||
Metamaps.ServerData.Mapper = {
|
Metamaps.ServerData.Mapper = {
|
||||||
models: <%= @maps.to_json.html_safe %>,
|
models: <%= @maps.to_json.html_safe %>,
|
||||||
id: <%= params[:id] %>
|
mapperId: <%= params[:id] %>
|
||||||
};
|
};
|
||||||
Metamaps.GlobalUI.Search.focus();
|
Metamaps.GlobalUI.Search.focus();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -29,8 +29,14 @@
|
||||||
<meta name="twitter:url" content="<%= request.original_url %>" />
|
<meta name="twitter:url" content="<%= request.original_url %>" />
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<%= stylesheet_link_tag "application", :media => "all" %>
|
<% if controller_name == 'maps' && action_name == "conversation" %>
|
||||||
<%= javascript_include_tag "application" %>
|
<%= stylesheet_link_tag "application", :media => "all" %>
|
||||||
|
<%= stylesheet_link_tag "application-secret", :media => "all" %>
|
||||||
|
<%= javascript_include_tag "application-secret" %>
|
||||||
|
<% else %>
|
||||||
|
<%= stylesheet_link_tag "application", :media => "all" %>
|
||||||
|
<%= javascript_include_tag "application" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<!-- typekit for vinyl font -->
|
<!-- typekit for vinyl font -->
|
||||||
<script type="text/javascript" src="https://use.typekit.net/tki2nyo.js"></script>
|
<script type="text/javascript" src="https://use.typekit.net/tki2nyo.js"></script>
|
||||||
|
|
|
@ -34,6 +34,11 @@
|
||||||
<p class="mapCreatedAt"><span>Created by:</span> {{user_name}} on {{created_at}}</p>
|
<p class="mapCreatedAt"><span>Created by:</span> {{user_name}} on {{created_at}}</p>
|
||||||
<p class="mapEditedAt"><span>Last edited:</span> {{updated_at}}</p>
|
<p class="mapEditedAt"><span>Last edited:</span> {{updated_at}}</p>
|
||||||
<div class="mapInfoButtonsWrapper">
|
<div class="mapInfoButtonsWrapper">
|
||||||
|
<div class="mapInfoThumbnail">
|
||||||
|
<div class="thumbnail"></div>
|
||||||
|
<div class="tooltip">Update Thumbnail</div>
|
||||||
|
<span>Thumb</span>
|
||||||
|
</div>
|
||||||
<div class="mapInfoDelete">
|
<div class="mapInfoDelete">
|
||||||
<div class="deleteMap"></div>
|
<div class="deleteMap"></div>
|
||||||
<span>Delete</span>
|
<span>Delete</span>
|
||||||
|
|
|
@ -39,7 +39,11 @@
|
||||||
<div class="showcard mapElement mapElementHidden" id="showcard"></div> <!-- the topic card -->
|
<div class="showcard mapElement mapElementHidden" id="showcard"></div> <!-- the topic card -->
|
||||||
<% if authenticated? %>
|
<% if authenticated? %>
|
||||||
<% # for creating and pulling in topics and synapses %>
|
<% # for creating and pulling in topics and synapses %>
|
||||||
<%= render :partial => 'maps/newtopic' %>
|
<% if controller_name == 'maps' && action_name == "conversation" %>
|
||||||
|
<%= render :partial => 'maps/newtopicsecret' %>
|
||||||
|
<% else %>
|
||||||
|
<%= render :partial => 'maps/newtopic' %>
|
||||||
|
<% end %>
|
||||||
<%= render :partial => 'maps/newsynapse' %>
|
<%= render :partial => 'maps/newsynapse' %>
|
||||||
<% # for populating the change metacode list on the topic card %>
|
<% # for populating the change metacode list on the topic card %>
|
||||||
<%= render :partial => 'shared/metacodeoptions' %>
|
<%= render :partial => 'shared/metacodeoptions' %>
|
||||||
|
@ -47,18 +51,20 @@
|
||||||
<%= render :partial => 'layouts/lowermapelements' %>
|
<%= render :partial => 'layouts/lowermapelements' %>
|
||||||
|
|
||||||
<div id="explore"></div>
|
<div id="explore"></div>
|
||||||
|
|
||||||
<div id="instructions">
|
<% if !(controller_name == 'maps' && action_name == "conversation") %>
|
||||||
<div class="addTopic">
|
<div id="instructions">
|
||||||
Double-click to<br>add a topic
|
<div class="addTopic">
|
||||||
|
Double-click to<br>add a topic
|
||||||
|
</div>
|
||||||
|
<div class="tabKey">
|
||||||
|
Use Tab & Shift+Tab to select a metacode
|
||||||
|
</div>
|
||||||
|
<div class="enterKey">
|
||||||
|
Press Enter to add the topic
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tabKey">
|
<% end %>
|
||||||
Use Tab & Shift+Tab to select a metacode
|
|
||||||
</div>
|
|
||||||
<div class="enterKey">
|
|
||||||
Press Enter to add the topic
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="infovis"></div>
|
<div id="infovis"></div>
|
||||||
<%= render :partial => 'layouts/mobilemenu' %>
|
<%= render :partial => 'layouts/mobilemenu' %>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<div style="padding: 16px; background: white; text-align: left;">
|
<div style="padding: 16px; background: white; text-align: left;">
|
||||||
<%= raw @notification.body %>
|
<%= raw @notification.body %>
|
||||||
|
<hr>
|
||||||
<p style="font-size: 12px;">Make sense with Metamaps</p>
|
<p style="font-size: 12px;">Make sense with Metamaps</p>
|
||||||
<%= render partial: 'shared/mailer_unsubscribe_link' %>
|
<%= render partial: 'shared/mailer_unsubscribe_link' %>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -77,6 +77,11 @@
|
||||||
<p class="mapCreatedAt"><span>Created by:</span> <%= @map.user == user ? "You" : @map.user.name %> on <%= @map.created_at.strftime("%m/%d/%Y") %></p>
|
<p class="mapCreatedAt"><span>Created by:</span> <%= @map.user == user ? "You" : @map.user.name %> on <%= @map.created_at.strftime("%m/%d/%Y") %></p>
|
||||||
<p class="mapEditedAt"><span>Last edited:</span> <%= @map.updated_at.strftime("%m/%d/%Y") %></p>
|
<p class="mapEditedAt"><span>Last edited:</span> <%= @map.updated_at.strftime("%m/%d/%Y") %></p>
|
||||||
<div class="mapInfoButtonsWrapper">
|
<div class="mapInfoButtonsWrapper">
|
||||||
|
<div class="mapInfoThumbnail">
|
||||||
|
<div class="thumbnail"></div>
|
||||||
|
<div class="tooltip">Update Thumbnail</div>
|
||||||
|
<span>Thumb</span>
|
||||||
|
</div>
|
||||||
<div class="mapInfoDelete">
|
<div class="mapInfoDelete">
|
||||||
<div class="deleteMap"></div>
|
<div class="deleteMap"></div>
|
||||||
<span>Delete</span>
|
<span>Delete</span>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<% @metacodes.each do |metacode| %>
|
<% @metacodes.each do |metacode| %>
|
||||||
<img class="cloudcarousel" width="40" height="40" src="<%= asset_path metacode.icon %>" alt="<%= metacode.name %>" title="<%= metacode.name %>" data-id="<%= metacode.id %>" />
|
<img class="cloudcarousel" width="40" height="40" src="<%= asset_path metacode.icon %>" alt="<%= metacode.name %>" title="<%= metacode.name %>" data-id="<%= metacode.id %>" />
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= form.text_field :name, :maxlength => 140, :placeholder => "title..." %>
|
<%= form.text_field :name, :maxlength => 140, :placeholder => "title..." %>
|
||||||
|
|
||||||
|
@ -23,12 +23,10 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
<% @metacodes.each do |metacode| %>
|
<% @metacodes.each do |metacode| %>
|
||||||
<% if !metacodeset() %>
|
Metamaps.Create.selectedMetacodes.push("<%= metacode.id %>");
|
||||||
Metamaps.Create.selectedMetacodes.push("<%= metacode.id %>");
|
Metamaps.Create.newSelectedMetacodes.push("<%= metacode.id %>");
|
||||||
Metamaps.Create.newSelectedMetacodes.push("<%= metacode.id %>");
|
Metamaps.Create.selectedMetacodeNames.push("<%= metacode.name %>");
|
||||||
Metamaps.Create.selectedMetacodeNames.push("<%= metacode.name %>");
|
Metamaps.Create.newSelectedMetacodeNames.push("<%= metacode.name %>");
|
||||||
Metamaps.Create.newSelectedMetacodeNames.push("<%= metacode.name %>");
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</script>
|
</script>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
36
app/views/maps/_newtopicsecret.html.erb
Normal file
36
app/views/maps/_newtopicsecret.html.erb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<%= form_for Topic.new, url: topics_url, remote: true do |form| %>
|
||||||
|
<div class="openMetacodeSwitcher openLightbox" data-open="switchMetacodes">
|
||||||
|
<div class="tooltipsAbove">Switch Metacodes</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="metacodeImg">
|
||||||
|
<% @metacodes = user_metacodes()
|
||||||
|
@metacodes.each do |m| %>
|
||||||
|
<img class="cloudcarousel" width="40" height="40" src="<%= asset_path m.icon %>" alt="<%= m.name %>" title="<%= m.name %>" data-id="<%= m.id %>" />
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="selectedMetacode">
|
||||||
|
<img src="<%= asset_path @metacodes[0].icon %>" />
|
||||||
|
<span><%= @metacodes[0].name %></span>
|
||||||
|
<div class="downArrow"></div>
|
||||||
|
</div>
|
||||||
|
<%= form.text_field :name, :maxlength => 140, :placeholder => "what are you thinking..." %>
|
||||||
|
<div class="clearfloat"></div>
|
||||||
|
<div id="metacodeSelector"></div>
|
||||||
|
<div class="clearfloat"></div>
|
||||||
|
<script>
|
||||||
|
<% @metacodes.each do |m| %>
|
||||||
|
Metamaps.Create.selectedMetacodes.push("<%= m.id %>");
|
||||||
|
Metamaps.Create.newSelectedMetacodes.push("<%= m.id %>");
|
||||||
|
Metamaps.Create.selectedMetacodeNames.push("<%= m.name %>");
|
||||||
|
Metamaps.Create.newSelectedMetacodeNames.push("<%= m.name %>");
|
||||||
|
<% end %>
|
||||||
|
Metamaps.Create.newTopic.metacode = <%= @metacodes[0].id %>
|
||||||
|
<% current_user.recent_metacodes.each do |id| %>
|
||||||
|
Metamaps.Create.recentMetacodes.push(<%= id %>);
|
||||||
|
<% end %>
|
||||||
|
<% current_user.most_used_metacodes.each do |id| %>
|
||||||
|
Metamaps.Create.mostUsedMetacodes.push(<%= id %>);
|
||||||
|
<% end %>
|
||||||
|
</script>
|
||||||
|
<% end %>
|
22
app/views/maps/conversation.html.erb
Normal file
22
app/views/maps/conversation.html.erb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<%#
|
||||||
|
# @file
|
||||||
|
# Code to display a map
|
||||||
|
# /maps/:id
|
||||||
|
#%>
|
||||||
|
|
||||||
|
<% content_for :title, @map.name + " | Metamaps" %>
|
||||||
|
<% content_for :mobile_title, @map.name %>
|
||||||
|
<script>
|
||||||
|
Metamaps.currentSection = "map";
|
||||||
|
Metamaps.currentPage = <%= @map.id.to_s %>;
|
||||||
|
Metamaps.ServerData = Metamaps.ServerData || {}
|
||||||
|
Metamaps.ServerData.ActiveMap = <%= @map.to_json.html_safe %>;
|
||||||
|
Metamaps.ServerData.Mappers = <%= @allmappers.to_json.html_safe %>;
|
||||||
|
Metamaps.ServerData.Collaborators = <%= @allcollaborators.to_json.html_safe %>;
|
||||||
|
Metamaps.ServerData.Topics = <%= @alltopics.to_json(user: current_user).html_safe %>;
|
||||||
|
Metamaps.ServerData.Synapses = <%= @allsynapses.to_json.html_safe %>;
|
||||||
|
Metamaps.ServerData.Mappings = <%= @allmappings.to_json.html_safe %>;
|
||||||
|
Metamaps.ServerData.Messages = <%= @allmessages.to_json.html_safe %>;
|
||||||
|
Metamaps.ServerData.Stars = <%= @allstars.to_json.html_safe %>;
|
||||||
|
Metamaps.ServerData.VisualizeType = "ForceDirected";
|
||||||
|
</script>
|
|
@ -7,7 +7,9 @@
|
||||||
<h2 class="title">Notifications</h4>
|
<h2 class="title">Notifications</h4>
|
||||||
</header>
|
</header>
|
||||||
<ul class="notifications">
|
<ul class="notifications">
|
||||||
<% @notifications.each do |notification| %>
|
<% blacklist = [MAP_ACCESS_REQUEST, MAP_ACCESS_APPROVED, MAP_INVITE_TO_EDIT] %>
|
||||||
|
<% notifications = @notifications.to_a.delete_if{|n| blacklist.include?(n.notification_code) && (n.notified_object.nil? || n.notified_object.map.nil?) }%>
|
||||||
|
<% notifications.each do |notification| %>
|
||||||
<% receipt = @receipts.find_by(notification_id: notification.id) %>
|
<% receipt = @receipts.find_by(notification_id: notification.id) %>
|
||||||
<li class="notification <%= receipt.is_read? ? 'read' : 'unread' %>" id="notification-<%= notification.id %>">
|
<li class="notification <%= receipt.is_read? ? 'read' : 'unread' %>" id="notification-<%= notification.id %>">
|
||||||
<%= link_to notification_path(notification.id) do %>
|
<%= link_to notification_path(notification.id) do %>
|
||||||
|
@ -16,7 +18,35 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="notification-body">
|
<div class="notification-body">
|
||||||
<div class="in-bold"><%= notification.sender.name %></div>
|
<div class="in-bold"><%= notification.sender.name %></div>
|
||||||
<%= raw NotificationService.text_for_notification(notification) %>
|
<%
|
||||||
|
case notification.notification_code
|
||||||
|
when MAP_ACCESS_APPROVED %>
|
||||||
|
<% map = notification.notified_object.map %>
|
||||||
|
granted your request to edit map <span class="in-bold"><%= map.name %></span>
|
||||||
|
<% when MAP_ACCESS_REQUEST %>
|
||||||
|
<% map = notification.notified_object.map %>
|
||||||
|
wants permission to map with you on <span class="in-bold"><%= map.name %></span>
|
||||||
|
<% if !notification.notified_object.answered %>
|
||||||
|
<div class="action">Offer a response</div>
|
||||||
|
<% end %>
|
||||||
|
<% when MAP_INVITE_TO_EDIT %>
|
||||||
|
<% map = notification.notified_object.map %>
|
||||||
|
gave you edit access to map <span class="in-bold"><%= map.name %></span>
|
||||||
|
<% when TOPIC_ADDED_TO_MAP %>
|
||||||
|
<% topic = notification.notified_object.eventable
|
||||||
|
map = notification.notified_object.map %>
|
||||||
|
added topic <span class="in-bold"><%= topic.name %></span> to map <span class="in-bold"><%= map.name %></span>
|
||||||
|
<% when TOPIC_CONNECTED_1 %>
|
||||||
|
<% topic1 = notification.notified_object.topic1
|
||||||
|
topic2 = notification.notified_object.topic2 %>
|
||||||
|
connected <span class="in-bold"><%= topic1.name %></span> to <span class="in-bold"><%= topic2.name %></span>
|
||||||
|
<% when TOPIC_CONNECTED_2 %>
|
||||||
|
<% topic1 = notification.notified_object.topic1
|
||||||
|
topic2 = notification.notified_object.topic2 %>
|
||||||
|
connected <span class="in-bold"><%= topic2.name %></span> to <span class="in-bold"><%= topic1.name %></span>
|
||||||
|
<% when MESSAGE_FROM_DEVS %>
|
||||||
|
<%= notification.subject %>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
<div class="notification-read-unread">
|
<div class="notification-read-unread">
|
||||||
|
@ -32,7 +62,7 @@
|
||||||
<div class="clearfloat"></div>
|
<div class="clearfloat"></div>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if @notifications.count == 0 %>
|
<% if notifications.count == 0 %>
|
||||||
<div class="emptyInbox">
|
<div class="emptyInbox">
|
||||||
You have no notifications. More time for dancing.
|
You have no notifications. More time for dancing.
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,10 +6,48 @@
|
||||||
<%= link_to 'Back to notifications', notifications_path %>
|
<%= link_to 'Back to notifications', notifications_path %>
|
||||||
</div>
|
</div>
|
||||||
<div class="centerContent notificationPage">
|
<div class="centerContent notificationPage">
|
||||||
<h2 class="notification-title"><%= @notification.subject %></h4>
|
<h2 class="notification-title">
|
||||||
<div class="notification-body">
|
<% case @notification.notification_code
|
||||||
<%= raw @notification.body %>
|
when MAP_ACCESS_REQUEST
|
||||||
</div>
|
request = @notification.notified_object
|
||||||
|
map = request.map %>
|
||||||
|
<%= image_tag @notification.sender.image(:thirtytwo), class: 'thirty-two-avatar' %> <span style='font-weight:bold;' class='requesterName'><%= request.user.name %></span> wants to collaborate on map <span style='font-weight:bold;'><%= map.name %></span>
|
||||||
|
<% else %>
|
||||||
|
<%= @notification.subject %>
|
||||||
|
<% end %>
|
||||||
|
</h2>
|
||||||
|
<% case @notification.notification_code
|
||||||
|
when MAP_ACCESS_REQUEST %>
|
||||||
|
<div class="notification-body">
|
||||||
|
<p class="main-text">
|
||||||
|
<% if false && request.answered %>
|
||||||
|
<% if request.approved %>
|
||||||
|
You already responded to this access request, and allowed access.
|
||||||
|
<% elsif !request.approved %>
|
||||||
|
You already responded to this access request, and declined access. If you changed your mind, you can still grant
|
||||||
|
them access by going to the map and adding them as a collaborator.
|
||||||
|
<% end %>
|
||||||
|
<% else %>
|
||||||
|
<%= image_tag asset_path('ellipsis.gif'), class: 'hidden' %>
|
||||||
|
<%= link_to 'Allow', approve_access_post_map_path(id: map.id, request_id: request.id), remote: true, method: :post, class: 'button allow' %>
|
||||||
|
<%= link_to 'Decline', deny_access_post_map_path(id: map.id, request_id: request.id), remote: true, method: :post, class: 'button decline' %>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('.notification-body .button').click(function() {
|
||||||
|
$(this).html('<img src="<%= asset_path('ellipsis.gif') %>" />')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<% end %>
|
||||||
|
</p>
|
||||||
|
<%= link_to 'Go to map', map_url(map) %>
|
||||||
|
<%= link_to 'View mapper profile', explore_path(id: request.user.id) %>
|
||||||
|
</div>
|
||||||
|
<% else %>
|
||||||
|
<div class="notification-body">
|
||||||
|
<%= raw @notification.body %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -93,12 +93,9 @@
|
||||||
<div id="metacodeSwitchTabsCustom">
|
<div id="metacodeSwitchTabsCustom">
|
||||||
<p class="setDesc">Choose Your Metacodes</p>
|
<p class="setDesc">Choose Your Metacodes</p>
|
||||||
<% @list = '' %>
|
<% @list = '' %>
|
||||||
|
<% metacodesInUse = user_metacodes() %>
|
||||||
<% Metacode.order("name").all.each_with_index do |m, index| %>
|
<% Metacode.order("name").all.each_with_index do |m, index| %>
|
||||||
<% if selectedSet == "custom" %>
|
<% mClass = metacodesInUse.index(m) == nil ? "toggledOff" : "" %>
|
||||||
<% mClass = metacodes.index(m.id.to_s) == nil ? "toggledOff" : "" %>
|
|
||||||
<% else %>
|
|
||||||
<% mClass = "toggledOff" %>
|
|
||||||
<% end %>
|
|
||||||
<% @list += '<li id="' + m.id.to_s + '" data-name="' + m.name + '" class="' + mClass + '"><img src="' + asset_path(m.icon) + '" alt="' + m.name + '" /><p>' + m.name.downcase + '</p><div class="clearfloat"></div></li>' %>
|
<% @list += '<li id="' + m.id.to_s + '" data-name="' + m.name + '" class="' + mClass + '"><img src="' + asset_path(m.icon) + '" alt="' + m.name + '" /><p>' + m.name.downcase + '</p><div class="clearfloat"></div></li>' %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
@ -119,4 +116,4 @@
|
||||||
<script>
|
<script>
|
||||||
Metamaps.Create.selectedMetacodeSet = "metacodeset-<%= selectedSet %>"
|
Metamaps.Create.selectedMetacodeSet = "metacodeset-<%= selectedSet %>"
|
||||||
Metamaps.Create.selectedMetacodeSetIndex = <%= index %>
|
Metamaps.Create.selectedMetacodeSetIndex = <%= index %>
|
||||||
</script>
|
</script>
|
5
app/views/tokens/new.html.erb
Normal file
5
app/views/tokens/new.html.erb
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<%= form_for @token, url: '/api/v2/tokens', method: :post do |form| %>
|
||||||
|
<h4>Request new API Token</h4>
|
||||||
|
<%= form.text_field :description, placeholder: "Token description..." %>
|
||||||
|
<%= form.submit %>
|
||||||
|
<% end %>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue