From 7a233c33b427b82d47036be7d861100c16211ac8 Mon Sep 17 00:00:00 2001 From: J S Diaz Date: Thu, 26 Jul 2018 15:42:36 -0400 Subject: [PATCH 01/70] added space between export buttons --- app/assets/stylesheets/hubs.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/stylesheets/hubs.scss b/app/assets/stylesheets/hubs.scss index aefcdba3..25db0811 100644 --- a/app/assets/stylesheets/hubs.scss +++ b/app/assets/stylesheets/hubs.scss @@ -19,6 +19,7 @@ $dark-gray: #777; background: $white; border-color: $dark-gray; color: $dark-gray; + margin-left: 2px; } .badge { From 48dc9612de195591d0edf35379c34bf042caa83f Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Mon, 6 Aug 2018 11:45:39 +0000 Subject: [PATCH 02/70] Allow to copy and move items between hubs --- app/controllers/feed_items_controller.rb | 79 ++++++++++++++++++- app/helpers/hub_feeds_helper.rb | 2 +- app/interactions/feed_items/copy_to_hub.rb | 14 ++++ app/interactions/feed_items/move_to_hub.rb | 17 ++++ app/views/feed_items/about.html.haml | 6 +- app/views/feed_items/content.html.haml | 6 +- app/views/feed_items/controls.html.haml | 20 ++++- app/views/feed_items/copy_move_to_hub.haml | 11 +++ app/views/feed_items/edit.html.haml | 6 +- app/views/feed_items/related.html.haml | 6 +- app/views/feed_items/show.html.haml | 6 +- .../hub_feed_item_tag_filters/index.html.haml | 6 +- config/routes.rb | 3 + 13 files changed, 171 insertions(+), 11 deletions(-) create mode 100644 app/interactions/feed_items/copy_to_hub.rb create mode 100644 app/interactions/feed_items/move_to_hub.rb create mode 100644 app/views/feed_items/copy_move_to_hub.haml diff --git a/app/controllers/feed_items_controller.rb b/app/controllers/feed_items_controller.rb index 6e2e6a49..87551107 100644 --- a/app/controllers/feed_items_controller.rb +++ b/app/controllers/feed_items_controller.rb @@ -116,6 +116,22 @@ def tag_list end end + def copy_move_to_hub + @from_hub = @hub_feed.hub + @hubs = current_user.my_bookmarkable_hubs.uniq - [@from_hub] + @type = params[:type] + + respond_to do |format| + format.html do + if request.xhr? + render layout: false + else + render + end + end + end + end + def edit authorize @feed_item @@ -126,7 +142,7 @@ def edit def update authorize @feed_item - + inputs = { feed_item: @feed_item, hub: @hub, user: current_user }.reverse_merge(feed_item_params) outcome = FeedItems::Update.run(inputs) @@ -150,6 +166,55 @@ def remove_item redirect_to items_hub_path(@hub_feed.hub) end + def copy_item + hub = Hub.find(params[:to_hub_id]) + + unless current_user.is?(%i[owner bookmarker], hub) + flash[:notice] = 'You have no access to the destination hub.' + redirect_after_move_copy(@hub_feed, @feed_item) + end + + FeedItems::CopyToHub.run!( + current_user: current_user, + feed_item: @feed_item, + hub: hub + ) + + flash[:notice] = 'Item successfully copied to the hub.' + redirect_after_move_copy(@hub_feed, @feed_item) + rescue Exception => e + flash[:notice] = 'There was a problem while copying the item to the hub.' + redirect_after_move_copy(@hub_feed, @feed_item) + end + + def move_item + from_hub = @hub_feed.hub + to_hub = Hub.find(params[:to_hub_id]) + + unless current_user.is?(%i[owner bookmarker], to_hub) + flash[:notice] = 'You have no access to the destination hub.' + redirect_after_move_copy(@hub_feed, @feed_item) + end + + unless policy(from_hub).remove_item? + flash[:notice] = 'You have no access to move items from the source hub.' + redirect_after_move_copy(@hub_feed, @feed_item) + end + + FeedItems::MoveToHub.run!( + current_user: current_user, + feed_item: @feed_item, + from_hub_feed: @hub_feed, + to_hub: to_hub + ) + + flash[:notice] = 'Item successfully moved from the hub.' + redirect_after_move_copy(@hub_feed, @feed_item) + rescue Exception => e + flash[:notice] = 'There was a problem while moving the item from the hub.' + redirect_after_move_copy(@hub_feed, @feed_item) + end + private def set_hub_feed @@ -181,8 +246,18 @@ def add_breadcrumbs end end end - + def feed_item_params params.require(:feed_item).permit(:description, :title, :url, :date_published) end + + def redirect_after_move_copy(hub_feed, feed_item) + referer = Rails.application.routes.recognize_path(request.referrer) + + if referer[:action] == 'items' + redirect_to items_hub_path(hub_feed.hub) + else + redirect_to hub_feed_feed_item_path(hub_feed, feed_item) + end + end end diff --git a/app/helpers/hub_feeds_helper.rb b/app/helpers/hub_feeds_helper.rb index a278ed50..ad5ac5bf 100644 --- a/app/helpers/hub_feeds_helper.rb +++ b/app/helpers/hub_feeds_helper.rb @@ -3,7 +3,7 @@ module HubFeedsHelper def hub_feed_updated(hub_feed) updated_at = - if hub_feed.feed_items.any? + if hub_feed.feed_items.any? && !hub_feed.feed_items.first.date_published.nil? hub_feed.feed_items.first.date_published else hub_feed.feed.updated_at diff --git a/app/interactions/feed_items/copy_to_hub.rb b/app/interactions/feed_items/copy_to_hub.rb new file mode 100644 index 00000000..1033e6ad --- /dev/null +++ b/app/interactions/feed_items/copy_to_hub.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module FeedItems + class CopyToHub < ActiveInteraction::Base + object :feed_item + object :hub + object :current_user, class: User + + def execute + feed_item.feeds << current_user.get_default_bookmarking_bookmark_collection_for(hub.id) + feed_item.save! + end + end +end diff --git a/app/interactions/feed_items/move_to_hub.rb b/app/interactions/feed_items/move_to_hub.rb new file mode 100644 index 00000000..99f87b9c --- /dev/null +++ b/app/interactions/feed_items/move_to_hub.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module FeedItems + # Return a collection of users of tag filters applied to the feed item + class MoveToHub < ActiveInteraction::Base + object :feed_item + object :from_hub_feed, class: HubFeed + object :to_hub, class: Hub + object :current_user, class: User + + def execute + feed_item.feeds -= [from_hub_feed.feed] + feed_item.feeds << current_user.get_default_bookmarking_bookmark_collection_for(to_hub.id) + feed_item.save! + end + end +end diff --git a/app/views/feed_items/about.html.haml b/app/views/feed_items/about.html.haml index 31b578a1..c13a87b1 100644 --- a/app/views/feed_items/about.html.haml +++ b/app/views/feed_items/about.html.haml @@ -1,6 +1,10 @@ - content_for :top_panel do .col-sm-12 - %h1= @feed_item.title + %h1 + = @feed_item.title + - unless current_user.nil? + = link_to controls_hub_feed_feed_item_path(@hub_feed, @feed_item), class: 'control', title: 'Item actions' do + = fa_icon 'cog' %h4 = @hub_feed.feed.title = @feed_item.created_at.strftime('%F') diff --git a/app/views/feed_items/content.html.haml b/app/views/feed_items/content.html.haml index 05db1552..9888d16f 100644 --- a/app/views/feed_items/content.html.haml +++ b/app/views/feed_items/content.html.haml @@ -1,6 +1,10 @@ - content_for :top_panel do .col-sm-12 - %h1= @feed_item.title + %h1 + = @feed_item.title + - unless current_user.nil? + = link_to controls_hub_feed_feed_item_path(@hub_feed, @feed_item), class: 'control', title: 'Item actions' do + = fa_icon 'cog' %h4 = @hub_feed.feed.title = @feed_item.created_at.strftime('%F') diff --git a/app/views/feed_items/controls.html.haml b/app/views/feed_items/controls.html.haml index eed3175b..342629e4 100644 --- a/app/views/feed_items/controls.html.haml +++ b/app/views/feed_items/controls.html.haml @@ -25,14 +25,30 @@ method: :post, confirm: 'Are you sure?' do = fa_icon 'minus-circle', text: 'Remove from this bookmarking collection', class: 'text-danger' + %li + = link_to copy_move_to_hub_hub_feed_feed_item_path(@hub_feed, @feed_item, type: 'copy'), + title: "Copy the item #{@feed_item} to a hub", + class: 'dialog-show', + data_item_type: 'FeedItem', + data_item_id: @feed_item.id do + = fa_icon 'copy', text: 'Copy the item to a hub', class: 'text-warning' + - if policy(@hub).remove_item? + %li + = link_to copy_move_to_hub_hub_feed_feed_item_path(@hub_feed, @feed_item, type: 'move'), + title: "Move the item #{@feed_item} to a hub", + class: 'dialog-show', + data_item_type: 'FeedItem', + data_item_id: @feed_item.id do + = fa_icon 'angle-double-right', text: 'Move the item to a hub', class: 'text-warning' - if policy(@hub).remove_item? %li = link_to remove_item_hub_feed_feed_item_path(@hub_feed, @feed_item), - title: "Remove #{@feed_item} from the hub", + title: "Remove the #{@feed_item} from the hub", method: :delete, data: { confirm: 'Are you sure?' } do - = fa_icon 'trash', text: 'Remove item from hub', class: 'text-warning' + = fa_icon 'trash', text: 'Remove the item from the hub', class: 'text-warning' - tag_list = @feed_item.all_tags_list_on(@hub.tagging_key).join(', ') + - if policy(@hub).remove_item? %li.top-spacer = link_to hub_feed_item_tag_filters_path(@hub, @feed_item), data_hub_id: @hub.id, diff --git a/app/views/feed_items/copy_move_to_hub.haml b/app/views/feed_items/copy_move_to_hub.haml new file mode 100644 index 00000000..5cc1910c --- /dev/null +++ b/app/views/feed_items/copy_move_to_hub.haml @@ -0,0 +1,11 @@ +- if @hubs.any? + Select a hub: + %ul + - @hubs.each do |hub| + %li + - if @type == 'copy' + = link_to hub, copy_item_hub_feed_feed_item_path(@hub_feed, @feed_item, to_hub_id: hub.id), method: :post + - else + = link_to hub, move_item_hub_feed_feed_item_path(@hub_feed, @feed_item, to_hub_id: hub.id), method: :post +- else + You can't copy the item, you don't have access to any other hub. diff --git a/app/views/feed_items/edit.html.haml b/app/views/feed_items/edit.html.haml index 68cbeac7..a9fe13e1 100644 --- a/app/views/feed_items/edit.html.haml +++ b/app/views/feed_items/edit.html.haml @@ -1,6 +1,10 @@ - content_for :top_panel do .col-sm-12 - %h1= @feed_item.title + %h1 + = @feed_item.title + - unless current_user.nil? + = link_to controls_hub_feed_feed_item_path(@hub_feed, @feed_item), class: 'control', title: 'Item actions' do + = fa_icon 'cog' %h4 = @hub_feed.feed.title = @feed_item.created_at.strftime('%F') diff --git a/app/views/feed_items/related.html.haml b/app/views/feed_items/related.html.haml index c74b0f43..4a4262a2 100644 --- a/app/views/feed_items/related.html.haml +++ b/app/views/feed_items/related.html.haml @@ -1,6 +1,10 @@ - content_for :top_panel do .col-sm-12 - %h1= @feed_item.title + %h1 + = @feed_item.title + - unless current_user.nil? + = link_to controls_hub_feed_feed_item_path(@hub_feed, @feed_item), class: 'control', title: 'Item actions' do + = fa_icon 'cog' %h4 = @hub_feed.feed.title if @hub_feed = @feed_item.created_at.strftime('%F') diff --git a/app/views/feed_items/show.html.haml b/app/views/feed_items/show.html.haml index 729150d3..94d253be 100644 --- a/app/views/feed_items/show.html.haml +++ b/app/views/feed_items/show.html.haml @@ -1,6 +1,10 @@ - content_for :top_panel do .col-sm-12 - %h1= @feed_item.title + %h1 + = @feed_item.title + - unless current_user.nil? + = link_to controls_hub_feed_feed_item_path(@hub_feed, @feed_item), class: 'control', title: 'Item actions' do + = fa_icon 'cog' %h4 = @hub_feed.feed.title = @feed_item.created_at.strftime('%F') diff --git a/app/views/hub_feed_item_tag_filters/index.html.haml b/app/views/hub_feed_item_tag_filters/index.html.haml index ad2ec6b3..db7467b3 100644 --- a/app/views/hub_feed_item_tag_filters/index.html.haml +++ b/app/views/hub_feed_item_tag_filters/index.html.haml @@ -1,6 +1,10 @@ - content_for :top_panel do .col-sm-12 - %h1= @feed_item.title + %h1 + = @feed_item.title + - unless current_user.nil? + = link_to controls_hub_feed_feed_item_path(@hub_feed, @feed_item), class: 'control', title: 'Item actions' do + = fa_icon 'cog' %h4 = items_feed_titles(@feed_item, @hub) = @feed_item.created_at.strftime('%F') diff --git a/config/routes.rb b/config/routes.rb index e31d6936..dee0753c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -57,6 +57,9 @@ get 'related' get 'controls' delete 'remove_item' + get 'copy_move_to_hub' + post 'copy_item' + post 'move_item' end end resources :tags From 27df5b1eb5fd07a33bb888094009aec26d60aa24 Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Fri, 10 Aug 2018 10:21:43 +0000 Subject: [PATCH 03/70] Allow to log out using both GET and DELETE http methods --- config/routes.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index dee0753c..97b122f6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -193,6 +193,11 @@ registrations: 'users/registrations' } + # Allow users to log out using both DELETE and GET + devise_scope :user do + get '/accounts/sign_out', to: 'devise/sessions#destroy' + end + require 'sidekiq/web' authenticate :user, ->(u) { u.has_role? :superadmin } do mount Sidekiq::Web => '/sidekiq' From 6c3d730acce540d43ef749344adf10e4a6173c15 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 15:27:09 +0000 Subject: [PATCH 04/70] Added Are you Sure? prompt for hub or hub-feed level AddTagFilter. Resolves #15635. --- app/assets/javascripts/application.js | 47 ++++--------------- app/helpers/tag_filter_helper.rb | 3 +- .../send_notification_job.rb | 2 +- .../hub_feed_item_tag_filters/index.html.haml | 2 +- .../hub_feed_tag_filters/index.html.haml | 2 +- app/views/hub_tag_filters/index.html.haml | 2 +- 6 files changed, 16 insertions(+), 42 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index f203ff8c..c5bb1e56 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -676,36 +676,6 @@ $(document).ready(function(){ }); if($('#logged_in, #bookmarklet-tag-controls-allowed').length > 0){ - $('.add_input_source_control').live({ - click: function(e){ - e.preventDefault(); - var remix_id = $(this).attr('republished_feed_id'); - var dialogNode = $('
'); - var prepend = ''; - var message = "

Please enter the tag you'd like to add

"; - $(dialogNode).append(prepend + '

' + message + '

