redmine_crm 0.0.8 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6c8441e8bec7aa49ee5f7c391e66cc174da87266
4
- data.tar.gz: dfddb0ee0c407a3e3d6bc1d6e4ff91bf3e6816df
3
+ metadata.gz: f9413c2fa619c94a97f50d2fb2808c41a80fe111
4
+ data.tar.gz: 7b4ef211bc2378aeef9f0046bdab96aa2c6a845e
5
5
  SHA512:
6
- metadata.gz: 56a5b5fc3381ee13dff3740e0c0f027b13db220e5571f1027b3829acf74eddeaa76102f9dca14cd3dbe8122c57862130c44af839c4b02115ec8d2f5f4741827a
7
- data.tar.gz: 34b79fa7e7ca483f9e3ad606c92e421f8409617ffa7f623d6a5018b26ad4f2280c3577ec464ed48f2f1e75104633e7336d1d5ad6d533f68dc744080890aacdf0
6
+ metadata.gz: 1feb362a5b1f2c77ca00d27866bf3d5a30f77f899275add18b019a03e5621d6ced4a23b54e9d6f690d7356c1f61de779fa77d172b9cb07e19c72e1f20e58714c
7
+ data.tar.gz: 14e0af5609fca123adf7d4ed8b5d7082f2cd98044d04c12d48ef9442a9617c946dd4054b1fa79379aebe543509122414bd4ae16b1a8ba93c1bddf9df47723412
data/lib/redmine_crm.rb CHANGED
@@ -4,6 +4,18 @@ require "redmine_crm/tag"
4
4
  require "redmine_crm/tag_list"
5
5
  require "redmine_crm/tagging"
6
6
  require "redmine_crm/rcrm_acts_as_taggable"
7
- require "redmine_crm/tags_helper"
7
+ require "redmine_crm/helpers/tags_helper"
8
8
  require "redmine_crm/currency"
9
9
  require "redmine_crm/money_helper"
10
+
11
+ #version 0.10
12
+ require "redmine_crm/rcrm_acts_as_votable"
13
+ require "redmine_crm/rcrm_acts_as_voter"
14
+ require "redmine_crm/vote"
15
+ require "redmine_crm/voter"
16
+ require "redmine_crm/rcrm_acts_as_viewed"
17
+
18
+ if defined?(ActiveRecord::Base)
19
+ ActiveRecord::Base.extend(RedmineCrm::ActsAsVotable::Votable)
20
+ ActiveRecord::Base.extend(RedmineCrm::ActsAsVotable::Voter)
21
+ end
@@ -0,0 +1,38 @@
1
+ module RedmineCrm
2
+ module ActsAsVotable::Helpers
3
+
4
+ # this helper provides methods that help find what words are
5
+ # up votes and what words are down votes
6
+ #
7
+ # It can be called
8
+ #
9
+ # votable_object.votable_words.that_mean_true
10
+ #
11
+ module Words
12
+
13
+ def votable_words
14
+ VotableWords
15
+ end
16
+
17
+ end
18
+
19
+ class VotableWords
20
+
21
+ def self.that_mean_true
22
+ ['up', 'upvote', 'like', 'liked', 'positive', 'yes', 'good', 'true', 1, true]
23
+ end
24
+
25
+ def self.that_mean_false
26
+ ['down', 'downvote', 'dislike', 'disliked', 'negative', 'no', 'bad', 'false', 0, false]
27
+ end
28
+
29
+ # check is word is a true or bad vote
30
+ # if the word is unknown, then it counts it as a true/good
31
+ # vote. this exists to allow all voting to be good by default
32
+ def self.meaning_of word
33
+ !that_mean_false.include?(word)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -10,6 +10,10 @@ module RedmineCrm
10
10
 
11
11
  module ClassMethods
12
12
 
13
+ def taggable?
14
+ false
15
+ end
16
+
13
17
  def rcrm_acts_as_taggable
14
18
  has_many :taggings, :as => :taggable, :dependent => :destroy, :class_name => '::RedmineCrm::Tagging'#, :include => :tag
15
19
  has_many :tags, :through => :taggings, :class_name => '::RedmineCrm::Tag'
@@ -23,6 +27,12 @@ module RedmineCrm
23
27
  extend RedmineCrm::Acts::Taggable::SingletonMethods
24
28
 
25
29
  alias_method_chain :reload, :tag_list
30
+
31
+ class_eval do
32
+ def self.taggable?
33
+ true
34
+ end
35
+ end
26
36
  end
27
37
 
28
38
  def cached_tag_list_column_name
