fat_free_crm 0.11.1 → 0.11.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fat_free_crm might be problematic. Click here for more details.

Files changed (179) hide show
  1. data/Gemfile +30 -12
  2. data/Gemfile.lock +131 -119
  3. data/Procfile +1 -1
  4. data/README.md +1 -1
  5. data/app/assets/images/notifications.png +0 -0
  6. data/app/assets/javascripts/application.js.erb +3 -0
  7. data/app/assets/javascripts/crm_textarea_autocomplete.js +44 -0
  8. data/app/assets/stylesheets/application.css.erb +2 -0
  9. data/app/assets/stylesheets/common.scss +7 -11
  10. data/app/assets/stylesheets/textarea_autocomplete.scss +42 -0
  11. data/app/controllers/admin/application_controller.rb +5 -5
  12. data/app/controllers/admin/field_groups_controller.rb +11 -51
  13. data/app/controllers/admin/fields_controller.rb +13 -59
  14. data/app/controllers/admin/plugins_controller.rb +1 -4
  15. data/app/controllers/admin/settings_controller.rb +0 -4
  16. data/app/controllers/admin/tags_controller.rb +11 -66
  17. data/app/controllers/admin/users_controller.rb +20 -83
  18. data/app/controllers/application_controller.rb +83 -69
  19. data/app/controllers/comments_controller.rb +12 -29
  20. data/app/controllers/emails_controller.rb +1 -5
  21. data/app/controllers/entities/accounts_controller.rb +13 -32
  22. data/app/controllers/entities/campaigns_controller.rb +17 -32
  23. data/app/controllers/entities/contacts_controller.rb +20 -38
  24. data/app/controllers/entities/leads_controller.rb +33 -55
  25. data/app/controllers/entities/opportunities_controller.rb +26 -42
  26. data/app/controllers/entities_controller.rb +92 -83
  27. data/app/controllers/home_controller.rb +1 -10
  28. data/app/controllers/lists_controller.rb +1 -4
  29. data/app/controllers/{entities/tasks_controller.rb → tasks_controller.rb} +21 -32
  30. data/app/controllers/users_controller.rb +6 -5
  31. data/app/helpers/accounts_helper.rb +32 -9
  32. data/app/helpers/application_helper.rb +15 -1
  33. data/app/helpers/campaigns_helper.rb +1 -1
  34. data/app/helpers/comments_helper.rb +11 -1
  35. data/app/helpers/leads_helper.rb +1 -1
  36. data/app/helpers/opportunities_helper.rb +1 -1
  37. data/app/{models/mailers/notifier.rb → mailers/dropbox_mailer.rb} +5 -16
  38. data/app/mailers/subscription_mailer.rb +37 -0
  39. data/{lib/tasks/dropbox.rake → app/mailers/user_mailer.rb} +11 -13
  40. data/app/models/entities/account.rb +3 -1
  41. data/app/models/entities/campaign.rb +3 -1
  42. data/app/models/entities/contact.rb +3 -1
  43. data/app/models/entities/lead.rb +6 -5
  44. data/app/models/entities/opportunity.rb +3 -1
  45. data/app/models/fields/field.rb +1 -1
  46. data/app/models/polymorphic/comment.rb +34 -0
  47. data/app/models/{entities → polymorphic}/task.rb +16 -3
  48. data/app/models/setting.rb +15 -15
  49. data/app/models/users/ability.rb +12 -5
  50. data/app/models/users/user.rb +7 -2
  51. data/app/views/accounts/index.html.haml +1 -1
  52. data/app/views/accounts/index.js.rjs +1 -1
  53. data/app/views/admin/plugins/index.html.haml +1 -7
  54. data/app/views/{shared/auto_complete.html.haml → application/_auto_complete.html.haml} +0 -0
  55. data/app/views/{shared → application}/index.atom.builder +1 -1
  56. data/app/views/{shared → application}/index.rss.builder +1 -1
  57. data/app/views/campaigns/index.html.haml +1 -1
  58. data/app/views/campaigns/index.js.rjs +1 -1
  59. data/app/views/comments/_new.html.haml +6 -0
  60. data/app/views/comments/_subscription_links.html.haml +13 -0
  61. data/app/views/comments/new.js.rjs +2 -0
  62. data/app/views/contacts/_top_section.html.haml +3 -13
  63. data/app/views/contacts/index.html.haml +1 -1
  64. data/app/views/contacts/index.js.rjs +1 -1
  65. data/app/views/{notifier/dropbox_ack_notification.html.haml → dropbox_mailer/dropbox_notification.html.haml} +2 -2
  66. data/app/views/{shared → entities}/attach.js.rjs +1 -1
  67. data/app/views/entities/contacts.js.rjs +1 -1
  68. data/app/views/{shared/discard.rjs → entities/discard.js.rjs} +0 -0
  69. data/app/views/entities/leads.js.rjs +1 -1
  70. data/app/views/entities/opportunities.js.rjs +1 -1
  71. data/app/views/entities/subscription_update.js.rjs +4 -0
  72. data/app/views/entities/versions.js.rjs +1 -1
  73. data/app/views/layouts/_footer.html.haml +1 -1
  74. data/app/views/layouts/application.html.haml +3 -0
  75. data/app/views/leads/_contact.html.haml +1 -0
  76. data/app/views/leads/index.html.haml +1 -1
  77. data/app/views/leads/index.js.rjs +1 -1
  78. data/app/views/opportunities/_top_section.html.haml +4 -14
  79. data/app/views/opportunities/index.html.haml +1 -1
  80. data/app/views/opportunities/index.js.rjs +1 -1
  81. data/app/views/subscription_mailer/comment_notification.text.erb +7 -0
  82. data/app/views/{notifier → user_mailer}/password_reset_instructions.html.haml +0 -0
  83. data/config/application.rb +3 -1
  84. data/config/environments/development.rb +1 -1
  85. data/config/environments/test.rb +3 -0
  86. data/config/initializers/action_mailer.rb +8 -5
  87. data/config/initializers/cancan.rb +151 -0
  88. data/config/initializers/constants.rb +1 -0
  89. data/config/initializers/locale.rb +20 -0
  90. data/config/initializers/paper_trail.rb +4 -5
  91. data/config/initializers/relative_url_root.rb +0 -1
  92. data/config/initializers/squeel.rb +5 -0
  93. data/config/locales/cz_fat_free_crm.yml +3 -3
  94. data/config/locales/de.yml +2 -2
  95. data/config/locales/de_fat_free_crm.yml +651 -596
  96. data/config/locales/en-GB_fat_free_crm.yml +3 -3
  97. data/config/locales/en-US_fat_free_crm.yml +13 -3
  98. data/config/locales/es_fat_free_crm.yml +3 -3
  99. data/config/locales/fr-CA_fat_free_crm.yml +3 -3
  100. data/config/locales/fr_fat_free_crm.yml +3 -3
  101. data/config/locales/it_fat_free_crm.yml +3 -3
  102. data/config/locales/pl_fat_free_crm.yml +3 -3
  103. data/config/locales/pt-BR_fat_free_crm.yml +3 -3
  104. data/config/locales/ru_fat_free_crm.yml +3 -3
  105. data/config/locales/sv-SE_fat_free_crm.yml +3 -3
  106. data/config/locales/th_fat_free_crm.yml +3 -3
  107. data/config/routes.rb +10 -0
  108. data/config/settings.default.yml +29 -10
  109. data/config/unicorn.rb +4 -0
  110. data/db/migrate/20111201030535_add_field_groups_klass_name.rb +3 -1
  111. data/db/migrate/20120314080441_add_subscribed_users_to_entities.rb +23 -0
  112. data/db/migrate/20120405080727_change_subscribed_users_to_set.rb +24 -0
  113. data/db/migrate/20120405080742_change_further_subscribed_users_to_set.rb +27 -0
  114. data/db/migrate/20120413034923_add_index_on_versions_item_type.rb +5 -0
  115. data/db/schema.rb +109 -126
  116. data/fat_free_crm.gemspec +12 -18
  117. data/lib/fat_free_crm.rb +0 -1
  118. data/lib/fat_free_crm/core_ext/array.rb +1 -0
  119. data/lib/fat_free_crm/gem_dependencies.rb +1 -0
  120. data/lib/fat_free_crm/mail_processor/base.rb +226 -0
  121. data/lib/fat_free_crm/mail_processor/comment_replies.rb +86 -0
  122. data/lib/fat_free_crm/mail_processor/dropbox.rb +288 -0
  123. data/lib/fat_free_crm/permissions.rb +6 -19
  124. data/lib/fat_free_crm/renderers.rb +0 -8
  125. data/lib/fat_free_crm/tabs.rb +1 -1
  126. data/lib/fat_free_crm/version.rb +1 -1
  127. data/lib/plugins/country_select/lib/country_select.rb +2 -2
  128. data/lib/tasks/mail_processing.rake +60 -0
  129. data/spec/controllers/admin/users_controller_spec.rb +0 -2
  130. data/spec/controllers/{accounts_controller_spec.rb → entities/accounts_controller_spec.rb} +7 -9
  131. data/spec/controllers/{campaigns_controller_spec.rb → entities/campaigns_controller_spec.rb} +7 -7
  132. data/spec/controllers/{contacts_controller_spec.rb → entities/contacts_controller_spec.rb} +5 -9
  133. data/spec/controllers/{leads_controller_spec.rb → entities/leads_controller_spec.rb} +7 -9
  134. data/spec/controllers/{opportunities_controller_spec.rb → entities/opportunities_controller_spec.rb} +8 -15
  135. data/spec/controllers/tasks_controller_spec.rb +1 -5
  136. data/spec/controllers/users_controller_spec.rb +5 -9
  137. data/spec/factories/subscription_factories.rb +6 -0
  138. data/spec/lib/mail_processor/base_spec.rb +164 -0
  139. data/spec/lib/mail_processor/comment_replies_spec.rb +63 -0
  140. data/spec/lib/{dropbox_spec.rb → mail_processor/dropbox_spec.rb} +73 -181
  141. data/spec/lib/mail_processor/sample_emails/dropbox.rb +167 -0
  142. data/spec/mailers/subscription_mailer_spec.rb +17 -0
  143. data/spec/models/{base → entities}/account_contact_spec.rb +0 -0
  144. data/spec/models/{base → entities}/account_opportunity_spec.rb +0 -0
  145. data/spec/models/{base → entities}/account_spec.rb +4 -0
  146. data/spec/models/{base → entities}/campaign_spec.rb +4 -0
  147. data/spec/models/{base → entities}/contact_opportunity_spec.rb +0 -0
  148. data/spec/models/{base → entities}/contact_spec.rb +4 -0
  149. data/spec/models/{base → entities}/lead_spec.rb +4 -0
  150. data/spec/models/{base → entities}/opportunity_spec.rb +4 -0
  151. data/spec/models/polymorphic/comment_spec.rb +15 -0
  152. data/spec/models/{base → polymorphic}/task_spec.rb +124 -30
  153. data/spec/models/polymorphic/version_spec.rb +1 -1
  154. data/spec/shared/controllers.rb +5 -7
  155. data/spec/shared/models.rb +46 -0
  156. data/spec/spec_helper.rb +3 -4
  157. data/spec/support/mail_processor_mocks.rb +30 -0
  158. data/spec/support/uploaded_file.rb +3 -0
  159. data/spec/views/{common → application}/auto_complete.haml_spec.rb +1 -1
  160. data/vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  161. data/vendor/assets/images/jquery-ui/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  162. data/vendor/assets/images/jquery-ui/ui-bg_flat_10_000000_40x100.png +0 -0
  163. data/vendor/assets/images/jquery-ui/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  164. data/vendor/assets/images/jquery-ui/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  165. data/vendor/assets/images/jquery-ui/ui-bg_glass_65_ffffff_1x400.png +0 -0
  166. data/vendor/assets/images/jquery-ui/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  167. data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  168. data/vendor/assets/images/jquery-ui/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  169. data/vendor/assets/images/jquery-ui/ui-icons_222222_256x240.png +0 -0
  170. data/vendor/assets/images/jquery-ui/ui-icons_228ef1_256x240.png +0 -0
  171. data/vendor/assets/images/jquery-ui/ui-icons_ef8c08_256x240.png +0 -0
  172. data/vendor/assets/images/jquery-ui/ui-icons_ffd27a_256x240.png +0 -0
  173. data/vendor/assets/images/jquery-ui/ui-icons_ffffff_256x240.png +0 -0
  174. data/vendor/assets/javascripts/textarea_autocomplete.js +605 -0
  175. data/vendor/assets/stylesheets/jquery-ui.custom.css.erb +565 -0
  176. metadata +234 -154
  177. data/config/locales/simple_form.en.yml +0 -24
  178. data/lib/fat_free_crm/dropbox.rb +0 -439
  179. data/spec/lib/dropbox/email_samples.rb +0 -77
