update with changes from Production branch

This commit is contained in:
Lee Lawlor
2014-02-17 12:05:39 -05:00
parent 5b640cf9d8
commit a4937fb2e5
384 changed files with 14690 additions and 2242 deletions

View File

@ -1,42 +1,50 @@
class ApiKeysController < ApplicationController
include KeyUtilities
include KeyUtilities, ApiKeys
before_filter :require_user, :set_channels_menu
before_filter :require_user, :set_channels_menu
def index
@channel = current_user.channels.find(params[:channel_id])
@write_key = @channel.api_keys.write_keys.first
@read_keys = @channel.api_keys.read_keys
end
def index
api_index params[:channel_id]
end
def destroy
current_user.api_keys.find_by_api_key(params[:id]).try(:destroy)
redirect_to :back
end
def destroy
current_user.api_keys.find_by_api_key(params[:id]).try(:destroy)
redirect_to :back
end
def create
@channel = current_user.channels.find(params[:channel_id])
@api_key = @channel.api_keys.write_keys.first
def create
@channel = current_user.channels.find(params[:channel_id])
@api_key = @channel.api_keys.write_keys.first
# if no api key found or read api key
if (@api_key.nil? or params[:write] == '0')
@api_key = ApiKey.new
@api_key.channel_id = @channel.id
@api_key.user_id = current_user.id
@api_key.write_flag = params[:write]
end
# if no api key found or read api key
if (@api_key.nil? || params[:write] == '0')
@api_key = ApiKey.new
@api_key.channel_id = @channel.id
@api_key.user_id = current_user.id
@api_key.write_flag = params[:write]
end
# set new api key and save
@api_key.api_key = generate_api_key
@api_key.save
# set new api key and save
@api_key.api_key = generate_api_key
@api_key.save
# redirect
redirect_to channel_api_keys_path(@channel)
end
# redirect
# redirect_to channel_api_keys_path(@channel.id)
redirect_to channel_path(@channel.id, :anchor => "apikeys")
end
def update
@api_key = current_user.api_keys.find_by_api_key(params[:id])
@api_key.update_attributes(api_key_params)
redirect_to :back
end
private
# only allow these params
def api_key_params
params.require(:api_key).permit(:note)
end
def update
@api_key = current_user.api_keys.find_by_api_key(params[:id])
@api_key.update_attributes(params[:api_key])
redirect_to :back
end
end

View File

@ -1,51 +1,134 @@
class ApplicationController < ActionController::Base
# include all helpers for controllers
# include all helpers for controllers
helper :all
# include these helper methods for views
helper_method :current_user_session, :current_user, :get_header_value
# include these helper methods for views
helper_method :current_user_session, :current_user, :logged_in?, :is_admin?, :get_header_value, :to_bytes
protect_from_forgery
before_filter :set_variables
before_filter :allow_cross_domain_access, :set_variables
# set up some variables across the entire application
# responds with blank
def respond_with_blank
respond_to do |format|
format.html { render :text => '' }
format.json { render :json => {}.to_json }
# fix xml response line breaks
format.xml { render :xml => {}.to_xml.gsub("\n", '').gsub("<hash>", "\n<hash>") }
end
end
# responds with an error
def respond_with_error(error_code)
error_response = ErrorResponse.new(error_code)
respond_to do |format|
format.html { render :text => error_response.error_code, :status => error_response.http_status }
format.json { render :json => error_response.to_json, :status => error_response.http_status }
format.xml { render :xml => error_response.to_xml, :status => error_response.http_status }
end
end
# set up some variables across the entire application
def set_variables
@api_domain ||= api_domain
@ssl_api_domain ||= ssl_api_domain
@locale ||= get_locale
I18n.locale = ALLOWED_LOCALES.include?(@locale) ? @locale : I18n.default_locale
I18n.locale = @locale
# sets timezone for current user, all DateTime outputs will be automatically formatted
Time.zone = current_user.time_zone if current_user
end
# get the locale, but don't fail if header value doesn't exist
def get_locale
locale = get_header_value('HTTP_ACCEPT_LANGUAGE')
# only look for 'pt-br' as first 5 letters, can make more robust in future if other languages are needed
locale = locale[0..4].downcase if locale
if locale and ALLOWED_LOCALES.include?(locale[0..1].downcase)
locale = locale[0..1].downcase
elsif locale and ALLOWED_LOCALES.include?(locale[0..4].downcase)
locale = locale[0..4].downcase
else
locale = I18n.default_locale
end
return locale
end
private
def set_channels_menu
@menu = 'channels'
end
# allow javascript requests from any domain
def allow_cross_domain_access
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, OPTIONS, DELETE, PATCH'
response.headers['Access-Control-Allow-Headers'] = 'origin, content-type, X-Requested-With'
response.headers['Access-Control-Max-Age'] = '1800'
end
def logged_in?
true if current_user
end
# check that user's email address matches admin
def is_admin?
current_user && ADMIN_EMAILS.include?(current_user.email)
end
def set_admin_menu
@menu = 'admin'
end
# converts a string to a byte string for c output
def to_bytes(input, separator='.', prefix='')
return '' if input == nil
output = []
# split the input array using the separator, and add necessary prefixes to each item
input.split(separator).each { |i| output.push(prefix + i) }
# rejoin the array into a comma separated string
return output.join(', ')
end
def set_channels_menu
@menu = 'channels'
end
def set_apps_menu
@menu = 'apps'
end
def set_plugins_menu
@menu = 'plugins'
end
def set_devices_menu
@menu = 'devices'
end
def current_user_session
return @current_user_session if defined?(@current_user_session)
@current_user_session = UserSession.find
end
def current_user
return @current_user if defined?(@current_user)
@current_user = current_user_session && current_user_session.record
end
# check that user is logged in
def require_user
if current_user.nil?
redirect_to login_path
false
end
end
def require_user
logger.info "Require User"
if current_user.nil?
respond_to do |format|
format.html {
session[:link_back] = request.url
logger.debug "Redirecting to login"
redirect_to login_path
return true
}
format.json do
render :json => {'error' => 'Could not authenticate you.'}, :status => :unauthorized
return true
end
end
return false
end
end
def require_no_user
if current_user
store_location
@ -54,142 +137,205 @@ class ApplicationController < ActionController::Base
end
end
def store_location
if params[:controller] != "user_sessions"
session[:return_to] = request.fullpath
end
def require_admin
unless current_user && is_admin?
render :nothing => true, :status => 403 and return
false
end
end
def store_location
if params[:controller] != "user_sessions"
session[:return_to] = request.fullpath
end
end
def redirect_back_or_default(default)
redirect_to(session[:return_to] || default)
session[:return_to] = nil
end
def domain
u = request.url
begin
# the number 12 is the position at which to begin searching for '/', so we don't get the intitial '/' from http://
u = u[0..u.index('/', 12)]
rescue
u += '/'
end
# uncomment the line below for https support in a production environment
#u = u.sub(/http:/, 'https:') if Rails.env == 'production'
return u
end
def domain(ssl=true)
u = request.url
begin
# the number 12 is the position at which to begin searching for '/', so we don't get the intitial '/' from http://
u = u[0..u.index('/', 12)]
rescue
u += '/'
end
u = u.sub(/http:/, 'https:') if (Rails.env == 'production' and ssl)
return u
end
# gets the api key
def get_userkey
return get_header_value('THINGSPEAKAPIKEY') || params[:key] || params[:api_key] || params[:apikey]
end
def ssl
(Rails.env == 'production') ? 'https' : 'http'
end
# get specified header value
def get_header_value(name)
value = nil
for header in request.env
value = header[1] if (header[0].upcase.index(name.upcase))
end
return value
end
# domain for the api
def api_domain
(Rails.env == 'production') ? API_DOMAIN : domain
end
# ssl domain for the api
def ssl_api_domain; (Rails.env == 'production') ? api_domain.sub('http', 'https'): api_domain; end
# gets the api key
def get_apikey
key = get_header_value(HTTP_HEADER_API_KEY_NAME) || params[:key] || params[:api_key] || params[:apikey]
key.strip if key.present?
return key
end
# get specified header value
def get_header_value(name)
name.upcase!
request.env.select {|header| header.upcase.index(name) }.values[0]
end
# generates a hash key unique to the user and url
def cache_key(type)
cache_key = request.host + request.path
user_id = current_user ? current_user.id : '0'
params.each do |key, value|
# add the parameter if appropriate
cache_key += "&#{key}=#{value}" if key != 'callback' && key != 'controller' && key != 'action' && key != 'format'
end
return "#{user_id}-#{type}-#{cache_key}"
end
# reads a file using the relative path to the file
def read_file(file_path)
path = file_path[0, file_path.rindex('/')]
filename = file_path[file_path.rindex('/') + 1, file_path.length]
output = ''
File.open("#{File.expand_path(path)}/#{filename}", 'r') do |f|
while line = f.gets
output += line
end
end
return output
end
# prepends or appends text
def add_prepend_append(input)
output = input.to_s
output = params[:prepend] + output if params[:prepend]
output += params[:append] if params[:append]
return output
end
# gets the same data for showing or editing
def get_channel_data
@channel = current_user.channels.find(params[:channel_id]) if params[:channel_id]
@channel = current_user.channels.find(params[:id]) if @channel.nil? and params[:id]
if @channel.ranking.blank?
@channel.ranking = @channel.calc_ranking
end
@key = @channel.api_keys.write_keys.first.try(:api_key) || ""
end
def check_permissions(channel)
render :text => t(:channel_permission) and return if (current_user.nil? || (channel.user_id != current_user.id))
end
def check_permissions(channel)
render :text => t(:channel_permission) and return if (current_user.nil? || (channel.user_id != current_user.id))
end
# checks permission for channel using api_key
def channel_permission?(channel, api_key)
if channel.public_flag or (api_key and api_key.channel_id == channel.id) or (current_user and channel.user_id == current_user.id)
return true
else
return false
end
end
# checks permission for channel using api_key
def channel_permission?(channel, api_key)
if channel.public_flag or (api_key and api_key.channel_id == channel.id) or (current_user and channel.user_id == current_user.id)
return true
else
return false
end
end
# outputs error for bad channel
def bad_channel_xml
channel_unauthorized = Channel.new
channel_unauthorized.id = -1
return channel_unauthorized.to_xml(:only => :id)
end
# outputs error for bad channel
def bad_channel_xml
channel_unauthorized = Channel.new
channel_unauthorized.id = -1
return channel_unauthorized.to_xml(:only => :id)
end
# outputs error for bad feed
def bad_feed_xml
feed_unauthorized = Feed.new
feedl_unauthorized.id = -1
return feed_unauthorized.to_xml(:only => :entry_id)
end
# outputs error for bad feed
def bad_feed_xml
feed_unauthorized = Feed.new
feed_unauthorized.id = -1
return feed_unauthorized.to_xml(:only => :entry_id)
end
# options: days = how many days ago, start = start date, end = end date, offset = timezone offset
def get_date_range(params)
# set timezone correctly
set_time_zone(params)
# options: days = how many days ago, start = start date, end = end date, offset = timezone offset
def get_date_range(params)
# set timezone correctly
set_time_zone(params)
# if results are specified without start or days parameters, allow start date to be larger
get_old_data = (params[:results] && params[:start].blank? and params[:days].blank?) ? true : false
# allow more past data if necessary
get_old_data = (params[:results].present? || params[:start].present? || params[:days].present?) ? true : false
start_date = (get_old_data) ? (Time.now - 1.year) : (Time.now - 1.day)
end_date = Time.now
start_date = (Time.now - params[:days].to_i.days) if params[:days]
start_date = DateTime.strptime(params[:start]) if params[:start]
end_date = DateTime.strptime(params[:end]) if params[:end]
date_range = (start_date..end_date)
# only get a maximum of 30 days worth of data
date_range = (end_date - 30.days..end_date) if ((end_date - start_date) > 30.days and !get_old_data)
return date_range
end
start_date = (get_old_data) ? Time.parse('2010-01-01') : (Time.now - 1.day)
end_date = Time.now
start_date = (Time.now - params[:days].to_i.days) if params[:days]
start_date = DateTime.parse(params[:start]) if params[:start]
end_date = DateTime.parse(params[:end]) if params[:end]
date_range = (start_date..end_date)
# only get a maximum of 30 days worth of data
date_range = (end_date - 30.days..end_date) if ((end_date - start_date) > 30.days and !get_old_data)
return date_range
end
def is_a_number?(s)
s.to_s.gsub(/,/, '.').match(/\A[+-]?\d+?(\.\d+)?\Z/) == nil ? false : true
end
def set_time_zone(params)
# set timezone correctly
if params[:offset]
def set_time_zone(params)
# set timezone correctly
if params[:offset]
# check for 0 offset first since it's the most common
if params[:offset] == '0'
Time.zone = 'UTC'
else
Time.zone = set_timezone_from_offset(params[:offset])
Time.zone = set_timezone_from_offset(params[:offset])
end
elsif current_user
Time.zone = current_user.time_zone
else
Time.zone = 'UTC'
end
end
elsif current_user
Time.zone = current_user.time_zone
else
Time.zone = 'UTC'
end
end
# use the offset to find an appropriate timezone
def set_timezone_from_offset(offset)
offset = offset.to_i
# keep track of whether a match was found
found = false
# loop through each timezone
ActiveSupport::TimeZone.zones_map.each do |z|
# set time zone
Time.zone = z[0]
timestring = Time.zone.now.to_s
# if time zone matches the offset, leave it as the current timezone
if (timestring.slice(-5..-3).to_i == offset and timestring.slice(-2..-1).to_i == 0)
found = true
break
end
end
# if no time zone found, set to utc
Time.zone = 'UTC' if !found
return Time.zone
end
def help
Helper.instance
end
class Helper
include Singleton
include ActionView::Helpers::TextHelper
end
end

