From f336a2c244392e74d609e3ec748d9aa731a0d01c Mon Sep 17 00:00:00 2001 From: Anton Argirov Date: Mon, 25 May 2015 16:57:02 +0600 Subject: [PATCH] #1: Need save and restore abilities --- .../custom_workflows_controller.rb | 26 +++++++++++++++- app/models/custom_workflow.rb | 15 ++++++++++ app/views/custom_workflows/_form.html.erb | 17 ++++++----- app/views/custom_workflows/edit.html.erb | 4 +-- app/views/custom_workflows/index.html.erb | 28 ++++++++++++++++-- .../settings/_custom_workflow.html.erb | 3 +- assets/images/export.png | Bin 0 -> 265 bytes assets/images/import.png | Bin 0 -> 279 bytes assets/stylesheets/style.css | 7 +++++ config/locales/en.yml | 14 +++++++-- config/locales/ru.yml | 12 +++++++- config/routes.rb | 15 ++++------ ...thor_and_timestamps_to_custom_workflows.rb | 6 ++++ 13 files changed, 117 insertions(+), 30 deletions(-) create mode 100644 assets/images/export.png create mode 100644 assets/images/import.png create mode 100644 db/migrate/20150525083345_add_author_and_timestamps_to_custom_workflows.rb diff --git a/app/controllers/custom_workflows_controller.rb b/app/controllers/custom_workflows_controller.rb index 7264677..f5eb760 100644 --- a/app/controllers/custom_workflows_controller.rb +++ b/app/controllers/custom_workflows_controller.rb @@ -2,7 +2,7 @@ class CustomWorkflowsController < ApplicationController layout 'admin' before_filter :require_admin - before_filter :find_workflow, :only => [:show, :edit, :update, :destroy] + before_filter :find_workflow, :only => [:show, :edit, :update, :destroy, :export] def index @workflows = CustomWorkflow.includes(:projects).all @@ -11,6 +11,10 @@ class CustomWorkflowsController < ApplicationController end end + def export + send_data @workflow.export_as_xml, :filename => @workflow.name + '.xml', :type => :xml + end + def show respond_to do |format| format.html { redirect_to edit_custom_workflow_path } @@ -22,16 +26,36 @@ class CustomWorkflowsController < ApplicationController def new @workflow = CustomWorkflow.new + @workflow.author = cookies[:custom_workflows_author] respond_to do |format| format.html end end + def import + xml = params[:file].read + begin + @workflow = CustomWorkflow.import_from_xml(xml) + if @workflow.save + flash[:notice] = l(:notice_successful_import) + else + flash[:error] = @workflow.errors.full_messages.to_sentence + end + rescue Exception => e + Rails.logger.warn "Workflow import error: #{e.message}\n #{e.backtrace.join("\n ")}" + flash[:error] = l(:error_failed_import) + end + respond_to do |format| + format.html { redirect_to(custom_workflows_path) } + end + end + def create @workflow = CustomWorkflow.new(params[:custom_workflow]) respond_to do |format| if @workflow.save flash[:notice] = l(:notice_successful_create) + cookies[:custom_workflows_author] = @workflow.author format.html { redirect_to(custom_workflows_path) } else format.html { render :action => "new" } diff --git a/app/models/custom_workflow.rb b/app/models/custom_workflow.rb index 6271b25..0dfa5c7 100644 --- a/app/models/custom_workflow.rb +++ b/app/models/custom_workflow.rb @@ -1,5 +1,6 @@ class WorkflowError < StandardError attr_accessor :error + def initialize(message) @error = message.dup super message @@ -19,6 +20,14 @@ class CustomWorkflow < ActiveRecord::Base default_scope { order(:position => :asc) } scope :for_all, lambda { where(:is_for_all => true) } + class << self + def import_from_xml(xml) + attributes = Hash.from_xml(xml).values.first + attributes.delete('exported_at') + CustomWorkflow.new(attributes) + end + end + def validate_syntax issue = Issue.new issue.send :instance_variable_set, :@issue, issue # compatibility with 0.0.1 @@ -36,6 +45,12 @@ class CustomWorkflow < ActiveRecord::Base end end + def export_as_xml + to_xml :only => [:author, :name, :description, :before_save, :after_save, :created_at] do |xml| + xml.tag! 'exported-at', Time.current.xmlschema + end + end + def <=>(other) self.position <=> other.position end diff --git a/app/views/custom_workflows/_form.html.erb b/app/views/custom_workflows/_form.html.erb index 25e9f16..5a642a8 100644 --- a/app/views/custom_workflows/_form.html.erb +++ b/app/views/custom_workflows/_form.html.erb @@ -1,12 +1,13 @@
-
-

