fat_free_crm 0.12.0 → 0.12.1

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.

checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NjYzZTU1ZTMxNGUyNGI2M2FkNjExNjdlOTI0MDZjYzg0YmM5NTE5MA==
4
+ ZWNmOGI2ODA3NzM3ZWY1ZDZlZjE5ZDE5YjhmNTU2YmQ3MmQzZDljZQ==
5
5
  data.tar.gz: !binary |-
6
- YjM0ZjE0ZDhjNjQxMTIxYmZjZTJiY2MyMzE3ZDA2MjRlYmU2ZDdlMg==
7
- !binary "U0hBNTEy":
6
+ ODQyMGQ0NDFlNDBkMTU2OWVmYzY1ODMzMmJkYTY3ZjVjOTY0MTQxNQ==
7
+ SHA512:
8
8
  metadata.gz: !binary |-
9
- OGY4M2Y1ZjQ4NGZkMDRhNWRiMTlmMTc2MjM5YWJhMmRlOWNlN2E1MjU1Njg2
10
- YWZkNzU3ZmVlNDUxMmIxMGRmYmYxYTA1NGU2NzI0MDIyZjZkNzQ3NDYwZDBk
11
- ZWYwMjE2NWIxYzgwYjhiZWM5MjYwYjMzYjNjZDkyYjBmNDY4NWQ=
9
+ NmFhNTU0YmVkZTBhNjkwZWQ3ZTYxODAyOGVkOGQ3Zjc3MzdmNmVkMDRlNGYx
10
+ MjljM2UxZDk4ZjY4MWMwNzBjZDk1YWMyODVmYjk2OGE1ODA3NDgwMDA2ZTcy
11
+ ODEyNTI5MGZjZDY5YjZmNDZlNzY4MmM0YmFhNDRkMjAyMGM2OTE=
12
12
  data.tar.gz: !binary |-
13
- Yzc4OTkwOTgyOWI1NjNkODc4MzY5YjVjODc3MWJjZDhjM2M4YTBjOWMwZjMy
14
- ZjdiZWYzNjU2YjM1YWM3YjkzYzgxZDUxMjZiYjgwNjM4NmQ2MmFiYzBlNWMx
15
- MzAwMTllYTRjNmJlNmRmYjI1NzY4YWNkZjhhZjcxMGMwYWM2ODc=
13
+ ZDllMDRjYmIyZjI5YTJhNWE3MmY0ZmRmNzA0N2FiYTY0M2ZiMmRhNjg1ODE4
14
+ YmM4ODQyY2VjMjAyMGE1YzkxYWVkYzVmZjJkMjU5YjVjNjUzZDQ0MDc4OWEx
15
+ M2ZlMjMzMTJkNmEzODQ0ZjA4ZjA4NmI1Y2U3MTJhNWE4ODAwNjg=
data/Gemfile.lock CHANGED
@@ -132,7 +132,7 @@ GEM
132
132
  thor (>= 0.14, < 2.0)
133
133
  json (1.7.7)
134
134
  kgio (2.7.4)
135
- libv8 (3.3.10.4)
135
+ libv8 (3.16.14.3)
136
136
  listen (0.7.3)
137
137
  mail (2.5.3)
138
138
  i18n (>= 0.4.0)
@@ -222,6 +222,7 @@ GEM
222
222
  ffi (>= 0.5.0)
223
223
  rdoc (3.12.2)
224
224
  json (~> 1.4)
225
+ ref (1.0.5)
225
226
  responds_to_parent (1.1.0)
226
227
  rest-client (1.6.7)
227
228
  mime-types (>= 1.16)
@@ -267,8 +268,9 @@ GEM
267
268
  rack (~> 1.0)
268
269
  tilt (~> 1.1, != 1.3.0)
269
270
  sqlite3 (1.3.7)
270
- therubyracer (0.10.1)
271
- libv8 (~> 3.3.10)
271
+ therubyracer (0.12.0)
272
+ libv8 (~> 3.16.14.0)
273
+ ref
272
274
  thin (1.5.0)
