diff --git a/app/assets/javascripts/exercises.js b/app/assets/javascripts/exercises.js
index e6773f40..81da8cf8 100644
--- a/app/assets/javascripts/exercises.js
+++ b/app/assets/javascripts/exercises.js
@@ -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 = "";
+ for (var i = 0; i < response.length; i++) {
+ options += ""
+ }
+ $("#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) {
diff --git a/app/assets/javascripts/file_templates.js.coffee b/app/assets/javascripts/file_templates.js.coffee
new file mode 100644
index 00000000..24f83d18
--- /dev/null
+++ b/app/assets/javascripts/file_templates.js.coffee
@@ -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/
diff --git a/app/assets/stylesheets/file_templates.css.scss b/app/assets/stylesheets/file_templates.css.scss
new file mode 100644
index 00000000..bf8e27e8
--- /dev/null
+++ b/app/assets/stylesheets/file_templates.css.scss
@@ -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/
diff --git a/app/controllers/code_ocean/files_controller.rb b/app/controllers/code_ocean/files_controller.rb
index 74c1932f..55d8b369 100644
--- a/app/controllers/code_ocean/files_controller.rb
+++ b/app/controllers/code_ocean/files_controller.rb
@@ -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
diff --git a/app/controllers/concerns/file_parameters.rb b/app/controllers/concerns/file_parameters.rb
index e61e719e..295b66c3 100644
--- a/app/controllers/concerns/file_parameters.rb
+++ b/app/controllers/concerns/file_parameters.rb
@@ -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
diff --git a/app/controllers/file_templates_controller.rb b/app/controllers/file_templates_controller.rb
new file mode 100644
index 00000000..a6039500
--- /dev/null
+++ b/app/controllers/file_templates_controller.rb
@@ -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
diff --git a/app/models/file_template.rb b/app/models/file_template.rb
new file mode 100644
index 00000000..ef068e13
--- /dev/null
+++ b/app/models/file_template.rb
@@ -0,0 +1,10 @@
+class FileTemplate < ActiveRecord::Base
+
+ belongs_to :file_type
+
+
+ def to_s
+ name
+ end
+
+end
diff --git a/app/models/file_type.rb b/app/models/file_type.rb
index 53bf18dc..d3b519d5 100644
--- a/app/models/file_type.rb
+++ b/app/models/file_type.rb
@@ -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?
diff --git a/app/policies/file_template_policy.rb b/app/policies/file_template_policy.rb
new file mode 100644
index 00000000..92ced442
--- /dev/null
+++ b/app/policies/file_template_policy.rb
@@ -0,0 +1,11 @@
+class FileTemplatePolicy < AdminOnlyPolicy
+
+ def show?
+ everyone
+ end
+
+ def by_file_type?
+ everyone
+ end
+
+end
diff --git a/app/views/application/_navigation.html.slim b/app/views/application/_navigation.html.slim
index 3c260cb8..8aa289d3 100644
--- a/app/views/application/_navigation.html.slim
+++ b/app/views/application/_navigation.html.slim
@@ -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"))
diff --git a/app/views/code_ocean/files/_form.html.slim b/app/views/code_ocean/files/_form.html.slim
index 07dd3355..46c5b2c2 100644
--- a/app/views/code_ocean/files/_form.html.slim
+++ b/app/views/code_ocean/files/_form.html.slim
@@ -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)
diff --git a/app/views/file_templates/_form.html.slim b/app/views/file_templates/_form.html.slim
new file mode 100644
index 00000000..1a5c34bc
--- /dev/null
+++ b/app/views/file_templates/_form.html.slim
@@ -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)
diff --git a/app/views/file_templates/edit.html.slim b/app/views/file_templates/edit.html.slim
new file mode 100644
index 00000000..c198271f
--- /dev/null
+++ b/app/views/file_templates/edit.html.slim
@@ -0,0 +1,3 @@
+h1 = @file_template
+
+= render('form')
diff --git a/app/views/file_templates/index.html.slim b/app/views/file_templates/index.html.slim
new file mode 100644
index 00000000..3022ea53
--- /dev/null
+++ b/app/views/file_templates/index.html.slim
@@ -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)
diff --git a/app/views/file_templates/new.html.slim b/app/views/file_templates/new.html.slim
new file mode 100644
index 00000000..bf434860
--- /dev/null
+++ b/app/views/file_templates/new.html.slim
@@ -0,0 +1,3 @@
+h1 = t('shared.new_model', model: FileTemplate.model_name.human)
+
+= render('form')
diff --git a/app/views/file_templates/show.html.slim b/app/views/file_templates/show.html.slim
new file mode 100644
index 00000000..19f0d28f
--- /dev/null
+++ b/app/views/file_templates/show.html.slim
@@ -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)
diff --git a/config/locales/de.yml b/config/locales/de.yml
index ce8ba98f..a56ca31e 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -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"
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 86acbb15..90590395 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -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"
diff --git a/config/routes.rb b/config/routes.rb
index b9f33826..0683a6f1 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -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
diff --git a/db/migrate/20160609185708_create_file_templates.rb b/db/migrate/20160609185708_create_file_templates.rb
new file mode 100644
index 00000000..43cde0d0
--- /dev/null
+++ b/db/migrate/20160609185708_create_file_templates.rb
@@ -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
diff --git a/db/migrate/20160610111602_add_file_template_to_file.rb b/db/migrate/20160610111602_add_file_template_to_file.rb
new file mode 100644
index 00000000..a595e90b
--- /dev/null
+++ b/db/migrate/20160610111602_add_file_template_to_file.rb
@@ -0,0 +1,5 @@
+class AddFileTemplateToFile < ActiveRecord::Migration
+ def change
+ add_reference :files, :file_template
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 79d13498..57ce32e7 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -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