lanes 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/client/lanes/Config.coffee +3 -1
- data/client/lanes/access/screens/user-management/UserManagement.cjsx +2 -2
- data/client/lanes/components/grid/Body.cjsx +8 -1
- data/client/lanes/components/grid/EditingMixin.cjsx +5 -2
- data/client/lanes/components/grid/Grid.cjsx +1 -1
- data/client/lanes/components/grid/PopOverMixin.cjsx +1 -1
- data/client/lanes/components/grid/row-editor.scss +6 -0
- data/client/lanes/components/grid/styles.scss +1 -1
- data/client/lanes/components/record-finder/RecordFinder.cjsx +22 -16
- data/client/lanes/components/select-field/SelectField.cjsx +20 -27
- data/client/lanes/components/shared/DateTime.cjsx +12 -23
- data/client/lanes/components/shared/DisplayValue.cjsx +5 -10
- data/client/lanes/components/shared/FieldMixin.cjsx +107 -56
- data/client/lanes/components/shared/FieldWrapper.cjsx +59 -6
- data/client/lanes/components/shared/FormGroup.cjsx +3 -8
- data/client/lanes/components/shared/Helpers.coffee +1 -1
- data/client/lanes/components/shared/Icon.cjsx +1 -1
- data/client/lanes/components/shared/ImageAsset.cjsx +46 -0
- data/client/lanes/components/shared/Input.cjsx +5 -1
- data/client/lanes/components/shared/InputFieldMixin.cjsx +8 -27
- data/client/lanes/components/shared/NumberInput.cjsx +11 -4
- data/client/lanes/components/shared/RadioField.cjsx +18 -8
- data/client/lanes/components/shared/ScreenWrapper.cjsx +1 -1
- data/client/lanes/components/shared/TextArea.cjsx +15 -0
- data/client/lanes/components/shared/ToggleField.cjsx +11 -19
- data/client/lanes/components/shared/fields.scss +22 -76
- data/client/lanes/components/shared/{image-saver.scss → image-asset.scss} +1 -1
- data/client/lanes/components/shared/styles.scss +1 -1
- data/client/lanes/components/toolbar/RemoteChangeSets.cjsx +13 -10
- data/client/lanes/components/toolbar/changes-notification.scss +14 -1
- data/client/lanes/extension/Base.coffee +5 -1
- data/client/lanes/models/Asset.coffee +81 -0
- data/client/lanes/models/AssociationMap.coffee +14 -5
- data/client/lanes/models/AssociationProxy.coffee +9 -6
- data/client/lanes/models/Base.coffee +6 -3
- data/client/lanes/models/Collection.coffee +1 -3
- data/client/lanes/models/JobStatus.coffee +3 -0
- data/client/lanes/models/Query.coffee +3 -2
- data/client/lanes/models/Sync.coffee +6 -4
- data/client/lanes/react/mixins/Access.coffee +1 -1
- data/client/lanes/react/mixins/Data.coffee +33 -31
- data/client/lanes/screens/Commands.coffee +0 -1
- data/client/lanes/screens/Definitions.coffee +1 -1
- data/client/lanes/screens/SystemSettings.cjsx +23 -11
- data/client/lanes/screens/UserPreferences.cjsx +3 -2
- data/client/lanes/styles/bootstrap-custom-grid.scss +6 -4
- data/client/lanes/styles/fonts.scss +9 -0
- data/client/lanes/styles/global/styles.scss +1 -0
- data/client/lanes/styles/variables.scss +1 -1
- data/client/lanes/testing/Helpers.coffee +5 -3
- data/client/lanes/vendor/development/base.js +17206 -19211
- data/client/lanes/vendor/development/calendar.js +67 -471
- data/client/lanes/vendor/development/commons.js +21680 -19814
- data/client/lanes/vendor/development/helpers.js +40 -29
- data/client/lanes/vendor/development/toggle.js +22 -19
- data/client/lanes/vendor/development/widgets.js +2476 -2625
- data/client/lanes/vendor/production/base.js +19034 -21038
- data/client/lanes/vendor/production/calendar.js +67 -471
- data/client/lanes/vendor/production/commons.js +21369 -19136
- data/client/lanes/vendor/production/toggle.js +22 -19
- data/client/lanes/vendor/production/widgets.js +2476 -2625
- data/client/lanes/vendor/styles/widgets.scss +2 -0
- data/client/lanes/workspace/FirstRun.cjsx +69 -0
- data/client/lanes/workspace/Navbar.cjsx +4 -2
- data/client/lanes/workspace/ScreenView.cjsx +9 -1
- data/client/lanes/workspace/index.js +1 -0
- data/client/lanes/workspace/styles/screens.scss +11 -0
- data/config/database.yml +2 -2
- data/db/migrate/01_create_system_settings.rb +0 -1
- data/db/migrate/02_create_assets.rb +15 -0
- data/lanes.gemspec +25 -28
- data/lib/lanes/access/authentication_provider.rb +20 -7
- data/lib/lanes/access/role_collection.rb +9 -2
- data/lib/lanes/api/default_routes.rb +4 -4
- data/lib/lanes/api/formatted_reply.rb +15 -11
- data/lib/lanes/api/handlers/asset.rb +37 -0
- data/lib/lanes/api/request_wrapper.rb +18 -4
- data/lib/lanes/api/routing.rb +17 -6
- data/lib/lanes/api/updates.rb +1 -1
- data/lib/lanes/api.rb +1 -1
- data/lib/lanes/asset.rb +38 -0
- data/lib/lanes/concerns/all.rb +1 -1
- data/lib/lanes/concerns/asset_uploader.rb +60 -0
- data/lib/lanes/configuration.rb +1 -2
- data/lib/lanes/extension.rb +1 -1
- data/lib/lanes/logger.rb +26 -16
- data/lib/lanes/system_settings.rb +13 -8
- data/lib/lanes/version.rb +1 -1
- data/lib/lanes.rb +1 -0
- data/npm-build/base.js +1 -1
- data/npm-build/package.json +9 -9
- data/spec/command-reference-files/initial/Gemfile +1 -1
- data/spec/fixtures/logo.png +0 -0
- data/spec/lanes/components/grid/PopoverEditorSpec.coffee +48 -0
- data/spec/server/asset_spec.rb +34 -0
- data/spec/server/spec_helper.rb +14 -2
- metadata +118 -127
- data/client/lanes/components/shared/ControlLabel.cjsx +0 -45
- data/client/lanes/components/shared/ImageSaver.cjsx +0 -33
- data/client/lanes/models/SystemSettings.coffee +0 -0
- data/client/lanes/models/mixins/FileSupport.coffee +0 -60
- data/lib/lanes/api/handlers/file.rb +0 -26
- data/lib/lanes/concerns/image_uploader.rb +0 -42
@@ -561,6 +561,7 @@ ul.rw-list.rw-list-grouped > li.rw-list-option,
|
|
561
561
|
cursor: not-allowed;
|
562
562
|
}
|
563
563
|
.rw-calendar-grid {
|
564
|
+
outline: none;
|
564
565
|
height: 14.28571429em;
|
565
566
|
table-layout: fixed;
|
566
567
|
width: 100%;
|
@@ -602,6 +603,7 @@ ul.rw-list.rw-list-grouped > li.rw-list-option,
|
|
602
603
|
position: relative;
|
603
604
|
min-height: 27px;
|
604
605
|
cursor: auto;
|
606
|
+
outline: none;
|
605
607
|
padding-left: 5px;
|
606
608
|
}
|
607
609
|
.rw-selectlist > ul > li.rw-list-option > label > input {
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Lanes.Workspace.FirstRun extends Lanes.React.Screen
|
2
|
+
|
3
|
+
openSS: ->
|
4
|
+
Lanes.Screens.Definitions.all.get('system-settings').display()
|
5
|
+
|
6
|
+
openUP: ->
|
7
|
+
Lanes.Screens.Definitions.all.get('user-preferences').display()
|
8
|
+
|
9
|
+
openUser: ->
|
10
|
+
Lanes.Screens.Definitions.all.get('user-management').display()
|
11
|
+
|
12
|
+
render: ->
|
13
|
+
title = Lanes.Extensions.controlling().title()
|
14
|
+
|
15
|
+
<LC.ScreenWrapper identifier="first-run">
|
16
|
+
|
17
|
+
<BS.Row>
|
18
|
+
<BS.Col xs=10 xsOffset=1>
|
19
|
+
<BS.PageHeader>
|
20
|
+
Welcome to {title}
|
21
|
+
</BS.PageHeader>
|
22
|
+
</BS.Col>
|
23
|
+
</BS.Row>
|
24
|
+
<BS.Row>
|
25
|
+
<BS.Col xs=8 xsOffset=1>
|
26
|
+
<p className="lead">
|
27
|
+
A few tips & tricks:
|
28
|
+
</p>
|
29
|
+
</BS.Col>
|
30
|
+
</BS.Row>
|
31
|
+
<BS.Row>
|
32
|
+
<BS.Col xs=8 xsOffset=2>
|
33
|
+
<ul>
|
34
|
+
<li>
|
35
|
+
{title} is composed of individual screens. Each screen can be opened from the menu at the left.
|
36
|
+
When opened, each screen opens into a new tab in this window.
|
37
|
+
</li>
|
38
|
+
<li>
|
39
|
+
Many screens are dual-purpose and support both edit and display modes.
|
40
|
+
They can be switched between the modes by clicking
|
41
|
+
the <Lanes.Vendor.ReactToggle defaultChecked={true} /> control
|
42
|
+
at the top right of the screen.
|
43
|
+
</li>
|
44
|
+
<li>
|
45
|
+
You can setup the system by
|
46
|
+
selecting <LC.Icon type="cogs" /> <a onClick={@openSS}>System Settings</a>
|
47
|
+
from the menu option with
|
48
|
+
the <LC.Icon type="wrench" lg /> icon.
|
49
|
+
</li>
|
50
|
+
<li>
|
51
|
+
There’s also <LC.Icon type="user-secret" /> <a onClick={@openUP}>User Preferences</a> where
|
52
|
+
you can set which screens are displayed when {title} loads.
|
53
|
+
</li>
|
54
|
+
<li>
|
55
|
+
User accounts can be setup from
|
56
|
+
the <LC.Icon type='group' /> <a onClick={@openUser}>User Management</a> screen.
|
57
|
+
</li>
|
58
|
+
</ul>
|
59
|
+
</BS.Col>
|
60
|
+
</BS.Row>
|
61
|
+
<BS.Row className="help">
|
62
|
+
<BS.Col xs=7 xsOffset=4>
|
63
|
+
<p>Have a suggestion on improving {title}?</p>
|
64
|
+
<p><a href="https://argosity.com/" target="_blank">Argosity</a> would
|
65
|
+
love to hear it and help you.</p>
|
66
|
+
</BS.Col>
|
67
|
+
</BS.Row>
|
68
|
+
|
69
|
+
</LC.ScreenWrapper>
|
@@ -19,9 +19,11 @@ class Lanes.Workspace.Navbar extends Lanes.React.Component
|
|
19
19
|
|
20
20
|
Logo: ->
|
21
21
|
if @settings.logo?.url
|
22
|
-
<img src={@settings.logo.
|
22
|
+
<img src={@settings.logo.thumbnail_url} />
|
23
23
|
else
|
24
|
-
<span>
|
24
|
+
<span>
|
25
|
+
{Lanes.Extensions.controlling().title()}
|
26
|
+
</span>
|
25
27
|
|
26
28
|
render: ->
|
27
29
|
|
@@ -27,8 +27,16 @@ class Lanes.Workspace.ScreenView extends Lanes.React.Component
|
|
27
27
|
screen = Lanes.Screens.Definitions.all.findWhere(loading: true)
|
28
28
|
<LC.NetworkActivityOverlay visible model={screen} message="Loading #{screen.title}…" />
|
29
29
|
|
30
|
+
BaseView: ->
|
31
|
+
Base = Lanes.Extensions.controlling().initialScreen?() or Lanes.Workspace.FirstRun
|
32
|
+
<div className="screen active base">
|
33
|
+
<Base />
|
34
|
+
</div>
|
35
|
+
|
30
36
|
render: ->
|
37
|
+
|
38
|
+
child = if @displaying.isEmpty() then <@BaseView /> else @displaying.map(@renderScreen)
|
31
39
|
<div className={"page-content #{@context.uistate.layout_size}"}>
|
32
40
|
{@renderLoading() if Lanes.Screens.Definitions.all.isLoading()}
|
33
|
-
{
|
41
|
+
{child}
|
34
42
|
</div>
|
data/config/database.yml
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
class CreateAssets < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
|
4
|
+
create_table "assets" do |t|
|
5
|
+
t.string :file, null: false
|
6
|
+
|
7
|
+
t.references :owner, polymorphic: true, null: false
|
8
|
+
t.integer :order
|
9
|
+
t.jsonb :metadata, null: false, default: {}
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :assets, [:owner_id, :owner_type]
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
data/lanes.gemspec
CHANGED
@@ -24,45 +24,42 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.required_ruby_version = ">= 2.0"
|
26
26
|
|
27
|
-
spec.add_dependency "activerecord", "~> 4.2"
|
28
27
|
spec.add_dependency "activejob", "~> 4.2"
|
29
|
-
spec.add_dependency "
|
30
|
-
|
31
|
-
spec.add_dependency "resque", "~> 1.25"
|
32
|
-
spec.add_dependency "pg", "~> 0.17"
|
33
|
-
spec.add_dependency "sinatra", "~> 1.4"
|
28
|
+
spec.add_dependency "activerecord", "~> 4.2"
|
34
29
|
spec.add_dependency "bcrypt", "~> 3.1"
|
35
|
-
spec.add_dependency "
|
36
|
-
spec.add_dependency "
|
37
|
-
spec.add_dependency "
|
38
|
-
spec.add_dependency "sprockets", "~> 3.0"
|
39
|
-
spec.add_dependency "coffee-script", "~> 2.3"
|
40
|
-
spec.add_dependency "coffee-react", "~> 3.0"
|
41
|
-
spec.add_dependency "execjs", "~> 2.2"
|
42
|
-
spec.add_dependency "thor", "~> 0.19"
|
43
|
-
spec.add_dependency "sass", "~> 3.4"
|
44
|
-
spec.add_dependency "sprockets-helpers", "~> 1.2"
|
30
|
+
spec.add_dependency "carrierwave", "~> 0.10.0"
|
31
|
+
spec.add_dependency "coffee-react", "~> 4.0"
|
32
|
+
spec.add_dependency "coffee-script", "~> 2.4"
|
45
33
|
spec.add_dependency "compass-import-once", "~> 1.0"
|
46
|
-
spec.add_dependency "
|
34
|
+
spec.add_dependency "execjs", "~> 2.6"
|
35
|
+
spec.add_dependency "fastimage", "~> 1.8.1"
|
36
|
+
spec.add_dependency "guard", "~> 2.13"
|
37
|
+
spec.add_dependency "guard-jasmine", "~> 2.0"
|
38
|
+
spec.add_dependency "guard-minitest", "~> 2.3"
|
47
39
|
spec.add_dependency "hashie", "~> 3.3"
|
48
|
-
spec.add_dependency "
|
40
|
+
spec.add_dependency "jasmine-core", "~> 2.0"
|
41
|
+
spec.add_dependency "jobba", "~> 1.4"
|
42
|
+
spec.add_dependency "message_bus", "2.0.0.beta.6"
|
43
|
+
spec.add_dependency "mini_magick", "~> 4.3.6"
|
49
44
|
spec.add_dependency "minitest-around", "~> 0.2"
|
50
45
|
spec.add_dependency "mocha", "~> 1.1"
|
51
|
-
spec.add_dependency "
|
52
|
-
|
53
|
-
spec.add_dependency "
|
54
|
-
spec.add_dependency "
|
46
|
+
spec.add_dependency "oj", "~> 2.1"
|
47
|
+
spec.add_dependency "pg", "~> 0.8"
|
48
|
+
spec.add_dependency "rack-protection", "~> 1.5"
|
49
|
+
spec.add_dependency "rack-test", "~> 0.6"
|
55
50
|
spec.add_dependency "rake", "~> 10.0"
|
56
|
-
spec.add_dependency "jasmine-core", "~> 2.0"
|
57
51
|
spec.add_dependency "require_all", "~> 1.3"
|
52
|
+
spec.add_dependency "resque", "~> 1.25"
|
58
53
|
spec.add_dependency "sanitize", "~> 3.0"
|
59
|
-
spec.add_dependency "
|
60
|
-
spec.add_dependency "
|
61
|
-
spec.add_dependency "
|
62
|
-
spec.add_dependency "
|
54
|
+
spec.add_dependency "sass", "~> 3.4"
|
55
|
+
spec.add_dependency "sinatra", "~> 1.4"
|
56
|
+
spec.add_dependency "sprockets", "~> 3.6"
|
57
|
+
spec.add_dependency "sprockets-helpers", "~> 1.2"
|
58
|
+
spec.add_dependency "thor", "~> 0.19"
|
59
|
+
spec.add_dependency "uglifier", "~> 2.7"
|
63
60
|
|
64
61
|
spec.add_development_dependency "bundler", "~> 1.5"
|
65
|
-
spec.add_development_dependency "growl", "~> 1.0"
|
66
62
|
spec.add_development_dependency "diffy", "~> 3.0"
|
63
|
+
spec.add_development_dependency "growl", "~> 1.0"
|
67
64
|
|
68
65
|
end
|
@@ -13,7 +13,6 @@ module Lanes
|
|
13
13
|
if Lanes.env.test? && request.env['HTTP_X_TESTING_USER'].present?
|
14
14
|
Lanes::User.where(login: request.env['HTTP_X_TESTING_USER']).first
|
15
15
|
else
|
16
|
-
return Lanes::User.first
|
17
16
|
Lanes::User.where(id: request.session['user_id']).first
|
18
17
|
end
|
19
18
|
)
|
@@ -47,19 +46,33 @@ module Lanes
|
|
47
46
|
end
|
48
47
|
end
|
49
48
|
|
50
|
-
def
|
49
|
+
def wrap_request(req)
|
50
|
+
if current_user
|
51
|
+
::Lanes::User.scoped_to(current_user) do | user |
|
52
|
+
yield
|
53
|
+
end
|
54
|
+
else
|
55
|
+
fail_request(req)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def wrap_model_access(model, req)
|
51
60
|
if allowed_access_to?(model)
|
52
61
|
::Lanes::User.scoped_to(current_user) do | user |
|
53
62
|
yield
|
54
63
|
end
|
55
64
|
else
|
56
|
-
|
57
|
-
Lanes.logger.warn "Unauthorized access attempted to #{req.url}"
|
58
|
-
req.halt( 401, Oj.dump({
|
59
|
-
success:false, errors: {user: "Access Denied"}, message: "Access Denied"
|
60
|
-
}))
|
65
|
+
fail_request(req)
|
61
66
|
end
|
62
67
|
end
|
68
|
+
|
69
|
+
def fail_request(req)
|
70
|
+
Lanes.logger.warn request.env['HTTP_X_TESTING_USER']
|
71
|
+
Lanes.logger.warn "Unauthorized access attempted to #{req.url}"
|
72
|
+
req.halt( 401, Oj.dump({
|
73
|
+
success:false, errors: {user: "Access Denied"}, message: "Access Denied"
|
74
|
+
}))
|
75
|
+
end
|
63
76
|
end
|
64
77
|
|
65
78
|
end
|
@@ -5,13 +5,20 @@ module Lanes
|
|
5
5
|
include Enumerable
|
6
6
|
|
7
7
|
def initialize(user)
|
8
|
-
@
|
8
|
+
@role_names = user.role_names
|
9
|
+
@roles = user.role_names.map{ |name|
|
9
10
|
"Lanes::Access::Roles::#{name.classify}".safe_constantize
|
10
11
|
}.compact.map{ |klass| klass.new(user) }
|
11
12
|
end
|
12
13
|
|
13
14
|
def exposed_data
|
14
|
-
@
|
15
|
+
@role_names
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param role [String]
|
19
|
+
# @return [Boolean] Does a role with the given id exist?
|
20
|
+
def include?(role)
|
21
|
+
@role_names.include?(role)
|
15
22
|
end
|
16
23
|
|
17
24
|
# @param model [Lanes::Model]
|
@@ -4,11 +4,11 @@ module Lanes
|
|
4
4
|
put Lanes.config.api_path + '/system-settings.json',
|
5
5
|
&SystemSettings.update_handler
|
6
6
|
|
7
|
-
post Lanes.config.api_path + '/
|
8
|
-
&API::Handlers::
|
7
|
+
post Lanes.config.api_path + '/asset',
|
8
|
+
&API::Handlers::Asset.saver
|
9
9
|
|
10
|
-
get Lanes.config.api_path + '/
|
11
|
-
&API::Handlers::
|
10
|
+
get Lanes.config.api_path + '/asset/*',
|
11
|
+
&API::Handlers::Asset.getter
|
12
12
|
|
13
13
|
Extensions.each(reversed: true) do | ext |
|
14
14
|
ext.route(self)
|
@@ -6,25 +6,29 @@ module Lanes
|
|
6
6
|
# populates them appropriately
|
7
7
|
|
8
8
|
def std_api_reply(type, data, options)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
json[:errors] = {}
|
13
|
-
success = false
|
14
|
-
data.errors.each{ | attr, message |
|
15
|
-
json[:errors][attr] = message
|
16
|
-
}
|
9
|
+
json = { success: options[:success].nil? ? true : options[:success] }
|
10
|
+
if data.is_a?(ActiveRecord::Base)
|
11
|
+
record_active_record_errors(data, json)
|
17
12
|
end
|
18
13
|
if options[:total_count]
|
19
14
|
json[:total] = options.delete(:total_count)
|
20
15
|
end
|
21
16
|
json.merge(
|
22
|
-
|
23
|
-
|
24
|
-
data: success ? records_for_reply(data, type, options) : []
|
17
|
+
message: options[:messsage] || json_status_str(data, type.to_s.capitalize, json[:success]),
|
18
|
+
data: json[:success] ? records_for_reply(data, type, options) : []
|
25
19
|
)
|
26
20
|
end
|
27
21
|
|
22
|
+
def record_active_record_errors(model, json = {})
|
23
|
+
return if model.errors.none?
|
24
|
+
json[:success] = false
|
25
|
+
json[:errors] = {}
|
26
|
+
model.errors.each{ | attr, message |
|
27
|
+
json[:errors][attr] = message
|
28
|
+
}
|
29
|
+
json
|
30
|
+
end
|
31
|
+
|
28
32
|
# @return Array<Array> returns either an array of fields
|
29
33
|
def records_for_reply(data, type, options)
|
30
34
|
return [] if :destroy == type
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Lanes::API::Handlers
|
2
|
+
|
3
|
+
class Asset
|
4
|
+
|
5
|
+
def self.saver
|
6
|
+
lambda do
|
7
|
+
Lanes.logger.debug "Saving File. Root=#{CarrierWave.root}"
|
8
|
+
|
9
|
+
path = "#{params['extension_id']}/#{params['owner_type']}"
|
10
|
+
model = path.underscore.camelize.constantize
|
11
|
+
|
12
|
+
authentication = Lanes::API::AuthenticationProvider.new(request)
|
13
|
+
authentication.wrap_model_access(model, self) do
|
14
|
+
owner = model.find(params['owner_id'])
|
15
|
+
asset = if params['id']
|
16
|
+
::Lanes::Asset.find(params['id'])
|
17
|
+
else
|
18
|
+
owner.send("build_#{params['owner_association']}")
|
19
|
+
end
|
20
|
+
asset.store_uploaded_file(params['file'])
|
21
|
+
|
22
|
+
json_reply std_api_reply asset.new_record? ? :update : :create,
|
23
|
+
asset,
|
24
|
+
success: asset.save
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.getter
|
30
|
+
lambda do
|
31
|
+
# files are stored using a random string, therefore we assume that anyone who
|
32
|
+
# knows the filename has access and don't empose any further restrictions
|
33
|
+
send_file CarrierWave::Uploader::Base.root.call + '/' + params['splat'].first
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -28,20 +28,34 @@ module Lanes
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
def make_handler(model, controller, parent_attribute)
|
31
|
+
def make_handler(model, controller, parent_attribute = nil)
|
32
32
|
lambda do
|
33
33
|
authentication = Lanes::API::AuthenticationProvider.new(request)
|
34
|
-
authentication.
|
34
|
+
authentication.wrap_model_access(model, self) do
|
35
35
|
if parent_attribute
|
36
36
|
params[:nested_attribute] = Hash[ parent_attribute,
|
37
37
|
params[parent_attribute] ]
|
38
38
|
end
|
39
|
-
wrap_reply(!request.get?) do
|
39
|
+
wrap_reply(with_transaction: !request.get?) do
|
40
40
|
yield controller.new(model, authentication, params, data)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
44
44
|
end
|
45
|
+
|
46
|
+
def with_authenticated_user(role:nil, with_transaction:true)
|
47
|
+
lambda do
|
48
|
+
authentication = Lanes::API::AuthenticationProvider.new(request)
|
49
|
+
user = authentication.current_user
|
50
|
+
if user and ( role.nil? or user.roles.include?(role) )
|
51
|
+
wrap_reply(with_transaction: with_transaction) do
|
52
|
+
yield authentication.current_user, self
|
53
|
+
end
|
54
|
+
else
|
55
|
+
authentication.fail_request(self)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
45
59
|
end
|
46
60
|
|
47
61
|
def log_request
|
@@ -49,7 +63,7 @@ module Lanes
|
|
49
63
|
Lanes.logger.debug JSON.pretty_generate(data) unless Lanes.env.production? or data.nil?
|
50
64
|
end
|
51
65
|
|
52
|
-
def wrap_reply(with_transaction
|
66
|
+
def wrap_reply(with_transaction:true)
|
53
67
|
response = { success: false, message: "No response was generated" }
|
54
68
|
log_request
|
55
69
|
if with_transaction
|
data/lib/lanes/api/routing.rb
CHANGED
@@ -32,20 +32,31 @@ module Lanes
|
|
32
32
|
prefix = parent_attribute ? parent_attribute + '/' : ''
|
33
33
|
|
34
34
|
# index
|
35
|
-
|
35
|
+
if controller.method_defined?(:perform_retrieval)
|
36
|
+
get "#{prefix}#{path}/?:id?.json",
|
37
|
+
&RequestWrapper.get(model, controller, parent_attribute)
|
38
|
+
end
|
36
39
|
|
37
40
|
# create
|
38
|
-
|
41
|
+
if controller.method_defined?(:perform_creation)
|
42
|
+
post "#{prefix}#{path}.json",
|
43
|
+
&RequestWrapper.post(model, controller, parent_attribute)
|
44
|
+
end
|
39
45
|
|
40
46
|
unless options[:immutable]
|
41
47
|
|
42
48
|
# update
|
43
|
-
|
44
|
-
|
49
|
+
if controller.method_defined?(:perform_update)
|
50
|
+
patch "#{prefix}#{path}/?:id?.json",
|
51
|
+
&RequestWrapper.update(model, controller, parent_attribute)
|
52
|
+
put "#{prefix}#{path}/?:id?.json",
|
53
|
+
&RequestWrapper.update(model, controller, parent_attribute)
|
54
|
+
end
|
45
55
|
|
46
|
-
|
56
|
+
if controller.method_defined?(:perform_destroy) and not options[:indestructible]
|
47
57
|
# destroy
|
48
|
-
delete "#{prefix}#{path}/?:id?.json",
|
58
|
+
delete "#{prefix}#{path}/?:id?.json",
|
59
|
+
&RequestWrapper.delete(model, controller, parent_attribute)
|
49
60
|
end
|
50
61
|
|
51
62
|
end
|
data/lib/lanes/api/updates.rb
CHANGED
data/lib/lanes/api.rb
CHANGED
data/lib/lanes/asset.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
module Lanes
|
2
|
+
|
3
|
+
class Asset < Lanes::Model
|
4
|
+
|
5
|
+
mount_uploader :file, Lanes::Concerns::AssetUploader
|
6
|
+
|
7
|
+
belongs_to :owner, polymorphic: true
|
8
|
+
|
9
|
+
validates :owner, set: true
|
10
|
+
|
11
|
+
after_update :remove_changed_file, if: lambda{ file_changed? }
|
12
|
+
|
13
|
+
def serializable_hash(options = nil)
|
14
|
+
values = super
|
15
|
+
values.delete('file')
|
16
|
+
values.merge!(file.as_json[:file].stringify_keys)
|
17
|
+
values['original'] = { 'url' => values.delete('url') }
|
18
|
+
values
|
19
|
+
end
|
20
|
+
|
21
|
+
def store_uploaded_file(f)
|
22
|
+
ext = File.extname(f[:filename])
|
23
|
+
if ext.blank?
|
24
|
+
ext = '.' + FastImage.type(f[:tempfile]).to_s
|
25
|
+
end
|
26
|
+
self[:file] = ::Lanes::Strings.random + ext
|
27
|
+
file.store!(f)
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def remove_changed_file
|
33
|
+
self.file_was.remove!
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
data/lib/lanes/concerns/all.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'mini_magick'
|
2
|
+
require 'carrierwave'
|
3
|
+
require 'fastimage'
|
4
|
+
|
5
|
+
module Lanes::Concerns
|
6
|
+
|
7
|
+
class AssetUploader < CarrierWave::Uploader::Base
|
8
|
+
|
9
|
+
include CarrierWave::MiniMagick
|
10
|
+
|
11
|
+
process :store_attributes
|
12
|
+
|
13
|
+
version :medium, :if => :image? do
|
14
|
+
process :resize_to_fit => [800, 800]
|
15
|
+
end
|
16
|
+
|
17
|
+
version :thumbnail, :if => :image? do
|
18
|
+
process :resize_to_fit => [200,200]
|
19
|
+
end
|
20
|
+
|
21
|
+
def cache_dir
|
22
|
+
'/tmp'
|
23
|
+
end
|
24
|
+
|
25
|
+
def filename
|
26
|
+
if original_filename && model && model.read_attribute(mounted_as).present?
|
27
|
+
model.read_attribute(mounted_as)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def store_dir
|
32
|
+
token = secure_token
|
33
|
+
"#{token[0, 2]}/#{token[2, 2]}"
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def store_attributes
|
39
|
+
if file && model
|
40
|
+
model.metadata['size'] = file.size
|
41
|
+
model.metadata['content_type'] = file.content_type
|
42
|
+
if image?(file)
|
43
|
+
img = ::MiniMagick::Image.open(file.file)
|
44
|
+
model.metadata['width'] = img.width
|
45
|
+
model.metadata['height'] = img.height
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def image?(new_file)
|
51
|
+
new_file.content_type.include? 'image'
|
52
|
+
end
|
53
|
+
|
54
|
+
def secure_token
|
55
|
+
model[mounted_as] ||= ::Lanes::Strings.random
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
data/lib/lanes/configuration.rb
CHANGED
@@ -4,7 +4,6 @@ require 'pathname'
|
|
4
4
|
require 'carrierwave'
|
5
5
|
require 'active_job'
|
6
6
|
require 'jobba'
|
7
|
-
require 'fog'
|
8
7
|
require 'message_bus'
|
9
8
|
|
10
9
|
module Lanes
|
@@ -106,7 +105,7 @@ module Lanes
|
|
106
105
|
|
107
106
|
# Storage engine to use, default to file, may also be set to
|
108
107
|
# 'fog' or any other value that CarrierWave accepts
|
109
|
-
config_option :storage_type,
|
108
|
+
config_option :storage_type, :file
|
110
109
|
|
111
110
|
def api_path
|
112
111
|
mounted_at + 'api'
|