mysociety/alaveteli

View on GitHub
app/controllers/track_controller.rb

Summary

Maintainability
B
4 hrs
Test Coverage
# app/controllers/track_controller.rb:
# Publicly visible email alerts and RSS - think an alert system crossed with
# social bookmarking.
#
# Copyright (c) 2008 UK Citizens Online Democracy. All rights reserved.
# Email: hello@mysociety.org; WWW: http://www.mysociety.org/

class TrackController < ApplicationController
  skip_before_action :html_response

  before_action :medium_cache

  # Track all updates to a particular request
  def track_request
    @info_request = InfoRequest
                      .not_embargoed
                        .find_by_url_title!(params[:url_title])
    @track_thing = TrackThing.create_track_for_request(@info_request)

    return atom_feed_internal if params[:feed] == 'feed'

    if track_set
      if AlaveteliConfiguration.enable_widgets && cookies[:widget_vote]
        @info_request.
          widget_votes.
            where(cookie: cookies[:widget_vote]).
              destroy_all
      end

      redirect_to request_url(@info_request)
    end
  end

  # Track all new/successful requests
  def track_list
    @view = params[:view]

    if @view == 'recent' || @view.nil? # the blank one for backwards compatibility
      @track_thing = TrackThing.create_track_for_all_new_requests
    elsif @view == 'successful'
      @track_thing = TrackThing.create_track_for_all_successful_requests
    else
      raise "unknown request list view " + @view.to_s
    end

    return atom_feed_internal if params[:feed] == 'feed'

    if track_set || @track_thing.errors.any?
      redirect_to request_list_url(view: @view)
    end
  end

  # Track all updates to a particular public body
  def track_public_body
    @public_body = PublicBody.find_by_url_name_with_historic(params[:url_name])
    raise ActiveRecord::RecordNotFound, "None found" if @public_body.nil?

    # If found by historic name, or alternate locale name, redirect to new name
    if @public_body.url_name != params[:url_name]
      redirect_to track_public_body_url(url_name: @public_body.url_name, feed: params[:feed], event_type: params[:event_type])
      return
    end

    if params[:event_type]
      @track_thing = TrackThing.create_track_for_public_body(@public_body, params[:event_type])
    else
      @track_thing = TrackThing.create_track_for_public_body(@public_body)
    end

    return atom_feed_internal if params[:feed] == 'feed'

    if track_set || @track_thing.errors.any?
      redirect_to public_body_url(@public_body)
    end
  end

  # Track a user
  def track_user
    @track_user = User.find_by_url_name(params[:url_name])
    raise ActiveRecord::RecordNotFound, "No such user" if @track_user.nil?

    @track_thing = TrackThing.create_track_for_user(@track_user)

    return atom_feed_internal if params[:feed] == 'feed'

    redirect_to user_url(@track_user) if track_set || @track_thing.errors.any?
  end

  # Track a search term
  def track_search_query
    @query = params[:query_array]

    # TODO: more hackery to make alternate formats still work with query_array
    if /^(.*)\.json$/.match(@query)
      @query = $1
      params[:format] = "json"
    end

    @track_thing = TrackThing.create_track_for_search_query(@query)

    return atom_feed_internal if params[:feed] == 'feed'

    if track_set || @track_thing.errors.any?
      if @query.scan("variety").length == 1
        # we're making a track for a simple filter, for which
        # there's an expression in the UI (rather than relying
        # on index:value strings in the query)
        if @query =~ /variety:user/
          postfix = "users"
          @query.sub!("variety:user", "")
        elsif @query =~ /variety:authority/
          postfix = "bodies"
          @query.sub!("variety:authority", "")
        elsif @query =~ /variety:sent/
          postfix = "requests"
          @query.sub!("variety:sent", "")
        end
        @query.strip!
      end
      redirect_to search_url([@query, postfix])
    end
  end

  # Generic request tracker - set @track_thing before calling
  def track_set
    if @user
      @existing_track = TrackThing.find_existing(@user, @track_thing)
      if @existing_track
        flash[:notice] =
          { partial: 'track/already_tracking',
            locals: { track_thing_id: @existing_track.id } }
        return true
      end
    end

    unless authenticated?
      ask_to_login(**@track_thing.params)
      return false
    end

    @track_thing.track_medium = 'email_daily'
    @track_thing.tracking_user_id = @user.id
    if @track_thing.save
      flash[:notice] =
        { partial: 'track/track_set',
          locals: {
            user_receive_email_alerts: @user.receive_email_alerts,
            user_url_name: @user.url_name,
            track_thing_id: @track_thing.id } }
      true
    else
      # this will most likely be tripped by a single error - probably track_query length
      flash[:error] = @track_thing.errors.map(&:message).join(", ")
      false
    end
  end

  # Old-Style atom track. We're phasing this out, so for now issue a
  # 301 Redirect. Most aggregators should honour this, but we should
  # keep an eye on the logs to see which ones are still used before
  # deleting this (or for safety, we may wish to move them to a new
  # table)
  def atom_feed
    @track_thing = TrackThing.find(params[:track_id].to_i)
    if @track_thing.track_medium != 'feed'
      raise "can only view feeds for feed tracks, not email ones"
    end

    redirect_to do_track_url(@track_thing, 'feed'), status: :moved_permanently
  end

  def atom_feed_internal
    @xapian_object = perform_search(
      [InfoRequestEvent], @track_thing.track_query,
      @track_thing.params[:feed_sortby], nil, 25
    )
    # We're assuming that a request to a feed url with no format suffix wants atom/xml
    # so set that as the default, regardless of content negotiation
    request.format = params[:format] || 'xml'
    respond_to do |format|
      format.json { render json: @xapian_object.results.map { |r| r[:model].json_for_api(true,
                                                                                            ->(t) do
                                                                                              view_context.highlight_and_excerpt(
                                                                                                t,
                                                                                                @xapian_object.words_to_highlight(
                                                                                                  regex: true,
                                                                                                include_original: true),
                                                                                                150
                                                                                              )
                                                                                            end
                                                                                            ) } }
      format.any { render template: 'track/atom_feed',
                   formats: [:atom],
                   layout: false,
                   content_type: 'application/atom+xml' }
    end
  end

  # Change or delete a track
  def update
    track_thing = TrackThing.find(params[:track_id].to_i)

    unless authenticated?(as: track_thing.tracking_user)
      ask_to_login(
        as: track_thing.tracking_user,
        web: _('To cancel this alert'),
        email: _('Then you can cancel the alert.'),
        email_subject: _('Cancel a {{site_name}} alert', site_name: site_name)
      )
      return
    end

    new_medium = params[:track_medium]
    if new_medium == 'delete'
      track_thing.destroy
      flash[:notice] = { inline: view_context.unsubscribe_notice(track_thing) }
      redirect_to SafeRedirect.new(params[:r]).path
    else
      msg =
        if new_medium
          "Given track_medium not handled: #{ new_medium }"
        else
          'No track_medium supplied'
        end

      raise msg
    end
  end

  # Remove all tracks of a certain type (e.g. requests / users / bodies)
  def delete_all_type
    user_id = User.find(params[:user].to_i)

    unless authenticated?(as: user_id)
      ask_to_login(
        as: user_id,
        web: _('To cancel these alerts'),
        email: _('Then you can cancel the alerts.'),
        email_subject: _('Cancel some {{site_name}} alerts',
                         site_name: site_name)
      )
      return
    end

    track_type = params[:track_type]

    flash[:notice] = _("You will no longer be emailed updates for those alerts")
    TrackThing.
      where(track_type: track_type, tracking_user_id: user_id).
        destroy_all
    redirect_to SafeRedirect.new(params[:r]).path
  end
end