effective_classifieds 0.0.2

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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +122 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/effective_classifieds_manifest.js +3 -0
  6. data/app/assets/javascripts/effective_classifieds/base.js +0 -0
  7. data/app/assets/javascripts/effective_classifieds.js +1 -0
  8. data/app/assets/stylesheets/effective_classifieds/base.scss +0 -0
  9. data/app/assets/stylesheets/effective_classifieds.scss +1 -0
  10. data/app/controllers/admin/classified_submissions_controller.rb +19 -0
  11. data/app/controllers/admin/classifieds_controller.rb +20 -0
  12. data/app/controllers/effective/classified_submissions_controller.rb +31 -0
  13. data/app/controllers/effective/classifieds_controller.rb +53 -0
  14. data/app/datatables/admin/effective_classified_submissions_datatable.rb +22 -0
  15. data/app/datatables/admin/effective_classifieds_datatable.rb +61 -0
  16. data/app/datatables/effective_classified_submissions_datatable.rb +37 -0
  17. data/app/datatables/effective_classifieds_datatable.rb +25 -0
  18. data/app/helpers/effective_classifieds_helper.rb +7 -0
  19. data/app/mailers/effective/classifieds_mailer.rb +22 -0
  20. data/app/models/concerns/effective_classifieds_classified_submission.rb +112 -0
  21. data/app/models/effective/classified.rb +164 -0
  22. data/app/models/effective/classified_submission.rb +7 -0
  23. data/app/views/admin/classifieds/_form.html.haml +12 -0
  24. data/app/views/admin/classifieds/_form_access.html.haml +10 -0
  25. data/app/views/admin/classifieds/_form_classified.html.haml +3 -0
  26. data/app/views/effective/classified_submissions/_classified.haml +12 -0
  27. data/app/views/effective/classified_submissions/_classified_submission.html.haml +8 -0
  28. data/app/views/effective/classified_submissions/_content.html.haml +1 -0
  29. data/app/views/effective/classified_submissions/_dashboard.html.haml +28 -0
  30. data/app/views/effective/classified_submissions/_layout.html.haml +3 -0
  31. data/app/views/effective/classified_submissions/_summary.html.haml +30 -0
  32. data/app/views/effective/classified_submissions/classified.html.haml +17 -0
  33. data/app/views/effective/classified_submissions/start.html.haml +16 -0
  34. data/app/views/effective/classified_submissions/submitted.html.haml +13 -0
  35. data/app/views/effective/classified_submissions/summary.html.haml +8 -0
  36. data/app/views/effective/classifieds/_classified.html.haml +50 -0
  37. data/app/views/effective/classifieds/_dashboard.html.haml +8 -0
  38. data/app/views/effective/classifieds/_fields.html.haml +28 -0
  39. data/app/views/effective/classifieds/_form.html.haml +3 -0
  40. data/app/views/effective/classifieds/_layout.html.haml +1 -0
  41. data/app/views/effective/classifieds/_spacer.html.haml +1 -0
  42. data/app/views/effective/classifieds/index.html.haml +8 -0
  43. data/app/views/effective/classifieds/show.html.haml +8 -0
  44. data/app/views/effective/classifieds_mailer/classified_submitted.html.haml +16 -0
  45. data/app/views/layouts/effective_classifieds_mailer_layout.html.haml +7 -0
  46. data/config/effective_classifieds.rb +45 -0
  47. data/config/routes.rb +24 -0
  48. data/db/migrate/01_create_effective_classifieds.rb.erb +62 -0
  49. data/db/seeds.rb +1 -0
  50. data/lib/effective_classifieds/engine.rb +18 -0
  51. data/lib/effective_classifieds/version.rb +3 -0
  52. data/lib/effective_classifieds.rb +45 -0
  53. data/lib/generators/effective_classifieds/install_generator.rb +31 -0
  54. data/lib/generators/templates/effective_classifieds_mailer_preview.rb +4 -0
  55. data/lib/tasks/effective_classifieds_tasks.rake +8 -0
  56. metadata +251 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 4b6ec3f5b89b358551558d22206f8ce57ef5002f199a635c9e004155a12ff246
