diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 000000000..e94f8140c --- /dev/null +++ b/.browserslistrc @@ -0,0 +1 @@ +defaults diff --git a/.circleci/config.yml b/.circleci/config.yml index 9e5bfeb66..43f5a9c25 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/ruby:2.7.2-node-browsers + - image: circleci/ruby:2.7.4-node-browsers environment: # environment variables for primary container BUNDLE_JOBS: 3 BUNDLE_RETRY: 3 diff --git a/.env.example b/.env.example index e7770ece7..1d014df52 100644 --- a/.env.example +++ b/.env.example @@ -49,3 +49,10 @@ EMAIL_SENDER= # See https://github.com/o19s/quepid/issues/272 for a bug in expand/collapse that some setups experience. # This lets you disable the sorting if you experience the bug. QUERY_LIST_SORTABLE=true + +# OAuth Settings +GOOGLE_CLIENT_ID=your_google_client_id +GOOGLE_CLIENT_SECRET=your_google_client_secret + +KEYCLOAK_REALM=quepid +KEYCLOAK_SITE=http://keycloak:9080 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..516857104 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +# See https://git-scm.com/docs/gitattributes for more about git attribute files. + +# Mark the database schema as having been generated. +db/schema.rb linguist-generated + +# Mark the yarn lockfile as having been generated. +yarn.lock linguist-generated + +# Mark any vendored files as having been vendored. +vendor/* linguist-vendored diff --git a/.github/stale.yml b/.github/stale.yml index dfb8c0e0d..c555eb844 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -11,7 +11,8 @@ staleLabel: wontfix # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had - recent activity. It will be closed if no further activity occurs. Thank you - for your contributions. + recent activity. It will be closed if no further activity occurs. Please do + advocate for this issue and ideally submit a patch to get the attention of + the maintainers! # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false diff --git a/.gitignore b/.gitignore index 8cd4feea5..ff5096212 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ npm-debug.log # Rails assets # Ignore compiled assets public/assets +public/packs # Test Coverage coverage/ @@ -43,3 +44,8 @@ public/srv # Test Reports test/reports + +/public/packs +/public/packs-test +yarn-debug.log* +.yarn-integrity diff --git a/.ruby-version b/.ruby-version index 37c2961c2..a4dd9dba4 100755 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.2 +2.7.4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 611eb6a78..2dfa3f10e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,36 @@ # Changelog -## 6.5.4 - ?? +## 6.6.0 - ???? + +### Features + +* OpenID Support! Historically you had to create your own account on Quepid, but that is another barrier to entry, and something else to keep track of. Inspired by the issue _Add OAuth Providers_ by @gregoryduckworth https://github.com/o19s/quepid/issues/188, https://github.com/o19s/quepid/pull/280 and https://github.com/o19s/quepid/pull/389 by @epugh implements the first two providers, Google and Keycloak. + +### Improvements + +* Quepid is running on Rails 6! Rails 6 was released ~26 months, and represents the future of Rails. I'm excited that this push initiated by @DmitryKey is going to bring us some great new features like: better developer experience with Webpack for JavaScript, ActionText to handle better text formatting of notes and messages about Cases and Queries, ActionCable which will let us notify users who are rating the same case. https://github.com/o19s/quepid/pull/381 by @DmitryKey with assist from @epugh. + +* Make our ActiveRecord modeling for ownership the same. Teams have an _owner_, Scorers have an _owner_, but Cases have a _user_. Now we have _case.owner_ relationship. https://github.com/o19s/quepid/pull/359 by @epugh. + +### Bugs + +## 6.5.5 - 2021-06-30 + +### Features + +* You can now tag a field with `translate:`, as in `translate:content` and you will get an icon to pop the text open in a Google Translate in a new browser window. https://github.com/o19s/quepid/pull/386 by @epugh. + +### Improvements + +* You can now export fields that have the formatting modifiers `thumb` and `image` using the detail format. Also improved the handling of the General and Detail export from the Case Listing page. https://github.com/o19s/quepid/pull/383 by @epugh fixes https://github.com/o19s/quepid/issues/382. Thanks @DmitryKey for the improvement assist and spotting the Case Listing export issue. + +* Admin user can now reset a users password with a new password. https://github.com/o19s/quepid/pull/385 by @epugh to fix issue identified by @michaelcizmar. Thanks Michael! + +* Trying to communciate about HTTPS better when you set up a case. https://github.com/o19s/quepid/pull/384 by @epugh inspired by https://github.com/o19s/quepid/issues/279 by @arafalov. + + + +## 6.5.4 - 2021-06-16 ### Features @@ -14,6 +44,8 @@ * Revamped the layout of the Scorer creation and editing screens to be visually cleaner. Retired the old _Fibonnaci_ scale, and renamed _Default_ to _Detail_, as well as _Short_ to _Graded_ scales. Introduced _Binary_ as a new, default scale. @DmitryKey and @epugh paired on this during Quepid Qommunity Qoding hour, resulting in https://github.com/o19s/quepid/pull/379. +* Shrink production Docker image of Quepid from 2.19GB to 2.17GB by not installing development and test Gems. Commit 426d2677f6c4a8380971ddc1b0faa42a53a48879 by @epugh. + ### Bugs * Preserve the chosen scorer when cloning a case, we were defaulting to the users default scorer. https://github.com/o19s/quepid/pull/372 by @epugh fixes https://github.com/o19s/quepid/issues/273. Thanks @binarymax and @nathancday for spotting this! diff --git a/Dockerfile.dev b/Dockerfile.dev index 76468e15a..f1e5a8284 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM ruby:2.7.2-buster +FROM ruby:2.7.4-buster LABEL maintainer="quepid_admin@opensourceconnections.com" @@ -19,14 +19,14 @@ RUN bundle install # Dependencies for Puppeteer/Chromium # Dependency for generating the ERD is at the end, 'graphviz' RUN apt-get update -qq \ - && apt-get install -y --no-install-recommends gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils libgbm1 wget graphviz \ + && apt-get install -y --no-install-recommends gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils libgbm1 wget graphviz \ && rm -rf /var/lib/apt/lists/* - +# may not need libgbm # Install Node -RUN curl -sL https://deb.nodesource.com/setup_10.x | bash +RUN curl -sL https://deb.nodesource.com/setup_14.x | bash RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list diff --git a/Dockerfile.prod b/Dockerfile.prod index b3397c470..592656cf7 100644 --- a/Dockerfile.prod +++ b/Dockerfile.prod @@ -1,9 +1,9 @@ -FROM node:10.20.1 AS build-dep +FROM node:14.17.3-buster AS build-dep WORKDIR /srv/app COPY package.json yarn.lock ./ RUN yarn install --production=true -FROM ruby:2.7.2-buster +FROM ruby:2.7.4-buster LABEL maintainer="quepid_admin@opensourceconnections.com" @@ -32,9 +32,19 @@ RUN apt-get update \ WORKDIR /srv/app COPY Gemfile Gemfile.lock ./ RUN gem install bundler:1.17.3 -RUN bundle install +#RUN bundle install +# Clean up Bundle +RUN bundle install --without development test && \ + bundle clean --force && \ + rm -rf /app/.bundle/cache && \ + rm -rf /app/vendor/bundle/ruby/*/cache COPY --from=build-dep /srv/app/node_modules ./node_modules/ COPY . . + RUN RAILS_ENV=production SECRET_KEY_BASE=fake_out_devise bundle exec rake assets:precompile + +# Remove some files not needed in resulting image +RUN rm package.json yarn.lock + CMD ["foreman", "s", "-f", "Procfile"] diff --git a/Gemfile b/Gemfile index 4f4c1acb4..9b35f485e 100755 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' -ruby '2.7.2' +ruby '2.7.4' gem 'activerecord-import', '>= 1.0.7' gem 'acts_as_list', '>= 1.0.1' @@ -16,30 +16,32 @@ gem 'devise', '>= 4.6.2' gem 'devise_invitable', '~> 2.0' gem 'gabba' gem 'intercom-rails' -gem 'jbuilder', '~> 2.5' +gem 'jbuilder', '~> 2.7' gem 'redis', '~> 4.0' gem 'jquery-rails' gem 'jquery-ui-rails' # Can we narrow the widgets to load faster? gem 'mysql2' -gem 'postmark-rails', '~> 0.10.0' -gem 'puma', '~> 3.11' +gem 'postmark-rails' +gem 'puma', '~> 5.0' gem 'puma_worker_killer' gem 'pundit' -gem 'rails', '~> 5.2.4', '>= 5.2.4.4' +gem 'rails', '= 6.1.4' # starting Rails 6 the webpack is included by default. gem 'responders' gem 'sidekiq' gem 'terser' -gem 'bootsnap' +gem 'bootsnap', '>= 1.4.4', require: false gem 'listen', '~> 3.3' gem 'd3-rails', '~> 3.5.5' # we have a very old version of D3. This provides D3 assets. gem 'cal-heatmap-rails', '~> 3.6' # provides assets for cal heatmap, that requires old d3 gem 'font-awesome-sass' +gem 'webpacker', '= 5.4.0' +gem 'rack-cors', '~> 1.1' +gem 'foreman' group :development, :test do gem 'annotate' gem 'bullet' gem 'byebug' - gem 'foreman' gem 'memory_profiler' gem 'rack-mini-profiler' end @@ -59,3 +61,11 @@ group :test do gem 'webmock' gem 'rails-controller-testing' # bring back compatibility with rails 4 assigns in controller tests. end + +gem 'omniauth', '~> 2.0' + +gem 'omniauth-keycloak', '~> 1.3' + +gem 'omniauth-rails_csrf_protection', '~> 1.0' + +gem 'omniauth-google-oauth2', '~> 1.0' diff --git a/Gemfile.lock b/Gemfile.lock index 4ee1bc7ff..f8923eb6d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,53 +1,72 @@ GEM remote: https://rubygems.org/ specs: - actioncable (5.2.5) - actionpack (= 5.2.5) + actioncable (6.1.4) + actionpack (= 6.1.4) + activesupport (= 6.1.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.5) - actionpack (= 5.2.5) - actionview (= 5.2.5) - activejob (= 5.2.5) + actionmailbox (6.1.4) + actionpack (= 6.1.4) + activejob (= 6.1.4) + activerecord (= 6.1.4) + activestorage (= 6.1.4) + activesupport (= 6.1.4) + mail (>= 2.7.1) + actionmailer (6.1.4) + actionpack (= 6.1.4) + actionview (= 6.1.4) + activejob (= 6.1.4) + activesupport (= 6.1.4) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.2.5) - actionview (= 5.2.5) - activesupport (= 5.2.5) - rack (~> 2.0, >= 2.0.8) + actionpack (6.1.4) + actionview (= 6.1.4) + activesupport (= 6.1.4) + rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.5) - activesupport (= 5.2.5) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (6.1.4) + actionpack (= 6.1.4) + activerecord (= 6.1.4) + activestorage (= 6.1.4) + activesupport (= 6.1.4) + nokogiri (>= 1.8.5) + actionview (6.1.4) + activesupport (= 6.1.4) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.2.5) - activesupport (= 5.2.5) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + activejob (6.1.4) + activesupport (= 6.1.4) globalid (>= 0.3.6) - activemodel (5.2.5) - activesupport (= 5.2.5) - activerecord (5.2.5) - activemodel (= 5.2.5) - activesupport (= 5.2.5) - arel (>= 9.0) - activerecord-import (1.0.7) + activemodel (6.1.4) + activesupport (= 6.1.4) + activerecord (6.1.4) + activemodel (= 6.1.4) + activesupport (= 6.1.4) + activerecord-import (1.2.0) activerecord (>= 3.2) - activestorage (5.2.5) - actionpack (= 5.2.5) - activerecord (= 5.2.5) + activestorage (6.1.4) + actionpack (= 6.1.4) + activejob (= 6.1.4) + activerecord (= 6.1.4) + activesupport (= 6.1.4) marcel (~> 1.0.0) - activesupport (5.2.5) + mini_mime (>= 1.1.0) + activesupport (6.1.4) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - acts_as_list (1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + acts_as_list (1.0.4) activerecord (>= 4.2) - addressable (2.7.0) + addressable (2.8.0) public_suffix (>= 2.0.2, < 5.0) + aes_key_wrap (1.1.0) angular-rails-templates (1.1.0) railties (>= 4.2, < 7) sprockets (>= 3.0, < 5) @@ -56,30 +75,31 @@ GEM activerecord (>= 3.2, < 7.0) rake (>= 10.4, < 14.0) ansi (1.5.0) - arel (9.0.0) - ast (2.4.1) + ast (2.4.2) bcrypt (3.1.16) - benchmark-ips (2.8.4) - bootsnap (1.7.5) + benchmark-ips (2.9.1) + bindata (2.4.10) + bootsnap (1.7.6) msgpack (~> 1.0) builder (3.2.4) - bullet (6.1.0) + bullet (6.1.4) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) byebug (11.1.3) cal-heatmap-rails (3.6.2) choice (0.2.0) colorize (0.8.1) - concurrent-ruby (1.1.8) - connection_pool (2.2.3) - cookies_eu (1.7.7) + concurrent-ruby (1.1.9) + connection_pool (2.2.5) + cookies_eu (1.7.8) js_cookie_rails (~> 2.2.0) - crack (0.4.4) + crack (0.4.5) + rexml crass (1.0.6) d3-rails (3.5.17) railties (>= 3.1) - dead_end (1.1.6) - derailed_benchmarks (2.0.1) + dead_end (1.1.7) + derailed_benchmarks (2.1.1) benchmark-ips (~> 2) dead_end get_process_mem (~> 0) @@ -91,35 +111,53 @@ GEM rake (> 10, < 14) ruby-statistics (>= 2.1) thor (>= 0.19, < 2) - devise (4.7.3) + devise (4.8.0) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) responders warden (~> 1.2.3) - devise_invitable (2.0.3) + devise_invitable (2.0.5) actionmailer (>= 5.0) devise (>= 4.6) - docile (1.3.5) + docile (1.4.0) erubi (1.10.0) - execjs (2.7.0) - ffi (1.15.0) + execjs (2.8.1) + faraday (1.5.1) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0.1) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.1) + faraday-patron (~> 1.0) + multipart-post (>= 1.2, < 3) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + ffi (1.15.3) font-awesome-sass (5.15.1) sassc (>= 1.11) foreman (0.87.2) gabba (1.0.1) get_process_mem (0.2.7) ffi (~> 1.0) - globalid (0.4.2) - activesupport (>= 4.2.0) + globalid (0.5.1) + activesupport (>= 5.0) hashdiff (1.0.1) + hashie (4.1.0) heapy (0.2.0) thor - i18n (1.8.9) + i18n (1.8.10) concurrent-ruby (~> 1.0) intercom-rails (0.4.2) activesupport (> 3.0) - jbuilder (2.10.1) + jbuilder (2.11.2) activesupport (>= 5.0.0) jquery-rails (4.4.0) rails-dom-testing (>= 1, < 3) @@ -129,50 +167,83 @@ GEM railties (>= 3.2.16) js_cookie_rails (2.2.0) railties (>= 3.1) - json (2.3.1) + json (2.5.1) + json-jwt (1.13.0) + activesupport (>= 4.2) + aes_key_wrap + bindata + jwt (2.2.3) launchy (2.5.0) addressable (~> 2.7) letter_opener (1.7.0) launchy (~> 2.2) - listen (3.3.3) + listen (3.6.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - loofah (2.9.0) + loofah (2.10.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) - marcel (1.0.0) + marcel (1.0.1) memory_profiler (1.0.0) method_source (1.0.0) mini_histogram (0.3.1) - mini_mime (1.0.3) - mini_portile2 (2.5.0) + mini_mime (1.1.0) + mini_portile2 (2.5.3) minitest (5.14.4) minitest-reporters (1.4.3) ansi builder minitest (>= 5.0) ruby-progressbar - mocha (1.11.2) + mocha (1.13.0) msgpack (1.4.2) + multi_json (1.15.0) + multi_xml (0.6.0) + multipart-post (2.1.1) mysql2 (0.5.3) nio4r (2.5.7) - nokogiri (1.11.2) + nokogiri (1.11.7) mini_portile2 (~> 2.5.0) racc (~> 1.4) + oauth2 (1.4.7) + faraday (>= 0.8, < 2.0) + jwt (>= 1.0, < 3.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + omniauth (2.0.4) + hashie (>= 3.4.6) + rack (>= 1.6.2, < 3) + rack-protection + omniauth-google-oauth2 (1.0.0) + jwt (>= 2.0) + oauth2 (~> 1.1) + omniauth (~> 2.0) + omniauth-oauth2 (~> 1.7.1) + omniauth-keycloak (1.3.0) + json-jwt (~> 1.13.0) + omniauth (~> 2.0.4) + omniauth-oauth2 (~> 1.7.1) + omniauth-oauth2 (1.7.1) + oauth2 (~> 1.4) + omniauth (>= 1.9, < 3) + omniauth-rails_csrf_protection (1.0.0) + actionpack (>= 4.2) + omniauth (~> 2.0) orm_adapter (0.5.0) parallel (1.20.1) - parser (2.7.2.0) + parser (3.0.2.0) ast (~> 2.4.1) - postmark (1.5.0) + postmark (1.21.8) json - rake - postmark-rails (0.10.0) + postmark-rails (0.21.0) actionmailer (>= 3.0.0) - postmark (~> 1.5.0) + postmark (>= 1.21.3, < 2.0) public_suffix (4.0.6) - puma (3.12.6) + puma (5.4.0) + nio4r (~> 2.0) puma_worker_killer (0.3.1) get_process_mem (~> 0.2) puma (>= 2.7) @@ -180,22 +251,30 @@ GEM activesupport (>= 3.0.0) racc (1.5.2) rack (2.2.3) + rack-cors (1.1.1) + rack (>= 2.0.0) rack-mini-profiler (2.3.2) rack (>= 1.2.0) + rack-protection (2.1.0) + rack + rack-proxy (0.7.0) + rack rack-test (1.1.0) rack (>= 1.0, < 3) - rails (5.2.5) - actioncable (= 5.2.5) - actionmailer (= 5.2.5) - actionpack (= 5.2.5) - actionview (= 5.2.5) - activejob (= 5.2.5) - activemodel (= 5.2.5) - activerecord (= 5.2.5) - activestorage (= 5.2.5) - activesupport (= 5.2.5) - bundler (>= 1.3.0) - railties (= 5.2.5) + rails (6.1.4) + actioncable (= 6.1.4) + actionmailbox (= 6.1.4) + actionmailer (= 6.1.4) + actionpack (= 6.1.4) + actiontext (= 6.1.4) + actionview (= 6.1.4) + activejob (= 6.1.4) + activemodel (= 6.1.4) + activerecord (= 6.1.4) + activestorage (= 6.1.4) + activesupport (= 6.1.4) + bundler (>= 1.15.0) + railties (= 6.1.4) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) @@ -211,42 +290,43 @@ GEM ruby-graphviz (~> 1.2) rails-html-sanitizer (1.3.0) loofah (~> 2.3) - railties (5.2.5) - actionpack (= 5.2.5) - activesupport (= 5.2.5) + railties (6.1.4) + actionpack (= 6.1.4) + activesupport (= 6.1.4) method_source - rake (>= 0.8.7) - thor (>= 0.19.0, < 2.0) + rake (>= 0.13) + thor (~> 1.0) rainbow (3.0.0) - rake (13.0.3) - rb-fsevent (0.10.4) + rake (13.0.6) + rb-fsevent (0.11.0) rb-inotify (0.10.1) ffi (~> 1.0) - redis (4.2.5) - regexp_parser (2.0.0) + redis (4.4.0) + regexp_parser (2.1.1) responders (3.0.1) actionpack (>= 5.0) railties (>= 5.0) - rexml (3.2.4) - rubocop (1.6.0) + rexml (3.2.5) + rubocop (1.18.4) parallel (~> 1.10) - parser (>= 2.7.1.5) + parser (>= 3.0.0.0) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml - rubocop-ast (>= 1.2.0, < 2.0) + rubocop-ast (>= 1.8.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 2.0) - rubocop-ast (1.3.0) - parser (>= 2.7.1.5) - rubocop-rails (2.9.0) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.8.0) + parser (>= 3.0.1.1) + rubocop-rails (2.11.3) activesupport (>= 4.2.0) rack (>= 1.1) - rubocop (>= 0.90.0, < 2.0) + rubocop (>= 1.7.0, < 2.0) ruby-graphviz (1.2.5) rexml ruby-progressbar (1.11.0) ruby-statistics (2.1.3) + ruby2_keywords (0.0.5) sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) @@ -255,7 +335,8 @@ GEM sprockets (> 3.0) sprockets-rails tilt - sidekiq (6.1.2) + semantic_range (3.0.0) + sidekiq (6.2.1) connection_pool (>= 2.2.2) rack (~> 2.0) redis (>= 4.2.0) @@ -264,7 +345,7 @@ GEM simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) simplecov-html (0.12.3) - simplecov_json_formatter (0.1.2) + simplecov_json_formatter (0.1.3) sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -272,27 +353,32 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - terser (1.1.3) + terser (1.1.5) execjs (>= 0.3.0, < 3) thor (1.1.0) - thread_safe (0.3.6) tilt (2.0.10) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) - tzinfo (1.2.9) - thread_safe (~> 0.1) - unicode-display_width (1.7.0) - uniform_notifier (1.13.0) + tzinfo (2.0.4) + concurrent-ruby (~> 1.0) + unicode-display_width (2.0.0) + uniform_notifier (1.14.2) warden (1.2.9) rack (>= 2.0.9) - webmock (3.10.0) + webmock (3.13.0) addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - websocket-driver (0.7.3) + webpacker (5.4.0) + activesupport (>= 5.2) + rack-proxy (>= 0.6.1) + railties (>= 5.2) + semantic_range (>= 2.3.0) + websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) + zeitwerk (2.4.2) PLATFORMS ruby @@ -303,7 +389,7 @@ DEPENDENCIES angular-rails-templates (>= 1.0.0.beta) annotate bcrypt (~> 3.1.7) - bootsnap + bootsnap (>= 1.4.4) bullet byebug cal-heatmap-rails (~> 3.6) @@ -317,7 +403,7 @@ DEPENDENCIES foreman gabba intercom-rails - jbuilder (~> 2.5) + jbuilder (~> 2.7) jquery-rails jquery-ui-rails letter_opener @@ -326,12 +412,17 @@ DEPENDENCIES minitest-reporters (>= 0.5.0) mocha (~> 1.11) mysql2 - postmark-rails (~> 0.10.0) - puma (~> 3.11) + omniauth (~> 2.0) + omniauth-google-oauth2 (~> 1.0) + omniauth-keycloak (~> 1.3) + omniauth-rails_csrf_protection (~> 1.0) + postmark-rails + puma (~> 5.0) puma_worker_killer pundit + rack-cors (~> 1.1) rack-mini-profiler - rails (~> 5.2.4, >= 5.2.4.4) + rails (= 6.1.4) rails-controller-testing rails-erd (~> 1.6) redis (~> 4.0) @@ -344,9 +435,10 @@ DEPENDENCIES terser turbolinks (~> 5) webmock + webpacker (= 5.4.0) RUBY VERSION - ruby 2.7.2p137 + ruby 2.7.4p191 BUNDLED WITH - 1.17.3 + 2.1.4 diff --git a/Procfile.dev b/Procfile.dev index 40e61e988..efcbe1a45 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,4 +1,5 @@ web: bundle exec puma -C config/puma.rb worker: bundle exec sidekiq -e development -C config/sidekiq.yml -q default +# For the future webpacker: ./bin/webpack-dev-server # web: bundle exec thin start -p 3001 --ssl --ssl-key-file .ssl/localhost.key --ssl-cert-file .ssl/localhost.crt diff --git a/README.md b/README.md index ed44da3f8..c6665ad62 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Below is information related to developing the Quepid open source project, prima - [IV. Debugging](#iv-debugging) - [Debugging Ruby](#debugging-ruby) - [Debugging JS](#debugging-js) + - [Webpacker](#webpacker) - [Convenience Scripts](#convenience-scripts) - [Rake](#rake) - [Thor](#thor) @@ -49,6 +50,7 @@ Below is information related to developing the Quepid open source project, prima - [Dev Errata](#dev-errata) - [I'd like to use a new Node module](#id-like-to-use-a-new-node-module) - [I'd like to test SSL](#id-like-to-test-ssl) + - [I'd like to test OpenID Auth](#id-like-to-test-openid-auth) - [QA](#qa) - [Seed Data](#seed-data) - [Data Map](#data-map) @@ -124,6 +126,7 @@ You can still use `docker-compose` directly, but for the basic stuff you can use * Run back end unit tests: `bin/docker r rails test` + ## II. Development Log While running the app under foreman, you'll only see a request log, for more detailed logging run the following: @@ -278,6 +281,25 @@ Also please note that the files `secure.js`, `application.js`, and `admin.js` ar JavaScript and CSS dependencies via the Rails Asset pipeline. If you are debugging Bootstrap, then you will want individual files. So replace `//= require sprockets` with `//= require bootstrap-sprockets`. + +### Webpacker +To use webpacker, that will compile javascript code into packs and will load changes faster, +you need to + +```bash +bin/rails webpacker:install +``` + +Prior to that I had to install: + +```bash +brew install mysql +``` + +### Debugging Splainer and other NPM packages + +`docker-compose.override.yml.example` can be copied to `docker-compose.override.yml` and use it to override environment variables or work with a local copy of the splainer-search JS library during development defined in `docker-compose.yml`. Example is included. Just update the path to splainer-search with your local checkout! https://docs.docker.com/compose/extends/ + ## Convenience Scripts This application has two ways of running scripts: `rake` & `thor`. @@ -401,6 +423,8 @@ which will install/upgrade the Node module, and then save that dependency to `pa Then check in the updated `package.json` and `yarn.lock` files. +Use `bin/docker r yarn outdated` to see what packages you can update!!!! + ## I'd like to use a new Ruby Gem, or update a existing one Typically you would simply do: @@ -426,6 +450,12 @@ bin/docker r bundle remove foobar --install Then check in the updated `Gemfile` and `Gemfile.lock` files. For good measure run the `bin/setup_docker`. +To understand if you have gems that are out of date run: + +``` +bin/docker r bundle outdated --groups +``` + ## I'd like to test SSL @@ -455,6 +485,11 @@ What you need to do: **PS:** Why are we using both `puma` and `thin`? Because I simply could not figure out how to get `puma` to work properly with SSL and did not want to spend any more time on it! +## I'd like to test OpenID Auth + +Add dev docs here! + + ## Modifying the database Here is an example of generating a migration: @@ -474,6 +509,16 @@ bin/docker r bundle install You will see a updated `Gemfile.lock`, go ahead and check it and `Gemfile` into Git. +## How does the Frontend work? + +We use Angular 1 for the front end, and as part of that we use the `angular-ui-bootstrap` package +for all our UI components. This package is tied to Bootstrap version 3. We import the Bootstrap 3 +CSS directly via the file `bootstrap.css`. + +For the various Admin pages, we actually are using Bootstrap 5! That is included via the `package.json` using NPM. See `admin.js` for the line `//= require bootstrap/dist/js/bootstrap.bundle` which is where we are including. + +We currently use Rails Sprockets to compile everything, but do have dreams of moving the JavaScript +over to Webpacker. # QA diff --git a/UPGRADE_RAILS_SIX.md b/UPGRADE_RAILS_SIX.md new file mode 100644 index 000000000..ede619327 --- /dev/null +++ b/UPGRADE_RAILS_SIX.md @@ -0,0 +1,30 @@ +UPGRADE_RAILS_SIX + +Todo: +* DONE - Test out the SSL. We can remove the force_ssl methods. https://api.rubyonrails.org/classes/ActionDispatch/SSL.html +and https://github.com/rails/rails/pull/32277 and force_ssl if: :ssl_enabled? +* DONE - Check if the home_controller.rb needs a redirect to before_action :redirect_to_non_ssl ? - + Turns Out it does! + +* DONE - Confirm webpack actually being used. - IT'S NOT BEING USED, LETS WORRY ABOUT IT IN SEPERATE PR! +* DONE - The "loose" option must be the same for @babel/plugin-proposal-class-properties, @babel/plugin-proposal-private-methods and @babel/plugin-proposal-private-property-in-object (when they are enabled): you can silence this warning by explicitly adding + + ["@babel/plugin-proposal-private-methods", { "loose": true }] + +to the "plugins" section of your Babel config. + +* Get `webpack-dev-server` to run in dev mode to enable the autoreload. https://dev.to/vvo/a-rails-6-setup-guide-for-2019-and-2020-hf5 + +* DONE - check out running `docker r rails app:update` https://selleo.com/blog/how-to-upgrade-to-rails-6 + +* DONE - check https://railsdiff.org/5.2.3/6.1.4 + +* check on application.html.erb +* DONE - bin/spring? +* DONE - bin/rails? + +* Check on node versions for prod and dev +* DONE - test:frontend doesn't run in Docker. +* DONE - Bump to Ruby 2.7.4 right before the merge ;-) +* check on bootstrap 3 to bootstrap 4 http://upgrade-bootstrap.bootply.com/ +* Check on bootstrap 4 versus 5 https://designmodo.com/migrate-bootstrap-5/ diff --git a/app.json b/app.json index 24e9b747d..a107988d2 100644 --- a/app.json +++ b/app.json @@ -75,6 +75,12 @@ }, "EMAIL_SENDER": { "required": true + }, + "GOOGLE_CLIENT_ID": { + "required": true + }, + "GOOGLE_CLIENT_SECRET": { + "required": true } }, "formation": { diff --git a/app/assets/javascripts/components/add_member/add_member.html b/app/assets/javascripts/components/add_member/add_member.html index f2307eb2a..013be4c94 100644 --- a/app/assets/javascripts/components/add_member/add_member.html +++ b/app/assets/javascripts/components/add_member/add_member.html @@ -1,6 +1,6 @@ diff --git a/app/assets/javascripts/components/case_listing/case_listing.html b/app/assets/javascripts/components/case_listing/case_listing.html index 50bd296d6..b4c4cc341 100644 --- a/app/assets/javascripts/components/case_listing/case_listing.html +++ b/app/assets/javascripts/components/case_listing/case_listing.html @@ -20,7 +20,7 @@ - + Export Case: {{ ctrl.theCase.ca
- + +