@@ -28,57 +28,51 @@ class LeadsController < EntitiesController
28
28
  # GET /leads/1
29
29
  #----------------------------------------------------------------------------
30
30
  def show
31
- @lead = Lead.my.find(params[:id])
32
-
33
31
  respond_with(@lead) do |format|
34
32
  format.html do
35
33
  @comment = Comment.new
36
34
  @timeline = timeline(@lead)
37
35
  end
38
36
  end
39
-
40
- rescue ActiveRecord::RecordNotFound
41
- respond_to_not_found(:html, :json, :xml)
42
37
  end
43
38
 
44
39
  # GET /leads/new
45
40
  #----------------------------------------------------------------------------
46
41
  def new
47
- @lead = Lead.new(:user => @current_user, :access => Setting.default_access)
42
+ @lead.attributes = {:user => @current_user, :access => Setting.default_access}
48
43
  @users = User.except(@current_user)
49
- @campaigns = Campaign.my.order("name")
44
+ get_campaigns
45
+
50
46
  if params[:related]
51
- model, id = params[:related].split("_")
52
- instance_variable_set("@#{model}", model.classify.constantize.my.find(id))
47
+ model, id = params[:related].split('_')
48
+ if related = model.classify.constantize.my.find_by_id(id)
49
+ instance_variable_set("@#{model}", related)
50
+ else
51
+ respond_to_related_not_found(model) and return
52
+ end
53
53
  end