273
275
  daemons (>= 1.0.9)
274
276
  eventmachine (>= 0.12.6)
@@ -5,6 +5,8 @@
5
5
  #------------------------------------------------------------------------------
6
6
  class ApplicationController < ActionController::Base
7
7
 
8
+ protect_from_forgery
9
+
8
10
  before_filter :set_context
9
11
  before_filter :clear_setting_cache
10
12
  before_filter "hook(:app_before_filter, self)"
@@ -58,14 +58,19 @@ class HomeController < ApplicationController
58
58
  # GET /home/timeline AJAX
59
59
  #----------------------------------------------------------------------------
60
60
  def timeline
61
- unless params[:type].empty?
62
- model = params[:type].camelize.constantize
63
- item = model.find(params[:id])
64
- item.update_attribute(:state, params[:state])
65
- else
66
- comments, emails = params[:id].split("+")
67
- Comment.update_all("state = '#{params[:state]}'", "id IN (#{comments})") unless comments.blank?
68
- Email.update_all("state = '#{params[:state]}'", "id IN (#{emails})") unless emails.blank?
61
+ state = params[:state].to_s
62
+ if %w(Collapsed Expanded).include?(state)
63
+ if (model_type = params[:type].to_s).present?
64
+ if %w(comment email).include?(model_type)
65
+ model = model_type.camelize.constantize
66
+ item = model.find(params[:id])
67
+ item.update_attribute(:state, state)
68
+ end
69
+ else
70
+ comments, emails = params[:id].split("+")
71
+ Comment.where(:id => comments.split(',')).update_all(:state => state) unless comments.blank?
72
+ Email.where(:id => emails.split(',')).update_all(:state => state) unless emails.blank?
73
+ end
69
74
  end
70
75
 
71
76
  render :nothing => true
@@ -118,6 +123,9 @@ class HomeController < ApplicationController
118
123
  end
119
124
 
120
125
  #----------------------------------------------------------------------------
126
+ # TODO: this is ugly, ugly code. It's being security patched now but urgently
127
+ # needs refactoring to use user id instead. Permuations based on name or email
128
+ # yield incorrect results.
121
129
  def activity_user
122
130
  user = current_user.pref[:activity_user]
123
131
  if user && user != "all_users"
@@ -126,12 +134,11 @@ class HomeController < ApplicationController
126
134
  else # first_name middle_name last_name any_name
127
135
  name_query = if user.include?(" ")
128
136
  user.name_permutations.map{ |first, last|
129
- "(upper(first_name) LIKE upper('%#{first}%') AND upper(last_name) LIKE upper('%#{last}%'))"
130
- }.join(" OR ")
137
+ User.where(:first_name => first, :last_name => last)
138
+ }.map(&:to_a).flatten.first
131
139
  else
132
- "upper(first_name) LIKE upper('%#{user}%') OR upper(last_name) LIKE upper('%#{user}%')"
140
+ [User.where(:first_name => user), User.where(:last_name => user)].map(&:to_a).flatten.first
133
141
  end
134
- User.where(name_query).first
135
142
  end
136
143
  end
137
144
  user.is_a?(User) ? user.id : nil
@@ -146,6 +146,14 @@ class User < ActiveRecord::Base
146
146
  super(value)
147
147
  end
148
148
 
149
+ def to_json(options = nil)
150
+ [name].to_json
151
+ end
152
+
153
+ def to_xml(options = nil)
154
+ [name].to_xml
155
+ end
156
+
149
157
  private
150
158
 
151
159
  # Suspend newly created user if signup requires an approval.