View File

@ -0,0 +1,9 @@
class AppsController < ApplicationController
def index
@menu = 'apps'
@title = 'Internet of Things Apps' if current_user.nil?
# @twitters = TwitterAccount.find(:all, :conditions => { :user_id => current_user.id }) if current_user
end
end

View File

@ -1,258 +1,474 @@
class ChannelsController < ApplicationController
before_filter :require_user, :except => [ :show, :post_data ]
before_filter :set_channels_menu
protect_from_forgery :except => :post_data
require 'csv'
include ChannelsHelper, ApiKeys
before_filter :require_user, :except => [ :show, :post_data, :social_show, :social_feed, :public]
before_filter :set_channels_menu
layout 'application', :except => [:social_show, :social_feed]
protect_from_forgery :except => :post_data
require 'csv'
def index
@channels = current_user.channels
end
# view list of watched channels
def watched
@channels = current_user.watched_channels
end
def show
@channel = Channel.find(params[:id]) if params[:id]
@domain = domain
# user watches a channel
def watch
@watching = Watching.find_by_user_id_and_channel_id(current_user.id, params[:id])
# if owner of channel
get_channel_data if current_user and @channel.user_id == current_user.id
end
# add watching
if params[:flag] == 'true'
@watching = Watching.new(:user_id => current_user.id, :channel_id => params[:id]) if @watching.nil?
@watching.save
# delete watching
else
@watching.delete if !@watching.nil?
end
def edit
get_channel_data
end
render :text => '1'
end
def update
@channel = current_user.channels.find(params[:id])
@channel.update_attributes(params[:channel])
# list public channels
def public
# get channels by ids
if params[:channel_ids].present?
flash[:notice] = t(:selected_channels)
@channels = Channel.public_viewable.by_array(params[:channel_ids]).order('ranking desc, updated_at DESC').paginate :page => params[:page]
# get channels that match a tag
elsif params[:tag].present?
flash[:notice] = "#{t(:tag).capitalize}: #{params[:tag]}"
@channels = Channel.public_viewable.active.order('ranking desc, updated_at DESC').with_tag(params[:tag]).paginate :page => params[:page]
# normal channel list
else
flash[:notice] = t(:featured_channels)
@channels = Channel.public_viewable.active.order('ranking desc, updated_at DESC').paginate :page => params[:page]
end
redirect_to channel_path(@channel.id)
end
respond_to do |format|
format.html
format.json { render :json => Channel.paginated_hash(@channels).to_json }
format.xml { render :xml => Channel.paginated_hash(@channels).to_xml(:root => 'response') }
end
end
# widget for social feeds
def social_feed
# get domain based on ssl
@domain = domain((get_header_value('x_ssl') == 'true'))
end
# main page for a socialsensornetwork.com project
def social_show
@channel = Channel.find_by_slug(params[:slug])
# redirect home if wrong slug
redirect_to '/' and return if @channel.nil?
api_key = ApiKey.find(:first, :conditions => { :channel_id => @channel.id, :write_flag => 1 } )
@post_url = "/update?key=#{api_key.api_key}"
# names of non-blank channel fields
@fields = []
@channel.attribute_names.each do |attr|
@fields.push(attr) if attr.index('field') and !@channel[attr].blank?
end
end
def social_new
@channel = Channel.new
end
def social_create
@channel = Channel.new(channel_params)
# check for blank name
@channel.errors.add(:base, t(:social_channel_error_name_blank)) if @channel.name.blank?
# check for blank slug
@channel.errors.add(:base, t(:social_channel_error_slug_blank)) if @channel.slug.blank?
# check for at least one field
fields = false
@channel.attribute_names.each do |attr|
if (attr.index('field') or attr.index('status')) and !@channel[attr].blank?
fields = true
break
end
end
@channel.errors.add(:base, t(:social_channel_error_fields)) if !fields
# check for existing slug
if @channel.errors.count == 0
@channel.errors.add(:base, t(:social_channel_error_slug_exists)) if Channel.find_by_slug(@channel.slug)
end
# if there are no errors
if @channel.errors.count == 0
@channel.user_id = current_user.id
@channel.social = true
@channel.public_flag = true
@channel.save
# create an api key for this channel
channel.add_write_api_key
redirect_to channels_path
else
render :action => :social_new
end
end
def index
@channels = current_user.channels
respond_to do |format|
format.html
format.json { render :json => @channels }
end
end
def show
@channel = Channel.find(params[:id]) if params[:id]
@title = @channel.name
@domain = domain
@mychannel = (current_user && current_user.id == @channel.user_id)
@width = Chart.default_width
@height = Chart.default_height
api_index @channel.id
# if owner of channel
get_channel_data if @mychannel
respond_to do |format|
format.html do
if @mychannel
render "private_show"
session[:errors] = nil
else
render "public_show"
session[:errors] = nil
end
end
format.json { render :json => @channel }
end
end
def edit
get_channel_data
end
def update
@channel = current_user.channels.find(params[:id])
puts params[:channel].inspect
# make sure channel isn't social
#render :text => '' and return if @channel.social
if params["channel"]["video_type"].blank? && !params["channel"]["video_id"].blank?
@channel.errors.add(:base, t(:channel_video_type_blank))
end
if @channel.errors.count <= 0
@channel.save_tags(params[:tags][:name])
@channel.assign_attributes(channel_params)
@channel.set_windows
@channel.save
else
session[:errors] = @channel.errors
redirect_to channel_path(@channel.id, :anchor => "channelsettings") and return
end
flash[:notice] = t(:channel_update_success)
redirect_to channel_path(@channel.id)
end
def create
channel = current_user.channels.create(:field1 => "#{t(:channel_default_field)} 1")
channel.set_windows
channel.save
channel.add_write_api_key
# redirect to edit the newly created channel
redirect_to edit_channel_path(channel)
@channel_id = channel.id
redirect_to channel_path(@channel_id, :anchor => "channelsettings")
end
# clear all data from a channel
def clear
channel = current_user.channels.find(params[:id])
channel.delete_feeds
channel.update_attribute(:last_entry_id, nil)
redirect_to channel_path(channel.id)
end
def destroy
channel = current_user.channels.find(params[:id])
channel.destroy
redirect_to channels_path
end
end
def destroy
channel = current_user.channels.find(params[:id])
channel.destroy
# response is '0' if failure, 'entry_id' if success
def post_data
redirect_to channels_path
end
status = '0'
feed = Feed.new
# response is '0' if failure, 'entry_id' if success
def post_data
status = '0'
feed = Feed.new
api_key = ApiKey.find_by_api_key(get_userkey)
api_key = ApiKey.find_by_api_key(get_apikey)
# if write persmission, allow post
if (api_key && api_key.write_flag)
channel = Channel.find(api_key.channel_id)
# if write permission, allow post
if (api_key && api_key.write_flag)
channel = api_key.channel
# update entry_id for channel and feed
entry_id = channel.last_entry_id.nil? ? 1 : channel.last_entry_id + 1
channel.last_entry_id = entry_id
feed.entry_id = entry_id
# don't rate limit if tstream parameter is present
tstream = params[:tstream] || false;
# try to get created_at datetime if appropriate
if params[:created_at]
begin
feed.created_at = DateTime.parse(params[:created_at])
# if invalid datetime, don't do anything--rails will set created_at
rescue
end
end
# modify parameters
params.each do |key, value|
# strip line feeds from end of parameters
params[key] = value.sub(/\\n$/, '').sub(/\\r$/, '') if value
# use ip address if found
params[key] = request.remote_addr if value.upcase == 'IP_ADDRESS'
end
# set feed details
feed.channel_id = channel.id
feed.raw_data = params
feed.field1 = params[:field1] if params[:field1]
feed.field2 = params[:field2] if params[:field2]
feed.field3 = params[:field3] if params[:field3]
feed.field4 = params[:field4] if params[:field4]
feed.field5 = params[:field5] if params[:field5]
feed.field6 = params[:field6] if params[:field6]
feed.field7 = params[:field7] if params[:field7]
feed.field8 = params[:field8] if params[:field8]
feed.status = params[:status] if params[:status]
feed.latitude = params[:lat] if params[:lat]
feed.latitude = params[:latitude] if params[:latitude]
feed.longitude = params[:long] if params[:long]
feed.longitude = params[:longitude] if params[:longitude]
feed.elevation = params[:elevation] if params[:elevation]
# don't rate limit if talkback_key parameter is present
talkback_key = params[:talkback_key] || false;
if channel.save && feed.save
status = entry_id
end
end
# output response code
render :text => '0', :status => 400 and return if status == '0'
render :text => status
end
# rate limit posts if channel is not social and timespan is smaller than the allowed window
render :text => '0' and return if (RATE_LIMIT && !tstream && !talkback_key && !channel.social && Time.now < channel.updated_at + RATE_LIMIT_FREQUENCY.to_i.seconds)
# if social channel, latitude MUST be present
render :text => '0' and return if (channel.social && params[:latitude].blank?)
# update entry_id for channel and feed
entry_id = channel.next_entry_id
channel.last_entry_id = entry_id
feed.entry_id = entry_id
# try to get created_at datetime if appropriate
if params[:created_at].present?
begin
feed.created_at = DateTime.parse(params[:created_at])
# if invalid datetime, don't do anything--rails will set created_at
rescue
end
end
# modify parameters
params.each do |key, value|
# this fails so much due to encoding problems that we need to ignore errors
begin
# strip line feeds from end of parameters
params[key] = value.sub(/\\n$/, '').sub(/\\r$/, '') if value
# use ip address if found
params[key] = get_header_value('X_REAL_IP') if value.try(:upcase) == 'IP_ADDRESS'
rescue
end
end
# set feed details
feed.channel_id = channel.id
feed.field1 = params[:field1] || params['1'] if params[:field1] || params['1']
feed.field2 = params[:field2] || params['2'] if params[:field2] || params['2']
feed.field3 = params[:field3] || params['3'] if params[:field3] || params['3']
feed.field4 = params[:field4] || params['4'] if params[:field4] || params['4']
feed.field5 = params[:field5] || params['5'] if params[:field5] || params['5']
feed.field6 = params[:field6] || params['6'] if params[:field6] || params['6']
feed.field7 = params[:field7] || params['7'] if params[:field7] || params['7']
feed.field8 = params[:field8] || params['8'] if params[:field8] || params['8']
feed.status = params[:status] if params[:status]
feed.latitude = params[:lat] if params[:lat]
feed.latitude = params[:latitude] if params[:latitude]
feed.longitude = params[:long] if params[:long]
feed.longitude = params[:longitude] if params[:longitude]
feed.elevation = params[:elevation] if params[:elevation]
feed.location = params[:location] if params[:location]
# if the saves were successful
if channel.save && feed.save
status = entry_id
# check for tweet
if params[:twitter] && params[:tweet]
# check username
twitter_account = TwitterAccount.find_by_user_id_and_screen_name(api_key.user_id, params[:twitter])
if twitter_account
twitter_account.tweet(params[:tweet])
end
end
else
raise "Channel or Feed didn't save correctly"
end
end
# if there is a talkback to execute
if params[:talkback_key].present?
talkback = Talkback.find_by_api_key(params[:talkback_key])
command = talkback.execute_command! if talkback.present?
end
# output response code
render(:text => '0', :status => 400) and return if status == '0'
# if there is a talkback_key and a command that was executed
if params[:talkback_key].present? && command.present?
respond_to do |format|
format.html { render :text => command.command_string }
format.json { render :json => command.to_json }
format.xml { render :xml => command.to_xml(Command.public_options) }
end and return
end
# if there is a talkback_key but no command
respond_with_blank and return if params[:talkback_key].present? && command.blank?
# normal route, respond with the entry id of the feed
render :text => status
end
# import view
def import
get_channel_data
end
# upload csv file to channel
def upload
channel = Channel.find(params[:id])
check_permissions(channel)
# if no data
if params[:upload].blank? || params[:upload][:csv].blank?
flash[:error] = t(:upload_no_file)
redirect_to channel_path(channel.id, :anchor => "dataimport") and return
end
# set time zone
Time.zone = params[:feed][:time_zone]
# read data from uploaded file
csv_array = CSV.parse(params[:upload][:csv].read)
if csv_array.nil? || csv_array.blank?
flash[:error] = t(:upload_no_data)
redirect_to channel_path(channel.id, :anchor => "dataimport") and return
end
# does the column have headers
headers = has_headers?(csv_array)
# remember the column positions
entry_id_column = -1
latitude_column = -1
longitude_column = -1
elevation_column = -1
location_column = -1
status_column = -1
if headers
csv_array[0].each_with_index do |column, index|
entry_id_column = index if column.downcase == 'entry_id'
latitude_column = index if column.downcase == 'latitude'
longitude_column = index if column.downcase == 'longitude'
elevation_column = index if column.downcase == 'elevation'
location_column = index if column.downcase == 'location'
status_column = index if column.downcase == 'status'
end
end
# delete the first row if it contains headers
csv_array.delete_at(0) if headers
# determine if the date can be parsed
parse_date = date_parsable?(csv_array[0][0]) unless csv_array[0].nil? || csv_array[0][0].nil?
# if 2 or more rows
if !csv_array[1].blank?
date1 = parse_date ? Time.parse(csv_array[0][0]) : Time.at(csv_array[0][0])
date2 = parse_date ? Time.parse(csv_array[1][0]) : Time.at(csv_array[1][0])
# reverse the array if 1st date is larger than 2nd date
csv_array = csv_array.reverse if date1 > date2
end
# loop through each row
csv_array.each do |row|
# if row isn't blank
if !row.blank?
feed = Feed.new
# set location and status then delete the rows
# these 5 deletes must be performed in the proper (reverse) order
feed.status = row.delete_at(status_column) if status_column > 0
feed.location = row.delete_at(location_column) if location_column > 0
feed.elevation = row.delete_at(elevation_column) if elevation_column > 0
feed.longitude = row.delete_at(longitude_column) if longitude_column > 0
feed.latitude = row.delete_at(latitude_column) if latitude_column > 0
# remove entry_id column if necessary
row.delete_at(entry_id_column) if entry_id_column > 0
# update entry_id for channel and feed
entry_id = channel.last_entry_id.nil? ? 1 : channel.last_entry_id + 1
channel.last_entry_id = entry_id
feed.entry_id = entry_id
# set feed data
feed.channel_id = channel.id
feed.created_at = parse_date ? Time.zone.parse(row[0]) : Time.zone.at(row[0].to_f)
feed.field1 = row[1]
feed.field2 = row[2]
feed.field3 = row[3]
feed.field4 = row[4]
feed.field5 = row[5]
feed.field6 = row[6]
feed.field7 = row[7]
feed.field8 = row[8]
# save channel and feed
feed.save
channel.save
end
end
# set the user's time zone back
set_time_zone(params)
# redirect
flash[:notice] = t(:upload_successful)
redirect_to channel_path(channel.id, :anchor => "dataimport")
end
# import view
def import
get_channel_data
end
private
# upload csv file to channel
def upload
# if no data
render :text => t(:select_file) and return if params[:upload].blank? or params[:upload][:csv].blank?
# only allow these params
def channel_params
params.require(:channel).permit(:name, :url, :description, :latitude, :longitude, :field1, :field2, :field3, :field4, :field5, :field6, :field7, :field8, :elevation, :public_flag, :status, :video_id, :video_type)
end
channel = Channel.find(params[:channel_id])
channel_id = channel.id
# make sure channel belongs to current user
check_permissions(channel)
# set time zone
Time.zone = params[:feed][:time_zone]
# determine if the date can be parsed
def date_parsable?(date)
return !is_a_number?(date)
end
# read data from uploaded file
csv_array = CSV.parse(params[:upload][:csv].read)
# determine if the csv file has headers
def has_headers?(csv_array)
headers = false
# does the column have headers
headers = has_headers?(csv_array)
# if there are at least 2 rows
if (csv_array[0] and csv_array[1])
row0_integers = 0
row1_integers = 0
# remember the column positions
entry_id_column = -1
latitude_column = -1
longitude_column = -1
elevation_column = -1
status_column = -1
if headers
csv_array[0].each_with_index do |column, index|
entry_id_column = index if column.downcase == 'entry_id'
latitude_column = index if column.downcase == 'latitude'
longitude_column = index if column.downcase == 'longitude'
elevation_column = index if column.downcase == 'elevation'
status_column = index if column.downcase == 'status'
end
end
# if first row, first value contains 'create' or 'date', assume it has headers
if (csv_array[0][0].downcase.include?('create') or csv_array[0][0].downcase.include?('date'))
headers = true
else
# count integers in row0
csv_array[0].each_with_index do |value, i|
row0_integers += 1 if is_a_number?(value)
end
# delete the first row if it contains headers
csv_array.delete_at(0) if headers
# count integers in row1
csv_array[1].each_with_index do |value, i|
row1_integers += 1 if is_a_number?(value)
end
# determine if the date can be parsed
parse_date = date_parsable?(csv_array[0][0])
# if row1 has more integers, assume row0 is headers
headers = true if row1_integers > row0_integers
end
end
# if 2 or more rows
if !csv_array[1].blank?
date1 = parse_date ? Time.parse(csv_array[0][0]) : Time.at(csv_array[0][0])
date2 = parse_date ? Time.parse(csv_array[1][0]) : Time.at(csv_array[1][0])
return headers
end
# reverse the array if 1st date is larger than 2nd date
csv_array = csv_array.reverse if date1 > date2
end
end
# loop through each row
csv_array.each do |row|
# if row isn't blank
if !row.blank?
feed = Feed.new
# set location and status then delete the rows
# these 4 deletes must be performed in the proper (reverse) order
feed.status = row.delete_at(status_column) if status_column > 0
feed.elevation = row.delete_at(elevation_column) if elevation_column > 0
feed.longitude = row.delete_at(longitude_column) if longitude_column > 0
feed.latitude = row.delete_at(latitude_column) if latitude_column > 0
# remove entry_id column if necessary
row.delete_at(entry_id_column) if entry_id_column > 0
# update entry_id for channel and feed
entry_id = channel.last_entry_id.nil? ? 1 : channel.last_entry_id + 1
channel.last_entry_id = entry_id
feed.entry_id = entry_id
# set feed data
feed.channel_id = channel_id
feed.created_at = parse_date ? Time.zone.parse(row[0]) : Time.zone.at(row[0].to_f)
feed.raw_data = row.to_s
feed.field1 = row[1]
feed.field2 = row[2]
feed.field3 = row[3]
feed.field4 = row[4]
feed.field5 = row[5]
feed.field6 = row[6]
feed.field7 = row[7]
feed.field8 = row[8]
# save channel and feed
feed.save
channel.save
end
end
# set the user's time zone back
set_time_zone(params)
# redirect
redirect_to channel_path(channel.id)
end
private
# determine if the date can be parsed
def date_parsable?(date)
return !is_a_number?(date)
end
# determine if the csv file has headers
def has_headers?(csv_array)
headers = false
# if there are at least 2 rows
if (csv_array[0] and csv_array[1])
row0_integers = 0
row1_integers = 0
# if first row, first value contains 'create' or 'date', assume it has headers
if (csv_array[0][0].downcase.include?('create') or csv_array[0][0].downcase.include?('date'))
headers = true
else
# count integers in row0
csv_array[0].each_with_index do |value, i|
row0_integers += 1 if is_a_number?(value)
end
# count integers in row1
csv_array[1].each_with_index do |value, i|
row1_integers += 1 if is_a_number?(value)
end
# if row1 has more integers, assume row0 is headers
headers = true if row1_integers > row0_integers
end
end
return headers
end
end

