add ActiveAdmin

This commit is contained in:
Lee Lawlor
2014-03-22 13:21:45 -04:00
parent f21dea069f
commit 156f4147ef
80 changed files with 912 additions and 241 deletions

25
app/admin/channel.rb Normal file
View 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
View 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
View 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
View 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

View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -0,0 +1,2 @@
#= require active_admin/base

View File

@@ -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%"),

View File

@@ -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');
}
}

View File

@@ -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('&amp;')), 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('&amp;')), 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();});
}) ;
}
}

View 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; }

View File

@@ -3,7 +3,7 @@ class ApplicationController < ActionController::Base
# 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?
@@ -47,8 +47,14 @@ class ApplicationController < ActionController::Base
params[:sum] = '1440' if params[:sum] == 'daily'
end
# change default devise sign_in page
def after_sign_in_path_for(resource); channels_path; 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
def get_locale
@@ -106,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.present? && 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
@@ -160,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
@@ -194,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

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -4,7 +4,7 @@ class RegistrationsController < Devise::RegistrationsController
# use defaults from devise
def new; super; end
def new; super; end
def edit; super; end
def create; super; end
private

View File

@@ -0,0 +1,2 @@
module AdminHelper
end

18
app/models/admin_user.rb Normal file
View 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

View 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) %>

View File

@@ -0,0 +1,4 @@
<% @users.each do |u| %>
<%= u.email %><br>
<% end %>

View 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 %>

View File

@@ -45,7 +45,7 @@
<% 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' %>

View File

@@ -41,7 +41,7 @@
<%= d.submit t(:channel_create), :class => 'btn btn-primary' %>
<% end %>
<% if is_admin? %>
<% if current_admin_user.present? %>
<br><br><br>

View File

@@ -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>

View File

@@ -10,11 +10,10 @@
<%= form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post, :class => 'form-horizontal' }) do |f| %>
<%= devise_error_messages! %>
<input name='userlogin' class='userlogin' />
<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 :login, :class => 'form-control' %></div>
<div class="col-sm-9 col-xs-9"><%= f.text_field :email, :class => 'form-control' %></div>
</div>
@@ -30,6 +29,6 @@
<script type="text/javascript">
document.getElementById('user_login').focus();
document.getElementById('user_email').focus();
</script>

View File

@@ -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>