@@ -1,18 +1 @@
1
- # Copyright (c) 2008-2013 Michael Dvorkin and contributors.
2
- #
3
- # Fat Free CRM is freely distributable under the terms of MIT license.
4
- # See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
- #------------------------------------------------------------------------------
6
- # Be sure to restart your server when you modify this file.
7
-
8
- # Your secret key for verifying the integrity of signed cookies.
9
- # If you change this key, all old signed cookies will become invalid!
10
- # Make sure the secret is at least 30 characters and all random,
11
- # no regular words or you'll be exposed to dictionary attacks.
12
-
13
- # PLEASE NOTE: This secret token must be changed in your fork of Fat Free CRM.
14
- # This problem is mitigated when running Fat Free CRM as a Rails Engine.
15
-
16
- if defined?(FatFreeCRM::Application)
17
- FatFreeCRM::Application.config.secret_token = '51aa366864a80316a85cff0d3762347f4ae3d029d548bef034d56e82b1a2ffac5353ee6719d9b64e4354e2a0b1a901679f46a851c360a2ea377188e4b196b6b6'
18
- end
1
+ FatFreeCRM::Application.config.secret_token = 'bb6810b6691d68fae269cfa4ce1805a5b4b0826e2680988e1102a4b3b80e5753af76d25a90f3ba7bc4c2fa68c4bce3b36df72baa18a0e35a71d3199918766db9'
data/config/routes.rb CHANGED
@@ -137,7 +137,7 @@ Rails.application.routes.draw do
137
137
  end
138
138
  end
139
139
 
140
- resources :users, :id => /\d+/ do
140
+ resources :users, :id => /\d+/, :except => [:index, :destroy] do
141
141
  member do
142
142
  get :avatar
143
143
  get :password
@@ -7,7 +7,7 @@ module FatFreeCRM
7
7
  module VERSION #:nodoc:
8
8
  MAJOR = 0
9
9
  MINOR = 12
10
- TINY = 0
10
+ TINY = 1
11
11
  PRE = nil
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
@@ -0,0 +1,17 @@
1
+ # Copyright (c) 2008-2013 Michael Dvorkin and contributors.
2
+ #
3
+ # Fat Free CRM is freely distributable under the terms of MIT license.
4
+ # See MIT-LICENSE file or http://www.opensource.org/licenses/mit-license.php
5
+ #------------------------------------------------------------------------------
6
+
7
+ namespace :ffcrm do
8
+
9
+ desc "Generate a secret token for Rails to use."
10
+ task :secret do
11
+ require 'securerandom'
12
+ secret = SecureRandom.hex(64)
13
+ filename = File.join(Rails.root, 'config', 'initializers', 'secret_token.rb')
14
+ File.open(filename, 'w'){|f| f.puts "FatFreeCRM::Application.config.secret_token = '#{secret}'"}
15
+ end
16
+
17
+ end
@@ -42,13 +42,13 @@ describe HomeController do
42
42
  assigns[:my_tasks].should == [task_1, task_2, task_3, task_4]
43
43
  end
44
44
 
45
- it "should not display completed tasks" do
46
- task_1 = FactoryGirl.create(:task, :user_id => current_user.id, :name => "Your first task", :bucket => "due_asap", :assigned_to => current_user.id)
47
- task_2 = FactoryGirl.create(:task, :user_id => current_user.id, :name => "Completed task", :bucket => "due_asap", :completed_at => 1.days.ago, :completed_by => current_user.id, :assigned_to => current_user.id)
45
+ it "should not display completed tasks" do
46
+ task_1 = FactoryGirl.create(:task, :user_id => current_user.id, :name => "Your first task", :bucket => "due_asap", :assigned_to => current_user.id)
47
+ task_2 = FactoryGirl.create(:task, :user_id => current_user.id, :name => "Completed task", :bucket => "due_asap", :completed_at => 1.days.ago, :completed_by => current_user.id, :assigned_to => current_user.id)
48
48
 
49
- get :index
50
- assigns[:my_tasks].should == [task_1]
51
- end
49
+ get :index
50
+ assigns[:my_tasks].should == [task_1]
51
+ end
52
52
 