View File

@ -1,78 +1,110 @@
class ChartsController < ApplicationController
before_filter :require_user, :only => [:edit]
def edit
# params[:id] is the windows ID
@channel = current_user.channels.find(params[:channel_id])
def index
set_channels_menu
@channel = Channel.find(params[:channel_id])
@channel_id = params[:channel_id]
@domain = domain
window_id = params[:id]
logger.debug "Windows ID is #{window_id}"
window_detail = @channel.windows.find(window_id).becomes(ChartWindow).window_detail
options = window_detail.options unless window_detail.nil?
logger.debug "Options for window #{window_id} are " + options.inspect
# default chart size
@width = default_width
@height = default_height
render :partial => "charts/config", :locals => {
:displayconfig => false,
:title => @channel.name,
:src => "/channels/#{@channel.id}/charts/#{window_id}",
:options => options,
:index => window_id,
:width => Chart.default_width,
:height => Chart.default_height
}
end
check_permissions(@channel)
end
def index
def show
# allow these parameters when creating feed querystring
feed_params = ['key','days','start','end','round','timescale','average','median','sum']
set_channels_menu
@channel = Channel.find(params[:channel_id])
@channel_id = params[:channel_id]
@domain = domain
# default chart size
@width = default_width
@height = default_height
# default chart size
@width = Chart.default_width
@height = Chart.default_height
# add extra parameters to querystring
@qs = ''
params.each do |p|
@qs += "&#{p[0]}=#{p[1]}" if feed_params.include?(p[0])
end
check_permissions(@channel)
end
# fix chart colors if necessary
params[:color] = fix_color(params[:color])
params[:bgcolor] = fix_color(params[:bgcolor])
def show
@domain = domain
render :layout => false
end
# allow these parameters when creating feed querystring
feed_params = ['key','days','start','end','round','timescale','average','median','sum','results','location','status']
# save chart options
def update
@channel = Channel.find(params[:channel_id])
@status = 0
# default chart size
@width = Chart.default_width
@height = Chart.default_height
# check permissions
if @channel.user_id == current_user.id
# add extra parameters to querystring
@qs = ''
params.each do |p|
@qs += "&#{p[0]}=#{p[1]}" if feed_params.include?(p[0])
end
# save data
@channel["options#{params[:id]}"] = params[:options]
if @channel.save
@status = 1
end
# fix chart colors if necessary
params[:color] = fix_color(params[:color])
params[:bgcolor] = fix_color(params[:bgcolor])
end
# set ssl
@ssl = (get_header_value('x_ssl') == 'true')
@domain = domain(@ssl)
# return response: 1=success, 0=failure
render :json => @status.to_json
end
# should data be pushed off the end in dynamic chart
@push = (params[:push] and params[:push] == 'false') ? false : true
@results = params[:results]
render :layout => false
end
private
def default_width
450
end
# save chart options
def update
#Check to see if we're using the new options, or the old
def default_height
250
end
@channel = Channel.find(params[:channel_id])
@status = 0
# fixes chart color if user forgets the leading '#'
def fix_color(color)
# check for 3 or 6 character hexadecimal value
if (color and color.match(/^([0-9]|[a-f]|[A-F]){3}(([0-9]|[a-f]|[A-F]){3})?$/))
color = '#' + color
end
# check permissions
if @channel.user_id == current_user.id
logger.debug "Saving Data with new options " + params[:newOptions].to_s
# save data
if params[:newOptions]
logger.debug "Updating new style options on window id #{params[:id]} with #{params[:newOptions][:options]}"
chart_window = @channel.windows.find(params[:id]).becomes(ChartWindow)
chart_window.window_detail.options = params[:newOptions][:options]
if !chart_window.save
raise "Couldn't save the Chart Window"
end
end
if @channel.save
@status = 1
end
return color
end
end
# return response: 1=success, 0=failure
render :json => @status.to_json
end
private
# fixes chart color if user forgets the leading '#'
def fix_color(color)
# check for 3 or 6 character hexadecimal value
if (color and color.match(/^([0-9]|[a-f]|[A-F]){3}(([0-9]|[a-f]|[A-F]){3})?$/))
color = '#' + color
end
return color
end
end

