rapid 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/Rakefile +66 -0
  2. data/lib/rad/http_controller/acts_as/authenticated.rb +131 -0
  3. data/lib/rad/http_controller/acts_as/authenticated_master_domain.rb +119 -0
  4. data/lib/rad/http_controller/acts_as/authorized.rb +83 -0
  5. data/lib/rad/http_controller/acts_as/localized.rb +27 -0
  6. data/lib/rad/http_controller/acts_as/multitenant.rb +53 -0
  7. data/lib/rad/http_controller/helpers/service_mix_helper.rb +50 -0
  8. data/lib/rad/http_controller.rb +15 -0
  9. data/lib/rad/lib/text_utils.rb +334 -0
  10. data/lib/rad/locales/en.yml +80 -0
  11. data/lib/rad/locales/ru.yml +83 -0
  12. data/lib/rad/locales.rb +2 -0
  13. data/lib/rad/models/account.rb +88 -0
  14. data/lib/rad/models/default_permissions.yml +26 -0
  15. data/lib/rad/models/micelaneous.rb +1 -0
  16. data/lib/rad/models/role.rb +88 -0
  17. data/lib/rad/models/secure_token.rb +33 -0
  18. data/lib/rad/models/space.rb +184 -0
  19. data/lib/rad/models/user.rb +158 -0
  20. data/lib/rad/models.rb +41 -0
  21. data/lib/rad/mongo_mapper/acts_as/authenticated_by_open_id.rb +29 -0
  22. data/lib/rad/mongo_mapper/acts_as/authenticated_by_password.rb +120 -0
  23. data/lib/rad/mongo_mapper/acts_as/authorized.rb +197 -0
  24. data/lib/rad/mongo_mapper/acts_as/authorized_object.rb +171 -0
  25. data/lib/rad/mongo_mapper/multitenant.rb +34 -0
  26. data/lib/rad/mongo_mapper/rad_micelaneous.rb +43 -0
  27. data/lib/rad/mongo_mapper/space_keys.rb +62 -0
  28. data/lib/rad/mongo_mapper/text_processor.rb +47 -0
  29. data/lib/rad/mongo_mapper.rb +20 -0
  30. data/lib/rad/paperclip/callbacks.rb +40 -0
  31. data/lib/rad/paperclip/extensions.rb +64 -0
  32. data/lib/rad/paperclip/integration.rb +165 -0
  33. data/lib/rad/paperclip/mime.rb +5 -0
  34. data/lib/rad/paperclip/validations.rb +64 -0
  35. data/lib/rad/paperclip.rb +11 -0
  36. data/lib/rad/spec/controller.rb +51 -0
  37. data/lib/rad/spec/model/factories.rb +65 -0
  38. data/lib/rad/spec/model.rb +85 -0
  39. data/lib/rad/spec/rem_helper.rb +145 -0
  40. data/lib/rad/spec.rb +4 -0
  41. data/lib/rad/tasks/backup.rake +64 -0
  42. data/lib/rad/tasks/initialize.rake +35 -0
  43. data/lib/rad.rb +32 -0
  44. data/readme.md +3 -0
  45. data/spec/controller/authorization_spec.rb +146 -0
  46. data/spec/controller/helper.rb +14 -0
  47. data/spec/lib/helper.rb +7 -0
  48. data/spec/lib/text_utils_spec.rb +238 -0
  49. data/spec/models/authorization_spec.rb +93 -0
  50. data/spec/models/authorized_object_spec.rb +258 -0
  51. data/spec/models/file_audit_spec/100.txt +1 -0
  52. data/spec/models/file_audit_spec/302.txt +3 -0
  53. data/spec/models/file_audit_spec.rb +168 -0
  54. data/spec/models/helper.rb +11 -0
  55. data/spec/models/space_key_spec.rb +68 -0
  56. data/spec/models/user_spec.rb +80 -0
  57. data/spec/mongo_mapper/basic_spec.rb +41 -0
  58. data/spec/mongo_mapper/helper.rb +10 -0
  59. data/spec/spec.opts +4 -0
  60. metadata +138 -0