53
53
  it "should get a list of my opportunities ordered by closes_on" do
54
54
  opportunity_1 = FactoryGirl.create(:opportunity, :name => "Your first opportunity", :closes_on => 15.days.from_now, :assigned_to => current_user.id, :stage => 'proposal')
@@ -153,42 +153,93 @@ describe HomeController do
153
153
  session[:hello].should == true
154
154
  end
155
155
  end
156
-
156
+
157
157
  describe "activity_user" do
158
-
158
+
159
159
  before(:each) do
160
160
  @user = mock(User, :id => 1, :is_a? => true)
161
161
  @cur_user = mock(User)
162
162
  end
163
-
163
+
164
164
  it "should find a user by email" do
165
165
  @cur_user.stub!(:pref).and_return(:activity_user => 'billy@example.com')
166
166
  controller.instance_variable_set(:@current_user, @cur_user)
167
167
  User.should_receive(:where).with(:email => 'billy@example.com').and_return([@user])
168
168
  controller.send(:activity_user).should == 1
169
169
  end
170
-
170
+
171
171
  it "should find a user by first name or last name" do
172
172
  @cur_user.stub!(:pref).and_return(:activity_user => 'Billy')
173
173
  controller.instance_variable_set(:@current_user, @cur_user)
174
- User.should_receive(:where).with("upper(first_name) LIKE upper('%Billy%') OR upper(last_name) LIKE upper('%Billy%')").and_return([@user])
174
+ User.should_receive(:where).with(:first_name => 'Billy').and_return([@user])
175
+ User.should_receive(:where).with(:last_name => 'Billy').and_return([@user])
175
176
  controller.send(:activity_user).should == 1
176
177
  end
177
-
178
+
178
179
  it "should find a user by first name and last name" do
179
180
  @cur_user.stub!(:pref).and_return(:activity_user => 'Billy Elliot')
180
181
  controller.instance_variable_set(:@current_user, @cur_user)
181
- User.should_receive(:where).with("(upper(first_name) LIKE upper('%Billy%') AND upper(last_name) LIKE upper('%Elliot%')) OR (upper(first_name) LIKE upper('%Elliot%') AND upper(last_name) LIKE upper('%Billy%'))").and_return([@user])
182
+ User.should_receive(:where).with(:first_name => 'Billy', :last_name => "Elliot").and_return([@user])
183
+ User.should_receive(:where).with(:first_name => 'Elliot', :last_name => "Billy").and_return([@user])
182
184
  controller.send(:activity_user).should == 1
183
185
  end
184
-
186
+
185
187
  it "should return nil when 'all_users' is specified" do
186
188
  @cur_user.stub!(:pref).and_return(:activity_user => 'all_users')
187
189
  controller.instance_variable_set(:@current_user, @cur_user)
188
190
  User.should_not_receive(:where)
189
191
  controller.send(:activity_user).should == nil
190
192
  end
