diff --git a/.gitignore b/.gitignore index d482edc41c..b0abad8970 100644 --- a/.gitignore +++ b/.gitignore @@ -50,10 +50,11 @@ test.db solr/* test/reports/ !config/application.yml +test.sqlite-journal # Ignore NPM node_modules .byebug_history vendor/bundle spec/TEST-Teaspoon-Result.xml yarn.lock -yarn-error.log \ No newline at end of file +yarn-error.log diff --git a/Gemfile b/Gemfile index bb440516ae..7356c62ebb 100644 --- a/Gemfile +++ b/Gemfile @@ -111,7 +111,7 @@ group :test, :development do gem 'phantomjs' gem 'puma', '~> 4.2' gem 'rails-perftest' - gem 'rake', '~> 12.3.3' + gem 'rake', '~> 13.0.1' gem 'rest-client' gem 'rspec' gem 'selenium-webdriver', '~> 3.142.4' diff --git a/Gemfile.lock b/Gemfile.lock index 4dbccdfd1f..14a3710efb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -78,8 +78,7 @@ GEM rack-test (>= 0.6.3) regexp_parser (~> 1.5) xpath (~> 3.2) - childprocess (2.0.0) - rake (< 13.0) + childprocess (1.0.0) chronic (0.10.2) ci_reporter (2.0.0) builder (>= 2.1.2) @@ -108,7 +107,7 @@ GEM term-ansicolor (~> 1.3) thor (>= 0.19.4, < 2.0) tins (~> 1.6) - crass (1.0.4) + crass (1.0.5) declarative (0.0.10) declarative-option (0.1.0) descendants_tracker (0.0.4) @@ -119,11 +118,11 @@ GEM domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) equalizer (0.0.11) - erubi (1.8.0) + erubi (1.9.0) execjs (2.7.0) faraday (0.15.4) multipart-post (>= 1.2, < 3) - ffi (1.11.1) + ffi (1.11.2) ffi-compiler (1.0.1) ffi (>= 1.0.0) rake @@ -202,7 +201,7 @@ GEM http_accept_language (2.1.1) http_parser.rb (0.6.0) httpclient (2.8.3) - i18n (1.6.0) + i18n (1.7.0) concurrent-ruby (~> 1.0) i18n-js (3.3.0) i18n (>= 0.6.6) @@ -243,7 +242,7 @@ GEM rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) - loofah (2.2.3) + loofah (2.3.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) @@ -268,7 +267,7 @@ GEM mimemagic (0.3.3) mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.12.0) + minitest (5.13.0) minitest-reporters (1.3.8) ansi builder @@ -287,7 +286,7 @@ GEM netrc (0.11.0) nifty-generators (0.4.6) nio4r (2.5.1) - nokogiri (1.10.4) + nokogiri (1.10.5) mini_portile2 (~> 2.4.0) nokogumbo (2.0.1) nokogiri (~> 1.8, >= 1.8.4) @@ -379,8 +378,8 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.2.0) - loofah (~> 2.2, >= 2.2.2) + rails-html-sanitizer (1.3.0) + loofah (~> 2.3) rails-i18n (5.1.3) i18n (>= 0.7, < 2) railties (>= 5.0, < 6) @@ -394,7 +393,7 @@ GEM rake (>= 0.8.7) thor (>= 0.19.0, < 2.0) rainbow (3.0.0) - rake (12.3.3) + rake (13.0.1) rb-fsevent (0.10.3) rb-inotify (0.10.0) ffi (~> 1.0) @@ -450,9 +449,8 @@ GEM crass (~> 1.0.2) nokogiri (>= 1.8.0) nokogumbo (~> 2.0) - sassc (2.0.1) + sassc (2.2.1) ffi (~> 1.9) - rake sassc-rails (2.1.2) railties (>= 4.0.0) sassc (>= 2.0) @@ -627,7 +625,7 @@ DEPENDENCIES rails-i18n (~> 5.1.3) rails-perftest rails_autolink - rake (~> 12.3.3) + rake (~> 13.0.1) rb-readline rdiscount (~> 2.2, >= 2.2.0.1) recaptcha diff --git a/README.md b/README.md index e23042424a..678e1cd74e 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,18 @@ We recommend you either work in a virtual environment, or on a dual booted syste 1. [Dual Booting](https://www.tecmint.com/install-ubuntu-alongside-with-windows-dual-boot/amp/), [option2](https://askubuntu.com/questions/1031993/how-to-install-ubuntu-18-04-alongside-windows-10), [video guide](https://www.youtube.com/watch?v=qNeJvujdB-0&fbclid=IwAR0APhs89jlNR_ENKbSwrp6TI6P-wxlx-a0My9XBvPNAfwtADZaAXqcKtP4) 2. [Setting up a linux virtual env](https://itsfoss.com/install-linux-in-virtualbox/) +## Redis Installation + +Public Lab uses Redis and may be required for some functionality when running the application locally. +1. Install Redis if you haven't already: + * Using **MacOS**: `brew install redis` + * Using **Linux**: `sudo yum -y install redis` +2. Run Redis server: + * Using **MacOS**: `brew services start redis` + * Using **Linux**: `redis-server` +3. Run SideKiq: `bundle exec sidekiq` +4. If SideKiq started correctly Redis is now configured and working! + ## SSL in Development We, at Public Lab use [openssl](https://github.com/ruby/openssl) gem to provide SSL (Secure Sockets Layer) for the secure connection in the development mode. You can run the https connection on the localhost by following the following steps: @@ -198,6 +210,7 @@ Help improve Public Lab software! * Try out some supportive tasks https://github.com/publiclab/plots2/wiki/Supportive-Tasks * Get involved with our weekly community check-ins. For guidelines: [https://github.com/publiclab/plots2/tree/master/doc/CHECKINS.md ](https://github.com/publiclab/plots2/tree/master/doc/CHECKINS.md) +* You can help us by opening first timers issues or fto. The template for opening an issue can be found https://docs.google.com/document/d/1dO-CAgModEGM5cOaMmcnBh2pEON0hv_rH3P2ou2r1eE/edit ## First Time? diff --git a/app/api/srch/search.rb b/app/api/srch/search.rb index 7c49e6f9d9..d1525929f5 100644 --- a/app/api/srch/search.rb +++ b/app/api/srch/search.rb @@ -103,7 +103,7 @@ class Search < Grape::API if results.present? docs = results.map do |model| DocResult.new( - doc_type: 'USERS', + doc_type: 'USERS', doc_url: '/profile/' + model.name, doc_title: model.username, latitude: model.lat, @@ -181,7 +181,7 @@ class Search < Grape::API DocList.new('', search_request) end end - + # Request URL should be /api/srch/nodes?query=QRY desc 'Perform a search of nodes', hidden: false, is_array: false, @@ -298,7 +298,6 @@ class Search < Grape::API end # Request URL should be /api/srch/taglocations?nwlat=200.0&selat=0.0&nwlng=0.0&selng=200.0[&tag=awesome] - # Note: Query(QRY as above) must have latitude and longitude as query=lat,lon desc 'Perform a search of documents having nearby latitude and longitude tag values', hidden: false, is_array: false, nickname: 'search_tag_locations' @@ -317,6 +316,8 @@ class Search < Grape::API doc_type: 'PLACES', doc_url: model.path(:items), doc_title: model.title, + doc_author: model.user.username, + doc_image_url: !model.images.empty? ? model.images.first.path : 0, score: model.answers.length, latitude: model.lat, longitude: model.lon, @@ -330,7 +331,6 @@ class Search < Grape::API end # Request URL should be /api/srch/nearbyPeople?nwlat=200.0&selat=0.0&nwlng=0.0&selng=200.0[&tag=awesome&sort_by=recent] - # Note: Query(QRY as above) must have latitude and longitude as query=lat,lon desc 'Perform a search to show people nearby a given location', hidden: false, is_array: false, nickname: 'search_nearby_people' diff --git a/app/assets/javascripts/application/i18n/translations.js b/app/assets/javascripts/application/i18n/translations.js deleted file mode 100644 index 203d59a497..0000000000 --- a/app/assets/javascripts/application/i18n/translations.js +++ /dev/null @@ -1,3 +0,0 @@ -I18n.translations || (I18n.translations = {}); -I18n.translations["en"] = I18n.extend((I18n.translations["en"] || {}), {"js":{"dashboard":{"all_updates":"All updates","none":"None","selected_updates":"Selected updates"}}}); -I18n.translations["de"] = I18n.extend((I18n.translations["de"] || {}), {"js":{"dashboard":{"all_updates":"Alle updates","none":"Keiner","selected_updates":"Ausgewählte Updates"}}}); diff --git a/app/assets/javascripts/async_tag_subscriptions.js b/app/assets/javascripts/async_tag_subscriptions.js index b2634d1c52..55363326b0 100644 --- a/app/assets/javascripts/async_tag_subscriptions.js +++ b/app/assets/javascripts/async_tag_subscriptions.js @@ -25,12 +25,13 @@ $(document).ready(function() window.location = "/tag/" + ($('#taginput').val()).replace(/\s/g, '-'); }); $('.index-follow-buttons').on('ajax:success', function(data, status, xhr){ - console.log("Hello"); - var data_recv = JSON.parse(JSON.stringify(status)); - notyNotification('relax', 3000, 'success', 'top', data_recv.message + 'Click here to manage your subscriptions. '); - var html_new = ' Following'; - $('#follow-unfollow-column-'+data_recv.id).html(html_new); - }); + console.log("Hello"); + var data_recv = JSON.parse(JSON.stringify(status)); + notyNotification('relax', 3000, 'success', 'top', data_recv.message + 'Click here to manage your subscriptions. '); + var html_new = ' Following'; + $('#follow-unfollow-column-'+data_recv.id).html(html_new); + window.history.pushState("", "", data_recv.url); // Preserve state + }); $('.index-follow-buttons').on('ajax:error', function(data, status, xhr){ var data_recv = JSON.parse(JSON.stringify(status)); notyNotification('relax', 3000, 'error', 'top', data_recv.message + 'Click here to manage your subscriptions. '); diff --git a/app/assets/javascripts/leaflet_helper.js b/app/assets/javascripts/leaflet_helper.js index 61e8aa7111..b6267a73ed 100644 --- a/app/assets/javascripts/leaflet_helper.js +++ b/app/assets/javascripts/leaflet_helper.js @@ -8,21 +8,6 @@ return map ; } - - function setupFullScreen(map , lat , lon) { - map.addControl(new L.Control.Fullscreen()); // to go full-screen - map.on('fullscreenchange', function () { - if (map.isFullscreen()) { - map.options.minZoom = 3 ; - } - else { - map.options.minZoom = 1 ; - map.panTo(new L.LatLng(lat,lon)); - } - }); - } - - function PLmarker_default(){ L.Icon.PLmarker = L.Icon.extend({ options: { @@ -65,13 +50,14 @@ }); } - function contentLayerParser(map,markers_hash, map_tagname) { + function contentLayerParser(map, markers_hash, map_tagname) { var NWlat = map.getBounds().getNorthWest().lat ; var NWlng = map.getBounds().getNorthWest().lng ; var SElat = map.getBounds().getSouthEast().lat ; var SElng = map.getBounds().getSouthEast().lng ; map.spin(true) ; - if(map_tagname === null && (typeof map_tagname === "undefined")) { + + if(map_tagname === null || (typeof map_tagname === "undefined")) { taglocation_url = "/api/srch/taglocations?nwlat=" + NWlat + "&selat=" + SElat + "&nwlng=" + NWlng + "&selng=" + SElng ; } else { @@ -82,12 +68,19 @@ for (i = 0; i < data.items.length; i++) { var url = data.items[i].doc_url; var title = data.items[i].doc_title; + var author = data.items[i].doc_author; + var image_url = data.items[i].doc_image_url; var default_url = PLmarker_default(); var mid = data.items[i].doc_id ; - var m = L.marker([data.items[i].latitude, data.items[i].longitude], {icon: default_url}).addTo(map).bindPopup("" + title + "") ; - + var m = L.marker([data.items[i].latitude, data.items[i].longitude], {icon: default_url}).bindPopup("" + title + "") ; + if(markers_hash.has(mid) === false){ - m.addTo(map).bindPopup("" + title + "") ; + + if(image_url) { + m.addTo(map).bindPopup("

" + "Title: " + title + "
Author: " + author + "
" + "" + "Read more..." + "
" ) ; + } else { + m.addTo(map).bindPopup("Title: " + title + "
Author: " + author + "
" + "" + "
Read more..." + "
" ) ; + } markers_hash.set(mid , m) ; } } @@ -96,46 +89,53 @@ }); } - function setupInlineLEL(map , layers, mainLayer) { + + + function setupInlineLEL(map , layers, mainLayer, markers_hash) { + layers = layers.split(','); - L.tileLayer('https://a.tiles.mapbox.com/v3/jywarren.map-lmrwb2em/{z}/{x}/{y}.png', { - attribution: '© OpenStreetMap contributors' - }).addTo(map) ; + L.tileLayer('https://a.tiles.mapbox.com/v3/jywarren.map-lmrwb2em/{z}/{x}/{y}.png').addTo(map) ; + + var oms = omsUtil(map, { + keepSpiderfied: true, + circleSpiralSwitchover: 0 + }); L.LayerGroup.EnvironmentalLayers({ include: layers, + embed: true, }).addTo(map); if(typeof mainLayer !== "undefined" && mainLayer !== ""){ if(mainLayer === "people"){ - let markers_hash1 = new Map() ; + map.on('zoomend' , function () { - peopleLayerParser(map, markers_hash1); + peopleLayerParser(map, markers_hash); }) ; map.on('moveend' , function () { - peopleLayerParser(map,markers_hash1); + peopleLayerParser(map, markers_hash); }) ; } else if(mainLayer === "content"){ - let markers_hash2 = new Map() ; + map.on('zoomend' , function () { - contentLayerParser(map,markers_hash2); + contentLayerParser(map, markers_hash); }) ; map.on('moveend' , function () { - contentLayerParser(map,markers_hash2); + contentLayerParser(map, markers_hash); }) ; } else { // it is a tagname - let markers_hash3 = new Map() ; + map.on('zoomend' , function () { - contentLayerParser(map,markers_hash3, mainLayer); + contentLayerParser(map, markers_hash, mainLayer); }) ; map.on('moveend' , function () { - contentLayerParser(map,markers_hash3, mainLayer); + contentLayerParser(map, markers_hash, mainLayer); }) ; } } @@ -146,6 +146,11 @@ attribution: '© OpenStreetMap contributors' }).addTo(map) ; + var oms = omsUtil(map, { + keepSpiderfied: true, + circleSpiralSwitchover: 0 + }); + L.LayerGroup.EnvironmentalLayers({ hash: !!sethash, }).addTo(map); diff --git a/app/assets/javascripts/wikis.js b/app/assets/javascripts/wikis.js index ba2ea23b80..da653f2dc0 100644 --- a/app/assets/javascripts/wikis.js +++ b/app/assets/javascripts/wikis.js @@ -26,9 +26,9 @@ function setupWiki(node_id, title, raw, logged_in) { }); $('#content').hide(); } else { - $('#content').html(shortCodePrompt($('#content')[0], { - submitUrl: '/wiki/replace/' + node_id - })); + // $('#content').html(shortCodePrompt($('#content')[0], { + // submitUrl: '/wiki/replace/' + node_id + // })); postProcessContent(); addDeepLinks($('#content')); } diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index c0884fef94..bc6078824b 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -34,4 +34,5 @@ *= require noty/lib/noty.css *= require jquery-confirm/css/jquery-confirm.css *= require jquery.atwho + *= require profile */ diff --git a/app/assets/stylesheets/profile.css b/app/assets/stylesheets/profile.css new file mode 100644 index 0000000000..b597ecdc3b --- /dev/null +++ b/app/assets/stylesheets/profile.css @@ -0,0 +1,52 @@ +.profile-card .card-header { + padding: 0.8em 0.8em 0 0.8em; + background-color: inherit; + border:none; +} +.profile-card .card-header p { + display: inline-block; + color: #808080; + font-size: 0.9rem; + margin-left: 6px; +} + +.profile-card .card-body { + font-family: Arial, sans-serif; + color: #808080; + font-size: 0.9rem; + padding: 0 0.8em 0 0.8em; +} +.profile-card .card-body>div { + padding: 0.4em 0 0.4em 0; +} + +.profile-card .card-footer { + font-family: Arial, sans-serif; + color: #808080; + font-size: 0.9rem; + border: 0; + background-color: #fff; + margin: 0; + padding: 0.8em 0 0.8em 0; +} +.profile-card .posted-by-links a { + text-decoration: underline; + color: #808080; + display: inline-block; +} +.profile-card .posted-by-links a:hover { + text-decoration: none; + color: #505050; +} +.profile-card a.post-title { + text-decoration: none; + color: #212529; + font-family: "Junction Light", lucida grande,lucida sans console,sans-serif; + font-size: 1.1rem; + display: inline-block; + margin-left: 6px; +} +.profile-card a:hover.post-title { + text-decoration: underline; + color: #212529; +} \ No newline at end of file diff --git a/app/assets/stylesheets/style.css b/app/assets/stylesheets/style.css index 25eb178dc1..0bf628d7a2 100644 --- a/app/assets/stylesheets/style.css +++ b/app/assets/stylesheets/style.css @@ -118,6 +118,10 @@ a .fa-white, display:none; } +.text-underline { + text-decoration: underline !important; +} + #pl_pad { width:300%; } @@ -408,10 +412,6 @@ div.note .meta { color:#888; } -.image-container { - height:auto; - overflow:auto; -} /*start of img aspect css that will affect the thumbnails which appears on the user's profile under their notes*/ @@ -483,6 +483,10 @@ div.note.moderated h4 { filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } +a.btn-outline-secondary .fa { + color: inherit; +} + .nav-button-wrapper { padding-top: 7.5px; } diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb index c1f62d971d..4406bfb03c 100644 --- a/app/channels/application_cable/connection.rb +++ b/app/channels/application_cable/connection.rb @@ -4,18 +4,13 @@ class Connection < ActionCable::Connection::Base def connect self.current_user = find_verified_user end - + def find_verified_user - if cookies.signed['user_token'] != nil - user = User.where(persistence_token: cookies.signed['user_token']) - if user.any? - return user.first - else - return nil - end - else - return nil - end + return if cookies.signed['user_token'].nil? + + user = User.where(persistence_token: cookies.signed['user_token']) + + return user.first if user.any? end end end diff --git a/app/channels/room_channel.rb b/app/channels/room_channel.rb index c435da479f..de6f5ffc9a 100644 --- a/app/channels/room_channel.rb +++ b/app/channels/room_channel.rb @@ -1,6 +1,5 @@ # This is a general channel connected to all active session class RoomChannel < ApplicationCable::Channel - def subscribed stream_from "room_channel" end @@ -10,8 +9,8 @@ def unsubscribed end def speak(message) - if current_user && current_user.role == "admin" - ActionCable.server.broadcast 'room_channel', message: message["message"] - end + return unless current_user && current_user.role == "admin" + + ActionCable.server.broadcast 'room_channel', message: message["message"] end end diff --git a/app/channels/user_channel.rb b/app/channels/user_channel.rb index df58a75d89..9403c79968 100644 --- a/app/channels/user_channel.rb +++ b/app/channels/user_channel.rb @@ -1,6 +1,6 @@ class UserChannel < ApplicationCable::Channel def subscribed - if current_user != nil + if !current_user.nil? stream_from "users:#{current_user.id}" else reject diff --git a/app/channels/user_notification_channel.rb b/app/channels/user_notification_channel.rb index accb48deb7..04415cb27c 100644 --- a/app/channels/user_notification_channel.rb +++ b/app/channels/user_notification_channel.rb @@ -1,6 +1,6 @@ class UserNotificationChannel < ApplicationCable::Channel def subscribed - if current_user != nil + if !current_user.nil? stream_from "users:notification:#{current_user.id}" else reject diff --git a/app/controllers/editor_controller.rb b/app/controllers/editor_controller.rb index 33bfcf468c..182391a537 100644 --- a/app/controllers/editor_controller.rb +++ b/app/controllers/editor_controller.rb @@ -34,6 +34,14 @@ def rich if params[:main_image] && Image.find_by(id: params[:main_image]) @main_image = Image.find_by(id: params[:main_image]).path end + + if params[:tags]&.include? "lat:" and params[:tags]&.include? "lon:" + tags = params[:tags].split(',') + tags.each do |x| + x.include? "lat:" and (@lat = x.split(':')[1]) + x.include? "lon:" and (@lon = x.split(':')[1]) + end + end template if params[:n] && !params[:body] # use another node body as a template image if params[:i] end diff --git a/app/controllers/subscription_controller.rb b/app/controllers/subscription_controller.rb index d7b1ce5f92..8c4126e0ef 100644 --- a/app/controllers/subscription_controller.rb +++ b/app/controllers/subscription_controller.rb @@ -64,8 +64,9 @@ def add if set_following(true, params[:type], tag.tid) if request.xhr? - # message = "Started following #{params[:name]}!" - # status = "200" + message = "Started following #{params[:name]}!" + status = "200" + render json: { status: status, message: message, id: tag.tid, tagname: params[:name], url: "/tags" + "?_=" + Time.now.to_i.to_s } else flash[:notice] = "You are now following '#{params[:name]}'." redirect_to "/subscriptions" + "?_=" + Time.now.to_i.to_s diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index aeca48e3f3..5b775a3bdb 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -301,7 +301,24 @@ def reset def comments comments = Comment.limit(20) .order("timestamp DESC") - .where(uid: params[:id]) + .where(uid: User.where(username: params[:id], status: 1).first) + .paginate(page: params[:page], per_page: 24) + + @normal_comments = comments.where('comments.status = 1') + if logged_in_as(['admin', 'moderator']) + @moderated_comments = comments.where('comments.status = 4') + end + render template: 'comments/index' + end + + def comments_by_tagname + comments = Comment.limit(20) + .order("timestamp DESC") + .where(uid: User.where(username: params[:id], status: 1).first) + .where(nid: Node.where(status: 1) + .includes(:node_tag, :tag) + .references(:term_data) + .where('term_data.name = ?', params[:tagname])) .paginate(page: params[:page], per_page: 24) @normal_comments = comments.where('comments.status = 1') diff --git a/app/models/comment.rb b/app/models/comment.rb index 93dca96858..6078bc18ce 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -475,4 +475,13 @@ def render_body end body end + + def self.find_by_tag_and_author(tagname, userid) + Comment.where(uid: userid) + .where(node: Node.where(status: 1) + .includes(:node_tag, :tag) + .references(:node, :term_data) + .where('term_data.name = ?', tagname)) + .order('timestamp DESC') + end end diff --git a/app/models/concerns/node_shared.rb b/app/models/concerns/node_shared.rb index 83a11dc065..1fc27c67a4 100644 --- a/app/models/concerns/node_shared.rb +++ b/app/models/concerns/node_shared.rb @@ -195,8 +195,8 @@ def self.notes_map(body) lat = Regexp.last_match(2) lon = Regexp.last_match(3) tagname = nil - - map_data_string(lat, lon, tagname, "leaflet") + + map_data_string(lat, lon, tagname, "plainInlineLeaflet") end end @@ -206,7 +206,7 @@ def self.notes_map_by_tag(body) lat = Regexp.last_match(3) lon = Regexp.last_match(4) - map_data_string(lat, lon, tagname, "leaflet") + map_data_string(lat, lon, tagname, "plainInlineLeaflet") end end @@ -221,8 +221,8 @@ def self.people_map(body, _page = 1) end end - # [map:layers:other_inline_layer:_latitude_:_longitude:skyTruth,mapKnitter] - # [map:layers::_latitude_:_longitude:skyTruth,sapKnitter] + # [map:layers:other_inline_layer:_latitude_:_longitude:skytruth,mapknitter] + # [map:layers::_latitude_:_longitude:skytruth,mapknitter] def self.layers_map(body, _page = 1) body.gsub(/(?`])(\)?\[map\:layers\:(\w*)\:(\S+)\:(\S+)\:(\w+)((\,\w+)*)]/) do |_tagname| mainLayer = Regexp.last_match(2) @@ -356,7 +356,7 @@ def self.data_string(view, tagname, nodes, type) def self.map_data_string(lat, lon, tagname, template, mainLayer = nil) a = ActionController::Base.new - locals_data = if template == "leaflet" + locals_data = if template == "plainInlineLeaflet" { lat: lat, lon: lon, tagname: tagname.to_s } elsif template == "inlineLeaflet" { lat: lat, lon: lon, layers: tagname.to_s, mainLayer: mainLayer } diff --git a/app/models/doc_result.rb b/app/models/doc_result.rb index df9b87cdf5..93a1dd71df 100644 --- a/app/models/doc_result.rb +++ b/app/models/doc_result.rb @@ -1,6 +1,6 @@ # A DocResult is an individual return item for a document (web page) search class DocResult - attr_accessor :doc_id, :doc_type, :doc_url, :doc_title, :doc_score, :latitude, :longitude, :blurred, :category + attr_accessor :doc_id, :doc_type, :doc_url, :doc_title, :doc_score, :latitude, :longitude, :blurred, :category, :doc_author, :doc_image_url def initialize(args = {}) @doc_id = args[:doc_id] @@ -8,6 +8,8 @@ def initialize(args = {}) @doc_url = args[:doc_url] @doc_title = args[:doc_title] @doc_score = args[:doc_score] + @doc_author = args[:doc_author] + @doc_image_url = args[:doc_image_url] @latitude = args[:latitude] @longitude = args[:longitude] @blurred = args[:blurred] diff --git a/app/models/node.rb b/app/models/node.rb index 69eb399ac0..950978f277 100644 --- a/app/models/node.rb +++ b/app/models/node.rb @@ -1,14 +1,12 @@ class UniqueUrlValidator < ActiveModel::Validator def validate(record) - if record.title == '' || record.title.nil? - # record.errors[:base] << "You must provide a title." + if record.title.blank? + record.errors[:base] << "You must provide a title." # otherwise the below title uniqueness check fails, as title presence validation doesn't run until after elsif record.type == 'page' array = %w(create edit update delete new) - array.each do |x| - if record.title == x - record.errors[:base] << "You may not use the title '" + x + "'" - end + if array.include? record.title.downcase + record.errors[:base] << "You may not use the title '#{record.title}'" end else if !Node.where(path: record.generate_path).first.nil? && record.type == 'note' @@ -96,7 +94,7 @@ def updated_month belongs_to :user, foreign_key: 'uid' - validates :title, presence: true + validates :title, presence: true, length: { minimum: 3 } validates_with UniqueUrlValidator, on: :create scope :published, -> { where(status: 1) } @@ -877,6 +875,30 @@ def self.questions .group('node.nid') end + # finds nodes by tag name, user id, and optional node type + def self.find_by_tag_and_author(tagname, user_id, type = 'notes') + + node_type = 'note' if type == 'notes' || 'questions' + node_type = 'page' if type == 'wiki' + # node_type = 'map' if type == 'maps' # Tag.tagged_nodes_by_author does not seem to work with maps, more testing required + + order = 'node_revisions.timestamp DESC' + order = 'created DESC' if node_type == 'note' + + qids = Node.questions.where(status: 1).collect(&:nid) + + nodes = Tag.tagged_nodes_by_author(tagname, user_id) + .includes(:revision) + .references(:node_revisions) + .where(status: 1, type: node_type) + .order(order) + + nodes = nodes.where('node.nid NOT IN (?)', qids) if type == 'notes' + nodes = nodes.where('node.nid IN (?)', qids) if type == 'questions' + + nodes + end + # so we can quickly fetch activities corresponding to this node # with node.activities def activities diff --git a/app/models/user.rb b/app/models/user.rb index 73bbfdab32..ca486019ca 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -71,7 +71,7 @@ def is_new_contributor? end def new_contributor - return "new contributor".html_safe if is_new_contributor? + return "new contributor".html_safe if is_new_contributor? end def set_token @@ -396,7 +396,7 @@ def find_by_username_case_insensitive(username) User.where('lower(username) = ?', username.downcase).first end - # all uses who've posted a node, comment, or answer in the given period + # all users who've posted a node, comment, or answer in the given period def contributor_count_for(start_time, end_time) notes = Node.where(type: 'note', status: 1, created: start_time.to_i..end_time.to_i).pluck(:uid) answers = Answer.where(created_at: start_time..end_time).pluck(:uid) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 3ceca6f637..d06a850446 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -154,7 +154,7 @@ def tagNearbyNodes(coordinates, tag, period = { "from" => nil, "to" => nil }, so else items.order(Arel.sql("created #{order_direction}")) .limit(limit) - end + end end # Search nearby people with respect to given latitude, longitute and tags diff --git a/app/views/admin/_nodes.html.erb b/app/views/admin/_nodes.html.erb index 007eb2f9e2..a3c91e3e33 100644 --- a/app/views/admin/_nodes.html.erb +++ b/app/views/admin/_nodes.html.erb @@ -10,8 +10,8 @@ $('#batch-delete').bind('click',function(e) { vals = [] alert_error('The mass spam button is disabled pending resolution of ticket #184.') - //$('.batch-checkbox').each(function(i,a) { if (a.checked) vals.push(a.value) }) - //window.location = "/spam/batch/"+vals.join(',') + $('.batch-checkbox').each(function(i,a) { if (a.checked) vals.push(a.value) }) + window.location = "/spam/batch/" + vals.join(',') }) })() diff --git a/app/views/comments/_comments.html.erb b/app/views/comments/_comments.html.erb index ac16731b1d..e1f44afc92 100644 --- a/app/views/comments/_comments.html.erb +++ b/app/views/comments/_comments.html.erb @@ -3,13 +3,13 @@ - <%= t('users.list.comment') %> - <%= t('users.list.last_activity') %> + <%= translation('users.list.comment') %> + <%= translation('users.list.last_activity') %> <% if logged_in_as(['admin', 'moderator']) %> - <%= t('users.list.moderation') %> + <%= translation('users.list.moderation') %> <% else %> - <%= t('users.list.moderation') %> + <%= translation('users.list.moderation') %> <% end %> @@ -23,14 +23,14 @@ "<%= raw strip_tags(sanitize(RDiscount.new(comment.comment).to_html)).truncate(150) %>" | <% if comment.parent %> - <%= t('home._comments.read_more') %> » + <%= translation('home._comments.read_more') %> » <% end %> <%= distance_of_time_in_words(comment.created_at, Time.current, { include_seconds: false, scope: 'datetime.time_ago_in_words' }) %> <% if comment.status == 4 && current_user &. can_moderate? %> - <%= t('dashboard.moderate.approve') %> - <%= t('dashboard.moderate.spam') %> + <%= translation('dashboard.moderate.approve') %> + <%= translation('dashboard.moderate.spam') %> <% elsif current_user &. can_moderate? %> diff --git a/app/views/comments/_edit.html.erb b/app/views/comments/_edit.html.erb index 479b45ff8d..a31ccf9f76 100644 --- a/app/views/comments/_edit.html.erb +++ b/app/views/comments/_edit.html.erb @@ -22,20 +22,20 @@

+ - <%= t('comments._edit.upload_image') %> + <%= translation('comments._edit.upload_image') %> @@ -126,7 +126,7 @@

- +   -
<%= raw t('comments._edit.logged_in', :username => current_user.username) %> | -
<%= t('comments._edit.formatting') %> | - <%= t('comments._edit.notifications') %> +
<%= raw translation('comments._edit.logged_in', :username => current_user.username) %> | + <%= translation('comments._edit.formatting') %> | + <%= translation('comments._edit.notifications') %>
diff --git a/app/views/comments/_form.html.erb b/app/views/comments/_form.html.erb index 220d70eaf8..1ff96be45d 100644 --- a/app/views/comments/_form.html.erb +++ b/app/views/comments/_form.html.erb @@ -40,21 +40,21 @@

- <%= raw t('comments._form.drag_and_drop') %> + <%= raw translation('comments._form.drag_and_drop') %> @@ -91,28 +91,28 @@ <%= javascript_include_tag "comment.js" %>

- + <% if local_assigns[:comment] %> - <%= t('comments._form.preview') %> + <%= translation('comments._form.preview') %> - <%= t('comments._form.cancel') %> + <%= translation('comments._form.cancel') %> <% else %> - <%= t('comments._form.preview') %> + <%= translation('comments._form.preview') %> <% end %>   -
<%= raw t('comments._form.logged_in', :username => current_user.username) %>   +
<%= raw translation('comments._form.logged_in', :username => current_user.username) %>    
diff --git a/app/views/dashboard/_activity.html.erb b/app/views/dashboard/_activity.html.erb index 7c2af71de4..371bc722dc 100644 --- a/app/views/dashboard/_activity.html.erb +++ b/app/views/dashboard/_activity.html.erb @@ -1,7 +1,7 @@

<%= t('dashboard._activity.activity') %>

- +
diff --git a/app/views/dashboard/_node_comment.html.erb b/app/views/dashboard/_node_comment.html.erb index ccf929d013..5eecc5f09f 100644 --- a/app/views/dashboard/_node_comment.html.erb +++ b/app/views/dashboard/_node_comment.html.erb @@ -13,7 +13,7 @@

<%= node.author.name %> <%= node.author.new_contributor %> - <%= t('dashboard._node_comment.commented_on') %> <% unless node.aid == 0 %> an answer about<% end %> <% if node.parent.has_power_tag('question') %><% else %><% end %><%= node.parent.title %> + <%= translation('dashboard._node_comment.commented_on') %> <% unless node.aid == 0 %> an answer about<% end %> <% if node.parent.has_power_tag('question') %><% else %><% end %><%= node.parent.title %> <%= distance_of_time_in_words(node.created_at, Time.current, { include_seconds: false, scope: 'datetime.time_ago_in_words' }) %>

<%= render partial: 'dashboard/comment_moderate', locals: { comment: node } %> diff --git a/app/views/dashboard/_node_meta.html.erb b/app/views/dashboard/_node_meta.html.erb index 125438d3a9..e604c339eb 100644 --- a/app/views/dashboard/_node_meta.html.erb +++ b/app/views/dashboard/_node_meta.html.erb @@ -1,5 +1,5 @@ <% unless node.is_a?(Comment) || node.is_a?(Revision) || node.type == 'page' %> - <%= t('dashboard.dashboard.by') %> <%= node.author.name %> <%= node.author.new_contributor %> + <%= translation('dashboard.dashboard.by') %> <%= node.author.name %> <%= node.author.new_contributor %> <% if node.has_power_tag('with') %> with <% node.coauthors.each_with_index do |coauthor,i| %> diff --git a/app/views/dashboard/_node_moderate.html.erb b/app/views/dashboard/_node_moderate.html.erb index aa57199235..d06e6b74e8 100644 --- a/app/views/dashboard/_node_moderate.html.erb +++ b/app/views/dashboard/_node_moderate.html.erb @@ -1,11 +1,11 @@ <% if node.status == 4 %>

<% if logged_in_as(['admin','moderator']) %> - <%= t('dashboard._node_moderate.first_time_post') %> - <%= t('dashboard.moderate.approve') %> - <%= t('dashboard.moderate.spam') %> + <%= translation('dashboard._node_moderate.first_time_post') %> + <%= translation('dashboard.moderate.approve') %> + <%= translation('dashboard.moderate.spam') %> <% else %> - <%= raw t('dashboard.moderate.pending_approval', :url => '/wiki/moderation') %> + <%= raw translation('dashboard.moderate.pending_approval', :url => '/wiki/moderation') %> <% end %>

<% end %> diff --git a/app/views/dashboard/_node_question.html.erb b/app/views/dashboard/_node_question.html.erb index 4b2ffb8130..bd74034a8a 100644 --- a/app/views/dashboard/_node_question.html.erb +++ b/app/views/dashboard/_node_question.html.erb @@ -15,7 +15,7 @@

<%= node.title %>

-

<%= t('dashboard._node_question.question') %> <%= render partial: "dashboard/node_meta", locals: { node: node } %>

+

<%= translation('dashboard._node_question.question') %> <%= render partial: "dashboard/node_meta", locals: { node: node } %>

diff --git a/app/views/dashboard/_node_wiki.html.erb b/app/views/dashboard/_node_wiki.html.erb index 5bf3c3c0b4..d91913a805 100644 --- a/app/views/dashboard/_node_wiki.html.erb +++ b/app/views/dashboard/_node_wiki.html.erb @@ -3,10 +3,10 @@ <% if node.is_a?(Node) %>
-

<%= t('dashboard._node_wiki.new_page_by') %> <%= node.latest.author.name %>

+

<%= translation('dashboard._node_wiki.new_page_by') %> <%= node.latest.author.name %>

<% else %>
-

<%= t('dashboard._node_wiki.new_edit_by') %> <%= node.author.try(:name) %>

+

<%= translation('dashboard._node_wiki.new_edit_by') %> <%= node.author.try(:name) %>

<% end %> @@ -27,7 +27,7 @@ <% if node.is_a?(Revision) && node.previous %> - <%= t('dashboard._node_wiki.changes') %> » + <%= translation('dashboard._node_wiki.changes') %> » <% end %>

<%= render partial: "dashboard/node_meta", locals: { node: node } %>

diff --git a/app/views/editor/rich.html.erb b/app/views/editor/rich.html.erb index 3d8b8deab9..b485c039dc 100644 --- a/app/views/editor/rich.html.erb +++ b/app/views/editor/rich.html.erb @@ -304,14 +304,8 @@ - +
+ <%= render partial: 'notes/notes', locals: { notes: nodes, unpaginated: true } %> +
diff --git a/app/views/grids/_wikis.html.erb b/app/views/grids/_wikis.html.erb index 8c317c2671..ea79506ba6 100644 --- a/app/views/grids/_wikis.html.erb +++ b/app/views/grids/_wikis.html.erb @@ -18,7 +18,7 @@ <%= distance_of_time_in_words(Time.at(wiki.latest.created_at), Time.current, { include_seconds: false, scope: 'datetime.time_ago_in_words' }) %> <%= raw t('wiki._wikis.by') %> <%= wiki.latest.author.name %> <%= wiki.revisions.length %> - <%= number_with_delimiter(wiki.views) %> + <%= number_with_delimiter(wiki.views) %> <%= number_with_delimiter(wiki.cached_likes) %> <% end %> diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index c856ad980f..3a92f1a574 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -1,4 +1,4 @@ - <% cache('feature_anniversary-banner', skip_digest: true) do %> +<% cache('feature_anniversary-banner', skip_digest: true) do %> <%= feature('anniversary-banner') %> <% end %> @@ -25,23 +25,40 @@