commit
c4fb98ef7f
9
Gemfile
9
Gemfile
@ -1,10 +1,10 @@
|
||||
source 'http://rubygems.org'
|
||||
|
||||
gem 'rails', '4.0.2'
|
||||
gem 'rails', '4.0.3'
|
||||
gem 'jquery-rails', '3.0.4'
|
||||
gem 'rails_autolink'
|
||||
gem 'mysql2'
|
||||
gem 'authlogic'
|
||||
gem 'devise'
|
||||
gem 'twitter_oauth', git: 'git://github.com/moomerman/twitter_oauth.git'
|
||||
gem 'therubyracer'
|
||||
gem 'exception_notification'
|
||||
@ -29,8 +29,9 @@ gem 'capistrano', '~> 2.15.4'
|
||||
gem 'rack-utf8_sanitizer'
|
||||
gem 'newrelic_rpm'
|
||||
gem 'actionpack-xml_parser'
|
||||
gem 'activeadmin', github: 'gregbell/active_admin'
|
||||
|
||||
# To use debugger
|
||||
# to use debugger
|
||||
# gem 'ruby-debug'
|
||||
|
||||
# assets
|
||||
@ -41,7 +42,7 @@ gem 'uglifier'
|
||||
group :development do
|
||||
gem 'annotate', '~> 2.6.1'
|
||||
gem 'quiet_assets'
|
||||
gem 'thin'
|
||||
gem 'puma'
|
||||
gem 'i18n-tasks', '~> 0.2.10'
|
||||
end
|
||||
|
||||
|
116
Gemfile.lock
116
Gemfile.lock
@ -1,3 +1,21 @@
|
||||
GIT
|
||||
remote: git://github.com/gregbell/active_admin.git
|
||||
revision: 3fb7f03335b1ec5743c305f2c37103e2bfface97
|
||||
specs:
|
||||
activeadmin (1.0.0.pre)
|
||||
arbre (~> 1.0)
|
||||
bourbon
|
||||
coffee-rails
|
||||
devise (~> 3.2)
|
||||
formtastic (~> 2.3.0.rc2)
|
||||
inherited_resources (~> 1.3)
|
||||
jquery-rails
|
||||
jquery-ui-rails
|
||||
kaminari (~> 0.15)
|
||||
rails (>= 3.2, < 4.1)
|
||||
ransack (~> 1.0)
|
||||
sass-rails
|
||||
|
||||
GIT
|
||||
remote: git://github.com/moomerman/twitter_oauth.git
|
||||
revision: 04e6bbfe635a376cae342d234214cdab864fe797
|
||||
@ -17,27 +35,27 @@ GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
ZenTest (4.9.5)
|
||||
actionmailer (4.0.2)
|
||||
actionpack (= 4.0.2)
|
||||
actionmailer (4.0.3)
|
||||
actionpack (= 4.0.3)
|
||||
mail (~> 2.5.4)
|
||||
actionpack (4.0.2)
|
||||
activesupport (= 4.0.2)
|
||||
actionpack (4.0.3)
|
||||
activesupport (= 4.0.3)
|
||||
builder (~> 3.1.0)
|
||||
erubis (~> 2.7.0)
|
||||
rack (~> 1.5.2)
|
||||
rack-test (~> 0.6.2)
|
||||
actionpack-xml_parser (1.0.1)
|
||||
actionpack (>= 4.0.0.rc1)
|
||||
activemodel (4.0.2)
|
||||
activesupport (= 4.0.2)
|
||||
activemodel (4.0.3)
|
||||
activesupport (= 4.0.3)
|
||||
builder (~> 3.1.0)
|
||||
activerecord (4.0.2)
|
||||
activemodel (= 4.0.2)
|
||||
activerecord (4.0.3)
|
||||
activemodel (= 4.0.3)
|
||||
activerecord-deprecated_finders (~> 1.0.2)
|
||||
activesupport (= 4.0.2)
|
||||
activesupport (= 4.0.3)
|
||||
arel (~> 4.0.0)
|
||||
activerecord-deprecated_finders (1.0.3)
|
||||
activesupport (4.0.2)
|
||||
activesupport (4.0.3)
|
||||
i18n (~> 0.6, >= 0.6.4)
|
||||
minitest (~> 4.2)
|
||||
multi_json (~> 1.3)
|
||||
@ -51,15 +69,20 @@ GEM
|
||||
annotate (2.6.1)
|
||||
activerecord (>= 2.3.0)
|
||||
rake (>= 0.8.7)
|
||||
arel (4.0.1)
|
||||
arbre (1.0.1)
|
||||
activesupport (>= 3.0.0)
|
||||
arel (4.0.2)
|
||||
atomic (1.1.14)
|
||||
authlogic (3.3.0)
|
||||
activerecord (>= 3.2)
|
||||
activesupport (>= 3.2)
|
||||
autotest (4.4.6)
|
||||
ZenTest (>= 4.4.1)
|
||||
autotest-rails (4.2.1)
|
||||
ZenTest (~> 4.5)
|
||||
bcrypt (3.1.7)
|
||||
bcrypt-ruby (3.1.5)
|
||||
bcrypt (>= 3.1.3)
|
||||
bourbon (3.1.8)
|
||||
sass (>= 3.2.0)
|
||||
thor
|
||||
builder (3.1.4)
|
||||
capistrano (2.15.4)
|
||||
highline
|
||||
@ -78,6 +101,12 @@ GEM
|
||||
daemons (1.1.9)
|
||||
dalli (2.7.0)
|
||||
database_cleaner (1.2.0)
|
||||
devise (3.2.3)
|
||||
bcrypt-ruby (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 3.2.6, < 5)
|
||||
thread_safe (~> 0.1)
|
||||
warden (~> 1.2.3)
|
||||
diff-lcs (1.2.5)
|
||||
dynamic_form (1.1.4)
|
||||
easy_translate (0.4.0)
|
||||
@ -111,9 +140,14 @@ GEM
|
||||
i18n (~> 0.5)
|
||||
faraday (0.8.9)
|
||||
multipart-post (~> 1.2.0)
|
||||
formtastic (2.3.0.rc2)
|
||||
actionpack (>= 3.0)
|
||||
geokit (1.8.4)
|
||||
multi_json (>= 1.3.2)
|
||||
gravatarify (3.1.0)
|
||||
has_scope (0.6.0.rc)
|
||||
actionpack (>= 3.2, < 5)
|
||||
activesupport (>= 3.2, < 5)
|
||||
highline (1.6.20)
|
||||
hike (1.2.3)
|
||||
http_parser.rb (0.6.0)
|
||||
@ -126,13 +160,21 @@ GEM
|
||||
rake
|
||||
term-ansicolor
|
||||
terminal-table
|
||||
inherited_resources (1.4.1)
|
||||
has_scope (~> 0.6.0.rc)
|
||||
responders (~> 1.0.0.rc)
|
||||
jquery-rails (3.0.4)
|
||||
railties (>= 3.0, < 5.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-ui-rails (4.2.0)
|
||||
railties (>= 3.2.16)
|
||||
json (1.8.1)
|
||||
json_spec (1.1.1)
|
||||
multi_json (~> 1.0)
|
||||
rspec (~> 2.0)
|
||||
kaminari (0.15.1)
|
||||
actionpack (>= 3.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
kgio (2.8.1)
|
||||
libv8 (3.16.14.3)
|
||||
mail (2.5.4)
|
||||
@ -157,7 +199,12 @@ GEM
|
||||
nokogiri (1.6.1)
|
||||
mini_portile (~> 0.5.0)
|
||||
oauth (0.4.7)
|
||||
polyglot (0.3.3)
|
||||
orm_adapter (0.5.0)
|
||||
polyamorous (0.6.4)
|
||||
activerecord (>= 3.0)
|
||||
polyglot (0.3.4)
|
||||
puma (2.8.1)
|
||||
rack (>= 1.1, < 2.0)
|
||||
quiet_assets (1.0.2)
|
||||
railties (>= 3.1, < 5.0)
|
||||
rack (1.5.2)
|
||||
@ -167,26 +214,32 @@ GEM
|
||||
rack (>= 1.0)
|
||||
rack-utf8_sanitizer (1.1.0)
|
||||
rack (~> 1.0)
|
||||
rails (4.0.2)
|
||||
actionmailer (= 4.0.2)
|
||||
actionpack (= 4.0.2)
|
||||
activerecord (= 4.0.2)
|
||||
activesupport (= 4.0.2)
|
||||
rails (4.0.3)
|
||||
actionmailer (= 4.0.3)
|
||||
actionpack (= 4.0.3)
|
||||
activerecord (= 4.0.3)
|
||||
activesupport (= 4.0.3)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.0.2)
|
||||
railties (= 4.0.3)
|
||||
sprockets-rails (~> 2.0.0)
|
||||
rails_autolink (1.1.5)
|
||||
rails (> 3.1)
|
||||
railties (4.0.2)
|
||||
actionpack (= 4.0.2)
|
||||
activesupport (= 4.0.2)
|
||||
railties (4.0.3)
|
||||
actionpack (= 4.0.3)
|
||||
activesupport (= 4.0.3)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rake (10.1.1)
|
||||
ransack (1.1.0)
|
||||
actionpack (>= 3.0)
|
||||
activerecord (>= 3.0)
|
||||
polyamorous (~> 0.6.0)
|
||||
redis (3.0.7)
|
||||
redis-namespace (1.4.1)
|
||||
redis (~> 3.0.4)
|
||||
ref (1.0.5)
|
||||
responders (1.0.0)
|
||||
railties (>= 3.2, < 5)
|
||||
resque (1.25.1)
|
||||
mono_logger (~> 1.0)
|
||||
multi_json (~> 1.0)
|
||||
@ -228,7 +281,7 @@ GEM
|
||||
rack-protection (~> 1.4)
|
||||
tilt (~> 1.3, >= 1.3.4)
|
||||
spork (0.9.2)
|
||||
sprockets (2.10.1)
|
||||
sprockets (2.11.0)
|
||||
hike (~> 1.2)
|
||||
multi_json (~> 1.0)
|
||||
rack (~> 1.0)
|
||||
@ -246,10 +299,6 @@ GEM
|
||||
therubyracer (0.12.0)
|
||||
libv8 (~> 3.16.14.0)
|
||||
ref
|
||||
thin (1.6.1)
|
||||
daemons (>= 1.0.9)
|
||||
eventmachine (>= 1.0.0)
|
||||
rack (>= 1.0.0)
|
||||
thor (0.18.1)
|
||||
thread (0.1.3)
|
||||
thread_safe (0.1.3)
|
||||
@ -275,6 +324,8 @@ GEM
|
||||
json (>= 1.8.0)
|
||||
vegas (0.1.11)
|
||||
rack (>= 1.0.0)
|
||||
warden (1.2.3)
|
||||
rack (>= 1.0)
|
||||
webrat (0.7.3)
|
||||
nokogiri (>= 1.2.0)
|
||||
rack (>= 1.0)
|
||||
@ -287,10 +338,10 @@ PLATFORMS
|
||||
DEPENDENCIES
|
||||
ZenTest
|
||||
actionpack-xml_parser
|
||||
activeadmin!
|
||||
acts_as_list
|
||||
acts_as_tree
|
||||
annotate (~> 2.6.1)
|
||||
authlogic
|
||||
autotest
|
||||
autotest-rails
|
||||
capistrano (~> 2.15.4)
|
||||
@ -298,6 +349,7 @@ DEPENDENCIES
|
||||
daemons
|
||||
dalli
|
||||
database_cleaner (~> 1.2.0)
|
||||
devise
|
||||
dynamic_form
|
||||
exception_notification
|
||||
factory_girl_rails
|
||||
@ -312,9 +364,10 @@ DEPENDENCIES
|
||||
nested_form
|
||||
newrelic_rpm
|
||||
nokogiri
|
||||
puma
|
||||
quiet_assets
|
||||
rack-utf8_sanitizer
|
||||
rails (= 4.0.2)
|
||||
rails (= 4.0.3)
|
||||
rails_autolink
|
||||
redis
|
||||
resque-scheduler (= 2.3.1)
|
||||
@ -325,7 +378,6 @@ DEPENDENCIES
|
||||
spork
|
||||
sqlite3-ruby (= 1.3.3)
|
||||
therubyracer
|
||||
thin
|
||||
tweetstream
|
||||
twitter_oauth!
|
||||
uglifier
|
||||
|
25
app/admin/channel.rb
Normal file
25
app/admin/channel.rb
Normal file
@ -0,0 +1,25 @@
|
||||
ActiveAdmin.register Channel do
|
||||
|
||||
filter :name
|
||||
filter :description
|
||||
filter :created_at
|
||||
|
||||
permit_params :name, :public_flag
|
||||
|
||||
index do
|
||||
column :id
|
||||
column(:name) { |channel| link_to channel.name, channel }
|
||||
column(:user) { |channel| link_to channel.user.login, admin_user_path(channel.user) if channel.user.present? }
|
||||
column :public_flag
|
||||
column :created_at
|
||||
default_actions
|
||||
end
|
||||
|
||||
form do |f|
|
||||
f.semantic_errors *f.object.errors.keys
|
||||
f.inputs :name, :public_flag
|
||||
f.actions
|
||||
end
|
||||
|
||||
end
|
||||
|
28
app/admin/dashboard.rb
Normal file
28
app/admin/dashboard.rb
Normal file
@ -0,0 +1,28 @@
|
||||
ActiveAdmin.register_page "Dashboard" do
|
||||
|
||||
menu priority: 1, label: proc{ I18n.t("active_admin.dashboard") }
|
||||
|
||||
content title: proc{ I18n.t("active_admin.dashboard") } do
|
||||
columns do
|
||||
|
||||
column do
|
||||
panel "Stats" do
|
||||
para "Total Users: #{User.all.count}"
|
||||
para "Total Channels: #{Channel.all.count}"
|
||||
end
|
||||
end
|
||||
|
||||
column do
|
||||
panel "Recent Channels" do
|
||||
ul do
|
||||
Channel.all.order("created_at desc").limit(5).map do |channel|
|
||||
li link_to(channel.name, admin_channel_path(channel))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end # content
|
||||
end
|
||||
|
19
app/admin/failedlogin.rb
Normal file
19
app/admin/failedlogin.rb
Normal file
@ -0,0 +1,19 @@
|
||||
ActiveAdmin.register Failedlogin do
|
||||
menu :parent => "Others"
|
||||
actions :all, :except => [:edit]
|
||||
|
||||
filter :login
|
||||
filter :password
|
||||
filter :created_at
|
||||
|
||||
index do
|
||||
column :id
|
||||
column :login
|
||||
column :password
|
||||
column :ip_address
|
||||
column :created_at
|
||||
default_actions
|
||||
end
|
||||
|
||||
end
|
||||
|
23
app/admin/plugin.rb
Normal file
23
app/admin/plugin.rb
Normal file
@ -0,0 +1,23 @@
|
||||
ActiveAdmin.register Plugin do
|
||||
|
||||
filter :name
|
||||
filter :created_at
|
||||
|
||||
permit_params :name, :html, :css, :js, :private_flag
|
||||
|
||||
index do
|
||||
column :id
|
||||
column(:user) { |object| link_to object.user.login, admin_user_path(object.user) if object.user.present? }
|
||||
column :name
|
||||
column :private_flag
|
||||
default_actions
|
||||
end
|
||||
|
||||
form do |f|
|
||||
f.semantic_errors *f.object.errors.keys
|
||||
f.inputs :name, :html, :css, :js, :private_flag
|
||||
f.actions
|
||||
end
|
||||
|
||||
end
|
||||
|
9
app/admin/useful_links.rb
Normal file
9
app/admin/useful_links.rb
Normal file
@ -0,0 +1,9 @@
|
||||
ActiveAdmin.register_page "Useful Links" do
|
||||
menu :parent => "Others"
|
||||
|
||||
content do
|
||||
render "index"
|
||||
end
|
||||
|
||||
end
|
||||
|
48
app/admin/user.rb
Normal file
48
app/admin/user.rb
Normal file
@ -0,0 +1,48 @@
|
||||
ActiveAdmin.register User do
|
||||
require 'csv'
|
||||
|
||||
filter :email
|
||||
filter :login
|
||||
filter :created_at
|
||||
|
||||
permit_params :email, :login, :bio, :website
|
||||
|
||||
index do
|
||||
column :id
|
||||
column :email
|
||||
column :login
|
||||
column :created_at
|
||||
default_actions
|
||||
end
|
||||
|
||||
show do
|
||||
attributes_table do
|
||||
rows :id, :email, :login, :time_zone, :bio, :website, :created_at, :sign_in_count, :current_sign_in_at, :last_sign_in_at, :current_sign_in_ip, :last_sign_in_ip
|
||||
end
|
||||
panel 'Channels' do
|
||||
table_for user.channels do
|
||||
column :id
|
||||
column(:name) { |channel| link_to channel.name, channel }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
form do |f|
|
||||
f.semantic_errors *f.object.errors.keys
|
||||
f.inputs :email, :login
|
||||
f.actions
|
||||
end
|
||||
|
||||
# custom action for signups per day
|
||||
collection_action :signups, :method => :get, :format => :csv do
|
||||
@csv_headers = [:day, :signups]
|
||||
@days = User.signups_per_day
|
||||
end
|
||||
|
||||
# custom action for emails list
|
||||
collection_action :emails, :method => :get do
|
||||
@users = User.all
|
||||
end
|
||||
|
||||
end
|
||||
|
BIN
app/assets/images/ajax-loader.gif
Normal file
BIN
app/assets/images/ajax-loader.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
BIN
app/assets/images/channel_public_view.png
Normal file
BIN
app/assets/images/channel_public_view.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 110 KiB |
2
app/assets/javascripts/active_admin.js.coffee
Normal file
2
app/assets/javascripts/active_admin.js.coffee
Normal file
@ -0,0 +1,2 @@
|
||||
#= require active_admin/base
|
||||
|
@ -1,6 +1,19 @@
|
||||
// when the document is ready
|
||||
$(document).ready(function() {
|
||||
|
||||
// allow flash notices to be dismissed
|
||||
if ($(".flash").length > 0) {
|
||||
$(".flash").on("click", function() {
|
||||
$(this).hide("slow");
|
||||
});
|
||||
// hide flash automatically after 15 seconds
|
||||
setTimeout(function() {
|
||||
if ($(".flash").length > 0) {
|
||||
$(".flash").hide("slow");
|
||||
}
|
||||
}, 15000);
|
||||
}
|
||||
|
||||
// show form to add a talkback command
|
||||
$('#talkback_command_add').click(function() {
|
||||
$(this).hide();
|
||||
|
@ -208,7 +208,7 @@ o(b[3],a[3]);this.axisOffset=[0,0,0,0];this.clipOffset=[0,0,0,0]},drawChartBox:f
|
||||
null,null,c-p,d-p));else{e={fill:j||Q};if(i)e.stroke=a.borderColor,e["stroke-width"]=i;this.chartBackground=b.rect(p/2,p/2,c-p,d-p,a.borderRadius,i).attr(e).add().shadow(a.shadow)}if(k)f?f.animate(s):this.plotBackground=b.rect(q,o,n,t,0).attr({fill:k}).add().shadow(a.plotShadow);if(l)h?h.animate(s):this.plotBGImage=b.image(l,q,o,n,t).add();r?r.animate({width:w.width,height:w.height}):this.clipRect=b.clipRect(w);if(m)g?g.animate(g.crisp(null,q,o,n,t)):this.plotBorder=b.rect(q,o,n,t,0,-m).attr({stroke:a.plotBorderColor,
|
||||
"stroke-width":m,zIndex:1}).add();this.isDirtyBox=!1},propFromSeries:function(){var a=this,b=a.options.chart,c,d=a.options.series,e,f;n(["inverted","angular","polar"],function(g){c=L[b.type||b.defaultSeriesType];f=a[g]||b[g]||c&&c.prototype[g];for(e=d&&d.length;!f&&e--;)(c=L[d[e].type])&&c.prototype[g]&&(f=!0);a[g]=f})},linkSeries:function(){var a=this,b=a.series;n(b,function(a){a.linkedSeries.length=0});n(b,function(b){var d=b.options.linkedTo;if(da(d)&&(d=d===":previous"?a.series[b.index-1]:a.get(d)))d.linkedSeries.push(b),
|
||||
b.linkedParent=d})},render:function(){var a=this,b=a.axes,c=a.renderer,d=a.options,e=d.labels,f=d.credits,g;a.setTitle();a.legend=new zb(a,d.legend);a.getStacks();n(b,function(a){a.setScale()});a.getMargins();a.maxTicks=null;n(b,function(a){a.setTickPositions(!0);a.setMaxTicks()});a.adjustTickAmounts();a.getMargins();a.drawChartBox();a.hasCartesianSeries&&n(b,function(a){a.render()});if(!a.seriesGroup)a.seriesGroup=c.g("series-group").attr({zIndex:3}).add();n(a.series,function(a){a.translate();a.setTooltipPoints();
|
||||
a.render()});e.items&&n(e.items,function(b){var d=r(e.style,b.style),f=z(d.left)+a.plotLeft,g=z(d.top)+a.plotTop+12;delete d.left;delete d.top;c.text(b.html,f,g).attr({zIndex:2}).css(d).add()});if(f.enabled&&!a.credits)g=f.href,a.credits=c.text(f.text,0,0).on("click",function(){if(g)location.href=g}).attr({align:f.position.align,zIndex:8}).css(f.style).add().align(f.position);a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;A(a,"destroy");Ja[a.index]=
|
||||
a.render()});e.items&&n(e.items,function(b){var d=r(e.style,b.style),f=z(d.left)+a.plotLeft,g=z(d.top)+a.plotTop+12;delete d.left;delete d.top;c.text(b.html,f,g).attr({zIndex:2}).css(d).add()});if(f.enabled&&!a.credits)g=f.href,a.credits=c.text(f.text,0,0).on("click",function(){if(g)parent.location.href=g}).attr({align:f.position.align,zIndex:8}).css(f.style).add().align(f.position);a.hasRendered=!0},destroy:function(){var a=this,b=a.axes,c=a.series,d=a.container,e,f=d&&d.parentNode;A(a,"destroy");Ja[a.index]=
|
||||
u;a.renderTo.removeAttribute("data-highcharts-chart");X(a);for(e=b.length;e--;)b[e]=b[e].destroy();for(e=c.length;e--;)c[e]=c[e].destroy();n("title,subtitle,chartBackground,plotBackground,plotBGImage,plotBorder,seriesGroup,clipRect,credits,pointer,scroller,rangeSelector,legend,resetZoomButton,tooltip,renderer".split(","),function(b){var c=a[b];c&&c.destroy&&(a[b]=c.destroy())});if(d)d.innerHTML="",X(d),f&&Oa(d);for(e in a)delete a[e]},isReadyToRender:function(){var a=this;return!V&&C==C.top&&v.readyState!==
|
||||
"complete"||ba&&!C.canvg?(ba?Mb.push(function(){a.firstRender()},a.options.global.canvasToolsURL):v.attachEvent("onreadystatechange",function(){v.detachEvent("onreadystatechange",a.firstRender);v.readyState==="complete"&&a.firstRender()}),!1):!0},firstRender:function(){var a=this,b=a.options,c=a.callback;if(a.isReadyToRender())a.getContainer(),A(a,"init"),a.resetMargins(),a.setChartSize(),a.propFromSeries(),a.getAxes(),n(b.series||[],function(b){a.initSeries(b)}),a.linkSeries(),A(a,"beforeRender"),
|
||||
a.pointer=new Za(a,b),a.render(),a.renderer.draw(),c&&c.apply(a,[a]),n(a.callbacks,function(b){b.apply(a,[a])}),a.cloneRenderTo(!0),A(a,"load")},splashArray:function(a,b){var c=b[a],c=S(c)?c:[c,c,c,c];return[o(b[a+"Top"],c[0]),o(b[a+"Right"],c[1]),o(b[a+"Bottom"],c[2]),o(b[a+"Left"],c[3])]}};eb.prototype.callbacks=[];var xb=Highcharts.CenteredSeriesMixin={getCenter:function(){var a=this.options,b=this.chart,c=2*(a.slicedOffset||0),d,e=b.plotWidth-2*c,f=b.plotHeight-2*c,b=a.center,a=[o(b[0],"50%"),
|
||||
|
@ -1,5 +1,6 @@
|
||||
// if on api subdomain, redirect
|
||||
// if on api subdomain except for charts, redirect
|
||||
var wloc = window.location.toString();
|
||||
if (wloc.indexOf('api') != -1 && wloc.indexOf('api') < 10) {
|
||||
if (wloc.indexOf('api') !== -1 && wloc.indexOf('api') < 10 && wloc.indexOf('charts') === -1) {
|
||||
window.location = wloc.replace('api', 'www');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
$(document).ready(function() {
|
||||
// execute on window load (and not document.ready), so that the sidebar is positioned correctly
|
||||
$(window).load(function() {
|
||||
// if affix function exists
|
||||
if ($.fn.affix) {
|
||||
|
||||
// add sidebar affix
|
||||
$('#bootstrap-sidebar').affix();
|
||||
// add sidebar affix, wrapped in a timeout so that it displays correctly
|
||||
setTimeout(function() {
|
||||
$('#bootstrap-sidebar').affix();
|
||||
}, 100);
|
||||
|
||||
// add sidebar scrollspy
|
||||
$(document.body).scrollspy({ target: '#leftcol', offset: 300 });
|
||||
|
@ -13,11 +13,11 @@ function getDimensions(element) {
|
||||
}
|
||||
|
||||
function updateChart(index,
|
||||
postUpdate,
|
||||
width,
|
||||
height,
|
||||
channelId,
|
||||
newOptionsSave) {
|
||||
postUpdate,
|
||||
width,
|
||||
height,
|
||||
channelId,
|
||||
newOptionsSave) {
|
||||
// default width and height
|
||||
var width = width;
|
||||
var height = height;
|
||||
@ -26,52 +26,52 @@ function updateChart(index,
|
||||
var iframe = $('#iframe' + index).attr("default_src");
|
||||
|
||||
if (!iframe) {
|
||||
iframe = $('#iframe' + index).attr('src');
|
||||
iframe = $('#iframe' + index).attr('src');
|
||||
}
|
||||
|
||||
src = iframe.split('?')[0];
|
||||
// if not a line chart, a timeslice should be present or set timescale=30
|
||||
if ($('#type_' + index).val() != 'line') {
|
||||
if ($('#timescale_' + index).val().length == 0 && $('#average_' + index).val().length == 0 && $('#median_' + index).val().length == 0 && $('#sum_' + index).val().length == 0) {
|
||||
$('#timescale_' + index).val(30);
|
||||
}
|
||||
if ($('#timescale_' + index).val().length == 0 && $('#average_' + index).val().length == 0 && $('#median_' + index).val().length == 0 && $('#sum_' + index).val().length == 0) {
|
||||
$('#timescale_' + index).val(30);
|
||||
}
|
||||
}
|
||||
|
||||
// add inputs to array
|
||||
var inputs = [];
|
||||
$('.chart_options' + index).each(function() {
|
||||
var v = $(this).val();
|
||||
var id = $(this).attr('id');
|
||||
var tag = id.split("_")[0];
|
||||
var v = $(this).val();
|
||||
var id = $(this).attr('id');
|
||||
var tag = id.split("_")[0];
|
||||
|
||||
if (v.length > 0) { inputs.push([tag, v]); }
|
||||
});
|
||||
if (v.length > 0) { inputs.push([tag, v]); }
|
||||
});
|
||||
|
||||
// create querystring
|
||||
var qs = '';
|
||||
while (inputs.length > 0) {
|
||||
var p = inputs.pop();
|
||||
if (p[0] == 'width') { width = parseInt(p[1]); }
|
||||
if (p[0] == 'height') { height = parseInt(p[1]); }
|
||||
var p = inputs.pop();
|
||||
if (p[0] == 'width') { width = parseInt(p[1]); }
|
||||
if (p[0] == 'height') { height = parseInt(p[1]); }
|
||||
|
||||
// don't add type=line to querystring, it's the default value
|
||||
if (!(p[0] == 'type' && p[1] == 'line')) {
|
||||
qs += '&' + p[0] + '=' + encodeURIComponent(p[1]);
|
||||
}
|
||||
// don't add type=line to querystring, it's the default value
|
||||
if (!(p[0] == 'type' && p[1] == 'line')) {
|
||||
qs += '&' + p[0] + '=' + encodeURIComponent(p[1]);
|
||||
}
|
||||
}
|
||||
// if querystring exists, add it to src
|
||||
if (qs.length > 0) { src += '?' + qs.substring(1); }
|
||||
|
||||
// save chart options to database
|
||||
if (postUpdate && index > 0 && newOptionsSave) {
|
||||
$.update("/channels/" + channelId + "/charts/" + index,
|
||||
{
|
||||
newOptions : { options: qs }
|
||||
} );
|
||||
$.update("/channels/" + channelId + "/charts/" + index,
|
||||
{
|
||||
newOptions : { options: qs }
|
||||
} );
|
||||
}
|
||||
else if (postUpdate && index > 0) {
|
||||
$.update("/channels/" + channelId + "/charts/" + index,
|
||||
{ options: qs } );
|
||||
$.update("/channels/" + channelId + "/charts/" + index,
|
||||
{ options: qs } );
|
||||
}
|
||||
|
||||
// set embed code
|
||||
@ -90,9 +90,9 @@ function updateSelectValues() {
|
||||
|
||||
function setupChartForm(channelIndex) {
|
||||
return function(index, value) {
|
||||
if (value.length > 0) {
|
||||
$('#' + value.split('=')[0] + "_" + channelIndex).val(decodeURIComponent(value.split('=')[1]));
|
||||
}
|
||||
if (value.length > 0) {
|
||||
$('#' + value.split('=')[0] + "_" + channelIndex).val(decodeURIComponent(value.split('=')[1]));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -107,52 +107,52 @@ function setupColumns(current_user, channel_id)
|
||||
|
||||
function createWindowsWithData (data, current_user, channel_id, colName) {
|
||||
|
||||
for (var i in data) {
|
||||
//each array element has a single chart object as an associative array with the type as the key
|
||||
// so I need to iterate over a array with size=1 to get a string with the window type
|
||||
for (var type in data[i]) {
|
||||
var wtype = type;
|
||||
}
|
||||
if (data[i].chart_window) window = data[i].chart_window;
|
||||
if (data[i].plugin_window) window = data[i].plugin_window;
|
||||
if (data[i].portlet_window) window = data[i].portlet_window;
|
||||
for (var i in data) {
|
||||
//each array element has a single chart object as an associative array with the type as the key
|
||||
// so I need to iterate over a array with size=1 to get a string with the window type
|
||||
for (var type in data[i]) {
|
||||
var wtype = type;
|
||||
}
|
||||
if (data[i].chart_window) window = data[i].chart_window;
|
||||
if (data[i].plugin_window) window = data[i].plugin_window;
|
||||
if (data[i].portlet_window) window = data[i].portlet_window;
|
||||
|
||||
if (window == "undefined")
|
||||
if (window == "undefined")
|
||||
var window = (data[i].portlet_window) ? data[i].portlet_window : data[i].chart_window;
|
||||
colId = window.col;
|
||||
title = window.title;
|
||||
colId = window.col;
|
||||
title = window.title;
|
||||
|
||||
var content = window.html;
|
||||
if (data[i].chart_window) {
|
||||
var windowId = window.id;
|
||||
$("body").append("<div id='chartConfig"+windowId+"'></div>");
|
||||
var content = window.html;
|
||||
if (data[i].chart_window) {
|
||||
var windowId = window.id;
|
||||
$("body").append("<div id='chartConfig"+windowId+"'></div>");
|
||||
}
|
||||
var portlet = addWindow(colName, colId, window.id, wtype, title, content);
|
||||
portlet.each ( decoratePortlet(current_user) ) ;
|
||||
var portlet = addWindow(colName, colId, window.id, wtype, title, content);
|
||||
portlet.each ( decoratePortlet(current_user) ) ;
|
||||
|
||||
portlet.find( ".ui-toggle" ).click( uiToggleClick );
|
||||
portlet.find( ".ui-view" ).click( uiViewClick (channel_id) );
|
||||
portlet.find( ".ui-edit" ).click( uiEditClick (channel_id) );
|
||||
portlet.find( ".ui-close" ).click( uiCloseClick (channel_id) );
|
||||
}
|
||||
portlet.find( ".ui-toggle" ).click( uiToggleClick );
|
||||
portlet.find( ".ui-view" ).click( uiViewClick (channel_id) );
|
||||
portlet.find( ".ui-edit" ).click( uiEditClick (channel_id) );
|
||||
portlet.find( ".ui-close" ).click( uiCloseClick (channel_id) );
|
||||
}
|
||||
}
|
||||
var createWindows = function (current_user, channel_id, colName) {
|
||||
return function(data) {
|
||||
createWindowsWithData(data, current_user, channel_id, colName);
|
||||
createWindowsWithData(data, current_user, channel_id, colName);
|
||||
};
|
||||
}
|
||||
|
||||
function addWindow(colName, colId, windowId, wtype, title, content) {
|
||||
$("#"+colName+"_dialog"+colId).append('<div class="portlet ui-widget ui-widget-content ui-helper-clearfix ui-corner-all" ' +
|
||||
'id="portlet_' + windowId +
|
||||
'"><div class="portlet-header wtype wtype-'+ wtype
|
||||
+ ' ui-widget-header ui-corner-all">' + title +
|
||||
'</div><div class="portlet-content">'+content+'</div>') ;
|
||||
'id="portlet_' + windowId +
|
||||
'"><div class="portlet-header wtype wtype-'+ wtype
|
||||
+ ' ui-widget-header ui-corner-all">' + title +
|
||||
'</div><div class="portlet-content">'+content+'</div>') ;
|
||||
|
||||
if ($("#portlet_"+windowId).length > 1) {
|
||||
throw "Portlet count doesn't match what's expected";
|
||||
throw "Portlet count doesn't match what's expected";
|
||||
} else {
|
||||
return $("#portlet_"+windowId);
|
||||
return $("#portlet_"+windowId);
|
||||
}
|
||||
|
||||
}
|
||||
@ -161,22 +161,22 @@ function addWindow(colName, colId, windowId, wtype, title, content) {
|
||||
var updatePortletPositions = function( current_user, channel_id) {
|
||||
return function() {
|
||||
if (current_user) {
|
||||
var result = $(this).sortable('serialize');
|
||||
colId = $(this).attr('id').charAt($(this).attr('id').length - 1);
|
||||
portletArray = getPortletArray(result);
|
||||
jsonResult = {
|
||||
"col" : colId,
|
||||
"positions" : portletArray
|
||||
} ;
|
||||
var result = $(this).sortable('serialize');
|
||||
colId = $(this).attr('id').charAt($(this).attr('id').length - 1);
|
||||
portletArray = getPortletArray(result);
|
||||
jsonResult = {
|
||||
"col" : colId,
|
||||
"positions" : portletArray
|
||||
} ;
|
||||
|
||||
if (portletArray.length > 0) {
|
||||
$.ajax({
|
||||
type: 'PUT',
|
||||
url: '../channels/' + channel_id + '/windows',
|
||||
data: {_method:'PUT', page : JSON.stringify(jsonResult ) },
|
||||
dataType: 'json'
|
||||
});
|
||||
}
|
||||
if (portletArray.length > 0) {
|
||||
$.ajax({
|
||||
type: 'PUT',
|
||||
url: '../channels/' + channel_id + '/windows',
|
||||
data: {_method:'PUT', page : JSON.stringify(jsonResult ) },
|
||||
dataType: 'json'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,31 +184,31 @@ var updatePortletPositions = function( current_user, channel_id) {
|
||||
function sortColumnSetup(current_user, channel_id) {
|
||||
|
||||
$( ".column" ).sortable({
|
||||
opacity: 0.6,
|
||||
helper: function( event ) {
|
||||
return $("<div class='ui-widget-header'>Drop to re-position</div>");
|
||||
},
|
||||
connectWith: ".column",
|
||||
update: updatePortletPositions(current_user, channel_id)
|
||||
});
|
||||
opacity: 0.6,
|
||||
helper: function( event ) {
|
||||
return $("<div class='ui-widget-header'>Drop to re-position</div>");
|
||||
},
|
||||
connectWith: ".column",
|
||||
update: updatePortletPositions(current_user, channel_id)
|
||||
});
|
||||
}
|
||||
var decoratePortlet = function (current_user) {
|
||||
return function() {
|
||||
var portletHeader = $(this).find( ".portlet-header") ;
|
||||
portletHeader.append( "<span id='commentBtn' class='ui-view ui-icon ui-icon-comment'></span>");
|
||||
var portletHeader = $(this).find( ".portlet-header") ;
|
||||
portletHeader.append( "<span id='commentBtn' class='ui-view ui-icon ui-icon-comment'></span>");
|
||||
|
||||
thisObject = $(this);
|
||||
if (current_user == "true") {
|
||||
// Use feature Rollout here - needs to be implemented for this user, and this channel needs to belong to this user.
|
||||
thisObject.find('.wtype').prepend( "<span id='minusBtn' class='ui-toggle ui-icon ui-icon-minusthick'></span>");
|
||||
thisObject.find(".wtype-chart_window").append("<span id='pencilBtn' class='ui-edit ui-icon ui-icon-pencil'></span>");
|
||||
thisObject.find(".wtype").append("<span id='closeBtn' class='ui-close ui-icon ui-icon-close'></span>");
|
||||
thisObject.find(".portlet-header").css("cursor","move");
|
||||
}
|
||||
else {
|
||||
$(".column").sortable({ disabled:true });
|
||||
}
|
||||
return $(this).attr("id");
|
||||
thisObject = $(this);
|
||||
if (current_user == "true") {
|
||||
// Use feature Rollout here - needs to be implemented for this user, and this channel needs to belong to this user.
|
||||
thisObject.find('.wtype').prepend( "<span id='minusBtn' class='ui-toggle ui-icon ui-icon-minusthick'></span>");
|
||||
thisObject.find(".wtype-chart_window").append("<span id='pencilBtn' class='ui-edit ui-icon ui-icon-pencil'></span>");
|
||||
thisObject.find(".wtype").append("<span id='closeBtn' class='ui-close ui-icon ui-icon-close'></span>");
|
||||
thisObject.find(".portlet-header").css("cursor","move");
|
||||
}
|
||||
else {
|
||||
$(".column").sortable({ disabled:true });
|
||||
}
|
||||
return $(this).attr("id");
|
||||
}
|
||||
}
|
||||
function getPortletArray(data) {
|
||||
@ -218,8 +218,8 @@ function getPortletArray(data) {
|
||||
|
||||
for (i in inputArray) {
|
||||
|
||||
val = inputArray[i].split("=")[1] ;
|
||||
resultArray.push(val);
|
||||
val = inputArray[i].split("=")[1] ;
|
||||
resultArray.push(val);
|
||||
}
|
||||
|
||||
return resultArray;
|
||||
@ -228,63 +228,63 @@ function getPortletArray(data) {
|
||||
|
||||
var uiEditClick = function (channel_id) {
|
||||
return function() {
|
||||
var id = $( this ).parents( ".portlet:first" ).attr("id").substring(8);
|
||||
var id = $( this ).parents( ".portlet:first" ).attr("id").substring(8);
|
||||
|
||||
var options = "";
|
||||
$("#chartConfig"+id).load("/channels/"+channel_id+"/charts/"+id+"/edit",
|
||||
function() {
|
||||
options = $("#chartOptions"+id).html();
|
||||
var options = "";
|
||||
$("#chartConfig"+id).load("/channels/"+channel_id+"/charts/"+id+"/edit",
|
||||
function() {
|
||||
options = $("#chartOptions"+id).html();
|
||||
|
||||
if (options != "undefined" && options.length >2) {
|
||||
$.each((options.split('&')), setupChartForm( id ));
|
||||
}
|
||||
$("#button"+id).click( function() {
|
||||
updateChart(id, true, 450, 250, channel_id, true);
|
||||
$("#chartConfig"+id).dialog("close");
|
||||
if (options != "undefined" && options.length >2) {
|
||||
$.each((options.split('&')), setupChartForm( id ));
|
||||
}
|
||||
$("#button"+id).click( function() {
|
||||
updateChart(id, true, 450, 250, channel_id, true);
|
||||
$("#chartConfig"+id).dialog("close");
|
||||
|
||||
});
|
||||
})
|
||||
.dialog({ title:"Chart Options", modal: true, resizable: false, width: 500, dialogClass: "dev-info-dialog" });
|
||||
});
|
||||
})
|
||||
.dialog({ title:"Chart Options", modal: true, resizable: false, width: 500, dialogClass: "dev-info-dialog" });
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
var uiViewClick = function (channel_id) {
|
||||
return function() {
|
||||
var x = $( this ).parents( ".portlet:first" ).find( ".portlet-content" ).offset().left;
|
||||
var y = $( this ).parents( ".portlet:first" ).find( ".portlet-content" ).offset().top;
|
||||
var id = $( this ).parents( ".portlet:first" ).attr("id").substring(8);
|
||||
var x = $( this ).parents( ".portlet:first" ).find( ".portlet-content" ).offset().left;
|
||||
var y = $( this ).parents( ".portlet:first" ).find( ".portlet-content" ).offset().top;
|
||||
var id = $( this ).parents( ".portlet:first" ).attr("id").substring(8);
|
||||
|
||||
$("body").append('<div id="iframepopup'+id+'" style="display:none">' +
|
||||
'<div id="iframeinner'+id+'"style="font-size:1.2em;overflow:auto;height:115px;background-color:white">' +
|
||||
'</div></div>');
|
||||
$("body").append('<div id="iframepopup'+id+'" style="display:none">' +
|
||||
'<div id="iframeinner'+id+'"style="font-size:1.2em;overflow:auto;height:115px;background-color:white">' +
|
||||
'</div></div>');
|
||||
|
||||
$.get("/channels/"+channel_id+"/windows/"+id+"/iframe",
|
||||
function(response) {
|
||||
var display = response.replace(/id=\"iframe[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?\"/, "" );
|
||||
$("#iframeinner"+id).text(display);
|
||||
}
|
||||
);
|
||||
$.get("/channels/"+channel_id+"/windows/"+id+"/iframe",
|
||||
function(response) {
|
||||
var display = response.replace(/id=\"iframe[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?[0-9]?\"/, "" );
|
||||
$("#iframeinner"+id).text(display);
|
||||
}
|
||||
);
|
||||
|
||||
$("#iframepopup"+id).dialog({
|
||||
resizable:false,
|
||||
width: "300px",
|
||||
position:[x+200,y-200],
|
||||
title: "Chart Iframe",
|
||||
dialogClass: "dev-info-dialog"
|
||||
});
|
||||
$("#iframepopup"+id).dialog({
|
||||
resizable:false,
|
||||
width: "300px",
|
||||
position:[x+200,y-200],
|
||||
title: "Chart Iframe",
|
||||
dialogClass: "dev-info-dialog"
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
var uiCloseClick = function (channel_id) {
|
||||
return function() {
|
||||
var id = $( this ).parents( ".portlet:first" ).attr("id").substring(8);
|
||||
var portlet = $( this ).parents( ".portlet:first" ) ;
|
||||
$.update("/channels/"+channel_id+"/windows/"+id+"/hide" ,
|
||||
function(response) {
|
||||
portlet.hide("drop", function(){
|
||||
portlet.remove();});
|
||||
}) ;
|
||||
var id = $( this ).parents( ".portlet:first" ).attr("id").substring(8);
|
||||
var portlet = $( this ).parents( ".portlet:first" ) ;
|
||||
$.update("/channels/"+channel_id+"/windows/"+id+"/hide" ,
|
||||
function(response) {
|
||||
portlet.hide("drop", function(){
|
||||
portlet.remove();});
|
||||
}) ;
|
||||
}
|
||||
}
|
||||
|
||||
|
17
app/assets/stylesheets/active_admin.css.scss
Normal file
17
app/assets/stylesheets/active_admin.css.scss
Normal file
@ -0,0 +1,17 @@
|
||||
// SASS variable overrides must be declared before loading up Active Admin's styles.
|
||||
//
|
||||
// To view the variables that Active Admin provides, take a look at
|
||||
// `app/assets/stylesheets/active_admin/mixins/_variables.css.scss` in the
|
||||
// Active Admin source.
|
||||
//
|
||||
// For example, to change the sidebar width:
|
||||
// $sidebar-width: 242px;
|
||||
|
||||
// Active Admin's got SASS!
|
||||
@import "active_admin/mixins";
|
||||
@import "active_admin/base";
|
||||
|
||||
// Overriding any non-variable SASS must be done after the fact.
|
||||
// For example, to change the default status-tag color:
|
||||
//
|
||||
// .status_tag { background: #6090DB; }
|
@ -7,6 +7,7 @@
|
||||
body { padding-top: 70px; }
|
||||
.break-word { word-break: break-word; }
|
||||
.col-pad { padding: 0 15px; }
|
||||
.dismiss { float: right; cursor: pointer; position: relative; top: -12px; left: 7px; }
|
||||
|
||||
/* multiline forms */
|
||||
.form-horizontal .multiline-label { margin-top: -10px; }
|
||||
@ -29,7 +30,9 @@ body { padding-top: 70px; }
|
||||
.format-xml { display: none; }
|
||||
.format-block { min-height: 200px; }
|
||||
.format-block-lg { min-height: 350px; }
|
||||
|
||||
.format-block-xl { min-height: 400px; }
|
||||
.format-block-xxl { min-height: 600px; }
|
||||
.format-block-xxxl { min-height: 1000px; }
|
||||
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
@ -70,7 +73,6 @@ body {
|
||||
.apps a:hover,
|
||||
.apps:hover div { text-decoration: none; }
|
||||
.commentarea { width: 300px; height: 80px; }
|
||||
#options { float: right; text-align: right; }
|
||||
#login {
|
||||
padding: 6px;
|
||||
border: 1px solid #bbbbbb;
|
||||
@ -228,10 +230,9 @@ input[type="text"].midfield { width: 120px; }
|
||||
textarea.tweet { margin-top: 0.5em; width: 40em; height: 3em; }
|
||||
|
||||
/* error messages */
|
||||
.errorExplanation { width: 95%; background-color: #ffffe0; display: table; margin-bottom: 20px; padding: 10px; border: 1px solid #aaaaaa; }
|
||||
#error {
|
||||
color: red;
|
||||
}
|
||||
.errorExplanation,
|
||||
#error_explanation { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; background-color: #f2dede; border-color: #ebccd1; color: #a94442; }
|
||||
#error { color: red; }
|
||||
.field_with_errors { display: inline; }
|
||||
/*.error_box { margin-top: 15px; padding: 5px; background-color: #f99; color: #300; border: 1px solid #f66; }*/
|
||||
.warning_box { margin: 15px 0 15px 0; padding: 10px; background-color: #fc3; color: #000; border: 1px solid #f90; }
|
||||
|
@ -7,6 +7,13 @@
|
||||
border: 1px solid #bbb;
|
||||
border-radius: 5px;
|
||||
padding: 2px 0;
|
||||
margin-top: 20px;
|
||||
width: 228px;
|
||||
}
|
||||
|
||||
#bootstrap-sidebar li a {
|
||||
padding: 1px 8px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
#bootstrap-sidebar li a:hover {
|
||||
@ -18,11 +25,23 @@
|
||||
border-right-width:4px;
|
||||
}
|
||||
|
||||
#bootstrap-sidebar li.subitem a {
|
||||
padding-left: 30px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@media (min-width: 979px) {
|
||||
#bootstrap-sidebar.affix-top, #bootstrap-sidebar.affix {
|
||||
position: fixed;
|
||||
top:90px;
|
||||
width:228px;
|
||||
width: 228px;
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 978px) {
|
||||
#bootstrap-sidebar.affix-top, #bootstrap-sidebar.affix {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,14 @@
|
||||
class ApplicationController < ActionController::Base
|
||||
skip_before_filter :verify_authenticity_token
|
||||
# include all helpers for controllers
|
||||
helper :all
|
||||
# include these helper methods for views
|
||||
helper_method :current_user_session, :current_user, :logged_in?, :is_admin?, :get_header_value, :to_bytes
|
||||
helper_method :current_user_session, :current_user, :logged_in?, :get_header_value, :to_bytes
|
||||
protect_from_forgery
|
||||
before_filter :allow_cross_domain_access, :set_variables
|
||||
before_filter :configure_permitted_parameters, if: :devise_controller?
|
||||
after_filter :remove_headers
|
||||
before_filter :authenticate_user_from_token!
|
||||
|
||||
# responds with blank
|
||||
def respond_with_blank
|
||||
@ -33,8 +36,24 @@ class ApplicationController < ActionController::Base
|
||||
@ssl_api_domain ||= ssl_api_domain
|
||||
@locale ||= get_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
|
||||
|
||||
# allows use of daily params
|
||||
params[:timescale] = '1440' if params[:timescale] == 'daily'
|
||||
params[:average] = '1440' if params[:average] == 'daily'
|
||||
params[:median] = '1440' if params[:median] == 'daily'
|
||||
params[:sum] = '1440' if params[:sum] == 'daily'
|
||||
end
|
||||
|
||||
# change default devise sign_in page; make admins sign in work correctly
|
||||
def after_sign_in_path_for(resource)
|
||||
if resource.is_a?(AdminUser)
|
||||
admin_dashboard_path
|
||||
else
|
||||
channels_path
|
||||
end
|
||||
end
|
||||
|
||||
# get the locale, but don't fail if header value doesn't exist
|
||||
@ -52,8 +71,30 @@ class ApplicationController < ActionController::Base
|
||||
return locale
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def configure_permitted_parameters
|
||||
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:login, :email, :password, :password_confirmation, :remember_me) }
|
||||
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:login, :email, :password, :remember_me) }
|
||||
devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:login, :email, :password, :password_confirmation, :time_zone, :password_current) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# authenticates user based on token from users#api_login
|
||||
def authenticate_user_from_token!
|
||||
# exit if no login or token
|
||||
return false if params[:login].blank? || params[:token].blank?
|
||||
|
||||
# get the user by login or email
|
||||
user = User.find_by_login_or_email(params[:login])
|
||||
|
||||
# safe compare, avoids timing attacks
|
||||
if user.present? && Devise.secure_compare(user.authentication_token, params[:token])
|
||||
sign_in user, store: false
|
||||
end
|
||||
end
|
||||
|
||||
# remove headers if necessary
|
||||
def remove_headers
|
||||
response.headers.delete_if {|key| true} if params[:headers] == 'false'
|
||||
@ -71,15 +112,6 @@ class ApplicationController < ActionController::Base
|
||||
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
|
||||
@ -90,35 +122,16 @@ class ApplicationController < ActionController::Base
|
||||
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
|
||||
# set menus
|
||||
def set_support_menu; @menu = 'support'; 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 require_user
|
||||
logger.info "Require User"
|
||||
if current_user.nil?
|
||||
if current_user.nil? && User.find_by_api_key(get_apikey).nil?
|
||||
respond_to do |format|
|
||||
format.html {
|
||||
session[:link_back] = request.url
|
||||
@ -144,7 +157,7 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def require_admin
|
||||
unless current_user && is_admin?
|
||||
unless current_admin_user.present?
|
||||
render :nothing => true, :status => 403 and return
|
||||
false
|
||||
end
|
||||
@ -178,8 +191,10 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
# domain for the api
|
||||
def api_domain
|
||||
(Rails.env == 'production') ? API_DOMAIN : domain
|
||||
def api_domain(ssl=false)
|
||||
output = (Rails.env == 'production') ? API_DOMAIN : domain
|
||||
output = output.sub(/http:/, 'https:') if ssl == true
|
||||
return output
|
||||
end
|
||||
|
||||
# ssl domain for the api
|
||||
@ -275,9 +290,6 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
# 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)
|
||||
|
||||
# allow more past data if necessary
|
||||
get_old_data = (params[:results].present? || params[:start].present? || params[:days].present?) ? true : false
|
||||
|
||||
@ -292,8 +304,6 @@ class ApplicationController < ActionController::Base
|
||||
return date_range
|
||||
end
|
||||
|
||||
|
||||
|
||||
def set_time_zone(params)
|
||||
# set timezone correctly
|
||||
if params[:offset]
|
||||
|
@ -3,7 +3,7 @@ class ChannelsController < ApplicationController
|
||||
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
|
||||
protect_from_forgery :except => [:post_data, :create, :destroy, :clear]
|
||||
require 'csv'
|
||||
|
||||
# view list of watched channels
|
||||
@ -29,17 +29,27 @@ class ChannelsController < ApplicationController
|
||||
|
||||
# list public channels
|
||||
def public
|
||||
@domain = domain
|
||||
# default blank response
|
||||
@channels = Channel.where(:id => 0).paginate :page => params[:page]
|
||||
|
||||
# get channels by ids
|
||||
if params[:channel_ids].present?
|
||||
flash[:notice] = t(:selected_channels)
|
||||
@header = 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 user
|
||||
elsif params[:username].present?
|
||||
@header = "#{t(:user).capitalize}: #{params[:username]}"
|
||||
searched_user = User.find_by_login(params[:username])
|
||||
@channels = searched_user.channels.public_viewable.active.order('ranking desc, updated_at DESC').paginate :page => params[:page] if searched_user.present?
|
||||
# get channels that match a tag
|
||||
elsif params[:tag].present?
|
||||
flash[:notice] = "#{t(:tag).capitalize}: #{params[:tag]}"
|
||||
@header = "#{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)
|
||||
@header = t(:featured_channels)
|
||||
respond_with_error(:error_resource_not_found) and return if params[:page] == '0'
|
||||
@channels = Channel.public_viewable.active.order('ranking desc, updated_at DESC').paginate :page => params[:page]
|
||||
end
|
||||
|
||||
@ -119,11 +129,10 @@ class ChannelsController < ApplicationController
|
||||
end
|
||||
|
||||
def index
|
||||
|
||||
@channels = current_user.channels
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.json { render :json => @channels }
|
||||
format.json { render :json => @channels.to_json(:root => false) }
|
||||
end
|
||||
end
|
||||
|
||||
@ -180,29 +189,52 @@ class ChannelsController < ApplicationController
|
||||
|
||||
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")
|
||||
# get the current user or find the user via their api key
|
||||
@user = current_user || User.find_by_api_key(get_apikey)
|
||||
channel = @user.channels.create(:field1 => "#{t(:channel_default_field)} 1")
|
||||
|
||||
# make updating attributes easier
|
||||
params[:channel] = params
|
||||
channel.update_attributes(channel_params)
|
||||
|
||||
channel.set_windows
|
||||
channel.save
|
||||
channel.save_tags(params[:channel][:tags]) if params[:channel][:tags].present?
|
||||
channel.add_write_api_key
|
||||
@channel_id = channel.id
|
||||
redirect_to channel_path(@channel_id, :anchor => "channelsettings")
|
||||
respond_to do |format|
|
||||
format.json { render :json => channel.to_json(Channel.private_options) }
|
||||
format.xml { render :xml => channel.to_xml(Channel.private_options) }
|
||||
format.any { redirect_to channel_path(@channel_id, :anchor => "channelsettings") }
|
||||
end
|
||||
end
|
||||
|
||||
# clear all data from a channel
|
||||
def clear
|
||||
channel = current_user.channels.find(params[:id])
|
||||
# get the current user or find the user via their api key
|
||||
@user = current_user || User.find_by_api_key(get_apikey)
|
||||
channel = @user.channels.find(params[:id])
|
||||
channel.delete_feeds
|
||||
redirect_to channel_path(channel.id)
|
||||
respond_to do |format|
|
||||
format.json { render :json => [] }
|
||||
format.xml { render :xml => [] }
|
||||
format.any { redirect_to channel_path(channel.id) }
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
channel = current_user.channels.find(params[:id])
|
||||
channel.destroy
|
||||
redirect_to channels_path
|
||||
# get the current user or find the user via their api key
|
||||
@user = current_user || User.find_by_api_key(get_apikey)
|
||||
@channel = @user.channels.find(params[:id])
|
||||
@channel.destroy
|
||||
respond_to do |format|
|
||||
format.json { render :json => @channel.to_json(Channel.public_options) }
|
||||
format.xml { render :xml => @channel.to_xml(Channel.public_options) }
|
||||
format.any { redirect_to channels_path, :status => 303 }
|
||||
end
|
||||
end
|
||||
|
||||
# response is '0' if failure, 'entry_id' if success
|
||||
@ -311,8 +343,13 @@ class ChannelsController < ApplicationController
|
||||
# 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
|
||||
# normal route, respond with the feed
|
||||
respond_to do |format|
|
||||
format.html { render :text => status }
|
||||
format.json { render :json => feed.to_json }
|
||||
format.xml { render :xml => feed.to_xml(Feed.public_options) }
|
||||
format.any { render :text => status }
|
||||
end and return
|
||||
end
|
||||
|
||||
# import view
|
||||
|
@ -55,8 +55,8 @@ class ChartsController < ApplicationController
|
||||
params[:bgcolor] = fix_color(params[:bgcolor])
|
||||
|
||||
# set ssl
|
||||
@ssl = (get_header_value('x_ssl') == 'true')
|
||||
@domain = domain(@ssl)
|
||||
ssl = (get_header_value('x_ssl') == 'true')
|
||||
@domain = domain(ssl)
|
||||
|
||||
# should data be pushed off the end in dynamic chart
|
||||
@push = (params[:push] and params[:push] == 'false') ? false : true
|
||||
|
@ -16,7 +16,7 @@ class CommentsController < ApplicationController
|
||||
@comment.body = params[:comment][:body].gsub(/<\/?[^>]*>/, '').gsub(/\n/, '<br />')
|
||||
# save comment
|
||||
if @comment.save
|
||||
flash[:success] = "Thanks for adding a comment!"
|
||||
flash[:notice] = "Thanks for adding a comment!"
|
||||
else
|
||||
flash[:error] = "Comment can't be blank!"
|
||||
end
|
||||
@ -45,3 +45,4 @@ class CommentsController < ApplicationController
|
||||
redirect_to :back
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,6 +1,45 @@
|
||||
class DocsController < ApplicationController
|
||||
before_filter :set_support_menu
|
||||
|
||||
def index; ;end
|
||||
def index; ; end
|
||||
def errors; ; end
|
||||
def tweetcontrol; ; end
|
||||
def plugins; ; end
|
||||
def importer; ; end
|
||||
def charts; ; end
|
||||
def users; ; end
|
||||
|
||||
def channels
|
||||
# default values
|
||||
@channel_api_key = 'XXXXXXXXXXXXXXXX'
|
||||
@user_api_key = 'XXXXXXXXXXXXXXXX'
|
||||
|
||||
# if user is signed in
|
||||
if current_user && current_user.channels.any?
|
||||
@channel_api_key = current_user.channels.order('updated_at desc').first.write_api_key
|
||||
@user_api_key = current_user.api_key
|
||||
end
|
||||
end
|
||||
|
||||
def thinghttp
|
||||
# default values
|
||||
@thinghttp_api_key = 'XXXXXXXXXXXXXXXX'
|
||||
|
||||
# if user is signed in
|
||||
if current_user && current_user.thinghttps.any?
|
||||
@thinghttp_api_key = current_user.thinghttps.order('updated_at desc').first.api_key
|
||||
end
|
||||
end
|
||||
|
||||
def thingtweet
|
||||
# default values
|
||||
@thingtweet_api_key = 'XXXXXXXXXXXXXXXX'
|
||||
|
||||
# if user is signed in
|
||||
if current_user && current_user.twitter_accounts.any?
|
||||
@thingtweet_api_key = current_user.twitter_accounts.order('updated_at desc').first.api_key
|
||||
end
|
||||
end
|
||||
|
||||
def talkback
|
||||
# default values
|
||||
|
@ -194,13 +194,13 @@ class FeedController < ApplicationController
|
||||
output = @feed.to_xml
|
||||
elsif params[:format] == 'csv'
|
||||
@csv_headers = Feed.select_options(@channel, params)
|
||||
elsif (params[:format] == 'txt' or params[:format] == 'text')
|
||||
elsif (params[:format] == 'txt' || params[:format] == 'text' || params[:format] == 'html' || params[:format].blank?)
|
||||
output = add_prepend_append(@feed["field#{params[:field_id]}"])
|
||||
else
|
||||
output = @feed.to_json
|
||||
|
||||
end
|
||||
# else set error code
|
||||
|
||||
# else set error code
|
||||
else
|
||||
if params[:format] == 'xml'
|
||||
output = bad_feed_xml
|
||||
@ -211,7 +211,7 @@ class FeedController < ApplicationController
|
||||
|
||||
# output data in proper format
|
||||
respond_to do |format|
|
||||
format.html { render :json => output }
|
||||
format.html { render :text => output }
|
||||
format.json { render :json => output, :callback => params[:callback] }
|
||||
format.xml { render :xml => output }
|
||||
format.csv
|
||||
|
16
app/controllers/passwords_controller.rb
Normal file
16
app/controllers/passwords_controller.rb
Normal file
@ -0,0 +1,16 @@
|
||||
class SessionsController < Devise::SessionsController
|
||||
before_filter :fix_params, :only => :create
|
||||
|
||||
# don't modify default devise controllers
|
||||
def create; super; end
|
||||
def new; super; end
|
||||
|
||||
private
|
||||
|
||||
# fixes password reset params so that devise config.reset_password_keys can be set to email for activeadmin
|
||||
def fix_params
|
||||
params[:user][:login] = params[:user][:email]
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,5 +1,5 @@
|
||||
class PipesController < ApplicationController
|
||||
before_filter :require_admin, :set_admin_menu
|
||||
before_filter :require_admin
|
||||
|
||||
def index
|
||||
@pipes = Pipe.paginate :page => params[:page], :order => 'created_at DESC'
|
||||
@ -14,3 +14,4 @@ class PipesController < ApplicationController
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
22
app/controllers/registrations_controller.rb
Normal file
22
app/controllers/registrations_controller.rb
Normal file
@ -0,0 +1,22 @@
|
||||
class RegistrationsController < Devise::RegistrationsController
|
||||
include KeyUtilities
|
||||
after_filter :add_api_key, :only => :create
|
||||
|
||||
# use defaults from devise
|
||||
def new; super; end
|
||||
def edit; super; end
|
||||
def create; super; end
|
||||
|
||||
private
|
||||
|
||||
# adds an api key to the new user
|
||||
def add_api_key
|
||||
@user = current_user
|
||||
if @user.present?
|
||||
@user.api_key = generate_api_key(16, 'user')
|
||||
@user.save
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
32
app/controllers/sessions_controller.rb
Normal file
32
app/controllers/sessions_controller.rb
Normal file
@ -0,0 +1,32 @@
|
||||
class SessionsController < Devise::SessionsController
|
||||
after_filter :log_failed_login, :only => :new
|
||||
|
||||
# don't modify default devise controllers
|
||||
def create; super; end
|
||||
def new; super; end
|
||||
|
||||
private
|
||||
|
||||
# logs failed login attempts
|
||||
def log_failed_login
|
||||
if failed_login?
|
||||
# log to failedlogins
|
||||
failed = Failedlogin.new
|
||||
failed.login = params['user']['login']
|
||||
failed.password = params['user']['password']
|
||||
failed.ip_address = get_header_value('X_REAL_IP')
|
||||
failed.save
|
||||
|
||||
# prevent timing and brute force password attacks
|
||||
sleep 1
|
||||
end
|
||||
end
|
||||
|
||||
# true if a login fails
|
||||
def failed_login?
|
||||
options = env["warden.options"]
|
||||
return (options.present? && options[:action] == "unauthenticated")
|
||||
end
|
||||
|
||||
end
|
||||
|
83
app/controllers/stream_controller.rb
Normal file
83
app/controllers/stream_controller.rb
Normal file
@ -0,0 +1,83 @@
|
||||
class StreamController < ApplicationController
|
||||
include ActionController::Live
|
||||
require 'csv'
|
||||
|
||||
def channel_feed
|
||||
channel = Channel.find(params[:id])
|
||||
api_key = ApiKey.find_by_api_key(get_apikey)
|
||||
|
||||
# set timezone correctly
|
||||
set_time_zone(params)
|
||||
|
||||
# output proper http response if error
|
||||
render :text => '-1', :status => 400 and return if !channel_permission?(channel, api_key)
|
||||
|
||||
# set the attachment headers
|
||||
response.headers['Content-Type'] = 'text/csv'
|
||||
response.headers['Content-Disposition'] = 'attachment; filename=feeds.csv'
|
||||
|
||||
# get the feed headers
|
||||
csv_headers = Feed.select_options(channel, params)
|
||||
|
||||
# set the total records and batch size
|
||||
total_records = channel.feeds.count
|
||||
batch = 1000
|
||||
|
||||
# write the headers row
|
||||
response.stream.write "#{CSV.generate_line(csv_headers)}"
|
||||
|
||||
# for every 1000 records
|
||||
(0..(total_records - batch).abs).step(batch) do |i|
|
||||
# variable to hold the streaming output for this batch
|
||||
batch_output = ""
|
||||
# feeds query
|
||||
feeds = Feed.where(:channel_id => channel.id).order('entry_id asc').offset(i).limit(batch)
|
||||
|
||||
# for each feed, add the data according to the csv_headers
|
||||
feeds.each do |feed|
|
||||
row = []
|
||||
csv_headers.each { |attr| row.push(feed.send(attr)) }
|
||||
batch_output += CSV.generate_line(row)
|
||||
end
|
||||
|
||||
# write the output for this batch
|
||||
response.stream.write batch_output
|
||||
# add a slight delay between database queries
|
||||
sleep 0.1
|
||||
end
|
||||
ensure
|
||||
response.stream.close
|
||||
end
|
||||
|
||||
def stream_example
|
||||
# get the channel
|
||||
channel = Channel.find(params[:channel_id])
|
||||
|
||||
# stream the response
|
||||
response.headers['Content-Type'] = 'text/csv'
|
||||
response.headers['Content-Disposition'] = 'attachment; filename=feeds.csv'
|
||||
20.times {
|
||||
response.stream.write "hello world\n"
|
||||
sleep 1
|
||||
}
|
||||
ensure
|
||||
response.stream.close
|
||||
end
|
||||
|
||||
def stream_chunked_example
|
||||
#response.headers['Content-Type'] = 'text/event-stream'
|
||||
response.headers['Content-Type'] = 'text/csv'
|
||||
response.headers['Content-Disposition'] = 'attachment; filename=feeds.csv'
|
||||
response.headers['Transfer-Encoding'] = 'chunked'
|
||||
10.times {
|
||||
response.stream.write "4\n" # size must be in hex format?
|
||||
response.stream.write "hel\n\n"
|
||||
sleep 1
|
||||
}
|
||||
response.stream.write "0\n\n"
|
||||
ensure
|
||||
response.stream.close
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -1,55 +0,0 @@
|
||||
class UserSessionsController < ApplicationController
|
||||
before_filter :require_no_user, :only => [:new, :create]
|
||||
before_filter :require_user, :only => :destroy
|
||||
|
||||
def new
|
||||
@title = t(:signin)
|
||||
@user_session = UserSession.new
|
||||
@mail_message = session[:mail_message] if !session[:mail_message].nil?
|
||||
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 @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
|
||||
end
|
||||
end
|
@ -1,7 +1,37 @@
|
||||
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, :edit_profile]
|
||||
skip_before_filter :verify_authenticity_token, :only => [:api_login]
|
||||
before_filter :require_user, :only => [:show, :edit, :update, :edit_profile]
|
||||
|
||||
# delete account
|
||||
def destroy
|
||||
user = current_user
|
||||
user.delete
|
||||
flash[:notice] = t(:account_deleted)
|
||||
redirect_to root_path
|
||||
end
|
||||
|
||||
# allow login via api
|
||||
def api_login
|
||||
# get the user by login or email
|
||||
user = User.find_by_login_or_email(params[:login])
|
||||
|
||||
# exit if no user or invalid password
|
||||
respond_with_error(:error_auth_required) and return if user.blank? || !user.valid_password?(params[:password])
|
||||
|
||||
# save new authentication token
|
||||
if user.authentication_token.blank?
|
||||
user.authentication_token = Devise.friendly_token
|
||||
user.save
|
||||
end
|
||||
|
||||
# output the user with token
|
||||
respond_to do |format|
|
||||
format.json { render :json => user.as_json(User.private_options_plus(:authentication_token)) }
|
||||
format.xml { render :xml => user.to_xml(User.private_options_plus(:authentication_token)) }
|
||||
format.any { render :text => user.authentication_token }
|
||||
end
|
||||
end
|
||||
|
||||
# generates a new api key
|
||||
def new_api_key
|
||||
@ -34,8 +64,8 @@ class UsersController < ApplicationController
|
||||
|
||||
# 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)
|
||||
# authenticate the user if the user is logged in (can be via token) or api key matches the target user
|
||||
authenticated = (current_user == @user) || (User.find_by_api_key(get_apikey) == @user)
|
||||
# set options correctly
|
||||
options = authenticated ? User.private_options : User.public_options(@user)
|
||||
end
|
||||
@ -75,27 +105,6 @@ class UsersController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
def new
|
||||
@title = t(:signup)
|
||||
@user = User.new
|
||||
end
|
||||
|
||||
def create
|
||||
@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 channels_path and return
|
||||
end
|
||||
else
|
||||
render :action => :new
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def show
|
||||
@menu = 'account'
|
||||
@user = @current_user
|
||||
@ -106,48 +115,18 @@ class UsersController < ApplicationController
|
||||
@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
|
||||
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(user_params)
|
||||
redirect_to account_path
|
||||
else
|
||||
redirect_to reset_password_path
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
@menu = 'account'
|
||||
@user = @current_user # makes our views "cleaner" and more consistent
|
||||
|
||||
# delete password and confirmation from params if not present
|
||||
params[:user].delete(:password) if params[:user][:password].blank?
|
||||
|
||||
# check current password and update
|
||||
if @user.valid_password?(params[:password_current]) && @user.update_attributes(user_params)
|
||||
if @user.valid_password?(params[:user][:password_current]) && @user.update_attributes(user_params)
|
||||
# sign the user back in, since devise will log the user out on update
|
||||
sign_in(current_user, :bypass => true)
|
||||
flash[:notice] = t('devise.registrations.updated')
|
||||
redirect_to account_path
|
||||
else
|
||||
@user.errors.add(:base, t(:password_incorrect))
|
||||
|
2
app/helpers/admin_helper.rb
Normal file
2
app/helpers/admin_helper.rb
Normal file
@ -0,0 +1,2 @@
|
||||
module AdminHelper
|
||||
end
|
18
app/models/admin_user.rb
Normal file
18
app/models/admin_user.rb
Normal file
@ -0,0 +1,18 @@
|
||||
class AdminUser < ActiveRecord::Base
|
||||
# Include default devise modules. Others available are:
|
||||
# :token_authenticatable, :encryptable, :confirmable, :lockable, :timeoutable and :omniauthable
|
||||
devise :database_authenticatable,
|
||||
:recoverable, :rememberable, :trackable, :validatable
|
||||
|
||||
attr_accessor :login
|
||||
|
||||
protected
|
||||
|
||||
def self.find_for_database_authentication(warden_conditions)
|
||||
conditions = warden_conditions.dup
|
||||
login = conditions.delete(:login)
|
||||
where(conditions).where(["lower(email) = :value", { :value => login.strip.downcase }]).first
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -54,10 +54,11 @@ class Channel < ActiveRecord::Base
|
||||
has_many :feeds
|
||||
has_many :daily_feeds
|
||||
has_many :api_keys, :dependent => :destroy
|
||||
has_many :taggings
|
||||
has_many :taggings, :dependent => :destroy
|
||||
has_many :tags, :through => :taggings
|
||||
has_many :comments, :dependent => :destroy
|
||||
has_many :windows, :dependent => :destroy, :autosave => true
|
||||
accepts_nested_attributes_for :tags
|
||||
|
||||
self.include_root_in_json = true
|
||||
|
||||
@ -81,6 +82,18 @@ class Channel < ActiveRecord::Base
|
||||
cattr_reader :per_page
|
||||
@@per_page = 15
|
||||
|
||||
# how often the channel is updated
|
||||
def update_rate
|
||||
last_feeds = self.feeds.order('entry_id desc').limit(2)
|
||||
rate = (last_feeds.first.created_at - last_feeds.last.created_at) if last_feeds.length == 2
|
||||
return rate
|
||||
end
|
||||
|
||||
# write key for a channel
|
||||
def write_api_key
|
||||
self.api_keys.where(:write_flag => true).first.api_key
|
||||
end
|
||||
|
||||
# select options
|
||||
def select_options
|
||||
only = [:name, :created_at, :updated_at, :id, :last_entry_id]
|
||||
@ -159,6 +172,19 @@ class Channel < ActiveRecord::Base
|
||||
}
|
||||
end
|
||||
|
||||
# used when creating a channel
|
||||
def self.private_options
|
||||
{
|
||||
:root => false,
|
||||
:only => [:id, :name, :description, :latitude, :longitude, :last_entry_id, :elevation, :created_at, :ranking],
|
||||
:methods => :username,
|
||||
:include => {
|
||||
:tags => {:only => [:id, :name]},
|
||||
:api_keys => {:only => [:api_key, :write_flag]}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
# login name of the user who created the channel
|
||||
def username; self.user.try(:login); end
|
||||
|
||||
@ -321,14 +347,15 @@ class Channel < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def delete_feeds
|
||||
if self.feeds.count < 1000
|
||||
# if a small number of feeds or redis is not present
|
||||
if self.feeds.count < 1000 || REDIS_ENABLED == false
|
||||
Feed.delete_all(["channel_id = ?", self.id])
|
||||
DailyFeed.delete_all(["channel_id = ?", self.id])
|
||||
begin
|
||||
self.update_attribute(:last_entry_id, nil)
|
||||
rescue Exception => e
|
||||
end
|
||||
|
||||
# else delete via background resque job
|
||||
else
|
||||
self.update_attribute(:clearing, true)
|
||||
Resque.enqueue(ClearChannelJob, self.id)
|
||||
|
@ -33,6 +33,13 @@ class Feed < ActiveRecord::Base
|
||||
|
||||
attr_readonly :created_at
|
||||
|
||||
# for to_xml, return only the public attributes
|
||||
def self.public_options
|
||||
{
|
||||
:except => [:id, :updated_at]
|
||||
}
|
||||
end
|
||||
|
||||
# only output these fields for feed
|
||||
def self.select_options(channel, params)
|
||||
only = [:created_at]
|
||||
@ -96,7 +103,7 @@ class Feed < ActiveRecord::Base
|
||||
|
||||
# custom json output
|
||||
def as_json(options = {})
|
||||
super(options.merge(:except => [:updated_at, :id]))
|
||||
super(Feed.public_options)
|
||||
end
|
||||
|
||||
# check if a field value is a number
|
||||
|
@ -2,24 +2,27 @@
|
||||
#
|
||||
# Table name: users
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# login :string(255) not null
|
||||
# email :string(255) not null
|
||||
# crypted_password :string(255) not null
|
||||
# password_salt :string(255) not null
|
||||
# persistence_token :string(255) not null
|
||||
# perishable_token :string(255) not null
|
||||
# current_login_at :datetime
|
||||
# last_login_at :datetime
|
||||
# current_login_ip :string(255)
|
||||
# last_login_ip :string(255)
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# time_zone :string(255)
|
||||
# public_flag :boolean default(FALSE)
|
||||
# bio :text
|
||||
# website :string(255)
|
||||
# api_key :string(16)
|
||||
# id :integer not null, primary key
|
||||
# login :string(255) not null
|
||||
# email :string(255) not null
|
||||
# encrypted_password :string(255) not null
|
||||
# password_salt :string(255)
|
||||
# current_sign_in_at :datetime
|
||||
# last_sign_in_at :datetime
|
||||
# current_sign_in_ip :string(255)
|
||||
# last_sign_in_ip :string(255)
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# time_zone :string(255)
|
||||
# public_flag :boolean default(FALSE)
|
||||
# bio :text
|
||||
# website :string(255)
|
||||
# api_key :string(16)
|
||||
# reset_password_token :string(255)
|
||||
# reset_password_sent_at :datetime
|
||||
# remember_created_at :datetime
|
||||
# sign_in_count :integer default(0), not null
|
||||
# authentication_token :string(255)
|
||||
#
|
||||
|
||||
####### NOTE #######
|
||||
@ -28,21 +31,20 @@
|
||||
####################
|
||||
class User < ActiveRecord::Base
|
||||
include KeyUtilities
|
||||
has_many :channels
|
||||
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
|
||||
has_many :channels, :dependent => :destroy
|
||||
has_many :twitter_accounts, :dependent => :destroy
|
||||
has_many :thinghttps, :dependent => :destroy
|
||||
has_many :tweetcontrols, :dependent => :destroy
|
||||
has_many :reacts, :dependent => :destroy
|
||||
has_many :scheduled_thinghttps, :dependent => :destroy
|
||||
has_many :talkbacks, :dependent => :destroy
|
||||
has_many :plugins
|
||||
has_many :devices
|
||||
has_many :api_keys
|
||||
has_many :plugins, :dependent => :destroy
|
||||
has_many :devices, :dependent => :destroy
|
||||
has_many :api_keys, :dependent => :destroy
|
||||
has_many :watchings, :dependent => :destroy
|
||||
has_many :watched_channels, :through => :watchings, :source => :channel
|
||||
has_many :comments
|
||||
|
||||
acts_as_authentic
|
||||
has_many :watched_channels, :through => :watchings, :source => :channel, :dependent => :destroy
|
||||
has_many :comments, :dependent => :destroy
|
||||
|
||||
self.include_root_in_json = false
|
||||
|
||||
@ -50,6 +52,39 @@ class User < ActiveRecord::Base
|
||||
cattr_reader :per_page
|
||||
@@per_page = 50
|
||||
|
||||
# allow login by login name also
|
||||
def self.find_first_by_auth_conditions(warden_conditions)
|
||||
conditions = warden_conditions.dup
|
||||
if login_param = conditions.delete(:login)
|
||||
where(conditions).where(["lower(login) = :value OR lower(email) = :value", { :value => login_param.downcase }]).first
|
||||
else
|
||||
where(conditions).first
|
||||
end
|
||||
end
|
||||
|
||||
# allow users to sign in with passwords from old authlogic authentication
|
||||
alias :devise_valid_password? :valid_password?
|
||||
def valid_password?(password)
|
||||
begin
|
||||
devise_valid_password?(password)
|
||||
rescue BCrypt::Errors::InvalidHash
|
||||
stretches = 20
|
||||
digest = "#{password}#{self.password_salt}"
|
||||
stretches.times {digest = Digest::SHA512.hexdigest(digest)}
|
||||
if digest == self.encrypted_password
|
||||
#Here update old Authlogic SHA512 Password with new Devise ByCrypt password
|
||||
# SOURCE: https://github.com/plataformatec/devise/blob/master/lib/devise/models/database_authenticatable.rb
|
||||
# Digests the password using bcrypt.
|
||||
self.encrypted_password = self.password_digest(password)
|
||||
self.save
|
||||
return true
|
||||
else
|
||||
# If not BCryt password and not old Authlogic SHA512 password don't authenticate user
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# find a user using login or email
|
||||
def self.find_by_login_or_email(login)
|
||||
User.find_by_login(login) || User.find_by_email(login)
|
||||
@ -81,6 +116,12 @@ class User < ActiveRecord::Base
|
||||
{ :only => [:id, :login, :created_at, :email, :website, :bio] }
|
||||
end
|
||||
|
||||
# add an extra attribute to private_options
|
||||
def self.private_options_plus(array)
|
||||
{ :only => User.private_options[:only].push(array).flatten }
|
||||
end
|
||||
|
||||
|
||||
# set new api key
|
||||
def set_new_api_key!
|
||||
new_api_key = generate_api_key(16, 'user')
|
||||
@ -90,4 +131,3 @@ class User < ActiveRecord::Base
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
4
app/views/admin/useful_links/_index.html.erb
Normal file
4
app/views/admin/useful_links/_index.html.erb
Normal file
@ -0,0 +1,4 @@
|
||||
<%= link_to 'List Email Addresses', admin_emails_path %>
|
||||
<br><br>
|
||||
<%= link_to 'List Users by Day (CSV)', admin_signups_path(:format => :csv) %>
|
||||
|
4
app/views/admin/users/emails.html.erb
Normal file
4
app/views/admin/users/emails.html.erb
Normal file
@ -0,0 +1,4 @@
|
||||
<% @users.each do |u| %>
|
||||
<%= u.email %><br>
|
||||
<% end %>
|
||||
|
2
app/views/admin/users/signups.csv.erb
Normal file
2
app/views/admin/users/signups.csv.erb
Normal file
@ -0,0 +1,2 @@
|
||||
<%= CSV.generate_line @csv_headers %><% @days.each do |day| %><% row = [] %><% @csv_headers.each do |attr| %><% row.push(day.to_json) %><% end %><%= CSV.generate_line(day).html_safe %><% end %>
|
||||
|
@ -1,11 +1,11 @@
|
||||
<div class="FL">
|
||||
<h3><%= t(:api_key_write) %></h3>
|
||||
<%= @write_key %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
|
||||
<%= button_to t(:api_key_write_new), channel_api_keys_path(@channel, :write => 1), :data => { :confirm => t(:confirm_new_api_key) } %>
|
||||
|
||||
<br /><br />
|
||||
<br><br>
|
||||
|
||||
<h3><%= t(:api_key_read) %></h3>
|
||||
<% @read_keys.each do |read_key| %>
|
||||
@ -29,22 +29,22 @@
|
||||
<%= button_to t(:api_key_delete), channel_api_key_path(@channel, read_key), :method => 'delete', :data => { :confirm => t(:confirm_read_key_delete) } %></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
|
||||
<% end %>
|
||||
|
||||
<%= button_to t(:api_key_read_new), channel_api_keys_path(@channel, :write => 0) %>
|
||||
<br />
|
||||
<br>
|
||||
</div>
|
||||
<div id="sidebar_old">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="helplink">(<a target="_blank" href="http://community.thingspeak.com/documentation/api/#api_keys"><%= t(:help_options) %></a>)</div>
|
||||
<div class="helplink">(<a target="_blank" href="/docs/channels#api_keys"><%= t(:help_options) %></a>)</div>
|
||||
<h2><%= t(:help) %></h2>
|
||||
<%= t(:help_channel_write_key) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:help_channel_read_key) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:help_channel_read_key_note) %>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="apps">
|
||||
<%= link_to thingtweets_path do %>
|
||||
<%= image_tag 'thingtweet.png', :size => '104x104' %>
|
||||
<br />
|
||||
<br>
|
||||
<%= t(:thingtweet) %>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -16,7 +16,7 @@
|
||||
<div class="apps">
|
||||
<%= link_to thinghttp_index_path do %>
|
||||
<%= image_tag 'thinghttp.png', :size => '104x104' %>
|
||||
<br />
|
||||
<br>
|
||||
<%= t(:thinghttp) %>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -24,7 +24,7 @@
|
||||
<div class="apps">
|
||||
<%= link_to tweetcontrol_index_path do %>
|
||||
<%= image_tag 'tweetcontrol.png', :size => '104x104' %>
|
||||
<br />
|
||||
<br>
|
||||
<%= t(:tweetcontrol) %>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -32,7 +32,7 @@
|
||||
<div class="apps">
|
||||
<%= link_to reacts_path do %>
|
||||
<%= image_tag 'react.png', :size => '104x104' %>
|
||||
<br />
|
||||
<br>
|
||||
<%= t(:react) %>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -40,16 +40,16 @@
|
||||
<div class="apps">
|
||||
<%= link_to talkbacks_path do %>
|
||||
<%= image_tag 'talkback.png', :size => '104x104' %>
|
||||
<br />
|
||||
<br>
|
||||
<%= t(:talkback) %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<% if is_admin? %>
|
||||
<% if current_admin_user.present? %>
|
||||
<div class="apps">
|
||||
<%= link_to scheduled_thinghttps_path do %>
|
||||
<%= image_tag 'scheduled_thinghttp.png', :size => '104x104' %>
|
||||
<br />
|
||||
<br>
|
||||
<%= t(:scheduled_thinghttp) %>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -66,7 +66,7 @@
|
||||
|
||||
<h3>
|
||||
<%= t(:thingtweet) %>
|
||||
<div class="helplink">(<a href="http://community.thingspeak.com/documentation/apps/thingtweet/"><%= t(:help_options) %></a>)</div>
|
||||
<div class="helplink">(<a href="/docs/thingtweet/"><%= t(:help_options) %></a>)</div>
|
||||
</h3>
|
||||
<%= t(:help_apps_thingtweet) %>
|
||||
<ul>
|
||||
@ -77,7 +77,7 @@
|
||||
|
||||
<h3>
|
||||
<%= t(:thinghttp) %>
|
||||
<div class="helplink">(<a href="http://community.thingspeak.com/documentation/apps/thinghttp/"><%= t(:help_options) %></a>)</div>
|
||||
<div class="helplink">(<a href="/docs/thinghttp/"><%= t(:help_options) %></a>)</div>
|
||||
</h3>
|
||||
<%= t(:help_apps_thinghttp) %>
|
||||
<ul>
|
||||
@ -88,14 +88,14 @@
|
||||
|
||||
<h3>
|
||||
<%= t(:tweetcontrol) %>
|
||||
<div class="helplink">(<a href="http://community.thingspeak.com/documentation/apps/tweetcontrol/"><%= t(:help_options) %></a>)</div>
|
||||
<div class="helplink">(<a href="/docs/tweetcontrol"><%= t(:help_options) %></a>)</div>
|
||||
</h3>
|
||||
<%= t(:help_apps_tweetcontrol) %>
|
||||
<br><br>
|
||||
|
||||
<h3>
|
||||
<%= t(:react) %>
|
||||
<div class="helplink">(<a href="http://community.thingspeak.com/documentation/apps/react/"><%= t(:help_options) %></a>)</div>
|
||||
<div class="helplink">(<a href="/docs/react"><%= t(:help_options) %></a>)</div>
|
||||
</h3>
|
||||
<%= t(:help_apps_react) %>
|
||||
<br><br>
|
||||
|
@ -1,10 +1,5 @@
|
||||
<div class="FL">
|
||||
<% flash.each do |name, msg| %>
|
||||
<div id="<%= name %>" class="fade" >
|
||||
<p><%= msg %></p>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= form_for @channel, :html => {:method => 'put'} do |c| %>
|
||||
<% unless session[:errors].nil?
|
||||
session[:errors].each do |attr, msg|
|
||||
@ -109,12 +104,12 @@
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
<br /><br />
|
||||
<br><br>
|
||||
|
||||
<h4><%= t(:channel_clear_message) %></h4>
|
||||
<%= button_to t(:channel_clear), { :controller => 'channels', :action => 'clear', :id => @channel.id }, :data => { :confirm => t(:confirm_channel_clear) } %>
|
||||
<%= button_to t(:channel_clear), "/channels/#{@channel.id}/clear", :data => { :confirm => t(:confirm_channel_clear) } %>
|
||||
|
||||
<br /><br />
|
||||
<br><br>
|
||||
|
||||
<h4><%= t(:channel_delete_message) %></h4>
|
||||
<%= button_to t(:channel_delete), channel_path(@channel.id), :method => 'delete', :data => { :confirm => t(:confirm_channel_delete) } %>
|
||||
@ -124,17 +119,17 @@
|
||||
<li>
|
||||
<h2><%= t(:help) %></h2>
|
||||
<%= t(:help_channel_public) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:help_channel_url) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:help_channel_video) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:help_channel_fields) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:help_channel_clear) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:help_channel_ranking) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -1,46 +1,48 @@
|
||||
<div class="FL">
|
||||
<h3><%= t(:import) %></h3>
|
||||
<%= t(:upload_select) %>
|
||||
<br /><br />
|
||||
<% flash.each do |name, msg| %>
|
||||
<div id="<%= name %>" class="fade" >
|
||||
<p><%= msg %></p>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
<% end %>
|
||||
<br>
|
||||
|
||||
<%= form_for :upload, :url => upload_channel_path(@channel), :html => { :multipart => true } do |f| %>
|
||||
<%= f.file_field :csv %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:time_zone) %>
|
||||
<%= time_zone_select 'feed', 'time_zone', nil, :default => 'UTC' %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= f.submit t(:upload), :disable_with => t(:uploading) %>
|
||||
<% end %>
|
||||
|
||||
<br><br>
|
||||
|
||||
<h3><%= t(:export) %></h3>
|
||||
<%= t(:download_feeds) %>
|
||||
<br><br>
|
||||
<%= button_to t(:download), "#{@ssl_api_domain}stream/channels/#{@channel.id}/feeds?api_key=#{@channel.write_api_key}" %>
|
||||
</div>
|
||||
|
||||
<div id="sidebar_old" >
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
<div class="helplink">(<a target="_blank" href="http://community.thingspeak.com/documentation/api/#send_data"><%= t(:help_options) %></a>)</div>
|
||||
<div class="helplink">(<a target="_blank" href="/docs/channels#update"><%= t(:help_options) %></a>)</div>
|
||||
<h2><%= t(:help_channel_update) %></h2>
|
||||
<%= t(:help_channel_post) %>
|
||||
<div class="code"><%= "#{@api_domain}update" %></div>
|
||||
<%= t(:help_channel_post_example) %>
|
||||
<div class="code"><%= "#{@api_domain}update?key=#{@key}&field1=0" %></div>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
|
||||
<div class="helplink">(<a target="_blank" href="http://community.thingspeak.com/documentation/api/#view_data"><%= t(:help_options) %></a>)</div>
|
||||
<div class="helplink">(<a target="_blank" href="/docs/channels#get_feed"><%= t(:help_options) %></a>)</div>
|
||||
<h2><%= t(:help_channel_feed) %></h2>
|
||||
<%= t(:help_channel_view) %>
|
||||
<br />
|
||||
<br>
|
||||
<div class="code"><%= link_to "#{@api_domain}channels/#{@channel.id}/feed.json?key=#{@key}", "#{@api_domain}channels/#{@channel.id}/feed.json?key=#{@key}", :target => '_blank' %></div>
|
||||
<br />
|
||||
<br>
|
||||
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<div class="helplink">(<a target="_blank" href="http://community.thingspeak.com/documentation/#importer"><%= t(:help_options) %></a>)</div>
|
||||
<div class="helplink">(<a target="_blank" href="/docs/channels#importer"><%= t(:help_options) %></a>)</div>
|
||||
<h2><%= t(:help) %></h2>
|
||||
<%= t(:help_channel_import) %>
|
||||
</li>
|
||||
|
@ -18,7 +18,7 @@
|
||||
<%= f.error_messages %>
|
||||
<input name='userlogin' class='userlogin' />
|
||||
<%= f.text_area :body, :rows => 7, :cols => 54 %>
|
||||
<br />
|
||||
<br>
|
||||
<%= submit_tag %>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -56,7 +56,7 @@ function commentflag(m_id, user_id)
|
||||
type: 'POST',
|
||||
data: {user_id : user_id},
|
||||
success: function(data) {
|
||||
$('#commentflag' + m_id).parent().parent().append("<span id='votemsg" + m_id + "'><br /><br /><%= t(:comment_reported) %></span>");
|
||||
$('#commentflag' + m_id).parent().parent().append("<span id='votemsg" + m_id + "'><br><br><%= t(:comment_reported) %></span>");
|
||||
$('#imgflag' + m_id).attr('src', '/images/flag_red.gif');
|
||||
$('#flaglink' + m_id).removeAttr('onclick');
|
||||
$('#flaglink' + m_id).unbind('mouseenter mouseleave');
|
||||
|
@ -32,7 +32,7 @@
|
||||
|
||||
</table>
|
||||
|
||||
<br />
|
||||
<br>
|
||||
|
||||
<% end %>
|
||||
|
||||
@ -41,18 +41,18 @@
|
||||
<%= d.submit t(:channel_create), :class => 'btn btn-primary' %>
|
||||
<% end %>
|
||||
|
||||
<% if is_admin? %>
|
||||
<% if current_admin_user.present? %>
|
||||
|
||||
<br /><br /><br />
|
||||
<br><br><br>
|
||||
|
||||
<% @channels.each do |c| %>
|
||||
<% if c.social %>
|
||||
<%= t(:social_channel) %>: <%= link_to c.name, (Rails.env == 'production') ? "http://www.socialsensornetwork.com/#{c.slug}" : "/s/#{c.slug}" %>
|
||||
<br />
|
||||
<br>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<br />
|
||||
<br>
|
||||
|
||||
<%= link_to t(:social_channel_create), :controller => 'channels', :action => 'social_new' %>
|
||||
<% end %>
|
||||
@ -66,7 +66,7 @@
|
||||
<div class="col-pad">
|
||||
|
||||
<%= t(:help_channel) %>
|
||||
(<a href="http://community.thingspeak.com/documentation/api/"><%= t(:help_options) %></a>)
|
||||
(<a href="/docs/channels"><%= t(:help_options) %></a>)
|
||||
<ul>
|
||||
<li><a href="http://community.thingspeak.com/tutorials/arduino/using-an-arduino-ethernet-shield-to-update-a-thingspeak-channel/">Arduino Tutorial</a></li>
|
||||
<li><a href="http://community.thingspeak.com/tutorials/netduino/create-your-own-web-of-things-using-the-netduino-plus-and-thingspeak/">Netduino Plus Tutorial</a></li>
|
||||
|
@ -1,11 +1,55 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-12">
|
||||
|
||||
<h4 class="breadcrumb"><%= flash[:notice] %></h4>
|
||||
<div class="col-xs-12 col-sm-9">
|
||||
<h4 class="breadcrumb"><%= @header %></h4>
|
||||
<%= render :partial => 'list' %>
|
||||
<br />
|
||||
<br>
|
||||
<%= will_paginate @channels %>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-3">
|
||||
<h4 class="breadcrumb"><%= t(:search).capitalize %></h4>
|
||||
|
||||
<div class="col-pad">
|
||||
<form role="form">
|
||||
<div class="form-group">
|
||||
<label for="tag_input"><%= t(:search_by_tag) %></label>
|
||||
<input type="text" class="form-control" id="tag_input" placeholder="<%= t(:search_input_tag) %>">
|
||||
</div>
|
||||
<button type="submit" id="search_tag" class="btn btn-primary btn-sm"><%= t(:submit) %></button>
|
||||
</form>
|
||||
|
||||
<br><br>
|
||||
|
||||
<form role="form">
|
||||
<div class="form-group">
|
||||
<label for="username_input"><%= t(:search_by_username) %></label>
|
||||
<input type="text" class="form-control" id="username_input" placeholder="<%= t(:search_input_username) %>">
|
||||
</div>
|
||||
<button type="submit" id="search_username" class="btn btn-primary btn-sm"><%= t(:submit) %></button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// when the document is ready
|
||||
$(document).ready(function() {
|
||||
|
||||
// when search tag is clicked
|
||||
$('#search_tag').on('click', function() {
|
||||
window.location.href = '<%= @domain %>channels/public?tag=' + encodeURIComponent($('#tag_input').val());
|
||||
return false;
|
||||
});
|
||||
|
||||
// when search username is clicked
|
||||
$('#search_username').on('click', function() {
|
||||
window.location.href = '<%= @domain %>channels/public?username=' + encodeURIComponent($('#username_input').val());
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -76,9 +76,9 @@
|
||||
<li>
|
||||
<h2><%= t(:help) %></h2>
|
||||
<%= t(:help_social_channel) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<div class="code">http://www.socialsensornetwork.com/<b>slug</b></div>
|
||||
<br />
|
||||
<br>
|
||||
<%= t(:help_social_channel_public) %>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -93,7 +93,7 @@
|
||||
|
||||
<% if !@channel.description.blank? %>
|
||||
<div class="default_text"><%= t(:channel_directions) %>: <%= @channel.description %></div>
|
||||
<br />
|
||||
<br>
|
||||
<% end %>
|
||||
|
||||
<%= form_tag "#{@post_url}" do %>
|
||||
@ -122,19 +122,19 @@
|
||||
</table>
|
||||
<% end %>
|
||||
|
||||
<br /><br />
|
||||
<br><br>
|
||||
|
||||
<div style="float: left; width: 600px;">
|
||||
|
||||
<iframe width="600" height="300" frameborder="0" scrolling="no" style="border: 1px solid #cccccc;" src="<%= @api_domain %>channels/<%= @channel.id %>/maps/default?width=600&height=300&results=100&round=2&status=true&dynamic=true"></iframe>
|
||||
|
||||
<% @fields.each do |f| %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<iframe width="600" height="300" frameborder="0" style="border: 1px solid #cccccc;" src="<%= @api_domain %>channels/<%= @channel.id %>/charts/<%= f[-1] %>?width=600&height=300&results=100&round=2&color=C46353&location=true&dynamic=true&push=false&max=20000&min=-20000"></iframe>
|
||||
<% end %>
|
||||
|
||||
<div id="footer">
|
||||
<br /><br />
|
||||
<br><br>
|
||||
©2011 Social Sensor Network - Powered by <a href="https://www.thingspeak.com/">ThingSpeak</a>
|
||||
</div>
|
||||
|
||||
|
@ -1,17 +1,15 @@
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="col-xs-12 col-sm-12">
|
||||
|
||||
<h4 class="breadcrumb"><%= t(:watched_channels) %></h4>
|
||||
|
||||
<div class="col-pad">
|
||||
<% if @channels.empty? %>
|
||||
<%= t(:watched_empty) %>
|
||||
<br /><br />
|
||||
<%= link_to t(:watched_find), public_channels_path %>
|
||||
<% else %>
|
||||
<%= render :partial => 'list' %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% if @channels.empty? %>
|
||||
<%= t(:watched_empty) %>
|
||||
<br><br>
|
||||
<%= link_to t(:watched_find), public_channels_path %>
|
||||
<% else %>
|
||||
<%= render :partial => 'list' %>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,10 +2,10 @@
|
||||
|
||||
<iframe id="iframe<%= index %>" width="<%= width %>" height="<%= height %>" style="border: 1px solid #cccccc;" src="" default_src="<%= src %>"></iframe>
|
||||
|
||||
<br /><br />
|
||||
<br><br>
|
||||
|
||||
<%= t(:chart_embed_code) %>:
|
||||
<br />
|
||||
<br>
|
||||
<textarea id="embed<%= index %>" rows="5" cols="53"><iframe width="<%= width %>" height="<%= height %>" style="border: 1px solid #cccccc;" src="<%= src %>"></iframe></textarea>
|
||||
|
||||
<br /><br /><br />
|
||||
<br><br><br>
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
<% @channel.attribute_names.each do |attr| %>
|
||||
<% if attr.index('field') and @channel[attr] and !@channel[attr].empty? %>
|
||||
|
||||
|
||||
<%= render :partial => 'config',
|
||||
:locals => {
|
||||
:displayconfig => true,
|
||||
@ -35,7 +35,7 @@
|
||||
:height => @height
|
||||
}
|
||||
%>
|
||||
|
||||
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
@ -43,12 +43,12 @@
|
||||
<div id="sidebar">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="helplink">(<a href="http://community.thingspeak.com/documentation/api/#charts"><%= t(:help_options) %></a>)</div>
|
||||
<div class="helplink">(<a href="/docs/charts"><%= t(:help_options) %></a>)</div>
|
||||
<h2><%= t(:help_charts) %></h2>
|
||||
<%= t(:help_charts_options) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:help_charts_embed) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
• <a href="http://community.thingspeak.com/tutorials/wordpress/how-to-embed-a-thingspeak-chart-on-your-wordpress-blog/">WordPress Tutorial</a>
|
||||
</li>
|
||||
</ul>
|
||||
@ -64,3 +64,4 @@
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||
<%= javascript_include_tag 'application' %>
|
||||
|
||||
<script type="text/javascript">
|
||||
@ -11,10 +11,10 @@
|
||||
|
||||
// converts date format from JSON
|
||||
function getChartDate(d) {
|
||||
// get the data using javascript's date object (year, month, day, hour, minute, second)
|
||||
// months in javascript start at 0, so remember to subtract 1 when specifying the month
|
||||
// offset in minutes is converted to milliseconds and subtracted so that chart's x-axis is correct
|
||||
return Date.UTC(d.substring(0,4), d.substring(5,7)-1, d.substring(8,10), d.substring(11,13), d.substring(14,16), d.substring(17,19)) - (myOffset * 60000);
|
||||
// get the data using javascript's date object (year, month, day, hour, minute, second)
|
||||
// months in javascript start at 0, so remember to subtract 1 when specifying the month
|
||||
// offset in minutes is converted to milliseconds and subtracted so that chart's x-axis is correct
|
||||
return Date.UTC(d.substring(0,4), d.substring(5,7)-1, d.substring(8,10), d.substring(11,13), d.substring(14,16), d.substring(17,19)) - (myOffset * 60000);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
@ -26,61 +26,51 @@
|
||||
var last_date;
|
||||
|
||||
// get the data with a webservice call
|
||||
|
||||
$.getJSON('<%= "#{@domain}channels/#{params[:channel_id]}/field/#{params[:id]}.json?callback=?&offset=0#{@qs}" %>', function(data) {
|
||||
|
||||
// if no access
|
||||
if (data == '-1') {
|
||||
$('#chart-container').append('<%= t(:chart_no_access) %>');
|
||||
}
|
||||
if (data == '-1') { $('#chart-container').append('<%= t(:chart_no_access) %>'); }
|
||||
|
||||
// iterate through each feed
|
||||
$.each(data.feeds, function() {
|
||||
var p = new Highcharts.Point();
|
||||
// set the proper values
|
||||
var v = this.field<%= params[:id] %>;
|
||||
p.x = getChartDate(this.created_at);
|
||||
p.y = parseFloat(v);
|
||||
// add location if possible
|
||||
if (this.location) { p.name = this.location; }
|
||||
// if a numerical value exists add it
|
||||
if (!isNaN(parseInt(v))<% if params[:max] %> && p.y <= <%= params[:max]%><% end %><% if params[:min] %> && p.y >= <%= params[:min]%><% end %>) { chartData.push(p); }
|
||||
});
|
||||
var p = new Highcharts.Point();
|
||||
// set the proper values
|
||||
var v = this.field<%= params[:id] %>;
|
||||
p.x = getChartDate(this.created_at);
|
||||
p.y = parseFloat(v);
|
||||
// add location if possible
|
||||
if (this.location) { p.name = this.location; }
|
||||
// if a numerical value exists add it
|
||||
if (!isNaN(parseInt(v))<% if params[:max] %> && p.y <= <%= params[:max]%><% end %><% if params[:min] %> && p.y >= <%= params[:min]%><% end %>) { chartData.push(p); }
|
||||
});
|
||||
|
||||
// specify the chart options
|
||||
var chartOptions = {
|
||||
chart: {
|
||||
chart: {
|
||||
renderTo: 'chart-container',
|
||||
defaultSeriesType: '<%= params[:type] ? "#{params[:type]}" : "line" %>',
|
||||
backgroundColor: '<%= params[:bgcolor] || "#ffffff" %>',
|
||||
events: {
|
||||
load: function() {
|
||||
//if dynamic and no "timeslice" options are set
|
||||
// GAK 02/16/2013 Let's try to add the last "average" slice if params[:average]
|
||||
|
||||
renderTo: 'chart-container',
|
||||
defaultSeriesType: '<%= params[:type] ? "#{params[:type]}" : "line" %>',
|
||||
backgroundColor: '<%= params[:bgcolor] || "#ffffff" %>',
|
||||
events: {
|
||||
load: function() {
|
||||
//if dynamic and no "timeslice" options are set
|
||||
// GAK 02/16/2013 Let's try to add the last "average" slice if params[:average]
|
||||
var url = '<%= "#{@domain}channels/#{params[:channel_id]}/feed/last.json?callback=?&offset=0&location=true#{@qs}" %>' ;
|
||||
if ("<%= params[:average] %>".length > 0) {
|
||||
url = '<%= "#{@domain}channels/#{params[:channel_id]}/feed/last_average.json?callback=?&offset=0&location=true&average=#{params[:average]}#{@qs}" %>' ;
|
||||
} else if ("<%= params[:median] %>".length > 0) {
|
||||
url = '<%= "#{@domain}channels/#{params[:channel_id]}/feed/last_median.json?callback=?&offset=0&location=true&median=#{params[:median]}#{@qs}" %>' ;
|
||||
} else if ("<%= params[:sum] %>".length > 0) {
|
||||
url = '<%= "#{@domain}channels/#{params[:channel_id]}/feed/last_sum.json?callback=?&offset=0&location=true&sum=#{params[:sum]}#{@qs}" %>' ;
|
||||
}
|
||||
|
||||
var url = '<%= "#{@domain}channels/#{params[:channel_id]}/feed/last.json?callback=?&offset=0&location=true#{@qs}" %>' ;
|
||||
if ("<%= params[:average] %>".length > 0) {
|
||||
url = '<%= "#{@domain}channels/#{params[:channel_id]}/feed/last_average.json?callback=?&offset=0&location=true&average=#{params[:average]}#{@qs}" %>' ;
|
||||
}
|
||||
else if ("<%= params[:median] %>".length > 0) {
|
||||
url = '<%= "#{@domain}channels/#{params[:channel_id]}/feed/last_median.json?callback=?&offset=0&location=true&median=#{params[:median]}#{@qs}" %>' ;
|
||||
}
|
||||
else if ("<%= params[:sum] %>".length > 0) {
|
||||
url = '<%= "#{@domain}channels/#{params[:channel_id]}/feed/last_sum.json?callback=?&offset=0&location=true&sum=#{params[:sum]}#{@qs}" %>' ;
|
||||
}
|
||||
|
||||
if ('true' === '<%= params[:dynamic] %>' && (
|
||||
'<%= params[:timescale] %>'.length < 1
|
||||
)) {
|
||||
// push data every 15 seconds
|
||||
|
||||
setInterval(function() {
|
||||
// get the data with a webservice call if we're just getting the last channel
|
||||
|
||||
$.getJSON(url, function(data) {
|
||||
// if data exists
|
||||
if (data && data.field<%= params[:id] %>) {
|
||||
if ('true' === '<%= params[:dynamic] %>' && ('<%= params[:timescale] %>'.length < 1)) {
|
||||
// push data every 15 seconds
|
||||
setInterval(function() {
|
||||
// get the data with a webservice call if we're just getting the last channel
|
||||
$.getJSON(url, function(data) {
|
||||
// if data exists
|
||||
if (data && data.field<%= params[:id] %>) {
|
||||
|
||||
var p = new Highcharts.Point();
|
||||
// set the proper values
|
||||
@ -92,7 +82,7 @@
|
||||
if (data.location) { p.name = data.location; }
|
||||
// get the last date if possible
|
||||
if (dynamicChart.series[0].data.length > 0) {
|
||||
last_date = dynamicChart.series[0].data[dynamicChart.series[0].data.length-1].x;
|
||||
last_date = dynamicChart.series[0].data[dynamicChart.series[0].data.length-1].x;
|
||||
}
|
||||
var shift = <%= (@push=='true') ? 'true' : 'false' %> ; //default for shift
|
||||
|
||||
@ -101,19 +91,17 @@
|
||||
var results = <%= (@results) ? @results : 60 %>;
|
||||
|
||||
if ( results && dynamicChart.series[0].data.length+1 >= results ) {
|
||||
shift = true ;
|
||||
shift = true ;
|
||||
}
|
||||
// if a numerical value exists and it is a new date, add it
|
||||
if (!isNaN(parseInt(v)) && (p.x != last_date)<% if params[:max] %> && p.y <= <%= params[:max]%><% end %><% if params[:min] %> && p.y >= <%= params[:min]%><% end %>) {
|
||||
dynamicChart.series[0].addPoint(p, true, shift);
|
||||
dynamicChart.series[0].addPoint(p, true, shift);
|
||||
}
|
||||
else {
|
||||
dynamicChart.series[0].data[dynamicChart.series[0].data.length-1].update(p);
|
||||
dynamicChart.series[0].data[dynamicChart.series[0].data.length-1].update(p);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}, 15000);
|
||||
}
|
||||
@ -169,9 +157,9 @@
|
||||
enabled: false
|
||||
},
|
||||
series: [{
|
||||
name: data.channel.field<%= params[:id] %>
|
||||
}]
|
||||
};
|
||||
name: data.channel.field<%= params[:id] %>
|
||||
}]
|
||||
};
|
||||
|
||||
// add the data to the chart
|
||||
chartOptions.series[0].data = chartData;
|
||||
@ -184,14 +172,16 @@
|
||||
// draw the chart
|
||||
var dynamicChart = new Highcharts.Chart(chartOptions);
|
||||
|
||||
});
|
||||
}); // end getJSON ajax call
|
||||
|
||||
});
|
||||
}); // end document.ready
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body style='background-color: <%= params[:bgcolor] ? params[:bgcolor] : 'white' %>;'>
|
||||
<div id="chart-container" style="width: <%= params[:width] ? params[:width].to_i - 25 : @width.to_i - 25 %>px; height: <%= params[:height] ? params[:height].to_i - 25 : @height.to_i - 25 %>px;"></div>
|
||||
<div id="chart-container" style="width: <%= params[:width] ? params[:width].to_i - 25 : @width.to_i - 25 %>px; height: <%= params[:height] ? params[:height].to_i - 25 : @height.to_i - 25 %>px; display: table-cell; vertical-align: middle;">
|
||||
<%= image_tag 'ajax-loader.gif', :style => "margin: auto; display: block;" %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
<% end %>
|
||||
<span class="username"><%= link_to User.find(comment.user_id).login, list_channels_path(User.find(comment.user_id).login) %></span>
|
||||
<span class="prettydate"><%= time_ago_in_words(comment.created_at) %> <%= t(:ago) %></span>
|
||||
<br />
|
||||
<br>
|
||||
<div>
|
||||
<%= auto_link_urls(comment.body) %>
|
||||
</div>
|
||||
@ -32,7 +32,7 @@
|
||||
<input name='userlogin' class='userlogin' />
|
||||
<%= hidden_field_tag :parent_id, comment.id %>
|
||||
<%= f.text_area :body, :value => '', :class => 'commentarea' %>
|
||||
<br />
|
||||
<br>
|
||||
<%= submit_tag %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
13
app/views/devise/confirmations/new.html.erb
Normal file
13
app/views/devise/confirmations/new.html.erb
Normal file
@ -0,0 +1,13 @@
|
||||
<h2>Resend confirmation instructions</h2>
|
||||
|
||||
<%= form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f| %>
|
||||
<%= devise_error_messages! %>
|
||||
|
||||
<div><%= f.label :login %><br>
|
||||
<%= f.text_field :login, :autofocus => true %></div>
|
||||
|
||||
<div><%= f.submit "Resend confirmation instructions" %></div>
|
||||
<% end %>
|
||||
|
||||
<%= render "devise/shared/links" %>
|
||||
|
@ -0,0 +1,5 @@
|
||||
<p>Welcome <%= @email %>!</p>
|
||||
|
||||
<p>You can confirm your account email through the link below:</p>
|
||||
|
||||
<p><%= link_to 'Confirm my account', confirmation_url(@resource, :confirmation_token => @token) %></p>
|
@ -0,0 +1,8 @@
|
||||
<p>Hello <%= @resource.email %>!</p>
|
||||
|
||||
<p>Someone has requested a link to change your password. You can do this through the link below.</p>
|
||||
|
||||
<p><%= link_to 'Change my password', edit_password_url(@resource, :reset_password_token => @token) %></p>
|
||||
|
||||
<p>If you didn't request this, please ignore this email.</p>
|
||||
<p>Your password won't change until you access the link above and create a new one.</p>
|
7
app/views/devise/mailer/unlock_instructions.html.erb
Normal file
7
app/views/devise/mailer/unlock_instructions.html.erb
Normal file
@ -0,0 +1,7 @@
|
||||
<p>Hello <%= @resource.email %>!</p>
|
||||
|
||||
<p>Your account has been locked due to an excessive number of unsuccessful sign in attempts.</p>
|
||||
|
||||
<p>Click the link below to unlock your account:</p>
|
||||
|
||||
<p><%= link_to 'Unlock my account', unlock_url(@resource, :unlock_token => @token) %></p>
|
28
app/views/devise/passwords/edit.html.erb
Normal file
28
app/views/devise/passwords/edit.html.erb
Normal file
@ -0,0 +1,28 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-xs-12">
|
||||
|
||||
<h4 class="breadcrumb"><%= t(:password_new) %></h4>
|
||||
|
||||
<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put, :class => 'form-horizontal' }) do |f| %>
|
||||
<%= devise_error_messages! %>
|
||||
<%= f.hidden_field :reset_password_token %>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-3 control-label"><%= t(:password) %></label>
|
||||
<div class="col-sm-8 col-xs-6"><%= f.password_field :password, :autofocus => true, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-3 control-label"><%= t(:password_confirmation) %></label>
|
||||
<div class="col-sm-8 col-xs-6"><%= f.password_field :password_confirmation, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label"></label>
|
||||
<div class="col-sm-8"><p class="form-control-static"><%= f.submit t(:password_change), :class => 'btn btn-primary' %></p></div>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
34
app/views/devise/passwords/new.html.erb
Normal file
34
app/views/devise/passwords/new.html.erb
Normal file
@ -0,0 +1,34 @@
|
||||
<div class="col-sm-7 col-xs-12">
|
||||
|
||||
<h4 class="breadcrumb"><%= t(:password_forgot) %></h4>
|
||||
|
||||
<div class="col-pad">
|
||||
|
||||
<%= t(:password_forgot_message) %>
|
||||
|
||||
<br><br>
|
||||
|
||||
<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post, :class => 'form-horizontal' }) do |f| %>
|
||||
<%= devise_error_messages! %>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-3 col-xs-3 control-label"><%= t(:email) %></label>
|
||||
<div class="col-sm-9 col-xs-9"><%= f.text_field :email, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-3 col-sm-9 col-xs-offset-3 col-xs-9"><p class="form-control-static"><%= f.submit t(:submit), :class => 'btn btn-primary' %></p></div>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById('user_email').focus();
|
||||
</script>
|
||||
|
30
app/views/devise/registrations/edit.html.erb
Normal file
30
app/views/devise/registrations/edit.html.erb
Normal file
@ -0,0 +1,30 @@
|
||||
<h2>Edit <%= resource_name.to_s.humanize %></h2>
|
||||
|
||||
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
|
||||
<%= devise_error_messages! %>
|
||||
|
||||
<div><%= f.label :login %><br>
|
||||
<%= f.text_field :login, :autofocus => true %></div>
|
||||
|
||||
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
|
||||
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
|
||||
<% end %>
|
||||
|
||||
<div><%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br>
|
||||
<%= f.password_field :password, :autocomplete => "off" %></div>
|
||||
|
||||
<div><%= f.label :password_confirmation %><br>
|
||||
<%= f.password_field :password_confirmation %></div>
|
||||
|
||||
<div><%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br>
|
||||
<%= f.password_field :current_password %></div>
|
||||
|
||||
<div><%= f.submit "Update" %></div>
|
||||
<% end %>
|
||||
|
||||
<h3>Cancel my account</h3>
|
||||
|
||||
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete %></p>
|
||||
|
||||
<%= link_to "Back", :back %>
|
||||
|
51
app/views/devise/registrations/new.html.erb
Normal file
51
app/views/devise/registrations/new.html.erb
Normal file
@ -0,0 +1,51 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-8 col-xs-12">
|
||||
|
||||
<h4 class="breadcrumb"><%= t(:signup_header) %></h4>
|
||||
|
||||
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => {:class => 'form-horizontal'}) do |f| %>
|
||||
<%= devise_error_messages! %>
|
||||
<input name='userlogin' class='userlogin' />
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-3 control-label"><%= t(:userid) %></label>
|
||||
<div class="col-sm-8 col-xs-6"><%= f.text_field :login, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-3 control-label"><%= t(:email) %></label>
|
||||
<div class="col-sm-8 col-xs-6"><%= f.text_field :email, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-3 control-label"><%= t(:time_zone) %></label>
|
||||
<div class="col-sm-8 col-xs-8">
|
||||
<p class="form-control-static">
|
||||
<%= time_zone_select 'user', 'time_zone', nil, {:default => 'Eastern Time (US & Canada)'}, {:class => 'form-control'} %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-3 control-label"><%= t(:password) %></label>
|
||||
<div class="col-sm-8 col-xs-6"><%= f.password_field :password, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-3 control-label"><%= t(:password_confirmation) %></label>
|
||||
<div class="col-sm-8 col-xs-6"><%= f.password_field :password_confirmation, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label"></label>
|
||||
<div class="col-sm-8"><p class="form-control-static"><%= f.submit t(:create_account), :id => "user_submit", :class => 'btn btn-primary' %></p></div>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById('user_login').focus();
|
||||
</script>
|
||||
|
57
app/views/devise/sessions/new.html.erb
Normal file
57
app/views/devise/sessions/new.html.erb
Normal file
@ -0,0 +1,57 @@
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-xs-12">
|
||||
|
||||
<h4 class="breadcrumb">
|
||||
<% if @failed %>
|
||||
<%= t(:signin_failure) %>
|
||||
<% else %>
|
||||
<%= t(:signin_please) %>
|
||||
<% end %>
|
||||
</h4>
|
||||
|
||||
<% if @failed %><%= t(:signin_try_again) %><br><br><% end %>
|
||||
<% if @mail_message.present? %><%= @mail_message %><br><br><% end %>
|
||||
|
||||
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :id => 'loginform', :class => 'form-horizontal' }) do |f| %>
|
||||
<input name='userlogin' class='userlogin' />
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-3 control-label"><%= t(:userid) %></label>
|
||||
<div class="col-sm-8 col-xs-9"><%= f.text_field :login, :value => cookies['user_id'], :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-3 control-label"><%= t(:password) %></label>
|
||||
<div class="col-sm-8 col-xs-9">
|
||||
<%= f.password_field :password, :class => 'form-control' %>
|
||||
<%= link_to t(:forgot), new_user_password_path, :id => 'forgot_password' %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-4 col-sm-8 col-xs-offset-3 col-xs-9">
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<%= f.check_box :remember_me, :checked => true %>
|
||||
<%= t(:remember_me) %>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-4 col-sm-8 col-xs-offset-3 col-xs-9"><p class="form-control-static"><%= f.submit t(:signin), :class => 'btn btn-primary' %></p></div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var login = document.getElementById('user_login');
|
||||
if (login.value.length == 0)
|
||||
login.focus();
|
||||
else
|
||||
document.getElementById('user_password').focus();
|
||||
</script>
|
||||
|
12
app/views/devise/shared/_links.erb
Normal file
12
app/views/devise/shared/_links.erb
Normal file
@ -0,0 +1,12 @@
|
||||
<%- if controller_name != 'sessions' %>
|
||||
<%= link_to t(:signin), new_session_path(resource_name) %><br>
|
||||
<% end -%>
|
||||
|
||||
<%- if devise_mapping.registerable? && controller_name != 'registrations' %>
|
||||
<%= link_to t(:signup), new_registration_path(resource_name) %><br>
|
||||
<% end -%>
|
||||
|
||||
<%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %>
|
||||
<%= link_to t(:password_forgot), new_password_path(resource_name) %><br>
|
||||
<% end -%>
|
||||
|
12
app/views/devise/unlocks/new.html.erb
Normal file
12
app/views/devise/unlocks/new.html.erb
Normal file
@ -0,0 +1,12 @@
|
||||
<h2>Resend unlock instructions</h2>
|
||||
|
||||
<%= form_for(resource, :as => resource_name, :url => unlock_path(resource_name), :html => { :method => :post }) do |f| %>
|
||||
<%= devise_error_messages! %>
|
||||
|
||||
<div><%= f.label :email %><br>
|
||||
<%= f.email_field :email, :autofocus => true %></div>
|
||||
|
||||
<div><%= f.submit "Resend unlock instructions" %></div>
|
||||
<% end %>
|
||||
|
||||
<%= render "devise/shared/links" %>
|
43
app/views/docs/_sidebar.html.erb
Normal file
43
app/views/docs/_sidebar.html.erb
Normal file
@ -0,0 +1,43 @@
|
||||
<ul class="nav nav-stacked" id="bootstrap-sidebar">
|
||||
|
||||
<li class="<%= 'active' if params[:action] == 'index' %>"><a href="/docs">Getting Started</a></li>
|
||||
|
||||
<% if params[:action] == 'channels' %>
|
||||
<li><a href="#channels">Channels</a></li>
|
||||
<li class="subitem"><a href="#keywords">Keywords</a></li>
|
||||
<li class="subitem"><a href="#urls">Base URLs</a></li>
|
||||
<li class="subitem"><a href="#api_keys">API Keys</a></li>
|
||||
<li class="subitem"><a href="#rate_limits">Rate Limits / Caching</a></li>
|
||||
<li class="subitem"><a href="#update">Update Channel Feed</a></li>
|
||||
<li class="subitem"><a href="#get_feed">Get Channel Feed</a></li>
|
||||
<li class="subitem"><a href="#get_field">Get Channel Field Feed</a></li>
|
||||
<li class="subitem"><a href="#get_status">Get Status Updates</a></li>
|
||||
<li class="subitem"><a href="#list_public">List Public Channels</a></li>
|
||||
<li class="subitem"><a href="#create">Create a Channel</a></li>
|
||||
<li class="subitem"><a href="#clear">Clear a Channel</a></li>
|
||||
<li class="subitem"><a href="#delete">Delete a Channel</a></li>
|
||||
<li class="subitem"><a href="#importer">Importer</a></li>
|
||||
<% else %>
|
||||
<li><a href="/docs/channels">Channels</a></li>
|
||||
<% end %>
|
||||
|
||||
<% if params[:action] == 'charts' %>
|
||||
<li><a href="#charts">Charts</a></li>
|
||||
<li class="subitem"><a href="#create">Create Charts</a></li>
|
||||
<li class="subitem"><a href="#embed">Embed Charts</a></li>
|
||||
<% else %>
|
||||
<li><a href="/docs/charts">Charts</a></li>
|
||||
<% end %>
|
||||
|
||||
<% if params[:action] == 'users' %>
|
||||
<li><a href="#users">Users</a></li>
|
||||
<li class="subitem"><a href="#get_user">Get User Information</a></li>
|
||||
<li class="subitem"><a href="#list_user_channels">List User's Channels</a></li>
|
||||
<% else %>
|
||||
<li><a href="/docs/users">Users</a></li>
|
||||
<% end %>
|
||||
|
||||
<li class="<%= 'active' if params[:action] == 'plugins' %>"><a href="/docs/plugins">Plugins</a></li>
|
||||
<li class="<%= 'active' if params[:action] == 'errors' %>"><a href="/docs/errors">Error Codes</a></li>
|
||||
</ul>
|
||||
|
136
app/views/docs/channels.html.erb
Normal file
136
app/views/docs/channels.html.erb
Normal file
@ -0,0 +1,136 @@
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-offset-1 col-sm-3 col-xs-12" id="leftcol">
|
||||
<%= render 'docs/sidebar' %>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7 col-xs-12">
|
||||
|
||||
<h1 id="channels">Channels</h1>
|
||||
Channels are where your application stores and retrieves any type of data. Each channel has a Private View and a Public View. The Private View is only accessible by signing into your ThingSpeak.com user account. The Public View is what other viewers will see when they visit your ThingSpeak Channel. You can have different info on each view, customize the view with Plugins, and even disable the Public View. <br><br>
|
||||
<br><br>
|
||||
<%= image_tag 'channel_public_view.png', :size => '600x533' %>
|
||||
<br><br><br>
|
||||
Channel Views have the following features:
|
||||
<br><br>
|
||||
<ul>
|
||||
<li>Channel Watch</li>
|
||||
<li>Share via Social Networks</li>
|
||||
<li>Developer Info</li>
|
||||
<li>Ability to embed ThingSpeak Plugins</li>
|
||||
<li>Drag-and-drop Organization</li>
|
||||
<li>Tags</li>
|
||||
<li>Comments</li>
|
||||
</ul>
|
||||
<br><br>
|
||||
<h4>Channels API</h4>
|
||||
To read and write to a ThingSpeak Channel, your application must make requests to the ThingSpeak API using HTTP requests. Each ThingSpeak Channel allows for 8 fields of data (both numeric and alphanumeric formats), location information, and a status update. Each entry is stored with a date and time stamp and is assigned a unique Entry ID (entry_id). After the data is stored, you can retrieve the data by time selection or by Entry ID. In addition to storing and retrieving numeric and alphanumeric data, the ThingSpeak API allows for numeric data processing such as timescaling, averaging, median, summing, and rounding. The channel feeds supports JSON, XML, and CSV formats for integration into applications.
|
||||
<br><br>
|
||||
|
||||
<hr />
|
||||
<h2 id="keywords">Keywords</h2>
|
||||
Here are some keywords that are used in the API. An understanding of the terms will make the API documentation easier to understand.
|
||||
<br><br>
|
||||
<ul>
|
||||
<li><strong>Channel </strong>- The name for where data can be inserted or retrieved within the ThingSpeak API, identified by a numerical Channel ID</li>
|
||||
<li><strong>Channel ID </strong>- Every channel has a unique Channel ID. The Channel ID number is used to identify the channel when your application reads data from the channel</li>
|
||||
<li><strong>Field </strong>- One of eight specific locations for data inside of a channel, identified by a number between 1 to 8 – A field can store numeric data from sensors or alphanumeric strings from serial devices or RFID readers</li>
|
||||
<li><strong>Status </strong>- A short status message to augment the data stored in a channel</li>
|
||||
<li><strong>Location </strong>- The latitude, longitude, and elevation of where data is being sent from</li>
|
||||
<li><strong>Feed </strong>- The collective name for the data stored inside a channel, which may be any combination of field data, status updates, and location info</li>
|
||||
<li><strong>Write API Key</strong> – A 16 digit code that allows an application to write data to a channel</li>
|
||||
<li><strong>Read API Key</strong> – A 16 digit code that allows an application to read the data stored in a channel</li>
|
||||
</ul>
|
||||
<br><br>
|
||||
|
||||
<hr />
|
||||
<h2 id="urls">Base URL Addresses and Locations</h2>
|
||||
Regular URL:
|
||||
<br><br>
|
||||
<pre>http://api.thingspeak.com</pre>
|
||||
<br>
|
||||
Secure URL:
|
||||
<br><br>
|
||||
<pre>https://api.thingspeak.com</pre>
|
||||
<br>
|
||||
IP Address:
|
||||
<br><br>
|
||||
<pre>http://184.106.153.149</pre>
|
||||
<br>
|
||||
Cross-domain XML:
|
||||
<br><br>
|
||||
<pre>http://api.thingspeak.com/crossdomain.xml</pre>
|
||||
<br><br>
|
||||
|
||||
<hr />
|
||||
<h2 id="api_keys">API Keys</h2>
|
||||
<br><br>
|
||||
<h4>Private / Public Channels</h4>
|
||||
By default, your channel is private and requires a Read API Key to access its feed. You can make a channel public which gives other users the ability to use your feed without a Read API Key.
|
||||
<br><br><br>
|
||||
<h4>Write API Key</h4>
|
||||
In order to update a channel, you need to know your Write API Key. If your Write API Key gets compromised you can generate a new key.
|
||||
<br><br>
|
||||
Follow these steps to get your Write API Key:
|
||||
<br><br>
|
||||
<ul>
|
||||
<li>Select Channels</li>
|
||||
<li>Select the Channel to update</li>
|
||||
<li>Select Manage API Keys</li>
|
||||
</ul>
|
||||
<br><br>
|
||||
<h4>Read API Key</h4>
|
||||
The Read API Key allows your application to read data from the API. You can generate multiple Read API Keys for different applications.
|
||||
<br><br>
|
||||
Follow these steps to get a Read API Key:
|
||||
<br><br>
|
||||
<ul>
|
||||
<li>Select Channels</li>
|
||||
<li>Select the Channel to update</li>
|
||||
<li>Select Manage API Keys</li>
|
||||
<li>Select Generate New Read API Key</li>
|
||||
</ul>
|
||||
<br><br><br>
|
||||
|
||||
<hr />
|
||||
<h2 id="rate_limits">Rate Limits</h2>
|
||||
The open service via ThingSpeak.com has a rate limit of an update per channel every 15 seconds. This limit is so that the service can remain free and give everyone a high-level of service. The API source will also be made available on <a href="https://github.com/iobridge/ThingSpeak">GitHub</a> so that you can run this locally or via a shared web host provider. At that point you will be able to to tweak settings for your application requirements.
|
||||
<br><br>
|
||||
|
||||
<h4>Caching</h4>
|
||||
Caching is implemented on JSON and XML formats on feeds. Feeds that return more than 100 entries are cached for 5 minutes. This will allow great performance for popular applications. The Last API call and feeds that specify "results=100" or less are not cached, so that you can produce real-time applications.
|
||||
<br><br><br><br><br><br><br><br><br><br><br><br><br><br>
|
||||
|
||||
<hr />
|
||||
<%= render 'docs/channels/update' %>
|
||||
|
||||
<hr />
|
||||
<%= render 'docs/channels/feed' %>
|
||||
|
||||
<hr />
|
||||
<%= render 'docs/channels/field' %>
|
||||
|
||||
<hr />
|
||||
<%= render 'docs/channels/status' %>
|
||||
|
||||
<hr />
|
||||
<%= render 'docs/channels/public_index' %>
|
||||
|
||||
<hr />
|
||||
<%= render 'docs/channels/create' %>
|
||||
|
||||
<hr />
|
||||
<%= render 'docs/channels/clear' %>
|
||||
|
||||
<hr />
|
||||
<%= render 'docs/channels/destroy' %>
|
||||
|
||||
<br><br>
|
||||
<hr />
|
||||
<%= render 'docs/channels/importer' %>
|
||||
|
||||
<br><br><br><br><br><br><br><br><br><br><br><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
47
app/views/docs/channels/_clear.html.erb
Normal file
47
app/views/docs/channels/_clear.html.erb
Normal file
@ -0,0 +1,47 @@
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="clear">Clear a Channel</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To clear all feed data from a Channel, send an HTTP DELETE to <code><%= @ssl_api_domain %>channels/<span class="customcode">CHANNEL_ID</span>/feeds<span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> .
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>api_key</b> (string) - User's API Key; please note that this is different than a Channel API key, and can be found in <a href="/account">your account details</a>. (required).</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example DELETE:
|
||||
|
||||
<pre>
|
||||
DELETE <span class="str"><%= @ssl_api_domain %>channels/<span class="customcode">4</span>/feeds<span class="format format-json">.json</span><span class="format format-xml">.xml</span></span>
|
||||
api_key=<span class="customcode"><%= @user_api_key %></span>
|
||||
</pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block format-text">
|
||||
The response will be a webpage with your Channel.
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-json">
|
||||
The response will be an empty JSON array, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
[]
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="format format-block format-xml">
|
||||
The response will be an empty XML array, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<nil-classes type="array" />
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
101
app/views/docs/channels/_create.html.erb
Normal file
101
app/views/docs/channels/_create.html.erb
Normal file
@ -0,0 +1,101 @@
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="create">Create a Channel</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To create a new Channel, send an HTTP POST to <code><%= @ssl_api_domain %>channels<span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> .
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>api_key</b> (string) - User's API Key; please note that this is different than a Channel API key, and can be found in <a href="/account">your account details</a>. (required).</li>
|
||||
<li><b>description</b> (string) - Description of the Channel (optional)</li>
|
||||
<li><b>elevation</b> (integer) - Elevation in meters (optional)</li>
|
||||
<li><b>field1</b> (string) - Field1 name (optional)</li>
|
||||
<li><b>field2</b> (string) - Field2 name (optional)</li>
|
||||
<li><b>field3</b> (string) - Field3 name (optional)</li>
|
||||
<li><b>field4</b> (string) - Field4 name (optional)</li>
|
||||
<li><b>field5</b> (string) - Field5 name (optional)</li>
|
||||
<li><b>field6</b> (string) - Field6 name (optional)</li>
|
||||
<li><b>field7</b> (string) - Field7 name (optional)</li>
|
||||
<li><b>field8</b> (string) - Field8 name (optional)</li>
|
||||
<li><b>latitude</b> (decimal) - Latitude in degrees (optional)</li>
|
||||
<li><b>longitude</b> (decimal) - Longitude in degrees (optional)</li>
|
||||
<li><b>name</b> (string) - Name of the Channel (optional)</li>
|
||||
<li><b>public_flag</b> (true/false) - Whether the Channel should be public, default false (optional)</li>
|
||||
<li><b>tags</b> (string) - Comma-separated list of tags (optional)</li>
|
||||
<li><b>url</b> (string) - Webpage URL for the Channel (optional)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example POST:
|
||||
|
||||
<pre>
|
||||
POST <span class="str"><%= @ssl_api_domain %>channels<span class="format format-json">.json</span><span class="format format-xml">.xml</span></span>
|
||||
api_key=<span class="customcode"><%= @user_api_key %></span>
|
||||
name=<span class="customcode">My New Channel</span>
|
||||
</pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block-xl format-text">
|
||||
The response will be a webpage with your newly created Channel.
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xl format-json">
|
||||
The response will be a JSON object of the new channel, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"id": 4,
|
||||
"name": "My New Channel",
|
||||
"description": null,
|
||||
"latitude": null,
|
||||
"longitude": null,
|
||||
"created_at": "2014-03-25T13:12:50-04:00",
|
||||
"elevation": null,
|
||||
"last_entry_id": null,
|
||||
"ranking": 15,
|
||||
"username": "hans",
|
||||
"tags": [],
|
||||
"api_keys":
|
||||
[
|
||||
{
|
||||
"api_key": "XXXXXXXXXXXXXXXX",
|
||||
"write_flag": true
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xl format-xml">
|
||||
The response will be an XML object of the new channel, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<channel>
|
||||
<id type="integer">4</id>
|
||||
<name>My New Channel</name>
|
||||
<description nil="true" />
|
||||
<latitude type="decimal" nil="true" />
|
||||
<longitude type="decimal" nil="true" />
|
||||
<created-at type="dateTime">2014-03-25T20:17:44-04:00</created-at>
|
||||
<elevation nil="true" />
|
||||
<last-entry-id type="integer" nil="true" />
|
||||
<ranking type="integer">15</ranking>
|
||||
<username>hans</username>
|
||||
<tags type="array" />
|
||||
<api-keys type="array">
|
||||
<api-key>
|
||||
<api-key>XXXXXXXXXXXXXXXX</api-key>
|
||||
<write-flag type="boolean">true</write-flag>
|
||||
</api-key>
|
||||
</api-keys>
|
||||
</channel>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
72
app/views/docs/channels/_destroy.html.erb
Normal file
72
app/views/docs/channels/_destroy.html.erb
Normal file
@ -0,0 +1,72 @@
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="delete">Delete a Channel</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To create a new Channel, send an HTTP DELETE to <code><%= @ssl_api_domain %>channels/<span class="customcode">CHANNEL_ID</span><span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> ,
|
||||
replacing <span class="customcode">CHANNEL_ID</span> with the ID of your Channel.
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>api_key</b> (string) - User's API Key; please note that this is different than a Channel API key, and can be found in <a href="/account">your account details</a>. (required).</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example DELETE:
|
||||
|
||||
<pre>
|
||||
DELETE <span class="str"><%= @ssl_api_domain %>channels/<span class="customcode">4</span><span class="format format-json">.json</span><span class="format format-xml">.xml</span></span>
|
||||
api_key=<span class="customcode"><%= @user_api_key %></span>
|
||||
</pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block-xl format-text">
|
||||
The response will be a webpage with a list of Channels.
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xl format-json">
|
||||
The response will be a JSON object of the Channel before it was deleted, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"id": 4,
|
||||
"name": "My New Channel",
|
||||
"description": null,
|
||||
"latitude": null,
|
||||
"longitude": null,
|
||||
"created_at": "2014-03-25T13:12:50-04:00",
|
||||
"elevation": null,
|
||||
"last_entry_id": null,
|
||||
"ranking": 15,
|
||||
"username": "hans",
|
||||
"tags": []
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xl format-xml">
|
||||
The response will be an XML object of the Channel before it was deleted, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<channel>
|
||||
<id type="integer">4</id>
|
||||
<name>My New Channel</name>
|
||||
<description nil="true" />
|
||||
<latitude type="decimal" nil="true" />
|
||||
<longitude type="decimal" nil="true" />
|
||||
<created-at type="dateTime">2014-03-25T20:17:44-04:00</created-at>
|
||||
<elevation nil="true" />
|
||||
<last-entry-id type="integer" nil="true" />
|
||||
<ranking type="integer">15</ranking>
|
||||
<username>hans</username>
|
||||
<tags type="array" />
|
||||
</channel>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
305
app/views/docs/channels/_feed.html.erb
Normal file
305
app/views/docs/channels/_feed.html.erb
Normal file
@ -0,0 +1,305 @@
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="get_feed">Get a Channel Feed</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To view a Channel feed, send an HTTP GET to <code><%= @ssl_api_domain %>channels/<span class="customcode">CHANNEL_ID</span>/feeds<span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> ,
|
||||
replacing <span class="customcode">CHANNEL_ID</span> with the ID of your Channel.
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>key</b> (string) Read API Key for this specific Channel (optional--no key required for public channels)</li>
|
||||
<li><b>results</b> (integer) Number of entries to retrieve, 8000 max, default of 100 (optional)</li>
|
||||
<li><b>days</b> (integer) Days from now to include in feed (optional)</li>
|
||||
<li><b>start</b> (datetime) Start date in format YYYY-MM-DD%20HH:NN:SS (optional)</li>
|
||||
<li><b>end</b> (datetime) End date in format YYYY-MM-DD%20HH:NN:SS (optional)</li>
|
||||
<li><b>offset</b> (integer) Offset of your timezone without daylight savings time (optional)</li>
|
||||
<li><b>status</b> (true/false) Include status updates in feed by setting "status=true" (optional)</li>
|
||||
<li><b>location</b> (true/false) Include latitude, longitude, and elevation in feed by setting "location=true" (optional)</li>
|
||||
<li><b>min</b> (decimal) Minimum value to include in response (optional)</li>
|
||||
<li><b>max</b> (decimal) Maximum value to include in response (optional)</li>
|
||||
<li><b>round</b> (integer) Round to this many decimal places (optional)</li>
|
||||
<li><b>timescale</b> (integer or string) Get first value in this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>sum</b> (integer or string) Get sum of this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>average</b> (integer or string) Get average of this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>median</b> (integer or string) Get median of this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>callback</b> (string) Function name to be used for JSONP cross-domain requests (optional)</li>
|
||||
</ul>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
Please note that the results parameter is not compatible with timescale, sum, average, or median.
|
||||
</div>
|
||||
|
||||
<br>
|
||||
Example GET:
|
||||
|
||||
<pre>GET <span class="str"><%= @ssl_api_domain %>channels/<span class="customcode">9</span>/feeds<span class="format format-json">.json</span><span class="format format-xml">.xml</span>?results=<span class="customcode">2</span></span></pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block-xxl format-text">
|
||||
The response will be an HTML page with the JSON Channel feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"channel":
|
||||
{
|
||||
"id": 9,
|
||||
"name": "my_house",
|
||||
"description": "Netduino Plus connected to sensors around the house",
|
||||
"latitude": "40.44",
|
||||
"longitude": "-79.996",
|
||||
"field1": "Light",
|
||||
"field2": "Outside Temperature",
|
||||
"created_at": "2010-12-13T20:20:06-05:00",
|
||||
"updated_at": "2014-02-26T12:43:04-05:00",
|
||||
"last_entry_id": 6060625
|
||||
},
|
||||
"feeds":
|
||||
[
|
||||
{
|
||||
"created_at": "2014-02-26T12:42:49-05:00",
|
||||
"entry_id": 6060624,
|
||||
"field1": "188",
|
||||
"field2": "25.902335456475583"
|
||||
},
|
||||
{
|
||||
"created_at": "2014-02-26T12:43:04-05:00",
|
||||
"entry_id": 6060625,
|
||||
"field1": "164",
|
||||
"field2": "25.222929936305732"
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xxl format-json">
|
||||
The response will be a JSON object of the Channel feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"channel":
|
||||
{
|
||||
"id": 9,
|
||||
"name": "my_house",
|
||||
"description": "Netduino Plus connected to sensors around the house",
|
||||
"latitude": "40.44",
|
||||
"longitude": "-79.996",
|
||||
"field1": "Light",
|
||||
"field2": "Outside Temperature",
|
||||
"created_at": "2010-12-13T20:20:06-05:00",
|
||||
"updated_at": "2014-02-26T12:43:04-05:00",
|
||||
"last_entry_id": 6060625
|
||||
},
|
||||
"feeds":
|
||||
[
|
||||
{
|
||||
"created_at": "2014-02-26T12:42:49-05:00",
|
||||
"entry_id": 6060624,
|
||||
"field1": "188",
|
||||
"field2": "25.902335456475583"
|
||||
},
|
||||
{
|
||||
"created_at": "2014-02-26T12:43:04-05:00",
|
||||
"entry_id": 6060625,
|
||||
"field1": "164",
|
||||
"field2": "25.222929936305732"
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xxl format-xml">
|
||||
The response will be an XML object of the Channel feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<channel>
|
||||
<id type="integer">9</id>
|
||||
<name>my_house</name>
|
||||
<description>Netduino Plus connected to sensors around the house</description>
|
||||
<latitude type="decimal">40.44</latitude>
|
||||
<longitude type="decimal">-79.996</longitude>
|
||||
<field1>Light</field1>
|
||||
<field2>Outside Temperature</field2>
|
||||
<created-at type="dateTime">2010-12-13T20:20:06-05:00</created-at>
|
||||
<updated-at type="dateTime">2014-02-26T12:49:19-05:00</updated-at>
|
||||
<last-entry-id type="integer">6060650</last-entry-id>
|
||||
<feeds type="array">
|
||||
<feed>
|
||||
<created-at type="dateTime">2014-02-26T12:49:04-05:00</created-at>
|
||||
<entry-id type="integer">6060649</entry-id>
|
||||
<field1>160</field1>
|
||||
<field2>25.307855626326962</field2>
|
||||
<id type="integer" nil="true"/>
|
||||
</feed>
|
||||
<feed>
|
||||
<created-at type="dateTime">2014-02-26T12:49:19-05:00</created-at>
|
||||
<entry-id type="integer">6060650</entry-id>
|
||||
<field1>171</field1>
|
||||
<field2>22.929936305732483</field2>
|
||||
<id type="integer" nil="true"/>
|
||||
</feed>
|
||||
</feeds>
|
||||
</channel>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
Live examples:
|
||||
<ul>
|
||||
<li><a href="http://api.thingspeak.com/channels/9/feeds.json">http://api.thingspeak.com/channels/9/feeds.json</a></li>
|
||||
<li><a href="http://api.thingspeak.com/channels/9/feeds.json?median=10">http://api.thingspeak.com/channels/9/feeds.json?median=10</a></li>
|
||||
<li><a href="http://api.thingspeak.com/channels/9/feeds.json?start=2011-11-11%2010:10:10&end=2011-11-11%2011:11:11">http://api.thingspeak.com/channels/9/feeds.json?start=2011-11-11%2010:10:10&end=2011-11-11%2011:11:11</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<br><br>
|
||||
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2>Get Last Entry in a Channel Feed</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To get the last entry in a Channel feed, send an HTTP GET to <code><%= @ssl_api_domain %>channels/<span class="customcode">CHANNEL_ID</span>/feeds/last<span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> ,
|
||||
replacing <span class="customcode">CHANNEL_ID</span> with the ID of your Channel.
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>key</b> (string) Read API Key for this specific Channel (optional--no key required for public channels)</li>
|
||||
<li><b>offset</b> (integer) Offset of your timezone without daylight savings time (optional)</li>
|
||||
<li><b>status</b> (true/false) Include status updates in feed by setting "status=true" (optional)</li>
|
||||
<li><b>location</b> (true/false) Include latitude, longitude, and elevation in feed by setting "location=true" (optional)</li>
|
||||
<li><b>callback</b> (string) Function name to be used for JSONP cross-domain requests (optional)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example GET:
|
||||
|
||||
<pre>GET <span class="str"><%= @ssl_api_domain %>channels/<span class="customcode">9</span>/feeds/last<span class="format format-json">.json</span><span class="format format-xml">.xml</span></span></pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block format-text">
|
||||
The response will be a JSON object of the most recent feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"created_at": "2014-02-26T21:27:21Z",
|
||||
"entry_id": 6061519,
|
||||
"field1": "176",
|
||||
"field2": "28.195329087048833"
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-json">
|
||||
The response will be a JSON object of the most recent feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"created_at": "2014-02-26T21:27:21Z",
|
||||
"entry_id": 6061519,
|
||||
"field1": "176",
|
||||
"field2": "28.195329087048833"
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-xml">
|
||||
The response will be an XML object of the most recent feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feed>
|
||||
<created-at type="dateTime">2014-02-26T21:28:51Z</created-at>
|
||||
<entry-id type="integer">6061525</entry-id>
|
||||
<field1>200</field1>
|
||||
<field2>28.365180467091296</field2>
|
||||
<id type="integer" nil="true"/>
|
||||
</feed>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<br><br>
|
||||
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2>Get Specific Entry in a Channel</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To get a specific entry in a Channel's feed, send an HTTP GET to <code><%= @ssl_api_domain %>channels/<span class="customcode">CHANNEL_ID</span>/feeds/<span class="customcode">ENTRY_ID</span><span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> ,
|
||||
replacing <span class="customcode">CHANNEL_ID</span> with the ID of your Channel and <span class="customcode">ENTRY_ID</span> with the ID of your entry.
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>key</b> (string) Read API Key for this specific Channel (optional--no key required for public channels)</li>
|
||||
<li><b>offset</b> (integer) Offset of your timezone without daylight savings time (optional)</li>
|
||||
<li><b>status</b> (true/false) Include status updates in feed by setting "status=true" (optional)</li>
|
||||
<li><b>location</b> (true/false) Include latitude, longitude, and elevation in feed by setting "location=true" (optional)</li>
|
||||
<li><b>callback</b> (string) Function name to be used for JSONP cross-domain requests (optional)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example GET:
|
||||
|
||||
<pre>GET <span class="str"><%= @ssl_api_domain %>channels/<span class="customcode">9</span>/feeds/<span class="customcode">6061519</span><span class="format format-json">.json</span><span class="format format-xml">.xml</span></span></pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block format-text">
|
||||
The response will be a JSON object of the feed entry, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"created_at": "2014-02-26T21:27:21Z",
|
||||
"entry_id": 6061519,
|
||||
"field1": "176",
|
||||
"field2": "28.195329087048833"
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-json">
|
||||
The response will be a JSON object of the feed entry, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"created_at": "2014-02-26T21:27:21Z",
|
||||
"entry_id": 6061519,
|
||||
"field1": "176",
|
||||
"field2": "28.195329087048833"
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-xml">
|
||||
The response will be an XML object of the most recent feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feed>
|
||||
<created-at type="dateTime">2014-02-26T21:27:21Z</created-at>
|
||||
<entry-id type="integer">6061519</entry-id>
|
||||
<field1>176</field1>
|
||||
<field2>28.195329087048833</field2>
|
||||
<id type="integer" nil="true"/>
|
||||
</feed>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
213
app/views/docs/channels/_field.html.erb
Normal file
213
app/views/docs/channels/_field.html.erb
Normal file
@ -0,0 +1,213 @@
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="get_field">Get a Channel Field Feed</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To view a Channel's field feed, send an HTTP GET to <code><%= @ssl_api_domain %>channels/<span class="customcode">CHANNEL_ID</span>/fields/<span class="customcode">FIELD_ID</span><span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> ,
|
||||
replacing <span class="customcode">CHANNEL_ID</span> with the ID of your Channel and <span class="customcode">FIELD_ID</span> with the ID of your field.
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>key</b> (string) Read API Key for this specific Channel (optional--no key required for public channels)</li>
|
||||
<li><b>results</b> (integer) Number of entries to retrieve, 8000 max, default of 100 (optional)</li>
|
||||
<li><b>days</b> (integer) Days from now to include in feed (optional)</li>
|
||||
<li><b>start</b> (datetime) Start date in format YYYY-MM-DD%20HH:NN:SS (optional)</li>
|
||||
<li><b>end</b> (datetime) End date in format YYYY-MM-DD%20HH:NN:SS (optional)</li>
|
||||
<li><b>offset</b> (integer) Offset of your timezone without daylight savings time (optional)</li>
|
||||
<li><b>status</b> (true/false) Include status updates in feed by setting "status=true" (optional)</li>
|
||||
<li><b>location</b> (true/false) Include latitude, longitude, and elevation in feed by setting "location=true" (optional)</li>
|
||||
<li><b>min</b> (decimal) Minimum value to include in response (optional)</li>
|
||||
<li><b>max</b> (decimal) Maximum value to include in response (optional)</li>
|
||||
<li><b>round</b> (integer) Round to this many decimal places (optional)</li>
|
||||
<li><b>timescale</b> (integer or string) Get first value in this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>sum</b> (integer or string) Get sum of this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>average</b> (integer or string) Get average of this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>median</b> (integer or string) Get median of this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>callback</b> (string) Function name to be used for JSONP cross-domain requests (optional)</li>
|
||||
</ul>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
Please note that the results parameter is not compatible with timescale, sum, average, or median.
|
||||
</div>
|
||||
|
||||
<br>
|
||||
Example GET:
|
||||
|
||||
<pre>GET <span class="str"><%= @ssl_api_domain %>channels/<span class="customcode">9</span>/fields/<span class="customcode">1</span><span class="format format-json">.json</span><span class="format format-xml">.xml</span>?results=<span class="customcode">2</span></span></pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block-xxl format-text">
|
||||
The response will be an HTML page with the JSON Channel's field feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"channel":
|
||||
{
|
||||
"id": 9,
|
||||
"name": "my_house",
|
||||
"description": "Netduino Plus connected to sensors around the house",
|
||||
"latitude": "40.44",
|
||||
"longitude": "-79.996",
|
||||
"field1": "Light",
|
||||
"field2": "Outside Temperature",
|
||||
"created_at": "2010-12-13T20:20:06-05:00",
|
||||
"updated_at": "2014-02-26T12:43:04-05:00",
|
||||
"last_entry_id": 6060625
|
||||
},
|
||||
"feeds":
|
||||
[
|
||||
{
|
||||
"created_at": "2014-02-26T12:42:49-05:00",
|
||||
"entry_id": 6060624,
|
||||
"field1": "188"
|
||||
},
|
||||
{
|
||||
"created_at": "2014-02-26T12:43:04-05:00",
|
||||
"entry_id": 6060625,
|
||||
"field1": "164"
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xxl format-json">
|
||||
The response will be a JSON object of the Channel's field feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"channel":
|
||||
{
|
||||
"id": 9,
|
||||
"name": "my_house",
|
||||
"description": "Netduino Plus connected to sensors around the house",
|
||||
"latitude": "40.44",
|
||||
"longitude": "-79.996",
|
||||
"field1": "Light",
|
||||
"field2": "Outside Temperature",
|
||||
"created_at": "2010-12-13T20:20:06-05:00",
|
||||
"updated_at": "2014-02-26T12:43:04-05:00",
|
||||
"last_entry_id": 6060625
|
||||
},
|
||||
"feeds":
|
||||
[
|
||||
{
|
||||
"created_at": "2014-02-26T12:42:49-05:00",
|
||||
"entry_id": 6060624,
|
||||
"field1": "188"
|
||||
},
|
||||
{
|
||||
"created_at": "2014-02-26T12:43:04-05:00",
|
||||
"entry_id": 6060625,
|
||||
"field1": "164"
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xxl format-xml">
|
||||
The response will be an XML object of the Channel's field feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<channel>
|
||||
<id type="integer">9</id>
|
||||
<name>my_house</name>
|
||||
<description>Netduino Plus connected to sensors around the house</description>
|
||||
<latitude type="decimal">40.44</latitude>
|
||||
<longitude type="decimal">-79.996</longitude>
|
||||
<field1>Light</field1>
|
||||
<field2>Outside Temperature</field2>
|
||||
<created-at type="dateTime">2010-12-13T20:20:06-05:00</created-at>
|
||||
<updated-at type="dateTime">2014-02-26T12:49:19-05:00</updated-at>
|
||||
<last-entry-id type="integer">6060650</last-entry-id>
|
||||
<feeds type="array">
|
||||
<feed>
|
||||
<created-at type="dateTime">2014-02-26T12:49:04-05:00</created-at>
|
||||
<entry-id type="integer">6060649</entry-id>
|
||||
<field1>160</field1>
|
||||
<id type="integer" nil="true"/>
|
||||
</feed>
|
||||
<feed>
|
||||
<created-at type="dateTime">2014-02-26T12:49:19-05:00</created-at>
|
||||
<entry-id type="integer">6060650</entry-id>
|
||||
<field1>171</field1>
|
||||
<id type="integer" nil="true"/>
|
||||
</feed>
|
||||
</feeds>
|
||||
</channel>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<br><br>
|
||||
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2>Get Last Entry in a Field Feed</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To get the last entry in a Channel's field feed, send an HTTP GET to <code><%= @ssl_api_domain %>channels/<span class="customcode">CHANNEL_ID</span>/fields/<span class="customcode">FIELD_ID</span>/last<span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> ,
|
||||
replacing <span class="customcode">CHANNEL_ID</span> with the ID of your Channel and <span class="customcode">FIELD_ID</span> with the ID of your field.
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul class="format-block">
|
||||
<li><b>key</b> (string) Read API Key for this specific Channel (optional--no key required for public channels)</li>
|
||||
<li><b>offset</b> (integer) Offset of your timezone without daylight savings time (optional)</li>
|
||||
<li><b>status</b> (true/false) Include status updates in feed by setting "status=true" (optional)</li>
|
||||
<li><b>location</b> (true/false) Include latitude, longitude, and elevation in feed by setting "location=true" (optional)</li>
|
||||
<li><b>callback</b> (string) Function name to be used for JSONP cross-domain requests (optional)</li>
|
||||
<li class="format format-text"><b>prepend</b> (string) Text to add before the API response (optional)</li>
|
||||
<li class="format format-text"><b>append</b> (string) Text to add after the API response (optional)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example GET:
|
||||
|
||||
<pre>GET <span class="str"><%= @ssl_api_domain %>channels/<span class="customcode">9</span>/fields/<span class="customcode">1</span>/last<span class="format format-json">.json</span><span class="format format-xml">.xml</span></span></pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block format-text">
|
||||
The response will be the Channel field's most recent value, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
176
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-json">
|
||||
The response will be a JSON object of the most recent feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"created_at": "2014-02-26T21:27:21Z",
|
||||
"entry_id": 6061519,
|
||||
"field1": "176"
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-xml">
|
||||
The response will be an XML object of the most recent feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feed>
|
||||
<created-at type="dateTime">2014-02-26T21:28:51Z</created-at>
|
||||
<entry-id type="integer">6061525</entry-id>
|
||||
<field1>200</field1>
|
||||
<id type="integer" nil="true"/>
|
||||
</feed>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
8
app/views/docs/channels/_importer.html.erb
Normal file
8
app/views/docs/channels/_importer.html.erb
Normal file
@ -0,0 +1,8 @@
|
||||
<h2 id="importer">Importer</h2>
|
||||
|
||||
<p>Using the ThingSpeak Importer, you are able to import data from a CSV file directly into a ThingSpeak Channel. The access the Importer, select a <em>Channel</em>, and click <em>Import Data</em>.</p>
|
||||
<p>The format for the CSV should be the following:</p>
|
||||
<pre>datetime,field1,field2,field3,field4,field5,field6,field7,field8,latitude,longitude,elevation,status</pre>
|
||||
<p>Here is an example CSV file: <a title="ThingSpeak Import Sample Data" href="/files/importer_sample.csv" target="_blank">Sample CSV File</a></p>
|
||||
You only have to send a datetime stamp and at least one field. The datetime stamp can be in many formats such as epoch, ISO 8601, or MySQL time. If the datetime includes a GMT/UTC offset, we will use that to properly import the data. If your datetime stamps do not have a GMT / UTC offset, you can specify a time zone that the data was logged in.
|
||||
|
149
app/views/docs/channels/_public_index.html.erb
Normal file
149
app/views/docs/channels/_public_index.html.erb
Normal file
@ -0,0 +1,149 @@
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="list_public">List Public Channels</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To view a list of public Channels, send an HTTP GET to
|
||||
<br>
|
||||
<code><%= @ssl_api_domain %>channels/public<span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> .
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>page</b> (integer) Page number to retrieve (optional)</li>
|
||||
<li><b>tag</b> (string) Name of tag to search for (optional)</li>
|
||||
<li><b>username</b> (string) Person's username that you want to search Channels for (optional)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example GET:
|
||||
|
||||
<pre>GET <span class="str"><%= @ssl_api_domain %>channels/public<span class="format format-json">.json</span><span class="format format-xml">.xml</span></span></pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block-xxxl format-text">
|
||||
The response will be a webpage with a <a href="/channels/public">list of public Channels</a>.
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xxxl format-json">
|
||||
The response will be a JSON object of public Channels, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"pagination":
|
||||
{
|
||||
"current_page": 1,
|
||||
"per_page": 15,
|
||||
"total_entries": 653
|
||||
},
|
||||
"channels":
|
||||
[
|
||||
{
|
||||
"id": 9,
|
||||
"name": "my_house",
|
||||
"description": "Netduino Plus connected to sensors around the house",
|
||||
"latitude": "40.44",
|
||||
"longitude": "-79.996",
|
||||
"created_at": "2010-12-13T20:20:06-05:00",
|
||||
"elevation": "",
|
||||
"last_entry_id": 6062691,
|
||||
"ranking" :100,
|
||||
"username":"hans",
|
||||
"tags":
|
||||
[
|
||||
{
|
||||
"id": 9,
|
||||
"name": "temp"
|
||||
},{
|
||||
"id": 25,
|
||||
"name": "light"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5683,
|
||||
"name": "Residential Data Points",
|
||||
"description": "Arduino Uno + Ethernet Shield",
|
||||
"latitude": "35.664548",
|
||||
"longitude": "-78.654972",
|
||||
"created_at": "2013-05-15T12:33:57-04:00",
|
||||
"elevation": "100",
|
||||
"last_entry_id": 731713,
|
||||
"ranking": 100,
|
||||
"username": "samlro",
|
||||
"tags":
|
||||
[
|
||||
{
|
||||
"id": 950,
|
||||
"name": "Analog Inputs"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xxxl format-xml">
|
||||
The response will be an XML object of public Channels, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<response>
|
||||
<pagination>
|
||||
<current-page type="WillPaginate::PageNumber">1</current-page>
|
||||
<per-page type="integer">15</per-page>
|
||||
<total-entries type="integer">654</total-entries>
|
||||
</pagination>
|
||||
<channels type="array">
|
||||
<channel>
|
||||
<id type="integer">9</id>
|
||||
<name>my_house</name>
|
||||
<description>
|
||||
Netduino Plus connected to sensors around the house
|
||||
</description>
|
||||
<latitude type="decimal">40.44</latitude>
|
||||
<longitude type="decimal">-79.996</longitude>
|
||||
<created-at type="dateTime">2010-12-13T20:20:06-05:00</created-at>
|
||||
<elevation/>
|
||||
<last-entry-id type="integer">6062720</last-entry-id>
|
||||
<ranking type="integer">100</ranking>
|
||||
<username>hans</username>
|
||||
<tags type="array">
|
||||
<tag>
|
||||
<id type="integer">9</id>
|
||||
<name>temp</name>
|
||||
</tag>
|
||||
<tag>
|
||||
<id type="integer">25</id>
|
||||
<name>light</name>
|
||||
</tag>
|
||||
</tags>
|
||||
</channel>
|
||||
<channel>
|
||||
<id type="integer">5683</id>
|
||||
<name>Residential Data Points</name>
|
||||
<description>Arduino Uno + Ethernet Shield</description>
|
||||
<latitude type="decimal">35.664548</latitude>
|
||||
<longitude type="decimal">-78.654972</longitude>
|
||||
<created-at type="dateTime">2013-05-15T12:33:57-04:00</created-at>
|
||||
<elevation>100</elevation>
|
||||
<last-entry-id type="integer">731720</last-entry-id>
|
||||
<ranking type="integer">100</ranking>
|
||||
<username>samlro</username>
|
||||
<tags type="array">
|
||||
<tag>
|
||||
<id type="integer">950</id>
|
||||
<name>Analog Inputs</name>
|
||||
</tag>
|
||||
</tags>
|
||||
</channel>
|
||||
</channels>
|
||||
</response>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
109
app/views/docs/channels/_status.html.erb
Normal file
109
app/views/docs/channels/_status.html.erb
Normal file
@ -0,0 +1,109 @@
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="get_status">Get Status Updates</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To view a Channel's status updates, send an HTTP GET to <code><%= @ssl_api_domain %>channels/<span class="customcode">CHANNEL_ID</span>/status<span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> ,
|
||||
replacing <span class="customcode">CHANNEL_ID</span> with the ID of your Channel.
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>key</b> (string) Read API Key for this specific Channel (optional--no key required for public channels)</li>
|
||||
<li><b>offset</b> (integer) Offset of your timezone without daylight savings time (optional)</li>
|
||||
<li><b>callback</b> (string) Function name to be used for JSONP cross-domain requests (optional)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example GET:
|
||||
|
||||
<pre>GET <span class="str"><%= @ssl_api_domain %>channels/<span class="customcode">1417</span>/status<span class="format format-json">.json</span><span class="format format-xml">.xml</span></span></pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block-xxl format-text">
|
||||
The response will be a JSON object of Channel statuses, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"channel":
|
||||
{
|
||||
"name": "CheerLights",
|
||||
"latitude": "40.5",
|
||||
"longitude": "-80.22"
|
||||
},
|
||||
"feeds":
|
||||
[
|
||||
{
|
||||
"created_at": "2014-02-26T02:28:01Z",
|
||||
"entry_id": 11888,
|
||||
"status": "@cheerlights green"
|
||||
},
|
||||
{
|
||||
"created_at": "2014-02-26T22:05:31Z",
|
||||
"entry_id" :11889,
|
||||
"status": "@cheerlights blue"
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xxl format-json">
|
||||
The response will be a JSON object of Channel statuses, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"channel":
|
||||
{
|
||||
"name": "CheerLights",
|
||||
"latitude": "40.5",
|
||||
"longitude": "-80.22"
|
||||
},
|
||||
"feeds":
|
||||
[
|
||||
{
|
||||
"created_at": "2014-02-26T02:28:01Z",
|
||||
"entry_id": 11888,
|
||||
"status": "@cheerlights green"
|
||||
},
|
||||
{
|
||||
"created_at": "2014-02-26T22:05:31Z",
|
||||
"entry_id" :11889,
|
||||
"status": "@cheerlights blue"
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xxl format-xml">
|
||||
The response will be an XML object of Channel statuses, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<channel>
|
||||
<name>CheerLights</name>
|
||||
<latitude type="decimal">40.5</latitude>
|
||||
<longitude type="decimal">-80.22</longitude>
|
||||
<feeds type="array">
|
||||
<feed>
|
||||
<created-at type="dateTime">2014-02-26T02:28:01Z</created-at>
|
||||
<entry-id type="integer">11888</entry-id>
|
||||
<status>@cheerlights green</status>
|
||||
<id type="integer" nil="true"/>
|
||||
</feed>
|
||||
<feed>
|
||||
<created-at type="dateTime">2014-02-26T22:05:31Z</created-at>
|
||||
<entry-id type="integer">11889</entry-id>
|
||||
<status>@cheerlights blue</status>
|
||||
<id type="integer" nil="true"/>
|
||||
</feed>
|
||||
</feeds>
|
||||
</channel>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
100
app/views/docs/channels/_update.html.erb
Normal file
100
app/views/docs/channels/_update.html.erb
Normal file
@ -0,0 +1,100 @@
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="update">Update Channel Feed</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To update a Channel feed, send an HTTP GET or POST to<br>
|
||||
<code><%= @ssl_api_domain %>update<span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> .
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>api_key</b> (string) - Write API Key for this specific Channel (required). The Write API Key can optionally be sent via an X-THINGSPEAKAPIKEY HTTP header.</li>
|
||||
<li><b>field1</b> (string) - Field 1 data (optional)</li>
|
||||
<li><b>field2</b> (string) - Field 2 data (optional)</li>
|
||||
<li><b>field3</b> (string) - Field 3 data (optional)</li>
|
||||
<li><b>field4</b> (string) - Field 4 data (optional)</li>
|
||||
<li><b>field5</b> (string) - Field 5 data (optional)</li>
|
||||
<li><b>field6</b> (string) - Field 6 data (optional)</li>
|
||||
<li><b>field7</b> (string) - Field 7 data (optional)</li>
|
||||
<li><b>field8</b> (string) - Field 8 data (optional)</li>
|
||||
<li><b>lat</b> (decimal) - Latitude in degrees (optional)</li>
|
||||
<li><b>long</b> (decimal) - Longitude in degrees (optional)</li>
|
||||
<li><b>elevation</b> (integer) - Elevation in meters (optional)</li>
|
||||
<li><b>status</b> (string) - Status update message (optional)</li>
|
||||
<li><b>twitter</b> (string) - Twitter username linked to <a href="/docs/thingtweet">ThingTweet</a> (optional)</li>
|
||||
<li><b>tweet</b> (string) - Twitter status update; see <a href="/docs/thingtweet#update">updating ThingTweet</a> for more info (optional)</li>
|
||||
<li><b>created_at</b> (datetime) - Date when this feed entry was created, in the format<br>YYYY-MM-DD%20HH:NN:SS (optional)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example POST:
|
||||
|
||||
<pre>
|
||||
POST <span class="str"><%= @ssl_api_domain %>update<span class="format format-json">.json</span><span class="format format-xml">.xml</span></span>
|
||||
api_key=<span class="customcode"><%= @channel_api_key %></span>
|
||||
field1=<span class="customcode">73</span>
|
||||
</pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block-xl format-text">
|
||||
The response will be the entry ID of the update, for example: <code>18</code>
|
||||
<br><br>
|
||||
If the response is <code>0</code> then the update failed.
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xl format-json">
|
||||
The response will be a JSON object of the new feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"channel_id": 3,
|
||||
"field1": "73",
|
||||
"field2": null,
|
||||
"field3": null,
|
||||
"field4": null,
|
||||
"field5": null,
|
||||
"field6": null,
|
||||
"field7": null,
|
||||
"field8": null,
|
||||
"created_at": "2014-02-25T14:13:01-05:00",
|
||||
"entry_id": 320,
|
||||
"status": null,
|
||||
"latitude": null,
|
||||
"longitude": null,
|
||||
"elevation": null,
|
||||
"location":null
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xl format-xml">
|
||||
The response will be an XML object of the new feed, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feed>
|
||||
<channel-id type="integer">3</channel-id>
|
||||
<field1>73</field1>
|
||||
<field2 nil="true"/>
|
||||
<field3 nil="true"/>
|
||||
<field4 nil="true"/>
|
||||
<field5 nil="true"/>
|
||||
<field6 nil="true"/>
|
||||
<field7 nil="true"/>
|
||||
<field8 nil="true"/>
|
||||
<created-at type="dateTime">2014-02-25T14:15:42-05:00</created-at>
|
||||
<entry-id type="integer">321</entry-id>
|
||||
<status nil="true"/>
|
||||
<latitude type="decimal" nil="true"/>
|
||||
<longitude type="decimal" nil="true"/>
|
||||
<elevation nil="true"/>
|
||||
<location nil="true"/>
|
||||
</feed>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
89
app/views/docs/charts.html.erb
Normal file
89
app/views/docs/charts.html.erb
Normal file
@ -0,0 +1,89 @@
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-offset-1 col-sm-3 col-xs-12" id="leftcol">
|
||||
<%= render 'docs/sidebar' %>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7 col-xs-12">
|
||||
|
||||
<h1 id="charts">Charts</h1>
|
||||
The Charts API allows you to create an instant visualization of your data. The chart displays properly in all modern browsers and mobile devices. The chart can also show dynamic data by loading new data automatically.
|
||||
<br><br>
|
||||
Use the Charts API to present numerical data stored in ThingSpeak Channels on charts. Supported chart types are line, bar, column, and step. Options include size, color, and labels.
|
||||
<br><br>
|
||||
|
||||
<hr />
|
||||
<h2 id="create">Creating a Chart</h2>
|
||||
|
||||
To create a chart, open the following URL, replacing <span class="customcode">CHANNEL_ID</span> and <span class="customcode">FIELD_ID</span> with values from one of your Channels.
|
||||
<br><br>
|
||||
<pre>http://api.thingspeak.com/channels/<span class="customcode">CHANNEL_ID</span>/charts/<span class="customcode">FIELD_ID</span></pre>
|
||||
|
||||
<br>
|
||||
Valid chart parameters:
|
||||
<ul>
|
||||
<li><b>title</b> (string) Chart title, default: Channel name (optional)</li>
|
||||
<li><b>xaxis</b> (string) Chart's x-axis label, default: "Date" (optional)</li>
|
||||
<li><b>yaxis</b> (string) Chart's y-axis label, default: field name (optional)</li>
|
||||
<li><b>color</b> (string) Line color, default: red (optional)</li>
|
||||
<li><b>bgcolor</b> (string) Background color, default: white (optional)</li>
|
||||
<li><b>type</b> (line/bar/column) Type of chart, default: line (optional)</li>
|
||||
<li><b>width</b> (integer) Chart width in pixels, iframe width will be 20px larger, default chart width: 400 (optional)</li>
|
||||
<li><b>height</b> (integer) Chart height in pixels, iframe height will be 20px larger, default chart height: 200 (optional)</li>
|
||||
<li><b>dynamic</b> (true/false) Make chart update automatically every 15 seconds, default: false (optional)</li>
|
||||
<li><b>step</b> (true/false) Draw chart as a step chart, default: false (optional)</li>
|
||||
<li><b>export</b> (true/false) Show export buttons, so that chart can be saved as an image, default: false (optional)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Valid feed parameters:
|
||||
<ul>
|
||||
<li><b>key</b> (string) Read API Key for this specific Channel (optional--no key required for public channels)</li>
|
||||
<li><b>results</b> (integer) Number of entries to retrieve, 8000 max, default of 100 (optional)</li>
|
||||
<li><b>days</b> (integer) Days from now to include in feed (optional)</li>
|
||||
<li><b>start</b> (datetime) Start date in format YYYY-MM-DD%20HH:NN:SS (optional)</li>
|
||||
<li><b>end</b> (datetime) End date in format YYYY-MM-DD%20HH:NN:SS (optional)</li>
|
||||
<li><b>offset</b> (integer) Offset of your timezone without daylight savings time (optional)</li>
|
||||
<li><b>status</b> (true/false) Include status updates in feed by setting "status=true" (optional)</li>
|
||||
<li><b>location</b> (true/false) Include latitude, longitude, and elevation in feed by setting "location=true" (optional)</li>
|
||||
<li><b>min</b> (decimal) Minimum value to include in response (optional)</li>
|
||||
<li><b>max</b> (decimal) Maximum value to include in response (optional)</li>
|
||||
<li><b>round</b> (integer) Round to this many decimal places (optional)</li>
|
||||
<li><b>timescale</b> (integer or string) Get first value in this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>sum</b> (integer or string) Get sum of this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>average</b> (integer or string) Get average of this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
<li><b>median</b> (integer or string) Get median of this many minutes, valid values: 10, 15, 20, 30, 60, 240, 720, 1440, "daily" (optional)</li>
|
||||
</ul>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
Please note that the results parameter is not compatible with timescale, sum, average, or median.
|
||||
</div>
|
||||
|
||||
<br><br>
|
||||
|
||||
<hr />
|
||||
<h2 id="embed">Embedding a Chart</h2>
|
||||
To place a ThingSpeak Chart on your webpage, use the Chart API as the source of an iframe.
|
||||
<br><br>
|
||||
Chart Embed Code:
|
||||
<br><br>
|
||||
<pre><iframe width="450" height="250" style="border: 1px solid #cccccc;" src="http://thingspeak.com/channels/<span class="customcode">CHANNEL_ID</span>/charts/<span class="customcode">FIELD_ID</span>"></iframe></pre>
|
||||
<br><br>
|
||||
Example Chart:
|
||||
<iframe width="100%" height="250" marginwidth="0" marginheight="0" scrolling="auto" frameborder="0" src="https://thingspeak.com/channels/3/charts/1?results=15"></iframe>
|
||||
|
||||
<br>
|
||||
<h4>Embedding a Dynamic Chart</h4>
|
||||
To place a dynamic ThingSpeak Chart on your webpage, use the Chart API as the source of an iframe and add the chart parameter ”dynamic=true”.
|
||||
<br><br>
|
||||
Chart Embed Code:
|
||||
<br><br>
|
||||
<pre><iframe width="450" height="250" style="border: 1px solid #cccccc;" src="http://thingspeak.com/channels/<span class="customcode">CHANNEL_ID</span>/charts/<span class="customcode">FIELD_ID</span>?dynamic=true"></iframe></pre>
|
||||
<br><br>
|
||||
Example Chart:
|
||||
<iframe width="100%" height="250" marginwidth="0" marginheight="0" scrolling="auto" frameborder="0" src="https://thingspeak.com/channels/9/charts/1?dynamic=true&results=15&title=Dynamic+Light+Levels"></iframe>
|
||||
<br><br><br><br><br><br><br><br><br><br><br><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
75
app/views/docs/errors.html.erb
Normal file
75
app/views/docs/errors.html.erb
Normal file
@ -0,0 +1,75 @@
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-offset-1 col-sm-3 col-xs-12" id="leftcol">
|
||||
<%= render 'docs/sidebar' %>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7 col-xs-12">
|
||||
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="errors">Error Codes</h2>
|
||||
</div>
|
||||
|
||||
<br><br>
|
||||
|
||||
<table class="table table-striped table-bordered table-condensed">
|
||||
<tr>
|
||||
<th><%= t(:error_code) %></th>
|
||||
<th><%= t(:error_http_status) %></th>
|
||||
<th><%= t(:error_message) %></th>
|
||||
<th><%= t(:error_details) %></th>
|
||||
</tr>
|
||||
|
||||
<% t(:error_codes).each do |key, values| %>
|
||||
<tr>
|
||||
<td><code><%= key %></code></td>
|
||||
<td><%= values[:http_status] %></td>
|
||||
<td><%= values[:message] %></td>
|
||||
<td><%= values[:details] %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
</table>
|
||||
|
||||
<div>
|
||||
All errors will be sent with their corresponding <a href="https://en.wikipedia.org/wiki/List_of_HTTP_status_codes" target="_blank">HTTP status code</a>.
|
||||
<br><br>
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-text">
|
||||
Example error response: <code>error_auth_required</code>
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-json">
|
||||
The response will be a JSON error object, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"status": "401",
|
||||
"error":
|
||||
{
|
||||
"error_code": "error_auth_required",
|
||||
"message": "Authorization Required",
|
||||
"details": "Please make sure that your API key is correct."
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-xml">
|
||||
The response will be an XML error object, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<error>
|
||||
<error-code>error_auth_required</error-code>
|
||||
<message>Authorization Required</message>
|
||||
<details>Please make sure that your API key is correct.</details>
|
||||
</error>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,39 @@
|
||||
<div class="row">
|
||||
welcome
|
||||
|
||||
<div class="col-sm-offset-1 col-sm-3 col-xs-12" id="leftcol">
|
||||
<%= render 'docs/sidebar' %>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7 col-xs-12">
|
||||
|
||||
<h1>Getting Started</h1>
|
||||
|
||||
<ul>
|
||||
<li>Sign Up for a New User Account - <a title="Create New ThingSpeak User Account" href="https://thingspeak.com/account/new">https://thingspeak.com/account/new</a></li>
|
||||
<li>Create a New Channel by selecting <em>Channels</em> and then <em>Create New Channel</em></li>
|
||||
<li>Follow a <a title="ThingSpeak Tutorials" href="http://community.thingspeak.com/tutorials/">tutorial</a> for common devices and applications</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
<h4>Support</h4>
|
||||
<p>Please post your questions, comments, and feature requests in the <a title="ThingSpeak Forum" href="http://community.thingspeak.com/forum/">ThingSpeak Forum</a>.</p>
|
||||
|
||||
<br><br>
|
||||
<h4>Open Source</h4>
|
||||
|
||||
<p>The ThingSpeak API is available on <a title="ThingSpeak Open Source Web of Things API on GitHub" href="https://github.com/iobridge/thingspeak" target="_blank">GitHub</a> and includes the complete ThingSpeak API for processing HTTP requests, storing numeric and alphanumeric data, numeric data processing, location tracking, and status updates. The open source version follows the same documentation as the ThingSpeak hosted service.</p>
|
||||
|
||||
<ul>
|
||||
<li><a title="Open Source Web of Things API - ThingSpeak on GitHub" href="https://github.com/iobridge/thingspeak" target="_blank">GitHub Repository for ThingSpeak</a></li>
|
||||
<li><a title="ThingSpeak GitHub Readme File" href="https://github.com/iobridge/thingspeak/blob/master/README.textile" target="_blank">ThingSpeak README and Setup Instructions</a></li>
|
||||
</ul>
|
||||
|
||||
<br><br>
|
||||
<h4>HTTP Headers</h4>
|
||||
If you would like to reduce the number of HTTP headers sent by our application, please add the parameter "headers=false" to any HTTP request.
|
||||
|
||||
<br><br><br><br><br><br><br><br><br><br><br><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
26
app/views/docs/plugins.html.erb
Normal file
26
app/views/docs/plugins.html.erb
Normal file
@ -0,0 +1,26 @@
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-offset-1 col-sm-3 col-xs-12" id="leftcol">
|
||||
<%= render 'docs/sidebar' %>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7 col-xs-12">
|
||||
|
||||
<h1>Plugins</h1>
|
||||
|
||||
Plugins are way to create your applications natively on the ThingSpeak platform. You can create applications using HTML, CSS, and JavaScript to create mashups of ThingSpeak services with other web services. Plugins can either be private or public. Private plugins can only be viewed from a private ThingSpeak Channel view. Public Plugins can be embedded on your website or added to a ThingSpeak Channel view.
|
||||
<br><br>
|
||||
|
||||
<hr />
|
||||
<h2>Example Plugin</h2>
|
||||
<p><a title="Display a Google Gauge Visualization using ThingSpeak Plugins" href="http://community.thingspeak.com/tutorials/google/display-a-google-gauge-visualization-using-thingspeak-plugins/">Display a Google Gauge Visualization using ThingSpeak Plugins</a> [<a title="Source ode for Google Gauge ThingSpeak Plugin" href="http://community.thingspeak.com/code/Google_Gauge.html">Source Code</a>]</p>
|
||||
|
||||
<p style="text-align: center;">
|
||||
<iframe width="100%" height="120" marginwidth="0" marginheight="0" scrolling="auto" frameborder="0" src="http://community.thingspeak.com/code/Google_Gauge.html"></iframe>
|
||||
</p>
|
||||
|
||||
<br><br><br><br><br><br><br><br><br><br><br><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
22
app/views/docs/users.html.erb
Normal file
22
app/views/docs/users.html.erb
Normal file
@ -0,0 +1,22 @@
|
||||
<div class="row">
|
||||
|
||||
<div class="col-sm-offset-1 col-sm-3 col-xs-12" id="leftcol">
|
||||
<%= render 'docs/sidebar' %>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-7 col-xs-12">
|
||||
|
||||
<h1 id="users">Users</h1>
|
||||
<br><br>
|
||||
|
||||
<hr />
|
||||
<%= render 'docs/users/show' %>
|
||||
|
||||
<hr />
|
||||
<%= render 'docs/users/user_channels' %>
|
||||
|
||||
<br><br><br><br><br><br><br><br><br><br><br><br>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
59
app/views/docs/users/_show.html.erb
Normal file
59
app/views/docs/users/_show.html.erb
Normal file
@ -0,0 +1,59 @@
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="get_user">Get User Information</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To get a user's information, send an HTTP GET to <code><%= @ssl_api_domain %>users/<span class="customcode">USERNAME</span><span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> ,
|
||||
replacing <span class="customcode">USERNAME</span> with the person's username.
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>api_key</b> (string) - User's API Key which, if provided, will show private information such as email addresses. (optional)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example GET:
|
||||
|
||||
<pre>
|
||||
GET <span class="str"><%= @ssl_api_domain %>users/hans<span class="format format-json">.json</span><span class="format format-xml">.xml</span></span>
|
||||
</pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block format-text">
|
||||
The response will be a webpage with information about the user.
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-json">
|
||||
The response will be a JSON object of the user, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"id": 4,
|
||||
"login": "hans",
|
||||
"created_at": "2010-12-03T09:17:52-05:00",
|
||||
"bio": "Web Developer @iobridge, @thingspeak",
|
||||
"website": "http://www.iamshadowlord.com"
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block format-xml">
|
||||
The response will be an XML object of the user, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<user>
|
||||
<id type="integer">4</id>
|
||||
<login>hans</login>
|
||||
<created-at type="dateTime">2010-12-03T09:17:52-05:00</created-at>
|
||||
<bio>Web Developer @iobridge, @thingspeak</bio>
|
||||
<website>http://www.iamshadowlord.com</website>
|
||||
</user>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
136
app/views/docs/users/_user_channels.html.erb
Normal file
136
app/views/docs/users/_user_channels.html.erb
Normal file
@ -0,0 +1,136 @@
|
||||
<div>
|
||||
<%= render 'response' %>
|
||||
<h2 id="list_user_channels">List User's Channels</h2>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
To get a list of a user's Channels, send an HTTP GET to <code><%= @ssl_api_domain %>users/<span class="customcode">USERNAME</span>/channels<span class="format format-json">.json</span><span class="format format-xml">.xml</span></code> ,
|
||||
replacing <span class="customcode">USERNAME</span> with the person's username.
|
||||
|
||||
<br><br>
|
||||
Valid parameters:
|
||||
<ul>
|
||||
<li><b>api_key</b> (string) - User's API Key which, if provided, will also show private channels. (optional)</li>
|
||||
</ul>
|
||||
|
||||
<br>
|
||||
Example GET:
|
||||
|
||||
<pre>
|
||||
GET <span class="str"><%= @ssl_api_domain %>users/hans/channels<span class="format format-json">.json</span><span class="format format-xml">.xml</span></span>
|
||||
</pre>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="format format-block-xl format-text">
|
||||
The response will be a webpage with a list of the user's Channels.
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xl format-json">
|
||||
The response will be a JSON object of the user's Channels, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
{
|
||||
"channels":
|
||||
[
|
||||
{
|
||||
"id": 3,
|
||||
"name": "ioBridge Server",
|
||||
"description": "ioBridge IO-204 connected to web server",
|
||||
"latitude": null,
|
||||
"longitude": null,
|
||||
"created_at": "2010-12-03T09:26:23-05:00",
|
||||
"elevation": "",
|
||||
"last_entry_id": 163690,
|
||||
"ranking": 85,
|
||||
"username": "hans",
|
||||
"tags":
|
||||
[
|
||||
{
|
||||
"id": 24,
|
||||
"name": "temperature"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"name": "my_house",
|
||||
"description": "Netduino Plus connected to sensors around the house",
|
||||
"latitude": "40.44",
|
||||
"longitude": "-79.996",
|
||||
"created_at": "2010-12-13T20:20:06-05:00",
|
||||
"elevation": "",
|
||||
"last_entry_id": 6062844,
|
||||
"ranking": 100,
|
||||
"username": "hans",
|
||||
"tags":
|
||||
[
|
||||
{
|
||||
"id": 9,
|
||||
"name": "temp"
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"name": "light"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="format format-block-xl format-xml">
|
||||
The response will be an XML object of the user's Channels, for example:
|
||||
|
||||
<pre class="prettyprint">
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<response>
|
||||
<channels type="array">
|
||||
<channel>
|
||||
<id type="integer">3</id>
|
||||
<name>ioBridge Server</name>
|
||||
<description>ioBridge IO-204 connected to web server</description>
|
||||
<latitude nil="true"/>
|
||||
<longitude nil="true"/>
|
||||
<created-at type="dateTime">2010-12-03T09:26:23-05:00</created-at>
|
||||
<elevation/>
|
||||
<last-entry-id type="integer">163690</last-entry-id>
|
||||
<ranking type="integer">85</ranking>
|
||||
<username>hans</username>
|
||||
<tags type="array">
|
||||
<tag>
|
||||
<id type="integer">24</id>
|
||||
<name>temperature</name>
|
||||
</tag>
|
||||
</tags>
|
||||
</channel>
|
||||
<channel>
|
||||
<id type="integer">9</id>
|
||||
<name>my_house</name>
|
||||
<description>Netduino Plus connected to sensors around the house</description>
|
||||
<latitude type="decimal">40.44</latitude>
|
||||
<longitude type="decimal">-79.996</longitude>
|
||||
<created-at type="dateTime">2010-12-13T20:20:06-05:00</created-at>
|
||||
<elevation/>
|
||||
<last-entry-id type="integer">6062860</last-entry-id>
|
||||
<ranking type="integer">100</ranking>
|
||||
<username>hans</username>
|
||||
<tags type="array">
|
||||
<tag>
|
||||
<id type="integer">9</id>
|
||||
<name>temp</name>
|
||||
</tag>
|
||||
<tag>
|
||||
<id type="integer">25</id>
|
||||
<name>light</name>
|
||||
</tag>
|
||||
</tags>
|
||||
</channel>
|
||||
</channels>
|
||||
</response>
|
||||
</pre>
|
||||
|
||||
</div>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<a href="http://www.thingspeak.com/" title="Open Source Internet of Things">ThingSpeak.com</a> |
|
||||
<a href="http://community.thingspeak.com/" title="The ThingSpeak Blog covering the web of things">Blog</a> |
|
||||
<a href="http://community.thingspeak.com/forum/" title="Open discussion forum for ThingSpeak">Forum</a> |
|
||||
<a href="http://community.thingspeak.com/documentation/" title="ThingSpeak Documentation">Documentation</a> |
|
||||
<a href="/docs" title="ThingSpeak Documentation">Documentation</a> |
|
||||
<a href="http://community.thingspeak.com/tutorials/" title="Tutorials on how to use ThingSpeak web services">Tutorials</a> |
|
||||
<a href="http://feeds.feedburner.com/internetofthings" title="Internet of Things RSS Feed" target="_blank">RSS Feed</a>
|
||||
</span>
|
||||
|
@ -34,6 +34,9 @@
|
||||
<li><%= link_to t(:profile_edit), edit_profile_path %></li>
|
||||
</ul>
|
||||
</li>
|
||||
<% if current_admin_user.present? %>
|
||||
<li><%= link_to t(:admin), "/admin" %></li>
|
||||
<% end %>
|
||||
|
||||
<% else %>
|
||||
<li <%= "class=active" if @menu == 'channels' %>><%= link_to t(:channels), public_channels_path %></li>
|
||||
@ -43,10 +46,10 @@
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
|
||||
<li class="dropdown">
|
||||
<li class="<%= "active " if @menu == 'support' %>dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><%=t(:support)%> <b class="caret"></b></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="http://community.thingspeak.com/documentation/"><%= t(:documentation) %></a></li>
|
||||
<li><a href="/docs"><%= t(:documentation) %></a></li>
|
||||
<li><a href="http://community.thingspeak.com/tutorials/"><%= t(:tutorials) %></a></li>
|
||||
<li><a href="http://community.thingspeak.com/forum/"><%= t(:forum) %></a></li>
|
||||
</ul>
|
||||
@ -58,7 +61,7 @@
|
||||
<li><%= link_to t(:signout), logout_path %></li>
|
||||
<% else %>
|
||||
<li><%= link_to t(:signin), login_path %></li>
|
||||
<li><%= link_to t(:signup), new_account_path %></li>
|
||||
<li><%= link_to t(:signup), new_user_registration_path %></li>
|
||||
<% end %>
|
||||
|
||||
</ul>
|
||||
|
@ -23,6 +23,12 @@
|
||||
<div id="wrap">
|
||||
<%= render 'layouts/header' %>
|
||||
<div class="container">
|
||||
<% if notice.present? %>
|
||||
<p class="flash alert alert-success"><%= notice %><span class="dismiss">X</span></p>
|
||||
<% end %>
|
||||
<% if alert.present? %>
|
||||
<p class="flash alert alert-danger"><%= alert %><span class="dismiss">X</span></p>
|
||||
<% end %>
|
||||
<%= yield %>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,11 +6,11 @@
|
||||
<body>
|
||||
<p>
|
||||
<%= t(:password_reset_message1) %>
|
||||
<br />
|
||||
<br>
|
||||
<%= t(:password_reset_message2) %>
|
||||
<br />
|
||||
<br>
|
||||
<%= t(:password_reset_message3) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<a href="<%= @webpage %>"><%= @webpage %></a>
|
||||
</p>
|
||||
</body>
|
||||
|
@ -111,6 +111,6 @@
|
||||
|
||||
</script>
|
||||
|
||||
<br />
|
||||
<br>
|
||||
|
||||
<div id="chart-container" style="width: 500px; height: 260px; padding-left: 20px; float: left;"></div>
|
||||
|
@ -1,7 +1,8 @@
|
||||
<table border="1">
|
||||
<% for header in request.env.select {|k,v| k.match("^HTTP.*")} %>
|
||||
<tr>
|
||||
<td><%=header[0].split('_',2)[1]%></td><td><%=header[1]%></td>
|
||||
<td><%=header[0].split('_',2)[1]%></td><td><%=header[1]%></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
|
||||
|
@ -203,7 +203,7 @@
|
||||
<li><a href="http://www.twitter.com/thingspeak" title="Follow ThingSpeak on Twitter">Twitter</a></li>
|
||||
<li><a href="https://github.com/iobridge/ThingSpeak" title="Open Source Internet of Things Project on GitHub">GitHub</a></li>
|
||||
<li><a href="http://community.thingspeak.com" title="Join the ThingSpeak Community">ThingSpeak Community</a></li>
|
||||
<li><a href="http://community.thingspeak.com/documentation" title="ThingSpeak API and Apps Documentation">Documentation</a> and <a href="http://community.thingspeak.com/tutorials" title="ThingSpeak Tutorials">Tutorials</a></li>
|
||||
<li><a href="/docs" title="ThingSpeak API and Apps Documentation">Documentation</a> and <a href="http://community.thingspeak.com/tutorials" title="ThingSpeak Tutorials">Tutorials</a></li>
|
||||
<li><a href="mailto:support@thingspeak.com" title="Contact ThingSpeak">Questions</a></li>
|
||||
</ul>
|
||||
</p>
|
||||
|
@ -87,10 +87,10 @@
|
||||
<img src='<%= "#{@api_domain}images/social_sensor_network_main.png "%>' width='600' height='415' border='0' />
|
||||
<% end %>
|
||||
<h2>the social sensor network is forming.</h2>
|
||||
<h3>We have one thing in common. At almost all times we are connected to the web.<br />
|
||||
<h3>We have one thing in common. At almost all times we are connected to the web.<br>
|
||||
The Social Sensor Network allows everyone to report and share data in an open and meaningful way.</h3>
|
||||
|
||||
<br />
|
||||
<br>
|
||||
|
||||
<form method=post action="https://app.icontact.com/icp/signup.php" name="icpsignup" id="icpsignup2312" accept-charset="UTF-8" onsubmit="return verifyRequired2312();" >
|
||||
<input type=hidden name=redirect value="http://www.socialsensornetwork.com?invite=success" />
|
||||
@ -128,12 +128,12 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<span id="demo_text">demo channels</span>
|
||||
|
||||
<br />
|
||||
<br>
|
||||
|
||||
<span id="demo"><a href="http://www.socialsensornetwork.com/irs-refund">IRS Refunds</a></span>
|
||||
<span id="demo"><a href="http://www.socialsensornetwork.com/online-poker">Frozen Online Poker Money</a></span>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<h2>Pipes</h2>
|
||||
<%= link_to 'New Pipe', new_pipe_path %>
|
||||
|
||||
<br /><br />
|
||||
<br><br>
|
||||
|
||||
<table class="nicetable">
|
||||
<tr class="header"><td>ID</td><td>Name</td><td>Slug</td><td>URL</td><td>Date</td></tr>
|
||||
@ -15,5 +15,5 @@
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
<br />
|
||||
<br>
|
||||
<%= will_paginate @pipes %>
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
<% end %>
|
||||
|
||||
<br /><br />
|
||||
<br><br>
|
||||
|
||||
<h3><%= t(:plugin_delete_message) %></h3>
|
||||
<%= button_to t(:plugin_delete), plugin_path(@plugin.id), :method => 'delete', :data => { :confirm => t(:confirm_plugin_delete) }, :class => 'btn btn-danger' %>
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
</table>
|
||||
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<% end %>
|
||||
|
||||
<%= form_for :plugin do |p| %>
|
||||
|
@ -8,8 +8,8 @@
|
||||
<body>
|
||||
<div class="recent_status">
|
||||
<% @statuses.each do |r| %>
|
||||
<div ><span class="status_messages" id="<%= r.entry_id %>"><%= r.status %><br /><abbr class="timeago" title="<%= r.created_at %>"><%= r.created_at %></abbr></div>
|
||||
<br />
|
||||
<div ><span class="status_messages" id="<%= r.entry_id %>"><%= r.status %><br><abbr class="timeago" title="<%= r.created_at %>"><%= r.created_at %></abbr></div>
|
||||
<br>
|
||||
<% end %>
|
||||
<div>
|
||||
<script>
|
||||
@ -31,7 +31,7 @@ function refreshStatus() {
|
||||
$(".recent_status").prepend("<div><span class=\"status_messages\" id=\"" +
|
||||
data[i].entry_id + "\">"+
|
||||
data[i].status +
|
||||
"</span><br /><abbr class=\"timeago\" title=\"" +
|
||||
"</span><br><abbr class=\"timeago\" title=\"" +
|
||||
data[i].created_at + "\">" +
|
||||
data[i].created_at + "</abbr></div></br>");
|
||||
}
|
||||
|
@ -6,14 +6,14 @@
|
||||
<td><%= t.text_field :name %><%= t.submit t(:submit) %></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<br>
|
||||
|
||||
<% if @tag_name %>
|
||||
<% if @results %>
|
||||
|
||||
<% @results.each do |channel| %>
|
||||
<%= link_to channel.name, channel_path(channel.id) %>
|
||||
<br />
|
||||
<br>
|
||||
<% end %>
|
||||
|
||||
<% else %>
|
||||
|
@ -1,54 +1,55 @@
|
||||
<div class="col-sm-8 col-xs-7">
|
||||
<div class="row">
|
||||
<div class="col-sm-8 col-xs-7">
|
||||
|
||||
<ol class="breadcrumb">
|
||||
<li><%= link_to t(:myaccount), account_path %></li>
|
||||
<li class="active"><%= t(:account_edit) %></li>
|
||||
</ol>
|
||||
<ol class="breadcrumb">
|
||||
<li><%= link_to t(:myaccount), account_path %></li>
|
||||
<li class="active"><%= t(:account_edit) %></li>
|
||||
</ol>
|
||||
|
||||
<%= form_for @user, :url => account_path, :html => {:class => 'form-horizontal'} do |f| %>
|
||||
<%= error_messages_for 'user', :header_message => t(:try_again), :message => t(:account_error_edit) %>
|
||||
<input name='userlogin' class='userlogin' />
|
||||
<%= form_for @user, :url => account_path, :html => {:class => 'form-horizontal'} do |f| %>
|
||||
<%= error_messages_for 'user', :header_message => t(:try_again), :message => t(:account_error_edit) %>
|
||||
<input name='userlogin' class='userlogin' />
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:userid) %></label>
|
||||
<div class="col-sm-8 col-xs-8"><%= f.text_field :login, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:email) %></label>
|
||||
<div class="col-sm-8 col-xs-8"><%= f.text_field :email, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:time_zone) %></label>
|
||||
<div class="col-sm-8 col-xs-8">
|
||||
<p class="form-control-static">
|
||||
<%= time_zone_select 'user', 'time_zone', nil, {:default => 'Eastern Time (US & Canada)'}, {:class => 'form-control'} %>
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:userid) %></label>
|
||||
<div class="col-sm-8 col-xs-8"><%= f.text_field :login, :class => 'form-control' %></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:password_change) %></label>
|
||||
<div class="col-sm-8 col-xs-8"><%= f.password_field :password, :class => 'form-control' %></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:email) %></label>
|
||||
<div class="col-sm-8 col-xs-8"><%= f.text_field :email, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:time_zone) %></label>
|
||||
<div class="col-sm-8 col-xs-8">
|
||||
<p class="form-control-static">
|
||||
<%= time_zone_select 'user', 'time_zone', nil, {:default => 'Eastern Time (US & Canada)'}, {:class => 'form-control'} %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:password_confirmation) %></label>
|
||||
<div class="col-sm-8 col-xs-8"><%= f.password_field :password_confirmation, :class => 'form-control' %></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:password_change) %></label>
|
||||
<div class="col-sm-8 col-xs-8"><%= f.password_field :password, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:password_current) %></label>
|
||||
<div class="col-sm-8 col-xs-8"><%= f.password_field :password_current, :class => 'form-control' %></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-xs-4 control-label"><%= t(:password_current) %></label>
|
||||
<div class="col-sm-8 col-xs-8"><%= f.password_field :password_current, :class => 'form-control' %></div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-4 col-sm-8 col-xs-offset-4 col-xs-8"><p class="form-control-static"><%= f.submit t(:account_update), :class => 'btn btn-primary' %></p></div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-4 col-sm-8 col-xs-offset-4 col-xs-8"><p class="form-control-static"><%= f.submit t(:account_update), :class => 'btn btn-primary' %></p></div>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br><br>
|
||||
|
||||
<h3><%= t(:account_delete_message) %></h3>
|
||||
<%= button_to t(:account_delete), user_path(@user.id), :method => 'delete', :data => { :confirm => t(:confirm_account_delete) }, :class => 'btn btn-danger' %>
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
<br />
|
||||
<br>
|
||||
<%= will_paginate @channels %>
|
||||
<% else %>
|
||||
<%= t(:user_no_public_channels) %>
|
||||
|
@ -1,17 +1,17 @@
|
||||
<% if @user.public_flag == true %>
|
||||
|
||||
<div class="FR">
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= gravatar_tag @user, :default => 'wavatar', :secure => true %>
|
||||
</div>
|
||||
|
||||
<h2><%= t(:profile_for) %> <%= @user.login %></h2>
|
||||
<%= link_to t(:channels_public_view), list_channels_path(@user.login) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:member_since) %> <%= @user.created_at.strftime('%B %-d, %Y') %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:profile_website) %>: <%= link_to @user.website, @user.website %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= t(:profile_bio) %>: <%= @user.bio %>
|
||||
|
||||
<% else %>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<div class="col-pad">
|
||||
<%= link_to t(:account_edit), edit_account_path, :class => 'btn btn-primary btn-sm' %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<td><%= t(:userid) %></td>
|
||||
@ -29,13 +29,13 @@
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<h4 class="breadcrumb"><%= t(:public_profile) %></h4>
|
||||
<div class="col-pad">
|
||||
<%= link_to t(:profile_edit), edit_profile_path, :class => 'btn btn-primary btn-sm' %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<%= link_to t(:profile_view), user_profile_path(@user.login) %>
|
||||
<br /><br />
|
||||
<br><br>
|
||||
<% if @user.public_flag %>
|
||||
<table class="table">
|
||||
<tr>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user