diff --git a/app/assets/javascripts/jobs.js b/app/assets/javascripts/jobs.js index 40a62e0..f899386 100644 --- a/app/assets/javascripts/jobs.js +++ b/app/assets/javascripts/jobs.js @@ -1,22 +1,23 @@ $(document).ready(function() { $("#job_template_schedule_id").change(function() { showSchedule(); }); $("#job_template_fileset_id").change(function() { showFileset(); }); }); function showSchedule() { var scheduleId = $("#job_template_schedule_id").val(); $.ajax({ - url: "/hosts/" + hostId + "/schedules/" + scheduleId + url: "/hosts/" + hostId + "/schedules/" + scheduleId, + data: jobIdParam }); } function showFileset() { var filesetId = $("#job_template_fileset_id").val(); $.ajax({ url: "/hosts/" + hostId + "/filesets/" + filesetId }); } diff --git a/app/controllers/jobs_controller.rb b/app/controllers/jobs_controller.rb index 5816f06..de15270 100644 --- a/app/controllers/jobs_controller.rb +++ b/app/controllers/jobs_controller.rb @@ -1,80 +1,81 @@ class JobsController < ApplicationController before_action :require_logged_in before_action :fetch_host, only: [:new, :edit, :show, :create, :update, :toggle_enable, :backup_now] before_action :fetch_job, only: [:show, :edit, :update, :destroy, :toggle_enable, :backup_now] # GET /jobs def new @job = @host.job_templates.new + @schedule = @host.schedules.find(params[:schedule_id]) if params[:schedule_id] end # POST /jobs def create @job = @host.job_templates.new(fetch_params) if @job.save flash[:success] = 'Job created successfully' redirect_to host_path(@host) else render :new end end # GET /jobs/1 def show; end # GET /jobs/1/edit def edit @fileset = @host.filesets.find(params[:fileset_id]) if params[:fileset_id] @schedule = @host.schedules.find(params[:schedule_id]) if params[:schedule_id] end # PUT /jobs/1 def update if @job.update_attributes(fetch_params) flash[:success] = 'Job updated' redirect_to host_job_path(@host, @job) else render :edit end end # DELETE /jobs/1 def destroy end # PATCH /hosts/1/jobs/1/enable def toggle_enable @job.enabled = !@job.enabled @job.save flash[:success] = @job.enabled? ? 'Job enabled' : 'Job disabled' redirect_to host_path(@host) end # POST /hosts/1/jobs/1/backup_now def backup_now if @job.backup_now flash[:success] = 'Backup directive was sent to bacula. Backup will be taken in a while' else flash[:error] = 'Backup was not sent, try again later' end redirect_to client_path(@host.client) end private def fetch_job @job = @host.job_templates.find(params[:id]) end def fetch_host @host = current_user.hosts.find(params[:host_id]) end def fetch_params params.require(:job_template). permit(:name, :fileset_id, :schedule_id, :client_before_run_file, :client_after_run_file) end end diff --git a/app/controllers/schedules_controller.rb b/app/controllers/schedules_controller.rb index fcfb33f..5345ba4 100644 --- a/app/controllers/schedules_controller.rb +++ b/app/controllers/schedules_controller.rb @@ -1,58 +1,72 @@ class SchedulesController < ApplicationController before_action :require_logged_in - before_action :fetch_host, only: [:new, :create, :show] - before_action :fetch_job_id, only: [:new, :create] + before_action :fetch_host, only: [:new, :create, :show, :edit, :update] + before_action :fetch_job_id, only: [:new, :create, :show, :edit, :update] + before_action :fetch_schedule, only: [:show, :edit, :update] def new @schedule = @host.schedules.new + @schedule.schedule_runs.build end def show - @schedule = @host.schedules.find(params[:id]) - respond_to do |format| format.js {} end end def edit end def update + if @schedule.update(fetch_params) + flash[:success] = 'Schedule updated successfully' + if @job_id + redirect_to edit_host_job_path(@host, @job_id, schedule_id: @schedule.id) + else + redirect_to new_host_job_path(@host, schedule_id: @schedule.id) + end + else + render :edit + end end def create @schedule = @host.schedules.new(fetch_params) @schedule.runtime = params[:schedule][:runtime] if params[:schedule][:runtime] if @schedule.save flash[:success] = 'Schedule created successfully' if @job_id.present? redirect_to edit_host_job_path(@host, @job_id, schedule_id: @schedule.id) else redirect_to new_host_job_path(@host, schedule_id: @schedule.id) end else render :new end end def destroy end private + def fetch_schedule + @schedule = @host.schedules.find(params[:id]) + end + def fetch_host @host = current_user.hosts.find(params[:host_id]) end def fetch_job_id @job_id = @host.job_templates.find(params[:job_id]).id if params[:job_id].present? end def fetch_params params.require(:schedule). - permit(:name, { schedule_runs_attributes: [[:level, :month, :day, :time]] }) + permit(:name, { schedule_runs_attributes: [[:id, :level, :month, :day, :time]] }) end end diff --git a/app/models/schedule_run.rb b/app/models/schedule_run.rb index af2ef1a..5d60ebb 100644 --- a/app/models/schedule_run.rb +++ b/app/models/schedule_run.rb @@ -1,133 +1,137 @@ # ScheduleRun is a helper class that modelizes the run directives of a schedule # resource. # # It can have 3 levels: # # * full # * differential # * incremental # # Each ScheduleRun instance holds info about the execution time of the schedule: # # * month specification is optional and is described by: # - month: full name (eg april) | month name three first letters (eg jul) # - month_range: month-month # - monthly: 'monthly' # * day specification is required and is described by: # - week_number (optional): weeks number in full text (eg: third, fourth) # - week_range (optional): week_number-week_number # - day: first three letters of day (eg: mon, fri) # - day_range: day-day # * time specification is required and is described by: # - 24h time (eg: 03:00) # # Schedule Run examples: # # Level=Full monthly first mon at 12:21 # Level=Full first sun at 12:34 # Level=Differential second-fifth sat at 15:23 # Level=Incremental mon-sat at 08:00 class ScheduleRun < ActiveRecord::Base enum level: { full: 0, differential: 1, incremental: 2 } MONTH_KW = %w{jan feb mar apr may jun jul aug sep oct nov dec january february march april may june july august september october november december} DAYS = (1..31).to_a WDAY_KW = %w{mon tue wed thu fri sat sun} WEEK_KW = %w{first second third fourth fifth} belongs_to :schedule validates :day, :time, :level, presence: true validate :correct_chars validate :month_valid, if: "month.present?" validate :time_valid validate :day_valid + def self.options_for_select + levels.keys.zip levels.keys + end + # Composes the schedule line for the bacula configuration # # @return [String] def schedule_line [ level_to_config, month, day, "at #{time}"].join(" ") end private def correct_chars [:month, :day, :time].each do |x| if self.send(x) && self.send(x).to_s.gsub(/[0-9a-zA-Z\-:]/, '').present? self.errors.add(x, 'Invalid characters') end end end def month_valid if !month_format? && !valid_month_range? self.errors.add(:month, 'Invalid month') end end def month_format? month_regex = "^(#{MONTH_KW.join('|')}|monthly)$" month.match(month_regex).present? end def valid_month_range? months = month.split('-') return false if months.length != 2 MONTH_KW.index(months.last) % 12 - MONTH_KW.index(months.first) % 12 > 0 end def time_valid if !time.match(/^([01][0-9]|2[0-3]):[0-5][0-9]$/) self.errors.add(:time, 'Invalid time') end end def day_valid components = day.split(' ') if components.length < 1 || components.length > 2 self.errors.add(:day, 'Invalid day') return false end if !valid_day?(components.last) && !valid_day_range?(components.last) self.errors.add(:day, 'Invalid day') return false end if components.length == 2 && !valid_week?(components.first) && !valid_week_range?(components.first) self.errors.add(:day, 'Invalid day') return false end true end def valid_day?(a_day) WDAY_KW.include? a_day end def valid_day_range?(a_range) days = a_range.split('-') return false if days.length != 2 WDAY_KW.index(days.last) - WDAY_KW.index(days.first) > 0 end def valid_week?(a_week) WEEK_KW.include? a_week end def valid_week_range?(a_range) weeks = a_range.split('-') return false if weeks.length != 2 WEEK_KW.index(weeks.last) - WEEK_KW.index(weeks.first) > 0 end def level_to_config "Level=#{level.capitalize}" end end diff --git a/app/views/jobs/_form.html.erb b/app/views/jobs/_form.html.erb index 652ea6f..d5524b5 100644 --- a/app/views/jobs/_form.html.erb +++ b/app/views/jobs/_form.html.erb @@ -1,33 +1,38 @@ <%= bootstrap_form_for(@job, url: @job.new_record? ? host_jobs_path : host_job_path(@host, @job), layout: :horizontal, label_col: 'col-xs-5', control_col: 'col-xs-5') do |f| %> <%= f.text_field :name %> <%= select_with_errors_and_button( @job, :job_template, :fileset_id, 'Fileset', options_from_collection_for_select( @host.filesets, :id, :name, params[:fileset_id] || @job.fileset_id), new_host_fileset_path(@host, job_id: @job.id)) %> <% if !@job.restore? %> <%= select_with_errors_and_button( @job, :job_template, :schedule_id, 'Schedule', options_from_collection_for_select( @host.schedules, :id, :name, params[:schedule_id] || @job.schedule_id), new_host_schedule_path(@host, job_id: @job.id)) %> <% end %> <%= f.text_field :client_before_run_file, label: 'Client Run Before Job' %> <%= f.text_field :client_after_run_file, label: 'Client Run After Job' %>
<%= f.submit class: "btn btn-success" %>
<% end %> diff --git a/app/views/jobs/_schedule.html.erb b/app/views/jobs/_schedule.html.erb index efe5fda..0b32487 100644 --- a/app/views/jobs/_schedule.html.erb +++ b/app/views/jobs/_schedule.html.erb @@ -1,8 +1,20 @@ +<% if job_id = (@job.try(:id) || @job_id) %> + <% url_options = { job_id: job_id } %> +<% else %> + <% url_options = {} %> +<% end %> +
-