<%= f.text_field :name, :required => true, :size => 50 %>

- -

<%= f.text_area :description, :cols => 40, :rows => 5 %>

- -

- -

+
+
+

<%= f.text_field :name, :required => true, :size => 50 %>

+

<%= f.text_field :author, :size => 50, :label => :field_custom_workflow_author %> + <%= l(:text_custom_workflow_author) %> +

+

<%= f.text_area :description, :cols => 40, :rows => 5 %>

+

<%= f.check_box :is_for_all, :onclick => "checkAndDisable('custom_workflow_enabled_projects', this.checked);", :label => :field_enabled_for_all_projects %>

+
diff --git a/app/views/custom_workflows/edit.html.erb b/app/views/custom_workflows/edit.html.erb index bd23ace..ffd0be0 100644 --- a/app/views/custom_workflows/edit.html.erb +++ b/app/views/custom_workflows/edit.html.erb @@ -2,11 +2,9 @@ <%= error_messages_for 'workflow' %> -<% form = form_for @workflow, :builder => (TabularFormBuilder rescue Redmine::Views::LabelledFormBuilder) do |f| %> +<%= labelled_form_for @workflow do |f| %> <%= render :partial => 'form', :locals => {:f => f} %> <%= submit_tag l(:button_save) %> <% end %> -<%= form if Redmine::VERSION::MAJOR >= 2 %> - <% html_title(l(:label_custom_workflow_plural), @workflow, l(:label_administration)) -%> diff --git a/app/views/custom_workflows/index.html.erb b/app/views/custom_workflows/index.html.erb index e3399e7..6e97dce 100644 --- a/app/views/custom_workflows/index.html.erb +++ b/app/views/custom_workflows/index.html.erb @@ -1,6 +1,7 @@ <% html_title(l(:label_custom_workflow_plural)) -%>
-<%= link_to l(:label_custom_workflow_new), new_custom_workflow_path, :class => 'icon icon-add' %> + <%= link_to l(:label_custom_workflow_import), '#', :class => 'icon icon-import', :onclick => "showModal('import-dialog', '450px'); return false;" %> + <%= link_to l(:label_custom_workflow_new), new_custom_workflow_path, :class => 'icon icon-add' %>

<%=l(:label_custom_workflow_plural)%>

@@ -12,6 +13,7 @@ <%=l(:field_name)%> <%=l(:field_description)%> + <%=l(:field_author)%> <%=l(:label_project_plural)%> <%= l(:button_sort) %> @@ -21,8 +23,9 @@ <% @workflows.each do |workflow| %> "> <%= link_to(workflow.name, edit_custom_workflow_path(workflow)) %> - <%= textilizable(workflow.description) %> - + <%= textilizable(workflow.description) %> + <%= workflow.author %> + <% if workflow.is_for_all? %> <%= l(:field_enabled_for_all_projects) %> <% elsif workflow.projects.empty? %> @@ -33,6 +36,7 @@ <%= reorder_links("custom_workflow", {:action => 'update', :id => workflow}) %> + <%= link_to(l(:label_custom_workflow_export), export_custom_workflow_path(workflow), :class => 'icon icon-export', :method => :get) %> <%= link_to(l(:button_delete), workflow, :class => 'icon icon-del', :data => {:confirm => l(:text_are_you_sure)}, :confirm => l(:text_are_you_sure), :method => :delete) %> @@ -42,4 +46,22 @@ <% else %>

<%= l(:label_no_data) %>