@@ -65,7 +75,7 @@ module RedmineCrm
65
75
 
66
76
  end
67
77
 
68
- def drop_taggable_table options = {}
78
+ def drop_taggable_table options = {}
69
79
  tag_name_table = options[:tags] || :tags
70
80
  if !self.connection.table_exists?(tag_name_table)
71
81
  self.connection.drop_table tag_name_table
@@ -0,0 +1,271 @@
1
+ # Copyright (c) 2008 Damian Martinelli
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.
21
+ module RedmineCrm
22
+ module ActiveRecord #:nodoc:
23
+ module Acts #:nodoc:
24
+
25
+ # == acts_as_viewed
26
+ # Adds views count capabilities to any ActiveRecord object.
27
+ # It has the ability to work with objects that have or don't special fields to keep a tally of the
28
+ # viewings for each object.
29
+ # In addition it will by default use the User model as the viewer object and keep the viewings per-user.
30
+ # It can be configured to use another class.
31
+ # The IP address are used to not repeat views from the same ip. Only one view are count by user or IP.
32
+ #
33
+ # Special methods are provided to create the viewings table and if needed, to add the special fields needed
34
+ # to keep per-objects viewings fast for access to viewed objects. Can be easily used in migrations.
35
+ #
36
+ # == Example of usage:
37
+ #
38
+ # class Video < ActiveRecord::Base
39
+ # acts_as_viewed
40
+ # end
41
+ #
42
+ # In a controller:
43
+ #
44
+ # bill = User.find_by_name 'bill'
45
+ # batman = Video.find_by_title 'Batman'
46
+ # toystory = Video.find_by_title 'Toy Story'
47
+ #
48
+ # batman.view request.remote_addr, bill
49
+ # toystory.view request.remote_addr, bill
50
+ #
51
+ # batman.view_count # => 1
52
+ #
53
+ #
54
+ module Viewed
55
+
56
+ class ViewedError < RuntimeError; end
57
+
58
+ def self.included(base) #:nodoc:
59
+ base.extend(ClassMethods)
60
+ end
61
+
62
+ module ClassMethods
63
+
64
+ # Make the model viewable.
65
+ # The Viewing model, holding the details of the viewings, will be created dynamically if it doesn't exist.
66
+ #
67
+ # * Adds a <tt>has_many :viewings</tt> association to the model for easy retrieval of the detailed viewings.
68
+ # * Adds a <tt>has_many :viewers</tt> association to the object.
69
+ # * Adds a <tt>has_many :viewings</tt> associations to the viewer class.
70
+ #
71
+ # === Options
72
+ # * <tt>:viewing_class</tt> -
73
+ # class of the model used for the viewings. Defaults to Viewing. This class will be dynamically created if not already defined.
74
+ # If the class is predefined, it must have in it the following definitions:
75
+ # <tt>belongs_to :viewed, :polymorphic => true</tt>
76
+ # <tt>belongs_to :viewer, :class_name => 'User', :foreign_key => :viewer_id</tt> replace user with the viewer class if needed.
77
+ # * <tt>:viewer_class</tt> -
78
+ # class of the model that creates the viewing.
79
+ # Defaults to User This class will NOT be created, so it must be defined in the app.
80
+ # Use the IP address to prevent multiple viewings from the same client.
81
+ #
82
+ def rcrm_acts_as_viewed(options = {})
83
+ # don't allow multiple calls
84
+ return if self.included_modules.include?(ActiveRecord::Acts::Viewed::ViewMethods)
85
+ send :include, ActiveRecord::Acts::Viewed::ViewMethods
86
+
87
+ # Create the model for ratings if it doesn't yet exist
88
+ viewing_class = options[:viewing_class] || 'Viewing'
89
+ viewer_class = options[:viewer_class] || 'User'
90
+
91
+ unless Object.const_defined?(viewing_class)
92
+ Object.class_eval <<-EOV
93
+ class #{viewing_class} < ActiveRecord::Base
94
+ belongs_to :viewed, :polymorphic => true
95
+ belongs_to :viewer, :class_name => #{viewer_class}, :foreign_key => :viewer_id
96
+ end
97
+ EOV
98
+ end
99
+
100
+ # Rails < 3
101
+ # write_inheritable_attribute( :acts_as_viewed_options ,
102
+ # { :viewing_class => viewing_class,
103
+ # :viewer_class => viewer_class } )
104
+ # class_inheritable_reader :acts_as_viewed_options
105
+
106
+ # Rails >= 3
107
+ class_attribute :acts_as_viewed_options
108
+ self.acts_as_viewed_options = { :viewing_class => viewing_class,
109
+ :viewer_class => viewer_class }
110
+ class_eval do
111
+ has_many :viewings, :as => :viewed, :dependent => :delete_all, :class_name => viewing_class.to_s
112
+ has_many(:viewers, :through => :viewings, :class_name => viewer_class.to_s)
113
+
114
+ before_create :init_viewing_fields
115
+ end
116
+
117
+ # Add to the User (or whatever the viewer is) a has_many viewings
118
+ viewer_as_class = viewer_class.constantize
119
+ return if viewer_as_class.instance_methods.include?('find_in_viewings')
120
+ viewer_as_class.class_eval <<-EOS
121
+ has_many :viewings, :foreign_key => :viewer_id, :class_name => #{viewing_class.to_s}
122
+ EOS
123
+ end
124
+ end
125
+
126
+ module ViewMethods
127
+
128
+ def self.included(base) #:nodoc:
129
+ base.extend ClassMethods
130
+ end
131
+
132
+ # Is this object viewed already?
133
+ def viewed?
134
+ return (!self.views.nil? && self.views > 0) if attributes.has_key? 'views'
135
+ !viewings.find(:first).nil?
136
+ end
137
+
138
+ # Get the number of viewings for this object based on the views field,
139
+ # or with a SQL query if the viewed objects doesn't have the views field
140
+ def view_count
141
+ return self.views || 0 if attributes.has_key? 'views'
142
+ viewings.count
143
+ end
144
+
145
+ # View the object with or without a viewer - create new or update as needed
146
+ #
147
+ # * <tt>ip</tt> - the viewer ip
148
+ # * <tt>viewer</tt> - an object of the viewer class. Must be valid and with an id to be used. Or nil
149
+ def view ip, viewer = nil
150
+ # Sanity checks for the parameters
151
+ viewing_class = acts_as_viewed_options[:viewing_class].constantize
152
+ if viewer && !(acts_as_viewed_options[:viewer_class].constantize === viewer)
153
+ raise ViewedError, "the viewer object must be the one used when defining acts_as_viewed (or a descendent of it). other objects are not acceptable"
154
+ end
155
+
156
+ viewing_class.transaction do
157
+ if !viewed_by? ip, viewer
158
+ view = viewing_class.new
159
+ view.viewer_id = viewer.id if viewer && !viewer.id.nil?
160
+ view.ip = ip
161
+ viewings << view
162
+ target = self if attributes.has_key? 'views'
163
+ target.views = ( (target.views || 0) + 1 ) if target
164
+ view.save
165
+ target.save_without_validation if target
166
+ return true
167
+ else
168
+ return false
169
+ end
170
+ end
171
+ end
172
+
173
+ # Check if an item was already viewed by the given viewer
174
+ def viewed_by? ip, viewer = nil
175
+ if viewer && !viewer.nil? && !(acts_as_viewed_options[:viewer_class].constantize === viewer)
176
+ raise ViewedError, "the viewer object must be the one used when defining acts_as_viewed (or a descendent of it). other objects are not acceptable"
177
+ end
178
+ if viewer && !viewer.id.nil? && !viewer.anonymous?
179
+ return viewings.where("viewer_id = '#{viewer.id}'").any?
180
+ else
181
+ return viewings.where("ip = '#{ip}'").any?
182
+ end
183
+ end
184
+
185
+ private
186
+
187
+ def init_viewing_fields #:nodoc:
188
+ if attributes.has_key? 'views'
189
+ self.views ||= 0
190
+ end
191
+ end
192
+
193
+ end
194
+
195
+ module ClassMethods
196
+
197
+ # Generate the viewings columns on a table, to be used when creating the table
198
+ # in a migration. This is the preferred way to do in a migration that creates
199
+ # new tables as it will make it as part of the table creation, and not generate
200
+ # ALTER TABLE calls after the fact
201
+ def generate_viewings_columns table
202
+ table.column :views, :integer
203
+ end
204
+
205
+ # Create the needed columns for acts_as_viewed.
206
+ # To be used during migration, but can also be used in other places.
207
+ def add_viewings_columns
208
+ if !self.content_columns.find { |c| 'views' == c.name }
209
+ self.connection.add_column table_name, :views, :integer, :default => '0'
210
+ self.reset_column_information
211
+ end
212
+ end
213
+
214
+ # Remove the acts_as_viewed specific columns added with add_viewings_columns
215
+ # To be used during migration, but can also be used in other places
216
+ def remove_viewings_columns
217
+ if self.content_columns.find { |c| 'views' == c.name }
218
+ self.connection.remove_column table_name, :views
219
+ self.reset_column_information
220
+ end
221
+ end
222
+
223
+ # Create the viewings table
224
+ # === Options hash:
225
+ # * <tt>:table_name</tt> - use a table name other than viewings
226
+ # To be used during migration, but can also be used in other places
227
+ def create_viewings_table options = {}
228
+ name = options[:table_name] || :viewings
229
+ self.connection.create_table(name) do |t|
230
+ t.column :viewer_id, :integer
231
+ t.column :viewed_id, :integer
232
+ t.column :viewed_type, :string
233
+ t.column :ip, :string, :limit => '24'
234
+ t.column :created_at, :datetime
235
+ end
236
+
237
+ self.connection.add_index name, :viewer_id
238
+ self.connection.add_index name, [:viewed_type, :viewed_id]
239
+
240
+ end
241
+
242
+ # Drop the viewings table.
243
+ # === Options hash:
244
+ # * <tt>:table_name</tt> - the name of the viewings table, defaults to viewings
245
+ # To be used during migration, but can also be used in other places
246
+ def drop_viewings_table options = {}
247
+ name = options[:table_name] || :viewings
248
+ self.connection.drop_table name
249
+ end
250
+
251
+ # Find all viewings for a specific viewer.
252
+ def find_viewed_by viewer
253
+ viewing_class = acts_as_viewed_options[:viewing_class].constantize
254
+ if !(acts_as_viewed_options[:viewer_class].constantize === viewer)
255
+ raise ViewedError, "The viewer object must be the one used when defining acts_as_viewed (or a descendent of it). other objects are not acceptable"
256
+ end
257
+ raise ViewedError, "Viewer must be a valid and existing object" if viewer.nil? || viewer.id.nil?
258
+ raise ViewedError, 'Viewer must be a valid viewer' if !viewing_class.column_names.include? "viewer_id"
259
+ viewed_class = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
260
+ conds = [ 'viewed_type = ? AND viewer_id = ?', viewed_class, viewer.id ]
261
+ acts_as_viewed_options[:viewing_class].constantize.find(:all, :conditions => conds).collect {|r| r.viewed_type.constantize.find_by_id r.viewed.id }
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+
270
+ ActiveRecord::Base.send :include, RedmineCrm::ActiveRecord::Acts::Viewed
271
+
@@ -0,0 +1,58 @@
1
+ require 'active_record'
2
+
3
+ module RedmineCrm
4
+ module ActsAsVotable #:nodoc:
5
+ module Votable #:nodoc:
6
+
7
+ def votable?
8
+ false
9
+ end
10
+
11
+ def rcrm_acts_as_votable
12
+ require 'redmine_crm/votable'
13
+ include ActsAsVotable::Votable
14
+
15
+ class_eval do
16
+ def self.votable?
17
+ true
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ def create_votable_table options = {}
24
+ votes_name_table = options[:votes] || :votes
25
+
26
+ if !self.connection.table_exists?(votes_name_table)
27
+ self.connection.create_table(votes_name_table) do |t|
28
+ t.references :votable, :polymorphic => true
29
+ t.references :voter, :polymorphic => true
30
+
31
+ t.boolean :vote_flag
32
+ t.string :vote_scope
33
+ t.integer :vote_weight
34
+
35
+ t.timestamps
36
+ end
37
+ # byebug
38
+ # if ActiveRecord::VERSION::MAJOR < 4
39
+ if self.parent::VERSION::MAJOR < 4
40
+ self.connection.add_index :votes, [:votable_id, :votable_type]
41
+ self.connection.add_index :votes, [:voter_id, :voter_type]
42
+ end
43
+
44
+ self.connection.add_index :votes, [:voter_id, :voter_type, :vote_scope]
45
+ self.connection.add_index :votes, [:votable_id, :votable_type, :vote_scope]
46
+ end
47
+ end
48
+
49
+ def drop_votable_table options = {}
50
+ votes_name_table = options[:votes] || :votes
51
+ if !self.connection.table_exists?(votes_name_table)
52
+ self.connection.drop_table votes_name_table
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,27 @@
1
+ module RedmineCrm
2
+ module ActsAsVotable
3
+ # module Extenders
4
+
5
+ module Voter
6
+
7
+ def voter?
8
+ false
9
+ end
10
+
11
+ def rcrm_acts_as_voter(*args)
12
+ # byebug
13
+ require 'redmine_crm/voter'
14
+ include ActsAsVotable::Voter
15
+
16
+ class_eval do
17
+ def self.voter?
18
+ true
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ # end
26
+ end
27
+ end