191
-
193
+
194
+ end
195
+
196
+ describe "timeline" do
197
+
198
+ before(:each) do
199
+ require_user
200
+ end
201
+
202
+ it "should collapse all comments and emails on a specific contact" do
203
+ comment = double(Comment)
204
+ Comment.should_receive(:find).with("1").and_return(comment)
205
+ comment.should_receive(:update_attribute).with(:state, 'Collapsed')
206
+ xhr :get, :timeline, :type => "comment", :id => "1", :state => "Collapsed"
207
+ end
208
+
209
+ it "should expand all comments and emails on a specific contact" do
210
+ comment = double(Comment)
211
+ Comment.should_receive(:find).with("1").and_return(comment)
212
+ comment.should_receive(:update_attribute).with(:state, 'Expanded')
213
+ xhr :get, :timeline, :type => "comment", :id => "1", :state => "Expanded"
214
+ end
215
+
216
+ it "should not do anything when state neither Expanded nor Collapsed" do
217
+ comment = double(Comment)
218
+ Comment.should_not_receive(:find).with("1")
219
+ xhr :get, :timeline, :type => "comment", :id => "1", :state => "Explode"
220
+ end
221
+
222
+ it "should collapse all comments and emails on Contact" do
223
+ where_stub = double
224
+ where_stub.should_receive(:update_all).with(:state => "Collapsed")
225
+ Comment.should_receive(:where).and_return(where_stub)
226
+ xhr :get, :timeline, :id => "1,2,3,4+", :state => "Collapsed"
227
+ end
228
+
229
+ it "should not allow an arbitary state (sanitizes input)" do
230
+ where_stub = double
231
+ where_stub.should_receive(:update_all).with(:state => "Expanded")
232
+ Comment.should_receive(:where).and_return(where_stub)
233
+ xhr :get, :timeline, :id => "1,2,3,4+", :state => "Expanded"
234
+ end
235
+
236
+ it "should not update an arbitary model (sanitizes input)" do
237
+ where_stub = double
238
+ where_stub.should_receive(:update_all).with(:state => "Expanded")
239
+ Comment.should_receive(:where).and_return(where_stub)
240
+ xhr :get, :timeline, :id => "1,2,3,4+", :state => "Expanded"
241
+ end
242
+
192
243
  end
193
244
 
194
245
  end
@@ -209,4 +209,18 @@ describe User do
209
209
  @user.single_access_token.should == "token"
210
210
  end
211
211
  end
212
+
213
+ describe "serialization" do
214
+
215
+ let(:user) { FactoryGirl.build(:user) }
216
+
217
+ it "to json" do
218
+ expect(user.to_json).to eql([user.name].to_json)
219
+ end
220
+
221
+ it "to xml" do
222
+ expect(user.to_xml).to eql([user.name].to_xml)
223
+ end
224
+
225
+ end
212
226
  end
@@ -8,8 +8,8 @@ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
8
8
  describe UsersController do
9
9
  describe "routing" do
10
10
 
11
- it "recognizes and generates #index" do
12
- { :get => "/users" }.should route_to(:controller => "users", :action => "index")
11
+ it "doesn't recognize #index" do
12
+ { :get => "/users" }.should_not be_routable
13
13
  end
14
14
 
15
15
  it "recognizes and generates #new as /signup" do
@@ -40,8 +40,8 @@ describe UsersController do
40
40
  { :put => "/opportunities/aaron" }.should_not be_routable
41
41
  end
42
42
 
43
- it "recognizes and generates #destroy" do
44
- { :delete => "/users/1" }.should route_to(:controller => "users", :action => "destroy", :id => "1")
43
+ it "doesn't recognize #destroy" do
44
+ { :delete => "/users/1" }.should_not be_routable
45
45
  end
46
46
 
47
47
  it "doesn't recognize #destroy with non-numeric id" do
@@ -81,4 +81,3 @@ describe UsersController do
81
81
  end
82
82
  end
83
83
  end
84
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fat_free_crm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Dvorkin
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-06-28 00:00:00.000000000 Z
14
+ date: 2013-12-27 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rails
@@ -1082,6 +1082,7 @@ files:
1082
1082
  - lib/tasks/ffcrm/demo.rake
1083
1083
  - lib/tasks/ffcrm/dropbox.rake
1084
1084
  - lib/tasks/ffcrm/missing_translations.rake
1085
+ - lib/tasks/ffcrm/secret.rake
1085
1086
  - lib/tasks/ffcrm/settings.rake
1086
1087
  - lib/tasks/ffcrm/setup.rake
1087
1088
  - lib/tasks/ffcrm/update_data.rake
@@ -1418,7 +1419,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1418
1419
  version: '0'
1419
1420
  requirements: []
1420
1421
  rubyforge_project:
1421
- rubygems_version: 2.0.3
1422
+ rubygems_version: 2.1.11
1422
1423
  signing_key:
1423
1424
  specification_version: 4
1424
1425
  summary: Fat Free CRM