Class: Edgarj::EdgarjController

Inherits:
ApplicationController
  • Object
show all
Includes:
ControllerMixinCommon, PermissionMixin
Defined in:
app/controllers/edgarj/edgarj_controller.rb

Overview

Generic CRUD(Create/Read/Update/Delte) controller with Ajax.

EdgarjController v.s. ApplicationController

When concreate controller (e.g. CustomersController) inherits EdgarjController, it has the following features:

  • CRUD with default form on Ajax

    • form can be customized.

  • QBE (Query By Example) search form

  • popup-selection on ‘belongs_to’ column

  • sort on list

  • saving search-conditions and reuse it

If these are not necessary, just inherits ApplicationController.

Tasks when adding new model which is handled under EdgarjController

For example, when want to add Post model:

  1. generate edgarj:scaffold:

    rails generate edgarj:scaffold post name:string body:text published:boolean
    
  2. add controller entry to CONTROLLER_MENU (config/initializers/edgarj.rb)

It will take about ~3 min.

For the detail of customization, please see:

http://sourceforge.net/apps/trac/jjedgarj/wiki/customize

Architecture

see architecture (OpenOffice Presentation)

Access Control

There are two dimentions of access control:

  1. Page level (Controller level) access control.

    • Edgarj::UserGroup with kind==ROLE and Edgarj::ModelPermission

      represents access control on each controller.
      
    • Admin user, who belongs to ‘admin’ user_group, can access any page.

    • When a user belongs to a user_group (kind==ROLE) and a model_permission belongs to the user_group, the user can access the controller which model is the model in model_permission.

    • More precisely, 4 kind of access controls, CREATE, READ, UPDATE, and DELETE can be set with any conbination on the controller.

    • See Edgarj::ModelPermission for more detail.

  2. Data scope.

    • data scope access is controlled by ‘user_scoped(user, context)’ scope defined at each model.

    • Where, user is currently accessing person to the model.

    • context is any kind of 2nd parameter. Default is session object of Edgarj::Sssn, but it can be overwritten ‘scope_context’ method at the target controller.

    • See Author.user_scoped as an example.

Naming Convention

  • ‘record’ is an instance of ‘Model’.

  • ‘drawer’ is an instance of ‘Drawer’ class.

Implementation Note

Why not to choose mixin rather than class is because it is easier to use edgarj’s view at client controller. For example, AuthorsController, which inherits from EdgarjController, can automatically use edgarj’s view by rails view-selection feature.

SEE ALSO

PopupController

‘belongs_to’ popup for EdgarjController

Constant Summary collapse

READ_ACTIONS =
%w(
index show clear search search_clear search_save search_load
zip_complete csv_download file_download map page_info_save)

Instance Method Summary collapse

Methods included from PermissionMixin

included

Methods included from ControllerMixinCommon

included, #page_info_save

Instance Method Details

#clearObject

Ajax method to clear form

Permission

ModelPermission::READ on this controller is required.



208
209
210
# File 'app/controllers/edgarj/edgarj_controller.rb', line 208

def clear
  @record = model.new
end

#createObject

save new record

Permission

ModelPermission::CREATE on this controller is required.



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'app/controllers/edgarj/edgarj_controller.rb', line 134

def create
  upsert do
    # NOTE: create!() is not used because assign to @record to draw form.
    # Otherwise, @record would be in nil so failure at edgarj/_form rendering.
    #
    # NOTE2: valid? after create() calls validate_on_update.  This is not
    # an expected behavior.  So, new, valid?, then save.
    @record = model.new(permitted_params(:create))
    @record_saved = @record     # copy for possible later use
    on_upsert
   #upsert_files
    raise ActiveRecord::RecordNotSaved if !@record.valid?
    @record.save

    # clear @record values for next data-entry
    @record = model.new
  end
end

#csv_downloadObject

download model under current condition

respond_to...format.csv approach was not used since @list is different as follows:

  • csv returns all of records under the conditions

  • HTML returns just in specified ‘page’.

Permission

ModelPermission::READ on this controller is required.

FIXME: file.close(true) deletes files BEFORE actually send file so that comment it out. Need to clean these work files.



329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
# File 'app/controllers/edgarj/edgarj_controller.rb', line 329

def csv_download
  dir = '/tmp/edgarj/csv_download'
  FileUtils.mkdir_p(dir)

  filename    = sprintf("%s-%s.csv",
                    model_name,
                    Time.now.strftime("%Y%m%d-%H%M%S"))
  file        = Tempfile.new(filename, dir)
  csv_visitor = EdgarjHelper::CsvVisitor.new(view_context)
  file.write CSV.generate_line(model.columns.map{|c| c.name})
  for rec in user_scoped.where(page_info.record.conditions).
      order(
        page_info.order_by.blank? ?
          nil :
          page_info.order_by + ' ' + page_info.dir) do
    array = []
    for col in model.columns do
      array << csv_visitor.visit_column(rec, col)
    end
    file.write CSV.generate_line(array)
  end
  file.close
  send_file(file.path, {
      type:     'text/csv',
      filename: filename})
 #file.close(true)
end

#destroyObject

Permission

ModelPermission::DELETE on this controller is required.



193
194
195
196
197
198
199
200
201
# File 'app/controllers/edgarj/edgarj_controller.rb', line 193

def destroy
  m = model.find(user_scoped.find(params[:id]).id)
  @record_saved = m     # copy for possible later use
  m.destroy

  prepare_list
  @record = model.new
  @flash_notice = t('delete_ok')