View File

@ -0,0 +1,47 @@
class CommentsController < ApplicationController
before_filter :require_user
def index
redirect_to channel_path(:id => params[:channel_id], :public => true)
end
def create
render :text => '' and return if params[:userlogin].length > 0
@channel = Channel.find(params[:channel_id])
@comment = @channel.comments.new
@comment.user = current_user
@comment.ip_address = get_header_value('X_REAL_IP')
@comment.parent_id = params[:parent_id]
@comment.body = params[:comment][:body].gsub(/<\/?[^>]*>/, '').gsub(/\n/, '<br />')
# save comment
if @comment.save
flash[:success] = "Thanks for adding a comment!"
else
flash[:error] = "Comment can't be blank!"
end
redirect_to :back
end
def vote
# make sure this is a post
render :text => '' and return if !request.post?
@comment = Comment.find(params[:id])
@comment.flags += 1
# delete if too many flags
if (@comment.flags > 3)
@comment.destroy
render :text => ''
# else save
else
@comment.save
render :text => '1'
end
end
def destroy
comment = current_user.comments.find(params[:id]).destroy
redirect_to :back
end
end

View File

@ -0,0 +1,8 @@
class CorsController < ApplicationController
skip_before_filter :verify_authenticity_token
# dummy method that responds with status 200 for CORS preflighting
def preflight; render :nothing => true; end
end

View File

@ -0,0 +1,19 @@
class DocsController < ApplicationController
def index; ;end
def talkback
# default values
@talkback_id = 3
@talkback_api_key = 'XXXXXXXXXXXXXXXX'
# if user is signed in
if current_user && current_user.talkbacks.any?
@talkback = current_user.talkbacks.order('updated_at desc').first
@talkback_id = @talkback.id
@talkback_api_key = @talkback.api_key
end
end
end

View File

