concerto_hardware 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +14 -0
  3. data/README.rdoc +7 -0
  4. data/Rakefile +40 -0
  5. data/app/assets/images/concerto_hardware/helpicon.png +0 -0
  6. data/app/assets/javascripts/concerto_hardware/application.js +15 -0
  7. data/app/assets/javascripts/concerto_hardware/players.js +2 -0
  8. data/app/assets/stylesheets/concerto_hardware/application.css +13 -0
  9. data/app/assets/stylesheets/concerto_hardware/players.css.scss +3 -0
  10. data/app/assets/stylesheets/scaffolds.css.scss +56 -0
  11. data/app/controllers/concerto_hardware/application_controller.rb +16 -0
  12. data/app/controllers/concerto_hardware/players_controller.rb +116 -0
  13. data/app/helpers/players_helper.rb +2 -0
  14. data/app/models/concerto_hardware/ability.rb +42 -0
  15. data/app/models/concerto_hardware/player.rb +169 -0
  16. data/app/views/concerto_hardware/players/_form.html.erb +77 -0
  17. data/app/views/concerto_hardware/players/_show_body.html.erb +45 -0
  18. data/app/views/concerto_hardware/players/_show_header.html.erb +17 -0
  19. data/app/views/concerto_hardware/players/edit.html.erb +9 -0
  20. data/app/views/concerto_hardware/players/index.html.erb +49 -0
  21. data/app/views/concerto_hardware/players/new.html.erb +8 -0
  22. data/app/views/concerto_hardware/players/show.html.erb +8 -0
  23. data/app/views/concerto_hardware/screens/_screen_link.html.erb +49 -0
  24. data/config/locales/en.yml +29 -0
  25. data/config/routes.rb +21 -0
  26. data/db/migrate/20121220000000_create_concerto_hardware_players.rb +12 -0
  27. data/db/migrate/20131127201048_add_updates_to_concerto_hardware_players.rb +6 -0
  28. data/lib/concerto-hardware.rb +6 -0
  29. data/lib/concerto_hardware/engine.rb +68 -0
  30. data/lib/concerto_hardware/version.rb +3 -0
  31. data/test/fixtures/players.yml +13 -0
  32. data/test/functional/players_controller_test.rb +49 -0
  33. data/test/unit/helpers/players_helper_test.rb +4 -0
  34. data/test/unit/player_test.rb +7 -0
  35. metadata +109 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2a8507b255441f28f56a5053cb63e0880e17d40f
