diff --git a/app/controllers/api_keys_controller.rb b/app/controllers/api_keys_controller.rb index 0474623..87d4c38 100644 --- a/app/controllers/api_keys_controller.rb +++ b/app/controllers/api_keys_controller.rb @@ -1,23 +1,22 @@ class ApiKeysController < ApplicationController + include KeyUtilities + before_filter :require_user, :set_channels_menu def index - get_channel_data - @read_keys = ApiKey.find(:all, :conditions => { :channel_id => @channel.id, :user_id => current_user.id, :write_flag => 0 }) + @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 destroy - @api_key = ApiKey.find_by_api_key(params[:api_key]) - @api_key.delete if @api_key.user_id == current_user.id + current_user.api_keys.find_by_api_key(params[:id]).try(:destroy) redirect_to :back end def create - @channel = Channel.find(params[:channel_id]) - # make sure channel belongs to current user - check_permissions(@channel) - - @api_key = ApiKey.find(:first, :conditions => { :channel_id => @channel.id, :user_id => current_user.id, :write_flag => 1 } ) + @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') @@ -32,14 +31,12 @@ class ApiKeysController < ApplicationController @api_key.save # redirect - redirect_to channel_api_keys_path(@channel.id) and return + redirect_to channel_api_keys_path(@channel) end def update - @api_key = ApiKey.find_by_api_key(params[:api_key][:api_key]) - - @api_key.note = params[:api_key][:note] - @api_key.save if current_user.id == @api_key.user_id - redirect_to channel_api_keys_path(@api_key.channel) + @api_key = current_user.api_keys.find_by_api_key(params[:id]) + @api_key.update_attributes(params[:api_key]) + redirect_to :back end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d9e5a00..4795ed0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -85,24 +85,19 @@ class ApplicationController < ActionController::Base # 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 + value = nil + for header in request.env + value = header[1] if (header[0].upcase.index(name.upcase)) + end + return value + end - # gets the same data for showing or editing - def get_channel_data - @channel = Channel.find(params[:channel_id]) if params[:channel_id] - @channel = Channel.find(params[:id]) if @channel.nil? and params[:id] - @key = '' - # make sure channel belongs to current user - check_permissions(@channel) - - @api_key = ApiKey.find(:first, :conditions => { :channel_id => @channel.id, :user_id => current_user.id, :write_flag => 1 } ) - @key = @api_key.api_key if @api_key - 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] + @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)) @@ -131,18 +126,6 @@ class ApplicationController < ActionController::Base return feed_unauthorized.to_xml(:only => :entry_id) end - # generates a database unique api key - def generate_api_key(size = 16) - alphanumerics = ('0'..'9').to_a + ('A'..'Z').to_a - k = (0..size).map {alphanumerics[Kernel.rand(36)]}.join - - # if key exists in database, regenerate key - k = generate_api_key if ApiKey.find_by_api_key(k) - - # output the key - return k - end - # options: days = how many days ago, start = start date, end = end date, offset = timezone offset def get_date_range(params) # set timezone correctly diff --git a/app/controllers/channels_controller.rb b/app/controllers/channels_controller.rb index eb1e2ac..7ef52ba 100644 --- a/app/controllers/channels_controller.rb +++ b/app/controllers/channels_controller.rb @@ -21,72 +21,33 @@ class ChannelsController < ApplicationController end def update - @channel = Channel.find(params[:id]) - # make sure channel belongs to current user - check_permissions(@channel) - # protect against bots - render :text => '' and return if params[:userlogin].length > 0 - + @channel = current_user.channels.find(params[:id]) @channel.update_attributes(params[:channel]) - @channel.name = "#{t(:channel_default_name)} #{@channel.id}" if params[:channel][:name].empty? - @channel.save - redirect_to channel_path(@channel.id) and return + redirect_to channel_path(@channel.id) end - def create - # protect against bots - render :text => '' and return if params[:userlogin].length > 0 + def create + channel = current_user.channels.create(:field1 => "#{t(:channel_default_field)} 1") + channel.add_write_api_key + + # redirect to edit the newly created channel + redirect_to edit_channel_path(channel) + end - # get default name for field - @d = t(:channel_default_field) + # clear all data from a channel + def clear + channel = current_user.channels.find(params[:id]) + channel.feeds.delete_all + channel.update_attribute(:last_entry_id, nil) - # add channel with defaults - @channel = Channel.new(:field1 => "#{@d}1") - @channel.user_id = current_user.id - @channel.save - - # now that the channel is saved, we can create the default name - @channel.name = "#{t(:channel_default_name)} #{@channel.id}" - @channel.save - - # create an api key for this channel - @api_key = ApiKey.new - @api_key.channel_id = @channel.id - @api_key.user_id = current_user.id - @api_key.write_flag = 1 - @api_key.api_key = generate_api_key - @api_key.save - - # redirect to edit the newly created channel - redirect_to edit_channel_path(@channel.id) - end - - # clear all data from a channel - def clear - channel = Channel.find(params[:id]) - # make sure channel belongs to current user - check_permissions(channel) - - # do the delete - channel.feeds.each do |f| - f.delete - end - - # set the channel's last_entry_id to nil - channel.last_entry_id = nil - channel.save - - redirect_to channels_path + redirect_to channels_path end def destroy - @channel = Channel.find(params[:id]) - # make sure channel belongs to current user - check_permissions(@channel) - - # do the delete - @channel.delete + channel = current_user.channels.find(params[:id]) + channel.destroy + redirect_to channels_path end @@ -255,6 +216,9 @@ class ChannelsController < ApplicationController redirect_to channel_path(channel.id) end + +private + # determine if the date can be parsed def date_parsable?(date) return !is_a_number?(date) diff --git a/app/controllers/feed_controller.rb b/app/controllers/feed_controller.rb index 96c1486..e580cbe 100644 --- a/app/controllers/feed_controller.rb +++ b/app/controllers/feed_controller.rb @@ -556,7 +556,7 @@ class FeedController < ApplicationController # creates an empty clone of an object def create_empty_clone(object) - empty_clone = object.clone + empty_clone = object.dup empty_clone.attribute_names.each { |attr| empty_clone[attr] = nil } return empty_clone end diff --git a/app/controllers/mailer_controller.rb b/app/controllers/mailer_controller.rb index 3104144..eb2e62e 100644 --- a/app/controllers/mailer_controller.rb +++ b/app/controllers/mailer_controller.rb @@ -1,23 +1,20 @@ class MailerController < ApplicationController def resetpassword - # protect against bots - render :text => '' and return if params[:userlogin].length > 0 - @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, "https://www.thingspeak.com/users/reset_password/#{@user.id}?token=#{@user.perishable_token}").deliver + # 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 :controller => 'user_session', :action => 'new' + redirect_to login_path end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 72962f4..cff9943 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -8,15 +8,12 @@ class UsersController < ApplicationController end def create - # protect against bots - render :text => '' and return if params[:userlogin].length > 0 - @user = User.new(params[:user]) # save user if @user.valid? if @user.save - redirect_back_or_default account_path and return + redirect_back_or_default account_path end else render :action => :new @@ -26,17 +23,16 @@ class UsersController < ApplicationController def show @menu = 'account' - @user = @current_user + @user = current_user end def edit @menu = 'account' - @user = @current_user + @user = current_user 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 @@ -44,7 +40,7 @@ class UsersController < ApplicationController # if user has been logged in (due to previous form submission) if !current_user.nil? @user = current_user - @user.errors.add_to_base(t(:password_problem)) + @user.errors.add(t(:password_problem)) @valid_link = true else @user = User.find_by_id(params[:id]) @@ -76,13 +72,13 @@ class UsersController < ApplicationController def update @menu = 'account' - @user = @current_user # makes our views "cleaner" and more consistent + @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]) redirect_to account_path else - @user.errors.add_to_base(t(:password_incorrect)) - render :action => :edit + @user.errors.add :base, t(:password_incorrect) + render :edit end end diff --git a/app/models/api_key.rb b/app/models/api_key.rb index 9b47730..cc240f1 100644 --- a/app/models/api_key.rb +++ b/app/models/api_key.rb @@ -1,7 +1,22 @@ class ApiKey < ActiveRecord::Base - belongs_to :channel + belongs_to :channel + belongs_to :user - validates_uniqueness_of :api_key + validates_uniqueness_of :api_key + + scope :write_keys, :conditions => { :write_flag => true } + scope :read_keys, :conditions => { :write_flag => false } + + attr_readonly :created_at + attr_accessible :note + + def to_s + api_key + end + + def to_param + api_key + end end diff --git a/app/models/channel.rb b/app/models/channel.rb index 282a8f9..b941021 100644 --- a/app/models/channel.rb +++ b/app/models/channel.rb @@ -1,7 +1,40 @@ class Channel < ActiveRecord::Base - belongs_to :user + include KeyUtilities + + belongs_to :user has_many :feeds has_many :api_keys + + attr_readonly :created_at + attr_protected :user_id, :last_entry_id + + after_create :set_initial_default_name + before_validation :set_default_name + + validates :name, :presence => true, :on => :update + + def add_write_api_key + write_key = self.api_keys.new + write_key.user = self.user + write_key.write_flag = true + write_key.api_key = generate_api_key + write_key.save + end + + def field_label(field_number) + self["field#{field_number}"] + end + +private + + def set_default_name + self.name = "#{I18n.t(:channel_default_name)} #{self.id}" if self.name.blank? + end + + def set_initial_default_name + update_attribute(:name, "#{I18n.t(:channel_default_name)} #{self.id}") + end + end diff --git a/app/models/feed.rb b/app/models/feed.rb index 866ea08..dca681c 100644 --- a/app/models/feed.rb +++ b/app/models/feed.rb @@ -1,7 +1,10 @@ class Feed < ActiveRecord::Base belongs_to :channel - self.include_root_in_json = false + self.include_root_in_json = false + + attr_readonly :created_at + attr_protected :channel_id end diff --git a/app/models/user.rb b/app/models/user.rb index d923483..200807d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,11 +1,12 @@ class User < ActiveRecord::Base - has_many :channels + has_many :channels + has_many :api_keys - acts_as_authentic + acts_as_authentic - def self.find_by_login_or_email(login) - User.find_by_login(login) || User.find_by_email(login) - end + def self.find_by_login_or_email(login) + User.find_by_login(login) || User.find_by_email(login) + end end diff --git a/app/views/api_keys/index.html.erb b/app/views/api_keys/index.html.erb index 96afae8..142d6fc 100644 --- a/app/views/api_keys/index.html.erb +++ b/app/views/api_keys/index.html.erb @@ -5,7 +5,7 @@