@ -1,570 +1,254 @@
class FeedController < ApplicationController
require 'csv'
layout 'application', :except => :index
include FeedHelper
require 'csv'
layout 'application', :except => [:index, :debug]
def index
channel = Channel.find(params[:channel_id])
api_key = ApiKey.find_by_api_key(get_userkey)
@success = channel_permission?(channel, api_key)
def index
feed_factory = FeedFactory.new(params)
channel = Channel.find(params[:channel_id])
api_key = ApiKey.find_by_api_key(get_apikey)
@success = channel_permission?(channel, api_key)
# set timezone correctly
set_time_zone(params)
# set callback for jsonp
@callback = params[:callback] if params[:callback]
# set limits
limit = params[:results].to_i if params[:results]
# set csv headers if necessary
@csv_headers = feed_factory.feed_select_options if params[:format] == 'csv'
# check for access
if @success
# set timezone correctly
set_time_zone(params)
# create options hash
channel_options = { :only => channel_select_data(channel) }
select_options = feed_select_data(channel)
# check for access
if @success
# get feed based on conditions
feeds = Feed.find(
:all,
:conditions => { :channel_id => channel.id, :created_at => get_date_range(params) },
:select => select_options,
:order => 'created_at desc',
:limit => limit
)
# keep track of whether data has been rounded already
rounded = false
# if a feed has data
if !feeds.empty?
# convert to timescales if necessary
if timeparam_valid?(params[:timescale])
feeds = feeds_into_timescales(feeds)
# convert to sums if necessary
elsif timeparam_valid?(params[:sum])
feeds = feeds_into_sums(feeds)
rounded = true
# convert to averages if necessary
elsif timeparam_valid?(params[:average])
feeds = feeds_into_averages(feeds)
rounded = true
# convert to medians if necessary
elsif timeparam_valid?(params[:median])
feeds = feeds_into_medians(feeds)
rounded = true
end
end
# if a feed needs to be rounded
if params[:round] and !rounded
feeds = object_round(feeds, params[:round].to_i)
if feed_factory.cache_feeds
# check cache for stored value
feed_output_cache_key = cache_key('feed_output')
channel_output_cache_key = cache_key('channel_output')
@feed_output = Rails.cache.read(feed_output_cache_key)
@channel_output = Rails.cache.read(channel_output_cache_key)
end
# set output correctly
if params[:format] == 'xml'
@channel_output = channel.to_xml(channel_options).sub('</channel>', '').strip
@feed_output = feeds.to_xml(:skip_instruct => true).gsub(/\n/, "\n ").chop.chop
elsif params[:format] == 'csv'
@feed_output = feeds
else
@channel_output = channel.to_json(channel_options).chop
@feed_output = feeds.to_json
end
# if cache miss, get data
if @feed_output.nil? or @channel_output.nil?
# else no access, set error code
else
if params[:format] == 'xml'
@channel_output = bad_channel_xml
else
@channel_output = '-1'.to_json
end
end
# get feeds
feeds = feed_factory.get_output_feeds
# set callback for jsonp
@callback = params[:callback] if params[:callback]
# set output correctly
if params[:format] == 'xml'
@channel_output = channel.to_xml(channel.select_options).sub('</channel>', '').strip
@feed_output = feeds.to_xml(:skip_instruct => true).gsub(/\n/, "\n ").chop.chop
elsif params[:format] == 'csv'
@feed_output = feeds
else
@channel_output = channel.to_json(channel.select_options).chop
@feed_output = feeds.to_json(:only => feed_factory.feed_select_options)
end
# set csv headers if necessary
@csv_headers = select_options if params[:format] == 'csv'
if feed_factory.cache_feeds
# save to cache
Rails.cache.write(feed_output_cache_key, @feed_output, :expires_in => 5.minutes)
Rails.cache.write(channel_output_cache_key, @channel_output, :expires_in => 5.minutes)
end
# output proper http response if error
render :text => '-1', :status => 400 and return if !@success
end # end if feeds not empty
# output data in proper format
respond_to do |format|
format.html
format.json
format.xml
format.csv
end
end
# else no access, set error code
else
if params[:format] == 'xml'
@channel_output = bad_channel_xml
else
@channel_output = '-1'.to_json
end
end
def show
@channel = Channel.find(params[:channel_id])
@api_key = ApiKey.find_by_api_key(get_userkey)
output = '-1'
# output proper http response if error
render :text => '-1', :status => 400 and return if !@success
# get most recent entry if necessary
params[:id] = @channel.last_entry_id if params[:id] == 'last'
# output data in proper format
respond_to do |format|
format.html
format.json
format.xml
format.csv
end
end
# set timezone correctly
set_time_zone(params)
def last_sum
last_method = method('last_group_call')
last_method.call('sums')
end
@feed = Feed.find(
:first,
:conditions => { :channel_id => @channel.id, :entry_id => params[:id] },
:select => feed_select_data(@channel)
)
@success = channel_permission?(@channel, @api_key)
def last_median
last_method = method('last_group_call')
last_method.call('medians')
end
def last_average
last_method = method('last_group_call')
last_method.call('averages')
end
def last_group_call(arg)
@channel = Channel.find(params[:channel_id])
@api_key = ApiKey.find_by_api_key(get_apikey)
set_time_zone(params)
# limit for the number of results to get
limit = 30
limit = params[:sum].to_i if params[:sum].present?
limit = params[:median].to_i if params[:median].present?
limit = params[:average].to_i if params[:average].present?
# max limit of 100 past results
limit = 100 if limit > 100
# get the last (limit) feeds
last_feeds = Feed.where(:channel_id => @channel.id).limit(limit).order('created_at desc')
# put feeds in correct order (oldest to most recent)
last_feeds.reverse!
feeds_into = self.method("feeds_into_#{arg}")
feed = feeds_into.call(last_feeds, params).last if last_feeds.length > 0
create_group_result(feed)
end
def create_group_result(feed)
@success = channel_permission?(@channel, @api_key)
# if a feed needs to be rounded
if params[:round] && feed.present?
feed = item_round(feed, params[:round].to_i)
end
# check for access
if @success && feed.present?
# set output correctly
if params[:format] == 'xml'
output = feed.to_xml
elsif params[:format] == 'csv'
@csv_headers = Feed.select_options(@channel, params)
elsif (params[:format] == 'txt' or params[:format] == 'text')
output = add_prepend_append(feed["field#{params[:field_id]}"])
else
output = feed.to_json
end
# else set error code
else
if params[:format] == 'xml'
output = bad_feed_xml
else
output = '-1'.to_json
end
end
# output data in proper format
respond_to do |format|
format.html { render :json => output }
format.json { render :json => output, :callback => params[:callback] }
format.xml { render :xml => output }
format.csv
format.text { render :text => output }
end
end
def show
@channel = Channel.find(params[:channel_id])
@api_key = ApiKey.find_by_api_key(get_apikey)
output = '-1'
# set timezone correctly
set_time_zone(params)
# make sure field parameter is set correctly, changes "field1" to "1"
params[:field_id] = params[:field_id].sub('field', '') if params[:field_id].present?
# if last entry
if params[:id] == 'last' && params[:field_id].present? && params[:field_id].to_i != 0
# look for a feed where the value isn't null
@feed = Feed.where(:channel_id => @channel.id)
.where("field? is not null", params[:field_id].to_i)
.select(Feed.select_options(@channel, params))
.order('entry_id desc')
.first
# else get by entry
else
# get most recent entry if necessary
params[:id] = @channel.last_entry_id if params[:id] == 'last'
@feed = Feed.where(:channel_id => @channel.id, :entry_id => params[:id]).select(Feed.select_options(@channel, params)).first
end
@success = channel_permission?(@channel, @api_key)
# if a feed needs to be rounded
if params[:round]
@feed = item_round(@feed, params[:round].to_i)
end
# check for access
if @success
# set output correctly
if params[:format] == 'xml'
output = @feed.to_xml
elsif params[:format] == 'csv'
@csv_headers = feed_select_data(@channel)
elsif (params[:format] == 'txt' or params[:format] == 'text')
output = add_prepend_append(@feed["field#{params[:field_id]}"])
else
output = @feed.to_json
end
# else set error code
else
if params[:format] == 'xml'
output = bad_feed_xml
else
output = '-1'.to_json
end
end
# check for access
if @success and @feed
# output data in proper format
respond_to do |format|
format.html { render :json => output }
format.json { render :json => output, :callback => params[:callback] }
format.xml { render :xml => output }
format.csv
format.text { render :text => output }
end
end
# set output correctly
if params[:format] == 'xml'
output = @feed.to_xml
elsif params[:format] == 'csv'
@csv_headers = Feed.select_options(@channel, params)
elsif (params[:format] == 'txt' or params[:format] == 'text')
output = add_prepend_append(@feed["field#{params[:field_id]}"])
else
output = @feed.to_json
private
# only output these fields for channel
def channel_select_data(channel)
only = [:name, :created_at, :updated_at, :id, :last_entry_id]
only += [:description] unless channel.description.blank?
only += [:latitude] unless channel.latitude.blank?
only += [:longitude] unless channel.longitude.blank?
only += [:elevation] unless channel.elevation.blank?
only += [:field1] unless channel.field1.blank?
only += [:field2] unless channel.field2.blank?
only += [:field3] unless channel.field3.blank?
only += [:field4] unless channel.field4.blank?
only += [:field5] unless channel.field5.blank?
only += [:field6] unless channel.field6.blank?
only += [:field7] unless channel.field7.blank?
only += [:field8] unless channel.field8.blank?
return only
end
# only output these fields for feed
def feed_select_data(channel)
only = [:created_at]
only += [:entry_id] unless timeparam_valid?(params[:timescale]) or timeparam_valid?(params[:average]) or timeparam_valid?(params[:median]) or timeparam_valid?(params[:sum])
only += [:field1] unless channel.field1.blank? or (params[:field_id] and params[:field_id] != '1')
only += [:field2] unless channel.field2.blank? or (params[:field_id] and params[:field_id] != '2')
only += [:field3] unless channel.field3.blank? or (params[:field_id] and params[:field_id] != '3')
only += [:field4] unless channel.field4.blank? or (params[:field_id] and params[:field_id] != '4')
only += [:field5] unless channel.field5.blank? or (params[:field_id] and params[:field_id] != '5')
only += [:field6] unless channel.field6.blank? or (params[:field_id] and params[:field_id] != '6')
only += [:field7] unless channel.field7.blank? or (params[:field_id] and params[:field_id] != '7')
only += [:field8] unless channel.field8.blank? or (params[:field_id] and params[:field_id] != '8')
# add geolocation data if necessary
if params[:location] and params[:location].upcase == 'TRUE'
only += [:latitude]
only += [:longitude]
only += [:elevation]
end
# add status if necessary
only += [:status] if params[:status] and params[:status].upcase == 'TRUE'
return only
end
# checks for valid timescale
def timeparam_valid?(timeparam)
valid_minutes = [10, 15, 20, 30, 60, 240, 720, 1440]
if timeparam and valid_minutes.include?(timeparam.to_i)
return true
else
return false
end
end
# applies rounding to an enumerable object
def object_round(object, round=nil, match='field')
object.each_with_index do |o, index|
object[index] = item_round(o, round, match)
end
return object
# else set error code
else
if params[:format] == 'xml'
output = bad_feed_xml
else
output = '-1'.to_json
end
end
# applies rounding to a single item's attributes if necessary
def item_round(item, round=nil, match='field')
# for each attribute
item.attribute_names.each do |attr|
# only add non-null numeric fields
if attr.index(match) and !item[attr].nil? and is_a_number?(item[attr])
# keep track of whether the value contains commas
comma_flag = (item[attr].to_s.index(',')) ? true : false
# replace commas with decimals if appropriate
item[attr] = item[attr].to_s.gsub(/,/, '.') if comma_flag
# do the actual rounding
item[attr] = sprintf "%.#{round}f", item[attr]
# replace decimals with commas if appropriate
item[attr] = item[attr].to_s.gsub(/\./, ',') if comma_flag
end
end
# output new item
return item
# output data in proper format
respond_to do |format|
format.html { render :json => output }
format.json { render :json => output, :callback => params[:callback] }
format.xml { render :xml => output }
format.csv
format.text { render :text => output }
end
end
# slice feed into timescales
def feeds_into_timescales(feeds)
# convert timescale (minutes) into seconds
seconds = params[:timescale].to_i * 60
# get floored time ranges
start_time = get_floored_time(feeds.first.created_at, seconds)
end_time = get_floored_time(feeds.last.created_at, seconds)
def debug
@time_start = Time.now
# create empty array with appropriate size
timeslices = Array.new((((end_time - start_time) / seconds).abs).floor)
channel = Channel.find(params[:channel_id])
api_key = ApiKey.find_by_api_key(get_apikey)
@success = channel_permission?(channel, api_key)
# create options hash
select_options = Feed.select_options(channel, params)
# create a blank clone of the first feed so that we only get the necessary attributes
empty_feed = create_empty_clone(feeds.first)
# get feed based on conditions
feeds = Feed.find(
:all,
:conditions => { :channel_id => channel.id, :created_at => get_date_range(params) },
:select => select_options,
:order => 'created_at desc'
)
@count = feeds.count
@time_after_db = Time.now
# add feeds to array
feeds.each do |f|
i = ((f.created_at - start_time) / seconds).floor
f.created_at = start_time + i * seconds
timeslices[i] = f if timeslices[i].nil?
end
# sort properly
feeds.reverse!
# fill in empty array elements
timeslices.each_index do |i|
if timeslices[i].nil?
current_feed = empty_feed.clone
current_feed.created_at = (start_time + (i * seconds))
timeslices[i] = current_feed
end
end
@time_after_sort = Time.now
return timeslices
end
@channel_output = channel.to_json(channel.select_options).chop
@feed_output = feeds.to_json
# slice feed into averages
def feeds_into_averages(feeds)
# convert timescale (minutes) into seconds
seconds = params[:average].to_i * 60
# get floored time ranges
start_time = get_floored_time(feeds.first.created_at, seconds)
end_time = get_floored_time(feeds.last.created_at, seconds)
# create empty array with appropriate size
timeslices = Array.new(((end_time - start_time) / seconds).floor)
# create a blank clone of the first feed so that we only get the necessary attributes
empty_feed = create_empty_clone(feeds.first)
# add feeds to array
feeds.each do |f|
i = ((f.created_at - start_time) / seconds).floor
f.created_at = start_time + i * seconds
# create multidimensional array
timeslices[i] = [] if timeslices[i].nil?
timeslices[i].push(f)
end
# keep track of whether numbers use commas as decimals
comma_flag = false
# fill in array
timeslices.each_index do |i|
# insert empty values
if timeslices[i].nil?
current_feed = empty_feed.clone
current_feed.created_at = (start_time + (i * seconds))
timeslices[i] = current_feed
# else average the inner array
else
sum_feed = empty_feed.clone
sum_feed.created_at = timeslices[i].first.created_at
# for each feed
timeslices[i].each do |f|
# for each attribute, add to sum_feed so that we have the total
sum_feed.attribute_names.each do |attr|
# only add non-null integer fields
if attr.index('field') and !f[attr].nil? and is_a_number?(f[attr])
# set comma_flag once if we find a number with a comma
comma_flag = true if !comma_flag and f[attr].to_s.index(',')
# set initial data
if sum_feed[attr].nil?
sum_feed[attr] = parsefloat(f[attr])
# add data
elsif f[attr]
sum_feed[attr] = parsefloat(sum_feed[attr]) + parsefloat(f[attr])
end
end
end
end
# set to the averaged feed
timeslices[i] = object_average(sum_feed, timeslices[i].length, comma_flag, params[:round])
end
end
return timeslices
end
# slice feed into medians
def feeds_into_medians(feeds)
# convert timescale (minutes) into seconds
seconds = params[:median].to_i * 60
# get floored time ranges
start_time = get_floored_time(feeds.first.created_at, seconds)
end_time = get_floored_time(feeds.last.created_at, seconds)
# create empty array with appropriate size
timeslices = Array.new(((end_time - start_time) / seconds).floor)
# create a blank clone of the first feed so that we only get the necessary attributes
empty_feed = create_empty_clone(feeds.first)
# add feeds to array
feeds.each do |f|
i = ((f.created_at - start_time) / seconds).floor
f.created_at = start_time + i * seconds
# create multidimensional array
timeslices[i] = [] if timeslices[i].nil?
timeslices[i].push(f)
end
# keep track of whether numbers use commas as decimals
comma_flag = false
# fill in array
timeslices.each_index do |i|
# insert empty values
if timeslices[i].nil?
current_feed = empty_feed.clone
current_feed.created_at = (start_time + (i * seconds))
timeslices[i] = current_feed
# else get median values for the inner array
else
# create blank hash called 'fields' to hold data
fields = {}
# for each feed
timeslices[i].each do |f|
# for each attribute
f.attribute_names.each do |attr|
if attr.index('field')
# create blank array for each field
fields["#{attr}"] = [] if fields["#{attr}"].nil?
# push numeric field data onto its array
if is_a_number?(f[attr])
# set comma_flag once if we find a number with a comma
comma_flag = true if !comma_flag and f[attr].to_s.index(',')
fields["#{attr}"].push(parsefloat(f[attr]))
end
end
end
end
# sort fields arrays
fields.each_key do |key|
fields[key] = fields[key].compact.sort
end
# get the median
median_feed = empty_feed.clone
median_feed.created_at = timeslices[i].first.created_at
median_feed.attribute_names.each do |attr|
median_feed[attr] = object_median(fields[attr], comma_flag, params[:round]) if attr.index('field')
end
timeslices[i] = median_feed
end
end
return timeslices
end
# slice feed into sums
def feeds_into_sums(feeds)
# convert timescale (minutes) into seconds
seconds = params[:sum].to_i * 60
# get floored time ranges
start_time = get_floored_time(feeds.first.created_at, seconds)
end_time = get_floored_time(feeds.last.created_at, seconds)
# create empty array with appropriate size
timeslices = Array.new(((end_time - start_time) / seconds).floor)
# create a blank clone of the first feed so that we only get the necessary attributes
empty_feed = create_empty_clone(feeds.first)
# add feeds to array
feeds.each do |f|
i = ((f.created_at - start_time) / seconds).floor
f.created_at = start_time + i * seconds
# create multidimensional array
timeslices[i] = [] if timeslices[i].nil?
timeslices[i].push(f)
end
# keep track of whether numbers use commas as decimals
comma_flag = false
# fill in array
timeslices.each_index do |i|
# insert empty values
if timeslices[i].nil?
current_feed = empty_feed.clone
current_feed.created_at = (start_time + (i * seconds))
timeslices[i] = current_feed
# else sum the inner array
else
sum_feed = empty_feed.clone
sum_feed.created_at = timeslices[i].first.created_at
# for each feed
timeslices[i].each do |f|
# for each attribute, add to sum_feed so that we have the total
sum_feed.attribute_names.each do |attr|
# only add non-null integer fields
if attr.index('field') and !f[attr].nil? and is_a_number?(f[attr])
# set comma_flag once if we find a number with a comma
comma_flag = true if !comma_flag and f[attr].to_s.index(',')
# set initial data
if sum_feed[attr].nil?
sum_feed[attr] = parsefloat(f[attr])
# add data
elsif f[attr]
sum_feed[attr] = parsefloat(sum_feed[attr]) + parsefloat(f[attr])
end
end
end
end
# set to the summed feed
timeslices[i] = object_sum(sum_feed, comma_flag, params[:round])
end
end
return timeslices
end
def is_a_number?(s)
s.to_s.gsub(/,/, '.').match(/\A[+-]?\d+?(\.\d+)?\Z/) == nil ? false : true
end
def parsefloat(number)
return number.to_s.gsub(/,/, '.').to_f
end
# gets the median for an object
def object_median(object, comma_flag=false, round=nil)
return nil if object.nil?
length = object.length
return nil if length == 0
output = ''
# do the calculation
if length % 2 == 0
output = (object[(length - 1) / 2] + object[length / 2]) / 2
else
output = object[(length - 1) / 2]
end
output = sprintf "%.#{round}f", output if round and is_a_number?(output)
# replace decimals with commas if appropriate
output = output.to_s.gsub(/\./, ',') if comma_flag
return output.to_s
end
# averages a summed object over length
def object_average(object, length, comma_flag=false, round=nil)
object.attribute_names.each do |attr|
# only average non-null integer fields
if !object[attr].nil? and is_a_number?(object[attr])
if round
object[attr] = sprintf "%.#{round}f", (parsefloat(object[attr]) / length)
else
object[attr] = (parsefloat(object[attr]) / length).to_s
end
# replace decimals with commas if appropriate
object[attr] = object[attr].gsub(/\./, ',') if comma_flag
end
end
return object
end
# formats a summed object correctly
def object_sum(object, comma_flag=false, round=nil)
object.attribute_names.each do |attr|
# only average non-null integer fields
if !object[attr].nil? and is_a_number?(object[attr])
if round
object[attr] = sprintf "%.#{round}f", parsefloat(object[attr])
else
object[attr] = parsefloat(object[attr]).to_s
end
# replace decimals with commas if appropriate
object[attr] = object[attr].gsub(/\./, ',') if comma_flag
end
end
return object
end
# creates an empty clone of an object
def create_empty_clone(object)
empty_clone = object.dup
empty_clone.attribute_names.each { |attr| empty_clone[attr] = nil }
return empty_clone
end
# gets time floored to proper interval
def get_floored_time(input_time, seconds)
return Time.zone.at((input_time.to_f / seconds).floor * seconds)
end
@time_after_json = Time.now
end
end

