pairer 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +7 -0
- data/README.md +134 -0
- data/Rakefile +20 -0
- data/app/assets/config/pairer_manifest.js +3 -0
- data/app/assets/images/pairer/favicon.ico +0 -0
- data/app/assets/images/pairer/sweep.png +0 -0
- data/app/assets/javascripts/pairer/application.js +47 -0
- data/app/assets/stylesheets/pairer/application.scss +82 -0
- data/app/assets/stylesheets/pairer/utility.scss +221 -0
- data/app/controllers/pairer/application_controller.rb +31 -0
- data/app/controllers/pairer/boards_controller.rb +202 -0
- data/app/controllers/pairer/sessions_controller.rb +32 -0
- data/app/helpers/pairer/application_helper.rb +4 -0
- data/app/jobs/pairer/application_job.rb +5 -0
- data/app/jobs/pairer/cleanup_boards_job.rb +9 -0
- data/app/mailers/pairer/application_mailer.rb +5 -0
- data/app/models/pairer/application_record.rb +19 -0
- data/app/models/pairer/board.rb +226 -0
- data/app/models/pairer/group.rb +45 -0
- data/app/models/pairer/person.rb +7 -0
- data/app/views/layouts/pairer/application.html.slim +53 -0
- data/app/views/layouts/pairer/application.js.erb +5 -0
- data/app/views/pairer/boards/_current_groups.html.slim +11 -0
- data/app/views/pairer/boards/_group.html.slim +17 -0
- data/app/views/pairer/boards/_group_lock_button.html.slim +4 -0
- data/app/views/pairer/boards/_person.html.slim +9 -0
- data/app/views/pairer/boards/_recently_accessed_boards.html.slim +26 -0
- data/app/views/pairer/boards/_role.html.slim +6 -0
- data/app/views/pairer/boards/_stats.html.slim +37 -0
- data/app/views/pairer/boards/create_group.js.erb +3 -0
- data/app/views/pairer/boards/create_person.js.erb +7 -0
- data/app/views/pairer/boards/delete_group.js.erb +7 -0
- data/app/views/pairer/boards/index.html.slim +28 -0
- data/app/views/pairer/boards/lock_group.js.erb +1 -0
- data/app/views/pairer/boards/lock_person.js.erb +1 -0
- data/app/views/pairer/boards/show.html.slim +240 -0
- data/app/views/pairer/boards/update_group.js.erb +0 -0
- data/app/views/pairer/exceptions/show.html.slim +8 -0
- data/app/views/pairer/sessions/sign_in.html.slim +10 -0
- data/app/views/pairer/shared/_flash.html.slim +6 -0
- data/app/views/pairer/shared/svg/_broom.html.slim +2 -0
- data/config/locales/en.yml +5 -0
- data/config/routes.rb +26 -0
- data/db/migrate/20210821001344_add_pairer_tables.rb +34 -0
- data/db/seeds.rb +0 -0
- data/lib/pairer/config.rb +38 -0
- data/lib/pairer/engine.rb +47 -0
- data/lib/pairer/version.rb +3 -0
- data/lib/pairer.rb +24 -0
- data/lib/tasks/pairer_engine_tasks.rake +4 -0
- metadata +261 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e8774463f2c0328260af7daf81b8c36eb0f019c841777e163fcb59ca7e799ba0
|
4
|
+
data.tar.gz: fdbffea2f68177d3b6d74ae71a6fd32e1a7a967118a624296091d9f1a105f0ca
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3f39555a2253f14613bee72894a8b96ed255d9d482daeb5e713099c0fa13bdb6038780c206235ae93d819b987fa8eaa8b86258346c90789aaf8f4a9448771704
|
7
|
+
data.tar.gz: cb2d36c125a60124159a8ed48835b92e52554fa2e253817b5a60edb19535b4e2a164616c7f42454174a884d3feb0bbf51810c6aa7d2c909597d076fa7f90c4f0
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2022 Weston Ganger
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
# Pairer
|
2
|
+
|
3
|
+
<a href='https://github.com/westonganger/pairer/actions' target='_blank'><img src="https://github.com/westonganger/pairer/workflows/Tests/badge.svg" style="max-width:100%;" height='21' style='border:0px;height:21px;' border='0' alt="CI Status"></a>
|
4
|
+
|
5
|
+
Pairer is a Rails app/engine to help you to easily generate and rotate pairs within a larger group. For example its great for pair programming teams where you want to work with someone new everyday.
|
6
|
+
|
7
|
+
Each organization has many boards. Within each board you can create people and roles. The tool will allow for both automated and manual assignments of these resources to working groups within the board.
|
8
|
+
|
9
|
+
![Screenshot](/screenshot.png)
|
10
|
+
|
11
|
+
## Setup
|
12
|
+
|
13
|
+
Developed as a Rails engine. So you can add to any existing app or create a brand new app with the functionality.
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
### Gemfile
|
17
|
+
gem 'pairer'
|
18
|
+
```
|
19
|
+
|
20
|
+
|
21
|
+
#### Option A: Mount to a path
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
### config/routes.rb
|
25
|
+
|
26
|
+
### As sub-path
|
27
|
+
mount Pairer::Engine, at: "/pairer", as: "pairer"
|
28
|
+
|
29
|
+
### OR as root-path
|
30
|
+
mount Pairer::Engine, at: "/", as: "pairer"
|
31
|
+
```
|
32
|
+
|
33
|
+
#### Option B: Mount as a subdomain
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
### config/routes.rb
|
37
|
+
|
38
|
+
pairer_subdomain = "pairer"
|
39
|
+
|
40
|
+
mount Pairer::Engine,
|
41
|
+
at: "/", as: "pairer",
|
42
|
+
constraints: Proc.new{|request| request.subdomain == pairer_subdomain }
|
43
|
+
|
44
|
+
not_engine = Proc.new{|request| request.subdomain != pairer_subdomain }
|
45
|
+
|
46
|
+
constraints not_engine do
|
47
|
+
# your app routes here...
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
### Configuration Options
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
### config/initializers/pairer.rb
|
55
|
+
|
56
|
+
Pairer.config do |config|
|
57
|
+
config.hash_id_salt = "Fy@%p0L^$Je6Ybc9uAjNU&T@" ### Dont lose this, this is used to generate public_ids for your records using hash_ids gem
|
58
|
+
|
59
|
+
config.allowed_org_ids = ["example-org", "other-example-org"]
|
60
|
+
### OR something more secure, for example
|
61
|
+
config.allowed_org_ids = ["pXtHe7YUW0@Wo$H3V*s6l4N5"]
|
62
|
+
|
63
|
+
config.max_iterations_to_track = 100 # Defaults to 100
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
## How Authentication Works
|
68
|
+
|
69
|
+
Authentication models is as follows:
|
70
|
+
|
71
|
+
1. Your main app defines a list of `Pairer.config.allowed_org_ids`. When an unauthenticated user visits the site they are taken to the sign-in page. On this page they are required to enter an "Organization ID" if they enter one of the `Pairer.config.allowed_org_ids` then they are signed-in to pairer and all boards will be scoped accordingly.
|
72
|
+
|
73
|
+
2. After the user is signed-in via #1 above, then the user can either A. access existing board by entering the boards password, or B. create a new board by defining a board password.
|
74
|
+
|
75
|
+
3. Since the authentication model is loose by design, it is strongly recommended that you add the gem `rack-attack` to your main application and configure it to prevent brute force attacks from unauthorized attackers
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
### Gemfile
|
79
|
+
gem "rack-attack"
|
80
|
+
```
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
### config/initializers/pairer.rb
|
84
|
+
|
85
|
+
Rack::Attack.throttle('limit unauthorized non-get requests', limit: 5, period: 1.minute) do |req|
|
86
|
+
if req.get?
|
87
|
+
subdomain = req.host.split('.').first
|
88
|
+
site_is_pairer = subdomain&.casecmp?("pairer") ### Replace this with whatever logic is applicable to your app
|
89
|
+
|
90
|
+
if site_is_pairer && !Pairer.config.allowed_org_ids.include?(req.session[:pairer_current_org_id])
|
91
|
+
### Not signed-in to Pairer
|
92
|
+
req.ip
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
## Configuring Exception Handling
|
99
|
+
|
100
|
+
If you want to add exception handling/notifications you can easily just add the behaviour directly to pairers application controller and do your custom exception handling logic. For example:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
Pairer::ApplicationController.class_eval do
|
104
|
+
rescue_from Exception do |exception|
|
105
|
+
ExceptionNotifier.notify_exception(exception)
|
106
|
+
render plain: "System error", status: 500
|
107
|
+
end
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
## Development
|
112
|
+
|
113
|
+
Run migrations using: `rails db:migrate`
|
114
|
+
|
115
|
+
Run server using: `bin/dev` or `cd test/dummy/; rails s`
|
116
|
+
|
117
|
+
## Testing
|
118
|
+
|
119
|
+
```
|
120
|
+
bundle exec rspec
|
121
|
+
```
|
122
|
+
|
123
|
+
We can locally test different versions of Rails using `ENV['RAILS_VERSION']` and different database gems using `ENV['DB_GEM']`
|
124
|
+
|
125
|
+
```
|
126
|
+
export RAILS_VERSION=7.0
|
127
|
+
export DB_GEM=sqlite3
|
128
|
+
bundle install
|
129
|
+
bundle exec rspec
|
130
|
+
```
|
131
|
+
|
132
|
+
# Credits
|
133
|
+
|
134
|
+
Created & Maintained by [Weston Ganger](https://westonganger.com) - [@westonganger](https://github.com/westonganger)
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
8
|
+
load 'rails/tasks/engine.rake'
|
9
|
+
|
10
|
+
load 'rails/tasks/statistics.rake'
|
11
|
+
|
12
|
+
require 'bundler/gem_tasks'
|
13
|
+
|
14
|
+
require 'rspec/core/rake_task'
|
15
|
+
|
16
|
+
RSpec::Core::RakeTask.new(:spec)
|
17
|
+
|
18
|
+
task test: [:spec]
|
19
|
+
|
20
|
+
task default: [:spec]
|
Binary file
|
Binary file
|
@@ -0,0 +1,47 @@
|
|
1
|
+
//= require rails-ujs
|
2
|
+
//= require bootstrap-sprockets
|
3
|
+
|
4
|
+
window.init = function(){
|
5
|
+
$('form').attr('autocomplete', 'off');
|
6
|
+
|
7
|
+
var alerts = $(".alert:not(.permanent)")
|
8
|
+
setTimeout(function(){
|
9
|
+
alerts.fadeOut();
|
10
|
+
}, 8000);
|
11
|
+
}
|
12
|
+
|
13
|
+
$(function(){
|
14
|
+
window.init();
|
15
|
+
});
|
16
|
+
|
17
|
+
function equals(obj1, obj2){
|
18
|
+
if((obj1 == null || typeof obj1 != 'object') || (obj2 == null || typeof obj2 != 'object')){
|
19
|
+
return obj1 == obj2;
|
20
|
+
}
|
21
|
+
|
22
|
+
for(var propName in obj1){
|
23
|
+
if(obj1.hasOwnProperty(propName) != obj2.hasOwnProperty(propName)){
|
24
|
+
return false;
|
25
|
+
}else if(typeof obj1[propName] != typeof obj2[propName]){
|
26
|
+
return false;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
for(var propName in obj2){
|
30
|
+
var val = obj1[propName];
|
31
|
+
var other = obj2[propName];
|
32
|
+
if(obj1.hasOwnProperty(propName) != obj2.hasOwnProperty(propName)){
|
33
|
+
return false;
|
34
|
+
}else if(typeof val != typeof other){
|
35
|
+
return false;
|
36
|
+
}
|
37
|
+
|
38
|
+
if(!obj1.hasOwnProperty(propName)){
|
39
|
+
continue;
|
40
|
+
}
|
41
|
+
|
42
|
+
if(!equals(val, other)){
|
43
|
+
return false;
|
44
|
+
}
|
45
|
+
}
|
46
|
+
return true;
|
47
|
+
};
|
@@ -0,0 +1,82 @@
|
|
1
|
+
/*
|
2
|
+
*= require ./utility
|
3
|
+
*= require_self
|
4
|
+
*/
|
5
|
+
|
6
|
+
$my-navbar-height: 3.5em;
|
7
|
+
|
8
|
+
@import "bootswatch/united/variables";
|
9
|
+
|
10
|
+
$grid-float-breakpoint: 992px; // collapse for xs and sm devices */
|
11
|
+
|
12
|
+
@import "bootstrap-sprockets";
|
13
|
+
@import "bootstrap";
|
14
|
+
@import "bootswatch/united/bootswatch";
|
15
|
+
|
16
|
+
.badge-danger{
|
17
|
+
background-color: $brand-danger !important;
|
18
|
+
color: white !important;
|
19
|
+
}
|
20
|
+
|
21
|
+
.navbar{
|
22
|
+
font-size: 1.1em;
|
23
|
+
min-height: $my-navbar-height;
|
24
|
+
|
25
|
+
.navbar-brand{
|
26
|
+
font-size: 1.5em;
|
27
|
+
font-family: monospace;
|
28
|
+
padding: 8px 15px;
|
29
|
+
&:hover{
|
30
|
+
color: initial;
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
.navbar-nav li a, .navbar-brand{
|
35
|
+
line-height: $my-navbar-height/2;
|
36
|
+
}
|
37
|
+
}
|
38
|
+
|
39
|
+
|
40
|
+
.alert{
|
41
|
+
.close{
|
42
|
+
opacity: 0.6;
|
43
|
+
&:hover{
|
44
|
+
opacity: 0.8;
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
h1, h2, h3, h4, h5{
|
50
|
+
color: #424242;
|
51
|
+
}
|
52
|
+
|
53
|
+
body{
|
54
|
+
background-color: #F5F6F6;
|
55
|
+
padding-top: 80px;
|
56
|
+
padding-bottom: 20px;
|
57
|
+
}
|
58
|
+
|
59
|
+
label{
|
60
|
+
margin-right: 10px;
|
61
|
+
}
|
62
|
+
|
63
|
+
footer{
|
64
|
+
position: fixed;
|
65
|
+
text-align: center;
|
66
|
+
bottom: 10px;
|
67
|
+
right: 0;
|
68
|
+
left: 0;
|
69
|
+
}
|
70
|
+
|
71
|
+
.btn-default{
|
72
|
+
border-color: gray;
|
73
|
+
background-color: gray;
|
74
|
+
background-image: none;
|
75
|
+
}
|
76
|
+
|
77
|
+
@media(max-width: 768px){
|
78
|
+
.form-inline .form-control{
|
79
|
+
display: inline-block;
|
80
|
+
width: initial;
|
81
|
+
}
|
82
|
+
}
|
@@ -0,0 +1,221 @@
|
|
1
|
+
.no-gutter{
|
2
|
+
padding:0 !important;
|
3
|
+
}
|
4
|
+
.bold{
|
5
|
+
font-weight:bold !important;
|
6
|
+
}
|
7
|
+
.no-bold{
|
8
|
+
font-weight:400 !important;
|
9
|
+
}
|
10
|
+
.italic{
|
11
|
+
font-style: italic !important;
|
12
|
+
}
|
13
|
+
.underline{
|
14
|
+
text-decoration:underline !important;
|
15
|
+
}
|
16
|
+
.space-left{
|
17
|
+
margin-left:5px !important;
|
18
|
+
}
|
19
|
+
.space-left2{
|
20
|
+
margin-left:10px !important;
|
21
|
+
}
|
22
|
+
.space-left3{
|
23
|
+
margin-left:15px !important;
|
24
|
+
}
|
25
|
+
.space-left4{
|
26
|
+
margin-left:20px !important;
|
27
|
+
}
|
28
|
+
.space-left5{
|
29
|
+
margin-left:30px !important;
|
30
|
+
}
|
31
|
+
.space-right{
|
32
|
+
margin-right:5px !important;
|
33
|
+
}
|
34
|
+
.space-right2{
|
35
|
+
margin-right:10px !important;
|
36
|
+
}
|
37
|
+
.space-right3{
|
38
|
+
margin-right:15px !important;
|
39
|
+
}
|
40
|
+
.space-right4{
|
41
|
+
margin-right:20px !important;
|
42
|
+
}
|
43
|
+
.space-right5{
|
44
|
+
margin-right:30px !important;
|
45
|
+
}
|
46
|
+
.space-above{
|
47
|
+
margin-top:5px !important;
|
48
|
+
}
|
49
|
+
.space-above2{
|
50
|
+
margin-top:10px !important;
|
51
|
+
}
|
52
|
+
.space-above3{
|
53
|
+
margin-top:20px !important;
|
54
|
+
}
|
55
|
+
.space-above4{
|
56
|
+
margin-top:30px !important;
|
57
|
+
}
|
58
|
+
.space-above5{
|
59
|
+
margin-top:40px !important;
|
60
|
+
}
|
61
|
+
.space-below{
|
62
|
+
margin-bottom:5px !important;
|
63
|
+
}
|
64
|
+
.space-below2{
|
65
|
+
margin-bottom:10px !important;
|
66
|
+
}
|
67
|
+
.space-below3{
|
68
|
+
margin-bottom:15px !important;
|
69
|
+
}
|
70
|
+
.space-below4{
|
71
|
+
margin-bottom:20px !important;
|
72
|
+
}
|
73
|
+
.space-below5{
|
74
|
+
margin-bottom:30px !important;
|
75
|
+
}
|
76
|
+
.width-100{
|
77
|
+
width:100%;
|
78
|
+
}
|
79
|
+
.width-90{
|
80
|
+
width: 90%;
|
81
|
+
}
|
82
|
+
.table-text-center{
|
83
|
+
th td{
|
84
|
+
text-align:center !important;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
.display-table{
|
88
|
+
height:130px;
|
89
|
+
display:table;
|
90
|
+
|
91
|
+
.vertical-align{
|
92
|
+
display:table-cell;
|
93
|
+
vertical-align:middle;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
/*
|
97
|
+
.visible-xs,
|
98
|
+
.visible-sm,
|
99
|
+
.visible-md,
|
100
|
+
.visible-lg {
|
101
|
+
display: none !important;
|
102
|
+
}
|
103
|
+
|
104
|
+
.visible-xs {
|
105
|
+
@media (max-width: 767px) {
|
106
|
+
display: block !important;
|
107
|
+
table{
|
108
|
+
display: table !important;
|
109
|
+
}
|
110
|
+
tr{
|
111
|
+
display: table-row !important;
|
112
|
+
}
|
113
|
+
th, td{
|
114
|
+
display: table-cell !important;
|
115
|
+
}
|
116
|
+
i, abbr, cite, code, strong, a, br, img, span, sub, button, input, label, select, textarea{
|
117
|
+
display: inline !important;
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
.visible-sm {
|
123
|
+
@media (min-width: 768px) and (max-width: 991px) {
|
124
|
+
display: block !important;
|
125
|
+
table{
|
126
|
+
display: table !important;
|
127
|
+
}
|
128
|
+
tr{
|
129
|
+
display: table-row !important;
|
130
|
+
}
|
131
|
+
th, td{
|
132
|
+
display: table-cell !important;
|
133
|
+
}
|
134
|
+
i, abbr, cite, code, strong, a, br, img, span, sub, button, input, label, select, textarea{
|
135
|
+
display: inline !important;
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
.visible-md {
|
141
|
+
@media (min-width: 992px) and (max-width: 1199px) {
|
142
|
+
display: block !important;
|
143
|
+
table{
|
144
|
+
display: table !important;
|
145
|
+
}
|
146
|
+
tr{
|
147
|
+
display: table-row !important;
|
148
|
+
}
|
149
|
+
th, td{
|
150
|
+
display: table-cell !important;
|
151
|
+
}
|
152
|
+
i, abbr, cite, code, strong, a, br, img, span, sub, button, input, label, select, textarea{
|
153
|
+
display: inline !important;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
.visible-lg {
|
159
|
+
@media (min-width: 1200px) {
|
160
|
+
display: block !important;
|
161
|
+
table{
|
162
|
+
display: table !important;
|
163
|
+
}
|
164
|
+
tr{
|
165
|
+
display: table-row !important;
|
166
|
+
}
|
167
|
+
th, td{
|
168
|
+
display: table-cell !important;
|
169
|
+
}
|
170
|
+
i, abbr, cite, code, strong, a, br, img, span, sub, button, input, label, select, textarea{
|
171
|
+
display: inline !important;
|
172
|
+
}
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
.hidden-xs {
|
177
|
+
@media (max-width: 767px) {
|
178
|
+
display: none !important;
|
179
|
+
}
|
180
|
+
}
|
181
|
+
.hidden-sm {
|
182
|
+
@media (min-width: 768px) and (max-width: 991px) {
|
183
|
+
display: none !important;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
.hidden-md {
|
187
|
+
@media (min-width: 992px) and (max-width: 1199px) {
|
188
|
+
display: none !important;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
.hidden-lg {
|
192
|
+
@media (min-width: 1200px) {
|
193
|
+
display: none !important;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
.visible-print {
|
197
|
+
display: none !important;
|
198
|
+
|
199
|
+
@media print {
|
200
|
+
display: block !important;
|
201
|
+
table{
|
202
|
+
display: table !important;
|
203
|
+
}
|
204
|
+
tr{
|
205
|
+
display: table-row !important;
|
206
|
+
}
|
207
|
+
th, td{
|
208
|
+
display: table-cell !important;
|
209
|
+
}
|
210
|
+
i, abbr, cite, code, strong, a, br, img, span, sub, button, input, label, select, textarea{
|
211
|
+
display: inline !important;
|
212
|
+
}
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
.hidden-print {
|
217
|
+
@media print {
|
218
|
+
display: none !important;
|
219
|
+
}
|
220
|
+
}
|
221
|
+
*/
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Pairer
|
2
|
+
class ApplicationController < ActionController::Base
|
3
|
+
protect_from_forgery with: :exception
|
4
|
+
|
5
|
+
helper_method :signed_in?
|
6
|
+
|
7
|
+
def robots
|
8
|
+
str = <<~STR
|
9
|
+
User-agent: *
|
10
|
+
Disallow: /
|
11
|
+
STR
|
12
|
+
|
13
|
+
render plain: str, layout: false, content_type: 'text/plain'
|
14
|
+
end
|
15
|
+
|
16
|
+
def render_404
|
17
|
+
if request.format.html?
|
18
|
+
render "pairer/exceptions/show", status: 404
|
19
|
+
else
|
20
|
+
render plain: "404 Not Found", status: 404
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def signed_in?
|
25
|
+
if session[:pairer_current_org_id].present?
|
26
|
+
Pairer.config.allowed_org_ids.collect(&:downcase).include?(session[:pairer_current_org_id].downcase)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|