<%= t(:api_key_write) %>

-<%= @key %> +<%= @write_key %>

@@ -18,21 +18,21 @@ - + - + + <%= button_to t(:api_key_delete), channel_api_key_path(@channel, read_key) , :method => 'delete', :confirm => t(:confirm_read_key_delete) %>
<%= t(:api_key_key) %>:<%= read_key.api_key %><%= read_key %>
<%= t(:note) %>: - <%= form_for read_key, :as => :api_key, :url => { :controller => 'api_keys', :action => 'update' }, :html => {:method => 'put'} do |f| %> + <%= form_for read_key, :as => :api_key, :url => channel_api_key_path(@channel, read_key), :html => {:method => 'put'} do |f| %> <%= f.text_area :note, :cols => 30, :rows => 4 %>
<%= f.hidden_field :api_key, :value => read_key.api_key %>
<%= f.submit t(:note_save) %>
<% end %> - <%= button_to t(:api_key_delete), { :controller => 'api_keys', :action => 'destroy', :api_key => read_key.api_key}, :confirm => t(:confirm_read_key_delete) %>


diff --git a/app/views/channels/edit.html.erb b/app/views/channels/edit.html.erb index 974158e..654ec05 100644 --- a/app/views/channels/edit.html.erb +++ b/app/views/channels/edit.html.erb @@ -78,12 +78,12 @@

