Class: CmseditController

Inherits:
DcApplicationController show all
Defined in:
app/controllers/cmsedit_controller.rb

Overview

This is main controller for processing actions by DRG forms. It provides CRUD actions for editing MongoDB documents. DRG CMS does not require controller to be made for every document model but centers all actions into single controller. Logic required to control data entry is provided within DRG forms which are loaded dynamically for every action.

Most of data entry controls must therefore be done in document models definitions. And there are controls that cannot be done in document models. Like controls which include url parameters or accessing session variables. This is hard to be done in model therefore cmsedit_controls had to be invented. cmsedit_controls are modules with methods that are injected into cmsedit_controller and act in runtime like they are part of cmsedit_controller.

Since Ruby and Rails provide some automagic loading of modules DRG CMS controls must be saved into app/controllers/drgcms_controls folder. Every model can have its own controls file. dc_page model’s controls live in dc_page_controls.rb file. If model has embedded document its control’s would be found in model_embedded_controls.rb. By convention module names are declared in camel case, so our dc_page_controls.rb declares DrgcmsControls::DcPageControls module.

Controls (among other) may contain 6 fixed callback methods. These methods are:

  • dc_new_record

  • dc_before_edit

  • dc_before_save

  • dc_after_save

  • dc_before_delete

  • dc_after_delete

Methods dc_before_edit, before_save or before_delete may also effect flow of the application. If method return false (not nil but FalseClass) normal flow of the program is interrupted and last operation is canceled.

Second control methods that can be declared in DRG CMS controls are filters for viewing and sorting documents. It is often required that dynamic filters are applied to result_set documents.

result_set:
  filter: current_users_documents

Example implemented controls method:

def current_users_documents
  if dc_user_can(DcPermission::CAN_READ)
    dc_page.where(created_by: session[:user_id])
  else
    flash[:error] = 'You can not perform this operation!'
    nil
  end
end

If filter method returns false user will be presented with flash error.

Instance Method Summary collapse

Methods inherited from DcApplicationController

#dc_dump, #dc_edit_mode?, #dc_find_form_file, #dc_get_site, #dc_log_visit, #dc_render_404, #dc_user_has_role, #set_page_title

Instance Method Details

#createObject

Create (or duplicate) action. Action is also used for turning filter on.



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
# File 'app/controllers/cmsedit_controller.rb', line 260

def create
# abusing create for turning filter on

  return index if params[:filter].to_s == 'on'
# not authorized

  unless dc_user_can(DcPermission::CAN_CREATE)
    flash[:error] = t('drgcms.not_authorized')
    return index
  end
#

  if params['id'].nil? # create record

# Prevent double form submit

    params[:form_time_stamp] = params[:form_time_stamp].to_i
    session[:form_time_stamp] ||= 0
    return index if params[:form_time_stamp] <= session[:form_time_stamp]
    session[:form_time_stamp] = params[:form_time_stamp]
#    

    create_new_empty_record
    if save_data
      flash[:info] = t('drgcms.doc_saved')
      params[:return_to] = 'index' if params[:commit] == t('drgcms.save&back') # save & back

      return process_return_to(params[:return_to]) if params[:return_to]
      
      @parms['id']     = @record.id     # must be set, for proper update link

      params[:id]      = @record.id     # must be set, for find_record

      edit
    else # error

      return process_return_to(params[:return_to]) if params[:return_to]
      render action: :new
    end
  else # duplicate record

    find_record
    params['dup_fields'] += ',' if params['dup_fields'] # for easier field matching

    new_doc = duplicate_document(@record)
    create_new_empty_record(new_doc)
    update_standards()
    @record.save!

    index
  end
end

#destroyObject

Destroy action. Used also for enabling and disabling record.



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# File 'app/controllers/cmsedit_controller.rb', line 354

def destroy
  find_record
# Which permission is required to delete 

  permission = if params['operation'].nil?
    if @record.respond_to?('created_by') # needs can_delete_all if created_by is present and not owner

      (@record.created_by == session[:user_id]) ? DcPermission::CAN_DELETE : DcPermission::CAN_DELETE_ALL
    else
      DcPermission::CAN_DELETE    # by default

    end
  else # enable or disable record

    if @record.respond_to?('created_by')
      (@record.created_by == session[:user_id]) ? DcPermission::CAN_EDIT : DcPermission::CAN_EDIT_ALL
    else
      DcPermission::CAN_EDIT      # by default

    end
  end
  ok2delete = dc_user_can(permission)
#

  case
