rapid 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +66 -0
- data/lib/rad/http_controller/acts_as/authenticated.rb +131 -0
- data/lib/rad/http_controller/acts_as/authenticated_master_domain.rb +119 -0
- data/lib/rad/http_controller/acts_as/authorized.rb +83 -0
- data/lib/rad/http_controller/acts_as/localized.rb +27 -0
- data/lib/rad/http_controller/acts_as/multitenant.rb +53 -0
- data/lib/rad/http_controller/helpers/service_mix_helper.rb +50 -0
- data/lib/rad/http_controller.rb +15 -0
- data/lib/rad/lib/text_utils.rb +334 -0
- data/lib/rad/locales/en.yml +80 -0
- data/lib/rad/locales/ru.yml +83 -0
- data/lib/rad/locales.rb +2 -0
- data/lib/rad/models/account.rb +88 -0
- data/lib/rad/models/default_permissions.yml +26 -0
- data/lib/rad/models/micelaneous.rb +1 -0
- data/lib/rad/models/role.rb +88 -0
- data/lib/rad/models/secure_token.rb +33 -0
- data/lib/rad/models/space.rb +184 -0
- data/lib/rad/models/user.rb +158 -0
- data/lib/rad/models.rb +41 -0
- data/lib/rad/mongo_mapper/acts_as/authenticated_by_open_id.rb +29 -0
- data/lib/rad/mongo_mapper/acts_as/authenticated_by_password.rb +120 -0
- data/lib/rad/mongo_mapper/acts_as/authorized.rb +197 -0
- data/lib/rad/mongo_mapper/acts_as/authorized_object.rb +171 -0
- data/lib/rad/mongo_mapper/multitenant.rb +34 -0
- data/lib/rad/mongo_mapper/rad_micelaneous.rb +43 -0
- data/lib/rad/mongo_mapper/space_keys.rb +62 -0
- data/lib/rad/mongo_mapper/text_processor.rb +47 -0
- data/lib/rad/mongo_mapper.rb +20 -0
- data/lib/rad/paperclip/callbacks.rb +40 -0
- data/lib/rad/paperclip/extensions.rb +64 -0
- data/lib/rad/paperclip/integration.rb +165 -0
- data/lib/rad/paperclip/mime.rb +5 -0
- data/lib/rad/paperclip/validations.rb +64 -0
- data/lib/rad/paperclip.rb +11 -0
- data/lib/rad/spec/controller.rb +51 -0
- data/lib/rad/spec/model/factories.rb +65 -0
- data/lib/rad/spec/model.rb +85 -0
- data/lib/rad/spec/rem_helper.rb +145 -0
- data/lib/rad/spec.rb +4 -0
- data/lib/rad/tasks/backup.rake +64 -0
- data/lib/rad/tasks/initialize.rake +35 -0
- data/lib/rad.rb +32 -0
- data/readme.md +3 -0
- data/spec/controller/authorization_spec.rb +146 -0
- data/spec/controller/helper.rb +14 -0
- data/spec/lib/helper.rb +7 -0
- data/spec/lib/text_utils_spec.rb +238 -0
- data/spec/models/authorization_spec.rb +93 -0
- data/spec/models/authorized_object_spec.rb +258 -0
- data/spec/models/file_audit_spec/100.txt +1 -0
- data/spec/models/file_audit_spec/302.txt +3 -0
- data/spec/models/file_audit_spec.rb +168 -0
- data/spec/models/helper.rb +11 -0
- data/spec/models/space_key_spec.rb +68 -0
- data/spec/models/user_spec.rb +80 -0
- data/spec/mongo_mapper/basic_spec.rb +41 -0
- data/spec/mongo_mapper/helper.rb +10 -0
- data/spec/spec.opts +4 -0
- 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&mode=content&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('<', '<').gsub('>', '>').gsub('"', '"'), '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: "Всем"
|
data/lib/rad/locales.rb
ADDED
@@ -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/
|