<% end %> +
+ + \ No newline at end of file diff --git a/app/views/projects/settings/_custom_workflow.html.erb b/app/views/projects/settings/_custom_workflow.html.erb index 1f4ee96..b3a4e8d 100644 --- a/app/views/projects/settings/_custom_workflow.html.erb +++ b/app/views/projects/settings/_custom_workflow.html.erb @@ -1,4 +1,4 @@ -<% form = form_for @project do |f| %> +<%= form_for @project do |f| %> <%= hidden_field_tag :tab, 'custom_workflow' %> <%= hidden_field_tag 'project[custom_workflow_ids][]', '' %>
@@ -17,4 +17,3 @@ <%= submit_tag l(:button_save) %> <% end %> -<%= form if Redmine::VERSION::MAJOR >= 2 %> diff --git a/assets/images/export.png b/assets/images/export.png new file mode 100644 index 0000000000000000000000000000000000000000..49bfc2b87aa619f2bfa9577f02e31fb6e3e420c9 GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`?>!lvI6;>1s;*b z3=DjSK$uZf!>a)(C{f}XQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXPVk-lnPYS?&;zf zV&Q+a=QL-7fdK1v2JVe=w>Nag=sIp-U47!FmCALM?=xCs&rFv%(#*`S@{Y&GQPVUc zd+B^{cXft!2RF&{Ez^w>6L3mdeKM9&jW?88HDX@)P4Np+?na01ADc1xOO?V@!D+QM zDtqM)JPt@xl00}kNI~U~|1ICDfJg3)*Y!lvI6;>1s;*b z3=DjSK$uZf!>a)(C{f}XQ4*Y=R#Ki=l*&+$n3-3imzP?iV4`QBXPVk-lnPWc)zif> z#KQk-|3S_M10Gl7hGvWY>>V!a`xJIG1%G)Xb1D0F_1EO4z$;ph4zf2cs^oXFoKx)D zbMf5dFVSt+J1(*M+IJ*;T(-))G&`6hF{PGMBH2vYh=t|brtkYw{@O4w7HcxgUhr^8 z*(rPY71Ok0M%i0;_&3z3Gf3XCd&qMw?1Q2Bh5wP!H}z#cE_TS<$8FQNb-RFXre~P& Uf{pFcKnF5-y85}Sb4q9e0CB8gkN^Mx literal 0 HcmV?d00001 diff --git a/assets/stylesheets/style.css b/assets/stylesheets/style.css index e32e9b9..5260306 100644 --- a/assets/stylesheets/style.css +++ b/assets/stylesheets/style.css @@ -6,6 +6,10 @@ table.list.custom-workflows td { vertical-align: middle; } +table.list.custom-workflows td.description { + text-align: left; +} + #custom_workflow_description, #custom_workflow_name { width: 98%; } @@ -19,3 +23,6 @@ table.list.custom-workflows td { max-height: 200px; overflow-y: auto; } + +.icon-export { background-image: url(../images/export.png); } +.icon-import { background-image: url(../images/import.png); } diff --git a/config/locales/en.yml b/config/locales/en.yml index 94a0ba7..5e5138c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1,20 +1,29 @@ en: project_module_custom_workflows_module: "Custom workflows" permission_manage_project_workflow: "Manage project custom workflows" + label_custom_workflow: "Custom workflows" label_custom_workflow_plural: "Custom workflows" label_custom_workflow_new: "Create a custom workflow" label_workflow_scripts: "Workflow scripts" label_enabled_projects: "Enabled for project(s)" + label_custom_workflow_export: "Export" + label_custom_workflow_import: "Import workflow" + + button_import: "Import" field_after_save: "Workflow script executable after saving the issue" field_before_save: "Workflow script executable before saving the issue" field_is_enabled: "Enabled" field_enabled_for_all_projects: "Enabled for all projects" - + field_custom_workflow_author: "Custom workflow's author" + field_custom_workflow_file: "Select the XML file previously exported process" field_custom_workflow: script: "Workflow script" + notice_successful_import: "Custom workflow has successfully imported" + error_failed_import: "Error importing custom workflow (unknown format? please see log)" + activerecord: errors: messages: @@ -26,4 +35,5 @@ en: text_custom_workflow_before_save_note: You can change properties of the issues here. Do not create or update related issues in this script. To finish with error, use raise WorkflowError, "Message to user". text_custom_workflow_after_save_note: You can update or create related issues here. Note that this script will be also executed for the newly created issues. So make appropriate checks to prevent infinite recursion. text_custom_workflow_general_note: Both scripts are executed in the context of the issue like ordinary before_save and after_save callbacks. So use methods and properties of the issue directly (or through "self"). Instance variables (@variable) are also allowed and may be used if needed. - text_no_enabled_projects: No projects \ No newline at end of file + text_no_enabled_projects: No projects + text_custom_workflow_author: Will be included in exported XML \ No newline at end of file diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 0827c0d..9e636d8 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1,20 +1,29 @@ ru: project_module_custom_workflows_module: "Пользовательские рабочие процессы" permission_manage_project_workflow: "Управление пользовательскими рабочими процессами в проекте" + label_custom_workflow: "Пользовательский рабочий процесс" label_custom_workflow_plural: "Пользовательские рабочие процессы" label_custom_workflow_new: "Новый процесс" label_workflow_scripts: "Сценарии" label_enabled_projects: "Разрешен в проектах" + label_custom_workflow_export: "Экспорт" + label_custom_workflow_import: "Импорт процесса" + + button_import: "Импортировать" field_after_save: "Сценарий выполняемый после сохранения задачи" field_before_save: "Сценарий выполняемый перед сохранением задачи" field_is_enabled: "Разрешено" field_enabled_for_all_projects: "Разрешен для всех проектов" - + field_custom_workflow_author: "Автор рабочего процесса" + field_custom_workflow_file: "Выберите XML файл ранее экспортированного процесса" field_custom_workflow: script: "Сценарий" + notice_successful_import: "Рабочий процесс успешно импортирован" + error_failed_import: "Ошибка импорта рабочего процесса (неверный формат? смотри журнал)" + activerecord: errors: messages: @@ -27,3 +36,4 @@ ru: text_custom_workflow_after_save_note: Вы можете обновлять и создавать задачи (в том числе и связанные задачи) здесь. Обратите внимание, что данный сценарий будет также выполняться и для вновь создаваемых задач. Поэтому используйте дополнительные проверки, чтобы избежать бесконечной рекурсии. text_custom_workflow_general_note: Оба сценария исполняются в контексте задачи, как и обычные обратные вызовы before_save и after_save. Поэтому используйте методы и свойства задачи напрямую или через ключевое слово self. text_no_enabled_projects: Нет проектов + text_custom_workflow_author: Будет использован в XML файле при экспорте \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 40d9705..14684bc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,11 +1,6 @@ -if Redmine::VERSION::MAJOR >= 2 - RedmineApp::Application.routes.draw do - resources :custom_workflows - post '/custom_workflows/:id', :to => 'custom_workflows#update' - end -else - ActionController::Routing::Routes.draw do |map| - map.resources :custom_workflows - map.connect '/custom_workflows/:id', :controller => 'custom_workflows', :action => 'update', :conditions => { :method => :post } - end +RedmineApp::Application.routes.draw do + post '/custom_workflows/import', :to => 'custom_workflows#import', :as => 'import_custom_workflow' + resources :custom_workflows + post '/custom_workflows/:id', :to => 'custom_workflows#update' + get '/custom_workflows/:id/export', :to => 'custom_workflows#export', :as => 'export_custom_workflow' end diff --git a/db/migrate/20150525083345_add_author_and_timestamps_to_custom_workflows.rb b/db/migrate/20150525083345_add_author_and_timestamps_to_custom_workflows.rb new file mode 100644 index 0000000..76be796 --- /dev/null +++ b/db/migrate/20150525083345_add_author_and_timestamps_to_custom_workflows.rb @@ -0,0 +1,6 @@ +class AddAuthorAndTimestampsToCustomWorkflows < ActiveRecord::Migration + def change + add_column :custom_workflows, :author, :string, :null => true + add_timestamps :custom_workflows + end +end