# not authorized    

  when !ok2delete then
    flash[:error] = t('drgcms.not_authorized')
    return index
    
  when params['operation'].nil? then
# Process before delete callback

    if (m = callback_method('before_delete') )
      ret = call_callback_method(m)
# Don't do anything if return is false

      return index if ret.class == FalseClass
    end
# document deleted

    if @record.destroy
      save_journal(:delete)
      flash[:info] = t('drgcms.record_deleted')
# Process after delete callback

      if (m = callback_method('after_delete') ) 
        call_callback_method(m)
      elsif params['after-delete'].to_s.match('return_to')
        params[:return_to] = params['after-delete']
      end
# Process return_to link

      return process_return_to(params[:return_to]) if params[:return_to]
    else
      flash[:error] = dc_error_messages_for(@record)
    end
    return index
    
# deaktivate document    

  when params['operation'] == 'disable' then
    if @record.respond_to?('active')
      @record.active = false
      save_journal(:update, @record.changes)
      update_standards()
      @record.save
      flash[:info] = t('drgcms.doc_disabled')
    end
    
# reaktivate document

  when params['operation'] == 'enable' then
    if @record.respond_to?('active')
      @record.active = true
      update_standards()
      save_journal(:update, @record.changes)
      @record.save
      flash[:info] = t('drgcms.doc_enabled')
    end

# reorder documents

  when params['operation'] == 'reorder' then

  end
#

  @parms['action'] = 'update'
  render action: :edit
end

#duplicate_document(source) ⇒ Object

Will create duplicate document of source document. This method is used for duplicating document and is called from create action.



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'app/controllers/cmsedit_controller.rb', line 235

def duplicate_document(source)
  dest = {}
  source.attribute_names.each do |attribute_name|
    next if attribute_name == '_id' # don't duplicate _id

# if duplicate, string must be added. Useful for unique attributes

    add_duplicate = params['dup_fields'].to_s.match(attribute_name + ',')
    dest[attribute_name] = source[attribute_name]
    dest[attribute_name] << ' dup' if add_duplicate
  end
# embedded documents

  source.embedded_relations.keys.each do |embedded_name|
    next if source[embedded_name].nil? # it happens

    dest[embedded_name] = []
    source[embedded_name].each do |embedded|
      dest[embedded_name] << duplicate_embedded(embedded)
    end
  end
  dest['created_at'] = Time.now if dest['created_at']
  dest['updated_at'] = Time.now if dest['updated_at']
  dest
end

#duplicate_embedded(source) ⇒ Object

Duplicate embedded document. Since embedded documents are returned differently then top level document. Subroutine of duplicate_socument.



209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'app/controllers/cmsedit_controller.rb', line 209

def duplicate_embedded(source) #:nodoc:

# TODO Works for two embedded levels. Dies with third and more levels.

  dest = {}
  source.each do |attribute_name, value|
    next if attribute_name == '_id' # don't duplicate _id

    if value.class == Array
      dest[attribute_name] = []
      value.each do |ar|
        dest[attribute_name] << duplicate_embedded(ar)
      end
    else      
# if duplicate string must be added. Useful for unique attributes

      add_duplicate = params['dup_fields'].to_s.match(attribute_name + ',')
      dest[attribute_name] = value
      dest[attribute_name] << ' dup' if add_duplicate
    end
  end
  dest['created_at'] = Time.now if dest['created_at']
  dest['updated_at'] = Time.now if dest['updated_at']
  dest
end

#editObject

Edit action.



304
305
306
307
308
309
310
311
312
313
# File 'app/controllers/cmsedit_controller.rb', line 304

def edit
  find_record
  if (m = callback_method('before_edit') )
    ret = call_callback_method(m)
# Don't do anything if return is false

    return index if ret.class == FalseClass
  end
  @parms['action'] = 'update'
  render action: :edit
end

#filterObject

Filter action.



103
104
105
# File 'app/controllers/cmsedit_controller.rb', line 103

def filter
  index
end

#indexObject

Index action



88
89
90
91
92
93
94
95
96
97
98
# File 'app/controllers/cmsedit_controller.rb', line 88

def index
  @form['result_set'] ||= {}
  redirected = (@form['table'] == 'dc_memory' ? process_in_memory : process_collections)
  return if redirected
#  

  call_callback_method(@form['result_set']['footer'] || 'dc_footer')
  respond_to do |format|
    format.html { render action:  :index }
    format.js   { render partial: :result }
  end
end

#loginObject

Login action. Used to login direct to CMS. It is mostly used when first time creating site and when something goes so wrong, that traditional login procedure is not available.

