rad_kit 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 +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
|