@@ -0,0 +1,334 @@
1
+ require 'digest/md5'
2
+
3
+ require 'nokogiri'
4
+ require 'BlueCloth'
5
+ require 'sanitize'
6
+
7
+ # String.class_eval do
8
+ # def to_url_with_escape
9
+ # to_url_without_escape.gsub /[^a-z0-9_-]/, ''
10
+ # end
11
+ # alias_method_chain :to_url, :escape
12
+ # end
13
+
14
+ class TextUtils
15
+ RELAXED = {
16
+ :elements => [
17
+ 'a', 'b', 'blockquote', 'br', 'caption', 'cite', 'code', 'col',
18
+ 'colgroup', 'dd', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
19
+ 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'small', 'strike', 'strong',
20
+ 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'u',
21
+ 'ul', 'div', 'font', 'span'],
22
+
23
+ :attributes => {
24
+ :all => ['class', 'style'],
25
+ 'a' => ['href', 'title', 'rel'],
26
+ 'blockquote' => ['cite'],
27
+ 'col' => ['span', 'width'],
28
+ 'colgroup' => ['span', 'width'],
29
+ 'img' => ['align', 'alt', 'height', 'src', 'title', 'width'],
30
+ 'ol' => ['start', 'type'],
31
+ 'q' => ['cite'],
32
+ 'table' => ['summary', 'width'],
33
+ 'td' => ['abbr', 'axis', 'colspan', 'rowspan', 'width'],
34
+ 'th' => ['abbr', 'axis', 'colspan', 'rowspan', 'scope', 'width'],
35
+ 'ul' => ['type']
36
+ },
37
+
38
+ :protocols => {
39
+ 'a' => {'href' => ['ftp', 'http', 'https', 'mailto', :relative]},
40
+ 'blockquote' => {'cite' => ['http', 'https', :relative]},
41
+ 'img' => {'src' => ['http', 'https', :relative]},
42
+ 'q' => {'cite' => ['http', 'https', :relative]}
43
+ }
44
+ }
45
+
46
+ class << self
47
+
48
+ def markup text
49
+ return text if text.blank?
50
+
51
+ text = gfm text
52
+ text = simplified_image_box text
53
+ text = hide_html_elements text # becouse markdown doesn't apply inside of html elements
54
+
55
+ text = do_markdown text
56
+
57
+ text = restore_html_elements text
58
+
59
+ text = StringParser.urls_to_links text
60
+
61
+ text = do_sanitaize text
62
+
63
+ text = embed_metaweb text
64
+ text = embed_tags text
65
+
66
+ text = text.gsub /[\n]+/, "\n"
67
+
68
+ # text = text.gsub("”", "\"").gsub("“", "\"") # hack for Maruku special symbols
69
+
70
+ # Escape all non-word unicode symbols, otherwise it will raise error when converting to BSON
71
+ text = Iconv.conv('UTF-8//IGNORE//TRANSLIT', 'UTF-8', text)
72
+
73
+ text
74
+ end
75
+
76
+ def random_string length = 3
77
+ @digits ||= ('a'..'z').to_a + (0..9).to_a
78
+ (0..(length-1)).map{@digits[rand(@digits.size)]}.join
79
+ end
80
+
81
+ def truncate str_or_html, length
82
+ str_or_html ||= ""
83
+
84
+ # Sanitize
85
+ str_or_html = do_sanitaize str_or_html
86
+
87
+ # Strip from HTML tags
88
+ str_or_html = str_or_html.gsub("<br", " <br").gsub("<p", " <p") # to preserve space in place of <> html elements
89
+ doc = Nokogiri::XML("<div class='root'>#{str_or_html}</div>")
90
+ str = doc.css('.root').first.content
91
+
92
+ str = str.gsub(/\s+/, ' ')
93
+
94
+
95
+ # Truncate with no broken words
96
+ if str.length >= length
97
+ shortened = str[0, length]
98
+ splitted = shortened.split(/\s/)
99
+ words = splitted.length
100
+ splitted[0, words-1].join(" ") + ' ...'
101
+ else
102
+ str
103
+ end
104
+ end
105
+
106
+ protected
107
+ def hide_html_elements text
108
+ text.gsub('<', 'HTML_BEGIN').gsub('>', 'HTML_END')
109
+ end
110
+
111
+ def restore_html_elements text
112
+ text.gsub('HTML_BEGIN', '<').gsub('HTML_END', '>')
113
+ end
114
+
115
+ def do_markdown text
116
+ # Maruku.new(text).to_html
117
+ text = text.gsub(" \n", "<br/>\n")
118
+
119
+ text = BlueCloth.new(text).to_html
120
+
121
+ text.gsub(/\A<.+?>/){"#{$&} "}.gsub(/<\/.+?>\Z/){" #{$&}"}
122
+ end
123
+
124
+ # Github Flawered Markdown
125
+ def gfm(text)
126
+ # Extract pre blocks
127
+ extractions = {}
128
+ text.gsub!(%r{<pre>.*?</pre>}m) do |match|
129
+ md5 = Digest::MD5.hexdigest(match)
130
+ extractions[md5] = match
131
+ "{gfm-extraction-#{md5}}"
132
+ end
133
+
134
+ # prevent foo_bar_baz from ending up with an italic word in the middle
135
+ text.gsub!(/(^(?! {4}|\t)\w+_\w+_\w[\w_]*)/) do |x|
136
+ x.gsub('_', '\_') if x.split('').sort.to_s[0..1] == '__'
137
+ end
138
+
139
+ # in very clear cases, let newlines become <br /> tags
140
+ text.gsub!(/^[\w\<\!][^\n]*\n+/) do |x|
141
+ if x =~ /\>[\n\r]*/
142
+ x
143
+ else
144
+ x =~ /\n{2}/ ? x : (x.strip!; x << " \n")
145
+ end
146
+ end
147
+
148
+ # Insert pre block extractions
149
+ text.gsub!(/\{gfm-extraction-([0-9a-f]{32})\}/) do
150
+ "\n\n" + extractions[$1]
151
+ end
152
+
153
+ text
154
+ end
155
+
156
+ def do_wikitext text
157
+ parser = Wikitext::Parser.new
158
+ parser.autolink = false
159
+ parser.internal_link_prefix = nil
160
+ parser.external_link_class = nil
161
+ parser.mailto_class = nil
162
+ parser.img_prefix = nil
163
+ parser.space_to_underscore = false
164
+ escaped_html = parser.parse(text)
165
+ html = CGI.unescapeHTML escaped_html
166
+ html
167
+ end
168
+
169
+ def do_sanitaize html
170
+ Sanitize.clean(html, RELAXED.merge(
171
+ :transformers => [EMBEDDED_VIDEO],
172
+ :add_attributes => {
173
+ :all => [:class]
174
+ }
175
+ ))
176
+ end
177
+
178
+ VIDEO_URLS = [
179
+ /^http:\/\/(?:www\.)?youtube\.com\/v\//,
180
+ ]
181
+
182
+ EMBEDDED_VIDEO = lambda do |env|
183
+ node = env[:node]
184
+ node_name = node.name.to_s.downcase
185
+ parent = node.parent
186
+
187
+ # Since the transformer receives the deepest nodes first, we look for a
188
+ # <param> element or an <embed> element whose parent is an <object>.
189
+ return nil unless (node_name == 'param' || node_name == 'embed') && parent.name.to_s.downcase == 'object'
190
+
191
+ if node_name == 'param'
192
+ # Quick XPath search to find the <param> node that contains the video URL.
193
+ return nil unless movie_node = parent.search('param[@name="movie"]')[0]
194
+ url = movie_node['value']
195
+ else
196
+ # Since this is an <embed>, the video URL is in the "src" attribute. No
197
+ # extra work needed.
198
+ url = node['src']
199
+ end
200
+
201
+ # # Verify that the video URL is actually a valid YouTube video URL.
202
+ return nil unless VIDEO_URLS.any?{|t| url =~ t}
203
+
204
+ # # We're now certain that this is a YouTube embed, but we still need to run
205
+ # # it through a special Sanitize step to ensure that no unwanted elements or
206
+ # # attributes that don't belong in a YouTube embed can sneak in.
207
+ Sanitize.clean_node!(parent, {
208
+ :elements => ['embed', 'object', 'param'],
209
+ :attributes => {
210
+ 'embed' => ['allowfullscreen', 'allowscriptaccess', 'height', 'src', 'type', 'width'],
211
+ 'object' => ['height', 'width'],
212
+ 'param' => ['name', 'value']
213
+ }
214
+ })
215
+
216
+ # Now that we're sure that this is a valid YouTube embed and that there are
217
+ # no unwanted elements or attributes hidden inside it, we can tell Sanitize
218
+ # to whitelist the current node (<param> or <embed>) and its parent
219
+ # (<object>).
220
+ {:whitelist_nodes => [node, parent]}
221
+ end
222
+
223
+ # !![img] => [![img_thumb]][img]
224
+ def simplified_image_box text
225
+ img_urls = {}
226
+ text = text.gsub(/!!\[(.+?)\]/) do
227
+ img_key = $1
228
+ if url = text.scan(/\[#{img_key}\]:\s*([^\s]+)$/).first.try(:first)
229
+ unless url =~ /\.[^\.]+\.[^\.]+$/ # image.png
230
+ thumb_img_key = "#{img_key}_thumb"
231
+
232
+ # building url with thumb (foo.png => foo.thumb.png)
233
+ img_urls[thumb_img_key] = url.sub(/\.([^\.]+)$/){".thumb.#{$1}"}
234
+
235
+ "[![][#{thumb_img_key}]][#{img_key}]"
236
+ else # image.(icon|thumb|...).png
237
+ img_key_full = "#{img_key}_full"
238
+
239
+ # building url with thumb (foo.png => foo.thumb.png)
240
+ img_urls[img_key_full] = url.sub(/\.([^\.]+)\.([^\.]+)$/){".#{$2}"}
241
+
242
+ "[![][#{img_key}]][#{img_key_full}]"
243
+ end
244
+ else
245
+ $&
246
+ end
247
+ end
248
+
249
+ unless img_urls.blank?
250
+ text << "\n"
251
+ text << img_urls.to_a.collect{|k, v| "[#{k}]: #{v}"}.join("\n")
252
+ end
253
+ text
254
+ end
255
+
256
+ # {metaweb:google_web_toolkit} => wiget html
257
+ def embed_metaweb text
258
+ html = <<HTML
259
+ <div itemtype="http://www.freebase.com/id/computer/software" itemid="http://www.freebase.com/id/en/google_web_toolkit" itemscope="" style="border: 0pt none; outline: 0pt none; padding: 0pt; margin: 0pt; position: relative;" id="fbtb-6ffc2545598340cbbc7945f43ebd45de" class="fb-widget">
260
+ <iframe frameborder="0" scrolling="no" src="http://www.freebase.com/widget/topic?track=topicblocks_homepage&amp;mode=content&amp;id=%2Fen%2F_topic_name_" style="height: 285px; width: 413px; border: 0pt none; outline: 0pt none; padding: 0pt; margin: 0pt;" classname="fb-widget-iframe" allowtransparency="true" class=" "></iframe>
261
+ <script defer="" type="text/javascript" src="http://freebaselibs.com/static/widgets/2/widget.js"></script>
262
+ </div>
263
+ HTML
264
+ text.gsub(/\{metaweb:(.+?)\}/){html.gsub('_topic_name_', $1)}
265
+ end
266
+
267
+ TAGS = {
268
+ /\[clear\]/ => lambda{"<div class='clear'></div>"},
269
+ /\[space\]/ => lambda{"<div class='space'></div>"}
270
+ }
271
+
272
+ def embed_tags text
273
+ TAGS.each do |k, v|
274
+ text.gsub!(k, &v)
275
+ end
276
+ text
277
+ end
278
+
279
+
280
+ # def slug text
281
+ # return "" if text.blank?
282
+ # text.gsub(/[^A-Za-z0-9\s\-]/, "")[0,20].strip.gsub(/\s+/, "-").downcase
283
+ # end
284
+ end
285
+
286
+
287
+ # Code ripped from StringParser, http://github.com/snitko/string_parser/blob/master/lib/string_parser.rb
288
+ module StringParser
289
+ class << self
290
+
291
+ # Creates <a> tags for all urls.
292
+ # IMPORTANT: make sure you've used #urls_to_images method first
293
+ # if you wanted all images urls to become <img> tags.
294
+ def urls_to_links html
295
+ # becouse it finds only one url in such string "http://some_domain.com http://some_domain.com" we need to aply it twice
296
+ regexp, sub = /(\s|^|\A|\n|\t|\r)(http:\/\/.*?)([,.])?(\s|$|\n|\Z|\t|\r|<)/, '\1<a href="\2">\2</a>\3\4'
297
+ html = html.gsub regexp, sub
298
+ html.gsub regexp, sub
299
+ html
300
+ end
301
+
302
+ # Highlights code using 'uv' library.
303
+ # Make sure you have ultraviolet gem installed.
304
+ def highlight_code(options={})
305
+ require 'uv'
306
+
307
+ wrap_with = options[:wrap_with] || ['','']
308
+ text = @modified_string
309
+
310
+ languages_syntax_list = File.readlines(
311
+ File.expand_path(File.dirname(__FILE__) + '/../config/languages_syntax_list')
312
+ ).map { |l| l.chomp }
313
+
314
+ text.gsub!(/<code(\s*?lang=["']?(.*?)["']?)?>(.*?)<\/code>/) do
315
+ if languages_syntax_list.include?($2)
316
+ lang = $2
317
+ else
318
+ lang = 'ruby'
319
+ end
320
+ unless $3.blank?
321
+ result = Uv.parse($3.gsub('<br/>', "\n").gsub('&lt;', '<').gsub('&gt;', '>').gsub('&quot;', '"'), 'xhtml', lang, false, 'active4d')
322
+ "#{wrap_with[0].gsub('$lang', lang)}#{result}#{wrap_with[1]}"
323
+ end
324
+ end
325
+
326
+ # TODO: split string longer than 80 characters
327
+
328
+ @modified_string = text
329
+ self
330
+
331
+ end
332
+ end
333
+ end
334
+ end
@@ -0,0 +1,80 @@
1
+ en:
2
+ #
3
+ # Authentication
4
+ #
5
+ access_denied: "Access Denied!"
6
+ cas_try_more: "Please try again"
7
+ choose_name: "Choose username"
8
+ choose_name_description: "Choose username (can't be changed in future)"
9
+ edit_password_title: "Update password"
10
+ email_verification_code_sent: "To complete registration please follow the link sent to your email %{email}."
11
+ failed_reset_password: "No user with such email %{email}!"
12
+ forgot_password_hint: "Link for password restore will be send to your email"
13
+ forgot_password_link: "Forgot password?"
14
+ forgot_password: "Restore password"
15
+ invalid_email_verification_token: "Email confirmation code is invalid or expired"
16
+ invalid_identity: "No user with such ID (%{identity_url})"
17
+ invalid_login: "Invalid login or password"
18
+ invalid_old_password: "Invalid old password"
19
+ invalid_reset_password_token: "The code for password restoration is invalid or expired"
20
+ login_title: "Login"
21
+ login_using_open_id: "Login using OpenID"
22
+ new_password: "New password"
23
+ new_user_title: "Signup"
24
+ not_unique_email: "User with such email already exists"
25
+ old_password: "Old password"
26
+ open_id: "OpenID"
27
+ openid_identifier: "OpenID"
28
+ password_restored: "Password updated"
29
+ password_updated: "Pasword updated"
30
+ register_link: "Signup"
31
+ reset_password: "Reset password"
32
+ should_not_be_blank: "should not be blank"
33
+ signup_email_description: "To finish registration please follow the link that will be send to your email"
34
+ successfully_identified_by_open_id: "Identification is successfully finished"
35
+ successfully_logged_in: "You are logged in"
36
+ successfully_logged_out: "You are logged out"
37
+ successfull_open_id_registration: "You are successfully registered and can log in"
38
+ successfully_registered: "You are successfully registered"
39
+ sucessfully_reset_password: "Link for password restoration sent to your email: %{email}"
40
+ update_password: "Update password"
41
+
42
+ #
43
+ # EMail
44
+ #
45
+ email_verification_title: "Registration on site %{host}"
46
+ email_verification_text: |
47
+ To finish registration on site %{host} please follow this link:
48
+ %{url}
49
+
50
+ forgot_password_title: "Password reset for %{name} on the %{host} site"
51
+ forgot_password_text: |
52
+ You (or somebody else by entering your email) requested password reset for your account %{name} on the %{host} site.
53
+
54
+ To reset password please follow this link %{url}
55
+ (or just delete this email and your password will remain intact)
56
+
57
+
58
+ #
59
+ # Paperclip
60
+ #
61
+ invalid_file_size: "File should be less than %{max_size} KB"
62
+ maximum_user_files_size_exceeded: "The maximum files size allowed for the user %{max_files_size} KB are exceeded (current files size is %{files_size} KB)"
63
+ maximum_space_files_size_exceeded: "The maximum files size allowed for the space %{max_files_size} KB are exceeded (current files size is %{files_size} KB)"
64
+
65
+ # email_registration_title: "%{name}, ваш аккаунт для %{host} создан"
66
+ # email_registration_text: |
67
+ # Ваш аккаунт для %{host} зарегистрирован.
68
+ #
69
+ # Имя пользователя %{name}
70
+ # Пароль %{password}
71
+ #
72
+ # Чтобы активировать его пожалуйста перейдите по ссылке:
73
+ # %{url}
74
+
75
+ #
76
+ # Authorization
77
+ #
78
+ # member_role: "Сообществу"
79
+ # owner_role: "Только мне"
80
+ # user_role: "Всем"
@@ -0,0 +1,83 @@
1
+ ru:
2
+ #
3
+ # Authentication
4
+ #
5
+ access_denied: "Доступ запрещен!"
6
+ cas_try_more: "Попробуйте еще раз"
7
+ choose_name: "Выберите имя пользователя"
8
+ choose_name_description: "Выберите имя пользователя (в дальнейшем изменить нельзя)"
9
+ edit_password_title: "Изменение пароля"
10
+ email_verification_code_sent: "Чтобы завершить регистрацию перейдите пожалуйста по ссылке высланной вам на почту %{email}"
11
+ failed_reset_password: "Пользвателя с почтой %{email} не существует!"
12
+ forgot_password_hint: "Ссылка для восстановления пароля будет выслана вам на почту"
13
+ forgot_password_link: "Забыли пароль?"
14
+ forgot_password: "Восстановление пароля"
15
+ invalid_email_verification_token: "Неверный код подтверждения почты, возможно у него истек срок действия"
16
+ invalid_identity: "Пользователя с таким ID не существует (%{identity_url})"
17
+ invalid_login: "Неверный логин или пароль"
18
+ invalid_old_password: "Неверный старый пароль"
19
+ invalid_reset_password_token: "Неверный код восстановления пароля, возможно у него истек срок действия"
20
+ login_title: "Войти"
21
+ login_using_open_id: "Войти используя OpenID"
22
+ new_password: "Новый пароль"
23
+ new_user_title: "Регистрация"
24
+ not_unique_email: "Пользователь с таким адресом уже существует"
25
+ old_password: "Старый пароль"
26
+ open_id: "OpenID"
27
+ openid_identifier: "OpenID"
28
+ password_restored: "Пароль изменен"
29
+ password_updated: "Пароль изменен"
30
+ register_link: "Регистрация"
31
+ reset_password: "Сброс пароля"
32
+ should_not_be_blank: "не заполнен"
33
+ signup_email_description: "Вам на почту будет выслана ссылка для завершения решистрации"
34
+ successfully_identified_by_open_id: "Идентификация успешно завершена"
35
+ successfully_logged_in: "Вы вошли"
36
+ successfully_logged_out: "Вы вышли"
37
+ successfull_open_id_registration: "Вы успешно зарегистрированы и можете войти"
38
+ successfully_registered: "Вы успешно зарегистрировались"
39
+ sucessfully_reset_password: "Ссылка для восстановления пароля выслана вам на почту: %{email}"
40
+ update_password: "Изменить пароль"
41
+
42
+ #
43
+ # EMail
44
+ #
45
+ email_verification_title: "Регистрация аккаунта на %{host}"
46
+ email_verification_text: |
47
+ Чтобы завершить решистрацию на %{host} перейдите пожалуйста по ссылке:
48
+ %{url}
49
+
50
+ # email_activation_title: "%{name}, ваш аккаунт на %{host} активирован"
51
+ # email_activation_text: |
52
+ # %{name} ваш аккаунт на %{host} активирован.
53
+
54
+ forgot_password_title: "Сброс пароля пользователя %{name} на %{host}"
55
+ forgot_password_text: |
56
+ Вы (или кто-то другой введя вашу почту) запросили сброс пароля для вашего аккаунта %{name} на %{host}
57
+
58
+ Чтобы сбросить пароль перейдите по ссылке %{url}
59
+ (или просто удалите это письмо, ваш пароль останется как и раньше)
60
+
61
+ #
62
+ # Paperclip
63
+ #
64
+ invalid_file_size: "Файл должен быть меньше %{max_size} КБ"
65
+ maximum_user_files_size_exceeded: "Превышен рабмер максимально допустимого обьема файлов для пользователя %{max_files_size} КБ (сейчас занято %{files_size} КБ)"
66
+ maximum_space_files_size_exceeded: "Превышен рабмер максимально допустимого обьема файлов для рабочего пространства %{max_files_size} КБ (сейчас занято %{files_size} КБ)"
67
+
68
+ # email_registration_title: "%{name}, ваш аккаунт для %{host} создан"
69
+ # email_registration_text: |
70
+ # Ваш аккаунт для %{host} зарегистрирован.
71
+ #
72
+ # Имя пользователя %{name}
73
+ # Пароль %{password}
74
+ #
75
+ # Чтобы активировать его пожалуйста перейдите по ссылке:
76
+ # %{url}
77
+
78
+ #
79
+ # Authorization
80
+ #
81
+ # member_role: "Сообществу"
82
+ # owner_role: "Только мне"
83
+ # user_role: "Всем"
@@ -0,0 +1,2 @@
1
+ dir = File.dirname __FILE__
2
+ I18n.load_path += Dir["#{dir}/locales/**/*.{rb,yml}"]
@@ -0,0 +1,88 @@
1
+ class Account
2
+ include MongoMapper::Document
3
+
4
+ #
5
+ # Multitenant
6
+ #
7
+ connect_to_global_database
8
+
9
+
10
+ key :name, String, :protected => true
11
+ key :title, String
12
+ key :domains, Array, :protected => true
13
+ key :web_analytics_js, String # Add JS Injection Protection TODO1
14
+
15
+ timestamps!
16
+
17
+
18
+ #
19
+ # Indexes
20
+ #
21
+ # defer do
22
+ ensure_index :name, :unique => true
23
+ ensure_index :domains
24
+ # end
25
+
26
+ #
27
+ # Validations
28
+ #
29
+ validates_presence_of :name
30
+ validates_length_of :name, :within => 2..20
31
+ validates_format_of :name, :with => /\A[a-z][a-z\-0-9]*[a-z0-9]\Z/
32
+ validates_exclusion_of :name, :within => %w{global}
33
+
34
+
35
+ #
36
+ # Form helpers
37
+ #
38
+ def domains_as_string
39
+ domains.join("\n")
40
+ end
41
+
42
+ def domains_as_string= str
43
+ self.domains = str.split("\n") unless str.nil?
44
+ end
45
+
46
+ many :spaces
47
+
48
+ before_validation :create_default_subdomain
49
+ def create_default_subdomain
50
+ default_subdomain = "#{name}.#{config.master_domain('localhost')}"
51
+ self.domains << default_subdomain unless name.blank? or domains.include?(default_subdomain)
52
+ end
53
+ protected :create_default_subdomain
54
+
55
+ after_create :create_default_space
56
+ def create_default_space
57
+ space = spaces.build
58
+ space.name = 'default'
59
+ space.save!
60
+ end
61
+ protected :create_default_space
62
+
63
+ def self.current= account
64
+ Thread.current['current_account'] = account
65
+ end
66
+
67
+ def self.current
68
+ Thread.current['current_account'].must_be.defined
69
+ end
70
+
71
+ def self.current?
72
+ !!Thread.current['current_account']
73
+ end
74
+
75
+ def select account_name, space_name = 'default'
76
+ Account.current = Account.find_by_name! account_name
77
+ Space.current = Account.current.spaces.find_by_name! space_name
78
+ end
79
+
80
+ #
81
+ # Files Audit
82
+ #
83
+ key :files_size, Integer, :default => 0, :protected => true
84
+ # defer do
85
+ key :max_file_size, Integer, :default => config.max_file_size(1.megabyte)
86
+ key :max_account_files_size, Integer, :default => config.max_account_files_size(100.megabytes)
87
+ # end
88
+ end
@@ -0,0 +1,26 @@
1
+ # General
2
+ view: [] #anonymous, registered
3
+
4
+ # Authorization
5
+ add_admin_role: []
6
+ add_custom_role: [manager]
7
+ add_manager_role: []
8
+ add_member_role: [manager]
9
+ remove_admin_role: []
10
+ remove_custom_role: [manager]
11
+ remove_manager_role: []
12
+ remove_member_role: [manager]
13
+ update_access: [manager, owner]
14
+
15
+ # User Management
16
+ update_profile: [owner]
17
+
18
+ # Items
19
+ create: [member]
20
+ # update: [manager, owner]
21
+ destroy: [manager, owner]
22
+
23
+ # Comments
24
+ create_comment: [registered]
25
+ update_comment: [manager, owner]
26
+ destroy_comment: [manager, owner]
@@ -0,0 +1 @@
1
+ STRONG_NAME = /\A[a-z_][a-z_0-9]*\Z/