View File

@ -1,20 +1,23 @@
class MailerController < ApplicationController
def resetpassword
@user = User.find_by_login_or_email(params[:user][:login])
def resetpassword
# protect against bots
render :text => '' and return if params[:userlogin].length > 0
if @user.nil?
session[:mail_message] = t(:account_not_found)
else
begin
@user.reset_perishable_token!
# Mailer.password_reset(@user, "https://www.thingspeak.com/users/#{@user.id}/reset_password?token=#{@user.perishable_token}").deliver
session[:mail_message] = t(:password_reset_mailed)
rescue
session[:mail_message] = t(:password_reset_error)
end
end
redirect_to login_path
end
@user = User.find_by_login_or_email(params[:user][:login])
if @user.nil?
sleep 2
session[:mail_message] = t(:account_not_found)
else
begin
@user.reset_perishable_token!
Mailer.password_reset(@user, "#{RESET_PASSWORD_URL}#{@user.id}?token=#{@user.perishable_token}").deliver
session[:mail_message] = t(:password_reset_mailed)
rescue
session[:mail_message] = t(:password_reset_error)
end
end
redirect_to login_path
end
end

View File

@ -0,0 +1,44 @@
class MapsController < ApplicationController
# show map with channel's location
def channel_show
set_map_vars
render :layout => false
end
# show social map with feed points as markers
def show
set_map_vars
render :layout => false
end
# set map variables
def set_map_vars
# allow these parameters when creating feed querystring
feed_params = ['key','days','start','end','round','timescale','average','median','sum','results','status']
# default map size
@width = default_width
@height = default_height
# add extra parameters to querystring
@qs = ''
params.each do |p|
@qs += "&#{p[0]}=#{p[1]}" if feed_params.include?(p[0])
end
# set ssl
@ssl = (get_header_value('x_ssl') == 'true')
@map_domain = @ssl ? 'https://maps-api-ssl.google.com' : 'http://maps.google.com'
@domain = domain(@ssl)
end
private
def default_width
450
end
def default_height
250
end
end

View File

@ -1,7 +1,23 @@
class PagesController < ApplicationController
layout 'application', :except => [:social_home]
def home
@menu = 'home'
end
def home
@menu = 'home'
@title = 'Internet of Things'
end
def social_home; ; end
def features
@menu = 'features'
end
def about
@menu = 'about'
end
def headers
end
end

View File

@ -0,0 +1,16 @@
class PipesController < ApplicationController
before_filter :require_admin, :set_admin_menu
def index
@pipes = Pipe.paginate :page => params[:page], :order => 'created_at DESC'
end
def new
@pipe = Pipe.new
end
def create
end
end

View File

@ -0,0 +1,161 @@
class PluginsController < ApplicationController
before_filter :require_user, :except => [:show_public, :show]
before_filter :set_plugins_menu
before_filter :check_permission, :only => ['edit', 'update', 'ajax_update', 'destroy']
def check_permission
@plugin = Plugin.find(params[:id])
if @plugin.user_id != current_user.id
render :text=> "#{t(:permission)} #{t(:plugin)}", :layout => true
return true
end
return false
end
def index
@plugins = current_user.plugins
end
def public_plugins
channel_id = params[:channel_id].to_i
return if channel_id.nil?
#private page should display all plugins
#plugins = current_user.plugins.where("private_flag = true")
@plugin_windows = []
plugins = current_user.plugins
plugins.each do |plugin|
plugin.make_windows channel_id, api_domain #will only make the window the first time
@plugin_windows = @plugin_windows + plugin.public_dashboard_windows(channel_id)
end
respond_to do |format|
format.html { render :partial => 'plugins' }
end
end
def private_plugins
channel_id = params[:channel_id].to_i
return if channel_id.nil?
#private page should display all plugins
@plugin_windows = []
plugins = current_user.plugins
plugins.each do |plugin|
plugin.make_windows channel_id, api_domain #will only make the window the first time
@plugin_windows = @plugin_windows + plugin.private_dashboard_windows(channel_id)
end
respond_to do |format|
format.html { render :partial => 'plugins' }
end
end
def create
# add plugin with defaults
@plugin = Plugin.new
@plugin.html = read_file('app/views/plugins/default.html')
@plugin.css = read_file('app/views/plugins/default.css')
@plugin.js = read_file('app/views/plugins/default.js')
@plugin.user_id = current_user.id
@plugin.private_flag = true
@plugin.save
# now that the plugin is saved, we can create the default name
@plugin.name = "#{t(:plugin_default_name)} #{@plugin.id}"
@plugin.save
# redirect to edit the newly created plugin
redirect_to edit_plugin_path(@plugin.id)
end
def show
# Have to check permissions in the method so I can use show to display public, or private plugins
@plugin = Plugin.find(params[:id])
if @plugin.private?
return if require_user
render :text=> "#{t(:permission)} #{t(:plugin)}", :layout => true and return if check_permission
end
@output = @plugin.html.sub('%%PLUGIN_CSS%%', @plugin.css).sub('%%PLUGIN_JAVASCRIPT%%', @plugin.js)
if @plugin.private?
render :layout => false and return
else
if request.url.include? api_domain
render :layout => false and return
else
protocol = ssl
host = api_domain.split('://')[1]
redirect_to :host => host,
:protocol => protocol,
:controller => "plugins",
:action => "show",
:id => @plugin.id and return
end
end
end
def show_public
@plugin = Plugin.find(params[:id])
@output = @plugin.html.sub('%%PLUGIN_CSS%%', @plugin.css).sub('%%PLUGIN_JAVASCRIPT%%', @plugin.js)
if @plugin.private?
render :layout => false
else
if request.url.include? 'api_domain'
render :layout => false
else
redirect_to :host => api_domain,
:controller => "plugins",
:action => "show",
:id => @plugin.id
end
end
end
def edit
end
def update
@plugin.update_attribute(:name, params[:plugin][:name])
@plugin.update_attribute(:private_flag, params[:plugin][:private_flag])
@plugin.update_attribute(:css, params[:plugin][:css])
@plugin.update_attribute(:js, params[:plugin][:js])
@plugin.update_attribute(:html,params[:plugin][:html])
if @plugin.save
@plugin.update_all_windows
redirect_to plugins_path and return
end
end
def ajax_update
status = 0
@plugin.update_attribute(:name, params[:plugin][:name])
@plugin.update_attribute(:private_flag, params[:plugin][:private_flag])
@plugin.update_attribute(:css, params[:plugin][:css])
@plugin.update_attribute(:js, params[:plugin][:js])
@plugin.update_attribute(:html, params[:plugin][:html])
if @plugin.save
@plugin.update_all_windows
status = 1
end
# return response: 1=success, 0=failure
render :json => status.to_json
end
def destroy
@plugin.destroy
redirect_to plugins_path
end
end

View File

