rapid 0.0.1

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