Commit 22696700 authored by Taico Aerts's avatar Taico Aerts
Browse files

Merge branch 'development' into 'master'

Project Forum Release 2.8.0

See merge request !749
parents 0a8d7d90 9f925163
......@@ -103,7 +103,7 @@ build-branch:
when: always
name: $CI_JOB_NAME Details
paths:
- coverage/$CI_JOB_NAME
- coverage/**
- test/reports/junit.xml
reports:
junit: test/reports/junit.xml
......@@ -149,54 +149,11 @@ build-branch:
expose_as: Failed system test screenshots
paths:
- tmp/screenshots/
- coverage/$CI_JOB_NAME
- coverage/System Test/
- test/reports/junit.xml
reports:
junit: test/reports/junit.xml
'Grouping Test':
stage: test
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
services:
- name: docker.elastic.co/elasticsearch/elasticsearch:7.17.1
alias: elasticsearch
command: [ "bin/elasticsearch", "-Ediscovery.type=single-node" ]
variables:
DB_TYPE: sqlite
RAILS_ENV: test
ELASTICSEARCH_URL: "http://elasticsearch:9200"
GROUPING_TEST: "true"
COVERAGE: "true"
before_script:
- bundle config --local path /projectforum/vendor
- mkdir /pf_tests
- cp -r . /pf_tests
script:
- cd /pf_tests
# Check elasticsearch is running
- sleep 10
- 'for i in $(seq 1 10); do curl "$ELASTICSEARCH_URL/_cat/health" && s=0 && break || s=$? && sleep 5; done; (exit $s)'
# Setup environment
- bin/rails db:environment:set
- DB_TYPE=sqlite bundle exec rake db:schema:load
- DB_TYPE=sqlite bundle exec rake db:seed
# Run the grouping tests
- bundle exec rake test TEST=test/jobs/*.rb
after_script:
- cd /pf_tests
- bundle exec rake test:combine
# Move the output back into the builds folder
- cp test/reports -r --parents $CI_PROJECT_DIR
- cp coverage -r --parents $CI_PROJECT_DIR
artifacts:
when: always
name: $CI_JOB_NAME Details
paths:
- coverage/$CI_JOB_NAME
- test/reports/junit.xml
reports:
junit: test/reports/junit.xml
'Lint (rubocop)':
stage: test
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
......@@ -271,7 +228,6 @@ coverage-report:
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
needs:
- Test
- Grouping Test
- System Test
coverage: '/LOC\s\(\d+\.\d+%\)\scovered/'
before_script:
......@@ -293,7 +249,6 @@ coverage-report:
# image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
# needs:
# - Test
# - Grouping Test
# - System Test
# before_script:
# - bundle config --local path /projectforum/vendor
......@@ -337,7 +292,6 @@ deploy-production:
needs:
- "Check Dependency Vulnerabilities (bundle-audit)"
- "Security Scan (brakeman)"
- Grouping Test
- System Test
- Test
before_script:
......
......@@ -223,9 +223,14 @@ group :development do
# Generate Entity Relationship diagrams based on the application's data models.
gem 'rails-erd'
end
group :development, :staging do
# preview emails in the browser when they are sent in development
gem 'letter_opener'
# Allow to see sent emails at an endpoint
gem 'letter_opener_web'
end
# =================================================================================================
......@@ -234,10 +239,13 @@ end
# Use Capistrano for deployment
group :development do
gem 'capistrano', '~> 3.16', require: false
gem 'capistrano', '~> 3.17', require: false
# Rails (db migrations and assets) support
gem 'capistrano-rails', '~> 1.6', require: false
# Force new version
gem 'net-scp', '>= 3.0.0'
end
# =================================================================================================
......
......@@ -47,40 +47,40 @@ GEM
specs:
aasm (5.2.0)
concurrent-ruby (~> 1.0)
actioncable (6.1.6)
actionpack (= 6.1.6)
activesupport (= 6.1.6)
actioncable (6.1.6.1)
actionpack (= 6.1.6.1)
activesupport (= 6.1.6.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.1.6)
actionpack (= 6.1.6)
activejob (= 6.1.6)
activerecord (= 6.1.6)
activestorage (= 6.1.6)
activesupport (= 6.1.6)
actionmailbox (6.1.6.1)
actionpack (= 6.1.6.1)
activejob (= 6.1.6.1)
activerecord (= 6.1.6.1)
activestorage (= 6.1.6.1)
activesupport (= 6.1.6.1)
mail (>= 2.7.1)
actionmailer (6.1.6)
actionpack (= 6.1.6)
actionview (= 6.1.6)
activejob (= 6.1.6)
activesupport (= 6.1.6)
actionmailer (6.1.6.1)
actionpack (= 6.1.6.1)
actionview (= 6.1.6.1)
activejob (= 6.1.6.1)
activesupport (= 6.1.6.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.1.6)
actionview (= 6.1.6)
activesupport (= 6.1.6)
actionpack (6.1.6.1)
actionview (= 6.1.6.1)
activesupport (= 6.1.6.1)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.1.6)
actionpack (= 6.1.6)
activerecord (= 6.1.6)
activestorage (= 6.1.6)
activesupport (= 6.1.6)
actiontext (6.1.6.1)
actionpack (= 6.1.6.1)
activerecord (= 6.1.6.1)
activestorage (= 6.1.6.1)
activesupport (= 6.1.6.1)
nokogiri (>= 1.8.5)
actionview (6.1.6)
activesupport (= 6.1.6)
actionview (6.1.6.1)
activesupport (= 6.1.6.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
......@@ -91,22 +91,22 @@ GEM
ruby2_keywords
active_record_union (1.3.0)
activerecord (>= 4.0)
activejob (6.1.6)
activesupport (= 6.1.6)
activejob (6.1.6.1)
activesupport (= 6.1.6.1)
globalid (>= 0.3.6)
activemodel (6.1.6)
activesupport (= 6.1.6)
activerecord (6.1.6)
activemodel (= 6.1.6)
activesupport (= 6.1.6)
activestorage (6.1.6)
actionpack (= 6.1.6)
activejob (= 6.1.6)
activerecord (= 6.1.6)
activesupport (= 6.1.6)
activemodel (6.1.6.1)
activesupport (= 6.1.6.1)
activerecord (6.1.6.1)
activemodel (= 6.1.6.1)
activesupport (= 6.1.6.1)
activestorage (6.1.6.1)
actionpack (= 6.1.6.1)
activejob (= 6.1.6.1)
activerecord (= 6.1.6.1)
activesupport (= 6.1.6.1)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (6.1.6)
activesupport (6.1.6.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
......@@ -139,7 +139,7 @@ GEM
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
byebug (11.1.3)
cancancan (3.3.0)
cancancan (3.4.0)
capistrano (3.17.0)
airbrussh (>= 1.0.0)
i18n
......@@ -191,7 +191,7 @@ GEM
devise_saml_authenticatable (1.9.0)
devise (> 2.0.0)
ruby-saml (~> 1.7)
diffy (3.4.1)
diffy (3.4.2)
docile (1.4.0)
elasticsearch (7.17.1)
elasticsearch-api (= 7.17.1)
......@@ -236,7 +236,7 @@ GEM
activesupport (>= 5.0)
hashie (5.0.0)
htmlentities (4.3.4)
i18n (1.10.0)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
image_processing (1.12.2)
mini_magick (>= 4.9.5, < 5)
......@@ -255,6 +255,7 @@ GEM
railties (>= 3.1)
jquery-ui-rails (6.0.1)
railties (>= 3.2.16)
json (2.6.2)
kaminari (1.2.2)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.2)
......@@ -278,6 +279,11 @@ GEM
lazy_priority_queue (0.1.1)
letter_opener (1.8.1)
launchy (>= 2.2, < 3)
letter_opener_web (2.0.0)
actionmailer (>= 5.2)
letter_opener (~> 1.7)
railties (>= 5.2)
rexml
lightbox2-rails (2.8.2.1)
railties (>= 3.1)
listen (3.7.1)
......@@ -299,7 +305,7 @@ GEM
rake
mini_magick (4.11.0)
mini_mime (1.1.2)
minitest (5.16.0)
minitest (5.16.2)
minitest-reporters (1.5.0)
ansi
builder
......@@ -314,7 +320,7 @@ GEM
net-ssh (>= 2.6.5, < 7.0.0)
net-ssh (6.1.0)
nio4r (2.5.8)
nokogiri (1.13.6-x86_64-linux)
nokogiri (1.13.7-x86_64-linux)
racc (~> 1.4)
orm_adapter (0.5.0)
paper_trail (12.3.0)
......@@ -333,23 +339,23 @@ GEM
puma (5.6.4)
nio4r (~> 2.0)
racc (1.6.0)
rack (2.2.3.1)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.1.6)
actioncable (= 6.1.6)
actionmailbox (= 6.1.6)
actionmailer (= 6.1.6)
actionpack (= 6.1.6)
actiontext (= 6.1.6)
actionview (= 6.1.6)
activejob (= 6.1.6)
activemodel (= 6.1.6)
activerecord (= 6.1.6)
activestorage (= 6.1.6)
activesupport (= 6.1.6)
rack (2.2.4)
rack-test (2.0.2)
rack (>= 1.3)
rails (6.1.6.1)
actioncable (= 6.1.6.1)
actionmailbox (= 6.1.6.1)
actionmailer (= 6.1.6.1)
actionpack (= 6.1.6.1)
actiontext (= 6.1.6.1)
actionview (= 6.1.6.1)
activejob (= 6.1.6.1)
activemodel (= 6.1.6.1)
activerecord (= 6.1.6.1)
activestorage (= 6.1.6.1)
activesupport (= 6.1.6.1)
bundler (>= 1.15.0)
railties (= 6.1.6)
railties (= 6.1.6.1)
sprockets-rails (>= 2.0.0)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
......@@ -363,9 +369,9 @@ GEM
loofah (~> 2.3)
rails-jquery-autocomplete (1.0.5)
rails (>= 3.2)
railties (6.1.6)
actionpack (= 6.1.6)
activesupport (= 6.1.6)
railties (6.1.6.1)
actionpack (= 6.1.6.1)
activesupport (= 6.1.6.1)
method_source
rake (>= 12.2)
thor (~> 1.0)
......@@ -387,18 +393,19 @@ GEM
rexml (~> 3.2, >= 3.2.4)
stream (~> 0.5.3)
rolify (6.0.0)
rubocop (1.30.1)
rubocop (1.32.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.1.0.0)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.18.0, < 2.0)
rubocop-ast (>= 1.19.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 3.0)
rubocop-ast (1.18.0)
rubocop-ast (1.19.1)
parser (>= 3.1.1.0)
rubocop-rails (2.15.0)
rubocop-rails (2.15.2)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.7.0, < 2.0)
......@@ -449,7 +456,7 @@ GEM
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
sprockets (4.0.3)
sprockets (4.1.1)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.4.2)
......@@ -464,16 +471,16 @@ GEM
generator
terrapin (0.6.0)
climate_control (>= 0.0.3, < 1.0)
terser (1.1.10)
terser (1.1.12)
execjs (>= 0.3.0, < 3)
thor (1.2.1)
tilt (2.0.10)
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
tzinfo (2.0.4)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
unicode-display_width (2.1.0)
unicode-display_width (2.2.0)
warden (1.2.9)
rack (>= 2.0.9)
web-console (4.2.0)
......@@ -509,7 +516,7 @@ DEPENDENCIES
bundler-audit
byebug
cancancan
capistrano (~> 3.16)
capistrano (~> 3.17)
capistrano-rails (~> 1.6)
capybara (~> 3.18)
caxlsx
......@@ -535,11 +542,13 @@ DEPENDENCIES
kaminari
kt-paperclip (~> 6.4, >= 6.4.1)
letter_opener
letter_opener_web
lightbox2-rails
minitest-reporters
mocha (~> 1.13)
mysql2
net-ldap
net-scp (>= 3.0.0)
nokogiri
paper_trail (~> 12.1)
passenger (>= 6.0)
......
# Project Forum
[![Latest Release](https://gitlab.ewi.tudelft.nl/eip/projectforum/projectforum/-/badges/release.svg)](https://gitlab.ewi.tudelft.nl/eip/projectforum/projectforum/-/releases) [![coverage report](https://gitlab.ewi.tudelft.nl/eip/projectforum/projectforum/badges/master/coverage.svg)](https://gitlab.ewi.tudelft.nl/eip/projectforum/projectforum/-/commits/master) [![pipeline status](https://gitlab.ewi.tudelft.nl/eip/projectforum/projectforum/badges/master/pipeline.svg)](https://gitlab.ewi.tudelft.nl/eip/projectforum/projectforum/-/commits/master)
This repository contains the source code of the Project Forum application.
Project Forum has grown out of the second major version of the _Bachelor Project System (BEPSys)_ used at Delft University of Technology.
The system was developed from scratch by David Alderliesten, Floris Doolaard, Jesse Tilro, and Niels Warnars in 2017 for their bachelor project.
......
......@@ -19,6 +19,7 @@
//= require cocoon
//= require selectize
//= require normal/selectize_inputs
//= require mammoth/mammoth.browser
//= require toastui/toastui-editor-all
//= require toastui/toastui-editor-plugin-code-syntax-highlight-all
//= require normal/toastui_editor_init
......
......@@ -23,6 +23,7 @@
//= require lightbox
//= require selectize
//= require normal/selectize_inputs
//= require mammoth/mammoth.browser
//= require toastui/toastui-editor-all
//= require toastui/toastui-editor-plugin-code-syntax-highlight-all
//= require normal/toastui_editor_init
......
This diff is collapsed.
......@@ -18,9 +18,16 @@ function init_tui_editor(editor_element_array) {
const editor = createEditor();
// configure toolbar options
// remove image button from toolbar
editor.removeToolbarItem('image');
// add word button to toolbar
editor.insertToolbarItem({groupIndex: -1, itemIndex: 0}, {
name: 'insertDocx',
tooltip: 'Replace description with a Word document (.docx)',
el: createWordButton()
});
// store editor instance
toastui.projectforum_markdown_editors[output_field.attr('id')] = editor;
......@@ -47,6 +54,7 @@ function init_tui_editor(editor_element_array) {
el: tui_editor[0],
height: height,
initialValue: editor_element.value,
extendedAutolinks: true,
initialEditType: 'wysiwyg',
previewStyle: 'vertical',
usageStatistics: false,
......@@ -77,6 +85,49 @@ function init_tui_editor(editor_element_array) {
});
}
/**
* Create a button element for converting a docx to markdown
*/
function createWordButton() {
const div = document.createElement('div');
// create file selector
const fileSelector = document.createElement('input');
fileSelector.type = 'file';
fileSelector.accept = '.docx';
fileSelector.style.display = 'none';
// read file and insert content in editor
fileSelector.addEventListener('change', function (e) {
let reader = new FileReader();
reader.onload = function (loadEvent) {
let options = getMammothOptions();
mammoth.convertToHtml({arrayBuffer: loadEvent.target.result}, options)
.then(function (result) {
result.messages.forEach(m => console.log(`${m.type}: ${m.message}`));
editor.setHTML(result.value);
})
.done();
};
reader.readAsArrayBuffer(e.target.files[0]);
});
// create button to trigger file selector
const button = document.createElement('button');
button.className = 'toastui-editor-toolbar-icons last';
button.type = 'button';
button.style.backgroundImage = 'none';
button.style.margin = '0';
button.innerHTML = `<i class="fa-solid fa-file-word fa-lg"></i>`;
button.addEventListener('click', () => fileSelector.click());
div.appendChild(fileSelector);
div.appendChild(button);
return div;
}
/**
* Set up editor destruction.
*/
......@@ -97,6 +148,35 @@ function init_tui_editor(editor_element_array) {
tui_editor.remove();
});
}
function getMammothOptions() {
return {
convertImage: mammoth.images.imgElement(function (image) {
return '';
}),
styleMap: 'p.Heading1 => h2:fresh\n' +
'p.Heading2 => h3:fresh\n' +
'p.Heading3 => h4:fresh\n' +
'p.Heading4 => h5:fresh\n' +
'p.Heading5 => h6:fresh\n' +
'p.Heading6 => h6:fresh\n' +
'p[style-name=\'Heading 1\'] => h2:fresh\n' +
'p[style-name=\'Heading 2\'] => h3:fresh\n' +
'p[style-name=\'Heading 3\'] => h4:fresh\n' +
'p[style-name=\'Heading 4\'] => h5:fresh\n' +
'p[style-name=\'Heading 5\'] => h6:fresh\n' +
'p[style-name=\'Heading 6\'] => h6:fresh\n' +
'p[style-name=\'heading 1\'] => h2:fresh\n' +
'p[style-name=\'heading 2\'] => h3:fresh\n' +
'p[style-name=\'heading 3\'] => h4:fresh\n' +
'p[style-name=\'heading 4\'] => h5:fresh\n' +
'p[style-name=\'heading 5\'] => h6:fresh\n' +
'p[style-name=\'heading 6\'] => h6:fresh\n' +
'p.Title => h1:fresh\n' +
'p[style-name=\'Title\'] => h1:fresh\n' +
'p[style-name=\'title\'] => h1:fresh\n'
};
}
}
}
......@@ -123,6 +203,7 @@ function init_tui_viewer(viewer_element_array) {
el: tui_editor[0],
viewer: true,
initialValue: text_element.text(),
extendedAutolinks: true,
usageStatistics: false,
plugins: [toastui.Editor.plugin.codeSyntaxHighlight]
});
......
......@@ -180,6 +180,12 @@
.bc-dkgray { border-color: #333; }
.bc-ltgray { border-color: #EEE; }
.btn-default-if-not-active:not(.active):not(:hover){
color: #333;
background-color: #fff;
border-color: #ccc;
}
/* --------------
TEXT
-------------- */
......
......@@ -224,7 +224,7 @@ module Admin
users = match_users(row.drop(1), index, errors)
groups.append({ project: project, users: users })
else
errors.append("Unable to process line #{index + 1}: invalid amount of entries. "\
errors.append("Unable to process line #{index + 1}: invalid amount of entries. " \
"Expected \"project ID,member1,member2,...,memberN\" but was \"#{row.join(',')}\"")
end
end
......
......@@ -162,7 +162,7 @@ module Admin
end
if group.id != -1 && role.id != -1 && !existing.add?({ group: group.id, role: role.id })
errors.append("Unable to process line #{index + 1}: group and role already defined in a previous line. "\
errors.append("Unable to process line #{index + 1}: group and role already defined in a previous line. " \
'Use \"group id,role name,user1,user2,...,userN\" to assign a role to multiple users.')
role = CourseSpecificRole.new(id: -1, name: "#{row[1]} (Duplicate)", role_type: :coach)
end
......@@ -170,7 +170,7 @@ module Admin
users = match_users(row.drop(2), index, errors)
role_assignments.append({ group: group, role: role, users: users })
else
errors.append("Unable to process line #{index + 1}: invalid amount of entries. "\
errors.append("Unable to process line #{index + 1}: invalid amount of entries. " \
"Expected \"group id,role name,user1,user2,...,userN\" but was \"#{row.join(',')}\"")
end
end
......
......@@ -147,7 +147,7 @@ module Admin
end
if project.id != -1 && role.id != -1 && !existing.add?({ project: project.id, role: role.id })
errors.append("Unable to process line #{index + 1}: project and role already defined in a previous line. "\
errors.append("Unable to process line #{index + 1}: project and role already defined in a previous line. " \
'Use \"project id,role name,user1,user2,...,userN\" to assign a role to multiple users.')
role = CourseSpecificRole.new(id: -1, name: "#{row[1]} (Duplicate)", role_type: :coach)
end
......@@ -155,7 +155,7 @@ module Admin
users = match_users(row.drop(2), index, errors)
role_assignments.append({ project: project, role: role, users: users })
else
errors.append("Unable to process line #{index + 1}: invalid amount of entries. "\
errors.append("Unable to process line #{index + 1}: invalid amount of entries. " \
"Expected \"project id,role name,user1,user2,...,userN\" but was \"#{row.join(',')}\"")
end
end
......
......@@ -161,6 +161,10 @@ module Admin
render 'admin/course_editions/exports/brightspace_groups_export'
end
def rubric_groups_export
render 'admin/course_editions/exports/rubric_groups_export'
end
def write_email
authorize! :send_email, @course_edition
@course_specific_role = @course_edition.course_specific_roles.find_by(id: params[:role]) if params[:type] == 'role'
......
......@@ -22,16 +22,22 @@ module Admin
def new; end
def create
authorize! :create_course, Programme.find_by(id: course_params[:programme_id])