54
- respond_with(@lead)
55
54
 
56
- rescue ActiveRecord::RecordNotFound # Kicks in if related asset was not found.
57
- respond_to_related_not_found(model, :js) if model
55
+ respond_with(@lead)
58
56
  end
59
57
 
60
58
  # GET /leads/1/edit AJAX
61
59
  #----------------------------------------------------------------------------
62
60
  def edit
63
- @lead = Lead.my.find(params[:id])
64
61
  @users = User.except(@current_user)
65
- @campaigns = Campaign.my.order("name")
62
+ get_campaigns
63
+
66
64
  if params[:previous].to_s =~ /(\d+)\z/
67
- @previous = Lead.my.find($1)
65
+ @previous = Lead.my.find_by_id($1) || $1.to_i
68
66
  end
69
- respond_with(@lead)
70
67
 
71
- rescue ActiveRecord::RecordNotFound
72
- @previous ||= $1.to_i
73
- respond_to_not_found(:js) unless @lead
68
+ respond_with(@lead)
74
69
  end
75
70
 
76
71
  # POST /leads
77
72
  #----------------------------------------------------------------------------
78
73
  def create
79
- @lead = Lead.new(params[:lead])
80
74
  @users = User.except(@current_user)
81
- @campaigns = Campaign.my.order("name")
75
+ get_campaigns
82
76
 