4
+ data.tar.gz: 0c36fa15a0dd1ae22c8cd74fef90e8b28bd6bec2e74efe2950c3bb32ee24e999
5
+ SHA512:
6
+ metadata.gz: 1377dab4d35fc6f2a0c160f006246380cf3af0adabd97100e5b78c61d5c2402c274d747c7eee3ca4126317fe38efc648dc142a549e0be7e31e2f257d4bcbb937
7
+ data.tar.gz: 2043dfe2ce40bd423fe50c5c80b2c44be329d28c53504c820bb009188382ccb8cdce2596deaf8d374f97786a8cbf09270c8465cb8067edbdaea1a2a1ae0b440a
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2022 Code and Effect Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # Effective Classifieds
2
+
3
+ Submit classified ads for job openings and equipment sales.
4
+
5
+ ## Getting Started
6
+
7
+ This requires Rails 6+ and Twitter Bootstrap 4 and just works with Devise.
8
+
9
+ Please first install the [effective_datatables](https://github.com/code-and-effect/effective_datatables) gem.
10
+
11
+ Please download and install the [Twitter Bootstrap4](http://getbootstrap.com)
12
+
13
+ Add to your Gemfile:
14
+
15
+ ```ruby
16
+ gem 'haml-rails' # or try using gem 'hamlit-rails'
17
+ gem 'effective_classifieds'
18
+ ```
19
+
20
+ Run the bundle command to install it:
21
+
22
+ ```console
23
+ bundle install
24
+ ```
25
+
26
+ Then run the generator:
27
+
28
+ ```ruby
29
+ rails generate effective_classifieds:install
30
+ ```
31
+
32
+ The generator will install an initializer which describes all configuration options and creates a database migration.
33
+
34
+ If you want to tweak the table names, manually adjust both the configuration file and the migration now.
35
+
36
+ Then migrate the database:
37
+
38
+ ```ruby
39
+ rake db:migrate
40
+ ```
41
+
42
+ Please add the following to your User model:
43
+
44
+ ```
45
+ effective_classifieds_user
46
+
47
+ Use the following datatables to display to your user their applicants dues:
48
+
49
+ ```haml
50
+ %h2 My Classifieds
51
+ - datatable = EffectiveClassifiedsDatatable.new(self)
52
+ ```
53
+
54
+ and
55
+
56
+ ```
57
+ Add a link to the admin menu:
58
+
59
+ ```haml
60
+ - if can? :admin, :effective_classifieds
61
+ - if can? :index, Effective::Classified
62
+ = nav_link_to 'Classifieds', effective_classifieds.admin_classifieds_path
63
+ ```
64
+
65
+ ## Configuration
66
+
67
+ ## Authorization
68
+
69
+ All authorization checks are handled via the effective_resources gem found in the `config/initializers/effective_resources.rb` file.
70
+
71
+ ## Effective Roles
72
+
73
+ This gem works with effective roles for the representative roles.
74
+
75
+ Configure your `config/initializers/effective_roles.rb` something like this:
76
+
77
+ ```
78
+ ```
79
+
80
+ ## Permissions
81
+
82
+ The permissions you actually want to define are as follows (using CanCan):
83
+
84
+ ```ruby
85
+ can([:index, :show], Effective::Classified) { |classified| classified.published? }
86
+ can([:show, :edit, :update], Effective::Classified) { |classified| classified.owner == user }
87
+
88
+ can([:show, :index, :destroy], EffectiveClassifieds.ClassifiedSubmission) { |submission| submission.owner == user }
89
+ can([:update], EffectiveClassifieds.ClassifiedSubmission) { |submission| submission.owner == user && !submission.was_submitted? }
90
+
91
+ if user.admin?
92
+ can :admin, :effective_classifieds
93
+
94
+ can(crud - [:destroy], Classified)
95
+
96
+ can(:approve, Classified) { |classified| classified.was_submitted? && !classified.approved? }
97
+ can(:destroy, Classified) { |classified| !classified.draft? }
98
+
99
+ can([:new, :index, :show], EffectiveClassifieds.ClassifiedSubmission)
100
+ end
101
+ ```
102
+
103
+ ## License
104
+
105
+ MIT License. Copyright [Code and Effect Inc.](http://www.codeandeffect.com/)
106
+
107
+ ## Testing
108
+
109
+ Run tests by:
110
+
111
+ ```ruby
112
+ rails test
113
+ ```
114
+
115
+ ## Contributing
116
+
117
+ 1. Fork it
118
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
119
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
120
+ 4. Push to the branch (`git push origin my-new-feature`)
121
+ 5. Bonus points for test coverage
122
+ 6. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
9
+
10
+ require "rake/testtask"
11
+
12
+ Rake::TestTask.new(:test) do |t|
13
+ t.libs << 'test'
14
+ t.pattern = 'test/**/*_test.rb'
15
+ t.verbose = false
16
+ end
17
+
18
+ task default: :test
@@ -0,0 +1,3 @@
1
+ //= link_directory ../javascripts .js
2
+ //= link_directory ../stylesheets .css
3
+ //= link_tree ../images
@@ -0,0 +1 @@
1
+ //= require_tree ./effective_classifieds
@@ -0,0 +1 @@
1
+ @import 'effective_classifieds/base';
@@ -0,0 +1,19 @@
1
+ module Admin
2
+ class ClassifiedSubmissionsController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_classifieds) }
5
+
6
+ include Effective::CrudController
7
+
8
+ resource_scope -> { EffectiveEvents.ClassifiedSubmission.deep.all }
9
+ datatable -> { Admin::EffectiveClassifiedSubmissionsDatatable.new }
10
+
11
+ private
12
+
13
+ def permitted_params
14
+ model = (params.key?(:effective_classified_submission) ? :effective_classified_submission : :classified_submission)
15
+ params.require(model).permit!
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ module Admin
2
+ class ClassifiedsController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+ before_action { EffectiveResources.authorize!(self, :admin, :effective_classifieds) }
5
+
6
+ include Effective::CrudController
7
+
8
+ submit :save, 'Save'
9
+ submit :save, 'Save and View', redirect: -> { effective_classifieds.classified_path(resource) }
10
+ submit :approve, 'Approve'
11
+
12
+ private
13
+
14
+ def permitted_params
15
+ model = (params.key?(:effective_classified) ? :effective_classified : :classified)
16
+ params.require(model).permit!
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ module Effective
2
+ class ClassifiedSubmissionsController < ApplicationController
3
+ before_action(:authenticate_user!) if defined?(Devise)
4
+
5
+ include Effective::WizardController
6
+
7
+ resource_scope -> { EffectiveClassifieds.ClassifiedSubmission.deep.where(owner: current_user) }
8
+
9
+ # Allow only 1 in-progress application at a time
10
+ before_action(only: [:new, :show], unless: -> { resource&.done? }) do
11
+ existing = resource_scope.in_progress.where.not(id: resource).first
12
+
13
+ if existing.present?
14
+ flash[:success] = "You have been redirected to your existing in progress classified submission"
15
+ redirect_to effective_classifieds.classified_submission_build_path(existing, existing.next_step)
16
+ end
17
+ end
18
+
19
+ after_save do
20
+ flash.now[:success] = ''
21
+ end
22
+
23
+ private
24
+
25
+ def permitted_params
26
+ model = (params.key?(:effective_classified_submission) ? :effective_classified_submission : :classified_submission)
27
+ params.require(model).permit!.except(:status, :status_steps, :wizard_steps, :submitted_at)
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,53 @@
1
+ module Effective
2
+ class ClassifiedsController < ApplicationController
3
+ include Effective::CrudController
4
+
5
+ resource_scope -> {
6
+ unpublished = EffectiveResources.authorized?(self, :admin, :effective_classifieds)
7
+ Effective::Classified.classifieds(user: current_user, unpublished: unpublished)
8
+ }
9
+
10
+ def index
11
+ @classifieds ||= resource_scope.published
12
+
13
+ @classifieds = @classifieds.paginate(page: params[:page])
14
+
15
+ # if params[:search].present?
16
+ # search = params[:search].permit(EffectiveClassifieds.permitted_params).delete_if { |k, v| v.blank? }
17
+ # @classifieds = @classifieds.where(search) if search.present?
18
+ # end
19
+
20
+ EffectiveResources.authorize!(self, :index, Effective::Classified)
21
+
22
+ @page_title ||= ['Classifieds', (" - Page #{params[:page]}" if params[:page])].compact.join
23
+ end
24
+
25
+ def show
26
+ @classified = resource_scope.find(params[:id])
27
+
28
+ if @classified.respond_to?(:roles_permit?)
29
+ raise Effective::AccessDenied.new('Access Denied', :show, @classified) unless @classified.roles_permit?(current_user)
30
+ end
31
+
32
+ EffectiveResources.authorize!(self, :show, @classified)
33
+
34
+ if EffectiveResources.authorized?(self, :admin, :effective_classifieds)
35
+ flash.now[:warning] = [
36
+ 'Hi Admin!',
37
+ ('You are viewing a hidden classified.' unless @classified.published?),
38
+ 'Click here to',
39
+ ("<a href='#{effective_classifieds.edit_admin_classified_path(@classified)}' class='alert-link'>edit classified settings</a>.")
40
+ ].compact.join(' ')
41
+ end
42
+
43
+ @page_title ||= @classified.to_s
44
+ end
45
+
46
+ private
47
+
48
+ def permitted_params
49
+ params.require(:effective_classified).permit!.except(:status, :status_steps)
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,22 @@
1
+ class Admin::EffectiveClassifiedSubmissionsDatatable < Effective::Datatable
2
+ datatable do
3
+ order :created_at
4
+
5
+ col :token, visible: false
6
+
7
+ col :created_at, label: 'Created', visible: false
8
+ col :updated_at, label: 'Updated', visible: false
9
+
10
+ col :submitted_at, label: 'Submitted', visible: false, as: :date
11
+
12
+ col :classified, search: :string
13
+ col :owner
14
+
15
+ actions_col
16
+ end
17
+
18
+ collection do
19
+ EffectiveSubmissions.ClassifiedSubmission.all.deep.done.joins(:classified)
20
+ end
21
+
22
+ end
@@ -0,0 +1,61 @@
1
+ module Admin
2
+ class EffectiveClassifiedsDatatable < Effective::Datatable
3
+ filters do
4
+ scope :all
5
+
6
+ unless EffectiveClassifieds.auto_approve
7
+ scope :submitted
8
+ scope :approved
9
+ end
10
+
11
+ scope :published
12
+ end
13
+
14
+ datatable do
15
+
16
+ col :updated_at, visible: false
17
+ col :created_at, visible: false
18
+
19
+ col :id, visible: false
20
+
21
+ col :classified_submission, visible: false, search: :string
22
+
23
+ col :start_on, as: :date
24
+ col :end_on, as: :date
25
+
26
+ if categories.present?
27
+ col :category, search: categories
28
+ end
29
+
30
+ col :title
31
+ col :body
32
+ col :slug, visible: false
33
+
34
+ col :organization
35
+ col :location
36
+
37
+ col :website
38
+ col :email
39
+ col :phone
40
+
41
+ col :archived
42
+
43
+ unless EffectiveClassifieds.auto_approve
44
+ col :status, search: ['submitted', 'approved']
45
+ end
46
+
47
+ actions_col do |classified|
48
+ dropdown_link_to('View Classified', effective_classifieds.classified_path(classified), target: '_blank')
49
+ end
50
+ end
51
+
52
+ collection do
53
+ Effective::Classified.all.where.not(status: :draft)
54
+ end
55
+
56
+ def categories
57
+ EffectiveClassifieds.categories
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,37 @@
1
+ # Dashboard Classified Submissions
2
+ class EffectiveClassifiedSubmissionsDatatable < Effective::Datatable
3
+ datatable do
4
+ order :created_at
5
+
6
+ col :token, visible: false
7
+ col :created_at, visible: false
8
+
9
+ col :submitted_at do |submission|
10
+ submission.submitted_at&.strftime('%F') || 'Incomplete'
11
+ end
12
+
13
+ col :classified, search: :string
14
+
15
+ col :owner, visible: false, search: :string
16
+
17
+ col :status do |submission|
18
+ submission.classified&.status || submission.status
19
+ end
20
+
21
+ actions_col(actions: []) do |submission|
22
+ if submission.draft?
23
+ dropdown_link_to('Continue', effective_classifieds.classified_submission_build_path(submission, submission.next_step), 'data-turbolinks' => false)
24
+ else
25
+ dropdown_link_to('Show', effective_classifieds.classified_submission_path(submission))
26
+ dropdown_link_to('Edit', effective_classifieds.edit_classified_path(submission.classified)) if submission.classified
27
+ end
28
+
29
+ dropdown_link_to('Delete', effective_classifieds.classified_submission_path(submission), 'data-confirm': "Really delete #{submission}?", 'data-method': :delete)
30
+ end
31
+ end
32
+
33
+ collection do
34
+ EffectiveClassifieds.ClassifiedSubmission.deep.where(owner: current_user).left_joins(:classified)
35
+ end
36
+
37
+ end
@@ -0,0 +1,25 @@
1
+ # Dashboard Classifieds
2
+ class EffectiveClassifiedsDatatable < Effective::Datatable
3
+ datatable do
4
+ order :start_on
5
+
6
+ col :start_on, as: :date, label: 'Starts'
7
+ col :end_on, as: :date, label: 'Ends'
8
+
9
+ col :title
10
+ col :organization
11
+ col :location
12
+
13
+ col :body, visible: false
14
+ col :website, visible: false
15
+ col :email, visible: false
16
+ col :phone, visible: false
17
+
18
+ actions_col(edit: false)
19
+ end
20
+
21
+ collection do
22
+ Effective::Classified.classifieds(user: current_user)
23
+ end
24
+
25
+ end
@@ -0,0 +1,7 @@
1
+ module EffectiveClassifiedsHelper
2
+
3
+ def edit_effective_classified_submissions_wizard?
4
+ params[:controller] == 'effective/classified_submissions' && defined?(resource) && resource.draft?
5
+ end
6
+
7
+ end
@@ -0,0 +1,22 @@
1
+ module Effective
2
+ class ClassifiedsMailer < EffectiveClassifieds.parent_mailer_class
3
+
4
+ default from: -> { EffectiveClassifieds.mailer_sender }
5
+ layout -> { EffectiveClassifieds.mailer_layout || 'effective_classifieds_mailer_layout' }
6
+
7
+ def classified_submitted(resource, opts = {})
8
+ raise('expected an Effective::Classification') unless resource.kind_of?(Effective::Classified)
9
+
10
+ @classified = resource
11
+
12
+ mail(to: EffectiveClassifieds.mailer_admin, **headers_for(resource, opts))
13
+ end
14
+
15
+ protected
16
+
17
+ def headers_for(resource, opts = {})
18
+ resource.respond_to?(:log_changes_datatable) ? opts.merge(log: resource) : opts
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ # EffectiveClassifiedsClassifiedSubmission
4
+ #
5
+ # Mark your owner model with effective_classifieds_classified_submission to get all the includes
6
+
7
+ module EffectiveClassifiedsClassifiedSubmission
8
+ extend ActiveSupport::Concern
9
+
10
+ module Base
11
+ def effective_classifieds_classified_submission
12
+ include ::EffectiveClassifiedsClassifiedSubmission
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+ def effective_classifieds_classified_submission?; true; end
18
+
19
+ def all_wizard_steps
20
+ const_get(:WIZARD_STEPS).keys
21
+ end
22
+
23
+ def required_wizard_steps
24
+ [:start, :summary, :submitted]
25
+ end
26
+ end
27
+
28
+ included do
29
+ acts_as_tokened
30
+
31
+ acts_as_statused(
32
+ :draft, # Just Started
33
+ :submitted # All done
34
+ )
35
+
36
+ acts_as_wizard(
37
+ start: 'Start',
38
+ classified: 'Classified',
39
+ summary: 'Review',
40
+ submitted: 'Submitted'
41
+ )
42
+
43
+ log_changes(except: :wizard_steps) if respond_to?(:log_changes)
44
+
45
+ # Application Namespace
46
+ belongs_to :owner, polymorphic: true
47
+ accepts_nested_attributes_for :owner
48
+
49
+ # Effective Namespace
50
+ has_one :classified, class_name: 'Effective::Classified', inverse_of: :classified_submission, dependent: :destroy
51
+ accepts_nested_attributes_for :classified, reject_if: :all_blank, allow_destroy: true
52
+
53
+ effective_resource do
54
+ # Acts as Statused
55
+ status :string, permitted: false
56
+ status_steps :text, permitted: false
57
+
58
+ # Dates
59
+ submitted_at :datetime
60
+
61
+ # Acts as Wizard
62
+ wizard_steps :text, permitted: false
63
+
64
+ timestamps
65
+ end
66
+
67
+ scope :deep, -> { includes(:classified) }
68
+ scope :sorted, -> { order(:id) }
69
+
70
+ scope :in_progress, -> { where.not(status: [:submitted]) }
71
+ scope :done, -> { where(status: [:submitted]) }
72
+
73
+ scope :for, -> (user) { where(owner: user) }
74
+
75
+ # All Steps validations
76
+ validates :owner, presence: true
77
+
78
+ # Classified Step
79
+ with_options(if: -> { current_step == :classified }) do
80
+ validates :classified, presence: true
81
+ end
82
+ end
83
+
84
+ # Instance Methods
85
+ def to_s
86
+ 'classified submission'
87
+ end
88
+
89
+ def in_progress?
90
+ draft?
91
+ end
92
+
93
+ def done?
94
+ submitted?
95
+ end
96
+
97
+ # Called on the Review / Summary Step
98
+ # But we actually want to submit it
99
+ def summary!
100
+ submit!
101
+ end
102
+
103
+ def submit!
104
+ raise('already submitted') if was_submitted?
105
+
106
+ classified.submit! unless classified.was_submitted?
107
+
108
+ wizard_steps[:submitted] = Time.zone.now
109
+ submitted!
110
+ end
111
+
112
+ end