@ -1,114 +1,158 @@
class StatusController < ApplicationController
require 'csv'
require 'csv'
layout false
def index
@channel = Channel.find(params[:channel_id])
@api_key = ApiKey.find_by_api_key(get_userkey)
@success = channel_permission?(@channel, @api_key)
def recent
logger.info "Domain is #{@domain}"
channel = Channel.find(params[:channel_id])
@channel_id = channel.id
if channel.public_flag || (current_user && current_user.id == channel.user_id)
@statuses = channel.recent_statuses
respond_to do |format|
format.html { render :partial => 'status/recent' }
format.json { render :json => @statuses}
end
else
respond_to do |format|
format.json { render :json => 'Status are not public' }
format.html { render :text => 'Sorry the statuses are not public' }
end
end
end
# check for access
if @success
# create options hash
channel_options = { :only => channel_select_terse(@channel) }
# display only 1 day by default
params[:days] = 1 if !params[:days]
def index
@channel = Channel.find(params[:channel_id])
@api_key = ApiKey.find_by_api_key(get_apikey)
@success = channel_permission?(@channel, @api_key)
# get feed based on conditions
@feeds = Feed.find(
:all,
:conditions => { :channel_id => @channel.id, :created_at => get_date_range(params) },
:select => [:created_at, :status],
:order => 'created_at'
)
# check for access
if @success
# create options hash
channel_options = { :only => channel_select_terse(@channel) }
# set output correctly
if params[:format] == 'xml'
@channel_output = @channel.to_xml(channel_options).sub('</channel>', '').strip
@feed_output = @feeds.to_xml(:skip_instruct => true).gsub(/\n/, "\n ").chop.chop
elsif params[:format] == 'csv'
@csv_headers = [:created_at, :status]
@feed_output = @feeds
else
@channel_output = @channel.to_json(channel_options).chop
@feed_output = @feeds.to_json
end
# else set error code
else
if params[:format] == 'xml'
@channel_output = bad_channel_xml
else
@channel_output = '-1'.to_json
end
end
# display only 1 day by default
params[:days] = 1 if !params[:days]
# set callback for jsonp
@callback = params[:callback] if params[:callback]
# output data in proper format
respond_to do |format|
format.html { render :text => @feed_output }
format.json { render "feed/index" }
format.xml { render "feed/index" }
format.csv { render "feed/index" }
end
end
# set limits
limit = (request.format == 'csv') ? 1000000 : 8000
limit = params[:results].to_i if (params[:results] and params[:results].to_i < 8000)
def show
@channel = Channel.find(params[:channel_id])
@api_key = ApiKey.find_by_api_key(params[:key])
output = '-1'
# get feed based on conditions
@feeds = @channel.feeds.find(
:all,
:conditions => { :created_at => get_date_range(params) },
:select => [:created_at, :entry_id, :status],
:order => 'created_at desc',
:limit => limit
)
# get most recent entry if necessary
params[:id] = @channel.last_entry_id if params[:id] == 'last'
# sort properly
@feeds.reverse!
@feed = Feed.find(
:first,
:conditions => { :channel_id => @channel.id, :entry_id => params[:id] },
:select => [:created_at, :status]
)
@success = channel_permission?(@channel, @api_key)
# check for access
if @success
# set output correctly
if params[:format] == 'xml'
output = @feed.to_xml
elsif params[:format] == 'csv'
@csv_headers = [:created_at, :entry_id, :status]
elsif (params[:format] == 'txt' or params[:format] == 'text')
output = add_prepend_append(@feed.status)
else
output = @feed.to_json
end
# else set error code
else
if params[:format] == 'xml'
output = bad_feed_xml
else
output = '-1'.to_json
end
end
# set output correctly
if request.format == 'xml'
@channel_output = @channel.to_xml(channel_options).sub('</channel>', '').strip
@feed_output = @feeds.to_xml(:skip_instruct => true).gsub(/\n/, "\n ").chop.chop
elsif request.format == 'csv'
@csv_headers = [:created_at, :entry_id, :status]
@feed_output = @feeds
else
@channel_output = @channel.to_json(channel_options).chop
@feed_output = @feeds.to_json
end
# else set error code
else
if params[:format] == 'xml'
@channel_output = bad_channel_xml
else
@channel_output = '-1'.to_json
# output data in proper format
respond_to do |format|
format.html { render :json => output }
format.json { render :json => output, :callback => params[:callback] }
format.xml { render :xml => output }
format.csv { render :action => 'feed/show' }
format.text { render :text => output }
end
end
end
end
# set callback for jsonp
@callback = params[:callback] if params[:callback]
# output data in proper format
respond_to do |format|
format.html { render :template => 'feed/index' }
format.json { render :template => 'feed/index' }
format.xml { render :template => 'feed/index' }
format.csv { render :template => 'feed/index' }
end
end
def show
@channel = Channel.find(params[:channel_id])
@api_key = ApiKey.find_by_api_key(get_apikey)
output = '-1'
# get most recent entry if necessary
params[:id] = @channel.last_entry_id if params[:id] == 'last'
@feed = @channel.feeds.find(
:first,
:conditions => { :entry_id => params[:id] },
:select => [:created_at, :entry_id, :status]
)
@success = channel_permission?(@channel, @api_key)
# check for access
if @success
# set output correctly
if request.format == 'xml'
output = @feed.to_xml
elsif request.format == 'csv'
@csv_headers = [:created_at, :entry_id, :status]
elsif (request.format == 'txt' or request.format == 'text')
output = add_prepend_append(@feed.status)
else
output = @feed.to_json
end
# else set error code
else
if request.format == 'xml'
output = bad_feed_xml
else
output = '-1'.to_json
end
end
# output data in proper format
respond_to do |format|
format.html { render :json => output }
format.json { render :json => output, :callback => params[:callback] }
format.xml { render :xml => output }
format.csv { render :action => 'feed/show' }
format.text { render :text => output }
end
end
private
# only output these fields for channel
def channel_select_terse(channel)
only = [:name]
only += [:latitude] unless channel.latitude.nil?
only += [:longitude] unless channel.longitude.nil?
only += [:elevation] unless channel.elevation.nil? or channel.elevation.empty?
return only
end
private
# only output these fields for channel
def channel_select_terse(channel)
only = [:name]
only += [:latitude] unless channel.latitude.nil?
only += [:longitude] unless channel.longitude.nil?
only += [:elevation] unless channel.elevation.nil? or channel.elevation.empty?
return only
end
end

View File

@ -1,15 +1,15 @@
class SubdomainsController < ApplicationController
# show a blank page if subdomain
def index
render :text => ''
end
# show a blank page if subdomain
def index
render :text => ''
end
# output the file crossdomain.xml.erb
def crossdomain
respond_to do |format|
format.xml
end
end
# output the file crossdomain.xml.erb
def crossdomain
respond_to do |format|
format.xml
end
end
end

View File

@ -0,0 +1,37 @@
class TagsController < ApplicationController
def index
render 'show' and return if params[:channel_id].nil?
channel = Channel.find(params[:channel_id])
if current_user && channel.nil?
tag = Tag.find_by_name(params[:id], :include => :channels, :conditions => ['channels.public_flag = true OR channels.user_id = ?', current_user.id])
else
channels = []
channel.tags.each do |tag|
channels << tag.channel_ids
end
channels = channels.flatten.uniq
end
redirect_to public_channels_path(:channel_ids => channels)
end
def create
redirect_to tag_path(params[:tag][:name])
end
def show
# if user is logged in, search their channels also
if current_user
tag = Tag.find_by_name(params[:id], :include => :channels, :conditions => ['channels.public_flag = true OR channels.user_id = ?', current_user.id])
# else only search public channels
else
tag = Tag.find_by_name(params[:id], :include => :channels, :conditions => ['channels.public_flag = true'])
end
@results = tag.channels if tag
end
end

View File

@ -3,42 +3,53 @@ class UserSessionsController < ApplicationController
before_filter :require_user, :only => :destroy
def new
@title = t(:signin)
@title = t(:signin)
@user_session = UserSession.new
@mail_message = session[:mail_message] if !session[:mail_message].nil?
@mail_message = session[:mail_message] if !session[:mail_message].nil?
end
def show
redirect_to root_path
end
def show
redirect_to root_path
end
def create
if params[:userlogin].length > 0
render :text => ''
else
@user_session = UserSession.new(params[:user_session])
# remember user_id if checkbox is checked
if params[:user_session][:remember_id] == '1'
cookies['user_id'] = { :value => params[:user_session][:login], :expires => 1.month.from_now }
else
cookies.delete 'user_id'
end
if params[:userlogin].length > 0
render :text => ''
else
@user_session = UserSession.new(params[:user_session])
if @user_session.save
redirect_to root_path and return
else
# prevent timing and brute force password attacks
sleep 1
@failed = true
render :action => :new
end
end
# remember user_id if checkbox is checked
if params[:user_session][:remember_id] == '1'
cookies['user_id'] = { :value => params[:user_session][:login], :expires => 1.month.from_now }
else
cookies.delete 'user_id'
end
if @user_session.save
# if link_back, redirect back
redirect_to session[:link_back] and return if session[:link_back]
redirect_to channels_path and return
else
# log to failedlogins
failed = Failedlogin.new
failed.login = params[:user_session][:login]
failed.password = params[:user_session][:password]
failed.ip_address = get_header_value('X_REAL_IP')
failed.save
# prevent timing and brute force password attacks
sleep 1
@failed = true
render :action => :new
end
end
end
def destroy
session[:link_back] = nil
current_user_session.destroy
reset_session
redirect_to root_path
reset_session
redirect_to root_path
end
end
end

View File