83
77
  respond_with(@lead) do |format|
84
78
  if @lead.save_with_permissions(params)
@@ -95,61 +89,48 @@ class LeadsController < EntitiesController
95
89
  # PUT /leads/1
96
90
  #----------------------------------------------------------------------------
97
91
  def update
98
- @lead = Lead.my.find(params[:id])
99
-
100
92
  respond_with(@lead) do |format|
101
93
  if @lead.update_with_permissions(params[:lead], params[:users])
102
94
  update_sidebar
103
95
  else
104
96
  @users = User.except(@current_user)
105
- @campaigns = Campaign.my.order("name")
97
+ @campaigns = Campaign.my.order('name')
106
98
  end
107
99
  end
108
-
109
- rescue ActiveRecord::RecordNotFound
110
- respond_to_not_found(:js, :json, :xml)
111
100
  end
112
101
 
113
102
  # DELETE /leads/1
114
103
  #----------------------------------------------------------------------------
115
104
  def destroy
116
- @lead = Lead.my.find(params[:id])
117
- @lead.destroy if @lead
105
+ @lead.destroy
118
106
 
119
107
  respond_with(@lead) do |format|
120
108
  format.html { respond_to_destroy(:html) }
121
109
  format.js { respond_to_destroy(:ajax) }
122
110
  end
123
-
124
- rescue ActiveRecord::RecordNotFound
125
- respond_to_not_found(:html, :js, :json, :xml)
126
111
  end
127
112
 
128
113
  # GET /leads/1/convert
129
114
  #----------------------------------------------------------------------------
130
115
  def convert
131
- @lead = Lead.my.find(params[:id])
132
116
  @users = User.except(@current_user)
133
117
  @account = Account.new(:user => @current_user, :name => @lead.company, :access => "Lead")
134
- @accounts = Account.my.order("name")
118
+ @accounts = Account.my.order('name')
135
119
  @opportunity = Opportunity.new(:user => @current_user, :access => "Lead", :stage => "prospecting", :campaign => @lead.campaign, :source => @lead.source)
120
+
136
121
  if params[:previous].to_s =~ /(\d+)\z/
137
- @previous = Lead.my.find($1)
122
+ @previous = Lead.my.find_by_id($1) || $1.to_i
138
123
  end
139
- respond_with(@lead)
140
124
 
141
- rescue ActiveRecord::RecordNotFound
142
- @previous ||= $1.to_i
143
- respond_to_not_found(:js, :json, :xml) unless @lead
125
+ respond_with(@lead)
144
126
  end
145
127
 
146
128
  # PUT /leads/1/promote
147
129
  #----------------------------------------------------------------------------
148
130
  def promote
149
- @lead = Lead.my.find(params[:id])
150
131
  @users = User.except(@current_user)
151
132
  @account, @opportunity, @contact = @lead.promote(params)
152
- @accounts = Account.my.order("name")
133
+ @accounts = Account.my.order('name')
153
134
  @stage = Setting.unroll(:opportunity_stage)
154
135
 
155
136
  respond_with(@lead) do |format|
@@ -161,33 +142,26 @@ class LeadsController < EntitiesController
161
142
  format.xml { render :xml => @account.errors + @opportunity.errors + @contact.errors, :status => :unprocessable_entity }
162
143
  end
163
144
  end
164
-
165
- rescue ActiveRecord::RecordNotFound
166
- respond_to_not_found(:js, :json, :xml)
167
145
  end
168
146
 
169
147
  # PUT /leads/1/reject
170
148
  #----------------------------------------------------------------------------
171
149
  def reject
172
- @lead = Lead.my.find(params[:id])
173
- @lead.reject if @lead
150
+ @lead.reject
174
151
  update_sidebar
175
152
 
176
153
  respond_with(@lead) do |format|
177
154
  format.html { flash[:notice] = t(:msg_asset_rejected, @lead.full_name); redirect_to leads_path }
178
155
  end
179
-
180
- rescue ActiveRecord::RecordNotFound
181
- respond_to_not_found(:html, :js, :json, :xml)
182
156
  end
