redmine_crm 0.0.8 → 0.0.10

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.
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