rad_kit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +11 -0
- data/lib/components/kit.rb +16 -0
- data/lib/components/kit.yml +3 -0
- data/lib/components/models.rb +7 -0
- data/lib/kit/factories.rb +9 -0
- data/lib/kit/gems.rb +16 -0
- data/lib/kit/http_controller.rb +4 -0
- data/lib/kit/http_controller/authorized.rb +51 -0
- data/lib/kit/http_controller/localized.rb +13 -0
- data/lib/kit/kit.rb +29 -0
- data/lib/kit/models.rb +8 -0
- data/lib/kit/models/attachment_uploader.rb +15 -0
- data/lib/kit/models/attachments_uploader_helper.rb +79 -0
- data/lib/kit/models/authorized.rb +188 -0
- data/lib/kit/models/authorized_object.rb +167 -0
- data/lib/kit/models/default_permissions.yml +29 -0
- data/lib/kit/models/file_uploader.rb +26 -0
- data/lib/kit/models/micelaneous.rb +1 -0
- data/lib/kit/models/role.rb +88 -0
- data/lib/kit/models_after.rb +27 -0
- data/lib/kit/mongoid.rb +22 -0
- data/lib/kit/mongoid/rad_micelaneous.rb +36 -0
- data/lib/kit/mongoid/text_processor.rb +44 -0
- data/lib/kit/spec.rb +77 -0
- data/lib/kit/spec/items_controller_crud.rb +64 -0
- data/lib/kit/support.rb +14 -0
- data/lib/kit/support/string.rb +6 -0
- data/lib/kit/tasks.rb +18 -0
- data/lib/kit/text_utils.rb +43 -0
- data/lib/kit/text_utils/code_highlighter.rb +58 -0
- data/lib/kit/text_utils/custom_markdown.rb +90 -0
- data/lib/kit/text_utils/ensure_utf.rb +8 -0
- data/lib/kit/text_utils/github_flavoured_markdown.rb +32 -0
- data/lib/kit/text_utils/html_sanitizer.rb +89 -0
- data/lib/kit/text_utils/image_box.rb +35 -0
- data/lib/kit/text_utils/markup.rb +43 -0
- data/lib/kit/text_utils/processor.rb +25 -0
- data/lib/kit/text_utils/tag_shortcuts.rb +14 -0
- data/lib/kit/text_utils/truncate.rb +29 -0
- data/lib/kit/text_utils/truncator.rb +15 -0
- data/lib/kit/text_utils/urls.rb +13 -0
- data/readme.md +10 -0
- data/spec/controller/authorization_spec.rb +149 -0
- data/spec/controller/comments_spec.rb +54 -0
- data/spec/controller/items_spec.rb +45 -0
- data/spec/models/attachments_spec.rb +24 -0
- data/spec/models/attachments_spec/a.txt +1 -0
- data/spec/models/attachments_uploader_helper_spec.rb +108 -0
- data/spec/models/attachments_uploader_helper_spec/v1/a.txt +1 -0
- data/spec/models/attachments_uploader_helper_spec/v1/b.txt +1 -0
- data/spec/models/attachments_uploader_helper_spec/v2/a.txt +1 -0
- data/spec/models/authorization_spec.rb +77 -0
- data/spec/models/authorized_object_spec.rb +254 -0
- data/spec/models/comments_spec.rb +1 -0
- data/spec/models/item_spec.rb +51 -0
- data/spec/models/role_spec.rb +17 -0
- data/spec/models/tags_spec.rb +44 -0
- data/spec/models/uploader_spec.rb +37 -0
- data/spec/models/uploader_spec//321/204/320/260/320/270/314/206/320/273 /321/201 /320/277/321/200/320/276/320/261/320/265/320/273/320/260/320/274/320/270.txt" +1 -0
- data/spec/mongoid/basic_spec.rb +36 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/spec_helper/controller.rb +9 -0
- data/spec/spec_helper/factories.rb +24 -0
- data/spec/spec_helper/user.rb +17 -0
- data/spec/utils/text_utils_spec.rb +280 -0
- metadata +232 -0
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
rad.register :kit, depends_on: [:web, :assets, :common_interface, :models] do
|
2
|
+
require 'kit/kit'
|
3
|
+
|
4
|
+
rad.router.default_url = '/'
|
5
|
+
(rad.face.availiable_layouts[:default] ||= []) << :default
|
6
|
+
|
7
|
+
rad.configure :web, "#{__FILE__}/../../.." do |c|
|
8
|
+
c.routes
|
9
|
+
c.locales
|
10
|
+
c.template_paths 'app/views'
|
11
|
+
c.asset_paths 'app/static'
|
12
|
+
c.autoload_paths 'app'
|
13
|
+
end
|
14
|
+
|
15
|
+
Rad::Kit.new
|
16
|
+
end
|
data/lib/kit/gems.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# core gems
|
2
|
+
gem 'bluecloth', '2.0.9'
|
3
|
+
gem 'sanitize', '1.2.1'
|
4
|
+
gem 'stringex', '1.2.0'
|
5
|
+
gem 'state_machine', '0.10.4'
|
6
|
+
# gem 'paperclip', '2.3.1.1'
|
7
|
+
gem 'factory_girl', '1.3.3'
|
8
|
+
gem 'coderay', '0.9.7'
|
9
|
+
|
10
|
+
if respond_to? :fake_gem
|
11
|
+
fake_gem 'rad_core'
|
12
|
+
fake_gem 'rad_ext'
|
13
|
+
fake_gem 'rad_common_interface'
|
14
|
+
fake_gem 'rad_assets'
|
15
|
+
fake_gem 'mongoid_misc'
|
16
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Rad::Controller::Http::Authorized
|
2
|
+
inherited do
|
3
|
+
helper_method :can?, :owner?
|
4
|
+
end
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def require_permission operation, *args, &object_proc
|
8
|
+
operation = operation.must_be.a(String, Symbol).to_s
|
9
|
+
|
10
|
+
options = args.extract_options!
|
11
|
+
# object_proc = args.size > 0 ? args.first : lambda{}
|
12
|
+
object_proc ||= lambda{|controller|}
|
13
|
+
|
14
|
+
method = "require_permission_#{operation}"
|
15
|
+
define_method method do
|
16
|
+
require_permission operation, instance_eval(&object_proc)
|
17
|
+
end
|
18
|
+
before method, options
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
def can? *args
|
24
|
+
rad.user.can? *args
|
25
|
+
end
|
26
|
+
|
27
|
+
def owner? *args
|
28
|
+
rad.user.owner? *args
|
29
|
+
end
|
30
|
+
|
31
|
+
def login_required
|
32
|
+
access_denied! unless rad.user.registered?
|
33
|
+
end
|
34
|
+
|
35
|
+
def login_not_required
|
36
|
+
raise_user_error t(:login_not_required) if rad.user.registered?
|
37
|
+
end
|
38
|
+
|
39
|
+
def require_permission operation, object = nil
|
40
|
+
operation = operation.must_be.a(String, Symbol).to_s
|
41
|
+
|
42
|
+
unless rad.user.can? operation, object
|
43
|
+
rad.logger.warn "RAD access denied, #{rad.user.name} hasn't rights to #{operation}!"
|
44
|
+
access_denied!
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def access_denied!
|
49
|
+
raise_user_error t(:access_denied)
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Rad::Controller::Http::Localized
|
2
|
+
inherited do
|
3
|
+
before :prepare_locale
|
4
|
+
end
|
5
|
+
|
6
|
+
protected
|
7
|
+
def prepare_locale
|
8
|
+
language = rad.environment.language
|
9
|
+
I18n.locale = params.l || language
|
10
|
+
# Delete l from params if language is the same as default
|
11
|
+
params.delete :l if params.l == language
|
12
|
+
end
|
13
|
+
end
|
data/lib/kit/kit.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'kit/support'
|
2
|
+
|
3
|
+
# Configs
|
4
|
+
class Rad::Kit
|
5
|
+
attr_accessor :default_item, :fs_prefix, :fs_type, :fs_cache_path, :fs_path, :tags_count
|
6
|
+
require_attr :default_item, :tags_count, :fs_prefix, :fs_type, :fs_path, :fs_cache_path
|
7
|
+
def items; @items ||= [] end
|
8
|
+
end
|
9
|
+
|
10
|
+
rad.router.class.class_eval do
|
11
|
+
attr_accessor :default_url
|
12
|
+
require_attr :default_url
|
13
|
+
end
|
14
|
+
|
15
|
+
rad.config.class.class_eval do
|
16
|
+
def custom_roles; @custom_roles ||= [] end
|
17
|
+
def permissions; @permissions ||= {} end
|
18
|
+
def default_viewers; @default_viewers ||= [] end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
# Kit
|
23
|
+
#
|
24
|
+
# TODO3 move :text_utils to standalone text_utils gem
|
25
|
+
%w(
|
26
|
+
support
|
27
|
+
text_utils
|
28
|
+
http_controller
|
29
|
+
).each{|f| require "kit/#{f}"}
|
data/lib/kit/models.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
class Models::AttachmentUploader < Models::FileUploader
|
2
|
+
rad.extension :attachment_file_path, self do
|
3
|
+
define_method :file_path do
|
4
|
+
"#{rad.kit.fs_prefix}/#{model.item.id}"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
version :icon do
|
9
|
+
process resize_to_fit: [50, 50]
|
10
|
+
end
|
11
|
+
|
12
|
+
version :thumb do
|
13
|
+
process resize_to_fit: [150, 150]
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Mongoid::AttachmentsUploaderHelper
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
class FileHelper
|
5
|
+
attr_reader :object
|
6
|
+
def initialize object
|
7
|
+
@object = object
|
8
|
+
end
|
9
|
+
|
10
|
+
def file?
|
11
|
+
object.is_a?(Hash) or object.is_a?(IO)
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
if object.is_a?(Hash)
|
16
|
+
object['filename'] || object[:filename]
|
17
|
+
elsif object.is_a?(IO)
|
18
|
+
File.basename(object.path)
|
19
|
+
else
|
20
|
+
object
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_attachments association_name, field_name
|
26
|
+
send(association_name).
|
27
|
+
sort{|a, b| a.send(field_name).name <=> b.send(field_name).name}.
|
28
|
+
collect{|o| {name: o.send(field_name).name, url: o.send(field_name).url}.to_openobject}
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_attachments association_name, field_name, values
|
32
|
+
association = send(association_name)
|
33
|
+
existing_names = association.collect{|o| o.send(field_name).name}.sort
|
34
|
+
|
35
|
+
add = values.select do |o|
|
36
|
+
h = FileHelper.new o
|
37
|
+
h.file? and !existing_names.include?(h.name)
|
38
|
+
end
|
39
|
+
update = values.select do |o|
|
40
|
+
h = FileHelper.new o
|
41
|
+
h.file? and existing_names.include?(h.name)
|
42
|
+
end
|
43
|
+
remove = association.select do |model|
|
44
|
+
values.none? do |o|
|
45
|
+
h = FileHelper.new o
|
46
|
+
model.send(field_name).name == h.name
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
add.each{|file| association.build field_name => file}
|
51
|
+
update.each do |file|
|
52
|
+
h = FileHelper.new file
|
53
|
+
association.each do |model|
|
54
|
+
if model.send(field_name).name == h.name
|
55
|
+
model.send "#{field_name}=", file
|
56
|
+
break
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
remove.each do |model|
|
61
|
+
association.where(_id: model.id).destroy_all
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module ClassMethods
|
66
|
+
def mount_attachments_uploader association_name, field_name
|
67
|
+
define_method "#{association_name}_as_attachments" do
|
68
|
+
get_attachments association_name, field_name
|
69
|
+
end
|
70
|
+
|
71
|
+
define_method "#{association_name}_as_attachments=" do |values|
|
72
|
+
set_attachments association_name, field_name, values
|
73
|
+
end
|
74
|
+
|
75
|
+
# we can't allow to destroy model with changed attachments because it's too complicated to support this case.
|
76
|
+
before_destroy{|o| raise "Can't destroy item with changed attachments!" if o.changes.include? association_name}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
module Mongoid::Authorized
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
class << self
|
5
|
+
attr_writer :custom_permissions
|
6
|
+
def custom_permissions; @custom_permissions ||= {} end
|
7
|
+
def permissions
|
8
|
+
@default_permissions ||= YAML.load_file("#{__FILE__.dirname}/default_permissions.yml").freeze
|
9
|
+
@default_permissions.merge(rad.config.permissions).merge(custom_permissions)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
included do
|
14
|
+
validate :validate_anonymous
|
15
|
+
validates_exclusion_of :name, in: Role::PRESERVED_USER_NAMES, if: lambda{|u| u.new_record?}
|
16
|
+
|
17
|
+
rad.extension :model_authorization, self do
|
18
|
+
field :roles, type: Array, protected: true, default: []
|
19
|
+
alias_method :mm_roles, :roles
|
20
|
+
alias_method :mm_roles=, :roles=
|
21
|
+
|
22
|
+
field :admin, type: Boolean, protected: true, default: false
|
23
|
+
end
|
24
|
+
|
25
|
+
alias_method :roles, :handy_roles
|
26
|
+
end
|
27
|
+
|
28
|
+
module ClassMethods
|
29
|
+
def anonymous
|
30
|
+
Models::User.by_name('anonymous') || raise("You probably don't create Anonymous User!")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Owner
|
36
|
+
#
|
37
|
+
def owner_name; anonymous? ? nil : name end
|
38
|
+
|
39
|
+
def owner? object
|
40
|
+
!object.blank? and !name.blank? and !anonymous? and object.respond_to(:owner_name) == self.name
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Roles
|
45
|
+
#
|
46
|
+
def anonymous?
|
47
|
+
name == 'anonymous'
|
48
|
+
end
|
49
|
+
|
50
|
+
def registered?
|
51
|
+
!anonymous?
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_role role
|
55
|
+
role = role.to_s
|
56
|
+
unless roles.include? role
|
57
|
+
if role == 'admin'
|
58
|
+
self.admin = true
|
59
|
+
else
|
60
|
+
self.mm_roles -= Role.denormalize_to_lower_roles [role]
|
61
|
+
self.mm_roles += [role]
|
62
|
+
end
|
63
|
+
clear_cache
|
64
|
+
end
|
65
|
+
roles
|
66
|
+
end
|
67
|
+
|
68
|
+
def remove_role role
|
69
|
+
role = role.to_s
|
70
|
+
if roles.include? role
|
71
|
+
if role == 'admin'
|
72
|
+
self.admin = false
|
73
|
+
else
|
74
|
+
self.mm_roles -= Role.denormalize_to_higher_roles [role]
|
75
|
+
end
|
76
|
+
clear_cache
|
77
|
+
end
|
78
|
+
roles
|
79
|
+
end
|
80
|
+
|
81
|
+
def handy_roles
|
82
|
+
unless roles = cache[:roles]
|
83
|
+
roles = if self.mm_roles.empty?
|
84
|
+
['user']
|
85
|
+
else
|
86
|
+
Role.denormalize_to_lower_roles self.mm_roles
|
87
|
+
end
|
88
|
+
if anonymous?
|
89
|
+
roles << 'anonymous'
|
90
|
+
else
|
91
|
+
roles << 'registered'
|
92
|
+
end
|
93
|
+
roles << "user:#{name}" unless name.blank?
|
94
|
+
if admin
|
95
|
+
roles << 'admin'
|
96
|
+
%w(manager member).each{|r| roles << r unless roles.include? r}
|
97
|
+
end
|
98
|
+
|
99
|
+
roles.must_be == roles.uniq
|
100
|
+
|
101
|
+
roles = HandyRoles.new roles.sort
|
102
|
+
cache[:roles] = roles
|
103
|
+
end
|
104
|
+
roles
|
105
|
+
end
|
106
|
+
|
107
|
+
def major_roles
|
108
|
+
cache[:major_roles] ||= Role.major_roles roles
|
109
|
+
end
|
110
|
+
|
111
|
+
def has_role? role
|
112
|
+
roles.include? role
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
#
|
117
|
+
# can?
|
118
|
+
#
|
119
|
+
def can? operation, object = nil
|
120
|
+
operation = operation.to_s
|
121
|
+
|
122
|
+
return true if has_role?(:admin)
|
123
|
+
|
124
|
+
custom_method = "able_#{operation}?"
|
125
|
+
return object.send custom_method, self if object.respond_to? custom_method
|
126
|
+
|
127
|
+
(
|
128
|
+
effective_permissions[operation] or
|
129
|
+
(owner?(object) and effective_permissions_as_owner[operation])
|
130
|
+
)
|
131
|
+
end
|
132
|
+
|
133
|
+
def can_view? object
|
134
|
+
can? :view, object
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
#
|
139
|
+
# Effective Permissions
|
140
|
+
#
|
141
|
+
def effective_permissions
|
142
|
+
unless ep = cache[:effective_permissions]
|
143
|
+
ep = calculate_effective_roles_for roles
|
144
|
+
cache[:effective_permissions] = ep
|
145
|
+
end
|
146
|
+
ep
|
147
|
+
end
|
148
|
+
|
149
|
+
def effective_permissions_as_owner
|
150
|
+
unless epo = cache[:effective_permissions_as_owner]
|
151
|
+
epo = calculate_effective_roles_for ['owner']
|
152
|
+
cache[:effective_permissions_as_owner] = epo
|
153
|
+
end
|
154
|
+
epo
|
155
|
+
end
|
156
|
+
|
157
|
+
protected
|
158
|
+
def calculate_effective_roles_for roles
|
159
|
+
effective_permissions = {}
|
160
|
+
permissions = ::Mongoid::Authorized.permissions
|
161
|
+
# permissions = rad.config.permissions(DEFAULT_PERMISSIONS).to_h #.to_h(to_s: true)
|
162
|
+
permissions.each do |operation, allowed_roles|
|
163
|
+
operation = operation.to_s
|
164
|
+
effective_permissions[operation.to_s] = roles.any?{|role| allowed_roles.include? role}
|
165
|
+
end
|
166
|
+
effective_permissions
|
167
|
+
end
|
168
|
+
|
169
|
+
def validate_anonymous
|
170
|
+
errors.add :base, "Anonymous can't have any roles!" if anonymous? and !self.mm_roles.blank?
|
171
|
+
end
|
172
|
+
|
173
|
+
class HandyRoles < Array
|
174
|
+
def include? role
|
175
|
+
super role.to_s
|
176
|
+
end
|
177
|
+
alias_method :has?, :include?
|
178
|
+
|
179
|
+
protected
|
180
|
+
def method_missing m, *args, &block
|
181
|
+
m = m.to_s
|
182
|
+
super unless m.last == '?'
|
183
|
+
|
184
|
+
self.include? m[0..-2]
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|