183
157
 
184
158
  # PUT /leads/1/attach
185
159
  #----------------------------------------------------------------------------
186
- # Handled by ApplicationController :attach
160
+ # Handled by EntitiesController :attach
187
161
 
188
162
  # POST /leads/1/discard
189
163
  #----------------------------------------------------------------------------
190
- # Handled by ApplicationController :discard
164
+ # Handled by EntitiesController :discard
191
165
 
192
166
  # POST /leads/auto_complete/query AJAX
193
167
  #----------------------------------------------------------------------------
@@ -229,15 +203,19 @@ class LeadsController < EntitiesController
229
203
  # POST /leads/filter AJAX
230
204
  #----------------------------------------------------------------------------
231
205
  def filter
232
- session[:filter_by_lead_status] = params[:status]
206
+ session[:leads_filter] = params[:status]
233
207
  @leads = get_leads(:page => 1) # Start one the first page.
234
208
  render :index
235
209
  end
236
210
 
237
- private
211
+ private
212
+
213
+ #----------------------------------------------------------------------------
214
+ alias :get_leads :get_list_of_records
215
+
238
216
  #----------------------------------------------------------------------------
239
- def get_leads(options = {})
240
- get_list_of_records(Lead, options.merge!(:filter => :filter_by_lead_status))
217
+ def get_campaigns
218
+ @campaigns = Campaign.my.order('name')
241
219
  end
242
220
 
243
221
  #----------------------------------------------------------------------------
@@ -18,7 +18,7 @@
18
18
  class OpportunitiesController < EntitiesController
19
19
  before_filter :load_settings
20
20
  before_filter :get_data_for_sidebar, :only => :index
21
- before_filter :set_params, :only => [:index, :redraw, :filter]
21
+ before_filter :set_params, :only => [ :index, :redraw, :filter ]
22
22
 
23
23
  # GET /opportunities
24
24
  #----------------------------------------------------------------------------
@@ -30,58 +30,51 @@ class OpportunitiesController < EntitiesController
30
30
  # GET /opportunities/1
31
31
  #----------------------------------------------------------------------------
32
32
  def show
33
- @opportunity = Opportunity.my.find(params[:id])
34
-
35
33
  respond_with(@opportunity) do |format|
36
34
  format.html do
37
35
  @comment = Comment.new
38
36
  @timeline = timeline(@opportunity)
39
37
  end
40
38
  end
41
-
42
- rescue ActiveRecord::RecordNotFound
43
- respond_to_not_found(:html, :json, :xml)
44
39
  end
45
40
 
46
41
  # GET /opportunities/new
47
42
  #----------------------------------------------------------------------------
48
43
  def new
49
- @opportunity = Opportunity.new(:user => @current_user, :stage => "prospecting", :access => Setting.default_access)
44
+ @opportunity.attributes = {:user => @current_user, :stage => "prospecting", :access => Setting.default_access}
50
45
  @users = User.except(@current_user)
51
- @account = Account.new(:user => @current_user)
52
- @accounts = Account.my.order("name")
46
+ @account = Account.new(:user => @current_user, :access => Setting.default_access)
47
+ @accounts = Account.my.order('name')
48
+
53
49
  if params[:related]
54
- model, id = params[:related].split("_")
55
- instance_variable_set("@#{model}", model.classify.constantize.my.find(id))
50
+ model, id = params[:related].split('_')
51
+ if related = model.classify.constantize.my.find_by_id(id)
52
+ instance_variable_set("@#{model}", related)
53
+ else
54
+ respond_to_related_not_found(model) and return
55
+ end
56
56
  end
57
- respond_with(@opportunity)
58
57
 
59
- rescue ActiveRecord::RecordNotFound # Kicks in if related asset was not found.
60
- respond_to_related_not_found(model, :js) if model
58
+ respond_with(@opportunity)
61
59
  end
62
60
 
63
61
  # GET /opportunities/1/edit AJAX
64
62
  #----------------------------------------------------------------------------
65
63
  def edit
66
- @opportunity = Opportunity.my.find(params[:id])
67
64
  @users = User.except(@current_user)
68
65
  @account = @opportunity.account || Account.new(:user => @current_user)
69
- @accounts = Account.my.order("name")
66
+ @accounts = Account.my.order('name')
67
+
70
68
  if params[:previous].to_s =~ /(\d+)\z/
71
- @previous = Opportunity.my.find($1)
69
+ @previous = Opportunity.my.find_by_id($1) || $1.to_i
72
70
  end
73
- respond_with(@opportunity)
74
71
 
75
- rescue ActiveRecord::RecordNotFound
76
- @previous ||= $1.to_i
77
- respond_to_not_found(:js) unless @opportunity
72
+ respond_with(@opportunity)
78
73
  end
79
74
 
80
75
  # POST /opportunities
81
76
  #----------------------------------------------------------------------------
82
77
  def create
83
- @opportunity = Opportunity.new(params[:opportunity])
84
-
85
78
  respond_with(@opportunity) do |format|
