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