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
@@ -64,6 +64,9 @@ class BaseModel
|
|
64
64
|
modelTypeIdentifier: ->
|
65
65
|
_.dasherize(_.last(@FILE?.path || ''))
|
66
66
|
|
67
|
+
extensionIdentifier: ->
|
68
|
+
@FILE.extension.identifier
|
69
|
+
|
67
70
|
api_path: ->
|
68
71
|
id = @FILE?.extension.identifier
|
69
72
|
( if id then "/#{id}" else '' ) + '/' + _.pluralize(@modelTypeIdentifier())
|
@@ -137,11 +140,11 @@ class BaseModel
|
|
137
140
|
record
|
138
141
|
|
139
142
|
# Sets the attribute data from a server respose
|
140
|
-
setFromServer: (data, options) ->
|
143
|
+
setFromServer: (data, options, method) ->
|
141
144
|
data = if _.isArray(data) then data[0] else data
|
142
145
|
BaseModel.__super__.set.call(this, data )
|
143
146
|
@unset('errors')
|
144
|
-
this.associations
|
147
|
+
this.associations?.setFromServer(this, data, options, method)
|
145
148
|
this.isDirty = false
|
146
149
|
|
147
150
|
# save the model's data to the server
|
@@ -203,7 +206,7 @@ class BaseModel
|
|
203
206
|
unCacheDerived: (name) ->
|
204
207
|
delete this._cache[name]
|
205
208
|
|
206
|
-
# True if the model has "name" as either a prop or
|
209
|
+
# True if the model has "name" as either a prop, session, or derived attribute
|
207
210
|
hasAttribute: (name) ->
|
208
211
|
!! (this._definition[name] || this._derived[name])
|
209
212
|
|
@@ -199,9 +199,7 @@ class Lanes.Models.AssociationCollection extends Lanes.Models.Collection
|
|
199
199
|
_.extend(attrs, @associationFilter)
|
200
200
|
model = super
|
201
201
|
if @options.inverse
|
202
|
-
parent
|
203
|
-
parent[@options.inverse.without].reset() if @options.inverse.without
|
204
|
-
model.set(@options.inverse.name, parent, options)
|
202
|
+
model.set(@options.inverse.name, @parent, options)
|
205
203
|
model
|
206
204
|
|
207
205
|
fetch: (options) ->
|
@@ -271,15 +271,16 @@ class Lanes.Models.Query extends Lanes.Models.Base
|
|
271
271
|
reset: ->
|
272
272
|
unless @defaultSort is false
|
273
273
|
sort = @defaultSort or @fields.findWhere(visible: true).id
|
274
|
-
@setSortField( @fields.findWhere(id: sort), sortAscending:
|
274
|
+
@setSortField( @fields.findWhere(id: sort), sortAscending: @sortAscending, silent: true )
|
275
275
|
|
276
276
|
@clauses.reset([
|
277
277
|
{query: this, available_fields: @fields, field: @initialField}
|
278
278
|
])
|
279
279
|
|
280
280
|
setSortField: (field, options = {silent: false}) ->
|
281
|
+
options.sortAscending ?= (if @sortField is field then !@sortAscending else true)
|
281
282
|
@set({
|
282
|
-
sortAscending: options.sortAscending
|
283
|
+
sortAscending: options.sortAscending
|
283
284
|
sortField: field
|
284
285
|
}, options)
|
285
286
|
|
@@ -38,13 +38,15 @@ Lanes.Models.Sync = {
|
|
38
38
|
handler = (reply) ->
|
39
39
|
delete model.requestInProgress
|
40
40
|
model.lastServerMessage = reply.message
|
41
|
-
|
42
|
-
model.trigger("save", model, reply, options) if isSave
|
43
|
-
model.trigger("sync", model, reply, options)
|
44
|
-
if reply.errors
|
41
|
+
if reply.errors or reply.success is false
|
45
42
|
Lanes.warn reply.errors
|
46
43
|
model.errors = reply.errors
|
47
44
|
model.trigger("error", model, options)
|
45
|
+
else
|
46
|
+
model.setFromServer(reply.data, options, method)
|
47
|
+
model.trigger("save", model, reply, options) if isSave
|
48
|
+
model.trigger("sync", model, reply, options)
|
49
|
+
|
48
50
|
resolve(model)
|
49
51
|
|
50
52
|
Lanes.Models.Sync.perform(method, options).then (reply) ->
|
@@ -13,33 +13,34 @@ class DataWrapper
|
|
13
13
|
|
14
14
|
rebind: (objects, options = {}) ->
|
15
15
|
customEvents = _.result(@component, 'bindDataEvents', {})
|
16
|
-
for name, state of objects
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
16
|
+
@_rebindAttr(name, state, customEvents[name], options) for name, state of objects when state isnt false
|
17
|
+
|
18
|
+
_rebindAttr: (name, state, ev, options) ->
|
19
|
+
unless state
|
20
|
+
Lanes.warn "#{name} is not set on #{@componentName()}"
|
21
|
+
return
|
22
|
+
|
23
|
+
prevState = @states[name]
|
24
|
+
|
25
|
+
# onto next if the object is the same
|
26
|
+
return if prevState == state
|
27
|
+
|
28
|
+
@states[name] = state
|
29
|
+
|
30
|
+
this.bindEvents(name, state, ev)
|
31
|
+
|
32
|
+
if Lanes.u.isModel(state)
|
33
|
+
@listenToNetworkEvents(state) if @component.listenNetworkEvents
|
34
|
+
if @isStateUsingPubsub(name, state)
|
35
|
+
if !prevState? or prevState.getId() != state.getId()
|
36
|
+
Lanes.Models.PubSub.remove(prevState) if prevState
|
37
|
+
unless false == state.pubsub
|
38
|
+
if state.isNew()
|
39
|
+
state.once "change:#{state.idAttribute}", -> Lanes.Models.PubSub.add(state)
|
40
|
+
else
|
41
|
+
Lanes.Models.PubSub.add(state)
|
42
|
+
@listenTo(state, 'remote-update', @onPubSubChangeSet)
|
43
|
+
this.setComponentState({}) unless options.silent
|
43
44
|
|
44
45
|
listenToNetworkEvents: (state) ->
|
45
46
|
@listenTo(state, 'error', @onError)
|
@@ -102,11 +103,12 @@ class DataWrapper
|
|
102
103
|
@component.setState(state)
|
103
104
|
true
|
104
105
|
|
105
|
-
isStateUsingPubsub: (name) ->
|
106
|
-
|
106
|
+
isStateUsingPubsub: (name, state) ->
|
107
|
+
_.result(state, 'registerforPubSub') isnt false and
|
108
|
+
not (false == @component.pubsub or false == @component.pubsub?[name])
|
107
109
|
|
108
110
|
destroy: (state, events, fn) ->
|
109
|
-
for name, state of @states when @isStateUsingPubsub(name)
|
111
|
+
for name, state of @states when @isStateUsingPubsub(name, state)
|
110
112
|
Lanes.Models.PubSub.remove(state) if Lanes.u.isModel(state)
|
111
113
|
this.stopListening()
|
112
114
|
delete @component.data
|
@@ -142,7 +144,7 @@ Lanes.React.Mixins.Data = {
|
|
142
144
|
newState = readDataObjects(this, newProps)
|
143
145
|
return if _.isEmpty(newState)
|
144
146
|
if @data
|
145
|
-
@data.rebind(newState
|
147
|
+
@data.rebind(newState)
|
146
148
|
else
|
147
149
|
@data = new DataWrapper(this, newState)
|
148
150
|
|
@@ -13,27 +13,37 @@ class Lanes.Screens.SystemSettings extends Lanes.React.Screen
|
|
13
13
|
|
14
14
|
renderFileOptions: ->
|
15
15
|
dir = @config.settings.lanes.storage_dir
|
16
|
-
<LC.FieldWrapper
|
16
|
+
<LC.FieldWrapper
|
17
|
+
model={@config} displayComponent={<span />}
|
18
|
+
label='Store Directory' sm=9 {...@props} value={dir}>
|
17
19
|
<input type="text" className='value form-control'
|
18
20
|
placeholder="Directory to store files" value={dir}
|
19
21
|
onChange={_.partial(@onChange, 'storage_dir')} />
|
20
22
|
</LC.FieldWrapper>
|
21
23
|
|
24
|
+
fogValue: ->
|
25
|
+
@state.fogValue or
|
26
|
+
JSON.stringify(@config.settings.lanes.fog_credentials, null, 2)
|
27
|
+
setFog: (ev) ->
|
28
|
+
@setState(fogValue: ev.target.value)
|
29
|
+
|
22
30
|
renderFogOptions: ->
|
23
|
-
|
24
|
-
|
25
|
-
<BS.Input type="textarea" placeholder="FOG options (as JSON)" value={value}
|
26
|
-
onChange={_.partial(@onChange, 'fog_credentials')} />
|
27
|
-
</LC.FieldWrapper>
|
31
|
+
<LC.TextArea sm=9 name='fog' placeholder="FOG options (as JSON)" model={@config}
|
32
|
+
getValue={@fogValue} onChange={@setFog} />
|
28
33
|
|
29
34
|
saveConfig: ->
|
35
|
+
comp.onBeforeSave?() for id, comp of @refs
|
36
|
+
if @state.fogValue
|
37
|
+
try
|
38
|
+
@config.settings.lanes.fog_credentials = JSON.parse(@state.fogValue)
|
30
39
|
@config.save(saveAll: true)
|
40
|
+
comp.onSave?() for id, comp of @refs
|
31
41
|
|
32
42
|
render: ->
|
33
43
|
choices = ['file', 'fog']
|
34
44
|
storage = @config.settings.lanes?.storage || 'file'
|
35
45
|
|
36
|
-
<LC.ScreenWrapper identifier="
|
46
|
+
<LC.ScreenWrapper identifier="system-settings">
|
37
47
|
<BS.Nav bsStyle="pills" className="lanes-toolbar">
|
38
48
|
<BS.Button navItem componentClass="button"
|
39
49
|
onClick={@saveConfig} className="save navbar-btn control">
|
@@ -42,15 +52,17 @@ class Lanes.Screens.SystemSettings extends Lanes.React.Screen
|
|
42
52
|
</BS.Nav>
|
43
53
|
|
44
54
|
<BS.Row>
|
45
|
-
<LC.FieldWrapper
|
55
|
+
<LC.FieldWrapper
|
56
|
+
model={@config} displayComponent={<span />}
|
57
|
+
label='File Storage' sm=3 {...@props} value={storage}>
|
46
58
|
<Lanes.Vendor.ReactWidgets.DropdownList
|
47
59
|
data={choices} value={storage} onChange={_.partial(@onChange, 'storage')} />
|
48
60
|
</LC.FieldWrapper>
|
49
61
|
{if storage is 'file' then @renderFileOptions() else @renderFogOptions()}
|
50
62
|
</BS.Row>
|
51
63
|
<BS.Row>
|
52
|
-
<LC.
|
64
|
+
<LC.ImageAsset sm=4 model={@config} name='logo' label='Logo' size='thumb' />
|
53
65
|
</BS.Row>
|
54
|
-
{for id, Ext of Lanes.Extensions.instances when Ext.
|
55
|
-
|
66
|
+
{for id, Ext of Lanes.Extensions.instances when Ext.getSettingsElement
|
67
|
+
Ext.getSettingsElement(ref: id, key: id, settings: @config.settings[id])}
|
56
68
|
</LC.ScreenWrapper>
|
@@ -16,6 +16,7 @@ class Lanes.Screens.UserPreferences extends Lanes.React.Screen
|
|
16
16
|
{id, label: screen.label}
|
17
17
|
)
|
18
18
|
|
19
|
+
|
19
20
|
render: ->
|
20
21
|
<LC.ScreenWrapper identifier="user-preferences">
|
21
22
|
<Lanes.Screens.CommonComponents commands={@state.commands} />
|
@@ -33,6 +34,6 @@ class Lanes.Screens.UserPreferences extends Lanes.React.Screen
|
|
33
34
|
getSelection={@getScreens}
|
34
35
|
/>
|
35
36
|
</BS.Row>
|
36
|
-
{for id, Ext of Lanes.Extensions.instances when Ext.
|
37
|
-
|
37
|
+
{for id, Ext of Lanes.Extensions.instances when Ext.getPreferenceElement
|
38
|
+
Ext.getPreferenceElement(key: id)}
|
38
39
|
</LC.ScreenWrapper>
|
@@ -90,9 +90,11 @@
|
|
90
90
|
}
|
91
91
|
}
|
92
92
|
|
93
|
-
.modal {
|
93
|
+
.lanes-modal, .modal, .popover {
|
94
|
+
@include make-grid-columns();
|
95
|
+
|
94
96
|
&.lg {
|
95
|
-
.modal-lg {
|
97
|
+
.modal-lg, .popover-content {
|
96
98
|
@include make-grid(xs);
|
97
99
|
@include make-grid(sm);
|
98
100
|
@include make-grid(md);
|
@@ -100,14 +102,14 @@
|
|
100
102
|
}
|
101
103
|
}
|
102
104
|
&.md {
|
103
|
-
.modal-lg {
|
105
|
+
.modal-lg, .popover-content {
|
104
106
|
@include make-grid(xs);
|
105
107
|
@include make-grid(sm);
|
106
108
|
@include make-grid(md);
|
107
109
|
}
|
108
110
|
}
|
109
111
|
&.sm {
|
110
|
-
.modal-lg {
|
112
|
+
.modal-lg, .popover-content {
|
111
113
|
@include make-grid(xs);
|
112
114
|
@include make-grid(sm);
|
113
115
|
}
|
@@ -13,3 +13,12 @@ button, a.btn {
|
|
13
13
|
font-family: $lanes-icon-font;
|
14
14
|
}
|
15
15
|
}
|
16
|
+
|
17
|
+
|
18
|
+
// replace glyphicons with fa
|
19
|
+
.glyphicon {
|
20
|
+
@extend .icon;
|
21
|
+
&.glyphicon-ok:before { content: $fa-var-check; }
|
22
|
+
&.glyphicon-warning-sign:before { content: $fa-var-exclamation-triangle; }
|
23
|
+
&.glyphicon-error:before { content: $fa-var-exclamation; }
|
24
|
+
}
|
@@ -25,12 +25,14 @@ Lanes.Test.renderComponent = (component, args = {}) ->
|
|
25
25
|
Lanes.Test.Utils = Lanes.Vendor.ReactTestUtils
|
26
26
|
|
27
27
|
wrap = (name) ->
|
28
|
-
->
|
29
|
-
|
28
|
+
(options = {}) ->
|
29
|
+
args = _.extend {target: @el}, options
|
30
|
+
Lanes.Test.Utils.Simulate[name](@el, args)
|
30
31
|
return @
|
31
32
|
|
32
33
|
for name, func of Lanes.Test.Utils.Simulate
|
33
34
|
Lanes.lib.Dom::[name] = wrap(name, func)
|
34
35
|
|
35
36
|
Lanes.lib.Dom::setValue = (value) ->
|
36
|
-
@
|
37
|
+
@value = value
|
38
|
+
@change()
|