Merge remote-tracking branch 'origin/master' into flowr
# Conflicts: # app/assets/javascripts/editor/evaluation.js # app/assets/javascripts/editor/participantsupport.js # app/views/exercises/_editor_output.html.slim # config/code_ocean.yml.example # config/routes.rb
This commit is contained in:
18
.babelrc
Normal file
18
.babelrc
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"presets": [
|
||||
["env", {
|
||||
"modules": false,
|
||||
"targets": {
|
||||
"browsers": "> 1%",
|
||||
"uglify": true
|
||||
},
|
||||
"useBuiltIns": true
|
||||
}]
|
||||
],
|
||||
|
||||
"plugins": [
|
||||
"syntax-dynamic-import",
|
||||
"transform-object-rest-spread",
|
||||
["transform-class-properties", { "spec": true }]
|
||||
]
|
||||
}
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -24,3 +24,9 @@
|
||||
/.vagrant
|
||||
*.iml
|
||||
*.DS_Store
|
||||
/node_modules
|
||||
/public/packs
|
||||
/public/packs-test
|
||||
/node_modules
|
||||
yarn-debug.log*
|
||||
.yarn-integrity
|
||||
|
3
.postcssrc.yml
Normal file
3
.postcssrc.yml
Normal file
@ -0,0 +1,3 @@
|
||||
plugins:
|
||||
postcss-import: {}
|
||||
postcss-cssnext: {}
|
@ -1,4 +1,5 @@
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.3
|
||||
Exclude:
|
||||
- bin/*
|
||||
- config/application.rb
|
||||
@ -7,11 +8,12 @@ AllCops:
|
||||
- db/schema.rb
|
||||
- public/uploads/**/*
|
||||
- tmp/**/*
|
||||
RunRailsCops: true
|
||||
Rails:
|
||||
Enabled: true
|
||||
Metrics/LineLength:
|
||||
Enabled: false
|
||||
require: rubocop-rspec
|
||||
Style/Documentation:
|
||||
Enabled: false
|
||||
Style/SpaceInsideHashLiteralBraces:
|
||||
Layout/SpaceInsideHashLiteralBraces:
|
||||
EnforcedStyle: no_space
|
||||
|
35
.travis.yml
35
.travis.yml
@ -3,12 +3,20 @@ sudo: required
|
||||
services:
|
||||
- docker
|
||||
|
||||
language: ruby
|
||||
rvm:
|
||||
- 2.5.1
|
||||
cache:
|
||||
bundler: true
|
||||
yarn: true
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "DkOGGPCrRgV08KGgav3Bl+keZQqb11TINQRVQS2aeMaYR5GW7Rt9zEcZzhUE0JdKVVOvm4Cclft7BO4OyMd6Cq9XnZkOOHY+Yn8Qv923761SKrRgkGUkO8eeVKMawAA8lS53XGrMZWCP2xaLsLQYq8xzinnE3GqstoZJaHLnqVs="
|
||||
|
||||
addons:
|
||||
code_climate:
|
||||
repo_token:
|
||||
secure: "cZoMNjQKB/D7W4B7JDk9PXooy2WCDypu7R4C/Vi0DziZCU9HRwLbdt9aoH5hgHFa7Fe2rHFgflPAAP7h698ozvP0waFtPqLAj+PbEt27LbBDvW8JcvNkKXA0rj5wyTkzuc/0kD+kPB4oDXMak6gZlB9HCJDsa3kdXScQGTVuPdU="
|
||||
postgresql: "9.6"
|
||||
firefox: "latest"
|
||||
firefox: "62.0.3"
|
||||
|
||||
before_install:
|
||||
- export DISPLAY=:99.0
|
||||
@ -19,11 +27,17 @@ before_install:
|
||||
- docker pull openhpi/co_execenv_python
|
||||
- docker pull openhpi/co_execenv_java
|
||||
- mkdir ~/geckodriver
|
||||
- wget -O ~/geckodriver/download.tar.gz https://github.com/mozilla/geckodriver/releases/download/v0.18.0/geckodriver-v0.18.0-linux64.tar.gz
|
||||
- wget -O ~/geckodriver/download.tar.gz https://github.com/mozilla/geckodriver/releases/download/v0.23.0/geckodriver-v0.23.0-linux64.tar.gz
|
||||
- tar -xvzf ~/geckodriver/download.tar.gz -C ~/geckodriver/
|
||||
- rm ~/geckodriver/download.tar.gz
|
||||
- chmod +x ~/geckodriver/geckodriver
|
||||
- export PATH=~/geckodriver/:$PATH
|
||||
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
||||
- chmod +x ./cc-test-reporter
|
||||
|
||||
install:
|
||||
- bundle install --jobs=3 --retry=3 --deployment --path=${BUNDLE_PATH:-vendor/bundle}
|
||||
- yarn install
|
||||
|
||||
before_script:
|
||||
- cp .rspec.travis .rspec
|
||||
@ -35,10 +49,9 @@ before_script:
|
||||
- cp config/mnemosyne.yml.travis config/mnemosyne.yml
|
||||
- psql --command='CREATE DATABASE travis_ci_test;' --username=postgres
|
||||
- bundle exec rake db:schema:load RAILS_ENV=test
|
||||
- ./cc-test-reporter before-build
|
||||
|
||||
cache: bundler
|
||||
language: ruby
|
||||
rvm:
|
||||
- 2.3.6
|
||||
|
||||
script: bundle exec rspec --color --format documentation --require spec_helper --require rails_helper && bundle exec codeclimate-test-reporter
|
||||
script: bundle exec rspec --color --format documentation --require spec_helper --require rails_helper
|
||||
|
||||
after_script:
|
||||
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
||||
|
43
Gemfile
43
Gemfile
@ -1,53 +1,48 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'activerecord-jdbcpostgresql-adapter', platform: :jruby
|
||||
gem 'bcrypt'
|
||||
gem 'bootstrap-will_paginate'
|
||||
gem 'carrierwave'
|
||||
gem 'concurrent-ruby'
|
||||
gem 'concurrent-ruby-ext', platform: :ruby
|
||||
gem 'activerecord-deprecated_finders', require: 'active_record/deprecated_finders'
|
||||
gem 'docker-api', require: 'docker'
|
||||
gem 'factory_bot_rails'
|
||||
gem 'forgery'
|
||||
gem 'highline'
|
||||
gem 'jbuilder'
|
||||
gem 'jquery-rails'
|
||||
gem 'jquery-turbolinks'
|
||||
gem 'ims-lti', '1.1.10' # version 1.1.13 will crash, because @provider.valid_request?(request) on lti.rb line 89 will return false.
|
||||
gem 'ims-lti', '< 2.0.0'
|
||||
gem 'kramdown'
|
||||
gem 'newrelic_rpm'
|
||||
gem 'pg', '< 1.0', platform: :ruby
|
||||
gem 'pg'
|
||||
gem 'pry-byebug'
|
||||
gem 'puma'
|
||||
gem 'pundit'
|
||||
gem 'rails', '4.2.10'
|
||||
gem 'rails', '5.2.1'
|
||||
gem 'rails-i18n'
|
||||
gem 'ransack'
|
||||
gem 'rubytree'
|
||||
gem 'sass-rails', '>= 5.0.7'
|
||||
gem 'sdoc', group: :doc
|
||||
gem 'sass-rails'
|
||||
gem 'slim-rails'
|
||||
gem 'bootstrap_pagedown', '>= 1.1.0'
|
||||
gem 'pagedown-rails'
|
||||
gem 'pagedown-bootstrap-rails'
|
||||
gem 'sorcery'
|
||||
gem 'thread_safe'
|
||||
gem 'turbolinks', '< 5.0.0' # newer versions prevent loading ACE if the page containing is not accessed directly / refreshed
|
||||
gem 'turbolinks'
|
||||
gem 'uglifier'
|
||||
gem 'will_paginate'
|
||||
gem 'tubesock'
|
||||
gem 'tubesock', git: 'https://github.com/gosukiwi/tubesock', branch: 'patch-1' # Switch to a fork which is compatible with Rails 5
|
||||
gem 'faye-websocket'
|
||||
gem 'eventmachine', '1.0.9.1' # explicitly added, this is used by faye-websocket, version 1.2.5 still has an error in eventmachine.rb:202: [BUG] Segmentation fault, which is not yet fixed and causes the whole ruby process to crash
|
||||
gem 'eventmachine', '1.0.9.1' # explicitly added, this is used by faye-websocket, newer versions might crash or
|
||||
gem 'nokogiri'
|
||||
gem 'd3-rails', '~>4.0'
|
||||
gem 'd3-rails'
|
||||
gem 'webpacker'
|
||||
gem 'rest-client'
|
||||
gem 'rubyzip'
|
||||
gem 'mnemosyne-ruby', '~> 1.0'
|
||||
gem 'mnemosyne-ruby'
|
||||
gem 'whenever', require: false
|
||||
|
||||
group :development, :staging do
|
||||
gem 'better_errors', platform: :ruby
|
||||
gem 'binding_of_caller', platform: :ruby
|
||||
gem 'bootsnap', require: false
|
||||
gem 'listen'
|
||||
gem 'better_errors'
|
||||
gem 'binding_of_caller'
|
||||
gem 'capistrano'
|
||||
gem 'capistrano3-puma'
|
||||
gem 'capistrano-rails'
|
||||
@ -56,23 +51,21 @@ group :development, :staging do
|
||||
gem 'rack-mini-profiler'
|
||||
gem 'rubocop', require: false
|
||||
gem 'rubocop-rspec'
|
||||
gem 'web-console', platform: :ruby
|
||||
gem 'web-console'
|
||||
end
|
||||
|
||||
group :development, :test, :staging do
|
||||
gem 'byebug', platform: :ruby
|
||||
gem 'spring'
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'rails-controller-testing'
|
||||
gem 'autotest-rails'
|
||||
gem 'capybara'
|
||||
gem 'capybara-selenium', '>= 0.0.6'
|
||||
gem 'selenium-webdriver'
|
||||
gem 'headless'
|
||||
gem 'codeclimate-test-reporter', require: false
|
||||
gem 'database_cleaner'
|
||||
gem 'nyan-cat-formatter'
|
||||
gem 'rake'
|
||||
gem 'rspec-autotest'
|
||||
gem 'rspec-rails'
|
||||
gem 'simplecov', require: false
|
||||
|
426
Gemfile.lock
426
Gemfile.lock
@ -1,75 +1,91 @@
|
||||
GIT
|
||||
remote: https://github.com/gosukiwi/tubesock
|
||||
revision: 86a5ca4f7d3c3a7b9a727ad91df3b9b4912eda39
|
||||
branch: patch-1
|
||||
specs:
|
||||
tubesock (0.2.7)
|
||||
rack (>= 1.5.0)
|
||||
websocket (>= 1.1.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
ZenTest (4.11.1)
|
||||
actionmailer (4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activejob (= 4.2.10)
|
||||
actioncable (5.2.1)
|
||||
actionpack (= 5.2.1)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailer (5.2.1)
|
||||
actionpack (= 5.2.1)
|
||||
actionview (= 5.2.1)
|
||||
activejob (= 5.2.1)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
actionpack (4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
rack (~> 1.6)
|
||||
rack-test (~> 0.6.2)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.2.1)
|
||||
actionview (= 5.2.1)
|
||||
activesupport (= 5.2.1)
|
||||
rack (~> 2.0)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
actionview (5.2.1)
|
||||
activesupport (= 5.2.1)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 1.0, >= 1.0.5)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
activejob (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
globalid (>= 0.3.0)
|
||||
activemodel (4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
builder (~> 3.1)
|
||||
activerecord (4.2.10)
|
||||
activemodel (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
arel (~> 6.0)
|
||||
activerecord-deprecated_finders (1.0.4)
|
||||
activesupport (4.2.10)
|
||||
i18n (~> 0.7)
|
||||
activejob (5.2.1)
|
||||
activesupport (= 5.2.1)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.2.1)
|
||||
activesupport (= 5.2.1)
|
||||
activerecord (5.2.1)
|
||||
activemodel (= 5.2.1)
|
||||
activesupport (= 5.2.1)
|
||||
arel (>= 9.0)
|
||||
activestorage (5.2.1)
|
||||
actionpack (= 5.2.1)
|
||||
activerecord (= 5.2.1)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (5.2.1)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.3, >= 0.3.4)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.5.2)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
airbrussh (1.3.0)
|
||||
airbrussh (1.3.1)
|
||||
sshkit (>= 1.6.1, != 1.7.0)
|
||||
amq-protocol (2.3.0)
|
||||
arel (6.0.4)
|
||||
arel (9.0.0)
|
||||
ast (2.4.0)
|
||||
autotest-rails (4.2.1)
|
||||
ZenTest (~> 4.5)
|
||||
bcrypt (3.1.11)
|
||||
better_errors (2.4.0)
|
||||
bcrypt (3.1.12)
|
||||
better_errors (2.5.0)
|
||||
coderay (>= 1.0.0)
|
||||
erubi (>= 1.0.0)
|
||||
rack (>= 0.9.0)
|
||||
bindex (0.5.0)
|
||||
binding_of_caller (0.8.0)
|
||||
debug_inspector (>= 0.0.1)
|
||||
bootsnap (1.3.2)
|
||||
msgpack (~> 1.0)
|
||||
bootstrap-will_paginate (1.0.0)
|
||||
will_paginate
|
||||
bootstrap_pagedown (1.1.0)
|
||||
rails (>= 3.2)
|
||||
builder (3.2.3)
|
||||
bunny (2.11.0)
|
||||
amq-protocol (~> 2.3.0)
|
||||
byebug (10.0.0)
|
||||
capistrano (3.10.1)
|
||||
bunny (2.12.0)
|
||||
amq-protocol (~> 2.3, >= 2.3.0)
|
||||
byebug (10.0.2)
|
||||
capistrano (3.11.0)
|
||||
airbrussh (>= 1.0.0)
|
||||
i18n
|
||||
rake (>= 10.0.0)
|
||||
sshkit (>= 1.9.0)
|
||||
capistrano-bundler (1.3.0)
|
||||
capistrano-bundler (1.4.0)
|
||||
capistrano (~> 3.1)
|
||||
sshkit (~> 1.2)
|
||||
capistrano-rails (1.3.1)
|
||||
capistrano-rails (1.4.0)
|
||||
capistrano (~> 3.1)
|
||||
capistrano-bundler (~> 1.1)
|
||||
capistrano-rvm (0.1.2)
|
||||
@ -81,59 +97,45 @@ GEM
|
||||
capistrano (~> 3.7)
|
||||
capistrano-bundler
|
||||
puma (~> 3.4)
|
||||
capybara (3.3.1)
|
||||
capybara (3.10.1)
|
||||
addressable
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
rack (>= 1.6.0)
|
||||
rack-test (>= 0.6.3)
|
||||
xpath (~> 3.1)
|
||||
capybara-selenium (0.0.6)
|
||||
capybara
|
||||
selenium-webdriver
|
||||
carrierwave (1.2.2)
|
||||
regexp_parser (~> 1.2)
|
||||
xpath (~> 3.2)
|
||||
carrierwave (1.2.3)
|
||||
activemodel (>= 4.0.0)
|
||||
activesupport (>= 4.0.0)
|
||||
mime-types (>= 1.16)
|
||||
childprocess (0.9.0)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
chronic (0.10.2)
|
||||
codeclimate-test-reporter (1.0.7)
|
||||
simplecov
|
||||
coderay (1.1.2)
|
||||
coffee-rails (4.2.2)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 4.0.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
concurrent-ruby (1.0.5)
|
||||
concurrent-ruby-ext (1.0.5)
|
||||
concurrent-ruby (= 1.0.5)
|
||||
concurrent-ruby (1.1.2)
|
||||
crass (1.0.4)
|
||||
d3-rails (4.13.0)
|
||||
d3-rails (5.7.0)
|
||||
railties (>= 3.1)
|
||||
database_cleaner (1.6.2)
|
||||
database_cleaner (1.7.0)
|
||||
debug_inspector (0.0.3)
|
||||
diff-lcs (1.3)
|
||||
docile (1.1.5)
|
||||
docker-api (1.34.1)
|
||||
docile (1.3.1)
|
||||
docker-api (1.34.2)
|
||||
excon (>= 0.47.0)
|
||||
multi_json
|
||||
domain_name (0.5.20170404)
|
||||
domain_name (0.5.20180417)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
erubi (1.7.1)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.0.9.1)
|
||||
excon (0.60.0)
|
||||
excon (0.62.0)
|
||||
execjs (2.7.0)
|
||||
factory_bot (4.8.2)
|
||||
factory_bot (4.11.1)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_bot_rails (4.8.2)
|
||||
factory_bot (~> 4.8.2)
|
||||
factory_bot_rails (4.11.1)
|
||||
factory_bot (~> 4.11.1)
|
||||
railties (>= 3.0.0)
|
||||
faraday (0.12.2)
|
||||
faraday (0.15.3)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faye-websocket (0.10.7)
|
||||
eventmachine (>= 0.12.0)
|
||||
@ -143,166 +145,178 @@ GEM
|
||||
globalid (0.4.1)
|
||||
activesupport (>= 4.2.0)
|
||||
headless (2.3.1)
|
||||
highline (1.7.10)
|
||||
highline (2.0.0)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
i18n (0.9.5)
|
||||
i18n (1.1.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
ims-lti (1.1.10)
|
||||
ims-lti (1.2.2)
|
||||
builder
|
||||
oauth (~> 0.4.5)
|
||||
jbuilder (2.7.0)
|
||||
oauth (>= 0.4.5, < 0.6)
|
||||
jaro_winkler (1.5.1)
|
||||
jbuilder (2.8.0)
|
||||
activesupport (>= 4.2.0)
|
||||
multi_json (>= 1.2)
|
||||
jquery-rails (4.3.1)
|
||||
jquery-rails (4.3.3)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
jquery-turbolinks (2.1.0)
|
||||
railties (>= 3.1.0)
|
||||
turbolinks
|
||||
json (2.1.0)
|
||||
jwt (1.5.6)
|
||||
kramdown (1.16.2)
|
||||
loofah (2.2.2)
|
||||
jwt (2.1.0)
|
||||
kramdown (1.17.0)
|
||||
listen (3.1.5)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
ruby_dep (~> 1.2)
|
||||
loofah (2.2.3)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.0)
|
||||
mail (2.7.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
method_source (0.9.0)
|
||||
mime-types (3.1)
|
||||
marcel (0.3.3)
|
||||
mimemagic (~> 0.3.2)
|
||||
method_source (0.9.1)
|
||||
mime-types (3.2.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2016.0521)
|
||||
mini_mime (1.0.0)
|
||||
mime-types-data (3.2018.0812)
|
||||
mimemagic (0.3.2)
|
||||
mini_mime (1.0.1)
|
||||
mini_portile2 (2.3.0)
|
||||
minitest (5.11.3)
|
||||
mnemosyne-ruby (1.5.1)
|
||||
activesupport (>= 4)
|
||||
bunny
|
||||
msgpack (1.2.4)
|
||||
multi_json (1.13.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
net-scp (1.2.1)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (4.2.0)
|
||||
net-ssh (5.0.2)
|
||||
netrc (0.11.0)
|
||||
newrelic_rpm (4.8.0.341)
|
||||
nokogiri (1.8.3)
|
||||
newrelic_rpm (5.4.0.347)
|
||||
nio4r (2.3.1)
|
||||
nokogiri (1.8.5)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
nyan-cat-formatter (0.12.0)
|
||||
rspec (>= 2.99, >= 2.14.2, < 4)
|
||||
oauth (0.4.7)
|
||||
oauth2 (1.4.0)
|
||||
faraday (>= 0.8, < 0.13)
|
||||
jwt (~> 1.0)
|
||||
oauth (0.5.4)
|
||||
oauth2 (1.4.1)
|
||||
faraday (>= 0.8, < 0.16.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
pagedown-rails (1.1.4)
|
||||
pagedown-bootstrap-rails (2.1.4)
|
||||
railties (> 3.1)
|
||||
parallel (1.12.1)
|
||||
parser (2.5.0.3)
|
||||
parser (2.5.3.0)
|
||||
ast (~> 2.4.0)
|
||||
pg (0.21.0)
|
||||
polyamorous (1.3.3)
|
||||
activerecord (>= 3.0)
|
||||
powerpack (0.1.1)
|
||||
pry (0.11.3)
|
||||
pg (1.1.3)
|
||||
powerpack (0.1.2)
|
||||
pry (0.12.0)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.9.0)
|
||||
pry-byebug (3.6.0)
|
||||
byebug (~> 10.0)
|
||||
pry (~> 0.10)
|
||||
public_suffix (3.0.2)
|
||||
puma (3.11.3)
|
||||
pundit (1.1.0)
|
||||
public_suffix (3.0.3)
|
||||
puma (3.12.0)
|
||||
pundit (2.0.0)
|
||||
activesupport (>= 3.0.0)
|
||||
rack (1.6.10)
|
||||
rack-mini-profiler (0.10.7)
|
||||
rack (2.0.6)
|
||||
rack-mini-profiler (1.0.0)
|
||||
rack (>= 1.2.0)
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rails (4.2.10)
|
||||
actionmailer (= 4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
actionview (= 4.2.10)
|
||||
activejob (= 4.2.10)
|
||||
activemodel (= 4.2.10)
|
||||
activerecord (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.2.10)
|
||||
sprockets-rails
|
||||
rails-deprecated_sanitizer (1.0.3)
|
||||
activesupport (>= 4.2.0.alpha)
|
||||
rails-dom-testing (1.0.9)
|
||||
activesupport (>= 4.2.0, < 5.0)
|
||||
nokogiri (~> 1.6)
|
||||
rails-deprecated_sanitizer (>= 1.0.1)
|
||||
rack-proxy (0.6.5)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.2.1)
|
||||
actioncable (= 5.2.1)
|
||||
actionmailer (= 5.2.1)
|
||||
actionpack (= 5.2.1)
|
||||
actionview (= 5.2.1)
|
||||
activejob (= 5.2.1)
|
||||
activemodel (= 5.2.1)
|
||||
activerecord (= 5.2.1)
|
||||
activestorage (= 5.2.1)
|
||||
activesupport (= 5.2.1)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 5.2.1)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.2)
|
||||
actionpack (~> 5.x, >= 5.0.1)
|
||||
actionview (~> 5.x, >= 5.0.1)
|
||||
activesupport (~> 5.x)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.0.4)
|
||||
loofah (~> 2.2, >= 2.2.2)
|
||||
rails-i18n (4.0.9)
|
||||
i18n (~> 0.7)
|
||||
railties (~> 4.0)
|
||||
railties (4.2.10)
|
||||
actionpack (= 4.2.10)
|
||||
activesupport (= 4.2.10)
|
||||
rails-i18n (5.1.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 5.0, < 6)
|
||||
railties (5.2.1)
|
||||
actionpack (= 5.2.1)
|
||||
activesupport (= 5.2.1)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
thor (>= 0.19.0, < 2.0)
|
||||
rainbow (3.0.0)
|
||||
rake (12.3.1)
|
||||
ransack (1.8.7)
|
||||
actionpack (>= 3.0)
|
||||
activerecord (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
ransack (2.1.0)
|
||||
actionpack (>= 5.0)
|
||||
activerecord (>= 5.0)
|
||||
activesupport (>= 5.0)
|
||||
i18n
|
||||
polyamorous (~> 1.3.2)
|
||||
rb-fsevent (0.10.3)
|
||||
rb-inotify (0.9.10)
|
||||
ffi (>= 0.5.0, < 2)
|
||||
rdoc (6.0.1)
|
||||
regexp_parser (1.2.0)
|
||||
rest-client (2.0.2)
|
||||
http-cookie (>= 1.0.2, < 2.0)
|
||||
mime-types (>= 1.16, < 4.0)
|
||||
netrc (~> 0.8)
|
||||
rspec (3.7.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-autotest (1.0.0)
|
||||
rspec (3.8.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-autotest (1.0.2)
|
||||
rspec-core (>= 2.99.0.beta1, < 4.0.0)
|
||||
rspec-core (3.7.1)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-expectations (3.7.0)
|
||||
rspec-core (3.8.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-expectations (3.8.2)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-mocks (3.7.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-mocks (3.8.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-rails (3.7.2)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-rails (3.8.1)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-support (3.7.1)
|
||||
rubocop (0.53.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-support (3.8.0)
|
||||
rubocop (0.60.0)
|
||||
jaro_winkler (~> 1.5.1)
|
||||
parallel (~> 1.10)
|
||||
parser (>= 2.5)
|
||||
parser (>= 2.5, != 2.5.1.1)
|
||||
powerpack (~> 0.1)
|
||||
rainbow (>= 2.2.2, < 4.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||
rubocop-rspec (1.24.0)
|
||||
rubocop (>= 0.53.0)
|
||||
ruby-progressbar (1.9.0)
|
||||
unicode-display_width (~> 1.4.0)
|
||||
rubocop-rspec (1.30.1)
|
||||
rubocop (>= 0.60.0)
|
||||
ruby-progressbar (1.10.0)
|
||||
ruby_dep (1.5.0)
|
||||
rubytree (1.0.0)
|
||||
json (~> 2.1)
|
||||
structured_warnings (~> 0.3)
|
||||
rubyzip (1.2.1)
|
||||
sass (3.5.6)
|
||||
rubyzip (1.2.2)
|
||||
sass (3.6.0)
|
||||
sass-listen (~> 4.0.0)
|
||||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
@ -313,24 +327,22 @@ GEM
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (>= 1.1, < 3)
|
||||
sdoc (1.0.0)
|
||||
rdoc (>= 5.0)
|
||||
selenium-webdriver (3.13.0)
|
||||
selenium-webdriver (3.141.0)
|
||||
childprocess (~> 0.5)
|
||||
rubyzip (~> 1.2)
|
||||
simplecov (0.15.1)
|
||||
docile (~> 1.1.0)
|
||||
rubyzip (~> 1.2, >= 1.2.2)
|
||||
simplecov (0.16.1)
|
||||
docile (~> 1.1)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.2)
|
||||
slim (3.0.9)
|
||||
slim (4.0.1)
|
||||
temple (>= 0.7.6, < 0.9)
|
||||
tilt (>= 1.3.3, < 2.1)
|
||||
slim-rails (3.1.3)
|
||||
tilt (>= 2.0.6, < 2.1)
|
||||
slim-rails (3.2.0)
|
||||
actionpack (>= 3.1)
|
||||
railties (>= 3.1)
|
||||
slim (~> 3.0)
|
||||
sorcery (0.11.0)
|
||||
slim (>= 3.0, < 5.0)
|
||||
sorcery (0.12.0)
|
||||
bcrypt (~> 3.1)
|
||||
oauth (~> 0.4, >= 0.4.4)
|
||||
oauth2 (~> 1.0, >= 0.8.0)
|
||||
@ -343,7 +355,7 @@ GEM
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sshkit (1.16.0)
|
||||
sshkit (1.18.0)
|
||||
net-scp (>= 1.1.2)
|
||||
net-ssh (>= 2.8.0)
|
||||
structured_warnings (0.3.0)
|
||||
@ -351,58 +363,55 @@ GEM
|
||||
thor (0.20.0)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.8)
|
||||
tubesock (0.2.7)
|
||||
rack (>= 1.5.0)
|
||||
websocket (>= 1.1.0)
|
||||
turbolinks (2.5.4)
|
||||
coffee-rails
|
||||
turbolinks (5.2.0)
|
||||
turbolinks-source (~> 5.2)
|
||||
turbolinks-source (5.2.0)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
uglifier (4.1.6)
|
||||
uglifier (4.1.19)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.5)
|
||||
unicode-display_width (1.3.0)
|
||||
web-console (3.3.0)
|
||||
activemodel (>= 4.2)
|
||||
debug_inspector
|
||||
unicode-display_width (1.4.0)
|
||||
web-console (3.7.0)
|
||||
actionview (>= 5.0)
|
||||
activemodel (>= 5.0)
|
||||
bindex (>= 0.4.0)
|
||||
railties (>= 5.0)
|
||||
webpacker (3.5.5)
|
||||
activesupport (>= 4.2)
|
||||
rack-proxy (>= 0.6.1)
|
||||
railties (>= 4.2)
|
||||
websocket (1.2.5)
|
||||
websocket (1.2.8)
|
||||
websocket-driver (0.7.0)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.3)
|
||||
whenever (0.10.0)
|
||||
chronic (>= 0.6.3)
|
||||
will_paginate (3.1.6)
|
||||
xpath (3.1.0)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
activerecord-deprecated_finders
|
||||
activerecord-jdbcpostgresql-adapter
|
||||
autotest-rails
|
||||
bcrypt
|
||||
better_errors
|
||||
binding_of_caller
|
||||
bootsnap
|
||||
bootstrap-will_paginate
|
||||
bootstrap_pagedown (>= 1.1.0)
|
||||
byebug
|
||||
capistrano
|
||||
capistrano-rails
|
||||
capistrano-rvm
|
||||
capistrano-upload-config
|
||||
capistrano3-puma
|
||||
capybara
|
||||
capybara-selenium (>= 0.0.6)
|
||||
carrierwave
|
||||
codeclimate-test-reporter
|
||||
concurrent-ruby
|
||||
concurrent-ruby-ext
|
||||
d3-rails (~> 4.0)
|
||||
d3-rails
|
||||
database_cleaner
|
||||
docker-api
|
||||
eventmachine (= 1.0.9.1)
|
||||
@ -411,24 +420,24 @@ DEPENDENCIES
|
||||
forgery
|
||||
headless
|
||||
highline
|
||||
ims-lti (= 1.1.10)
|
||||
ims-lti (< 2.0.0)
|
||||
jbuilder
|
||||
jquery-rails
|
||||
jquery-turbolinks
|
||||
kramdown
|
||||
mnemosyne-ruby (~> 1.0)
|
||||
listen
|
||||
mnemosyne-ruby
|
||||
newrelic_rpm
|
||||
nokogiri
|
||||
nyan-cat-formatter
|
||||
pagedown-rails
|
||||
pg (< 1.0)
|
||||
pagedown-bootstrap-rails
|
||||
pg
|
||||
pry-byebug
|
||||
puma
|
||||
pundit
|
||||
rack-mini-profiler
|
||||
rails (= 4.2.10)
|
||||
rails (= 5.2.1)
|
||||
rails-controller-testing
|
||||
rails-i18n
|
||||
rake
|
||||
ransack
|
||||
rest-client
|
||||
rspec-autotest
|
||||
@ -437,19 +446,18 @@ DEPENDENCIES
|
||||
rubocop-rspec
|
||||
rubytree
|
||||
rubyzip
|
||||
sass-rails (>= 5.0.7)
|
||||
sdoc
|
||||
sass-rails
|
||||
selenium-webdriver
|
||||
simplecov
|
||||
slim-rails
|
||||
sorcery
|
||||
spring
|
||||
thread_safe
|
||||
tubesock
|
||||
turbolinks (< 5.0.0)
|
||||
tubesock!
|
||||
turbolinks
|
||||
uglifier
|
||||
web-console
|
||||
webpacker
|
||||
whenever
|
||||
will_paginate
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.1
|
||||
1.16.5
|
||||
|
@ -59,7 +59,7 @@ All problems that have occurred resulted from a more restrictive rights manageme
|
||||
### Start server
|
||||
vagrant ssh
|
||||
cd /vagrant
|
||||
rails s -p 3000
|
||||
rails s -p 3000 -b 0.0.0.0
|
||||
|
||||
### Login to CodeOcean
|
||||
192.168.59.104:3000
|
||||
|
@ -4,7 +4,6 @@ Code Ocean
|
||||
[](https://travis-ci.org/openHPI/codeocean)
|
||||
[](https://codeclimate.com/github/openHPI/codeocean)
|
||||
[](https://codeclimate.com/github/openHPI/codeocean)
|
||||
[](https://gemnasium.com/openHPI/codeocean)
|
||||
|
||||
## Development Setup
|
||||
|
||||
|
1
Vagrantfile
vendored
1
Vagrantfile
vendored
@ -7,6 +7,7 @@ Vagrant.configure(2) do |config|
|
||||
v.memory = 8192
|
||||
end
|
||||
config.vm.network "private_network", ip: "192.168.59.104"
|
||||
config.vm.network "forwarded_port", guest: 3035, host: 3035
|
||||
# config.vm.synced_folder "../data", "/vagrant_data"
|
||||
config.vm.provision "shell", path: "provision.sh", privileged: false
|
||||
end
|
||||
|
@ -10,20 +10,18 @@
|
||||
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
||||
// about supported directives.
|
||||
//
|
||||
//= require jquery
|
||||
//
|
||||
//= require ace/ace
|
||||
//= require chosen.jquery.min
|
||||
//= require jquery-ui.min
|
||||
//= require d3
|
||||
//= require jquery.turbolinks
|
||||
//= require jquery_ujs
|
||||
//= require jstree/jstree.min
|
||||
//= require turbolinks
|
||||
//= require_tree ../../../lib
|
||||
//= require pagedown_bootstrap
|
||||
//= require d3
|
||||
//
|
||||
// lib/assets
|
||||
//= require flash
|
||||
//= require url
|
||||
//
|
||||
// vendor/assets
|
||||
//= require ace/ace
|
||||
//= require ace/ext-language_tools
|
||||
//
|
||||
// app/assets
|
||||
//= require_tree .
|
||||
//= require bootstrap_pagedown
|
||||
//= require markdown.converter
|
||||
//= require markdown.sanitizer
|
||||
//= require markdown.editor
|
||||
//= require ace/ext-language_tools
|
@ -8,20 +8,18 @@ window.CodeOcean = {
|
||||
}
|
||||
};
|
||||
|
||||
$(function() {
|
||||
var ANIMATION_DURATION = 500;
|
||||
var ANIMATION_DURATION = 500;
|
||||
|
||||
$.isController = function(name) {
|
||||
return $('.container[data-controller="' + name + '"]').isPresent();
|
||||
};
|
||||
$.isController = function(name) {
|
||||
return $('.container[data-controller="' + name + '"]').isPresent();
|
||||
};
|
||||
|
||||
$.fn.isPresent = function() {
|
||||
return this.length > 0;
|
||||
};
|
||||
$.fn.isPresent = function() {
|
||||
return this.length > 0;
|
||||
};
|
||||
|
||||
$.fn.scrollTo = function(selector) {
|
||||
$(this).animate({
|
||||
scrollTop: $(selector).offset().top - $(this).offset().top + $(this).scrollTop()
|
||||
}, ANIMATION_DURATION);
|
||||
};
|
||||
});
|
||||
$.fn.scrollTo = function(selector) {
|
||||
$(this).animate({
|
||||
scrollTop: $(selector).offset().top - $(this).offset().top + $(this).scrollTop()
|
||||
}, ANIMATION_DURATION);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(document).ready(function () {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
|
||||
var subMenusSelector = 'ul.dropdown-menu [data-toggle=dropdown]';
|
||||
|
||||
@ -14,7 +14,7 @@ $(document).ready(function () {
|
||||
var menu = $(this).parent().find("ul");
|
||||
var menupos = menu.offset();
|
||||
|
||||
var newPos;
|
||||
var newPos = 0.0;
|
||||
if ((menupos.left + menu.width()) + 30 > $(window).width()) {
|
||||
newPos = -menu.width();
|
||||
} else {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
var CHART_START = window.vis ? vis.moment().add(-1, 'minute') : undefined;
|
||||
var DEFAULT_REFRESH_INTERVAL = 5000;
|
||||
|
||||
@ -51,6 +51,7 @@ $(function() {
|
||||
} else {
|
||||
var jqxhr = $.ajax({
|
||||
dataType: 'json',
|
||||
url: '/admin/dashboard',
|
||||
method: 'GET'
|
||||
});
|
||||
jqxhr.done(function(response) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
|
||||
//Merge all editor components.
|
||||
$.extend(
|
@ -33,6 +33,7 @@ var CodeOceanEditor = {
|
||||
<% @config ||= CodeOcean::Config.new(:code_ocean).read(erb: false) %>
|
||||
sendEvents: <%= @config['codeocean_events'] ? @config['codeocean_events']['enabled'] : false %>,
|
||||
eventURL: "<%= @config['codeocean_events'] ? events_path : '' %>",
|
||||
fileTypeURL: "<%= file_types_path %>",
|
||||
|
||||
|
||||
configureEditors: function () {
|
||||
@ -43,7 +44,7 @@ configureEditors: function () {
|
||||
|
||||
confirmDestroy: function (event) {
|
||||
event.preventDefault();
|
||||
if (confirm($(this).data('message-confirm'))) {
|
||||
if (confirm($(event.target).data('message-confirm'))) {
|
||||
this.destroyFile();
|
||||
}
|
||||
},
|
||||
@ -63,7 +64,7 @@ configureEditors: function () {
|
||||
if ($('#output-' + index).isPresent()) {
|
||||
return $('#output-' + index);
|
||||
} else {
|
||||
var element = $('<pre>').attr('id', 'output-' + index);
|
||||
var element = $('<pre class="mt-2">').attr('id', 'output-' + index);
|
||||
$('#output').append(element);
|
||||
return element;
|
||||
}
|
||||
@ -79,13 +80,13 @@ configureEditors: function () {
|
||||
}
|
||||
},
|
||||
|
||||
getPanelClass: function (result) {
|
||||
getCardClass: function (result) {
|
||||
if (result.stderr && !result.score) {
|
||||
return 'panel-danger';
|
||||
return 'card bg-danger text-white';
|
||||
} else if (result.score < 1) {
|
||||
return 'panel-warning';
|
||||
return 'card bg-warning text-white';
|
||||
} else {
|
||||
return 'panel-success';
|
||||
return 'card bg-success text-white';
|
||||
}
|
||||
},
|
||||
|
||||
@ -107,11 +108,22 @@ configureEditors: function () {
|
||||
progress_bar.css('width', percentage + '%');
|
||||
},
|
||||
|
||||
// The event ready.jstree is fired too early and thus doesn't work.
|
||||
selectFileInJsTree: function(filetree, file_id) {
|
||||
if (!filetree.hasClass('jstree-loading')) {
|
||||
filetree.jstree("deselect_all");
|
||||
filetree.jstree().select_node(file_id);
|
||||
} else {
|
||||
setTimeout(this.selectFileInJsTree.bind(null, filetree, file_id), 250);
|
||||
}
|
||||
},
|
||||
|
||||
showFirstFile: function() {
|
||||
var frame = $('.frame[data-role="main_file"]').isPresent() ? $('.frame[data-role="main_file"]') : $('.frame').first();
|
||||
var file_id = frame.find('.editor').data('file-id');
|
||||
this.setActiveFile(frame.data('filename'), file_id);
|
||||
$('#files').jstree().select_node(file_id);
|
||||
var filetree = $('#files');
|
||||
this.selectFileInJsTree(filetree, file_id);
|
||||
this.showFrame(frame);
|
||||
this.toggleButtonStates();
|
||||
},
|
||||
@ -124,11 +136,11 @@ configureEditors: function () {
|
||||
|
||||
getProgressBarClass: function (percentage) {
|
||||
if (percentage < this.ADEQUATE_PERCENTAGE) {
|
||||
return 'progress-bar progress-bar-striped progress-bar-danger';
|
||||
return 'progress-bar progress-bar-striped bg-danger';
|
||||
} else if (percentage < this.SUCCESSFULL_PERCENTAGE) {
|
||||
return 'progress-bar progress-bar-striped progress-bar-warning';
|
||||
return 'progress-bar progress-bar-striped bg-warning';
|
||||
} else {
|
||||
return 'progress-bar progress-bar-striped progress-bar-success';
|
||||
return 'progress-bar progress-bar-striped bg-success';
|
||||
}
|
||||
},
|
||||
|
||||
@ -158,7 +170,7 @@ configureEditors: function () {
|
||||
category: 'editor_paste',
|
||||
data: pasteObject.text,
|
||||
exercise_id: $('#editor').data('exercise-id'),
|
||||
file_id: $(this).data('file-id')
|
||||
file_id: $(event.target).data('file-id')
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -177,8 +189,8 @@ configureEditors: function () {
|
||||
},
|
||||
|
||||
resizeParentOfAceEditor: function (element){
|
||||
// calculate needed size: window height - position of top of button-bar - 60 for bar itself and margins
|
||||
var windowHeight = window.innerHeight - $('#editor-buttons').offset().top - 60;
|
||||
// calculate needed size: window height - position of top of ACE editor - height of autosave label below editor - 5 for bar margins
|
||||
var windowHeight = window.innerHeight - $(element).offset().top - $('#autosave-label').height() - 5;
|
||||
$(element).parent().height(windowHeight);
|
||||
},
|
||||
|
||||
@ -266,14 +278,29 @@ configureEditors: function () {
|
||||
this.initializeRequestForComments()
|
||||
},
|
||||
|
||||
updateEditorModeToFileTypeID: function (editor, fileTypeID) {
|
||||
var newMode = 'ace/mode/text'
|
||||
|
||||
$.ajax(this.fileTypeURL + '/' + fileTypeID, {
|
||||
dataType: 'json'
|
||||
}).done(function (data) {
|
||||
if (data['editor_mode'] !== null) {
|
||||
newMode = data['editor_mode'];
|
||||
}
|
||||
}).fail(_.noop)
|
||||
.always(function () {
|
||||
ace.edit(editor).session.setMode(newMode);
|
||||
});
|
||||
},
|
||||
|
||||
initializeFileTree: function () {
|
||||
$('#files').jstree($('#files').data('entries'));
|
||||
$('#files').on('click', 'li.jstree-leaf', function (event) {
|
||||
active_file = {
|
||||
filename: $(event.target).parent().text(),
|
||||
id: parseInt($(event.target).parent().attr('id'))
|
||||
};
|
||||
var frame = $('[data-file-id="' + active_file.id + '"]').parent();
|
||||
this.setActiveFile(
|
||||
$(event.target).parent().text(),
|
||||
parseInt($(event.target).parent().attr('id'))
|
||||
);
|
||||
var frame = $('[data-file-id="' + this.active_file.id + '"]').parent();
|
||||
this.showFrame(frame);
|
||||
this.toggleButtonStates();
|
||||
}.bind(this));
|
||||
@ -296,8 +323,8 @@ configureEditors: function () {
|
||||
|
||||
handleSideBarToggle: function() {
|
||||
$('#sidebar').toggleClass('sidebar-col').toggleClass('sidebar-col-collapsed');
|
||||
$('#sidebar-collapsed').toggleClass('hidden');
|
||||
$('#sidebar-uncollapsed').toggleClass('hidden');
|
||||
$('#sidebar-collapsed').toggleClass('d-none');
|
||||
$('#sidebar-uncollapsed').toggleClass('d-none');
|
||||
},
|
||||
|
||||
initializeRegexes: function () {
|
||||
@ -369,17 +396,17 @@ configureEditors: function () {
|
||||
return Modernizr.websockets;
|
||||
},
|
||||
|
||||
populatePanel: function (panel, result, index) {
|
||||
panel.removeClass('panel-default').addClass(this.getPanelClass(result));
|
||||
panel.find('.panel-title .filename').text(result.filename);
|
||||
panel.find('.panel-title .number').text(index + 1);
|
||||
panel.find('.row .col-sm-9').eq(0).find('.number').eq(0).text(result.passed);
|
||||
panel.find('.row .col-sm-9').eq(0).find('.number').eq(1).text(result.count);
|
||||
panel.find('.row .col-sm-9').eq(1).find('.number').eq(0).text(parseFloat((result.score * result.weight).toFixed(2)));
|
||||
panel.find('.row .col-sm-9').eq(1).find('.number').eq(1).text(result.weight);
|
||||
panel.find('.row .col-sm-9').eq(2).html(result.message);
|
||||
if (result.error_messages) panel.find('.row .col-sm-9').eq(3).text(result.error_messages.join(' '));
|
||||
//panel.find('.row .col-sm-9').eq(4).find('a').attr('href', '#output-' + index);
|
||||
populateCard: function (card, result, index) {
|
||||
card.addClass(this.getCardClass(result));
|
||||
card.find('.card-title .filename').text(result.filename);
|
||||
card.find('.card-title .number').text(index + 1);
|
||||
card.find('.row .col-sm-9').eq(0).find('.number').eq(0).text(result.passed);
|
||||
card.find('.row .col-sm-9').eq(0).find('.number').eq(1).text(result.count);
|
||||
card.find('.row .col-sm-9').eq(1).find('.number').eq(0).text(parseFloat((result.score * result.weight).toFixed(2)));
|
||||
card.find('.row .col-sm-9').eq(1).find('.number').eq(1).text(result.weight);
|
||||
card.find('.row .col-sm-9').eq(2).html(result.message);
|
||||
if (result.error_messages) card.find('.row .col-sm-9').eq(3).text(result.error_messages.join(' '));
|
||||
//card.find('.row .col-sm-9').eq(4).find('a').attr('href', '#output-' + index);
|
||||
},
|
||||
|
||||
publishCodeOceanEvent: function (payload) {
|
||||
@ -409,7 +436,7 @@ configureEditors: function () {
|
||||
url: $('#editor').data('errors-url')
|
||||
});
|
||||
jqxhr.always(this.hideSpinner);
|
||||
jqxhr.success(this.renderHint);
|
||||
jqxhr.done(this.renderHint);
|
||||
},
|
||||
|
||||
toggleButtonStates: function () {
|
||||
@ -556,14 +583,14 @@ configureEditors: function () {
|
||||
},
|
||||
|
||||
showOutputBar: function() {
|
||||
$('#output_sidebar_collapsed').addClass('hidden');
|
||||
$('#output_sidebar_uncollapsed').removeClass('hidden');
|
||||
$('#output_sidebar_collapsed').addClass('d-none');
|
||||
$('#output_sidebar_uncollapsed').removeClass('d-none');
|
||||
$('#output_sidebar').removeClass('output-col-collapsed').addClass('output-col');
|
||||
},
|
||||
|
||||
hideOutputBar: function() {
|
||||
$('#output_sidebar_collapsed').removeClass('hidden');
|
||||
$('#output_sidebar_uncollapsed').addClass('hidden');
|
||||
$('#output_sidebar_collapsed').removeClass('d-none');
|
||||
$('#output_sidebar_uncollapsed').addClass('d-none');
|
||||
$('#output_sidebar').removeClass('output-col').addClass('output-col-collapsed');
|
||||
},
|
||||
|
||||
@ -572,16 +599,17 @@ configureEditors: function () {
|
||||
},
|
||||
|
||||
initializeDescriptionToggle: function() {
|
||||
$('#exercise-headline').on('click', this.toggleDescriptionPanel.bind(this));
|
||||
$('a#toggle').on('click', this.toggleDescriptionPanel.bind(this));
|
||||
$('#exercise-headline').on('click', this.toggleDescriptionCard.bind(this));
|
||||
$('a#toggle').on('click', this.toggleDescriptionCard.bind(this));
|
||||
},
|
||||
|
||||
toggleDescriptionPanel: function() {
|
||||
$('#description-panel').toggleClass('description-panel-collapsed').toggleClass('description-panel');
|
||||
toggleDescriptionCard: function() {
|
||||
$('#description-card').toggleClass('description-card-collapsed').toggleClass('description-card');
|
||||
$('#description-symbol').toggleClass('fa-chevron-down').toggleClass('fa-chevron-right');
|
||||
var toggle = $('a#toggle');
|
||||
toggle.text(toggle.text() == toggle.data('hide') ? toggle.data('show') : toggle.data('hide'));
|
||||
this.resizeAceEditors();
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -9,7 +9,7 @@ CodeOceanEditorEvaluation = {
|
||||
this.clearScoringOutput();
|
||||
this.createSubmission('#assess', null, function (response) {
|
||||
this.showSpinner($('#assess'));
|
||||
$('#score_div').removeClass('hidden');
|
||||
$('#score_div').removeClass('d-none');
|
||||
var url = response.score_url;
|
||||
this.initializeSocketForScoring(url);
|
||||
}.bind(this));
|
||||
@ -26,9 +26,9 @@ CodeOceanEditorEvaluation = {
|
||||
|
||||
printScoringResult: function (result, index) {
|
||||
$('#results').show();
|
||||
var panel = $('#dummies').children().first().clone();
|
||||
this.populatePanel(panel, result, index);
|
||||
$('#results ul').first().append(panel);
|
||||
var card = $('#dummies').children().first().clone();
|
||||
this.populateCard(card, result, index);
|
||||
$('#results ul').first().append(card);
|
||||
},
|
||||
|
||||
printScoringResults: function (response) {
|
||||
@ -60,7 +60,7 @@ CodeOceanEditorEvaluation = {
|
||||
renderHint: function (object) {
|
||||
var hint = object.data || object.hint;
|
||||
if (hint) {
|
||||
$('#hint .panel-body').text(hint);
|
||||
$('#hint .card-body').text(hint);
|
||||
$('#hint').fadeIn();
|
||||
}
|
||||
},
|
||||
@ -152,22 +152,22 @@ CodeOceanEditorEvaluation = {
|
||||
var element = this.findOrCreateOutputElement(index);
|
||||
if (!colorize) {
|
||||
if (output.stdout != undefined && output.stdout != '') {
|
||||
element.append(output.stdout)
|
||||
//element.text(element.text() + output.stdout)
|
||||
//element.append(output.stdout)
|
||||
element.text(element.text() + output.stdout)
|
||||
}
|
||||
|
||||
if (output.stderr != undefined && output.stderr != '') {
|
||||
element.append('StdErr: ' + output.stderr);
|
||||
//element.text('StdErr: ' + element.text() + output.stderr);
|
||||
//element.append('StdErr: ' + output.stderr);
|
||||
element.text('StdErr: ' + element.text() + output.stderr);
|
||||
}
|
||||
|
||||
} else if (output.stderr) {
|
||||
element.addClass('text-warning').append(output.stderr);
|
||||
//element.addClass('text-warning').text(element.text() + output.stderr);
|
||||
//element.addClass('text-warning').append(output.stderr);
|
||||
element.addClass('text-warning').text(element.text() + output.stderr);
|
||||
this.QaApiOutputBuffer.stderr += output.stderr;
|
||||
} else if (output.stdout) {
|
||||
element.addClass('text-success').append(output.stdout);
|
||||
//element.addClass('text-success').text(element.text() + output.stdout);
|
||||
//element.addClass('text-success').append(output.stdout);
|
||||
element.addClass('text-success').text(element.text() + output.stdout);
|
||||
this.QaApiOutputBuffer.stdout += output.stdout;
|
||||
} else {
|
||||
element.addClass('text-muted').text($('#output').data('message-no-output'));
|
@ -2,8 +2,9 @@ CodeOceanEditorWebsocket = {
|
||||
websocket: null,
|
||||
|
||||
createSocketUrl: function(url) {
|
||||
var sockURL = new URL(window.location);
|
||||
sockURL.pathname = url;
|
||||
var sockURL = new URL(url, window.location);
|
||||
// not needed any longer, we put it directly into the url: sockURL.pathname = url;
|
||||
|
||||
// sanitize socket protocol string, strip trailing slash and other malicious chars if they are there
|
||||
sockURL.protocol = '<%= DockerClient.config['ws_client_protocol']&.match(/(\w+):*\/*/)&.to_a&.at(1) %>:';
|
||||
|
||||
|
@ -90,7 +90,6 @@ CodeOceanEditorFlowr = {
|
||||
var collapsibleTileHtml = self.flowrResultHtml
|
||||
.replace(/{{collapseId}}/g, 'collapse-' + index).replace(/{{headingId}}/g, 'heading-' + index);
|
||||
var resultTile = $(collapsibleTileHtml);
|
||||
|
||||
var questionUrl = 'https://stackoverflow.com/questions/' + result.question_id;
|
||||
|
||||
var header = resultTile.find('h4 > a');
|
||||
@ -162,7 +161,7 @@ CodeOceanEditorRequestForComments = {
|
||||
$.flash.success({text: $('#askForCommentsButton').data('message-success')});
|
||||
// trigger a run
|
||||
this.runSubmission.call(this, submission);
|
||||
}.bind(this)).error(this.ajaxError.bind(this));
|
||||
}.bind(this)).fail(this.ajaxError.bind(this));
|
||||
};
|
||||
|
||||
this.createSubmission($('#requestComments'), null, createRequestForComments.bind(this));
|
||||
|
@ -2,19 +2,19 @@ CodeOceanEditorPrompt = {
|
||||
prompt: '#prompt',
|
||||
|
||||
showPrompt: function(msg) {
|
||||
var label = $('#prompt .input-group-addon');
|
||||
var label = $('#prompt .input-group-text');
|
||||
var prompt = $(this.prompt);
|
||||
label.text(msg.data || label.data('prompt'));
|
||||
if (prompt.isPresent() && prompt.hasClass('hidden')) {
|
||||
prompt.removeClass('hidden');
|
||||
if (prompt.isPresent() && prompt.hasClass('d-none')) {
|
||||
prompt.removeClass('d-none');
|
||||
}
|
||||
$('#prompt input').focus();
|
||||
},
|
||||
|
||||
hidePrompt: function() {
|
||||
var prompt = $(this.prompt);
|
||||
if (prompt.isPresent() && !prompt.hasClass('hidden')) {
|
||||
prompt.addClass('hidden');
|
||||
if (prompt.isPresent() && !prompt.hasClass('d-none')) {
|
||||
prompt.addClass('d-none');
|
||||
}
|
||||
},
|
||||
|
@ -106,14 +106,16 @@ CodeOceanEditorSubmissions = {
|
||||
this.ajax({
|
||||
method: 'GET',
|
||||
url: $('#start-over').data('url')
|
||||
}).success(function(response) {
|
||||
}).done(function(response) {
|
||||
this.hideSpinner();
|
||||
_.each(this.editors, function(editor) {
|
||||
var file_id = $(editor.container).data('file-id');
|
||||
var file = _.find(response.files, function(file) {
|
||||
return file.id === file_id;
|
||||
});
|
||||
editor.setValue(file.content);
|
||||
if(file){
|
||||
editor.setValue(file.content);
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
@ -153,7 +155,7 @@ CodeOceanEditorSubmissions = {
|
||||
$('#stop').data('url', submission.stop_url);
|
||||
this.running = true;
|
||||
this.showSpinner($('#run'));
|
||||
$('#score_div').addClass('hidden');
|
||||
$('#score_div').addClass('d-none');
|
||||
this.toggleButtonStates();
|
||||
var url = submission.run_url.replace(this.FILENAME_URL_PLACEHOLDER, this.active_file.filename.replace(/#$/,'')); // remove # if it is the last character, this is not part of the filename and just an anchor
|
||||
this.initializeSocketForRunning(url);
|
||||
@ -173,8 +175,8 @@ CodeOceanEditorSubmissions = {
|
||||
if ($('#test').is(':visible')) {
|
||||
this.createSubmission('#test', null, function(response) {
|
||||
this.showSpinner($('#test'));
|
||||
$('#score_div').addClass('hidden');
|
||||
var url = response.test_url.replace(this.FILENAME_URL_PLACEHOLDER, this.active_file.filenamereplace(/#$/,'')); // remove # if it is the last character, this is not part of the filename and just an anchor
|
||||
$('#score_div').addClass('d-none');
|
||||
var url = response.test_url.replace(this.FILENAME_URL_PLACEHOLDER, this.active_file.filename.replace(/#$/,'')); // remove # if it is the last character, this is not part of the filename and just an anchor
|
||||
this.initializeSocketForTesting(url);
|
||||
}.bind(this));
|
||||
}
|
@ -38,10 +38,10 @@ CodeOceanEditorTurtle = {
|
||||
|
||||
showCanvas: function () {
|
||||
if ($('#turtlediv').isPresent()
|
||||
&& this.turtlecanvas.hasClass('hidden')) {
|
||||
&& this.turtlecanvas.hasClass('d-none')) {
|
||||
// initialize two-column layout
|
||||
$('#output-col1').addClass('col-lg-7 col-md-7 two-column');
|
||||
this.turtlecanvas.removeClass('hidden');
|
||||
this.turtlecanvas.removeClass('d-none');
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
if ($.isController('error_templates')) {
|
||||
$('#add-attribute').find('button').on('click', function () {
|
||||
$.ajax(location + '/attribute.json', {
|
||||
@ -8,9 +8,9 @@ $(function() {
|
||||
dataType: 'json',
|
||||
error_template_attribute_id: $('#add-attribute').find('select').val()
|
||||
}
|
||||
}).success(function () {
|
||||
}).done(function () {
|
||||
location.reload();
|
||||
}).error(function (error) {
|
||||
}).fail(function (error) {
|
||||
$.flash.danger({text: error.statusText});
|
||||
});
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
if ($.isController('execution_environments')) {
|
||||
if ($('.edit_execution_environment, .new_execution_environment').isPresent()) {
|
||||
new MarkdownEditor('#execution_environment_help');
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
if ($.isController('exercise_collections')) {
|
||||
var dataElement = $('#data');
|
||||
var exerciseList = $('#exercise-list');
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
// /exercises/38/statistics good for testing
|
||||
|
||||
if ($.isController('exercises') && $('.graph-functions-2').isPresent()) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
// ruby part adds the relative_url_root, if it is set.
|
||||
var ACE_FILES_PATH = '<%= (defined? Rails.application.config.relative_url_root) && Rails.application.config.relative_url_root != nil && Rails.application.config.relative_url_root != "" ? Rails.application.config.relative_url_root : "" %>' + '/assets/ace/';
|
||||
var THEME = 'ace/theme/textmate';
|
||||
@ -66,10 +66,9 @@ $(function() {
|
||||
$('#files').append(html);
|
||||
$('#files li:last select[name*="file_type_id"]').val(getSelectedExecutionEnvironment().file_type_id);
|
||||
$('#files li:last select').chosen(window.CodeOcean.CHOSEN_OPTIONS);
|
||||
$('#files li:last select').remove();
|
||||
$('#files li:last>div:last').removeClass('in').addClass('show')
|
||||
$('body, html').scrollTo('#add-file');
|
||||
// if we collapse the file forms by default, we need to click on the new element in order to open it.
|
||||
// however, this crashes for more files (if we add several ones by clicking the add button more often), since the elements are probably not correctly added to the files list.
|
||||
//$('#files li:last>div:first>a>div').click();
|
||||
|
||||
// initialize the ace editor for the new textarea.
|
||||
// pass the correct index and the last ace editor under the node files. this is the last one, since we just added it.
|
||||
@ -93,7 +92,7 @@ $(function() {
|
||||
|
||||
var deleteFile = function(event) {
|
||||
event.preventDefault();
|
||||
var fileUrl = $(this).data('file-url');
|
||||
var fileUrl = $(event.target).data('file-url');
|
||||
|
||||
if (confirm('<%= I18n.t('shared.confirm_destroy') %>')) {
|
||||
var jqxhr = $.ajax({
|
||||
@ -139,9 +138,9 @@ $(function() {
|
||||
var enableBatchUpdate = function() {
|
||||
$('thead .batch a').on('click', function(event) {
|
||||
event.preventDefault();
|
||||
if (!$(this).data('toggled')) {
|
||||
$(this).data('toggled', true);
|
||||
$(this).text($(this).data('text'));
|
||||
if (!$(event.target).data('toggled')) {
|
||||
$(event.target).data('toggled', true);
|
||||
$(event.target).text($(event.target).data('text'));
|
||||
buildCheckboxes();
|
||||
} else {
|
||||
performBatchUpdate();
|
||||
@ -199,7 +198,7 @@ $(function() {
|
||||
var observeFileRoleChanges = function() {
|
||||
$(document).on('change', 'select[name$="[role]"]', function() {
|
||||
var is_test_file = $(this).val() === 'teacher_defined_test';
|
||||
var parent = $(this).parents('.panel');
|
||||
var parent = $(this).parents('.card');
|
||||
var fields = parent.find('.test-related-fields');
|
||||
if (is_test_file) {
|
||||
fields.slideDown();
|
||||
@ -262,17 +261,13 @@ $(function() {
|
||||
jqxhr.fail(ajaxError);
|
||||
}
|
||||
|
||||
if ($.isController('exercises')) {
|
||||
if ($.isController('exercises') || $.isController('submissions')) {
|
||||
// ignore tags table since it is in the dom before other tables
|
||||
if ($('table:not(#tags-table)').isPresent()) {
|
||||
enableBatchUpdate();
|
||||
} else if ($('.edit_exercise, .new_exercise').isPresent()) {
|
||||
execution_environments = $('form').data('execution-environments');
|
||||
file_types = $('form').data('file-types');
|
||||
// new MarkdownEditor('#exercise_instructions');
|
||||
// new MarkdownEditor('#exercise_description')
|
||||
// todo: add an ace editor for each file
|
||||
new PagedownEditor('#exercise_description');
|
||||
|
||||
enableInlineFileCreation();
|
||||
inferFileAttributes();
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
var grid = $('#tag-grid');
|
||||
|
||||
if ($.isController('external_users') && grid.isPresent()) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
var CHOSEN_OPTIONS = {
|
||||
allow_single_deselect: true,
|
||||
disable_search_threshold: 5,
|
||||
@ -14,11 +14,11 @@ $(function() {
|
||||
var alternative_input = parent.find('.alternative-input');
|
||||
|
||||
if (alternative_input.attr('disabled')) {
|
||||
$(this).text($(this).data('text-toggled'));
|
||||
$(this).text($(event.target).data('text-toggled'));
|
||||
original_input.attr('disabled', true).hide();
|
||||
alternative_input.attr('disabled', false).show();
|
||||
} else {
|
||||
$(this).text($(this).data('text-initial'));
|
||||
$(this).text($(event.target).data('text-initial'));
|
||||
alternative_input.attr('disabled', true).hide();
|
||||
original_input.attr('disabled', false).show();
|
||||
}
|
||||
@ -26,5 +26,27 @@ $(function() {
|
||||
});
|
||||
|
||||
window.CodeOcean.CHOSEN_OPTIONS = CHOSEN_OPTIONS;
|
||||
$('select:visible').chosen(CHOSEN_OPTIONS);
|
||||
chosen_inputs = $('select').filter(function(){
|
||||
return !$(this).parents('ul').is('#dummies');
|
||||
});
|
||||
|
||||
// enable chosen hook when editing an exercise to update ace code highlighting
|
||||
if ($.isController('exercises') && $('.edit_exercise, .new_exercise').isPresent()) {
|
||||
chosen_inputs.filter(function(){
|
||||
return $(this).attr('id').includes('file_type_id');
|
||||
}).on('change chosen:ready', function(event, parameter) {
|
||||
// Set ACE editor mode (for code highlighting) on change of file type and after initialization
|
||||
editorInstance = $(event.target).closest('.card-body').find('.editor')[0];
|
||||
selectedFileType = event.target.value;
|
||||
CodeOceanEditor.updateEditorModeToFileTypeID(editorInstance, selectedFileType);
|
||||
})
|
||||
}
|
||||
|
||||
chosen_inputs.chosen(CHOSEN_OPTIONS);
|
||||
});
|
||||
|
||||
// Remove some elements before going back to an older site. Otherwise, they might not work.
|
||||
$(document).on('turbolinks:before-cache', function() {
|
||||
$('.chosen-container').remove();
|
||||
$('#wmd-button-row-description').remove();
|
||||
});
|
||||
|
@ -9,7 +9,7 @@
|
||||
});
|
||||
editor.setShowPrintMargin(false);
|
||||
var session = editor.getSession();
|
||||
session.setMode('markdown');
|
||||
session.setMode('ace/mode/markdown');
|
||||
session.setUseWrapMode(true);
|
||||
session.setValue($(selector).val());
|
||||
};
|
||||
|
@ -1,10 +0,0 @@
|
||||
(function() {
|
||||
var ACE_FILES_PATH = '/assets/ace/';
|
||||
|
||||
window.PagedownEditor = function(selector) {
|
||||
var converter = Markdown.getSanitizingConverter();
|
||||
var editor = new Markdown.Editor( converter );
|
||||
|
||||
editor.run();
|
||||
};
|
||||
})();
|
2131
app/assets/javascripts/pagedown/markdown.editor.js.erb
Normal file
2131
app/assets/javascripts/pagedown/markdown.editor.js.erb
Normal file
File diff suppressed because it is too large
Load Diff
43
app/assets/javascripts/pagedown/pagedown.js.erb
Normal file
43
app/assets/javascripts/pagedown/pagedown.js.erb
Normal file
@ -0,0 +1,43 @@
|
||||
//= require markdown.converter
|
||||
// markdown.editor is slightly adjusted to work with Bootstrap 4.
|
||||
// Taken from https://github.com/hughevans/pagedown-bootstrap-rails, V2.1.4
|
||||
//= require markdown.editor
|
||||
//= require markdown.sanitizer
|
||||
//= require markdown.extra
|
||||
|
||||
renderPagedown = function() {
|
||||
$(".wmd-output").each(function (i) {
|
||||
const converter = new Markdown.Converter();
|
||||
const content = $(this).html();
|
||||
return $(this).html(converter.makeHtml(content));
|
||||
})
|
||||
};
|
||||
|
||||
createPagedownEditor = function( selector, context ) {
|
||||
if (context == null) { context = 'body'; }
|
||||
return $(selector, context).each(function(i, input) {
|
||||
if ($(input).data('is_rendered')) {
|
||||
return;
|
||||
}
|
||||
const attr = $(input).attr('id').split('wmd-input')[1];
|
||||
const converter = new Markdown.Converter();
|
||||
Markdown.Extra.init(converter);
|
||||
const help = {
|
||||
handler() {
|
||||
window.open('http://daringfireball.net/projects/markdown/syntax');
|
||||
return false;
|
||||
},
|
||||
title: "<%= I18n.t('components.markdown_editor.help', default: 'Markdown Editing Help') %>"
|
||||
};
|
||||
|
||||
const editor = new Markdown.Editor(converter, attr, help);
|
||||
editor.run();
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
return $(input).data('is_rendered', true);
|
||||
});
|
||||
};
|
||||
|
||||
$(document).on('turbolinks:load', function() {
|
||||
renderPagedown();
|
||||
return createPagedownEditor('.wmd-input');
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
var ENTER_KEY_CODE = 13;
|
||||
|
||||
var clearOutput = function() {
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(document).ready(function(){
|
||||
$(document).on('turbolinks:load', function(){
|
||||
(function vendorTableSorter(){
|
||||
/*
|
||||
SortTable
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(document).ready(function () {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
|
||||
function manageActivityHistory(prefix) {
|
||||
var containerId = prefix + '-activity-history';
|
||||
@ -49,7 +49,7 @@ $(document).ready(function () {
|
||||
|
||||
var refreshData = function (callback) {
|
||||
var params = new URLSearchParams(window.location.search.slice(1));
|
||||
var jqxhr = $.ajax(prefix + '-activity-history.json', {
|
||||
var jqxhr = $.ajax('/statistics/graphs/' + prefix + '-activity-history.json', {
|
||||
dataType: 'json',
|
||||
data: {from: params.get('from'), to: params.get('to'), interval: params.get('interval')},
|
||||
method: 'GET'
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(document).ready(function () {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
if ($.isController('statistics') && $('.graph#user-activity').isPresent()) {
|
||||
|
||||
function manageGraph(containerId, url, refreshAfter) {
|
||||
@ -101,7 +101,7 @@ $(document).ready(function () {
|
||||
});
|
||||
}
|
||||
|
||||
manageGraph('user-activity', 'graphs/user-activity', 10);
|
||||
manageGraph('rfc-activity', 'graphs/rfc-activity', 30);
|
||||
manageGraph('user-activity', '/statistics/graphs/user-activity', 10);
|
||||
manageGraph('rfc-activity', '/statistics/graphs/rfc-activity', 30);
|
||||
}
|
||||
});
|
||||
|
@ -1,34 +1,44 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
|
||||
var ACE_FILES_PATH = '/assets/ace/';
|
||||
var THEME = 'ace/theme/textmate';
|
||||
|
||||
var currentSubmission = 0;
|
||||
var active_file = undefined;
|
||||
var fileTrees = []
|
||||
var fileTrees = [];
|
||||
var editor = undefined;
|
||||
var fileTypeById = {}
|
||||
var fileTypeById = {};
|
||||
|
||||
var showActiveFile = function() {
|
||||
var session = editor.getSession();
|
||||
var fileType = fileTypeById[active_file.file_type_id]
|
||||
var fileType = fileTypeById[active_file.file_type_id];
|
||||
session.setMode(fileType.editor_mode);
|
||||
session.setTabSize(fileType.indent_size);
|
||||
session.setValue(active_file.content);
|
||||
session.setUseSoftTabs(true);
|
||||
session.setUseWrapMode(true);
|
||||
|
||||
// The event ready.jstree is fired too early and thus doesn't work.
|
||||
var selectFileInJsTree = function() {
|
||||
if (!filetree.hasClass('jstree-loading')) {
|
||||
filetree.jstree("deselect_all");
|
||||
filetree.jstree().select_node(active_file.file_id);
|
||||
} else {
|
||||
setTimeout(selectFileInJsTree, 250);
|
||||
}
|
||||
};
|
||||
|
||||
filetree = $(fileTrees[currentSubmission]);
|
||||
selectFileInJsTree();
|
||||
// Finally change jstree element to prevent flickering
|
||||
showFileTree(currentSubmission);
|
||||
filetree = $(fileTrees[currentSubmission])
|
||||
filetree.jstree("deselect_all");
|
||||
filetree.jstree().select_node(active_file.file_id);
|
||||
};
|
||||
|
||||
var initializeFileTree = function() {
|
||||
$('.files').each(function(index, element) {
|
||||
fileTree = $(element).jstree($(element).data('entries'));
|
||||
fileTree.on('click', 'li.jstree-leaf', function() {
|
||||
var id = parseInt($(this).attr('id'))
|
||||
var id = parseInt($(this).attr('id'));
|
||||
_.each(files[currentSubmission], function(file) {
|
||||
if (file.file_id === id) {
|
||||
active_file = file;
|
||||
@ -42,8 +52,8 @@ $(function() {
|
||||
|
||||
var showFileTree = function(index) {
|
||||
$('.files').hide();
|
||||
$(fileTrees[index].context).show();
|
||||
}
|
||||
$(fileTrees[index]).show();
|
||||
};
|
||||
|
||||
if ($.isController('exercises') && $('#timeline').isPresent()) {
|
||||
|
||||
@ -85,7 +95,7 @@ $(function() {
|
||||
if (file.name === active_file.name) {
|
||||
fileIndex = index;
|
||||
}
|
||||
})
|
||||
});
|
||||
active_file = currentFiles[fileIndex];
|
||||
showActiveFile();
|
||||
});
|
||||
@ -94,10 +104,10 @@ $(function() {
|
||||
clearInterval(playInterval);
|
||||
playInterval = undefined;
|
||||
playButton.find('span.fa').removeClass('fa-pause').addClass('fa-play')
|
||||
}
|
||||
};
|
||||
|
||||
playButton.on('click', function(event) {
|
||||
if (playInterval == undefined) {
|
||||
if (playInterval === undefined) {
|
||||
playInterval = setInterval(function() {
|
||||
if ($.isController('exercises') && $('#timeline').isPresent() && slider.val() < submissions.length - 1) {
|
||||
slider.val(parseInt(slider.val()) + 1);
|
||||
@ -112,7 +122,7 @@ $(function() {
|
||||
}
|
||||
});
|
||||
|
||||
active_file = files[0][0]
|
||||
active_file = files[0][0];
|
||||
initializeFileTree();
|
||||
showActiveFile();
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
$(function() {
|
||||
$(document).on('turbolinks:load', function() {
|
||||
// http://localhost:3333/exercises/38/statistics good for testing
|
||||
// originally at--> localhost:3333/exercises/69/statistics
|
||||
|
||||
@ -30,7 +30,7 @@ $(function() {
|
||||
var studentTime = minutes_array[i];
|
||||
|
||||
for (var j = 0; j < studentTime; j++){
|
||||
if (minutes_count[j] == undefined){
|
||||
if (minutes_count[j] === undefined){
|
||||
minutes_count[j] = 0;
|
||||
} else{
|
||||
minutes_count[j] += 1;
|
||||
@ -173,23 +173,23 @@ $(function() {
|
||||
//}
|
||||
|
||||
// DRAW THE SECOND GRAPH ------------------------------------------------------------------------------
|
||||
//<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
|
||||
function draw_bar_graph() {
|
||||
var group_incrament = 5;
|
||||
var group_ranges = group_incrament; // just for the start
|
||||
var number_of_bars = 40;
|
||||
var group_increment = Math.ceil(maximum_minutes / number_of_bars); // range in minutes
|
||||
var group_ranges = group_increment; // just for the start
|
||||
var minutes_array_for_bar = [];
|
||||
|
||||
do {
|
||||
var section_value = 0;
|
||||
for (var i = 0; i < minutes_array.length; i++) {
|
||||
if ((minutes_array[i] < group_ranges) && (minutes_array[i] >= (group_ranges - group_incrament))) {
|
||||
if ((minutes_array[i] < group_ranges) && (minutes_array[i] >= (group_ranges - group_increment))) {
|
||||
section_value++;
|
||||
}
|
||||
}
|
||||
minutes_array_for_bar.push(section_value);
|
||||
group_ranges += group_incrament;
|
||||
group_ranges += group_increment;
|
||||
}
|
||||
while (group_ranges < maximum_minutes + group_incrament);
|
||||
while (group_ranges < maximum_minutes + group_increment);
|
||||
|
||||
//console.log(minutes_array_for_bar); // this var used as the bars
|
||||
//minutes_array_for_bar = [39, 20, 28, 20, 39, 34, 26, 23, 16, 8];
|
||||
@ -199,30 +199,30 @@ $(function() {
|
||||
|
||||
|
||||
var width_ratio = .8;
|
||||
if (getWidth()*width_ratio > 1000){
|
||||
width_ratio = 1000/getWidth();
|
||||
}
|
||||
var height_ratio = .7; // percent of height
|
||||
|
||||
var margin = {top: 100, right: 20, bottom: 70, left: 70},//30,50
|
||||
width = (getWidth() * width_ratio) - margin.left - margin.right,
|
||||
height = (width * height_ratio) - margin.top - margin.bottom;
|
||||
|
||||
var x = d3.scale.ordinal()
|
||||
.rangeRoundBands([0, width], .1);
|
||||
var x = d3.scaleBand()
|
||||
.range([0, width], .1);
|
||||
|
||||
var y = d3.scaleLinear()
|
||||
.range([0,height-(margin.top + margin.bottom)]);
|
||||
|
||||
|
||||
var xAxis = d3.svg.axis()
|
||||
.scale(x)
|
||||
.orient("bottom")
|
||||
var xAxis = d3.axisBottom(x)
|
||||
.ticks(10);
|
||||
|
||||
|
||||
var yAxis = d3.svg.axis()
|
||||
.scale(d3.scaleLinear().domain([0,max_of_array]).range([height,0]))//y
|
||||
.orient("left")
|
||||
var yAxis = d3
|
||||
.axisLeft(d3.scaleLinear().domain([0,max_of_array]).range([height,0]))//y
|
||||
.ticks(10)
|
||||
.innerTickSize(-width);
|
||||
.tickSizeInner(-width);
|
||||
|
||||
var tip = d3.tip()
|
||||
.attr('class', 'd3-tip')
|
||||
@ -241,8 +241,8 @@ $(function() {
|
||||
|
||||
x.domain(minutes_array_for_bar.map(function (d, i) {
|
||||
i++;
|
||||
var high_side = i * group_incrament;
|
||||
var low_side = high_side - group_incrament;
|
||||
var high_side = i * group_increment;
|
||||
var low_side = high_side - group_increment;
|
||||
return (low_side+"-"+high_side);
|
||||
}));
|
||||
|
||||
@ -253,7 +253,14 @@ $(function() {
|
||||
svg.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("transform", "translate(0," + height + ")")
|
||||
.call(xAxis);
|
||||
.call(xAxis)
|
||||
.selectAll("text")
|
||||
.style("text-anchor", "end")
|
||||
.attr("dx", "-.8em")
|
||||
.attr("dy", ".15em")
|
||||
.attr("transform", function(d) {
|
||||
return "rotate(-45)"
|
||||
});
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "y axis")
|
||||
@ -278,7 +285,7 @@ $(function() {
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("x", width / 2)
|
||||
.attr("y", height)
|
||||
.attr("dy", ((height / 20) + 20) + 'px')
|
||||
.attr("dy", ((height / 20) + 40) + 'px')
|
||||
.text("Working Time (Minutes)")
|
||||
.style('font-size', 14);
|
||||
|
||||
@ -291,10 +298,10 @@ $(function() {
|
||||
.data(minutes_array_for_bar)
|
||||
.enter().append("rect")
|
||||
.attr("class", "bar")
|
||||
.attr("x", function(d,i) { var bar_incriment = width/ minutes_array_for_bar.length;
|
||||
var bar_x = i * bar_incriment;
|
||||
.attr("x", function(d,i) { var bar_increment = width / minutes_array_for_bar.length;
|
||||
var bar_x = i * bar_increment;
|
||||
return (bar_x)})
|
||||
.attr("width", x.rangeBand())
|
||||
.attr("width", x.bandwidth())
|
||||
.attr("y", function(d) { return height - y(d); })
|
||||
.attr("height", function(d) { return y(d); })
|
||||
.on('mouseover', tip.show)
|
||||
@ -311,7 +318,7 @@ $(function() {
|
||||
.style('text-decoration', 'underline');
|
||||
|
||||
}
|
||||
// draw_bar_graph();
|
||||
draw_bar_graph();
|
||||
}
|
||||
|
||||
});
|
||||
|
@ -10,10 +10,11 @@
|
||||
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
||||
* file per style scope.
|
||||
*
|
||||
*= require pagedown_bootstrap
|
||||
*
|
||||
* lib/assets
|
||||
*= require flash
|
||||
*
|
||||
* app/assets
|
||||
*= require_tree .
|
||||
*= require_tree ../../../lib
|
||||
*= require_tree ../../../vendor/assets/stylesheets/
|
||||
*= require_self
|
||||
*= require bootstrap_pagedown
|
||||
*= require markdown
|
||||
*/
|
||||
|
@ -3,6 +3,10 @@ h1 {
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.lead {
|
||||
font-size: 16px;
|
||||
color: rgba(70, 70, 70, 1);
|
||||
@ -15,28 +19,47 @@ i.fa {
|
||||
pre {
|
||||
background-color: #FAFAFA;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding: .25rem!important;
|
||||
border: 1px solid #CCCCCC;
|
||||
}
|
||||
|
||||
span.caret {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.btn {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-weight: 500;
|
||||
:not(.btn-lg) {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin: 0;
|
||||
border: 1px solid #CCCCCC;
|
||||
padding: 0.125rem !important;
|
||||
height: 1.25rem !important;
|
||||
|
||||
.progress-bar {
|
||||
line-height: initial;
|
||||
min-width: 2em;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.attribute-row + .attribute-row {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.badge {
|
||||
.badge-pill {
|
||||
font-size: 100%;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.container[data-controller] {
|
||||
|
@ -3,10 +3,14 @@
|
||||
}
|
||||
|
||||
.dropdown-submenu > .dropdown-menu {
|
||||
top: 0;
|
||||
top: -0.2em;
|
||||
left: 100%;
|
||||
}
|
||||
|
||||
.dropdown-submenu.open > ul.dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-submenu > a:after {
|
||||
display: block;
|
||||
content: " ";
|
||||
@ -25,11 +29,11 @@
|
||||
border-left-color: #ffffff;
|
||||
}
|
||||
|
||||
.dropdown-submenu.pull-left {
|
||||
.dropdown-submenu.float-left {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.dropdown-submenu.pull-left > .dropdown-menu {
|
||||
.dropdown-submenu.float-left > .dropdown-menu {
|
||||
left: -100%;
|
||||
margin-left: 10px;
|
||||
-webkit-border-radius: 6px 0 6px 6px;
|
||||
|
@ -45,10 +45,6 @@ button i.fa-spin {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
button {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
button, .btn-group {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@ -144,7 +140,8 @@ button i.fa-spin {
|
||||
min-height: 1px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
box-sizing: border-box
|
||||
box-sizing: border-box;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.output-col-collapsed {
|
||||
@ -155,7 +152,8 @@ button i.fa-spin {
|
||||
min-height: 1px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
box-sizing: border-box
|
||||
box-sizing: border-box;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.enforce-top-margin {
|
||||
@ -166,14 +164,14 @@ button i.fa-spin {
|
||||
margin-right: 10px !important;
|
||||
}
|
||||
|
||||
.description-panel-collapsed {
|
||||
.description-card-collapsed {
|
||||
-webkit-transition: width 2s;
|
||||
transition: width 2s;
|
||||
height: 0px;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.description-panel {
|
||||
.description-card {
|
||||
height: auto;
|
||||
-webkit-transition: height 2s;
|
||||
transition: height 2s;
|
||||
|
@ -57,10 +57,6 @@ rect.value-bar {
|
||||
}
|
||||
}
|
||||
|
||||
.table-responsive#exercise-list {
|
||||
max-height: 512px;
|
||||
}
|
||||
|
||||
.exercise-id-tooltip {
|
||||
position: absolute;
|
||||
display: none;
|
||||
|
@ -35,11 +35,19 @@ input[type='file'] {
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
.lead.description-panel-collapsed {
|
||||
.lead.description-card-collapsed {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-toggle="collapse"] .fa:before {
|
||||
content: "\f139";
|
||||
}
|
||||
|
||||
[data-toggle="collapse"].collapsed .fa:before {
|
||||
content: "\f13a";
|
||||
}
|
||||
|
||||
// Graph Settings
|
||||
|
||||
.axis path {
|
||||
|
@ -68,8 +68,10 @@
|
||||
.result {
|
||||
margin-right: 10px;
|
||||
margin-top: 20px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
min-width: 10px;
|
||||
max-width: 10px;
|
||||
min-height: 10px;
|
||||
max-height: 10px;
|
||||
}
|
||||
|
||||
.passed {
|
||||
@ -127,7 +129,7 @@
|
||||
background-color:#f9f9f9
|
||||
}
|
||||
|
||||
.ace_tooltip {
|
||||
:not(.allow_ace_tooltip) > .ace_tooltip {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
@ -76,8 +76,8 @@ tr.highlight {
|
||||
grid-gap: 10px;
|
||||
|
||||
> a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
color: #fff !important;
|
||||
text-decoration: none !important;
|
||||
|
||||
> div {
|
||||
border: 2px solid #0055ba;
|
||||
|
@ -6,7 +6,7 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
after_action :verify_authorized, except: [:help, :welcome]
|
||||
before_action :set_locale, :allow_iframe_requests
|
||||
protect_from_forgery(with: :exception)
|
||||
protect_from_forgery(with: :exception, prepend: true)
|
||||
rescue_from Pundit::NotAuthorizedError, with: :render_not_authorized
|
||||
|
||||
def current_user
|
||||
@ -14,11 +14,8 @@ class ApplicationController < ActionController::Base
|
||||
@current_user ||= ExternalUser.find_by(id: session[:external_user_id]) || login_from_session || login_from_other_sources
|
||||
end
|
||||
|
||||
def help
|
||||
end
|
||||
|
||||
def render_not_authorized
|
||||
redirect_to(:root, alert: t('application.not_authorized'))
|
||||
redirect_to(request.referrer || :root, alert: t('application.not_authorized'))
|
||||
end
|
||||
private :render_not_authorized
|
||||
|
||||
|
45
app/controllers/code_ocean/errors_controller.rb
Normal file
45
app/controllers/code_ocean/errors_controller.rb
Normal file
@ -0,0 +1,45 @@
|
||||
module CodeOcean
|
||||
class ErrorsController < ApplicationController
|
||||
before_action :set_execution_environment
|
||||
|
||||
def authorize!
|
||||
authorize(@error || @errors)
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
def create
|
||||
@error = CodeOcean::Error.new(error_params)
|
||||
authorize!
|
||||
hint = Whistleblower.new(execution_environment: @error.execution_environment).generate_hint(@error.message)
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
if hint
|
||||
render(json: {hint: hint})
|
||||
else
|
||||
head (@error.save ? :created : :unprocessable_entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def error_params
|
||||
params[:error].permit(:message, :submission_id).merge(execution_environment_id: @execution_environment.id) if params[:error].present?
|
||||
end
|
||||
private :error_params
|
||||
|
||||
def index
|
||||
@errors = CodeOcean::Error.for_execution_environment(@execution_environment).grouped_by_message.paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
def set_execution_environment
|
||||
@execution_environment = ExecutionEnvironment.find(params[:execution_environment_id])
|
||||
end
|
||||
private :set_execution_environment
|
||||
|
||||
def show
|
||||
@error = CodeOcean::Error.find(params[:id])
|
||||
authorize!
|
||||
end
|
||||
end
|
||||
end
|
@ -41,7 +41,7 @@ module CodeOcean
|
||||
end
|
||||
|
||||
def file_params
|
||||
params[:code_ocean_file].permit(file_attributes).merge(context_type: 'Submission', role: 'user_defined_file')
|
||||
params[:code_ocean_file].permit(file_attributes).merge(context_type: 'Submission', role: 'user_defined_file') if params[:code_ocean_file].present?
|
||||
end
|
||||
private :file_params
|
||||
end
|
||||
|
@ -23,9 +23,9 @@ module Lti
|
||||
session.delete(:consumer_id)
|
||||
session.delete(:external_user_id)
|
||||
else
|
||||
LtiParameter.destroy_all(consumers_id: consumer_id,
|
||||
external_users_id: user_id,
|
||||
exercises_id: exercise_id)
|
||||
LtiParameter.where(consumers_id: consumer_id,
|
||||
external_users_id: user_id,
|
||||
exercises_id: exercise_id).destroy_all
|
||||
end
|
||||
end
|
||||
private :clear_lti_session_data
|
||||
@ -138,7 +138,7 @@ module Lti
|
||||
external_users_id: @current_user.id,
|
||||
exercises_id: @exercise.id)
|
||||
|
||||
lti_parameters.lti_parameters = options[:parameters].slice(*SESSION_PARAMETERS).to_json
|
||||
lti_parameters.lti_parameters = options[:parameters].slice(*SESSION_PARAMETERS).permit!.to_h
|
||||
lti_parameters.save!
|
||||
@lti_parameters = lti_parameters
|
||||
|
||||
|
@ -2,7 +2,7 @@ module RemoteEvaluationParameters
|
||||
include FileParameters
|
||||
|
||||
def remote_evaluation_params
|
||||
remote_evaluation_params = params[:remote_evaluation].permit(:validation_token, files_attributes: file_attributes)
|
||||
remote_evaluation_params = params[:remote_evaluation].permit(:validation_token, files_attributes: file_attributes) if params[:remote_evaluation].present?
|
||||
end
|
||||
private :remote_evaluation_params
|
||||
end
|
@ -16,7 +16,7 @@ module SubmissionParameters
|
||||
current_user_id = current_user.id
|
||||
current_user_class_name = current_user.class.name
|
||||
end
|
||||
submission_params = params[:submission].permit(:cause, :exercise_id, files_attributes: file_attributes).merge(user_id: current_user_id, user_type: current_user_class_name)
|
||||
submission_params = params[:submission].present? ? params[:submission].permit(:cause, :exercise_id, files_attributes: file_attributes).merge(user_id: current_user_id, user_type: current_user_class_name) : {}
|
||||
reject_illegal_file_attributes!(submission_params)
|
||||
submission_params
|
||||
end
|
||||
|
@ -22,7 +22,7 @@ class ConsumersController < ApplicationController
|
||||
end
|
||||
|
||||
def consumer_params
|
||||
params[:consumer].permit(:name, :oauth_key, :oauth_secret)
|
||||
params[:consumer].permit(:name, :oauth_key, :oauth_secret) if params[:consumer].present?
|
||||
end
|
||||
private :consumer_params
|
||||
|
||||
|
@ -81,6 +81,6 @@ class ErrorTemplateAttributesController < ApplicationController
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def error_template_attribute_params
|
||||
params[:error_template_attribute].permit(:key, :description, :regex, :important)
|
||||
params[:error_template_attribute].permit(:key, :description, :regex, :important) if params[:error_template_attribute].present?
|
||||
end
|
||||
end
|
||||
|
@ -99,6 +99,6 @@ class ErrorTemplatesController < ApplicationController
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def error_template_params
|
||||
params[:error_template].permit(:name, :execution_environment_id, :signature, :description, :hint)
|
||||
params[:error_template].permit(:name, :execution_environment_id, :signature, :description, :hint) if params[:error_template].present?
|
||||
end
|
||||
end
|
||||
|
@ -1,43 +0,0 @@
|
||||
class ErrorsController < ApplicationController
|
||||
before_action :set_execution_environment
|
||||
|
||||
def authorize!
|
||||
authorize(@error || @errors)
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
def create
|
||||
@error = Error.new(error_params)
|
||||
authorize!
|
||||
hint = Whistleblower.new(execution_environment: @error.execution_environment).generate_hint(@error.message)
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
if hint
|
||||
render(json: {hint: hint})
|
||||
else
|
||||
render(nothing: true, status: @error.save ? :created : :unprocessable_entity)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def error_params
|
||||
params[:error].permit(:message, :submission_id).merge(execution_environment_id: @execution_environment.id)
|
||||
end
|
||||
private :error_params
|
||||
|
||||
def index
|
||||
@errors = Error.for_execution_environment(@execution_environment).grouped_by_message.paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
def set_execution_environment
|
||||
@execution_environment = ExecutionEnvironment.find(params[:execution_environment_id])
|
||||
end
|
||||
private :set_execution_environment
|
||||
|
||||
def show
|
||||
@error = Error.find(params[:id])
|
||||
authorize!
|
||||
end
|
||||
end
|
@ -86,11 +86,11 @@ class ExecutionEnvironmentsController < ApplicationController
|
||||
working_time_statistics = {}
|
||||
user_statistics = {}
|
||||
|
||||
ActiveRecord::Base.connection.execute(working_time_query).each do |tuple|
|
||||
ApplicationRecord.connection.execute(working_time_query).each do |tuple|
|
||||
working_time_statistics[tuple["exercise_id"].to_i] = tuple
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connection.execute(user_query).each do |tuple|
|
||||
ApplicationRecord.connection.execute(user_query).each do |tuple|
|
||||
user_statistics[tuple["exercise_id"].to_i] = tuple
|
||||
end
|
||||
|
||||
@ -101,7 +101,7 @@ class ExecutionEnvironmentsController < ApplicationController
|
||||
end
|
||||
|
||||
def execution_environment_params
|
||||
params[:execution_environment].permit(:docker_image, :exposed_ports, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :name, :network_enabled, :permitted_execution_time, :pool_size, :run_command, :test_command, :testing_framework).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
params[:execution_environment].permit(:docker_image, :exposed_ports, :editor_mode, :file_extension, :file_type_id, :help, :indent_size, :memory_limit, :name, :network_enabled, :permitted_execution_time, :pool_size, :run_command, :test_command, :testing_framework).merge(user_id: current_user.id, user_type: current_user.class.name) if params[:execution_environment].present?
|
||||
end
|
||||
private :execution_environment_params
|
||||
|
||||
|
@ -51,7 +51,7 @@ class ExerciseCollectionsController < ApplicationController
|
||||
end
|
||||
|
||||
def exercise_collection_params
|
||||
sanitized_params = params[:exercise_collection].permit(:name, :use_anomaly_detection, :user_id, :user_type, :exercise_ids => []).merge(user_type: InternalUser.name)
|
||||
sanitized_params = params[:exercise_collection].present? ? params[:exercise_collection].permit(:name, :use_anomaly_detection, :user_id, :user_type, :exercise_ids => []).merge(user_type: InternalUser.name) : {}
|
||||
sanitized_params[:exercise_ids] = sanitized_params[:exercise_ids].reject {|v| v.nil? or v == ''}
|
||||
sanitized_params.tap {|p| p[:exercise_collection_items] = p[:exercise_ids].map.with_index {|_id, index| ExerciseCollectionItem.find_or_create_by(exercise_id: _id, exercise_collection_id: @exercise_collection.id, position: index)}; p.delete(:exercise_ids)}
|
||||
end
|
||||
|
@ -12,9 +12,9 @@ class ExercisesController < ApplicationController
|
||||
before_action :set_file_types, only: [:create, :edit, :new, :update]
|
||||
before_action :set_course_token, only: [:implement]
|
||||
|
||||
skip_before_filter :verify_authenticity_token, only: [:import_proforma_xml]
|
||||
skip_before_action :verify_authenticity_token, only: [:import_proforma_xml]
|
||||
skip_after_action :verify_authorized, only: [:import_proforma_xml]
|
||||
skip_after_action :verify_policy_scoped, only: [:import_proforma_xml]
|
||||
skip_after_action :verify_policy_scoped, only: [:import_proforma_xml], raise: false
|
||||
|
||||
def authorize!
|
||||
authorize(@exercise || @exercises)
|
||||
@ -77,7 +77,7 @@ class ExercisesController < ApplicationController
|
||||
def create
|
||||
@exercise = Exercise.new(exercise_params)
|
||||
collect_set_and_unset_exercise_tags
|
||||
myparam = exercise_params
|
||||
myparam = exercise_params.present? ? exercise_params : { }
|
||||
checked_exercise_tags = @exercise_tags.select { | et | myparam[:tag_ids].include? et.tag.id.to_s }
|
||||
removed_exercise_tags = @exercise_tags.reject { | et | myparam[:tag_ids].include? et.tag.id.to_s }
|
||||
|
||||
@ -160,19 +160,21 @@ class ExercisesController < ApplicationController
|
||||
private :user_by_code_harbor_token
|
||||
|
||||
def exercise_params
|
||||
params[:exercise].permit(:description, :execution_environment_id, :file_id, :instructions, :public, :hide_file_tree, :allow_file_creation, :allow_auto_completion, :title, :expected_difficulty, files_attributes: file_attributes, :tag_ids => []).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
params[:exercise].permit(:description, :execution_environment_id, :file_id, :instructions, :public, :hide_file_tree, :allow_file_creation, :allow_auto_completion, :title, :expected_difficulty, files_attributes: file_attributes, :tag_ids => []).merge(user_id: current_user.id, user_type: current_user.class.name) if params[:exercise].present?
|
||||
end
|
||||
private :exercise_params
|
||||
|
||||
def handle_file_uploads
|
||||
exercise_params[:files_attributes].try(:each) do |index, file_attributes|
|
||||
if file_attributes[:content].respond_to?(:read)
|
||||
file_params = params[:exercise][:files_attributes][index]
|
||||
if FileType.find_by(id: file_attributes[:file_type_id]).try(:binary?)
|
||||
file_params[:content] = nil
|
||||
file_params[:native_file] = file_attributes[:content]
|
||||
else
|
||||
file_params[:content] = file_attributes[:content].read
|
||||
if exercise_params
|
||||
exercise_params[:files_attributes].try(:each) do |index, file_attributes|
|
||||
if file_attributes[:content].respond_to?(:read)
|
||||
file_params = params[:exercise][:files_attributes][index]
|
||||
if FileType.find_by(id: file_attributes[:file_type_id]).try(:binary?)
|
||||
file_params[:content] = nil
|
||||
file_params[:native_file] = file_attributes[:content]
|
||||
else
|
||||
file_params[:content] = file_attributes[:content].read
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -185,28 +187,8 @@ class ExercisesController < ApplicationController
|
||||
count_interventions_today = UserExerciseIntervention.where(user: current_user).where("created_at >= ?", Time.zone.now.beginning_of_day).count
|
||||
user_got_intervention_in_exercise = UserExerciseIntervention.where(user: current_user, exercise: @exercise).size >= max_intervention_count_per_exercise
|
||||
user_got_enough_interventions = count_interventions_today >= max_intervention_count_per_day or user_got_intervention_in_exercise
|
||||
@is_experimental_course = @course_token and experimental_course?(@course_token)
|
||||
|
||||
@experiment_group = UserGroupSeparator.getInterventionGroup(current_user)
|
||||
|
||||
showInterventions = (@is_experimental_course and not user_solved_exercise and not user_got_enough_interventions) ? "true" : "false"
|
||||
|
||||
case @experiment_group
|
||||
when :rfc_intervention_stale_rfc
|
||||
@show_rfc_interventions = showInterventions
|
||||
when :break_intervention_stale_rfc
|
||||
@show_break_interventions = showInterventions
|
||||
when :no_intervention_stale_rfc
|
||||
when :no_intervention_hide_rfc
|
||||
@hide_rfc_button = "true"
|
||||
when :break_intervention_show_rfc
|
||||
@show_break_interventions = showInterventions
|
||||
when :no_intervention_show_rfc
|
||||
when :rfc_intervention_show_rfc
|
||||
@show_rfc_interventions = showInterventions
|
||||
end
|
||||
|
||||
|
||||
@show_rfc_interventions = (not user_solved_exercise and not user_got_enough_interventions).to_s
|
||||
|
||||
|
||||
@search = Search.new
|
||||
@ -364,7 +346,7 @@ class ExercisesController < ApplicationController
|
||||
query = "SELECT user_id, MAX(score) AS maximum_score, COUNT(id) AS runs
|
||||
FROM submissions WHERE exercise_id = #{@exercise.id} GROUP BY
|
||||
user_id;"
|
||||
ActiveRecord::Base.connection.execute(query).each do |tuple|
|
||||
ApplicationRecord.connection.execute(query).each do |tuple|
|
||||
user_statistics[tuple["user_id"].to_i] = tuple
|
||||
end
|
||||
render locals: {
|
||||
@ -428,11 +410,6 @@ class ExercisesController < ApplicationController
|
||||
return
|
||||
end
|
||||
|
||||
if @is_experimental_course and (@rfc_group == :hide_rfc)
|
||||
redirect_to_lti_return_path
|
||||
return
|
||||
end
|
||||
|
||||
rfc = @submission.own_unsolved_rfc
|
||||
if rfc
|
||||
# set a message that informs the user that his own RFC should be closed.
|
||||
|
@ -57,7 +57,7 @@ class ExternalUsersController < ApplicationController
|
||||
|
||||
statistics = {}
|
||||
|
||||
ActiveRecord::Base.connection.execute(working_time_query(params[:tag])).each do |tuple|
|
||||
ApplicationRecord.connection.execute(working_time_query(params[:tag])).each do |tuple|
|
||||
statistics[tuple["exercise_id"].to_i] = tuple
|
||||
end
|
||||
|
||||
|
@ -89,6 +89,6 @@ class FileTemplatesController < ApplicationController
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def file_template_params
|
||||
params[:file_template].permit(:name, :file_type_id, :content)
|
||||
params[:file_template].permit(:name, :file_type_id, :content) if params[:file_template].present?
|
||||
end
|
||||
end
|
||||
|
@ -23,7 +23,7 @@ class FileTypesController < ApplicationController
|
||||
end
|
||||
|
||||
def file_type_params
|
||||
params[:file_type].permit(:binary, :editor_mode, :executable, :file_extension, :name, :indent_size, :renderable).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
params[:file_type].permit(:binary, :editor_mode, :executable, :file_extension, :name, :indent_size, :renderable).merge(user_id: current_user.id, user_type: current_user.class.name) if params[:file_type].present?
|
||||
end
|
||||
private :file_type_params
|
||||
|
||||
@ -38,7 +38,7 @@ class FileTypesController < ApplicationController
|
||||
end
|
||||
|
||||
def set_editor_modes
|
||||
@editor_modes = Dir.glob('vendor/assets/javascripts/ace/mode-*.js').map do |filename|
|
||||
@editor_modes = Dir.glob('vendor/assets/javascripts/ace/mode-*.js').sort.map do |filename|
|
||||
name = filename.gsub(/\w+\/|mode-|.js$/, '')
|
||||
[name, "ace/mode/#{name}"]
|
||||
end
|
||||
|
@ -23,7 +23,7 @@ class HintsController < ApplicationController
|
||||
end
|
||||
|
||||
def hint_params
|
||||
params[:hint].permit(:locale, :message, :name, :regular_expression).merge(execution_environment_id: @execution_environment.id)
|
||||
params[:hint].permit(:locale, :message, :name, :regular_expression).merge(execution_environment_id: @execution_environment.id) if params[:hint].present?
|
||||
end
|
||||
private :hint_params
|
||||
|
||||
|
@ -21,7 +21,7 @@ class InternalUsersController < ApplicationController
|
||||
if @user.update(params[:internal_user].permit(:password, :password_confirmation))
|
||||
@user.change_password!(params[:internal_user][:password])
|
||||
format.html { redirect_to(sign_in_path, notice: t('.success')) }
|
||||
format.json { render(nothing: true, status: :ok) }
|
||||
format.json { head :ok }
|
||||
else
|
||||
respond_with_invalid_object(format, object: @user, template: :reset_password)
|
||||
end
|
||||
@ -66,7 +66,7 @@ class InternalUsersController < ApplicationController
|
||||
end
|
||||
|
||||
def internal_user_params
|
||||
params[:internal_user].permit(:consumer_id, :email, :name, :role)
|
||||
params[:internal_user].permit(:consumer_id, :email, :name, :role) if params[:internal_user].present?
|
||||
end
|
||||
private :internal_user_params
|
||||
|
||||
@ -105,7 +105,7 @@ class InternalUsersController < ApplicationController
|
||||
if @user.update(params[:internal_user].permit(:password, :password_confirmation))
|
||||
@user.activate!
|
||||
format.html { redirect_to(sign_in_path, notice: t('.success')) }
|
||||
format.json { render(nothing: true, status: :ok) }
|
||||
format.json { head :ok }
|
||||
else
|
||||
respond_with_invalid_object(format, object: @user, template: :activate)
|
||||
end
|
||||
|
@ -22,7 +22,7 @@ class InterventionsController < ApplicationController
|
||||
end
|
||||
|
||||
def intervention_params
|
||||
params[:intervention].permit(:name)
|
||||
params[:intervention].permit(:name) if params[:intervention].present?
|
||||
end
|
||||
private :intervention_params
|
||||
|
||||
|
@ -39,7 +39,7 @@ class ProxyExercisesController < ApplicationController
|
||||
end
|
||||
|
||||
def proxy_exercise_params
|
||||
params[:proxy_exercise].permit(:description, :title, :exercise_ids => [])
|
||||
params[:proxy_exercise].permit(:description, :title, :exercise_ids => []) if params[:proxy_exercise].present?
|
||||
end
|
||||
private :proxy_exercise_params
|
||||
|
||||
|
@ -15,14 +15,12 @@ class RemoteEvaluationController < ApplicationController
|
||||
|
||||
# todo extra: validiere, ob files wirklich zur Übung gehören (wenn allowNewFiles-flag nicht gesetzt ist)
|
||||
if (remote_evaluation_mapping = RemoteEvaluationMapping.find_by(:validation_token => validation_token))
|
||||
puts remote_evaluation_mapping.exercise_id
|
||||
puts remote_evaluation_mapping.user_id
|
||||
|
||||
_params = remote_evaluation_params.except(:validation_token)
|
||||
_params[:exercise_id] = remote_evaluation_mapping.exercise_id
|
||||
_params[:user_id] = remote_evaluation_mapping.user_id
|
||||
_params[:cause] = "remoteAssess"
|
||||
_params[:user_type] = "ExternalUser"
|
||||
_params[:user_type] = remote_evaluation_mapping.user_type
|
||||
|
||||
@submission = Submission.create(_params)
|
||||
render json: score_submission(@submission)
|
||||
|
@ -115,7 +115,7 @@ class SubmissionsController < ApplicationController
|
||||
if @file.native_file?
|
||||
send_file(@file.native_file.path, disposition: 'inline')
|
||||
else
|
||||
render(text: @file.content)
|
||||
render(plain: @file.content)
|
||||
end
|
||||
end
|
||||
|
||||
@ -140,7 +140,7 @@ class SubmissionsController < ApplicationController
|
||||
# probably add:
|
||||
# ensure
|
||||
# #guarantee that the thread is releasing the DB connection after it is done
|
||||
# ActiveRecord::Base.connectionpool.releaseconnection
|
||||
# ApplicationRecord.connectionpool.releaseconnection
|
||||
# end
|
||||
Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive?
|
||||
|
||||
@ -329,7 +329,7 @@ class SubmissionsController < ApplicationController
|
||||
|
||||
def set_file
|
||||
@file = @files.detect { |file| file.name_with_extension == params[:filename] }
|
||||
render(nothing: true, status: 404) unless @file
|
||||
head :not_found unless @file
|
||||
end
|
||||
private :set_file
|
||||
|
||||
@ -362,11 +362,11 @@ class SubmissionsController < ApplicationController
|
||||
DockerClient.destroy_container(container)
|
||||
rescue Docker::Error::NotFoundError
|
||||
ensure
|
||||
render(nothing: true)
|
||||
head :ok
|
||||
end
|
||||
|
||||
def store_error(stderr)
|
||||
::Error.create(submission_id: @submission.id, execution_environment_id: @submission.execution_environment.id, message: stderr)
|
||||
CodeOcean::Error.create(submission_id: @submission.id, execution_environment_id: @submission.execution_environment.id, message: stderr)
|
||||
end
|
||||
private :store_error
|
||||
|
||||
@ -398,13 +398,13 @@ class SubmissionsController < ApplicationController
|
||||
private :with_server_sent_events
|
||||
|
||||
def create_remote_evaluation_mapping
|
||||
user_id = @submission.user_id
|
||||
user = @submission.user
|
||||
exercise_id = @submission.exercise_id
|
||||
|
||||
remote_evaluation_mapping = RemoteEvaluationMapping.create(:user_id => user_id, :exercise_id => exercise_id)
|
||||
remote_evaluation_mapping = RemoteEvaluationMapping.create(user: user, exercise_id: exercise_id)
|
||||
|
||||
# create .co file
|
||||
path = "tmp/" + user_id.to_s + ".co"
|
||||
path = "tmp/" + user.id.to_s + ".co"
|
||||
# parse validation token
|
||||
content = "#{remote_evaluation_mapping.validation_token}\n"
|
||||
# parse remote request url
|
||||
|
@ -56,7 +56,7 @@ class SubscriptionsController < ApplicationController
|
||||
def subscription_params
|
||||
current_user_id = current_user.try(:id)
|
||||
current_user_class_name = current_user.try(:class).try(:name)
|
||||
params[:subscription].permit(:request_for_comment_id, :subscription_type).merge(user_id: current_user_id, user_type: current_user_class_name, deleted: false)
|
||||
params[:subscription].permit(:request_for_comment_id, :subscription_type).merge(user_id: current_user_id, user_type: current_user_class_name, deleted: false) if params[:subscription].present?
|
||||
end
|
||||
private :subscription_params
|
||||
end
|
||||
|
@ -22,7 +22,7 @@ class TagsController < ApplicationController
|
||||
end
|
||||
|
||||
def tag_params
|
||||
params[:tag].permit(:name)
|
||||
params[:tag].permit(:name) if params[:tag].present?
|
||||
end
|
||||
private :tag_params
|
||||
|
||||
|
@ -1,8 +1,7 @@
|
||||
class UserExerciseFeedbacksController < ApplicationController
|
||||
include CommonBehavior
|
||||
|
||||
before_action :set_user_exercise_feedback, only: [:edit, :update]
|
||||
before_action :set_user_exercise_feedback_by_id, only: [:show, :destroy]
|
||||
before_action :set_user_exercise_feedback, only: [:edit, :update, :show, :destroy]
|
||||
|
||||
def comment_presets
|
||||
[[0,t('user_exercise_feedback.difficulty_easy')],
|
||||
@ -103,16 +102,12 @@ class UserExerciseFeedbacksController < ApplicationController
|
||||
end
|
||||
|
||||
def set_user_exercise_feedback
|
||||
@exercise = Exercise.find(params[:user_exercise_feedback][:exercise_id])
|
||||
@uef = UserExerciseFeedback.find_by(exercise_id: params[:user_exercise_feedback][:exercise_id], user: current_user)
|
||||
end
|
||||
|
||||
def set_user_exercise_feedback_by_id
|
||||
@uef = UserExerciseFeedback.find(params[:id])
|
||||
@exercise = @uef.exercise
|
||||
end
|
||||
|
||||
def uef_params
|
||||
params[:user_exercise_feedback].permit(:feedback_text, :difficulty, :exercise_id, :user_estimated_worktime).merge(user_id: current_user.id, user_type: current_user.class.name)
|
||||
params[:user_exercise_feedback].permit(:feedback_text, :difficulty, :exercise_id, :user_estimated_worktime).merge(user_id: current_user.id, user_type: current_user.class.name) if params[:user_exercise_feedback].present?
|
||||
end
|
||||
|
||||
def validate_inputs(uef_params)
|
||||
|
@ -1,3 +1,5 @@
|
||||
require 'oauth/request_proxy/action_controller_request' # Rails 5 changed `Rack::Request` to `ActionDispatch::Request`
|
||||
|
||||
module LtiHelper
|
||||
def lti_outcome_service?(exercise_id, external_user_id, consumer_id)
|
||||
return false if external_user_id == '' || consumer_id == ''
|
||||
|
42
app/helpers/pagedown_form_builder.rb
Normal file
42
app/helpers/pagedown_form_builder.rb
Normal file
@ -0,0 +1,42 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PagedownFormBuilder < ActionView::Helpers::FormBuilder
|
||||
def pagedown(method, args)
|
||||
# Adopt simple form builder to work with form_for
|
||||
@attribute_name = method
|
||||
@input_html_options = args[:input_html]
|
||||
|
||||
@template.capture do
|
||||
@template.concat wmd_button_bar
|
||||
@template.concat wmd_textarea
|
||||
@template.concat wmd_preview if show_wmd_preview?
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def wmd_button_bar
|
||||
@template.content_tag :div, nil, id: "wmd-button-bar-#{base_id}"
|
||||
end
|
||||
|
||||
def wmd_textarea
|
||||
@template.text_area @object_name, @attribute_name,
|
||||
**@input_html_options,
|
||||
class: 'form-control wmd-input',
|
||||
id: "wmd-input-#{base_id}"
|
||||
end
|
||||
|
||||
def wmd_preview
|
||||
@template.content_tag :div, nil,
|
||||
class: 'wmd-preview',
|
||||
id: "wmd-preview-#{base_id}"
|
||||
end
|
||||
|
||||
def show_wmd_preview?
|
||||
@input_html_options[:preview].present?
|
||||
end
|
||||
|
||||
def base_id
|
||||
options[:pagedown_id_suffix] || @attribute_name
|
||||
end
|
||||
end
|
@ -1,2 +0,0 @@
|
||||
module RequestForCommentsHelper
|
||||
end
|
36
app/javascript/packs/application.js
Normal file
36
app/javascript/packs/application.js
Normal file
@ -0,0 +1,36 @@
|
||||
/* eslint no-console:0 */
|
||||
// This file is automatically compiled by Webpack, along with any other files
|
||||
// present in this directory. You're encouraged to place your actual application logic in
|
||||
// a relevant structure within app/javascript and only use these pack files to reference
|
||||
// that code so it'll be compiled.
|
||||
//
|
||||
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
|
||||
// layout file, like app/views/layouts/application.html.slim
|
||||
|
||||
// JS
|
||||
import 'jquery'
|
||||
import 'bootstrap/dist/js/bootstrap.bundle.min';
|
||||
import 'chosen-js/chosen.jquery';
|
||||
import 'jstree';
|
||||
import 'underscore';
|
||||
window._ = _; // Publish underscore's `_` in global namespace
|
||||
|
||||
// CSS
|
||||
import 'chosen-js/chosen.css';
|
||||
import 'jstree/dist/themes/default/style.min.css';
|
||||
|
||||
// custom jquery-ui library for minimal mouse interaction support
|
||||
import 'jquery-ui/ui/widget'
|
||||
import 'jquery-ui/ui/data'
|
||||
import 'jquery-ui/ui/disable-selection'
|
||||
import 'jquery-ui/ui/scroll-parent'
|
||||
import 'jquery-ui/ui/widgets/draggable'
|
||||
import 'jquery-ui/ui/widgets/droppable'
|
||||
import 'jquery-ui/ui/widgets/resizable'
|
||||
import 'jquery-ui/ui/widgets/selectable'
|
||||
import 'jquery-ui/ui/widgets/sortable'
|
||||
import 'jquery-ui/themes/base/draggable.css'
|
||||
import 'jquery-ui/themes/base/core.css'
|
||||
import 'jquery-ui/themes/base/resizable.css'
|
||||
import 'jquery-ui/themes/base/selectable.css'
|
||||
import 'jquery-ui/themes/base/sortable.css'
|
12
app/javascript/packs/d3-tip.js
vendored
Normal file
12
app/javascript/packs/d3-tip.js
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
/* eslint no-console:0 */
|
||||
// This file is automatically compiled by Webpack, along with any other files
|
||||
// present in this directory. You're encouraged to place your actual application logic in
|
||||
// a relevant structure within app/javascript and only use these pack files to reference
|
||||
// that code so it'll be compiled.
|
||||
//
|
||||
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
|
||||
// layout file, like app/views/layouts/application.html.slim
|
||||
|
||||
// JS
|
||||
import d3Tip from 'd3-tip'
|
||||
window.d3.tip = d3Tip;
|
15
app/javascript/packs/highlight.js
Normal file
15
app/javascript/packs/highlight.js
Normal file
@ -0,0 +1,15 @@
|
||||
/* eslint no-console:0 */
|
||||
// This file is automatically compiled by Webpack, along with any other files
|
||||
// present in this directory. You're encouraged to place your actual application logic in
|
||||
// a relevant structure within app/javascript and only use these pack files to reference
|
||||
// that code so it'll be compiled.
|
||||
//
|
||||
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
|
||||
// layout file, like app/views/layouts/application.html.slim
|
||||
|
||||
// JS
|
||||
import hljs from 'highlight.js'
|
||||
window.hljs = hljs;
|
||||
|
||||
// CSS
|
||||
import 'highlight.js/styles/default.css'
|
16
app/javascript/packs/stylesheets.scss
Normal file
16
app/javascript/packs/stylesheets.scss
Normal file
@ -0,0 +1,16 @@
|
||||
/* eslint no-console:0 */
|
||||
// This file is automatically compiled by Webpack, along with any other files
|
||||
// present in this directory. You're encouraged to place your actual application logic in
|
||||
// a relevant structure within app/javascript and only use these pack files to reference
|
||||
// that code so it'll be compiled.
|
||||
//
|
||||
// To reference this file, add <%= stylesheet_pack_tag 'stylesheets' %> to the appropriate
|
||||
// layout file, like app/views/layouts/application.html.slim
|
||||
|
||||
@import '~bootswatch/dist/yeti/variables';
|
||||
@import '~bootstrap/scss/bootstrap';
|
||||
@import '~bootswatch/dist/yeti/bootswatch';
|
||||
$fa-font-path: '~font-awesome/fonts';
|
||||
@import '~font-awesome/scss/font-awesome';
|
||||
$opensans-path: '~opensans-webkit/fonts/';
|
||||
@import '~opensans-webkit/src/sass/open-sans';
|
15
app/javascript/packs/vis.js
Normal file
15
app/javascript/packs/vis.js
Normal file
@ -0,0 +1,15 @@
|
||||
/* eslint no-console:0 */
|
||||
// This file is automatically compiled by Webpack, along with any other files
|
||||
// present in this directory. You're encouraged to place your actual application logic in
|
||||
// a relevant structure within app/javascript and only use these pack files to reference
|
||||
// that code so it'll be compiled.
|
||||
//
|
||||
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
|
||||
// layout file, like app/views/layouts/application.html.slim
|
||||
|
||||
// JS
|
||||
import 'vis'
|
||||
window.vis = vis;
|
||||
|
||||
// CSS
|
||||
import 'vis/dist/vis.min.css'
|
@ -1,4 +1,4 @@
|
||||
class AnomalyNotification < ActiveRecord::Base
|
||||
class AnomalyNotification < ApplicationRecord
|
||||
belongs_to :user, polymorphic: true
|
||||
belongs_to :exercise
|
||||
belongs_to :exercise_collection
|
||||
|
3
app/models/application_record.rb
Normal file
3
app/models/application_record.rb
Normal file
@ -0,0 +1,3 @@
|
||||
class ApplicationRecord < ActiveRecord::Base
|
||||
self.abstract_class = true
|
||||
end
|
@ -1,4 +1,4 @@
|
||||
class CodeHarborLink < ActiveRecord::Base
|
||||
class CodeHarborLink < ApplicationRecord
|
||||
validates :oauth2token, presence: true
|
||||
validates :user_id, presence: true
|
||||
|
||||
|
19
app/models/code_ocean/error.rb
Normal file
19
app/models/code_ocean/error.rb
Normal file
@ -0,0 +1,19 @@
|
||||
module CodeOcean
|
||||
class Error < ApplicationRecord
|
||||
belongs_to :execution_environment
|
||||
|
||||
scope :for_execution_environment, ->(execution_environment) { where(execution_environment_id: execution_environment.id) }
|
||||
scope :grouped_by_message, -> { select('MAX(created_at) AS created_at, MAX(id) AS id, message, COUNT(id) AS count').group(:message).order('count DESC') }
|
||||
|
||||
validates :execution_environment_id, presence: true
|
||||
validates :message, presence: true
|
||||
|
||||
def self.nested_resource?
|
||||
true
|
||||
end
|
||||
|
||||
def to_s
|
||||
id.to_s
|
||||
end
|
||||
end
|
||||
end
|
@ -15,7 +15,7 @@ module CodeOcean
|
||||
end
|
||||
end
|
||||
|
||||
class File < ActiveRecord::Base
|
||||
class File < ApplicationRecord
|
||||
include DefaultValues
|
||||
|
||||
DEFAULT_WEIGHT = 1.0
|
||||
@ -28,12 +28,11 @@ module CodeOcean
|
||||
before_validation :set_ancestor_values, if: :incomplete_descendent?
|
||||
|
||||
belongs_to :context, polymorphic: true
|
||||
belongs_to :execution_environment
|
||||
belongs_to :file
|
||||
belongs_to :file, class_name: 'CodeOcean::File', optional: true # This is only required for submissions and is validated below
|
||||
alias_method :ancestor, :file
|
||||
belongs_to :file_type
|
||||
|
||||
has_many :files
|
||||
has_many :files, class_name: 'CodeOcean::File'
|
||||
has_many :testruns
|
||||
has_many :comments
|
||||
alias_method :descendants, :files
|
||||
@ -47,6 +46,8 @@ module CodeOcean
|
||||
scope :"#{role}s", -> { where(role: role) }
|
||||
end
|
||||
|
||||
default_scope { order(name: :asc) }
|
||||
|
||||
validates :feedback_message, if: :teacher_defined_test?, presence: true
|
||||
validates :feedback_message, absence: true, unless: :teacher_defined_test?
|
||||
validates :file_type_id, presence: true
|
||||
@ -57,6 +58,7 @@ module CodeOcean
|
||||
validates :role, inclusion: {in: ROLES}
|
||||
validates :weight, if: :teacher_defined_test?, numericality: true, presence: true
|
||||
validates :weight, absence: true, unless: :teacher_defined_test?
|
||||
validates :file, presence: true if :context.is_a?(Submission)
|
||||
|
||||
validates_with FileNameValidator, fields: [:name, :path, :file_type_id]
|
||||
|
||||
@ -78,6 +80,14 @@ module CodeOcean
|
||||
end
|
||||
private :content_present?
|
||||
|
||||
def filepath
|
||||
if path.present?
|
||||
::File.join(path, name_with_extension)
|
||||
else
|
||||
name_with_extension
|
||||
end
|
||||
end
|
||||
|
||||
def hash_content
|
||||
self.hashed_content = Digest::MD5.new.hexdigest(file_type.try(:binary?) ? ::File.new(native_file.file.path, 'r').read : content)
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
class Comment < ActiveRecord::Base
|
||||
class Comment < ApplicationRecord
|
||||
# inherit the creation module: encapsulates that this is a polymorphic user, offers some aliases and makes sure that all necessary attributes are set.
|
||||
include Creation
|
||||
attr_accessor :username, :date, :updated, :editable
|
||||
|
@ -1,4 +1,4 @@
|
||||
class Consumer < ActiveRecord::Base
|
||||
class Consumer < ApplicationRecord
|
||||
has_many :users
|
||||
|
||||
scope :with_users, -> { where('id IN (SELECT consumer_id FROM internal_users)') }
|
||||
|
@ -1,13 +0,0 @@
|
||||
class Error < ActiveRecord::Base
|
||||
belongs_to :execution_environment
|
||||
|
||||
scope :for_execution_environment, ->(execution_environment) { where(execution_environment_id: execution_environment.id) }
|
||||
scope :grouped_by_message, -> { select('MAX(created_at) AS created_at, MAX(id) AS id, message, COUNT(id) AS count').group(:message).order('count DESC') }
|
||||
|
||||
validates :execution_environment_id, presence: true
|
||||
validates :message, presence: true
|
||||
|
||||
def self.nested_resource?
|
||||
true
|
||||
end
|
||||
end
|
@ -1,8 +1,8 @@
|
||||
class ErrorTemplate < ActiveRecord::Base
|
||||
class ErrorTemplate < ApplicationRecord
|
||||
belongs_to :execution_environment
|
||||
has_and_belongs_to_many :error_template_attributes
|
||||
|
||||
def to_s
|
||||
"#{id} [#{name}]"
|
||||
name
|
||||
end
|
||||
end
|
||||
|
@ -1,7 +1,7 @@
|
||||
class ErrorTemplateAttribute < ActiveRecord::Base
|
||||
class ErrorTemplateAttribute < ApplicationRecord
|
||||
has_and_belongs_to_many :error_template
|
||||
|
||||
def to_s
|
||||
"#{id} [#{key}]"
|
||||
key
|
||||
end
|
||||
end
|
||||
|
@ -1,7 +1,7 @@
|
||||
class Event < ActiveRecord::Base
|
||||
class Event < ApplicationRecord
|
||||
belongs_to :user, polymorphic: true
|
||||
belongs_to :exercise
|
||||
belongs_to :file
|
||||
belongs_to :file, class_name: 'CodeOcean::File'
|
||||
|
||||
validates :category, presence: true
|
||||
validates :data, presence: true
|
||||
|
@ -1,6 +1,6 @@
|
||||
require File.expand_path('../../../lib/active_model/validations/boolean_presence_validator', __FILE__)
|
||||
|
||||
class ExecutionEnvironment < ActiveRecord::Base
|
||||
class ExecutionEnvironment < ApplicationRecord
|
||||
include Creation
|
||||
include DefaultValues
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user