@ -1,85 +1,180 @@
class UsersController < ApplicationController
include KeyUtilities
before_filter :require_no_user, :only => [:new, :create, :forgot_password]
before_filter :require_user, :only => [:show, :edit, :update, :change_password]
before_filter :require_user, :only => [:show, :edit, :update, :change_password, :edit_profile]
# generates a new api key
def new_api_key
current_user.set_new_api_key!
redirect_to account_path
end
# edit public profile
def edit_profile
@user = current_user
end
# update public profile
def update_profile
@user = current_user # makes our views "cleaner" and more consistent
# update
@user.update_attributes(user_params)
redirect_to account_path
end
# public profile for a user
def profile
# set params and request.format correctly
set_request_details!(params)
@user = User.find_by_login(params[:id])
# output error if user not found
render :text => t(:user_not_found) and return if @user.nil?
# if a json or xml request
if request.format == :json || request.format == :xml
# authenticate the user if api key matches the target user
authenticated = (User.find_by_api_key(get_apikey) == @user)
# set options correctly
options = authenticated ? User.private_options : User.public_options(@user)
end
respond_to do |format|
format.html
format.json { render :json => @user.as_json(options) }
format.xml { render :xml => @user.to_xml(options) }
end
end
# list all public channels for a user
def list_channels
@user = User.find_by_login(params[:id])
# output error if user not found
render :text => t(:user_not_found) and return if @user.nil?
# if html request
if request.format == :html
@title = "Internet of Things - Public Channels for #{@user.login}"
@channels = @user.channels.public_viewable.paginate :page => params[:page], :order => 'last_entry_id DESC'
# if a json or xml request
elsif request.format == :json || request.format == :xml
# authenticate the user if api key matches the target user
authenticated = (User.find_by_api_key(get_apikey) == @user)
# get all channels if authenticated, otherwise only public ones
channels = authenticated ? @user.channels : @user.channels.public_viewable
# set channels correctly
@channels = { channels: channels.as_json(Channel.public_options) }
end
respond_to do |format|
format.html
format.json { render :json => @channels }
format.xml { render :xml => @channels.to_xml(:root => 'response') }
end
end
def new
@title = t(:signup)
@title = t(:signup)
@user = User.new
end
def create
@user = User.new(params[:user])
@user = User.new(user_params)
@user.api_key = generate_api_key(16, 'user')
# save user
if @user.valid?
if @user.save
redirect_back_or_default account_path
end
else
render :action => :new
end
# save user
if @user.valid?
if @user.save
redirect_back_or_default channels_path and return
end
else
render :action => :new
end
end
def show
@menu = 'account'
@user = current_user
@menu = 'account'
@user = @current_user
end
def edit
@menu = 'account'
@user = current_user
@menu = 'account'
@user = @current_user
end
# displays forgot password page
def forgot_password
end
# displays forgot password page
def forgot_password
@user = User.new
end
# this action is called from an email link when a password reset is requested
def reset_password
# if user has been logged in (due to previous form submission)
if !current_user.nil?
@user = current_user
@user.errors.add(t(:password_problem))
@valid_link = true
else
@user = User.find_by_id(params[:id])
# make sure tokens match and password reset is within last 10 minutes
if @user.perishable_token == params[:token] && @user.updated_at > 600.seconds.ago
@valid_link = true
# log the user in
@user_session = UserSession.new(@user)
@user_session.save
end
end
end
# do the actual password change
def change_password
# protect against bots
render :text => '' and return if params[:userlogin].length > 0
# this action is called from an email link when a password reset is requested
def reset_password
# if user has been logged in (due to previous form submission)
if !current_user.nil?
@user = current_user
@user.errors.add(:base, t(:password_problem))
@valid_link = true
else
@user = User.find_by_id(params[:id])
# make sure tokens match and password reset is within last 10 minutes
if @user.perishable_token == params[:token] && @user.updated_at > 600.seconds.ago
@valid_link = true
# log the user in
@user_session = UserSession.new(@user)
@user_session.save
end
end
end
# do the actual password change
def change_password
@user = current_user
# if no password entered, redirect
redirect_to reset_password_path and return if params[:user][:password].empty?
# check current password and update
if @user.update_attributes(params[:user])
# if no password entered, redirect
redirect_to reset_password_path and return if params[:user][:password].empty?
# check current password and update
if @user.update_attributes(user_params)
redirect_to account_path
else
redirect_to reset_password_path
end
end
end
def update
@menu = 'account'
@user = current_user # makes our views "cleaner" and more consistent
# check current password and update
if @user.valid_password?(params[:password_current]) && @user.update_attributes(params[:user])
@menu = 'account'
@user = @current_user # makes our views "cleaner" and more consistent
# check current password and update
if @user.valid_password?(params[:password_current]) && @user.update_attributes(user_params)
redirect_to account_path
else
@user.errors.add :base, t(:password_incorrect)
render :edit
@user.errors.add(:base, t(:password_incorrect))
render :action => :edit
end
end
end
private
# only allow these params
def user_params
params.require(:user).permit(:email, :login, :time_zone, :public_flag, :bio, :website, :password, :password_confirmation)
end
# set params[:id] and request.format correctly
def set_request_details!(params)
# set format
new_format = 'html' if params[:glob].end_with?('.html')
new_format = 'json' if params[:glob].end_with?('.json')
new_format = 'xml' if params[:glob].end_with?('.xml')
# remove the format from the end of the glob
params[:id] = params[:glob].chomp(".#{new_format}")
# set the new format if it exists
request.format = new_format.to_sym if new_format.present?
end
end

View File

@ -0,0 +1,219 @@
class WindowsController < ApplicationController
before_filter :require_user, :except => [:index, :html, :iframe]
def hide
window = Window.find(params[:id])
window.show_flag = false
if window.save
render :text => window.id.to_s
else
render :text => '-1'
end
end
# Call WindowsController.display when we want to display a window on the dashboard
# params[:visibility_flag] is whether it is the private or public dashboard
# params[:plugin] is for displaying a plugin, instead of a window
# params[:id] is the window ID for conventional windows, but the plugin_id for plugins
# params[:channel_id] is the channel_id
def display
@visibility = params[:visibility_flag]
window = Window.find(params[:id])
window = Window.new if window.nil?
window.show_flag = true
#Just save this change, then modify the object before rendering the JSON
savedWindow = window.save
config_window window
@mychannel = current_user && current_user.id == window.channel.user_id
if savedWindow
render :json => window.to_json
else
render :json => 'An error occurred'.to_json
end
end
def config_window(window)
if window.type == "PluginWindow"
pluginName = Plugin.find(window.window_detail.plugin_id).name
window.title = t(window.title, {:name => pluginName})
elsif window.type == "ChartWindow"
window.title = t(window.title, {:field_number => window.window_detail.field_number})
options = window.becomes(ChartWindow).window_detail.options if !window.becomes(ChartWindow).window_detail.nil?
options ||= ""
window.html["::OPTIONS::"] = options unless window.html.nil? || window.html.index("::OPTIONS::").nil?
else
window.title = t(window.title)
end
end
def html
window = Window.find(params[:id])
options = window.window_detail.options unless window.window_detail.nil? || window.type!="ChartWindow"
window.html["::OPTIONS::"] = options unless window.html.nil? || window.html.index("::OPTIONS::").nil?
html = window.html
render :text => html
end
def iframe
window = Window.find(params[:id])
options = window.window_detail.options unless window.window_detail.nil? || window.type!="ChartWindow"
window.html["::OPTIONS::"] = options unless window.html.nil? || window.html.index("::OPTIONS::").nil?
iframe_html = window.html
iframe_html = iframe_html.gsub(/src=\"[\/.]/, 'src="' + api_domain);
render :text => iframe_html
end
def index
channel = Channel.find(params[:channel_id])
channel.update_status_portlet false if (channel.windows.select { |w| w.wtype == :status && w.private_flag == false } )
channel.update_status_portlet true if (channel.windows.select { |w| w.wtype == :status && w.private_flag == true } )
channel.update_video_portlet false if (channel.windows.select { |w| w.wtype == :video && w.private_flag == false } )
channel.update_video_portlet true if (channel.windows.select { |w| w.wtype == :video && w.private_flag == true } )
channel.update_location_portlet false if (channel.windows.select { |w| w.wtype == :location && w.private_flag == false } )
channel.update_location_portlet true if (channel.windows.select { |w| w.wtype == :location && w.private_flag == true } )
channel.update_chart_portlets if (channel.windows.select { |w| w.wtype == :chart } )
windows = channel.public_windows(true).order(:position) unless params[:channel_id].nil?
if channel.recent_statuses.nil? || channel.recent_statuses.size <= 0
@windows = windows.delete_if { |w| w.wtype == "status" }
else
@windows = windows
end
@windows.each do |window|
if window.type == "PluginWindow"
pluginName = Plugin.find(window.window_detail.plugin_id).name
window.title = t(window.title, {:name => pluginName})
elsif window.type == "ChartWindow"
window.title = t(window.title, {:field_number => window.window_detail.field_number})
options = window.becomes(ChartWindow).window_detail.options if !window.becomes(ChartWindow).window_detail.nil?
options ||= ""
window.html["::OPTIONS::"] = options unless window.html.nil? || window.html.index("::OPTIONS::").nil?
else
window.title = t(window.title)
end
end
respond_to do |format|
format.html
format.json { render :json => @windows.as_json( :include => [:window_detail] ) }
end
end
# This is going to display windows that are hidden (show_flag = false)
# The "visibility_flag" param indicates whether it's public or private visibility
def hidden_windows
@visibility = params[:visibility_flag]
channel = Channel.find(params[:channel_id])
if @visibility == "private"
@windows = channel.private_windows(false) unless channel.nil?
else
@windows = channel.public_windows(false) unless channel.nil?
end
@windows.reject! { |window| window.type == "PluginWindow" }
@windows.each do |window|
if window.type == "PluginWindow"
elsif window.type == "ChartWindow"
window.title = t(window.title, {:field_number => window.window_detail.field_number})
options = window.becomes(ChartWindow).window_detail.options unless window.becomes(ChartWindow).window_detail.nil?
options ||= ""
window.html["::OPTIONS::"] = options unless window.html.nil? || window.html.index("::OPTIONS::").nil?
else
window.title = t(window.title)
end
end
respond_to do |format|
format.html { render :partial => "hidden_windows" }
# format.json { render :json => @windows.as_json( :include => [:window_detail] ) }
end
end
def private_windows
channel = Channel.find(params[:channel_id])
channel.update_status_portlet false if (channel.windows.select { |w| w.wtype == :status && w.private_flag == false } )
channel.update_status_portlet true if (channel.windows.select { |w| w.wtype == :status && w.private_flag == true } )
channel.update_video_portlet false if (channel.windows.select { |w| w.wtype == :video && w.private_flag == false } )
channel.update_video_portlet true if (channel.windows.select { |w| w.wtype == :video && w.private_flag == true } )
channel.update_location_portlet false if (channel.windows.select { |w| w.wtype == :location && w.private_flag == false } )
channel.update_location_portlet true if (channel.windows.select { |w| w.wtype == :location && w.private_flag == true } )
channel.update_chart_portlets if (channel.windows.select { |w| w.wtype == :chart } )
windows = channel.private_windows(true).order(:position) unless params[:channel_id].nil?
if channel.recent_statuses.nil? || channel.recent_statuses.size <= 0
@windows = windows.delete_if { |w| w.wtype == "status" }
else
@windows = windows
end
@windows.each do |window|
if window.type == "PluginWindow"
windowDetail = window.window_detail
pluginName = Plugin.find(windowDetail.plugin_id).name
window.title = t(window.title, {:name => pluginName})
elsif window.type == "ChartWindow"
window.title = t(window.title, {:field_number => window.window_detail.field_number})
options = window.becomes(ChartWindow).window_detail.options unless window.becomes(ChartWindow).window_detail.nil?
options ||= ""
window.html["::OPTIONS::"] = options unless window.html.nil? || window.html.index("::OPTIONS::").nil?
else
window.title = t(window.title)
end
end
respond_to do |format|
format.html
format.json { render :json => @windows.as_json( :include => [:window_detail] ) }
end
end
def update
logger.info "We're trying to update the windows with " + params.to_s
#params for this put are going to look like
# page"=>"{\"col\":0,\"positions\":[1,2,3]}"
#So.. the position values are Windows.id They should get updated with the ordinal value based
# on their array position and the column should get updated according to col value.
# Since the windows are order by position, when a window record changes from
# col1,position0 -> col0,position0 the entire new column is reordered.
# The old column is missing a position, but the remaining are just left to their order
# (ie., 0,1,2 become 1,2) Unless they are also changed
# First parse the JSON in params["page"] ...
values = JSON(params[:page])
# .. then find each window and update with new ordinal position and col.
logger.info "Channel id = " + params[:channel_id].to_s
@channel = current_user.channels.find(params[:channel_id])
col = values["col"]
saved = true
values["positions"].each_with_index do |p,i|
windows = @channel.windows.where({:id => p}) unless p.nil?
unless windows.nil? || windows.empty?
w = windows[0]
w.position = i
w.col = col
if !w.save
saved = false
end
end
end
if saved
render :text => '0'
else
render :text => '-1'
end
end
end