+ + Detailed export is only supported from the individual Case view. +

CSV file with Team Name,Case Name,Case ID,Query Text,Doc ID,Title,Rating,Field1,...,FieldN where Field1,...,FieldN are specified under Settings in the Displayed Fields field. @@ -74,5 +78,5 @@
diff --git a/app/assets/javascripts/components/export_case/export_case_controller.js b/app/assets/javascripts/components/export_case/export_case_controller.js index f96ffb55e..3f43b324a 100644 --- a/app/assets/javascripts/components/export_case/export_case_controller.js +++ b/app/assets/javascripts/components/export_case/export_case_controller.js @@ -13,7 +13,7 @@ angular.module('QuepidApp') 'querySnapshotSvc', function ( $uibModal, - $rootScope, + $scope, $log, caseSvc, caseCSVSvc, @@ -22,18 +22,19 @@ angular.module('QuepidApp') ) { var ctrl = this; - this.iconOnly = $rootScope.iconOnly; + this.iconOnly = $scope.iconOnly; + this.supportsDetailedExport = $scope.supportsDetailedExport; - // If called from the cases listing page, $rootScope.theCase is populated, + // If called from the cases listing page, $scope.theCase is populated, // otherwise on the main page get it from the caseSvc. - if ($rootScope.theCase) { - ctrl.theCase = $rootScope.theCase; + if ($scope.theCase) { + ctrl.theCase = $scope.theCase; } else { ctrl.theCase = caseSvc.getSelectedCase(); } - $rootScope.$on('caseSelected', function() { + $scope.$on('caseSelected', function() { ctrl.theCase = caseSvc.getSelectedCase(); }); @@ -46,14 +47,17 @@ angular.module('QuepidApp') if ( options.which === 'general' ) { $log.info('Selected "general" as export option.'); - $log.info(ctrl.theCase); - csv = caseCSVSvc.stringify(ctrl.theCase, true); - blob = new Blob([csv], { - type: 'text/csv' - }); - /*global saveAs */ - saveAs(blob, caseCSVSvc.formatDownloadFileName(ctrl.theCase.caseName + '_general.csv')); + // Go back to the API in case other users have updates that we should include. + caseSvc.get(ctrl.theCase.caseNo, false).then(function(acase) { + csv = caseCSVSvc.stringify(acase, true); + blob = new Blob([csv], { + type: 'text/csv' + }); + + /*global saveAs */ + saveAs(blob, caseCSVSvc.formatDownloadFileName(acase.caseName + '_general.csv')); + }); } else if ( options.which === 'detailed' ) { $log.info('Selected "detailed" as export option.'); @@ -129,6 +133,9 @@ angular.module('QuepidApp') resolve: { theCase: function() { return ctrl.theCase; + }, + supportsDetailedExport: function() { + return ctrl.supportsDetailedExport; } } }); diff --git a/app/assets/javascripts/components/export_case/export_case_directive.js b/app/assets/javascripts/components/export_case/export_case_directive.js index 55439f178..470c9ed1d 100644 --- a/app/assets/javascripts/components/export_case/export_case_directive.js +++ b/app/assets/javascripts/components/export_case/export_case_directive.js @@ -10,7 +10,8 @@ angular.module('QuepidApp') templateUrl: 'export_case/export_case.html', scope: { theCase: '=', - iconOnly: '=' + iconOnly: '=', + supportsDetailedExport: '=' } }; } diff --git a/app/assets/javascripts/components/export_case/export_case_modal_instance_controller.js b/app/assets/javascripts/components/export_case/export_case_modal_instance_controller.js index ea51feeee..90339fb2c 100644 --- a/app/assets/javascripts/components/export_case/export_case_modal_instance_controller.js +++ b/app/assets/javascripts/components/export_case/export_case_modal_instance_controller.js @@ -6,10 +6,12 @@ angular.module('QuepidApp') '$uibModalInstance', 'querySnapshotSvc', 'theCase', - function ($scope, $uibModalInstance, querySnapshotSvc, theCase) { + 'supportsDetailedExport', + function ($scope, $uibModalInstance, querySnapshotSvc, theCase, supportsDetailedExport) { var ctrl = this; ctrl.theCase = theCase; + ctrl.supportsDetailedExport = supportsDetailedExport; // If called from the cases listing page, then we need the call back with the bootstrap, // otherwise on the main page the querySnapshotSvc.snapshots was bootstrapped. diff --git a/app/assets/javascripts/components/user_listing/user_listing.html b/app/assets/javascripts/components/user_listing/user_listing.html index 2e9c67a02..f77838143 100644 --- a/app/assets/javascripts/components/user_listing/user_listing.html +++ b/app/assets/javascripts/components/user_listing/user_listing.html @@ -4,6 +4,7 @@ ng-src="{{ ctrl.user.avatar_url }}" class="img-responsive img-rounded" alt="{{ ctrl.user.display_name }}" + width="48" height="48" /> diff --git a/app/assets/javascripts/controllers/login.js b/app/assets/javascripts/controllers/login.js deleted file mode 100644 index 161e4a6d5..000000000 --- a/app/assets/javascripts/controllers/login.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -angular.module('QuepidSecureApp') - .controller('LoginCtrl', [ - '$scope', '$location', - 'loginSvc', - function ($scope, $location, loginSvc) { - $scope.submit = function (user, pass) { - $scope.warnDefined = false; - $scope.warnErr = false; - $scope.warnInvalid = false; - $scope.warnLocked = false; - - loginSvc.login(user, pass, function(response) { // need to define an error handler - if( response.status/1 === 404 ) { - $scope.warnUndefined = true; - return; - } - - if ( response.status/1 === 400 ) { - $scope.warnInvalid = true; - return; - } - - if ( response.status/1 === 422 && response.data.reason === 'LOCKED' ) { - $scope.warnLocked = true; - return; - } - - $scope.warnErr = true; - }); //handle error - already a user - }; - } - ]); diff --git a/app/assets/javascripts/controllers/scorer.js b/app/assets/javascripts/controllers/scorer.js index 439145ab7..b3b5da3c6 100644 --- a/app/assets/javascripts/controllers/scorer.js +++ b/app/assets/javascripts/controllers/scorer.js @@ -77,7 +77,7 @@ angular.module('QuepidApp') }); }); } else { - console.log('Is this dead code path?'); + $log.info('Is this dead code path?'); caseSvc.saveDefaultScorer(caseNo) .then(function() { // TODO move to customer scorer svc, needs major updates to queriessvc first diff --git a/app/assets/javascripts/controllers/searchResult.js b/app/assets/javascripts/controllers/searchResult.js index eead38b60..41df0c0a4 100644 --- a/app/assets/javascripts/controllers/searchResult.js +++ b/app/assets/javascripts/controllers/searchResult.js @@ -68,7 +68,6 @@ angular.module('QuepidApp') }; $scope.showDetailed = function() { - console.log('ive been pressed'); $uibModal.open({ templateUrl: 'views/detailedExplain.html', controller: 'DocExplainCtrl', diff --git a/app/assets/javascripts/controllers/signup.js b/app/assets/javascripts/controllers/signup.js deleted file mode 100644 index 9d45aa8c7..000000000 --- a/app/assets/javascripts/controllers/signup.js +++ /dev/null @@ -1,60 +0,0 @@ -'use strict'; - -angular.module('QuepidSecureApp') - .controller('SignupCtrl', [ - '$scope', '$location', - 'signupSvc', - 'configurationSvc', - function ($scope, $location, signupSvc, configurationSvc) { - $scope.hasTermsAndConditions = configurationSvc.hasTermsAndConditions(); - if (configurationSvc.hasTermsAndConditions()){ - $scope.termsAndConditionsUrl = configurationSvc.getTermsAndConditionsUrl(); - } - - $scope.isEmailMarketingMode = configurationSvc.isEmailMarketingMode(); - $scope.isSignupEnabled = configurationSvc.isSignupEnabled(); - - $scope.submit = function (agree, emailMarketingAgree, name, email, pass, confirm) { - $scope.warnAgree = false; - $scope.warnEmail = false; - $scope.warnPass = false; - $scope.warnDefined = false; - $scope.warnErr = false; - $scope.warnName = false; - - - if ( !name ) { - $scope.warnName = true; - return; - } - - if( $scope.hasTermsAndConditions && !agree ) { - $scope.warnAgree = true; - return; - } - if( pass !== confirm ) { - $scope.warnPass = true; - return; - } - - - const emailVer = /^(([^<>()[\]\\.,;:\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,}))$/; - if( !emailVer.test(email) ){ - $scope.warnEmail = true; - return; - } - - var user = { - email: email, - name: name, - password: pass, - email_marketing: emailMarketingAgree - }; - - signupSvc.createUser(user, function creationError() { - $scope.warnDefined = true; - return; - }); //handle error - already a user - }; - } - ]); diff --git a/app/assets/javascripts/controllers/wizardModal.js b/app/assets/javascripts/controllers/wizardModal.js index 64d863778..c3ec2b2be 100644 --- a/app/assets/javascripts/controllers/wizardModal.js +++ b/app/assets/javascripts/controllers/wizardModal.js @@ -213,8 +213,6 @@ angular.module('QuepidApp') angular.merge($scope.pendingWizardSettings, settingsSvc.editableSettings()); $scope.pendingWizardSettings.newQueries = []; - console.log('User completedCaseWizard is ' + userSvc.getUser().completedCaseWizard); - if(userSvc.getUser().completedCaseWizard===false){ $scope.pendingWizardSettings.caseName = 'Movies Search'; // should we be setting up more here? diff --git a/app/assets/javascripts/factories/TryFactory.js b/app/assets/javascripts/factories/TryFactory.js index 5778b46e9..5b1b38dd7 100644 --- a/app/assets/javascripts/factories/TryFactory.js +++ b/app/assets/javascripts/factories/TryFactory.js @@ -6,12 +6,14 @@ angular.module('QuepidApp') .factory('TryFactory', [ '$http', + '$log', 'fieldSpecSvc', 'caseTryNavSvc','varExtractorSvc', TryFactory ]); function TryFactory( $http, + $log, fieldSpecSvc, caseTryNavSvc, varExtractorSvc ) { var Try = function(data) { @@ -19,8 +21,7 @@ var self = this; if ( angular.isUndefined(data.search_engine) ) { - console.log('We have an undefined data.search_engine so setting to Solr, should this ever happen?'); - console.log(data); + $log.info('We have an undefined data.search_engine so setting to Solr, should this ever happen?'); data.search_engine = 'solr'; } diff --git a/app/assets/javascripts/home.js b/app/assets/javascripts/home.js index d171e9ef3..d82d618b4 100644 --- a/app/assets/javascripts/home.js +++ b/app/assets/javascripts/home.js @@ -1,4 +1,4 @@ -// This is a manifest file that'll be compiled into application.js, which will include all the files +// This is a manifest file that'll be compiled into home.js, which will include all the files // listed below. // // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, @@ -60,7 +60,6 @@ //= require FileSaver.js/FileSaver //= require urijs/src/URI //= require utilitiesModule -//= require secureApp //= require app //= require routes //= require_tree ./components diff --git a/app/assets/javascripts/mockBackend.js b/app/assets/javascripts/mockBackend.js index c699ffcba..2a67dfd24 100644 --- a/app/assets/javascripts/mockBackend.js +++ b/app/assets/javascripts/mockBackend.js @@ -283,4 +283,4 @@ window.mockBackend = function(angModule) { }; // declare for testing -angular.module('QuepidTest', ['ngMock', 'ngRoute', 'QuepidApp', 'QuepidSecureApp']); +angular.module('QuepidTest', ['ngMock', 'ngRoute', 'QuepidApp']); diff --git a/app/assets/javascripts/secure.js b/app/assets/javascripts/secure.js deleted file mode 100755 index 0a06b2cb1..000000000 --- a/app/assets/javascripts/secure.js +++ /dev/null @@ -1,36 +0,0 @@ -// This is a manifest file that'll be compiled into secure.js, which will include all the files -// listed below. -// -// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, -// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. -// -// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the -// compiled file. -// -// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details -// about supported directives. -// -//= require jquery -//= require angular/angular - -//= require angular-resource/angular-resource -//= require angular-cookies/angular-cookies -//= require angular-route/angular-route -//= require angular-sanitize/angular-sanitize -//= require angular-ui-bootstrap/dist/ui-bootstrap -//= require angular-ui-bootstrap/dist/ui-bootstrap-tpls -//= require angular-flash/dist/angular-flash.js -//= require angular-rails-templates -//= require utilitiesModule -//= require secureApp -//= require app -//= require services/configurationSvc -//= require services/secureRedirectSvc -//= require services/loginSvc -//= require services/userSvc -//= require services/signupSvc -//= require controllers/login -//= require controllers/signup -//= require controllers/404Ctrl -//= require_tree ./interceptors -//= require_tree ../templates diff --git a/app/assets/javascripts/secureApp.js b/app/assets/javascripts/secureApp.js deleted file mode 100644 index 89f2e9c09..000000000 --- a/app/assets/javascripts/secureApp.js +++ /dev/null @@ -1,24 +0,0 @@ -'use strict'; - -try { - angular.module('QuepidSecureApp'); -} -catch (e) { - angular.module('QuepidSecureApp', ['UtilitiesModule', 'ngRoute', 'ng-rails-csrf', 'templates']) - .config([ - '$locationProvider', '$routeProvider', - function ($locationProvider, $routeProvider) { - $locationProvider.html5Mode(true); - - $routeProvider - .when('/', { - controller: 'SignupCtrl', - templateUrl: 'views/signup.html' - }) - .otherwise({ - templateUrl: 'views/404.html', - controller: '404Ctrl' - }); - } - ]); -} diff --git a/app/assets/javascripts/services/bestFetcherSvc.js b/app/assets/javascripts/services/bestFetcherSvc.js index a386918cf..8b111e54a 100644 --- a/app/assets/javascripts/services/bestFetcherSvc.js +++ b/app/assets/javascripts/services/bestFetcherSvc.js @@ -3,7 +3,11 @@ angular.module('QuepidApp') .service('bestFetcherSvc', [ 'docCacheSvc', - function bestFetcherSvc(docCacheSvc) { + '$log', + function bestFetcherSvc( + docCacheSvc, + $log + ) { var BestFetcher = function(ratingsStore) { this.docs = []; @@ -41,11 +45,11 @@ angular.module('QuepidApp') return docB.getRating() - docA.getRating(); }); }, function(response) { - console.log('Got an error from docCacheSvc.update: ', response); + $log.info('Got an error from docCacheSvc.update: ', response); return response; }) .catch(function(response) { - console.log('Got an error from docCacheSvc.update: ', response); + $log.info('Got an error from docCacheSvc.update: ', response); return response; }); }; diff --git a/app/assets/javascripts/services/caseCSVSvc.js b/app/assets/javascripts/services/caseCSVSvc.js index 0ced84f80..db45a8dc9 100644 --- a/app/assets/javascripts/services/caseCSVSvc.js +++ b/app/assets/javascripts/services/caseCSVSvc.js @@ -254,7 +254,10 @@ } var firstQuery = queries[Object.keys(queries)[0]]; - var fields = firstQuery.fieldSpec().subs; + var fields = firstQuery.fieldSpec().fields; + // subtract the mandatory fields from our list of fields to be written out + fields.splice(fields.indexOf(firstQuery.fieldSpec().id),1); + fields.splice(fields.indexOf(firstQuery.fieldSpec().title),1); if (withHeader) { csvContent += self.detailedQueriesHeaderToCSV(fields); @@ -276,7 +279,7 @@ infoArray.push(stringifyField(doc.getRating())); angular.forEach(fields, function(field) { - infoArray.push(stringifyField(doc.subs[field])); + infoArray.push(stringifyField(doc.doc[field])); }); dataString = infoArray.join(','); diff --git a/app/assets/javascripts/services/configurationSvc.js b/app/assets/javascripts/services/configurationSvc.js index 35bf464db..ab5c0e3da 100644 --- a/app/assets/javascripts/services/configurationSvc.js +++ b/app/assets/javascripts/services/configurationSvc.js @@ -2,47 +2,10 @@ angular.module('UtilitiesModule') .service('configurationSvc', [ - '$window', '$log', function ConfigurationSvc() { - var emailMarketingMode; - var termsAndConditionsUrl; - var isSignupEnabled; var communalScorersOnly; var queryListSortable; - this.setEmailMarketingMode = function(val) { - emailMarketingMode = JSON.parse(val); - }; - - this.isEmailMarketingMode = function() { - return emailMarketingMode; - }; - - this.setTermsAndConditionsUrl = function (url) { - termsAndConditionsUrl = url; - }; - - this.hasTermsAndConditions = function () { - if (angular.isUndefined(termsAndConditionsUrl) || termsAndConditionsUrl === ''){ - return false; - } - else { - return true; - } - - }; - - this.getTermsAndConditionsUrl = function () { - return termsAndConditionsUrl; - }; - - this.setSignupEnabled = function (val) { - isSignupEnabled = JSON.parse(val); - }; - - this.isSignupEnabled = function() { - return isSignupEnabled; - }; this.setCommunalScorersOnly = function(val) { communalScorersOnly = JSON.parse(val); diff --git a/app/assets/javascripts/services/docCacheSvc.js b/app/assets/javascripts/services/docCacheSvc.js index 8ac54987e..2d14f2c8e 100644 --- a/app/assets/javascripts/services/docCacheSvc.js +++ b/app/assets/javascripts/services/docCacheSvc.js @@ -3,8 +3,13 @@ angular.module('QuepidApp') .service('docCacheSvc', [ '$q', + '$log', 'docResolverSvc', - function docCacheSvc($q, docResolverSvc) { + function docCacheSvc( + $q, + $log, + docResolverSvc + ) { var docCache = {}; this.addIds = function(moreIds) { @@ -57,11 +62,11 @@ angular.module('QuepidApp') docCache[doc.id] = doc; }); }, function(response) { - console.log('Error fetching Docs in docCacheSvc: ', response); + $log.info('Error fetching Docs in docCacheSvc: ', response); return response; }) .catch(function(response) { - console.log('Got an error from docCacheSvc: ', response); + $log.info('Got an error from docCacheSvc: ', response); return response; }); } else { diff --git a/app/assets/javascripts/services/loginSvc.js b/app/assets/javascripts/services/loginSvc.js deleted file mode 100644 index f22e3aa0a..000000000 --- a/app/assets/javascripts/services/loginSvc.js +++ /dev/null @@ -1,20 +0,0 @@ -'use strict'; - -// When your user needs to log in. -angular.module('QuepidSecureApp') - .service('loginSvc', [ - '$window', '$http', - 'secureRedirectSvc', - function loginSvc($window, $http, secureRedirectSvc) { - this.login = function(email, password, errorHandler, nextUrl) { - if(!errorHandler) { - errorHandler = function(){}; - } - - $http.post('/users/login', {'email':email, 'password':password}) - .then( function() { - secureRedirectSvc.redirectToMain(nextUrl); - }, errorHandler); - }; - } - ]); diff --git a/app/assets/javascripts/services/secureRedirectSvc.js b/app/assets/javascripts/services/secureRedirectSvc.js deleted file mode 100644 index 3deda1bd5..000000000 --- a/app/assets/javascripts/services/secureRedirectSvc.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -angular.module('UtilitiesModule') - .service('secureRedirectSvc', [ - '$window', '$log', - function SecureRedirectSvc($window, $log) { - var scheme = 'https://'; - var port = '443'; - this.debugServer = function(debugPort) { - port = debugPort.toString(); - scheme = 'http://'; - $log.debug('USING THE DEBUG SERVER: ' + scheme + ' port: ' + port); - }; - this.redirectToSecure = function (path) { - $window.location = scheme + $window.location.hostname + ':' + port + '/secure' + (path || ''); - }; - this.redirectToMain = function (path) { - $window.location = 'http://' + $window.location.host + (path || ''); - }; - } - ]); diff --git a/app/assets/javascripts/services/signupSvc.js b/app/assets/javascripts/services/signupSvc.js deleted file mode 100644 index 24e1320bc..000000000 --- a/app/assets/javascripts/services/signupSvc.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -// When your user needs to log in. -angular.module('QuepidSecureApp') - .service('signupSvc', [ - '$location', '$http', - 'loginSvc', - function signupSvc($location, $http, loginSvc) { - var self = this; - - this.signupSuccess = function(email, password) { - return function() { - loginSvc.login(email, password); - }; - }; - - this.createUser = function(user, errorHandler) { - if(!errorHandler) { - errorHandler = function() {}; - } - - user.agreed = true; - - var user_params = { - user: user - }; - - $http.post('/api/signups', user_params) - .then( - self.signupSuccess(user.email, user.password), - errorHandler - ); - }; - } - ]); diff --git a/app/assets/javascripts/services/userSvc.js b/app/assets/javascripts/services/userSvc.js index 5c0ecaa3c..13b8315d4 100644 --- a/app/assets/javascripts/services/userSvc.js +++ b/app/assets/javascripts/services/userSvc.js @@ -28,17 +28,6 @@ angular.module('UtilitiesModule') self.casesInvolvedWithCount = userObj.cases_involved_with_count; self.teamsInvolvedWithCount = userObj.teams_involved_with_count; - this.updatePassword = function(oldPass, newPass, success, failure) { - $http.post('/api/users/' + self.id, { - oldPassword:oldPass, - newPassword:newPass - }) - .then( - success || function(){}, - failure || function(){} - ); - }; - this.shownIntroWizard = function() { var self = this; self.introWizardSeen=true; diff --git a/app/assets/javascripts/start.js b/app/assets/javascripts/start.js new file mode 100644 index 000000000..9dfa9f336 --- /dev/null +++ b/app/assets/javascripts/start.js @@ -0,0 +1,13 @@ +// This is a manifest file that'll be compiled into start.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require jquery diff --git a/app/assets/stylesheets/signup.css b/app/assets/stylesheets/signup.css index 40a8585d3..9ac630eef 100644 --- a/app/assets/stylesheets/signup.css +++ b/app/assets/stylesheets/signup.css @@ -25,3 +25,42 @@ h3 { .m-t-20 { margin-top: 20px !important; } + +/* + * Social Buttons for Bootstrap + * + * Copyright 2013-2016 Panayiotis Lipiridis + * Licensed under the MIT License + * + * https://github.com/lipis/bootstrap-social + */ + +/* + * We only include the ones for Google and OpenId, but maybe we should include all of them! + */ + + +.btn-social{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.btn-social>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} +.btn-social.btn-lg{padding-left:61px}.btn-social.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em} +.btn-social.btn-sm{padding-left:38px}.btn-social.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em} +.btn-social.btn-xs{padding-left:30px}.btn-social.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em} +.btn-social-icon{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:34px;width:34px;padding:0}.btn-social-icon>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} +.btn-social-icon.btn-lg{padding-left:61px}.btn-social-icon.btn-lg>:first-child{line-height:45px;width:45px;font-size:1.8em} +.btn-social-icon.btn-sm{padding-left:38px}.btn-social-icon.btn-sm>:first-child{line-height:28px;width:28px;font-size:1.4em} +.btn-social-icon.btn-xs{padding-left:30px}.btn-social-icon.btn-xs>:first-child{line-height:20px;width:20px;font-size:1.2em} +.btn-social-icon>:first-child{border:none;text-align:center;width:100% !important} +.btn-social-icon.btn-lg{height:45px;width:45px;padding-left:0;padding-right:0} +.btn-social-icon.btn-sm{height:30px;width:30px;padding-left:0;padding-right:0} +.btn-social-icon.btn-xs{height:22px;width:22px;padding-left:0;padding-right:0} +.btn-google{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}.btn-google:focus,.btn-google.focus{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)} +.btn-google:hover{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)} +.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)}.btn-google:active:hover,.btn-google.active:hover,.open>.dropdown-toggle.btn-google:hover,.btn-google:active:focus,.btn-google.active:focus,.open>.dropdown-toggle.btn-google:focus,.btn-google:active.focus,.btn-google.active.focus,.open>.dropdown-toggle.btn-google.focus{color:#fff;background-color:#a32b1c;border-color:rgba(0,0,0,0.2)} +.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{background-image:none} +.btn-google.disabled:hover,.btn-google[disabled]:hover,fieldset[disabled] .btn-google:hover,.btn-google.disabled:focus,.btn-google[disabled]:focus,fieldset[disabled] .btn-google:focus,.btn-google.disabled.focus,.btn-google[disabled].focus,fieldset[disabled] .btn-google.focus{background-color:#dd4b39;border-color:rgba(0,0,0,0.2)} +.btn-google .badge{color:#dd4b39;background-color:#fff} +.btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,0.2)}.btn-openid:focus,.btn-openid.focus{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)} +.btn-openid:hover{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)} +.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)}.btn-openid:active:hover,.btn-openid.active:hover,.open>.dropdown-toggle.btn-openid:hover,.btn-openid:active:focus,.btn-openid.active:focus,.open>.dropdown-toggle.btn-openid:focus,.btn-openid:active.focus,.btn-openid.active.focus,.open>.dropdown-toggle.btn-openid.focus{color:#fff;background-color:#b86607;border-color:rgba(0,0,0,0.2)} +.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{background-image:none} +.btn-openid.disabled:hover,.btn-openid[disabled]:hover,fieldset[disabled] .btn-openid:hover,.btn-openid.disabled:focus,.btn-openid[disabled]:focus,fieldset[disabled] .btn-openid:focus,.btn-openid.disabled.focus,.btn-openid[disabled].focus,fieldset[disabled] .btn-openid.focus{background-color:#f7931e;border-color:rgba(0,0,0,0.2)} +.btn-openid .badge{color:#f7931e;background-color:#fff} diff --git a/app/assets/stylesheets/secure.css.scss b/app/assets/stylesheets/start.css.scss old mode 100755 new mode 100644 similarity index 51% rename from app/assets/stylesheets/secure.css.scss rename to app/assets/stylesheets/start.css.scss index ec586dd54..ed7e31216 --- a/app/assets/stylesheets/secure.css.scss +++ b/app/assets/stylesheets/start.css.scss @@ -1,11 +1,14 @@ /* - * This is a manifest file that'll be compiled into secure.css, which will include all the files + * This is a manifest file that'll be compiled into start.css, which will include all the files * listed below. */ @import "style"; @import "bootstrap"; +@import "font-awesome-sprockets"; +@import "font-awesome"; + @import "cookies_eu"; @import "fonts"; diff --git a/app/assets/templates/views/detailedDoc.html b/app/assets/templates/views/detailedDoc.html index cf440d479..e9ce4e233 100644 --- a/app/assets/templates/views/detailedDoc.html +++ b/app/assets/templates/views/detailedDoc.html @@ -23,6 +23,25 @@

{{doc.title}}

+
+
{{fieldName}}
+
{{fieldValue}}
+
+ +
+
{{fieldName}}
+
{{fieldValue}}
+
+ +
+
Thumb
+
{{doc.thumb}}
+
+
+
Image
+
{{doc.image}}
+
+ View Document diff --git a/app/assets/templates/views/queriesLayout.html b/app/assets/templates/views/queriesLayout.html index bfe3d81ca..bff2fc3ca 100644 --- a/app/assets/templates/views/queriesLayout.html +++ b/app/assets/templates/views/queriesLayout.html @@ -98,6 +98,7 @@

+ +
  • + {{fieldName}}: + + + + +
  • +
  • {{fieldName}}: diff --git a/app/assets/templates/views/signup.html b/app/assets/templates/views/signup.html deleted file mode 100644 index 35783ed6e..000000000 --- a/app/assets/templates/views/signup.html +++ /dev/null @@ -1,131 +0,0 @@ -
    -
    -
    -

    Login

    -
    -
    -
    - Unknown email/password. Try again or sign up! -
    -
    - Unknown email/password combo. Double check you have the correct email address and password, or sign up for a new account. -
    -
    - Please provide a valid email address and password. -
    -
    - Your account is locked. -
    - - -
    - -
    - -
    - -
    -
    -
    -
    - -
    -

    OR

    -
    - -
    -
    -
    -

    Signup to get started!

    - -
    - Something weird happened. Contact us for assistance -
    - -
    - You must enter an email address. -
    - -
    - The email you entered is already registered! -
    - -
    - The passwords do not match! -
    - -
    - Please provide your full name, so we can properly address you :) -
    - -
    - You must agree to the terms and conditions. -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - -
    - - - -
    -
    -
    -
    - -
    -
    -

    - Read more about the Quepid story. Contact us - to talk about Quepid or ask how we can help with your search problems! -

    -
    -
    -
    diff --git a/app/assets/templates/views/teams/show.html b/app/assets/templates/views/teams/show.html index 90e7a26e7..042f938e7 100644 --- a/app/assets/templates/views/teams/show.html +++ b/app/assets/templates/views/teams/show.html @@ -14,6 +14,7 @@

    Team: {{ currentTeam.name }}

    ng-src="{{ currentTeam.owner.avatar_url }}" class="img-responsive img-rounded" alt="{{ currentTeam.owner.display_name }}" + width="96" height="96" />

    diff --git a/app/assets/templates/views/wizardModal.html b/app/assets/templates/views/wizardModal.html index d7bc2efa0..c4634f1dc 100644 --- a/app/assets/templates/views/wizardModal.html +++ b/app/assets/templates/views/wizardModal.html @@ -53,9 +53,8 @@

    Solr or Elasticsearch?

    • Do you see any errors when you visit your Solr URL directly?
    • -
    • Will your request handler accept the JSONP writer type? Or does it only return XML or JSON?
    • - If you are using Solr 8.4 or later, you may need to tweak the content-type being set by wt=json. + If you are using a version of Solr between 8.4 and 9.0, you may need to tweak the content-type being set by wt=json. You can do this via the below curl command:
       curl -X POST -H 'Content-type:application/json' -d '{
      diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb
      new file mode 100644
      index 000000000..9aec23053
      --- /dev/null
      +++ b/app/channels/application_cable/channel.rb
      @@ -0,0 +1,6 @@
      +# frozen_string_literal: true
      +
      +module ApplicationCable
      +  class Channel < ActionCable::Channel::Base
      +  end
      +end
      diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb
      new file mode 100644
      index 000000000..8d6c2a1bf
      --- /dev/null
      +++ b/app/channels/application_cable/connection.rb
      @@ -0,0 +1,6 @@
      +# frozen_string_literal: true
      +
      +module ApplicationCable
      +  class Connection < ActionCable::Connection::Base
      +  end
      +end
      diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
      index 769013881..8b4a191e4 100644
      --- a/app/controllers/accounts_controller.rb
      +++ b/app/controllers/accounts_controller.rb
      @@ -1,8 +1,6 @@
       # frozen_string_literal: true
       
       class AccountsController < ApplicationController
      -  force_ssl if: :ssl_enabled?
      -
         # rubocop:disable Metrics/MethodLength
         def update
           @user = current_user
      @@ -41,7 +39,7 @@ def destroy
       
           respond_to do |format|
             if @user.destroy
      -        format.html { redirect_to secure_url, notice: 'Account was deleted' }
      +        format.html { redirect_to sessions_path, notice: 'Account was deleted' }
               format.json { head :no_content }
             else
               format.html { render 'profiles/show' }
      diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
      index f8835c6df..06065b78b 100644
      --- a/app/controllers/admin/users_controller.rb
      +++ b/app/controllers/admin/users_controller.rb
      @@ -70,11 +70,13 @@ def set_user
             @user = User.find(params[:id])
           end
       
      -    # Never trust parameters from the scary internet, only allow the white list through.
      +    # Never trust parameters from the scary internet, only allow the permitted list through.
           def user_params
             params.require(:user).permit(
               :administrator,
      -        :email
      +        :email,
      +        :password,
      +        :password_confirmation
             )
           end
         end
      diff --git a/app/controllers/api/v1/cases/dropdown_controller.rb b/app/controllers/api/v1/cases/dropdown_controller.rb
      index 90fe85777..8ff92168b 100644
      --- a/app/controllers/api/v1/cases/dropdown_controller.rb
      +++ b/app/controllers/api/v1/cases/dropdown_controller.rb
      @@ -19,7 +19,7 @@ def index
                   LEFT OUTER JOIN `teams` ON `teams`.`id` = `teams_cases`.`team_id`
                   LEFT OUTER JOIN `teams_members` ON `teams_members`.`team_id` = `teams`.`id`
                   LEFT OUTER JOIN `users` ON `users`.`id` = `teams_members`.`member_id`
      -            WHERE (`teams_members`.`member_id` = #{current_user.id} OR `cases`.`user_id` = #{current_user.id})
      +            WHERE (`teams_members`.`member_id` = #{current_user.id} OR `cases`.`owner_id` = #{current_user.id})
                   AND (`cases`.`archived` = false OR `cases`.`archived` IS NULL)
                   ORDER BY `case_metadata`.`last_viewed_at` DESC, `cases`.`id` DESC
                   LIMIT 3
      diff --git a/app/controllers/api/v1/cases_controller.rb b/app/controllers/api/v1/cases_controller.rb
      index be29327b8..0f19f795f 100644
      --- a/app/controllers/api/v1/cases_controller.rb
      +++ b/app/controllers/api/v1/cases_controller.rb
      @@ -20,7 +20,7 @@ def index
               if archived
                 @no_tries = true
                 @no_teams = true
      -          @cases = Case.where(archived: archived, user_id: current_user.id).all
      +          @cases = Case.where(archived: archived, owner_id: current_user.id).all
               else
                 @cases = if 'last_viewed_at' == sort_by
                            current_user.cases_involved_with.not_archived.includes(:metadata).references(:metadata)
      diff --git a/app/controllers/api/v1/export/ratings_controller.rb b/app/controllers/api/v1/export/ratings_controller.rb
      index 26c2b2cee..f43f2d273 100644
      --- a/app/controllers/api/v1/export/ratings_controller.rb
      +++ b/app/controllers/api/v1/export/ratings_controller.rb
      @@ -27,9 +27,9 @@ def show
       
                 respond_to do |format|
                   format.json do
      -              json_template = file_format.nil? ? 'show.json.jbuilder' : "show.#{file_format.downcase}.json.jbuilder"
      +              json_template = file_format.nil? ? 'show' : "show_#{file_format.downcase}"
       
      -              render json_template
      +              render json_template, formats: :json
                   end
                   format.csv do
                     # We have crazy rendering formatting in the view because we don't want a trailing LF at the end of the
      diff --git a/app/controllers/api/v1/import/ratings_controller.rb b/app/controllers/api/v1/import/ratings_controller.rb
      index f28b5774e..cc5761732 100644
      --- a/app/controllers/api/v1/import/ratings_controller.rb
      +++ b/app/controllers/api/v1/import/ratings_controller.rb
      @@ -81,7 +81,7 @@ def create
                 rescue Exception => e
                   # TODO: report this to logging infrastructure so we won't lose any important
                   # errors that we might have to fix.
      -            Rails.logger.debug "Import ratings failed: #{e.inspect}"
      +            Rails.logger.debug { "Import ratings failed: #{e.inspect}" }
       
                   render json: { message: e.message }, status: :bad_request
                 end
      diff --git a/app/controllers/api/v1/teams_controller.rb b/app/controllers/api/v1/teams_controller.rb
      index 9ffd2116e..13971df39 100644
      --- a/app/controllers/api/v1/teams_controller.rb
      +++ b/app/controllers/api/v1/teams_controller.rb
      @@ -12,7 +12,7 @@ def index
               # @teams = current_user.teams_im_in
               # @teams = @teams.preload(:scorers, :members, :cases, :owner).all
               # There may be some more fields we could include...
      -        @teams = current_user.teams.includes( :owner, :members, :teams_scorers, :cases, scorers: [ :teams ] ).all
      +        @teams = current_user.teams.includes( :owner, :members, :cases, scorers: [ :teams ] ).all
       
               respond_with @teams
             end
      diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
      index 3167f50b3..7d4b575ad 100755
      --- a/app/controllers/application_controller.rb
      +++ b/app/controllers/application_controller.rb
      @@ -20,10 +20,6 @@ class ApplicationController < ActionController::Base
       
         private
       
      -  def ssl_enabled?
      -    'true' == ENV['FORCE_SSL']
      -  end
      -
         def signup_enabled?
           Rails.application.config.signup_enabled
         end
      diff --git a/app/controllers/concerns/authentication/current_user_manager.rb b/app/controllers/concerns/authentication/current_user_manager.rb
      index db1cdd1f8..b17208bd6 100644
      --- a/app/controllers/concerns/authentication/current_user_manager.rb
      +++ b/app/controllers/concerns/authentication/current_user_manager.rb
      @@ -43,7 +43,7 @@ def set_current_user
           end
       
           def require_login
      -      redirect_to secure_path unless @current_user
      +      redirect_to sessions_path unless @current_user
           end
       
           def auto_login user
      diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb
      index be17929ce..14609a683 100755
      --- a/app/controllers/home_controller.rb
      +++ b/app/controllers/home_controller.rb
      @@ -1,6 +1,10 @@
       # frozen_string_literal: true
       
       class HomeController < ApplicationController
      +  # If Quepid is running on HTTPS, like on Heroku, then it needs to switch
      +  # to HTTP in order to make calls to a Solr that is running in HTTP as well, otherwise
      +  # you get this "Mixed Content", which browsers block as a security issue.
      +  # https://developer.mozilla.org/en-US/docs/Web/Security/Mixed_content
         before_action :redirect_to_non_ssl
       
         def index
      diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
      index 854497c2a..547925bf0 100644
      --- a/app/controllers/profiles_controller.rb
      +++ b/app/controllers/profiles_controller.rb
      @@ -1,7 +1,6 @@
       # frozen_string_literal: true
       
       class ProfilesController < ApplicationController
      -  force_ssl if: :ssl_enabled?
         layout 'account'
       
         def show; end
      diff --git a/app/controllers/secure_controller.rb b/app/controllers/secure_controller.rb
      index ba9b7812d..588fbc127 100644
      --- a/app/controllers/secure_controller.rb
      +++ b/app/controllers/secure_controller.rb
      @@ -1,7 +1,6 @@
       # frozen_string_literal: true
       
       class SecureController < ApplicationController
      -  force_ssl if: :ssl_enabled?
         skip_before_action :require_login
       
         def index
      diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
      index 3d49abd89..79528efee 100644
      --- a/app/controllers/sessions_controller.rb
      +++ b/app/controllers/sessions_controller.rb
      @@ -1,17 +1,32 @@
       # frozen_string_literal: true
       
       class SessionsController < ApplicationController
      -  force_ssl if: :ssl_enabled?
      -  skip_before_action :require_login,              only: :create
      +  skip_before_action :require_login,              only: [ :create, :index ]
         skip_before_action :check_current_user_locked!, only: :create
         skip_before_action :verify_authenticity_token,  only: :create
       
      +  layout 'start'
      +
      +  def index
      +    @user = User.new
      +  end
      +
         def create
      -    user = login(params[:email], params[:password])
      +    login_params = user_params
      +
      +    @user = login(login_params[:email], login_params[:password])
           respond_to do |format|
      -      if user
      +      if @user
      +        format.html { redirect_to root_path }
               format.json { render json: { message: 'connected' }, status: :ok }
             else
      +        @user = User.new(email: login_params[:email])
      +
      +        # rubocop:disable Layout/LineLength
      +        @user.errors.add(:base,
      +                         'Unknown email/password combo. Double check you have the correct email address and password, or sign up for a new account.' )
      +        # rubocop:enable Layout/LineLength
      +        format.html { render :index }
               format.json { render json: { reason: @error }, status: :unprocessable_entity }
             end
           end
      @@ -20,7 +35,7 @@ def create
         def destroy
           clear_user_session
       
      -    redirect_to secure_path
      +    redirect_to sessions_path
         end
       
         private
      @@ -51,4 +66,8 @@ def login email, password
       
           user
         end
      +
      +  def user_params
      +    params.require(:user).permit(:email, :password)
      +  end
       end
      diff --git a/app/controllers/users/invitations_controller.rb b/app/controllers/users/invitations_controller.rb
      index 3b8ca8f42..e76830b42 100644
      --- a/app/controllers/users/invitations_controller.rb
      +++ b/app/controllers/users/invitations_controller.rb
      @@ -2,10 +2,9 @@
       
       module Users
         class InvitationsController < Devise::InvitationsController
      -    force_ssl if: :ssl_enabled?
           skip_before_action :require_login, only: [ :edit, :update ]
       
      -    layout 'secure'
      +    layout 'start'
       
           # Intercepts the login path and redirects the user to their
           # Team page as their first page after joining Quepid!
      @@ -16,7 +15,7 @@ def after_accept_path_for resource
           def update
             unless signup_enabled?
               flash.now[:error] = 'Signups are disabled.'
      -        redirect_to secure_path and return
      +        redirect_to sessions_path and return
             end
       
             super
      diff --git a/app/controllers/users/omniauth_callbacks_controller.rb b/app/controllers/users/omniauth_callbacks_controller.rb
      new file mode 100644
      index 000000000..67eb3ad29
      --- /dev/null
      +++ b/app/controllers/users/omniauth_callbacks_controller.rb
      @@ -0,0 +1,85 @@
      +# frozen_string_literal: true
      +
      +module Users
      +  class OmniauthCallbacksController < Devise::OmniauthCallbacksController
      +    skip_before_action :require_login, only: [ :keycloakopenid, :google_oauth2, :failure ]
      +
      +    # rubocop:disable Metrics/AbcSize
      +    def keycloakopenid
      +      Rails.logger.debug(request.env['omniauth.auth'])
      +      @user = create_user_from_omniauth(request.env['omniauth.auth'])
      +      @user.errors.add(:base, "Can't log in a locked user." ) if @user.locked
      +      if @user.persisted? & !@user.locked
      +        session[:current_user_id] = @user.id # this populates our session variable.
      +
      +        # in this flow, we have a new user joining, so we create a empty case for them, which
      +        # on the home_controller.rb triggers the bootstrap and the new case wizard.
      +        @user.cases.build case_name: "Case #{@user.cases.size}"
      +
      +        # sign_in_and_redirect @user, event: :authentication
      +        redirect_to root_path
      +      else
      +        Rails.logger.warn('user not persisted, what do we need to do?')
      +        session['devise.keycloakopenid_data'] = request.env['omniauth.auth']
      +        redirect_to new_user_registration_url
      +      end
      +    end
      +
      +    def google_oauth2
      +      Rails.logger.debug(request.env['omniauth.auth'])
      +      @user = create_user_from_omniauth(request.env['omniauth.auth'])
      +      @user.errors.add(:base, "Can't log in a locked user." ) if @user.locked
      +      if @user.errors.empty?
      +        session[:current_user_id] = @user.id # this populates our session variable.
      +
      +        # in this flow, we have a new user joining, so we create a empty case for them, which
      +        # on the home_controller.rb triggers the bootstrap and the new case wizard.
      +        @user.cases.build case_name: "Case #{@user.cases.size}"
      +
      +        redirect_to root_path
      +        # sign_in_and_redirect @user, event: :authentication
      +      else
      +        # Removing extra as it can overflow some session stores
      +        session['devise.google_data'] = request.env['omniauth.auth'].except('extra')
      +        redirect_to root_path, alert: @user.errors.full_messages.join("\n") if @user
      +      end
      +    end
      +    # rubocop:enable Metrics/AbcSize
      +
      +    def failure
      +      redirect_to root_path, alert: 'Could not sign user in with OAuth provider.'
      +    end
      +
      +    private
      +
      +    # rubocop:disable Metrics/AbcSize
      +    # rubocop:disable Metrics/MethodLength
      +    def create_user_from_omniauth auth
      +      if Rails.application.config.signup_enabled
      +        user = User.find_or_initialize_by(email: auth['info']['email'])
      +      else
      +        user = User.find_by(email: auth['info']['email'])
      +        if user.nil? # we looked for a existing user account and didn't find it
      +          user = User.new(email: auth['info']['email'])
      +          user.errors.add(:base, 'You can only sign in with already created users.' )
      +        end
      +      end
      +
      +      user.name = auth['info']['name']
      +      user.password = 'fake' if user.password.blank? # If you don't have a password, fake it.
      +      user.agreed = true
      +
      +      user.num_logins ||= 0
      +      user.num_logins  += 1
      +
      +      user.profile_pic = auth['info']['image']
      +      # user.access_token = auth['credentials']['token']
      +      # user.refresh_token = auth['credentials']['refresh_token'] unless auth['credentials']['refresh_token'].nil?
      +      # user.expires_at = auth['credentials']['expires_at'] unless auth['credentials']['refresh_token'].nil?
      +      user.save! if user.errors.empty?
      +      user
      +    end
      +    # rubocop:enable Metrics/AbcSize
      +    # rubocop:enable Metrics/MethodLength
      +  end
      +end
      diff --git a/app/controllers/users/passwords_controller.rb b/app/controllers/users/passwords_controller.rb
      index fbddfff95..22cd1d763 100644
      --- a/app/controllers/users/passwords_controller.rb
      +++ b/app/controllers/users/passwords_controller.rb
      @@ -4,13 +4,12 @@ module Users
         class PasswordsController < Devise::PasswordsController
           include NotificationsManager
       
      -    force_ssl if: :ssl_enabled?
           skip_before_action :require_login
           skip_before_action :require_no_authentication
       
           before_action :check_email
       
      -    layout 'secure'
      +    layout 'start'
       
           # GET /resource/password/new
           # def new
      diff --git a/app/controllers/users/signups_controller.rb b/app/controllers/users/signups_controller.rb
      new file mode 100644
      index 000000000..4944933a1
      --- /dev/null
      +++ b/app/controllers/users/signups_controller.rb
      @@ -0,0 +1,64 @@
      +# frozen_string_literal: true
      +
      +module Users
      +  class SignupsController < ApplicationController
      +    skip_before_action :require_login
      +    layout 'start'
      +
      +    # rubocop:disable Metrics/MethodLength
      +    def create
      +      user_params_to_save = user_params
      +
      +      # Check if we already have an invite out for this user, and if so let's use that
      +      if user_params_to_save[:email].blank?
      +        @user = User.new user_params_to_save
      +      else
      +        @user = User.where(email: user_params_to_save[:email]).where.not(invitation_token: nil).first
      +        if @user
      +          @user.assign_attributes(user_params_to_save)
      +        else
      +          @user = User.new user_params_to_save
      +          # in this flow, we have a new user joining, so we create a empty case for them, which
      +          # on the home_controller.rb triggers the bootstrap and the new case wizard.
      +          @user.cases.build case_name: "Case #{@user.cases.size}"
      +        end
      +      end
      +
      +      respond_to do |format|
      +        format.html do
      +          if @user.save
      +            session[:current_user_id] = @user.id # not sure if we need to do more here?
      +            Analytics::Tracker.track_signup_event @user
      +            redirect_to root_path
      +          else
      +            render template: 'sessions/index'
      +          end
      +        end
      +        format.js
      +      end
      +      # respond_to do |format|
      +      #  if @user.save
      +      #    session[:current_user_id] = @user.id # not sure if we need to do more here?
      +      #    Analytics::Tracker.track_signup_event @user
      +      #    format.html { redirect_to root_path }
      +      #  else
      +      #    format.html { render template: 'sessions/index' }
      +      #  end
      +      # end
      +    end
      +    # rubocop:enable Metrics/MethodLength
      +
      +    private
      +
      +    def user_params
      +      params.require(:user).permit(
      +        :name,
      +        :email,
      +        :password,
      +        :password_confirmation,
      +        :agreed,
      +        :email_marketing
      +      )
      +    end
      +  end
      +end
      diff --git a/app/helpers/devise_helper.rb b/app/helpers/devise_helper.rb
      index f03c7c4f2..d7fd97ec6 100644
      --- a/app/helpers/devise_helper.rb
      +++ b/app/helpers/devise_helper.rb
      @@ -20,7 +20,7 @@ def devise_error_messages!
         end
       
         def devise_error_messages?
      -    resource.errors.empty? ? false : true
      +    !resource.errors.empty?
         end
       
         def devise_reset_password_error_messages
      diff --git a/app/javascripts/channels/consumer.js b/app/javascripts/channels/consumer.js
      new file mode 100644
      index 000000000..8ec3aad3a
      --- /dev/null
      +++ b/app/javascripts/channels/consumer.js
      @@ -0,0 +1,6 @@
      +// Action Cable provides the framework to deal with WebSockets in Rails.
      +// You can generate new channels where WebSocket features live using the `bin/rails generate channel` command.
      +
      +import { createConsumer } from "@rails/actioncable"
      +
      +export default createConsumer()
      diff --git a/app/javascripts/channels/index.js b/app/javascripts/channels/index.js
      new file mode 100644
      index 000000000..0cfcf7491
      --- /dev/null
      +++ b/app/javascripts/channels/index.js
      @@ -0,0 +1,5 @@
      +// Load all the channels within this directory and all subdirectories.
      +// Channel files must be named *_channel.js.
      +
      +const channels = require.context('.', true, /_channel\.js$/)
      +channels.keys().forEach(channels)
      diff --git a/app/javascripts/packs/application.js b/app/javascripts/packs/application.js
      new file mode 100644
      index 000000000..f710851a8
      --- /dev/null
      +++ b/app/javascripts/packs/application.js
      @@ -0,0 +1,13 @@
      +// This file is automatically compiled by Webpack, along with any other files
      +// present in this directory. You're encouraged to place your actual application logic in
      +// a relevant structure within app/javascript and only use these pack files to reference
      +// that code so it'll be compiled.
      +
      +import Rails from "@rails/ujs"
      +import Turbolinks from "turbolinks"
      +import * as ActiveStorage from "@rails/activestorage"
      +import "channels"
      +
      +Rails.start()
      +Turbolinks.start()
      +ActiveStorage.start()
      diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb
      index d92ffddcb..bef395997 100644
      --- a/app/jobs/application_job.rb
      +++ b/app/jobs/application_job.rb
      @@ -1,4 +1,9 @@
       # frozen_string_literal: true
       
       class ApplicationJob < ActiveJob::Base
      +  # Automatically retry jobs that encountered a deadlock
      +  # retry_on ActiveRecord::Deadlocked
      +
      +  # Most jobs are safe to ignore if the underlying records are no longer available
      +  # discard_on ActiveJob::DeserializationError
       end
      diff --git a/app/jobs/google_analytics_event_job.rb b/app/jobs/google_analytics_event_job.rb
      index ff70afed3..a261de255 100644
      --- a/app/jobs/google_analytics_event_job.rb
      +++ b/app/jobs/google_analytics_event_job.rb
      @@ -4,9 +4,9 @@ class GoogleAnalyticsEventJob < ApplicationJob
         queue_as :default
       
         def perform data
      -    return unless Analytics::GA.enabled?
      +    return unless Analytics::GoogleAnalytics.enabled?
       
      -    Analytics::GA.ga.event(
      +    Analytics::GoogleAnalytics.ga.event(
             data[:category],
             data[:action],
             data[:label],
      diff --git a/app/models/case.rb b/app/models/case.rb
      index 5fac5dd1c..d09e6b6ca 100644
      --- a/app/models/case.rb
      +++ b/app/models/case.rb
      @@ -9,7 +9,7 @@
       #  search_url      :string(500)
       #  field_spec      :string(500)
       #  last_try_number :integer
      -#  user_id         :integer
      +#  owner_id         :integer
       #  archived        :boolean
       #  scorer_id       :integer
       #  created_at      :datetime         not null
      @@ -27,7 +27,8 @@ class Case < ApplicationRecord
       
         belongs_to :scorer, optional: true
       
      -  belongs_to :user, optional: true
      +  belongs_to :owner,
      +             class_name: 'User', optional: true
       
         has_many   :tries,     -> { order(try_number: :desc) },
                    dependent:  :destroy,
      @@ -36,7 +37,7 @@ class Case < ApplicationRecord
         has_many   :metadata,
                    dependent: :destroy
       
      -  # has_many   :ratings,  # wed ont' actually need htis.
      +  # has_many   :ratings,  # we don't actually need this.
         #           through: :queries
       
         # rubocop:disable Rails/InverseOf
      @@ -84,7 +85,7 @@ class Case < ApplicationRecord
       
         scope :for_user_directly_owned, ->(user) {
           where('
      -        `cases`.`user_id` = ?
      +        `cases`.`owner_id` = ?
           ',  user.id)
         }
       
      @@ -106,7 +107,7 @@ def really_destroy
         # rubocop:disable Metrics/ParameterLists
         def clone_case original_case, user, try: nil, clone_queries: false, clone_ratings: false, preserve_history: false
           transaction do
      -      self.user = user
      +      self.owner = user
       
             if preserve_history
               original_case.tries.each do |a_try|
      @@ -153,8 +154,8 @@ def last_score
         def set_scorer
           return if scorer_id.present?
       
      -    self.scorer = if user&.default_scorer
      -                    user.default_scorer
      +    self.scorer = if owner&.default_scorer
      +                    owner.default_scorer
                         else
                           Scorer.system_default_scorer
                         end
      diff --git a/app/models/concerns/profile.rb b/app/models/concerns/profile.rb
      index 5e77378c9..0f23cc85f 100644
      --- a/app/models/concerns/profile.rb
      +++ b/app/models/concerns/profile.rb
      @@ -12,9 +12,13 @@ module Profile
         }.freeze
       
         def avatar_url size = :small
      -    gravatar_id   = Digest::MD5.hexdigest(email.downcase)
      -    gravatar_size = size_to_number size
      -    "https://secure.gravatar.com/avatar/#{gravatar_id}.png?s=#{gravatar_size}&d=retro"
      +    if profile_pic.present?
      +      profile_pic
      +    else
      +      gravatar_id   = Digest::MD5.hexdigest(email.downcase)
      +      gravatar_size = size_to_number size
      +      "https://secure.gravatar.com/avatar/#{gravatar_id}.png?s=#{gravatar_size}&d=retro"
      +    end
         end
       
         def display_name
      diff --git a/app/models/try.rb b/app/models/try.rb
      index 5efd1956e..e9d3d919a 100644
      --- a/app/models/try.rb
      +++ b/app/models/try.rb
      @@ -81,7 +81,7 @@ def add_curator_vars vars = {}
         end
       
         def curator_vars_map
      -    Hash[curator_variables.map { |each| [ each.name.to_sym, each.value ] }]
      +    curator_variables.map { |each| [ each.name.to_sym, each.value ] }.to_h
         end
       
         def solr_args
      diff --git a/app/models/user.rb b/app/models/user.rb
      index 09aa2b289..ddde88683 100644
      --- a/app/models/user.rb
      +++ b/app/models/user.rb
      @@ -30,8 +30,13 @@ class User < ApplicationRecord
         # Associations
         belongs_to :default_scorer, class_name: 'Scorer', optional: true # for communal scorers there isn't a owner
       
      +  # has_many :cases,
      +  #         dependent:   :nullify # sometimes a case belongs to a team, so don't just delete it.
         has_many :cases,
      -           dependent:   :nullify # sometimes a case belongs to a team, so don't just delete it.
      +           class_name:  'Case',
      +           foreign_key: :owner_id,
      +           inverse_of:  :owner,
      +           dependent:   :nullify
       
         has_many :queries, through: :cases
       
      @@ -91,7 +96,7 @@ class User < ApplicationRecord
         validates_with ::DefaultScorerExistsValidator
       
         validates :agreed,
      -            acceptance: { message: 'You must agree to the terms and conditions.' },
      +            acceptance: { message: 'checkbox must be clicked to signify you agree to the terms and conditions.' },
                   if:         :terms_and_conditions?
       
         def terms_and_conditions?
      @@ -128,7 +133,9 @@ def check_scorer_ownership_before_removing!
         # :confirmable, :lockable, :timeoutable and :omniauthable
         # devise :invitable, :database_authenticatable, :registerable,
         # :recoverable, :rememberable, :trackable, :validatable
      -  devise :invitable, :recoverable, reset_password_keys: [ :email ]
      +  devise :invitable, :recoverable, :omniauthable, omniauth_providers: [ :keycloakopenid, :google_oauth2 ]
      +  # devise :omniauthable, omniauth_providers: %i[keycloakopenid]
      +  # devise :invitable, :recoverable, :omniauthable
       
         # Devise hacks since we only use the recoverable module
         attr_accessor :password_confirmation
      diff --git a/app/views/admin/users/_form.html.erb b/app/views/admin/users/_form.html.erb
      index f7f67a614..5c1d7622e 100644
      --- a/app/views/admin/users/_form.html.erb
      +++ b/app/views/admin/users/_form.html.erb
      @@ -1,4 +1,7 @@
      -
      +
      + + Account + <%= form_for(@user, url: admin_user_path(@user), class: 'form-horizontal') do |f| %> <% if @user.errors.any? %>
      @@ -31,9 +34,44 @@
      -
      - <%= f.submit class: 'btn btn-primary' %> -
      + <%= f.submit class: 'btn btn-primary btn-lg' %>
      <% end %> +
      + +
      + +<%= form_for(@user, url: admin_user_path(@user), class: 'form-horizontal') do |f| %> + + Password + + + <% if @user.errors.any? %> +
      +

      <%= pluralize(@user.errors.count, "error") %> prohibited this from being saved:

      + +
        + <% @user.errors.full_messages.each do |message| %> +
      • <%= message %>
      • + <% end %> +
      +
      + <% end %> + +
      + <%= f.label :password, class: 'control-label' %> + <%= f.password_field :password, class: 'form-control input-lg', placeholder: 'New Password' %> + +
      + +
      + <%= f.label :password_confirmation, class: 'control-label' %> + <%= f.password_field :password_confirmation, class: 'form-control input-lg', placeholder: 'Confirm New Password' %> +
      + +
      + +
      + <% end %> +
      diff --git a/app/views/admin/users/_member.html.erb b/app/views/admin/users/_member.html.erb deleted file mode 100644 index d586de2d0..000000000 --- a/app/views/admin/users/_member.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -
    • - <%= image_tag member.avatar_url %> - <%= member.display_name %> - <% if member == current_user %> - You - <% end %> -
    • diff --git a/app/views/admin/users/edit.html.erb b/app/views/admin/users/edit.html.erb index 486b88eff..27956d877 100644 --- a/app/views/admin/users/edit.html.erb +++ b/app/views/admin/users/edit.html.erb @@ -1,7 +1,8 @@

      Editing User

      - +
      <%= render 'form' %> +
      <%= link_to 'Show', admin_user_path(@user) %> | <%= link_to 'Back', admin_users_path %> diff --git a/app/views/admin/users/show.html.erb b/app/views/admin/users/show.html.erb index bc04e4836..01356455a 100644 --- a/app/views/admin/users/show.html.erb +++ b/app/views/admin/users/show.html.erb @@ -3,6 +3,7 @@

      <%= @user.display_name %>

      <%= notice %>

      + <%= image_tag @user.avatar_url(:big), size:'96x96', class:'img-rounded' %>
      diff --git a/app/views/api/v1/cases/_case.json.jbuilder b/app/views/api/v1/cases/_case.json.jbuilder index 36516eec5..c8ab56c80 100644 --- a/app/views/api/v1/cases/_case.json.jbuilder +++ b/app/views/api/v1/cases/_case.json.jbuilder @@ -9,8 +9,8 @@ teams = acase.teams.find_all { |t| current_user.teams.all.include?(t) } unless n json.case_name acase.case_name json.caseNo acase.id json.scorerId acase.scorer_id -json.owned acase.user_id == current_user.id -json.owner_name acase.user.name unless acase.user.nil? +json.owned acase.owner_id == current_user.id +json.owner_name acase.owner.name if acase.owner.present? json.queriesCount acase.queries.count json.teams teams unless no_teams diff --git a/app/views/api/v1/cases/dropdown/index.json.jbuilder b/app/views/api/v1/cases/dropdown/index.json.jbuilder index f56438dcd..1c3df21ea 100644 --- a/app/views/api/v1/cases/dropdown/index.json.jbuilder +++ b/app/views/api/v1/cases/dropdown/index.json.jbuilder @@ -4,7 +4,7 @@ json.allCases do json.array! @cases do |acase| json.case_name acase.case_name json.caseNo acase.id - json.owned acase.user_id == current_user.id + json.owned acase.owner_id == current_user.id json.last_try_number acase.tries.best.try_number if acase.tries.present? && acase.tries.best.present? end diff --git a/app/views/api/v1/export/ratings/show.rre.json.jbuilder b/app/views/api/v1/export/ratings/show_rre.json.jbuilder similarity index 100% rename from app/views/api/v1/export/ratings/show.rre.json.jbuilder rename to app/views/api/v1/export/ratings/show_rre.json.jbuilder diff --git a/app/views/layouts/_common_js.html.erb b/app/views/layouts/_common_js.html.erb index 55d673c0d..054570c57 100644 --- a/app/views/layouts/_common_js.html.erb +++ b/app/views/layouts/_common_js.html.erb @@ -1,11 +1,11 @@ -<% if Analytics::GA.enabled? %> +<% if Analytics::GoogleAnalytics.enabled? %>