Login can be called directly with url site.com/cmsedit/login



131
132
133
134
135
136
137
138
# File 'app/controllers/cmsedit_controller.rb', line 131

def 
  if    params[:id] == 'test' then set_test_site
  elsif params[:ok]           then render action: 'login', layout: 'cms' 
  else
    session[:edit_mode] = 0
    render action: 'login', layout: 'cms'
  end
end

#logoutObject

Logout action. Used to logout direct from CMS.

Logout can be called directly with url site.com/cmsedit/logout



145
146
147
148
149
# File 'app/controllers/cmsedit_controller.rb', line 145

def logout 
  session[:edit_mode] = 0
  session[:user_id]   = nil
  render action: 'login', layout: 'cms'
end

#newObject

New action.



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'app/controllers/cmsedit_controller.rb', line 172

def new
# clear flash messages.

  flash[:error] = flash[:warning] = flash[:info] = nil 
  create_new_empty_record
  if (m = callback_method('before_new') )
    ret = call_callback_method(m)
# Don't do anything if return is false

    return index if ret.class == FalseClass
  end  
  table = @tables.last[1] + '.'
# initial values set on page

  if cookies[:record] and cookies[:record].size > 0
    Marshal.load(cookies[:record]).each do |k,v|
      k = k.to_s
      if k.match(table)
        field = k.split('.').last
        @record.send("#{field}=", v) if @record.respond_to?(field)
      end
    end
  end
# initial values set in url

  params.each do |k,v|
    if k.match(table)
      field = k.split('.').last
      @record.send("#{field}=", v) if @record.respond_to?(field)
    end
  end
# This is how we set default values for new record

  #dc_new_record() if respond_to?('dc_new_record') 

  if (m = callback_method('new_record') ) then call_callback_method(m)  end
  @parms['action'] = 'create'
end

#runObject

Run action



434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'app/controllers/cmsedit_controller.rb', line 434

def run
  control_name, method_name = params[:control].split('.')
  if method_name.nil?
    method_name  = control_name
    control_name = params[:table]   
  end
  extend_with_control_module(control_name)
  if respond_to?(method_name)
    respond_to do |format|
      format.json { send method_name }
      format.html { send method_name }
    end    
  else
    # Error message

    text = "Method #{method_name} not defined in #{control_name}_control"
    respond_to do |format|
      format.json { render json: {msg_error: text} }
      format.html { render plain: text }
    end    
  end

end

#set_test_siteObject

Logout action. Used to logout direct from CMS.

Logout can be called directly with url site.com/cmsedit/logout



156
157
158
159
160
161
162
163
164
165
166
167
# File 'app/controllers/cmsedit_controller.rb', line 156

def set_test_site 
  # only in development

  return dc_render_404 if !Rails.env.development?
  alias_site = DcSite.find_by(:name => params[:site])
  return dc_render_404 unless alias_site
  # update alias for  

  site           = DcSite.find_by(:name => 'test')
  site.alias_for = params[:site]
  site.save
  # redirect to root  

  redirect_to '/'
end

#showObject

Show displays record in readonly mode.



110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'app/controllers/cmsedit_controller.rb', line 110

def show
  find_record
  if (m = callback_method('before_show') )
    ret = call_callback_method(m)
# Don't do anything if return is false

    if ret.class == FalseClass
      @form['readonly'] = nil # must be

      return index 
    end
  end  

  render action: 'edit', layout: 'cms'
end

#updateObject

Update action.



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'app/controllers/cmsedit_controller.rb', line 318

def update
  find_record
# check if record was not updated in mean time

  if @record.respond_to?(:updated_at)
    if params[:last_updated_at].to_i != @record.updated_at.to_i
      flash[:error] = t('drgcms.updated_by_other')
      return render(action: :edit)
    end
  end
#   

  if dc_user_can(DcPermission::CAN_EDIT_ALL) or
    ( @record.respond_to?('created_by') and
      @record.created_by == session[:user_id] and
      dc_user_can(DcPermission::CAN_EDIT) )
#

    if save_data
      params[:return_to] = 'index' if params[:commit] == t('drgcms.save&back') # save & back

      @parms['action'] = 'update'
# Process return_to link

      return process_return_to(params[:return_to]) if params[:return_to]
    else
      # do not forget before_edit callback

      if m = callback_method('before_edit')
        call_callback_method(m)
      end
      return render action: :edit
    end
  else
    flash[:error] = t('drgcms.not_authorized')
  end
  edit
end