86
79
  if @opportunity.save_with_account_and_permissions(params)
87
80
  if called_from_index_page?
@@ -94,7 +87,7 @@ class OpportunitiesController < EntitiesController
94
87
  end
95
88
  else
96
89
  @users = User.except(@current_user)
97
- @accounts = Account.my.order("name")
90
+ @accounts = Account.my.order('name')
98
91
  unless params[:account][:id].blank?
99
92
  @account = Account.find(params[:account][:id])
100
93
  else
@@ -113,8 +106,6 @@ class OpportunitiesController < EntitiesController
113
106
  # PUT /opportunities/1
114
107
  #----------------------------------------------------------------------------
115
108
  def update
116
- @opportunity = Opportunity.my.find(params[:id])
117
-
118
109
  respond_with(@opportunity) do |format|
119
110
  if @opportunity.update_with_account_and_permissions(params)
120
111
  if called_from_index_page?
@@ -126,7 +117,7 @@ class OpportunitiesController < EntitiesController
126
117
  end
127
118
  else
128
119
  @users = User.except(@current_user)
129
- @accounts = Account.my.order("name")
120
+ @accounts = Account.my.order('name')
130
121
  if @opportunity.account
131
122
  @account = Account.find(@opportunity.account.id)
132
123
  else
@@ -134,38 +125,31 @@ class OpportunitiesController < EntitiesController
134
125
  end
135
126
  end
136
127
  end
137
-
138
- rescue ActiveRecord::RecordNotFound
139
- respond_to_not_found(:js, :json, :xml)
140
128
  end
141
129
 
142
130
  # DELETE /opportunities/1
143
131
  #----------------------------------------------------------------------------
144
132
  def destroy
145
- @opportunity = Opportunity.my.find(params[:id])
146
133
  if called_from_landing_page?(:accounts)
147
134
  @account = @opportunity.account # Reload related account if any.
148
135
  elsif called_from_landing_page?(:campaigns)
149
136
  @campaign = @opportunity.campaign # Reload related campaign if any.
150
137
  end
151
- @opportunity.destroy if @opportunity
138
+ @opportunity.destroy
152
139
 
153
140
  respond_with(@opportunity) do |format|
154
141
  format.html { respond_to_destroy(:html) }
155
142
  format.js { respond_to_destroy(:ajax) }
156
143
  end
157
-
158
- rescue ActiveRecord::RecordNotFound
159
- respond_to_not_found(:html, :js, :json, :xml)
160
144
  end
161
145
 
162
146
  # PUT /opportunities/1/attach
163
147
  #----------------------------------------------------------------------------
164
- # Handled by ApplicationController :attach
148
+ # Handled by EntitiesController :attach
165
149
 
166
150
  # POST /opportunities/1/discard
167
151
  #----------------------------------------------------------------------------
168
- # Handled by ApplicationController :discard
152
+ # Handled by EntitiesController :discard
169
153
 
170
154
  # POST /opportunities/auto_complete/query AJAX
171
155
  #----------------------------------------------------------------------------
@@ -195,11 +179,10 @@ class OpportunitiesController < EntitiesController
195
179
  render :index
196
180
  end
197
181
 
198
- private
182
+ private
183
+
199
184
  #----------------------------------------------------------------------------
200
- def get_opportunities(options = {})
201
- get_list_of_records(Opportunity, options.merge!(:filter => :filter_by_opportunity_stage))
202
- end
185
+ alias :get_opportunities :get_list_of_records
203
186
 
204
187
  #----------------------------------------------------------------------------
205
188
  def respond_to_destroy(method)
@@ -241,10 +224,11 @@ class OpportunitiesController < EntitiesController
241
224
  @stage = Setting.unroll(:opportunity_stage)
242
225
  end
243
226
 
227
+ #----------------------------------------------------------------------------
244
228
  def set_params
245
229
  @current_user.pref[:opportunities_per_page] = params[:per_page] if params[:per_page]
246
230
  @current_user.pref[:opportunities_outline] = params[:outline] if params[:outline]
247
231
  @current_user.pref[:opportunities_sort_by] = Opportunity::sort_by_map[params[:sort_by]] if params[:sort_by]
248
- session[:filter_by_opportunity_stage] = params[:stage] if params[:stage]
232
+ session[:opportunities_filter] = params[:stage] if params[:stage]
249
233
  end
250
234
  end
@@ -18,119 +18,105 @@
18
18
  class EntitiesController < ApplicationController
19
19
  before_filter :require_user
20
20
  before_filter :set_current_tab, :only => [ :index, :show ]
21
- after_filter :update_recently_viewed, :only => :show
22
21
 
23
- respond_to :html, :only => [ :index, :show, :auto_complete ]
24
- respond_to :js
25
- respond_to :json, :xml, :except => :edit
26
- respond_to :atom, :csv, :rss, :xls, :only => :index
22
+ load_and_authorize_resource
27
23
 