'); - $(dialogNode).dialog({ - modal: true, - width: 600, - minWidth: 400, - height: 'auto', - title: '', - buttons: { - Cancel: function(){ - $(dialogNode).dialog('close'); - $(dialogNode).remove(); - }, - Submit: function(){ - $('#new_tag_for_filter').parent('form').submit(); - $(dialogNode).dialog('close'); - $(dialogNode).remove(); - } - } - }); - return false; - } - }); - $('.add_filter_control').live({ click: function(e){ e.preventDefault(); @@ -713,6 +683,7 @@ $(document).ready(function(){ var hub_id = $(this).attr('data_hub_id'); var filter_type = $(this).attr('data_type'); var filter_href = $(this).attr('href'); + var forceConfirm = $(this).hasClass('force_confirm'); var tagList = ''; if ($(this).attr('tag_list') != null && $(this).attr('tag_list') != '' ) { tagList = '
Tags applied: ' + $(this).attr('tag_list') + '
'; @@ -774,14 +745,16 @@ $(document).ready(function(){ { text: 'Submit', click: function(){ - var replace_tag = undefined; - if ($(this).find('#modify_tag_for_filter').length > 0){ - replace_tag = $(this).find('#modify_tag_for_filter').val(); - } - if ($(this).find('#supplement_tag_for_filter').length > 0){ - replace_tag = $(this).find('#supplement_tag_for_filter').val(); + if(!forceConfirm || confirm('Are you sure you want to add a filter to all feed items?')) { + var replace_tag = undefined; + if ($(this).find('#modify_tag_for_filter').length > 0){ + replace_tag = $(this).find('#modify_tag_for_filter').val(); + } + if ($(this).find('#supplement_tag_for_filter').length > 0){ + replace_tag = $(this).find('#supplement_tag_for_filter').val(); + } + $.submitTagFilter(filter_href, filter_type, tag_id, $(this).find('#new_tag_for_filter').val(), replace_tag); } - $.submitTagFilter(filter_href, filter_type, tag_id, $(this).find('#new_tag_for_filter').val(), replace_tag); $(dialogNode).dialog('close'); $(dialogNode).remove(); }, diff --git a/app/helpers/tag_filter_helper.rb b/app/helpers/tag_filter_helper.rb index e92a2b84..52bc970c 100644 --- a/app/helpers/tag_filter_helper.rb +++ b/app/helpers/tag_filter_helper.rb @@ -8,7 +8,8 @@ def filter_buttons data_type: 'AddTagFilter', text: 'Add a tag to all items in this hub', icon: 'plus-circle', - button: 'success' + button: 'success', + extra_class: 'force_confirm' }, { role: :hub_tag_deleter, diff --git a/app/jobs/tagging_notifications/send_notification_job.rb b/app/jobs/tagging_notifications/send_notification_job.rb index 4f58443f..5a55afdf 100644 --- a/app/jobs/tagging_notifications/send_notification_job.rb +++ b/app/jobs/tagging_notifications/send_notification_job.rb @@ -7,7 +7,7 @@ class SendNotificationJob < ApplicationJob def perform(hub, feed_items, tag_filters, updated_by_user, changes, recipients = :owners) return unless hub.notify_taggers? - return unless changes.any? + return unless changes.present? && changes.any? notifications = {} # Notifications are either going to the owners of the feed items (skipping the updater) diff --git a/app/views/hub_feed_item_tag_filters/index.html.haml b/app/views/hub_feed_item_tag_filters/index.html.haml index db7467b3..660016fa 100644 --- a/app/views/hub_feed_item_tag_filters/index.html.haml +++ b/app/views/hub_feed_item_tag_filters/index.html.haml @@ -21,7 +21,7 @@ data_hub_id: @hub.id, data_type: 'AddTagFilter', tag_list: tag_list, - class: 'add_filter_control hub_feed_item_tag_filter btn btn-success' do + class: 'add_filter_control force_confirm hub_feed_item_tag_filter btn btn-success' do = fa_icon 'plus-circle', text: 'Add a tag to this item' %li = link_to hub_feed_item_tag_filters_path(@hub, @feed_item), diff --git a/app/views/hub_feed_tag_filters/index.html.haml b/app/views/hub_feed_tag_filters/index.html.haml index 112f3a09..f6675649 100644 --- a/app/views/hub_feed_tag_filters/index.html.haml +++ b/app/views/hub_feed_tag_filters/index.html.haml @@ -16,7 +16,7 @@ data_hub_id: @hub.id, data_type: 'AddTagFilter', tag_list: tag_list, - class: 'add_filter_control hub_feed_tag_filter btn btn-success' do + class: 'add_filter_control force_confirm hub_feed_tag_filter btn btn-success' do = fa_icon 'plus-circle', text: 'Add a tag to all items in this feed' %li = link_to hub_feed_tag_filters_path(@hub_feed), diff --git a/app/views/hub_tag_filters/index.html.haml b/app/views/hub_tag_filters/index.html.haml index 21caf131..211b1e94 100644 --- a/app/views/hub_tag_filters/index.html.haml +++ b/app/views/hub_tag_filters/index.html.haml @@ -14,7 +14,7 @@ data_hub_id: @hub.id, data_type: method[:data_type], tag_list: @tag_filters.map {|f| f.tag.name}.uniq.join(', '), - class: "add_filter_control hub_tag_filter btn btn-#{method[:button]}" do + class: "add_filter_control hub_tag_filter btn btn-#{method[:button]} #{method[:extra_class].to_s}" do = fa_icon method[:icon], text: method[:text] %h1.hub-filters-title Active filters - if @tag_filters.empty? From 33aeda820576bb63fd45221fdf4ad93cb6fe1d6f Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 15:30:41 +0000 Subject: [PATCH 05/70] Merging Gauravs work for deleting a feed item filter. Resolves #14961. --- app/controllers/tag_filters_controller.rb | 15 ++++--- .../_list_item.html.haml | 5 ++- .../hub_feed_item_tag_filters/destroy.js.haml | 2 + app/views/layouts/application.html.haml | 3 +- app/views/shared/_flashes.html.haml | 39 +++++++++-------- .../tag_filters_controller_spec.rb | 42 +++++++++++++++++++ 6 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 app/views/hub_feed_item_tag_filters/destroy.js.haml create mode 100644 spec/controllers/tag_filters_controller_spec.rb diff --git a/app/controllers/tag_filters_controller.rb b/app/controllers/tag_filters_controller.rb index b8016d75..43065eba 100644 --- a/app/controllers/tag_filters_controller.rb +++ b/app/controllers/tag_filters_controller.rb @@ -15,7 +15,7 @@ def index breadcrumbs.add @hub_feed, hub_hub_feed_path(@hub, @hub_feed) if @hub_feed breadcrumbs.add @feed_item, hub_feed_item_path(@hub, @feed_item) if @feed_item respond_to do |format| - format.html { render template, layout: request.xhr? ? false : 'tabs' } + format.html { render controller_response[:template], layout: request.xhr? ? false : 'tabs' } format.json { render_for_api :default, json: @tag_filters } format.xml { render_for_api :default, xml: @tag_filters } end @@ -51,19 +51,22 @@ def destroy flash[:notice] = 'Deleting that tag filter.' - redirect_back fallback_location: hub_tag_filters_path(@hub) + respond_to do |format| + format.html { redirect_back fallback_location: controller_response[:redirection_path] } + format.js { render template: 'hub_feed_item_tag_filters/destroy', layout: false } #only used in the case of the item-lvel filter + end end private - def template + def controller_response case @scope.class.name when 'Hub' - 'hub_tag_filters/index' + { template: 'hub_tag_filters/index', redirection_path: hub_tag_filters_path(@hub) } when 'HubFeed' - 'hub_feed_tag_filters/index' + { template: 'hub_feed_tag_filters/index', redirection_path: hub_feed_tag_filters(@hub, @scope) } when 'FeedItem' - 'hub_feed_item_tag_filters/index' + { template:'hub_feed_item_tag_filters/index', redirection_path: hub_feed_item_tag_filters_path(@hub, @scope) } end end diff --git a/app/views/hub_feed_item_tag_filters/_list_item.html.haml b/app/views/hub_feed_item_tag_filters/_list_item.html.haml index 0ff160e6..9ddb0485 100644 --- a/app/views/hub_feed_item_tag_filters/_list_item.html.haml +++ b/app/views/hub_feed_item_tag_filters/_list_item.html.haml @@ -1,10 +1,11 @@ -%div{ class: "tag_filter hub_feed_item_tag_filter #{filter_css_class(hub_feed_item_tag_filter)}" } +%div{ class: "tag_filter hub_feed_item_tag_filter #{filter_css_class(hub_feed_item_tag_filter)}", id: "tag-filter-#{hub_feed_item_tag_filter.id}" } - if user_signed_in? && (current_user.is?(:owner, @hub) || current_user.is?(:owner, hub_feed_item_tag_filter)) .pull-right = link_to hub_feed_item_tag_filter_path(hub_feed_item_tag_filter.hub, hub_feed_item_tag_filter.scope, hub_feed_item_tag_filter), method: :delete, title: 'Remove this filter', - confirm: 'Are you sure you want to remove this filter?' do + remote: true, + data: { confirm: 'Are you sure you want to remove this filter?' } do = fa_icon 'times' %span.filter-action= filter_description(hub_feed_item_tag_filter) tag diff --git a/app/views/hub_feed_item_tag_filters/destroy.js.haml b/app/views/hub_feed_item_tag_filters/destroy.js.haml new file mode 100644 index 00000000..f3deb636 --- /dev/null +++ b/app/views/hub_feed_item_tag_filters/destroy.js.haml @@ -0,0 +1,2 @@ +$('#notices').html("#{escape_javascript render(partial: 'shared/flashes')}") +$("#tag-filter-#{@tag_filter.id}").remove() diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 83fd7576..3337040c 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -43,7 +43,8 @@ %section.row.breadcrumbs_holder .breadcrumbs.col-md-12 = breadcrumbs.render(format: :inline, separator: fa_icon('chevron-right')) - = render partial: 'shared/flashes' + %section#notices.row + = render partial: 'shared/flashes' %section.row#content .col-md-12 = content_for?(:content) ? yield(:content) : yield diff --git a/app/views/shared/_flashes.html.haml b/app/views/shared/_flashes.html.haml index e3a40568..6a7d17d1 100644 --- a/app/views/shared/_flashes.html.haml +++ b/app/views/shared/_flashes.html.haml @@ -1,20 +1,19 @@ -- unless flash.blank? - %section#notices.row - - unless flash[:notice].blank? - .alert.alert-success.alert-dismissable.col-md-12{:role => "alert"} - %button.close{"data-dismiss" => "alert", :type => "button"} - %span{"aria-hidden" => "true"} × - %span.sr-only Close - %strong= flash[:notice] - - unless flash[:error].blank? - .alert.alert-danger.alert-dismissable.col-md-12{:role => "alert"} - %button.close{"data-dismiss" => "alert", :type => "button"} - %span{"aria-hidden" => "true"} × - %span.sr-only Close - %strong= flash[:error] - - unless flash[:alert].blank? - .alert.alert-warning.alert-dismissable.col-md-12{:role => "alert"} - %button.close{"data-dismiss" => "alert", :type => "button"} - %span{"aria-hidden" => "true"} × - %span.sr-only Close - %strong= flash[:alert] +- if flash.present? + - if flash[:notice].present? + .alert.alert-success.alert-dismissable.col-md-12{ role: 'alert' } + %button.close{ 'data-dismiss': 'alert', type: 'button' } + %span{ 'aria-hidden': 'true' } × + %span.sr-only Close + %strong= flash[:notice] + - if flash[:error].present? + .alert.alert-danger.alert-dismissable.col-md-12{ role: 'alert' } + %button.close{ 'data-dismiss': 'alert', type: 'button' } + %span{ 'aria-hidden': 'true' } × + %span.sr-only Close + %strong= flash[:error] + - if flash[:alert].present? + .alert.alert-warning.alert-dismissable.col-md-12{ role: 'alert' } + %button.close{ 'data-dismiss': 'alert', type: 'button' } + %span{ 'aria-hidden': 'true' } × + %span.sr-only Close + %strong= flash[:alert] diff --git a/spec/controllers/tag_filters_controller_spec.rb b/spec/controllers/tag_filters_controller_spec.rb new file mode 100644 index 00000000..ade7a99e --- /dev/null +++ b/spec/controllers/tag_filters_controller_spec.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe TagFiltersController, type: :controller do + let(:hub) { create(:hub, :owned) } + let(:other_user) { create(:user) } + let(:tag_filter) { create(:tag_filter, scope: hub, hub: hub) } + let(:feed_item) { create(:feed_item_from_feed) } + + it 'does not allow #destroy with out user signed_in' do + delete :destroy, params: { hub_id: hub.id, id: tag_filter.id } + + expect(flash[:alert]).to eq 'You need to sign in or sign up before continuing.' + expect(response).to redirect_to(new_user_session_path) + end + + it 'hub owner should destroy the hub wide filter' do + sign_in(hub.owners.first) + + delete :destroy, params: { hub_id: hub.id, id: tag_filter.id } + expect(flash[:notice]).to eq 'Deleting that tag filter.' + expect(response).to redirect_to(hub_tag_filters_path(hub)) + end + + it 'other user should not destroy the hub wide filter' do + sign_in(other_user) + + delete :destroy, params: { hub_id: hub.id, id: tag_filter.id } + expect(flash[:alert]).to eq 'You can\'t access that - sorry!' + expect(response).to redirect_to(root_path) + end + + it 'user should delete the feed_item level filter' do + sign_in(hub.owners.first) + item_level_tag_filter = create(:tag_filter, scope: feed_item, hub: hub) + + delete :destroy, params: { hub_id: hub.id, feed_item_id: feed_item.id, id: item_level_tag_filter.id, format: :js } + expect(flash[:notice]).to eq 'Deleting that tag filter.' + expect(response).to render_template('hub_feed_item_tag_filters/destroy') + end +end From 1a01823a3e3b836cd1dde8154935f139423ac1f5 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 15:33:29 +0000 Subject: [PATCH 06/70] Removed confirmation on feed item level AddTagFilter. --- app/views/hub_feed_item_tag_filters/index.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/hub_feed_item_tag_filters/index.html.haml b/app/views/hub_feed_item_tag_filters/index.html.haml index 660016fa..db7467b3 100644 --- a/app/views/hub_feed_item_tag_filters/index.html.haml +++ b/app/views/hub_feed_item_tag_filters/index.html.haml @@ -21,7 +21,7 @@ data_hub_id: @hub.id, data_type: 'AddTagFilter', tag_list: tag_list, - class: 'add_filter_control force_confirm hub_feed_item_tag_filter btn btn-success' do + class: 'add_filter_control hub_feed_item_tag_filter btn btn-success' do = fa_icon 'plus-circle', text: 'Add a tag to this item' %li = link_to hub_feed_item_tag_filters_path(@hub, @feed_item), From cb47bacf40830addbc1b7c076465233344e918e0 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 15:35:17 +0000 Subject: [PATCH 07/70] Search on deprecated tags. Resolves #14960. --- app/assets/stylesheets/tagteam.scss | 15 +++++++++++++++ app/controllers/hubs_controller.rb | 5 +++++ app/models/modify_tag_filter.rb | 10 ++++++++++ app/views/hubs/item_search.html.haml | 9 +++++++-- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/tagteam.scss b/app/assets/stylesheets/tagteam.scss index 8ccff631..8bf3629f 100644 --- a/app/assets/stylesheets/tagteam.scss +++ b/app/assets/stylesheets/tagteam.scss @@ -300,6 +300,21 @@ th, dt, b, strong, label { } } +.recommend-tag { + background: $gray-lightish; + color: #222222; + padding: 2px $padding-large-horizontal 4px; + margin-top: 30px; + margin-right: 2px; + margin-left: 2px; + border-radius: 10px; + font-weight: normal; + line-height: 2.5em; + span { + color: #555555; + } +} + .alert { margin-top: $padding-large-vertical; } diff --git a/app/controllers/hubs_controller.rb b/app/controllers/hubs_controller.rb index 6cd6c5e1..f64ac8c1 100644 --- a/app/controllers/hubs_controller.rb +++ b/app/controllers/hubs_controller.rb @@ -704,6 +704,11 @@ def item_search sort = params[:sort] == 'Date published' ? 'date_published' : 'created_at' order = ['desc', 'asc'].include?(params[:order]) ? params[:order] : 'desc' + @modify_tag_filters = params[:q].gsub(/^#/, '').split(/\s/).delete_if { |b| !b.present? }.map do |tag_name| + ModifyTagFilter.find_recursive(@hub.id, tag_name) + end.compact + @filtered_params = params[:q].dup + @modify_tag_filters.each { |mf| @filtered_params.gsub!(/#{mf.tag.name}/, mf.new_tag.name) } @search = FeedItem.search do with :hub_ids, hub_id diff --git a/app/models/modify_tag_filter.rb b/app/models/modify_tag_filter.rb index 4fa1fe45..eb8b0cea 100644 --- a/app/models/modify_tag_filter.rb +++ b/app/models/modify_tag_filter.rb @@ -93,4 +93,14 @@ def filters_after def tag_changes { tags_modified: [tag, new_tag] } end + + def self.find_recursive(hub_id, tag_name, filter = nil) + tag = ActsAsTaggableOn::Tag.find_by_name_normalized(tag_name) + return filter if tag.nil? + + new_filter = self.where(scope_type: 'Hub', scope_id: hub_id, tag_id: tag.id) + return filter if new_filter.empty? + + find_recursive(hub_id, new_filter.first.new_tag.name, new_filter.first) + end end diff --git a/app/views/hubs/item_search.html.haml b/app/views/hubs/item_search.html.haml index cb39e957..d2ad5a21 100644 --- a/app/views/hubs/item_search.html.haml +++ b/app/views/hubs/item_search.html.haml @@ -16,7 +16,7 @@ = text_field_tag :q, params[:q], size: 50, placeholder: 'Search this hub', class: 'form-control' %span.input-group-btn = submit_tag('Search', class: 'btn btn-success') - - unless @search.blank? || @search.results.length == 0 + - unless @modify_tag_filters.any? || @search.results.empty? = link_to('Permalink', item_search_hub_path(@hub, q: params[:q])) - if current_user.present? @@ -25,7 +25,12 @@ data_item_type: 'SearchRemix', title: "Choose the remix feed to add this search to", class: "dialog-show add_item_source_to_custom_republished_feed") - - if @search.blank? || @search.results.length == 0 + - if @modify_tag_filters.any? + - if @modify_tag_filters.size > 1 + %p.recommend-tag Tags #{@modify_tag_filters.collect { |mf| mf.tag.name }.join(', ')} have been deprecated. Search for these tags instead: #{link_to(@filtered_params, item_search_hub_path(@hub, q: @filtered_params)) } + - else + %p.recommend-tag #{@modify_tag_filters.first.tag.name} has been deprecated. Search for this instead: #{link_to(@filtered_params, item_search_hub_path(@hub, q: @filtered_params)) } + - elsif @search.results.empty? %h3 Nothing. Please try a different term. - else .row From ba89dc6a8ac13e50900d8c3fed2b482fdea571da Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 15:37:49 +0000 Subject: [PATCH 08/70] Added return path on emails via config. Resolves #14548. --- app/mailers/application_mailer.rb | 3 ++- app/mailers/contact.rb | 3 ++- app/mailers/feed_items/notifications_mailer.rb | 2 -- app/mailers/messages_mailer.rb | 3 ++- app/mailers/notifications_mailer.rb | 3 ++- app/mailers/tagging_notifications/notifications_mailer.rb | 3 ++- config/tagteam.yml.example | 1 + 7 files changed, 11 insertions(+), 7 deletions(-) diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 4b4ea117..61dae161 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class ApplicationMailer < ActionMailer::Base - default from: Tagteam::Application.config.default_sender + default from: Tagteam::Application.config.default_sender, + return_path: Tagteam::Application.config.return_path layout 'mailer' end diff --git a/app/mailers/contact.rb b/app/mailers/contact.rb index 84b450d6..79605877 100644 --- a/app/mailers/contact.rb +++ b/app/mailers/contact.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Contact < ActionMailer::Base - default from: Tagteam::Application.config.default_sender + default from: Tagteam::Application.config.default_sender, + return_path: Tagteam::Application.config.return_path def request_rights(params, hub) @hub = hub diff --git a/app/mailers/feed_items/notifications_mailer.rb b/app/mailers/feed_items/notifications_mailer.rb index 25a3218a..e31d6cba 100644 --- a/app/mailers/feed_items/notifications_mailer.rb +++ b/app/mailers/feed_items/notifications_mailer.rb @@ -3,8 +3,6 @@ module FeedItems # Send email to users when changes are made to a feed item class NotificationsMailer < ApplicationMailer - default from: Tagteam::Application.config.default_sender - def feed_item_change_notification(hub:, modified_item:, users_to_notify:, current_user:, changes:) @hub = hub @hub_url = hub_url(@hub) diff --git a/app/mailers/messages_mailer.rb b/app/mailers/messages_mailer.rb index a0996197..280085e9 100644 --- a/app/mailers/messages_mailer.rb +++ b/app/mailers/messages_mailer.rb @@ -2,7 +2,8 @@ # mailer to send messages to hub members class MessagesMailer < ActionMailer::Base - default from: Tagteam::Application.config.default_sender + default from: Tagteam::Application.config.default_sender, + return_path: Tagteam::Application.config.return_path def send_message(recipient, subject, body) mail(to: recipient, subject: subject, body: body) diff --git a/app/mailers/notifications_mailer.rb b/app/mailers/notifications_mailer.rb index c931651c..d2de837e 100644 --- a/app/mailers/notifications_mailer.rb +++ b/app/mailers/notifications_mailer.rb @@ -1,5 +1,6 @@ class NotificationsMailer < ActionMailer::Base - default from: Tagteam::Application.config.default_sender + default from: Tagteam::Application.config.default_sender, + return_path: Tagteam::Application.config.return_path def user_data_import_completion_notification(email, status) @status = status diff --git a/app/mailers/tagging_notifications/notifications_mailer.rb b/app/mailers/tagging_notifications/notifications_mailer.rb index ac5b247a..67e9208c 100644 --- a/app/mailers/tagging_notifications/notifications_mailer.rb +++ b/app/mailers/tagging_notifications/notifications_mailer.rb @@ -3,7 +3,8 @@ module TaggingNotifications # Send email to user with tagging changes made to their item class NotificationsMailer < ActionMailer::Base - default from: Tagteam::Application.config.default_sender + default from: Tagteam::Application.config.default_sender, + return_path: Tagteam::Application.config.return_path def tagging_change_notification(hub, modified_items, user_to_notify, updated_by_user, changes) @hub = hub diff --git a/config/tagteam.yml.example b/config/tagteam.yml.example index 38562bd0..cd9e6e2e 100644 --- a/config/tagteam.yml.example +++ b/config/tagteam.yml.example @@ -6,6 +6,7 @@ hostport: 80 default_sender: no_reply@example.com exceptions_mailed_to: exceptions@example.com ssl_for_user_accounts: true +return_path: user@example.com ############################### # Spidering and schedule options From 4d865e0e87ab7c8ccf7e438e2450a6e14ce08329 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 15:44:44 +0000 Subject: [PATCH 09/70] Change order of approval required and email confirmation. Hides users from hub team autocomplete. Resolves #15391. --- app/controllers/users/search_controller.rb | 6 +++++- app/models/user.rb | 11 +++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/controllers/users/search_controller.rb b/app/controllers/users/search_controller.rb index 7ec90d14..75317442 100644 --- a/app/controllers/users/search_controller.rb +++ b/app/controllers/users/search_controller.rb @@ -10,7 +10,11 @@ class SearchController < ApplicationController def autocomplete authorize User - search = Sunspot.search(User) { fulltext params[:term], fields: [:email, :username] } + search = Sunspot.search(User) do + fulltext params[:term], fields: [:email, :username] + with :approved, true + with(:confirmed_at).less_than(Time.now) + end results = search.results.map do |user| { diff --git a/app/models/user.rb b/app/models/user.rb index 52d4ae8b..30904841 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -5,8 +5,8 @@ class User < ApplicationRecord has_many :hub_user_notifications # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable - devise :database_authenticatable, :registerable, - :recoverable, :rememberable, :trackable, :validatable, :confirmable, :lockable + devise :database_authenticatable, :registerable, :confirmable, + :recoverable, :rememberable, :trackable, :validatable, :lockable # Virtual attribute for authenticating by either username or email attr_accessor :login @@ -21,8 +21,14 @@ class User < ApplicationRecord validates :terms_of_service, acceptance: true validates :signup_reason, presence: true, unless: :auto_approved?, on: :create before_create do + self.skip_confirmation_notification! self.approved = auto_approved? end + after_update do + if self.approved_changed? && self.approved + self.send_confirmation_instructions + end + end scope :unapproved, -> { where(approved: false) } scope :superadmin, -> { joins(:roles).where('roles.name = ?', :superadmin).distinct } @@ -34,6 +40,7 @@ class User < ApplicationRecord string :first_name string :last_name string :url + boolean :approved time :confirmed_at end From b809157de50206613364222eaf72646e25933dd2 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 15:47:17 +0000 Subject: [PATCH 10/70] Modify language on Request to Collaborate checkboxes. Resolves #13248. --- app/views/hubs/contact.html.haml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/app/views/hubs/contact.html.haml b/app/views/hubs/contact.html.haml index 1644cb99..7b27aa6a 100644 --- a/app/views/hubs/contact.html.haml +++ b/app/views/hubs/contact.html.haml @@ -22,11 +22,17 @@ label: "Ways you'd like to collaborate", required: false, hint: 'Please describe why you\'d like to collaborate in this hub in the field below.', - collection: ['Full rights to manage this hub', - 'Add bookmarked items', - 'Add new input RSS / Atom feeds', - 'Create new feed remixes', - 'Manage tag filters'] + collection: ['Owner', + 'Tagger', + 'Feed remixer', + 'Hub-wide tag filter adder', + 'Hub-wide tag filter deleter', + 'Hub-wide tag filter modifier', + 'Hub-wide tag filter supplementer', + 'Feed-wide tag filter manager', + 'Item-level tag filter manager', + 'Input feed manager', + 'Statistics viewer'] = f.input :message, as: :text, input_html: { rows: 8 } = f.actions do = f.action :submit, as: :button, label: 'Submit', button_html: { class: 'btn btn-primary' } From cd707f083880d7d9bd332588e36c4f010c6a8e6f Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 16:10:10 +0000 Subject: [PATCH 11/70] Added filter counts. Resolves #7824. --- app/views/hub_feed_item_tag_filters/index.html.haml | 2 +- app/views/hub_feed_tag_filters/index.html.haml | 2 +- app/views/hub_tag_filters/index.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/hub_feed_item_tag_filters/index.html.haml b/app/views/hub_feed_item_tag_filters/index.html.haml index db7467b3..11bc9b3f 100644 --- a/app/views/hub_feed_item_tag_filters/index.html.haml +++ b/app/views/hub_feed_item_tag_filters/index.html.haml @@ -49,7 +49,7 @@ = render partial: 'shared/line_items/tag', collection: (@feed_item.all_tags_on(@hub.tagging_key) - @hub.deprecated_tags), locals: { feed_item: @feed_item } - %h1 Active filters + %h1 Active filters (#{@tag_filters.size}) - if @tag_filters.empty? %h2 No active filters - else diff --git a/app/views/hub_feed_tag_filters/index.html.haml b/app/views/hub_feed_tag_filters/index.html.haml index f6675649..fa061e1f 100644 --- a/app/views/hub_feed_tag_filters/index.html.haml +++ b/app/views/hub_feed_tag_filters/index.html.haml @@ -39,7 +39,7 @@ tag_list: tag_list, class: 'add_filter_control hub_feed_tag_filter btn btn-primary' do = fa_icon 'plus-circle', text: 'Supplement a tag with a second tag on all items in this feed' - %h1 Active filters + %h1 Active filters (#{@tag_filters.size}) - if @tag_filters.empty? %h2 No active filters - else diff --git a/app/views/hub_tag_filters/index.html.haml b/app/views/hub_tag_filters/index.html.haml index 211b1e94..24a50935 100644 --- a/app/views/hub_tag_filters/index.html.haml +++ b/app/views/hub_tag_filters/index.html.haml @@ -16,7 +16,7 @@ tag_list: @tag_filters.map {|f| f.tag.name}.uniq.join(', '), class: "add_filter_control hub_tag_filter btn btn-#{method[:button]} #{method[:extra_class].to_s}" do = fa_icon method[:icon], text: method[:text] - %h1.hub-filters-title Active filters + %h1.hub-filters-title Active filters (#{@tag_filters.size}) - if @tag_filters.empty? %p No active filters - else From a7a0c7b34860b5fa5d9f8964ab2e42e080f3109d Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 16:13:53 +0000 Subject: [PATCH 12/70] Support for enter key. Resolves #6475. --- app/assets/javascripts/application.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index c5bb1e56..4a2845ac 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -744,6 +744,7 @@ $(document).ready(function(){ }, { text: 'Submit', + type: 'submit', click: function(){ if(!forceConfirm || confirm('Are you sure you want to add a filter to all feed items?')) { var replace_tag = undefined; @@ -762,6 +763,14 @@ $(document).ready(function(){ } ] }); + + $('#new_tag_for_filter').on('keypress', function (event) { + // event 13 is the Enter/Return key + if (event.which === 13) { + dialogNode.parent().find('button:submit').click(); + } + }); + return false; } $.submitTagFilter($(this).attr('href'), filter_type, tag_id,'',''); From 57e8b6997886f338c4b9a2c6fe25ddf5e4179a4a Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 17:08:02 +0000 Subject: [PATCH 13/70] Updating language regarding all items / every item. Resolves #15627. --- app/controllers/tag_filters_controller.rb | 2 +- app/helpers/tag_filter_helper.rb | 2 +- app/views/hub_feed_tag_filters/index.html.haml | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/controllers/tag_filters_controller.rb b/app/controllers/tag_filters_controller.rb index 43065eba..2d9f771c 100644 --- a/app/controllers/tag_filters_controller.rb +++ b/app/controllers/tag_filters_controller.rb @@ -64,7 +64,7 @@ def controller_response when 'Hub' { template: 'hub_tag_filters/index', redirection_path: hub_tag_filters_path(@hub) } when 'HubFeed' - { template: 'hub_feed_tag_filters/index', redirection_path: hub_feed_tag_filters(@hub, @scope) } + { template: 'hub_feed_tag_filters/index', redirection_path: hub_feed_tag_filters_path(@hub, @scope) } when 'FeedItem' { template:'hub_feed_item_tag_filters/index', redirection_path: hub_feed_item_tag_filters_path(@hub, @scope) } end diff --git a/app/helpers/tag_filter_helper.rb b/app/helpers/tag_filter_helper.rb index 52bc970c..7f674518 100644 --- a/app/helpers/tag_filter_helper.rb +++ b/app/helpers/tag_filter_helper.rb @@ -6,7 +6,7 @@ def filter_buttons { role: :hub_tag_adder, data_type: 'AddTagFilter', - text: 'Add a tag to all items in this hub', + text: 'Add a tag to every item in this hub', icon: 'plus-circle', button: 'success', extra_class: 'force_confirm' diff --git a/app/views/hub_feed_tag_filters/index.html.haml b/app/views/hub_feed_tag_filters/index.html.haml index fa061e1f..e5671612 100644 --- a/app/views/hub_feed_tag_filters/index.html.haml +++ b/app/views/hub_feed_tag_filters/index.html.haml @@ -17,28 +17,28 @@ data_type: 'AddTagFilter', tag_list: tag_list, class: 'add_filter_control force_confirm hub_feed_tag_filter btn btn-success' do - = fa_icon 'plus-circle', text: 'Add a tag to all items in this feed' + = fa_icon 'plus-circle', text: 'Add a tag to every item in this feed' %li = link_to hub_feed_tag_filters_path(@hub_feed), data_hub_id: @hub.id, data_type: 'DeleteTagFilter', tag_list: tag_list, class: 'add_filter_control hub_feed_tag_filter btn btn-danger' do - = fa_icon 'minus-circle', text: 'Remove a tag from all items in this feed' + = fa_icon 'minus-circle', text: 'Remove a tag from every item in this feed' %li = link_to hub_feed_tag_filters_path(@hub_feed), data_hub_id: @hub.id, data_type: 'ModifyTagFilter', tag_list: tag_list, class: 'add_filter_control hub_feed_tag_filter btn btn-default' do - = fa_icon 'pencil', text: 'Modify a tag on all items in this feed' + = fa_icon 'pencil', text: 'Modify a tag on every item in this feed' %li = link_to hub_feed_tag_filters_path(@hub_feed), data_hub_id: @hub.id, data_type: 'SupplementTagFilter', tag_list: tag_list, class: 'add_filter_control hub_feed_tag_filter btn btn-primary' do - = fa_icon 'plus-circle', text: 'Supplement a tag with a second tag on all items in this feed' + = fa_icon 'plus-circle', text: 'Supplement a tag with a second tag on every item in this feed' %h1 Active filters (#{@tag_filters.size}) - if @tag_filters.empty? %h2 No active filters From 926445022d6bb4a1f2f2061de78807dddf61089d Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 17:09:54 +0000 Subject: [PATCH 14/70] Toggling on bookmarklet form. Resolves #13746. --- app/assets/stylesheets/bookmarklet.scss | 12 ++++++++++++ app/views/bookmarklets/add.html.haml | 15 ++++++++------- .../shared/forms/_feed_item_bookmarklet.html.haml | 10 +++++----- 3 files changed, 25 insertions(+), 12 deletions(-) diff --git a/app/assets/stylesheets/bookmarklet.scss b/app/assets/stylesheets/bookmarklet.scss index 5ae7c052..e6fcefa0 100644 --- a/app/assets/stylesheets/bookmarklet.scss +++ b/app/assets/stylesheets/bookmarklet.scss @@ -15,3 +15,15 @@ margin-left: 5px; } } +.basic-advance-option { + display: inline-block; + width: 100%; + text-align: center; + ul { + display: inline-block; + } +} +.add-item-submit { + position: relative; + bottom: 70px; +} \ No newline at end of file diff --git a/app/views/bookmarklets/add.html.haml b/app/views/bookmarklets/add.html.haml index 9023e230..6a96221f 100644 --- a/app/views/bookmarklets/add.html.haml +++ b/app/views/bookmarklets/add.html.haml @@ -1,10 +1,11 @@ #bookmarklet-tag-controls-allowed .page-header - %ul.nav.nav-pills.pull-right{:role => "tablist"} - %li.active{:role => "presentation"} - %a{"data-toggle" => "tab", :href => "#basics"} Basics - %li{:role => "presentation"} - %a{"data-toggle" => "tab", :href => "#advanced"} Advanced - - unless @mini_title.blank? + - if @mini_title.present? %h1= fa_icon 'bolt', text: @mini_title -= render :partial => 'shared/forms/feed_item_bookmarklet' += render partial: 'shared/forms/feed_item_bookmarklet' +.basic-advance-option + %ul.nav.nav-pills.text-center{ role: 'tablist' } + %li.active{ role: 'presentation' } + %a{ 'data-toggle': 'tab', href: '#basics' } Basics + %li{ role: 'presentation' } + %a{ 'data-toggle': 'tab', href: '#advanced' } Advanced diff --git a/app/views/shared/forms/_feed_item_bookmarklet.html.haml b/app/views/shared/forms/_feed_item_bookmarklet.html.haml index fdc838c8..221ce9b9 100644 --- a/app/views/shared/forms/_feed_item_bookmarklet.html.haml +++ b/app/views/shared/forms/_feed_item_bookmarklet.html.haml @@ -2,6 +2,9 @@ - submit_label = @feed_item.new_record? ? 'Add to TagTeam' : 'Update TagTeam' .tab-content .tab-pane.active#basics + = f.actions do + .text-center.add-item-submit.pull-right + = f.action :submit, label: submit_label, as: :button, button_html: { class: 'btn btn-primary' } = f.inputs do .row .col-xs-6 @@ -21,10 +24,10 @@ .row .col-xs-3 = f.input :date_published, as: :string, label: 'Date published', hint: 'yyyy-mm-dd format', input_html: { class: :datepicker } + .tab-pane#advanced = f.actions do - .text-center + .text-center.add-item-submit.pull-right = f.action :submit, label: submit_label, as: :button, button_html: { class: 'btn btn-primary' } - .tab-pane#advanced = f.inputs do = f.input :authors = f.input :contributors @@ -32,9 +35,6 @@ .row .col-xs-3 = f.input :last_updated, as: :string, hint: 'yyyy-mm-dd format', input_html: { class: :datepicker } - = f.actions do - .text-center - = f.action :submit, label: submit_label, as: :button, button_html: { class: 'btn btn-primary' } :javascript $(document).ready(function() { From 416cfac47ba72fc6281518044bb3934e874faeaf Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 17:11:11 +0000 Subject: [PATCH 15/70] Redirect on 404 page. Resolves #12379. --- app/controllers/hubs_controller.rb | 2 +- app/controllers/tags_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/hubs_controller.rb b/app/controllers/hubs_controller.rb index f64ac8c1..4fe8cf29 100644 --- a/app/controllers/hubs_controller.rb +++ b/app/controllers/hubs_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # A Hub is the base unit of organization for TagTeam. Please see README_FOR_APP for more details on how everything fits together. class HubsController < ApplicationController - caches_action :index, :items, :show, :search, :by_date, :retrievals, :taggers, :meta, unless: proc { |_c| current_user }, expires_in: Tagteam::Application.config.default_action_cache_time, cache_path: proc { + caches_action :index, :items, :show, :search, :by_date, :retrievals, :taggers, :meta, unless: proc { |_c| current_user || params[:no_cache] == 'true' }, expires_in: Tagteam::Application.config.default_action_cache_time, cache_path: proc { Digest::MD5.hexdigest(request.fullpath + '&per_page=' + get_per_page) } caches_action :statistics, expires_in: 6.hours diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 82568061..f566f299 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -239,7 +239,7 @@ def load_tag_from_name unless @tag flash[:error] = "We're sorry, but '#{params[:name]}' is not a tag for '#{@hub.title}'" - redirect_to hub_path(@hub) + '?no_cache=true' + redirect_to items_hub_path(@hub) + '?no_cache=true' end end From 5e303b06104ec5c3f82457a8ddd586416f111f11 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 17:55:34 +0000 Subject: [PATCH 16/70] Display option to hide tags from autocomplete. Resolves #12817. --- app/assets/javascripts/application.js | 21 ++++++++++++++++++- app/controllers/hubs_controller.rb | 15 +++++++++++++ app/controllers/tags_controller.rb | 6 +++++- app/models/removed_tag_suggestion.rb | 8 +++++++ app/models/user.rb | 5 +++++ app/policies/hub_policy.rb | 8 +++++++ app/views/tags/_graph_item.html.haml | 3 +++ app/views/tags/deprecated_tags.html.haml | 2 +- app/views/tags/index.html.haml | 3 ++- app/views/tags/tags_approved.html.haml | 2 +- .../tags/tags_used_not_approved.html.haml | 2 +- config/routes.rb | 1 + ...18110458_create_removed_tag_suggestions.rb | 14 +++++++++++++ db/schema.rb | 7 +++++++ 14 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 app/models/removed_tag_suggestion.rb create mode 100644 db/migrate/20180418110458_create_removed_tag_suggestions.rb diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 4a2845ac..0e75aa7f 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -877,5 +877,24 @@ $(document).ready(function(){ } } - unescapeUrl(); + unescapeUrl(); + + $('.remove-suggestion-toggle').live('click', function(e) { + e.preventDefault(); + var $link = $(this); + var hubId = $link.attr('data-hub-id'); + $.ajax({ + url: $.rootPath() + 'hubs/' + hubId + '/removed_tag_suggestion', + type: 'post', + data: { tag_id: $link.attr('data-tag-id'), remove: $link.find('span').hasClass('fa-eye-slash') }, + success: (data) => { + $link.find('span').toggleClass('fa-eye fa-eye-slash'); + if($link.find('span').hasClass('fa-eye')) { + $link.attr('title', 'Show on autocomplete'); + } else { + $link.attr('title', 'Hide from autocomplete'); + } + } + }); + }); }); diff --git a/app/controllers/hubs_controller.rb b/app/controllers/hubs_controller.rb index 4fe8cf29..8b0b5e05 100644 --- a/app/controllers/hubs_controller.rb +++ b/app/controllers/hubs_controller.rb @@ -49,6 +49,7 @@ class HubsController < ApplicationController :request_rights, :remove_delimiter, :remove_roles, + :removed_tag_suggestion, :retrievals, :show, :tag_controls, @@ -506,6 +507,20 @@ def tag_controls end end + def removed_tag_suggestion + authorize @hub, :toggle_tag_display? + + if params[:remove] == 'true' + removed_tag_suggestions = ActsAsTaggableOn::Tag.where(id: params[:tag_id]).map do |tag| + RemovedTagSuggestion.create(tag: tag, hub_id: @hub.id, user_id: current_user.id) + end + else + RemovedTagSuggestion.where(tag_id: params[:tag_id], hub_id: @hub.id).destroy_all + end + + render json: {} + end + def add_feed @feed = Feed.find_or_initialize_by(feed_url: params[:feed_url]) diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index f566f299..9ac59bc6 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -7,7 +7,7 @@ class TagsController < ApplicationController before_action :add_breadcrumbs before_action :set_prefixed_tags, only: [:index] - caches_action :rss, :atom, :json, :xml, :autocomplete, :index, :show, :statistics, unless: proc { |_c| (current_user && current_user.is?(:owner, @hub)) || params.has_key?(:username) || params[:no_cache] == 'true' }, expires_in: Tagteam::Application.config.default_action_cache_time, cache_path: proc { + caches_action :rss, :atom, :json, :xml, :autocomplete, :show, :statistics, unless: proc { |_c| (current_user && current_user.is?(:owner, @hub)) || params.has_key?(:username) || params[:no_cache] == 'true' }, expires_in: Tagteam::Application.config.default_action_cache_time, cache_path: proc { if request.fullpath =~ /tag\/rss/ params[:format] = :rss elsif request.fullpath =~ /tag\/atom/ @@ -59,10 +59,12 @@ def autocomplete tag_ids = Rails.cache.fetch("all-tag-ids-#{@hub.id}", expires_in: 1.hour) do FeedItem.joins(:hubs, :taggings).where(hubs: { id: @hub.id }).pluck('taggings.tag_id').uniq end + remove_suggested = RemovedTagSuggestion.where(hub_id: @hub.id).map { |rts| rts.tag.name } result = ActsAsTaggableOn::Tag .left_joins(:taggings) .where.not(name: deprecated_tags_names) + .where.not(name: remove_suggested) .where('name LIKE \'' + params[:term] + '%\'') .where(id: tag_ids) .group(:id) @@ -89,6 +91,8 @@ def index @tags = @hub_feed.blank? ? @hub.tag_counts : FeedItem.tag_counts_on_items(@hub_feed.feed_items.pluck(:id), @hub.tagging_key).all + @removed_tag_suggestions = RemovedTagSuggestion.where(hub_id: @hub.id).map(&:tag_id) + if @tags.any? # tag_sorter = TagSorter.new(:tags => @tags, :sort_by => :created_at, :context => @hub.tagging_key, :class => FeedItem) tag_sorter = TagSorter.new(tags: @tags, sort_by: :frequency) diff --git a/app/models/removed_tag_suggestion.rb b/app/models/removed_tag_suggestion.rb new file mode 100644 index 00000000..7b78600e --- /dev/null +++ b/app/models/removed_tag_suggestion.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# It relates user and tag, which are not allow to display in the suggestion list of tags. +class RemovedTagSuggestion < ApplicationRecord + belongs_to :tag, class_name: 'ActsAsTaggableOn::Tag' + belongs_to :user + belongs_to :hub +end diff --git a/app/models/user.rb b/app/models/user.rb index 30904841..3142c5d2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,6 +3,7 @@ class User < ApplicationRecord acts_as_tagger has_many :hub_user_notifications + has_many :removed_tag_suggestions # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :confirmable, @@ -159,6 +160,10 @@ def superadmin? has_role?(:superadmin) end + def removed_tag_suggestions_name + removed_tag_suggestions.joins(:tag).map{|removed_suggestion| removed_suggestion.tag.name }.uniq + end + protected def gen_role_cache diff --git a/app/policies/hub_policy.rb b/app/policies/hub_policy.rb index d6ee0442..dcea1ea0 100644 --- a/app/policies/hub_policy.rb +++ b/app/policies/hub_policy.rb @@ -12,6 +12,10 @@ def add_feed? user.has_role?(:inputter, record) || user.has_role?(:owner, record) end + def removed_tag_suggestion? + owner_or_admin? + end + def add_roles? owner_or_admin? end @@ -145,6 +149,10 @@ def update? owner_or_admin? end + def toggle_tag_display? + owner_or_admin? + end + def recalc_all_tags? return false if user.blank? return true if user.has_role?(:superadmin) diff --git a/app/views/tags/_graph_item.html.haml b/app/views/tags/_graph_item.html.haml index 1f14c1f4..b228b8ae 100644 --- a/app/views/tags/_graph_item.html.haml +++ b/app/views/tags/_graph_item.html.haml @@ -2,4 +2,7 @@ = tag_display(tag, show_count: true, use_count: true, hub: hub, hub_feed: hub_feed) %span.tag-count = tag.count + - if show_removed_tags + = link_to '#', title: "#{@removed_tag_suggestions.include?(tag.id) ? 'Show on' : 'Hide from'} autocomplete", class: 'remove-suggestion-toggle', "data-hub-id": hub.id, "data-tag-id": tag.id do + %span{ class: "fa #{@removed_tag_suggestions.include?(tag.id) ? 'fa-eye' : 'fa-eye-slash'}" } .tag-graph-bar{:style => "width: #{tag.count.to_f / max_count * 100}%"} diff --git a/app/views/tags/deprecated_tags.html.haml b/app/views/tags/deprecated_tags.html.haml index 0179bdc2..d80fd8d7 100644 --- a/app/views/tags/deprecated_tags.html.haml +++ b/app/views/tags/deprecated_tags.html.haml @@ -22,4 +22,4 @@ - if @deprecated_hub_tags.any? %ul.list-unstyled#tag-cloud{ 'data-hub-id' => @hub.id } = render partial: 'tags/graph_item', as: :tag, collection: @deprecated_hub_tags, - locals: { hub: @hub, hub_feed: nil, max_count: @deprecated_hub_tags.first.count } + locals: { show_removed_tags: false, hub: @hub, hub_feed: nil, max_count: @deprecated_hub_tags.first.count } diff --git a/app/views/tags/index.html.haml b/app/views/tags/index.html.haml index 5a0cdf30..adc8909a 100644 --- a/app/views/tags/index.html.haml +++ b/app/views/tags/index.html.haml @@ -28,9 +28,10 @@ %select.form-control#sort-tags-direction %option{ value: 'asc' } Ascending %option{ selected: 'selected', value: 'desc' } Descending + %ul.list-unstyled#tag-cloud{ 'data-hub-id' => @hub.id } = render partial: 'tags/graph_item', as: :tag, collection: @tags, - locals: { hub: @hub, hub_feed: @hub_feed, max_count: @tags.empty? ? 0 : @tags.first.count } + locals: { show_removed_tags: policy(@hub).toggle_tag_display?, hub: @hub, hub_feed: @hub_feed, max_count: @tags.empty? ? 0 : @tags.first.count } :javascript $(document).ready( function () { diff --git a/app/views/tags/tags_approved.html.haml b/app/views/tags/tags_approved.html.haml index 1f2e7a31..e24ea26b 100644 --- a/app/views/tags/tags_approved.html.haml +++ b/app/views/tags/tags_approved.html.haml @@ -9,4 +9,4 @@ - if @tags_approved.any? %ul.list-unstyled#tag-cloud{ 'data-hub-id' => @hub.id } = render partial: 'tags/graph_item', as: :tag, collection: @tags_approved, - locals: { hub: @hub, hub_feed: nil, max_count: @tags_approved.first.count } + locals: { show_removed_tags: false, hub: @hub, hub_feed: nil, max_count: @tags_approved.first.count } diff --git a/app/views/tags/tags_used_not_approved.html.haml b/app/views/tags/tags_used_not_approved.html.haml index e8d5b839..e17d6541 100644 --- a/app/views/tags/tags_used_not_approved.html.haml +++ b/app/views/tags/tags_used_not_approved.html.haml @@ -9,4 +9,4 @@ - if @hub.hub_approved_tags.any? && @tags_used_not_approved.any? %ul.list-unstyled#tag-cloud{ 'data-hub-id' => @hub.id } = render partial: 'tags/graph_item', as: :tag, collection: @tags_used_not_approved, - locals: { hub: @hub, hub_feed: nil, max_count: @tags_used_not_approved.first.count } + locals: { show_removed_tags: false, hub: @hub, hub_feed: nil, max_count: @tags_used_not_approved.first.count } diff --git a/config/routes.rb b/config/routes.rb index 97b122f6..4311914a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -105,6 +105,7 @@ get 'about' post 'recalc_all_tags' get 'item_search' + post 'removed_tag_suggestion' post 'add_feed' put 'unsubscribe_feed/:feed_id' => 'hubs#unsubscribe_feed', as: 'unsubscribe_feed' get 'custom_republished_feeds' diff --git a/db/migrate/20180418110458_create_removed_tag_suggestions.rb b/db/migrate/20180418110458_create_removed_tag_suggestions.rb new file mode 100644 index 00000000..b39abc64 --- /dev/null +++ b/db/migrate/20180418110458_create_removed_tag_suggestions.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +# migration to create removed_tag_suggestions table +class CreateRemovedTagSuggestions < ActiveRecord::Migration[5.0] + def change + create_table :removed_tag_suggestions do |t| + t.integer :tag_id + t.integer :hub_id + t.integer :user_id + end + + add_index :removed_tag_suggestions, :hub_id + end +end diff --git a/db/schema.rb b/db/schema.rb index f728d6bd..52e75a0e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -216,6 +216,13 @@ t.index ["republished_feed_id"], name: "index_input_sources_on_republished_feed_id" end + create_table "removed_tag_suggestions", id: :serial, force: :cascade do |t| + t.integer "tag_id" + t.integer "hub_id" + t.integer "user_id" + t.index ["hub_id"], name: "index_removed_tag_suggestions_on_hub_id" + end + create_table "republished_feeds", id: :serial, force: :cascade do |t| t.integer "hub_id" t.string "title", limit: 500, null: false From 33d94a0212b2f8ae4d252532c9ad0dbf1bdb08af Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 17 Aug 2018 18:12:01 +0000 Subject: [PATCH 17/70] Tag descriptions. Resolves #15025. Also some fixes on redundant tagging policy logic. --- app/assets/javascripts/application.js | 36 +++++++++++++++++++ app/assets/stylesheets/tagteam.scss | 17 +++++++++ app/controllers/hubs_controller.rb | 2 +- app/controllers/tags_controller.rb | 16 ++++++++- app/models/hub_tag_description.rb | 3 ++ app/policies/hub_policy.rb | 2 +- app/views/tags/_top_panel.html.haml | 18 ++++++++++ app/views/tags/index.html.haml | 2 +- app/views/tags/show.html.haml | 2 +- config/routes.rb | 1 + ...80816161305_create_hub_tag_descriptions.rb | 12 +++++++ db/schema.rb | 13 ++++++- 12 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 app/models/hub_tag_description.rb create mode 100644 db/migrate/20180816161305_create_hub_tag_descriptions.rb diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 0e75aa7f..24da1c28 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -675,6 +675,42 @@ $(document).ready(function(){ $.initEditor(this); }); + $('#update-tag-description').live({ + click: function(e) { + e.preventDefault(); + $('textarea#tag-description,a#submit-tag-description,a#cancel-tag-description').removeClass('hidden'); + $('span#readonly-tag-description,a#update-tag-description').hide(); + } + }); + $('#submit-tag-description').live({ + click: function(e) { + e.preventDefault(); + $.ajax({ + cache: false, + dataType: 'json', + url: $(this).attr('href'), + type: 'post', + data: { description: $('textarea#tag-description').val() }, + success: function(data){ + if($('textarea#tag-description').val() != '') { + $('span#readonly-tag-description').html($('textarea#tag-description').val()).removeClass('empty'); + } + $('textarea#tag-description,a#submit-tag-description,a#cancel-tag-description').addClass('hidden'); + $('span#readonly-tag-description,a#update-tag-description').show(); + }, + error: function(data) { + } + }); + } + }); + $('#cancel-tag-description').live({ + click: function(e) { + e.preventDefault(); + $('textarea#tag-description,a#submit-tag-description,a#cancel-tag-description').addClass('hidden'); + $('span#readonly-tag-description,a#update-tag-description').show(); + } + }); + if($('#logged_in, #bookmarklet-tag-controls-allowed').length > 0){ $('.add_filter_control').live({ click: function(e){ diff --git a/app/assets/stylesheets/tagteam.scss b/app/assets/stylesheets/tagteam.scss index 8bf3629f..6048179f 100644 --- a/app/assets/stylesheets/tagteam.scss +++ b/app/assets/stylesheets/tagteam.scss @@ -536,3 +536,20 @@ abbr[title="required"] { .tab-layout .tab-content ul.nicely-padded.delimiter-list { margin-top: 5px; } + +span#readonly-tag-description { + margin-right: 5px; + &.empty { + opacity: 0.5; + } +} +textarea#tag-description { + width: 100%; +} +#update-tag-description, +#cancel-tag-description, +#submit-tag-dewscription { + .fa { + font-size: 14px; + } +} diff --git a/app/controllers/hubs_controller.rb b/app/controllers/hubs_controller.rb index 8b0b5e05..63f6824f 100644 --- a/app/controllers/hubs_controller.rb +++ b/app/controllers/hubs_controller.rb @@ -508,7 +508,7 @@ def tag_controls end def removed_tag_suggestion - authorize @hub, :toggle_tag_display? + authorize @hub if params[:remove] == 'true' removed_tag_suggestions = ActsAsTaggableOn::Tag.where(id: params[:tag_id]).map do |tag| diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 9ac59bc6..e186408c 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class TagsController < ApplicationController before_action :load_hub - before_action :load_tag_from_name, only: [:rss, :atom, :show, :json, :xml, :statistics] + before_action :load_tag_from_name, only: [:rss, :atom, :show, :json, :xml, :statistics, :description] before_action :load_feed_items_for_rss, only: [:rss, :atom, :json, :xml] before_action :load_feed_items, only: :statistics before_action :add_breadcrumbs @@ -149,11 +149,25 @@ def show end @show_auto_discovery_params = hub_tag_rss_url(@hub, @tag.name) + @tag_description = HubTagDescription.where(hub_id: @hub.id, tag_id: @tag.id).first template = params[:view] == 'grid' ? 'show_grid' : 'show' render template, layout: request.xhr? ? false : 'tabs' end + def description + # TODO: Authorize + + hub_tag_desc = HubTagDescription.find_or_initialize_by(tag_id: @tag.id, hub_id: @hub.id) + hub_tag_desc.update_attributes!(description: params[:description]) + + # TODO: Eventually track role here? + + render json: {} + rescue Exception => e + render json: {} + end + def statistics authorize @hub diff --git a/app/models/hub_tag_description.rb b/app/models/hub_tag_description.rb new file mode 100644 index 00000000..1c5ae435 --- /dev/null +++ b/app/models/hub_tag_description.rb @@ -0,0 +1,3 @@ +class HubTagDescription < ApplicationRecord + validates_presence_of :hub_id, :tag_id +end diff --git a/app/policies/hub_policy.rb b/app/policies/hub_policy.rb index dcea1ea0..d6838d71 100644 --- a/app/policies/hub_policy.rb +++ b/app/policies/hub_policy.rb @@ -149,7 +149,7 @@ def update? owner_or_admin? end - def toggle_tag_display? + def tag_descriptions? owner_or_admin? end diff --git a/app/views/tags/_top_panel.html.haml b/app/views/tags/_top_panel.html.haml index 9b425ca2..f7fe5ed3 100644 --- a/app/views/tags/_top_panel.html.haml +++ b/app/views/tags/_top_panel.html.haml @@ -5,6 +5,24 @@ " – \#{feed_items.count} #{'item'.pluralize(feed_items.count)} + - if policy(hub).tag_descriptions? + %p + %span#readonly-tag-description{class: tag_description.nil? ? 'empty' : '' } + #{tag_description.present? ? tag_description : 'Add a description'} + = link_to '#', title: 'Edit this hub tag description', id: 'update-tag-description' do + = fa_icon 'pencil' + %textarea#tag-description.hidden.noEditor{placeholder: 'Add a description'} + #{tag_description} + = link_to hub_tag_description_url(hub, tag.name), title: 'Update this hub tag description', id: 'submit-tag-description', class: 'hidden' do + = fa_icon 'check' + = link_to '#', title: 'Cancel editing this hub tag description', id: 'cancel-tag-description', class: 'hidden' do + = fa_icon 'close' + - elsif tag_description.present? + %p + %span#readonly-tag-description + #{tag_description} + + .col-sm-4 .export-menu.text-right = link_to hub_tag_rss_path(hub, tag.name), title: 'An RSS feed of all items in this hub, with tag filters applied.', class: 'btn btn-default' do diff --git a/app/views/tags/index.html.haml b/app/views/tags/index.html.haml index adc8909a..00aa6101 100644 --- a/app/views/tags/index.html.haml +++ b/app/views/tags/index.html.haml @@ -31,7 +31,7 @@ %ul.list-unstyled#tag-cloud{ 'data-hub-id' => @hub.id } = render partial: 'tags/graph_item', as: :tag, collection: @tags, - locals: { show_removed_tags: policy(@hub).toggle_tag_display?, hub: @hub, hub_feed: @hub_feed, max_count: @tags.empty? ? 0 : @tags.first.count } + locals: { show_removed_tags: policy(@hub).removed_tag_suggestion?, hub: @hub, hub_feed: @hub_feed, max_count: @tags.empty? ? 0 : @tags.first.count } :javascript $(document).ready( function () { diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml index 77264d50..43215b62 100644 --- a/app/views/tags/show.html.haml +++ b/app/views/tags/show.html.haml @@ -1,7 +1,7 @@ - content_for :title do = page_title(@tag.name) - content_for :top_panel do - = render partial: 'top_panel', locals: { feed_items: @feed_items, hub: @hub, tag: @tag } + = render partial: 'top_panel', locals: { feed_items: @feed_items, hub: @hub, tag: @tag, tag_description: @tag_description&.description } - content_for :tabs do = render partial: 'tabs', locals: { active: 'show' } - content_for :tab_content do diff --git a/config/routes.rb b/config/routes.rb index 4311914a..2db84b03 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -80,6 +80,7 @@ get 'tag/xml/:name' => 'tags#xml', :as => 'tag_xml', :constraints => { name: /.+/ } get 'tag/:name/statistics' => 'tags#statistics', :as => 'tag_statistics', :constraints => { name: /.+/ } get 'tag/:name' => 'tags#show', :as => 'tag_show', :constraints => { name: /.+/ }, :defaults => { :format => 'html' } + post 'tag/:name/description' => 'tags#description', :as => 'tag_description', :constraints => { name: /.+/ } get 'user/:username' => 'users#hub_items', :as => 'user_hub_items', :constraints => { username: /[^\/]+/ } get 'user/:username/tags' => 'tags#index', :as => 'user_hub_items_tags', :constraints => { username: /[^\/]+/ } diff --git a/db/migrate/20180816161305_create_hub_tag_descriptions.rb b/db/migrate/20180816161305_create_hub_tag_descriptions.rb new file mode 100644 index 00000000..e4685cb8 --- /dev/null +++ b/db/migrate/20180816161305_create_hub_tag_descriptions.rb @@ -0,0 +1,12 @@ +class CreateHubTagDescriptions < ActiveRecord::Migration[5.1] + def change + create_table :hub_tag_descriptions do |t| + t.references :hub, nil: false + t.references :tag, nil: false + t.string :description, limit: 400 + t.timestamps + end + + add_index :hub_tag_descriptions, [:hub_id, :tag_id], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 52e75a0e..9427a84f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180710170657) do +ActiveRecord::Schema.define(version: 20180816161305) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -167,6 +167,17 @@ t.index ["hub_id"], name: "index_hub_feeds_on_hub_id" end + create_table "hub_tag_descriptions", force: :cascade do |t| + t.bigint "hub_id" + t.bigint "tag_id" + t.string "description", limit: 400 + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["hub_id", "tag_id"], name: "index_hub_tag_descriptions_on_hub_id_and_tag_id", unique: true + t.index ["hub_id"], name: "index_hub_tag_descriptions_on_hub_id" + t.index ["tag_id"], name: "index_hub_tag_descriptions_on_tag_id" + end + create_table "hub_user_notifications", id: :serial, force: :cascade do |t| t.integer "user_id", null: false t.integer "hub_id", null: false From 0a713d99b96fd451940137760423b1198b872239 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Mon, 20 Aug 2018 16:21:11 +0000 Subject: [PATCH 18/70] Destroy tag filter application immediately when scope is FeedItem. Resolves #14961. --- app/assets/images/ui-icons_8ec63f_256x240.png | Bin 0 -> 68 bytes app/controllers/hubs_controller.rb | 6 ++-- app/controllers/tag_filters_controller.rb | 11 +++--- app/jobs/tag_filters/destroy_job.rb | 31 ++-------------- app/models/tag_filter.rb | 33 ++++++++++++++++-- .../_list_item.html.haml | 1 - 6 files changed, 41 insertions(+), 41 deletions(-) create mode 100644 app/assets/images/ui-icons_8ec63f_256x240.png diff --git a/app/assets/images/ui-icons_8ec63f_256x240.png b/app/assets/images/ui-icons_8ec63f_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..cb148d07129b97a0da4957b69bffdffbf3e5ec6f GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzv|f-m@YG(D@p3x Q4HRbZboFyt=akR{0Ej0JqW}N^ literal 0 HcmV?d00001 diff --git a/app/controllers/hubs_controller.rb b/app/controllers/hubs_controller.rb index 63f6824f..579a4ed0 100644 --- a/app/controllers/hubs_controller.rb +++ b/app/controllers/hubs_controller.rb @@ -834,7 +834,7 @@ def deprecate_tag user: current_user ) - if (add_filter.nil? || add_filter.rollback_and_destroy_async(current_user)) && deprecation_filter.valid? + if (add_filter.nil? || add_filter.rollback_and_destroy(current_user)) && deprecation_filter.valid? if request.xhr? render(html: 'Successfully deprecated.', layout: false) else @@ -871,8 +871,8 @@ def undeprecate_tag removed_filters = TagFilter.where(removed_params) replaced_filters = TagFilter.where(replaced_params) - removed_filters.map { |filter| filter.rollback_and_destroy_async(current_user) } - replaced_filters.map { |filter| filter.rollback_and_destroy_async(current_user) } + removed_filters.map { |filter| filter.rollback_and_destroy(current_user) } + replaced_filters.map { |filter| filter.rollback_and_destroy(current_user) } if request.xhr? render(html: 'Successfully undeprecated.', layout: false) diff --git a/app/controllers/tag_filters_controller.rb b/app/controllers/tag_filters_controller.rb index 2d9f771c..65012833 100644 --- a/app/controllers/tag_filters_controller.rb +++ b/app/controllers/tag_filters_controller.rb @@ -47,14 +47,13 @@ def create end def destroy - @tag_filter.rollback_and_destroy_async(current_user) + # If scope is a single feed item, rollback and destroy immediately, + # else destroy asynchronously (default is asynchronous) + @tag_filter.rollback_and_destroy(current_user, !@tag_filter.scope_type == 'FeedItem') - flash[:notice] = 'Deleting that tag filter.' + flash[:notice] = @tag_filter.scope_type == 'FeedItem' ? 'Deleted that tag filter.' : 'Deleting that tag filter.' - respond_to do |format| - format.html { redirect_back fallback_location: controller_response[:redirection_path] } - format.js { render template: 'hub_feed_item_tag_filters/destroy', layout: false } #only used in the case of the item-lvel filter - end + redirect_back fallback_location: controller_response[:redirection_path] end private diff --git a/app/jobs/tag_filters/destroy_job.rb b/app/jobs/tag_filters/destroy_job.rb index 13929b9d..2aac11ec 100644 --- a/app/jobs/tag_filters/destroy_job.rb +++ b/app/jobs/tag_filters/destroy_job.rb @@ -1,40 +1,13 @@ # frozen_string_literal: true module TagFilters - # Revert (rollback) and destroy a TagFilter class DestroyJob < ApplicationJob queue_as :default - def perform(tag_filter, current_user) - hub = tag_filter.hub - - TaggingNotifications::SendNotificationJob.perform_later( - hub, - tag_filter.filtered_feed_items, - [tag_filter], - current_user, - [determine_changes(tag_filter)] - ) - + def perform(tag_filter, updater) + tag_filter.queue_destroy_notification(updater) tag_filter.rollback tag_filter.destroy end - - private - - def determine_changes(tag_filter) - case tag_filter.class.to_s - when 'AddTagFilter' - { type: 'tags_deleted', values: [tag_filter.tag_name] } - when 'DeleteTagFilter' - { type: 'tags_added', values: [tag_filter.tag_name] } - when 'ModifyTagFilter' - { type: 'tags_modified', values: [[tag_filter.new_tag_name], [tag_filter.tag_name]] } - when 'SupplementTagFilter' - { type: 'tags_supplemented_deletion', values: [[tag_filter.tag_name, tag_filter.new_tag_name]] } - else - raise 'Unknown tag filter type' - end - end end end diff --git a/app/models/tag_filter.rb b/app/models/tag_filter.rb index 3048c81c..55546957 100644 --- a/app/models/tag_filter.rb +++ b/app/models/tag_filter.rb @@ -62,8 +62,37 @@ def rollback self.class.base_class.notify_observers :after_rollback, self end - def rollback_and_destroy_async(current_user) - TagFilters::DestroyJob.perform_later(self, current_user) + def rollback_and_destroy(current_user, async = true) + if async + TagFilters::DestroyJob.perform_later(self, current_user) + else + # TagFilter::DestroyJob.perform_now still puts this into a queue and the page loads + # before it's done, so we are pulling it out of queue + self.queue_destroy_notification(current_user) + self.rollback + self.destroy + end + end + + def queue_destroy_notification(updater) + changes = case self.class.to_s + when 'AddTagFilter' + { type: 'tags_deleted', values: [self.tag_name] } + when 'DeleteTagFilter' + { type: 'tags_added', values: [self.tag_name] } + when 'ModifyTagFilter' + { type: 'tags_modified', values: [[self.new_tag_name], [self.tag_name]] } + when 'SupplementTagFilter' + { type: 'tags_supplemented_deletion', values: [[self.tag_name, self.new_tag_name]] } + end + + TaggingNotifications::SendNotificationJob.perform_later( + self.hub, + self.filtered_feed_items, + [self], + updater, + [changes] + ) end # End API for filters, diff --git a/app/views/hub_feed_item_tag_filters/_list_item.html.haml b/app/views/hub_feed_item_tag_filters/_list_item.html.haml index 9ddb0485..5901a417 100644 --- a/app/views/hub_feed_item_tag_filters/_list_item.html.haml +++ b/app/views/hub_feed_item_tag_filters/_list_item.html.haml @@ -4,7 +4,6 @@ = link_to hub_feed_item_tag_filter_path(hub_feed_item_tag_filter.hub, hub_feed_item_tag_filter.scope, hub_feed_item_tag_filter), method: :delete, title: 'Remove this filter', - remote: true, data: { confirm: 'Are you sure you want to remove this filter?' } do = fa_icon 'times' %span.filter-action= filter_description(hub_feed_item_tag_filter) From afebacb9608bbb0b2322a941c041b41ee67350fa Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Mon, 20 Aug 2018 16:39:39 +0000 Subject: [PATCH 19/70] Fixed policy on hubs so taggers can not see settings. Resolves #15648. --- app/policies/hub_policy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/policies/hub_policy.rb b/app/policies/hub_policy.rb index d6838d71..c9752e51 100644 --- a/app/policies/hub_policy.rb +++ b/app/policies/hub_policy.rb @@ -106,7 +106,7 @@ def scoreboard? def settings? return false if user.blank? - user.has_role?(:superadmin) || user.has_role?(:owner, record) || user.has_role?(:bookmarker, record) + user.has_role?(:superadmin) || user.has_role?(:owner, record) end def remove_roles? From 62ae46b5faa722d16379ba4be2b62ca1ace1fdd3 Mon Sep 17 00:00:00 2001 From: J S Diaz Date: Mon, 20 Aug 2018 16:16:36 -0400 Subject: [PATCH 20/70] decrease logging in prod to warnings --- config/environments/production.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index 6d652e10..cbfff417 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -51,7 +51,7 @@ # Use the lowest log level to ensure availability of diagnostic information # when problems arise. - config.log_level = :debug + config.log_level = :warn # Prepend all log lines with the following tags. config.log_tags = [:subdomain, :uuid] From 504c56cbf99b935422515bc663accd6a2f4b9a27 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Wed, 22 Aug 2018 17:46:34 +0000 Subject: [PATCH 21/70] Added in migration from ActsAsTaggable update. --- ...171418_delayed_acts_as_taggable_upgrade.rb | 24 +++++++++++++++++++ db/schema.rb | 3 ++- 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20180822171418_delayed_acts_as_taggable_upgrade.rb diff --git a/db/migrate/20180822171418_delayed_acts_as_taggable_upgrade.rb b/db/migrate/20180822171418_delayed_acts_as_taggable_upgrade.rb new file mode 100644 index 00000000..be3ecfed --- /dev/null +++ b/db/migrate/20180822171418_delayed_acts_as_taggable_upgrade.rb @@ -0,0 +1,24 @@ +class DelayedActsAsTaggableUpgrade < ActiveRecord::Migration[5.1] + def change + add_column :tags, :taggings_count, :integer, default: 0 + + ActsAsTaggableOn::Tag.reset_column_information + ActsAsTaggableOn::Tag.find_each do |tag| + ActsAsTaggableOn::Tag.reset_counters(tag.id, :taggings) rescue nil + end + + add_index :taggings, :tag_id unless index_exists? :taggings, :tag_id + add_index :taggings, :taggable_id unless index_exists? :taggings, :taggable_id + add_index :taggings, :taggable_type unless index_exists? :taggings, :taggable_type + add_index :taggings, :tagger_id unless index_exists? :taggings, :tagger_id + add_index :taggings, :context unless index_exists? :taggings, :context + + unless index_exists? :taggings, [:tagger_id, :tagger_type] + add_index :taggings, [:tagger_id, :tagger_type] + end + + unless index_exists? :taggings, [:taggable_id, :taggable_type, :tagger_id, :context], name: 'taggings_idy' + add_index :taggings, [:taggable_id, :taggable_type, :tagger_id, :context], name: 'taggings_idy' + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 9427a84f..0d401aaa 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180816161305) do +ActiveRecord::Schema.define(version: 20180822171418) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -311,6 +311,7 @@ create_table "tags", id: :serial, force: :cascade do |t| t.string "name", limit: 255 + t.integer "taggings_count", default: 0 t.index ["name"], name: "index_tags_on_name", unique: true end From b297d0b0a8c4b273d25f808099459b4c1bf6d627 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Thu, 23 Aug 2018 16:45:11 +0000 Subject: [PATCH 22/70] Remaining work on 15025: Copying descriptions from deprecated tags to current tags. --- app/assets/javascripts/application.js | 20 ++++++++++++++++++++ app/controllers/tags_controller.rb | 8 +++++++- app/views/tags/_top_panel.html.haml | 3 +++ app/views/tags/show.html.haml | 2 +- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 24da1c28..2ef1ba6f 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -682,6 +682,26 @@ $(document).ready(function(){ $('span#readonly-tag-description,a#update-tag-description').hide(); } }); + $('#apply-old-description').live({ + click: function(e) { + var $link = $(this); + e.preventDefault(); + $.ajax({ + cache: false, + dataType: 'json', + url: $link.attr('href'), + type: 'post', + data: { description: $link.attr('data-old-value') }, + success: function(data){ + $('span#readonly-tag-description').html($link.attr('data-old-value')).removeClass('empty'); + $('p.old-description,textarea#tag-description,a#submit-tag-description,a#cancel-tag-description').addClass('hidden'); + $('span#readonly-tag-description,a#update-tag-description').show(); + }, + error: function(data) { + } + }); + } + }); $('#submit-tag-description').live({ click: function(e) { e.preventDefault(); diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index e186408c..cac1d80d 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -144,12 +144,18 @@ def show if @hub.deprecated_tags.include?(@tag) @feed_items = [].paginate else - feed_item_ids = ActsAsTaggableOn::Tagging.where(tag_id: @tag.id, context: @hub.tagging_key, taggable_type: 'FeedItem').map(&:taggable_id).compact + feed_item_ids = ActsAsTaggableOn::Tagging.where(tag_id: @tag.id, context: @hub.tagging_key, taggable_type: 'FeedItem').map(&:taggable_id).compact @feed_items = FeedItem.where(id: feed_item_ids).order("feed_items.#{sort} #{order}").paginate(page: params[:page], per_page: get_per_page) end @show_auto_discovery_params = hub_tag_rss_url(@hub, @tag.name) @tag_description = HubTagDescription.where(hub_id: @hub.id, tag_id: @tag.id).first + if @tag_description.nil? + @tag_filter = TagFilter.where(type: 'ModifyTagFilter', hub_id: @hub.id, new_tag_id: @tag.id, scope_type: 'Hub').first + if @tag_filter.present? + @old_tag_description = HubTagDescription.where(hub_id: @hub.id, tag_id: @tag_filter.tag_id).first + end + end template = params[:view] == 'grid' ? 'show_grid' : 'show' render template, layout: request.xhr? ? false : 'tabs' diff --git a/app/views/tags/_top_panel.html.haml b/app/views/tags/_top_panel.html.haml index f7fe5ed3..a18c13cb 100644 --- a/app/views/tags/_top_panel.html.haml +++ b/app/views/tags/_top_panel.html.haml @@ -17,6 +17,9 @@ = fa_icon 'check' = link_to '#', title: 'Cancel editing this hub tag description', id: 'cancel-tag-description', class: 'hidden' do = fa_icon 'close' + - if @old_tag_description.present? + %p.old-description + An active hub-wide filter is changing tag #{@tag_filter.tag.name} to #{@tag_filter.new_tag.name}. The old tag, #{@tag_filter.tag.name}, had an accompanying definition. Would you like to move that definition to this page? #{link_to 'Yes', hub_tag_description_url(hub, tag.name), title: 'Apply this description.', id: 'apply-old-description', 'data-old-value': @old_tag_description.description} - elsif tag_description.present? %p %span#readonly-tag-description diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml index 43215b62..0780093d 100644 --- a/app/views/tags/show.html.haml +++ b/app/views/tags/show.html.haml @@ -1,7 +1,7 @@ - content_for :title do = page_title(@tag.name) - content_for :top_panel do - = render partial: 'top_panel', locals: { feed_items: @feed_items, hub: @hub, tag: @tag, tag_description: @tag_description&.description } + = render partial: 'top_panel', locals: { feed_items: @feed_items, hub: @hub, tag: @tag, tag_description: @tag_description&.description, old_tag_description: @old_tag_description } - content_for :tabs do = render partial: 'tabs', locals: { active: 'show' } - content_for :tab_content do From 4e4e0113a57ead11e1668264b265a09622a0e77e Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Thu, 23 Aug 2018 19:11:31 +0000 Subject: [PATCH 23/70] Show tagging history in bookmarklet form --- app/assets/javascripts/application.js | 17 +++ app/assets/stylesheets/tagteam.scss | 12 ++- app/controllers/feed_items_controller.rb | 16 ++- .../feed_items/item_tag_actions_by_user.rb | 102 ++++++++++++++++++ app/views/feed_items/tags_actions.html.haml | 16 +++ .../forms/_feed_item_bookmarklet.html.haml | 1 + config/routes.rb | 1 + 7 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 app/interactions/feed_items/item_tag_actions_by_user.rb create mode 100644 app/views/feed_items/tags_actions.html.haml diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 2ef1ba6f..9cf81169 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -314,6 +314,21 @@ }); } }, + initHubFeedItemTagActionsList: function(hubId, feedItemId) { + $('.feed-item-tags-actions').empty(); + if (feedItemId >= 0) { + $.ajax({ + type: 'GET', + cache: false, + url: $.rootPath() + 'hubs/' + hubId + '/feed_items/' + + feedItemId + '/tags_actions', + success: function(tagActionsList){ + $('.feed-item-tags-actions').append( + '