<%= t(:channel_clear_message) %>

-<%= button_to t(:channel_clear), { :controller => 'channels', :action => 'clear', :id => @channel.id }, :confirm => t(:confirm_channel_clear) %> +<%= button_to t(:channel_clear), clear_channel_path(@channel), :confirm => t(:confirm_channel_clear) %>

<%= t(:channel_delete_message) %>

-<%= button_to t(:channel_delete), channel_path(@channel.id), :method => 'delete', :confirm => t(:confirm_channel_delete) %> +<%= button_to t(:channel_delete), channel_path(@channel), :method => 'delete', :confirm => t(:confirm_channel_delete) %> +

<%= link_to t(:channels), channels_path %> » diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb index 2de5cce..0aaa2ff 100644 --- a/app/views/layouts/_header.html.erb +++ b/app/views/layouts/_header.html.erb @@ -1,6 +1,6 @@
<% if current_user %> - <%= link_to t(:signout), logout_path %> + <%= link_to t(:signout), logout_path, :method => 'delete' %> <% else %> <%= link_to t(:signup), new_user_path %> diff --git a/app/views/users/_login.html.erb b/app/views/users/_login.html.erb index 1edddd8..e69a7e9 100644 --- a/app/views/users/_login.html.erb +++ b/app/views/users/_login.html.erb @@ -1,4 +1,4 @@ -<%= form_for (@user_session = UserSession.new), :url => user_session_path, :html => { :id => 'loginform' } do |f| %> +<%= form_for @user_session, :url => user_session_path, :html => { :id => 'loginform' } do |f| %> <%= f.hidden_field :remember_me, :value => false %> @@ -21,7 +21,7 @@ - + diff --git a/app/views/users/forgot_password.html.erb b/app/views/users/forgot_password.html.erb index 816db3a..19f27a9 100644 --- a/app/views/users/forgot_password.html.erb +++ b/app/views/users/forgot_password.html.erb @@ -1,7 +1,7 @@