28
- helper_method :search
24
+ after_filter :update_recently_viewed, :only => :show
29
25
 
30
- # Common auto_complete handler for all core controllers.
31
- #----------------------------------------------------------------------------
32
- def auto_complete
33
- @query = params[:auto_complete_query]
34
- @auto_complete = hook(:auto_complete, self, :query => @query, :user => @current_user)
35
- if @auto_complete.empty?
36
- @auto_complete = klass.my.text_search(@query).limit(10)
37
- else
38
- @auto_complete = @auto_complete.last
39
- end
40
- session[:auto_complete] = controller_name.to_sym
41
- respond_to do |format|
42
- format.any(:js, :html) { render "shared/auto_complete", :layout => nil }
43
- format.json { render :json => @auto_complete.inject({}){|h,a| h[a.id] = a.name; h } }
44
- end
45
- end
26
+ helper_method :entity, :entities, :search
46
27
 
47
28
  # Common attach handler for all core controllers.
48
29
  #----------------------------------------------------------------------------
49
30
  def attach
50
- model = klass.my.find(params[:id])
51
31
  @attachment = params[:assets].classify.constantize.find(params[:asset_id])
52
- @attached = model.attach!(@attachment)
53
- @account = model.reload if model.is_a?(Account)
54
- @campaign = model.reload if model.is_a?(Campaign)
55
-
56
- respond_to do |format|
57
- format.js { render "shared/attach" }
58
- format.json { render :json => model.reload }
59
- format.xml { render :xml => model.reload }
60
- end
32
+ @attached = entity.attach!(@attachment)
33
+ entity.reload
61
34
 
62
- rescue ActiveRecord::RecordNotFound
63
- respond_to_not_found(:html, :js, :json, :xml)
35
+ respond_with(entity)
64
36
  end
65
37
 
66
38
  # Common discard handler for all core controllers.
67
39
  #----------------------------------------------------------------------------
68
40
  def discard
69
- model = klass.my.find(params[:id])
70
41
  @attachment = params[:attachment].constantize.find(params[:attachment_id])
71
- model.discard!(@attachment)
72
- @account = model.reload if model.is_a?(Account)
73
- @campaign = model.reload if model.is_a?(Campaign)
74
-
75
- respond_to do |format|
76
- format.js { render "shared/discard" }
77
- format.json { render :json => model.reload }
78
- format.xml { render :xml => model.reload }
42
+ entity.discard!(@attachment)
43
+ entity.reload
44
+
45
+ respond_with(entity)
46
+ end
47
+
48
+ # Common subscribe handler for all core controllers.
49
+ #----------------------------------------------------------------------------
50
+ def subscribe
51
+ entity.subscribed_users += [current_user.id]
52
+ entity.save
53
+
54
+ respond_with(entity) do |format|
55
+ format.js { render 'subscription_update' }
79
56
  end
57
+ end
80
58
 
81
- rescue ActiveRecord::RecordNotFound
82
- respond_to_not_found(:html, :js, :json, :xml)
59
+ # Common unsubscribe handler for all core controllers.
60
+ #----------------------------------------------------------------------------
61
+ def unsubscribe
62
+ entity.subscribed_users -= [current_user.id]
63
+ entity.save
64
+
65
+ respond_with(entity) do |format|
66
+ format.js { render 'subscription_update' }
67
+ end
83
68
  end
84
69
 
85
70
  # GET /entities/contacts AJAX
86
71
  #----------------------------------------------------------------------------
87
72
  def contacts
88
- @entity = klass.my.find(params[:id])
89
73
  end
90
74
 
91
75
  # GET /entities/leads AJAX
92
76
  #----------------------------------------------------------------------------
93
77
  def leads
94
- @entity = klass.my.find(params[:id])
95
78
  end
96
79
 
97
80
  # GET /entities/opportunities AJAX
98
81
  #----------------------------------------------------------------------------
99
82
  def opportunities
100
- @entity = klass.my.find(params[:id])
101
83
  end
102
84
 
103
85
  # GET /entities/versions AJAX
104
86
  #----------------------------------------------------------------------------
105
87
  def versions
106
- @entity = klass.my.find(params[:id])
107
88
  end
108
89
 
109
- def timeline(asset)
110
- (asset.comments + asset.emails).sort { |x, y| y.created_at <=> x.created_at }
90
+ protected
91
+
92
+ #----------------------------------------------------------------------------
93
+ def entity=(entity)
94
+ instance_variable_set("@#{controller_name.singularize}", entity)
111
95
  end
112
96
 
113
- # Controller instance method that responds to /controlled/tagged/tag request.
114
- # It stores given tag as current query and redirect to index to display all
115
- # records tagged with the tag.
116
97
  #----------------------------------------------------------------------------
117
- def tagged
118
- self.send(:current_query=, "#" << params[:id]) unless params[:id].blank?
119
- redirect_to :action => "index"
98
+ def entity
99
+ instance_variable_get("@#{controller_name.singularize}")
120
100
  end
121
101
 