Actions

' + tagActionsList); + } + }); + } + }, observeHubSelector: function(feedItemId){ if($.cookie('bookmarklet_hub_choice') != undefined){ // A selection! Set the defaults. @@ -321,9 +336,11 @@ } $.initBookmarkCollectionChoices($('#feed_item_hub_id').val()); $.initHubFeedItemTagList($('#feed_item_hub_id').val(), feedItemId); + $.initHubFeedItemTagActionsList($('#feed_item_hub_id').val(), feedItemId); $('#feed_item_hub_id').change(function() { $.initBookmarkCollectionChoices($(this).val()); $.initHubFeedItemTagList($(this).val(), feedItemId); + $.initHubFeedItemTagActionsList($('#feed_item_hub_id').val(), feedItemId); $.setEmptyDescriptionNotification(); }); $.setEmptyDescriptionNotification(); diff --git a/app/assets/stylesheets/tagteam.scss b/app/assets/stylesheets/tagteam.scss index 6048179f..6ccd0d0e 100644 --- a/app/assets/stylesheets/tagteam.scss +++ b/app/assets/stylesheets/tagteam.scss @@ -282,7 +282,8 @@ th, dt, b, strong, label { } } -.tag { +.tag, +.feed-item-tags-actions-action-user { background: $gray-light; color: white; padding: 2px $padding-large-horizontal 4px; @@ -553,3 +554,12 @@ textarea#tag-description { font-size: 14px; } } + +.feed-item-tags-actions-action { + background-color: #EDEDED; + padding: 0px 5px; + margin-right: 10px; + display: inline-block; + margin-bottom: 7px; + border-radius: 6px; +} diff --git a/app/controllers/feed_items_controller.rb b/app/controllers/feed_items_controller.rb index 87551107..e1377d5b 100644 --- a/app/controllers/feed_items_controller.rb +++ b/app/controllers/feed_items_controller.rb @@ -6,7 +6,7 @@ class FeedItemsController < ApplicationController protect_from_forgery except: :index - before_action :set_hub_feed, except: [:tag_list] + before_action :set_hub_feed, except: [:tag_list, :tags_actions] before_action :set_feed_item, except: [:index] SORT_OPTIONS = { @@ -132,6 +132,18 @@ def copy_move_to_hub end end + def tags_actions + @hub = Hub.find(params[:hub_id]) + + @actions = FeedItems::ItemTagActionsByUser.run!(hub: @hub, feed_item: @feed_item) + + respond_to do |format| + format.html do + render layout: request.xhr? ? false : 'tabs' + end + end + end + def edit authorize @feed_item @@ -246,7 +258,7 @@ def add_breadcrumbs end end end - + def feed_item_params params.require(:feed_item).permit(:description, :title, :url, :date_published) end diff --git a/app/interactions/feed_items/item_tag_actions_by_user.rb b/app/interactions/feed_items/item_tag_actions_by_user.rb new file mode 100644 index 00000000..b8995c91 --- /dev/null +++ b/app/interactions/feed_items/item_tag_actions_by_user.rb @@ -0,0 +1,102 @@ +# frozen_string_literal: true + +module FeedItems + class ItemTagActionsByUser < ActiveInteraction::Base + object :feed_item + object :hub + + def execute + actions = {} + + @feed_item.taggings.where(context: hub.tagging_key).each do |tagging| + begin + if tagging[:tagger_type] == 'User' + (actions[tagging.tagger.username] ||= []) << { + tagger: tagging.tagger, + tag: tagging.tag, + type: 'added', + date: tagging.created_at + } + end + + if tagging[:tagger_type] == 'TagFilter' + filter_type = tagging.tagger.type + tagger = tagging.tagger.users.first + + if filter_type == 'AddTagFilter' + (actions[tagger.username] ||= []) << { + tagger: tagger, + tag: tagging.tag, + type: 'added', + date: tagging.created_at + } + end + + if filter_type == 'ModifyTagFilter' + (actions[tagger.username] ||= []) << { + tagger: tagger, + tag: tagging.tagger.tag, + new_tag: tagging.tagger.new_tag, + type: 'modified', + date: tagging.created_at + } + end + end + rescue + # Removed user, tag filter + end + end + + deactivated_taggings = DeactivatedTagging.where( + taggable_id: @feed_item.id, + deactivator_type: 'TagFilter', + context: hub.tagging_key + ) + deactivated_taggings.each do |tagging| + begin + deactivator = TagFilter.find(tagging.deactivator_id) + if tagging.tagger_type == 'TagFilter' + activator = TagFilter.find(tagging.tagger_id).users.first + elsif tagging.tagger_type == 'User' + activator = User.find(tagging.tagger_id) + end + tag = ActsAsTaggableOn::Tag.find(tagging.tag_id) + filter_type = deactivator.type + + if filter_type == 'DeleteTagFilter' + (actions[deactivator.users.first.username] ||= []) << { + tagger: deactivator.users.first, + tag: tag, + type: 'deleted', + date: deactivator.created_at + } + + (actions[activator.username] ||= []) << { + tagger: activator, + tag: tag, + type: 'added', + date: tagging.created_at + } + end + + if filter_type == 'ModifyTagFilter' + (actions[activator.username] ||= []) << { + tagger: activator, + tag: tag, + type: 'added', + date: tagging.created_at + } + end + rescue + # Removed user, tag filter + end + end + + actions.each do |username, user_actions| + user_actions.sort_by! { |k| k[:date] } + end + + actions + end + end +end diff --git a/app/views/feed_items/tags_actions.html.haml b/app/views/feed_items/tags_actions.html.haml new file mode 100644 index 00000000..a46aa29b --- /dev/null +++ b/app/views/feed_items/tags_actions.html.haml @@ -0,0 +1,16 @@ +- @actions.each do |username, user_actions| + .feed-item-tags-actions-user-actions + = link_to(username, hub_user_hub_items_path(@hub, username), class: 'feed-item-tags-actions-action-user') + - user_actions.each do |action| + %span.feed-item-tags-actions-action + - if action[:type] == 'added' + added + = tag_display(action[:tag], hub: @hub, hub_feed_item: @feed_item) + - elsif action[:type] == 'modified' + changed + = tag_display(action[:tag], hub: @hub, hub_feed_item: @feed_item) + to + = tag_display(action[:new_tag], hub: @hub, hub_feed_item: @feed_item) + - elsif action[:type] == 'deleted' + deleted + = tag_display(action[:tag], hub: @hub, hub_feed_item: @feed_item) diff --git a/app/views/shared/forms/_feed_item_bookmarklet.html.haml b/app/views/shared/forms/_feed_item_bookmarklet.html.haml index 221ce9b9..05c1885a 100644 --- a/app/views/shared/forms/_feed_item_bookmarklet.html.haml +++ b/app/views/shared/forms/_feed_item_bookmarklet.html.haml @@ -15,6 +15,7 @@ = f.input :bookmark_collection_id, as: :select, required: true, label: 'To collection', class: 'form-control' = f.input :tag_list, label: 'Tags', hint: 'A comma separated list of tags. Type at least 3 characters to load tag suggestions.' .feed-item-existing-tags + .feed-item-tags-actions.form-group = f.input :title, required: true = f.input :url, label: 'URL', required: true = f.input :description, as: :text, hint: "Select text on the page you're bookmarking to auto-populate the description field" diff --git a/config/routes.rb b/config/routes.rb index 2db84b03..c7331d6d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -182,6 +182,7 @@ get 'tag_list' get 'content' get 'related' + get 'tags_actions' end # collection do # get 'by_date/:year/:month/:day' => 'feed_items#by_date', :as => 'by_date' From 77aacbb79594eb3cad5b2e4d7d55ac6a712ab585 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 24 Aug 2018 15:26:14 +0000 Subject: [PATCH 24/70] Sending email confirmation to users upon auto approval. --- app/models/user.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 3142c5d2..36af426b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -22,8 +22,14 @@ class User < ApplicationRecord validates :terms_of_service, acceptance: true validates :signup_reason, presence: true, unless: :auto_approved?, on: :create before_create do - self.skip_confirmation_notification! - self.approved = auto_approved? + # If a user is auto approved, send them the confirmation right away + # else wait to send them a confirmation until approved + if auto_approved? + self.approved = true + else + self.skip_confirmation_notification! + self.approved = false + end end after_update do if self.approved_changed? && self.approved From 4dad401d51de907fa8db3c4aa79ea05e8088205f Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 24 Aug 2018 16:40:57 +0000 Subject: [PATCH 25/70] Fixed resend of confirmation. --- app/controllers/users_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 36b40739..b942a539 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -103,11 +103,11 @@ def resend_unlock_token def resend_confirmation_token authorize User - @user.resend_confirmation_token + @user.resend_confirmation_instructions flash[:notice] = 'We resent the account confirmation email to that user.' redirect_to request.referer rescue Exception => e - flash[:notice] = 'Woops. We could not send that right now. Please try again later.' + flash[:notice] = 'Whoops. We could not send that right now. Please try again later.' redirect_to request.referer end From 0d341b0791b09752ae557c76e4b6216d8e109722 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Tue, 28 Aug 2018 14:53:23 +0000 Subject: [PATCH 26/70] Deal with recursive loop issues if supplement tags point to each other. Resolves #15682. --- app/models/modify_tag_filter.rb | 2 +- app/models/tag_filter.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/models/modify_tag_filter.rb b/app/models/modify_tag_filter.rb index eb8b0cea..417bde34 100644 --- a/app/models/modify_tag_filter.rb +++ b/app/models/modify_tag_filter.rb @@ -98,7 +98,7 @@ def self.find_recursive(hub_id, tag_name, filter = nil) tag = ActsAsTaggableOn::Tag.find_by_name_normalized(tag_name) return filter if tag.nil? - new_filter = self.where(scope_type: 'Hub', scope_id: hub_id, tag_id: tag.id) + new_filter = self.where(scope_type: 'Hub', scope_id: hub_id, tag_id: tag.id).where.not(type: 'SupplementTagFilter') return filter if new_filter.empty? find_recursive(hub_id, new_filter.first.new_tag.name, new_filter.first) diff --git a/app/models/tag_filter.rb b/app/models/tag_filter.rb index 55546957..68164cee 100644 --- a/app/models/tag_filter.rb +++ b/app/models/tag_filter.rb @@ -186,7 +186,7 @@ def self.apply_hub_filters(hub, feed_item) filter.apply([feed_item.id]) applied_tag_ids.reject! { |tag_id| tag_id == filter.tag_id } applied_tag_ids << filter.new_tag_id - elsif filter.type == 'SupplementTagFilter' && applied_tag_ids.include?(filter.tag_id) + elsif filter.type == 'SupplementTagFilter' && applied_tag_ids.include?(filter.tag_id) && !applied_tag_ids.include?(filter.new_tag_id) filter.apply([feed_item.id]) applied_tag_ids << filter.new_tag_id elsif filter.type == 'AddTagFilter' @@ -200,7 +200,7 @@ def self.find_recursive(hub_id, tag_name, filter = nil) tag = ActsAsTaggableOn::Tag.find_by_name_normalized(tag_name) return filter if tag.nil? - new_filter = self.where(scope_type: 'Hub', scope_id: hub_id, tag_id: tag.id) + new_filter = self.where(scope_type: 'Hub', scope_id: hub_id, tag_id: tag.id).where.not(type: 'SupplementTagFilter') return filter if new_filter.empty? return new_filter.first if new_filter.first.type == 'DeleteTagFilter' From b09350f65b98bdfa39dbd59e6a026dba97e295fd Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Tue, 28 Aug 2018 15:07:11 +0000 Subject: [PATCH 27/70] Added gray styling to tags on old description message. --- app/views/tags/_top_panel.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/tags/_top_panel.html.haml b/app/views/tags/_top_panel.html.haml index a18c13cb..319aadac 100644 --- a/app/views/tags/_top_panel.html.haml +++ b/app/views/tags/_top_panel.html.haml @@ -19,7 +19,7 @@ = fa_icon 'close' - if @old_tag_description.present? %p.old-description - An active hub-wide filter is changing tag #{@tag_filter.tag.name} to #{@tag_filter.new_tag.name}. The old tag, #{@tag_filter.tag.name}, had an accompanying definition. Would you like to move that definition to this page? #{link_to 'Yes', hub_tag_description_url(hub, tag.name), title: 'Apply this description.', id: 'apply-old-description', 'data-old-value': @old_tag_description.description} + An active hub-wide filter is changing tag #{@tag_filter.tag.name} to #{@tag_filter.new_tag.name}. The old tag, #{@tag_filter.tag.name}, had an accompanying definition. Would you like to move that definition to this page? #{link_to 'Yes', hub_tag_description_url(hub, tag.name), title: 'Apply this description.', id: 'apply-old-description', 'data-old-value': @old_tag_description.description} - elsif tag_description.present? %p %span#readonly-tag-description From 8163a4b0fbf95ba0d9607413c39f00bddf0aa4fe Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Tue, 28 Aug 2018 15:12:45 +0000 Subject: [PATCH 28/70] Sprockets update to 3.7.2. Resovles #15713. --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 19376c8e..eef767ae 100644 --- a/Gemfile +++ b/Gemfile @@ -36,7 +36,7 @@ gem 'sanitize', '~> 4.6' gem 'sass-rails', '~> 5.0' gem 'sidekiq', '~> 5.1' gem 'sinatra', '~> 2.0', require: false -gem 'sprockets', '~> 3.7' +gem 'sprockets', '~> 3.7.2' # TODO: resolve errors when upgrading sunspot_rails/sunspot_solr from these pinned versions gem 'sunspot_rails', '2.2.7' # @drg frozen gem 'sunspot_solr', '2.2.0' # @drg frozen diff --git a/Gemfile.lock b/Gemfile.lock index 6ac8c689..b9cab8ae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -335,7 +335,7 @@ GEM spring (>= 0.9.1) spring-commands-rubocop (0.2.0) spring (>= 1.0, < 3.0) - sprockets (3.7.1) + sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.2.1) @@ -453,7 +453,7 @@ DEPENDENCIES spring (~> 2.0) spring-commands-rspec (~> 1.0) spring-commands-rubocop (~> 0.2) - sprockets (~> 3.7) + sprockets (~> 3.7.2) sqlite3 (~> 1.3) sunspot_rails (= 2.2.7) sunspot_solr (= 2.2.0) @@ -472,4 +472,4 @@ RUBY VERSION ruby 2.4.1p111 BUNDLED WITH - 1.16.1 + 1.16.2 From 185b07091fc26a7a9daaea8dcfabbb7fe8370bd3 Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Mon, 27 Aug 2018 09:57:58 +0000 Subject: [PATCH 29/70] Allow wildcards in modify filters --- app/models/modify_tag_filter.rb | 75 ++++++++++++++++++++++++++- app/models/tag_filter.rb | 15 +++++- spec/models/modify_tag_filter_spec.rb | 50 ++++++++++++++++++ 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/app/models/modify_tag_filter.rb b/app/models/modify_tag_filter.rb index 417bde34..4d6ae8ff 100644 --- a/app/models/modify_tag_filter.rb +++ b/app/models/modify_tag_filter.rb @@ -17,13 +17,22 @@ class ModifyTagFilter < TagFilter # Options are either item_ids passed in, or nothing passed in def apply(item_ids = []) + # Wildcard replacement + if tag.name.include?('*') + apply_wildcard(item_ids) + else + apply_simple(item_ids) + end + end + + def apply_simple(item_ids = []) items = item_ids.any? ? FeedItem.where(id: item_ids).tagged_with(tag.name, on: hub.tagging_key) : scope.taggable_items.tagged_with(tag.name, on: hub.tagging_key) # This deactivates old and duplicate tags, which forces the cache to clear deactivate_taggings!(items.map(&:id)) - # Mass insert doesn't work on really large inserts + # Mass insert doesn't work on really large inserts # values = items.map { |item| "(#{new_tag.id},#{item.id},'FeedItem',#{self.id},'TagFilter','#{hub.tagging_key}')" }.join(',') # ActiveRecord::Base.connection.execute("INSERT INTO taggings (tag_id, taggable_id, taggable_type, tagger_id, tagger_type, context) VALUES #{values}") # items.each { |item| item.solr_index } @@ -38,6 +47,70 @@ def apply(item_ids = []) update_column(:applied, true) end + def apply_wildcard(item_ids = []) + items = item_ids.any? ? FeedItem.where(id: item_ids) : scope.taggable_items + items_ids = items.pluck(:id) + queried_tag_name = tag.name.tr('*', '%') + + filtered_taggings = ActsAsTaggableOn::Tagging + .joins(:tag) + .where('tags.name LIKE ?', queried_tag_name) + .where( + taggable_id: items_ids, + context: hub.tagging_key + ) + + filtered_taggings_ids = filtered_taggings.pluck(:id) + filtered_item_ids = filtered_taggings.pluck(:taggable_id) + filtered_items = items.where(id: filtered_item_ids) + + filter_from_in_parts = tag.name.split(/(\*)/) + filter_from_in_parts.map! do |filter_part| + if filter_part == '*' + '(.*)' + else + '(' + Regexp.quote(filter_part) + ')' + end + end + filter_from_regex = Regexp.new(filter_from_in_parts.join) + filter_to_parts = new_tag.name.split(/(\*)/) + + filtered_items.each_with_index do |item, item_index| + if item_index % 100 == 0 + GC.start + end + + taggings_to_change = item.taggings.select { |item_tagging| filtered_taggings_ids.include?(item_tagging.id) } + + taggings_to_change.each do |tagging_to_change| + tag_to_change = tagging_to_change.tag.name + tag_to_change_parts = tag_to_change.match(filter_from_regex).captures + new_tag_name_parts = [] + + filter_to_parts.each_with_index do |filter_to_part, index| + if filter_to_part == '*' + new_tag_name_parts << tag_to_change_parts[index] + else + new_tag_name_parts << filter_to_part + end + end + + new_tag_name_joined = new_tag_name_parts.join + new_tag_name_object = ActsAsTaggableOn::Tag.find_or_create_by_name_normalized(new_tag_name_joined) + + deactivate_tagging(tagging_to_change) + + new_tagging = item.taggings.build(tag: new_tag_name_object, tagger: self, context: hub.tagging_key) + if new_tagging.valid? + new_tagging.save! + item.solr_index + end + end + end + + update_column(:applied, true) + end + def deactivates_taggings(item_ids) taggings = ActsAsTaggableOn::Tagging.arel_table diff --git a/app/models/tag_filter.rb b/app/models/tag_filter.rb index 68164cee..691f0531 100644 --- a/app/models/tag_filter.rb +++ b/app/models/tag_filter.rb @@ -176,13 +176,26 @@ def self.apply_hub_filters(hub, feed_item) hub_feed = hub.hub_feed_for_feed_item(feed_item) filters = TagFilter.where("(scope_type = 'Hub' AND scope_id = #{hub.id}) OR (scope_type = 'HubFeed' AND scope_id = #{hub_feed.id})").order("created_at ASC") applied_tag_ids = ActsAsTaggableOn::Tagging.where(taggable_id: feed_item.id).where("context <= 'hub_#{hub.id}'").map(&:tag_id) + applied_tags = ActsAsTaggableOn::Tag.where(id: applied_tag_ids) + filters.each do |filter| # apply tag filter if item has tag for certain filter types # apply tag filter for all AddTagFilters if filter.type == 'DeleteTagFilter' && applied_tag_ids.include?(filter.tag_id) filter.apply([feed_item.id]) applied_tag_ids.reject! { |tag_id| tag_id == filter.tag_id } - elsif filter.type == 'ModifyTagFilter' && applied_tag_ids.include?(filter.tag_id) + elsif filter.type == 'ModifyTagFilter' + filter_tag_name = ActsAsTaggableOn::Tag.find(filter.tag_id).name + if filter_tag_name.include?('*') + queried_tag_name = filter_tag_name.tr('*', '%') + filter_applied_tags = applied_tags + .where('name LIKE ?', queried_tag_name) + + next if filter_applied_tags.count.zero? + else + next unless applied_tag_ids.include?(filter.tag_id) + end + filter.apply([feed_item.id]) applied_tag_ids.reject! { |tag_id| tag_id == filter.tag_id } applied_tag_ids << filter.new_tag_id diff --git a/spec/models/modify_tag_filter_spec.rb b/spec/models/modify_tag_filter_spec.rb index 81603a43..7b110e16 100644 --- a/spec/models/modify_tag_filter_spec.rb +++ b/spec/models/modify_tag_filter_spec.rb @@ -229,6 +229,56 @@ def filter_list expect(new_tag_lists).to show_effects_of filter end + + it 'modifies tags using wildcards' do + old_tag_name = 'just-a-great-tag' + new_tag_name = 'just-a-bad-tag' + filter_tag_name = '*great*' + new_filter_tag_name = '*bad*' + + old_tag = create(:tag, name: old_tag_name) + create(:tagging, tag: old_tag, taggable: @feed_items.first, + context: @hub.tagging_key) + + filter = add_filter(filter_tag_name, new_filter_tag_name) + filter.apply + + new_tag_lists = tag_lists_for(@feed_items.reload, @hub.tagging_key) + + expect(new_tag_lists.first).to include(new_tag_name) + + old_tag_name = 'testy-tag' + new_tag_name = 'no-wildcards-tag' + filter_tag_name = 'testy*' + new_filter_tag_name = 'no-wildcards-tag' + + old_tag = create(:tag, name: old_tag_name) + create(:tagging, tag: old_tag, taggable: @feed_items.first, + context: @hub.tagging_key) + + filter = add_filter(filter_tag_name, new_filter_tag_name) + filter.apply + + new_tag_lists = tag_lists_for(@feed_items.reload, @hub.tagging_key) + + expect(new_tag_lists.first).to include(new_tag_name) + + old_tag_name = 'testy-single-wildcard-tag' + new_tag_name = 'single-wildcard-tag' + filter_tag_name = 'testy-*' + new_filter_tag_name = '*' + + old_tag = create(:tag, name: old_tag_name) + create(:tagging, tag: old_tag, taggable: @feed_items.first, + context: @hub.tagging_key) + + filter = add_filter(filter_tag_name, new_filter_tag_name) + filter.apply + + new_tag_lists = tag_lists_for(@feed_items.reload, @hub.tagging_key) + + expect(new_tag_lists.first).to include(new_tag_name) + end end it_behaves_like 'a hub-level tag filter' From a7acfb0591e82f6652d6041a6b1071f67ef2c2a5 Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Tue, 28 Aug 2018 17:33:00 +0000 Subject: [PATCH 30/70] Make RSS and Atom feeds W3C valid --- app/controllers/users_controller.rb | 4 ++-- app/helpers/hubs_helper.rb | 11 +++++++++++ app/views/feed_items/index.atom.builder | 6 ++---- app/views/feed_items/index.rss.builder | 10 +++++----- app/views/hubs/items.atom.builder | 6 ++---- app/views/hubs/items.rss.builder | 10 +++++----- app/views/republished_feeds/items.atom.builder | 6 ++---- app/views/republished_feeds/items.rss.builder | 10 +++++----- app/views/tags/atom.builder | 8 +++----- app/views/tags/rss.builder | 10 +++++----- app/views/users/tags_atom.builder | 6 ++---- app/views/users/tags_rss.builder | 8 ++++---- 12 files changed, 48 insertions(+), 47 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index b942a539..c6675f20 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -183,8 +183,8 @@ def load_hub end def set_home_url - @home_url = @tag ? hub_user_tags_name_path(@hub, @user.username, @tag.name) : - hub_user_hub_items_path(@hub, @user.username) + @home_url = @tag ? hub_user_tags_name_url(@hub, @user.username, @tag.name) : + hub_user_hub_items_url(@hub, @user.username) end def load_feed_items diff --git a/app/helpers/hubs_helper.rb b/app/helpers/hubs_helper.rb index 83dfbf66..9dc8de86 100644 --- a/app/helpers/hubs_helper.rb +++ b/app/helpers/hubs_helper.rb @@ -26,4 +26,15 @@ def sortable_link(name, sort, order, path = 'hubs_path', options = {}) def items_feed_titles(item, hub) item.feeds.where(id: hub.feeds.pluck(:id)).map(&:title).join(', ') end + + def valid_url?(url) + uri = URI.parse(url) + uri.is_a?(URI::HTTP) && !uri.host.nil? + rescue URI::InvalidURIError + false + end + + def valid_email?(email) + /\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/.match(email) + end end diff --git a/app/views/feed_items/index.atom.builder b/app/views/feed_items/index.atom.builder index 4eaff604..e74be22a 100644 --- a/app/views/feed_items/index.atom.builder +++ b/app/views/feed_items/index.atom.builder @@ -5,10 +5,8 @@ atom_feed(:root_url => hub_hub_feed_url(@hub,@hub_feed), :language => 'en-US') d @feed_items.each do |item| atom.entry( item , :url => item.url ) do |entry| - unless item.authors.blank? - entry.author do |author| - author.name item.authors - end + entry.author do |author| + author.name item.authors || '' end unless item.contributors.blank? entry.contributor do |contributor| diff --git a/app/views/feed_items/index.rss.builder b/app/views/feed_items/index.rss.builder index 9e7672b0..e8ff0b09 100644 --- a/app/views/feed_items/index.rss.builder +++ b/app/views/feed_items/index.rss.builder @@ -6,7 +6,7 @@ xml.rss( ) do xml.channel do xml.title @hub_feed.title - xml.description @hub_feed.description + xml.description strip_tags(@hub_feed.description) xml.link hub_hub_feed_url(@hub,@hub_feed) xml.generator Tagteam::Application.config.rss_generator @@ -14,7 +14,7 @@ xml.rss( xml.item do xml.title item.title unless item.description.blank? - xml.description item.description + xml.description strip_tags(item.description) end unless item.content.blank? xml.tag!('content:encoded', item.content) @@ -22,9 +22,9 @@ xml.rss( unless item.date_published.blank? xml.pubDate item.date_published.to_s(:rfc822) end - xml.link item.url - xml.guid item.guid - xml.author item.authors + xml.link item.url if valid_url?(item.url) + xml.guid item.guid if valid_url?(item.guid) + xml.author item.authors if valid_email?(item.authors) (item.all_tags_on(@hub_feed.hub.tagging_key) - @hub.deprecated_tags).each do|tag| xml.category (@hub_feed.hub.tag_prefix.blank?) ? tag.name : tag.name_prefixed_with(@hub_feed.hub.tag_prefix) end diff --git a/app/views/hubs/items.atom.builder b/app/views/hubs/items.atom.builder index c8cb586b..a97ab353 100644 --- a/app/views/hubs/items.atom.builder +++ b/app/views/hubs/items.atom.builder @@ -5,10 +5,8 @@ atom_feed(:root_url => hub_url(@hub), :language => 'en-US') do |atom| @feed_items.each do |item| atom.entry( item , :url => item.url ) do |entry| - unless item.authors.blank? - entry.author do |author| - author.name item.authors - end + entry.author do |author| + author.name item.authors || '' end unless item.contributors.blank? entry.contributor do |contributor| diff --git a/app/views/hubs/items.rss.builder b/app/views/hubs/items.rss.builder index 5823375b..931f8591 100644 --- a/app/views/hubs/items.rss.builder +++ b/app/views/hubs/items.rss.builder @@ -6,7 +6,7 @@ xml.rss( ) do xml.channel do xml.title @hub.title - xml.description @hub.description + xml.description strip_tags(@hub.description) xml.link hub_url(@hub) xml.generator Tagteam::Application.config.rss_generator @@ -14,7 +14,7 @@ xml.rss( xml.item do xml.title item.title unless item.description.blank? - xml.description item.description + xml.description strip_tags(item.description) end unless item.content.blank? xml.tag!('content:encoded', item.content) @@ -22,9 +22,9 @@ xml.rss( unless item.date_published.blank? xml.pubDate item.date_published.to_s(:rfc822) end - xml.link item.url - xml.guid item.guid - xml.author item.authors + xml.link item.url if valid_url?(item.url) + xml.guid item.guid if valid_url?(item.guid) + xml.author item.authors if valid_email?(item.authors) (item.all_tags_on(@hub.tagging_key) - @hub.deprecated_tags).each do |tag| xml.category (@hub.tag_prefix.blank?) ? tag.name : tag.name_prefixed_with(@hub.tag_prefix) end diff --git a/app/views/republished_feeds/items.atom.builder b/app/views/republished_feeds/items.atom.builder index 9c96654c..d0d5eeec 100644 --- a/app/views/republished_feeds/items.atom.builder +++ b/app/views/republished_feeds/items.atom.builder @@ -6,10 +6,8 @@ atom_feed(:root_url => hub_republished_feed_url(@hub,@republished_feed), :langua unless @republished_feed.item_search.nil? @republished_feed.item_search.results.each do |item| atom.entry( item , :url => item.url ) do |entry| - unless item.authors.blank? - entry.author do |author| - author.name item.authors - end + entry.author do |author| + author.name item.authors || '' end unless item.contributors.blank? entry.contributor do |contributor| diff --git a/app/views/republished_feeds/items.rss.builder b/app/views/republished_feeds/items.rss.builder index 586edf55..8dd5758f 100644 --- a/app/views/republished_feeds/items.rss.builder +++ b/app/views/republished_feeds/items.rss.builder @@ -6,7 +6,7 @@ xml.rss( ) do xml.channel do xml.title @republished_feed.title - xml.description @republished_feed.description + xml.description strip_tags(@republished_feed.description) xml.link hub_republished_feed_url(@hub,@republished_feed) xml.generator Tagteam::Application.config.rss_generator @@ -15,7 +15,7 @@ xml.rss( xml.item do xml.title item.title unless item.description.blank? - xml.description item.description + xml.description strip_tags(item.description) end unless item.content.blank? xml.tag!('content:encoded', item.content) @@ -23,9 +23,9 @@ xml.rss( unless item.date_published.blank? xml.pubDate item.date_published.to_s(:rfc822) end - xml.link item.url - xml.guid item.guid - xml.author item.authors + xml.link item.url if valid_url?(item.url) + xml.guid item.guid if valid_url?(item.guid) + xml.author item.authors if valid_email?(item.authors) item.all_tags_on(@republished_feed.hub.tagging_key).each do|tag| xml.category (@republished_feed.hub.tag_prefix.blank?) ? tag.name : tag.name_prefixed_with(@republished_feed.hub.tag_prefix) end diff --git a/app/views/tags/atom.builder b/app/views/tags/atom.builder index d7f8baf0..139b7583 100644 --- a/app/views/tags/atom.builder +++ b/app/views/tags/atom.builder @@ -5,10 +5,8 @@ atom_feed(:root_url => hub_tag_show_url(@hub,@tag.name), :language => 'en-US') d @feed_items.each do |item| atom.entry( item, :url => item.url ) do |entry| - unless item.authors.blank? - entry.author do |author| - author.name item.authors - end + entry.author do |author| + author.name item.authors || '' end unless item.contributors.blank? entry.contributor do |contributor| @@ -19,7 +17,7 @@ atom_feed(:root_url => hub_tag_show_url(@hub,@tag.name), :language => 'en-US') d # entry.link( :type => 'text/html', :href => hub_feed_feed_item_url(item.hub_feed_for_hub(@hub.id),item), :rel => 'self' ) entry.title item.title (item.all_tags_on(@hub.tagging_key) - @hub.deprecated_tags).each do |tag| - entry.category(:term => (@hub.tag_prefix.blank?) ? tag.name : tag.name_prefixed_with(@hub.tag_prefix), :scheme => hub_tag_path(@hub,@tag)) + entry.category(:term => (@hub.tag_prefix.blank?) ? tag.name : tag.name_prefixed_with(@hub.tag_prefix), :scheme => hub_tag_url(@hub,@tag)) end unless item.rights.blank? entry.rights item.rights diff --git a/app/views/tags/rss.builder b/app/views/tags/rss.builder index 26253efe..2e24ca2a 100644 --- a/app/views/tags/rss.builder +++ b/app/views/tags/rss.builder @@ -7,14 +7,14 @@ xml.rss( xml.channel do xml.title "Items tagged with #{@tag.name} in #{@hub.title}" xml.description "Items tagged with #{@tag.name} in #{@hub.title}" - xml.link hub_tag_path(@hub,@tag) + xml.link hub_tag_url(@hub,@tag) xml.generator Tagteam::Application.config.rss_generator @feed_items.each do |item| xml.item do xml.title item.title unless item.description.blank? - xml.description item.description + xml.description strip_tags(item.description) end unless item.content.blank? xml.tag!('content:encoded', item.content) @@ -22,9 +22,9 @@ xml.rss( unless item.date_published.blank? xml.pubDate item.date_published.to_s(:rfc822) end - xml.link item.url - xml.guid item.guid - xml.author item.authors + xml.link item.url if valid_url?(item.url) + xml.guid item.guid if valid_url?(item.guid) + xml.author item.authors if valid_email?(item.authors) (item.all_tags_on(@hub.tagging_key) - @hub.deprecated_tags).each do|tag| xml.category (@hub.tag_prefix.blank?) ? tag.name : tag.name_prefixed_with(@hub.tag_prefix) end diff --git a/app/views/users/tags_atom.builder b/app/views/users/tags_atom.builder index d09f4915..254f4341 100644 --- a/app/views/users/tags_atom.builder +++ b/app/views/users/tags_atom.builder @@ -5,10 +5,8 @@ atom_feed(:root_url => @home_url, :language => 'en-US') do |atom| @feed_items.each do |item| atom.entry( item, :url => item.url ) do |entry| - unless item.authors.blank? - entry.author do |author| - author.name item.authors - end + entry.author do |author| + author.name item.authors || '' end unless item.contributors.blank? entry.contributor do |contributor| diff --git a/app/views/users/tags_rss.builder b/app/views/users/tags_rss.builder index 57b4766a..c2007d3d 100644 --- a/app/views/users/tags_rss.builder +++ b/app/views/users/tags_rss.builder @@ -14,7 +14,7 @@ xml.rss( xml.item do xml.title item.title unless item.description.blank? - xml.description item.description + xml.description strip_tags(item.description) end unless item.content.blank? xml.tag!('content:encoded', item.content) @@ -22,9 +22,9 @@ xml.rss( unless item.date_published.blank? xml.pubDate item.date_published.to_s(:rfc822) end - xml.link item.url - xml.guid item.guid - xml.author item.authors + xml.link item.url if valid_url?(item.url) + xml.guid item.guid if valid_url?(item.guid) + xml.author item.authors if valid_email?(item.authors) (item.all_tags_on(@hub.tagging_key) - @hub.deprecated_tags).each do|tag| xml.category (@hub.tag_prefix.blank?) ? tag.name : tag.name_prefixed_with(@hub.tag_prefix) end From b2165c763d7a0fa7be746003eea264718dcde208 Mon Sep 17 00:00:00 2001 From: J S Diaz Date: Tue, 28 Aug 2018 15:05:05 -0400 Subject: [PATCH 31/70] added skylight --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index eef767ae..ec85b9fe 100644 --- a/Gemfile +++ b/Gemfile @@ -36,6 +36,7 @@ gem 'sanitize', '~> 4.6' gem 'sass-rails', '~> 5.0' gem 'sidekiq', '~> 5.1' gem 'sinatra', '~> 2.0', require: false +gem 'skylight' gem 'sprockets', '~> 3.7.2' # TODO: resolve errors when upgrading sunspot_rails/sunspot_solr from these pinned versions gem 'sunspot_rails', '2.2.7' # @drg frozen From aa6e0b77c0c7e2bc8f20a8febe3b60a2fd28835a Mon Sep 17 00:00:00 2001 From: jsdiaz Date: Tue, 28 Aug 2018 17:47:01 -0400 Subject: [PATCH 32/70] added skylight link --- README.rdoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.rdoc b/README.rdoc index 0d33918c..90b1e9a0 100644 --- a/README.rdoc +++ b/README.rdoc @@ -92,3 +92,8 @@ TagTeam is licensed under the AGPL == Copyright 2016 President and Fellows of Harvard College + +Performance Monitoring +====================== + +[![View performance data on Skylight](https://badges.skylight.io/status/M7Po69KatXyB.svg?token=3JeDBjiwdf1D1dBRbQOqoMnB9BrUjajtvvQXwOTlvXc)](https://www.skylight.io/app/applications/M7Po69KatXyB) From 1cc40789e76968ac6a103cf18813d91bf28dfd54 Mon Sep 17 00:00:00 2001 From: jsdiaz Date: Tue, 28 Aug 2018 17:47:48 -0400 Subject: [PATCH 33/70] fixed formatting --- README.rdoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.rdoc b/README.rdoc index 90b1e9a0..7dc7831c 100644 --- a/README.rdoc +++ b/README.rdoc @@ -93,7 +93,6 @@ TagTeam is licensed under the AGPL 2016 President and Fellows of Harvard College -Performance Monitoring -====================== +== Performance Monitoring [![View performance data on Skylight](https://badges.skylight.io/status/M7Po69KatXyB.svg?token=3JeDBjiwdf1D1dBRbQOqoMnB9BrUjajtvvQXwOTlvXc)](https://www.skylight.io/app/applications/M7Po69KatXyB) From 9e329c1be22a05f4889ba537238b3916b8944a89 Mon Sep 17 00:00:00 2001 From: jsdiaz Date: Tue, 28 Aug 2018 17:51:28 -0400 Subject: [PATCH 34/70] updated link formatting --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 7dc7831c..7d0f1bac 100644 --- a/README.rdoc +++ b/README.rdoc @@ -95,4 +95,4 @@ TagTeam is licensed under the AGPL == Performance Monitoring -[![View performance data on Skylight](https://badges.skylight.io/status/M7Po69KatXyB.svg?token=3JeDBjiwdf1D1dBRbQOqoMnB9BrUjajtvvQXwOTlvXc)](https://www.skylight.io/app/applications/M7Po69KatXyB) +[View performance data on Skylight](https://www.skylight.io/app/applications/M7Po69KatXyB) From 84a52bf99e50da72b2b1fc37c330468fb6081201 Mon Sep 17 00:00:00 2001 From: jsdiaz Date: Tue, 28 Aug 2018 17:52:14 -0400 Subject: [PATCH 35/70] updated link formatting --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 7d0f1bac..76767617 100644 --- a/README.rdoc +++ b/README.rdoc @@ -95,4 +95,4 @@ TagTeam is licensed under the AGPL == Performance Monitoring -[View performance data on Skylight](https://www.skylight.io/app/applications/M7Po69KatXyB) +* View performance data on Skylight - https://www.skylight.io/app/applications/M7Po69KatXyB From 5238f8c689068f7a9bac0c987f7748dc2613a7ea Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Wed, 29 Aug 2018 14:02:51 +0000 Subject: [PATCH 36/70] Fix bookmarklet form in Firefox --- app/views/shared/_bookmarklet_button.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/_bookmarklet_button.html.haml b/app/views/shared/_bookmarklet_button.html.haml index 60edc0fc..a42a8ea7 100644 --- a/app/views/shared/_bookmarklet_button.html.haml +++ b/app/views/shared/_bookmarklet_button.html.haml @@ -1,2 +1,2 @@ -%a.btn.btn-success{ href: "javascript:(function(){f='#{bookmarklets_add_url}?feed_item%5burl%5d='+encodeURIComponent(window.location.href)+'&feed_item%5btitle%5d='+encodeURIComponent(document.title)+'&feed_item%5bdescription%5d='+encodeURIComponent(''+(window.getSelection?window.getSelection():document.getSelection?document.getSelection():document.selection.createRange().text))+'&';a=function(){if(!window.open(f+'noui=1&jump=doclose','tagteam','links=0,scrollbars=0,toolbar=0,width=710,height=900'))location.href=f+'jump=yes'};if(/Firefox/.test(navigator.userAgent)){setTimeout(a,0)}else{a()}})()" } +%a.btn.btn-success{ href: "javascript:(function(){f='#{bookmarklets_add_url}?feed_item%5burl%5d='+encodeURIComponent(window.location.href)+'&feed_item%5btitle%5d='+encodeURIComponent(document.title)+'&feed_item%5bdescription%5d='+encodeURIComponent(''+(window.getSelection?window.getSelection():document.getSelection?document.getSelection():document.selection.createRange().text))+'&';a=function(){if(!window.open(f+'noui=1&jump=doclose','tagteam','links=0,scrollbars=1,toolbar=0,width=710,height=900'))location.href=f+'jump=yes'};if(/Firefox/.test(navigator.userAgent)){setTimeout(a,0)}else{a()}})()" } = fa_icon 'plus-circle', text: 'Add to TagTeam' From 2f3b579a8fcfc06f9ebedd0ee9aa6eab153b1b4e Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Wed, 29 Aug 2018 14:33:56 +0000 Subject: [PATCH 37/70] Let users remove themselves from hubs --- app/controllers/hubs_controller.rb | 20 ++++++++++-- app/controllers/users_controller.rb | 16 ++++++++++ .../feed_items/locate_tagging_users_by_hub.rb | 8 ++++- app/interactions/hubs/leave.rb | 32 +++++++++++++++++++ app/interactions/users/self_remove.rb | 23 +++++++++++++ app/models/user.rb | 4 +++ app/policies/hub_policy.rb | 6 ++++ app/policies/user_policy.rb | 6 ++++ app/views/devise/registrations/edit.html.haml | 14 ++++++-- app/views/hubs/_top_panel.html.haml | 15 +++++---- config/routes.rb | 2 ++ spec/policies/hub_policy_spec.rb | 6 ++++ 12 files changed, 140 insertions(+), 12 deletions(-) create mode 100644 app/interactions/hubs/leave.rb create mode 100644 app/interactions/users/self_remove.rb diff --git a/app/controllers/hubs_controller.rb b/app/controllers/hubs_controller.rb index 579a4ed0..8fd9eb0b 100644 --- a/app/controllers/hubs_controller.rb +++ b/app/controllers/hubs_controller.rb @@ -23,7 +23,8 @@ class HubsController < ApplicationController :retrievals, :search, :show, - :scoreboard + :scoreboard, + :leave ] after_action :verify_authorized, except: [:index, :home, :meta] @@ -67,7 +68,8 @@ class HubsController < ApplicationController :deprecate_tag, :undeprecate_tag, :unsubscribe_feed, - :scoreboard + :scoreboard, + :leave ] before_action :store_feed_visitor, only: :items @@ -896,6 +898,20 @@ def remove_delimiter redirect_to settings_hub_path(@hub) end + def leave + authorize @hub + + outcome = Hubs::Leave.run(hub: @hub, user: current_user) + + if outcome.valid? + flash[:notice] = 'You have been removed from the hub.' + else + flash[:error] = outcome.errors.full_messages.join(' and ') + end + + redirect_to request.referer + end + private def sanitize_params diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index c6675f20..90c4d17a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -167,6 +167,22 @@ def documentation_admin_role redirect_to user_path end + def selfremove + authorize current_user + + outcome = Users::SelfRemove.run(user: current_user) + + if outcome.valid? + flash[:notice] = 'You have been removed from the site.' + + redirect_to root_path + else + flash[:error] = outcome.errors.full_messages.join(' and ') + + redirect_to edit_user_registration_path + end + end + private def load_user diff --git a/app/interactions/feed_items/locate_tagging_users_by_hub.rb b/app/interactions/feed_items/locate_tagging_users_by_hub.rb index 7b3a4197..e9465818 100644 --- a/app/interactions/feed_items/locate_tagging_users_by_hub.rb +++ b/app/interactions/feed_items/locate_tagging_users_by_hub.rb @@ -14,7 +14,13 @@ def execute context: "hub_#{hub.id}" ) - User.find(taggings.pluck(:tagger_id)) + taggers = User.find(taggings.pluck(:tagger_id)) + + # Remove users that tagged the item, but are not subscribed to the hub + # anymore + taggers.select! { |tagger| tagger.subscribed_to_hub?(hub) } + + taggers end end end diff --git a/app/interactions/hubs/leave.rb b/app/interactions/hubs/leave.rb new file mode 100644 index 00000000..0ec45daa --- /dev/null +++ b/app/interactions/hubs/leave.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true +module Hubs + # Remove a user for a hub + class Leave < ActiveInteraction::Base + object :hub + object :user + + def execute + if user.is?([:owner], hub) + number_of_owners = Role.find_by( + authorizable_type: 'Hub', + authorizable_id: hub.id, + name: 'owner' + ).users.count + + if number_of_owners == 1 + errors[:base] << 'You are the only owner of the hub. Add an additional owner first to remove yourself.' + else + remove_user_from_hub(user, hub) + end + else + remove_user_from_hub(user, hub) + end + end + + private + + def remove_user_from_hub(user, hub) + user.has_no_roles_for!(hub) + end + end +end diff --git a/app/interactions/users/self_remove.rb b/app/interactions/users/self_remove.rb new file mode 100644 index 00000000..df56c75e --- /dev/null +++ b/app/interactions/users/self_remove.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true +module Users + # Remove a user + class SelfRemove < ActiveInteraction::Base + object :user + + def execute + if user.nil? + errors[:base] << 'No user found.' + + return + end + + # Destroy all user's hubs, but omit these with a solo owner + user_hubs = user.my + user_hubs.reject! { |hub| hub.owners.count > 1 } + user_hubs.each(&:destroy) + + # Destroy the user + user.destroy + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 36af426b..1555354f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -170,6 +170,10 @@ def removed_tag_suggestions_name removed_tag_suggestions.joins(:tag).map{|removed_suggestion| removed_suggestion.tag.name }.uniq end + def subscribed_to_hub?(hub) + has_roles_for?(hub) + end + protected def gen_role_cache diff --git a/app/policies/hub_policy.rb b/app/policies/hub_policy.rb index c9752e51..9c865463 100644 --- a/app/policies/hub_policy.rb +++ b/app/policies/hub_policy.rb @@ -214,6 +214,12 @@ def remove_item? owner_or_admin? end + def leave? + return false if user.blank? + + user.has_roles_for?(record) + end + private def owner_or_admin? diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index 540f2089..68b74294 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -77,6 +77,12 @@ def tags_atom? true end + def selfremove? + return false if user.blank? + + true + end + class Scope < ApplicationPolicy::Scope def resolve return User.none if user.blank? diff --git a/app/views/devise/registrations/edit.html.haml b/app/views/devise/registrations/edit.html.haml index 03187c67..9fa4f2b8 100644 --- a/app/views/devise/registrations/edit.html.haml +++ b/app/views/devise/registrations/edit.html.haml @@ -34,7 +34,15 @@ .actions = f.submit "Update", class: "btn btn-primary btn-block" %hr/ - %h3 Cancel my account - %p - Unhappy? #{button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete, class: "btn"} + %h3 Remove my account + %p.alert.alert-danger + Deleting your account will also delete all the hubs of which you are the sole owner. + If you want any of those hubs to continue, then create at least one co-owner of each before you delete your account. + You can do this through the Team tab of your hubs. + %br + %br + You can also export all your data before removing it using the #{ link_to('Export section', export_import_path) }. + + #{button_to "Remove my account", users_selfremove_path, data: { confirm: "Are you sure?" }, method: :post, class: "btn"} + = link_to "Back", :back diff --git a/app/views/hubs/_top_panel.html.haml b/app/views/hubs/_top_panel.html.haml index fd0da1ed..c0876737 100644 --- a/app/views/hubs/_top_panel.html.haml +++ b/app/views/hubs/_top_panel.html.haml @@ -2,12 +2,15 @@ %h1 = hub.title %span.actions - - if current_user && (current_user.is?(:owner, hub) || current_user.is?(:superadmin)) - = link_to edit_hub_path(hub), title: 'Edit this hub' do - = fa_icon 'pencil' - = link_to hub_path(hub), method: :delete, data: { confirm: 'Are you sure you want to delete this hub?' }, title: 'Delete this hub' do - = fa_icon 'remove' - + - if current_user + - if current_user.is?(:owner, @hub) || current_user.is?(:superadmin) + = link_to edit_hub_path(@hub), title: 'Edit this hub' do + = fa_icon 'pencil' + = link_to hub_path(@hub), method: :delete, data: { confirm: 'Are you sure you want to delete this hub?' }, title: 'Delete this hub' do + = fa_icon 'remove' + - if current_user.is?([:owner, :bookmarker], @hub) + = link_to leave_hub_path(@hub), method: :post, data: { confirm: 'Are you sure you want to leave this hub?' }, title: 'Leave this hub' do + = fa_icon 'sign-out' .col-sm-4 .export-menu.text-right = link_to items_hub_path(hub, format: :rss), title: 'An RSS feed of all items in this hub, with tag filters applied.', class: 'btn btn-default' do diff --git a/config/routes.rb b/config/routes.rb index c7331d6d..22b8c176 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -17,6 +17,7 @@ namespace :users do get 'search/autocomplete' + post 'selfremove' end post 'bookmarklets/add_item' @@ -135,6 +136,7 @@ post 'deprecate_tag' post 'undeprecate_tag' delete 'remove_delimiter' + post 'leave' end collection do diff --git a/spec/policies/hub_policy_spec.rb b/spec/policies/hub_policy_spec.rb index 355c4740..7b8856b3 100644 --- a/spec/policies/hub_policy_spec.rb +++ b/spec/policies/hub_policy_spec.rb @@ -44,6 +44,7 @@ it { is_expected.to forbid_action(:statistics) } it { is_expected.to forbid_action(:active_taggers) } it { is_expected.to forbid_action(:tags_used_not_approved) } + it { is_expected.to forbid_action(:leave) } end context 'for a logged in user' do @@ -91,6 +92,7 @@ it { is_expected.to permit_action(:statistics) } it { is_expected.to permit_action(:active_taggers) } it { is_expected.to permit_action(:tags_used_not_approved) } + it { is_expected.to permit_action(:leave) } end context 'with an inputter role on the hub' do @@ -104,6 +106,7 @@ it { is_expected.to forbid_action(:remove_roles) } it { is_expected.to forbid_action(:team) } it { is_expected.to forbid_action(:update) } + it { is_expected.to permit_action(:leave) } end context 'with a remixer role on the hub' do @@ -117,6 +120,7 @@ it { is_expected.to forbid_action(:remove_roles) } it { is_expected.to forbid_action(:team) } it { is_expected.to forbid_action(:update) } + it { is_expected.to permit_action(:leave) } end context "for a hub they don't have a role on" do @@ -129,6 +133,7 @@ it { is_expected.to forbid_action(:set_notifications) } it { is_expected.to forbid_action(:team) } it { is_expected.to forbid_action(:update) } + it { is_expected.to forbid_action(:leave) } end end @@ -170,5 +175,6 @@ it { is_expected.to permit_action(:statistics) } it { is_expected.to permit_action(:active_taggers) } it { is_expected.to permit_action(:tags_used_not_approved) } + it { is_expected.to forbid_action(:leave) } end end From e1679a4f807414948dd4fc36a9c590e3ca5ee1b0 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Wed, 29 Aug 2018 15:11:17 +0000 Subject: [PATCH 38/70] Fixed display of total on item search. --- app/views/hubs/item_search.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/hubs/item_search.html.haml b/app/views/hubs/item_search.html.haml index d2ad5a21..4ed85c73 100644 --- a/app/views/hubs/item_search.html.haml +++ b/app/views/hubs/item_search.html.haml @@ -37,7 +37,7 @@ .nicely-padded = render partial: 'shared/paginate', locals: { results: @search.results } %h2 - Items: #{@search.total} total + Items: #{@search.results.size} total %ul.hub.list-unstyled - @search.results.each do|r| = render partial: 'feed_items/list_item', as: :feed_item, object: r From b9df528d3f61e99a6eb875b6b08238e73fc0e094 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Wed, 29 Aug 2018 15:42:15 +0000 Subject: [PATCH 39/70] Fix on deprecated tag lookup, and autocomplete display. --- app/assets/javascripts/application.js | 8 ++++---- app/controllers/tags_controller.rb | 16 ++++++---------- app/models/hub.rb | 10 ++-------- app/views/tags/_graph_item.html.haml | 4 ++-- 4 files changed, 14 insertions(+), 24 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 9cf81169..68adaf6a 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -959,13 +959,13 @@ $(document).ready(function(){ $.ajax({ url: $.rootPath() + 'hubs/' + hubId + '/removed_tag_suggestion', type: 'post', - data: { tag_id: $link.attr('data-tag-id'), remove: $link.find('span').hasClass('fa-eye-slash') }, + data: { tag_id: $link.attr('data-tag-id'), remove: $link.find('span').hasClass('fa-eye') }, success: (data) => { $link.find('span').toggleClass('fa-eye fa-eye-slash'); - if($link.find('span').hasClass('fa-eye')) { - $link.attr('title', 'Show on autocomplete'); + if($link.find('span').hasClass('fa-eye-slash')) { + $link.attr('title', 'Click to include this tag in the autocomplete tag list.'); } else { - $link.attr('title', 'Hide from autocomplete'); + $link.attr('title', 'Click to exclude this tag from the autocomplete tag list.'); } } }); diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index cac1d80d..70d235d4 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -24,7 +24,7 @@ class TagsController < ApplicationController # Autocomplete ActsAsTaggableOn::Tag results for a Hub as json. def autocomplete - deprecated_tags_names = @hub.deprecated_tags.pluck(:name) + deprecated_tag_names = @hub.deprecated_tag_names tags_applied = params.has_key?(:tags_applied) ? params[:tags_applied].split(', ') : [] @@ -37,7 +37,7 @@ def autocomplete if @hub.settings[:suggest_only_approved_tags] approved_tags = @hub .hub_approved_tags - .where.not(tag: deprecated_tags_names) + .where.not(tag: deprecated_tag_names) .pluck(:tag) result = ActsAsTaggableOn::Tag @@ -51,27 +51,23 @@ def autocomplete count = ActsAsTaggableOn::Tag .select('DISTINCT(tags.id)') .left_joins(:taggings) - .where.not(name: deprecated_tags_names) + .where.not(name: deprecated_tag_names) .where('name LIKE \'' + params[:term] + '%\'') .where(name: approved_tags) .count else - tag_ids = Rails.cache.fetch("all-tag-ids-#{@hub.id}", expires_in: 1.hour) do - FeedItem.joins(:hubs, :taggings).where(hubs: { id: @hub.id }).pluck('taggings.tag_id').uniq - end - remove_suggested = RemovedTagSuggestion.where(hub_id: @hub.id).map { |rts| rts.tag.name } + tag_ids = FeedItem.joins(:hubs, :taggings).where(hubs: { id: @hub.id }).pluck('taggings.tag_id').uniq + exclude_tag_names = RemovedTagSuggestion.where(hub_id: @hub.id).map { |rts| rts.tag.name } + deprecated_tag_names result = ActsAsTaggableOn::Tag .left_joins(:taggings) - .where.not(name: deprecated_tags_names) - .where.not(name: remove_suggested) + .where.not(name: exclude_tag_names) .where('name LIKE \'' + params[:term] + '%\'') .where(id: tag_ids) .group(:id) .order('COUNT(taggings.id) DESC') count = result.length end - respond_to do |format| format.json do # Should probably change this to use render_for_api diff --git a/app/models/hub.rb b/app/models/hub.rb index 713daf69..63b4022a 100644 --- a/app/models/hub.rb +++ b/app/models/hub.rb @@ -259,17 +259,11 @@ def hub_feed_for_feed_item(feed_item) end def deprecated_tags - TagFilter.where(hub_id: self.id, scope_type: 'Hub', type: ['DeleteTagFilter', 'ModifyTagFilter']).map(&:tag).uniq + TagFilter.where(hub_id: self.id, scope_type: 'Hub', type: 'DeleteTagFilter').map(&:tag).uniq end def deprecated_tag_names - tags = all_tag_filters.where(type: 'DeleteTagFilter', scope_type: 'Hub').select(:tag_id) - ActsAsTaggableOn::Tag.where(id: tags).map(&:name).uniq - end - - def fetch_deprecated_tags - tags = all_tag_filters.where(type: 'DeleteTagFilter', scope_type: 'Hub').select(:tag_id) - ActsAsTaggableOn::Tag.where(id: tags) + deprecated_tags.map(&:name) end private diff --git a/app/views/tags/_graph_item.html.haml b/app/views/tags/_graph_item.html.haml index b228b8ae..30e91078 100644 --- a/app/views/tags/_graph_item.html.haml +++ b/app/views/tags/_graph_item.html.haml @@ -3,6 +3,6 @@ %span.tag-count = tag.count - if show_removed_tags - = link_to '#', title: "#{@removed_tag_suggestions.include?(tag.id) ? 'Show on' : 'Hide from'} autocomplete", class: 'remove-suggestion-toggle', "data-hub-id": hub.id, "data-tag-id": tag.id do - %span{ class: "fa #{@removed_tag_suggestions.include?(tag.id) ? 'fa-eye' : 'fa-eye-slash'}" } + = link_to '#', title: "#{@removed_tag_suggestions.include?(tag.id) ? 'Click to include this tag in' : 'Click to exclude this tag from'} the autocomplete tag list.", class: 'remove-suggestion-toggle', "data-hub-id": hub.id, "data-tag-id": tag.id do + %span{ class: "fa #{@removed_tag_suggestions.include?(tag.id) ? 'fa-eye-slash' : 'fa-eye'}" } .tag-graph-bar{:style => "width: #{tag.count.to_f / max_count * 100}%"} From 347187231120da2322e805342b75296f570a3f0c Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Wed, 29 Aug 2018 15:42:27 +0000 Subject: [PATCH 40/70] Skylight update. --- Gemfile.lock | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index b9cab8ae..095ea76e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -329,6 +329,10 @@ GEM rack (~> 2.0) rack-protection (= 2.0.2) tilt (~> 2.0) + skylight (2.0.2) + skylight-core (= 2.0.2) + skylight-core (2.0.2) + activesupport (>= 4.2.0) spring (2.0.2) activesupport (>= 4.2) spring-commands-rspec (1.0.4) @@ -450,6 +454,7 @@ DEPENDENCIES shoulda-matchers (~> 3.1) sidekiq (~> 5.1) sinatra (~> 2.0) + skylight spring (~> 2.0) spring-commands-rspec (~> 1.0) spring-commands-rubocop (~> 0.2) From c9b598d06dbcbdacebb14d4684fb2fb301c630fc Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Wed, 29 Aug 2018 16:17:58 +0000 Subject: [PATCH 41/70] Fixed display messages of failed filters. Prevented infinite loop of modify tag filters. --- app/controllers/tag_filters_controller.rb | 5 +++-- app/models/modify_tag_filter.rb | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/controllers/tag_filters_controller.rb b/app/controllers/tag_filters_controller.rb index 65012833..edea1f02 100644 --- a/app/controllers/tag_filters_controller.rb +++ b/app/controllers/tag_filters_controller.rb @@ -33,7 +33,7 @@ def create # Allow multiple TagFilters to be created from a comma-separated string of tags params[:new_tag] = params[:new_tag].split(',').join('.') - tag_filters = if params[:new_tag].empty? + tag_filter_interactions = if params[:new_tag].empty? [TagFilters::Create.run(tag_filter_params)] else new_tags = TagFilterHelper.split_tags(params[:new_tag], @hub) @@ -43,6 +43,7 @@ def create end end + tag_filters = tag_filter_interactions.map(&:result) tag_filters.all?(&:valid?) ? process_successful_create(tag_filters) : process_failed_create(tag_filters) end @@ -122,7 +123,7 @@ def process_successful_create(tag_filters) render plain: notice, layout: !request.xhr? end - def process_failed_create + def process_failed_create(tag_filters) flash[:error] = t('tag_filters.errors_when_adding', count: tag_filters.size) errors = tag_filters.map { |tag_filter| tag_filter.errors.full_messages.join('
') } diff --git a/app/models/modify_tag_filter.rb b/app/models/modify_tag_filter.rb index 4d6ae8ff..94b52799 100644 --- a/app/models/modify_tag_filter.rb +++ b/app/models/modify_tag_filter.rb @@ -4,6 +4,10 @@ class ModifyTagFilter < TagFilter validates :new_tag_id, presence: true validate :new_tag_id do + terminating_tag_filter = ModifyTagFilter.find_recursive(self.hub_id, self.new_tag.name) + if terminating_tag_filter.present? && terminating_tag_filter.new_tag.name == self.tag.name + errors.add(:new_tag_id, " can't create an infinite loop of tag filters") + end if new_tag_id == tag_id errors.add(:new_tag_id, " can't be the same as the original tag") end From 67daaabf282725ff6772f9c202cbbf60d841885e Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Wed, 29 Aug 2018 16:54:43 +0000 Subject: [PATCH 42/70] Make it able to copy/move feed items together with tags --- app/controllers/feed_items_controller.rb | 11 ++-- .../feed_items/copy_move_to_hub.rb | 58 +++++++++++++++++++ app/interactions/feed_items/copy_to_hub.rb | 14 ----- app/interactions/feed_items/move_to_hub.rb | 17 ------ 4 files changed, 65 insertions(+), 35 deletions(-) create mode 100644 app/interactions/feed_items/copy_move_to_hub.rb delete mode 100644 app/interactions/feed_items/copy_to_hub.rb delete mode 100644 app/interactions/feed_items/move_to_hub.rb diff --git a/app/controllers/feed_items_controller.rb b/app/controllers/feed_items_controller.rb index e1377d5b..127f9be6 100644 --- a/app/controllers/feed_items_controller.rb +++ b/app/controllers/feed_items_controller.rb @@ -186,10 +186,12 @@ def copy_item redirect_after_move_copy(@hub_feed, @feed_item) end - FeedItems::CopyToHub.run!( + FeedItems::CopyMoveToHub.run!( current_user: current_user, feed_item: @feed_item, - hub: hub + from_hub_feed: @hub_feed, + to_hub: hub, + action_type: 'copy' ) flash[:notice] = 'Item successfully copied to the hub.' @@ -213,11 +215,12 @@ def move_item redirect_after_move_copy(@hub_feed, @feed_item) end - FeedItems::MoveToHub.run!( + FeedItems::CopyMoveToHub.run!( current_user: current_user, feed_item: @feed_item, from_hub_feed: @hub_feed, - to_hub: to_hub + to_hub: to_hub, + action_type: 'move' ) flash[:notice] = 'Item successfully moved from the hub.' diff --git a/app/interactions/feed_items/copy_move_to_hub.rb b/app/interactions/feed_items/copy_move_to_hub.rb new file mode 100644 index 00000000..deb0c81b --- /dev/null +++ b/app/interactions/feed_items/copy_move_to_hub.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module FeedItems + class CopyMoveToHub < ActiveInteraction::Base + object :feed_item + object :from_hub_feed, class: HubFeed + object :to_hub, class: Hub + object :current_user, class: User + string :action_type + + def execute + from_hub = from_hub_feed.hub + + feed_item.all_tags_on(from_hub.tagging_key).map do |tag| + from_tagging = ActsAsTaggableOn::Tagging.find_by( + tag: tag, + taggable: feed_item, + context: from_hub.tagging_key + ) + + new_tag_user = current_user + if from_tagging.tagger_type == 'User' + from_user = from_tagging.tagger + elsif from_tagging.tagger_type == 'TagFilter' + unless from_tagging.tagger.nil? + from_user = from_tagging.tagger.users.first + end + end + + unless from_user.nil? + if from_user.is?([:owner, :bookmarker, :hub_tag_adder, :hub_feed_tag_filterer]) || + from_user.superadmin? + new_tag_user = from_user + end + end + + ActsAsTaggableOn::Tagging.create( + tag: tag, + taggable: feed_item, + tagger: new_tag_user, + context: to_hub.tagging_key + ) + end + + if action_type == 'copy' + feed_item.feeds << current_user.get_default_bookmarking_bookmark_collection_for(to_hub.id) + feed_item.save! + elsif action_type == 'move' + feed_item.feeds -= [from_hub_feed.feed] + feed_item.feeds << current_user.get_default_bookmarking_bookmark_collection_for(to_hub.id) + feed_item.save! + end + + TagFilter.apply_hub_filters(to_hub, feed_item) + feed_item.reload.solr_index + end + end +end diff --git a/app/interactions/feed_items/copy_to_hub.rb b/app/interactions/feed_items/copy_to_hub.rb deleted file mode 100644 index 1033e6ad..00000000 --- a/app/interactions/feed_items/copy_to_hub.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -module FeedItems - class CopyToHub < ActiveInteraction::Base - object :feed_item - object :hub - object :current_user, class: User - - def execute - feed_item.feeds << current_user.get_default_bookmarking_bookmark_collection_for(hub.id) - feed_item.save! - end - end -end diff --git a/app/interactions/feed_items/move_to_hub.rb b/app/interactions/feed_items/move_to_hub.rb deleted file mode 100644 index 99f87b9c..00000000 --- a/app/interactions/feed_items/move_to_hub.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -module FeedItems - # Return a collection of users of tag filters applied to the feed item - class MoveToHub < ActiveInteraction::Base - object :feed_item - object :from_hub_feed, class: HubFeed - object :to_hub, class: Hub - object :current_user, class: User - - def execute - feed_item.feeds -= [from_hub_feed.feed] - feed_item.feeds << current_user.get_default_bookmarking_bookmark_collection_for(to_hub.id) - feed_item.save! - end - end -end From d08887f0e2c4da7aca8b62e1d18d9519a15e132a Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Mon, 20 Aug 2018 13:54:28 +0000 Subject: [PATCH 43/70] Allow to remove tags directly from bookmarklet form --- app/assets/javascripts/application.js | 3 +++ app/assets/stylesheets/tagteam.scss | 4 ++++ app/helpers/tags_helper.rb | 28 ++++++++++++++++++++++++- app/views/feed_items/tag_list.html.haml | 2 +- 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 68adaf6a..ab88c0ff 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -305,6 +305,9 @@ cache: false, url: $.rootPath() + 'hubs/' + hubId + '/feed_items/' + feedItemId + '/tag_list', + data: { + allow_remove: true + }, success: function(tagList){ if ($(tagList).find('a').length > 0) { $('.feed-item-existing-tags').append( diff --git a/app/assets/stylesheets/tagteam.scss b/app/assets/stylesheets/tagteam.scss index 6ccd0d0e..7fda9d05 100644 --- a/app/assets/stylesheets/tagteam.scss +++ b/app/assets/stylesheets/tagteam.scss @@ -301,6 +301,10 @@ th, dt, b, strong, label { } } +.tag-remover { + vertical-align: middle; +} + .recommend-tag { background: $gray-lightish; color: #222222; diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb index 87e070af..6f72014c 100644 --- a/app/helpers/tags_helper.rb +++ b/app/helpers/tags_helper.rb @@ -22,7 +22,6 @@ def tag_display(tag, options = {}) options['data-hub-id'] = options[:hub].id hub_id = options[:hub].id options.delete(:hub) - end unless options[:hub_feed].blank? @@ -41,6 +40,29 @@ def tag_display(tag, options = {}) options.delete(:show_count) end + if options[:allow_remove].present? + if hub_filter_possible?(:delete, params, current_user) || feed_filter_possible?(params, current_user) || item_filter_possible?(params, current_user) + remove_tag_link = link_to_tag_filter( + fa_icon('times', class: 'tag-remover'), + 'Delete', + hub: @hub, + item: @feed_item, + tag: tag, + tag_list: @feed_item.applied_tags(@hub).map(&:name), + confirm: true, + confirm_message: 'Are you sure to remove the "' + tag.name + '" tag?' + ) + + options.delete(:allow_remove) + + return link_to( + tag.name, + hub_tag_show_path(hub_id, tag.name), + options + ) + remove_tag_link + end + end + link_to(tag.name, hub_tag_show_path(hub_id, tag.name), options) end @@ -98,6 +120,10 @@ def link_to_tag_filter(text, type, context = {}) add_class = 'hub_tag_filter' end + if context[:confirm].present? && context[:confirm_message].present? + options['data-confirm'] = context[:confirm_message] + end + options[:class] += ' ' + add_class link_to text, path, options diff --git a/app/views/feed_items/tag_list.html.haml b/app/views/feed_items/tag_list.html.haml index 1ec5083c..061e162c 100644 --- a/app/views/feed_items/tag_list.html.haml +++ b/app/views/feed_items/tag_list.html.haml @@ -1,3 +1,3 @@ .feed-item-tag-list - unless @feed_item.all_tags_on(@hub.tagging_key).empty? - = raw (@feed_item.all_tags_on(@hub.tagging_key) - @hub.deprecated_tags ).collect{|t| tag_display(t, hub: @hub, hub_feed_item: @feed_item) }.join(' ') + = raw (@feed_item.all_tags_on(@hub.tagging_key) - @hub.deprecated_tags ).collect{|t| tag_display(t, hub: @hub, hub_feed_item: @feed_item, allow_remove: params.dig(:allow_remove)) }.join(' ') From 704c335f2bed8217451f18c56bb33a6b2065009f Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Mon, 3 Sep 2018 10:48:34 +0000 Subject: [PATCH 44/70] Update tags-removing permissions --- app/helpers/tags_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/tags_helper.rb b/app/helpers/tags_helper.rb index 6f72014c..39d0fab8 100644 --- a/app/helpers/tags_helper.rb +++ b/app/helpers/tags_helper.rb @@ -41,7 +41,7 @@ def tag_display(tag, options = {}) end if options[:allow_remove].present? - if hub_filter_possible?(:delete, params, current_user) || feed_filter_possible?(params, current_user) || item_filter_possible?(params, current_user) + if hub_filter_possible?(:add, params, current_user) || feed_filter_possible?(params, current_user) || item_filter_possible?(params, current_user) remove_tag_link = link_to_tag_filter( fa_icon('times', class: 'tag-remover'), 'Delete', From ecde9ffa17e706fede974f3eac3523874faa57ad Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Mon, 3 Sep 2018 17:16:24 +0000 Subject: [PATCH 45/70] Fix tags tooltip menu --- app/assets/javascripts/tags/controls.js | 3 ++- vendor/assets/javascripts/jquery.bt.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/tags/controls.js b/app/assets/javascripts/tags/controls.js index f8c3743e..7758bbbc 100644 --- a/app/assets/javascripts/tags/controls.js +++ b/app/assets/javascripts/tags/controls.js @@ -28,7 +28,8 @@ $(function () { ajaxPath: $.rootPath() + 'hubs/' + hubId + '/tag_controls/?tag_id=' + tagId + '&hub_feed_id=' + hubFeedId + '&hub_feed_item_id=' + hubFeedItemId, trigger: 'none', closeWhenOthersOpen: true, - clickAnywhereToClose: true + clickAnywhereToClose: true, + positions: ['left', 'right', 'bottom', 'top'] }) $(this).btOn() diff --git a/vendor/assets/javascripts/jquery.bt.js b/vendor/assets/javascripts/jquery.bt.js index 48470938..7ffd22e8 100644 --- a/vendor/assets/javascripts/jquery.bt.js +++ b/vendor/assets/javascripts/jquery.bt.js @@ -413,7 +413,7 @@ jQuery.bt = {version: '0.9.5-rc1'}; } } } - + alert // horizontal (left) offset for the box var horiz = left + ((width - textOutWidth) * .5); // vertical (top) offset for the box From 7d923ebaac49a62e8b2395e95ec9310802e48cfc Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Wed, 5 Sep 2018 17:36:23 +0000 Subject: [PATCH 46/70] Removed uniqueness enforcement on supplemental tag. Resolves #15697. --- app/models/tag_filter.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/tag_filter.rb b/app/models/tag_filter.rb index 691f0531..f94d0ad7 100644 --- a/app/models/tag_filter.rb +++ b/app/models/tag_filter.rb @@ -16,7 +16,8 @@ class TagFilter < ApplicationRecord validates :tag_id, presence: true validates :scope_type, inclusion: { in: VALID_SCOPE_TYPES } validates :tag_id, uniqueness: { scope: [:scope_type, :scope_id, :type], - message: 'Filter conflicts with existing filter.' } + message: 'Filter conflicts with existing filter.' }, + unless: Proc.new { |tag_filter| Rails.logger.warn "stephie here: #{tag_filter.inspect}"; tag_filter.type == 'SupplementTagFilter' } attr_accessible :tag_id, :hub_id, :new_tag_id, :type, :scope_type, :scope_id, :applied From 98917321213c1c9f4145d97587a6c3b890b2a132 Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Mon, 3 Sep 2018 10:36:58 +0000 Subject: [PATCH 47/70] Add date facets to search --- app/assets/javascripts/application.js | 13 ------------- app/assets/javascripts/hubs/search.js | 9 +++++++++ app/assets/stylesheets/application.scss | 1 + app/assets/stylesheets/jquery_ui.scss | 4 ++++ app/assets/stylesheets/search.scss | 10 ++++++++++ app/controllers/hubs_controller.rb | 24 ++++++++++++++++++++++++ app/views/hubs/item_search.html.haml | 13 +++++++++++++ 7 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 app/assets/javascripts/hubs/search.js create mode 100644 app/assets/stylesheets/search.scss diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index ab88c0ff..77a5db90 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -871,19 +871,6 @@ $(document).ready(function(){ } }); - if($('.ui-widget-content').length > 0){ - $('#hub_search_form,#hub_tag_search_form').live({ - submit: function(e){ - e.preventDefault(); - $(this).ajaxSubmit({ - success: function(html){ - $('#hub_search_form').closest('.ui-widget-content').html(html); - } - }); - } - }); - } - $.observeDialogShow('.dialog-show'); $('a.add_item_source_to_custom_republished_feed,a.remove_item_source_from_custom_republished_feed').live({ diff --git a/app/assets/javascripts/hubs/search.js b/app/assets/javascripts/hubs/search.js new file mode 100644 index 00000000..ccee1c68 --- /dev/null +++ b/app/assets/javascripts/hubs/search.js @@ -0,0 +1,9 @@ +$(function () { + $('#hub_search_form .datepicker').datepicker({ + changeMonth: true, + changeYear: true, + changeDay: true, + yearRange: 'c-500', + dateFormat: 'mm/dd/yy' + }) +}) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index b60140f3..0c30b965 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -11,6 +11,7 @@ @import "footer"; @import "bookmarklet"; @import "hubs"; +@import "search"; @import "devise"; @import "jquery_ui"; @import "messages"; diff --git a/app/assets/stylesheets/jquery_ui.scss b/app/assets/stylesheets/jquery_ui.scss index 9bd65170..e51caba5 100644 --- a/app/assets/stylesheets/jquery_ui.scss +++ b/app/assets/stylesheets/jquery_ui.scss @@ -102,3 +102,7 @@ font-weight: 300; } } + +#ui-datepicker-div { + z-index: 100 !important; +} diff --git a/app/assets/stylesheets/search.scss b/app/assets/stylesheets/search.scss new file mode 100644 index 00000000..26d77dca --- /dev/null +++ b/app/assets/stylesheets/search.scss @@ -0,0 +1,10 @@ +.search-date-range-element { + display: inline-block; + width: 160px; + margin: 0 10px 10px 0; + vertical-align: bottom; + + input { + margin-bottom: 5px; + } +} diff --git a/app/controllers/hubs_controller.rb b/app/controllers/hubs_controller.rb index 8fd9eb0b..3d062be9 100644 --- a/app/controllers/hubs_controller.rb +++ b/app/controllers/hubs_controller.rb @@ -727,8 +727,32 @@ def item_search @filtered_params = params[:q].dup @modify_tag_filters.each { |mf| @filtered_params.gsub!(/#{mf.tag.name}/, mf.new_tag.name) } + date_params = [ + { + param: :last_updated, + param_from: :last_updated_from, + param_to: :last_updated_to, + }, + { + param: :date_published, + param_from: :date_published_from, + param_to: :date_published_to, + } + ] + @search = FeedItem.search do with :hub_ids, hub_id + date_params.each do |date_param| + if !params[date_param[:param_from]].present? && params[date_param[:param_to]].present? + with(date_param[:param]).less_than Date.strptime(params[date_param[:param_to]], '%m/%d/%Y').to_date + end + if params[date_param[:param_from]].present? && !params[date_param[:param_to]].present? + with(date_param[:param]).greater_than Date.strptime(params[date_param[:param_from]], '%m/%d/%Y').to_date + end + if params[date_param[:param_from]].present? && params[date_param[:param_to]].present? + with(date_param[:param]).between Date.strptime(params[date_param[:param_from]], '%m/%d/%Y').to_date..Date.strptime(params[date_param[:param_to]], '%m/%d/%Y').to_date + end + end paginate page: params[:page], per_page: get_per_page order_by(sort.to_sym, order.to_sym) unless params[:q].blank? diff --git a/app/views/hubs/item_search.html.haml b/app/views/hubs/item_search.html.haml index 4ed85c73..cd9b5415 100644 --- a/app/views/hubs/item_search.html.haml +++ b/app/views/hubs/item_search.html.haml @@ -16,6 +16,19 @@ = text_field_tag :q, params[:q], size: 50, placeholder: 'Search this hub', class: 'form-control' %span.input-group-btn = submit_tag('Search', class: 'btn btn-success') + .search-date-range-elements + .search-date-range-element + %span.form-label + %span.control-label + Date tagged + = text_field_tag :last_updated_from, params[:last_updated_from], class: 'datepicker form-control', placeholder: 'from' + = text_field_tag :last_updated_to, params[:last_updated_to], class: 'datepicker form-control', placeholder: 'to' + .search-date-range-element + %span.form-label + %span.control-label + Date published + = text_field_tag :date_published_from, params[:date_published_from], class: 'datepicker form-control', placeholder: 'from' + = text_field_tag :date_published_to, params[:date_published_to], class: 'datepicker form-control', placeholder: 'to' - unless @modify_tag_filters.any? || @search.results.empty? = link_to('Permalink', item_search_hub_path(@hub, q: params[:q])) From 308081a141ef809c70bfd4713e45609110d5d2c8 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Thu, 20 Sep 2018 14:54:07 +0000 Subject: [PATCH 48/70] Removed rogue alert statement. --- vendor/assets/javascripts/jquery.bt.js | 1 - 1 file changed, 1 deletion(-) diff --git a/vendor/assets/javascripts/jquery.bt.js b/vendor/assets/javascripts/jquery.bt.js index 7ffd22e8..c1465742 100644 --- a/vendor/assets/javascripts/jquery.bt.js +++ b/vendor/assets/javascripts/jquery.bt.js @@ -413,7 +413,6 @@ jQuery.bt = {version: '0.9.5-rc1'}; } } } - alert // horizontal (left) offset for the box var horiz = left + ((width - textOutWidth) * .5); // vertical (top) offset for the box From 22687d0473ce6d3c784a8b734bd68338af79fc80 Mon Sep 17 00:00:00 2001 From: Patrick Lewis Date: Fri, 5 Oct 2018 13:59:07 +0000 Subject: [PATCH 49/70] Pin minor versions in Gemfile --- Gemfile | 6 +++--- Gemfile.lock | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index ec85b9fe..e452eee3 100644 --- a/Gemfile +++ b/Gemfile @@ -36,8 +36,8 @@ gem 'sanitize', '~> 4.6' gem 'sass-rails', '~> 5.0' gem 'sidekiq', '~> 5.1' gem 'sinatra', '~> 2.0', require: false -gem 'skylight' -gem 'sprockets', '~> 3.7.2' +gem 'skylight', '~> 2.0' +gem 'sprockets', '~> 3.7' # TODO: resolve errors when upgrading sunspot_rails/sunspot_solr from these pinned versions gem 'sunspot_rails', '2.2.7' # @drg frozen gem 'sunspot_solr', '2.2.0' # @drg frozen @@ -54,7 +54,7 @@ group :development do gem 'haml_lint', '~> 0.27' gem 'listen', '~> 3.1' gem 'rubocop', '~> 0.57' - gem 'rubocop-rspec', '~> 1.26' + gem 'rubocop-rspec', '~> 1.27' gem 'spring', '~> 2.0' gem 'spring-commands-rspec', '~> 1.0' gem 'spring-commands-rubocop', '~> 0.2' diff --git a/Gemfile.lock b/Gemfile.lock index 095ea76e..26e847d2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -446,7 +446,7 @@ DEPENDENCIES redis-namespace (~> 1.6) rspec-rails (~> 3.7) rubocop (~> 0.57) - rubocop-rspec (~> 1.26) + rubocop-rspec (~> 1.27) sanitize (~> 4.6) sass-rails (~> 5.0) selenium-client (~> 1.2) @@ -454,11 +454,11 @@ DEPENDENCIES shoulda-matchers (~> 3.1) sidekiq (~> 5.1) sinatra (~> 2.0) - skylight + skylight (~> 2.0) spring (~> 2.0) spring-commands-rspec (~> 1.0) spring-commands-rubocop (~> 0.2) - sprockets (~> 3.7.2) + sprockets (~> 3.7) sqlite3 (~> 1.3) sunspot_rails (= 2.2.7) sunspot_solr (= 2.2.0) @@ -477,4 +477,4 @@ RUBY VERSION ruby 2.4.1p111 BUNDLED WITH - 1.16.2 + 1.16.6 From 19eb67a67a9962606e1c729e4aa333ac4981dad9 Mon Sep 17 00:00:00 2001 From: Gaurav Soni Date: Wed, 11 Apr 2018 15:54:27 +0530 Subject: [PATCH 50/70] Add recaptcha to contact page --- Gemfile | 1 + Gemfile.lock | 3 +++ app/assets/javascripts/application.js | 1 + app/controllers/hubs_controller.rb | 5 +++++ app/helpers/hubs_helper.rb | 2 ++ app/views/hubs/contact.html.haml | 4 +++- config/initializers/recaptcha.rb | 6 ++++++ 7 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 config/initializers/recaptcha.rb diff --git a/Gemfile b/Gemfile index e452eee3..fb20acab 100644 --- a/Gemfile +++ b/Gemfile @@ -31,6 +31,7 @@ gem 'protected_attributes_continued', '~> 1.3' # TODO: switch to strong paramete gem 'pundit', '~> 1.1' gem 'rails', '~> 5.1.0' # @drg frozen gem 'rails-observers', '~> 0.1' +gem 'recaptcha' gem 'redis-namespace', '~> 1.6' gem 'sanitize', '~> 4.6' gem 'sass-rails', '~> 5.0' diff --git a/Gemfile.lock b/Gemfile.lock index 26e847d2..ef1652f9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -254,6 +254,8 @@ GEM rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) + recaptcha (4.12.0) + json redis (4.0.1) redis-namespace (1.6.0) redis (>= 3.0.4) @@ -443,6 +445,7 @@ DEPENDENCIES pundit-matchers (~> 1.6) rails (~> 5.1.0) rails-observers (~> 0.1) + recaptcha redis-namespace (~> 1.6) rspec-rails (~> 3.7) rubocop (~> 0.57) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 77a5db90..8d1ad10e 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -54,6 +54,7 @@ modal: true }).dialog({modal: true, width: 700, height: 'auto'}).dialog('open'); } + grecaptcha.reset(); }, initPerPage: function(){ $('.per_page_selector').val($.cookie('per_page') || 25); diff --git a/app/controllers/hubs_controller.rb b/app/controllers/hubs_controller.rb index 3d062be9..b3855f66 100644 --- a/app/controllers/hubs_controller.rb +++ b/app/controllers/hubs_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true # A Hub is the base unit of organization for TagTeam. Please see README_FOR_APP for more details on how everything fits together. class HubsController < ApplicationController + include Recaptcha::Verify + caches_action :index, :items, :show, :search, :by_date, :retrievals, :taggers, :meta, unless: proc { |_c| current_user || params[:no_cache] == 'true' }, expires_in: Tagteam::Application.config.default_action_cache_time, cache_path: proc { Digest::MD5.hexdigest(request.fullpath + '&per_page=' + get_per_page) } @@ -137,6 +139,9 @@ def list def request_rights breadcrumbs.add @hub, hub_path(@hub) @errors = '' + + @errors += 'reCAPTCHA verification failed
' unless verify_recaptcha + if params[:contact][:email].nil? || params[:contact][:email] !~ /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i @errors += 'Email address is invalid
' end diff --git a/app/helpers/hubs_helper.rb b/app/helpers/hubs_helper.rb index 9dc8de86..3264700b 100644 --- a/app/helpers/hubs_helper.rb +++ b/app/helpers/hubs_helper.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true module HubsHelper + include Recaptcha::ClientHelper + def limit_html(html, limit) require 'nokogiri' html = html[0..limit] diff --git a/app/views/hubs/contact.html.haml b/app/views/hubs/contact.html.haml index 7b27aa6a..96808792 100644 --- a/app/views/hubs/contact.html.haml +++ b/app/views/hubs/contact.html.haml @@ -13,7 +13,7 @@ = f.inputs do = f.input :name, required: false = f.input :email, input_html: { value: (current_user.blank? ? '' : current_user.email) } - = f.input :reason, required: false, as: :select, collection: [%w(Question question), + = f.input :reason, required: false, as: :select, collection: [%w[Question question], ['Bug report', 'bug'], ['Broken feed', 'broken feed'], ['Feed suggestion', 'feed suggestion'], @@ -34,6 +34,8 @@ 'Input feed manager', 'Statistics viewer'] = f.input :message, as: :text, input_html: { rows: 8 } + = recaptcha_tags + %br = f.actions do = f.action :submit, as: :button, label: 'Submit', button_html: { class: 'btn btn-primary' } :javascript diff --git a/config/initializers/recaptcha.rb b/config/initializers/recaptcha.rb new file mode 100644 index 00000000..26097777 --- /dev/null +++ b/config/initializers/recaptcha.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +Recaptcha.configure do |config| + config.site_key = ENV['RECAPTCHA_SITE_KEY'] + config.secret_key = ENV['RECAPTCHA_SECRET_KEY'] +end From 7ce4d301ba2edb7df91a2c6245353d298599e807 Mon Sep 17 00:00:00 2001 From: Gaurav Soni Date: Fri, 20 Apr 2018 14:46:51 +0530 Subject: [PATCH 51/70] Add test case to verify the recaptch --- Gemfile | 1 + Gemfile.lock | 5 +++ .../documentations_controller_spec.rb | 1 + spec/controllers/hubs_controller_spec.rb | 43 +++++++++++++++++++ 4 files changed, 50 insertions(+) create mode 100644 spec/controllers/hubs_controller_spec.rb diff --git a/Gemfile b/Gemfile index fb20acab..308c792f 100644 --- a/Gemfile +++ b/Gemfile @@ -81,6 +81,7 @@ group :test do gem 'headless', '~> 2.3' gem 'launchy', '~> 2.4' gem 'pundit-matchers', '~> 1.6' + gem 'rails-controller-testing', '~> 1.0.2' gem 'selenium-client', '~> 1.2' gem 'selenium-webdriver', '~> 3.12' gem 'shoulda-matchers', '~> 3.1' diff --git a/Gemfile.lock b/Gemfile.lock index ef1652f9..b5bf98d2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -235,6 +235,10 @@ GEM bundler (>= 1.3.0) railties (= 5.1.6) sprockets-rails (>= 2.0.0) + rails-controller-testing (1.0.2) + actionpack (~> 5.x, >= 5.0.1) + actionview (~> 5.x, >= 5.0.1) + activesupport (~> 5.x) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) @@ -444,6 +448,7 @@ DEPENDENCIES pundit (~> 1.1) pundit-matchers (~> 1.6) rails (~> 5.1.0) + rails-controller-testing (~> 1.0.2) rails-observers (~> 0.1) recaptcha redis-namespace (~> 1.6) diff --git a/spec/controllers/documentations_controller_spec.rb b/spec/controllers/documentations_controller_spec.rb index 0f805944..5edc8a1b 100644 --- a/spec/controllers/documentations_controller_spec.rb +++ b/spec/controllers/documentations_controller_spec.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'rails_helper' RSpec.describe DocumentationsController, type: :controller do diff --git a/spec/controllers/hubs_controller_spec.rb b/spec/controllers/hubs_controller_spec.rb new file mode 100644 index 00000000..b69e3ad8 --- /dev/null +++ b/spec/controllers/hubs_controller_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe HubsController, type: :controller do + let(:current_user) { create(:user) } + let(:hub) { create(:hub, :owned) } + + context 'contact action' do + it 'available to sign-in user' do + sign_in current_user + get :contact, params: { id: hub.id } + + expect(response).to render_template('contact') + end + + it 'available to non sign-in user' do + get :contact, params: { id: hub.id } + + expect(response).to render_template('contact') + end + end + + context 'request_rights action' do + it 'contact to hub by filling captcha' do + allow_any_instance_of(Recaptcha::Verify).to receive(:verify_recaptcha).and_return(true) + + post :request_rights, params: { id: hub.id, contact: { email: 'tester@endpoint.com', message: 'this is a test message', rights: ['rights'] } } + + expect(response).to be_success + expect(assigns(:errors)).to be_empty + end + + it 'contact to hub without filling captcha' do + allow_any_instance_of(Recaptcha::Verify).to receive(:verify_recaptcha).and_return(false) + + post :request_rights, params: { id: hub.id, contact: { email: 'tester@endpoint.com', message: 'this is a test message', rights: ['rights'] } } + + expect(response.status).to be(406) + expect(assigns(:errors)).to include('reCAPTCHA verification failed') + end + end +end From 87afe13e54cd951e39c2bc1fe2c1c5555afa18fa Mon Sep 17 00:00:00 2001 From: Patrick Lewis Date: Fri, 5 Oct 2018 13:40:00 +0000 Subject: [PATCH 52/70] Pin minor versions in Gemfile --- Gemfile | 4 ++-- Gemfile.lock | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 308c792f..ca744d18 100644 --- a/Gemfile +++ b/Gemfile @@ -31,7 +31,7 @@ gem 'protected_attributes_continued', '~> 1.3' # TODO: switch to strong paramete gem 'pundit', '~> 1.1' gem 'rails', '~> 5.1.0' # @drg frozen gem 'rails-observers', '~> 0.1' -gem 'recaptcha' +gem 'recaptcha', '~> 4.12' gem 'redis-namespace', '~> 1.6' gem 'sanitize', '~> 4.6' gem 'sass-rails', '~> 5.0' @@ -81,7 +81,7 @@ group :test do gem 'headless', '~> 2.3' gem 'launchy', '~> 2.4' gem 'pundit-matchers', '~> 1.6' - gem 'rails-controller-testing', '~> 1.0.2' + gem 'rails-controller-testing', '~> 1.0' gem 'selenium-client', '~> 1.2' gem 'selenium-webdriver', '~> 3.12' gem 'shoulda-matchers', '~> 3.1' diff --git a/Gemfile.lock b/Gemfile.lock index b5bf98d2..cf4684f2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -448,9 +448,9 @@ DEPENDENCIES pundit (~> 1.1) pundit-matchers (~> 1.6) rails (~> 5.1.0) - rails-controller-testing (~> 1.0.2) + rails-controller-testing (~> 1.0) rails-observers (~> 0.1) - recaptcha + recaptcha (~> 4.12) redis-namespace (~> 1.6) rspec-rails (~> 3.7) rubocop (~> 0.57) From f5d402aa1b6d52afcd89d150f82fd48adb428fda Mon Sep 17 00:00:00 2001 From: Patrick Lewis Date: Fri, 5 Oct 2018 13:51:31 +0000 Subject: [PATCH 53/70] Add dotenv-rails --- Gemfile | 1 + Gemfile.lock | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Gemfile b/Gemfile index ca744d18..e59caec7 100644 --- a/Gemfile +++ b/Gemfile @@ -14,6 +14,7 @@ gem 'bootstrap-sass', '~> 3.3' gem 'breadcrumbs', '~> 0.1' gem 'browser', '~> 2.5' gem 'devise', '~> 4.4' +gem 'dotenv-rails', '~> 2.5' gem 'exception_notification', '~> 4.2' gem 'feed-abstract', '0.0.15' gem 'font-awesome-rails', '~> 4.7' diff --git a/Gemfile.lock b/Gemfile.lock index cf4684f2..d07a5006 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -104,6 +104,10 @@ GEM responders warden (~> 1.2.3) diff-lcs (1.3) + dotenv (2.5.0) + dotenv-rails (2.5.0) + dotenv (= 2.5.0) + railties (>= 3.2, < 6.0) drg (1.5.2) bundler (~> 1.10) highline (~> 1.6) @@ -421,6 +425,7 @@ DEPENDENCIES capybara-webkit (~> 1.15) database_cleaner (~> 1.7) devise (~> 4.4) + dotenv-rails (~> 2.5) drg (~> 1.5) exception_notification (~> 4.2) factory_bot_rails (~> 4.10) From 27bc93ea4f590e69c2682eca4c895e561487a0f0 Mon Sep 17 00:00:00 2001 From: Patrick Lewis Date: Fri, 5 Oct 2018 14:10:13 +0000 Subject: [PATCH 54/70] Add dev environment reCAPTCHA keys --- .env.development | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .env.development diff --git a/.env.development b/.env.development new file mode 100644 index 00000000..8483eaca --- /dev/null +++ b/.env.development @@ -0,0 +1,2 @@ +RECAPTCHA_SITE_KEY='6Leqp3MUAAAAALYR362-o36MbA_jTWgLfZgEifPj' +RECAPTCHA_SECRET_KEY='6Leqp3MUAAAAAO4ZQlXOl1JVHawn0EoN5oHIC_PV' From e762aa3ec7b32aad97c47cc44599ff98384b9eeb Mon Sep 17 00:00:00 2001 From: Patrick Lewis Date: Fri, 5 Oct 2018 14:12:18 +0000 Subject: [PATCH 55/70] Add production dotenv file to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 8f278ac9..084350cd 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ redis/ spec/examples.txt add_test_mappings.vim run-test-listener.sh +.env.production From 3bb4ae05aa75082c4b2224a26773eb7a4c88b27c Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Fri, 5 Oct 2018 19:29:09 +0200 Subject: [PATCH 56/70] Improve date facets in search view --- app/assets/javascripts/hubs/search.js | 27 ++++++++++++++++++- app/views/hubs/_search_form.html.haml | 2 +- .../custom-theme/jquery-ui-1.9.2.custom.css | 16 +++++------ 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/hubs/search.js b/app/assets/javascripts/hubs/search.js index ccee1c68..8b62dad2 100644 --- a/app/assets/javascripts/hubs/search.js +++ b/app/assets/javascripts/hubs/search.js @@ -4,6 +4,31 @@ $(function () { changeYear: true, changeDay: true, yearRange: 'c-500', - dateFormat: 'mm/dd/yy' + dateFormat: 'mm/dd/yy', + onChangeMonthYear: function (year, month, inst) { + var dayToday = new Date().getDate() + var date = $(this).val() + if (inst.currentDay !== 0) { + currentDay = inst.currentDay + } else { + currentDay = dayToday + } + var newDate = month + '/' + currentDay + '/' + year + var newDateObject = new Date(newDate) + $(this).val($.datepicker.formatDate('mm/dd/yy', newDateObject)) + $(this).datepicker('setDate', newDateObject) + } }) + + var searchForm = $('#hub_search_form') + // Make sure it won't submit empty fields + searchForm.submit(function () { + $(this).find(':input').filter(function () { + return !this.value + }).attr('disabled', 'disabled') + + return true + }) + // Un-disable form fields when page loads, in case they click back after submission + searchForm.find(':input').prop('disabled', false) }) diff --git a/app/views/hubs/_search_form.html.haml b/app/views/hubs/_search_form.html.haml index b5a2ce4d..39c509a4 100644 --- a/app/views/hubs/_search_form.html.haml +++ b/app/views/hubs/_search_form.html.haml @@ -1,4 +1,4 @@ -= form_tag(item_search_hub_path(@hub), :method => :get) do += form_tag(item_search_hub_path(@hub), :method => :get, enforce_utf8: false) do .input-group = text_field_tag :q, params[:q], size: 14, placeholder: 'Search', class: 'form-control' %span.input-group-btn diff --git a/vendor/assets/stylesheets/custom-theme/jquery-ui-1.9.2.custom.css b/vendor/assets/stylesheets/custom-theme/jquery-ui-1.9.2.custom.css index bf0c02de..6f6d24a0 100644 --- a/vendor/assets/stylesheets/custom-theme/jquery-ui-1.9.2.custom.css +++ b/vendor/assets/stylesheets/custom-theme/jquery-ui-1.9.2.custom.css @@ -261,14 +261,14 @@ body .ui-tooltip { border-width: 2px; } ----------------------------------*/ /* states and images */ -.ui-icon { width: 16px; height: 16px; background-image: url("images/ui-icons_8ec63f_256x240.png"); } -.ui-widget-content .ui-icon {background-image: url("images/ui-icons_8ec63f_256x240.png"); } -.ui-widget-header .ui-icon {background-image: url("images/ui-icons_ffffff_256x240.png"); } -.ui-state-default .ui-icon { background-image: url("images/ui-icons_8ec63f_256x240.png"); } -.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url("images/ui-icons_8ec63f_256x240.png"); } -.ui-state-active .ui-icon {background-image: url("images/ui-icons_8ec63f_256x240.png"); } -.ui-state-highlight .ui-icon {background-image: url("images/ui-icons_151c21_256x240.png"); } -.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url("images/ui-icons_a33724_256x240.png"); } +.ui-icon { width: 16px; height: 16px; background-image: url("../images/ui-icons_8ec63f_256x240.png"); } +.ui-widget-content .ui-icon {background-image: url("../images/ui-icons_8ec63f_256x240.png"); } +.ui-widget-header .ui-icon {background-image: url("../images/ui-icons_ffffff_256x240.png"); } +.ui-state-default .ui-icon { background-image: url("../images/ui-icons_8ec63f_256x240.png"); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url("../images/ui-icons_8ec63f_256x240.png"); } +.ui-state-active .ui-icon {background-image: url("../images/ui-icons_8ec63f_256x240.png"); } +.ui-state-highlight .ui-icon {background-image: url("../images/ui-icons_151c21_256x240.png"); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url("../images/ui-icons_a33724_256x240.png"); } /* positioning */ .ui-icon-carat-1-n { background-position: 0 0; } From 7b4ec94d1b7cc310e0284b90fd8f5559b0f41828 Mon Sep 17 00:00:00 2001 From: J S Diaz Date: Thu, 11 Oct 2018 18:11:15 -0400 Subject: [PATCH 57/70] added supplemental rules per issue 3739 --- public/robots.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/robots.txt b/public/robots.txt index bf5c95ca..cd45041b 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -5,3 +5,9 @@ # Disallow: / User-Agent: * Disallow: /hub_feeds/*/feed_items/*/related +Disallow: /hubs/*/feed_items/*/tag_filters +Disallow: /hubs/*/tag/* +Disallow: /hubs/*/by_date/ +Disallow: /hubs/*/items.atom +Disallow: /hubs/*/items.json +Disallow: /hubs/*/items.rss From 80718c7502a7df4abefcc1f50c14fb7b296182ae Mon Sep 17 00:00:00 2001 From: J S Diaz Date: Thu, 18 Oct 2018 11:12:23 -0400 Subject: [PATCH 58/70] adding AGPL license file --- LICENSE | 661 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 661 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..dba13ed2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. From 765d9dbaf151a386651f782a8a214487d02cf8de Mon Sep 17 00:00:00 2001 From: Patrick Lewis Date: Tue, 23 Oct 2018 14:37:51 +0000 Subject: [PATCH 59/70] Update error messaging for tag filter infinite loops Resolves: #15682 --- app/models/modify_tag_filter.rb | 2 +- config/locales/en.yml | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/modify_tag_filter.rb b/app/models/modify_tag_filter.rb index 94b52799..4d86efd0 100644 --- a/app/models/modify_tag_filter.rb +++ b/app/models/modify_tag_filter.rb @@ -6,7 +6,7 @@ class ModifyTagFilter < TagFilter validate :new_tag_id do terminating_tag_filter = ModifyTagFilter.find_recursive(self.hub_id, self.new_tag.name) if terminating_tag_filter.present? && terminating_tag_filter.new_tag.name == self.tag.name - errors.add(:new_tag_id, " can't create an infinite loop of tag filters") + errors.add(:base, "New filter can't create an infinite loop of tag filters") end if new_tag_id == tag_id errors.add(:new_tag_id, " can't be the same as the original tag") diff --git a/config/locales/en.yml b/config/locales/en.yml index 56adb591..f147cbb5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -47,6 +47,10 @@ en: error_when_adding: one: "Could not add that tag filter." other: "Errors occurred when adding some tag filters." + errors_when_adding: + one: "Could not add that tag filter." + other: "Errors occurred when adding some tag filters." + will_paginate: page_entries_info: From 8641e5724094289fd6692c52c50029757cd32a57 Mon Sep 17 00:00:00 2001 From: Steph Skardal Date: Fri, 2 Nov 2018 16:22:07 +0000 Subject: [PATCH 60/70] RT #15391: Fix on double confirmation emails being sent. --- app/controllers/admin/user_approvals_controller.rb | 3 +-- app/controllers/users/registrations_controller.rb | 2 +- app/mailers/admin/user_approvals_mailer.rb | 6 ------ app/models/user.rb | 5 ----- .../user_approvals_mailer/notify_user_of_approval.html.haml | 1 - .../user_approvals_mailer/notify_user_of_approval.text.haml | 1 - 6 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 app/views/admin/user_approvals_mailer/notify_user_of_approval.html.haml delete mode 100644 app/views/admin/user_approvals_mailer/notify_user_of_approval.text.haml diff --git a/app/controllers/admin/user_approvals_controller.rb b/app/controllers/admin/user_approvals_controller.rb index 9fcb7e0b..fe25b414 100644 --- a/app/controllers/admin/user_approvals_controller.rb +++ b/app/controllers/admin/user_approvals_controller.rb @@ -21,8 +21,7 @@ def approve flash[:notice] = "Access for #{@user.email} has been approved." @user.update!(approved: true) - - UserApprovalsMailer.notify_user_of_approval(@user).deliver_later + @user.send_confirmation_instructions redirect_to action: :index end diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb index 2f7ec96b..35299058 100644 --- a/app/controllers/users/registrations_controller.rb +++ b/app/controllers/users/registrations_controller.rb @@ -16,7 +16,7 @@ def create private def notify_for_resource?(resource) - resource.persisted? && !resource.edu_email? + resource.persisted? && !resource.approved end def set_setting diff --git a/app/mailers/admin/user_approvals_mailer.rb b/app/mailers/admin/user_approvals_mailer.rb index 72c65b92..2fe8efa9 100644 --- a/app/mailers/admin/user_approvals_mailer.rb +++ b/app/mailers/admin/user_approvals_mailer.rb @@ -13,12 +13,6 @@ def notify_admin_of_signup(unapproved_user) ) end - def notify_user_of_approval(user) - @user = user - - mail(to: user.email, subject: 'TagTeam: Signup approved') - end - def notify_user_of_denial(email) mail(to: email, subject: 'TagTeam: Signup denied') end diff --git a/app/models/user.rb b/app/models/user.rb index 1555354f..44eef736 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -31,11 +31,6 @@ class User < ApplicationRecord self.approved = false end end - after_update do - if self.approved_changed? && self.approved - self.send_confirmation_instructions - end - end scope :unapproved, -> { where(approved: false) } scope :superadmin, -> { joins(:roles).where('roles.name = ?', :superadmin).distinct } diff --git a/app/views/admin/user_approvals_mailer/notify_user_of_approval.html.haml b/app/views/admin/user_approvals_mailer/notify_user_of_approval.html.haml deleted file mode 100644 index 2c914740..00000000 --- a/app/views/admin/user_approvals_mailer/notify_user_of_approval.html.haml +++ /dev/null @@ -1 +0,0 @@ -%p Your signup has been approved by a TagTeam administrator and you can now log in to the site: #{link_to(root_url, root_url)} diff --git a/app/views/admin/user_approvals_mailer/notify_user_of_approval.text.haml b/app/views/admin/user_approvals_mailer/notify_user_of_approval.text.haml deleted file mode 100644 index afa692ad..00000000 --- a/app/views/admin/user_approvals_mailer/notify_user_of_approval.text.haml +++ /dev/null @@ -1 +0,0 @@ -Your signup has been approved by a TagTeam administrator and you can now log in to the site: #{root_url} From 99baac2ba76ed141cc8e38f63f1a0388c94599e3 Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Thu, 30 May 2019 17:34:16 +0200 Subject: [PATCH 61/70] Dockerize it --- .gitignore | 3 +++ Dockerfile | 21 ++++++++++++++++++++ docker-compose.yml | 17 ++++++++++++++++ docker/database.yml | 48 +++++++++++++++++++++++++++++++++++++++++++++ docker/init_db.sql | 3 +++ docker/start_dev.sh | 10 ++++++++++ 6 files changed, 102 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.yml create mode 100644 docker/database.yml create mode 100644 docker/init_db.sql create mode 100755 docker/start_dev.sh diff --git a/.gitignore b/.gitignore index 084350cd..4c2dbf58 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ spec/examples.txt add_test_mappings.vim run-test-listener.sh .env.production + +# Docker +/docker/postgres_data diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..0bb8b44f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +FROM ruby:2.4.1 + +# Debian issue in Docker +# https://superuser.com/a/1423685 +RUN printf "deb http://archive.debian.org/debian/ jessie main\ndeb-src http://archive.debian.org/debian/ jessie main\ndeb http://security.debian.org jessie/updates main\ndeb-src http://security.debian.org jessie/updates main" > /etc/apt/sources.list +RUN apt-get update && apt-get install -y build-essential nodejs bash \ + postgresql tzdata git sqlite3 libsqlite3-dev default-jre \ + g++ qt5-default libqt5webkit5-dev gstreamer1.0-plugins-base \ + gstreamer1.0-tools gstreamer1.0-x && \ + gem install mailcatcher --no-ri --no-rdoc + +RUN mkdir /app +WORKDIR /app + +COPY . . +COPY docker/start_dev.sh . +# capybara-webkit needs it +ENV PATH /usr/lib/qt5/bin:$PATH +RUN bundle install + +CMD ["/bin/bash", "start_dev.sh"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..1de90e37 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: '2' + +services: + postgres: + image: 'postgres:10.3-alpine' + volumes: + - './docker/postgres_data:/var/lib/postgresql/data' + - './docker/init_db.sql:/docker-entrypoint-initdb.d/init.sql' + + app: + depends_on: + - 'postgres' + build: . + ports: + - '3000:3000' + env_file: + - '.env.development' diff --git a/docker/database.yml b/docker/database.yml new file mode 100644 index 00000000..f7358c5f --- /dev/null +++ b/docker/database.yml @@ -0,0 +1,48 @@ +# PostgreSQL. Versions 7.4 and 8.x are supported. +# +# Install the pg driver: +# gem install pg +# On Mac OS X with macports: +# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config +# On Windows: +# gem install pg +# Choose the win32 build. +# Install PostgreSQL and put its /bin directory on your path. +development: + adapter: postgresql + encoding: unicode + host: postgres + user: docker + password: docker + database: tagteam_development + pool: 5 + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # The server defaults to notice. + #min_messages: warning + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + adapter: postgresql + encoding: unicode + database: tagteam_test + pool: 5 + +production: + adapter: postgresql + encoding: unicode + database: tagteam_production + pool: 5 diff --git a/docker/init_db.sql b/docker/init_db.sql new file mode 100644 index 00000000..ac71bec3 --- /dev/null +++ b/docker/init_db.sql @@ -0,0 +1,3 @@ +CREATE USER docker WITH ENCRYPTED PASSWORD 'docker'; +CREATE DATABASE tagteam_development; +GRANT ALL PRIVILEGES ON DATABASE tagteam_development TO docker; diff --git a/docker/start_dev.sh b/docker/start_dev.sh new file mode 100755 index 00000000..10bdc4de --- /dev/null +++ b/docker/start_dev.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +touch log/sidekiq.log +cp -n docker/database.yml config/database.yml +cp -n config/tagteam.yml.example config/tagteam.yml + +bundle exec rake db:migrate +bundle exec rake sunspot:solr:start +sidekiq -C config/sidekiq.yml -L log/sidekiq.log -d -e development +rails server --binding 0.0.0.0 --port 3000 From 97849fd6f281b54405f018c60e165b5c2b95333e Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Fri, 31 May 2019 22:46:28 +0200 Subject: [PATCH 62/70] Update README with Docker info --- README.rdoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.rdoc b/README.rdoc index 76767617..3f04da3a 100644 --- a/README.rdoc +++ b/README.rdoc @@ -48,6 +48,14 @@ TagTeam is a fairly traditional rails 5.0 app that needs a java daemon and redis * rake sunspot:solr:start * Start sidekiq (bundle exec sidekiq) to run scheduled tasks +== Development + +The only thing you need is Docker (https://docs.docker.com/install/). + +* Install Docker +* Run "docker-compose up" and wait until it sets up everything +* The app will be available on "http://localhost:8888" + == Caching Most of TagTeam is action cached for non-authenticated users, and sometimes for users without administrative rights. The default cache time is 15 minutes, and the file-based cache is expired via the cron jobs articulated above. Feel free to switch the caching backend, but honestly if you're not clustering and have a moderately fast disk subsystem there's almost no point. "rake tmp:clear" can be your cache-clearing sledgehammer, of course. From 3697fde04486f748545c256805ce222603f1c63c Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Mon, 3 Jun 2019 13:50:27 +0200 Subject: [PATCH 63/70] Run rails server and sidekiq with bundler in Docker --- docker/start_dev.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/start_dev.sh b/docker/start_dev.sh index 10bdc4de..074d8283 100755 --- a/docker/start_dev.sh +++ b/docker/start_dev.sh @@ -6,5 +6,5 @@ cp -n config/tagteam.yml.example config/tagteam.yml bundle exec rake db:migrate bundle exec rake sunspot:solr:start -sidekiq -C config/sidekiq.yml -L log/sidekiq.log -d -e development -rails server --binding 0.0.0.0 --port 3000 +bundle exec sidekiq -C config/sidekiq.yml -L log/sidekiq.log -d -e development +bundle exec rails server --binding 0.0.0.0 --port 3000 From 5984a9c2f30cf808c60993422e8998c24809ea6e Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Mon, 3 Jun 2019 14:03:10 +0200 Subject: [PATCH 64/70] Update server port in README --- README.rdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rdoc b/README.rdoc index 3f04da3a..da6dbe96 100644 --- a/README.rdoc +++ b/README.rdoc @@ -54,7 +54,7 @@ The only thing you need is Docker (https://docs.docker.com/install/). * Install Docker * Run "docker-compose up" and wait until it sets up everything -* The app will be available on "http://localhost:8888" +* The app will be available on "http://localhost:3000" == Caching From 2793df1f14bafd8781754fcca88bc72a77c48701 Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Mon, 3 Jun 2019 14:11:07 +0200 Subject: [PATCH 65/70] Create test db and set up connection in Docker --- docker/database.yml | 3 +++ docker/init_db.sql | 1 + 2 files changed, 4 insertions(+) diff --git a/docker/database.yml b/docker/database.yml index f7358c5f..87b38640 100644 --- a/docker/database.yml +++ b/docker/database.yml @@ -38,6 +38,9 @@ development: test: adapter: postgresql encoding: unicode + host: postgres + user: docker + password: docker database: tagteam_test pool: 5 diff --git a/docker/init_db.sql b/docker/init_db.sql index ac71bec3..cdee0219 100644 --- a/docker/init_db.sql +++ b/docker/init_db.sql @@ -1,3 +1,4 @@ CREATE USER docker WITH ENCRYPTED PASSWORD 'docker'; CREATE DATABASE tagteam_development; +CREATE DATABASE tagteam_test; GRANT ALL PRIVILEGES ON DATABASE tagteam_development TO docker; From ecc4499631c9ccb877a74a342f3498b391545101 Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Mon, 3 Jun 2019 15:35:11 +0200 Subject: [PATCH 66/70] Mount Rails files as Docker volume --- Dockerfile | 8 ++------ docker-compose.yml | 2 ++ docker/start_dev.sh | 4 ++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0bb8b44f..8d11be29 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,10 +12,6 @@ RUN apt-get update && apt-get install -y build-essential nodejs bash \ RUN mkdir /app WORKDIR /app -COPY . . -COPY docker/start_dev.sh . -# capybara-webkit needs it -ENV PATH /usr/lib/qt5/bin:$PATH -RUN bundle install +COPY . /app -CMD ["/bin/bash", "start_dev.sh"] +CMD ["/bin/bash", "docker/start_dev.sh"] diff --git a/docker-compose.yml b/docker-compose.yml index 1de90e37..308232a8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,3 +15,5 @@ services: - '3000:3000' env_file: - '.env.development' + volumes: + - '.:/app' diff --git a/docker/start_dev.sh b/docker/start_dev.sh index 074d8283..61dd2335 100755 --- a/docker/start_dev.sh +++ b/docker/start_dev.sh @@ -1,5 +1,9 @@ #!/bin/bash +cd /app +# capybara-webkit needs it +export PATH="/usr/lib/qt5/bin:$PATH" +bundle install touch log/sidekiq.log cp -n docker/database.yml config/database.yml cp -n config/tagteam.yml.example config/tagteam.yml From 4703798fc648df53684c63d5aaf306e11a861fc4 Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Wed, 29 May 2019 14:45:04 -0400 Subject: [PATCH 67/70] Fix gems vulnerabilities --- Gemfile.lock | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index d07a5006..a5346451 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -91,13 +91,13 @@ GEM ffi (~> 1.0, >= 1.0.11) chronic (0.10.2) coderay (1.1.2) - concurrent-ruby (1.0.5) + concurrent-ruby (1.1.5) connection_pool (2.2.2) crack (0.4.3) safe_yaml (~> 1.0.0) crass (1.0.4) database_cleaner (1.7.0) - devise (4.4.3) + devise (4.6.2) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0, < 6.0) @@ -111,7 +111,7 @@ GEM drg (1.5.2) bundler (~> 1.10) highline (~> 1.6) - erubi (1.7.1) + erubi (1.8.0) erubis (2.7.0) exception_notification (4.2.2) actionmailer (>= 4.0, < 6) @@ -160,7 +160,7 @@ GEM haml (>= 4.0, < 6) nokogiri (>= 1.6.0) ruby_parser (~> 3.5) - i18n (1.0.1) + i18n (1.6.0) concurrent-ruby (~> 1.0) jaro_winkler (1.5.1) jquery-rails (4.3.3) @@ -176,21 +176,21 @@ GEM rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) ruby_dep (~> 1.2) - loofah (2.2.2) + loofah (2.2.3) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.0) mini_mime (>= 0.1.1) - method_source (0.9.0) + method_source (0.9.2) mini_mime (1.0.0) - mini_portile2 (2.3.0) + mini_portile2 (2.4.0) minitest (5.11.3) msgpack (1.2.4) multipart-post (2.0.0) mustermann (1.0.2) nio4r (2.3.1) - nokogiri (1.8.2) - mini_portile2 (~> 2.3.0) + nokogiri (1.10.3) + mini_portile2 (~> 2.4.0) nokogumbo (1.5.0) nokogiri non-stupid-digest-assets (1.0.9) @@ -222,10 +222,10 @@ GEM activesupport (>= 3.0.0) pundit-matchers (1.6.0) rspec-rails (>= 3.0.0) - rack (2.0.5) + rack (2.0.7) rack-protection (2.0.2) rack - rack-test (1.0.0) + rack-test (1.1.0) rack (>= 1.0, < 3) rails (5.1.6) actioncable (= 5.1.6) @@ -258,7 +258,7 @@ GEM thor (>= 0.18.1, < 2.0) rainbow (3.0.0) raindrops (0.19.0) - rake (12.3.1) + rake (12.3.2) rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) @@ -268,9 +268,9 @@ GEM redis-namespace (1.6.0) redis (>= 3.0.4) ref (2.0.0) - responders (2.4.0) - actionpack (>= 4.2.0, < 5.3) - railties (>= 4.2.0, < 5.3) + responders (2.4.1) + actionpack (>= 4.2.0, < 6.0) + railties (>= 4.2.0, < 6.0) rsolr (2.2.1) builder (>= 2.1.2) faraday (>= 0.9.0) @@ -305,7 +305,7 @@ GEM ruby_dep (1.5.0) ruby_parser (3.11.0) sexp_processor (~> 4.9) - rubyzip (1.2.1) + rubyzip (1.2.3) safe_yaml (1.0.4) sanitize (4.6.5) crass (~> 1.0.2) @@ -372,7 +372,7 @@ GEM therubyracer (0.12.3) libv8 (~> 3.16.14.15) ref - thor (0.20.0) + thor (0.20.3) thread_safe (0.3.6) tilt (2.0.8) tzinfo (1.2.5) @@ -384,8 +384,8 @@ GEM kgio (~> 2.6) raindrops (~> 0.7) vcr (4.0.0) - warden (1.2.7) - rack (>= 1.0) + warden (1.2.8) + rack (>= 2.0.6) web-console (3.6.2) actionview (>= 5.0) activemodel (>= 5.0) From 1712aa4c7b94468c3b8b39127cdcd0ac82542783 Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Tue, 4 Jun 2019 20:20:34 +0200 Subject: [PATCH 68/70] Fix specs --- app/models/add_tag_filter.rb | 8 +++++-- app/models/delete_tag_filter.rb | 8 +++++-- app/models/modify_tag_filter.rb | 8 +++++-- app/models/supplement_tag_filter.rb | 8 +++++-- app/models/tag_filter.rb | 2 +- spec/controllers/hubs_controller_spec.rb | 4 ++-- .../tag_filters_controller_spec.rb | 13 ++++++----- .../admin/user_approvals_mailer_spec.rb | 9 -------- spec/models/add_tag_filter_spec.rb | 4 ++-- spec/models/delete_tag_filter_spec.rb | 4 ++-- spec/models/modify_tag_filter_spec.rb | 6 ++--- spec/rails_helper.rb | 3 +++ spec/support/controller_helpers.rb | 11 +++++++++ spec/support/shared_tag_filter_examples.rb | 23 +++++++++---------- 14 files changed, 66 insertions(+), 45 deletions(-) create mode 100644 spec/support/controller_helpers.rb diff --git a/app/models/add_tag_filter.rb b/app/models/add_tag_filter.rb index 5a5b1f88..3416607c 100644 --- a/app/models/add_tag_filter.rb +++ b/app/models/add_tag_filter.rb @@ -2,8 +2,12 @@ class AddTagFilter < TagFilter def apply(item_ids = []) - items = item_ids.any? ? FeedItem.find(item_ids) : - scope.taggable_items + if item_ids.any? + item_ids &= scope.taggable_items.pluck(:id) + items = FeedItem.where(id: item_ids) + else + items = scope.taggable_items + end # This does not deal with duplicates deactivate_taggings!(items.map(&:id)) diff --git a/app/models/delete_tag_filter.rb b/app/models/delete_tag_filter.rb index 780dd6f7..31c37f37 100644 --- a/app/models/delete_tag_filter.rb +++ b/app/models/delete_tag_filter.rb @@ -1,8 +1,12 @@ # frozen_string_literal: true class DeleteTagFilter < TagFilter def apply(item_ids = []) - items = item_ids.any? ? FeedItem.where(id: item_ids).tagged_with(tag.name, on: hub.tagging_key) : - scope.taggable_items.tagged_with(tag.name, on: hub.tagging_key) + if item_ids.any? + item_ids &= scope.taggable_items.pluck(:id) + items = FeedItem.where(id: item_ids).tagged_with(tag.name, on: hub.tagging_key) + else + items = scope.taggable_items.tagged_with(tag.name, on: hub.tagging_key) + end deactivate_taggings!(items.map(&:id)) update_column(:applied, true) diff --git a/app/models/modify_tag_filter.rb b/app/models/modify_tag_filter.rb index 4d86efd0..917a8a5e 100644 --- a/app/models/modify_tag_filter.rb +++ b/app/models/modify_tag_filter.rb @@ -30,8 +30,12 @@ def apply(item_ids = []) end def apply_simple(item_ids = []) - items = item_ids.any? ? FeedItem.where(id: item_ids).tagged_with(tag.name, on: hub.tagging_key) : - scope.taggable_items.tagged_with(tag.name, on: hub.tagging_key) + if item_ids.any? + item_ids &= scope.taggable_items.pluck(:id) + items = FeedItem.where(id: item_ids).tagged_with(tag.name, on: hub.tagging_key) + else + items = scope.taggable_items.tagged_with(tag.name, on: hub.tagging_key) + end # This deactivates old and duplicate tags, which forces the cache to clear deactivate_taggings!(items.map(&:id)) diff --git a/app/models/supplement_tag_filter.rb b/app/models/supplement_tag_filter.rb index 27581199..d3a51eca 100644 --- a/app/models/supplement_tag_filter.rb +++ b/app/models/supplement_tag_filter.rb @@ -10,8 +10,12 @@ class SupplementTagFilter < TagFilter end def apply(item_ids = []) - items = item_ids.any? ? FeedItem.where(id: item_ids).tagged_with(tag.name, on: hub.tagging_key) : - scope.taggable_items.tagged_with(tag.name, on: hub.tagging_key) + if item_ids.any? + item_ids &= scope.taggable_items.pluck(:id) + items = FeedItem.where(id: item_ids) + else + items = scope.taggable_items + end # TODO: This can be done, but caching would need to be dealt with #values = items.map { |item| "(#{new_tag.id},#{item.id},'FeedItem',#{self.id},'TagFilter','#{hub.tagging_key}')" }.join(',') diff --git a/app/models/tag_filter.rb b/app/models/tag_filter.rb index f94d0ad7..4ba06ce7 100644 --- a/app/models/tag_filter.rb +++ b/app/models/tag_filter.rb @@ -113,7 +113,7 @@ def deactivate_taggings!(item_ids) def deactivates_taggings(item_ids) # Deactivates any taggings that are the same except in owner, and do not # deactivate own taggings. - return ActsAsTaggableOn::Tagging.where('1=2') if item_ids.empty? + return ActsAsTaggableOn::Tagging.none if item_ids.empty? ActsAsTaggableOn::Tagging .where(context: hub.tagging_key, tag_id: tag.id, diff --git a/spec/controllers/hubs_controller_spec.rb b/spec/controllers/hubs_controller_spec.rb index b69e3ad8..74fd71df 100644 --- a/spec/controllers/hubs_controller_spec.rb +++ b/spec/controllers/hubs_controller_spec.rb @@ -3,12 +3,12 @@ require 'rails_helper' RSpec.describe HubsController, type: :controller do - let(:current_user) { create(:user) } + let(:user) { create(:confirmed_user) } let(:hub) { create(:hub, :owned) } context 'contact action' do it 'available to sign-in user' do - sign_in current_user + stub_sign_in user get :contact, params: { id: hub.id } expect(response).to render_template('contact') diff --git a/spec/controllers/tag_filters_controller_spec.rb b/spec/controllers/tag_filters_controller_spec.rb index ade7a99e..613445fe 100644 --- a/spec/controllers/tag_filters_controller_spec.rb +++ b/spec/controllers/tag_filters_controller_spec.rb @@ -4,7 +4,7 @@ RSpec.describe TagFiltersController, type: :controller do let(:hub) { create(:hub, :owned) } - let(:other_user) { create(:user) } + let(:other_user) { create(:confirmed_user) } let(:tag_filter) { create(:tag_filter, scope: hub, hub: hub) } let(:feed_item) { create(:feed_item_from_feed) } @@ -16,7 +16,7 @@ end it 'hub owner should destroy the hub wide filter' do - sign_in(hub.owners.first) + stub_sign_in hub.owners.first delete :destroy, params: { hub_id: hub.id, id: tag_filter.id } expect(flash[:notice]).to eq 'Deleting that tag filter.' @@ -24,7 +24,7 @@ end it 'other user should not destroy the hub wide filter' do - sign_in(other_user) + stub_sign_in other_user delete :destroy, params: { hub_id: hub.id, id: tag_filter.id } expect(flash[:alert]).to eq 'You can\'t access that - sorry!' @@ -32,11 +32,12 @@ end it 'user should delete the feed_item level filter' do - sign_in(hub.owners.first) + stub_sign_in hub.owners.first item_level_tag_filter = create(:tag_filter, scope: feed_item, hub: hub) delete :destroy, params: { hub_id: hub.id, feed_item_id: feed_item.id, id: item_level_tag_filter.id, format: :js } - expect(flash[:notice]).to eq 'Deleting that tag filter.' - expect(response).to render_template('hub_feed_item_tag_filters/destroy') + + expect(flash[:notice]).to eq 'Deleted that tag filter.' + expect(response).to redirect_to(hub_feed_item_tag_filters_path(hub.id, feed_item.id)) end end diff --git a/spec/mailers/admin/user_approvals_mailer_spec.rb b/spec/mailers/admin/user_approvals_mailer_spec.rb index 55741011..2dcaa8d3 100644 --- a/spec/mailers/admin/user_approvals_mailer_spec.rb +++ b/spec/mailers/admin/user_approvals_mailer_spec.rb @@ -16,15 +16,6 @@ module Admin end end - describe '#notify_user_of_approval' do - let(:user) { create(:user) } - let(:mail) { described_class.notify_user_of_approval(user) } - - it 'sets the recipient' do - expect(mail.to).to contain_exactly(user.email) - end - end - describe '#notify_user_of_denial' do let(:user) { create(:user) } let(:mail) { described_class.notify_user_of_denial(user.email) } diff --git a/spec/models/add_tag_filter_spec.rb b/spec/models/add_tag_filter_spec.rb index 29144179..840f1e0b 100644 --- a/spec/models/add_tag_filter_spec.rb +++ b/spec/models/add_tag_filter_spec.rb @@ -47,7 +47,7 @@ def new_add_filter(tag_name = 'just-a-tag') tag_context: @hub.tagging_key) end it 'does not return that tagging' do - expect(@filter.deactivates_taggings) + expect(@filter.deactivates_taggings(@filter.scope.taggable_items.pluck(:id))) .not_to include(@feed_item.taggings.first) end end @@ -108,7 +108,7 @@ def new_add_filter(tag_name = 'just-a-tag') describe '#deactivates_taggings' do it 'returns the taggings attaching tag "a" to those feed items' do - expect(@filter.deactivates_taggings) + expect(@filter.deactivates_taggings(@filter.scope.taggable_items.pluck(:id))) .to match_array(@tagged_feed_items.map(&:taggings).flatten) end end diff --git a/spec/models/delete_tag_filter_spec.rb b/spec/models/delete_tag_filter_spec.rb index c0beb4dc..019c57af 100644 --- a/spec/models/delete_tag_filter_spec.rb +++ b/spec/models/delete_tag_filter_spec.rb @@ -52,7 +52,7 @@ def new_add_filter(tag_name = 'just-a-tag') it 'returns all tag "a" taggings' do taggings = ActsAsTaggableOn::Tagging .where(context: @hub.tagging_key, tag_id: @tag.id) - expect(@filter.deactivates_taggings).to match_array(taggings) + expect(@filter.deactivates_taggings(@filter.scope.taggable_items.pluck(:id))).to match_array(taggings) end context 'a feed item exists with tag "b"' do @@ -61,7 +61,7 @@ def new_add_filter(tag_name = 'just-a-tag') tag_context: @hub.tagging_key) end it 'does not return that tagging' do - expect(@filter.deactivates_taggings) + expect(@filter.deactivates_taggings(@filter.scope.taggable_items.pluck(:id))) .to not_contain @feed_item.taggings.first end end diff --git a/spec/models/modify_tag_filter_spec.rb b/spec/models/modify_tag_filter_spec.rb index 7b110e16..cc7be439 100644 --- a/spec/models/modify_tag_filter_spec.rb +++ b/spec/models/modify_tag_filter_spec.rb @@ -54,7 +54,7 @@ def new_add_filter(tag_name = 'just-a-tag') it 'only changes taggings on items that had tag "a" even if passed them' do affected = @feed_items.tagged_with(@tag.name, on: @hub.tagging_key).to_a - @filter.apply(items: @feed_items) + @filter.apply(@feed_items.pluck(:id)) now_affected = @feed_items.tagged_with(@new_tag.name, on: @hub.tagging_key).to_a expect(affected).to match_array(now_affected) end @@ -89,7 +89,7 @@ def new_add_filter(tag_name = 'just-a-tag') context: @hub.tagging_key) end it 'returns both taggings' do - expect(@filter.deactivates_taggings).to include(*@feed_item.taggings) + expect(@filter.deactivates_taggings(@hub.taggable_items.pluck(:id))).to include(*@feed_item.taggings) end end @@ -99,7 +99,7 @@ def new_add_filter(tag_name = 'just-a-tag') tag_context: @hub.tagging_key) end it 'does not return that tagging' do - expect(@filter.deactivates_taggings) + expect(@filter.deactivates_taggings(@hub.taggable_items.pluck(:id))) .to not_contain @feed_item.taggings.first end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index abbfdc9c..e6748a39 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -22,6 +22,7 @@ require 'support/shared_context' require 'support/shared_tag_filter_examples' require 'webmock/rspec' +require 'support/controller_helpers' # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are @@ -70,4 +71,6 @@ config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") + + config.include ControllerHelpers, type: :controller end diff --git a/spec/support/controller_helpers.rb b/spec/support/controller_helpers.rb new file mode 100644 index 00000000..d0a70a01 --- /dev/null +++ b/spec/support/controller_helpers.rb @@ -0,0 +1,11 @@ +module ControllerHelpers + def stub_sign_in(user = double('user')) + if user.nil? + allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, scope: :user) + allow(controller).to receive(:current_user).and_return(nil) + else + allow(request.env['warden']).to receive(:authenticate!).and_return(user) + allow(controller).to receive(:current_user).and_return(user) + end + end +end diff --git a/spec/support/shared_tag_filter_examples.rb b/spec/support/shared_tag_filter_examples.rb index 5c70ab69..e5add3b7 100644 --- a/spec/support/shared_tag_filter_examples.rb +++ b/spec/support/shared_tag_filter_examples.rb @@ -16,21 +16,21 @@ def add_filter(tag_name = 'add-test') context 'scoped to hub' do it 'returns all items in the hub' do filter = create(filter_type, scope: @hub) - expect(filter.items_in_scope).to match_array(@hub.feed_items) + expect(filter.scope.taggable_items).to match_array(@hub.feed_items) end end context 'scoped to feed' do it 'returns all items from the feed' do filter = create(filter_type, scope: @hub.hub_feeds.first) - expect(filter.items_in_scope).to match_array(@hub_feed.feed_items) + expect(filter.scope.taggable_items).to match_array(@hub_feed.feed_items) end end context 'scoped to item' do it 'returns the item itself' do filter = create(filter_type, scope: @hub.feed_items.first) - expect(filter.items_in_scope).to match_array([@hub.feed_items.first]) + expect(filter.scope.taggable_items).to match_array([@hub.feed_items.first]) end end end @@ -122,7 +122,7 @@ def add_filter(tag_name = 'add-test') describe '#deactivates_taggings' do it 'returns nothing' do - expect(@filter.deactivates_taggings).to be_empty + expect(@filter.deactivates_taggings(@hub.taggable_items.pluck(:id))).to be_empty end end end @@ -135,7 +135,7 @@ def add_filter(tag_name = 'add-test') without_some_items = @feed_items .where('feed_items.id NOT IN (?)', some_ids) - @filter.apply(items: some_items) + @filter.apply(some_items.pluck(:id)) tag_lists = tag_lists_for(some_items, @hub.tagging_key) without_tag_lists = tag_lists_for(without_some_items, @hub.tagging_key) @@ -154,8 +154,7 @@ def add_filter(tag_name = 'add-test') describe '#deactivates_taggings' do it 'does not return its own taggings' do - @filter.apply - expect(@filter.deactivates_taggings.map(&:tagger).uniq) + expect(@filter.deactivates_taggings(@hub.taggable_items.pluck(:id)).map(&:tagger).uniq) .to not_contain @filter end end @@ -234,13 +233,13 @@ def add_filter(tag_name = 'add-test') end it 'cannot be compelled to affect items outside its scope' do - filer_old = new_add_filter - filer_old.apply + filter_old = new_add_filter + filter_old.apply filter = add_filter setup_other_items_tags(filter, @feed_item2) - filter.apply(items: FeedItem.where(id: [@feed_item.id, @feed_item2.id])) + filter.apply(FeedItem.where(id: [@feed_item.id, @feed_item2.id]).pluck(:id)) tag_lists = tag_lists_for(@feed_item, @hub.tagging_key) other_tag_lists = tag_lists_for(@feed_item2, @hub.tagging_key) @@ -250,8 +249,8 @@ def add_filter(tag_name = 'add-test') end it "doesn't affect other items" do - filer_old = new_add_filter - filer_old.apply + filter_old = new_add_filter + filter_old.apply filter = add_filter setup_other_items_tags(filter, @feed_item2) From bccbfe1a3304b357faa780a063790f4eb330ea03 Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Fri, 5 Jul 2019 22:46:03 +0200 Subject: [PATCH 69/70] Update rails and bootstrap-sass --- Gemfile | 4 +-- Gemfile.lock | 87 +++++++++++++++++++++++++++------------------------- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/Gemfile b/Gemfile index e59caec7..6ec0085a 100644 --- a/Gemfile +++ b/Gemfile @@ -10,7 +10,7 @@ gem 'active_interaction', '~> 3.6' gem 'acts-as-taggable-on', '~> 5.0' gem 'acts_as_api', '~> 1.0' gem 'bootsnap', '~> 1.3', require: false -gem 'bootstrap-sass', '~> 3.3' +gem 'bootstrap-sass', '~> 3.4' gem 'breadcrumbs', '~> 0.1' gem 'browser', '~> 2.5' gem 'devise', '~> 4.4' @@ -30,7 +30,7 @@ gem 'pg', '~> 1.0' gem 'progress_bar', '~> 1.2' gem 'protected_attributes_continued', '~> 1.3' # TODO: switch to strong parameters gem 'pundit', '~> 1.1' -gem 'rails', '~> 5.1.0' # @drg frozen +gem 'rails', '~> 5.1.6' # @drg frozen gem 'rails-observers', '~> 0.1' gem 'recaptcha', '~> 4.12' gem 'redis-namespace', '~> 1.6' diff --git a/Gemfile.lock b/Gemfile.lock index a5346451..afed6fa6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,43 +10,43 @@ GEM specs: acl9 (3.1.0) rails (~> 5.0) - actioncable (5.1.6) - actionpack (= 5.1.6) + actioncable (5.1.7) + actionpack (= 5.1.7) nio4r (~> 2.0) websocket-driver (~> 0.6.1) - actionmailer (5.1.6) - actionpack (= 5.1.6) - actionview (= 5.1.6) - activejob (= 5.1.6) + actionmailer (5.1.7) + actionpack (= 5.1.7) + actionview (= 5.1.7) + activejob (= 5.1.7) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.1.6) - actionview (= 5.1.6) - activesupport (= 5.1.6) + actionpack (5.1.7) + actionview (= 5.1.7) + activesupport (= 5.1.7) rack (~> 2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) actionpack-action_caching (1.2.0) actionpack (>= 4.0.0, < 6) - actionview (5.1.6) - activesupport (= 5.1.6) + actionview (5.1.7) + activesupport (= 5.1.7) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) active_interaction (3.6.1) activemodel (>= 4, < 6) - activejob (5.1.6) - activesupport (= 5.1.6) + activejob (5.1.7) + activesupport (= 5.1.7) globalid (>= 0.3.6) - activemodel (5.1.6) - activesupport (= 5.1.6) - activerecord (5.1.6) - activemodel (= 5.1.6) - activesupport (= 5.1.6) + activemodel (5.1.7) + activesupport (= 5.1.7) + activerecord (5.1.7) + activemodel (= 5.1.7) + activesupport (= 5.1.7) arel (~> 8.0) - activesupport (5.1.6) + activesupport (5.1.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -61,16 +61,16 @@ GEM public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) ast (2.4.0) - autoprefixer-rails (8.6.0) + autoprefixer-rails (9.6.0) execjs awesome_print (1.8.0) bcrypt (3.1.12) bindex (0.5.0) bootsnap (1.3.0) msgpack (~> 1.0) - bootstrap-sass (3.3.7) + bootstrap-sass (3.4.1) autoprefixer-rails (>= 5.2.1) - sass (>= 3.3.4) + sassc (>= 2.0.0) brakeman (4.3.0) breadcrumbs (0.1.7) i18n @@ -125,7 +125,7 @@ GEM faraday (0.15.2) multipart-post (>= 1.2, < 3) feed-abstract (0.0.15) - ffi (1.9.25) + ffi (1.11.1) font-awesome-rails (4.7.0.4) railties (>= 3.2, < 6.0) formtastic (3.1.5) @@ -135,7 +135,7 @@ GEM fuubar (2.3.1) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) - globalid (0.4.1) + globalid (0.4.2) activesupport (>= 4.2.0) haml (5.0.4) temple (>= 0.8.0) @@ -179,10 +179,10 @@ GEM loofah (2.2.3) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.7.0) + mail (2.7.1) mini_mime (>= 0.1.1) method_source (0.9.2) - mini_mime (1.0.0) + mini_mime (1.0.1) mini_portile2 (2.4.0) minitest (5.11.3) msgpack (1.2.4) @@ -227,17 +227,17 @@ GEM rack rack-test (1.1.0) rack (>= 1.0, < 3) - rails (5.1.6) - actioncable (= 5.1.6) - actionmailer (= 5.1.6) - actionpack (= 5.1.6) - actionview (= 5.1.6) - activejob (= 5.1.6) - activemodel (= 5.1.6) - activerecord (= 5.1.6) - activesupport (= 5.1.6) + rails (5.1.7) + actioncable (= 5.1.7) + actionmailer (= 5.1.7) + actionpack (= 5.1.7) + actionview (= 5.1.7) + activejob (= 5.1.7) + activemodel (= 5.1.7) + activerecord (= 5.1.7) + activesupport (= 5.1.7) bundler (>= 1.3.0) - railties (= 5.1.6) + railties (= 5.1.7) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) @@ -250,9 +250,9 @@ GEM loofah (~> 2.2, >= 2.2.2) rails-observers (0.1.5) activemodel (>= 4.0) - railties (5.1.6) - actionpack (= 5.1.6) - activesupport (= 5.1.6) + railties (5.1.7) + actionpack (= 5.1.7) + activesupport (= 5.1.7) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) @@ -322,6 +322,9 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) + sassc (2.0.1) + ffi (~> 1.9) + rake selenium-client (1.2.18) selenium-webdriver (3.12.0) childprocess (~> 0.5) @@ -397,7 +400,7 @@ GEM hashdiff websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.3) + websocket-extensions (0.1.4) whenever (0.10.0) chronic (>= 0.6.3) will_paginate (3.1.6) @@ -417,7 +420,7 @@ DEPENDENCIES acts_as_api (~> 1.0) awesome_print (~> 1.8) bootsnap (~> 1.3) - bootstrap-sass (~> 3.3) + bootstrap-sass (~> 3.4) brakeman (~> 4.3) breadcrumbs (~> 0.1) browser (~> 2.5) @@ -452,7 +455,7 @@ DEPENDENCIES pry-rails (~> 0.3) pundit (~> 1.1) pundit-matchers (~> 1.6) - rails (~> 5.1.0) + rails (~> 5.1.6) rails-controller-testing (~> 1.0) rails-observers (~> 0.1) recaptcha (~> 4.12) From 78bad83cd5e4fcdb96749f0db54a5f539625a4ff Mon Sep 17 00:00:00 2001 From: Peter Hankiewicz Date: Wed, 17 Jul 2019 14:16:08 +0200 Subject: [PATCH 70/70] Replace outdated "expires" usages with "expires_in" --- app/views/feed_items/_grid_item.html.haml | 2 +- app/views/hubs/statistics.html.haml | 2 +- app/views/shared/line_items/_feed_item.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/feed_items/_grid_item.html.haml b/app/views/feed_items/_grid_item.html.haml index c5c78f37..f22171f6 100644 --- a/app/views/feed_items/_grid_item.html.haml +++ b/app/views/feed_items/_grid_item.html.haml @@ -2,7 +2,7 @@ :ruby hub = @hub || feed_item.hubs.first hub_feed = @hub_feed || feed_item.hub_feed_for_hub(hub.id) || feed_item.hub_feeds.first -- cache("feed-item-tag-list-#{hub.id}-#{hub_feed.id}-#{feed_item.id}-grid", :expires => 120.minutes ) do +- cache("feed-item-tag-list-#{hub.id}-#{hub_feed.id}-#{feed_item.id}-grid", :expires_in => 120.minutes ) do .feed-item.grid-item.col-md-6 .grid-image{ :style => "background-image: url('#{feed_item.image_url or image_path('feed_item_placeholder.png')}');" } .feed-item-content.equal-height diff --git a/app/views/hubs/statistics.html.haml b/app/views/hubs/statistics.html.haml index 3a5d3f4b..0fefcaf1 100644 --- a/app/views/hubs/statistics.html.haml +++ b/app/views/hubs/statistics.html.haml @@ -3,7 +3,7 @@ - content_for :tabs do = render partial: 'hubs/tabs', locals: { active: 'statistics' } - content_for :tab_content do - - cache("hub-stats-#{@hub.try(:id)}", :expires => 6.hours ) do + - cache("hub-stats-#{@hub.try(:id)}", :expires_in => 6.hours ) do .nicely-padded.statistics %h1 Statistics .row diff --git a/app/views/shared/line_items/_feed_item.html.haml b/app/views/shared/line_items/_feed_item.html.haml index cc561b5f..82d2f6da 100644 --- a/app/views/shared/line_items/_feed_item.html.haml +++ b/app/views/shared/line_items/_feed_item.html.haml @@ -3,7 +3,7 @@ - hub_feed = @hub_feed || feed_item.hub_feed_for_hub(hub.id) || feed_item.hub_feeds.first %tr.feed_item{:id => "feed_item_#{feed_item.id}"} %td - - cache("feed-item-tag-list-#{hub.id}-#{hub_feed.id}-#{feed_item.id}", :expires => 120.minutes ) do + - cache("feed-item-tag-list-#{hub.id}-#{hub_feed.id}-#{feed_item.id}", :expires_in => 120.minutes ) do .feed_item_title = link_to(raw(strip_tags(feed_item.title)), hub_feed_feed_item_path(hub_feed,feed_item)) %a{:name => feed_item.id}