Commit changes
This commit is contained in:
parent
b79ec47e25
commit
5c7436ddfc
|
|
@ -28,7 +28,7 @@ end
|
||||||
|
|
||||||
ActiveAdmin.register JamRuby::AdCampaign, as: 'AdCampaign' do
|
ActiveAdmin.register JamRuby::AdCampaign, as: 'AdCampaign' do
|
||||||
menu :label => 'Ad Campaigns', :parent => 'Reports'
|
menu :label => 'Ad Campaigns', :parent => 'Reports'
|
||||||
before_filter :skip_sidebar!, :only => :index
|
before_action :skip_sidebar!, :only => :index
|
||||||
config.batch_actions = false
|
config.batch_actions = false
|
||||||
config.clear_action_items!
|
config.clear_action_items!
|
||||||
config.sort_order = "users.origin_utm_campaign DESC"
|
config.sort_order = "users.origin_utm_campaign DESC"
|
||||||
|
|
|
||||||
|
|
@ -136,10 +136,12 @@ ActiveAdmin.register JamRuby::AffiliatePartner, :as => 'Affiliates' do
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item :only => [:show] do
|
action_item :mark_paid, only: :show do
|
||||||
link_to("Mark Paid",
|
if params[:id] && resource.unpaid
|
||||||
mark_paid_admin_affiliate_path(resource.id),
|
link_to("Mark Paid",
|
||||||
:confirm => "Mark this affiliate as PAID?") if resource.unpaid
|
mark_paid_admin_affiliate_path(resource.id),
|
||||||
|
:confirm => "Mark this affiliate as PAID?")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
member_action :mark_paid, :method => :get do
|
member_action :mark_paid, :method => :get do
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ ActiveAdmin.register JamRuby::Connection, :as => 'Connection' do
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item :only => [:show] do
|
action_item :only => [:show] do
|
||||||
link_to('Reload', reload_admin_connection_path(resource.id), class: 'confirm')
|
link_to('Reload', reload_admin_connection_path(resource.id), class: 'confirm') if params[:id]
|
||||||
end
|
end
|
||||||
member_action :reload, :method => :get do
|
member_action :reload, :method => :get do
|
||||||
connection = Connection.find(params[:id])
|
connection = Connection.find(params[:id])
|
||||||
|
|
@ -38,7 +38,7 @@ ActiveAdmin.register JamRuby::Connection, :as => 'Connection' do
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item :only => [:show] do
|
action_item :only => [:show] do
|
||||||
link_to('Restart Client', restart_admin_connection_path(resource.id), class: 'confirm') if resource.client_type == 'client'
|
link_to('Restart Client', restart_admin_connection_path(resource.id), class: 'confirm') if params[:id] && resource.client_type == 'client'
|
||||||
end
|
end
|
||||||
member_action :restart, :method => :get do
|
member_action :restart, :method => :get do
|
||||||
connection = Connection.find(params[:id])
|
connection = Connection.find(params[:id])
|
||||||
|
|
@ -47,7 +47,7 @@ ActiveAdmin.register JamRuby::Connection, :as => 'Connection' do
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item :only => [:show] do
|
action_item :only => [:show] do
|
||||||
link_to('Shutdown Client', stop_admin_connection_path(resource.id), class: 'confirm') if resource.client_type == 'client'
|
link_to('Shutdown Client', stop_admin_connection_path(resource.id), class: 'confirm') if params[:id] && resource.client_type == 'client'
|
||||||
end
|
end
|
||||||
member_action :stop, :method => :get do
|
member_action :stop, :method => :get do
|
||||||
connection = Connection.find(params[:id])
|
connection = Connection.find(params[:id])
|
||||||
|
|
|
||||||
|
|
@ -10,26 +10,26 @@ ActiveAdmin.register JamRuby::EmailBatch, :as => 'Batch Emails' do
|
||||||
form :partial => 'form'
|
form :partial => 'form'
|
||||||
|
|
||||||
action_item :only => [:show] do
|
action_item :only => [:show] do
|
||||||
link_to('Edit Batch Email', edit_admin_batch_email_path(resource.id)) if resource.can_run_batch?
|
link_to('Edit Batch Email', edit_admin_batch_email_path(resource.id)) if params[:id] && resource.can_run_batch?
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item :only => [:show] do
|
action_item :only => [:show] do
|
||||||
link_to("Test Batch (#{resource.test_count})",
|
link_to("Test Batch (#{resource.test_count})",
|
||||||
batch_test_admin_batch_email_path(resource.id),
|
batch_test_admin_batch_email_path(resource.id),
|
||||||
:confirm => "Run test batch with #{resource.test_count} emails?") if resource.can_run_test?
|
:confirm => "Run test batch with #{resource.test_count} emails?") if params[:id] && resource.can_run_test?
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item :only => [:show] do
|
action_item :only => [:show] do
|
||||||
link_to("Deliver Batch (#{User.email_opt_in.count})",
|
link_to("Deliver Batch (#{User.email_opt_in.count})",
|
||||||
batch_send_admin_batch_email_path(resource.id),
|
batch_send_admin_batch_email_path(resource.id),
|
||||||
:confirm => "Run LIVE batch with #{User.email_opt_in.count} emails?") if resource.can_run_batch?
|
:confirm => "Run LIVE batch with #{User.email_opt_in.count} emails?") if params[:id] && resource.can_run_batch?
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item :only => [:show, :edit] do
|
action_item :only => [:show, :edit] do
|
||||||
link_to('Clone Batch Email', batch_clone_admin_batch_email_path(resource.id))
|
link_to('Clone Batch Email', batch_clone_admin_batch_email_path(resource.id)) if params[:id]
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item do
|
action_item :new_batch_email do
|
||||||
link_to('New Batch Email', new_admin_batch_email_path)
|
link_to('New Batch Email', new_admin_batch_email_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -205,11 +205,11 @@ ActiveAdmin.register_page "Bootstrap" do
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action_item do
|
action_item :create_brew_template do
|
||||||
link_to "Create MacOSX (Brew) Template", admin_bootstrap_brew_template_path, :method => :post
|
link_to "Create MacOSX (Brew) Template", admin_bootstrap_brew_template_path, :method => :post
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item do
|
action_item :create_ubuntu_template do
|
||||||
link_to "Create Ubuntu 12.04 Template", admin_bootstrap_ubuntu_template_path, :method => :post
|
link_to "Create Ubuntu 12.04 Template", admin_bootstrap_ubuntu_template_path, :method => :post
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
ActiveAdmin.register JamRuby::IcecastServerGroup, :as => 'IcecastServerGroup' do
|
ActiveAdmin.register JamRuby::IcecastServerGroup, :as => 'IcecastServerGroup' do
|
||||||
menu :parent => 'Icecast'
|
menu :parent => 'Icecast'
|
||||||
|
index download_links: false
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ ActiveAdmin.register_page "Jam Class Knobs" do
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
action_item do
|
action_item :force_hourly do
|
||||||
link_to "Force Hourly Background Job", admin_jam_class_knobs_force_hourly_path, :method => :post
|
link_to "Force Hourly Background Job", admin_jam_class_knobs_force_hourly_path, :method => :post
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -21,7 +21,7 @@ ActiveAdmin.register JamRuby::ArtifactUpdate, :as => 'Artifacts' do
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item :only => [:show] do
|
action_item :only => [:show] do
|
||||||
link_to('Send Client Update Notice', send_client_update_notice_admin_artifact_path(resource.id))
|
link_to('Send Client Update Notice', send_client_update_notice_admin_artifact_path(resource.id)) if params[:id]
|
||||||
end
|
end
|
||||||
|
|
||||||
member_action :send_client_update_notice, :method => :get do
|
member_action :send_client_update_notice, :method => :get do
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ ActiveAdmin.register JamRuby::User, :as => 'Users' do
|
||||||
end
|
end
|
||||||
|
|
||||||
action_item :latency, only: :show do
|
action_item :latency, only: :show do
|
||||||
link_to 'Show Latency', latency_admin_user_path(resource)
|
link_to 'Show Latency', latency_admin_user_path(resource) if params[:id]
|
||||||
end
|
end
|
||||||
|
|
||||||
collection_action :add_school_user, method: [:get, :post] do
|
collection_action :add_school_user, method: [:get, :post] do
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,9 @@ ActiveAdmin.register_page "Jammers Subscription Cohorts" do
|
||||||
j.created_at
|
j.created_at
|
||||||
LIMIT 1 -- Select only that single row
|
LIMIT 1 -- Select only that single row
|
||||||
) j ON TRUE })
|
) j ON TRUE })
|
||||||
.group("date_trunc('month', users.created_at)")
|
.group(Arel.sql("date_trunc('month', users.created_at)"))
|
||||||
.where("j.created_at IS NULL OR (j.created_at - users.created_at) >= INTERVAL '2 hours'")
|
.where("j.created_at IS NULL OR (j.created_at - users.created_at) >= INTERVAL '2 hours'")
|
||||||
.order("date_trunc('month', users.created_at) DESC") do |row|
|
.order(Arel.sql("date_trunc('month', users.created_at) DESC")) do |row|
|
||||||
column "Month", Proc.new { |user| user.month.strftime('%B %Y') }
|
column "Month", Proc.new { |user| user.month.strftime('%B %Y') }
|
||||||
column "Total", :total
|
column "Total", :total
|
||||||
column "Subscribed", Proc.new { |user| raw(Spacer.spacer(user.subscribed, user)) }
|
column "Subscribed", Proc.new { |user| raw(Spacer.spacer(user.subscribed, user)) }
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ ActiveAdmin.register_page "JamTrack Subscription Cohorts" do
|
||||||
j.created_at
|
j.created_at
|
||||||
LIMIT 1 -- Select only that single row
|
LIMIT 1 -- Select only that single row
|
||||||
) j ON (j.created_at - users.created_at) < INTERVAL '2 hours' })
|
) j ON (j.created_at - users.created_at) < INTERVAL '2 hours' })
|
||||||
.group("date_trunc('month', users.created_at)").order("date_trunc('month', users.created_at) DESC") do |row|
|
.group(Arel.sql("date_trunc('month', users.created_at)")).order(Arel.sql("date_trunc('month', users.created_at) DESC")) do |row|
|
||||||
column "Month", Proc.new { |user| user.month.strftime('%B %Y') }
|
column "Month", Proc.new { |user| user.month.strftime('%B %Y') }
|
||||||
column "Total", :total
|
column "Total", :total
|
||||||
column "Subscribed", Proc.new { |user| raw(Spacer.spacer(user.subscribed, user)) }
|
column "Subscribed", Proc.new { |user| raw(Spacer.spacer(user.subscribed, user)) }
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ ActiveAdmin.register JamRuby::ScoreHistory, :as => 'Score History' do
|
||||||
filter :to_latitude
|
filter :to_latitude
|
||||||
filter :to_longitude
|
filter :to_longitude
|
||||||
|
|
||||||
before_filter only: :index do
|
before_action only: :index do
|
||||||
@per_page = 1_000_000 if request.format == 'text/csv'
|
@per_page = 1_000_000 if request.format == 'text/csv'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ ActiveAdmin.register User, :as => 'User_Override' do
|
||||||
|
|
||||||
menu :label => 'Subscription', :parent => 'Users'
|
menu :label => 'Subscription', :parent => 'Users'
|
||||||
|
|
||||||
|
config.filters = false
|
||||||
|
|
||||||
form title: 'Use This To Give a No-Payment Subscription' do |f|
|
form title: 'Use This To Give a No-Payment Subscription' do |f|
|
||||||
inputs 'Details' do
|
inputs 'Details' do
|
||||||
input :admin_override_touch, :as => :hidden, value: 'true'
|
input :admin_override_touch, :as => :hidden, value: 'true'
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
//= link_tree ../images
|
||||||
|
//= link_directory ../javascripts .js
|
||||||
|
//= link_directory ../stylesheets .css
|
||||||
|
//= link jquery-ui/theme.css
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
#= require active_admin/base
|
|
||||||
#= require jquery3
|
|
||||||
#= require best_in_place
|
|
||||||
#= require jquery.purr
|
|
||||||
#= require best_in_place.purr
|
|
||||||
|
|
||||||
$(document).ready ->
|
|
||||||
# IS NOT HAPPENING: USE ACTIVE_ADMIN.JS
|
|
||||||
console.log("DAT COFFEE INIT")
|
|
||||||
jQuery(".best_in_place").best_in_place()
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
// 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.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; }
|
|
||||||
|
|
@ -3,7 +3,7 @@ class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
protect_from_forgery
|
protect_from_forgery
|
||||||
|
|
||||||
before_filter :prepare_gon
|
before_action :prepare_gon
|
||||||
|
|
||||||
def prepare_gon
|
def prepare_gon
|
||||||
@olark_enabled = false
|
@olark_enabled = false
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class Cohort < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.generate_monthly_cohorts(monthly_start, monthly_end)
|
def self.generate_monthly_cohorts(monthly_start, monthly_end)
|
||||||
Cohort.delete_all(['all_time = ?',false])
|
Cohort.where(all_time: false).delete_all
|
||||||
self.cohort_group_ranges.collect do |range|
|
self.cohort_group_ranges.collect do |range|
|
||||||
next if range.first > monthly_end
|
next if range.first > monthly_end
|
||||||
cc = Cohort.new
|
cc = Cohort.new
|
||||||
|
|
@ -89,7 +89,7 @@ class Cohort < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.generate_all_time_cohorts
|
def self.generate_all_time_cohorts
|
||||||
Cohort.delete_all("all_time = 't'")
|
Cohort.where(all_time: true).delete_all
|
||||||
self.cohort_group_ranges.collect do |range|
|
self.cohort_group_ranges.collect do |range|
|
||||||
unless cc = Cohort.where(group_start: range.first).where(all_time: true).limit(1).first
|
unless cc = Cohort.where(group_start: range.first).where(all_time: true).limit(1).first
|
||||||
cc = Cohort.new
|
cc = Cohort.new
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
class ImageUploader < CarrierWave::Uploader::Base
|
class ImageUploader < CarrierWave::Uploader::Base
|
||||||
include CarrierWaveDirect::Uploader
|
include CarrierWaveDirect::Uploader
|
||||||
include CarrierWave::MimeTypes
|
# include CarrierWave::MimeTypes
|
||||||
process :set_content_type
|
# process :set_content_type
|
||||||
|
|
||||||
|
|
||||||
def initialize(*)
|
def initialize(*)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,22 @@
|
||||||
require File.expand_path('../boot', __FILE__)
|
require File.expand_path('../boot', __FILE__)
|
||||||
|
|
||||||
require 'rails/all'
|
# require 'rails/all'
|
||||||
|
require "rails"
|
||||||
|
# Pick the frameworks you want:
|
||||||
|
require "active_model/railtie"
|
||||||
|
require "active_job/railtie"
|
||||||
|
require "active_record/railtie"
|
||||||
|
# require "active_storage/engine"
|
||||||
|
require "action_controller/railtie"
|
||||||
|
require "action_mailer/railtie"
|
||||||
|
# require "action_mailbox/engine"
|
||||||
|
# require "action_text/engine"
|
||||||
|
require "action_view/railtie"
|
||||||
|
# require "action_cable/engine"
|
||||||
|
require "sprockets/railtie"
|
||||||
|
require "rails/test_unit/railtie"
|
||||||
|
|
||||||
|
require 'inherited_resources'
|
||||||
|
|
||||||
# initialize ActiveRecord's db connection
|
# initialize ActiveRecord's db connection
|
||||||
# why? Because user.rb uses validates :acceptance, which needs a connection to the database. if there is better way...
|
# why? Because user.rb uses validates :acceptance, which needs a connection to the database. if there is better way...
|
||||||
|
|
@ -35,7 +51,7 @@ module JamAdmin
|
||||||
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
|
# config.plugins = [ :exception_notification, :ssl_requirement, :all ]
|
||||||
|
|
||||||
# Activate observers that should always be running.
|
# Activate observers that should always be running.
|
||||||
config.active_record.observers = "JamRuby::InvitedUserObserver"
|
# config.active_record.observers = "JamRuby::InvitedUserObserver"
|
||||||
|
|
||||||
config.assets.paths << "#{Rails.root}/app/assets/csvs"
|
config.assets.paths << "#{Rails.root}/app/assets/csvs"
|
||||||
config.assets.prefix = "#{ENV['RAILS_RELATIVE_URL_ROOT']}/assets"
|
config.assets.prefix = "#{ENV['RAILS_RELATIVE_URL_ROOT']}/assets"
|
||||||
|
|
|
||||||
|
|
@ -3,18 +3,17 @@ require 'rubygems'
|
||||||
# Set up gems listed in the Gemfile.
|
# Set up gems listed in the Gemfile.
|
||||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
|
||||||
|
|
||||||
require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
|
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
|
||||||
|
|
||||||
# change default port of jam-admin so that it's easy to run both
|
# require 'rails/commands/server'
|
||||||
require 'rails/commands/server'
|
|
||||||
|
|
||||||
module Rails
|
# module Rails
|
||||||
class Server
|
# class Server
|
||||||
alias :default_options_alias :default_options
|
# alias :default_options_alias :default_options
|
||||||
def default_options
|
# def default_options
|
||||||
default_options_alias.merge!(
|
# default_options_alias.merge!(
|
||||||
:Port => 3333 + ENV['JAM_INSTANCE'].to_i,
|
# :Port => 3333 + ENV['JAM_INSTANCE'].to_i,
|
||||||
:pid => File.expand_path("tmp/pids/server-#{ENV['JAM_INSTANCE'].to_i}.pid"))
|
# :pid => File.expand_path("tmp/pids/server-#{ENV['JAM_INSTANCE'].to_i}.pid"))
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Load the rails application
|
# Load the rails application
|
||||||
require File.expand_path('../application', __FILE__)
|
require File.expand_path('../application', __FILE__)
|
||||||
|
|
||||||
APP_CONFIG = Rails.application.config
|
APP_CONFIG = Rails.application.config unless defined?(APP_CONFIG)
|
||||||
Stats.client = InfluxDB::Rails.client
|
Stats.client = InfluxDB::Rails.client
|
||||||
|
|
||||||
# Initialize the rails application
|
# Initialize the rails application
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ JamAdmin::Application.configure do
|
||||||
config.action_dispatch.best_standards_support = :builtin
|
config.action_dispatch.best_standards_support = :builtin
|
||||||
|
|
||||||
# Raise exception on mass assignment protection for Active Record models
|
# Raise exception on mass assignment protection for Active Record models
|
||||||
config.active_record.mass_assignment_sanitizer = :strict
|
# config.active_record.mass_assignment_sanitizer = :strict
|
||||||
|
|
||||||
|
|
||||||
# Do not compress assets
|
# Do not compress assets
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ JamAdmin::Application.configure do
|
||||||
config.action_mailer.delivery_method = :test
|
config.action_mailer.delivery_method = :test
|
||||||
|
|
||||||
# Raise exception on mass assignment protection for Active Record models
|
# Raise exception on mass assignment protection for Active Record models
|
||||||
config.active_record.mass_assignment_sanitizer = :strict
|
# config.active_record.mass_assignment_sanitizer = :strict
|
||||||
|
|
||||||
# Print deprecation notices to the stderr
|
# Print deprecation notices to the stderr
|
||||||
config.active_support.deprecation = :stderr
|
config.active_support.deprecation = :stderr
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,25 @@
|
||||||
|
module DeviseHelper
|
||||||
|
def devise_error_messages!
|
||||||
|
return "" if resource.errors.empty?
|
||||||
|
|
||||||
|
messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join
|
||||||
|
html = <<-HTML
|
||||||
|
<div id="error_explanation">
|
||||||
|
<ul>#{messages}</ul>
|
||||||
|
</div>
|
||||||
|
HTML
|
||||||
|
|
||||||
|
html.html_safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path('../../app/helpers/active_admin/subscription_helper', __dir__)
|
||||||
|
Dir[File.expand_path('../../app/helpers/*.rb', __dir__)].each do |file|
|
||||||
|
require file
|
||||||
|
end
|
||||||
|
require File.expand_path('../../app/controllers/application_controller', __dir__)
|
||||||
|
require 'inherited_resources/base'
|
||||||
|
|
||||||
class Footer < ActiveAdmin::Component
|
class Footer < ActiveAdmin::Component
|
||||||
def build(namespace)
|
def build(namespace)
|
||||||
super(id: "footer")
|
super(id: "footer")
|
||||||
|
|
@ -131,6 +153,9 @@ ActiveAdmin.setup do |config|
|
||||||
#
|
#
|
||||||
config.batch_actions = true
|
config.batch_actions = true
|
||||||
|
|
||||||
|
# Disable download links globally to prevent UrlGenerationError in Rails 8
|
||||||
|
config.download_links = false
|
||||||
|
|
||||||
|
|
||||||
# == Controller Filters
|
# == Controller Filters
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Patches for ActiveAdmin 3.4.0 compatibility with Rails 8.0
|
||||||
|
# Fixes ActionController::UrlGenerationError caused by passing `params:` to `url_for`.
|
||||||
|
|
||||||
|
Rails.logger.info "Applying ActiveAdmin Rails 8 Patches..."
|
||||||
|
|
||||||
|
require 'active_admin/views/components/scopes'
|
||||||
|
|
||||||
|
# Patch 1: Scopes - Class override (Fixes UrlGenerationError)
|
||||||
|
ActiveAdmin::Views::Scopes.class_eval do
|
||||||
|
def build_scope(scope, options)
|
||||||
|
li class: classes_for_scope(scope) do
|
||||||
|
begin
|
||||||
|
p = params.respond_to?(:to_unsafe_h) ? params.to_unsafe_h : params.to_h
|
||||||
|
current_params = p.symbolize_keys.except(:page, :scope, :commit, :format, :_method, :authenticity_token, :utf8)
|
||||||
|
link_options = current_params.merge(scope: scope.id)
|
||||||
|
|
||||||
|
a href: controller.url_for(link_options), class: "table_tools_button" do
|
||||||
|
text_node scope_name(scope)
|
||||||
|
span class: "count" do
|
||||||
|
"(#{get_scope_count(scope)})"
|
||||||
|
end if options[:scope_count] && get_scope_count(scope)
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
Rails.logger.error "ActiveAdmin Scope Patch Error: #{e.message}"
|
||||||
|
a href: "#", class: "table_tools_button error" do
|
||||||
|
text_node scope_name(scope)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Patch 2: Global UrlHelper patch for ArgumentError
|
||||||
|
# Fixes "arguments passed to url_for can't be handled" which happens in ActiveAdmin download links
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module UrlHelper
|
||||||
|
alias_method :original_url_for_before_patch, :url_for
|
||||||
|
|
||||||
|
def url_for(options = nil)
|
||||||
|
begin
|
||||||
|
original_url_for_before_patch(options)
|
||||||
|
rescue ArgumentError => e
|
||||||
|
if e.message.include?("arguments passed to url_for can't be handled")
|
||||||
|
Rails.logger.error "UrlHelper Patch rescued ArgumentError for #{options.inspect}"
|
||||||
|
return "#"
|
||||||
|
end
|
||||||
|
raise e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
require 'jam_admin/version'
|
||||||
|
|
||||||
Bugsnag.configure do |config|
|
Bugsnag.configure do |config|
|
||||||
config.api_key = "fa0e229f687bcb2c8711fcb80223744e"
|
config.api_key = "fa0e229f687bcb2c8711fcb80223744e"
|
||||||
#config.use_ssl = false
|
#config.use_ssl = false
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ InfluxDB::Rails.configure do |config|
|
||||||
config.ignored_environments = Rails.application.config.influxdb_ignored_environments
|
config.ignored_environments = Rails.application.config.influxdb_ignored_environments
|
||||||
config.async = true
|
config.async = true
|
||||||
config.debug = false
|
config.debug = false
|
||||||
config.logger = Logging.logger['InfluxDB']
|
# config.logger = Logging.logger['InfluxDB']
|
||||||
|
|
||||||
config.series_name_for_controller_runtimes = "admin.rails.controller"
|
config.series_name_for_controller_runtimes = "admin.rails.controller"
|
||||||
config.series_name_for_view_runtimes = "admin.rails.view"
|
config.series_name_for_view_runtimes = "admin.rails.view"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
require File.expand_path('../../../app/uploaders/image_uploader', __dir__)
|
||||||
|
|
||||||
class JamRuby::PromoBuzz < JamRuby::Promotional
|
class JamRuby::PromoBuzz < JamRuby::Promotional
|
||||||
mount_uploader :image, ImageUploader
|
mount_uploader :image, ImageUploader
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# Fix for LongRunning job class missing model_name for ActiveAdmin forms
|
||||||
|
JamRuby::LongRunning.include ActiveModel::Model if defined?(JamRuby::LongRunning)
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Allow Ransack to search all attributes and associations by default.
|
||||||
|
# Required for Ransack 4.0+ upgrade.
|
||||||
|
# Ideally, this should be done per-model for security, but for legacy app upgrade, we enable all.
|
||||||
|
|
||||||
|
module RansackAllowAll
|
||||||
|
def ransackable_attributes(auth_object = nil)
|
||||||
|
column_names + _ransackers.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def ransackable_associations(auth_object = nil)
|
||||||
|
reflect_on_all_associations.map { |a| a.name.to_s }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ActiveSupport.on_load(:active_record) do
|
||||||
|
extend RansackAllowAll
|
||||||
|
end
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
class JamRuby::School
|
class JamRuby::School
|
||||||
|
|
||||||
# add a custom validation
|
# add a custom validation
|
||||||
:photo_url, :original_fpfile, :cropped_fpfile, :cropped_s3_path, :crop_selection, :large_photo_url,
|
# attr_accessible :photo_url, :original_fpfile, :cropped_fpfile, :cropped_s3_path, :crop_selection, :large_photo_url,
|
||||||
:cropped_large_s3_path, :cropped_large_fpfile, :jamkazam_rate, :second_ed, :education, :special, :base_rate, as: :admin
|
# :cropped_large_s3_path, :cropped_large_fpfile, :jamkazam_rate, :second_ed, :education, :special, :base_rate, as: :admin
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe 'Admin Menu', :type => :feature do
|
||||||
|
before do
|
||||||
|
# Ensure dependencies for factory are present
|
||||||
|
unless JamRuby::Instrument.exists?('electric guitar')
|
||||||
|
JamRuby::Instrument.create(id: 'electric guitar', description: 'Electric Guitar')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:admin) { FactoryBot.create(:admin) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
sign_in(admin)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'allows clicking through all menu items' do
|
||||||
|
visit root_path
|
||||||
|
|
||||||
|
# Collect links
|
||||||
|
links = []
|
||||||
|
|
||||||
|
# ActiveAdmin menu structure selectors
|
||||||
|
# Top level links
|
||||||
|
all('#header ul#tabs > li > a', visible: :all).each do |a|
|
||||||
|
href = a[:href]
|
||||||
|
links << href if href && href != '#' && !href.start_with?('javascript:')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Submenu links
|
||||||
|
all('#header ul#tabs > li > ul > li > a', visible: :all).each do |a|
|
||||||
|
href = a[:href]
|
||||||
|
links << href if href && href != '#' && !href.start_with?('javascript:')
|
||||||
|
end
|
||||||
|
|
||||||
|
links.uniq!
|
||||||
|
# Remove logout to avoid ending session
|
||||||
|
links.reject! { |l| l.include?('logout') || l.include?('sign_out') }
|
||||||
|
|
||||||
|
# Ignore pages referring to Gift Cards, Lessons, Students, or Teachers
|
||||||
|
links.reject! { |l| l =~ /gift_card|lesson|student|teacher|slow_responses|jam_class|jamclass|posa|onboarder|schooluserupload/i }
|
||||||
|
|
||||||
|
puts "Testing #{links.size} menu links: #{links.inspect}"
|
||||||
|
|
||||||
|
links.each do |link|
|
||||||
|
visit link
|
||||||
|
|
||||||
|
# Check for common Rails/ActiveAdmin error indicators
|
||||||
|
if page.has_content?("We're sorry, but something went wrong") ||
|
||||||
|
page.has_content?("Exception") ||
|
||||||
|
(page.respond_to?(:status_code) && page.status_code == 500)
|
||||||
|
fail "Failed to load #{link}"
|
||||||
|
end
|
||||||
|
|
||||||
|
expect(page.status_code).to eq(200) if page.respond_to?(:status_code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,112 @@
|
||||||
|
Add a filter to the current report (link below) that lets us view: All, Organic, Advertising, or Campaign. And if Campaign is selected, add a drop down list box with Campaign strings.
|
||||||
|
|
||||||
|
https://www.jamkazam.com/admin/admin/jammers_subscription_cohorts
|
||||||
|
|
||||||
|
Add a field to the user record in the database that records if a user signup was Organic or Advertising, and if Advertising, add a field to capture/store the Campaign string.
|
||||||
|
|
||||||
|
Implement the tech to enable data-driven analysis and advertising optimization for the Meta ad network, as described in the Claude AI notes below. Basically, if we place an ads on Facebook/Instagram/etc, that ad will cause a campaign attribution string to be included in the URL a visitor uses to come to the JamKazam website (typically the Wordpress marketing site). If the user signs up for a JamKazam account, we should tag their account as described above, so that we know this account was generated from Advertising, and from the specific Campaign string in the URL for attribution. Importantly, we should also implement a conversion indicator that reports back to Meta when a user hits the conversion funnel step of Gear Setup (i.e. they have downloaded the desktop app AND then run the FTUE gear setup wizard to set up some gear in the app). We will consider this a “conversion” for the purposes of evaluating and optimizing Meta ads (and Meta will itself use this data to optimize our ROI on ad placements, given that these days AI is being used to make ads more efficient than we can manually accomplish via more traditional targeting methodologies).
|
||||||
|
|
||||||
|
Here’s my prompt to Claude: “I'm interested in testing the use of Facebook ads to promote the use of my company's app. It's a desktop app, not a mobile app. It looks like online advertising tech has changed since I last used it, and there is automated optimization for targeting the best prospective users with the best ROI on ad spend. To take advantage of this new optimization, I've read that a good way to use it with an app is to identify a success benchmark and report that benchmark back to the Facebook ads platform for each referred user that "converts" - where converts is defined as hitting that benchmark or milestone. In my case, conversion would equate to having the user download and install the app, and then complete one specified action in the app. This is more complex and time-consuming than, for example, just following an ad link to a particular page, and this process will not live within a single browser session. So I think I would need to somehow call a Facebook Ads API (or something like that) to report back a conversion success on a referred user. To do this, I think I would need some kind of unique ID for each user referred from a Facebook ad, and an understanding of the API (or URL or other tech) used to report this conversion success. Can you please help me by describing how to implement this process technically?”
|
||||||
|
|
||||||
|
And below is Claude’s answer:
|
||||||
|
|
||||||
|
You're on the right track! What you're describing is Facebook's Conversions API (formerly called Server-Side API), which is designed exactly for this scenario. Here's how to implement it:
|
||||||
|
|
||||||
|
Technical Overview
|
||||||
|
Capture Facebook Click ID (fbclid) at Landing
|
||||||
|
|
||||||
|
When someone clicks your Facebook ad, Facebook appends a fbclid parameter to the URL (e.g., yoursite.com?fbclid=ABC123). You need to:
|
||||||
|
|
||||||
|
Capture this fbclid from the URL when the user lands on your download page
|
||||||
|
|
||||||
|
Store it in a cookie or local storage (Facebook recommends a cookie named _fbc with format fb.1.timestamp.fbclid)
|
||||||
|
|
||||||
|
Also capture the _fbp cookie (Facebook Pixel browser ID) if you're using the Facebook Pixel
|
||||||
|
|
||||||
|
Pass the Click ID Through Your Download Flow
|
||||||
|
|
||||||
|
When the user downloads your app installer, you need to pass this tracking data to the app. Common approaches:
|
||||||
|
|
||||||
|
URL parameters in download link: Embed the fbclid/fbc in the download URL itself
|
||||||
|
|
||||||
|
Server-side tracking: Store the fbclid associated with the download session on your server
|
||||||
|
|
||||||
|
Installer metadata: Some installers can accept custom parameters
|
||||||
|
|
||||||
|
Store the Click ID in Your App
|
||||||
|
|
||||||
|
When your desktop app first runs after installation, capture and store the tracking identifier locally (registry, local file, or app database).
|
||||||
|
|
||||||
|
Report Conversion via Conversions API
|
||||||
|
|
||||||
|
When the user completes your target action, make an HTTPS POST request to Facebook's Conversions API:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
POST https://graph.facebook.com/v18.0/{PIXEL_ID}/events
|
||||||
|
Required parameters:
|
||||||
|
|
||||||
|
access_token: Your Conversions API access token
|
||||||
|
|
||||||
|
data: JSON array containing event data
|
||||||
|
|
||||||
|
Example payload:
|
||||||
|
|
||||||
|
json
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"event_name": "CompleteAction",
|
||||||
|
"event_time": 1702345678,
|
||||||
|
"action_source": "app",
|
||||||
|
"user_data": {
|
||||||
|
"fbc": "fb.1.1702345600.ABC123",
|
||||||
|
"fbp": "fb.1.1702340000.987654321",
|
||||||
|
"em": "hashed_email@example.com", // SHA256 hashed
|
||||||
|
"client_user_agent": "Mozilla/5.0...",
|
||||||
|
"client_ip_address": "192.168.1.1"
|
||||||
|
},
|
||||||
|
"event_source_url": "https://yoursite.com/download"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
Implementation Steps
|
||||||
|
Set up Facebook Pixel on your landing/download page (optional but recommended for attribution)
|
||||||
|
|
||||||
|
Get Conversions API credentials from Facebook Business Manager:
|
||||||
|
|
||||||
|
Your Pixel ID
|
||||||
|
|
||||||
|
Generate an access token with ads_management permission
|
||||||
|
|
||||||
|
Capture tracking data on your website:
|
||||||
|
|
||||||
|
javascript
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Capture fbclid from URL
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const fbclid = urlParams.get('fbclid');
|
||||||
|
// Store in cookie for 90 days
|
||||||
|
if (fbclid) {
|
||||||
|
const timestamp = Math.floor(Date.now() / 1000);
|
||||||
|
document.cookie = `_fbc=fb.1.${timestamp}.${fbclid}; max-age=7776000; path=/`;
|
||||||
|
}
|
||||||
|
Associate tracking data with download - perhaps by generating a unique download token that includes the fbc/fbp values
|
||||||
|
|
||||||
|
In your desktop app, when the conversion action occurs, make the API call (from your backend server, not directly from the app for security)
|
||||||
|
|
||||||
|
Important Considerations
|
||||||
|
Privacy/GDPR: Ensure you have proper consent and privacy policies
|
||||||
|
|
||||||
|
Hash PII: Email addresses and phone numbers must be SHA256 hashed
|
||||||
|
|
||||||
|
Event deduplication: Use event_id to prevent duplicate reporting
|
||||||
|
|
||||||
|
Test events: Use the Test Events feature in Facebook Events Manager during development
|
||||||
|
|
||||||
|
Attribution window: Facebook typically has a 7-day click attribution window
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddResetPasswordSentAtToUsers < ActiveRecord::Migration[4.2]
|
||||||
|
def change
|
||||||
|
add_column :users, :reset_password_sent_at, :datetime
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
class Bugsnag
|
||||||
|
def self.notify(e, &block)
|
||||||
|
# no-op
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Reference in New Issue