Merge pull request #66 from openHPI/feature-file-templates
Feature file templates
This commit is contained in:
@ -151,6 +151,22 @@ $(function() {
|
||||
});
|
||||
};
|
||||
|
||||
var updateFileTemplates = function(fileType) {
|
||||
var jqxhr = $.ajax({
|
||||
url: '/file_templates/by_file_type/' + fileType + '.json',
|
||||
dataType: 'json'
|
||||
});
|
||||
jqxhr.done(function(response) {
|
||||
var noTemplateLabel = $('#noTemplateLabel').data('text');
|
||||
var options = "<option value>" + noTemplateLabel + "</option>";
|
||||
for (var i = 0; i < response.length; i++) {
|
||||
options += "<option value='" + response[i].id + "'>" + response[i].name + "</option>"
|
||||
}
|
||||
$("#code_ocean_file_file_template_id").find('option').remove().end().append($(options));
|
||||
});
|
||||
jqxhr.fail(ajaxError);
|
||||
}
|
||||
|
||||
if ($.isController('exercises')) {
|
||||
if ($('table').isPresent()) {
|
||||
enableBatchUpdate();
|
||||
@ -165,6 +181,10 @@ $(function() {
|
||||
inferFileAttributes();
|
||||
observeFileRoleChanges();
|
||||
overrideTextareaTabBehavior();
|
||||
} else if ($('#files.jstree').isPresent()) {
|
||||
var fileTypeSelect = $('#code_ocean_file_file_type_id');
|
||||
fileTypeSelect.on("change", function() {updateFileTemplates(fileTypeSelect.val())});
|
||||
updateFileTemplates(fileTypeSelect.val());
|
||||
}
|
||||
toggleCodeHeight();
|
||||
if (window.hljs) {
|
||||
|
3
app/assets/javascripts/file_templates.js.coffee
Normal file
3
app/assets/javascripts/file_templates.js.coffee
Normal file
@ -0,0 +1,3 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
3
app/assets/stylesheets/file_templates.css.scss
Normal file
3
app/assets/stylesheets/file_templates.css.scss
Normal file
@ -0,0 +1,3 @@
|
||||
// Place all the styles related to the FileTemplates controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
@ -10,6 +10,11 @@ module CodeOcean
|
||||
|
||||
def create
|
||||
@file = CodeOcean::File.new(file_params)
|
||||
if @file.file_template_id
|
||||
content = FileTemplate.find(@file.file_template_id).content
|
||||
content.sub! '{{file_name}}', @file.name
|
||||
@file.content = content
|
||||
end
|
||||
authorize!
|
||||
create_and_respond(object: @file, path: proc { implement_exercise_path(@file.context.exercise, tab: 2) })
|
||||
end
|
||||
|
@ -1,6 +1,6 @@
|
||||
module FileParameters
|
||||
def file_attributes
|
||||
%w(content context_id feedback_message file_id file_type_id hidden id name native_file path read_only role weight)
|
||||
%w(content context_id feedback_message file_id file_type_id hidden id name native_file path read_only role weight file_template_id)
|
||||
end
|
||||
private :file_attributes
|
||||
end
|
||||
|
94
app/controllers/file_templates_controller.rb
Normal file
94
app/controllers/file_templates_controller.rb
Normal file
@ -0,0 +1,94 @@
|
||||
class FileTemplatesController < ApplicationController
|
||||
before_action :set_file_template, only: [:show, :edit, :update, :destroy]
|
||||
|
||||
def authorize!
|
||||
authorize(@file_template || @file_templates)
|
||||
end
|
||||
private :authorize!
|
||||
|
||||
def by_file_type
|
||||
@file_templates = FileTemplate.where(:file_type_id => params[:file_type_id])
|
||||
authorize!
|
||||
respond_to do |format|
|
||||
format.json { render :show, status: :ok, json: @file_templates.to_json }
|
||||
end
|
||||
end
|
||||
|
||||
# GET /file_templates
|
||||
# GET /file_templates.json
|
||||
def index
|
||||
@file_templates = FileTemplate.all.order(:file_type_id).paginate(page: params[:page])
|
||||
authorize!
|
||||
end
|
||||
|
||||
# GET /file_templates/1
|
||||
# GET /file_templates/1.json
|
||||
def show
|
||||
authorize!
|
||||
end
|
||||
|
||||
# GET /file_templates/new
|
||||
def new
|
||||
@file_template = FileTemplate.new
|
||||
authorize!
|
||||
end
|
||||
|
||||
# GET /file_templates/1/edit
|
||||
def edit
|
||||
authorize!
|
||||
end
|
||||
|
||||
# POST /file_templates
|
||||
# POST /file_templates.json
|
||||
def create
|
||||
@file_template = FileTemplate.new(file_template_params)
|
||||
authorize!
|
||||
|
||||
respond_to do |format|
|
||||
if @file_template.save
|
||||
format.html { redirect_to @file_template, notice: 'File template was successfully created.' }
|
||||
format.json { render :show, status: :created, location: @file_template }
|
||||
else
|
||||
format.html { render :new }
|
||||
format.json { render json: @file_template.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PATCH/PUT /file_templates/1
|
||||
# PATCH/PUT /file_templates/1.json
|
||||
def update
|
||||
authorize!
|
||||
respond_to do |format|
|
||||
if @file_template.update(file_template_params)
|
||||
format.html { redirect_to @file_template, notice: 'File template was successfully updated.' }
|
||||
format.json { render :show, status: :ok, location: @file_template }
|
||||
else
|
||||
format.html { render :edit }
|
||||
format.json { render json: @file_template.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /file_templates/1
|
||||
# DELETE /file_templates/1.json
|
||||
def destroy
|
||||
authorize!
|
||||
@file_template.destroy
|
||||
respond_to do |format|
|
||||
format.html { redirect_to file_templates_url, notice: 'File template was successfully destroyed.' }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_file_template
|
||||
@file_template = FileTemplate.find(params[:id])
|
||||
end
|
||||
|
||||
# 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)
|
||||
end
|
||||
end
|
10
app/models/file_template.rb
Normal file
10
app/models/file_template.rb
Normal file
@ -0,0 +1,10 @@
|
||||
class FileTemplate < ActiveRecord::Base
|
||||
|
||||
belongs_to :file_type
|
||||
|
||||
|
||||
def to_s
|
||||
name
|
||||
end
|
||||
|
||||
end
|
@ -12,6 +12,7 @@ class FileType < ActiveRecord::Base
|
||||
|
||||
has_many :execution_environments
|
||||
has_many :files
|
||||
has_many :file_templates
|
||||
|
||||
validates :binary, boolean_presence: true
|
||||
validates :editor_mode, presence: true, unless: :binary?
|
||||
|
11
app/policies/file_template_policy.rb
Normal file
11
app/policies/file_template_policy.rb
Normal file
@ -0,0 +1,11 @@
|
||||
class FileTemplatePolicy < AdminOnlyPolicy
|
||||
|
||||
def show?
|
||||
everyone
|
||||
end
|
||||
|
||||
def by_file_type?
|
||||
everyone
|
||||
end
|
||||
|
||||
end
|
@ -8,7 +8,7 @@
|
||||
- if current_user.admin?
|
||||
li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path)
|
||||
li.divider
|
||||
- models = [ExecutionEnvironment, Exercise, Consumer, CodeHarborLink, ExternalUser, FileType, InternalUser, Submission].sort_by { |model| model.model_name.human(count: 2) }
|
||||
- models = [ExecutionEnvironment, Exercise, Consumer, CodeHarborLink, ExternalUser, FileType, FileTemplate, InternalUser, Submission].sort_by { |model| model.model_name.human(count: 2) }
|
||||
- models.each do |model|
|
||||
- if policy(model).index?
|
||||
li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path"))
|
||||
|
@ -8,5 +8,9 @@
|
||||
.form-group
|
||||
= f.label(:file_type_id, t('activerecord.attributes.file.file_type_id'))
|
||||
= f.collection_select(:file_type_id, FileType.where(binary: false).order(:name), :id, :name, {selected: @exercise.execution_environment.file_type.try(:id)}, class: 'form-control')
|
||||
.form-group
|
||||
= f.label(:file_template_id, t('activerecord.attributes.file.file_template_id'))
|
||||
= f.collection_select(:file_template_id, FileTemplate.all.order(:name), :id, :name, {:include_blank => true}, class: 'form-control')
|
||||
= f.hidden_field(:context_id)
|
||||
.hidden#noTemplateLabel data-text=t('file_template.no_template_label')
|
||||
.actions = render('shared/submit_button', f: f, object: CodeOcean::File.new)
|
||||
|
12
app/views/file_templates/_form.html.slim
Normal file
12
app/views/file_templates/_form.html.slim
Normal file
@ -0,0 +1,12 @@
|
||||
= form_for(@file_template) do |f|
|
||||
= render('shared/form_errors', object: @file_template)
|
||||
.form-group
|
||||
= f.label(:name)
|
||||
= f.text_field(:name, class: 'form-control', required: true)
|
||||
.form-group
|
||||
= f.label(:file_type_id)
|
||||
= f.collection_select(:file_type_id, FileType.all.order(:name), :id, :name, {}, class: 'form-control')
|
||||
.form-group
|
||||
= f.label(:content)
|
||||
= f.text_area(:content, class: 'form-control')
|
||||
.actions = render('shared/submit_button', f: f, object: @file_template)
|
3
app/views/file_templates/edit.html.slim
Normal file
3
app/views/file_templates/edit.html.slim
Normal file
@ -0,0 +1,3 @@
|
||||
h1 = @file_template
|
||||
|
||||
= render('form')
|
20
app/views/file_templates/index.html.slim
Normal file
20
app/views/file_templates/index.html.slim
Normal file
@ -0,0 +1,20 @@
|
||||
h1 = FileTemplate.model_name.human(count: 2)
|
||||
|
||||
.table-responsive
|
||||
table.table
|
||||
thead
|
||||
tr
|
||||
th = t('activerecord.attributes.file_template.name')
|
||||
th = t('activerecord.attributes.file_template.file_type')
|
||||
th colspan=3 = t('shared.actions')
|
||||
tbody
|
||||
- @file_templates.each do |file_template|
|
||||
tr
|
||||
td = file_template.name
|
||||
td = link_to(file_template.file_type, file_type_path(file_template.file_type))
|
||||
td = link_to(t('shared.show'), file_template)
|
||||
td = link_to(t('shared.edit'), edit_file_template_path(file_template))
|
||||
td = link_to(t('shared.destroy'), file_template, data: {confirm: t('shared.confirm_destroy')}, method: :delete)
|
||||
|
||||
= render('shared/pagination', collection: @file_templates)
|
||||
p = render('shared/new_button', model: FileTemplate)
|
3
app/views/file_templates/new.html.slim
Normal file
3
app/views/file_templates/new.html.slim
Normal file
@ -0,0 +1,3 @@
|
||||
h1 = t('shared.new_model', model: FileTemplate.model_name.human)
|
||||
|
||||
= render('form')
|
7
app/views/file_templates/show.html.slim
Normal file
7
app/views/file_templates/show.html.slim
Normal file
@ -0,0 +1,7 @@
|
||||
h1
|
||||
= @file_template
|
||||
= render('shared/edit_button', object: @file_template)
|
||||
|
||||
= row(label: 'file_template.name', value: @file_template.name)
|
||||
= row(label: 'file_template.file_type', value: link_to(@file_template.file_type, file_type_path(@file_template.file_type)))
|
||||
= row(label: 'file_template.content', value: @file_template.content)
|
@ -52,6 +52,7 @@ de:
|
||||
read_only: Schreibgeschützt
|
||||
role: Rolle
|
||||
weight: Punktzahl
|
||||
file_template_id: "Dateivorlage"
|
||||
file_type:
|
||||
binary: Binär
|
||||
editor_mode: Editor-Modus
|
||||
@ -89,6 +90,10 @@ de:
|
||||
files: Dateien
|
||||
score: Punktzahl
|
||||
user: Autor
|
||||
file_template:
|
||||
name: "Name"
|
||||
file_type: "Dateityp"
|
||||
content: "Code"
|
||||
models:
|
||||
code_harbor_link:
|
||||
one: CodeHarbor-Link
|
||||
@ -111,6 +116,9 @@ de:
|
||||
file:
|
||||
one: Datei
|
||||
other: Dateien
|
||||
file_template:
|
||||
one: Dateivorlage
|
||||
other: Dateivorlagen
|
||||
file_type:
|
||||
one: Dateityp
|
||||
other: Dateitypen
|
||||
@ -447,3 +455,5 @@ de:
|
||||
next_label: 'Nächste Seite →'
|
||||
page_gap: '…'
|
||||
previous_label: '← Vorherige Seite'
|
||||
file_template:
|
||||
no_template_label: "Leere Datei"
|
||||
|
@ -52,6 +52,7 @@ en:
|
||||
read_only: Read-only
|
||||
role: Role
|
||||
weight: Score
|
||||
file_template_id: "File Template"
|
||||
file_type:
|
||||
binary: Binary
|
||||
editor_mode: Editor Mode
|
||||
@ -89,6 +90,10 @@ en:
|
||||
files: Files
|
||||
score: Score
|
||||
user: Author
|
||||
file_template:
|
||||
name: "Name"
|
||||
file_type: "File Type"
|
||||
content: "Content"
|
||||
models:
|
||||
code_harbor_link:
|
||||
one: CodeHarbor Link
|
||||
@ -111,6 +116,9 @@ en:
|
||||
file:
|
||||
one: File
|
||||
other: Files
|
||||
file_template:
|
||||
one: File Template
|
||||
other: File Templates
|
||||
file_type:
|
||||
one: File Type
|
||||
other: File Types
|
||||
@ -447,3 +455,5 @@ en:
|
||||
next_label: 'Next Page →'
|
||||
page_gap: '…'
|
||||
previous_label: '← Previous Page'
|
||||
file_template:
|
||||
no_template_label: "Empty File"
|
||||
|
@ -1,6 +1,11 @@
|
||||
FILENAME_REGEXP = /[\w\.]+/ unless Kernel.const_defined?(:FILENAME_REGEXP)
|
||||
|
||||
Rails.application.routes.draw do
|
||||
resources :file_templates do
|
||||
collection do
|
||||
get 'by_file_type/:file_type_id', as: :by_file_type, to: :by_file_type
|
||||
end
|
||||
end
|
||||
resources :code_harbor_links
|
||||
resources :request_for_comments do
|
||||
member do
|
||||
|
10
db/migrate/20160609185708_create_file_templates.rb
Normal file
10
db/migrate/20160609185708_create_file_templates.rb
Normal file
@ -0,0 +1,10 @@
|
||||
class CreateFileTemplates < ActiveRecord::Migration
|
||||
def change
|
||||
create_table :file_templates do |t|
|
||||
t.string :name
|
||||
t.text :content
|
||||
t.belongs_to :file_type
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
5
db/migrate/20160610111602_add_file_template_to_file.rb
Normal file
5
db/migrate/20160610111602_add_file_template_to_file.rb
Normal file
@ -0,0 +1,5 @@
|
||||
class AddFileTemplateToFile < ActiveRecord::Migration
|
||||
def change
|
||||
add_reference :files, :file_template
|
||||
end
|
||||
end
|
@ -100,6 +100,14 @@ ActiveRecord::Schema.define(version: 20160704143402) do
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
create_table "file_templates", force: true do |t|
|
||||
t.string "name"
|
||||
t.text "content"
|
||||
t.integer "file_type_id"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
end
|
||||
|
||||
create_table "file_types", force: true do |t|
|
||||
t.string "editor_mode"
|
||||
t.string "file_extension"
|
||||
@ -131,6 +139,7 @@ ActiveRecord::Schema.define(version: 20160704143402) do
|
||||
t.string "feedback_message"
|
||||
t.float "weight"
|
||||
t.string "path"
|
||||
t.integer "file_template_id"
|
||||
end
|
||||
|
||||
add_index "files", ["context_id", "context_type"], name: "index_files_on_context_id_and_context_type", using: :btree
|
||||
|
Reference in New Issue
Block a user