diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 25e45d1c8d..c3d1bbcaf4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,11 +1,17 @@ -Make sure these boxes are checked before your pull request is ready to be reviewed and merged. Thanks! +Make sure these boxes are checked before your pull request (PR) is ready to be reviewed and merged. Thanks! -* [ ] all tests pass -- `rake test:all` -* [ ] code is in uniquely-named feature branch, and has been rebased on top of latest master (especially if you've been asked to make additional changes) -* [ ] pull request is descriptively named with #number reference back to original issue +* [ ] tests pass -- look for a green checkbox ✔️ a few minutes after opening your PR -- or run tests locally with `rake test` +* [ ] code is in uniquely-named feature branch and has no merge conflicts +* [ ] PR is descriptively titled +* [ ] PR body includes `fixes #0000`-style reference to original issue # +* [ ] ask `@publiclab/reviewers` for help, in a comment below -Please be sure you've reviewed our contribution guidelines at https://publiclab.org/wiki/contributing-to-public-lab-software +> We're happy to help you get this ready -- don't be afraid to ask for help, and **don't be discouraged** if your tests fail at first! -We have a loose schedule of reviewing and pulling in changes every Tuesday and Friday, and publishing changes on Fridays. Please alert developers on plots-dev@googlegroups.com when your request is ready or if you need assistance. +If tests do fail, click on the red `X` to learn why by reading the logs. + +Please be sure you've reviewed our contribution guidelines at https://publiclab.org/contributing-to-public-lab-software + +We have a loose schedule of reviewing and pulling in changes every Tuesday and Friday, and publishing changes on Fridays. Thanks! diff --git a/.github/first-timers-issue-template.md b/.github/first-timers-issue-template.md index d1f183bc45..6adbf5325f 100644 --- a/.github/first-timers-issue-template.md +++ b/.github/first-timers-issue-template.md @@ -14,8 +14,11 @@ Nothing. This issue is meant to welcome you to Open Source :) We are happy to wa - [ ] 🙋 **Claim this issue**: Comment below. If someone else has claimed it, ask if they've opened a pull request already and if they're stuck -- maybe you can help them solve a problem or move it along! -- [ ] 📝 **Update** the file [$FILENAME]($BRANCH_URL) in the `$REPO` repository (press the little pen Icon) and edit the line as shown below. [See this page](https://github.com/publiclab/plots2/projects/2) for some help in taking your first steps! +- [ ] 📝 **Update** the file [$FILENAME]($BRANCH_URL) in the `$REPO` repository (press the little pen Icon) and edit the line as shown below. +[See this page](https://publiclab.github.io/community-toolbox/#r=all) for some help in taking your first steps! + +Below is a "diff" showing in red (and a `-`) which lines to remove, and in green (and a `+`) which lines to add: ```diff $DIFF @@ -39,6 +42,6 @@ Leave a comment below! ### Is someone else already working on this? -We encourage you to link to this issue by mentioning the issue # in your pull request, so we can see if someone's already started on it. **If someone seem stuck, offer them some help!** Otherwise, [take a look at some other issues you can help with](https://github.com/publiclab/plots2/projects/2). Thanks! +We encourage you to link to this issue by mentioning the issue # in your pull request, so we can see if someone's already started on it. **If someone seem stuck, offer them some help!** Otherwise, [take a look at some other issues you can help with](https://publiclab.github.io/community-toolbox/#r=all). Thanks! (This issue was created by [First-Timers-Bot](https://github.com/hoodiehq/first-timers-bot).) diff --git a/.rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml b/.rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml index d3c946a143..232fc30753 100644 --- a/.rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml +++ b/.rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml @@ -1082,7 +1082,7 @@ Performance/FlatMap: Performance/HashEachMethods: Enabled: true - + Performance/LstripRstrip: Enabled: true diff --git a/.rubocop.yml b/.rubocop.yml index 8ce2190ed8..906bc867a1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -15,4 +15,25 @@ AllCops: - 'log/*' - 'db/**/*' - 'Gemfile' - TargetRubyVersion: '2.4' \ No newline at end of file + - 'config/**/*' + - 'script/**/*' + - 'lib/**/*' + - 'test/**/*' + - 'public/**/*' + - 'Dangerfile' + TargetRubyVersion: '2.4' + +Style/FrozenStringLiteralComment: + Enabled: false + +Metrics/ParameterLists: + Enabled: false + +Metrics/ModuleLength: + Max: 303 + +Metrics/ClassLength: + Max: 660 + +Style/RegexpLiteral: + Enabled: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fe14e84d92..c27888d5c9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,43 +3,19 @@ Contributing to PLOTS2 We welcome community contributions to PLOTS2! To install PLOTS2 locally, follow the instructions in the [README.md file](https://github.com/publiclab/plots2#installation). -Learn more about contributing to PLOTS2 or other Public Lab code projects on these pages: - -* https://publiclab.org/wiki/developers -* https://publiclab.org/wiki/contributing-to-public-lab-software - -## Bug reports & troubleshooting - -If you are submitting a bug, please include: - -* the URL you're on when you see the issue -* the URL you just left, if you suspect your last action triggered the problem -* your PublicLab.org username (if you have one) -* your browser (and version if possible!) and operating system (Windows 7, Ubuntu Linux 14.x, etc) -* anything you can about the sequence of events which led to the bug -* any data you're attempting to upload, such as a photo, which can help us troubleshoot +## First Timers Welcome! +New to open source/free software? Here are a selection of issues we've made especially for first-timers. We're here to help, so just ask if one looks interesting: +https://publiclab.github.io/community-toolbox/#r=all -## First Timers +Thank you so much! -New to open source/free software? Here are a selection of issues we've made especially for first-timers. We're here to help, so just ask if one looks interesting : https://github.com/publiclab/plots2/projects/2 -We also have a slightly larger list of easy-ish but small and self contained issues: https://github.com/publiclab/plots2/labels/help-wanted +Learn more about contributing to Public Lab code projects on these pages: -When you think you've solved the bug and are ready to submit a pull request, please keep in mind the following: +* https://publiclab.org/developers +* https://publiclab.org/contributing-to-public-lab-software -Make sure these boxes are checked before your pull request is ready to be reviewed and merged. -* [ ] all tests pass -- `rake test:all` -* [ ] code is in uniquely-named feature branch, and has been rebased on top of latest master (especially if you've been asked to make additional changes) -* [ ] pull request is descriptively named with #number reference back to original issue -* [ ] if possible, multiple commits squashed if they're smaller changes -* [ ] reviewed/confirmed/tested by another contributor or maintainer -* [ ] `schema.rb.example` has been updated if any database migrations were added - -Make sure you've reviewed our contribution guidelines at https://publiclab.org/wiki/contributing-to-public-lab-software - -Also, make sure to reference the issue no as "fixes #" in your PRs, so that they can be auto-closed on merging. - -We have a loose schedule of reviewing and pulling in changes every Tuesday and Friday, and publishing changes on Fridays. Please alert developers on plots-dev@googlegroups.com when your request is ready or if you need assistance. +## Bug reports & troubleshooting -Thank you for your help! +If you are submitting a bug, please go to https://github.com/publiclab/plots2/issues/new diff --git a/Dangerfile b/Dangerfile index 014cb3a3b6..3343330563 100644 --- a/Dangerfile +++ b/Dangerfile @@ -55,6 +55,5 @@ begin end rescue => ex - puts "There was an error with Danger bot's Junit parsing:" - puts ex.message + fail "There was an error with Danger bot's Junit parsing: #{ex.message}" end diff --git a/Dockerfile b/Dockerfile index 5d96750064..3cf9b6a1e4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,11 +11,11 @@ RUN mkdir -p /app ENV HOME /root ENV PHANTOMJS_VERSION 2.1.1 -RUN echo \ - 'deb ftp://ftp.us.debian.org/debian/ jessie main\n \ - deb ftp://ftp.us.debian.org/debian/ jessie-updates main\n \ - deb http://security.debian.org jessie/updates main\n' \ - > /etc/apt/sources.list +#RUN echo \ +# 'deb ftp://ftp.us.debian.org/debian/ jessie main\n \ +# deb ftp://ftp.us.debian.org/debian/ jessie-updates main\n \ +# deb http://security.debian.org jessie/updates main\n' \ +# > /etc/apt/sources.list # Install dependencies RUN apt-get update -qq && apt-get install -y bundler libmysqlclient-dev ruby-rmagick libfreeimage3 nodejs-legacy npm wget openjdk-7-jre openjdk-7-jdk procps diff --git a/Gemfile b/Gemfile index 43504ce023..51bdbf8128 100644 --- a/Gemfile +++ b/Gemfile @@ -77,7 +77,7 @@ gem 'geokit-rails' gem 'rails_autolink' gem 'rb-readline' -gem "paperclip", "~> 4.2.2" +gem "paperclip", "~> 4.2.0" gem "ruby-openid", :require => "openid" gem "rack-openid" gem "authlogic", "~> 3.5.0" @@ -110,3 +110,7 @@ gem 'jbuilder' # Pin mustermann to Ruby 2.1 compatible gem 'mustermann' , '~> 0.4' + +#OAuth Based login +gem 'omniauth', '~> 1.3', '>= 1.3.1' +gem 'omniauth-facebook', '~> 3.0' diff --git a/Gemfile.lock b/Gemfile.lock index 554ce7d97f..d8b2140980 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -401,7 +401,7 @@ DEPENDENCIES mustermann (~> 0.4) mysql2 (~> 0.3.20) nifty-generators - paperclip (< 4.2.0) + paperclip (~> 4.2.0) passenger php-serialize progress_bar @@ -435,3 +435,4 @@ DEPENDENCIES BUNDLED WITH 1.16.1 + \ No newline at end of file diff --git a/Gemfile.new b/Gemfile.new deleted file mode 100644 index 727c5a1ffc..0000000000 --- a/Gemfile.new +++ /dev/null @@ -1,97 +0,0 @@ -source 'https://rubygems.org' - -ruby '2.1.2' -gem 'rails', '~> 3.2.20' -gem 'passenger' - -gem 'rails-i18n', '~> 3.0.0' - -# run with `bundle install --without production` or `bundle install --without mysql` to exclude this -group :mysql, :production do - gem 'mysql2', '~> 0.3.20' - # mysql 0.4.3+ causes a version mismatch, apparently, and demands 'activerecord-mysql2-adapter' -end - -# ships with sqlite set up for easy setup during development -# run with `bundle install --without development` or `bundle install --without sqlite` to exclude this -group :sqlite, :development do - gem 'sqlite3' -end - -#group :postgresql do -# gem "activerecord-postgresql-adapter" -#end - -# Gems used only for assets and not required in production environments by default. -group :assets do - gem 'sass-rails', '~> 3.2.3' - gem 'coffee-rails', '~> 3.2.1' - gem 'execjs' # See https://github.com/sstephenson/execjs#readme for more supported runtimes - gem 'therubyracer' - gem 'uglifier', '>= 1.0.3' -end - -# run with `bundle install --without development` to exclude these -group :development do - gem "nifty-generators" - gem 'byebug' -end - -# run with `bundle install --without test` to exclude these -group :test do - gem 'test-unit' - gem 'rake', '~> 10.5.0' - # gems to test RESTful API - gem 'rest-client' - gem 'rspec' - gem 'json_expressions' - gem 'timecop' - gem 'jasmine-rails' - gem 'jasmine-jquery-rails' -end - -# run with `bundle install --without production` to exclude these -group :production do - gem "scrypt", "~> 1.2.1" -end - -gem 'composite_primary_keys' -gem 'jquery-rails' -gem 'rdiscount', '1.6.8' # Markdown -gem 'will_paginate', '>= 3.0.6' -gem 'will_paginate-bootstrap', '>= 1.0.1' -gem 'georuby', '2.0' -gem 'geokit-rails' -gem 'rails_autolink' -gem 'rb-readline' -gem "paperclip", "~> 4.1.1" -gem "ruby-openid", :require => "openid" -gem "rack-openid" -gem "authlogic", "3.2.0" -gem "php-serialize", :require => "php_serialize" -gem 'less-rails', '~> 2.6' -gem 'progress_bar' -gem 'impressionist' - -# RESTful API Support -gem 'grape' -gem 'grape-entity' -gem 'grape-swagger' -gem 'grape-swagger-entity' -gem 'grape-swagger-rails' -gem 'grape-swagger-ui' -gem 'rack-cors', :require => 'rack/cors' - -gem 'mocha', '~> 1.1' - -gem 'sunspot_rails' -gem 'sunspot_solr' - -gem 'geocoder' -gem "i18n-js", ">= 3.0.0.rc11" -gem 'http_accept_language' - -# The default friendly_id version compatible with Rails 3 is v4.0 -gem 'friendly_id' -gem 'jbuilder' -gem 'strong_parameters' diff --git a/README.md b/README.md index 23cbb5bb79..9cdca36a54 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,14 @@ It features a Bootstrap-based UI and a variety of community and attribution feat Some key features include: -* a Markdown-based research note and wiki editor +* a [Question and Answer system](https://publiclab.org/questions) for peer-based problem solving +* a rich text and Markdown research note and wiki [editor](https://github.com/publiclab/PublicLab.Editor) * [wiki editing](https://publiclab.org/wiki) and revision tracking -* tagging and tag-based content organization +* tagging and [tag-based content organization](http://publiclab.org/tags) * email notification subscriptions for tags and comments -* a barebones search interface -* a user dashboard [presenting recent activity](https://publiclab.org/research) -* a [Question and Answer system](https://publiclab.org/questions) +* a search interface built out of [our growing API](https://github.com/publiclab/plots2/blob/master/doc/API.md) +* a user dashboard [presenting recent activity](https://publiclab.org/dashboard) +* a privacy-sensitive, Leaflet-based [location tagging system](https://github.com/publiclab/leaflet-blurred-location/) and [community map](http://publiclab.org/people) ![Diagram](https://publiclab.org/system/images/photos/000/021/061/original/diagram.png) @@ -62,6 +63,10 @@ Please read and abide by our [Code of Conduct](https://publiclab.org/conduct); o 12. Wheeeee! You're up and running! Log in with test usernames "user", "moderator", or "admin", and password "password". 13. Run `rake test` to confirm that your install is working properly. For some setups, you may see warnings even if test pass; [see this issue](https://github.com/publiclab/plots2/issues/440) we're working to resolve. +### Ruby version + +Make sure to use ruby-2.3.4. To check your ruby version run `ruby -v`.If you are using some other version then install ruby-2.3.4 with `rvm install 2.3.4`. Later to use ruby-2.3.4, run `rvm use 2.3.4`. Always make sure that you are using the correct ruby version since it might go back to its original version if you close the terminal. You might have to redo the entire installation process after switching to a different version. + ### Bundle exec For some, it will be necessary to prepend your gem-related commands with `bundle exec`, for example, `bundle exec passenger start`; adding `bundle exec` ensures you're using the version of passenger you just installed with Bundler. `bundle exec rake db: setup`, `bundle exec rake db: seed` are other examples of where this might be necessary. diff --git a/README.rdoc b/README.rdoc deleted file mode 100644 index 7c36f2356e..0000000000 --- a/README.rdoc +++ /dev/null @@ -1,261 +0,0 @@ -== Welcome to Rails - -Rails is a web-application framework that includes everything needed to create -database-backed web applications according to the Model-View-Control pattern. - -This pattern splits the view (also called the presentation) into "dumb" -templates that are primarily responsible for inserting pre-built data in between -HTML tags. The model contains the "smart" domain objects (such as Account, -Product, Person, Post) that holds all the business logic and knows how to -persist themselves to a database. The controller handles the incoming requests -(such as Save New Account, Update Product, Show Post) by manipulating the model -and directing data to the view. - -In Rails, the model is handled by what's called an object-relational mapping -layer entitled Active Record. This layer allows you to present the data from -database rows as objects and embellish these data objects with business logic -methods. You can read more about Active Record in -link:files/vendor/rails/activerecord/README.html. - -The controller and view are handled by the Action Pack, which handles both -layers by its two parts: Action View and Action Controller. These two layers -are bundled in a single package due to their heavy interdependence. This is -unlike the relationship between the Active Record and Action Pack that is much -more separate. Each of these packages can be used independently outside of -Rails. You can read more about Action Pack in -link:files/vendor/rails/actionpack/README.html. - - -== Getting Started - -1. At the command prompt, create a new Rails application: - rails new myapp (where myapp is the application name) - -2. Change directory to myapp and start the web server: - cd myapp; rails server (run with --help for options) - -3. Go to http://localhost:3000/ and you'll see: - "Welcome aboard: You're riding Ruby on Rails!" - -4. Follow the guidelines to start developing your application. You can find -the following resources handy: - -* The Getting Started Guide: http://guides.rubyonrails.org/getting_started.html -* Ruby on Rails Tutorial Book: http://www.railstutorial.org/ - - -== Debugging Rails - -Sometimes your application goes wrong. Fortunately there are a lot of tools that -will help you debug it and get it back on the rails. - -First area to check is the application log files. Have "tail -f" commands -running on the server.log and development.log. Rails will automatically display -debugging and runtime information to these files. Debugging info will also be -shown in the browser on requests from 127.0.0.1. - -You can also log your own messages directly into the log file from your code -using the Ruby logger class from inside your controllers. Example: - - class WeblogController < ActionController::Base - def destroy - @weblog = Weblog.find(params[:id]) - @weblog.destroy - logger.info("#{Time.now} Destroyed Weblog ID ##{@weblog.id}!") - end - end - -The result will be a message in your log file along the lines of: - - Mon Oct 08 14:22:29 +1000 2007 Destroyed Weblog ID #1! - -More information on how to use the logger is at http://www.ruby-doc.org/core/ - -Also, Ruby documentation can be found at http://www.ruby-lang.org/. There are -several books available online as well: - -* Programming Ruby: http://www.ruby-doc.org/docs/ProgrammingRuby/ (Pickaxe) -* Learn to Program: http://pine.fm/LearnToProgram/ (a beginners guide) - -These two books will bring you up to speed on the Ruby language and also on -programming in general. - - -== Debugger - -Debugger support is available through the debugger command when you start your -Mongrel or WEBrick server with --debugger. This means that you can break out of -execution at any point in the code, investigate and change the model, and then, -resume execution! You need to install ruby-debug to run the server in debugging -mode. With gems, use sudo gem install ruby-debug. Example: - - class WeblogController < ActionController::Base - def index - @posts = Post.all - debugger - end - end - -So the controller will accept the action, run the first line, then present you -with a IRB prompt in the server window. Here you can do things like: - - >> @posts.inspect - => "[#nil, "body"=>nil, "id"=>"1"}>, - #"Rails", "body"=>"Only ten..", "id"=>"2"}>]" - >> @posts.first.title = "hello from a debugger" - => "hello from a debugger" - -...and even better, you can examine how your runtime objects actually work: - - >> f = @posts.first - => #nil, "body"=>nil, "id"=>"1"}> - >> f. - Display all 152 possibilities? (y or n) - -Finally, when you're ready to resume execution, you can enter "cont". - - -== Console - -The console is a Ruby shell, which allows you to interact with your -application's domain model. Here you'll have all parts of the application -configured, just like it is when the application is running. You can inspect -domain models, change values, and save to the database. Starting the script -without arguments will launch it in the development environment. - -To start the console, run rails console from the application -directory. - -Options: - -* Passing the -s, --sandbox argument will rollback any modifications - made to the database. -* Passing an environment name as an argument will load the corresponding - environment. Example: rails console production. - -To reload your controllers and models after launching the console run -reload! - -More information about irb can be found at: -link:http://www.rubycentral.org/pickaxe/irb.html - - -== dbconsole - -You can go to the command line of your database directly through rails -dbconsole. You would be connected to the database with the credentials -defined in database.yml. Starting the script without arguments will connect you -to the development database. Passing an argument will connect you to a different -database, like rails dbconsole production. Currently works for MySQL, -PostgreSQL and SQLite 3. - -== Description of Contents - -The default directory structure of a generated Ruby on Rails application: - - |-- app - | |-- assets - | |-- images - | |-- javascripts - | `-- stylesheets - | |-- controllers - | |-- helpers - | |-- mailers - | |-- models - | `-- views - | `-- layouts - |-- config - | |-- environments - | |-- initializers - | `-- locales - |-- db - |-- doc - |-- lib - | `-- tasks - |-- log - |-- public - |-- script - |-- test - | |-- fixtures - | |-- functional - | |-- integration - | |-- performance - | `-- unit - |-- tmp - | |-- cache - | |-- pids - | |-- sessions - | `-- sockets - `-- vendor - |-- assets - `-- stylesheets - `-- plugins - -app - Holds all the code that's specific to this particular application. - -app/assets - Contains subdirectories for images, stylesheets, and JavaScript files. - -app/controllers - Holds controllers that should be named like weblogs_controller.rb for - automated URL mapping. All controllers should descend from - ApplicationController which itself descends from ActionController::Base. - -app/models - Holds models that should be named like post.rb. Models descend from - ActiveRecord::Base by default. - -app/views - Holds the template files for the view that should be named like - weblogs/index.html.erb for the WeblogsController#index action. All views use - eRuby syntax by default. - -app/views/layouts - Holds the template files for layouts to be used with views. This models the - common header/footer method of wrapping views. In your views, define a layout - using the layout :default and create a file named default.html.erb. - Inside default.html.erb, call <% yield %> to render the view using this - layout. - -app/helpers - Holds view helpers that should be named like weblogs_helper.rb. These are - generated for you automatically when using generators for controllers. - Helpers can be used to wrap functionality for your views into methods. - -config - Configuration files for the Rails environment, the routing map, the database, - and other dependencies. - -db - Contains the database schema in schema.rb. db/migrate contains all the - sequence of Migrations for your schema. - -doc - This directory is where your application documentation will be stored when - generated using rake doc:app - -lib - Application specific libraries. Basically, any kind of custom code that - doesn't belong under controllers, models, or helpers. This directory is in - the load path. - -public - The directory available for the web server. Also contains the dispatchers and the - default HTML files. This should be set as the DOCUMENT_ROOT of your web - server. - -script - Helper scripts for automation and generation. - -test - Unit and functional tests along with fixtures. When using the rails generate - command, template test files will be generated for you and placed in this - directory. - -vendor - External libraries that the application depends on. Also includes the plugins - subdirectory. If the app has frozen rails, those gems also go here, under - vendor/rails/. This directory is in the load path. diff --git a/app/api/srch/search.rb b/app/api/srch/search.rb index e647e3ae86..56d899170e 100644 --- a/app/api/srch/search.rb +++ b/app/api/srch/search.rb @@ -118,9 +118,9 @@ class Search < Grape::API # Request URL should be /api/srch/locations?srchString=QRY[&seq=KEYCOUNT&showCount=NUM_ROWS&pageNum=PAGE_NUM] # Note: Query(QRY as above) must have latitude and longitude as srchString=lat,lon desc 'Perform a search of documents having nearby latitude and longitude tag values', - hidden: false, - is_array: false, - nickname: 'srchGetLocations' + hidden: false, + is_array: false, + nickname: 'srchGetLocations' params do requires :srchString, type: String, documentation: { example: 'Spec' } optional :seq, type: Integer, documentation: { example: 995 } @@ -138,6 +138,29 @@ class Search < Grape::API sresult end + #API TO FETCH QRY RECENT CONTRIBUTORS + # Request URL should be /api/srch/peoplelocations?srchString=QRY[&seq=KEYCOUNT&showCount=NUM_ROWS&pageNum=PAGE_NUM] + #QRY should be a number + desc 'Perform a search to show x Recent People', + hidden: false, + is_array: false, + nickname: 'srchGetPeople' + params do + requires :srchString, type: String, documentation: { example: 'Spec' } + optional :seq, type: Integer, documentation: { example: 995 } + optional :showCount, type: Integer, documentation: { example: 3 } + optional :pageNum, type: Integer, documentation: { example: 0 } + end + get :peoplelocations do + sresult = DocList.new + unless params[:srchString].nil? || params[:srchString] == 0 + sservice = SearchService.new + sresult = sservice.recentPeople(params[:srchString]) + end + sparms = SearchRequest.fromRequest(params) + sresult.srchParams = sparms + sresult + end # end endpoint definitions end end diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index b98b80f1a2..01a59b2c06 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -39,16 +39,14 @@ //= require jsdiff/diff.js //= require main_image.js //= require restful_typeahead.js -//= require searchform.js //= require users.js //= require searchform.js -//= require location_tags.js //= require tagging.js //= require grids.js //= require graph.js //= require short-code-forms/dist/short-code-prompts.js //= require wikis.js //= require chart.js/dist/Chart.js -//= require turbolinks -//= require google_analytics.js +//= require header_footer.js +// require turbolinks // Temporarily removed while fixing diff --git a/app/assets/javascripts/editor.js b/app/assets/javascripts/editor.js index d1d44a4486..5243ac4334 100644 --- a/app/assets/javascripts/editor.js +++ b/app/assets/javascripts/editor.js @@ -94,12 +94,11 @@ $E = { else $E.textarea.val($E.templates[template]) }, templates: { - 'blog': "###The beginning\n\n###What we did\n\n###Why it matters\n\n###How can you help", - 'default': "###What I want to do\n\n###My attempt and results\n\n###Questions and next steps\n\n###Why I'm interested", - 'support': "###Details about the problem\n\n###A photo or screenshot of the setup", - 'event': "###Event details\n\nWhen, where, what\n\n###Background\n\nWho, why", - 'oiltestkit': "##Reflections on the Alpha Oil Testing Kit\n\n###What was tested\n\nWhen, where, what\n\n###Things that went well\n\n###Challenges encountered\n\n###Suggestions to improve the tool\n\n\n", - 'question': "###What I want to do or know\n\n###Background story" + 'blog': "## The beginning\n\n## What we did\n\n## Why it matters\n\n## How can you help", + 'default': "## What I want to do\n\n## My attempt and results\n\n## Questions and next steps\n\n## Why I'm interested", + 'support': "## Details about the problem\n\n## A photo or screenshot of the setup", + 'event': "## Event details\n\nWhen, where, what\n\n## Background\n\nWho, why", + 'question': "## What I want to do or know\n\n## Background story" }, previewing: false, generate_preview: function(id,text) { diff --git a/app/assets/javascripts/google_analytics.js b/app/assets/javascripts/google_analytics.js deleted file mode 100644 index c0d5253bf1..0000000000 --- a/app/assets/javascripts/google_analytics.js +++ /dev/null @@ -1,6 +0,0 @@ -$(document).on("turbolinks:load", function() { - if (typeof ga == "function"){ - ga("set", "location", event.data.url) - ga("send", "pageview") - } -}); diff --git a/app/assets/javascripts/grids.js b/app/assets/javascripts/grids.js index 616d8a1e3a..8845e54eff 100644 --- a/app/assets/javascripts/grids.js +++ b/app/assets/javascripts/grids.js @@ -10,6 +10,7 @@ function sortGrid(type, selector, headerLink) { headerLink = $(headerLink), desc = headerLink.hasClass('desc'), header = table.find('tr:first').detach(), + footer = table.find('tfoot').detach(), rows = table.find('tr').detach(); rows = rows.sort(function(a, b){ @@ -39,6 +40,7 @@ function sortGrid(type, selector, headerLink) { table.html(rows); table.prepend(header); + table.append(footer); console.log('rows') rows.each(function(row) { diff --git a/app/assets/javascripts/header_footer.js b/app/assets/javascripts/header_footer.js new file mode 100644 index 0000000000..3a7c0dc191 --- /dev/null +++ b/app/assets/javascripts/header_footer.js @@ -0,0 +1,11 @@ +jQuery(document).ready(function() { + var duration = 300; + + $(".back-to-top").click(function(event) { + event.preventDefault(); + jQuery("html, body").animate({scrollTop: 0}, duration); + return false; + }) + +}); + \ No newline at end of file diff --git a/app/assets/javascripts/location_tags.js b/app/assets/javascripts/location_tags.js deleted file mode 100644 index 87d1bedb13..0000000000 --- a/app/assets/javascripts/location_tags.js +++ /dev/null @@ -1,148 +0,0 @@ -$(document).ready(function() { - - if (window.hasOwnProperty('google')) { - - var geo_location = $('#geo_location'); - if (geo_location) var autocomplete = new google.maps.places.Autocomplete(geo_location); - - autocomplete.addListener('place_changed', function() { - var place = autocomplete.getPlace(); - - var user = $('#infoform').data('user'); - $.ajax({ - url: "/profile/location/create/" + user, - type: "POST", - data: { - type: 'location', - value: { - address: place.formatted_address - } - }, - success: function(data) { - response = data; - if (response.status) { - $("#location_map").html("
"); - var mymap = new L.map('map').setView([response.location.lat, response.location.lon], 15); - if (response.location_privacy) { - var lat = response.location.lat; - var long = response.location.lon; - - L.tileLayer("https://a.tiles.mapbox.com/v3/jywarren.map-lmrwb2em/{z}/{x}/{y}.png",{ - attribution: "OSM tiles by MapBox", - }).addTo(mymap); - - var marker = L.marker([lat, long]).addTo(mymap); - marker.bindPopup("" + response.name + "").openPopup(); - - } else { - var lat = parseFloat(response.location.lat).toFixed(4); - var long = parseFloat(response.location.lon).toFixed(4); - - var options = { - radius : 20, // Size of the hexagons/bins - opacity: 0.5, // Opacity of the hexagonal layer - duration: 200, // millisecond duration of d3 transitions (see note below) - lng: function(d){ return d[1]; }, // longitude accessor - lat: function(d){ return d[0]; }, // latitude accessor - value: function(d){ return d.length; }, // value accessor - derives the bin value - valueFloor: 0, // override the color scale domain low value - valueCeil: undefined, // override the color scale domain high value - colorRange: ['#f7fbff', '#08306b'], // default color range for the heat map (see note below) - onmouseover: function(d, node, layer) {}, - onmouseout: function(d, node, layer) {}, - onclick: function(d, node, layer) {} - } - - L.tileLayer("https://a.tiles.mapbox.com/v3/jywarren.map-lmrwb2em/{z}/{x}/{y}.png",{ - attribution: "OSM tiles by MapBox", - }).addTo(mymap); - - var hexlayer = L.hexbinLayer(options).addTo(mymap); - hexlayer.colorScale().range(["white", "grey"]); - - hexlayer.data([[lat, long]]); - } - } - else { - $("#geo_location").append(''+response['errors']+''); - } - } - }) - }) - - - $('#location_privacy').click(function(e) { - e.preventDefault(); - that = this - var status = $(this).is(':checked') - - $.ajax({ - url: '/profile/user/privacy', - type: 'POST', - data: { - location_privacy: status, - id: $('#infoform').data('user') - }, - success: function(data) { - - if (data.status) { - $(that).prop('checked', data.model.location_privacy); - if (data.model.location_privacy) { - if (data.lat && data.long) { - $("#location_map").html("
"); - var mymap = new L.map('map').setView([data.lat, data.long], 15); - - L.tileLayer("https://a.tiles.mapbox.com/v3/jywarren.map-lmrwb2em/{z}/{x}/{y}.png",{ - attribution: "OSM tiles by MapBox", - }).addTo(mymap); - - var marker = L.marker([data.lat, data.long]).addTo(mymap); - marker.bindPopup("" + data.model.username + "").openPopup(); - } - - } - else { - if (data.lat && data.long) { - $("#location_map").html("
"); - var lat = parseFloat(data.lat).toFixed(4); - var long = parseFloat(data.long).toFixed(4); - var mymap = new L.map('map').setView([lat, long], 15); - - var options = { - radius : 20, // Size of the hexagons/bins - opacity: 0.5, // Opacity of the hexagonal layer - duration: 200, // millisecond duration of d3 transitions (see note below) - lng: function(d){ return d[1]; }, // longitude accessor - lat: function(d){ return d[0]; }, // latitude accessor - value: function(d){ return d.length; }, // value accessor - derives the bin value - valueFloor: 0, // override the color scale domain low value - valueCeil: undefined, // override the color scale domain high value - colorRange: ['#f7fbff', '#08306b'], // default color range for the heat map (see note below) - onmouseover: function(d, node, layer) {}, - onmouseout: function(d, node, layer) {}, - onclick: function(d, node, layer) {} - } - - L.tileLayer("https://a.tiles.mapbox.com/v3/jywarren.map-lmrwb2em/{z}/{x}/{y}.png",{ - attribution: "OSM tiles by MapBox", - }).addTo(mymap); - - - var hexlayer = L.hexbinLayer(options).addTo(mymap); - hexlayer.colorScale().range(["white", "grey"]); - - hexlayer.data([[lat, long]]); - } - - } - } - - } - - }) - - }); - - } - -}); diff --git a/app/assets/javascripts/restful_typeahead.js b/app/assets/javascripts/restful_typeahead.js index 8512881219..437ed82077 100644 --- a/app/assets/javascripts/restful_typeahead.js +++ b/app/assets/javascripts/restful_typeahead.js @@ -9,7 +9,7 @@ jQuery(document).ready(function() { var el = $('input.search-query.typeahead'); var typeahead = el.typeahead({ - items: 8, + items: 15, minLength: 3, autoSelect: false, source: function (query, process) { diff --git a/app/assets/javascripts/searchform.js b/app/assets/javascripts/searchform.js index cdcf0b5207..b82fee5dff 100644 --- a/app/assets/javascripts/searchform.js +++ b/app/assets/javascripts/searchform.js @@ -7,6 +7,5 @@ e.preventDefault() window.location = '/search/'+$('#searchform_input').val() }); - }); diff --git a/app/assets/javascripts/users.js b/app/assets/javascripts/users.js index 322750ea1c..235f658825 100644 --- a/app/assets/javascripts/users.js +++ b/app/assets/javascripts/users.js @@ -89,3 +89,20 @@ var Profile = { }); } } + +class Reset { + validateEmail(sEmail){ + const filter = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return filter.test(sEmail); + } + + runValidation(event){ + const email = $('#validEmail').val(); + + if(!this.validateEmail(email)) { + $("#validPrint").attr("style", "display:block"); + $("#validPrint").html("

Invalid email address

"); + event.preventDefault(); + } + } +} diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 7a4600662e..4c4f0ef42c 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -24,4 +24,5 @@ *= require comments *= require wiki *= require question + *= require tags */ diff --git a/app/assets/stylesheets/style.css b/app/assets/stylesheets/style.css index 9cec4337ae..cffc232267 100644 --- a/app/assets/stylesheets/style.css +++ b/app/assets/stylesheets/style.css @@ -417,3 +417,15 @@ div.note.moderated h4 { font-size: 80%; } } + +.back-to-top { + + float: right; + +} + +.back-to-top i { + + font-size: 60px; + +} diff --git a/app/assets/stylesheets/tags.css b/app/assets/stylesheets/tags.css new file mode 100644 index 0000000000..1276c3be84 --- /dev/null +++ b/app/assets/stylesheets/tags.css @@ -0,0 +1,3 @@ +.popover{ + z-index: 1; +} \ No newline at end of file diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index f36c4446f3..bcbe6d6e07 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -1,5 +1,5 @@ class AdminController < ApplicationController - before_filter :require_user, only: %i[spam spam_revisions] + before_filter :require_user, only: %i(spam spam_revisions mark_comment_spam publish_comment) def promote_admin @user = User.find params[:id] @@ -79,9 +79,9 @@ def spam .order('nid DESC') @nodes = if params[:type] == 'wiki' @nodes.where(type: 'page', status: 1) - else - @nodes.where(status: 0) - end + else + @nodes.where(status: 0) + end else flash[:error] = 'Only moderators can moderate posts.' redirect_to '/dashboard' @@ -123,6 +123,38 @@ def mark_spam end end + def mark_comment_spam + @comment = Comment.find params[:id] + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + if @comment.status == 1 + @comment.spam + flash[:notice] = "Comment has been marked as spam." + else + flash[:notice] = "Comment already marked as spam." + end + else + flash[:error] = 'Only moderators can moderate comments.' + end + redirect_to '/dashboard' + end + + def publish_comment + if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') + @comment = Comment.find params[:id] + if @comment.status == 1 + flash[:notice] = 'Comment already published.' + else + @comment.publish + flash[:notice] = 'Comment published.' + end + @node = @comment.node + redirect_to @node.path + else + flash[:error] = 'Only moderators can publish comments.' + redirect_to '/dashboard' + end + end + def publish if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') @node = Node.find params[:id] diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb index 1df16ddd1e..e8c19a445f 100644 --- a/app/controllers/answers_controller.rb +++ b/app/controllers/answers_controller.rb @@ -41,7 +41,7 @@ def delete current_user.role == 'admin' || current_user.role == 'moderator' respond_to do |format| - if @answer.delete + if @answer.destroy format.html { redirect_to @answer.node.path(:question), notice: 'Answer deleted' } format.js else diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 80a1d470d4..537e22d819 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -26,20 +26,18 @@ def set_sidebar(type = :generic, data = :all, args = {}) else # type is generic # remove "classroom" postings; also switch to an EXCEPT operator in sql, see https://github.com/publiclab/plots2/issues/375 hidden_nids = Node.joins(:node_tag) - .joins('LEFT OUTER JOIN term_data ON term_data.tid = community_tags.tid') - .select('node.*, term_data.*, community_tags.*') - .where(type: 'note', status: 1) - .where('term_data.name = (?)', 'hidden:response') - .collect(&:nid) + .joins('LEFT OUTER JOIN term_data ON term_data.tid = community_tags.tid') + .select('node.*, term_data.*, community_tags.*') + .where(type: 'note', status: 1) + .where('term_data.name = (?)', 'hidden:response') + .collect(&:nid) @notes = if params[:controller] == 'questions' Node.questions - .joins(:revision) - else - Node.research_notes - .joins(:revision) - .order('node.nid DESC') - .paginate(page: params[:page]) - end + .joins(:revision) + else + Node.research_notes.joins(:revision).order('node.nid DESC').paginate(page: params[:page]) + end + @notes = @notes.where('node.nid != (?)', @node.nid) if @node @notes = @notes.where('node_revisions.status = 1 AND node.nid NOT IN (?)', hidden_nids) unless hidden_nids.empty? @@ -52,11 +50,11 @@ def set_sidebar(type = :generic, data = :all, args = {}) end @wikis = Node.order('changed DESC') - .joins(:revision) - .where('node_revisions.status = 1 AND node.status = 1 AND type = "page"') - .limit(10) - .group('node_revisions.nid') - .order('node_revisions.timestamp DESC') + .joins(:revision) + .where('node_revisions.status = 1 AND node.status = 1 AND type = "page"') + .limit(10) + .group('node_revisions.nid') + .order('node_revisions.timestamp DESC') end end @@ -73,7 +71,7 @@ def current_user_session def current_user unless defined?(@current_user) - @current_user = current_user_session && current_user_session.record + @current_user = current_user_session&.record end # if banned or moderated: if @current_user.try(:drupal_user).try(:status) == 0 @@ -135,7 +133,7 @@ def alert_and_redirect_moderated flash[:error] = I18n.t('application_controller.author_has_been_banned') redirect_to '/' elsif @node.status == 4 && (current_user && (current_user.role == 'admin' || current_user.role == 'moderator')) - flash[:warning] = "First-time poster #{@node.author.name} submitted this #{time_ago_in_words(@node.created_at)} ago and it has not yet been approved by a moderator. Approve Spam" + flash[:warning] = "First-time poster #{@node.author.name} submitted this #{time_ago_in_words(@node.created_at)} ago and it has not yet been approved by a moderator. Approve Spam" elsif @node.status == 4 && (current_user && current_user.id == @node.author.id) && !flash[:first_time_post] flash[:warning] = "Thank you for contributing open research, and thanks for your patience while your post is approved by community moderators and we'll email you when it is published. In the meantime, if you have more to contribute, feel free to do so." elsif @node.status != 1 && !(current_user && (current_user.role == 'admin' || current_user.role == 'moderator')) @@ -162,18 +160,18 @@ def comments_node_and_path @node = if @comment.aid == 0 # finding node for node comments @comment.node - else - # finding node for answer comments - @comment.answer.node - end + else + # finding node for answer comments + @comment.answer.node + end @path = if params[:type] && params[:type] == 'question' # questions path @node.path(:question) - else - # notes path - @node.path - end + else + # notes path + @node.path + end end # used for url redirects for friendly_id diff --git a/app/controllers/comment_controller.rb b/app/controllers/comment_controller.rb index cbfbcb3197..ed44dc5538 100644 --- a/app/controllers/comment_controller.rb +++ b/app/controllers/comment_controller.rb @@ -2,11 +2,11 @@ class CommentController < ApplicationController include CommentHelper respond_to :html, :xml, :json - before_filter :require_user, only: %i[create update make_answer delete] + before_filter :require_user, only: %i(create update make_answer delete) def index @comments = Comment.paginate(page: params[:page], per_page: 30) - .order('timestamp DESC') + .order('timestamp DESC') render template: 'comments/index' end @@ -148,17 +148,18 @@ def make_answer comments_node_and_path if @comment.uid == current_user.uid || - current_user.role == 'admin' || - current_user.role == 'moderator' + current_user.role == 'admin' || + current_user.role == 'moderator' @answer = Answer.new( nid: @comment.nid, uid: @comment.uid, - content: @comment.comment + content: @comment.comment, + created_at: @comment.created_at, + updated_at: @comment.created_at ) if @answer.save && @comment.delete - @answer.answer_notify(current_user) @answer_id = @comment.aid respond_with do |format| format.js { render template: 'comment/make_answer' } diff --git a/app/controllers/editor_controller.rb b/app/controllers/editor_controller.rb index a7653ddeef..6dd41a99d2 100644 --- a/app/controllers/editor_controller.rb +++ b/app/controllers/editor_controller.rb @@ -1,5 +1,5 @@ class EditorController < ApplicationController - before_filter :require_user, only: %i[post rich legacy editor] + before_filter :require_user, only: %i(post rich legacy editor) # main image via URL passed as GET param def legacy @@ -14,7 +14,7 @@ def legacy node = Node.find(params[:n]) params[:body] = node.body if node end - if params[:tags] && params[:tags].include?('question:') + if params[:tags]&.include?('question:') redirect_to "/questions/new?#{request.env['QUERY_STRING']}" else render template: 'editor/post' @@ -26,7 +26,7 @@ def editor end def post - if params[:tags] && params[:tags].include?('question:') + if params[:tags]&.include?('question:') redirect_to "/questions/new?#{request.env['QUERY_STRING']}" elsif params[:legacy] || params[:template] == 'event' legacy diff --git a/app/controllers/features_controller.rb b/app/controllers/features_controller.rb index 585b0ef35a..e338a02742 100644 --- a/app/controllers/features_controller.rb +++ b/app/controllers/features_controller.rb @@ -3,7 +3,7 @@ class FeaturesController < ApplicationController def index @features = Node.where(type: 'feature') - .paginate(page: params[:page]) + .paginate(page: params[:page]) end def embed diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index d4c476a59b..7f504aa949 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,5 +1,5 @@ class HomeController < ApplicationController - before_filter :require_user, only: %i[subscriptions nearby] + before_filter :require_user, only: %i(subscriptions nearby) # caches_action :index, :cache_path => proc { |c| # node = Node.find :last #c.params[:id] @@ -9,14 +9,11 @@ class HomeController < ApplicationController # caches_action :index, :cache_path => { :last => Node.find(:last).updated_at.to_i } def home - @activity, @blog, @notes, @wikis, @revisions, @comments, @answer_comments = Rails.cache.fetch("front-activity", expires_in: 30.minutes) do - self.activity - end - - @title = I18n.t('home_controller.science_community') if current_user redirect_to '/dashboard' else + set_activity :cache + @title = I18n.t('home_controller.science_community') render template: 'home/home' end end @@ -36,26 +33,42 @@ def fetch # route for seeing the front page even if you are logged in def front + set_activity :cache @title = I18n.t('home_controller.environmental_investigation') - @activity, @blog, @notes, @wikis, @revisions, @comments, @answer_comments = Rails.cache.fetch("front-activity", expires_in: 30.minutes) do - self.activity - end render template: 'home/home' end def dashboard - @note_count = Node.select(%i[created type status]) - .where(type: 'note', status: 1, created: Time.now.to_i - 1.weeks.to_i..Time.now.to_i) - .count(:all) - @wiki_count = Revision.select(:timestamp) - .where(timestamp: Time.now.to_i - 1.weeks.to_i..Time.now.to_i) - .count - @user_note_count = Node.where(type: 'note', status: 1, uid: current_user.uid).count if current_user - @activity, @blog, @notes, @wikis, @revisions, @comments, @answer_comments = self.activity - render template: 'dashboard/dashboard' - @title = I18n.t('home_controller.community_research') unless current_user + if current_user + @note_count = Node.select(%i(created type status)) + .where(type: 'note', status: 1, created: Time.now.to_i - 1.weeks.to_i..Time.now.to_i) + .count(:all) + @wiki_count = Revision.select(:timestamp) + .where(timestamp: Time.now.to_i - 1.weeks.to_i..Time.now.to_i) + .count + @user_note_count = Node.where(type: 'note', status: 1, uid: current_user.uid).count + set_activity + render template: 'dashboard/dashboard' + else + redirect_to '/research' + end end + def research + if current_user + redirect_to '/dashboard' + else + @note_count = Node.select(%i(created type status)) + .where(type: 'note', status: 1, created: Time.now.to_i - 1.weeks.to_i..Time.now.to_i) + .count(:all) + @wiki_count = Revision.select(:timestamp) + .where(timestamp: Time.now.to_i - 1.weeks.to_i..Time.now.to_i) + .count + set_activity + render template: 'dashboard/dashboard' + @title = I18n.t('home_controller.community_research') + end + end # trashy... clean this up! # this will eventually be based on the profile_tags data where people can mark their location with "location:lat,lon" def nearby @@ -69,19 +82,21 @@ def nearby end end + private + def activity blog = Tag.find_nodes_by_type('blog', 'note', 1).first # remove "classroom" postings; also switch to an EXCEPT operator in sql, see https://github.com/publiclab/plots2/issues/375 hidden_nids = Node.joins(:node_tag) - .joins('LEFT OUTER JOIN term_data ON term_data.tid = community_tags.tid') - .select('node.*, term_data.*, community_tags.*') - .where(type: 'note', status: 1) - .where('term_data.name = (?)', 'hidden:response') - .collect(&:nid) + .joins('LEFT OUTER JOIN term_data ON term_data.tid = community_tags.tid') + .select('node.*, term_data.*, community_tags.*') + .where(type: 'note', status: 1) + .where('term_data.name = (?)', 'hidden:response') + .collect(&:nid) notes = Node.where(type: 'note') - .where('node.nid NOT IN (?)', hidden_nids + [0]) # in case hidden_nids is empty - .order('nid DESC') - .page(params[:page]) + .where('node.nid NOT IN (?)', hidden_nids + [0]) # in case hidden_nids is empty + .order('nid DESC') + .page(params[:page]) notes = notes.where('nid != (?)', blog.nid) if blog if current_user && (current_user.role == 'moderator' || current_user.role == 'admin') @@ -95,34 +110,34 @@ def activity # include revisions, then mix with new pages: wikis = Node.where(type: 'page', status: 1) - .order('nid DESC') - .limit(10) + .order('nid DESC') + .limit(10) revisions = Revision.joins(:node) - .order('timestamp DESC') - .where('type = (?)', 'page') - .where('node.status = 1') - .where('node_revisions.status = 1') - .where('timestamp - node.created > ?', 300) # don't report edits within 5 mins of page creation - .limit(10) - .group('node.title') + .order('timestamp DESC') + .where('type = (?)', 'page') + .where('node.status = 1') + .where('node_revisions.status = 1') + .where('timestamp - node.created > ?', 300) # don't report edits within 5 mins of page creation + .limit(10) + .group('node.title') # group by day: http://stackoverflow.com/questions/5970938/group-by-day-from-timestamp revisions = revisions.group('DATE(FROM_UNIXTIME(timestamp))') if Rails.env == 'production' revisions = revisions.to_a # ensure it can be serialized for caching wikis += revisions wikis = wikis.sort_by(&:created_at).reverse comments = Comment.joins(:node, :drupal_user) - .order('timestamp DESC') - .where('timestamp - node.created > ?', 86_400) # don't report edits within 1 day of page creation - .page(params[:page]) - .group('title') # group by day: http://stackoverflow.com/questions/5970938/group-by-day-from-timestamp + .order('timestamp DESC') + .where('timestamp - node.created > ?', 86_400) # don't report edits within 1 day of page creation + .page(params[:page]) + .group('title') # group by day: http://stackoverflow.com/questions/5970938/group-by-day-from-timestamp # group by day: http://stackoverflow.com/questions/5970938/group-by-day-from-timestamp comments = comments.group('DATE(FROM_UNIXTIME(timestamp))') if Rails.env == 'production' comments = comments.to_a # ensure it can be serialized for caching answer_comments = Comment.joins(:answer, :drupal_user) - .order('timestamp DESC') - .where('timestamp - answers.created_at > ?', 86_400) - .limit(20) - .group('answers.id') + .order('timestamp DESC') + .where('timestamp - answers.created_at > ?', 86_400) + .limit(20) + .group('answers.id') answer_comments = answer_comments.group('DATE(FROM_UNIXTIME(timestamp))') if Rails.env == 'production' answer_comments = answer_comments.to_a # ensure it can be serialized for caching activity = (notes + wikis + comments + answer_comments).sort_by(&:created_at).reverse @@ -138,4 +153,15 @@ def activity response end + def set_activity(source = :database) + @activity, @blog, @notes, @wikis, @revisions, @comments, @answer_comments = + if source == :cache + Rails.cache.fetch("front-activity", expires_in: 30.minutes) do + activity + end + else + activity + end + end + end diff --git a/app/controllers/images_controller.rb b/app/controllers/images_controller.rb index cb4f12c9b4..ee62044bb2 100644 --- a/app/controllers/images_controller.rb +++ b/app/controllers/images_controller.rb @@ -2,7 +2,7 @@ class ImagesController < ApplicationController respond_to :html, :xml, :json - before_filter :require_user, only: %i[create new update delete] + before_filter :require_user, only: %i(create new update delete) def create if params[:i] diff --git a/app/controllers/like_controller.rb b/app/controllers/like_controller.rb index d45db2f3c8..f489b2162b 100644 --- a/app/controllers/like_controller.rb +++ b/app/controllers/like_controller.rb @@ -1,6 +1,6 @@ class LikeController < ApplicationController respond_to :html, :xml, :json - before_filter :require_user, only: %i[create delete] + before_filter :require_user, only: %i(create delete) #list all recent likes def index @@ -19,9 +19,9 @@ def liked? result = NodeSelection.find_by_user_id_and_nid(current_user.uid, params[:id]) result = if result.nil? false - else - result.liking - end + else + result.liking + end render json: result end diff --git a/app/controllers/map_controller.rb b/app/controllers/map_controller.rb index fa9fb940fd..7ce4924fda 100644 --- a/app/controllers/map_controller.rb +++ b/app/controllers/map_controller.rb @@ -2,13 +2,13 @@ class MapController < ApplicationController def index @title = 'Maps' @nodes = Node.paginate(page: params[:page], per_page: 32) - .order('nid DESC') - .where(type: 'map', status: 1) + .order('nid DESC') + .where(type: 'map', status: 1) # I'm not sure if this is actually eager loading the tags... @maps = Node.joins(:tag) - .where('type = "map" AND status = 1 AND (term_data.name LIKE ? OR term_data.name LIKE ?)', 'lat:%', 'lon:%') - .uniq + .where('type = "map" AND status = 1 AND (term_data.name LIKE ? OR term_data.name LIKE ?)', 'lat:%', 'lon:%') + .uniq # This is supposed to eager load the url_aliases, and seems to run, but doesn't actually eager load them...? # @maps = Node.select("node.*,url_alias.dst AS dst").joins(:tag).where('type = "map" AND status = 1 AND (term_data.name LIKE ? OR term_data.name LIKE ?)', 'lat:%', 'lon:%').joins("INNER JOIN url_alias ON url_alias.src = CONCAT('node/',node.nid)") @@ -56,10 +56,8 @@ def update @revision.title = params[:title] @revision.body = params[:body] - if params[:tags] - params[:tags].split(',').each do |tagname| + params[:tags]&.split(',').each do |tagname| @node.add_tag(tagname, current_user) - end end # save main image @@ -133,10 +131,8 @@ def create main_image: params[:main_image]) if saved - if params[:tags] - params[:tags].split(',').each do |tagname| + params[:tags]&.split(',').each do |tagname| @node.add_tag(tagname, current_user) - end end # save main image @@ -205,8 +201,8 @@ def tag @tagnames = params[:id].split(',') nids = Tag.find_nodes_by_type(params[:id], 'map', 20).collect(&:nid) @notes = Node.paginate(page: params[:page]) - .where('nid in (?)', nids) - .order('nid DESC') + .where('nid in (?)', nids) + .order('nid DESC') @title = @tagnames.join(', ') if @tagnames @unpaginated = true diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index 9ee90170d8..fb364ea179 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -1,6 +1,6 @@ class NotesController < ApplicationController respond_to :html - before_filter :require_user, only: %i[create edit update delete rsvp] + before_filter :require_user, only: %i(create edit update delete rsvp) def index @title = I18n.t('notes_controller.research_notes') @@ -16,14 +16,14 @@ def places @notes = Node.joins('LEFT OUTER JOIN node_revisions ON node_revisions.nid = node.nid LEFT OUTER JOIN community_tags ON community_tags.nid = node.nid LEFT OUTER JOIN term_data ON term_data.tid = community_tags.tid') - .select('*, max(node_revisions.timestamp)') - .where(status: 1, type:%w[page place]) - .includes(:revision, :tag) - .references(:term_data) - .where('term_data.name = ?', 'chapter') - .group('node.nid') - .order('max(node_revisions.timestamp) DESC, node.nid') - .paginate(page: params[:page], per_page: 24) + .select('*, max(node_revisions.timestamp)') + .where(status: 1, type:%w(page place)) + .includes(:revision, :tag) + .references(:term_data) + .where('term_data.name = ?', 'chapter') + .group('node.nid') + .order('max(node_revisions.timestamp) DESC, node.nid') + .paginate(page: params[:page], per_page: 24) render template: 'notes/tools_places' end @@ -52,6 +52,12 @@ def show @node = Node.find params[:id] end + if @node.status == 3 && (current_user.nil? || @node.author != current_user) + flash[:notice] = "Only author can access the draft note" + redirect_to '/' + return + end + if @node.has_power_tag('question') redirect_to @node.path(:question) return @@ -97,30 +103,33 @@ def create main_image: params[:main_image]) if saved - if params[:tags] - params[:tags].tr(' ', ',').split(',').each do |tagname| + params[:tags]&.tr(' ', ',').split(',').each do |tagname| @node.add_tag(tagname.strip, current_user) - end end if params[:event] == 'on' @node.add_tag('event', current_user) @node.add_tag('event:rsvp', current_user) @node.add_tag('date:' + params[:date], current_user) if params[:date] end - if current_user.first_time_poster - AdminMailer.notify_node_moderators(@node) - flash[:first_time_post] = true - if @node.has_power_tag('question') - flash[:notice] = I18n.t('notes_controller.thank_you_for_question').html_safe + if params[:draft] != true + if current_user.first_time_poster + AdminMailer.notify_node_moderators(@node) + flash[:first_time_post] = true + if @node.has_power_tag('question') + flash[:notice] = I18n.t('notes_controller.thank_you_for_question').html_safe + else + flash[:notice] = I18n.t('notes_controller.thank_you_for_contribution').html_safe + end else - flash[:notice] = I18n.t('notes_controller.thank_you_for_contribution').html_safe + if @node.has_power_tag('question') + flash[:notice] = I18n.t('notes_controller.question_note_published').html_safe + else + flash[:notice] = I18n.t('notes_controller.research_note_published').html_safe + end end else - if @node.has_power_tag('question') - flash[:notice] = I18n.t('notes_controller.question_note_published').html_safe - else - flash[:notice] = I18n.t('notes_controller.research_note_published').html_safe - end + @node.draft + flash[:notice] = I18n.t('notes_controller.saved_as_draft').html_safe end # Notice: Temporary redirect.Remove this condition after questions show page is complete. # Just keep @node.path(:question) @@ -136,7 +145,7 @@ def create else if request.xhr? # rich editor! errors = @node.errors - errors = errors.to_hash.merge(@revision.errors.to_hash) if @revision && @revision.errors + errors = errors.to_hash.merge(@revision.errors.to_hash) if @revision&.errors render json: errors else render template: 'editor/post' @@ -180,8 +189,9 @@ def update @revision = @node.latest @revision.title = params[:title] @revision.body = params[:body] + @revision.timestamp = Time.now.to_i if params[:tags] - params[:tags].tr(' ', ',').split(',').each do |tagname| + params[:tags]&.tr(' ', ',')&.split(',')&.each do |tagname| @node.add_tag(tagname, current_user) end end @@ -221,7 +231,7 @@ def update flash[:error] = I18n.t('notes_controller.edit_not_saved') if request.xhr? || params[:rich] errors = @node.errors - errors = errors.to_hash.merge(@revision.errors.to_hash) if @revision && @revision.errors + errors = errors.to_hash.merge(@revision.errors.to_hash) if @revision&.errors render json: errors else render 'editor/post' @@ -234,18 +244,23 @@ def update # only for notes def delete @node = Node.find(params[:id]) - if current_user.uid == @node.uid && @node.type == 'note' || current_user.role == 'admin' || current_user.role == 'moderator' - @node.delete - respond_with do |format| - format.html do - if request.xhr? - render text: I18n.t('notes_controller.content_deleted') - else - flash[:notice] = I18n.t('notes_controller.content_deleted') - redirect_to '/dashboard' + '?_=' + Time.now.to_i.to_s + if current_user && (current_user.uid == @node.uid || current_user.role == "moderator" || current_user.role == "admin") + if @node.authors.uniq.length == 1 + @node.destroy + respond_with do |format| + format.html do + if request.xhr? + render text: I18n.t('notes_controller.content_deleted') + else + flash[:notice] = I18n.t('notes_controller.content_deleted') + redirect_to '/dashboard' + '?_=' + Time.now.to_i.to_s + end end - end end + else + flash[:error] = I18n.t('notes_controller.more_than_one_contributor') + redirect_to '/dashboard' + '?_=' + Time.now.to_i.to_s + end else prompt_login end @@ -256,8 +271,8 @@ def author @user = DrupalUser.find_by(name: params[:id]) @title = @user.name @notes = Node.paginate(page: params[:page], per_page: 24) - .order('nid DESC') - .where(type: 'note', status: 1, uid: @user.uid) + .order('nid DESC') + .where(type: 'note', status: 1, uid: @user.uid) render template: 'notes/index' end @@ -275,13 +290,23 @@ def author_topic def liked @title = I18n.t('notes_controller.highly_liked_research_notes') @wikis = Node.limit(10) - .where(type: 'page', status: 1) - .order('nid DESC') + .where(type: 'page', status: 1) + .order('nid DESC') @notes = Node.research_notes - .where(status: 1) - .limit(20) - .order('nid DESC') + .where(status: 1) + .limit(20) + .order('nid DESC') + @unpaginated = true + render template: 'notes/index' + end + + def recent + @title = I18n.t('notes_controller.recent_research_notes') + @wikis = Node.limit(10) + .where(type: 'page', status: 1) + .order('nid DESC') + @notes = Node.where(type: 'note', status: 1, created: Time.now.to_i - 1.weeks.to_i..Time.now.to_i) @unpaginated = true render template: 'notes/index' end @@ -290,20 +315,20 @@ def liked def popular @title = I18n.t('notes_controller.popular_research_notes') @wikis = Node.limit(10) - .where(type: 'page', status: 1) - .order('nid DESC') + .where(type: 'page', status: 1) + .order('nid DESC') @notes = Node.research_notes - .limit(20) - .where(status: 1) - .order('views DESC') + .limit(20) + .where(status: 1) + .order('views DESC') @unpaginated = true render template: 'notes/index' end def rss @notes = Node.limit(20) - .order('nid DESC') - .where('type = ? AND status = 1 AND created < ?', 'note', (Time.now.to_i - 30.minutes.to_i)) + .order('nid DESC') + .where('type = ? AND status = 1 AND created < ?', 'note', (Time.now.to_i - 30.minutes.to_i)) respond_to do |format| format.rss do render layout: false @@ -315,8 +340,8 @@ def rss def liked_rss @notes = Node.limit(20) - .order('created DESC') - .where('type = ? AND status = 1 AND cached_likes > 0', 'note') + .order('created DESC') + .where('type = ? AND status = 1 AND cached_likes > 0', 'note') respond_to do |format| format.rss do render layout: false, template: 'notes/rss' diff --git a/app/controllers/openid_controller.rb b/app/controllers/openid_controller.rb index 0aacc6445d..74bf14e02e 100644 --- a/app/controllers/openid_controller.rb +++ b/app/controllers/openid_controller.rb @@ -42,7 +42,7 @@ def index if oidreq requested_username = '' - if request.env['ORIGINAL_FULLPATH'] && request.env['ORIGINAL_FULLPATH'].split('?')[1] + if request.env['ORIGINAL_FULLPATH']&.split('?')[1] request.env['ORIGINAL_FULLPATH'].split('?')[1].split('&').each do |param| requested_username = param.split('=')[1].split('%2F').last if param.split('=')[0] == 'openid.claimed_id' end @@ -130,7 +130,7 @@ def user_page # This is not technically correct, and should eventually be updated # to do real Accept header parsing and logic. Though I expect it will work # 99% of the time. - if accept && accept.include?('application/xrds+xml') + if accept&.include?('application/xrds+xml') user_xrds return end diff --git a/app/controllers/questions_controller.rb b/app/controllers/questions_controller.rb index a14cab0380..08479e2a92 100644 --- a/app/controllers/questions_controller.rb +++ b/app/controllers/questions_controller.rb @@ -6,9 +6,9 @@ def filter_questions_by_tag(questions, tagnames) tagnames = tagnames.split(',') nids = questions.collect(&:nid) questions = Node.where(status: 1, type: 'note') - .joins(:tag) - .where('node.nid IN (?)', nids) - .group('node.nid') + .joins(:tag) + .where('node.nid IN (?)', nids) + .group('node.nid') if !tagnames.empty? questions.where('term_data.name IN (?)', tagnames) else @@ -22,9 +22,9 @@ def index @title = 'Questions and Answers' set_sidebar @questions = Node.questions - .where(status: 1) - .order('node.nid DESC') - .paginate(page: params[:page], per_page: 24) + .where(status: 1) + .order('node.nid DESC') + .paginate(page: params[:page], per_page: 24) end # a form for new questions, at /questions/new @@ -62,8 +62,8 @@ def show @tags = @node.power_tag_objects('question') @tagnames = @tags.collect(&:name) @users = @node.answers.group(:uid) - .order('count(*) DESC') - .collect(&:author) + .order('count(*) DESC') + .collect(&:author) set_sidebar :tags, @tagnames end @@ -71,28 +71,28 @@ def show def answered @title = 'Recently answered' @questions = Node.questions - .where(status: 1) + .where(status: 1) @questions = filter_questions_by_tag(@questions, params[:tagnames]) - .joins(:answers) - .order('answers.created_at DESC') - .group('node.nid') - .paginate(page: params[:page], per_page: 24) + .joins(:answers) + .order('answers.created_at DESC') + .group('node.nid') + .paginate(page: params[:page], per_page: 24) @wikis = Node.limit(10) - .where(type: 'page', status: 1) - .order('nid DESC') + .where(type: 'page', status: 1) + .order('nid DESC') render template: 'questions/index' end def unanswered @title = 'Unanswered questions' @questions = Node.questions - .where(status: 1) - .includes(:answers) - .references(:answers) - .where(answers: { id: nil }) - .order('answers.created_at DESC') - .group('node.nid') - .paginate(page: params[:page], per_page: 24) + .where(status: 1) + .includes(:answers) + .references(:answers) + .where(answers: { id: nil }) + .order('node.nid DESC') + .group('node.nid') + .paginate(page: params[:page], per_page: 24) render template: 'questions/index' end @@ -108,14 +108,14 @@ def shortlink def popular @title = 'Popular Questions' @questions = Node.questions - .where(status: 1) + .where(status: 1) @questions = filter_questions_by_tag(@questions, params[:tagnames]) - .order('views DESC') - .limit(20) + .order('views DESC') + .limit(20) @wikis = Node.limit(10) - .where(type: 'page', status: 1) - .order('nid DESC') + .where(type: 'page', status: 1) + .order('nid DESC') @unpaginated = true render template: 'questions/index' end @@ -124,12 +124,12 @@ def liked @title = 'Highly liked Questions' @questions = Node.questions.where(status: 1) @questions = filter_questions_by_tag(@questions, params[:tagnames]) - .order('cached_likes DESC') - .limit(20) + .order('cached_likes DESC') + .limit(20) @wikis = Node.limit(10) - .where(type: 'page', status: 1) - .order('nid DESC') + .where(type: 'page', status: 1) + .order('nid DESC') @unpaginated = true render template: 'questions/index' end diff --git a/app/controllers/stats_controller.rb b/app/controllers/stats_controller.rb index 44abdf88f5..29ff2647eb 100644 --- a/app/controllers/stats_controller.rb +++ b/app/controllers/stats_controller.rb @@ -11,60 +11,60 @@ def subscriptions def range @start = params[:start] ? Time.parse(params[:start]) : Time.now - 1.month @end = params[:end] ? Time.parse(params[:end]) : Time.now - @notes = Node.select(%i[created type status]) - .where(type: 'note', status: 1, created: @start.to_i..@end.to_i) - .count(:all) + @notes = Node.select(%i(created type status)) + .where(type: 'note', status: 1, created: @start.to_i..@end.to_i) + .count(:all) @wikis = Revision.select(:timestamp) - .where(timestamp: @start.to_i..@end.to_i) - .count - @notes # because notes each have one revision + .where(timestamp: @start.to_i..@end.to_i) + .count - @notes # because notes each have one revision @people = User.where(created_at: @start..@end) - .joins('INNER JOIN users ON users.uid = rusers.id') - .where('users.status = 1') - .count + .joins('INNER JOIN users ON users.uid = rusers.id') + .where('users.status = 1') + .count @answers = Answer.where(created_at: @start..@end) - .count + .count @comments = Comment.select(:timestamp) - .where(timestamp: @start.to_i..@end.to_i) - .count + .where(timestamp: @start.to_i..@end.to_i) + .count @questions = Node.questions.where(status: 1, created: @start.to_i..@end.to_i) - .count + .count @contributors = User.contributor_count_for(@start,@end) end def index @time = if params[:time] Time.parse(params[:time]) - else - Time.now - end + else + Time.now + end - @weekly_notes = Node.select(%i[created type status]) - .where(type: 'note', status: 1, created: @time.to_i - 1.weeks.to_i..@time.to_i) - .count(:all) + @weekly_notes = Node.select(%i(created type status)) + .where(type: 'note', status: 1, created: @time.to_i - 1.weeks.to_i..@time.to_i) + .count(:all) @weekly_wikis = Revision.select(:timestamp) - .where(timestamp: @time.to_i - 1.weeks.to_i..@time.to_i) - .count + .where(timestamp: @time.to_i - 1.weeks.to_i..@time.to_i) + .count @weekly_members = User.where(created_at: @time - 1.weeks..@time) - .joins('INNER JOIN users ON users.uid = rusers.id') - .where('users.status = 1') - .count - @monthly_notes = Node.select(%i[created type status]) - .where(type: 'note', status: 1, created: @time.to_i - 1.months.to_i..@time.to_i) - .count(:all) + .joins('INNER JOIN users ON users.uid = rusers.id') + .where('users.status = 1') + .count + @monthly_notes = Node.select(%i(created type status)) + .where(type: 'note', status: 1, created: @time.to_i - 1.months.to_i..@time.to_i) + .count(:all) @monthly_wikis = Revision.select(:timestamp) - .where(timestamp: @time.to_i - 1.months.to_i..@time.to_i) - .count + .where(timestamp: @time.to_i - 1.months.to_i..@time.to_i) + .count @monthly_members = User.where(created_at: @time - 1.months..@time) - .joins('INNER JOIN users ON users.uid = rusers.id') - .where('users.status = 1') - .count + .joins('INNER JOIN users ON users.uid = rusers.id') + .where('users.status = 1') + .count - @notes_per_week_past_year = Node.select(%i[created type status]) - .where(type: 'note', status: 1, created: @time.to_i - 1.years.to_i..@time.to_i) - .count(:all) / 52.0 + @notes_per_week_past_year = Node.select(%i(created type status)) + .where(type: 'note', status: 1, created: @time.to_i - 1.years.to_i..@time.to_i) + .count(:all) / 52.0 @edits_per_week_past_year = Revision.select(:timestamp) - .where(timestamp: @time.to_i - 1.years.to_i..@time.to_i) - .count / 52.0 + .where(timestamp: @time.to_i - 1.years.to_i..@time.to_i) + .count / 52.0 @graph_notes = Node.weekly_tallies('note', 52, @time).to_a.sort.to_json @graph_wikis = Node.weekly_tallies('page', 52, @time).to_a.sort.to_json diff --git a/app/controllers/subscription_controller.rb b/app/controllers/subscription_controller.rb index e5cbe44f5d..01c321c1a8 100644 --- a/app/controllers/subscription_controller.rb +++ b/app/controllers/subscription_controller.rb @@ -5,7 +5,7 @@ class SubscriptionController < ApplicationController respond_to :html, :xml, :json - before_filter :require_user, :only => [:create, :delete, :index] + before_filter :require_user, :only => [:create, :delete, :index, :digest] def index @title = "Subscriptions" @@ -53,7 +53,7 @@ def add end # test for uniqueness, handle it as a validation error if you like - if TagSelection.where(following: true, user_id: current_user.uid, tid: tag.tid).length > 0 + if TagSelection.where(following: true, user_id: current_user.uid, tid: tag.tid).length.positive? flash[:error] = "You are already subscribed to '#{params[:name]}'" redirect_to "/subscriptions" + "?_=" + Time.now.to_i.to_s else @@ -110,6 +110,14 @@ def delete end end + def digest + @wikis = current_user.content_followed_in_period(Time.now - 1.week, Time.now) + .paginate(page: params[:page], per_page: 100) + + @paginated = true + render :template => "subscriptions/digest" + end + private def set_following(value,type,id) diff --git a/app/controllers/tag_controller.rb b/app/controllers/tag_controller.rb index cf439351c8..bbde1a2cdb 100644 --- a/app/controllers/tag_controller.rb +++ b/app/controllers/tag_controller.rb @@ -1,12 +1,12 @@ class TagController < ApplicationController respond_to :html, :xml, :json, :ics - before_filter :require_user, only: %i[create delete] + before_filter :require_user, only: %i(create delete) def index - if params[:format] - @toggle = params[:format].to_i + if params[:sort] + @toggle = params[:sort] else - @toggle = 1 + @toggle = "uses" end @title = I18n.t('tag_controller.tags') @@ -14,36 +14,36 @@ def index if params[:search] prefix = params[:search] @tags = Tag.joins(:node_tag, :node) - .select('node.nid, node.status, term_data.*, community_tags.*') - .where('node.status = ?', 1) - .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) - .where("name LIKE :prefix", prefix: "#{prefix}%") - .group(:name) - .order('count DESC') - .paginate(page: params[:page], per_page: 24) - elsif @toggle == 1 + .select('node.nid, node.status, term_data.*, community_tags.*') + .where('node.status = ?', 1) + .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) + .where("name LIKE :prefix", prefix: "#{prefix}%") + .group(:name) + .order('count DESC') + .paginate(page: params[:page], per_page: 24) + elsif @toggle == "uses" @tags = Tag.joins(:node_tag, :node) - .select('node.nid, node.status, term_data.*, community_tags.*') - .where('node.status = ?', 1) - .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) - .group(:name) - .order('count DESC') - .paginate(page: params[:page], per_page: 24) - elsif @toggle == 2 + .select('node.nid, node.status, term_data.*, community_tags.*') + .where('node.status = ?', 1) + .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) + .group(:name) + .order('count DESC') + .paginate(page: params[:page], per_page: 24) + elsif @toggle == "name" @tags = Tag.joins(:node_tag, :node) - .select('node.nid, node.status, term_data.*, community_tags.*') - .where('node.status = ?', 1) - .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) - .group(:name) - .order('name') - .paginate(page: params[:page], per_page: 24) + .select('node.nid, node.status, term_data.*, community_tags.*') + .where('node.status = ?', 1) + .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) + .group(:name) + .order('name') + .paginate(page: params[:page], per_page: 24) else tags = Tag.joins(:node_tag, :node) - .select('node.nid, node.status, term_data.*, community_tags.*') - .where('node.status = ?', 1) - .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) - .group(:name) - .order('name') + .select('node.nid, node.status, term_data.*, community_tags.*') + .where('node.status = ?', 1) + .where('community_tags.date > ?', (DateTime.now - 1.month).to_i) + .group(:name) + .order('name') followed = [] not_followed = [] @@ -56,18 +56,18 @@ def index end ids = followed + not_followed - @tags = Tag.where(tid: ids).sort_by{|p| ids.index(p.tid) }.paginate(page: params[:page], per_page: 24) + @tags = Tag.where(tid: ids).sort_by { |p| ids.index(p.tid) }.paginate(page: params[:page], per_page: 24) end end def show # try for a matching /wiki/_TAGNAME_ or /_TAGNAME_ @wiki = Node.where(path: "/wiki/#{params[:id]}").try(:first) || Node.where(path: "/#{params[:id]}").try(:first) - @wiki = Node.find(@wiki.power_tag('redirect')) if @wiki && @wiki.has_power_tag('redirect') # use a redirected wiki page if it exists + @wiki = Node.find(@wiki.power_tag('redirect')) if @wiki&.has_power_tag('redirect') # use a redirected wiki page if it exists default_type = if params[:id].match('question:') 'questions' - else + else 'note' end # params[:node_type] - this is an optional param @@ -83,19 +83,19 @@ def show @wildcard = true @tags = Tag.where('name LIKE (?)', params[:id][0..-2] + '%') nodes = Node.where(status: 1, type: node_type) - .includes(:revision, :tag) - .references(:term_data, :node_revisions) - .where('term_data.name LIKE (?) OR term_data.parent LIKE (?)', params[:id][0..-2] + '%', params[:id][0..-2] + '%') - .paginate(page: params[:page], per_page: 24) - .order('node_revisions.timestamp DESC') + .includes(:revision, :tag, :answers) + .references(:term_data, :node_revisions) + .where('term_data.name LIKE (?) OR term_data.parent LIKE (?)', params[:id][0..-2] + '%', params[:id][0..-2] + '%') + .paginate(page: params[:page], per_page: 24) + .order('node_revisions.timestamp DESC') else @tags = Tag.where(name: params[:id]) nodes = Node.where(status: 1, type: node_type) - .includes(:revision, :tag) - .references(:term_data, :node_revisions) - .where('term_data.name = ? OR term_data.parent = ?', params[:id], params[:id]) - .paginate(page: params[:page], per_page: 24) - .order('node_revisions.timestamp DESC') + .includes(:revision, :tag) + .references(:term_data, :node_revisions) + .where('term_data.name = ? OR term_data.parent = ?', params[:id], params[:id]) + .paginate(page: params[:page], per_page: 24) + .order('node_revisions.timestamp DESC') end # breaks the parameter @@ -104,20 +104,24 @@ def show @notes = nodes.where('node.nid NOT IN (?)', qids) if @node_type == 'note' @questions = nodes.where('node.nid IN (?)', qids) if @node_type == 'questions' + @answered_questions = [] + if @questions + @questions.each { |question| @answered_questions << question if question.answers.any? { |answer| answer.accepted } } + end @wikis = nodes if @node_type == 'wiki' @nodes = nodes if @node_type == 'maps' @title = params[:id] # the following could be refactored into a Tag.contributor_count method: notes = Node.where(status: 1, type: 'note') - .select('node.nid, node.type, node.uid, node.status, term_data.*, community_tags.*') - .includes(:tag) - .references(:term_data) - .where('term_data.name = ?', params[:id]) + .select('node.nid, node.type, node.uid, node.status, term_data.*, community_tags.*') + .includes(:tag) + .references(:term_data) + .where('term_data.name = ?', params[:id]) @length = Tag.contributor_count(params[:id]) || 0 @tagnames = [params[:id]] @tag = Tag.find_by(name: params[:id]) - @noteCount = Tag.taggedNodeCount(params[:id]) || 0 + @note_count = Tag.tagged_node_count(params[:id]) || 0 @users = Tag.contributors(@tagnames[0]) respond_with(nodes) do |format| @@ -126,7 +130,7 @@ def show format.json do json = [] nodes.each do |node| - json << node.as_json(except: %i[path tags]) + json << node.as_json(except: %i(path tags)) json.last['path'] = 'https://' + request.host.to_s + node.path json.last['preview'] = node.body_preview(500) json.last['image'] = node.main_image.path(:large) if node.main_image @@ -138,45 +142,57 @@ def show end def show_for_author + # try for a matching /wiki/_TAGNAME_ or /_TAGNAME_ @wiki = Node.where(path: "/wiki/#{params[:id]}").try(:first) || Node.where(path: "/#{params[:id]}").try(:first) - @wiki = Node.find(@wiki.power_tag('redirect')) if @wiki && @wiki.has_power_tag('redirect') - if params[:id][-1..-1] == '*' # wildcard tags - @wildcard = true - @tags = Tag.where('name LIKE (?)', params[:id][0..-2] + '%') - else - @tags = Tag.where(name: params[:id]) - end - @tagname = params[:id] + @wiki = Node.find(@wiki.power_tag('redirect')) if @wiki&.has_power_tag('redirect') + default_type = if params[:id].match('question:') 'questions' - else + else 'note' end # params[:node_type] - this is an optional param # if params[:node_type] is nil - use @default_type @node_type = params[:node_type] || default_type - @user = User.find_by(name: params[:author]) - @title = "'" + @tagname.to_s + "' by " + params[:author] + node_type = 'note' if @node_type == 'questions' || @node_type == 'note' + node_type = 'page' if @node_type == 'wiki' + node_type = 'map' if @node_type == 'maps' qids = Node.questions.where(status: 1).collect(&:nid) + + if params[:id][-1..-1] == '*' # wildcard tags + @wildcard = true + @tags = Tag.where('name LIKE (?)', params[:id][0..-2] + '%') + else + @tags = Tag.where(name: params[:id]) + end + @tagname = params[:id] + @user = User.find_by(name: params[:author]) + nodes = Tag.tagged_nodes_by_author(@tagname, @user) - .paginate(page: params[:page], per_page: 24) + .where(status: 1, type: node_type) + .paginate(page: params[:page], per_page: 24) + + # breaks the parameter + # sets everything to an empty array + set_sidebar :tags, [params[:id]] @notes = nodes.where('node.nid NOT IN (?)', qids) if @node_type == 'note' - @unpaginated = true - node_type = 'note' if @node_type == 'questions' || @node_type == 'note' - node_type = 'page' if @node_type == 'wiki' - node_type = 'map' if @node_type == 'maps' @questions = nodes.where('node.nid IN (?)', qids) if @node_type == 'questions' + ans_ques = Answer.where(uid: @user.id, accepted: true).includes(:node).map do |ans| + ans.node + end + @answered_questions = ans_ques.paginate(page: params[:page], per_page: 24) @wikis = nodes if @node_type == 'wiki' @nodes = nodes if @node_type == 'maps' + @title = "'" + @tagname.to_s + "' by " + params[:author] # the following could be refactored into a Tag.contributor_count method: notes = Node.where(status: 1, type: 'note') - .select('node.nid, node.type, node.uid, node.status, term_data.*, community_tags.*') - .includes(:tag) - .references(:term_data) - .where('term_data.name = ?', params[:id]) + .select('node.nid, node.type, node.uid, node.status, term_data.*, community_tags.*') + .includes(:tag) + .references(:term_data) + .where('term_data.name = ?', params[:id]) @length = Tag.contributor_count(params[:id]) || 0 respond_with(nodes) do |format| format.html { render 'tag/show' } @@ -184,9 +200,9 @@ def show_for_author format.json do json = [] nodes.each do |node| - json << node.as_json(except: %i[path tags]) - json.last['path'] = 'https://' + request.host. - to_s + node.path + json << node.as_json(except: %i(path tags)) + json.last['path'] = 'https://' + request.host + .to_s + node.path json.last['preview'] = node.body_preview(500) json.last['image'] = node.main_image.path(:large) if node.main_image json.last['tags'] = Node.find(node.id).tags.collect(&:name) if node.tags @@ -194,23 +210,23 @@ def show_for_author render json: json end end -end + end def widget num = params[:n] || 4 nids = Tag.find_nodes_by_type(params[:id], 'note', num).collect(&:nid) @notes = Node.paginate(page: params[:page], per_page: 24) - .where('status = 1 AND nid in (?)', nids) - .order('nid DESC') + .where('status = 1 AND nid in (?)', nids) + .order('nid DESC') render layout: false end def blog nids = Tag.find_nodes_by_type(params[:id], 'note', 20).collect(&:nid) @notes = Node.paginate(page: params[:page], per_page: 6) - .where('status = 1 AND nid in (?)', nids) - .order('nid DESC') + .where('status = 1 AND nid in (?)', nids) + .order('nid DESC') @tags = Tag.where(name: params[:id]) @tagnames = @tags.collect(&:name).uniq! || [] @title = @tagnames.join(',') + ' Blog' if @tagnames @@ -256,6 +272,22 @@ def create @output[:errors] << I18n.t('tag_controller.tag_already_exists') elsif node.can_tag(tagname, current_user) === true || current_user.role == 'admin' # || current_user.role == "moderator" saved, tag = node.add_tag(tagname.strip, current_user) + if tagname.split(':')[0] == "barnstar" + CommentMailer.notify_barnstar(current_user, node) + barnstar_info_link = 'barnstar' + node.add_comment(subject: 'barnstar', + uid: current_user.uid, + body: "@#{current_user.username} awards a #{barnstar_info_link} to #{node.drupal_user.name} for their awesome contribution!") + + elsif tagname.split(':')[0] == "with" + user = User.find_by_username_case_insensitive(tagname.split(':')[1]) + CommentMailer.notify_coauthor(user, node) + node.add_comment(subject: 'co-author', + uid: current_user.uid, + body: " @#{current_user.username} has marked #{tagname.split(':')[1]} as a co-author. ") + + end + if saved @tags << tag @output[:saved] << [tag.name, tag.id] @@ -273,8 +305,8 @@ def create render json: @output else flash[:notice] = I18n.t('tag_controller.tags_created_error', - tag_count: @output[:saved].length, - error_count: @output[:errors].length).html_safe + tag_count: @output[:saved].length, + error_count: @output[:errors].length).html_safe redirect_to node.path end end @@ -306,14 +338,14 @@ def delete end def suggested - if params[:id].length > 2 + if !params[:id].empty? && params[:id].length > 2 @suggestions = [] # filtering out tag spam by requiring tags attached to a published node Tag.where('name LIKE ?', '%' + params[:id] + '%') - .includes(:node) - .references(:node) - .where('node.status = 1') - .limit(10).each do |tag| + .includes(:node) + .references(:node) + .where('node.status = 1') + .limit(10).each do |tag| @suggestions << tag.name.downcase end render json: @suggestions.uniq @@ -325,11 +357,11 @@ def suggested def rss if params[:tagname][-1..-1] == '*' @notes = Node.where(status: 1, type: 'note') - .includes(:revision, :tag) - .references(:term_data, :node_revisions) - .where('term_data.name LIKE (?)', params[:tagname][0..-2] + '%') - .limit(20) - .order('node_revisions.timestamp DESC') + .includes(:revision, :tag) + .references(:term_data, :node_revisions) + .where('term_data.name LIKE (?)', params[:tagname][0..-2] + '%') + .limit(20) + .order('node_revisions.timestamp DESC') else @notes = Tag.find_nodes_by_type([params[:tagname]], 'note', 20) end @@ -347,11 +379,30 @@ def rss end end + def rss_for_tagged_with_author + @user = User.find_by(name: params[:authorname]) + @notes = Tag.tagged_nodes_by_author(params[:tagname], @user) + .where(status: 1) + .limit(20) + respond_to do |format| + format.rss do + response.headers['Content-Type'] = 'application/xml; charset=utf-8' + response.headers['Access-Control-Allow-Origin'] = '*' + render layout: false + end + format.ics do + response.headers['Content-Disposition'] = "attachment; filename='public-lab-events.ics'" + response.headers['Content-Type'] = 'text/calendar; charset=utf-8' + render layout: false, template: 'tag/icalendar.ics', filename: 'public-lab-events.ics' + end + end + end + def contributors set_sidebar :tags, [params[:id]], note_count: 20 @tagnames = [params[:id]] @tag = Tag.find_by(name: params[:id]) - @noteCount = Tag.taggedNodeCount(params[:id]) || 0 + @note_count = Tag.tagged_node_count(params[:id]) || 0 @users = Tag.contributors(@tagnames[0]) end diff --git a/app/controllers/user_sessions_controller.rb b/app/controllers/user_sessions_controller.rb index a9cecefdcd..47174a1abf 100644 --- a/app/controllers/user_sessions_controller.rb +++ b/app/controllers/user_sessions_controller.rb @@ -1,4 +1,6 @@ class UserSessionsController < ApplicationController + + before_action :require_no_user, :only => [:new] def new @title = I18n.t('user_sessions_controller.log_in') end @@ -14,10 +16,10 @@ def create params[:user_session][:username] = @user.username end - if params[:user_session].nil? || @user && @user.drupal_user.status == 1 || @user.nil? + if params[:user_session].nil? || @user&.drupal_user.status == 1 || @user.nil? # an existing native user if params[:user_session].nil? || @user - if @user && @user.crypted_password.nil? # the user has not created a pwd in the new site + if @user&.crypted_password.nil? # the user has not created a pwd in the new site params[:user_session][:openid_identifier] = 'https://old.publiclab.org/people/' + username + '/identity' if username params[:user_session].delete(:password) params[:user_session].delete(:username) @@ -60,7 +62,7 @@ def create redirect_to '/signup' end end - elsif params[:user_session].nil? || @user && @user.drupal_user.status == 5 || @user.nil? + elsif params[:user_session].nil? || @user&.drupal_user.status == 5 || @user.nil? flash[:error] = I18n.t('user_sessions_controller.user_has_been_moderated', username: @user.username).html_safe redirect_to '/' else diff --git a/app/controllers/user_tags_controller.rb b/app/controllers/user_tags_controller.rb index b724ad4bf8..960bf02b78 100644 --- a/app/controllers/user_tags_controller.rb +++ b/app/controllers/user_tags_controller.rb @@ -45,7 +45,7 @@ def create else flash[:notice] = I18n.t('user_tags_controller.tag_created', tag_name: @output[:saved][0][0]).html_safe end - redirect_to info_path, id: params[:id] + redirect_to '/profile/' + user.username end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index e856d05058..f1d3db2277 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -10,10 +10,11 @@ def new def create @user = User.new(params[:user]) + @user.status = 1 using_recaptcha = !params[:spamaway] && Rails.env == "production" recaptcha = verify_recaptcha(model: @user) if using_recaptcha @spamaway = Spamaway.new(params[:spamaway]) unless using_recaptcha - if ((@spamaway && @spamaway.valid?) || recaptcha) && @user.save({}) + if ((@spamaway&.valid?) || recaptcha) && @user.save({}) if current_user.crypted_password.nil? # the user has not created a pwd in the new site flash[:warning] = I18n.t('users_controller.account_migrated_create_new_password') redirect_to "/profile/edit" @@ -81,19 +82,36 @@ def edit end def list + sort_param = params[:sort] + + if params[:id] + order_string = 'updated_at DESC' + else + order_string = 'last_updated DESC' + end + + if sort_param == 'username' + order_string = 'name ASC' + elsif sort_param == 'last_activity' + order_string = 'last_updated DESC' + elsif sort_param == 'joined' + order_string = 'created DESC' + end + # allow admins to view recent users if params[:id] @users = DrupalUser.joins('INNER JOIN rusers ON rusers.username = users.name') - .order("updated_at DESC") + .order(order_string) .where('rusers.role = ?', params[:id]) + .where('rusers.status = 1') .page(params[:page]) else # recently active @users = DrupalUser.select('*, MAX(node.changed) AS last_updated') .joins(:node) .group('users.uid') - .where('users.status = 1 AND node.status = 1') - .order("last_updated DESC") + .where('node.status = 1') + .order(order_string) .page(params[:page]) end @users = @users.where('users.status = 1') unless current_user && (current_user.role == "admin" || current_user.role == "moderator") @@ -107,41 +125,43 @@ def profile @profile_user = User.find_by(username: params[:id]) @title = @user.name @notes = Node.research_notes - .paginate(page: params[:page], per_page: 24) - .order("nid DESC") - .where(status: 1, uid: @user.uid) + .paginate(page: params[:page], per_page: 24) + .order("nid DESC") + .where(status: 1, uid: @user.uid) @coauthored = @profile_user.coauthored_notes .paginate(page: params[:page], per_page: 24) .order('node_revisions.timestamp DESC') @questions = @user.user.questions .order('node.nid DESC') .paginate(:page => params[:page], :per_page => 24) + @likes = (@user.liked_notes.includes([:tag, :comments])+@user.liked_pages) + .paginate(page: params[:page], per_page: 24) questions = Node.questions .where(status: 1) .order('node.nid DESC') - @answered_questions = questions.select{|q| q.answers.collect(&:author).include?(@user)} + ans_ques = questions.select{|q| q.answers.collect(&:author).include?(@user)} + @answered_questions = ans_ques.paginate(page: params[:page], per_page: 24) wikis = Revision.order("nid DESC") .where('node.type' => 'page', 'node.status' => 1, uid: @user.uid) .joins(:node) .limit(20) @wikis = wikis.collect(&:parent).uniq + @comment_count = Comment.where(status: 0, uid: @user.uid).count + # User's social links @github = @profile_user.social_link("github") @twitter = @profile_user.social_link("twitter") @facebook = @profile_user.social_link("facebook") @instagram = @profile_user.social_link("instagram") - - + @count_activities_posted = Tag.tagged_nodes_by_author("activity:*", @user).count + @count_activities_attempted = Tag.tagged_nodes_by_author("replication:*", @user).count @map_lat = nil - @map_lon = nil - @map_blurred = nil - if(@profile_user.has_power_tag("lat") && @profile_user.has_power_tag("lon")) - @map_lat = @profile_user.get_value_of_power_tag("lat").to_f - @map_lon = @profile_user.get_value_of_power_tag("lon").to_f - if(@profile_user.has_power_tag("blurred")) - @map_blurred = @profile_user.get_value_of_power_tag("blurred") - end + @map_lon = nil + if @profile_user.has_power_tag("lat") && @profile_user.has_power_tag("lon") + @map_lat = @profile_user.get_value_of_power_tag("lat").to_f + @map_lon = @profile_user.get_value_of_power_tag("lon").to_f + @map_blurred = @profile_user.has_tag("blurred:true") end if @user.status == 0 @@ -261,7 +281,7 @@ def photo def info @user = User.find_by(username: params[:id]) end - + # content this person follows def followed user = User.find_by(username: params[:id]) diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index fe6a0d8bdb..50fbba2249 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -1,7 +1,7 @@ require 'rss' class WikiController < ApplicationController - before_filter :require_user, only: %i[new create edit update delete replace] + before_filter :require_user, only: %i(new create edit update delete replace) def subdomain url = "//#{request.host}/wiki/" @@ -21,11 +21,11 @@ def subdomain def show @node = if params[:lang] Node.find_wiki(params[:lang] + '/' + params[:id]) - else - Node.find_wiki(params[:id]) - end + else + Node.find_wiki(params[:id]) + end - if @node && @node.has_power_tag('redirect') && Node.where(nid: @node.power_tag('redirect')).exists? + if @node&.has_power_tag('redirect') && Node.where(nid: @node.power_tag('redirect')).exists? if current_user.nil? || (current_user.role != 'admin' && current_user.role != 'moderator') redirect_to Node.find(@node.power_tag('redirect')).path return @@ -35,7 +35,7 @@ def show end end - if @node && @node.has_power_tag('abtest') && !Node.where(nid: @node.power_tag('abtest')).empty? + if @node&.has_power_tag('abtest') && !Node.where(nid: @node.power_tag('abtest')).empty? if current_user.nil? || (current_user.role != 'admin' && current_user.role != 'moderator') if Random.rand(2) == 0 redirect_to Node.find(@node.power_tag('abtest')).path @@ -90,9 +90,10 @@ def raw def edit @node = if params[:lang] Node.find_wiki(params[:lang] + '/' + params[:id]) - else - Node.find_wiki(params[:id]) - end + else + Node.find_wiki(params[:id]) + end + if @node.has_tag('locked') && (current_user.role != 'admin' && current_user.role != 'moderator') flash[:warning] = "This page is locked, and only moderators can edit it." redirect_to @node.path @@ -112,17 +113,17 @@ def new @node = Node.new if params[:n] && !params[:body] # use another node body as a template node = Node.find(params[:n]) - params[:body] = node.latest.body if node && node.latest + params[:body] = node.latest.body if node&.latest end @tags = [] if params[:id] flash.now[:notice] = I18n.t('wiki_controller.page_does_not_exist_create') title = params[:id].tr('-', ' ') @related = Node.limit(10) - .order('node.nid DESC') - .where('type = "page" AND node.status = 1 AND (node.title LIKE ? OR node_revisions.body LIKE ?)', '%' + title + '%', '%' + title + '%') - .includes(:revision) - .references(:node_revisions) + .order('node.nid DESC') + .where('type = "page" AND node.status = 1 AND (node.title LIKE ? OR node_revisions.body LIKE ?)', '%' + title + '%', '%' + title + '%') + .includes(:revision) + .references(:node_revisions) tag = Tag.find_by(name: params[:id]) # add page name as a tag, too @tags << tag if tag @related += Tag.find_nodes_by_type(@tags.collect(&:name), 'page', 10) @@ -292,16 +293,16 @@ def index order_string = if params[:order] == 'alphabetic' 'node_revisions.title ASC' - else - 'node_revisions.timestamp DESC' - end + else + 'node_revisions.timestamp DESC' + end @wikis = Node.includes(:revision) - .references(:node_revisions) - .group('node_revisions.nid') - .order(order_string) - .where("node_revisions.status = 1 AND node.status = 1 AND (type = 'page' OR type = 'tool' OR type = 'place')") - .page(params[:page]) + .references(:node_revisions) + .group('node_revisions.nid') + .order(order_string) + .where("node_revisions.status = 1 AND node.status = 1 AND (type = 'page' OR type = 'tool' OR type = 'place')") + .page(params[:page]) @paginated = true end @@ -310,11 +311,11 @@ def stale @title = I18n.t('wiki_controller.wiki') @wikis = Node.includes(:revision) - .references(:node_revisions) - .group('node_revisions.nid') - .order('node_revisions.timestamp ASC') - .where("node_revisions.status = 1 AND node.status = 1 AND (type = 'page' OR type = 'tool' OR type = 'place')") - .page(params[:page]) + .references(:node_revisions) + .group('node_revisions.nid') + .order('node_revisions.timestamp ASC') + .where("node_revisions.status = 1 AND node.status = 1 AND (type = 'page' OR type = 'tool' OR type = 'place')") + .page(params[:page]) @paginated = true render template: 'wiki/index' @@ -323,19 +324,20 @@ def stale def popular @title = I18n.t('wiki_controller.popular_wiki_pages') @wikis = Node.limit(40) - .joins(:revision) - .group('node_revisions.nid') - .order('node_revisions.timestamp DESC') - .where("node.status = 1 AND node_revisions.status = 1 AND node.nid != 259 AND (type = 'page' OR type = 'tool' OR type = 'place')") - .sort_by(&:totalviews).reverse + .joins(:revision) + .group('node_revisions.nid') + .order('node_revisions.timestamp DESC') + .where("node.status = 1 AND node_revisions.status = 1 AND node.nid != 259 AND (type = 'page' OR type = 'tool' OR type = 'place')") + .sort_by(&:totalviews).reverse render template: 'wiki/index' end def liked @title = I18n.t('wiki_controller.well_liked_wiki_pages') @wikis = Node.limit(40) - .order('node.cached_likes DESC') - .where("status = 1 AND nid != 259 AND (type = 'page' OR type = 'tool' OR type = 'place') AND cached_likes > 0") + .order('node.cached_likes DESC') + .where("status = 1 AND nid != 259 AND (type = 'page' OR type = 'tool' OR type = 'place') AND cached_likes >= 0") + render template: 'wiki/index' end @@ -368,36 +370,36 @@ def techniques def methods @nodes = Node.where(status: 1, type: ['page']) - .where('term_data.name = ?', 'method') - .includes(:revision, :tag) - .references(:node_revision) - .order('node_revisions.timestamp DESC') + .where('term_data.name = ?', 'method') + .includes(:revision, :tag) + .references(:node_revision) + .order('node_revisions.timestamp DESC') # deprecating the following in favor of javascript implementation in /app/assets/javascripts/methods.js if params[:topic] nids = @nodes.collect(&:nid) || [] @notes = Node.where(status: 1, type: ['page']) - .where('node.nid IN (?)', nids) - .where('(type = "note" OR type = "page" OR type = "map") AND node.status = 1 AND (node.title LIKE ? OR node_revisions.title LIKE ? OR node_revisions.body LIKE ? OR term_data.name = ?)', - '%' + params[:topic] + '%', - '%' + params[:topic] + '%', - '%' + params[:topic] + '%', - params[:topic]) - .includes(:revision, :tag) - .references(:node_revision, :term_data) - .order('node_revisions.timestamp DESC') + .where('node.nid IN (?)', nids) + .where('(type = "note" OR type = "page" OR type = "map") AND node.status = 1 AND (node.title LIKE ? OR node_revisions.title LIKE ? OR node_revisions.body LIKE ? OR term_data.name = ?)', + '%' + params[:topic] + '%', + '%' + params[:topic] + '%', + '%' + params[:topic] + '%', + params[:topic]) + .includes(:revision, :tag) + .references(:node_revision, :term_data) + .order('node_revisions.timestamp DESC') end if params[:topic] nids = @nodes.collect(&:nid) || [] @nodes = Node.where(status: 1, type: ['page']) - .where('node.nid IN (?)', nids) - .where('(type = "note" OR type = "page" OR type = "map") AND node.status = 1 AND (node.title LIKE ? OR node_revisions.title LIKE ? OR node_revisions.body LIKE ? OR term_data.name = ?)', - '%' + params[:topic] + '%', - '%' + params[:topic] + '%', - '%' + params[:topic] + '%', - params[:topic]) - .includes(:revision, :tag) - .references(:node_revision, :term_data) - .order('node_revisions.timestamp DESC') + .where('node.nid IN (?)', nids) + .where('(type = "note" OR type = "page" OR type = "map") AND node.status = 1 AND (node.title LIKE ? OR node_revisions.title LIKE ? OR node_revisions.body LIKE ? OR term_data.name = ?)', + '%' + params[:topic] + '%', + '%' + params[:topic] + '%', + '%' + params[:topic] + '%', + params[:topic]) + .includes(:revision, :tag) + .references(:node_revision, :term_data) + .order('node_revisions.timestamp DESC') end @unpaginated = true diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 14d93e4f7d..59afa3d1a3 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -34,8 +34,8 @@ def insert_extras(body) body = NodeShared.upgrades_grid(body) body = NodeShared.notes_map(body) body = NodeShared.notes_map_by_tag(body) - body = NodeShared.people_grid(body) body = NodeShared.people_map(body) + body = NodeShared.people_grid(body, @current_user || nil) # <= to allow testing of insert_extras body = NodeShared.graph_grid(body) body = NodeShared.wikis_grid(body) body @@ -47,13 +47,13 @@ def render_map(lat, lon, items) # returns the comment body which is to be shown in the comments section def render_comment_body(comment) - raw sanitize (RDiscount.new(title_suggestion(comment)).to_html), attributes: %w(class style href data-method src) + raw sanitize RDiscount.new(title_suggestion(comment)).to_html, attributes: %w(class style href data-method src) end # replaces inline title suggestion(e.g: {New Title}) with the required link to change the title def title_suggestion(comment) - comment.body.gsub(/\[propose:title](.*?)\[\/propose]/) do |title_suggestion| - a = ActionController::Base.new() + comment.body.gsub(/\[propose:title\](.*?)\[\/propose\]/) do || + a = ActionController::Base.new is_creator = current_user.drupal_user == Node.find(comment.nid).author title = Regexp.last_match(1) output = a.render_to_string(template: "notes/_title_suggestion", @@ -62,9 +62,8 @@ def title_suggestion(comment) user: comment.drupal_user.name, nid: comment.nid, title: title, - is_creator: is_creator, - } - ) + is_creator: is_creator + }) output end end diff --git a/app/helpers/user_tags_helper.rb b/app/helpers/user_tags_helper.rb index 0a07d11ae6..e15e57f3bb 100644 --- a/app/helpers/user_tags_helper.rb +++ b/app/helpers/user_tags_helper.rb @@ -1,6 +1,6 @@ module UserTagsHelper def fetch_tags(uid, type) - tag_types = %w[skill gear role tool] + tag_types = %w(skill gear role tool) tags = [] if tag_types.include? type tags = UserTag.where(uid: uid).where('value LIKE ?', type + ':' + '%') diff --git a/app/mailers/admin_mailer.rb b/app/mailers/admin_mailer.rb index 871a21501b..8bb900256e 100644 --- a/app/mailers/admin_mailer.rb +++ b/app/mailers/admin_mailer.rb @@ -8,7 +8,7 @@ def notify_node_moderators(node) @node = node @user = node.author.user @footer = feature('email-footer') - moderators = User.where(role: %w[moderator admin]).collect(&:email) + moderators = User.where(role: %w(moderator admin)).collect(&:email) mail( to: "moderators@#{ActionMailer::Base.default_url_options[:host]}", bcc: moderators, @@ -38,7 +38,7 @@ def notify_moderators_of_approval(node, moderator) @moderator = moderator @node = node @footer = feature('email-footer') - moderators = User.where(role: %w[moderator admin]).collect(&:email) + moderators = User.where(role: %w(moderator admin)).collect(&:email) mail( to: "moderators@#{ActionMailer::Base.default_url_options[:host]}", bcc: moderators, @@ -52,7 +52,7 @@ def notify_moderators_of_spam(node, moderator) @moderator = moderator @node = node @footer = feature('email-footer') - moderators = User.where(role: %w[moderator admin]).collect(&:email) + moderators = User.where(role: %w(moderator admin)).collect(&:email) mail( to: "moderators@#{ActionMailer::Base.default_url_options[:host]}", bcc: moderators, diff --git a/app/mailers/comment_mailer.rb b/app/mailers/comment_mailer.rb index 97b1a4bde4..74b30e7275 100644 --- a/app/mailers/comment_mailer.rb +++ b/app/mailers/comment_mailer.rb @@ -46,4 +46,12 @@ def notify_answer_author(user, comment) @footer = feature('email-footer') mail(to: user.email, subject: "New comment on your answer on '" + comment.parent.title + "'") end + + def notify_coauthor(user, note) + @user = user + @note = note + @author = note.author + @footer = feature('email-footer') + mail(to: user.email, subject: 'You were added as a co-author!').deliver + end end diff --git a/app/models/answer.rb b/app/models/answer.rb index b42161a212..ef12ceaa0d 100644 --- a/app/models/answer.rb +++ b/app/models/answer.rb @@ -3,10 +3,10 @@ class Answer < ActiveRecord::Base attr_accessible :uid, :nid, :content, :cached_likes, :created_at, :updated_at - belongs_to :node, foreign_key: 'nid', dependent: :destroy + belongs_to :node, foreign_key: 'nid' belongs_to :drupal_user, foreign_key: 'uid' has_many :answer_selections, foreign_key: 'aid' - has_many :comments, foreign_key: 'aid' + has_many :comments, foreign_key: 'aid', dependent: :destroy validates :content, presence: true diff --git a/app/models/comment.rb b/app/models/comment.rb index d6c830ba69..0554d39b75 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -2,11 +2,11 @@ class Comment < ActiveRecord::Base include CommentsShared # common methods for comment-like models attr_accessible :pid, :nid, :uid, :aid, - :subject, :hostname, :comment, - :status, :format, :thread, :timestamp + :subject, :hostname, :comment, + :status, :format, :thread, :timestamp - belongs_to :node, foreign_key: 'nid', touch: true, - dependent: :destroy, counter_cache: true + belongs_to :node, foreign_key: 'nid', touch: true, counter_cache: true + # dependent: :destroy, counter_cache: true belongs_to :drupal_user, foreign_key: 'uid' belongs_to :answer, foreign_key: 'aid' @@ -27,8 +27,8 @@ def self.comment_weekly_tallies(span = 52, time = Time.now) weeks = {} (0..span).each do |week| weeks[span - week] = Comment.select(:timestamp) - .where(timestamp: time.to_i - week.weeks.to_i..time.to_i - (week - 1).weeks.to_i) - .count + .where(timestamp: time.to_i - week.weeks.to_i..time.to_i - (week - 1).weeks.to_i) + .count end weeks end @@ -67,7 +67,7 @@ def parent if aid == 0 node else - answer.node + return answer.node unless answer.nil? end end @@ -142,4 +142,17 @@ def answer_comment_notify(current_user) notify_users(uids, current_user) notify_tag_followers(already + uids) end + + def spam + self.status = 0 + save + self + end + + def publish + self.status = 1 + save + self + end + end diff --git a/app/models/concerns/node_shared.rb b/app/models/concerns/node_shared.rb index ad60560288..e33ee6f27b 100644 --- a/app/models/concerns/node_shared.rb +++ b/app/models/concerns/node_shared.rb @@ -170,26 +170,17 @@ def self.upgrades_grid(body) end end + #Blank map loaded only , markers will be loaded using API call . def self.notes_map(body) body.gsub(/[^\>`](\)?\[map\:content\:(\S+)\:(\S+)\]/) do |_tagname| lat = Regexp.last_match(2) lon = Regexp.last_match(3) - nids = NodeTag.joins(:tag) - .where('name LIKE ?', 'lat:' + lat[0..lat.length - 2] + '%') - .collect(&:nid) - nids = nids || [] - items = Node.includes(:tag) - .references(:node, :term_data) - .where('node.nid IN (?) AND term_data.name LIKE ?', nids, 'lon:' + lon[0..lon.length - 2] + '%') - .limit(200) - .order('node.nid DESC') a = ActionController::Base.new() output = a.render_to_string(template: "map/_leaflet", layout: false, locals: { lat: lat, - lon: lon, - items: items + lon: lon } ) output @@ -227,24 +218,20 @@ def self.notes_map_by_tag(body) end end - # in our interface, "users" are known as "people" because it's more human + # in our interface, "users" are known as "people" because it's more human def self.people_map(body, _page = 1) body.gsub(/[^\>`](\)?\[map\:people\:(\S+)\:(\S+)\]/) do |_tagname| tagname = Regexp.last_match(2) lat = Regexp.last_match(2) lon = Regexp.last_match(3) nids = nids || [] - users = User.where(status: 1) - .includes(:user_tags) - .references(:user_tags) - .where('user_tags.value LIKE ?', 'lat:' + lat[0..lat.length - 2] + '%') + a = ActionController::Base.new() - output = a.render_to_string(template: "map/_leaflet", + output = a.render_to_string(template: "map/_peopleLeaflet", layout: false, locals: { - lat: lat, - lon: lon, - items: users, + lat: lat , + lon: lon , people: true } ) @@ -252,9 +239,8 @@ def self.people_map(body, _page = 1) end end - # in our interface, "users" are known as "people" because it's more human - def self.people_grid(body, _page = 1) + def self.people_grid(body, current_user = nil, _page = 1) body.gsub(/[^\>`](\)?\[people\:(\S+)\]/) do |_tagname| tagname = Regexp.last_match(2) exclude = nil @@ -285,6 +271,7 @@ def self.people_grid(body, _page = 1) tagname: tagname, randomSeed: rand(1000).to_s, className: 'people-grid-' + tagname.parameterize, + current_user: current_user, users: users }) output diff --git a/app/models/doc_list.rb b/app/models/doc_list.rb index 8d0de08a61..2cf1a83ef1 100644 --- a/app/models/doc_list.rb +++ b/app/models/doc_list.rb @@ -11,7 +11,7 @@ def addDoc(ndoc) def addAll(dlist) @items ||= [] - dlist.each { |docItem| @items << docItem } unless dlist.nil? + dlist&.each { |docItem| @items << docItem } end def getDocs diff --git a/app/models/drupal_user.rb b/app/models/drupal_user.rb index a012118836..c7fe6b921d 100644 --- a/app/models/drupal_user.rb +++ b/app/models/drupal_user.rb @@ -17,7 +17,7 @@ class DrupalUser < ActiveRecord::Base has_many :comments, foreign_key: 'uid' def user - User.where(username: name).first + User.where(id: self.id).first end def bio @@ -41,12 +41,13 @@ def created_at # End rails-style adaptors def role - user.role if user + user&.role end def moderate self.status = 5 save + update_user_status(5) # user is logged out next time they access current_user in a controller; see application controller self end @@ -54,6 +55,7 @@ def moderate def unmoderate self.status = 1 save + update_user_status(1) self end @@ -61,6 +63,7 @@ def ban self.status = 0 decrease_likes_banned save + update_user_status(0) # user is logged out next time they access current_user in a controller; see application controller self end @@ -69,6 +72,7 @@ def unban self.status = 1 increase_likes_unbanned save + update_user_status(1) self end @@ -76,6 +80,12 @@ def email mail end + def update_user_status(status) + u = self.user + u.status = status + u.save! + end + def first_time_poster user.first_time_poster end @@ -90,25 +100,25 @@ def like_count def liked_notes Node.includes(:node_selections) - .references(:node_selections) - .where("type = 'note' AND node_selections.liking = ? AND node_selections.user_id = ? AND node.status = 1", true, uid) - .order('node_selections.nid DESC') + .references(:node_selections) + .where("type = 'note' AND node_selections.liking = ? AND node_selections.user_id = ? AND node.status = 1", true, uid) + .order('node_selections.nid DESC') end def liked_pages NodeSelection.where("status = 1 AND user_id = ? AND liking = ? AND (node.type = 'page' OR node.type = 'tool' OR node.type = 'place')", uid, true) - .includes(:node) - .references(:node) - .collect(&:node) - .reverse + .includes(:node) + .references(:node) + .collect(&:node) + .reverse end # last node def last Node.limit(1) - .where(uid: uid) - .order('changed DESC') - .first + .where(uid: uid) + .order('changed DESC') + .first end def profile_values diff --git a/app/models/node.rb b/app/models/node.rb index 12c28fb1a4..2d3bbdfff9 100644 --- a/app/models/node.rb +++ b/app/models/node.rb @@ -3,11 +3,16 @@ def validate(record) if record.title == '' || record.title.nil? # 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.title == 'new' && record.type == 'page' - record.errors[:base] << "You may not use the title 'new'." # otherwise the below title uniqueness check fails, as title presence validation doesn't run until after + elsif record.type == 'page' + array = ['create', 'edit', 'update', 'delete', 'new'] + array.each { |x| + if record.title == x + record.errors[:base] << "You may not use the title '" + x + "'" + end + } else if !Node.where(path: record.generate_path).first.nil? && record.type == 'note' - record.errors[:base] << 'You have already used this title today.' + record.errors[:base] << 'You have already used this title.' end end end @@ -38,19 +43,18 @@ def updated_month has_many :tag, through: :node_tag # these override the above... have to do it manually: # has_many :tag, :through => :drupal_node_tag - has_many :comments, foreign_key: 'nid' #, dependent: :destroy # re-enable in Rails 5 + has_many :comments, foreign_key: 'nid' , dependent: :destroy # re-enable in Rails 5 has_many :drupal_content_type_map, foreign_key: 'nid' #, dependent: :destroy # re-enable in Rails 5 has_many :drupal_content_field_mappers, foreign_key: 'nid' #, dependent: :destroy # re-enable in Rails 5 has_many :drupal_content_field_map_editor, foreign_key: 'nid' #, dependent: :destroy # re-enable in Rails 5 has_many :images, foreign_key: :nid has_many :node_selections, foreign_key: :nid - has_many :answers, foreign_key: :nid + has_many :answers, foreign_key: :nid, dependent: :destroy belongs_to :drupal_user, foreign_key: 'uid' validates :title, presence: :true validates_with UniqueUrlValidator, on: :create - validates :path, uniqueness: { message: 'This title has already been taken' } # making drupal and rails database conventions play nice; # 'changed' is a reserved word in rails @@ -166,13 +170,13 @@ def files end def answered - self.answers && self.answers.length > 0 + self.answers&.length.positive? end def has_accepted_answers - self.answers.where(accepted: true).count > 0 + self.answers.where(accepted: true).count.positive? end - + # users who like this node def likers node_selections @@ -243,7 +247,7 @@ def updated_at end def body - latest.body if latest + latest&.body end # was unable to set up this relationship properly with ActiveRecord associations @@ -326,7 +330,7 @@ def power_tag(tag) .collect(&:tid) node_tag = NodeTag.where('nid = ? AND tid IN (?)', id, tids) .order('nid DESC') - if node_tag && node_tag.first + if node_tag&.first node_tag.first.tag.name.gsub(tag + ':', '') else '' @@ -454,9 +458,9 @@ def tagnames_as_classes def edit_path path = if type == 'page' || type == 'tool' || type == 'place' '/wiki/edit/' + self.path.split('/').last - else - '/notes/edit/' + id.to_s - end + else + '/notes/edit/' + id.to_s + end path end @@ -514,9 +518,9 @@ def prev_by_author def add_comment(params = {}) thread = if !comments.empty? && !comments.last.nil? comments.last.next_thread - else - '01/' - end + else + '01/' + end c = Comment.new(pid: 0, nid: nid, uid: params[:uid], @@ -788,6 +792,8 @@ def can_tag(tagname, user, errors = false) else true end + elsif tagname == 'format:raw' && user.role != 'admin' + errors ? "Only admins may create raw pages." : false elsif tagname[0..4] == 'rsvp:' && user.username != tagname.split(':')[1] errors ? I18n.t('node.only_RSVP_for_yourself') : false elsif tagname == 'locked' && user.role != 'admin' @@ -817,9 +823,9 @@ def is_liked_by(user) def toggle_like(user) nodes = NodeSelection.where(nid: self.id , liking: true).count if is_liked_by(user) - self.cached_likes = nodes-1 + self.cached_likes = nodes-1 else - self.cached_likes = nodes+1 + self.cached_likes = nodes+1 end end @@ -827,13 +833,13 @@ def self.like(nid , user) # scope like variable outside the transaction like = nil count = nil - + ActiveRecord::Base.transaction do # Create the entry if it isn't already created. like = NodeSelection.where(user_id: user.uid, nid: nid).first_or_create like.liking = true - node = Node.find(nid) + node = Node.find(nid) if node.type == 'note' SubscriptionMailer.notify_note_liked(node, like.user) end @@ -849,17 +855,24 @@ def self.like(nid , user) def self.unlike(nid , user) like = nil count = nil - + ActiveRecord::Base.transaction do like = NodeSelection.where(user_id: user.uid, nid: nid).first_or_create like.liking = false - count = -1 - node = Node.find(nid) + count = -1 + node = Node.find(nid) node.toggle_like(like.user) node.save! like.save! - end + end count end + + # status = 3 for draft nodes,visible to author only + def draft + self.status = 3 + save + self + end end diff --git a/app/models/node_tag.rb b/app/models/node_tag.rb index e4f29973e9..a2dd8c51ff 100644 --- a/app/models/node_tag.rb +++ b/app/models/node_tag.rb @@ -33,7 +33,7 @@ def name end def description - self.tag.description if self.tag && self.tag.description && !self.tag.description.empty? + self.tag.description if self.tag&.description && !self.tag.description.empty? end end diff --git a/app/models/relationship.rb b/app/models/relationship.rb index 9914d54edf..94a915f8b2 100644 --- a/app/models/relationship.rb +++ b/app/models/relationship.rb @@ -2,4 +2,6 @@ class Relationship < ActiveRecord::Base attr_accessible :followed_id, :follower_id belongs_to :follower, class_name: 'User' belongs_to :followed, class_name: 'User' + validates :followed_id, presence: true + validates :follower_id, presence: true end diff --git a/app/models/revision.rb b/app/models/revision.rb index c64f13d2ab..eb81a84035 100644 --- a/app/models/revision.rb +++ b/app/models/revision.rb @@ -6,11 +6,13 @@ class Revision < ActiveRecord::Base belongs_to :node, foreign_key: 'nid', dependent: :destroy, counter_cache: :drupal_node_revisions_count has_one :drupal_users, foreign_key: 'uid' + has_many :node_tag, foreign_key: 'nid' + has_many :tag, through: :node_tag validates :title, - presence: :true, - length: { minimum: 2 }, - format: { with: /[A-Z][\w\-_]*/i, message: 'can only include letters, numbers, and dashes' } + presence: :true, + length: { minimum: 2 }, + format: { with: /[A-Z][\w\-_]*/i, message: 'can only include letters, numbers, and dashes' } validates :body, presence: :true validates :uid, presence: :true validates :nid, presence: :true @@ -78,14 +80,14 @@ def is_initial? def previous parent.revision.order('timestamp DESC') - .where('timestamp < ?', timestamp) - .first + .where('timestamp < ?', timestamp) + .first end def next parent.revision.order('timestamp DESC') - .where('timestamp > ?', timestamp) - .last + .where('timestamp > ?', timestamp) + .last end # filtered version of node content @@ -98,6 +100,16 @@ def render_body body = body.gsub(Callouts.const_get(:HASHTAG), Callouts.const_get(:HASHLINKHTML)) body_extras(body) end + + # filtered version of node content, but without running Markdown + def render_body_raw + body = self.body || '' + body = body.to_html + body = body.gsub(Callouts.const_get(:FINDER), Callouts.const_get(:PRETTYLINKHTML)) + body = body.gsub(Callouts.const_get(:HASHTAGNUMBER), Callouts.const_get(:NODELINKHTML)) + body = body.gsub(Callouts.const_get(:HASHTAG), Callouts.const_get(:HASHLINKHTML)) + insert_extras(body_extras(body)) + end # filtered version additionally appending http/https protocol to protocol-relative URLs like "/foo" # render_body plus making all relative links absolute diff --git a/app/models/spamaway.rb b/app/models/spamaway.rb index 61a85817d8..d8495bca98 100644 --- a/app/models/spamaway.rb +++ b/app/models/spamaway.rb @@ -10,10 +10,10 @@ class Spamaway < Tableless column :statement4, :string attr_accessible :follow_instructions, - :statement1, - :statement2, - :statement3, - :statement4 + :statement1, + :statement2, + :statement3, + :statement4 validate :clean_honeypot, :human_responses diff --git a/app/models/tableless.rb b/app/models/tableless.rb index 98fec22659..592c391023 100644 --- a/app/models/tableless.rb +++ b/app/models/tableless.rb @@ -8,7 +8,7 @@ def self.columns def self.column(name, sql_type = nil, default = nil, null = true) columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, - sql_type.to_s, null) + sql_type.to_s, null) end # Override the save method to prevent exceptions. diff --git a/app/models/tag.rb b/app/models/tag.rb index 37d3813ced..cc46a6bf0b 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -105,7 +105,7 @@ def self.find_nodes_by_type(tagnames, type = 'note', limit = 10) # just like find_nodes_by_type, but searches wiki pages, places, and tools def self.find_pages(tagnames, limit = 10) - find_nodes_by_type(tagnames, %w[page place tool], limit) + find_nodes_by_type(tagnames, %w(page place tool), limit) end def self.find_nodes_by_type_with_all_tags(tagnames, type = 'note', limit = 10) @@ -180,7 +180,7 @@ def weekly_tallies(type = 'note', span = 52) end def self.nodes_for_period(type, nids, start, finish) - Node.select(%i[created status type nid]) + Node.select(%i(created status type nid)) .where( 'type = ? AND status = 1 AND nid IN (?) AND created > ? AND created <= ?', type, @@ -246,25 +246,25 @@ def self.trending(limit = 5 , start_date = DateTime.now - 1.month , end_date = D def self.tagged_nodes_by_author(tagname, user_id) if tagname[-1..-1] == '*' @wildcard = true - Node.where('term_data.name LIKE(?)', tagname[0..-2]+'%') - .includes(:node_tag, :tag) - .order('node.nid DESC') - .references(:term_data) - .where('node.uid = ?', user_id) + Node.includes(:node_tag, :tag) + .where('term_data.name LIKE(?) OR term_data.parent LIKE (?)', tagname[0..-2]+'%', tagname[0..-2]+'%') + .references(:term_data, :node_tag) + .where('node.uid = ?', user_id) + .order('node.nid DESC') else - Node.where('term_data.name = ?', tagname) - .includes(:node_tag, :tag) - .order('node.nid DESC') - .references(:term_data) - .where('node.uid = ?', user_id) + Node.includes(:node_tag, :tag) + .where('term_data.name = ? OR term_data.parent = ?', tagname, tagname) + .references(:term_data, :node_tag) + .where('node.uid = ?', user_id) + .order('node.nid DESC') end end - def self.taggedNodeCount(tagName) + def self.tagged_node_count(tag_name) Node.where(status: 1, type: 'note') .includes(:revision, :tag) .references(:term_data, :node_revisions) - .where('term_data.name = ?', tagName) + .where('term_data.name = ?', tag_name) .count end end diff --git a/app/models/tag_list.rb b/app/models/tag_list.rb index 667273d48f..c6b7beaf71 100644 --- a/app/models/tag_list.rb +++ b/app/models/tag_list.rb @@ -15,7 +15,7 @@ def addTag(ntag) def addAll(tlist) @items ||= [] - tlist.each { |tItem| @items << tItem } unless tlist.nil? + tlist&.each { |tItem| @items << tItem } end def getTags diff --git a/app/models/user.rb b/app/models/user.rb index 35ea91612d..369e2f21a4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,11 +8,11 @@ def validate(record) class User < ActiveRecord::Base self.table_name = 'rusers' - attr_accessible :username, :email, :password, :password_confirmation, :openid_identifier, :key, :photo, :photo_file_name, :bio + attr_accessible :username, :email, :password, :password_confirmation, :openid_identifier, :key, :photo, :photo_file_name, :bio, :status alias_attribute :name, :username acts_as_authentic do |c| - c.openid_required_fields = %i[nickname email] + c.openid_required_fields = %i(nickname email) c.validates_format_of_email_field_options = { with: /@/ } c.crypto_provider = Authlogic::CryptoProviders::Sha512 end @@ -90,16 +90,16 @@ def drupal_user def notes Node.where(uid: uid) - .where(type: 'note') - .order('created DESC') + .where(type: 'note') + .order('created DESC') end def coauthored_notes coauthored_tag = "with:" + self.name.downcase Node.where(status: 1, type: "note") - .includes(:revision, :tag) - .references(:term_data, :node_revisions) - .where('term_data.name = ? OR term_data.parent = ?', coauthored_tag.to_s , coauthored_tag.to_s) + .includes(:revision, :tag) + .references(:term_data, :node_revisions) + .where('term_data.name = ? OR term_data.parent = ?', coauthored_tag.to_s , coauthored_tag.to_s) end def generate_reset_key @@ -137,6 +137,19 @@ def has_role(r) role == r end + def admin? + role == 'admin' + end + + def moderator? + role == 'moderator' + end + + def can_moderate? + # use instead of "user.role == 'admin' || user.role == 'moderator'" + admin? || moderator? + end + def tags(limit = 10) Tag.where('name in (?)', tagnames).limit(limit) end @@ -188,11 +201,11 @@ def weekly_note_tally(span = 52) weeks = {} (0..span).each do |week| weeks[span - week] = Node.select(:created) - .where( uid: drupal_user.uid, + .where( uid: drupal_user.uid, type: 'note', status: 1, created: Time.now.to_i - week.weeks.to_i..Time.now.to_i - (week - 1).weeks.to_i) - .count + .count end weeks end @@ -201,10 +214,10 @@ def weekly_comment_tally(span = 52) weeks = {} (0..span).each do |week| weeks[span - week] = Comment.select(:timestamp) - .where( uid: drupal_user.uid, + .where( uid: drupal_user.uid, status: 1, timestamp: Time.now.to_i - week.weeks.to_i..Time.now.to_i - (week - 1).weeks.to_i) - .count + .count end weeks end @@ -215,11 +228,11 @@ def note_streak(span = 365) note_count = 0 (0..span).each do |day| days[day] = Node.select(:created) - .where( uid: drupal_user.uid, + .where( uid: drupal_user.uid, type: 'note', status: 1, created: Time.now.midnight.to_i - day.days.to_i..Time.now.midnight.to_i - (day - 1).days.to_i) - .count + .count break if days[day] == 0 streak += 1 note_count += days[day] @@ -233,11 +246,11 @@ def wiki_edit_streak(span = 365) wiki_edit_count = 0 (0..span).each do |day| days[day] = Revision.joins(:node) - .where( uid: drupal_user.uid, + .where( uid: drupal_user.uid, status: 1, timestamp: Time.now.midnight.to_i - day.days.to_i..Time.now.midnight.to_i - (day - 1).days.to_i) - .where('node.type != ?', 'note') - .count + .where('node.type != ?', 'note') + .count break if days[day] == 0 streak += 1 wiki_edit_count += days[day] @@ -251,10 +264,10 @@ def comment_streak(span = 365) comment_count = 0 (0..span).each do |day| days[day] = Comment.select(:timestamp) - .where( uid: drupal_user.uid, + .where( uid: drupal_user.uid, status: 1, timestamp: Time.now.midnight.to_i - day.days.to_i..Time.now.midnight.to_i - (day - 1).days.to_i) - .count + .count break if days[day] == 0 streak += 1 comment_count += days[day] @@ -273,8 +286,8 @@ def streak(span = 365) def barnstars NodeTag.includes(:node, :tag) - .references(:term_data) - .where('type = ? AND term_data.name LIKE ? AND node.uid = ?', 'note', 'barnstar:%', uid) + .references(:term_data) + .where('type = ? AND term_data.name LIKE ? AND node.uid = ?', 'note', 'barnstar:%', uid) end def photo_path(size = :medium) @@ -310,8 +323,19 @@ def questions Node.questions.where(status: 1, uid: id) end - def content_followed_in_past_period(time_period) - self.node.where("created >= #{time_period.to_i} AND changed >= #{time_period.to_i}") + def content_followed_in_period(start_time, end_time) + tagnames = TagSelection.where(following: true, user_id: uid) + node_ids = [] + tagnames.each do |tagname| + node_ids = node_ids + NodeTag.where(tid: tagname.tid).collect(&:nid) + end + + Node.where(nid: node_ids) + .includes(:revision, :tag) + .references(:node_revision) + .where("(created >= #{start_time.to_i} AND created <= #{end_time.to_i}) OR (timestamp >= #{start_time.to_i} AND timestamp <= #{end_time.to_i})") + .order('node_revisions.timestamp DESC') + .uniq end def social_link(site) diff --git a/app/models/user_tag.rb b/app/models/user_tag.rb index b145a3ce50..9e4b0de43d 100644 --- a/app/models/user_tag.rb +++ b/app/models/user_tag.rb @@ -12,7 +12,7 @@ def preprocess end def self.exists?(uid, value) - UserTag.where(uid: uid, value: value).count > 0 + UserTag.where(uid: uid, value: value).count.positive? end def name diff --git a/app/services/search_service.rb b/app/services/search_service.rb index b34dbc1b0e..36aa70637a 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -34,43 +34,43 @@ def comments def find_users(input, limit = 10) User.limit(limit) - .order('id DESC') - .where(status: 1) - .where('username LIKE ?', '%' + input + '%') + .order('id DESC') + .where(status: 1) + .where('username LIKE ?', '%' + input + '%') end def find_tags(input, limit = 5) Tag.includes(:node) - .references(:node) - .where('node.status = 1') - .limit(limit) - .where('name LIKE ?', '%' + input + '%') + .references(:node) + .where('node.status = 1') + .limit(limit) + .where('name LIKE ?', '%' + input + '%') end def find_comments(input, limit = 5) Comment.limit(limit) - .order('nid DESC') - .where('status = 1 AND comment LIKE ?', '%' + input + '%') + .order('nid DESC') + .where('status = 1 AND comment LIKE ?', '%' + input + '%') end def find_nodes(input, limit = 5) Node.limit(limit) - .order('nid DESC') - .where('node.status = 1 AND title LIKE ?', '%' + input + '%') + .order('nid DESC') + .where('node.status = 1 AND title LIKE ?', '%' + input + '%') end ## search for node title only ## FIXme with solr def find_notes(input, limit = 5) Node.limit(limit) - .order('nid DESC') - .where('type = "note" AND node.status = 1 AND title LIKE ?', '%' + input + '%') + .order('nid DESC') + .where('type = "note" AND node.status = 1 AND title LIKE ?', '%' + input + '%') end def find_maps(input, limit = 5) Node.limit(limit) - .order('nid DESC') - .where('type = "map" AND node.status = 1 AND title LIKE ?', '%' + input + '%') + .order('nid DESC') + .where('type = "map" AND node.status = 1 AND title LIKE ?', '%' + input + '%') end # Run a search in any of the associated systems for references that contain the search string @@ -83,9 +83,9 @@ def textSearch_all(srchString) # Node search Node.limit(5) - .order('nid DESC') - .where('(type = "page" OR type = "place" OR type = "tool") AND node.status = 1 AND title LIKE ?', '%' + srchString + '%') - .select('title,type,nid,path').each do |match| + .order('nid DESC') + .where('(type = "page" OR type = "place" OR type = "tool") AND node.status = 1 AND title LIKE ?', '%' + srchString + '%') + .select('title,type,nid,path').each do |match| doc = DocResult.fromSearch(match.nid, match.icon, match.path, match.title, '', 0) sresult.addDoc(doc) end @@ -154,10 +154,10 @@ def textSearch_tags(srchString) # Tags sterms = srchString.split(' ') tlist = Tag.where(name: sterms) - .joins(:node_tag) - .joins(:node) - .where('node.status = 1') - .select('DISTINCT node.nid,node.title,node.path') + .joins(:node_tag) + .joins(:node) + .where('node.status = 1') + .select('DISTINCT node.nid,node.title,node.path') tlist.each do |match| tagdoc = DocResult.fromSearch(match.nid, 'tag', match.path, match.title, '', 0) sresult.addDoc(tagdoc) @@ -173,10 +173,10 @@ def textSearch_questions(srchString) 'type = "note" AND node.status = 1 AND title LIKE ?', '%' + srchString + '%' ) - .joins(:tag) - .where('term_data.name LIKE ?', 'question:%') - .order('node.nid DESC') - .limit(25) + .joins(:tag) + .where('term_data.name LIKE ?', 'question:%') + .order('node.nid DESC') + .limit(25) questions.each do |match| doc = DocResult.fromSearch(match.nid, 'question-circle', match.path(:question), match.title, 0, match.answers.length.to_i) sresult.addDoc(doc) @@ -192,16 +192,16 @@ def nearbyNodes(srchString) lon = coordinates[1] nids = NodeTag.joins(:tag) - .where('name LIKE ?', 'lat:' + lat[0..lat.length - 2] + '%') - .collect(&:nid) + .where('name LIKE ?', 'lat:' + lat[0..lat.length - 2] + '%') + .collect(&:nid) - nids = nids || [] + nids ||= [] items = Node.includes(:tag) - .references(:node, :term_data) - .where('node.nid IN (?) AND term_data.name LIKE ?', nids, 'lon:' + lon[0..lon.length - 2] + '%') - .limit(200) - .order('node.nid DESC') + .references(:node, :term_data) + .where('node.nid IN (?) AND term_data.name LIKE ?', nids, 'lon:' + lon[0..lon.length - 2] + '%') + .limit(200) + .order('node.nid DESC') items.each do |match| blurred = false @@ -219,4 +219,27 @@ def nearbyNodes(srchString) sresult end +#GET X number of latest people/contributors +# X = srchString +def recentPeople(srchString) + sresult = DocList.new + nodes = Node.all.order("changed DESC").limit(100).uniq + users = [] + nodes.each do |node| + users << node.author.user + end + users = users.uniq + users.each do |user| + if user.has_power_tag("lat") && user.has_power_tag("lon") + blurred = false + if user.has_power_tag("location") + blurred = user.get_value_of_power_tag("location") + end + doc = DocResult.fromLocationSearch(user.id, 'people_coordinates', user.path , user.username , 0 , 0 , user.lat , user.lon , blurred) + sresult.addDoc(doc) + end + end + sresult + end + end diff --git a/app/services/typeahead_service.rb b/app/services/typeahead_service.rb index 2cd14acb3c..c4eb9c3abe 100644 --- a/app/services/typeahead_service.rb +++ b/app/services/typeahead_service.rb @@ -15,111 +15,111 @@ def initialize; end def users(input, limit = 5) if ActiveRecord::Base.connection.adapter_name == 'Mysql2' User.search(input) - .limit(limit) - .where(status: 1) + .limit(limit) + .where(status: 1) else User.limit(limit) - .order('id DESC') - .where('username LIKE ? AND status = 1', '%' + input + '%') + .order('id DESC') + .where('username LIKE ? AND status = 1', '%' + input + '%') end end def tags(input, limit = 5) Tag.includes(:node) - .references(:node) - .where('node.status = 1') - .limit(limit) - .where('name LIKE ?', '%' + input + '%') - .group('node.nid') + .references(:node) + .where('node.status = 1') + .limit(limit) + .where('name LIKE ?', '%' + input + '%') + .group('node.nid') end def comments(input, limit = 5) if ActiveRecord::Base.connection.adapter_name == 'Mysql2' Comment.search(input) - .limit(limit) - .order('nid DESC') - .where(status: 1) + .limit(limit) + .order('nid DESC') + .where(status: 1) else Comment.limit(limit) - .order('nid DESC') - .where('status = 1 AND comment LIKE ?', '%' + input + '%') + .order('nid DESC') + .where('status = 1 AND comment LIKE ?', '%' + input + '%') end end def notes(input, limit = 5) if ActiveRecord::Base.connection.adapter_name == 'Mysql2' Node.search(input) - .group(:nid) - .includes(:node) - .references(:node) - .limit(limit) - .where("node.type": "note", "node.status": 1) - .order('node.changed DESC') + .group(:nid) + .includes(:node) + .references(:node) + .limit(limit) + .where("node.type": "note", "node.status": 1) + .order('node.changed DESC') else Node.limit(limit) - .group(:nid) - .where(type: "note", status: 1) - .order(changed: :desc) - .where('title LIKE ?', '%' + input + '%') + .group(:nid) + .where(type: "note", status: 1) + .order(changed: :desc) + .where('title LIKE ?', '%' + input + '%') end end def wikis(input, limit = 5) if ActiveRecord::Base.connection.adapter_name == 'Mysql2' Node.search(input) - .group('node.nid') - .includes(:node) - .references(:node) - .limit(limit) - .where("node.type": "page", "node.status": 1) + .group('node.nid') + .includes(:node) + .references(:node) + .limit(limit) + .where("node.type": "page", "node.status": 1) else Node.limit(limit) - .order('nid DESC') - .where('type = "page" AND node.status = 1 AND title LIKE ?', '%' + input + '%') + .order('nid DESC') + .where('type = "page" AND node.status = 1 AND title LIKE ?', '%' + input + '%') end end def maps(input, limit = 5) Node.limit(limit) - .order('nid DESC') - .where('type = "map" AND node.status = 1 AND title LIKE ?', '%' + input + '%') + .order('nid DESC') + .where('type = "map" AND node.status = 1 AND title LIKE ?', '%' + input + '%') end # Run a search in any of the associated systems for references that contain the search string - def search_all(srchString, limit = 5) + def search_all(search_string, limit = 5) sresult = TagList.new - unless srchString.nil? || srchString == 0 + unless search_string.nil? || search_string.blank? # notes - notesrch = search_notes(srchString, limit) + notesrch = search_notes(search_string, limit) sresult.addAll(notesrch.getTags) # wikis - wikisrch = search_wikis(srchString, limit) + wikisrch = search_wikis(search_string, limit) sresult.addAll(wikisrch.getTags) # User profiles - usersrch = search_profiles(srchString, limit) + usersrch = search_profiles(search_string, limit) sresult.addAll(usersrch.getTags) # Tags -- handled differently because tag - tagsrch = search_tags(srchString, limit) + tagsrch = search_tags(search_string, limit) sresult.addAll(tagsrch.getTags) # maps - mapsrch = search_maps(srchString, limit) + mapsrch = search_maps(search_string, limit) sresult.addAll(mapsrch.getTags) # questions - qsrch = search_questions(srchString, limit) + qsrch = search_questions(search_string, limit) sresult.addAll(qsrch.getTags) #comments - commentsrch = search_comments(srchString, limit) + commentsrch = search_comments(search_string, limit) sresult.addAll(commentsrch.getTags) end sresult end # Search profiles for matching text - def search_profiles(srchString, limit = 5) + def search_profiles(search_string, limit = 5) sresult = TagList.new - unless srchString.nil? || srchString == 0 + unless search_string.nil? || search_string.blank? # User profiles - users(srchString, limit).each do |match| + users(search_string, limit).each do |match| tval = TagResult.new tval.tagId = 0 tval.tagType = 'user' @@ -132,10 +132,10 @@ def search_profiles(srchString, limit = 5) end # Search notes for matching strings - def search_notes(srchString, limit = 5) + def search_notes(search_string, limit = 5) sresult = TagList.new - unless srchString.nil? || srchString == 0 - notes(srchString, limit).uniq.each do |match| + unless search_string.nil? || search_string.blank? + notes(search_string, limit).uniq.each do |match| tval = TagResult.new tval.tagId = match.nid tval.tagVal = match.title @@ -148,10 +148,10 @@ def search_notes(srchString, limit = 5) end # Search wikis for matching strings - def search_wikis(srchString, limit = 5) + def search_wikis(search_string, limit = 5) sresult = TagList.new - unless srchString.nil? || srchString == 0 - wikis(srchString, limit).select('node.title,node.type,node.nid,node.path').each do |match| + unless search_string.nil? || search_string.blank? + wikis(search_string, limit).select('node.title,node.type,node.nid,node.path').each do |match| tval = TagResult.new tval.tagId = match.nid tval.tagVal = match.title @@ -164,11 +164,11 @@ def search_wikis(srchString, limit = 5) end # Search maps for matching text - def search_maps(srchString, limit = 5) + def search_maps(search_string, limit = 5) sresult = TagList.new - unless srchString.nil? || srchString == 0 + unless search_string.nil? || search_string.blank? # maps - maps(srchString, limit).select('title,type,nid,path').each do |match| + maps(search_string, limit).select('title,type,nid,path').each do |match| tval = TagResult.new tval.tagId = match.nid tval.tagVal = match.title @@ -181,11 +181,11 @@ def search_maps(srchString, limit = 5) end # Search tag values for matching text - def search_tags(srchString, limit = 5) + def search_tags(search_string, limit = 5) sresult = TagList.new - unless srchString.nil? || srchString == 0 + unless search_string.nil? || search_string.blank? # Tags - tlist = tags(srchString, limit) + tlist = tags(search_string, limit) tlist.each do |match| ntag = TagResult.new ntag.tagId = 0 @@ -198,16 +198,27 @@ def search_tags(srchString, limit = 5) end # Search question entries for matching text - def search_questions(srchString, limit = 5) + def search_questions(input, limit = 5) sresult = TagList.new - questions = Node.where( - 'type = "note" AND node.status = 1 AND title LIKE ?', - '%' + srchString + '%' - ) - .joins(:tag) - .where('term_data.name LIKE ?', 'question:%') - .order('node.nid DESC') - .limit(limit) + questions = if ActiveRecord::Base.connection.adapter_name == 'Mysql2' + Node.search(input) + .group(:nid) + .includes(:node) + .references(:node) + .limit(limit) + .where("node.type": "note", "node.status": 1) + .order('node.changed DESC') + .joins(:tag) + .where('term_data.name LIKE ?', 'question:%') + else + Node.where('title LIKE ?', '%' + input + '%') + .joins(:tag) + .where('term_data.name LIKE ?', 'question:%') + .limit(limit) + .group(:nid) + .where(type: "note", status: 1) + .order(changed: :desc) + end questions.each do |match| tval = TagResult.fromSearch( match.nid, @@ -221,10 +232,10 @@ def search_questions(srchString, limit = 5) end # Search comments for matching text - def search_comments(srchString, limit = 5) + def search_comments(search_string, limit = 5) sresult = TagList.new - unless srchString.nil? || srchString == 0 - comments(srchString, limit).each do |match| + unless search_string.nil? || search_string.blank? + comments(search_string, limit).each do |match| tval = TagResult.new tval.tagId = match.pid tval.tagVal = match.comment.truncate(20) diff --git a/app/views/admin/spam.html.erb b/app/views/admin/spam.html.erb index 8fa7d77ca2..7e4ef5ba35 100644 --- a/app/views/admin/spam.html.erb +++ b/app/views/admin/spam.html.erb @@ -1,6 +1,6 @@
-

Spam moderation:

+

Spam moderation:

Moderators and admins have the ability to "spam" posts if they include inappropriate content, blatant advertising, or are otherwise problematic. If you're not sure, email organizers@<%= request.host %>. If we all work together, we can keep spam to a minimum; thanks for helping out!

@@ -16,6 +16,7 @@
  • class="active"<% end %>>Filtered
  • class="active"<% end %>>
  • class="active"<% end %>>
  • +

  • diff --git a/app/views/comment_mailer/notify_answer_author.html.erb b/app/views/comment_mailer/notify_answer_author.html.erb index 67e44e29b6..a71671f869 100644 --- a/app/views/comment_mailer/notify_answer_author.html.erb +++ b/app/views/comment_mailer/notify_answer_author.html.erb @@ -2,7 +2,7 @@ Hi! There's been a new comment to your answer on '<%= @giver.name %>' has awarded you a 'Barnstar' for your work in the research note '<%= @note.title %>'. Do NOT reply to this email; click this link to respond:

    https://<%= ActionMailer::Base.default_url_options[:host] %><%= @note.path %>

    diff --git a/app/views/comment_mailer/notify_coauthor.html.erb b/app/views/comment_mailer/notify_coauthor.html.erb new file mode 100644 index 0000000000..40c2436f25 --- /dev/null +++ b/app/views/comment_mailer/notify_coauthor.html.erb @@ -0,0 +1,15 @@ +'<%= @author.name %>' has added you as a co-author of '<%= @note.title %>'. Do NOT reply to this email; click this link to respond: + +

    https://<%= ActionMailer::Base.default_url_options[:host] %><%= @note.path %>

    + +
    + +
    + +

    Reply at: https://<%= ActionMailer::Base.default_url_options[:host] %><%= @note.path %>#comments

    + +

    Report abuse to: moderators@<%= ActionMailer::Base.default_url_options[:host] %>

    + + <%= @footer %> + +
    diff --git a/app/views/comment_mailer/notify_note_author.html.erb b/app/views/comment_mailer/notify_note_author.html.erb index b5665198ff..1d745db9b8 100644 --- a/app/views/comment_mailer/notify_note_author.html.erb +++ b/app/views/comment_mailer/notify_note_author.html.erb @@ -11,7 +11,7 @@ Do NOT reply to this email; click this link to respond:

    https://<%= ActionMailer::Base.default_url_options[:host] %><%= @comment.node.path %>#c<%= @comment.cid %>

    <% end %> -

    <%= @comment.author.name %> wrote:

    +

    <%= @comment.author.name %> wrote:


    diff --git a/app/views/comments/_form.html.erb b/app/views/comments/_form.html.erb index c72770b61d..7cf1da846e 100644 --- a/app/views/comments/_form.html.erb +++ b/app/views/comments/_form.html.erb @@ -1,4 +1,4 @@ -
    action= "/answers/create/<%= @node.nid %>" <% else %> action="/comment/create/<%= @node.nid %>" <% end %>> + action= "/answers/create/<%= @node.nid %>" <% else %> action="/comment/create/<%= @node.nid %>" <% end %> method="post" >

    <%= title %>

    @@ -91,7 +101,7 @@

    <%= t('home.home.the_problem.title') %>

    <%= t('home.home.the_problem.lack_access') %> <%= t('home.home.the_problem.tools') %> <%= t('home.home.the_problem.need_participation') %>

    -

    Read stories »

    +

    Read blog stories »

    diff --git a/app/views/home/subscriptions.html.erb b/app/views/home/subscriptions.html.erb index 8d646e58b4..87725735c0 100644 --- a/app/views/home/subscriptions.html.erb +++ b/app/views/home/subscriptions.html.erb @@ -40,7 +40,8 @@ Digest --> - <%= t('home.subscriptions.share') %> + <%= t('home.subscriptions.share') %> <%= t('home.subscriptions.unsubscribe') %>
    diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb index c214a6ab42..41a42a2821 100644 --- a/app/views/layouts/_footer.html.erb +++ b/app/views/layouts/_footer.html.erb @@ -39,6 +39,11 @@ <% end %> + + + + +
    diff --git a/app/views/layouts/_google_analytics.html.erb b/app/views/layouts/_google_analytics.html.erb index 765cdd8db4..9740309256 100644 --- a/app/views/layouts/_google_analytics.html.erb +++ b/app/views/layouts/_google_analytics.html.erb @@ -1,9 +1,10 @@ <% if Rails.env.production? %> - + + + <% end %> diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index 4a861a77fa..af7133774d 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -47,16 +47,17 @@ @@ -66,7 +67,7 @@
    - +
    @@ -112,7 +113,7 @@ <% unless params[:action] == "register" || params[:action] == "signup" %>