<%= t(:password_forgot) %>

<%= t(:password_forgot_message) %>

-<%= form_for @user, :url => { :controller => 'mailer', :action => 'resetpassword' } do |f| %> +<%= form_for :user, :url => resetpassword_path do |f| %> <%= f.text_field :login %> <%= f.submit t(:submit) %> diff --git a/app/views/users/reset_password.html.erb b/app/views/users/reset_password.html.erb index 1f1d697..0e7e26c 100644 --- a/app/views/users/reset_password.html.erb +++ b/app/views/users/reset_password.html.erb @@ -1,6 +1,6 @@ <% if @valid_link %>

<%= t(:password_new) %>

- <%= form_for @user, :url => { :controller => 'users', :action => 'change_password', :id => @user.id } do |f| %> + <%= form_for @user, :url => change_password_user_path(@user) do |f| %> <%= error_messages_for 'user', :header_message => t(:try_again), :message => t(:password_new_error) %>
<%= link_to t(:forgot), forgot_password_path, :id => 'forgot_password' %><%= link_to t(:forgot), forgot_password_users_path, :id => 'forgot_password' %>
diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb new file mode 100644 index 0000000..503e435 --- /dev/null +++ b/config/initializers/wrap_parameters.rb @@ -0,0 +1,12 @@ +# This file contains settings for ActionController::ParamsWrapper which +# is enabled by default. + +# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. +ActiveSupport.on_load(:action_controller) do + wrap_parameters format: [:json] +end + +# # Disable root element in JSON by default. +# ActiveSupport.on_load(:active_record) do +# self.include_root_in_json = false +# end \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index a0f8aaf..ffe55d2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,42 +1,52 @@ Thingspeak::Application.routes.draw do - # main data posts using this route - match 'update', :to => 'channels#post_data', :as => 'update', :via => ((GET_SUPPORT) ? ['get', 'post'] : 'post') + # main data posts using this route + match 'update', :to => 'channels#post_data', :as => 'update', :via => ((GET_SUPPORT) ? ['get', 'post'] : 'post') - # handle subdomain routes - match '/', :to => 'subdomains#index', :constraints => { :subdomain => 'api' } - match 'crossdomain', :to => 'subdomains#crossdomain', :constraints => { :subdomain => 'api' } - match 'crossdomain', :to => 'subdomains#crossdomain' + # handle subdomain routes + match '/', :to => 'subdomains#index', :constraints => { :subdomain => 'api' } + match 'crossdomain', :to => 'subdomains#crossdomain', :constraints => { :subdomain => 'api' } + match 'crossdomain', :to => 'subdomains#crossdomain' - root :to => 'pages#home' + root :to => 'pages#home' - resource :user_session - resource 'account', :to => 'users' - resources :users + resource :user_session + resource 'account', :to => 'users' + resources :users do + member do + get :reset_password + put :change_password + end + collection do + get :forgot_password + end + end - # specific feeds + # specific feeds match 'channels/:channel_id/feed(s)(.:format)' => 'feed#index' - match 'channels/:channel_id/field(s)/:field_id(.:format)' => 'feed#index' - match 'channels/:channel_id/field(s)/:field_id/:id(.:format)' => 'feed#show' - match 'channels/:channel_id/feed(s)/entry/:id(.:format)' => 'feed#show' + match 'channels/:channel_id/field(s)/:field_id(.:format)' => 'feed#index' + match 'channels/:channel_id/field(s)/:field_id/:id(.:format)' => 'feed#show' + match 'channels/:channel_id/feed(s)/entry/:id(.:format)' => 'feed#show' - # import - match 'channels/:channel_id/import' => 'channels#import', :as => 'channel_import' - match 'channels/:channel_id/upload' => 'channels#upload' + # import + match 'channels/:channel_id/import' => 'channels#import', :as => 'channel_import' + match 'channels/:channel_id/upload' => 'channels#upload' - # nest feeds into channels - resources :channels do - resources :feed + # nest feeds into channels + resources :channels do + member do + get :import + post :upload + post :clear + end + resources :feed resources :feeds, :to => 'feed' - resources :api_keys - resources :status + resources :api_keys, :except => [:show, :edit] + resources :status resources :statuses, :to => 'statuses' - resources :charts - end + resources :charts + end - match 'login' => 'user_sessions#new', :as => :login - match 'logout' => 'user_sessions#destroy', :as => :logout - match 'users/reset_password', :to => 'users#reset_password', :as => 'reset_password' - match 'forgot_password', :to => 'users#forgot_password', :as => 'forgot_password' - - match ':controller(/:action(/:id(.:format)))' + match 'login' => 'user_sessions#new', :as => :login, :via => :get + match 'logout' => 'user_sessions#destroy', :as => :logout, :via => :delete + match 'mailer/resetpassword', :to => 'mailer#resetpassword', :as => :resetpassword, :via => :post end diff --git a/lib/key_utilities.rb b/lib/key_utilities.rb new file mode 100644 index 0000000..798e83f --- /dev/null +++ b/lib/key_utilities.rb @@ -0,0 +1,14 @@ +module KeyUtilities + + # generates a database unique api key + def generate_api_key(size = 16) + alphanumerics = ('0'..'9').to_a + ('A'..'Z').to_a + k = (0..size).map {alphanumerics[Kernel.rand(36)]}.join + + # if key exists in database, regenerate key + k = generate_api_key if ApiKey.find_by_api_key(k) + + # output the key + k + end +end \ No newline at end of file