integrate admin dropdown
[shapado:shapado.git] / app / models / group.rb
1 class Group
2   include Mongoid::Document
3   include Mongoid::Timestamps
4
5   include MongoidExt::Slugizer
6   include MongoidExt::Storage
7   include MongoidExt::Filter
8
9   include Shapado::Models::CustomHtmlMethods
10
11   BLACKLIST_GROUP_NAME = ["www", "net", "org", "admin", "ftp", "mail", "test", "blog",
12                  "bug", "bugs", "dev", "ftp", "forum", "community", "mail", "email",
13                  "webmail", "pop", "pop3", "imap", "smtp", "stage", "stats", "status",
14                  "support", "survey", "download", "downloads", "faqs", "wiki",
15                  "assets1", "assets2", "assets3", "assets4", "staging", "code"]
16
17   identity :type => String
18
19   field :name, :type => String
20   field :subdomain, :type => String
21   field :domain, :type => String
22   index :domain, :unique => true
23   field :legend, :type => String
24   field :description, :type => String
25   field :default_tags, :type => Array, :default => []
26   field :has_custom_ads, :type => Boolean, :default => true
27   field :state, :type => String, :default => "pending" #pending, active, closed
28   field :isolate, :type => Boolean, :default => false
29   field :private, :type => Boolean, :default => false
30   field :theme, :type => String, :default => "plain"
31   field :owner_id, :type => String
32   field :analytics_id, :type => String
33   field :analytics_vendor, :type => String
34   field :has_custom_analytics, :type => Boolean, :default => true
35
36   field :language, :type => String
37   field :languages, :type => Set, :default => Set.new
38   index :languages
39
40   field :activity_rate, :type => Float, :default => 0.0
41   field :openid_only, :type => Boolean, :default => false
42   field :registered_only, :type => Boolean, :default => false
43   field :has_adult_content, :type => Boolean, :default => false
44
45   field :wysiwyg_editor, :type => Boolean, :default => false
46
47   field :has_reputation_constrains, :type => Boolean, :default => true
48   field :reputation_rewards, :type => Hash, :default => REPUTATION_REWARDS
49   field :reputation_constrains, :type => Hash, :default => REPUTATION_CONSTRAINS
50   field :forum, :type => Boolean, :default => false
51
52   embeds_one :custom_html
53   field :has_custom_html, :type => Boolean, :default => true
54   field :has_custom_js, :type => Boolean, :default => true
55   field :fb_button, :type => Boolean, :default => true
56
57   field :enable_latex, :type => Boolean, :default => false
58
59
60   field :logo_info, :type => Hash, :default => {"width" => 215, "height" => 60}
61   embeds_one :share
62
63   embeds_on notification_opts, :class_name => "GroupNotificationConfig"
64
65   field :twitter_account, :type => Hash, :default => { }
66
67   file_key :logo, :max_length => 2.megabytes
68   file_key :custom_css, :max_length => 256.kilobytes
69   file_key :custom_favicon, :max_length => 256.kilobytes
70
71   slug_key :name, :unique => true
72   filterable_keys :name
73
74   references_many :ads, :dependent => :destroy
75   references_many :tags, :dependent => :destroy
76
77   embeds_many :mainlist_widgets, :class_name => "Widget", :as => "group_mainlist"
78   embeds_many :question_widgets, :class_name => "Widget", :as => "group_questions"
79   embeds_many :external_widgets, :class_name => "Widget", :as => "group_external"
80
81   references_many :badges, :dependent => :destroy, :validate => false
82   references_many :questions, :dependent => :destroy, :validate => false
83   references_many :answers, :dependent => :destroy, :validate => false
84 #   references_many :votes, :dependent => :destroy # FIXME:
85   references_many :pages, :dependent => :destroy
86   references_many :announcements, :dependent => :destroy
87   references_many :constrains_configs, :dependent => :destroy
88
89   referenced_in :owner, :class_name => "User"
90   embeds_many :comments
91
92   validates_presence_of     :owner
93   validates_presence_of     :name
94
95   validates_length_of       :name,           :in => 3..40
96   validates_length_of       :description,    :in => 3..10000, :allow_blank => true
97   validates_length_of       :legend,         :maximum => 50
98   validates_length_of       :default_tags,   :in => 0..15,
99       :message =>  I18n.t('activerecord.models.default_tags_message')
100   validates_uniqueness_of   :name
101   validates_uniqueness_of   :subdomain
102   validates_presence_of     :subdomain
103   validates_format_of       :subdomain, :with => /^[a-z0-9\-]+$/i
104   validates_length_of       :subdomain, :in => 3..32
105
106   validates_inclusion_of :language, :in => AVAILABLE_LANGUAGES, :allow_blank => true
107   #validates_inclusion_of :theme, :in => AVAILABLE_THEMES
108
109   validate :set_subdomain, :on => :create
110   validate :check_domain, :on => :create
111
112   validate :check_reputation_configs
113
114   validates_exclusion_of      :subdomain,
115                               :in => BLACKLIST_GROUP_NAME,
116                               :message => "Sorry, this group subdomain is reserved by"+
117                                           " our system, please choose another one"
118
119   before_save :disallow_javascript
120   before_save :modify_attributes
121
122   # TODO: store this variable
123   def has_custom_domain?
124     @has_custom_domain ||= self[:domain].to_s !~ /#{AppConfig.domain}/
125   end
126
127   def tag_list
128     TagList.where(:group_id => self.id).first || TagList.create(:group_id => self.id)
129   end
130
131   def top_tags(limit=5)
132     tags.desc(:count).limit(limit)
133   end
134
135   def top_tags_strings(limit=5)
136     tags = []
137     top_tags(limit).each do |tag|
138       tags << [tag.name]
139     end
140     tags
141   end
142
143   def top_users(limit=5)
144     users.desc(:followers_count).limit(limit)
145   end
146
147   def default_tags=(c)
148     if c.kind_of?(String)
149       c = c.downcase.split(",").join(" ").split(" ")
150     end
151     self[:default_tags] = c
152   end
153   alias :user :owner
154
155   def add_member(user, role)
156     membership = user.config_for(self.id, true)
157     if membership.reputation < 5
158       membership.reputation = 5
159     end
160     membership.role = role
161
162     user.save
163   end
164
165   def is_member?(user)
166     user.member_of?(self)
167   end
168
169   def users(conditions = {})
170     conditions.merge!("membership_list.#{self.id}.reputation" => {:$exists => true})
171
172     unless conditions[:near]
173       User.where(conditions)
174     else
175       point = options.delete(:near)
176       User.near(point, {}).where(conditions)
177     end
178   end
179   alias_method :members, :users
180
181   def owners
182     users({ "membership_list.#{self.id}.role" => 'owner' })
183   end
184
185   def mods
186     users({ "membership_list.#{self.id}.role" => 'moderator' })
187   end
188   alias_method :moderators, :mods
189
190   def mods_owners
191     users({ "membership_list.#{self.id}.role" => {:$in => ['moderator', 'owner']} })
192   end
193
194   def pending?
195     state == "pending"
196   end
197
198   def on_activity(action)
199     value = 0
200     case action
201       when :ask_question
202         value = 0.1
203       when :answer_question
204         value = 0.3
205     end
206     self.increment(:activity_rate => value)
207   end
208
209   def language=(lang)
210     if lang != "none"
211       self[:language] = lang
212     else
213       self[:language] = nil
214     end
215   end
216
217   def self.humanize_reputation_constrain(key)
218     I18n.t("groups.shared.reputation_constrains.#{key}", :default => key.humanize)
219   end
220
221   def self.humanize_reputation_rewards(key)
222     I18n.t("groups.shared.reputation_rewards.#{key}", :default => key.humanize)
223   end
224
225   def self.find_file_from_params(params, request)
226     if request.path =~ /\/(logo|css|favicon)\/([^\/\.?]+)/
227       @group = Group.find($2)
228       case $1
229       when "logo"
230         @group.logo
231       when "css"
232         if @group.has_custom_css?
233           css=@group.custom_css
234           css.content_type = "text/css"
235           css
236         end
237       when "favicon"
238         @group.custom_favicon if @group.has_custom_favicon?
239       end
240     end
241   end
242
243   def reset_twitter_account
244     self.twitter_account = { }
245     self.save!
246   end
247
248   def update_twitter_account_with_oauth_token(token, secret, screen_name)
249     self.twitter_account = self.twitter_account ? self.twitter_account : { }
250     self.twitter_account["token"] = token
251     self.twitter_account["secret"] = secret
252     self.twitter_account["screen_name"] = screen_name
253     self.save!
254   end
255
256   def has_twitter_oauth?
257     self.twitter_account && self.twitter_account["token"] && self.twitter_account["secret"]
258   end
259
260   def twitter_client
261       if self.has_twitter_oauth? && (config = Multiauth.providers["Twitter"])
262         TwitterOAuth::Client.new(
263           :consumer_key => config["id"],
264           :consumer_secret => config["token"],
265           :token => self.twitter_account["token"],
266           :secret => self.twitter_account["secret"]
267         )
268       end
269   end
270   protected
271   #validations
272   def set_subdomain
273     self["subdomain"] = self["slug"]
274   end
275
276   def check_domain
277     if domain.blank?
278       self[:domain] = "#{subdomain}.#{AppConfig.domain}"
279     end
280   end
281
282   def check_reputation_configs
283     if self.reputation_constrains_changed?
284       self.reputation_constrains.each do |k,v|
285         self.reputation_constrains[k] = v.to_i
286         if !REPUTATION_CONSTRAINS.has_key?(k)
287           self.errors.add(:reputation_constrains, "Invalid key")
288           return false
289         end
290       end
291     end
292
293     if self.reputation_rewards_changed?
294       valid = true
295       [["vote_up_question", "undo_vote_up_question"],
296        ["vote_down_question", "undo_vote_down_question"],
297        ["question_receives_up_vote", "question_undo_up_vote"],
298        ["question_receives_down_vote", "question_undo_down_vote"],
299        ["vote_up_answer", "undo_vote_up_answer"],
300        ["vote_down_answer", "undo_vote_down_answer"],
301        ["answer_receives_up_vote", "answer_undo_up_vote"],
302        ["answer_receives_down_vote", "answer_undo_down_vote"],
303        ["answer_picked_as_solution", "answer_unpicked_as_solution"]].each do |action, undo|
304         if self.reputation_rewards[action].to_i > (self.reputation_rewards[undo].to_i*-1)
305           valid = false
306           self.errors.add(undo, "should be less than #{(self.reputation_rewards[action].to_i)*-1}")
307         end
308       end
309       return false unless valid
310
311       self.reputation_rewards.each do |k,v|
312         self.reputation_rewards[k] = v.to_i
313         if !REPUTATION_REWARDS.has_key?(k)
314           self.errors.add(:reputation_rewards, "Invalid key")
315           return false
316         end
317       end
318     end
319
320     return true
321   end
322
323   #callbacks
324   def modify_attributes
325     self.domain.downcase!
326     self.subdomain.downcase!
327     self.languages << self.language
328   end
329
330   def disallow_javascript
331     unless self.has_custom_js
332        %w[footer _head _question_help _question_prompt head_tag].each do |key|
333          value = self.custom_html[key]
334          if value.kind_of?(Hash)
335            value.each do |k,v|
336              value[k] = v.to_s.gsub(/<*.?script.*?>/, "")
337            end
338          elsif value.kind_of?(String)
339            value = value.gsub(/<*.?script.*?>/, "")
340          end
341          self.custom_html[key] = value
342        end
343     end
344   end
345 end