agile_rails 0.0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/MIT-LICENSE +20 -0
- data/README.md +285 -0
- data/Rakefile +39 -0
- data/agile_rails.gemspec +36 -0
- data/app/assets/fonts/ibm-plex-sans-300.woff2 +0 -0
- data/app/assets/fonts/ibm-plex-sans-400.woff2 +0 -0
- data/app/assets/fonts/ibm-plex-sans-500.woff2 +0 -0
- data/app/assets/fonts/ibm-plex-sans-600.woff2 +0 -0
- data/app/assets/fonts/ibm-plex-sans-700.woff2 +0 -0
- data/app/assets/fonts/ibm-plex-sans-italic.woff2 +0 -0
- data/app/assets/images/32px.png +0 -0
- data/app/assets/images/throbber.gif +0 -0
- data/app/assets/javascripts/agile/agile.js +1489 -0
- data/app/assets/javascripts/agile/jquery-migrate.js +702 -0
- data/app/assets/javascripts/agile/jquery.bpopup.js +372 -0
- data/app/assets/javascripts/agile/jquery.datetimepicker.js +1353 -0
- data/app/assets/javascripts/agile/jstree.min.js +6 -0
- data/app/assets/javascripts/agile/select-multiple.js +459 -0
- data/app/assets/javascripts/agile/some_scripts.js +33 -0
- data/app/assets/javascripts/agile.js +22 -0
- data/app/assets/javascripts/agile_application.js +22 -0
- data/app/assets/javascripts/agile_editor.js +22 -0
- data/app/assets/stylesheets/agile/agile.css +1882 -0
- data/app/assets/stylesheets/agile/agile_apps.css +149 -0
- data/app/assets/stylesheets/agile/jquery.datetimepicker.css +304 -0
- data/app/assets/stylesheets/agile/jstree.css +1107 -0
- data/app/assets/stylesheets/agile/select-multiple.css +110 -0
- data/app/assets/stylesheets/agile/th-bg.png +0 -0
- data/app/assets/stylesheets/agile/theme.css +49 -0
- data/app/assets/stylesheets/agile.css +21 -0
- data/app/assets/stylesheets/agile_application.css +20 -0
- data/app/assets/stylesheets/agile_editor.css +19 -0
- data/app/controllers/agile_application_controller.rb +735 -0
- data/app/controllers/agile_common_controller.rb +345 -0
- data/app/controllers/agile_controller.rb +977 -0
- data/app/controllers/agile_main_controller.rb +36 -0
- data/app/controls/agile_control.rb +120 -0
- data/app/controls/agile_report.rb +364 -0
- data/app/controls/ar_category_control.rb +39 -0
- data/app/controls/ar_help_control.rb +139 -0
- data/app/controls/ar_image_control.rb +180 -0
- data/app/controls/ar_journal_control.rb +47 -0
- data/app/controls/ar_menu_item_control.rb +55 -0
- data/app/controls/ar_page_control.rb +64 -0
- data/app/controls/ar_poll_result_control.rb +84 -0
- data/app/controls/ar_setup_control.rb +62 -0
- data/app/controls/belongs_to_control.rb +61 -0
- data/app/controls/browse_models_control.rb +98 -0
- data/app/controls/settings_form_control.rb +137 -0
- data/app/forms/agile_help.yml +112 -0
- data/app/forms/agile_menu.yml +140 -0
- data/app/forms/agile_report_defaults.yml +39 -0
- data/app/forms/all_options.yml +810 -0
- data/app/forms/ar_ad.yml +121 -0
- data/app/forms/ar_big_table.yml +60 -0
- data/app/forms/ar_big_table_value.yml +52 -0
- data/app/forms/ar_browse_fields.yml +35 -0
- data/app/forms/ar_browse_models.yml +48 -0
- data/app/forms/ar_category.yml +73 -0
- data/app/forms/ar_category_as_tree.yml +31 -0
- data/app/forms/ar_design.yml +75 -0
- data/app/forms/ar_filter.yml +52 -0
- data/app/forms/ar_folder_permission.yml +56 -0
- data/app/forms/ar_folder_rule.yml +48 -0
- data/app/forms/ar_gallery.yml +55 -0
- data/app/forms/ar_image.yml +126 -0
- data/app/forms/ar_image_search.yml +83 -0
- data/app/forms/ar_journal.yml +76 -0
- data/app/forms/ar_json_ld.yml +56 -0
- data/app/forms/ar_key_value.yml +33 -0
- data/app/forms/ar_key_value_store.yml +33 -0
- data/app/forms/ar_link.yml +60 -0
- data/app/forms/ar_menu.yml +67 -0
- data/app/forms/ar_menu_item.yml +141 -0
- data/app/forms/ar_page.yml +187 -0
- data/app/forms/ar_part.yml +91 -0
- data/app/forms/ar_permission.yml +52 -0
- data/app/forms/ar_permission_rule.yml +40 -0
- data/app/forms/ar_piece.yml +106 -0
- data/app/forms/ar_policy.yml +64 -0
- data/app/forms/ar_policy_rule.yml +42 -0
- data/app/forms/ar_policy_rule_nocms.yml +40 -0
- data/app/forms/ar_poll.yml +118 -0
- data/app/forms/ar_poll_item.yml +78 -0
- data/app/forms/ar_poll_result.yml +88 -0
- data/app/forms/ar_poll_result_export.yml +35 -0
- data/app/forms/ar_removed_url.yml +41 -0
- data/app/forms/ar_role.yml +43 -0
- data/app/forms/ar_seo.yml +32 -0
- data/app/forms/ar_setup.yml +45 -0
- data/app/forms/ar_site.yml +149 -0
- data/app/forms/ar_steps_template.yml +51 -0
- data/app/forms/ar_user.yml +140 -0
- data/app/forms/ar_user_role.yml +57 -0
- data/app/forms/help/dc_category_as_tree.en +4 -0
- data/app/forms/help/dc_category_as_tree.sl +5 -0
- data/app/forms/json_ld_schema.yml +168 -0
- data/app/helpers/agile_application_helper.rb +1162 -0
- data/app/helpers/agile_category_helper.rb +128 -0
- data/app/helpers/agile_common_helper.rb +308 -0
- data/app/helpers/agile_edit_helper.rb +645 -0
- data/app/helpers/agile_helper.rb +509 -0
- data/app/helpers/agile_index_helper.rb +677 -0
- data/app/helpers/ar_image_helper.rb +128 -0
- data/app/models/agile_form_fields/action.rb +61 -0
- data/app/models/agile_form_fields/agile_form_field.rb +322 -0
- data/app/models/agile_form_fields/belongs_to.rb +112 -0
- data/app/models/agile_form_fields/check_box.rb +73 -0
- data/app/models/agile_form_fields/comment.rb +62 -0
- data/app/models/agile_form_fields/date_picker.rb +104 -0
- data/app/models/agile_form_fields/date_select.rb +68 -0
- data/app/models/agile_form_fields/datetime_picker.rb +88 -0
- data/app/models/agile_form_fields/datetime_select.rb +73 -0
- data/app/models/agile_form_fields/file_field.rb +52 -0
- data/app/models/agile_form_fields/file_select.rb +69 -0
- data/app/models/agile_form_fields/hidden_field.rb +51 -0
- data/app/models/agile_form_fields/html_field.rb +69 -0
- data/app/models/agile_form_fields/journal_diff.rb +62 -0
- data/app/models/agile_form_fields/link_to.rb +69 -0
- data/app/models/agile_form_fields/method.rb +66 -0
- data/app/models/agile_form_fields/multitext_autocomplete.rb +215 -0
- data/app/models/agile_form_fields/number_field.rb +92 -0
- data/app/models/agile_form_fields/password_field.rb +63 -0
- data/app/models/agile_form_fields/radio_button.rb +95 -0
- data/app/models/agile_form_fields/readonly.rb +77 -0
- data/app/models/agile_form_fields/select.rb +281 -0
- data/app/models/agile_form_fields/submit_tag.rb +58 -0
- data/app/models/agile_form_fields/text_area.rb +61 -0
- data/app/models/agile_form_fields/text_autocomplete.rb +171 -0
- data/app/models/agile_form_fields/text_field.rb +55 -0
- data/app/models/agile_form_fields/text_with_select.rb +94 -0
- data/app/models/agile_form_fields/tree_select.rb +170 -0
- data/app/models/ar_big_table.rb +82 -0
- data/app/models/ar_big_table_value.rb +53 -0
- data/app/models/ar_category.rb +109 -0
- data/app/models/ar_design.rb +116 -0
- data/app/models/ar_filter.rb +200 -0
- data/app/models/ar_folder_permission.rb +50 -0
- data/app/models/ar_folder_rule.rb +47 -0
- data/app/models/ar_gallery.rb +53 -0
- data/app/models/ar_image.rb +198 -0
- data/app/models/ar_internals.rb +60 -0
- data/app/models/ar_journal.rb +46 -0
- data/app/models/ar_json_ld.rb +131 -0
- data/app/models/ar_key_value_store.rb +128 -0
- data/app/models/ar_link.rb +48 -0
- data/app/models/ar_memory.rb +172 -0
- data/app/models/ar_menu.rb +144 -0
- data/app/models/ar_menu_item.rb +106 -0
- data/app/models/ar_page.rb +74 -0
- data/app/models/ar_part.rb +66 -0
- data/app/models/ar_permission.rb +180 -0
- data/app/models/ar_permission_rule.rb +65 -0
- data/app/models/ar_policy.rb +78 -0
- data/app/models/ar_policy_rule.rb +65 -0
- data/app/models/ar_poll.rb +74 -0
- data/app/models/ar_poll_item.rb +47 -0
- data/app/models/ar_poll_result.rb +38 -0
- data/app/models/ar_removed_url.rb +42 -0
- data/app/models/ar_role.rb +84 -0
- data/app/models/ar_setup.rb +115 -0
- data/app/models/ar_site.rb +68 -0
- data/app/models/ar_temp.rb +150 -0
- data/app/models/ar_user.rb +72 -0
- data/app/models/ar_user_group.rb +38 -0
- data/app/models/ar_user_role.rb +54 -0
- data/app/models/ar_visit.rb +41 -0
- data/app/models/concerns/ar_page_concern.rb +128 -0
- data/app/models/concerns/ar_part_concern.rb +48 -0
- data/app/models/concerns/ar_piece_concern.rb +48 -0
- data/app/models/concerns/ar_policy_rule_concern.rb +87 -0
- data/app/models/concerns/ar_seo_concern.rb +66 -0
- data/app/models/concerns/ar_site_concern.rb +103 -0
- data/app/models/concerns/ar_user_concern.rb +195 -0
- data/app/renderers/agile_common_renderer.rb +93 -0
- data/app/renderers/agile_renderer.rb +59 -0
- data/app/renderers/ar_ad_renderer.rb +219 -0
- data/app/renderers/ar_captcha_renderer.rb +113 -0
- data/app/renderers/ar_common_renderer.rb +90 -0
- data/app/renderers/ar_gallery_renderer.rb +107 -0
- data/app/renderers/ar_menu_renderer.rb +195 -0
- data/app/renderers/ar_page_renderer.rb +147 -0
- data/app/renderers/ar_part_renderer.rb +235 -0
- data/app/renderers/ar_piece_renderer.rb +119 -0
- data/app/renderers/ar_poll_renderer.rb +272 -0
- data/app/views/agile/_edit_stuff.html.erb +57 -0
- data/app/views/agile/_form.html.erb +24 -0
- data/app/views/agile/_result.html.erb +28 -0
- data/app/views/agile/edit.html.erb +13 -0
- data/app/views/agile/error.html.erb +2 -0
- data/app/views/agile/index.html.erb +14 -0
- data/app/views/agile/login.html.erb +19 -0
- data/app/views/agile/new.html.erb +12 -0
- data/app/views/agile_common/_help.html.erb +18 -0
- data/app/views/agile_common/_iframe_edit.html.erb +2 -0
- data/app/views/agile_common/paste_clipboard.html.erb +17 -0
- data/app/views/layouts/agile.html.erb +17 -0
- data/app/views/layouts/content.html.erb +20 -0
- data/app/views/models/dump_models.html.erb +47 -0
- data/config/initializers/kaminari_patch.rb +56 -0
- data/config/locales/agile_de.yml +138 -0
- data/config/locales/agile_en.yml +162 -0
- data/config/locales/agile_sl.yml +163 -0
- data/config/locales/datetimepicker.yml +19 -0
- data/config/locales/de.yml +231 -0
- data/config/locales/en.yml +13 -0
- data/config/locales/kaminari.yml +26 -0
- data/config/locales/models_en.yml +1032 -0
- data/config/locales/models_sl.yml +1065 -0
- data/config/locales/sl.yml +211 -0
- data/db/migrate/20240120160001_add_sessions_table.rb +12 -0
- data/db/migrate/20240120160002_ar_big_table.rb +17 -0
- data/db/migrate/20240120160003_ar_big_table_value.rb +18 -0
- data/db/migrate/20240120160004_ar_category.rb +22 -0
- data/db/migrate/20240120160005_ar_design.rb +20 -0
- data/db/migrate/20240120160006_ar_filter.rb +17 -0
- data/db/migrate/20240120160007_ar_gallery.rb +21 -0
- data/db/migrate/20240120160008_ar_journal.rb +19 -0
- data/db/migrate/20240120160009_ar_key_value_store.rb +11 -0
- data/db/migrate/20240120160010_ar_link.rb +19 -0
- data/db/migrate/20240120160011_ar_memory.rb +8 -0
- data/db/migrate/20240120160012_ar_menu.rb +21 -0
- data/db/migrate/20240120160013_ar_menu_item.rb +28 -0
- data/db/migrate/20240120160014_ar_page.rb +50 -0
- data/db/migrate/20240120160015_ar_part.rb +33 -0
- data/db/migrate/20240120160016_ar_permission.rb +16 -0
- data/db/migrate/20240120160017_ar_permission_rule.rb +17 -0
- data/db/migrate/20240120160018_ar_piece.rb +28 -0
- data/db/migrate/20240120160019_ar_policy.rb +21 -0
- data/db/migrate/20240120160020_ar_policy_rule.rb +18 -0
- data/db/migrate/20240120160021_ar_poll.rb +27 -0
- data/db/migrate/20240120160022_ar_poll_item.rb +23 -0
- data/db/migrate/20240120160023_ar_poll_result.rb +14 -0
- data/db/migrate/20240120160024_ar_removed_url.rb +16 -0
- data/db/migrate/20240120160025_ar_role.rb +17 -0
- data/db/migrate/20240120160026_ar_site.rb +37 -0
- data/db/migrate/20240120160027_ar_temp.rb +11 -0
- data/db/migrate/20240120160028_ar_user.rb +42 -0
- data/db/migrate/20240120160029_ar_user_group.rb +12 -0
- data/db/migrate/20240120160030_ar_user_role.rb +18 -0
- data/db/migrate/20240120160031_ar_visit.rb +15 -0
- data/db/migrate/20240703016001_ar_setup.rb +16 -0
- data/db/migrate/20240703016002_ar_folder_permission.rb +15 -0
- data/db/migrate/20240703016003_ar_folder_rule.rb +14 -0
- data/db/migrate/20250115000001_ar_image.rb +25 -0
- data/lib/agile/configuration.rb +43 -0
- data/lib/agile/engine.rb +29 -0
- data/lib/agile/version.rb +27 -0
- data/lib/agile.rb +282 -0
- data/lib/agile_rails.rb +1 -0
- data/lib/generators/agile/USAGE +11 -0
- data/lib/generators/agile/new_form_generator.rb +369 -0
- data/lib/generators/convert_to_ar/convert_to_ar_generator.rb +158 -0
- data/lib/tasks/agile_db_clone.rake +132 -0
- data/lib/tasks/agile_db_export_to_yaml.rake +37 -0
- data/lib/tasks/agile_db_migrate.rake +35 -0
- metadata +414 -0
@@ -0,0 +1,977 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2024+ Damjan Rems
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#+
|
23
|
+
|
24
|
+
########################################################################
|
25
|
+
# This is main controller for processing actions by AgileRails forms. It provides
|
26
|
+
# CRUD actions for editing database records. AgileRails does not require controller
|
27
|
+
# to be made for every table model but implements all actions in single
|
28
|
+
# controller. Logic required to control data entry is provided within AgileRails
|
29
|
+
# forms which are loaded dynamically for every action.
|
30
|
+
#
|
31
|
+
# Data entry validations must therefore reside in document models definitions or can be implemented in forms.
|
32
|
+
# There are always validations that cannot be done in models. Like validations
|
33
|
+
# which include url parameters or accessing session variables. This is hard to be done
|
34
|
+
# in model therefore AgileRails controls had to be invented. AgileRails controls are
|
35
|
+
# modules with methods that are injected into agile controller and act in runtime like
|
36
|
+
# they are part of Agile controller.
|
37
|
+
#
|
38
|
+
# Since Ruby and Rails provide some "automagic" loading of modules AgileRails controls must be saved
|
39
|
+
# into app/controls folder. Every model can have its own controls file.
|
40
|
+
# ar_page model's controls live in ar_page_controls.rb file. By convention module names
|
41
|
+
# are declared in camel case, so our ar_page_controls.rb declares ArPageControls module.
|
42
|
+
#
|
43
|
+
# Controls (among other) may contain 8 callback methods.
|
44
|
+
# These methods are:
|
45
|
+
# - before_new
|
46
|
+
# - new_record
|
47
|
+
# - dup_record
|
48
|
+
# - before_edit
|
49
|
+
# - before_save
|
50
|
+
# - after_save
|
51
|
+
# - before_delete
|
52
|
+
# - after_delete
|
53
|
+
#
|
54
|
+
# Methods before_new, before_edit, before_save or before_delete may also effect flow of the application. If
|
55
|
+
# method return false (not nil but FalseClass) normal flow of the program is interrupted and last operation
|
56
|
+
# is canceled.
|
57
|
+
#
|
58
|
+
# Second control methods that can be declared in AgileRails controls are filters for
|
59
|
+
# viewing and sorting documents. It is often required that dynamic filters are
|
60
|
+
# applied to data_set documents.
|
61
|
+
#
|
62
|
+
# data_set:
|
63
|
+
# filter: current_users_documents
|
64
|
+
#
|
65
|
+
# Example implemented controls method:
|
66
|
+
#
|
67
|
+
# def current_users_documents
|
68
|
+
# if agile_user_can(ArPermission::CAN_READ)
|
69
|
+
# ArPage.where(created_by: session[:user_id])
|
70
|
+
# else
|
71
|
+
# flash[:error] = 'User can not perform this operation!'
|
72
|
+
# false
|
73
|
+
# end
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# If filter method returns false user will be presented with flash error.
|
77
|
+
########################################################################
|
78
|
+
class AgileController < AgileApplicationController
|
79
|
+
before_action :authorization_check, :except => [:login, :logout, :test, :run]
|
80
|
+
protect_from_forgery with: :null_session, only: Proc.new { _1.request.format.json? }
|
81
|
+
|
82
|
+
layout 'agile'
|
83
|
+
|
84
|
+
########################################################################
|
85
|
+
# Index action
|
86
|
+
########################################################################
|
87
|
+
def index
|
88
|
+
@form['index']['data_set'] ||= {}
|
89
|
+
redirected = (@form['table'] == 'ar_memory' ? process_in_memory : process_data_set)
|
90
|
+
return if redirected
|
91
|
+
|
92
|
+
callback_method_call(@form.dig('index', 'data_set', 'footer') || 'update_footer')
|
93
|
+
respond_to do |format|
|
94
|
+
format.html { render action: :index }
|
95
|
+
format.js { render partial: :result }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
########################################################################
|
100
|
+
# Filter action.
|
101
|
+
########################################################################
|
102
|
+
def _filter
|
103
|
+
index
|
104
|
+
end
|
105
|
+
|
106
|
+
########################################################################
|
107
|
+
# Show displays record in readonly mode.
|
108
|
+
########################################################################
|
109
|
+
def show
|
110
|
+
find_record
|
111
|
+
# before_show callback
|
112
|
+
if (m = callback_method('before_show') )
|
113
|
+
ret = callback_method_call(m)
|
114
|
+
if ret.class == FalseClass
|
115
|
+
@form['readonly'] = nil # must be
|
116
|
+
return index
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
render action: 'edit'
|
121
|
+
end
|
122
|
+
|
123
|
+
########################################################################
|
124
|
+
# Login action. Used to login direct to CMS. It is mostly used when first time
|
125
|
+
# creating site and when something goes so wrong, that common login procedure
|
126
|
+
# is not available.
|
127
|
+
#
|
128
|
+
# Login can be called directly with url http://site.com/agile/login
|
129
|
+
########################################################################
|
130
|
+
def login
|
131
|
+
return set_development_site if params[:id] == 'test'
|
132
|
+
|
133
|
+
session[:edit_mode] = 0 unless params[:ok]
|
134
|
+
render action: 'login'
|
135
|
+
end
|
136
|
+
|
137
|
+
########################################################################
|
138
|
+
# Logout action. Used to logout direct from CMS.
|
139
|
+
#
|
140
|
+
# Logout can be called directly with url http://site.com/agile/logout
|
141
|
+
########################################################################
|
142
|
+
def logout
|
143
|
+
session[:edit_mode] = 0
|
144
|
+
session[:user_id] = nil
|
145
|
+
session[:user_roles] = nil
|
146
|
+
render action: 'login'
|
147
|
+
end
|
148
|
+
|
149
|
+
########################################################################
|
150
|
+
# Shortcut for setting currently selected site in development. Will search
|
151
|
+
# for ar_site document with site name 'development' and set alias_for to site
|
152
|
+
# url parameter.
|
153
|
+
########################################################################
|
154
|
+
def set_development_site
|
155
|
+
# only in development
|
156
|
+
return agile_render_404 unless Rails.env.development?
|
157
|
+
|
158
|
+
alias_site = ArSite.find_by(name: params[:site])
|
159
|
+
return agile_render_404 unless alias_site
|
160
|
+
|
161
|
+
# update alias for
|
162
|
+
site = ArSite.find_by(name: 'development')
|
163
|
+
site.alias_for = params[:site]
|
164
|
+
site.save
|
165
|
+
redirect_to '/'
|
166
|
+
end
|
167
|
+
|
168
|
+
########################################################################
|
169
|
+
# New action.
|
170
|
+
########################################################################
|
171
|
+
def new
|
172
|
+
flash[:error] = flash[:warning] = flash[:info] = nil
|
173
|
+
# not authorized
|
174
|
+
unless agile_user_can(ArPermission::CAN_CREATE)
|
175
|
+
flash[:error] = t('agile.not_authorized')
|
176
|
+
return index
|
177
|
+
end
|
178
|
+
create_new_empty_record()
|
179
|
+
|
180
|
+
if (m = callback_method('before_new') )
|
181
|
+
ret = callback_method_call(m)
|
182
|
+
return index if ret.class == FalseClass
|
183
|
+
end
|
184
|
+
load_initial_values()
|
185
|
+
|
186
|
+
# new_record callback. Set default values for new record
|
187
|
+
if (m = callback_method('new_record') ) then callback_method_call(m) end
|
188
|
+
@form_params['action'] = 'create'
|
189
|
+
end
|
190
|
+
|
191
|
+
########################################################################
|
192
|
+
# Will duplicate source document into new record. This method is used for
|
193
|
+
# duplicating record and is subroutine of create action.
|
194
|
+
########################################################################
|
195
|
+
def duplicate_record(source)
|
196
|
+
duplicates = params['dup_fields'].split(',').map(&:strip)
|
197
|
+
dest = {}
|
198
|
+
source.attribute_names.each do |attribute_name|
|
199
|
+
next if attribute_name == 'id' # don't duplicate _id
|
200
|
+
|
201
|
+
dest[attribute_name] = source[attribute_name]
|
202
|
+
# if duplicate, string dup is added. For unique fields
|
203
|
+
dest[attribute_name] += ' dup' if duplicates.include?(attribute_name)
|
204
|
+
end
|
205
|
+
dest['created_at'] = Time.now if dest['created_at']
|
206
|
+
dest['updated_at'] = Time.now if dest['updated_at']
|
207
|
+
dest
|
208
|
+
end
|
209
|
+
|
210
|
+
########################################################################
|
211
|
+
# Create (or duplicate) action.
|
212
|
+
########################################################################
|
213
|
+
def create
|
214
|
+
# not authorized
|
215
|
+
unless agile_user_can(ArPermission::CAN_CREATE)
|
216
|
+
flash[:error] = t('agile.not_authorized')
|
217
|
+
return index
|
218
|
+
end
|
219
|
+
|
220
|
+
# create document
|
221
|
+
if params['id'].nil?
|
222
|
+
# Prevent double form submit
|
223
|
+
return index if double_form_submit?
|
224
|
+
|
225
|
+
create_new_empty_record
|
226
|
+
if save_data
|
227
|
+
flash[:info] = t('agile.record_saved')
|
228
|
+
params[:return_to] = 'index' if params[:commit] == t('agile.save&back') # save & back
|
229
|
+
return process_return_to(params[:return_to]) if params[:return_to]
|
230
|
+
|
231
|
+
@form_params['id'] = @record.id # must be set, for proper update link
|
232
|
+
params[:id] = @record.id # must be set, for find_record
|
233
|
+
edit
|
234
|
+
else # error
|
235
|
+
return process_return_to(params[:return_to]) if params[:return_to]
|
236
|
+
|
237
|
+
render action: :new
|
238
|
+
end
|
239
|
+
else # duplicate record
|
240
|
+
find_record
|
241
|
+
new_record = duplicate_record(@record)
|
242
|
+
create_new_empty_record(new_record)
|
243
|
+
if (m = callback_method('dup_record')) then callback_method_call(m) end
|
244
|
+
update_standards
|
245
|
+
@record.save!
|
246
|
+
index
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
########################################################################
|
251
|
+
# Edit action.
|
252
|
+
########################################################################
|
253
|
+
def edit
|
254
|
+
find_record
|
255
|
+
if (m = callback_method('before_edit') )
|
256
|
+
ret = callback_method_call(m)
|
257
|
+
# don't do anything if return is false
|
258
|
+
return index if ret.class == FalseClass
|
259
|
+
end
|
260
|
+
@form_params['action'] = 'update'
|
261
|
+
render action: :edit
|
262
|
+
end
|
263
|
+
|
264
|
+
########################################################################
|
265
|
+
# Update action.
|
266
|
+
########################################################################
|
267
|
+
def update
|
268
|
+
find_record
|
269
|
+
# check if record was not updated in mean time
|
270
|
+
if @record.respond_to?(:updated_at)
|
271
|
+
if params[:last_updated_at].to_i != @record.updated_at.to_i
|
272
|
+
flash[:error] = t('agile.updated_by_other')
|
273
|
+
return render(action: :edit)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
if agile_user_can(ArPermission::CAN_EDIT_ALL) ||
|
278
|
+
(@record.respond_to?('created_by') && @record.created_by == session[:user_id] && agile_user_can(ArPermission::CAN_EDIT))
|
279
|
+
|
280
|
+
if save_data
|
281
|
+
params[:return_to] = 'index' if params[:commit] == t('agile.save&back') # save & back
|
282
|
+
@form_params['action'] = 'update'
|
283
|
+
# Process return_to
|
284
|
+
return process_return_to(params[:return_to]) if params[:return_to]
|
285
|
+
else
|
286
|
+
# do not forget before_edit callback
|
287
|
+
if m = callback_method('before_edit') then callback_method_call(m) end
|
288
|
+
return render action: :edit
|
289
|
+
end
|
290
|
+
else
|
291
|
+
flash[:error] = t('agile.not_authorized')
|
292
|
+
end
|
293
|
+
edit
|
294
|
+
end
|
295
|
+
|
296
|
+
########################################################################
|
297
|
+
# Destroy action. Used also for enabling and disabling record.
|
298
|
+
########################################################################
|
299
|
+
def destroy
|
300
|
+
find_record
|
301
|
+
# check permission required to delete
|
302
|
+
permission = if params['operation'].nil?
|
303
|
+
if @record.respond_to?('created_by') # needs can_delete_all if created_by is present and not owner
|
304
|
+
(@record.created_by == session[:user_id]) ? ArPermission::CAN_DELETE : ArPermission::CAN_DELETE_ALL
|
305
|
+
else
|
306
|
+
ArPermission::CAN_DELETE # by default
|
307
|
+
end
|
308
|
+
else # enable or disable record
|
309
|
+
if @record.respond_to?('created_by')
|
310
|
+
(@record.created_by == session[:user_id]) ? ArPermission::CAN_EDIT : ArPermission::CAN_EDIT_ALL
|
311
|
+
else
|
312
|
+
ArPermission::CAN_EDIT # by default
|
313
|
+
end
|
314
|
+
end
|
315
|
+
ok2delete = agile_user_can(permission)
|
316
|
+
|
317
|
+
case
|
318
|
+
# not authorized
|
319
|
+
when !ok2delete then
|
320
|
+
flash[:error] = t('agile.not_authorized')
|
321
|
+
return index
|
322
|
+
|
323
|
+
# delete document
|
324
|
+
when params['operation'].nil? then
|
325
|
+
# before_delete callback
|
326
|
+
if (m = callback_method('before_delete') )
|
327
|
+
ret = callback_method_call(m)
|
328
|
+
# don't do anything if return is false
|
329
|
+
return index if ret.class == FalseClass
|
330
|
+
end
|
331
|
+
|
332
|
+
# take care of transaction
|
333
|
+
begin
|
334
|
+
transaction_begin()
|
335
|
+
if @record.destroy
|
336
|
+
save_journal(:delete)
|
337
|
+
flash[:info] = t('agile.record_deleted')
|
338
|
+
# after_delete callback
|
339
|
+
if (m = callback_method('after_delete') )
|
340
|
+
callback_method_call(m)
|
341
|
+
elsif params['after-delete'].to_s.match('return_to')
|
342
|
+
params[:return_to] = params['after-delete']
|
343
|
+
end
|
344
|
+
# Process return_to link
|
345
|
+
if params[:return_to]
|
346
|
+
transaction_end()
|
347
|
+
return process_return_to(params[:return_to])
|
348
|
+
end
|
349
|
+
else
|
350
|
+
flash[:error] = agile_error_messages_for(@record)
|
351
|
+
transaction_abort('')
|
352
|
+
end
|
353
|
+
rescue Exception => e
|
354
|
+
transaction_abort()
|
355
|
+
transaction_end()
|
356
|
+
logger.error(%(#{e.message}\n\n#{e.backtrace.join("\n")}))
|
357
|
+
return if Rails.env.test? # or test will fail
|
358
|
+
|
359
|
+
raise
|
360
|
+
end
|
361
|
+
# end transaction normaly
|
362
|
+
transaction_end()
|
363
|
+
return index
|
364
|
+
|
365
|
+
# deactivate document
|
366
|
+
when params['operation'] == 'disable' then
|
367
|
+
if @record.respond_to?('active')
|
368
|
+
@record.active = false
|
369
|
+
save_journal(:update, @record.changes)
|
370
|
+
update_standards()
|
371
|
+
@record.save
|
372
|
+
flash[:info] = t('agile.record_disabled')
|
373
|
+
end
|
374
|
+
|
375
|
+
# reactivate document
|
376
|
+
when params['operation'] == 'enable' then
|
377
|
+
if @record.respond_to?('active')
|
378
|
+
@record.active = true
|
379
|
+
update_standards()
|
380
|
+
save_journal(:update, @record.changes)
|
381
|
+
@record.save
|
382
|
+
flash[:info] = t('agile.record_enabled')
|
383
|
+
end
|
384
|
+
|
385
|
+
#TODO reorder documents
|
386
|
+
when params['operation'] == 'reorder' then
|
387
|
+
|
388
|
+
end
|
389
|
+
|
390
|
+
@form_params['action'] = 'update'
|
391
|
+
render action: :edit
|
392
|
+
end
|
393
|
+
|
394
|
+
########################################################################
|
395
|
+
# Run action
|
396
|
+
########################################################################
|
397
|
+
def run
|
398
|
+
# determine control file name and method
|
399
|
+
control_name, method_name = params[:control].split('.')
|
400
|
+
if method_name.nil?
|
401
|
+
method_name = control_name
|
402
|
+
control_name = AgileHelper.table_param(params)
|
403
|
+
end
|
404
|
+
# extend with control methods
|
405
|
+
extend_with_control_module(control_name)
|
406
|
+
if respond_to?(method_name)
|
407
|
+
# can it be called
|
408
|
+
return return_run_error t('agile.not_authorized') unless can_process_run
|
409
|
+
# call method
|
410
|
+
respond_to do |format|
|
411
|
+
format.json { send method_name }
|
412
|
+
format.html { send method_name }
|
413
|
+
end
|
414
|
+
else # Error message
|
415
|
+
return_run_error "Method #{method_name} not defined in #{control_name}_control"
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
protected
|
420
|
+
|
421
|
+
########################################################################
|
422
|
+
# Respond with error on run action
|
423
|
+
########################################################################
|
424
|
+
def return_run_error(text)
|
425
|
+
respond_to do |format|
|
426
|
+
format.json { render json: { msg_error: text } }
|
427
|
+
format.html { render plain: text }
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
########################################################################
|
432
|
+
# Can run call be processed
|
433
|
+
########################################################################
|
434
|
+
def can_process_run
|
435
|
+
if respond_to?( :can_process?)
|
436
|
+
response = send( :can_process?)
|
437
|
+
return response unless response.class == Array
|
438
|
+
else
|
439
|
+
response = [ArPermission::CAN_VIEW, AgileHelper.table_param(params) || 'ar_memory']
|
440
|
+
end
|
441
|
+
agile_user_can *response
|
442
|
+
end
|
443
|
+
|
444
|
+
########################################################################
|
445
|
+
# Checks if user has permissions to perform operation on table and if not
|
446
|
+
# prepares response for not authorized message.
|
447
|
+
#
|
448
|
+
# @param [Integer] permission : Permission level defined in ArPermission constants eg. ArPermission::CAN_EDIT
|
449
|
+
# @param [String] collection_name : Table name on which user must have permission
|
450
|
+
#
|
451
|
+
# @return [Boolean] true when user has required permission otherwise false
|
452
|
+
########################################################################
|
453
|
+
def user_has_permission?(permission, collection_name)
|
454
|
+
unless agile_user_can(permission, collection_name.to_s)
|
455
|
+
respond_to do |format|
|
456
|
+
format.json { render json: { msg_error: t('agile.not_authorized') } }
|
457
|
+
format.html { render plain: t('agile.not_authorized') }
|
458
|
+
end
|
459
|
+
return true
|
460
|
+
end
|
461
|
+
true
|
462
|
+
end
|
463
|
+
|
464
|
+
############################################################################
|
465
|
+
# Load module if available. Try not to mask errors in control module
|
466
|
+
############################################################################
|
467
|
+
def controls_module_load(controls_string)
|
468
|
+
begin
|
469
|
+
controls_string.classify.constantize
|
470
|
+
rescue NameError => e
|
471
|
+
return if e.message.match('uninitialized constant') || e.message.match('wrong constant name')
|
472
|
+
# report errors when loading existing module
|
473
|
+
raise e
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
############################################################################
|
478
|
+
# Dynamically extend ra controller class with methods defined in controls module.
|
479
|
+
############################################################################
|
480
|
+
def extend_with_control_module(control_name = @form['controls'] || @form['control'])
|
481
|
+
# May include embedded forms so ; => _
|
482
|
+
control_name ||= AgileHelper.table_param(params).gsub(';', '_')
|
483
|
+
control_name += '_control' unless control_name.match(/control$|report$/i)
|
484
|
+
|
485
|
+
controls = controls_module_load(control_name)
|
486
|
+
if controls
|
487
|
+
# extend first with agile_report when report
|
488
|
+
if control_name.match(/report$/i)
|
489
|
+
extend AgileReport
|
490
|
+
report_init(control_name)
|
491
|
+
end
|
492
|
+
extend controls
|
493
|
+
# Form may be dynamically updated before processed
|
494
|
+
send(:update_form) if respond_to?(:update_form)
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
############################################################################
|
499
|
+
# Check if user is authorized for the action. If authorization is in order it will also
|
500
|
+
# load AgileRails form.
|
501
|
+
############################################################################
|
502
|
+
def authorization_check
|
503
|
+
params[:table] ||= params[:t] || AgileHelper.form_param(params)
|
504
|
+
# Only show menu
|
505
|
+
return login if params[:id].in?(%w[login logout test])
|
506
|
+
|
507
|
+
table = params[:table].to_s.strip.downcase
|
508
|
+
set_default_guest_user_role if session[:user_roles].nil?
|
509
|
+
# request shouldn't pass
|
510
|
+
if table != 'ar_memory' &&
|
511
|
+
(table.size < 3 || !agile_user_can(ArPermission::CAN_VIEW))
|
512
|
+
return render(action: 'error', locals: { error: t('agile.not_authorized')} )
|
513
|
+
end
|
514
|
+
agile_form_read
|
515
|
+
|
516
|
+
# Permissions can be also defined on form
|
517
|
+
#TODO So far only can_view is used. Think about if using other permissions has sense
|
518
|
+
can_view = @form.dig('permissions', 'can_view')
|
519
|
+
if can_view.nil? || can_view.split(',').find{ agile_user_has_role?(_1) }
|
520
|
+
extend_with_control_module
|
521
|
+
else
|
522
|
+
render(action: 'error', locals: { error: t('agile.not_authorized')} )
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
########################################################################
|
527
|
+
# Find current record for edit, update or delete.
|
528
|
+
########################################################################
|
529
|
+
def find_record #:nodoc:
|
530
|
+
@record = @tables.last[0].find(params[:id])
|
531
|
+
end
|
532
|
+
|
533
|
+
########################################################################
|
534
|
+
# Creates new empty record for new and create action.
|
535
|
+
########################################################################
|
536
|
+
def create_new_empty_record(initial_data = nil) #:nodoc:
|
537
|
+
if @tables.size == 1
|
538
|
+
@record = @tables.first[0].new(initial_data)
|
539
|
+
else
|
540
|
+
rec = @tables.first[0].find(@ids.first) # top most record
|
541
|
+
1.upto(@tables.size - 2) { |i| rec = rec.send(@tables[i][1].pluralize).find(@ids[i]) } # find embedded children by ids
|
542
|
+
@record = @tables.last[0].new(initial_data) # new record
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
########################################################################
|
547
|
+
# Update standard fields like updated_by, created_by, site_id
|
548
|
+
########################################################################
|
549
|
+
def update_standards(record = @record)
|
550
|
+
record.updated_by = session[:user_id] if record.respond_to?('updated_by')
|
551
|
+
if record.new_record?
|
552
|
+
record.created_by = session[:user_id] if record.respond_to?('created_by')
|
553
|
+
# set this only initially. Allow to be set to nil on updates. Record can then belong to all sites
|
554
|
+
# and will be directly visible only to admins
|
555
|
+
record.ar_site_id = agile_get_site.id if record.respond_to?('ar_site_id') && record.ar_site_id.nil?
|
556
|
+
end
|
557
|
+
record.send(:set_history, self) if record.respond_to?(:set_history)
|
558
|
+
end
|
559
|
+
|
560
|
+
########################################################################
|
561
|
+
# Save record's changes to journal table. Saves all parameters to retrieve record if needed.
|
562
|
+
#
|
563
|
+
# [Parameters:]
|
564
|
+
# [operation] 'delete' or 'update'.
|
565
|
+
# [changes] Current record's changed fields.
|
566
|
+
########################################################################
|
567
|
+
def save_journal(operation, changes = {})
|
568
|
+
if operation == :delete
|
569
|
+
@record.attributes.each { |k, v| changes[k] = v }
|
570
|
+
end
|
571
|
+
changes.except!('created_at', 'updated_at', 'created_by', 'updated_by')
|
572
|
+
|
573
|
+
if (operation != :update) || changes.size > 0
|
574
|
+
# determine site_id
|
575
|
+
site_id = @record.site_id if @record.respond_to?('site_id')
|
576
|
+
site_id = site_id || agile_get_site&.id
|
577
|
+
ArJournal.create(site_id: site_id,
|
578
|
+
operation: operation,
|
579
|
+
user_id: session[:user_id],
|
580
|
+
tables: @form['table'],
|
581
|
+
ids: params[:ids],
|
582
|
+
record_id: params[:id],
|
583
|
+
ip: request.remote_ip,
|
584
|
+
time: Time.now,
|
585
|
+
diff: changes.to_json)
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
########################################################################
|
590
|
+
# Determines if callback method is defined in parameters or in control module.
|
591
|
+
# Returns callback method name or nil if not defined.
|
592
|
+
########################################################################
|
593
|
+
def callback_method(key) #:nodoc:
|
594
|
+
data_key = key.gsub('_', '-') # convert _ to -
|
595
|
+
callback = case
|
596
|
+
when params['data'] && params['data'][data_key] then params['data'][data_key]
|
597
|
+
# method is present then call it automatically
|
598
|
+
when @form.dig('form', key) then @form['form'][key]
|
599
|
+
when respond_to?(key) then key
|
600
|
+
when params[data_key] then params[data_key]
|
601
|
+
else nil
|
602
|
+
end
|
603
|
+
|
604
|
+
ret = case
|
605
|
+
when callback.nil? then callback # otherwise there will be errors in next lines
|
606
|
+
when callback.match('eval ') then callback.sub('eval ', '')
|
607
|
+
when callback.match('return_to ')
|
608
|
+
params[:return_to] = callback.sub('return_to ', '')
|
609
|
+
return nil
|
610
|
+
else callback
|
611
|
+
end
|
612
|
+
ret
|
613
|
+
end
|
614
|
+
|
615
|
+
########################################################################
|
616
|
+
# Calls callback method.
|
617
|
+
########################################################################
|
618
|
+
def callback_method_call(m) #:nodoc:
|
619
|
+
send(m) if respond_to?(m)
|
620
|
+
end
|
621
|
+
|
622
|
+
########################################################################
|
623
|
+
# Same as javascript_tag helper. Ajax form actions may results in javascript code to be returned.
|
624
|
+
# This will add javascript tag to code.
|
625
|
+
########################################################################
|
626
|
+
def js_tag(script) #:nodoc:
|
627
|
+
"<script>#{script}</script>"
|
628
|
+
end
|
629
|
+
|
630
|
+
########################################################################
|
631
|
+
# Process return_to parameter when defined on form or set by controls methods.
|
632
|
+
# params['return_to'] may contain 'index', 'reload' or 'parent.reload' or any valid url to
|
633
|
+
# return to, after successful controls method call.
|
634
|
+
########################################################################
|
635
|
+
def process_return_to(return_to)
|
636
|
+
script = case
|
637
|
+
when return_to == 'index' then return index
|
638
|
+
when return_to.match(/eval=/i) then return_to.sub('eval=', '')
|
639
|
+
when return_to.match(/parent\.reload/i) then 'parent.location.href=parent.location.href;'
|
640
|
+
when return_to.match(/reload/i) then 'location.href=location.href;'
|
641
|
+
when return_to.match(/window\.close/i) then 'window.close();'
|
642
|
+
when return_to.match(/none/i) then return
|
643
|
+
else "location.href='#{return_to}'"
|
644
|
+
end
|
645
|
+
render html: js_tag(script).html_safe, layout: false
|
646
|
+
end
|
647
|
+
|
648
|
+
########################################################################
|
649
|
+
# Since tabs have been introduced on form it is a little more complicated
|
650
|
+
# to collect all edit fields on form. This method does it. Subroutine of save_data.
|
651
|
+
########################################################################
|
652
|
+
def fields_on_form #:nodoc:
|
653
|
+
form_fields = []
|
654
|
+
if @form['form']['fields']
|
655
|
+
# read only field elements (key is Integer)
|
656
|
+
@form['form']['fields'].each { |key, options| form_fields << options if key.class == Integer }
|
657
|
+
else
|
658
|
+
@form['form']['tabs'].keys.each do |tab|
|
659
|
+
@form['form']['tabs'][tab].each { |key, options| form_fields << options if key.class == Integer }
|
660
|
+
end
|
661
|
+
end
|
662
|
+
form_fields
|
663
|
+
end
|
664
|
+
|
665
|
+
########################################################################
|
666
|
+
# Save edited data. Take care that only fields defined on form are affected.
|
667
|
+
# It also saves journal data and calls before_save and after_save callbacks.
|
668
|
+
########################################################################
|
669
|
+
def save_data
|
670
|
+
form_fields = fields_on_form()
|
671
|
+
return true if form_fields.size == 0
|
672
|
+
|
673
|
+
form_fields.each do |v|
|
674
|
+
session[:form_processing] = v['name'] # for debuging
|
675
|
+
next if v['type'].nil? or v['name'].nil? or
|
676
|
+
v['type'].match('belongs_to') or # don't wipe embedded types
|
677
|
+
(params[:edit_only] and params[:edit_only] != v['name']) or # otherwise other fields would be wiped
|
678
|
+
v['readonly'] or # fields with readonly option don't return value and would be wiped
|
679
|
+
!@record.respond_to?(v['name']) # there are temporary fields on the form
|
680
|
+
# return value from form field definition
|
681
|
+
value = AgileFormFields.const_get(v['type'].camelize).get_data(params, v['name'])
|
682
|
+
@record.send("#{v['name']}=", value)
|
683
|
+
end
|
684
|
+
|
685
|
+
# before_save callback
|
686
|
+
if (m = callback_method('before_save') )
|
687
|
+
ret = callback_method_call(m)
|
688
|
+
# don't save if callback returns false
|
689
|
+
return false if ret.class == FalseClass
|
690
|
+
end
|
691
|
+
|
692
|
+
# save data. Take care for active transaction and end it properly
|
693
|
+
begin
|
694
|
+
changes = @record.changes
|
695
|
+
transaction_begin()
|
696
|
+
update_standards() if changes.size > 0 # update only if there has been some changes
|
697
|
+
operation = @record.new_record? ? :new : :update
|
698
|
+
if (saved = @record.save)
|
699
|
+
save_journal(operation, @record.previous_changes)
|
700
|
+
# after_save callback
|
701
|
+
if (m = callback_method('after_save') ) then callback_method_call(m) end
|
702
|
+
else
|
703
|
+
transaction_abort()
|
704
|
+
end
|
705
|
+
rescue Exception => e
|
706
|
+
transaction_abort()
|
707
|
+
transaction_end()
|
708
|
+
logger.error(%(\n#{e.message}\n\n#{e.backtrace.join("\n")}))
|
709
|
+
return if Rails.env.test?
|
710
|
+
|
711
|
+
raise
|
712
|
+
end
|
713
|
+
transaction_end()
|
714
|
+
saved
|
715
|
+
end
|
716
|
+
|
717
|
+
########################################################################
|
718
|
+
# Begins database transaction if requested
|
719
|
+
########################################################################
|
720
|
+
def transaction_begin
|
721
|
+
return unless %w[1 yes true].include?(@form['transaction'].to_s.downcase)
|
722
|
+
|
723
|
+
manager = ActiveRecord::Base.connection.transaction_manager
|
724
|
+
manager.begin_transaction
|
725
|
+
@transaction = 'OK'
|
726
|
+
end
|
727
|
+
|
728
|
+
########################################################################
|
729
|
+
# Aborts database transaction. It will rollback in transaction_end
|
730
|
+
########################################################################
|
731
|
+
def transaction_abort(message = 'Transaction aborted')
|
732
|
+
return if @transaction.nil?
|
733
|
+
|
734
|
+
@transaction = @transaction == 'OK' ? message : "#{@transaction}; #{message}"
|
735
|
+
end
|
736
|
+
|
737
|
+
########################################################################
|
738
|
+
# Commits or rollbacks the transaction. Operation depend on the value of
|
739
|
+
# @transaction variable. If value is OK, then transaction is commited, otherwise
|
740
|
+
# transaction is aborted with the message added
|
741
|
+
########################################################################
|
742
|
+
def transaction_end
|
743
|
+
return if @transaction.nil?
|
744
|
+
|
745
|
+
manager = ActiveRecord::Base.connection.transaction_manager
|
746
|
+
if @transaction == 'OK'
|
747
|
+
manager.commit_transaction
|
748
|
+
else
|
749
|
+
manager.rollback_transaction
|
750
|
+
flash[:error] = (flash[:error].present? ? flash[:error].to_s + "<br>" : '') + @transaction
|
751
|
+
flash[:info] = nil # must be
|
752
|
+
end
|
753
|
+
end
|
754
|
+
########################################################################
|
755
|
+
# Will return comma separated data (field names) as array of symbols. For usage
|
756
|
+
# in select_fields and deny_fields
|
757
|
+
########################################################################
|
758
|
+
def separated_to_symbols(data)
|
759
|
+
data.chomp.split(',').map { _1.strip.downcase.to_sym }
|
760
|
+
end
|
761
|
+
|
762
|
+
########################################################################
|
763
|
+
# Will process data_set['select'] option
|
764
|
+
# @TODO drops No route matches error, for no apparent reason
|
765
|
+
########################################################################
|
766
|
+
def process_select_options(model) #:nodoc:
|
767
|
+
data_set = @form['index']['data_set']
|
768
|
+
return model unless data_set['select']
|
769
|
+
|
770
|
+
model.select(*data_set['select'].chomp.split(',').map(&:to_sym))
|
771
|
+
end
|
772
|
+
|
773
|
+
########################################################################
|
774
|
+
# Will check and set sorting options for current dataset. Subroutine of index method.
|
775
|
+
########################################################################
|
776
|
+
def check_sort_options #:nodoc:
|
777
|
+
table_name = @tables.first[1]
|
778
|
+
sort_data = session.dig(:filters, table_name, :sort)
|
779
|
+
return unless sort_data && @records.kind_of?(ActiveRecord::Relation)
|
780
|
+
|
781
|
+
sort, direction = sort_data.split(' ')
|
782
|
+
@records = @records.order(sort => direction.to_sym)
|
783
|
+
end
|
784
|
+
|
785
|
+
########################################################################
|
786
|
+
# Return currently defined filter on a table.
|
787
|
+
#
|
788
|
+
# Use in custom form filter: agile_filter_options(ArPage).and(ar_site_id: params[:site_id])
|
789
|
+
########################################################################
|
790
|
+
def agile_filter_options(model)
|
791
|
+
table_name = @tables.first[1]
|
792
|
+
filter_data = session.dig(:filters, table_name)
|
793
|
+
ArFilter.get_filter(filter_data) || model.all
|
794
|
+
end
|
795
|
+
|
796
|
+
########################################################################
|
797
|
+
# Return current sort options for model (table)
|
798
|
+
########################################################################
|
799
|
+
def agile_sort_options(model) #:nodoc:
|
800
|
+
table_name = model.to_s.underscore
|
801
|
+
sort_data = session.dig(:filters, table_name, :sort)
|
802
|
+
return if sort_data.nil?
|
803
|
+
|
804
|
+
field, direction = sort_data.split(' ')
|
805
|
+
{ field.to_sym => direction.to_sym }
|
806
|
+
end
|
807
|
+
|
808
|
+
########################################################################
|
809
|
+
# Will check and set current filter options for data_set. Subroutine of index method.
|
810
|
+
########################################################################
|
811
|
+
def check_filter_options #:nodoc:
|
812
|
+
table_name = AgileHelper.table_param(params).strip.split(';').first.underscore
|
813
|
+
model = table_name.classify.constantize
|
814
|
+
save_filter_value(params[:page], table_name, :page) if params[:page]
|
815
|
+
# if data model has field ar_site_id ensure that only records which belong to the site are selected.
|
816
|
+
site_id = agile_get_site.id if agile_get_site
|
817
|
+
|
818
|
+
# don't filter site if no ar_site_id field or user is ADMIN
|
819
|
+
site_id = nil if !model.method_defined?('ar_site_id') || agile_user_can(ArPermission::CAN_ADMIN)
|
820
|
+
site_id = nil if session.dig(:filters, table_name, 'filter').to_s.match('ar_site_id')
|
821
|
+
|
822
|
+
if @records = ArFilter.get_filter(session.dig(:filters, table_name))
|
823
|
+
@records = @records.where(ar_site_id: site_id) if site_id
|
824
|
+
else
|
825
|
+
@records = site_id ? model.where(ar_site_id: site_id) : model.all
|
826
|
+
end
|
827
|
+
# pagination if required
|
828
|
+
per_page = (@form['index']['data_set']['per_page'] || 25).to_i
|
829
|
+
current_page = session.dig(:filters, table_name, :page) || 1
|
830
|
+
@records = @records.page(current_page).per(per_page) if per_page > 0
|
831
|
+
end
|
832
|
+
|
833
|
+
######################################################################
|
834
|
+
# Save specified filter value to session
|
835
|
+
######################################################################
|
836
|
+
def save_filter_value(value, table_name, *parms)
|
837
|
+
session[:filters] ||= {}
|
838
|
+
session[:filters][table_name] ||= {}
|
839
|
+
case parms.size
|
840
|
+
when 0 then session[:filters][table_name] = value
|
841
|
+
when 1 then session[:filters][table_name][parms[0]] = value
|
842
|
+
when 2 then
|
843
|
+
session[:filters][table_name][parms[0]] ||= {}
|
844
|
+
session[:filters][table_name][parms[0]][parms[1]] = value
|
845
|
+
end
|
846
|
+
session[:filters][table_name][:updated] = Time.now
|
847
|
+
end
|
848
|
+
|
849
|
+
######################################################################
|
850
|
+
# Save specified filter value to session
|
851
|
+
######################################################################
|
852
|
+
def read_filter_value(table_name, *parms)
|
853
|
+
case parms.size
|
854
|
+
when 0 then session.dig(:filters, table_name)
|
855
|
+
when 1 then session.dig(:filters, table_name, parms[0])
|
856
|
+
when 2 then session.dig(:filters, table_name, parms[0], parms[1])
|
857
|
+
end
|
858
|
+
end
|
859
|
+
|
860
|
+
########################################################################
|
861
|
+
# Select data from table for index action
|
862
|
+
########################################################################
|
863
|
+
def process_data_set #:nodoc
|
864
|
+
# If data_set is not defined on form, then it will fail. :return_to should know where to go
|
865
|
+
data_set = @form['index']['data_set']
|
866
|
+
if data_set.nil?
|
867
|
+
process_return_to(params[:return_to] || 'reload')
|
868
|
+
return true
|
869
|
+
end
|
870
|
+
# when data_set is evaluated as Rails helper
|
871
|
+
data_set['type'] ||= 'default'
|
872
|
+
return unless data_set['type'] == 'default'
|
873
|
+
|
874
|
+
# for now enable only filtering of top level records
|
875
|
+
if @tables.size == 1
|
876
|
+
check_filter_options()
|
877
|
+
check_sort_options()
|
878
|
+
end
|
879
|
+
# dataset is defined by filter method in control object
|
880
|
+
form_filter = data_set['filter'] || 'default_filter'
|
881
|
+
if respond_to?(form_filter)
|
882
|
+
@records = send(form_filter)
|
883
|
+
# something went wrong. flash[:error] should have explanation.
|
884
|
+
if @records.class == FalseClass
|
885
|
+
@records = []
|
886
|
+
render(action: :index)
|
887
|
+
return true
|
888
|
+
end
|
889
|
+
# process_select_options
|
890
|
+
# pagination but only if not already set
|
891
|
+
unless (@form['table'] == 'ar_memory') #TODO || @records.options[:limit])
|
892
|
+
per_page = (data_set['per_page'] || 25).to_i
|
893
|
+
@records = @records.page(params[:page]).per(per_page) if per_page > 0
|
894
|
+
end
|
895
|
+
elsif form_filter != 'default_filter'
|
896
|
+
Rails.logger.error "Error: data_set:filter: #{data_set['filter']} not found in controls!"
|
897
|
+
end
|
898
|
+
false
|
899
|
+
end
|
900
|
+
|
901
|
+
########################################################################
|
902
|
+
# Process index action for in memory data. default_filter method must fill @records array
|
903
|
+
# with data, that will be displayed in the browser object.
|
904
|
+
########################################################################
|
905
|
+
def process_in_memory #:nodoc
|
906
|
+
@records = []
|
907
|
+
# dataset is defined by filter method in control object
|
908
|
+
if (method = @form['index']['data_set']['filter'] || default_filter)
|
909
|
+
send(method) if respond_to?(method)
|
910
|
+
end
|
911
|
+
# ensure that record has id field
|
912
|
+
if @records.size > 0
|
913
|
+
raise "Exception: id field must be set in ar_memory record!" unless @records.first.id
|
914
|
+
end
|
915
|
+
false
|
916
|
+
end
|
917
|
+
|
918
|
+
########################################################################
|
919
|
+
# Prevent double form submit
|
920
|
+
#
|
921
|
+
# Program will save form_time_stamp to session. If form is saved with
|
922
|
+
# the same form_time_stamp, program will block create action.
|
923
|
+
########################################################################
|
924
|
+
def double_form_submit?
|
925
|
+
form_name = AgileHelper.form_param(params) || AgileHelper.table_param(params)
|
926
|
+
session[:dfs] ||= {}
|
927
|
+
params[:form_time_stamp] = params[:form_time_stamp].to_i
|
928
|
+
if params[:form_time_stamp] <= update_dfs_time(form_name) && !Rails.env.test? # test must be excluded
|
929
|
+
flash[:error] = I18n.t('agile.dfs')
|
930
|
+
return true
|
931
|
+
end
|
932
|
+
|
933
|
+
update_dfs_time(form_name, params[:form_time_stamp])
|
934
|
+
false
|
935
|
+
end
|
936
|
+
|
937
|
+
########################################################################
|
938
|
+
# Updates double_form_submit timings.
|
939
|
+
########################################################################
|
940
|
+
def update_dfs_time(form_name, time = nil)
|
941
|
+
if time.nil?
|
942
|
+
session[:dfs][form_name] || 0
|
943
|
+
else
|
944
|
+
session[:dfs][form_name] = time
|
945
|
+
if session[:dfs].size > 3
|
946
|
+
oldest = session[:dfs].invert.min
|
947
|
+
session[:dfs].delete(oldest.last)
|
948
|
+
end
|
949
|
+
end
|
950
|
+
end
|
951
|
+
|
952
|
+
########################################################################
|
953
|
+
# Loads initial values for new action from cookie or params. Initial values
|
954
|
+
# are provided in cookies[:record] or in params as table_name.field_name=value
|
955
|
+
########################################################################
|
956
|
+
def load_initial_values
|
957
|
+
table = @tables.last[1] + '.'
|
958
|
+
# initial values set on page
|
959
|
+
if cookies[:record].present?
|
960
|
+
Marshal.load(cookies[:record]).each do |k, v|
|
961
|
+
k = k.to_s
|
962
|
+
if k.match(table + '.')
|
963
|
+
field = k.split('.').last
|
964
|
+
@record.send("#{field}=", v) if @record.respond_to?(field)
|
965
|
+
end
|
966
|
+
end
|
967
|
+
end
|
968
|
+
# initial values set in url (params)
|
969
|
+
params.each do |k, v|
|
970
|
+
if k.match(table + '.')
|
971
|
+
field = k.split('.').last
|
972
|
+
@record.send("#{field}=", v) if @record.respond_to?(field)
|
973
|
+
end
|
974
|
+
end
|
975
|
+
end
|
976
|
+
|
977
|
+
end
|