1 class TraceController < ApplicationController
 
   2   layout "site", :except => :georss
 
   4   skip_before_action :verify_authenticity_token, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
 
   5   before_action :authorize_web
 
   6   before_action :set_locale
 
   7   before_action :require_user, :only => [:mine, :create, :edit, :delete]
 
   8   before_action :authorize, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
 
   9   before_action :check_database_readable, :except => [:api_read, :api_data]
 
  10   before_action :check_database_writable, :only => [:create, :edit, :delete, :api_create, :api_update, :api_delete]
 
  11   before_action :check_api_readable, :only => [:api_read, :api_data]
 
  12   before_action :check_api_writable, :only => [:api_create, :api_update, :api_delete]
 
  13   before_action :require_allow_read_gpx, :only => [:api_read, :api_data]
 
  14   before_action :require_allow_write_gpx, :only => [:api_create, :api_update, :api_delete]
 
  15   before_action :offline_warning, :only => [:mine, :view]
 
  16   before_action :offline_redirect, :only => [:create, :edit, :delete, :data, :api_create, :api_delete, :api_data]
 
  17   around_action :api_call_handle_error, :only => [:api_create, :api_read, :api_update, :api_delete, :api_data]
 
  19   # Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
 
  20   #  target_user - if set, specifies the user to fetch traces for.  if not set will fetch all traces
 
  22     # from display name, pick up user id if one user's traces only
 
  23     display_name = params[:display_name]
 
  24     if display_name.present?
 
  25       target_user = User.active.where(:display_name => display_name).first
 
  27         render_unknown_user display_name
 
  33     @title = if target_user.nil?
 
  34                t "trace.list.public_traces"
 
  35              elsif current_user && current_user == target_user
 
  36                t "trace.list.your_traces"
 
  38                t "trace.list.public_traces_from", :user => target_user.display_name
 
  41     @title += t "trace.list.tagged_with", :tags => params[:tag] if params[:tag]
 
  44     # 1 - all traces, logged in = all public traces + all user's (i.e + all mine)
 
  45     # 2 - all traces, not logged in = all public traces
 
  46     # 3 - user's traces, logged in as same user = all user's traces
 
  47     # 4 - user's traces, not logged in as that user = all user's public traces
 
  48     @traces = if target_user.nil? # all traces
 
  50                   Trace.visible_to(current_user) # 1
 
  52                   Trace.visible_to_all # 2
 
  54               elsif current_user && current_user == target_user
 
  55                 current_user.traces # 3 (check vs user id, so no join + can't pick up non-public traces by changing name)
 
  57                 target_user.traces.visible_to_all # 4
 
  60     @traces = @traces.tagged(params[:tag]) if params[:tag]
 
  62     @params = params.permit(:display_name, :tag)
 
  64     @page = (params[:page] || 1).to_i
 
  67     @traces = @traces.visible
 
  68     @traces = @traces.order(:id => :desc)
 
  69     @traces = @traces.offset((@page - 1) * @page_size)
 
  70     @traces = @traces.limit(@page_size)
 
  71     @traces = @traces.includes(:user, :tags)
 
  73     # put together SET of tags across traces, for related links
 
  75     @traces.each do |trace|
 
  76       trace.tags.reload if params[:tag] # if searched by tag, ActiveRecord won't bring back other tags, so do explicitly here
 
  77       trace.tags.each do |tag|
 
  78         tagset[tag.tag] = tag.tag
 
  82     # final helper vars for view
 
  83     @target_user = target_user
 
  84     @display_name = target_user.display_name if target_user
 
  85     @all_tags = tagset.values
 
  89     redirect_to :action => :list, :display_name => current_user.display_name
 
  93     @trace = Trace.find(params[:id])
 
  95     if @trace && @trace.visible? &&
 
  96        (@trace.public? || @trace.user == current_user)
 
  97       @title = t "trace.view.title", :name => @trace.name
 
  99       flash[:error] = t "trace.view.trace_not_found"
 
 100       redirect_to :action => "list"
 
 102   rescue ActiveRecord::RecordNotFound
 
 103     flash[:error] = t "trace.view.trace_not_found"
 
 104     redirect_to :action => "list"
 
 109       logger.info(params[:trace][:gpx_file].class.name)
 
 111       if params[:trace][:gpx_file].respond_to?(:read)
 
 113           do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
 
 114                     params[:trace][:description], params[:trace][:visibility])
 
 115         rescue StandardError => ex
 
 120           flash[:notice] = t "trace.create.trace_uploaded"
 
 122           if current_user.traces.where(:inserted => false).count > 4
 
 123             flash[:warning] = t "trace.trace_header.traces_waiting", :count => current_user.traces.where(:inserted => false).count
 
 126           redirect_to :action => :list, :display_name => current_user.display_name
 
 129         @trace = Trace.new(:name => "Dummy",
 
 130                            :tagstring => params[:trace][:tagstring],
 
 131                            :description => params[:trace][:description],
 
 132                            :visibility => params[:trace][:visibility],
 
 133                            :inserted => false, :user => current_user,
 
 134                            :timestamp => Time.now.getutc)
 
 136         @trace.errors.add(:gpx_file, "can't be blank")
 
 139       @trace = Trace.new(:visibility => default_visibility)
 
 142     @title = t "trace.create.upload_trace"
 
 146     trace = Trace.find(params[:id])
 
 148     if trace.visible? && (trace.public? || (current_user && current_user == trace.user))
 
 149       if Acl.no_trace_download(request.remote_ip)
 
 151       elsif request.format == Mime[:xml]
 
 152         send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => request.format.to_s, :disposition => "attachment")
 
 153       elsif request.format == Mime[:gpx]
 
 154         send_file(trace.xml_file, :filename => "#{trace.id}.gpx", :type => request.format.to_s, :disposition => "attachment")
 
 156         send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => "attachment")
 
 161   rescue ActiveRecord::RecordNotFound
 
 166     @trace = Trace.find(params[:id])
 
 170     elsif current_user.nil? || @trace.user != current_user
 
 173       @title = t "trace.edit.title", :name => @trace.name
 
 175       if request.post? && params[:trace]
 
 176         @trace.description = params[:trace][:description]
 
 177         @trace.tagstring = params[:trace][:tagstring]
 
 178         @trace.visibility = params[:trace][:visibility]
 
 180           redirect_to :action => "view", :display_name => current_user.display_name
 
 184   rescue ActiveRecord::RecordNotFound
 
 189     trace = Trace.find(params[:id])
 
 193     elsif current_user.nil? || trace.user != current_user
 
 196       trace.visible = false
 
 198       flash[:notice] = t "trace.delete.scheduled_for_deletion"
 
 199       redirect_to :action => :list, :display_name => current_user.display_name
 
 201   rescue ActiveRecord::RecordNotFound
 
 206     @traces = Trace.visible_to_all.visible
 
 208     if params[:display_name]
 
 209       @traces = @traces.joins(:user).where(:users => { :display_name => params[:display_name] })
 
 212     @traces = @traces.tagged(params[:tag]) if params[:tag]
 
 213     @traces = @traces.order("timestamp DESC")
 
 214     @traces = @traces.limit(20)
 
 215     @traces = @traces.includes(:user)
 
 219     trace = Trace.find(params[:id])
 
 221     if trace.visible? && trace.inserted?
 
 222       if trace.public? || (current_user && current_user == trace.user)
 
 223         expires_in 7.days, :private => !trace.public?, :public => trace.public?
 
 224         send_file(trace.large_picture_name, :filename => "#{trace.id}.gif", :type => "image/gif", :disposition => "inline")
 
 231   rescue ActiveRecord::RecordNotFound
 
 236     trace = Trace.find(params[:id])
 
 238     if trace.visible? && trace.inserted?
 
 239       if trace.public? || (current_user && current_user == trace.user)
 
 240         expires_in 7.days, :private => !trace.public?, :public => trace.public?
 
 241         send_file(trace.icon_picture_name, :filename => "#{trace.id}_icon.gif", :type => "image/gif", :disposition => "inline")
 
 248   rescue ActiveRecord::RecordNotFound
 
 253     trace = Trace.visible.find(params[:id])
 
 255     if trace.public? || trace.user == current_user
 
 256       render :xml => trace.to_xml.to_s
 
 263     trace = Trace.visible.find(params[:id])
 
 265     if trace.user == current_user
 
 266       trace.update_from_xml(request.raw_post)
 
 276     trace = Trace.visible.find(params[:id])
 
 278     if trace.user == current_user
 
 279       trace.visible = false
 
 289     trace = Trace.visible.find(params[:id])
 
 291     if trace.public? || trace.user == current_user
 
 292       if request.format == Mime[:xml]
 
 293         send_file(trace.xml_file, :filename => "#{trace.id}.xml", :type => request.format.to_s, :disposition => "attachment")
 
 294       elsif request.format == Mime[:gpx]
 
 295         send_file(trace.xml_file, :filename => "#{trace.id}.gpx", :type => request.format.to_s, :disposition => "attachment")
 
 297         send_file(trace.trace_name, :filename => "#{trace.id}#{trace.extension_name}", :type => trace.mime_type, :disposition => "attachment")
 
 305     tags = params[:tags] || ""
 
 306     description = params[:description] || ""
 
 307     visibility = params[:visibility]
 
 310       visibility = if params[:public] && params[:public].to_i.nonzero?
 
 317     if params[:file].respond_to?(:read)
 
 318       do_create(params[:file], tags, description, visibility)
 
 321         render :plain => @trace.id.to_s
 
 323         head :internal_server_error
 
 334   def do_create(file, tags, description, visibility)
 
 335     # Sanitise the user's filename
 
 336     name = file.original_filename.gsub(/[^a-zA-Z0-9.]/, "_")
 
 338     # Get a temporary filename...
 
 339     filename = "/tmp/#{rand}"
 
 341     # ...and save the uploaded file to that location
 
 342     File.open(filename, "wb") { |f| f.write(file.read) }
 
 344     # Create the trace object, falsely marked as already
 
 345     # inserted to stop the import daemon trying to load it
 
 349       :description => description,
 
 350       :visibility => visibility,
 
 352       :user => current_user,
 
 353       :timestamp => Time.now.getutc
 
 358         # Save the trace object
 
 361         # Rename the temporary file to the final name
 
 362         FileUtils.mv(filename, @trace.trace_name)
 
 364         # Remove the file as we have failed to update the database
 
 365         FileUtils.rm_f(filename)
 
 367         # Pass the exception on
 
 372         # Clear the inserted flag to make the import daemon load the trace
 
 373         @trace.inserted = false
 
 376         # Remove the file as we have failed to update the database
 
 377         FileUtils.rm_f(@trace.trace_name)
 
 379         # Pass the exception on
 
 384     # Finally save the user's preferred privacy level
 
 385     if pref = current_user.preferences.where(:k => "gps.trace.visibility").first
 
 389       current_user.preferences.create(:k => "gps.trace.visibility", :v => visibility)
 
 394     flash.now[:warning] = t "trace.offline_warning.message" if STATUS == :gpx_offline
 
 398     redirect_to :action => :offline if STATUS == :gpx_offline
 
 401   def default_visibility
 
 402     visibility = current_user.preferences.where(:k => "gps.trace.visibility").first
 
 406     elsif current_user.preferences.where(:k => "gps.trace.public", :v => "default").first.nil?