Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
12f487e2
Commit
12f487e2
authored
Feb 01, 2016
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(plugin-editors): more work on plugin editor loading
parent
30a8a434
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
240 additions
and
181 deletions
+240
-181
public/app/core/core.ts
+2
-0
public/app/core/directives/plugin_directive_loader.ts
+92
-0
public/app/core/directives/rebuild_on_change.ts
+58
-0
public/app/features/datasources/partials/edit.html
+6
-1
public/app/features/panel/panel_directive.ts
+1
-1
public/app/features/panel/query_editor.ts
+0
-101
public/app/plugins/datasource/graphite/module.js
+5
-0
public/app/plugins/datasource/prometheus/datasource.d.ts
+0
-3
public/app/plugins/datasource/prometheus/datasource.ts
+0
-0
public/app/plugins/datasource/prometheus/module.js
+0
-20
public/app/plugins/datasource/prometheus/module.ts
+23
-0
public/app/plugins/datasource/prometheus/query_ctrl.ts
+53
-54
public/views/index.html
+0
-1
No files found.
public/app/core/core.ts
View file @
12f487e2
...
@@ -16,6 +16,8 @@ import "./directives/password_strenght";
...
@@ -16,6 +16,8 @@ import "./directives/password_strenght";
import
"./directives/spectrum_picker"
;
import
"./directives/spectrum_picker"
;
import
"./directives/tags"
;
import
"./directives/tags"
;
import
"./directives/value_select_dropdown"
;
import
"./directives/value_select_dropdown"
;
import
"./directives/plugin_directive_loader"
;
import
"./directives/rebuild_on_change"
;
import
"./directives/give_focus"
;
import
"./directives/give_focus"
;
import
'./jquery_extended'
;
import
'./jquery_extended'
;
import
'./partials'
;
import
'./partials'
;
...
...
public/app/core/directives/plugin_directive_loader.ts
0 → 100644
View file @
12f487e2
///<reference path="../../headers/common.d.ts" />
import
angular
from
'angular'
;
import
_
from
'lodash'
;
import
coreModule
from
'../core_module'
;
function
pluginDirectiveLoader
(
$compile
,
datasourceSrv
)
{
function
getPluginComponentDirective
(
options
)
{
return
function
()
{
return
{
templateUrl
:
options
.
Component
.
templateUrl
,
restrict
:
'E'
,
controller
:
options
.
Component
,
controllerAs
:
'ctrl'
,
bindToController
:
true
,
scope
:
options
.
bindings
,
link
:
(
scope
,
elem
,
attrs
,
ctrl
)
=>
{
if
(
ctrl
.
link
)
{
ctrl
.
link
(
scope
,
elem
,
attrs
,
ctrl
);
}
}
};
};
}
function
getModule
(
scope
,
attrs
)
{
switch
(
attrs
.
type
)
{
case
"metrics-query-editor"
:
let
datasource
=
scope
.
target
.
datasource
||
scope
.
ctrl
.
panel
.
datasource
;
return
datasourceSrv
.
get
(
datasource
).
then
(
ds
=>
{
if
(
!
scope
.
target
.
refId
)
{
scope
.
target
.
refId
=
'A'
;
}
return
System
.
import
(
ds
.
meta
.
module
).
then
(
dsModule
=>
{
return
{
name
:
'metrics-query-editor-'
+
ds
.
meta
.
id
,
bindings
:
{
target
:
"="
,
panelCtrl
:
"="
},
attrs
:
{
"target"
:
"target"
,
"panel-ctrl"
:
"ctrl"
},
Component
:
dsModule
.
MetricsQueryEditor
};
});
});
case
'datasource-config-view'
:
return
System
.
import
(
scope
.
datasourceMeta
.
module
).
then
(
function
(
dsModule
)
{
return
{
name
:
'ds-config-'
+
scope
.
datasourceMeta
.
id
,
bindings
:
{
meta
:
"="
,
current
:
"="
},
attrs
:
{
meta
:
"datasourceMeta"
,
current
:
"current"
},
Component
:
dsModule
.
ConfigView
,
};
});
}
}
function
appendAndCompile
(
scope
,
elem
,
componentInfo
)
{
console
.
log
(
'compile'
,
elem
,
componentInfo
);
var
child
=
angular
.
element
(
document
.
createElement
(
componentInfo
.
name
));
_
.
each
(
componentInfo
.
attrs
,
(
value
,
key
)
=>
{
child
.
attr
(
key
,
value
);
});
$compile
(
child
)(
scope
);
elem
.
empty
();
elem
.
append
(
child
);
}
function
registerPluginComponent
(
scope
,
elem
,
attrs
,
componentInfo
)
{
if
(
!
componentInfo
.
Component
.
registered
)
{
var
directiveName
=
attrs
.
$normalize
(
componentInfo
.
name
);
var
directiveFn
=
getPluginComponentDirective
(
componentInfo
);
coreModule
.
directive
(
directiveName
,
directiveFn
);
componentInfo
.
Component
.
registered
=
true
;
}
appendAndCompile
(
scope
,
elem
,
componentInfo
);
}
return
{
restrict
:
'E'
,
link
:
function
(
scope
,
elem
,
attrs
)
{
getModule
(
scope
,
attrs
).
then
(
function
(
componentInfo
)
{
registerPluginComponent
(
scope
,
elem
,
attrs
,
componentInfo
);
});
}
};
}
coreModule
.
directive
(
'pluginDirectiveLoader'
,
pluginDirectiveLoader
);
public/app/core/directives/rebuild_on_change.ts
0 → 100644
View file @
12f487e2
///<reference path="../../headers/common.d.ts" />
import
angular
from
'angular'
;
import
_
from
'lodash'
;
import
$
from
'jquery'
;
import
coreModule
from
'../core_module'
;
function
getBlockNodes
(
nodes
)
{
var
node
=
nodes
[
0
];
var
endNode
=
nodes
[
nodes
.
length
-
1
];
var
blockNodes
;
for
(
var
i
=
1
;
node
!==
endNode
&&
(
node
=
node
.
nextSibling
);
i
++
)
{
if
(
blockNodes
||
nodes
[
i
]
!==
node
)
{
if
(
!
blockNodes
)
{
blockNodes
=
$
([].
slice
.
call
(
nodes
,
0
,
i
));
}
blockNodes
.
push
(
node
);
}
}
return
blockNodes
||
nodes
;
}
function
rebuildOnChange
(
$compile
)
{
return
{
transclude
:
true
,
priority
:
600
,
restrict
:
'A'
,
link
:
function
(
scope
,
elem
,
attrs
,
ctrl
,
transclude
)
{
var
childScope
,
previousElements
;
var
uncompiledHtml
;
scope
.
$watch
(
attrs
.
rebuildOnChange
,
function
rebuildOnChangeAction
(
value
)
{
if
(
childScope
)
{
childScope
.
$destroy
();
childScope
=
null
;
elem
.
empty
();
}
if
(
value
)
{
if
(
!
childScope
)
{
transclude
(
function
(
clone
,
newScope
)
{
childScope
=
newScope
;
elem
.
append
(
$compile
(
clone
)(
childScope
));
});
}
}
});
}
};
}
coreModule
.
directive
(
'rebuildOnChange'
,
rebuildOnChange
);
public/app/features/datasources/partials/edit.html
View file @
12f487e2
...
@@ -41,7 +41,12 @@
...
@@ -41,7 +41,12 @@
<div
class=
"clearfix"
></div>
<div
class=
"clearfix"
></div>
</div>
</div>
<ds-config-view
ng-if=
"datasourceMeta.id"
ds-meta=
"datasourceMeta"
current=
"current"
></ds-config-view>
<div
rebuild-on-change=
"datasourceMeta.id"
>
<plugin-directive-loader
type=
"datasource-config-view"
>
</plugin-directive-loader>
</div>
<!-- <ds-config-view ds-meta="datasourceMeta" current="current"></ds-config-view> -->
<div
ng-if=
"testing"
style=
"margin-top: 25px"
>
<div
ng-if=
"testing"
style=
"margin-top: 25px"
>
<h5
ng-show=
"!testing.done"
>
Testing....
<i
class=
"fa fa-spiner fa-spin"
></i></h5>
<h5
ng-show=
"!testing.done"
>
Testing....
<i
class=
"fa fa-spiner fa-spin"
></i></h5>
...
...
public/app/features/panel/panel_directive.ts
View file @
12f487e2
...
@@ -50,7 +50,7 @@ var module = angular.module('grafana.directives');
...
@@ -50,7 +50,7 @@ var module = angular.module('grafana.directives');
module
.
directive
(
'grafanaPanel'
,
function
()
{
module
.
directive
(
'grafanaPanel'
,
function
()
{
return
{
return
{
restrict
:
'E'
,
restrict
:
'E'
,
templateUrl
:
'app/features/panel/partials/panel.html'
,
templateUrl
:
'
public/
app/features/panel/partials/panel.html'
,
transclude
:
true
,
transclude
:
true
,
scope
:
{
ctrl
:
"="
},
scope
:
{
ctrl
:
"="
},
link
:
function
(
scope
,
elem
)
{
link
:
function
(
scope
,
elem
)
{
...
...
public/app/features/panel/query_editor.ts
View file @
12f487e2
...
@@ -5,105 +5,6 @@ import _ from 'lodash';
...
@@ -5,105 +5,6 @@ import _ from 'lodash';
var
directivesModule
=
angular
.
module
(
'grafana.directives'
);
var
directivesModule
=
angular
.
module
(
'grafana.directives'
);
function
pluginDirectiveLoader
(
$compile
,
datasourceSrv
)
{
function
getPluginComponentDirective
(
options
)
{
return
function
()
{
return
{
templateUrl
:
options
.
Component
.
templateUrl
,
restrict
:
'E'
,
controller
:
options
.
Component
,
controllerAs
:
'ctrl'
,
bindToController
:
true
,
scope
:
options
.
bindings
,
link
:
(
scope
,
elem
,
attrs
,
ctrl
)
=>
{
if
(
ctrl
.
link
)
{
ctrl
.
link
(
scope
,
elem
,
attrs
,
ctrl
);
}
}
};
};
}
function
getModule
(
scope
,
attrs
)
{
switch
(
attrs
.
type
)
{
case
"metrics-query-editor"
:
{
let
datasource
=
scope
.
target
.
datasource
||
scope
.
ctrl
.
panel
.
datasource
;
return
datasourceSrv
.
get
(
datasource
).
then
(
ds
=>
{
if
(
!
scope
.
target
.
refId
)
{
scope
.
target
.
refId
=
'A'
;
}
return
System
.
import
(
ds
.
meta
.
module
).
then
(
dsModule
=>
{
return
{
name
:
'metrics-query-editor-'
+
ds
.
meta
.
id
,
bindings
:
{
target
:
"="
,
panelCtrl
:
"="
},
attrs
:
{
"target"
:
"target"
,
"panel-ctrl"
:
"ctrl"
},
Component
:
dsModule
.
MetricsQueryEditor
};
});
});
}
}
}
function
appendAndCompile
(
scope
,
elem
,
componentInfo
)
{
var
child
=
angular
.
element
(
document
.
createElement
(
componentInfo
.
name
));
_
.
each
(
componentInfo
.
attrs
,
(
value
,
key
)
=>
{
child
.
attr
(
key
,
value
);
});
$compile
(
child
)(
scope
);
elem
.
empty
();
elem
.
append
(
child
);
}
function
registerPluginComponent
(
scope
,
elem
,
attrs
,
componentInfo
)
{
if
(
!
componentInfo
.
Component
.
registered
)
{
var
directiveName
=
attrs
.
$normalize
(
componentInfo
.
name
);
var
directiveFn
=
getPluginComponentDirective
(
componentInfo
);
directivesModule
.
directive
(
directiveName
,
directiveFn
);
componentInfo
.
Component
.
registered
=
true
;
}
appendAndCompile
(
scope
,
elem
,
componentInfo
);
}
return
{
restrict
:
'E'
,
link
:
function
(
scope
,
elem
,
attrs
)
{
getModule
(
scope
,
attrs
).
then
(
function
(
componentInfo
)
{
registerPluginComponent
(
scope
,
elem
,
attrs
,
componentInfo
);
});
}
};
}
/** @ngInject */
function
metricsQueryEditor
(
dynamicDirectiveSrv
,
datasourceSrv
)
{
return
dynamicDirectiveSrv
.
create
({
watchPath
:
"ctrl.panel.datasource"
,
directive
:
scope
=>
{
let
datasource
=
scope
.
target
.
datasource
||
scope
.
ctrl
.
panel
.
datasource
;
return
datasourceSrv
.
get
(
datasource
).
then
(
ds
=>
{
scope
.
datasource
=
ds
;
if
(
!
scope
.
target
.
refId
)
{
scope
.
target
.
refId
=
'A'
;
}
return
System
.
import
(
ds
.
meta
.
module
).
then
(
dsModule
=>
{
return
{
name
:
'metrics-query-editor-'
+
ds
.
meta
.
id
,
fn
:
dsModule
.
metricsQueryEditor
,
};
});
});
}
});
}
/** @ngInject */
/** @ngInject */
function
metricsQueryOptions
(
dynamicDirectiveSrv
,
datasourceSrv
)
{
function
metricsQueryOptions
(
dynamicDirectiveSrv
,
datasourceSrv
)
{
return
dynamicDirectiveSrv
.
create
({
return
dynamicDirectiveSrv
.
create
({
...
@@ -121,6 +22,4 @@ function metricsQueryOptions(dynamicDirectiveSrv, datasourceSrv) {
...
@@ -121,6 +22,4 @@ function metricsQueryOptions(dynamicDirectiveSrv, datasourceSrv) {
});
});
}
}
directivesModule
.
directive
(
'pluginDirectiveLoader'
,
pluginDirectiveLoader
);
directivesModule
.
directive
(
'metricsQueryEditor'
,
metricsQueryEditor
);
directivesModule
.
directive
(
'metricsQueryOptions'
,
metricsQueryOptions
);
directivesModule
.
directive
(
'metricsQueryOptions'
,
metricsQueryOptions
);
public/app/plugins/datasource/graphite/module.js
View file @
12f487e2
...
@@ -23,11 +23,16 @@ function (GraphiteDatasource) {
...
@@ -23,11 +23,16 @@ function (GraphiteDatasource) {
return
{
templateUrl
:
'public/app/plugins/datasource/graphite/partials/config.html'
};
return
{
templateUrl
:
'public/app/plugins/datasource/graphite/partials/config.html'
};
}
}
function
ConfigView
()
{
}
ConfigView
.
templateUrl
=
'public/app/plugins/datasource/graphite/partials/config.html'
;
return
{
return
{
Datasource
:
GraphiteDatasource
,
Datasource
:
GraphiteDatasource
,
configView
:
configView
,
configView
:
configView
,
annotationsQueryEditor
:
annotationsQueryEditor
,
annotationsQueryEditor
:
annotationsQueryEditor
,
metricsQueryEditor
:
metricsQueryEditor
,
metricsQueryEditor
:
metricsQueryEditor
,
metricsQueryOptions
:
metricsQueryOptions
,
metricsQueryOptions
:
metricsQueryOptions
,
ConfigView
:
ConfigView
};
};
});
});
public/app/plugins/datasource/prometheus/datasource.d.ts
deleted
100644 → 0
View file @
30a8a434
declare
var
Datasource
:
any
;
export
default
Datasource
;
public/app/plugins/datasource/prometheus/datasource.
j
s
→
public/app/plugins/datasource/prometheus/datasource.
t
s
View file @
12f487e2
This diff is collapsed.
Click to expand it.
public/app/plugins/datasource/prometheus/module.js
deleted
100644 → 0
View file @
30a8a434
define
([
'./datasource'
,
],
function
(
PromDatasource
)
{
'use strict'
;
function
metricsQueryEditor
()
{
return
{
controller
:
'PrometheusQueryCtrl'
,
templateUrl
:
'public/app/plugins/datasource/prometheus/partials/query.editor.html'
};
}
function
configView
()
{
return
{
templateUrl
:
'public/app/plugins/datasource/prometheus/partials/config.html'
};
}
return
{
Datasource
:
PromDatasource
,
metricsQueryEditor
:
metricsQueryEditor
,
configView
:
configView
,
};
});
public/app/plugins/datasource/prometheus/module.ts
0 → 100644
View file @
12f487e2
import
{
PrometheusDatasource
}
from
'./datasource'
;
import
{
PrometheusQueryCtrl
}
from
'./query_ctrl'
;
// function metricsQueryEditor() {
// return {controller: 'PrometheusQueryCtrl', templateUrl: 'public/app/plugins/datasource/prometheus/partials/query.editor.html'};
// }
//
// function configView() {
// return {templateUrl: ''};
// }
class
PrometheusConfigViewCtrl
{
static
templateUrl
=
'public/app/plugins/datasource/prometheus/partials/config.html'
;
}
export
{
PrometheusDatasource
as
Datasource
,
PrometheusQueryCtrl
as
MetricsQueryEditor
,
PrometheusConfigViewCtrl
as
ConfigView
};
public/app/plugins/datasource/prometheus/query_ctrl.
j
s
→
public/app/plugins/datasource/prometheus/query_ctrl.
t
s
View file @
12f487e2
define
([
///<reference path="../../../headers/common.d.ts" />
'angular'
,
'lodash'
,
],
function
(
angular
,
_
)
{
'use strict'
;
var
module
=
angular
.
module
(
'grafana.controllers'
);
import
angular
from
'angular'
;
import
_
from
'lodash'
;
import
moment
from
'moment'
;
module
.
controller
(
'PrometheusQueryCtrl'
,
function
(
$scope
,
templateSrv
)
{
import
*
as
dateMath
from
'app/core/utils/datemath'
;
$scope
.
panelCtrl
=
$scope
.
ctrl
;
$scope
.
panel
=
$scope
.
panelCtrl
.
panel
;
$scope
.
init
=
function
()
{
function
PrometheusQueryCtrl
(
$scope
,
templateSrv
)
{
var
target
=
$scope
.
target
;
$scope
.
panelCtrl
=
$scope
.
ctrl
;
$scope
.
panel
=
$scope
.
panelCtrl
.
panel
;
target
.
expr
=
target
.
expr
||
''
;
$scope
.
init
=
function
()
{
target
.
intervalFactor
=
target
.
intervalFactor
||
2
;
var
target
=
$scope
.
target
;
$scope
.
metric
=
''
;
target
.
expr
=
target
.
expr
||
''
;
$scope
.
resolutions
=
_
.
map
([
1
,
2
,
3
,
4
,
5
,
10
],
function
(
f
)
{
target
.
intervalFactor
=
target
.
intervalFactor
||
2
;
return
{
factor
:
f
,
label
:
'1/'
+
f
};
});
$scope
.
$on
(
'typeahead-updated'
,
function
()
{
$scope
.
metric
=
''
;
$scope
.
$apply
(
$scope
.
inputMetric
);
$scope
.
resolutions
=
_
.
map
([
1
,
2
,
3
,
4
,
5
,
10
],
function
(
f
)
{
$scope
.
refreshMetricData
();
return
{
factor
:
f
,
label
:
'1/'
+
f
};
});
});
};
$scope
.
refreshMetricData
=
function
()
{
$scope
.
$on
(
'typeahead-updated'
,
function
()
{
if
(
!
_
.
isEqual
(
$scope
.
oldTarget
,
$scope
.
target
))
{
$scope
.
$apply
(
$scope
.
inputMetric
);
$scope
.
oldTarget
=
angular
.
copy
(
$scope
.
target
);
$scope
.
refreshMetricData
();
$scope
.
paneCtrl
.
refresh
();
});
}
};
};
$scope
.
inputMetric
=
function
()
{
$scope
.
refreshMetricData
=
function
()
{
$scope
.
target
.
expr
+=
$scope
.
target
.
metric
;
if
(
!
_
.
isEqual
(
$scope
.
oldTarget
,
$scope
.
target
))
{
$scope
.
metric
=
''
;
$scope
.
oldTarget
=
angular
.
copy
(
$scope
.
target
);
};
$scope
.
paneCtrl
.
refresh
();
}
};
$scope
.
suggestMetrics
=
function
(
query
,
callback
)
{
$scope
.
inputMetric
=
function
()
{
$scope
.
datasource
$scope
.
target
.
expr
+=
$scope
.
target
.
metric
;
.
performSuggestQuery
(
query
)
$scope
.
metric
=
''
;
.
then
(
callback
);
};
};
$scope
.
suggestMetrics
=
function
(
query
,
callback
)
{
$scope
.
datasource
.
performSuggestQuery
(
query
)
.
then
(
callback
);
};
$scope
.
linkToPrometheus
=
function
()
{
$scope
.
linkToPrometheus
=
function
()
{
var
range
=
Math
.
ceil
((
$scope
.
range
.
to
.
valueOf
()
-
$scope
.
range
.
from
.
valueOf
())
/
1000
);
var
range
=
Math
.
ceil
((
$scope
.
range
.
to
.
valueOf
()
-
$scope
.
range
.
from
.
valueOf
())
/
1000
);
var
endTime
=
$scope
.
range
.
to
.
utc
().
format
(
'YYYY-MM-DD HH:mm'
);
var
endTime
=
$scope
.
range
.
to
.
utc
().
format
(
'YYYY-MM-DD HH:mm'
);
var
expr
=
{
var
expr
=
{
expr
:
templateSrv
.
replace
(
$scope
.
target
.
expr
,
$scope
.
panel
.
scopedVars
),
expr
:
templateSrv
.
replace
(
$scope
.
target
.
expr
,
$scope
.
panel
.
scopedVars
),
range_input
:
range
+
's'
,
range_input
:
range
+
's'
,
end_input
:
endTime
,
end_input
:
endTime
,
step_input
:
''
,
step_input
:
''
,
stacked
:
$scope
.
panel
.
stack
,
stacked
:
$scope
.
panel
.
stack
,
tab
:
0
tab
:
0
};
var
hash
=
encodeURIComponent
(
JSON
.
stringify
([
expr
]));
return
$scope
.
datasource
.
directUrl
+
'/graph#'
+
hash
;
};
};
var
hash
=
encodeURIComponent
(
JSON
.
stringify
([
expr
]));
return
$scope
.
datasource
.
directUrl
+
'/graph#'
+
hash
;
};
$scope
.
init
();
$scope
.
init
();
});
}
})
;
export
{
PrometheusQueryCtrl
}
;
public/views/index.html
View file @
12f487e2
...
@@ -59,7 +59,6 @@
...
@@ -59,7 +59,6 @@
<script
src=
"[[.AppSubUrl]]/public/vendor/npm/es5-shim/es5-shim.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/vendor/npm/es5-shim/es5-shim.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/vendor/npm/es6-shim/es6-shim.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/vendor/npm/es6-shim/es6-shim.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/vendor/npm/es6-promise/dist/es6-promise.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/vendor/npm/es6-promise/dist/es6-promise.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/vendor/npm/systemjs/dist/system-polyfills.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/vendor/npm/systemjs/dist/system.src.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/vendor/npm/systemjs/dist/system.src.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/app/system.conf.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/app/system.conf.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/app/boot.js"
></script>
<script
src=
"[[.AppSubUrl]]/public/app/boot.js"
></script>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment