edgarj 0.01.12
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/MIT-LICENSE +20 -0
- data/README.rdoc +23 -0
- data/Rakefile +40 -0
- data/app/assets/javascripts/edgarj/base.js +49 -0
- data/app/assets/javascripts/edgarj/menu.js +39 -0
- data/app/assets/javascripts/edgarj/operator_selection.js +133 -0
- data/app/assets/stylesheets/edgarj/base.css +304 -0
- data/app/assets/stylesheets/edgarj/menu.css +65 -0
- data/app/controllers/edgarj/authentication_mixin.rb.sample +30 -0
- data/app/controllers/edgarj/controller_mixin_common.rb +80 -0
- data/app/controllers/edgarj/controller_mixin_for_app.rb +71 -0
- data/app/controllers/edgarj/edgarj_controller.rb +535 -0
- data/app/controllers/edgarj/model_permissions_controller.rb +8 -0
- data/app/controllers/edgarj/permission_mixin.rb +84 -0
- data/app/controllers/edgarj/popup_controller.rb +128 -0
- data/app/controllers/edgarj/rescue_mixin.rb +37 -0
- data/app/controllers/edgarj/user_group_users_controller.rb +8 -0
- data/app/controllers/edgarj/user_groups_controller.rb +9 -0
- data/app/controllers/edgarj/user_groups_popup_controller.rb +8 -0
- data/app/helpers/edgarj/assoc_helper.rb +369 -0
- data/app/helpers/edgarj/common_helper.rb +37 -0
- data/app/helpers/edgarj/edgarj_helper.rb +20 -0
- data/app/helpers/edgarj/field_helper.rb +397 -0
- data/app/helpers/edgarj/form_drawer.rb +322 -0
- data/app/helpers/edgarj/list_drawer.rb +169 -0
- data/app/helpers/edgarj/menu_helper.rb +92 -0
- data/app/helpers/edgarj/model_permissions_helper.rb +9 -0
- data/app/helpers/edgarj/popup_helper.rb +40 -0
- data/app/helpers/edgarj/search_helper.rb +14 -0
- data/app/helpers/edgarj/sessions_helper.rb +9 -0
- data/app/helpers/edgarj/user_group_users_helper.rb +9 -0
- data/app/helpers/edgarj/user_groups_helper.rb +9 -0
- data/app/helpers/edgarj/user_groups_popup_helper.rb +9 -0
- data/app/models/edgarj/drawer/base.rb +234 -0
- data/app/models/edgarj/drawer/normal.rb +6 -0
- data/app/models/edgarj/drawer/popup.rb +47 -0
- data/app/models/edgarj/drawer.rb +13 -0
- data/app/models/edgarj/model_permission.rb +41 -0
- data/app/models/edgarj/page_info.rb +47 -0
- data/app/models/edgarj/search.rb +81 -0
- data/app/models/edgarj/search_form.rb +255 -0
- data/app/models/edgarj/search_popup.rb +44 -0
- data/app/models/edgarj/sssn.rb +120 -0
- data/app/models/edgarj/user_group.rb +67 -0
- data/app/models/edgarj/user_group_user.rb +18 -0
- data/app/models/edgarj.rb +5 -0
- data/app/views/edgarj/edgarj/_form.html.erb +25 -0
- data/app/views/edgarj/edgarj/_list.html.erb +52 -0
- data/app/views/edgarj/edgarj/_message_popup.html.erb +15 -0
- data/app/views/edgarj/edgarj/_search_form.html.erb +64 -0
- data/app/views/edgarj/edgarj/_search_operator.html.erb +17 -0
- data/app/views/edgarj/edgarj/_search_operator_selection.html.erb +12 -0
- data/app/views/edgarj/edgarj/clear.js.erb +1 -0
- data/app/views/edgarj/edgarj/create.js.erb +4 -0
- data/app/views/edgarj/edgarj/destroy.js.erb +2 -0
- data/app/views/edgarj/edgarj/index.html.erb +37 -0
- data/app/views/edgarj/edgarj/index.js.erb +1 -0
- data/app/views/edgarj/edgarj/message_popup.js.erb +3 -0
- data/app/views/edgarj/edgarj/page_info_save.js.erb +1 -0
- data/app/views/edgarj/edgarj/search.js.erb +4 -0
- data/app/views/edgarj/edgarj/search_clear.js.erb +1 -0
- data/app/views/edgarj/edgarj/show.js.erb +1 -0
- data/app/views/edgarj/edgarj/top.html.erb +4 -0
- data/app/views/edgarj/edgarj/update.js.erb +4 -0
- data/app/views/edgarj/popup/_index.html.erb +16 -0
- data/app/views/edgarj/popup/_list.html.erb +42 -0
- data/app/views/edgarj/popup/_message_popup.html.erb +15 -0
- data/app/views/edgarj/popup/_search.html.erb +42 -0
- data/app/views/edgarj/popup/_search_save_popup.html.erb +16 -0
- data/app/views/edgarj/popup/index.js.erb +5 -0
- data/app/views/edgarj/popup/message_popup.js.erb +3 -0
- data/app/views/edgarj/popup/page_info_save.js.erb +1 -0
- data/app/views/edgarj/popup/search.js.erb +15 -0
- data/app/views/edgarj/popup/view_status_save.js.erb +1 -0
- data/config/routes.rb +15 -0
- data/config/settings.yml +5 -0
- data/db/migrate/20131118084600_create_edgar_sssns.rb +14 -0
- data/db/migrate/20131123124730_create_edgar_page_infos.rb +15 -0
- data/db/migrate/20140116062252_create_edgar_user_groups.rb +18 -0
- data/db/migrate/20140116062327_create_edgar_user_group_users.rb +10 -0
- data/db/migrate/20140206222308_create_edgar_model_permissions.rb +14 -0
- data/db/migrate/20141209053055_rename_edgar_to_edgarj.rb +17 -0
- data/lib/core_ext/active_record.rb +123 -0
- data/lib/core_ext/resources.rb +71 -0
- data/lib/edgarj/engine.rb +64 -0
- data/lib/edgarj/enum_cache.rb +46 -0
- data/lib/edgarj/templates/rails/helper/helper.rb +10 -0
- data/lib/edgarj/templates/test_unit/scaffold/functional_test.rb +575 -0
- data/lib/edgarj/version.rb +3 -0
- data/lib/edgarj.rb +14 -0
- data/lib/generators/edgarj/popup_scaffold/USAGE +6 -0
- data/lib/generators/edgarj/popup_scaffold/popup_scaffold_generator.rb +35 -0
- data/lib/generators/edgarj/popup_scaffold/templates/controller.rb +13 -0
- data/lib/generators/edgarj/popup_scaffold/templates/functional_test.rb +197 -0
- data/lib/generators/edgarj/popup_scaffold/templates/helper.rb +9 -0
- data/lib/generators/edgarj/scaffold/USAGE +17 -0
- data/lib/generators/edgarj/scaffold/scaffold_generator.rb +40 -0
- data/lib/generators/edgarj/scaffold/templates/controller.rb +13 -0
- data/lib/tasks/edgarj_tasks.rake +32 -0
- data/lib/tasks/pakcage.rake +18 -0
- data/locale/en.yml +94 -0
- data/locale/ja.yml +100 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +21 -0
- data/test/dummy/app/assets/javascripts/authors.js +2 -0
- data/test/dummy/app/assets/stylesheets/application.css +16 -0
- data/test/dummy/app/assets/stylesheets/authors.css +4 -0
- data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
- data/test/dummy/app/controllers/application_controller.rb +15 -0
- data/test/dummy/app/controllers/authors_controller.rb +6 -0
- data/test/dummy/app/controllers/authors_popup_controller.rb +6 -0
- data/test/dummy/app/controllers/books_controller.rb +6 -0
- data/test/dummy/app/controllers/dummy_auth_mixin.rb +36 -0
- data/test/dummy/app/decorators/models/edgarj/user_group_decorator.rb +1 -0
- data/test/dummy/app/helpers/application_helper.rb +3 -0
- data/test/dummy/app/helpers/authors_helper.rb +7 -0
- data/test/dummy/app/helpers/authors_popup_helper.rb +7 -0
- data/test/dummy/app/helpers/books_helper.rb +7 -0
- data/test/dummy/app/models/author.rb +32 -0
- data/test/dummy/app/models/book.rb +5 -0
- data/test/dummy/app/models/user.rb +13 -0
- data/test/dummy/app/views/layouts/application.html.erb +32 -0
- data/test/dummy/app/views/layouts/login.html.erb +20 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +15 -0
- data/test/dummy/config/edgarj/menu_config.rb +28 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +42 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +34 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/strong_parameter.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +26 -0
- data/test/dummy/config/routes.rb +60 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/migrate/20131107120635_create_authors.rb +9 -0
- data/test/dummy/db/migrate/20131218011851_create_books.rb +10 -0
- data/test/dummy/db/migrate/20140201000000_add_user_group_id_to_authors.rb +5 -0
- data/test/dummy/db/migrate/20140807065420_create_users.rb +10 -0
- data/test/dummy/db/schema.rb +93 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/test/functional/authors_controller_test.rb +631 -0
- data/test/dummy/test/functional/books_controller_test.rb +516 -0
- data/test/dummy/test/helpers/authors_helper_test.rb +4 -0
- data/test/dummy/test/unit/author_test.rb +7 -0
- data/test/dummy/test/unit/book_test.rb +7 -0
- data/test/edgar_test.rb +7 -0
- data/test/fixtures/authors.yml +32 -0
- data/test/fixtures/books.yml +56 -0
- data/test/fixtures/edgarj/model_permissions.yml +97 -0
- data/test/fixtures/edgarj/page_infos.yml +84 -0
- data/test/fixtures/edgarj/sssns.yml +38 -0
- data/test/fixtures/edgarj/user_group_users.yml +114 -0
- data/test/fixtures/edgarj/user_groups.yml +95 -0
- data/test/fixtures/users.yml +49 -0
- data/test/functional/edgarj/edgarj_controller_test.rb +24 -0
- data/test/functional/edgarj/model_permissions_controller_test.rb +554 -0
- data/test/functional/edgarj/user_group_users_controller_test.rb +567 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/support/edgarj/controller_supporter.rb +23 -0
- data/test/test_helper.rb +23 -0
- data/test/unit/edgarj/model_permission_test.rb +27 -0
- data/test/unit/edgarj/page_info_test.rb +7 -0
- data/test/unit/edgarj/sssn_test.rb +10 -0
- data/test/unit/edgarj/user_group_test.rb +32 -0
- data/test/unit/edgarj/user_group_user_test.rb +9 -0
- data/test/unit/helpers/edgarj/model_permissions_helper_test.rb +6 -0
- data/test/unit/helpers/edgarj/user_group_users_helper_test.rb +6 -0
- metadata +456 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
module Edgarj
|
|
2
|
+
# 'SearchForm' instance contains search-conditions entered at search-form as:
|
|
3
|
+
# 1. search conditions (mainly string, but integers for timestamp)
|
|
4
|
+
# for eash attribute of model
|
|
5
|
+
# 1. operator (=, <, >, ...) for each attribute
|
|
6
|
+
# 1. parent name for redraw
|
|
7
|
+
#
|
|
8
|
+
# Model(ActiveRecord) intance itself (e.g. Customer.new) doesn't satisfy to keep
|
|
9
|
+
# search condition because:
|
|
10
|
+
#
|
|
11
|
+
# 1. id cannot be set because of protection.
|
|
12
|
+
# 1. search condition may have operator (like '=', '>=')
|
|
13
|
+
#
|
|
14
|
+
# This is why SearchForm class is created.
|
|
15
|
+
#
|
|
16
|
+
# NOTE: model is stored in SearhForm as model_str.constantize to avoid following error:
|
|
17
|
+
# ArgumentError (undefined class/module Customer)
|
|
18
|
+
#
|
|
19
|
+
# === Operator
|
|
20
|
+
# Operator for some search-fields (e.g. integer, date) are initialized
|
|
21
|
+
# by params[:edgarj_search_form_operator]. For example, when search condition
|
|
22
|
+
# for Question screen GUI is:
|
|
23
|
+
#
|
|
24
|
+
# Priority < 3
|
|
25
|
+
#
|
|
26
|
+
# then, params[:search] is:
|
|
27
|
+
#
|
|
28
|
+
# :priority=>3, :search_form_operator=>{:priority=>'<'}
|
|
29
|
+
#
|
|
30
|
+
# and SearchForm object is built as:
|
|
31
|
+
#
|
|
32
|
+
# s = SearchForm.new(Question, params[:search])
|
|
33
|
+
#
|
|
34
|
+
# and finally it generates SQL condition as follows:
|
|
35
|
+
#
|
|
36
|
+
# s.conditions # ["priority<?", 3]
|
|
37
|
+
#
|
|
38
|
+
# === Parent name
|
|
39
|
+
# In order to hold parent object 'name' value for redraw, _parent[] hash is
|
|
40
|
+
# used. It is passed from params[:search_form_parent].
|
|
41
|
+
#
|
|
42
|
+
# === BUGS
|
|
43
|
+
# date-type(e.g. 'created_at(1i)'=2010, 'created_at(2i)'=2, ...) and
|
|
44
|
+
# datetime-type are not supported now while jQuery datepicker is OK.
|
|
45
|
+
#
|
|
46
|
+
class SearchForm < Edgarj::Search
|
|
47
|
+
attr_accessor :_operator, :_parent
|
|
48
|
+
validate :validate_data_type
|
|
49
|
+
|
|
50
|
+
module Dbms
|
|
51
|
+
MYSQL = 1
|
|
52
|
+
ELSE = 99
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Store comparison-operator for each search field
|
|
56
|
+
class Operator
|
|
57
|
+
# required at 'fields_for' helper
|
|
58
|
+
extend ActiveModel::Naming
|
|
59
|
+
extend ActiveModel::Conversion
|
|
60
|
+
|
|
61
|
+
ALLOWED = HashWithIndifferentAccess.new.tap do |a|
|
|
62
|
+
%w(= <> > >= < <=).each do |op|
|
|
63
|
+
a[op] = true
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# accepts only allowed operators to avoid SQL injection
|
|
68
|
+
def initialize(attrs = {})
|
|
69
|
+
@attrs = HashWithIndifferentAccess.new
|
|
70
|
+
|
|
71
|
+
for k, v in (attrs || {}) do
|
|
72
|
+
if ALLOWED[v]
|
|
73
|
+
@attrs[k] = v
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# generate a part of expression like '=?', ' in(?)', etc.
|
|
79
|
+
#
|
|
80
|
+
# When operator contains place-holder '?', it is not appended.
|
|
81
|
+
def exp(attr)
|
|
82
|
+
if @attrs[attr]
|
|
83
|
+
if @attrs[attr].index('?')
|
|
84
|
+
@attrs[attr]
|
|
85
|
+
else
|
|
86
|
+
@attrs[attr] + '?'
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
'=?'
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def method_missing(method_name, *args)
|
|
94
|
+
@attrs[method_name.to_sym]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Build SearchForm object from ActiveRecord 'klass' and attrs.
|
|
99
|
+
#
|
|
100
|
+
# === INPUTS
|
|
101
|
+
# klass:: ActiveRecord
|
|
102
|
+
# attrs:: hash of key=>value pair
|
|
103
|
+
def initialize(klass, attrs={})
|
|
104
|
+
super(klass)
|
|
105
|
+
@_table_name = klass.table_name
|
|
106
|
+
@attrs = attrs.dup
|
|
107
|
+
@_operator = Operator.new(attrs[:edgarj_search_form_operator])
|
|
108
|
+
@_parent = attrs[:search_form_parent]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# map attribute name to hash entry.
|
|
112
|
+
# This mechanism is required to be used in 'form_for' helper.
|
|
113
|
+
#
|
|
114
|
+
# When column type is integer and has digits value, return integer.
|
|
115
|
+
# This is required for selection helper.
|
|
116
|
+
# Even it is integer but has no value, keeps blank.
|
|
117
|
+
#
|
|
118
|
+
# When attribute name ends '=', assignment to hash works.
|
|
119
|
+
#
|
|
120
|
+
# === EXAMPLE
|
|
121
|
+
# s = Searchform.new(Product, :name=>'a')
|
|
122
|
+
# s.name
|
|
123
|
+
# s.conditions # ["name=?", "a"]
|
|
124
|
+
# s.name = 'b' # method_missing('name=', 'b') is called
|
|
125
|
+
# s.conditions # ["name=?", "b"]
|
|
126
|
+
#
|
|
127
|
+
def method_missing(method_name, *args)
|
|
128
|
+
if method_name.to_s =~ /^(.*)=$/
|
|
129
|
+
@attrs[$1.to_sym] = args[0]
|
|
130
|
+
else
|
|
131
|
+
val = @attrs[method_name.to_sym]
|
|
132
|
+
case column_type(method_name)
|
|
133
|
+
when :integer
|
|
134
|
+
if val =~ /^\d+$/
|
|
135
|
+
val.to_i
|
|
136
|
+
else
|
|
137
|
+
val
|
|
138
|
+
end
|
|
139
|
+
else
|
|
140
|
+
val
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
DEFAULT_TIMEZONE_FOR_TIMESTAMP_DATE_COMPARISON = '9'
|
|
146
|
+
|
|
147
|
+
# Generate search-conditions.
|
|
148
|
+
# Wildcard search by '*' is supported on string attribute.
|
|
149
|
+
#
|
|
150
|
+
# === RETURN
|
|
151
|
+
# condition_string, value_array:: values for :conditions option on model
|
|
152
|
+
def conditions
|
|
153
|
+
return ['1=0'] if !valid?
|
|
154
|
+
|
|
155
|
+
conds = []
|
|
156
|
+
values = []
|
|
157
|
+
for col in @_klass_str.constantize.columns do
|
|
158
|
+
col_str = col_name(col)
|
|
159
|
+
val = @attrs[encode_name(col)]
|
|
160
|
+
if !val.blank?
|
|
161
|
+
case col.type
|
|
162
|
+
when :string, :text
|
|
163
|
+
if val =~ /\*$/
|
|
164
|
+
conds << col_str + ' like ?'
|
|
165
|
+
values << val.gsub(/\*$/, '%')
|
|
166
|
+
else
|
|
167
|
+
conds << col_str + '=?'
|
|
168
|
+
values << val
|
|
169
|
+
end
|
|
170
|
+
when :datetime
|
|
171
|
+
case is_dbms?
|
|
172
|
+
when Dbms::MYSQL
|
|
173
|
+
# conds << col_str + @_operator.exp(encode_name(col))
|
|
174
|
+
conds << sprintf("date(%s)",
|
|
175
|
+
col_str) +
|
|
176
|
+
@_operator.exp(encode_name(col))
|
|
177
|
+
else
|
|
178
|
+
conds << sprintf("date(timezone('%s',%s))",
|
|
179
|
+
DEFAULT_TIMEZONE_FOR_TIMESTAMP_DATE_COMPARISON,
|
|
180
|
+
col_str) +
|
|
181
|
+
@_operator.exp(encode_name(col))
|
|
182
|
+
end
|
|
183
|
+
values << val.to_s
|
|
184
|
+
when :boolean
|
|
185
|
+
case is_dbms?
|
|
186
|
+
when Dbms::MYSQL
|
|
187
|
+
conds << col_str + @_operator.exp(encode_name(col))
|
|
188
|
+
values << (val=='true')
|
|
189
|
+
else
|
|
190
|
+
conds << col_str + @_operator.exp(encode_name(col))
|
|
191
|
+
values << val
|
|
192
|
+
end
|
|
193
|
+
else
|
|
194
|
+
conds << col_str + @_operator.exp(encode_name(col))
|
|
195
|
+
values << val
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
return [conds.join(' and ')] + values
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
private
|
|
203
|
+
def is_dbms?
|
|
204
|
+
@is_dbms ||=
|
|
205
|
+
if (Edgarj::Sssn.connection.class.to_s =~ /mysql/i)
|
|
206
|
+
Dbms::MYSQL
|
|
207
|
+
else
|
|
208
|
+
Dbms::ELSE
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
# 'rec.id' is unintentionally interpreted as Object.id rather than
|
|
213
|
+
# rec.attrs['id'] so encode it to _id.
|
|
214
|
+
#
|
|
215
|
+
# 'rec.type' has the same issue so to _type.
|
|
216
|
+
#
|
|
217
|
+
# === SEE ALSO
|
|
218
|
+
# decode_name()
|
|
219
|
+
def encode_name(col)
|
|
220
|
+
if col.name == 'id'
|
|
221
|
+
:_id
|
|
222
|
+
elsif col.name == 'type'
|
|
223
|
+
:_type
|
|
224
|
+
else
|
|
225
|
+
col.name.to_sym
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
#
|
|
230
|
+
# === SEE ALSO
|
|
231
|
+
# encode_name()
|
|
232
|
+
def decode_name(col)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# generate full-qualified column name. Example: authors.name
|
|
236
|
+
def col_name(col)
|
|
237
|
+
[@_table_name, col.name].join('.')
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def validate_data_type
|
|
241
|
+
for col in @_klass_str.constantize.columns do
|
|
242
|
+
col_str = col_name(col)
|
|
243
|
+
encoded_col = encode_name(col)
|
|
244
|
+
val = @attrs[encoded_col]
|
|
245
|
+
case col.type
|
|
246
|
+
when :integer
|
|
247
|
+
if val.present? && val !~ /^[\d\.\-]+$/
|
|
248
|
+
errors.add(encoded_col, :not_an_integer)
|
|
249
|
+
end
|
|
250
|
+
else
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Edgarj
|
|
2
|
+
# Search condition for popup
|
|
3
|
+
class SearchPopup < Search
|
|
4
|
+
attr_accessor :klass_str, :col, :val
|
|
5
|
+
|
|
6
|
+
validates_format_of :col, with: /\A[a-zA-Z_0-9\.]+\z/, allow_nil: true
|
|
7
|
+
validate :validate_integer
|
|
8
|
+
|
|
9
|
+
def initialize(klass, hash = nil)
|
|
10
|
+
super(klass)
|
|
11
|
+
@col = hash ? hash[:col] : nil
|
|
12
|
+
@val = hash ? hash[:val] : nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# implement to generate search-conditions
|
|
16
|
+
def conditions
|
|
17
|
+
return ['1=0'] if !valid?
|
|
18
|
+
|
|
19
|
+
if @val.blank?
|
|
20
|
+
[]
|
|
21
|
+
else
|
|
22
|
+
# FIXME: assume type is just string
|
|
23
|
+
op = '=?'
|
|
24
|
+
val = @val
|
|
25
|
+
if val =~ /\*$/
|
|
26
|
+
op = ' like ?'
|
|
27
|
+
val = @val.gsub(/\*/, '%')
|
|
28
|
+
end
|
|
29
|
+
["#{@col}#{op}", val]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
def validate_integer
|
|
35
|
+
case column_type(col)
|
|
36
|
+
when :integer
|
|
37
|
+
if val.present? && val !~ /^[\d\.\-]+$/
|
|
38
|
+
errors.add(:val, :not_an_integer)
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
module Edgarj
|
|
2
|
+
# Edgarj specific session
|
|
3
|
+
#
|
|
4
|
+
# ActiveRecord::SessionStore::Session and Sssn are merged into Sssn because:
|
|
5
|
+
#
|
|
6
|
+
# * I could not find the way to build/prepare session for test;-(
|
|
7
|
+
# * It was verbose to Sync Sssn and Session.
|
|
8
|
+
#
|
|
9
|
+
# See ActiveRecord::SessionStore how to do that.
|
|
10
|
+
class Sssn < ActiveRecord::Base
|
|
11
|
+
self.table_name = 'edgarj_sssns'
|
|
12
|
+
|
|
13
|
+
#has_many :cart_items, dependent: :destroy
|
|
14
|
+
#has_many :album_slide_shows, dependent: :destroy,
|
|
15
|
+
# :class_name=>'Album::SlideShow'
|
|
16
|
+
belongs_to :user
|
|
17
|
+
has_many :page_infos, dependent: :destroy, autosave: true
|
|
18
|
+
|
|
19
|
+
#-----------------------------------------
|
|
20
|
+
# implementation section required by Rails
|
|
21
|
+
#-----------------------------------------
|
|
22
|
+
|
|
23
|
+
before_save :marshal_data!
|
|
24
|
+
before_save :raise_on_session_data_overflow!
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
def data_column_size_limit
|
|
28
|
+
@data_column_size_limit ||= columns_hash['data'].limit
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def find_by_session_id(session_id)
|
|
32
|
+
where(session_id: session_id).first
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def marshal(data)
|
|
36
|
+
Base64.encode64(Marshal.dump(data)) if data
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def unmarshal(data)
|
|
40
|
+
Marshal.load(Base64.decode64(data)) if data
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
=begin
|
|
45
|
+
def initialize(attributes = nil)
|
|
46
|
+
@data = nil
|
|
47
|
+
super
|
|
48
|
+
end
|
|
49
|
+
=end
|
|
50
|
+
|
|
51
|
+
# Lazy-unmarshal session state.
|
|
52
|
+
def data
|
|
53
|
+
@data ||= self.class.unmarshal(read_attribute('data')) || {}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
attr_writer :data
|
|
57
|
+
|
|
58
|
+
# Has the session been loaded yet?
|
|
59
|
+
def loaded?
|
|
60
|
+
!!@data
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
def marshal_data!
|
|
65
|
+
return false unless loaded?
|
|
66
|
+
write_attribute('data', self.class.marshal(data))
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Ensures that the data about to be stored in the database is not
|
|
70
|
+
# larger than the data storage column. Raises
|
|
71
|
+
# ActionController::SessionOverflowError.
|
|
72
|
+
def raise_on_session_data_overflow!
|
|
73
|
+
return false unless loaded?
|
|
74
|
+
limit = self.class.data_column_size_limit
|
|
75
|
+
if limit and read_attribute('data').size > limit
|
|
76
|
+
raise ActionController::SessionOverflowError
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
#---------------------------------------------
|
|
81
|
+
# End implementation section required by Rails
|
|
82
|
+
#---------------------------------------------
|
|
83
|
+
|
|
84
|
+
public
|
|
85
|
+
# delete stale sessions no longer active than SESSION_TIME_OUT minutes ago
|
|
86
|
+
#
|
|
87
|
+
# === INPUTS
|
|
88
|
+
# dry_run:: dry-run mode (default = true)
|
|
89
|
+
#
|
|
90
|
+
def self.delete_stale_sessions(dry_run=true)
|
|
91
|
+
self.transaction do
|
|
92
|
+
for s in Sssn.all(
|
|
93
|
+
:conditions=>['updated_at<?', Edgarj::BaseConfig::SESSION_TIME_OUT.minutes.ago.utc]) do
|
|
94
|
+
begin
|
|
95
|
+
session_id = s.session_id
|
|
96
|
+
s.destroy
|
|
97
|
+
logger.info("deleting session(#{session_id})")
|
|
98
|
+
rescue ActiveRecord::RecordNotFound => ex
|
|
99
|
+
logger.warn("session not found(#{session_id})")
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
raise ActiveRecord::Rollback if dry_run
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def name
|
|
107
|
+
user.name + ' session'
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def before_destroy
|
|
111
|
+
if Edgarj::Login::ENABLE_LOGGING
|
|
112
|
+
a = ActionEntry.new
|
|
113
|
+
a.user = self.user
|
|
114
|
+
a.action = Action::Login.new(:kind=>Action::Login::Kind::LOGOUT)
|
|
115
|
+
a.save!
|
|
116
|
+
end
|
|
117
|
+
true
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# UserGroup can be used for several types of user group like the followings:
|
|
2
|
+
#
|
|
3
|
+
# * user organization
|
|
4
|
+
# * user role
|
|
5
|
+
module Edgarj
|
|
6
|
+
class UserGroup < ActiveRecord::Base
|
|
7
|
+
self.table_name = 'edgarj_user_groups'
|
|
8
|
+
|
|
9
|
+
acts_as_nested_set scope: :kind
|
|
10
|
+
|
|
11
|
+
has_many :user_group_users, dependent: :destroy
|
|
12
|
+
has_many :users, through: :user_group_users
|
|
13
|
+
has_many :model_permissions, dependent: :destroy
|
|
14
|
+
|
|
15
|
+
# 'Kind' value is to identify what kind of user-group many-to-many
|
|
16
|
+
# relation is established. Following two values are reserved:
|
|
17
|
+
#
|
|
18
|
+
# ROLE:: controller-permission
|
|
19
|
+
# USER_ORG:: organization of users
|
|
20
|
+
#
|
|
21
|
+
# Any uniq value can be added for application usage. One example
|
|
22
|
+
# is in test/dummy/app/decorators/models/edgarj/user_group_decorator.rb
|
|
23
|
+
module Kind
|
|
24
|
+
ROLE = 100
|
|
25
|
+
USER_ORG = 200
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def validate
|
|
29
|
+
super
|
|
30
|
+
validate_tree_kind
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# return true if the role has enough permission on the controller.
|
|
34
|
+
#
|
|
35
|
+
# If user role is 'admin' then all operations are permitted.
|
|
36
|
+
#
|
|
37
|
+
# Always return false if the user-group is not ROLE.
|
|
38
|
+
#
|
|
39
|
+
# if requested_flags is omitted, just checks existence of
|
|
40
|
+
# model_permissions and doesn't check CRUD level.
|
|
41
|
+
def permitted?(model_name, requested_flags = 0)
|
|
42
|
+
return false if self.kind != Kind::ROLE
|
|
43
|
+
return true if admin?
|
|
44
|
+
|
|
45
|
+
p = self.model_permissions.find_by_model(model_name)
|
|
46
|
+
if requested_flags == 0
|
|
47
|
+
p
|
|
48
|
+
else
|
|
49
|
+
p && p.permitted?(requested_flags)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def admin?
|
|
54
|
+
self.kind == Kind::ROLE && name == 'admin'
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
# USER_ORG's parent must be USER_ORG
|
|
59
|
+
def validate_tree_kind
|
|
60
|
+
if parent_id
|
|
61
|
+
if kind != parent.kind
|
|
62
|
+
err_on(:kind, 'different_kind_from_parent')
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# UserGroup - User intersection model
|
|
2
|
+
#
|
|
3
|
+
module Edgarj
|
|
4
|
+
class UserGroupUser < ActiveRecord::Base
|
|
5
|
+
self.table_name = 'edgarj_user_group_users'
|
|
6
|
+
|
|
7
|
+
belongs_to :user_group
|
|
8
|
+
belongs_to :user
|
|
9
|
+
|
|
10
|
+
validates_presence_of :user_group_id
|
|
11
|
+
validates_presence_of :user_id
|
|
12
|
+
validates_uniqueness_of :user_id, :scope=>:user_group_id
|
|
13
|
+
|
|
14
|
+
def name
|
|
15
|
+
self.user.name + ' of ' + self.user_group.name
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Form for record detail
|
|
3
|
+
|
|
4
|
+
= INPUTS
|
|
5
|
+
|
|
6
|
+
@drawer:: drawer instance
|
|
7
|
+
@record:: form value
|
|
8
|
+
|
|
9
|
+
= SEE ALSO
|
|
10
|
+
|
|
11
|
+
_search_form.htm.erb:: search form
|
|
12
|
+
%>
|
|
13
|
+
<div id=edgarj_form>
|
|
14
|
+
<% if @record.errors.any? %>
|
|
15
|
+
<div class="error_explanation">
|
|
16
|
+
<h2><%= t('error.form.title', count: @record.errors.count) %></h2>
|
|
17
|
+
<ul>
|
|
18
|
+
<% @record.errors.full_messages.each do |msg| %>
|
|
19
|
+
<li><%= msg %></li>
|
|
20
|
+
<% end %>
|
|
21
|
+
</ul>
|
|
22
|
+
</div>
|
|
23
|
+
<% end %>
|
|
24
|
+
<%= @drawer.draw_form(@record) %>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
= INPUTS
|
|
3
|
+
|
|
4
|
+
@list:: model instance list
|
|
5
|
+
@count:: model instance list count
|
|
6
|
+
@page_info:: page_info instance
|
|
7
|
+
%>
|
|
8
|
+
<div id=edgarj_list> <!-- edgarj/_list.html.erb -->
|
|
9
|
+
<table width="100%">
|
|
10
|
+
<tr>
|
|
11
|
+
<td><%= t('edgarj.default.total_records') % @count %></td>
|
|
12
|
+
<td>
|
|
13
|
+
<%= paginate @list,
|
|
14
|
+
remote: true,
|
|
15
|
+
params: {action: 'index'} %>
|
|
16
|
+
</td>
|
|
17
|
+
<td align=right>
|
|
18
|
+
<%=
|
|
19
|
+
form_for(@page_info,
|
|
20
|
+
remote: true,
|
|
21
|
+
url: {action: 'page_info_save', id: @page_info.id,
|
|
22
|
+
controller: params[:controller]},
|
|
23
|
+
html: {id: 'edgarj_form_lines_per_page', method: :put}
|
|
24
|
+
) do |f| %>
|
|
25
|
+
<%= f.select('lines', Edgarj::LINES_PER_PAGE.map{|x| [x,x]}) %>
|
|
26
|
+
<%= v('records_per_page') %>
|
|
27
|
+
<% end %>
|
|
28
|
+
</td>
|
|
29
|
+
<td>
|
|
30
|
+
<%= button_to v('csv_download'), {
|
|
31
|
+
controller: params[:controller],
|
|
32
|
+
action: 'csv_download'},
|
|
33
|
+
{method: :get} %>
|
|
34
|
+
</td>
|
|
35
|
+
</tr>
|
|
36
|
+
</table>
|
|
37
|
+
<%= drawer.draw_list(@list) %>
|
|
38
|
+
|
|
39
|
+
<%= javascript_tag do %>
|
|
40
|
+
$(function(){
|
|
41
|
+
/* action on changing records/page at the selection in list */
|
|
42
|
+
$('#edgarj_page_info_lines').change(function(){
|
|
43
|
+
$("#edgarj_form_lines_per_page").trigger("submit.rails");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
/* show detail on clicking a list row */
|
|
47
|
+
$('._edgarj_list_column').click(function(){
|
|
48
|
+
$.ajax($(this).attr('data-url'));
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
<% end %>
|
|
52
|
+
</div>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!-- edgarj/_message_popup.html.erb -->
|
|
2
|
+
<%#
|
|
3
|
+
message popup template.
|
|
4
|
+
%>
|
|
5
|
+
|
|
6
|
+
<center>
|
|
7
|
+
<div id=edgarj_message_error ><%= flash[:error] || '' %></div>
|
|
8
|
+
<div id=edgarj_message_notice><%= flash[:notice] || '' %></div>
|
|
9
|
+
<hr>
|
|
10
|
+
<%= button_tag t('close'),
|
|
11
|
+
type: 'button',
|
|
12
|
+
id: 'edgarj_message_popup_close',
|
|
13
|
+
onClick: "$('#edgarj_message_popup').dialog('close');"
|
|
14
|
+
%>
|
|
15
|
+
</center>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Form for search condition.
|
|
3
|
+
|
|
4
|
+
= INPUTS
|
|
5
|
+
|
|
6
|
+
@drawer:: Edgarj::Drawer::Normal object
|
|
7
|
+
@search:: form value
|
|
8
|
+
@notice:: notice message
|
|
9
|
+
@error:: error message
|
|
10
|
+
|
|
11
|
+
= SEE ALSO
|
|
12
|
+
|
|
13
|
+
_form.html.erb:: record form
|
|
14
|
+
%>
|
|
15
|
+
|
|
16
|
+
<div id=edgarj_search_form> <!-- edgarj/_search_form.html.erb -->
|
|
17
|
+
<% if @search.errors.any? %>
|
|
18
|
+
<div class="error_explanation">
|
|
19
|
+
<h2><%= t('error.search_form.title', count: @search.errors.count) %></h2>
|
|
20
|
+
<ul>
|
|
21
|
+
<% @search.errors.full_messages.each do |msg| %>
|
|
22
|
+
<li><%= msg %></li>
|
|
23
|
+
<% end %>
|
|
24
|
+
</ul>
|
|
25
|
+
</div>
|
|
26
|
+
<% end %>
|
|
27
|
+
<%= form_for(@search, url: {action: 'search'}, html: {
|
|
28
|
+
id: '_edgarj_search_form',
|
|
29
|
+
remote: true,
|
|
30
|
+
method: :get}) do |f| %>
|
|
31
|
+
<%= f.fields_for(@search._operator) do |o| %>
|
|
32
|
+
<%= Edgarj::FormDrawer::Search.new(@drawer, @search, f, o).draw() %>
|
|
33
|
+
<%# to avoid submit on 1-textfield form when hit [ENTER] key: %>
|
|
34
|
+
<input type="text" name="dummy" style="visibility:hidden" size=0>
|
|
35
|
+
<% end %>
|
|
36
|
+
<% end %>
|
|
37
|
+
<%= draw_search_form_buttons %>
|
|
38
|
+
|
|
39
|
+
<%= javascript_tag do %>
|
|
40
|
+
<% id = 'edgarj_search_operator_selection' %>
|
|
41
|
+
$('div.search_operator').contextMenu('<%= id -%>', {
|
|
42
|
+
bindings: {
|
|
43
|
+
'<%= id + '_eq' %>': function(target){
|
|
44
|
+
Edgarj.OperatorSelection.on_select(target, '=');
|
|
45
|
+
},
|
|
46
|
+
'<%= id + '_ne' %>': function(target){
|
|
47
|
+
Edgarj.OperatorSelection.on_select(target, '<>');
|
|
48
|
+
},
|
|
49
|
+
'<%= id + '_gt' %>': function(target){
|
|
50
|
+
Edgarj.OperatorSelection.on_select(target, '>');
|
|
51
|
+
},
|
|
52
|
+
'<%= id + '_ge' %>': function(target){
|
|
53
|
+
Edgarj.OperatorSelection.on_select(target, '>=');
|
|
54
|
+
},
|
|
55
|
+
'<%= id + '_lt' %>': function(target){
|
|
56
|
+
Edgarj.OperatorSelection.on_select(target, '<');
|
|
57
|
+
},
|
|
58
|
+
'<%= id + '_le' %>': function(target){
|
|
59
|
+
Edgarj.OperatorSelection.on_select(target, '<=');
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
<% end %>
|
|
64
|
+
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Template for search_operator() helper.
|
|
3
|
+
|
|
4
|
+
=== INPUTS
|
|
5
|
+
o:: operator form builder
|
|
6
|
+
method:: attribute name of the object
|
|
7
|
+
%>
|
|
8
|
+
|
|
9
|
+
<%
|
|
10
|
+
name = "edgarj_search_form[edgarj_search_form_operator][#{method.to_s}]"
|
|
11
|
+
id = name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
|
12
|
+
op = o.object.send(method).blank? ? '=' : o.object.send(method)
|
|
13
|
+
%>
|
|
14
|
+
<div id='<%= id + "_label" -%>' class='search_operator'>
|
|
15
|
+
<%= h(op) %>
|
|
16
|
+
</div>
|
|
17
|
+
<%= o.hidden_field method %>
|