122
- def field_group
123
- if @tag = Tag.find_by_name(params[:tag].strip)
124
- if @field_group = FieldGroup.find_by_tag_id_and_klass_name(@tag.id, klass.to_s)
125
- @asset = klass.find_by_id(params[:asset_id]) || klass.new
126
- render 'fields/group' and return
127
- end
128
- end
129
- render :text => ''
102
+ #----------------------------------------------------------------------------
103
+ def entities=(entities)
104
+ instance_variable_set("@#{controller_name}", entities)
130
105
  end
131
106
 
132
- private
107
+ #----------------------------------------------------------------------------
108
+ def entities
109
+ instance_variable_get("@#{controller_name}") || klass.my
110
+ end
133
111
 
112
+ private
113
+
114
+ #----------------------------------------------------------------------------
115
+ def get_users
116
+ @users ||= User.except(current_user)
117
+ end
118
+
119
+ #----------------------------------------------------------------------------
134
120
  def search
135
121
  @search ||= begin
136
122
  search = klass.search(params[:q])
@@ -141,46 +127,69 @@ class EntitiesController < ApplicationController
141
127
 
142
128
  # Get list of records for a given model class.
143
129
  #----------------------------------------------------------------------------
144
- def get_list_of_records(klass, options = {})
145
- items = klass.name.tableize
130
+ def get_list_of_records(options = {})
146
131
  options[:query] ||= params[:query] if params[:query]
147
132
  self.current_page = options[:page] if options[:page]
148
133
  query, tags = parse_query_and_tags(options[:query]) if options[:query]
149
134
  self.current_query = query
150
135
 
151
- records = {
152
- :user => current_user,
153
- :order => current_user.pref[:"#{items}_sort_by"] || klass.sort_by
154
- }
136
+ order = current_user.pref[:"#{controller_name}_sort_by"] || klass.sort_by
137
+
155
138
  pages = {
156
139
  :page => current_page,
157
- :per_page => current_user.pref[:"#{items}_per_page"]
140
+ :per_page => current_user.pref[:"#{controller_name}_per_page"]
158
141
  }
159
142
 
160
- # Call the hook and return its output if any.
161
- assets = hook(:"get_#{items}", self, :records => records, :pages => pages)
162
- return assets.last unless assets.empty?
163
-
164
143
  # Use default processing if no hooks are present. Note that comma-delimited
165
144
  # export includes deleted records, and the pagination is enabled only for
166
145
  # plain HTTP, Ajax and XML API requests.
167
146
  wants = request.format
168
- filter = session[options[:filter]].to_s.split(',') if options[:filter]
147
+ filter = session[:"#{controller_name}_filter"].to_s.split(',')
169
148
 
170
- scope = klass.my(records)
171
- scope = scope.merge(search.result)
149
+ scope = entities.merge(search.result)
172
150
  scope = scope.state(filter) if filter.present?
173
151
  scope = scope.text_search(query) if query.present?
174
152
  scope = scope.tagged_with(tags, :on => :tags) if tags.present?
175
- scope = scope.unscoped if wants.csv?
153
+ scope = scope.order(order)
176
154
  scope = scope.paginate(pages) if wants.html? || wants.js? || wants.xml?
177
155
  scope
178
156
  end
179
157
 
180
158
  #----------------------------------------------------------------------------
181
159
  def update_recently_viewed
182
- if item = instance_variable_get("@#{controller_name.singularize}")
183
- item.send(item.class.versions_association_name).create(:event => :view, :whodunnit => PaperTrail.whodunnit)
160
+ entity.versions.create(:event => :view, :whodunnit => PaperTrail.whodunnit)
161
+ end
162
+
163
+ #----------------------------------------------------------------------------
164
+ def field_group
165
+ if @tag = Tag.find_by_name(params[:tag].strip)
166
+ if @field_group = FieldGroup.find_by_tag_id_and_klass_name(@tag.id, klass.to_s)
167
+ @asset = klass.find_by_id(params[:asset_id]) || klass.new
168
+ render 'fields/group' and return
169
+ end
170
+ end
171
+ render :text => ''
172
+ end
173
+
174
+ # Somewhat simplistic parser that extracts query and hash-prefixed tags from
175
+ # the search string and returns them as two element array, for example:
176
+ #
177
+ # "#real Billy Bones #pirate" => [ "Billy Bones", "real, pirate" ]
178
+ #----------------------------------------------------------------------------
179
+ def parse_query_and_tags(search_string)
180
+ query, tags = [], []
181
+ search_string.scan(/[\w@\-\.#]+/).each do |token|
182
+ if token.starts_with?("#")
183
+ tags << token[1 .. -1]
184
+ else
185
+ query << token
186
+ end
184
187
  end
188
+ [ query.join(" "), tags.join(", ") ]
189
+ end
190
+
191
+ #----------------------------------------------------------------------------
192
+ def timeline(asset)
193
+ (asset.comments + asset.emails).sort { |x, y| y.created_at <=> x.created_at }
185
194
  end
186
195
  end