end

#file_downloadObject

To prevent unassociated file access, do:

  1. check if it is in model object

  2. check if it is a edgarj_file column

Permission

ModelPermission::READ on this controller is required.



364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
# File 'app/controllers/edgarj/edgarj_controller.rb', line 364

def file_download
  if !model.edgarj_file?(params[:column])
    flash[:error] = t('edgarj_file.no_assoc')
    return
  end

  file_info_id = user_scoped.find(params[:id]).send(params[:column])
  if file_info_id
    file_info = FileInfo.find(file_info_id)
    if file_info
      send_file(file_info.full_filename, :filename => file_info.filename)
      return
    end
  end
  logger.warn 'invalid file_info'
end

#indexObject

draw search result in whole page. default update DOM id and template is as follows:

DOM id

‘edgarj_list’

template

‘edgarj/list’

However, these can be replaced by params and params

Permission

ModelPermission::READ on this controller is required.

SEE ALSO

popup()

draw popup



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'app/controllers/edgarj/edgarj_controller.rb', line 112

def index
  page_info

  # update @page_info.page when page is specified.
  # Otherwise, reset page to 1.
  #
  # Just set, not save.  It will be done later when saving sssn with
  # 'has_many page_infos ... autosave: true'
  @page_info.page = (params[:page] || 1)

 #clear_topic_path
  prepare_list

  @search = page_info.record
  @record = model.new
end

#mapObject

draw Google map.

Permission

ModelPermission::READ on this controller is required.



386
387
388
# File 'app/controllers/edgarj/edgarj_controller.rb', line 386

def map
  render :template=>'edgarj/map', :layout=>false
end

#searchObject

Ajax method to execute search

Actually, this doesn’t execute search. Rather, this just saves condition. Search will be executed at any listing action like ‘index’, ‘create’, or ‘update’.

Permission

ModelPermission::READ on this controller is required.



221
222
223
224
225
226
227
228
# File 'app/controllers/edgarj/edgarj_controller.rb', line 221

def search
  pi        = page_info
  pi.record = SearchForm.new(model, params[:edgarj_search_form])
  pi.page = 1
  pi.save!
  @search   = pi.record
  prepare_list if @search.valid?
end

#search_clearObject

Ajax method to clear search conditions

Permission

ModelPermission::READ on this controller is required.



235
236
237
# File 'app/controllers/edgarj/edgarj_controller.rb', line 235

def search_clear
  @search   = SearchForm.new(model)
end

#search_loadObject

Ajax method to load search condition, lines, order_by, dir, and page.



278
279
280
281
# File 'app/controllers/edgarj/edgarj_controller.rb', line 278

def search_load
  @search = current_user.saved_page_infos.find(params[:id]).load(@sssn).model
  draw_search_form
end

#search_saveObject

Ajax method to save search conditions

call flow

Edgarj.SearchSavePopup.open() (javascript)
 (show $('search_save_popup'))
  Edgarj.SearchSavePopup.submit() (javascript)
   (copy entered name into $('saved_page_info_name') in form)
    call :action=>'search_save'

TRICKY PART

There are two requirements:

  1. use modal-dialog to enter name to decrese busy in search form.

  2. send Search Form with name to server.

To comply these, Edgarj.SearchSavePopup copies the entered name to ‘saved_page_info_name’ hidden field and then sends the form which includes the copied name.

Permission

ModelPermission::READ on this controller is required.



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# File 'app/controllers/edgarj/edgarj_controller.rb', line 260

def search_save
  SavedVcontext.save(current_user, nil,
                     params[:saved_page_info_name], page_info)

  render :update do |page|
    page << "Edgarj.SearchSavePopup.close();"
    page.replace 'edgarj_load_condition_menu',
       :partial=>'edgarj/load_condition_menu'
  end
rescue ActiveRecord::ActiveRecordError
  app_rescue
  render :update do |page|
    page.replace_html 'search_save_popup_flash_error', :text=>t('save_fail')
  end
end

#showObject

Show detail of one record. Format of html & js should be supported.

Permission

ModelPermission::READ on this controller is required.



158
159
160
161
162
163
164
165
166
167
168
169
# File 'app/controllers/edgarj/edgarj_controller.rb', line 158

def show
  @record = user_scoped.find(params[:id])
 #add_topic_path
  respond_to do |format|
    format.html {
      prepare_list
      @search = page_info.record
      render :action=>'index'
    }
    format.js
  end
end

#topObject

This page is for following purposes:

  • top page which contain latest info (TBD)

  • any error message on HTML format access

    • on Ajax access, rather edgarj_error_popup is used



95
96
97
# File 'app/controllers/edgarj/edgarj_controller.rb', line 95

def top
  render :action => 'top'
end

#updateObject

save existence modified record

Permission

ModelPermission::UPDATE on this controller is required.



176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'app/controllers/edgarj/edgarj_controller.rb', line 176

def update
  upsert do
    # NOTE:
    # 1. update ++then++ valid to set new values in @record to draw form.
    # 1. user_scoped may have joins so that record could be
    #    ActiveRecord::ReadOnlyRecord so that's why access again from
    #    model.
    @record  = model.find(user_scoped.find(params[:id]).id)
   #upsert_files
    if !@record.update_attributes(permitted_params(:update))
      raise ActiveRecord::RecordInvalid.new(@record)
    end
  end
end