4
+ data.tar.gz: 1bdd8dff01bfb754b32b7cc9e2538f573bebc030
5
+ SHA512:
6
+ metadata.gz: a67113f0fd71723c6f450a4d651de1682e35465c1bd3113f23ea12e6d45e8588a4d092f7807398ef480c81b5e9a4efbd09491fa666d20ed2acdab78398ab7f16
7
+ data.tar.gz: e5490b2f6b25587619c9760bb8ea3194a0eca5bb49de639a2b7949c74c342d4c16495a8100bdaef7fb167e0b441ee4260b299496062d2440a3218111e000f4b0
data/LICENSE ADDED
@@ -0,0 +1,14 @@
1
+ Copyright 2011 Concerto Authors
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
14
+
data/README.rdoc ADDED
@@ -0,0 +1,7 @@
1
+ A Rails Engine for managing Bandshell-powered Concerto hardware
2
+
3
+ To use this engine, add the following to the Concerto Gemfile:
4
+ gem 'concerto-hardware', :path => '/path/to/concerto-hardware'
5
+
6
+ To create the proper migrations, use:
7
+ rails generate concerto-hardware
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ConcertoHardware'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,15 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,3 @@
1
+ // Place all the styles related to the Players controller here.
2
+ // They will automatically be included in application.css.
3
+ // You can use Sass (SCSS) here: http://sass-lang.com/
@@ -0,0 +1,56 @@
1
+ body {
2
+ background-color: #fff;
3
+ color: #333;
4
+ font-family: verdana, arial, helvetica, sans-serif;
5
+ font-size: 13px;
6
+ line-height: 18px; }
7
+
8
+ p, ol, ul, td {
9
+ font-family: verdana, arial, helvetica, sans-serif;
10
+ font-size: 13px;
11
+ line-height: 18px; }
12
+
13
+ pre {
14
+ background-color: #eee;
15
+ padding: 10px;
16
+ font-size: 11px; }
17
+
18
+ a {
19
+ color: #000;
20
+ &:visited {
21
+ color: #666; }
22
+ &:hover {
23
+ color: #fff;
24
+ background-color: #000; } }
25
+
26
+ div {
27
+ &.field, &.actions {
28
+ margin-bottom: 10px; } }
29
+
30
+ #notice {
31
+ color: green; }
32
+
33
+ .field_with_errors {
34
+ padding: 2px;
35
+ background-color: red;
36
+ display: table; }
37
+
38
+ #error_explanation {
39
+ width: 450px;
40
+ border: 2px solid red;
41
+ padding: 7px;
42
+ padding-bottom: 0;
43
+ margin-bottom: 20px;
44
+ background-color: #f0f0f0;
45
+ h2 {
46
+ text-align: left;
47
+ font-weight: bold;
48
+ padding: 5px 5px 5px 15px;
49
+ font-size: 12px;
50
+ margin: -7px;
51
+ margin-bottom: 0px;
52
+ background-color: #c00;
53
+ color: #fff; }
54
+ ul li {
55
+ font-size: 12px;
56
+ list-style: square; } }
@@ -0,0 +1,16 @@
1
+ module ConcertoHardware
2
+ # Congratulations! You've found this engine's secret sauce.
3
+ # In a regular isolated engine, the engine's ApplicationController
4
+ # inherits from ActionController::Base. We're using the the main app's
5
+ # ApplicationController, making the isolation a little less strict.
6
+ # For example, we get the layout from the main Concerto app.
7
+ # Note that links back to the main application will need to directly
8
+ # reference the main_app router.
9
+ class ApplicationController < ::ApplicationController
10
+ def current_ability
11
+ # Use the Ability class defined in this engine's namespace.
12
+ # It is designed to also include the rules from the main app.
13
+ @current_ability ||= Ability.new(current_accessor)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,116 @@
1
+ require_dependency "concerto_hardware/application_controller"
2
+
3
+ module ConcertoHardware
4
+ class PlayersController < ApplicationController
5
+ unloadable #marks this class for reloading in between requests
6
+ #include routes.named_routes.helpers
7
+ before_filter :screen_api
8
+
9
+ # GET /players
10
+ # GET /players.json
11
+ def index
12
+ @players = Player.all
13
+ auth!
14
+
15
+ respond_to do |format|
16
+ format.html # index.html.erb
17
+ format.json { render :json => @players }
18
+ end
19
+ end
20
+
21
+ # GET /players/1
22
+ # GET /players/1.json
23
+ # GET /players/by_screen/2(.json)
24
+ # GET /players/current(.json)
25
+ def show
26
+ if params.has_key? :screen_id
27
+ screen_id = params[:screen_id]
28
+ @player = ConcertoHardware::Player.find_by_screen_id!(screen_id)
29
+ elsif params.has_key? :id
30
+ @player = Player.find(params[:id])
31
+ else # Return data about the logged-in screen
32
+ if current_screen.nil?
33
+ raise ActiveRecord::RecordNotFound, "Couldn't find an authenticated screen."
34
+ else
35
+ @player = Player.find_by_screen_id!(current_screen.id)
36
+ end
37
+ end
38
+ auth!
39
+
40
+ respond_to do |format|
41
+ format.html # show.html.erb
42
+ format.json { render :json => @player }
43
+ end
44
+ end
45
+
46
+ # GET /players/new
47
+ # GET /players/new.json
48
+ def new
49
+ @player = Player.new
50
+ if !params[:screen_id].nil?
51
+ # TODO: Error handling
52
+ @player.screen = Screen.find(params[:screen_id])
53
+ end
54
+ auth!
55
+
56
+ respond_to do |format|
57
+ format.html # new.html.erb
58
+ format.json { render :json => @player }
59
+ end
60
+ end
61
+
62
+ # GET /players/1/edit
63
+ def edit
64
+ @player = Player.find(params[:id])
65
+ auth!
66
+ end
67
+
68
+ # POST /players
69
+ # POST /players.json
70
+ def create
71
+ @player = Player.new(params[:player])
72
+ auth!
73
+
74
+ respond_to do |format|
75
+ if @player.save
76
+ format.html { redirect_to [hardware, @player], :notice => 'Player was successfully created.' }
77
+ format.json { render :json => @player, :status => :created, :location => @player }
78
+ else
79
+ format.html { render :action => "new" }
80
+ format.json { render :json => @player.errors, :status => :unprocessable_entity }
81
+ end
82
+ end
83
+ end
84
+
85
+ # PUT /players/1
86
+ # PUT /players/1.json
87
+ def update
88
+ @player = Player.find(params[:id])
89
+ auth!
90
+
91
+ respond_to do |format|
92
+ if @player.update_attributes(params[:player])
93
+ format.html { redirect_to [hardware, @player], :notice => 'Player was successfully updated.' }
94
+ format.json { head :no_content }
95
+ else
96
+ format.html { render :action => "edit" }
97
+ format.json { render :json => @player.errors, :status => :unprocessable_entity }
98
+ end
99
+ end
100
+ end
101
+
102
+ # DELETE /players/1
103
+ # DELETE /players/1.json
104
+ def destroy
105
+ @player = Player.find(params[:id])
106
+ auth!
107
+ @player.destroy
108
+
109
+ respond_to do |format|
110
+ format.html { redirect_to hardware.players_url }
111
+ format.json { head :no_content }
112
+ end
113
+ end
114
+ end
115
+
116
+ end
@@ -0,0 +1,2 @@
1
+ module PlayersHelper
2
+ end
@@ -0,0 +1,42 @@
1
+ module ConcertoHardware
2
+ # The enigne's Ability class simply extends the existing Ability
3
+ # class for the application. We rely on the fact that it already
4
+ # includes CanCan::Ability.
5
+ class Ability < ::Ability
6
+ def initialize(accessor)
7
+ super # Get the main application's rules
8
+ # The main app will delegate to user_abilities, etc.
9
+ # Note the inherited rules give Admins rights to manage everything
10
+ end
11
+
12
+ def user_abilities(user)
13
+ super # Get the user rules from the main applications
14
+
15
+ # For debugging you may want to make all Players readable
16
+ # can :read, Player
17
+
18
+ # Let's defer to Concerto's rules for Screen Permissions.
19
+ # This will apply to both users browsing the hardware info
20
+ # as well as public screens in public instances accessing their data.
21
+ # - Right now, only admins can create players (TODO)
22
+ # - It may become desirable in the future for reading to be
23
+ # restricted or curtailed if a lot of sensitive data is stored here.
24
+ can :read, Player do |player|
25
+ !player.screen.nil? and can? :read, player.screen
26
+ end
27
+ can :update, Player do |player|
28
+ !player.screen.nil? and can? :update, player.screen
29
+ end
30
+ can :delete, Player do |player|
31
+ !player.screen.nil? and can? :delete, player.screen
32
+ end
33
+ end # user_abilities
34
+
35
+ def screen_abilities(screen)
36
+ # A logged-in screen can read its own Player information.
37
+ can :read, Player, :screen_id => screen.id
38
+ # In the future it may also need to write reporting data, so
39
+ # this will need to be expanded.
40
+ end # screen_abilities
41
+ end # class Ability
42
+ end # module ConcertoHardware
@@ -0,0 +1,169 @@
1
+ module ConcertoHardware
2
+ class Player < ActiveRecord::Base
3
+ belongs_to :screen
4
+
5
+ def self.time_accessor(*syms)
6
+ syms.each do |sym|
7
+ attr_accessor sym
8
+ composed_of sym,
9
+ :class_name => 'Time',
10
+ :mapping => [sym.to_s, "to_s"],
11
+ :constructor => Proc.new{ |item| item },
12
+ :converter => Proc.new{ |item| item }
13
+ end
14
+ end
15
+
16
+ # Hack to get the multiparameter virtual attributes working
17
+ def self.create_time_zone_conversion_attribute?(name, column)
18
+ column.nil? ? true : super
19
+ end
20
+
21
+ validates_associated :screen
22
+ validates_presence_of :screen_id
23
+ validates_presence_of :screen, :message => 'must exist'
24
+ validates_uniqueness_of :screen_id
25
+
26
+ time_accessor :wkday_on_time, :wkday_off_time
27
+ time_accessor :wknd_on_time, :wknd_off_time
28
+ time_accessor :party_time
29
+ attr_accessor :wknd_disable
30
+ attr_accessor :force_off
31
+
32
+ after_initialize :default_values
33
+ after_find :retrieve_screen_on_off
34
+ before_save :process_screen_on_off
35
+
36
+ def default_values
37
+ self.screen_on_off ||= [
38
+ {
39
+ :action => "on",
40
+ :wkday => "12345", # M-F
41
+ :time_after => "07:00",
42
+ :time_before => "20:00"
43
+ },
44
+ {
45
+ :action => "on",
46
+ :wkday => "06", # Sun, Sat
47
+ :time_after => "09:00",
48
+ :time_before => "20:00"
49
+ }
50
+ ].to_json
51
+ retrieve_screen_on_off #populate the virtual attributes.
52
+ end
53
+
54
+ # Take screen controls from the form and store them
55
+ # in a standard format that the player will understand.
56
+ # https://github.com/concerto/concerto-hardware/
57
+ # wiki/Player-API#screen-onoff-times
58
+ # TODO: Formatting for datetimes
59
+ # TODO: Validation
60
+ # TODO: TIMEZONES
61
+ def process_screen_on_off
62
+ ruleset = []
63
+ unless self.wkday_on_time.nil? or self.wkday_off_time.nil?
64
+ ruleset << {
65
+ :action => "on",
66
+ :wkday => "12345", # M-F
67
+ :time_after => fmt_time(wkday_on_time), # "07:00"
68
+ :time_before => fmt_time(wkday_off_time), # "23:00"
69
+ }
70
+ end
71
+ unless self.wknd_on_time.nil? or self.wknd_off_time.nil?
72
+ ruleset << {
73
+ :action => self.wknd_disable=="1" ? "off" : "on",
74
+ :wkday => "06", # Sun, Sat
75
+ :time_after => fmt_time(wknd_on_time), # "07:00"
76
+ :time_before => fmt_time(wknd_off_time), # "23:00"
77
+ }
78
+ end
79
+ if self.force_off == "1"
80
+ ruleset << {
81
+ :action => "off",
82
+ :date => Time.now.strftime("%Y-%m-%d")
83
+ }
84
+ end
85
+ if ruleset.empty? && self.screen_on_off.blank?
86
+ ruleset << {
87
+ :action => "on"
88
+ }
89
+ end
90
+
91
+ self.screen_on_off = ruleset.to_json unless ruleset.empty?
92
+ end
93
+
94
+ # This is a very limited parsing of the on/off rules
95
+ # Pretty much it will only work on rulesets created by the methods
96
+ # in this model. The format is very flexible, but this model only
97
+ # supports 3 simple rules.
98
+ def retrieve_screen_on_off
99
+ return nil if screen_on_off.blank?
100
+
101
+ ruleset = ActiveSupport::JSON.decode(self.screen_on_off)
102
+ ruleset.each do |rule|
103
+ if rule.has_key? 'action'
104
+ if rule.has_key? 'wkday' and rule['wkday']=='12345' and
105
+ rule['action']='on'
106
+ if rule.has_key? 'time_after' and rule.has_key? 'time_before'
107
+ self.wkday_on_time = Time.parse(rule['time_after'])
108
+ self.wkday_off_time = Time.parse(rule['time_before'])
109
+ end
110
+ end # weekday
111
+ if rule.has_key? 'wkday' and rule['wkday']=='06'
112
+ if rule.has_key? 'time_after' and rule.has_key? 'time_before'
113
+ self.wknd_on_time = Time.parse(rule['time_after'])
114
+ self.wknd_off_time = Time.parse(rule['time_before'])
115
+ end
116
+ self.wknd_disable = (rule['action'] != 'on')
117
+ end # weekend
118
+ if rule.has_key? 'date'
119
+ if rule['date'] == Time.now.strftime("%Y-%m-%d")
120
+ self.force_off = (rule['action'] != 'on')
121
+ end
122
+ end # force off rules
123
+ end # has an action
124
+ end # each rule
125
+ end # retrive_screen_on_off
126
+
127
+ # Relies on retrive_screen_on_off having been called at load.
128
+ def screen_on_off_valid
129
+ !(
130
+ wkday_on_time.nil? or wkday_off_time.nil? or
131
+ wknd_on_time.nil? or wknd_off_time.nil? or wknd_disable.nil?
132
+ )
133
+ end
134
+
135
+ # Returns an array of strings describing the screen's behavior.
136
+ # Relies on retrive_screen_on_off having been called at load.
137
+ def describe_screen_on_off
138
+ rules = []
139
+ if self.screen_on_off.blank?
140
+ rules << "On/off times not configured. The screen will always be on."
141
+ elsif !screen_on_off_valid
142
+ rules << "On/off rules are invalid. Edit and save the Player to fix."
143
+ else
144
+ rules << "Weekdays: on at "+fmt_time(wkday_on_time, "%l:%M%P")+", "+
145
+ "off at "+fmt_time(wkday_off_time, "%l:%M%P")+"."
146
+ if wknd_disable
147
+ rules << "Weekends: off."
148
+ else
149
+ rules << "Weekends: on at "+fmt_time(wknd_on_time, "%l:%M%P")+", "+
150
+ "off at "+fmt_time(wknd_off_time, "%l:%M%P")+"."
151
+ end
152
+ if force_off
153
+ rules << "Manual override: screen will be off until midnight tonight."
154
+ end
155
+ end
156
+ rules
157
+ end
158
+
159
+ def fmt_time(timeobj, fmt = "%H:%M")
160
+ if !timeobj.nil?
161
+ if timeobj.is_a?(String)
162
+ timeobj = Time.parse(timeobj)
163
+ end
164
+ timeobj.strftime(fmt).strip
165
+ end
166
+ end
167
+
168
+ end # class Player
169
+ end # module ConcertoHardware
@@ -0,0 +1,77 @@
1
+ <%= form_for(@player) do |f| %>
2
+ <%= render :partial => "layouts/errors", :object => @player %>
3
+
4
+ <fieldset>
5
+ <legend><span><%= t('.provide_details') %></span></legend>
6
+
7
+ <%= f.hidden_field :screen_id %>
8
+
9
+ <div class="clearfix">
10
+ <%= f.label :ip_address %>
11
+ <div class="input">
12
+ <%= f.text_field :ip_address %>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="clearfix">
17
+ <div class="input checkbox">
18
+ <ul class="inputs-list">
19
+ <li><%= f.check_box :activated %> <%= f.label :activated %></li>
20
+ </ul>
21
+ </div>
22
+ </div>
23
+
24
+ <!-- Screen on/off times. Good luck, UI designer. -->
25
+ <div class="clearfix">
26
+ <%= f.label :wkday_on_time %>
27
+ <div class="input-prepend">
28
+ <span class="add-on"><%= t(:at) %></span>
29
+ <%= f.text_field(:wkday_on_time, :maxlength => 20, :class => "timefield input-small", :value => @player.fmt_time(@player.wkday_on_time, "%l:%M%P") || ConcertoConfig[:content_default_start_time]) %>
30
+ </div>
31
+ </div>
32
+ <div class="clearfix">
33
+ <%= f.label :wkday_off_time %>
34
+ <div class="input-prepend">
35
+ <span class="add-on"><%= t(:at) %></span>
36
+ <%= f.text_field(:wkday_off_time, :maxlength => 20, :class => "timefield input-small", :value => @player.fmt_time(@player.wkday_off_time, "%l:%M%P") || ConcertoConfig[:content_default_end_time]) %>
37
+ </div>
38
+ </div>
39
+
40
+ <div class="clearfix">
41
+ <div class="input checkbox">
42
+ <ul class="inputs-list">
43
+ <li><%= f.check_box :wknd_disable %> <%= f.label :wknd_disable %></li>
44
+ </ul>
45
+ </div>
46
+ </div>
47
+ <div class="clearfix">
48
+ <%= f.label :wknd_on_time %>
49
+ <div class="input-prepend">
50
+ <span class="add-on"><%= t(:at) %></span>
51
+ <%= f.text_field(:wknd_on_time, :maxlength => 20, :class => "timefield input-small", :value => @player.fmt_time(@player.wknd_on_time, "%l:%M%P") || ConcertoConfig[:content_default_start_time]) %>
52
+ </div>
53
+ </div>
54
+ <div class="clearfix">
55
+ <%= f.label :wknd_off_time %>
56
+ <div class="input-prepend">
57
+ <span class="add-on"><%= t(:at) %></span>
58
+ <%= f.text_field(:wknd_off_time, :maxlength => 20, :class => "timefield input-small", :value => @player.fmt_time(@player.wknd_off_time, "%l:%M%P") || ConcertoConfig[:content_default_end_time]) %>
59
+ </div>
60
+ </div>
61
+
62
+ <div class="clearfix">
63
+ <div class="input checkbox">
64
+ <ul class="inputs-list">
65
+ <li><%= f.check_box :force_off %> <%= f.label :force_off %> (temporarily turns off the screen until midnight tonight)</li>
66
+ </ul>
67
+ </div>
68
+ </div>
69
+ <p><em>All time fields should be entered in 24 hour format (HH:mm), for example 23:30 for 11:30 PM.</em></p>
70
+ </fieldset>
71
+
72
+ <div class="submit_bar actions">
73
+ <%= f.submit button_text, :class => "btn btn-primary" %>
74
+ <%= link_to t(:cancel), (@player.persisted? ? player_path : players_path), :class => "btn" %>
75
+ </div>
76
+
77
+ <% end %>
@@ -0,0 +1,45 @@
1
+ <div class="row-fluid">
2
+ <div class="span12">
3
+ <div class="default-padding">
4
+ <div class="subblock">
5
+ <div class="default-padding clearfix">
6
+
7
+ <p id="notice"><%= notice %></p>
8
+
9
+ <p>
10
+ <b>Ip address:</b>
11
+ <%= @player.ip_address %>
12
+ </p>
13
+
14
+ <p>
15
+ <b>Screen:</b>
16
+ <% if @player.screen.nil? %>
17
+ Not found
18
+ <% else %>
19
+ <% if can? :read, @player.screen %>
20
+ <%= link_to @player.screen.name, [main_app, @player.screen] %>
21
+ <% else %>
22
+ <%= @player.screen.name %>
23
+ <% end %>
24
+ <% end %>
25
+ </p>
26
+
27
+ <p>
28
+ <b>Activated:</b>
29
+ <%= @player.activated %>
30
+ </p>
31
+
32
+ <p>
33
+ <b>Screen on/off times:</b>
34
+ <ul>
35
+ <% @player.describe_screen_on_off.each do |rule| %>
36
+ <li><%=rule%></li>
37
+ <% end %>
38
+ </ul>
39
+ </p>
40
+
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
@@ -0,0 +1,17 @@
1
+ <div class="viewblock-header_right">
2
+ <div class="button-padding">
3
+ <% if can? :update, @player %>
4
+ <%= link_to t(:edit_model, :model => ConcertoHardware::Player.model_name.human), edit_player_path(@player), :class => "btn" %>
5
+ <% end %>
6
+ <% if can? :delete, @player %>
7
+ <%= link_to t(:destroy_model, :model => ConcertoHardware::Player.model_name.human), @player, :data => { :confirm => t(:are_you_sure_delete_model_key, :model => ConcertoHardware::Player.model_name.human, :key => @player.screen.name) }, :method => :delete, :class => "btn" %>
8
+ <% end %>
9
+ </div>
10
+ </div>
11
+
12
+ <div class="default-padding">
13
+ <h1>
14
+ <%= link_to t(:all_model, :model => ConcertoHardware::Player.model_name.human.pluralize), players_path %> &gt;
15
+ <%= @player.screen.name + " Player" %>
16
+ </h1>
17
+ </div>
@@ -0,0 +1,9 @@
1
+ <section class="viewblock">
2
+ <header class="viewblock-header">
3
+ <h1 class="default-padding"><%= t('.header', :screen => "#{@player.screen.name if !@player.screen.nil?}") %></h1>
4
+ </header>
5
+ <div class="viewblock-cont">
6
+ <%= render :partial => 'form', :locals => { :button_text => t(:update_model, :model => ConcertoHardware::Player.model_name.human) } %>
7
+ </div>
8
+ </section>
9
+
@@ -0,0 +1,49 @@
1
+ <section class="viewblock">
2
+ <header class="viewblock-header">
3
+ <div class="viewblock-header_right">
4
+ <div class="button-padding">
5
+ <% if can? :create, ConcertoHardware::Player %>
6
+ <%= link_to t(:new_model, :model => ConcertoHardware::Player.model_name.human), new_player_path, :class => "btn" %>
7
+ <% end %>
8
+ </div>
9
+ </div>
10
+ <h1 class="default-padding">
11
+ <%= t(:all_model, :model => ConcertoHardware::Player.model_name.human.pluralize) %>
12
+ </h1>
13
+ </header>
14
+ <div class="viewblock-cont">
15
+ <table>
16
+ <tr>
17
+ <th><%= ConcertoHardware::Player.human_attribute_name(:ip_address) %></th>
18
+ <th><%= ConcertoHardware::Player.human_attribute_name(:screen_id) %></th>
19
+ <th><%= ConcertoHardware::Player.human_attribute_name(:activated) %></th>
20
+ <th>Actions</th>
21
+ </tr>
22
+
23
+ <% @players.each do |player| %>
24
+ <tr>
25
+ <td><%= player.ip_address %></td>
26
+ <td><%= player.screen.name %></td>
27
+ <td><%= player.activated %></td>
28
+
29
+ <td>
30
+ <% if can? :read, player %>
31
+ <%= link_to t(:show, :model => ConcertoHardware::Player.model_name.human), player, :class => "btn" %>
32
+ <% end %>
33
+ <% if can? :edit, player %>
34
+ <%= link_to t(:edit, :model => ConcertoHardware::Player.model_name.human), edit_player_path(player), :class => "btn" %>
35
+ <% end %>
36
+ <% if can? :delete, player %>
37
+ <%= link_to t(:destroy, :model => ConcertoHardware::Player), player,
38
+ :data => { :confirm => t(:are_you_sure_delete_model_key, :model =>ConcertoHardware::Player.model_name.human, :key => player.screen.name) }, :method => :delete, :class => "btn" %>
39
+ <% end %>
40
+ </td>
41
+ </tr>
42
+ <% end %>
43
+ </table>
44
+ </div>
45
+ </section>
46
+ <div class="default-padding">
47
+ <%# for demo purposes: %>
48
+ Note: the poll interval is currently set to <%=ConcertoConfig[:poll_interval]%> seconds.
49
+ </div>
@@ -0,0 +1,8 @@
1
+ <section class="viewblock">
2
+ <header class="viewblock-header">
3
+ <h1 class="default-padding"><%= t('.header', :screen => "#{@player.screen.name if !@player.screen.nil?}") %></h1>
4
+ </header>
5
+ <div class="viewblock-cont">
6
+ <%= render :partial => 'form', :locals => { :button_text => t(:create_model, :model => ConcertoHardware::Player.model_name.human) } %>
7
+ </div>
8
+ </section>
@@ -0,0 +1,8 @@
1
+ <section class="viewblock">
2
+ <header class="viewblock-header" id="screens-header">
3
+ <%= render :partial => "show_header" %>
4
+ </header>
5
+ <div class="viewblock-cont">
6
+ <%= render :partial => "show_body" %>
7
+ </div>
8
+ </section>
@@ -0,0 +1,49 @@
1
+ <%# This partial will be read in the context of the main Concerto app. %>
2
+ <%# URL helpers require use of the 'hardware' routing helper. %>
3
+ <%# ConcertoHardware authorization rules are automatically provided. %>
4
+
5
+ <section class="viewblock">
6
+ <header class="viewblock-header">
7
+ <div class="viewblock-header_right">
8
+ <div class="button-padding">
9
+ <% if @player.nil? %>
10
+ <%= link_to 'Add Player', hardware.new_player_path(:screen_id => @screen.id), :class => "btn" %>
11
+ <% else %>
12
+ <% if can? :update, @player %>
13
+ <%= link_to 'Edit Player', hardware.edit_player_path(@player), :class => "btn" %>
14
+ <% end %>
15
+ <% end %>
16
+ </div>
17
+ </div>
18
+
19
+ <div class="default-padding">
20
+ <h1><%= t('.concerto_player') %></h1>
21
+ </div>
22
+ </header>
23
+ <div class="viewblock-cont">
24
+ <div class="default-padding">
25
+ <% if @player.nil? %>
26
+ <div class="alert alert-block alert-zero">
27
+ <p class="alert-heading"><%= t('.no_player_header') %></p>
28
+ <p><%= t('.no_player_msg') %></p>
29
+ <% if can? :update, @player %>
30
+ <%= link_to t('.add_player_msg'), hardware.new_player_path(:screen_id => @screen.id) %>
31
+ <% end %>
32
+ </div>
33
+ <% else %>
34
+ <h3><%= @screen.name + " Player" %></h3>
35
+ <p><b><%= @player.activated ? t('.activated') : t('.not_activated') %></b></p>
36
+ <br />
37
+ <% if can? :read, @player %>
38
+ <p><b>IP Address:</b> <%= @player.ip_address %></p>
39
+ <p><b>Screen On/Off Times:</b></p>
40
+ <ul>
41
+ <% @player.describe_screen_on_off.each do |rule| %>
42
+ <li><%= rule %></li>
43
+ <% end %>
44
+ </ul>
45
+ <% end %>
46
+ <% end %>
47
+ </div>
48
+ </div>
49
+ </section>
@@ -0,0 +1,29 @@
1
+ en:
2
+ activerecord:
3
+ models:
4
+ concerto_hardware/player: 'Player'
5
+ attributes:
6
+ concerto_hardware/player:
7
+ activated: 'Active'
8
+ ip_address: 'IP Address'
9
+ wkday_on_time: 'Weekday turn On time'
10
+ wkday_off_time: 'Weekday turn Off time'
11
+ wknd_disable: 'Disable on weekends'
12
+ wknd_on_time: 'Weekend turn On time'
13
+ wknd_off_time: 'Weekend turn Off time'
14
+
15
+ concerto_hardware:
16
+ players:
17
+ new:
18
+ header: 'New %{screen} Player'
19
+ edit:
20
+ header: 'Edit %{screen} Player'
21
+
22
+ screens:
23
+ screen_link:
24
+ concerto_player: "Concerto Player"
25
+ no_player_header: "No Player"
26
+ no_player_msg: "A Concerto Player has not yet been connected to this screen."
27
+ add_player_msg: "Specify a Player manually."
28
+ activated: "Activated"
29
+ not_activated: "Not Activated"
data/config/routes.rb ADDED
@@ -0,0 +1,21 @@
1
+ # The routing is configured such that it does its own routing.
2
+ # This means the central application will specifically mount
3
+ # the engine to make the routes available in a certain sub-url,
4
+ # for example one route would look like
5
+ # /hardware/players/new
6
+ ConcertoHardware::Engine.routes.draw do
7
+ # Just a test welcome page, we'll replace this with something
8
+ # more useful later.
9
+ root :to => proc { |env| [200, {}, ["Welcome to the hardware plugin!"]] }
10
+
11
+ # Since we have an isolated namespace, routes are automaticaly scoped
12
+ # to the ConcertoHardware module.
13
+ resources :players do
14
+ collection do
15
+ # Look up a player based on the screen ID.
16
+ match 'by_screen/:screen_id' => ConcertoHardware::PlayersController.action(:show)
17
+ # Show the player associated with the logged in screen.
18
+ match 'current' => ConcertoHardware::PlayersController.action(:show)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ class CreateConcertoHardwarePlayers < ActiveRecord::Migration
2
+ def change
3
+ create_table :concerto_hardware_players do |t|
4
+ t.string :secret
5
+ t.string :ip_address
6
+ t.integer :screen_id
7
+ t.boolean :activated
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ class AddUpdatesToConcertoHardwarePlayers < ActiveRecord::Migration
2
+ def change
3
+ remove_column :concerto_hardware_players, :secret
4
+ add_column :concerto_hardware_players, :screen_on_off, :string
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ require "concerto_hardware/engine"
2
+
3
+ module ConcertoHardware
4
+ # There is no important functionality at the module level.
5
+ # Plugin configuration happens in engine.rb.
6
+ end
@@ -0,0 +1,68 @@
1
+ module ConcertoHardware
2
+ class Engine < ::Rails::Engine
3
+ # Isolated Namespace enforces healthy separation from the Concerto app.
4
+ # Nothing else should come before this call in this class.
5
+ isolate_namespace ConcertoHardware
6
+
7
+ # The engine name will be the name of the class
8
+ # that contains the URL helpers for our routes.
9
+ # This must come after isolate_namespace!
10
+ engine_name 'hardware'
11
+
12
+ # Define plugin information for the Concerto application to read.
13
+ # Do not modify @plugin_info outside of this static configuration block.
14
+ def plugin_info(plugin_info_class)
15
+ @plugin_info ||= plugin_info_class.new do
16
+ # Make the engine's controller accessible at /hardware
17
+ add_route("hardware", ConcertoHardware::Engine)
18
+
19
+ # Initialize configuration settings with a description and a default.
20
+ # Administrators can change the value through the Concerto dashboard.
21
+ add_config("poll_interval", "60",
22
+ :value_type => "integer",
23
+ :category => "System",
24
+ :seq_no => 999,
25
+ :description => "Client hardware polling interval in seconds")
26
+
27
+ # Some code to run at app boot
28
+ init do
29
+ Rails.logger.info "ConcertoHardware: Initialization code is running"
30
+ end
31
+
32
+ # The following hooks allow integration into the main Concerto app
33
+ # at the controller and view levels.
34
+
35
+ add_controller_hook "ScreensController", :show, :before do
36
+ @player = Player.find_by_screen_id(@screen.id)
37
+ end
38
+
39
+ add_controller_hook "ScreensController", :change, :before do
40
+ Rails.logger.info "concerto-hardware: screen change callback"
41
+ if @screen.auth_in_progress? # have a temp token to look at
42
+ if Player.where(:screen_id => @screen.id).count == 0 # No existing player
43
+ if ((@screen.temp_token.length > Screen::TEMP_TOKEN_LENGTH) and
44
+ (@screen.temp_token[Screen::TEMP_TOKEN_LENGTH].downcase == "s"))
45
+ # Okay, we have a legit player situation.
46
+ Rails.logger.info "concerto-hardware: creating Player for the new Screen!"
47
+ flash[:notice] ||= ""
48
+ player = Player.new
49
+ player.screen_id = @screen.id
50
+ player.activated = true
51
+ if player.save
52
+ Rails.logger.info " Success!"
53
+ #flash[:notice] << " A player hardware profile was automatically created!"
54
+ # TODO: User notification.
55
+ else
56
+ Rails.logger.info " Failed."
57
+ #flash[:notice] << " We could not create a player hardware profile, however."
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ add_view_hook "ScreensController", :screen_details, :partial => "concerto_hardware/screens/screen_link"
65
+ end
66
+ end
67
+ end # class Engine
68
+ end # module ConcertoHardware
@@ -0,0 +1,3 @@
1
+ module ConcertoHardware
2
+ VERSION = "0.0.5"
3
+ end
@@ -0,0 +1,13 @@
1
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
2
+
3
+ one:
4
+ secret: MyString
5
+ ip_address: MyString
6
+ screen_id: 1
7
+ activated: false
8
+
9
+ two:
10
+ secret: MyString
11
+ ip_address: MyString
12
+ screen_id: 1
13
+ activated: false
@@ -0,0 +1,49 @@
1
+ require 'test_helper'
2
+
3
+ class PlayersControllerTest < ActionController::TestCase
4
+ setup do
5
+ @player = players(:one)
6
+ end
7
+
8
+ test "should get index" do
9
+ get :index
10
+ assert_response :success
11
+ assert_not_nil assigns(:players)
12
+ end
13
+
14
+ test "should get new" do
15
+ get :new
16
+ assert_response :success
17
+ end
18
+
19
+ test "should create player" do
20
+ assert_difference('Player.count') do
21
+ post :create, player: @player.attributes
22
+ end
23
+
24
+ assert_redirected_to player_path(assigns(:player))
25
+ end
26
+
27
+ test "should show player" do
28
+ get :show, id: @player
29
+ assert_response :success
30
+ end
31
+
32
+ test "should get edit" do
33
+ get :edit, id: @player
34
+ assert_response :success
35
+ end
36
+
37
+ test "should update player" do
38
+ put :update, id: @player, player: @player.attributes
39
+ assert_redirected_to player_path(assigns(:player))
40
+ end
41
+
42
+ test "should destroy player" do
43
+ assert_difference('Player.count', -1) do
44
+ delete :destroy, id: @player
45
+ end
46
+
47
+ assert_redirected_to players_path
48
+ end
49
+ end
@@ -0,0 +1,4 @@
1
+ require 'test_helper'
2
+
3
+ class PlayersHelperTest < ActionView::TestCase
4
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class PlayerTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: concerto_hardware
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.5
5
+ platform: ruby
6
+ authors:
7
+ - Concerto Team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.9
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.9
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: A Rails Engine for managing Bandshell-powered Concerto hardware
42
+ email:
43
+ - team@concerto-signage.org
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - app/assets/images/concerto_hardware/helpicon.png
49
+ - app/assets/javascripts/concerto_hardware/application.js
50
+ - app/assets/javascripts/concerto_hardware/players.js
51
+ - app/assets/stylesheets/concerto_hardware/application.css
52
+ - app/assets/stylesheets/concerto_hardware/players.css.scss
53
+ - app/assets/stylesheets/scaffolds.css.scss
54
+ - app/controllers/concerto_hardware/application_controller.rb
55
+ - app/controllers/concerto_hardware/players_controller.rb
56
+ - app/helpers/players_helper.rb
57
+ - app/models/concerto_hardware/ability.rb
58
+ - app/models/concerto_hardware/player.rb
59
+ - app/views/concerto_hardware/players/_form.html.erb
60
+ - app/views/concerto_hardware/players/_show_body.html.erb
61
+ - app/views/concerto_hardware/players/_show_header.html.erb
62
+ - app/views/concerto_hardware/players/edit.html.erb
63
+ - app/views/concerto_hardware/players/index.html.erb
64
+ - app/views/concerto_hardware/players/new.html.erb
65
+ - app/views/concerto_hardware/players/show.html.erb
66
+ - app/views/concerto_hardware/screens/_screen_link.html.erb
67
+ - config/locales/en.yml
68
+ - config/routes.rb
69
+ - db/migrate/20121220000000_create_concerto_hardware_players.rb
70
+ - db/migrate/20131127201048_add_updates_to_concerto_hardware_players.rb
71
+ - lib/concerto-hardware.rb
72
+ - lib/concerto_hardware/engine.rb
73
+ - lib/concerto_hardware/version.rb
74
+ - LICENSE
75
+ - Rakefile
76
+ - README.rdoc
77
+ - test/fixtures/players.yml
78
+ - test/functional/players_controller_test.rb
79
+ - test/unit/helpers/players_helper_test.rb
80
+ - test/unit/player_test.rb
81
+ homepage: http://concerto-signage.org
82
+ licenses:
83
+ - Apache-2.0
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.0.14
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: A Rails Engine for managing Bandshell-powered Concerto hardware
105
+ test_files:
106
+ - test/fixtures/players.yml
107
+ - test/functional/players_controller_test.rb
108
+ - test/unit/helpers/players_helper_test.rb
109
+ - test/unit/player_test.rb