Schedule "<%= schedule.name %>"

+

+ Schedule "<%= schedule.name %>" + <%= link_to edit_host_schedule_path(@host, schedule.id, url_options) do %> + + <% end %> +

+
-  <%= schedule.to_bacula_config_array.join("\n") %>
+<%= schedule.to_bacula_config_array.join("\n") %>
   
diff --git a/app/views/jobs/new.html.erb b/app/views/jobs/new.html.erb index 5b822f8..0790b0a 100644 --- a/app/views/jobs/new.html.erb +++ b/app/views/jobs/new.html.erb @@ -1,27 +1,29 @@

New Job Template

Job Attributes


<%= render 'form' %>
+ <% schedule = @schedule || @job.schedule %> + <%= render partial: 'schedule', locals: { schedule: schedule } if schedule %>
<%= link_to 'Cancel', host_path(@job.host), class: 'btn btn-danger', role: 'button' %>
diff --git a/app/views/schedules/_form.html.erb b/app/views/schedules/_form.html.erb index f53d299..5926f30 100644 --- a/app/views/schedules/_form.html.erb +++ b/app/views/schedules/_form.html.erb @@ -1,49 +1,49 @@ -<%= bootstrap_form_for(@schedule, url: host_schedules_path(@host), layout: :horizontal, +<%= bootstrap_form_for(@schedule, url: url, method: method, layout: :horizontal, label_col: 'col-xs-3', control_col: 'col-xs-8') do |f| %> <%= f.text_field :name, required: true %>
<% if ['schedule_runs.day','schedule_runs.month','scheduled_runs.time'] - @schedule.errors.keys %>
<% end %> - <%= f.fields_for :schedule_runs_attributes, index: nil do |r| %> + <%= f.fields_for :schedule_runs, @schedule.schedule_runs do |r| %>
- <%= r.select :level, options_for_select(ScheduleRun.levels.keys) %> + <%= r.select :level, options_for_select(ScheduleRun.options_for_select, r.object.level) %> <%= r.text_field :month, placeholder: '[month | month-range]' %>

eg: jan-mar, feb, monthly

<%= r.text_field :day, placeholder: '[week | week-range] day | day-range', required: true %>

eg: first sun, second-fifth mon, mon-sat

- <%= r.time_field :time, placeholder: 'HH:MM', required: true %> + <%= r.text_field :time, placeholder: 'HH:MM', required: true %>

<% end %>
<%= link_to '#', class: 'schedule_run_form_remove', style: 'display:none;' do %> <% end %>
<%= link_to '#', class: 'schedule_run_form_plus' do %> <% end %>
- <%= (hidden_field_tag :job_id, @job_id) if @job_id%> + <%= (hidden_field_tag :job_id, @job_id) if @job_id %>
<%= f.submit class: 'btn btn-success' %>
<% end %> diff --git a/app/views/schedules/edit.html.erb b/app/views/schedules/edit.html.erb new file mode 100644 index 0000000..2f16826 --- /dev/null +++ b/app/views/schedules/edit.html.erb @@ -0,0 +1,9 @@ +

Edit Schedule: <%= @schedule.name %>

+ +
+ <%= render partial: 'form', + locals: { url: host_schedule_path(@host, @schedule.id, job_id: @job_id), method: :patch } %> +
+ +<%= link_to 'Back to job', + @job_id.present? ? edit_host_job_path(@host, @job_id) : new_host_job_path(@host) %> diff --git a/app/views/schedules/new.html.erb b/app/views/schedules/new.html.erb index 4eab219..15112b5 100644 --- a/app/views/schedules/new.html.erb +++ b/app/views/schedules/new.html.erb @@ -1,8 +1,8 @@

New Schedule

- <%= render 'form' %> + <%= render partial: 'form', locals: { url: host_schedules_path(@host), method: :post } %>
<%= link_to 'Back to job', @job_id.present? ? edit_host_job_path(@host, @job_id) : new_host_job_path(@host) %>