From b1e8ebd9b4d65f5154419758c7530230046de89c Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 14 Aug 2017 19:06:24 +0200 Subject: [PATCH 01/18] Hack added to speed up better_errors --- app/controllers/application_controller.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index fe1c1c7..65f0686 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,6 +7,7 @@ class ApplicationController < ActionController::Base # filter_parameter_logging :password before_action :get_current_users_screenshots, :debian_sso + before_action :better_errors_hack, if: -> { Rails.env.development? } # Query for packages that were uploaded by the current user. # As AAA is not yet implemented it means looking for uploads @@ -50,4 +51,11 @@ class ApplicationController < ActionController::Base def after_sign_in_path_for(resource) my_welcome_path end + + # better_errors gets extremely slow without this hack + # see: https://github.com/charliesome/better_errors/issues/341 + def better_errors_hack + env['puma.config'].options.user_options.delete :app + end + end From 58ab5f10738c5b5f00015dc161a84805715939ce Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 14 Aug 2017 19:06:39 +0200 Subject: [PATCH 02/18] Get screenshots for logged-in as well as anonymous users --- app/controllers/application_controller.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 65f0686..44fc619 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,10 +10,12 @@ class ApplicationController < ActionController::Base before_action :better_errors_hack, if: -> { Rails.env.development? } # Query for packages that were uploaded by the current user. - # As AAA is not yet implemented it means looking for uploads - # that correspond to the user's cookie token. + # If the user is anonymous then find the uploads by the cookie token. + # If the user is logged in then find the uploads by matching the user id. def get_current_users_screenshots - if session[:token] + if user_signed_in? + @current_users_screenshots = current_user.screenshots + else @current_users_screenshots = Screenshot.uploaded_by(session[:token]) end end From 88b4189731984938648a11d4781a8cd5b22cbadd Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 14 Aug 2017 19:07:35 +0200 Subject: [PATCH 03/18] Sending a copy of log messages to the app log --- app/models/log.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/log.rb b/app/models/log.rb index 4618711..9ddb7cd 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -27,5 +27,6 @@ class Log < ApplicationRecord logger.error "Could not log to database - validation errors: #{log.errors.to_a}" unless log.valid? log.save # log + logger.info "Log.log - #{log.section} - #{log.message}" end end From ccd590cfc3d519c02d5b806c53f65a530a0fda21 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 14 Aug 2017 19:07:57 +0200 Subject: [PATCH 04/18] Fixed controller that received uploads --- app/views/packages/upload_receive.slim | 59 ++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 app/views/packages/upload_receive.slim diff --git a/app/views/packages/upload_receive.slim b/app/views/packages/upload_receive.slim new file mode 100644 index 0000000..95abdcd --- /dev/null +++ b/app/views/packages/upload_receive.slim @@ -0,0 +1,59 @@ +/ This page only gets displayed if there was invalid uploads. +/ Otherwise the user gets redirected to the upload_review page directly. +.row + h1 Some images feel wrong… + +.row + p + ' Thanks for your upload. Just wanted to let you know that + ' these images were not accepted: + + ul + - @invalid_images.each do |image| + li + ' #{image.image_file_name} (#{image.errors[:image].join(' and the image ')}) + + + + - if @valid_images.any? + p + ' But do not worry. #{pluralize(@valid_images.length, 'screenshot')} seems good. + ' Let us take a look at them. + + p + a.button href=package_path Okay + +/ +/ = form_for :screenshot, url: upload_review2_path do |f| +/ +/ p 'Please check the images and add +/ ' short one-line descriptions that describe each of the images. +/ +/ - @valid_images.each do |screenshot| +/ +/ .row +/ +/ .small-4.columns +/ a.black.fancybox href=screenshot.image.url(:large, timestamp: false) title='Uploaded image' +/ = image_tag(screenshot.image.url(:large, timestamp: false), alt: 'Uploaded image') +/ .small-8.columns +/ em Your file name: +/ p +/ = screenshot.image_file_name +/ +/ em Description: +/ p +/ = text_field_tag "description-#{screenshot.id}" +/ +/ em Delete this image? +/ p +/ = check_box "delete-#{screenshot.id}", "Delete this image" +/ +/ .row +/ button.button type="submit" id="file-submit" +/ = fa_stacked_icon "check", base: "circle-thin" +/ ' Done +/ +/ - else +/ .row +/ a.button href=upload_path Back to upload form From c03be398f6d640ae8a0054415cde4d6a1ecdbfe9 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 14 Aug 2017 19:08:18 +0200 Subject: [PATCH 05/18] No longer required. Editing will move to details page --- app/views/packages/upload_review.slim | 50 --------------------------- 1 file changed, 50 deletions(-) delete mode 100644 app/views/packages/upload_review.slim diff --git a/app/views/packages/upload_review.slim b/app/views/packages/upload_review.slim deleted file mode 100644 index 88d7b30..0000000 --- a/app/views/packages/upload_review.slim +++ /dev/null @@ -1,50 +0,0 @@ -.row - h1 Your uploaded images - -.row - p - ' Thanks for your upload. Please check the images and add - ' short one-line descriptions that describe each of the images. - - - if @invalid_images.any? - .callout.warning - p - ' However the following screenshots were not accepted: - ul - - @invalid_images.each do |image| - li - ' #{image.image_file_name} (#{image.errors[:image].join(' and ')}) - - -- if @valid_images.any? - - = form_for :screenshot, url: upload_review2_path do |f| - - - @valid_images.each do |screenshot| - - .row - - .small-4.columns - a.black.fancybox href=screenshot.image.url(:large, timestamp: false) title='Uploaded image' - = image_tag(screenshot.image.url(:large, timestamp: false), alt: 'Uploaded image') - .small-8.columns - em Your file name: - p - = screenshot.image_file_name - - em Description: - p - = text_field_tag "description-#{screenshot.id}" - - em Delete this image? - p - = check_box "delete-#{screenshot.id}", "Delete this image" - - .row - button.button type="submit" id="file-submit" - = fa_stacked_icon "check", base: "circle-thin" - ' Done - -- else - .row - a.button href=upload_path Back to upload form From d9d0babb2c424240dcf00daca3d2b163cd488bff Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 14 Aug 2017 19:08:52 +0200 Subject: [PATCH 06/18] Create a cookie token only if the user is anonymous --- app/controllers/packages_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/packages_controller.rb b/app/controllers/packages_controller.rb index 80ab8b6..4787312 100644 --- a/app/controllers/packages_controller.rb +++ b/app/controllers/packages_controller.rb @@ -26,9 +26,11 @@ class PackagesController < ApplicationController # POST target of the screenshots upload form. # Receives uploaded images. Checks if they are valid. Asks for description. + # This action saves the screenshots already if they are valid. The user is + # then given the chance to comment on and delete the screenshots again. def upload_receive # Send a cookie to remember the user by the cookie session. - create_user_token + create_user_token unless user_signed_in? @package = Package.find_by!(name: params[:name]) @valid_images = [] From 0bfacbee30be454068ecc718f64985dd913074b3 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Thu, 17 Aug 2017 09:31:06 +0200 Subject: [PATCH 07/18] Anonymous uploads replaced by creation of ad-hoc users --- app/controllers/application_controller.rb | 2 - app/controllers/packages_controller.rb | 42 ++++++++++++++------- app/models/package.rb | 2 +- app/models/screenshot.rb | 30 --------------- app/views/packages/_grid_thumbnail.slim | 2 +- app/views/packages/list.slim | 2 +- doc/TODO | 46 +++++++++++++++++++++++ 7 files changed, 78 insertions(+), 48 deletions(-) create mode 100644 doc/TODO diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 44fc619..c40f76c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -15,8 +15,6 @@ class ApplicationController < ActionController::Base def get_current_users_screenshots if user_signed_in? @current_users_screenshots = current_user.screenshots - else - @current_users_screenshots = Screenshot.uploaded_by(session[:token]) end end diff --git a/app/controllers/packages_controller.rb b/app/controllers/packages_controller.rb index 4787312..0b4bd38 100644 --- a/app/controllers/packages_controller.rb +++ b/app/controllers/packages_controller.rb @@ -29,14 +29,16 @@ class PackagesController < ApplicationController # This action saves the screenshots already if they are valid. The user is # then given the chance to comment on and delete the screenshots again. def upload_receive - # Send a cookie to remember the user by the cookie session. - create_user_token unless user_signed_in? + # Create a pseudo user account for the user. + # The user won't know that an account is created. + # But this makes it easier to track who screenshots belong to. + create_pseudo_user unless user_signed_in? @package = Package.find_by!(name: params[:name]) @valid_images = [] @invalid_images = [] - params[:file].each do |img| + params[:file].each do |img| new_screenshot = @package.screenshots.new(image: img) # Check if the image was valid @@ -44,14 +46,22 @@ class PackagesController < ApplicationController new_screenshot.uploaderhash = session[:token] new_screenshot.uploaderip = session[:ip] new_screenshot.version = @package.version + + # TODO: must it be moderated first? + new_screenshot.save - Log.log "Screenshot #{new_screenshot.id} uploaded successfully from #{session[:ip]}. User has token #{session[:token]}" + Log.log "Screenshot #{new_screenshot.id} uploaded successfully from #{session[:ip]}. User has token #{session[:token]} or is #{current_user}" @valid_images.push new_screenshot else + Log.log "Screenshot #{new_screenshot.image_file_name} invalid (#{new_screenshot.errors[:image]})." @invalid_images.push new_screenshot end end + # Show a list of invalid uploads by default. Or redirect to the review page + # if all uploads were okay. + redirect_to upload_review_path unless @invalid_images + # TODO # if @invalid_images… # ' #{image.image_file_name} (#{image.errors[:image].join(' and ')}) @@ -92,7 +102,10 @@ class PackagesController < ApplicationController # Is the user allowed to delete the screenshot? @screenshot = Screenshot.find(params[:id]) - if user_can_alter_screenshot? + # Check if the user is allowed to change this screenshot + # - Is this the user's own screenshot? (anonymous) + if self.user = current_user or current_user.is_admin? + logger.debug "User #{current_user} deletes screenshot #{@screenshot}" @screenshot.destroy flash['notice'] = "Screenshot deleted." redirect_to :back @@ -203,6 +216,17 @@ class PackagesController < ApplicationController private + # Seamlessly create a user account for the current client. + # It helps track uploads. + def create_pseudo_user + generated_password = Devise.friendly_token.first(8) + new_user = User.create( + name: 'Anonymous', + password: generated_password) + Log.log "New pseudo user for anonymous upload created: #{new_user}" + sign_in(new_user) + end + # Send a dummy thumbnail reading "No screenshot available. Sorry." def thumbnail404 send_file Rails.root.join('public/images/dummy/thumbnail404.png'), @@ -240,13 +264,6 @@ class PackagesController < ApplicationController return packages end - # Check if the user is allowed to do changed to a screenshot - def user_can_alter_screenshot? - # - Is this the user's own screenshot? - # - Is the user an admin (=logged in)? - @screenshot.uploaderhash == session[:token] or user_signed_in? - end - # Store a random identifier and the client's IP address in the session # for later identification. def create_user_token @@ -254,7 +271,6 @@ class PackagesController < ApplicationController session[:ip] ||= request.remote_ip end - # Get reviews of this package from the Ubuntu API def get_ubuntu_reviews(packagename) # Use the URL defined in the configuration to get a JSON string diff --git a/app/models/package.rb b/app/models/package.rb index 2da1678..358bfc4 100644 --- a/app/models/package.rb +++ b/app/models/package.rb @@ -48,7 +48,7 @@ class Package < ApplicationRecord # Return a query of all approved/public screenshots of this package def screenshots_approved - self.screenshots.find_by(approved: true) + self.screenshots.where(approved: true) end # Return a query of all approved/public screenshots of this package diff --git a/app/models/screenshot.rb b/app/models/screenshot.rb index 95bedab..4040bb8 100644 --- a/app/models/screenshot.rb +++ b/app/models/screenshot.rb @@ -90,33 +90,8 @@ class Screenshot < ApplicationRecord end end - # Check if the user is allowed to change this screenshot - def user_can_alter? - # - Is this the user's own screenshot? (anonymous) - if self.uploaderhash == session[:token] - logger.debug "Screenshot belongs to current anonymous user" - return true - end - - # - Is this the user's own screenshot? (anonymous) - if self.user = current_user - logger.debug "Screenshot belongs to user who is currently logged in" - return true - end - - # - Is the user an admin (=logged in)? - if current_user.can_admin? - logger.debug "User is an administrator and has super powers" - return true - end - - return false - end - # Publish a screenshot from the moderation queue def approve_screenshot! - - self.delete_reason = nil self.markedfordelete = false self.approved = true @@ -131,11 +106,6 @@ class Screenshot < ApplicationRecord self.find_by(approved: true) end - # Query for screenshots being uploaded by a certain user (by their token) - def self.uploaded_by(token) - self.where(approved: false, uploaderhash: token) - end - # Check whether the user has administrative permissions def can_admin? self.admin == 1 diff --git a/app/views/packages/_grid_thumbnail.slim b/app/views/packages/_grid_thumbnail.slim index c07fa5d..500f782 100644 --- a/app/views/packages/_grid_thumbnail.slim +++ b/app/views/packages/_grid_thumbnail.slim @@ -1,7 +1,7 @@ // TODO: Handle packages with multiple screenshots a.black href=package_path(name: pkg.name) div.grid-thumbnail - - if pkg.screenshots.any? + - if pkg.screenshots_approved.any? // TODO: smarter selection of the most useful screenshot instead of taking the first one - screenshot = pkg.screenshots.first = image_tag(screenshot.image.url(:thumb, timestamp: false), alt: screenshot.caption, class: 'thumbnail') diff --git a/app/views/packages/list.slim b/app/views/packages/list.slim index 99ae8f1..a65b7bd 100644 --- a/app/views/packages/list.slim +++ b/app/views/packages/list.slim @@ -19,7 +19,7 @@ .pkgname =pkg.name ' > - - if pkg.screenshots.any? + - if pkg.screenshots_approved.any? // TODO: smarter search for the best screenshot instead of taking the first one - screenshot = pkg.screenshots.first a.black title=screenshot.caption href=package_path(name: pkg.name) diff --git a/doc/TODO b/doc/TODO new file mode 100644 index 0000000..ae8ce2a --- /dev/null +++ b/doc/TODO @@ -0,0 +1,46 @@ +Next tasks + +> Before next deployment. + +Why does weboob-qt show an uploaded image in grid mode if no screenshot is approved? + +Fix upload process. + +Fix my/my uploads. + +Make sure that the moderation workflow works. + +Any upload will trigger the creation of an ad-hoc account. +Screenshots will always be assigned to a user. + +After an upload ask the user to add descriptions to the +screenshots. And allow them to delete the screenshots. + +Check all code with "current_user" and "user_signed_in?" to +make sure it matches the new concept. + +Create a user account called "Anonymous" - via a migration. +All existing screenshots will become uploaded by that user. + +Turn moderation into a seperate page showing just the one +screenshot and aks the user for a deletion reason. + +Make sure that users can delete their own screenshots but +no other. + +Fix formatting of http://localhost:3000/packages/list?search=weboob&show= +(Enumerations etc.) + +> After next deployment + +Make sure that ad-hoc users are recognized when they visit again. +Devise should recognize them by their session cookie. + +Offer anonymous users to become real users to track their uploads. +The screenshots will have to be moved to the new account +and the ad-hoc account can be deleted. + +Move ad-hoc/anonymous uploads to the main anonymous user after +a week. + +Add tests. From 09ca7c94f292dcd905c0f0c4b6e447f240dd676a Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Thu, 17 Aug 2017 18:59:35 +0200 Subject: [PATCH 08/18] Minor addition --- doc/TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/TODO b/doc/TODO index ae8ce2a..55ebf20 100644 --- a/doc/TODO +++ b/doc/TODO @@ -8,6 +8,8 @@ Fix upload process. Fix my/my uploads. +Fix admin menu. + Make sure that the moderation workflow works. Any upload will trigger the creation of an ad-hoc account. From 02d96bd6b7a1fc7d630d0677db15a32aaff86f70 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Thu, 17 Aug 2017 19:00:11 +0200 Subject: [PATCH 09/18] Avoiding Devise's "true" flash popup --- app/views/layouts/_messages.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/layouts/_messages.slim b/app/views/layouts/_messages.slim index a2b9014..e561c89 100644 --- a/app/views/layouts/_messages.slim +++ b/app/views/layouts/_messages.slim @@ -1,7 +1,7 @@ // Display alert messages to the user - if flash - - flash.each do |name,msg| + - flash.except(:timedout).each do |name,msg| - case name - when 'notice' - flash_color = 'white' From 317e583b97f728fb276a4959aea0f15663ad7190 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Thu, 17 Aug 2017 19:00:35 +0200 Subject: [PATCH 10/18] Fixed ACL check for deleting screenshots --- app/controllers/packages_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/packages_controller.rb b/app/controllers/packages_controller.rb index 0b4bd38..a37681c 100644 --- a/app/controllers/packages_controller.rb +++ b/app/controllers/packages_controller.rb @@ -104,7 +104,7 @@ class PackagesController < ApplicationController # Check if the user is allowed to change this screenshot # - Is this the user's own screenshot? (anonymous) - if self.user = current_user or current_user.is_admin? + if @screenshot.user == current_user or current_user.is_admin? logger.debug "User #{current_user} deletes screenshot #{@screenshot}" @screenshot.destroy flash['notice'] = "Screenshot deleted." From d22bfe411a76edeae294aa2cbaa0e6ea32d40503 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Thu, 17 Aug 2017 19:22:11 +0200 Subject: [PATCH 11/18] Fix for Devise's "true" flash message was broken. Fixed the fix. --- app/views/layouts/_messages.slim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/layouts/_messages.slim b/app/views/layouts/_messages.slim index e561c89..ba6aa55 100644 --- a/app/views/layouts/_messages.slim +++ b/app/views/layouts/_messages.slim @@ -1,7 +1,7 @@ // Display alert messages to the user - if flash - - flash.except(:timedout).each do |name,msg| + - flash.each do |name,msg| - case name - when 'notice' - flash_color = 'white' @@ -9,6 +9,8 @@ - when 'alert' - flash_color = 'white' - flash_bgcolor = 'red' + - when 'timedout' + - next - else - flash_color = 'black' - flash_bgcolor = 'gray' From 45e543ab87728f8a9c7f52e913a4c8b4e072426a Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Thu, 17 Aug 2017 19:22:26 +0200 Subject: [PATCH 12/18] Setting owner for new uploads --- app/controllers/packages_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/packages_controller.rb b/app/controllers/packages_controller.rb index a37681c..03c56a3 100644 --- a/app/controllers/packages_controller.rb +++ b/app/controllers/packages_controller.rb @@ -46,6 +46,7 @@ class PackagesController < ApplicationController new_screenshot.uploaderhash = session[:token] new_screenshot.uploaderip = session[:ip] new_screenshot.version = @package.version + new_screenshot.user = current_user # TODO: must it be moderated first? From 7c9bce03eb4ac5d420fa94db54ef32684d71e722 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Sun, 6 May 2018 19:05:47 +0200 Subject: [PATCH 13/18] Trying to use docker-compose for deployment --- Dockerfile | 8 +- Gemfile.lock | 205 +++++++++++++++++++++++---------------------- docker-compose.yml | 16 ++++ 3 files changed, 124 insertions(+), 105 deletions(-) create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile index c39edc9..b62c213 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,12 @@ -FROM debian:8 +#FROM debian:9 +FROM ruby:2.5-stretch MAINTAINER email@christoph-haas.de # Install apt based dependencies required to run Rails as # well as RubyGems. As the Ruby image itself is based on a # Debian image, we use apt-get to install those. RUN apt-get update && apt-get install -y \ - build-essential ruby + build-essential ruby ruby-dev libpq-dev libbz2-dev libbz2-1.0 zlib1g zlib1g-dev postgresql-client RUN apt-get install -y git RUN apt-get install -y rbenv @@ -21,7 +22,8 @@ WORKDIR /app # will be cached unless changes to one of those two files # are made. COPY Gemfile Gemfile.lock ./ -RUN gem install bundler && bundle install --jobs 20 --retry 5 +#RUN gem install bundler && bundle install --jobs 20 --retry 5 +RUN bundle install --jobs 20 --retry 5 # Copy the main application. COPY . ./ diff --git a/Gemfile.lock b/Gemfile.lock index c77aa75..ebe5960 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -7,41 +7,41 @@ GIT GEM remote: https://rubygems.org/ specs: - actioncable (5.0.5) - actionpack (= 5.0.5) + actioncable (5.0.7) + actionpack (= 5.0.7) nio4r (>= 1.2, < 3.0) websocket-driver (~> 0.6.1) - actionmailer (5.0.5) - actionpack (= 5.0.5) - actionview (= 5.0.5) - activejob (= 5.0.5) + actionmailer (5.0.7) + actionpack (= 5.0.7) + actionview (= 5.0.7) + activejob (= 5.0.7) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.0.5) - actionview (= 5.0.5) - activesupport (= 5.0.5) + actionpack (5.0.7) + actionview (= 5.0.7) + activesupport (= 5.0.7) rack (~> 2.0) rack-test (~> 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.5) - activesupport (= 5.0.5) + actionview (5.0.7) + activesupport (= 5.0.7) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.0.5) - activesupport (= 5.0.5) + activejob (5.0.7) + activesupport (= 5.0.7) globalid (>= 0.3.6) - activemodel (5.0.5) - activesupport (= 5.0.5) - activerecord (5.0.5) - activemodel (= 5.0.5) - activesupport (= 5.0.5) + activemodel (5.0.7) + activesupport (= 5.0.7) + activerecord (5.0.7) + activemodel (= 5.0.7) + activesupport (= 5.0.7) arel (~> 7.0) - activesupport (5.0.5) + activesupport (5.0.7) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) + i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) ansi (1.5.0) @@ -51,21 +51,19 @@ GEM babel-source (>= 4.0, < 6) execjs (~> 2.0) bcrypt (3.1.11) - better_errors (2.3.0) + better_errors (2.4.0) coderay (>= 1.0.0) erubi (>= 1.0.0) rack (>= 0.9.0) bindex (0.5.0) - binding_of_caller (0.7.2) + binding_of_caller (0.8.0) debug_inspector (>= 0.0.1) builder (3.2.3) - byebug (9.0.6) + byebug (10.0.2) callsite (0.0.11) choice (0.2.0) climate_control (0.2.0) - cocaine (0.5.8) - climate_control (>= 0.0.3, < 1.0) - coderay (1.1.1) + coderay (1.1.2) coffee-rails (4.2.2) coffee-script (>= 2.2.0) railties (>= 4.0.0) @@ -74,36 +72,37 @@ GEM execjs coffee-script-source (1.12.2) concurrent-ruby (1.0.5) - cookies_eu (1.6.2) + cookies_eu (1.6.3) js_cookie_rails + crass (1.0.4) debug_inspector (0.0.3) - devise (4.3.0) + devise (4.4.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 4.1.0, < 5.2) + railties (>= 4.1.0, < 6.0) responders warden (~> 1.2.3) - erubi (1.6.1) + erubi (1.7.1) erubis (2.7.0) execjs (2.7.0) fancybox2-rails (0.2.7) railties (>= 3.1.0) faraday (0.12.2) multipart-post (>= 1.2, < 3) - ffi (1.9.18) - font-awesome-rails (4.7.0.2) - railties (>= 3.2, < 5.2) + ffi (1.9.23) + font-awesome-rails (4.7.0.4) + railties (>= 3.2, < 6.0) formatador (0.2.5) - foundation-rails (6.4.1.2) + foundation-rails (6.4.3.0) railties (>= 3.1.0) sass (>= 3.3.0, < 3.5) sprockets-es6 (>= 0.9.0) - globalid (0.4.0) + globalid (0.4.1) activesupport (>= 4.2.0) - guard (2.14.1) + guard (2.14.2) formatador (>= 0.2.4) listen (>= 2.7, < 4.0) - lumberjack (~> 1.0) + lumberjack (>= 1.0.12, < 2.0) nenv (~> 0.1) notiffany (~> 0.0) pry (>= 0.9.12) @@ -113,11 +112,12 @@ GEM guard-minitest (2.4.6) guard-compat (~> 1.2) minitest (>= 3.0) - guard-rails (0.8.0) + guard-rails (0.8.1) guard (~> 2.11) guard-compat (~> 1.0) - hashie (3.5.6) - i18n (0.8.6) + hashie (3.5.7) + i18n (1.0.0) + concurrent-ruby (~> 1.0) jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) @@ -133,37 +133,39 @@ GEM listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - loofah (2.0.3) + loofah (2.2.2) + crass (~> 1.0.2) nokogiri (>= 1.5.9) - lumberjack (1.0.12) - mail (2.6.6) - mime-types (>= 1.16, < 4) - meta_request (0.4.3) + lumberjack (1.0.13) + mail (2.7.0) + mini_mime (>= 0.1.1) + meta_request (0.6.0) callsite (~> 0.0, >= 0.0.11) rack-contrib (>= 1.1, < 3) - railties (>= 3.0.0, < 5.2.0) - method_source (0.8.2) + railties (>= 3.0.0, < 6) + method_source (0.9.0) mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mimemagic (0.3.2) - mini_portile2 (2.2.0) - minitest (5.10.3) + mini_mime (1.0.0) + mini_portile2 (2.3.0) + minitest (5.11.3) minitest-rails (3.0.0) minitest (~> 5.8) railties (~> 5.0) - minitest-reporters (1.1.14) + minitest-reporters (1.2.0) ansi builder minitest (>= 5.0) ruby-progressbar - multi_json (1.12.1) + multi_json (1.13.1) multi_xml (0.6.0) multipart-post (2.0.0) nenv (0.3.0) - nio4r (2.1.0) - nokogiri (1.8.0) - mini_portile2 (~> 2.2.0) + nio4r (2.3.0) + nokogiri (1.8.2) + mini_portile2 (~> 2.3.0) notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) @@ -173,7 +175,7 @@ GEM multi_json (~> 1.3) multi_xml (~> 0.5) rack (>= 1.2, < 3) - omniauth (1.6.1) + omniauth (1.8.1) hashie (>= 3.4.6, < 3.6.0) rack (>= 1.6.2, < 3) omniauth-amazon (1.0.1) @@ -182,53 +184,51 @@ GEM omniauth-github (1.3.0) omniauth (~> 1.5) omniauth-oauth2 (>= 1.4.0, < 2.0) - omniauth-google-oauth2 (0.5.2) - jwt (~> 1.5) - multi_json (~> 1.3) + omniauth-google-oauth2 (0.5.3) + jwt (>= 1.5) omniauth (>= 1.1.1) - omniauth-oauth2 (>= 1.3.1) - omniauth-oauth2 (1.4.0) - oauth2 (~> 1.0) + omniauth-oauth2 (>= 1.5) + omniauth-oauth2 (1.5.0) + oauth2 (~> 1.1) omniauth (~> 1.2) omniauth-openid (1.0.1) omniauth (~> 1.0) rack-openid (~> 1.3.1) orm_adapter (0.5.0) - paperclip (5.1.0) + paperclip (6.0.0) activemodel (>= 4.2.0) activesupport (>= 4.2.0) - cocaine (~> 0.5.5) mime-types mimemagic (~> 0.3.0) - pg (0.21.0) - pg_search (2.1.0) + terrapin (~> 0.6.0) + pg (1.0.0) + pg_search (2.1.2) activerecord (>= 4.2) activesupport (>= 4.2) arel (>= 6) - pry (0.10.4) + pry (0.11.3) coderay (~> 1.1.0) - method_source (~> 0.8.1) - slop (~> 3.4) - puma (3.9.1) - rack (2.0.3) - rack-contrib (1.2.0) - rack (>= 0.9.1) + method_source (~> 0.9.0) + puma (3.11.3) + rack (2.0.4) + rack-contrib (2.0.1) + rack (~> 2.0) rack-openid (1.3.1) rack (>= 1.1.0) ruby-openid (>= 2.1.8) rack-test (0.6.3) rack (>= 1.0) - rails (5.0.5) - actioncable (= 5.0.5) - actionmailer (= 5.0.5) - actionpack (= 5.0.5) - actionview (= 5.0.5) - activejob (= 5.0.5) - activemodel (= 5.0.5) - activerecord (= 5.0.5) - activesupport (= 5.0.5) + rails (5.0.7) + actioncable (= 5.0.7) + actionmailer (= 5.0.7) + actionpack (= 5.0.7) + actionview (= 5.0.7) + activejob (= 5.0.7) + activemodel (= 5.0.7) + activerecord (= 5.0.7) + activesupport (= 5.0.7) bundler (>= 1.3.0) - railties (= 5.0.5) + railties (= 5.0.7) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) @@ -238,16 +238,16 @@ GEM activesupport (>= 3.2) choice (~> 0.2.0) ruby-graphviz (~> 1.2) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) - railties (5.0.5) - actionpack (= 5.0.5) - activesupport (= 5.0.5) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) + railties (5.0.7) + actionpack (= 5.0.7) + activesupport (= 5.0.7) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (12.0.0) - rb-fsevent (0.10.2) + rake (12.3.1) + rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) rdoc (4.3.0) @@ -257,9 +257,9 @@ GEM railties (>= 4.2.0, < 5.3) ruby-graphviz (1.2.3) ruby-openid (2.7.0) - ruby-progressbar (1.8.1) + ruby-progressbar (1.9.0) sass (3.4.25) - sass-rails (5.0.6) + sass-rails (5.0.7) railties (>= 4.0.0, < 6) sass (~> 3.1) sprockets (>= 2.8, < 4.0) @@ -269,14 +269,13 @@ GEM json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) shellany (0.0.1) - slim (3.0.8) + slim (3.0.9) temple (>= 0.7.6, < 0.9) tilt (>= 1.3.3, < 2.1) - slim-rails (3.1.2) + slim-rails (3.1.3) actionpack (>= 3.1) railties (>= 3.1) slim (~> 3.0) - slop (3.6.0) spring (2.0.2) activesupport (>= 4.2) spring-watcher-listen (2.0.1) @@ -289,23 +288,25 @@ GEM babel-source (>= 5.8.11) babel-transpiler sprockets (>= 3.0.0) - sprockets-rails (3.2.0) + sprockets-rails (3.2.1) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) temple (0.8.0) + terrapin (0.6.0) + climate_control (>= 0.0.3, < 1.0) therubyracer (0.12.3) libv8 (~> 3.16.14.15) ref - thor (0.19.4) + thor (0.20.0) thread_safe (0.3.6) tilt (2.0.8) - turbolinks (5.0.1) - turbolinks-source (~> 5) - turbolinks-source (5.0.3) - tzinfo (1.2.3) + turbolinks (5.1.0) + turbolinks-source (~> 5.1) + turbolinks-source (5.1.0) + tzinfo (1.2.5) thread_safe (~> 0.1) - uglifier (3.2.0) + uglifier (4.1.8) execjs (>= 0.3.0, < 3) warden (1.2.7) rack (>= 1.0) @@ -316,7 +317,7 @@ GEM railties (>= 5.0) websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.2) + websocket-extensions (0.1.3) will_paginate (3.1.6) PLATFORMS diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..998968b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,16 @@ +version: '3' +services: + # db: + # image: postgres + # volumes: + # - ./tmp/db:/var/lib/postgresql/data + debshots: + build: . + command: bundle exec rails s -p 3000 -b '0.0.0.0' + volumes: + - .:/myapp + ports: + - "3000:3000" + # depends_on: + # - db + From 09b0df40f78a9e7739ec2357d226d8667d46da5e Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Sun, 6 May 2018 19:07:10 +0200 Subject: [PATCH 14/18] Todo list updated --- doc/TODO | 14 +++++- .../assets/images/jquery-fancybox/blank.gif | Bin 43 -> 0 bytes .../images/jquery-fancybox/blank.gif.xmp | 40 ------------------ .../jquery-fancybox/fancybox_loading.gif | Bin 6567 -> 0 bytes .../jquery-fancybox/fancybox_loading@2x.gif | Bin 13984 -> 0 bytes .../jquery-fancybox/fancybox_overlay.png | Bin 1003 -> 0 bytes .../jquery-fancybox/fancybox_sprite.png | Bin 1362 -> 0 bytes .../jquery-fancybox/fancybox_sprite@2x.png | Bin 6553 -> 0 bytes vendor/assets/javascripts/.keep | 0 vendor/assets/javascripts/jquery.amaran.js | 1 - vendor/assets/javascripts/jquery.cycle2.js | 16 ------- vendor/assets/stylesheets/.keep | 0 vendor/assets/stylesheets/amaran.css | 6 --- 13 files changed, 13 insertions(+), 64 deletions(-) delete mode 100644 vendor/assets/images/jquery-fancybox/blank.gif delete mode 100644 vendor/assets/images/jquery-fancybox/blank.gif.xmp delete mode 100644 vendor/assets/images/jquery-fancybox/fancybox_loading.gif delete mode 100644 vendor/assets/images/jquery-fancybox/fancybox_loading@2x.gif delete mode 100644 vendor/assets/images/jquery-fancybox/fancybox_overlay.png delete mode 100644 vendor/assets/images/jquery-fancybox/fancybox_sprite.png delete mode 100644 vendor/assets/images/jquery-fancybox/fancybox_sprite@2x.png delete mode 100644 vendor/assets/javascripts/.keep delete mode 100644 vendor/assets/javascripts/jquery.amaran.js delete mode 100644 vendor/assets/javascripts/jquery.cycle2.js delete mode 100644 vendor/assets/stylesheets/.keep delete mode 100755 vendor/assets/stylesheets/amaran.css diff --git a/doc/TODO b/doc/TODO index 55ebf20..7e75dce 100644 --- a/doc/TODO +++ b/doc/TODO @@ -2,7 +2,19 @@ Next tasks > Before next deployment. -Why does weboob-qt show an uploaded image in grid mode if no screenshot is approved? +FIXED Auto-approve admin uploads + +FIXED Why does weboob-qt show an uploaded image in grid mode if no screenshot is approved? + +FIXED Redirect back to upload form if all uploads are invalid + +FIXED Improve callout design. No javascript. Better readability. At the top. + +Main text on details page is not shown + +Remove appearances of "token" everywhere. + +Hint the user to add missing descriptions. Fix upload process. diff --git a/vendor/assets/images/jquery-fancybox/blank.gif b/vendor/assets/images/jquery-fancybox/blank.gif deleted file mode 100644 index 35d42e808f0a8017b8d52a06be2f8fec0b466a66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43 scmZ?wbhEHbWMp7uXkcLY|NlP&1B2pE7Dgb&paUX6G7L;iE{qJ;0LZEa`2YX_ diff --git a/vendor/assets/images/jquery-fancybox/blank.gif.xmp b/vendor/assets/images/jquery-fancybox/blank.gif.xmp deleted file mode 100644 index 42946e8..0000000 --- a/vendor/assets/images/jquery-fancybox/blank.gif.xmp +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vendor/assets/images/jquery-fancybox/fancybox_loading.gif b/vendor/assets/images/jquery-fancybox/fancybox_loading.gif deleted file mode 100644 index a03a40c097ee728709f65d4ea7397903a389d484..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6567 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 diff --git a/vendor/assets/images/jquery-fancybox/fancybox_loading@2x.gif b/vendor/assets/images/jquery-fancybox/fancybox_loading@2x.gif deleted file mode 100644 index 9205aeb09fffa6b571b4c6beee30b18400829c03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13984 zcmdtpX;f2(+Ar{(ot=4L0|COk2@%3*z%UvmBmq&;1`CRoVn8Z`pb=0JsqPHSQ$#>O zlpu(Ls8JA6P{W{NYc1kXYOM{3ii(OuTWdX>n;y^c^`4${?|t8U*In!0e1XN{3&Z}c z|NnWOELa%o9}vd{xWEAbfIvv4P>F@&!Tv!ui=7ak(7eBSVtj&O!iZcJ$@AuAre}7Z z=&Y=+yfJd)^~={(GPR_rBz#f$m9MV2ySk^Qq_(%U+gRDetcu|W@^i9tde8K}d;4zc z*;M_$`a5^;KzK-%rxJ&X2QLn?o!AL$6PjC^Cmu{N%o&l(BYD2O%*~mdU7huH^*3(a zc>T+3DxF$VRuaB6{L0lU?w;r>m0w%gd*#Kgw%=ks&6=k%WIeK+$C_{YoPuau=B zk)Mi!B_ZCPTr9@$b%);|5($_Gxc~7zfBcvLenz)X7Uph+@%iqcoEz9Md`R*#z-GR2 zt8dXVpW?ZVIZ+-Gx=Ycb(>>SzCy`;_Wwyar_cK7Wh&O zrlIseae92AkZ50;y?W<7KWti>V~)@`f6qLxhBEd3d4LHAEHdDkiYOs-Y)E{V> zW6~D2Vh>eoeqwMQvwM#Fl3qfgiFP)WjF1Bwg^0QQ6y5A-#_f4rQ>{ZN!bL)0FKcTj zmtREz>uarK!Iv6s=Pl9&3g{3SgfGt;U2si=UkR|9DQrcaG60u7isr@<0SI=eK&Fo^ zD5be?9#mQCKQjUyhnGl(MIkd-Q|7a?DLlO$K=fO7R5j{NxbUU>FJ2r>(*il^6xhiY z>VYN#BOC1&Nbi>(V6PW1W^Qu2@}*POh)!lqE&*prUJD90A6Ur~1Cn9C3_=KY-6?@2 z+kBkHC|pv+ec`({{OnGtI;_W%MbkT22*IID5#$9S zXvQ)C7@*nAY@T$WGDj4Gs1X2WF#xqhL=24KkO8B4T{q2%dO;l3^!ANQqQmk-HIRco zYYlba_0G!GuXnx?`&;QJn|JA-4k2QTD;$VyMqM~oZ3Y1Sv_
bMxRL(pijsY@019xTxeF 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 diff --git a/vendor/assets/images/jquery-fancybox/fancybox_overlay.png b/vendor/assets/images/jquery-fancybox/fancybox_overlay.png deleted file mode 100644 index a4391396a9d6b6d7ff3b781f16904732fea40bdd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1003 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 S1P^~LSQ@H2DwWEJcmD(3qHSjY diff --git a/vendor/assets/javascripts/.keep b/vendor/assets/javascripts/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/vendor/assets/javascripts/jquery.amaran.js b/vendor/assets/javascripts/jquery.amaran.js deleted file mode 100644 index 800a171..0000000 --- a/vendor/assets/javascripts/jquery.amaran.js +++ /dev/null @@ -1 +0,0 @@ -(function(){!function(t,i){var n,e;return n=function(i){var n;n={position:"bottom right",content:" ",delay:3e3,sticky:!1,inEffect:"fadeIn",outEffect:"fadeOut",theme:"default",themeTemplate:null,closeOnClick:!0,closeButton:!1,clearAll:!1,cssanimationIn:!1,cssanimationOut:!1,beforeStart:function(){},afterEnd:function(){},onClick:function(){},wrapper:".amaran-wrapper"},this.config=t.extend({},n,i),this.config.beforeStart(),this.init(),this.close()},n.prototype={init:function(){var i,n,o,a,s,c,h,r;h=null,r=null,o=this.config.position.split(" "),t(this.config.wrapper).length&&t(this.config.wrapper).hasClass(this.config.position)?(h=t(this.config.wrapper+"."+o[0]+"."+o[1]),s=h.find(".amaran-wrapper-inner")):(h=t("

",{"class":this.config.wrapper.substr(1,this.config.wrapper.length)+" "+this.config.position}).appendTo("body"),s=t("
",{"class":"amaran-wrapper-inner"}).appendTo(h)),"object"==typeof this.config.content?c=null!=this.config.themeTemplate?this.config.themeTemplate(this.config.content):e[this.config.theme.split(" ")[0]+"Theme"](this.config.content):(this.config.content={},this.config.content.message=this.config.message,this.config.content.color="#27ae60",c=e.defaultTheme(this.config.content)),i={"class":this.config.themeTemplate?"amaran "+this.config.content.themeName:this.config.theme&&!this.config.themeTemplate?"amaran "+this.config.theme:"amaran",html:this.config.closeButton?''+c:c},this.config.clearAll&&t(".amaran").remove(),a=t("
",i).appendTo(s),"center"===o[0]&&this.centerCalculate(h,s),this.animation(this.config.inEffect,a,"show"),this.config.onClick&&(n=this,t(a).on("click",function(i){return t(i.target).is(".amaran-close")?void i.preventDefault():void n.config.onClick()})),this.config.sticky!==!0&&this.hideDiv(a)},centerCalculate:function(t,i){var n,e,o;e=i.find(".amaran").length,o=i.height(),n=(t.height()-o)/2,i.find(".amaran:first-child").animate({"margin-top":n},200)},animation:function(t,i,n){return"fadeIn"===t||"fadeOut"===t?this.fade(i,n):"show"===t?this.cssanimate(i,n):this.slide(t,i,n)},fade:function(t,i){var n;return n=this,"show"===i?this.config.cssanimationIn?t.addClass("animated "+this.config.cssanimationIn).show():t.fadeIn():this.config.cssanimationOut?(t.addClass("animated "+this.config.cssanimationOut),t.css({"min-height":0,height:t.outerHeight()}),void t.animate({opacity:0},function(){t.animate({height:0},function(){n.removeIt(t)})})):(t.css({"min-height":0,height:t.outerHeight()}),void t.animate({opacity:0},function(){t.animate({height:0},function(){n.removeIt(t)})}))},removeIt:function(i){var n,e;clearTimeout(this.timeout),i.remove(),e=t(this.config.wrapper+"."+this.config.position.split(" ")[0]+"."+this.config.position.split(" ")[1]),n=e.find(".amaran-wrapper-inner"),"center"===this.config.position.split(" ")[0]&&this.centerCalculate(e,n),this.config.afterEnd()},getWidth:function(t){var i,n;return i=t.clone().hide().appendTo("body"),n=i.outerWidth()+i.outerWidth()/2,i.remove(),n},getInfo:function(i){var n,e;return n=i.offset(),e=t(this.config.wrapper).offset(),{t:n.top,l:n.left,h:i.height(),w:i.outerWidth(),wT:e.top,wL:e.left,wH:t(this.config.wrapper).outerHeight(),wW:t(this.config.wrapper).outerWidth()}},getPosition:function(n,e){var o,a,s;return o=this.getInfo(n),a=this.config.position.split(" ")[1],s={slideTop:{start:{top:-(o.wT+o.wH+2*o.h)},move:{top:0},hide:{top:-(o.t+2*o.h)},height:o.h},slideBottom:{start:{top:t(i).height()-o.wH+2*o.h},move:{top:0},hide:{top:t(i).height()-o.wH+2*o.h},height:o.h},slideLeft:{start:{left:"left"===a?1.5*-o.w:-t(i).width()},move:{left:0},hide:{left:"left"===a?1.5*-o.w:-t(i).width()},height:o.h},slideRight:{start:{left:"right"===a?1.5*o.w:t(i).width()},move:{left:0},hide:{left:"right"===a?1.5*o.w:t(i).width()},height:o.h}},s[e]?s[e]:0},slide:function(t,i,n){var e,o;return o=this.getPosition(i,t),"show"!==n?(e=this,i.animate(o.hide,function(){i.css({"min-height":0,height:o.height},function(){i.html(" ")})}).animate({height:0},function(){return e.removeIt(i)})):void i.show().css(o.start).animate(o.move)},close:function(){var i;return i=this,t("[data-amaran-close]").on("click",function(){i.animation(i.config.outEffect,t(this).closest("div.amaran"),"hide")}),!this.config.closeOnClick&&this.config.closeButton?void i.animation(i.config.outEffect,t(this).parent("div.amaran"),"hide"):void(this.config.closeOnClick&&t(".amaran").on("click",function(){i.animation(i.config.outEffect,t(this),"hide")}))},hideDiv:function(t){var i;i=this,i.timeout=setTimeout(function(){i.animation(i.config.outEffect,t,"hide")},i.config.delay)}},e={defaultTheme:function(t){var i;return i="","undefined"!=typeof t.color&&(i=t.color),"
"+t.message+"
"},awesomeTheme:function(t){return'

'+t.title+"

"+t.message+''+t.info+"

"},userTheme:function(t){return'
'+t.user+""+t.message+"
"},colorfulTheme:function(t){var i,n;return"undefined"!=typeof t.color&&(n=t.color),"undefined"!=typeof t.bgcolor&&(i=t.bgcolor),"
"+t.message+"
"},tumblrTheme:function(t){return'
'+t.title+'
'+t.message+"
"}},t.amaran=function(t){var i;return i=new n(t)},t.amaran.close=function(){return t(".amaran-wrapper").remove(),!1}}(jQuery,window,document)}).call(this); \ No newline at end of file diff --git a/vendor/assets/javascripts/jquery.cycle2.js b/vendor/assets/javascripts/jquery.cycle2.js deleted file mode 100644 index 25bcc5e..0000000 --- a/vendor/assets/javascripts/jquery.cycle2.js +++ /dev/null @@ -1,16 +0,0 @@ -/*! -* jQuery Cycle2; version: 2.1.5 build: 20140415 -* http://jquery.malsup.com/cycle2/ -* Copyright (c) 2014 M. Alsup; Dual licensed: MIT/GPL -*/ -!function(a){"use strict";function b(a){return(a||"").toLowerCase()}var c="2.1.5";a.fn.cycle=function(c){var d;return 0!==this.length||a.isReady?this.each(function(){var d,e,f,g,h=a(this),i=a.fn.cycle.log;if(!h.data("cycle.opts")){(h.data("cycle-log")===!1||c&&c.log===!1||e&&e.log===!1)&&(i=a.noop),i("--c2 init--"),d=h.data();for(var j in d)d.hasOwnProperty(j)&&/^cycle[A-Z]+/.test(j)&&(g=d[j],f=j.match(/^cycle(.*)/)[1].replace(/^[A-Z]/,b),i(f+":",g,"("+typeof g+")"),d[f]=g);e=a.extend({},a.fn.cycle.defaults,d,c||{}),e.timeoutId=0,e.paused=e.paused||!1,e.container=h,e._maxZ=e.maxZ,e.API=a.extend({_container:h},a.fn.cycle.API),e.API.log=i,e.API.trigger=function(a,b){return e.container.trigger(a,b),e.API},h.data("cycle.opts",e),h.data("cycle.API",e.API),e.API.trigger("cycle-bootstrap",[e,e.API]),e.API.addInitialSlides(),e.API.preInitSlideshow(),e.slides.length&&e.API.initSlideshow()}}):(d={s:this.selector,c:this.context},a.fn.cycle.log("requeuing slideshow (dom not ready)"),a(function(){a(d.s,d.c).cycle(c)}),this)},a.fn.cycle.API={opts:function(){return this._container.data("cycle.opts")},addInitialSlides:function(){var b=this.opts(),c=b.slides;b.slideCount=0,b.slides=a(),c=c.jquery?c:b.container.find(c),b.random&&c.sort(function(){return Math.random()-.5}),b.API.add(c)},preInitSlideshow:function(){var b=this.opts();b.API.trigger("cycle-pre-initialize",[b]);var c=a.fn.cycle.transitions[b.fx];c&&a.isFunction(c.preInit)&&c.preInit(b),b._preInitialized=!0},postInitSlideshow:function(){var b=this.opts();b.API.trigger("cycle-post-initialize",[b]);var c=a.fn.cycle.transitions[b.fx];c&&a.isFunction(c.postInit)&&c.postInit(b)},initSlideshow:function(){var b,c=this.opts(),d=c.container;c.API.calcFirstSlide(),"static"==c.container.css("position")&&c.container.css("position","relative"),a(c.slides[c.currSlide]).css({opacity:1,display:"block",visibility:"visible"}),c.API.stackSlides(c.slides[c.currSlide],c.slides[c.nextSlide],!c.reverse),c.pauseOnHover&&(c.pauseOnHover!==!0&&(d=a(c.pauseOnHover)),d.hover(function(){c.API.pause(!0)},function(){c.API.resume(!0)})),c.timeout&&(b=c.API.getSlideOpts(c.currSlide),c.API.queueTransition(b,b.timeout+c.delay)),c._initialized=!0,c.API.updateView(!0),c.API.trigger("cycle-initialized",[c]),c.API.postInitSlideshow()},pause:function(b){var c=this.opts(),d=c.API.getSlideOpts(),e=c.hoverPaused||c.paused;b?c.hoverPaused=!0:c.paused=!0,e||(c.container.addClass("cycle-paused"),c.API.trigger("cycle-paused",[c]).log("cycle-paused"),d.timeout&&(clearTimeout(c.timeoutId),c.timeoutId=0,c._remainingTimeout-=a.now()-c._lastQueue,(c._remainingTimeout<0||isNaN(c._remainingTimeout))&&(c._remainingTimeout=void 0)))},resume:function(a){var b=this.opts(),c=!b.hoverPaused&&!b.paused;a?b.hoverPaused=!1:b.paused=!1,c||(b.container.removeClass("cycle-paused"),0===b.slides.filter(":animated").length&&b.API.queueTransition(b.API.getSlideOpts(),b._remainingTimeout),b.API.trigger("cycle-resumed",[b,b._remainingTimeout]).log("cycle-resumed"))},add:function(b,c){var d,e=this.opts(),f=e.slideCount,g=!1;"string"==a.type(b)&&(b=a.trim(b)),a(b).each(function(){var b,d=a(this);c?e.container.prepend(d):e.container.append(d),e.slideCount++,b=e.API.buildSlideOpts(d),e.slides=c?a(d).add(e.slides):e.slides.add(d),e.API.initSlide(b,d,--e._maxZ),d.data("cycle.opts",b),e.API.trigger("cycle-slide-added",[e,b,d])}),e.API.updateView(!0),g=e._preInitialized&&2>f&&e.slideCount>=1,g&&(e._initialized?e.timeout&&(d=e.slides.length,e.nextSlide=e.reverse?d-1:1,e.timeoutId||e.API.queueTransition(e)):e.API.initSlideshow())},calcFirstSlide:function(){var a,b=this.opts();a=parseInt(b.startingSlide||0,10),(a>=b.slides.length||0>a)&&(a=0),b.currSlide=a,b.reverse?(b.nextSlide=a-1,b.nextSlide<0&&(b.nextSlide=b.slides.length-1)):(b.nextSlide=a+1,b.nextSlide==b.slides.length&&(b.nextSlide=0))},calcNextSlide:function(){var a,b=this.opts();b.reverse?(a=b.nextSlide-1<0,b.nextSlide=a?b.slideCount-1:b.nextSlide-1,b.currSlide=a?0:b.nextSlide+1):(a=b.nextSlide+1==b.slides.length,b.nextSlide=a?0:b.nextSlide+1,b.currSlide=a?b.slides.length-1:b.nextSlide-1)},calcTx:function(b,c){var d,e=b;return e._tempFx?d=a.fn.cycle.transitions[e._tempFx]:c&&e.manualFx&&(d=a.fn.cycle.transitions[e.manualFx]),d||(d=a.fn.cycle.transitions[e.fx]),e._tempFx=null,this.opts()._tempFx=null,d||(d=a.fn.cycle.transitions.fade,e.API.log('Transition "'+e.fx+'" not found. Using fade.')),d},prepareTx:function(a,b){var c,d,e,f,g,h=this.opts();return h.slideCount<2?void(h.timeoutId=0):(!a||h.busy&&!h.manualTrump||(h.API.stopTransition(),h.busy=!1,clearTimeout(h.timeoutId),h.timeoutId=0),void(h.busy||(0!==h.timeoutId||a)&&(d=h.slides[h.currSlide],e=h.slides[h.nextSlide],f=h.API.getSlideOpts(h.nextSlide),g=h.API.calcTx(f,a),h._tx=g,a&&void 0!==f.manualSpeed&&(f.speed=f.manualSpeed),h.nextSlide!=h.currSlide&&(a||!h.paused&&!h.hoverPaused&&h.timeout)?(h.API.trigger("cycle-before",[f,d,e,b]),g.before&&g.before(f,d,e,b),c=function(){h.busy=!1,h.container.data("cycle.opts")&&(g.after&&g.after(f,d,e,b),h.API.trigger("cycle-after",[f,d,e,b]),h.API.queueTransition(f),h.API.updateView(!0))},h.busy=!0,g.transition?g.transition(f,d,e,b,c):h.API.doTransition(f,d,e,b,c),h.API.calcNextSlide(),h.API.updateView()):h.API.queueTransition(f))))},doTransition:function(b,c,d,e,f){var g=b,h=a(c),i=a(d),j=function(){i.animate(g.animIn||{opacity:1},g.speed,g.easeIn||g.easing,f)};i.css(g.cssBefore||{}),h.animate(g.animOut||{},g.speed,g.easeOut||g.easing,function(){h.css(g.cssAfter||{}),g.sync||j()}),g.sync&&j()},queueTransition:function(b,c){var d=this.opts(),e=void 0!==c?c:b.timeout;return 0===d.nextSlide&&0===--d.loop?(d.API.log("terminating; loop=0"),d.timeout=0,e?setTimeout(function(){d.API.trigger("cycle-finished",[d])},e):d.API.trigger("cycle-finished",[d]),void(d.nextSlide=d.currSlide)):void 0!==d.continueAuto&&(d.continueAuto===!1||a.isFunction(d.continueAuto)&&d.continueAuto()===!1)?(d.API.log("terminating automatic transitions"),d.timeout=0,void(d.timeoutId&&clearTimeout(d.timeoutId))):void(e&&(d._lastQueue=a.now(),void 0===c&&(d._remainingTimeout=b.timeout),d.paused||d.hoverPaused||(d.timeoutId=setTimeout(function(){d.API.prepareTx(!1,!d.reverse)},e))))},stopTransition:function(){var a=this.opts();a.slides.filter(":animated").length&&(a.slides.stop(!1,!0),a.API.trigger("cycle-transition-stopped",[a])),a._tx&&a._tx.stopTransition&&a._tx.stopTransition(a)},advanceSlide:function(a){var b=this.opts();return clearTimeout(b.timeoutId),b.timeoutId=0,b.nextSlide=b.currSlide+a,b.nextSlide<0?b.nextSlide=b.slides.length-1:b.nextSlide>=b.slides.length&&(b.nextSlide=0),b.API.prepareTx(!0,a>=0),!1},buildSlideOpts:function(c){var d,e,f=this.opts(),g=c.data()||{};for(var h in g)g.hasOwnProperty(h)&&/^cycle[A-Z]+/.test(h)&&(d=g[h],e=h.match(/^cycle(.*)/)[1].replace(/^[A-Z]/,b),f.API.log("["+(f.slideCount-1)+"]",e+":",d,"("+typeof d+")"),g[e]=d);g=a.extend({},a.fn.cycle.defaults,f,g),g.slideNum=f.slideCount;try{delete g.API,delete g.slideCount,delete g.currSlide,delete g.nextSlide,delete g.slides}catch(i){}return g},getSlideOpts:function(b){var c=this.opts();void 0===b&&(b=c.currSlide);var d=c.slides[b],e=a(d).data("cycle.opts");return a.extend({},c,e)},initSlide:function(b,c,d){var e=this.opts();c.css(b.slideCss||{}),d>0&&c.css("zIndex",d),isNaN(b.speed)&&(b.speed=a.fx.speeds[b.speed]||a.fx.speeds._default),b.sync||(b.speed=b.speed/2),c.addClass(e.slideClass)},updateView:function(a,b){var c=this.opts();if(c._initialized){var d=c.API.getSlideOpts(),e=c.slides[c.currSlide];!a&&b!==!0&&(c.API.trigger("cycle-update-view-before",[c,d,e]),c.updateView<0)||(c.slideActiveClass&&c.slides.removeClass(c.slideActiveClass).eq(c.currSlide).addClass(c.slideActiveClass),a&&c.hideNonActive&&c.slides.filter(":not(."+c.slideActiveClass+")").css("visibility","hidden"),0===c.updateView&&setTimeout(function(){c.API.trigger("cycle-update-view",[c,d,e,a])},d.speed/(c.sync?2:1)),0!==c.updateView&&c.API.trigger("cycle-update-view",[c,d,e,a]),a&&c.API.trigger("cycle-update-view-after",[c,d,e]))}},getComponent:function(b){var c=this.opts(),d=c[b];return"string"==typeof d?/^\s*[\>|\+|~]/.test(d)?c.container.find(d):a(d):d.jquery?d:a(d)},stackSlides:function(b,c,d){var e=this.opts();b||(b=e.slides[e.currSlide],c=e.slides[e.nextSlide],d=!e.reverse),a(b).css("zIndex",e.maxZ);var f,g=e.maxZ-2,h=e.slideCount;if(d){for(f=e.currSlide+1;h>f;f++)a(e.slides[f]).css("zIndex",g--);for(f=0;f=0;f--)a(e.slides[f]).css("zIndex",g--);for(f=h-1;f>e.currSlide;f--)a(e.slides[f]).css("zIndex",g--)}a(c).css("zIndex",e.maxZ-1)},getSlideIndex:function(a){return this.opts().slides.index(a)}},a.fn.cycle.log=function(){window.console&&console.log&&console.log("[cycle2] "+Array.prototype.join.call(arguments," "))},a.fn.cycle.version=function(){return"Cycle2: "+c},a.fn.cycle.transitions={custom:{},none:{before:function(a,b,c,d){a.API.stackSlides(c,b,d),a.cssBefore={opacity:1,visibility:"visible",display:"block"}}},fade:{before:function(b,c,d,e){var f=b.API.getSlideOpts(b.nextSlide).slideCss||{};b.API.stackSlides(c,d,e),b.cssBefore=a.extend(f,{opacity:0,visibility:"visible",display:"block"}),b.animIn={opacity:1},b.animOut={opacity:0}}},fadeout:{before:function(b,c,d,e){var f=b.API.getSlideOpts(b.nextSlide).slideCss||{};b.API.stackSlides(c,d,e),b.cssBefore=a.extend(f,{opacity:1,visibility:"visible",display:"block"}),b.animOut={opacity:0}}},scrollHorz:{before:function(a,b,c,d){a.API.stackSlides(b,c,d);var e=a.container.css("overflow","hidden").width();a.cssBefore={left:d?e:-e,top:0,opacity:1,visibility:"visible",display:"block"},a.cssAfter={zIndex:a._maxZ-2,left:0},a.animIn={left:0},a.animOut={left:d?-e:e}}}},a.fn.cycle.defaults={allowWrap:!0,autoSelector:".cycle-slideshow[data-cycle-auto-init!=false]",delay:0,easing:null,fx:"fade",hideNonActive:!0,loop:0,manualFx:void 0,manualSpeed:void 0,manualTrump:!0,maxZ:100,pauseOnHover:!1,reverse:!1,slideActiveClass:"cycle-slide-active",slideClass:"cycle-slide",slideCss:{position:"absolute",top:0,left:0},slides:"> img",speed:500,startingSlide:0,sync:!0,timeout:4e3,updateView:0},a(document).ready(function(){a(a.fn.cycle.defaults.autoSelector).cycle()})}(jQuery),/*! Cycle2 autoheight plugin; Copyright (c) M.Alsup, 2012; version: 20130913 */ -function(a){"use strict";function b(b,d){var e,f,g,h=d.autoHeight;if("container"==h)f=a(d.slides[d.currSlide]).outerHeight(),d.container.height(f);else if(d._autoHeightRatio)d.container.height(d.container.width()/d._autoHeightRatio);else if("calc"===h||"number"==a.type(h)&&h>=0){if(g="calc"===h?c(b,d):h>=d.slides.length?0:h,g==d._sentinelIndex)return;d._sentinelIndex=g,d._sentinel&&d._sentinel.remove(),e=a(d.slides[g].cloneNode(!0)),e.removeAttr("id name rel").find("[id],[name],[rel]").removeAttr("id name rel"),e.css({position:"static",visibility:"hidden",display:"block"}).prependTo(d.container).addClass("cycle-sentinel cycle-slide").removeClass("cycle-slide-active"),e.find("*").css("visibility","hidden"),d._sentinel=e}}function c(b,c){var d=0,e=-1;return c.slides.each(function(b){var c=a(this).height();c>e&&(e=c,d=b)}),d}function d(b,c,d,e){var f=a(e).outerHeight();c.container.animate({height:f},c.autoHeightSpeed,c.autoHeightEasing)}function e(c,f){f._autoHeightOnResize&&(a(window).off("resize orientationchange",f._autoHeightOnResize),f._autoHeightOnResize=null),f.container.off("cycle-slide-added cycle-slide-removed",b),f.container.off("cycle-destroyed",e),f.container.off("cycle-before",d),f._sentinel&&(f._sentinel.remove(),f._sentinel=null)}a.extend(a.fn.cycle.defaults,{autoHeight:0,autoHeightSpeed:250,autoHeightEasing:null}),a(document).on("cycle-initialized",function(c,f){function g(){b(c,f)}var h,i=f.autoHeight,j=a.type(i),k=null;("string"===j||"number"===j)&&(f.container.on("cycle-slide-added cycle-slide-removed",b),f.container.on("cycle-destroyed",e),"container"==i?f.container.on("cycle-before",d):"string"===j&&/\d+\:\d+/.test(i)&&(h=i.match(/(\d+)\:(\d+)/),h=h[1]/h[2],f._autoHeightRatio=h),"number"!==j&&(f._autoHeightOnResize=function(){clearTimeout(k),k=setTimeout(g,50)},a(window).on("resize orientationchange",f._autoHeightOnResize)),setTimeout(g,30))})}(jQuery),/*! caption plugin for Cycle2; version: 20130306 */ -function(a){"use strict";a.extend(a.fn.cycle.defaults,{caption:"> .cycle-caption",captionTemplate:"{{slideNum}} / {{slideCount}}",overlay:"> .cycle-overlay",overlayTemplate:"
{{title}}
{{desc}}
",captionModule:"caption"}),a(document).on("cycle-update-view",function(b,c,d,e){if("caption"===c.captionModule){a.each(["caption","overlay"],function(){var a=this,b=d[a+"Template"],f=c.API.getComponent(a);f.length&&b?(f.html(c.API.tmpl(b,d,c,e)),f.show()):f.hide()})}}),a(document).on("cycle-destroyed",function(b,c){var d;a.each(["caption","overlay"],function(){var a=this,b=c[a+"Template"];c[a]&&b&&(d=c.API.getComponent("caption"),d.empty())})})}(jQuery),/*! command plugin for Cycle2; version: 20140415 */ -function(a){"use strict";var b=a.fn.cycle;a.fn.cycle=function(c){var d,e,f,g=a.makeArray(arguments);return"number"==a.type(c)?this.cycle("goto",c):"string"==a.type(c)?this.each(function(){var h;return d=c,f=a(this).data("cycle.opts"),void 0===f?void b.log('slideshow must be initialized before sending commands; "'+d+'" ignored'):(d="goto"==d?"jump":d,e=f.API[d],a.isFunction(e)?(h=a.makeArray(g),h.shift(),e.apply(f.API,h)):void b.log("unknown command: ",d))}):b.apply(this,arguments)},a.extend(a.fn.cycle,b),a.extend(b.API,{next:function(){var a=this.opts();if(!a.busy||a.manualTrump){var b=a.reverse?-1:1;a.allowWrap===!1&&a.currSlide+b>=a.slideCount||(a.API.advanceSlide(b),a.API.trigger("cycle-next",[a]).log("cycle-next"))}},prev:function(){var a=this.opts();if(!a.busy||a.manualTrump){var b=a.reverse?1:-1;a.allowWrap===!1&&a.currSlide+b<0||(a.API.advanceSlide(b),a.API.trigger("cycle-prev",[a]).log("cycle-prev"))}},destroy:function(){this.stop();var b=this.opts(),c=a.isFunction(a._data)?a._data:a.noop;clearTimeout(b.timeoutId),b.timeoutId=0,b.API.stop(),b.API.trigger("cycle-destroyed",[b]).log("cycle-destroyed"),b.container.removeData(),c(b.container[0],"parsedAttrs",!1),b.retainStylesOnDestroy||(b.container.removeAttr("style"),b.slides.removeAttr("style"),b.slides.removeClass(b.slideActiveClass)),b.slides.each(function(){a(this).removeData(),c(this,"parsedAttrs",!1)})},jump:function(a,b){var c,d=this.opts();if(!d.busy||d.manualTrump){var e=parseInt(a,10);if(isNaN(e)||0>e||e>=d.slides.length)return void d.API.log("goto: invalid slide index: "+e);if(e==d.currSlide)return void d.API.log("goto: skipping, already on slide",e);d.nextSlide=e,clearTimeout(d.timeoutId),d.timeoutId=0,d.API.log("goto: ",e," (zero-index)"),c=d.currSlide .cycle-pager",pagerActiveClass:"cycle-pager-active",pagerEvent:"click.cycle",pagerEventBubble:void 0,pagerTemplate:""}),a(document).on("cycle-bootstrap",function(a,c,d){d.buildPagerLink=b}),a(document).on("cycle-slide-added",function(a,b,d,e){b.pager&&(b.API.buildPagerLink(b,d,e),b.API.page=c)}),a(document).on("cycle-slide-removed",function(b,c,d){if(c.pager){var e=c.API.getComponent("pager");e.each(function(){var b=a(this);a(b.children()[d]).remove()})}}),a(document).on("cycle-update-view",function(b,c){var d;c.pager&&(d=c.API.getComponent("pager"),d.each(function(){a(this).children().removeClass(c.pagerActiveClass).eq(c.currSlide).addClass(c.pagerActiveClass)}))}),a(document).on("cycle-destroyed",function(a,b){var c=b.API.getComponent("pager");c&&(c.children().off(b.pagerEvent),b.pagerTemplate&&c.empty())})}(jQuery),/*! prevnext plugin for Cycle2; version: 20140408 */ -function(a){"use strict";a.extend(a.fn.cycle.defaults,{next:"> .cycle-next",nextEvent:"click.cycle",disabledClass:"disabled",prev:"> .cycle-prev",prevEvent:"click.cycle",swipe:!1}),a(document).on("cycle-initialized",function(a,b){if(b.API.getComponent("next").on(b.nextEvent,function(a){a.preventDefault(),b.API.next()}),b.API.getComponent("prev").on(b.prevEvent,function(a){a.preventDefault(),b.API.prev()}),b.swipe){var c=b.swipeVert?"swipeUp.cycle":"swipeLeft.cycle swipeleft.cycle",d=b.swipeVert?"swipeDown.cycle":"swipeRight.cycle swiperight.cycle";b.container.on(c,function(){b._tempFx=b.swipeFx,b.API.next()}),b.container.on(d,function(){b._tempFx=b.swipeFx,b.API.prev()})}}),a(document).on("cycle-update-view",function(a,b){if(!b.allowWrap){var c=b.disabledClass,d=b.API.getComponent("next"),e=b.API.getComponent("prev"),f=b._prevBoundry||0,g=void 0!==b._nextBoundry?b._nextBoundry:b.slideCount-1;b.currSlide==g?d.addClass(c).prop("disabled",!0):d.removeClass(c).prop("disabled",!1),b.currSlide===f?e.addClass(c).prop("disabled",!0):e.removeClass(c).prop("disabled",!1)}}),a(document).on("cycle-destroyed",function(a,b){b.API.getComponent("prev").off(b.nextEvent),b.API.getComponent("next").off(b.prevEvent),b.container.off("swipeleft.cycle swiperight.cycle swipeLeft.cycle swipeRight.cycle swipeUp.cycle swipeDown.cycle")})}(jQuery),/*! progressive loader plugin for Cycle2; version: 20130315 */ -function(a){"use strict";a.extend(a.fn.cycle.defaults,{progressive:!1}),a(document).on("cycle-pre-initialize",function(b,c){if(c.progressive){var d,e,f=c.API,g=f.next,h=f.prev,i=f.prepareTx,j=a.type(c.progressive);if("array"==j)d=c.progressive;else if(a.isFunction(c.progressive))d=c.progressive(c);else if("string"==j){if(e=a(c.progressive),d=a.trim(e.html()),!d)return;if(/^(\[)/.test(d))try{d=a.parseJSON(d)}catch(k){return void f.log("error parsing progressive slides",k)}else d=d.split(new RegExp(e.data("cycle-split")||"\n")),d[d.length-1]||d.pop()}i&&(f.prepareTx=function(a,b){var e,f;return a||0===d.length?void i.apply(c.API,[a,b]):void(b&&c.currSlide==c.slideCount-1?(f=d[0],d=d.slice(1),c.container.one("cycle-slide-added",function(a,b){setTimeout(function(){b.API.advanceSlide(1)},50)}),c.API.add(f)):b||0!==c.currSlide?i.apply(c.API,[a,b]):(e=d.length-1,f=d[e],d=d.slice(0,e),c.container.one("cycle-slide-added",function(a,b){setTimeout(function(){b.currSlide=1,b.API.advanceSlide(-1)},50)}),c.API.add(f,!0)))}),g&&(f.next=function(){var a=this.opts();if(d.length&&a.currSlide==a.slideCount-1){var b=d[0];d=d.slice(1),a.container.one("cycle-slide-added",function(a,b){g.apply(b.API),b.container.removeClass("cycle-loading")}),a.container.addClass("cycle-loading"),a.API.add(b)}else g.apply(a.API)}),h&&(f.prev=function(){var a=this.opts();if(d.length&&0===a.currSlide){var b=d.length-1,c=d[b];d=d.slice(0,b),a.container.one("cycle-slide-added",function(a,b){b.currSlide=1,b.API.advanceSlide(-1),b.container.removeClass("cycle-loading")}),a.container.addClass("cycle-loading"),a.API.add(c,!0)}else h.apply(a.API)})}})}(jQuery),/*! tmpl plugin for Cycle2; version: 20121227 */ -function(a){"use strict";a.extend(a.fn.cycle.defaults,{tmplRegex:"{{((.)?.*?)}}"}),a.extend(a.fn.cycle.API,{tmpl:function(b,c){var d=new RegExp(c.tmplRegex||a.fn.cycle.defaults.tmplRegex,"g"),e=a.makeArray(arguments);return e.shift(),b.replace(d,function(b,c){var d,f,g,h,i=c.split(".");for(d=0;d1)for(h=g,f=0;f Date: Sun, 6 May 2018 19:07:41 +0200 Subject: [PATCH 15/18] =?UTF-8?q?Dammit=E2=80=A6=20I=20can't=20remember=20?= =?UTF-8?q?what=20I=20changed=20and=20why?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/assets/javascripts/application.js | 1 - app/assets/stylesheets/_settings.scss | 338 ++++++++++++++++++--- app/assets/stylesheets/application.scss | 3 - app/assets/stylesheets/customisations.scss | 23 ++ app/controllers/application_controller.rb | 1 + app/controllers/packages_controller.rb | 226 ++++++++------ app/helpers/packages_helper.rb | 14 +- app/views/layouts/_messages.slim | 38 +-- app/views/layouts/_topbar.slim | 10 +- app/views/moderate/index.slim | 2 +- app/views/my/profile.slim | 18 +- app/views/packages/_admin_dropdown.slim | 60 ++-- app/views/packages/_receive_upload.slim | 22 ++ app/views/packages/_user_dropdown.slim | 30 +- app/views/packages/details.slim | 25 +- app/views/packages/list.slim | 2 +- app/views/packages/upload.slim | 2 + app/views/packages/upload_receive.slim | 78 ++--- app/views/welcome/home.slim | 3 +- config/routes.rb | 2 +- 20 files changed, 579 insertions(+), 319 deletions(-) create mode 100644 app/views/packages/_receive_upload.slim diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 51ec189..278ae21 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -15,7 +15,6 @@ //= require foundation //= require fancybox //= require cookies_eu -//= require jquery.amaran //= require_tree . $(function(){ $(document).foundation(); }); diff --git a/app/assets/stylesheets/_settings.scss b/app/assets/stylesheets/_settings.scss index b6911de..e00bc13 100644 --- a/app/assets/stylesheets/_settings.scss +++ b/app/assets/stylesheets/_settings.scss @@ -21,25 +21,44 @@ // 16. Drilldown // 17. Dropdown // 18. Dropdown Menu -// 19. Forms -// 20. Label -// 21. Media Object -// 22. Menu -// 23. Meter -// 24. Off-canvas -// 25. Orbit -// 26. Pagination -// 27. Progress Bar -// 28. Responsive Embed -// 29. Reveal -// 30. Slider -// 31. Switch -// 32. Table -// 33. Tabs -// 34. Thumbnail -// 35. Title Bar -// 36. Tooltip -// 37. Top Bar +// 19. Flexbox Utilities +// 20. Forms +// 21. Label +// 22. Media Object +// 23. Menu +// 24. Meter +// 25. Off-canvas +// 26. Orbit +// 27. Pagination +// 28. Progress Bar +// 29. Prototype Arrow +// 30. Prototype Border-Box +// 31. Prototype Border-None +// 32. Prototype Bordered +// 33. Prototype Display +// 34. Prototype Font-Styling +// 35. Prototype List-Style-Type +// 36. Prototype Overflow +// 37. Prototype Position +// 38. Prototype Rounded +// 39. Prototype Separator +// 40. Prototype Shadow +// 41. Prototype Sizing +// 42. Prototype Spacing +// 43. Prototype Text-Decoration +// 44. Prototype Text-Transformation +// 45. Prototype Text-Utilities +// 46. Responsive Embed +// 47. Reveal +// 48. Slider +// 49. Switch +// 50. Table +// 51. Tabs +// 52. Thumbnail +// 53. Title Bar +// 54. Tooltip +// 55. Top Bar +// 56. Xy Grid @import 'util/util'; @@ -67,11 +86,16 @@ $body-font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; $body-antialiased: true; $global-margin: 1rem; $global-padding: 1rem; +$global-position: 1rem; $global-weight-normal: normal; $global-weight-bold: bold; $global-radius: 0; +$global-menu-padding: 0 1rem; +$global-menu-nested-margin: 1rem; $global-text-direction: ltr; -$global-flexbox: false; +$global-flexbox: true; +$global-prototype-breakpoints: false; +$global-color-pick-contrast-tolerance: 0; $print-transparent-backgrounds: true; @include add-foundation-colors; @@ -99,6 +123,7 @@ $grid-column-gutter: ( medium: 30px, ); $grid-column-align-edge: true; +$grid-column-alias: 'columns'; $block-grid-max: 8; // 4. Base Typography @@ -210,8 +235,17 @@ $accordion-content-padding: 1rem; // 8. Accordion Menu // ----------------- +$accordionmenu-padding: $global-menu-padding; +$accordionmenu-nested-margin: $global-menu-nested-margin; +$accordionmenu-submenu-padding: $accordionmenu-padding; $accordionmenu-arrows: true; $accordionmenu-arrow-color: $primary-color; +$accordionmenu-item-background: null; +$accordionmenu-border: null; +$accordionmenu-submenu-toggle-background: null; +$accordion-submenu-toggle-border: $accordionmenu-border; +$accordionmenu-submenu-toggle-width: 40px; +$accordionmenu-submenu-toggle-height: $accordionmenu-submenu-toggle-width; $accordionmenu-arrow-size: 6px; // 9. Badge @@ -235,11 +269,15 @@ $breadcrumbs-item-color-current: $black; $breadcrumbs-item-color-disabled: $medium-gray; $breadcrumbs-item-margin: 0.75rem; $breadcrumbs-item-uppercase: true; -$breadcrumbs-item-slash: true; +$breadcrumbs-item-separator: true; +$breadcrumbs-item-separator-item: '/'; +$breadcrumbs-item-separator-item-rtl: '\\'; +$breadcrumbs-item-separator-color: $medium-gray; // 11. Button // ---------- +$button-font-family: inherit; $button-padding: 0.85em 1em; $button-margin: 0 0 $global-margin 0; $button-fill: solid; @@ -248,6 +286,7 @@ $button-background-hover: scale-color($button-background, $lightness: -15%); $button-color: $white; $button-color-alt: $black; $button-radius: $global-radius; +$button-hollow-border-width: 1px; $button-sizes: ( tiny: 0.6rem, small: 0.75rem, @@ -292,7 +331,7 @@ $card-border: 1px solid $light-gray; $card-shadow: none; $card-border-radius: $global-radius; $card-padding: $global-padding; -$card-margin: $global-margin; +$card-margin-bottom: $global-margin; // 15. Close Button // ---------------- @@ -319,9 +358,13 @@ $closebutton-color-hover: $black; $drilldown-transition: transform 0.15s linear; $drilldown-arrows: true; +$drilldown-padding: $global-menu-padding; +$drilldown-nested-margin: 0; +$drilldown-background: $white; +$drilldown-submenu-padding: $drilldown-padding; +$drilldown-submenu-background: $white; $drilldown-arrow-color: $primary-color; $drilldown-arrow-size: 6px; -$drilldown-background: $white; // 17. Dropdown // ------------ @@ -344,11 +387,24 @@ $dropdown-sizes: ( $dropdownmenu-arrows: true; $dropdownmenu-arrow-color: $anchor-color; $dropdownmenu-arrow-size: 6px; +$dropdownmenu-arrow-padding: 1.5rem; $dropdownmenu-min-width: 200px; $dropdownmenu-background: $white; +$dropdownmenu-submenu-background: $dropdownmenu-background; +$dropdownmenu-padding: $global-menu-padding; +$dropdownmenu-nested-margin: 0; +$dropdownmenu-submenu-padding: $dropdownmenu-padding; $dropdownmenu-border: 1px solid $medium-gray; +$dropdown-menu-item-color-active: get-color(primary); +$dropdown-menu-item-background-active: transparent; -// 19. Forms +// 19. Flexbox Utilities +// --------------------- + +$flex-source-ordering-count: 6; +$flexbox-responsive-breakpoints: true; + +// 20. Forms // --------- $fieldset-border: 1px solid $medium-gray; @@ -375,11 +431,13 @@ $input-placeholder-color: $medium-gray; $input-font-family: inherit; $input-font-size: rem-calc(16); $input-font-weight: $global-weight-normal; +$input-line-height: $global-lineheight; $input-background: $white; $input-background-focus: $white; $input-background-disabled: $light-gray; $input-border: 1px solid $medium-gray; $input-border-focus: 1px solid $dark-gray; +$input-padding: $form-spacing / 2; $input-shadow: inset 0 1px 2px rgba($black, 0.1); $input-shadow-focus: 0 0 5px $medium-gray; $input-cursor-disabled: not-allowed; @@ -388,7 +446,7 @@ $input-number-spinners: true; $input-radius: $global-radius; $form-button-radius: $global-radius; -// 20. Label +// 21. Label // --------- $label-background: $primary-color; @@ -399,26 +457,28 @@ $label-font-size: 0.8rem; $label-padding: 0.33333rem 0.5rem; $label-radius: $global-radius; -// 21. Media Object +// 22. Media Object // ---------------- $mediaobject-margin-bottom: $global-margin; $mediaobject-section-padding: $global-padding; $mediaobject-image-width-stacked: 100%; -// 22. Menu +// 23. Menu // -------- $menu-margin: 0; -$menu-margin-nested: 1rem; -$menu-item-padding: 0.7rem 1rem; +$menu-nested-margin: $global-menu-nested-margin; +$menu-items-padding: $global-menu-padding; +$menu-simple-margin: 1rem; $menu-item-color-active: $white; $menu-item-background-active: get-color(primary); $menu-icon-spacing: 0.25rem; $menu-item-background-hover: $light-gray; -$menu-border: $light-gray; +$menu-state-back-compat: true; +$menu-centered-back-compat: true; -// 23. Meter +// 24. Meter // --------- $meter-height: 1rem; @@ -428,23 +488,26 @@ $meter-fill-good: $success-color; $meter-fill-medium: $warning-color; $meter-fill-bad: $alert-color; -// 24. Off-canvas +// 25. Off-canvas // -------------- $offcanvas-size: 250px; $offcanvas-vertical-size: 250px; $offcanvas-background: $light-gray; $offcanvas-shadow: 0 0 10px rgba($black, 0.7); -$offcanvas-push-zindex: 1; -$offcanvas-overlap-zindex: 10; -$offcanvas-reveal-zindex: 1; +$offcanvas-inner-shadow-size: 20px; +$offcanvas-inner-shadow-color: rgba($black, 0.25); +$offcanvas-overlay-zindex: 11; +$offcanvas-push-zindex: 12; +$offcanvas-overlap-zindex: 13; +$offcanvas-reveal-zindex: 12; $offcanvas-transition-length: 0.5s; $offcanvas-transition-timing: ease; $offcanvas-fixed-reveal: true; $offcanvas-exit-background: rgba($white, 0.25); $maincontent-class: 'off-canvas-content'; -// 25. Orbit +// 26. Orbit // --------- $orbit-bullet-background: $medium-gray; @@ -459,7 +522,7 @@ $orbit-control-background-hover: rgba($black, 0.5); $orbit-control-padding: 1rem; $orbit-control-zindex: 10; -// 26. Pagination +// 27. Pagination // -------------- $pagination-font-size: rem-calc(14); @@ -477,7 +540,7 @@ $pagination-mobile-items: false; $pagination-mobile-current-item: false; $pagination-arrows: true; -// 27. Progress Bar +// 28. Progress Bar // ---------------- $progress-height: 1rem; @@ -486,7 +549,168 @@ $progress-margin-bottom: $global-margin; $progress-meter-background: $primary-color; $progress-radius: $global-radius; -// 28. Responsive Embed +// 29. Prototype Arrow +// ------------------- + +$prototype-arrow-directions: ( + down, + up, + right, + left +); +$prototype-arrow-size: 0.4375rem; +$prototype-arrow-color: $black; + +// 30. Prototype Border-Box +// ------------------------ + +$prototype-border-box-breakpoints: $global-prototype-breakpoints; + +// 31. Prototype Border-None +// ------------------------- + +$prototype-border-none-breakpoints: $global-prototype-breakpoints; + +// 32. Prototype Bordered +// ---------------------- + +$prototype-bordered-breakpoints: $global-prototype-breakpoints; +$prototype-border-width: rem-calc(1); +$prototype-border-type: solid; +$prototype-border-color: $medium-gray; + +// 33. Prototype Display +// --------------------- + +$prototype-display-breakpoints: $global-prototype-breakpoints; +$prototype-display: ( + inline, + inline-block, + block, + table, + table-cell +); + +// 34. Prototype Font-Styling +// -------------------------- + +$prototype-font-breakpoints: $global-prototype-breakpoints; +$prototype-wide-letter-spacing: rem-calc(4); +$prototype-font-normal: $global-weight-normal; +$prototype-font-bold: $global-weight-bold; + +// 35. Prototype List-Style-Type +// ----------------------------- + +$prototype-list-breakpoints: $global-prototype-breakpoints; +$prototype-style-type-unordered: ( + disc, + circle, + square +); +$prototype-style-type-ordered: ( + decimal, + lower-alpha, + lower-latin, + lower-roman, + upper-alpha, + upper-latin, + upper-roman +); + +// 36. Prototype Overflow +// ---------------------- + +$prototype-overflow-breakpoints: $global-prototype-breakpoints; +$prototype-overflow: ( + visible, + hidden, + scroll +); + +// 37. Prototype Position +// ---------------------- + +$prototype-position-breakpoints: $global-prototype-breakpoints; +$prototype-position: ( + static, + relative, + absolute, + fixed +); +$prototype-position-z-index: 975; + +// 38. Prototype Rounded +// --------------------- + +$prototype-rounded-breakpoints: $global-prototype-breakpoints; +$prototype-border-radius: rem-calc(3); + +// 39. Prototype Separator +// ----------------------- + +$prototype-separator-breakpoints: $global-prototype-breakpoints; +$prototype-separator-align: center; +$prototype-separator-height: rem-calc(2); +$prototype-separator-width: 3rem; +$prototype-separator-background: $primary-color; +$prototype-separator-margin-top: $global-margin; + +// 40. Prototype Shadow +// -------------------- + +$prototype-shadow-breakpoints: $global-prototype-breakpoints; +$prototype-box-shadow: 0 2px 5px 0 rgba(0,0,0,.16), + 0 2px 10px 0 rgba(0,0,0,.12); + +// 41. Prototype Sizing +// -------------------- + +$prototype-sizing-breakpoints: $global-prototype-breakpoints; +$prototype-sizing: ( + width, + height +); +$prototype-sizes: ( + 25: 25%, + 50: 50%, + 75: 75%, + 100: 100% +); + +// 42. Prototype Spacing +// --------------------- + +$prototype-spacing-breakpoints: $global-prototype-breakpoints; +$prototype-spacers-count: 3; + +// 43. Prototype Text-Decoration +// ----------------------------- + +$prototype-decoration-breakpoints: $global-prototype-breakpoints; +$prototype-text-decoration: ( + overline, + underline, + line-through, +); + +// 44. Prototype Text-Transformation +// --------------------------------- + +$prototype-transformation-breakpoints: $global-prototype-breakpoints; +$prototype-text-transformation: ( + lowercase, + uppercase, + capitalize +); + +// 45. Prototype Text-Utilities +// ---------------------------- + +$prototype-utilities-breakpoints: $global-prototype-breakpoints; +$prototype-text-overflow: ellipsis; + +// 46. Responsive Embed // -------------------- $responsive-embed-margin-bottom: rem-calc(16); @@ -495,7 +719,7 @@ $responsive-embed-ratios: ( widescreen: 16 by 9, ); -// 29. Reveal +// 47. Reveal // ---------- $reveal-background: $white; @@ -507,7 +731,7 @@ $reveal-radius: $global-radius; $reveal-zindex: 1005; $reveal-overlay-background: rgba($black, 0.45); -// 30. Slider +// 48. Slider // ---------- $slider-width-vertical: 0.5rem; @@ -521,7 +745,7 @@ $slider-handle-background: $primary-color; $slider-opacity-disabled: 0.25; $slider-radius: $global-radius; -// 31. Switch +// 49. Switch // ---------- $switch-background: $medium-gray; @@ -537,7 +761,7 @@ $switch-paddle-offset: 0.25rem; $switch-paddle-radius: $global-radius; $switch-paddle-transition: all 0.25s ease-out; -// 32. Table +// 50. Table // --------- $table-background: $white; @@ -557,8 +781,9 @@ $table-foot-row-hover: darken($table-foot-background, $table-hover-scale); $table-head-font-color: $body-font-color; $table-foot-font-color: $body-font-color; $show-header-for-stacked: false; +$table-stack-breakpoint: medium; -// 33. Tabs +// 51. Tabs // -------- $tab-margin: 0; @@ -575,7 +800,7 @@ $tab-content-border: $light-gray; $tab-content-color: $body-font-color; $tab-content-padding: 1rem; -// 34. Thumbnail +// 52. Thumbnail // ------------- $thumbnail-border: solid 4px $white; @@ -585,7 +810,7 @@ $thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5); $thumbnail-transition: box-shadow 200ms ease-out; $thumbnail-radius: $global-radius; -// 35. Title Bar +// 53. Title Bar // ------------- $titlebar-background: $black; @@ -596,20 +821,22 @@ $titlebar-icon-color: $white; $titlebar-icon-color-hover: $medium-gray; $titlebar-icon-spacing: 0.25rem; -// 36. Tooltip +// 54. Tooltip // ----------- +$has-tip-cursor: help; $has-tip-font-weight: $global-weight-bold; $has-tip-border-bottom: dotted 1px $dark-gray; $tooltip-background-color: $black; $tooltip-color: $white; $tooltip-padding: 0.75rem; +$tooltip-max-width: 10rem; $tooltip-font-size: $small-font-size; $tooltip-pip-width: 0.75rem; $tooltip-pip-height: $tooltip-pip-width * 0.866; $tooltip-radius: $global-radius; -// 37. Top Bar +// 55. Top Bar // ----------- $topbar-padding: 0.5rem; @@ -618,3 +845,18 @@ $topbar-submenu-background: $topbar-background; $topbar-title-spacing: 0.5rem 1rem 0.5rem 0; $topbar-input-width: 200px; $topbar-unstack-breakpoint: medium; + +// 56. Xy Grid +// ----------- + +$xy-grid: true; +$grid-container: $global-width; +$grid-columns: 12; +$grid-margin-gutters: ( + small: 20px, + medium: 30px +); +$grid-padding-gutters: $grid-margin-gutters; +$grid-container-padding: $grid-padding-gutters; +$grid-container-max: $global-width; +$block-grid-max: 8; diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 246b8d8..5f83651 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -12,8 +12,5 @@ *= require foundation_and_overrides *= require cookies_eu *= require fancybox - * Flash/notifications using http://www.amaranjs.com/ - * https://github.com/hakanersu/AmaranJS - *= require amaran *= require font-awesome */ diff --git a/app/assets/stylesheets/customisations.scss b/app/assets/stylesheets/customisations.scss index 12e4ac4..56a6e2e 100644 --- a/app/assets/stylesheets/customisations.scss +++ b/app/assets/stylesheets/customisations.scss @@ -370,3 +370,26 @@ a.black // font-weight: bold; line-height: 1; } + +.button { + box-shadow: 0px 0px 5px #000000; + border-radius: 10px !important; +} + +// Simple animation for FontAwesome icons in callouts at the top. +.fa-wobble { + animation-name: fa-wobble; + animation-duration: 0.5s; + animation-direction: alternate; + animation-iteration-count: infinite; +} + +@keyframes fa-wobble { + 0% { opacity: 0.2; } + 50% { opacity: 0.8; } + 100% { opacity: 1; } +} + +.menu .menu-text { + font-size: 2.5rem; +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c40f76c..212b556 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -7,6 +7,7 @@ class ApplicationController < ActionController::Base # filter_parameter_logging :password before_action :get_current_users_screenshots, :debian_sso + # TODO: Deprecation. What is the replacement for Rails.env.development? ? before_action :better_errors_hack, if: -> { Rails.env.development? } # Query for packages that were uploaded by the current user. diff --git a/app/controllers/packages_controller.rb b/app/controllers/packages_controller.rb index 03c56a3..5870c95 100644 --- a/app/controllers/packages_controller.rb +++ b/app/controllers/packages_controller.rb @@ -1,29 +1,30 @@ class PackagesController < ApplicationController - + protect_from_forgery :except => :legacy_uploadfile - + def list @packages = query_packages.paginate(page: params[:page], per_page: 6) end - + def grid @packages = query_packages.paginate(page: params[:page], per_page: 24) end - + def details + # TODO: Get only screenshots visible to the user (admin or owner or approved) @package = Package.find_by(name: params[:name]) - + unless @package @packagename = params[:name] render 'notfound' end end - + # Show upload form for new images def upload @package = Package.find_by!(name: params[:name]) end - + # POST target of the screenshots upload form. # Receives uploaded images. Checks if they are valid. Asks for description. # This action saves the screenshots already if they are valid. The user is @@ -33,43 +34,63 @@ class PackagesController < ApplicationController # The user won't know that an account is created. # But this makes it easier to track who screenshots belong to. create_pseudo_user unless user_signed_in? - + @package = Package.find_by!(name: params[:name]) @valid_images = [] @invalid_images = [] - + params[:file].each do |img| new_screenshot = @package.screenshots.new(image: img) - + # Check if the image was valid if new_screenshot.valid? new_screenshot.uploaderhash = session[:token] new_screenshot.uploaderip = session[:ip] new_screenshot.version = @package.version new_screenshot.user = current_user - + # TODO: must it be moderated first? - + new_screenshot.save Log.log "Screenshot #{new_screenshot.id} uploaded successfully from #{session[:ip]}. User has token #{session[:token]} or is #{current_user}" + + if current_user.is_admin? + Log.log "Admin upload is automatically approved." + new_screenshot.approve_screenshot! + end + @valid_images.push new_screenshot else Log.log "Screenshot #{new_screenshot.image_file_name} invalid (#{new_screenshot.errors[:image]})." @invalid_images.push new_screenshot end end - + + errors = [] + @invalid_images.each do |image| + # errors << "The image #{image.image_file_name} #{image.errors[:image].join(' and ')}." + errors << "The image #{image.image_file_name} is not valid." + flash[:error] = errors.join(" ") + end + + # Redirect back to upload form if all uploads were invalid + unless @valid_images.any? + Log.log "No valid images uploaded. Back to upload form." + redirect_to(upload_path, error: errors) and return + end + # Show a list of invalid uploads by default. Or redirect to the review page # if all uploads were okay. - redirect_to upload_review_path unless @invalid_images - + # redirect_to upload_review_path unless @invalid_images + # TODO # if @invalid_images… # ' #{image.image_file_name} (#{image.errors[:image].join(' and ')}) - - # redirect_to package_path + + redirect_to package_path + # render :details end - + # Legacy action to upload an image along with metadata. # This was used in Debshots 1.x as the default upload method. # This method allows that old-style way to upload screenshots. @@ -82,9 +103,9 @@ class PackagesController < ApplicationController # - file def legacy_uploadfile @package = Package.find_by!(name: params[:packagename]) - + new_screenshot = @package.screenshots.new(image: params[:file]) - + # Check if the image was valid if new_screenshot.valid? # new_screenshot.uploaderhash = session[:token] @@ -98,30 +119,30 @@ class PackagesController < ApplicationController head :not_acceptable end end - + def delete_screenshot # Is the user allowed to delete the screenshot? @screenshot = Screenshot.find(params[:id]) - + # Check if the user is allowed to change this screenshot # - Is this the user's own screenshot? (anonymous) if @screenshot.user == current_user or current_user.is_admin? logger.debug "User #{current_user} deletes screenshot #{@screenshot}" @screenshot.destroy flash['notice'] = "Screenshot deleted." - redirect_to :back + redirect_back(fallback_location: package_path(name: @screenshot.package.name)) else head :forbidden end end - + def approve_screenshot @screenshot = Screenshot.find(params[:id]) @screenshot.approve_screenshot! flash['notice'] = "Screenshot approved." - redirect_to :back + redirect_back(fallback_location: package_path) end - + # Returns a 160x120 thumbnail image if posssible. # If the package is not found it returns a dummy image along with status 404. # If the package is found but has no screenshots then it also returns a @@ -132,25 +153,25 @@ class PackagesController < ApplicationController thumbnail404 return end - + # Called as /thumbnail-with-version/:name/:version if params[:version] @screenshot = @package.best_screenshot_for_version(params[:version]) - # Called as /thumbnail/:name + # Called as /thumbnail/:name else @screenshot = @package.screenshots.first end - + # Return a 404 if the package has no screenshots or the image was not found unless @screenshot and @screenshot.image.path thumbnail404 return end - + # Send the thumbnail (uses X-Sendfile or similar if possible) send_file @screenshot.image.path(:thumb), type: "image/png", disposition: 'inline' end - + # Returns a large screenshot image if posssible. # If the package is not found it returns a dummy image along with status 404. # If the package is found but has no screenshots then it also returns a @@ -161,39 +182,41 @@ class PackagesController < ApplicationController screenshot404 return end - + # Called as /thumbnail-with-version/:name/:version if params[:version] @screenshot = @package.best_screenshot_for_version(params[:version]) - # Called as /thumbnail/:name + # Called as /thumbnail/:name else @screenshot = @package.screenshots.first end - + # Return a 404 if the package has no screenshots or the image was not found unless @screenshot and @screenshot.image.path screenshot404 return end - + # Send the thumbnail (uses X-Sendfile or similar if possible) send_file @screenshot.image.path(:large), type: "image/png", disposition: 'inline' end - + # Receives a form with a simple text field 'description' so that users can update # the description of their screenshot. def update_screenshot_description - @screenshot = Screenshot.find(params[:id]) - @screenshot.description = params[:description] - @screenshot.save! - flash['notice'] = "Description updated." - redirect_to :back - end - - # Receive an anonymous report from a user to have a screenshot removed. - def report_screenshot - @screenshot = Screenshot.find(params[:id]) - # if verify_recaptcha + # TODO: Check permissions to do that + @screenshot = Screenshot.find(params[:id]) + # @screenshot.description = params[:description] + @screenshot.update params_screenshot_description + @screenshot.save! + flash['notice'] = "Description updated." + redirect_back(fallback_location: package_path(name: @screenshot.package.name)) + end + + # Receive an anonymous report from a user to have a screenshot removed. + def report_screenshot + @screenshot = Screenshot.find(params[:id]) + # if verify_recaptcha @screenshot.delete_reason = params[:delete_reason] @screenshot.markedfordelete = true if @screenshot.valid? @@ -203,57 +226,57 @@ class PackagesController < ApplicationController errors = @screenshot.errors.to_a.join(' and ') flash['alert'] = "Sorry. #{errors}" end - # end - redirect_to :back - end - - # Show an HTML partial with reviews of this package from the Ubuntu API - def reviews - expires_in 1.day, public: true - # @reviews = Package.find_by_name!(params[:name]).ubuntu_reviews - @reviews = get_ubuntu_reviews params[:name] - render '_reviews', layout: false - end - - private - - # Seamlessly create a user account for the current client. - # It helps track uploads. - def create_pseudo_user - generated_password = Devise.friendly_token.first(8) - new_user = User.create( + # end + redirect_back(fallback_location: package_path) + end + + # Show an HTML partial with reviews of this package from the Ubuntu API + def reviews + expires_in 1.day, public: true + # @reviews = Package.find_by_name!(params[:name]).ubuntu_reviews + @reviews = get_ubuntu_reviews params[:name] + render '_reviews', layout: false + end + + private + + # Seamlessly create a user account for the current client. + # It helps track uploads. + def create_pseudo_user + generated_password = Devise.friendly_token.first(8) + new_user = User.create( name: 'Anonymous', password: generated_password) - Log.log "New pseudo user for anonymous upload created: #{new_user}" - sign_in(new_user) - end - - # Send a dummy thumbnail reading "No screenshot available. Sorry." - def thumbnail404 - send_file Rails.root.join('public/images/dummy/thumbnail404.png'), - type: "image/png", - disposition: 'inline', - status: 404 - end - - def screenshot404 - send_file Rails.root.join('public/images/dummy/screenshot404.png'), - type: "image/png", - disposition: 'inline', - status: 404 - end - - # Return packages matching the criteria given by parameters - def query_packages - packages = Package.includes(:screenshots).order(visits: :desc) - - # text search - if params[:search].present? - logger.debug "Searching for #{params[:search]}" - packages = packages.general_search(params[:search]) + Log.log "New pseudo user for anonymous upload created: #{new_user}" + sign_in(new_user) end - - case params[:show] + + # Send a dummy thumbnail reading "No screenshot available. Sorry." + def thumbnail404 + send_file Rails.root.join('public/images/dummy/thumbnail404.png'), + type: "image/png", + disposition: 'inline', + status: 404 + end + + def screenshot404 + send_file Rails.root.join('public/images/dummy/screenshot404.png'), + type: "image/png", + disposition: 'inline', + status: 404 + end + + # Return packages matching the criteria given by parameters + def query_packages + packages = Package.includes(:screenshots).order(visits: :desc) + + # text search + if params[:search].present? + logger.debug "Searching for #{params[:search]}" + packages = packages.general_search(params[:search]) + end + + case params[:show] when 'with' packages = packages.with_screenshots logger.debug 'Limiting packages to those with screenshots' @@ -261,23 +284,23 @@ class PackagesController < ApplicationController packages = packages.without_screenshots logger.debug 'Limiting packages to those without screenshots' end - + return packages end - + # Store a random identifier and the client's IP address in the session # for later identification. def create_user_token session[:token] ||= SecureRandom.hex session[:ip] ||= request.remote_ip end - + # Get reviews of this package from the Ubuntu API def get_ubuntu_reviews(packagename) # Use the URL defined in the configuration to get a JSON string url = Rails.configuration.ubuntu_reviews_api_url % packagename logger.debug "Loading Ubuntu reviews for package #{packagename} from #{url}" - + body = open(url).read # Turn JSON into a Ruby data structure json = JSON.parse(body) @@ -288,5 +311,8 @@ class PackagesController < ApplicationController json = json.sort { |x,y| y['usefulness_total'].to_i <=> x['usefulness_total'].to_i} return json end - + + def params_screenshot_description + params.require(:screenshot).permit(:description) + end end diff --git a/app/helpers/packages_helper.rb b/app/helpers/packages_helper.rb index 3cae56c..123181d 100644 --- a/app/helpers/packages_helper.rb +++ b/app/helpers/packages_helper.rb @@ -3,16 +3,16 @@ module PackagesHelper # Return a query of all screenshots that the current user may see # Consists of: # - approved (public) screenshots - # - screenshots uploaded by the user (determined by cookie session) - # - all screenshots if the user is logged in + # - screenshots uploaded by the user + # - all screenshots if the user is an admin def screenshots_visible_to_user(package) - if user_signed_in? + if user_signed_in? and current_user.is_admin? # User is an admin - package.screenshots.order('created_at DESC') + package.screenshots + elsif user_signed_in? + current_user.screenshots else - package.screenshots.where( - "approved=true OR uploaderhash=?", session[:token] - ).order('created_at DESC') + package.screenshots.where(approved: true).order('created_at DESC') end end diff --git a/app/views/layouts/_messages.slim b/app/views/layouts/_messages.slim index ba6aa55..9d3fd1e 100644 --- a/app/views/layouts/_messages.slim +++ b/app/views/layouts/_messages.slim @@ -3,26 +3,18 @@ - if flash - flash.each do |name,msg| - case name - - when 'notice' - - flash_color = 'white' - - flash_bgcolor = 'green' - - when 'alert' - - flash_color = 'white' - - flash_bgcolor = 'red' - - when 'timedout' - - next - - else - - flash_color = 'black' - - flash_bgcolor = 'gray' - - javascript: - $.amaran({ - 'content': { - 'message' :'#{msg}', - 'color' : '#{flash_color}', - 'bgcolor' : '#{flash_bgcolor}' - }, - 'position' :'bottom right', - 'theme' : 'colorful', - 'delay' : 7000 - }); + - when 'timedout' + / Workaround for Devise sometimes sending "true" + - next + - when 'notice' + .callout.warning data-closable=true + = fa_icon 'info-circle wobble 2x' + strong< = msg + - when 'error' + .callout.alert data-closable=true + = fa_icon 'meh-o wobble 2x' + strong< = msg + - else + .callout.primary data-closable=true + = fa_icon 'info-circle wobble 2x' + strong< = msg diff --git a/app/views/layouts/_topbar.slim b/app/views/layouts/_topbar.slim index ff49b59..1810df5 100644 --- a/app/views/layouts/_topbar.slim +++ b/app/views/layouts/_topbar.slim @@ -1,14 +1,14 @@ -nav.top-bar - .row - .top-bar-left - .top-bar-title +.top-bar + .top-bar-left + ul.menu + li.menu-text a href='/' // TODO: Make the logo depend on the virtual host img src="/images/logos/debian.svg" width="35" alt="Debian logo" // TODO: Use proper distribution name ' Debian Screenshots > - .top-bar-right + .top-bar-right ul.dropdown.menu data-dropdown-menu=true li class=('active' if controller_name=='welcome' and action_name=='home') a href="/" Home diff --git a/app/views/moderate/index.slim b/app/views/moderate/index.slim index 00cacf3..a6a28ef 100644 --- a/app/views/moderate/index.slim +++ b/app/views/moderate/index.slim @@ -17,7 +17,7 @@ .small-6.columns p Please moderate: - @pending_screenshots.each do |screenshot| - .container.listview.text-center + .container.listview / = screenshot.status a.black.fancybox href=screenshot.image.url(:large, timestamp: false) rel='fancybox-thumb' title=screenshot.caption = image_tag(screenshot.image.url(:large, timestamp: false), alt: screenshot.caption) diff --git a/app/views/my/profile.slim b/app/views/my/profile.slim index 007f823..58c199e 100644 --- a/app/views/my/profile.slim +++ b/app/views/my/profile.slim @@ -13,14 +13,16 @@ / TODO: submit - save - h2 Your email address - p = current_user.email - p.help-text - ' This web site recognizes you by your email address. - ' Don't worry - it will not be shown or given to anyone. - - h2 Single-sign-on provider - p You logged in using #{current_user.pretty_provider}. + - unless current_user.email.blank? + h2 Your email address + p = current_user.email + p.help-text + ' This web site recognizes you by your email address. + ' Don't worry - it will not be shown or given to anyone. + + - unless current_user.provider.blank? + h2 Single-sign-on provider + p You logged in using #{current_user.pretty_provider}. h2 Number of screenshots you uploaded p = current_user.screenshots.count diff --git a/app/views/packages/_admin_dropdown.slim b/app/views/packages/_admin_dropdown.slim index cc98e5b..6a966e5 100644 --- a/app/views/packages/_admin_dropdown.slim +++ b/app/views/packages/_admin_dropdown.slim @@ -1,35 +1,33 @@ // Button that reveals a dropdown/modal for moderation/reporting -.button-bar - ul.button-group - - if not screenshot.approved - li - a.button.small.success[ - href=approve_screenshot_path(screenshot.id) - method='post' - ] Approve screenshot - - elsif screenshot.markedfordelete - li - a.button.small.success[ - href=approve_screenshot_path(screenshot.id) - method='post' - ] Keep screenshot +button-group + - if user_signed_in? + a.button.small.alert[ + href=delete_screenshot_path(screenshot.id) + onclick="return confirm('Really delete the screenshot?');" + ] Delete screenshot - - if user_signed_in? - li - a.button.small.alert[ - href=delete_screenshot_path(screenshot.id) - onclick="return confirm('Really delete the screenshot?');" - ] Delete screenshot - / Display additional information to admins - p Status: #{screenshot.adminstatus} - p Uploader IP=#{screenshot.uploaderip} - p Uploader Token=#{session[:token]} - p Uploaded #{screenshot.age_days} (#{screenshot.created_at}) + - if not screenshot.approved + a.button.small.success[ + href=approve_screenshot_path(screenshot.id) + method='post' + ] Approve screenshot + - elsif screenshot.markedfordelete + a.button.small.success[ + href=approve_screenshot_path(screenshot.id) + method='post' + ] Keep screenshot - / TODO: Move reporting screenshots to an extra page with a form and captcha - / - else - / / Allow anonymous users to report inappropriate screenshots - / = form_tag(report_screenshot_path(screenshot.id)) - / = text_area_tag 'delete_reason', nil, class: 'input-group-field', maxlength: 100, rows: 3, cols: 50 - / = submit_tag 'Request removal', class: 'button alert' + + / Display additional information to admins + p Status: #{screenshot.adminstatus} + p Uploader IP=#{screenshot.uploaderip} + p Uploader Token=#{session[:token]} + p Uploaded #{screenshot.age_days} (#{screenshot.created_at}) + + / TODO: Move reporting screenshots to an extra page with a form and captcha + / - else + / / Allow anonymous users to report inappropriate screenshots + / = form_tag(report_screenshot_path(screenshot.id)) + / = text_area_tag 'delete_reason', nil, class: 'input-group-field', maxlength: 100, rows: 3, cols: 50 + / = submit_tag 'Request removal', class: 'button alert' diff --git a/app/views/packages/_receive_upload.slim b/app/views/packages/_receive_upload.slim new file mode 100644 index 0000000..044fcad --- /dev/null +++ b/app/views/packages/_receive_upload.slim @@ -0,0 +1,22 @@ +/ Render this callout on top of the package's details page +/ if images have just been uploaded. +- if (@valid_images and @valid_images.any?) or (@invalid_images and @invalid_images.any?) + .row + .callout.warning + + - if @invalid_images.any? + = fa_icon 'exclamation-circle 2x pull-left' + p These images were not accepted: + + ul + - @invalid_images.each do |image| + li + ' #{image.image_file_name} (#{image.errors[:image].join(' and the image ')}) + + + + - if @valid_images.any? + = fa_icon 'check-circle 2x pull-left' + p + ' #{pluralize(@valid_images.length, 'screenshot')} received successfully. + ' Please review the images and add a description for each image. Thanks. diff --git a/app/views/packages/_user_dropdown.slim b/app/views/packages/_user_dropdown.slim index 642e7c3..baf2003 100644 --- a/app/views/packages/_user_dropdown.slim +++ b/app/views/packages/_user_dropdown.slim @@ -1,13 +1,17 @@ -// TODO: Can be deleted? -// -// Button that reveals a dropdown/modal for users (for their own screenshots) -.text-right - button.small.dropdown.warning.button type="button" data-toggle="admin-info-#{screenshot.id}" - 'Manage your screenshot -.dropdown-pane data-dropdown=true id="admin-info-#{screenshot.id}" - a.button.small.alert[ - href=delete_screenshot_path(screenshot.id) - onclick="return confirm('Really delete the screenshot?');" - ] Delete screenshot - p - i =screenshot.status +/ Ask the user to add a description if not yet done +- if screenshot.description.blank? + / TODO: Allow the user to edit the screenshot's description again? + div + = form_for screenshot, url: update_screenshot_description_path(screenshot) do |f| + label for='description' Please add a short description of this screenshot: + .input-group + span.input-group-label = fa_icon 'edit wobble' + = f.text_field 'description', class: 'input-group-field', maxlength: 80 + .input-group-button + = f.submit 'Save', class: 'button success' + +/ Offer to delete the screenshot +a.button.small.alert[ + href=delete_screenshot_path(screenshot.id) + onclick="return confirm('Really delete your screenshot again?');" + ] Delete your screenshot diff --git a/app/views/packages/details.slim b/app/views/packages/details.slim index 0beb485..e1d3be6 100644 --- a/app/views/packages/details.slim +++ b/app/views/packages/details.slim @@ -1,4 +1,6 @@ -/ = link_to 'Back', :back, class: 'button small' +/ If images were just uploaded then show upload success/errors +/ in a callout. += render 'receive_upload' .row.packagepage .small-12.columns @@ -10,7 +12,7 @@ - if screenshots_visible_to_user(@package).count > 0 - screenshots_visible_to_user(@package).each do |screenshot| .row.listview - .text-center + / .text-center a.black.fancybox href=screenshot.image.url(:large, timestamp: false) rel='fancybox-thumb' title=screenshot.caption = image_tag(screenshot.image.url(:large, timestamp: false), alt: screenshot.caption) - if screenshot.description @@ -26,22 +28,9 @@ - if user_signed_in? and current_user.is_admin? = render(partial: 'admin_dropdown', locals: {screenshot: screenshot}) // or does the screenshot belong to the user (determined by session cookie) - - elsif screenshot.uploaderhash == session[:token] - / = render(partial: 'user_dropdown', locals: {screenshot: screenshot}) - / Ask the user to add a description if not yet done - - if screenshot.description.blank? - .imgcaption - p Please add a brief description of this screenshot: - = form_tag(update_screenshot_description_path(screenshot.package.name, screenshot.id)) - .input-group - = text_field_tag 'description', nil, class: 'input-group-field', maxlength: 80 - .input-group-button - =submit_tag 'Save', class: 'button success' - / Offer to delete the screenshot - a.button.small.alert[ - href=delete_screenshot_path(screenshot.id) - onclick="return confirm('Really delete your screenshot again?');" - ] Delete your screenshot + - elsif user_signed_in? and screenshot.user == current_user + = render(partial: 'user_dropdown', locals: {screenshot: screenshot}) + // or is the user not related to the screenshot and the screenshot is public? // TODO: Move to a seperate page with a form diff --git a/app/views/packages/list.slim b/app/views/packages/list.slim index a65b7bd..5557575 100644 --- a/app/views/packages/list.slim +++ b/app/views/packages/list.slim @@ -26,7 +26,7 @@ = image_tag(screenshot.image.url(:large, timestamp: false), alt: screenshot.caption) - if pkg.screenshots.length > 1 a.black href=package_path(name: pkg.name) - .text-center and #{pluralize(pkg.screenshots.length-1, 'screenshot')} more… + 'and #{pluralize(pkg.screenshots.length-1, 'screenshot')} more… - else a href=package_path(name: pkg.name) img.screenshot src="/images/dummy/no-screenshots-upload-one.svg" diff --git a/app/views/packages/upload.slim b/app/views/packages/upload.slim index 08006dd..3fc9bad 100644 --- a/app/views/packages/upload.slim +++ b/app/views/packages/upload.slim @@ -1,3 +1,5 @@ += render 'receive_upload' + .row h1 = "Upload screenshots for #{@package.name}" diff --git a/app/views/packages/upload_receive.slim b/app/views/packages/upload_receive.slim index 95abdcd..bc9ff64 100644 --- a/app/views/packages/upload_receive.slim +++ b/app/views/packages/upload_receive.slim @@ -1,59 +1,23 @@ -/ This page only gets displayed if there was invalid uploads. -/ Otherwise the user gets redirected to the upload_review page directly. -.row - h1 Some images feel wrong… +h1 Nothing to see here! -.row - p - ' Thanks for your upload. Just wanted to let you know that - ' these images were not accepted: - - ul - - @invalid_images.each do |image| - li - ' #{image.image_file_name} (#{image.errors[:image].join(' and the image ')}) - - - - - if @valid_images.any? - p - ' But do not worry. #{pluralize(@valid_images.length, 'screenshot')} seems good. - ' Let us take a look at them. - - p - a.button href=package_path Okay - -/ -/ = form_for :screenshot, url: upload_review2_path do |f| -/ -/ p 'Please check the images and add -/ ' short one-line descriptions that describe each of the images. -/ -/ - @valid_images.each do |screenshot| -/ +/ / Show upload success/errors and load details view. +/ .row +/ .callout +/ +/ - if @invalid_images.any? +/ p These images were not accepted: +/ +/ ul +/ - @invalid_images.each do |image| +/ li +/ ' #{image.image_file_name} (#{image.errors[:image].join(' and the image ')}) +/ +/ +/ +/ - if @valid_images.any? / .row -/ -/ .small-4.columns -/ a.black.fancybox href=screenshot.image.url(:large, timestamp: false) title='Uploaded image' -/ = image_tag(screenshot.image.url(:large, timestamp: false), alt: 'Uploaded image') -/ .small-8.columns -/ em Your file name: -/ p -/ = screenshot.image_file_name -/ -/ em Description: -/ p -/ = text_field_tag "description-#{screenshot.id}" -/ -/ em Delete this image? -/ p -/ = check_box "delete-#{screenshot.id}", "Delete this image" -/ -/ .row -/ button.button type="submit" id="file-submit" -/ = fa_stacked_icon "check", base: "circle-thin" -/ ' Done -/ -/ - else -/ .row -/ a.button href=upload_path Back to upload form +/ p +/ ' #{pluralize(@valid_images.length, 'screenshot')} received successfully. +/ ' Please review the images and add a description for each image. Thanks. +/ +/ = render 'packages/details.slim' diff --git a/app/views/welcome/home.slim b/app/views/welcome/home.slim index 8903070..377885d 100644 --- a/app/views/welcome/home.slim +++ b/app/views/welcome/home.slim @@ -23,7 +23,6 @@ input.button.postfix type="submit" value="Search" div.row - hr div.large-4.medium-12.small-12.columns.text-center h2 Newest upload a.black href=package_path(@newest_upload.package.name) @@ -47,7 +46,7 @@ div.row a href=upload_path(@most_wanted_package.name) img src="/images/dummy/no-screenshots-upload-one.svg" width="160" p - ' We lack a screenshot for the + ' We lack a screenshot for the strong =>@most_wanted_package.name ' package. Can you a href=package_path(@most_wanted_package.name) provide one diff --git a/config/routes.rb b/config/routes.rb index 34ae0ef..6f05df9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,7 +27,7 @@ Rails.application.routes.draw do post 'upload/:name' => 'packages#upload_receive', as: :upload_receive, name: /[^\/]+/ # TODO: "get" is probably the wrong method to delete a screenshot get 'delete_screenshot/:id' => 'packages#delete_screenshot', as: :delete_screenshot - post 'update_screenshot_description/:name/:id' => 'packages#update_screenshot_description', as: :update_screenshot_description + patch 'update_screenshot_description/:id' => 'packages#update_screenshot_description', as: :update_screenshot_description #post 'report_screenshot/:id' => 'packages#report_screenshot', as: :report_screenshot # TODO: "get" is probably the wrong method to delete a screenshot get 'approve_screenshot/:id' => 'packages#approve_screenshot', as: :approve_screenshot From 32e20a4b6fb1982b0cd19bbbc0535032dda2d7d2 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 14 May 2018 21:33:32 +0200 Subject: [PATCH 16/18] Screw Docker. It's just annoying. --- Dockerfile | 39 --------------------------------------- docker-compose.yml | 16 ---------------- 2 files changed, 55 deletions(-) delete mode 100644 Dockerfile delete mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index b62c213..0000000 --- a/Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -#FROM debian:9 -FROM ruby:2.5-stretch -MAINTAINER email@christoph-haas.de - -# Install apt based dependencies required to run Rails as -# well as RubyGems. As the Ruby image itself is based on a -# Debian image, we use apt-get to install those. -RUN apt-get update && apt-get install -y \ - build-essential ruby ruby-dev libpq-dev libbz2-dev libbz2-1.0 zlib1g zlib1g-dev postgresql-client - -RUN apt-get install -y git -RUN apt-get install -y rbenv - -# Configure the main working directory. This is the base -# directory used in any further RUN, COPY, and ENTRYPOINT -# commands. -RUN mkdir -p /app -WORKDIR /app - -# Copy the Gemfile as well as the Gemfile.lock and install -# the RubyGems. This is a separate step so the dependencies -# will be cached unless changes to one of those two files -# are made. -COPY Gemfile Gemfile.lock ./ -#RUN gem install bundler && bundle install --jobs 20 --retry 5 -RUN bundle install --jobs 20 --retry 5 - -# Copy the main application. -COPY . ./ - -# Expose port 3000 to the Docker host, so we can access it -# from the outside. -EXPOSE 3000 - -# The main command to run when the container starts. Also -# tell the Rails dev server to bind to all interfaces by -# default. -CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"] - diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 998968b..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: '3' -services: - # db: - # image: postgres - # volumes: - # - ./tmp/db:/var/lib/postgresql/data - debshots: - build: . - command: bundle exec rails s -p 3000 -b '0.0.0.0' - volumes: - - .:/myapp - ports: - - "3000:3000" - # depends_on: - # - db - From b6fff15a7baef6507508469f9f6fe88bb617ac1b Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 14 May 2018 22:15:13 +0200 Subject: [PATCH 17/18] Added pretty text for local auth --- app/models/user.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/user.rb b/app/models/user.rb index 4e3b7f0..99812d7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -32,6 +32,8 @@ class User < ApplicationRecord 'Amazon' when 'github' 'GitHub' + else + 'local authentication' end end From ba440ffb3a21bd2fb75d54e8c8e04623aea8eaba Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Mon, 14 May 2018 22:15:18 +0200 Subject: [PATCH 18/18] Typo --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 5e65484..57b9497 100644 --- a/README.md +++ b/README.md @@ -17,5 +17,3 @@ Install the dependencies: Install a PostgreSQL server: apt install postgresql - -C