Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
atlas
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
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
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
dataplatform
atlas
Commits
20f2fba7
Commit
20f2fba7
authored
Sep 22, 2017
by
kevalbhatt
Committed by
Madhan Neethiraj
Sep 22, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ATLAS-2156: save-search UI to allow user to edit name
Signed-off-by:
Madhan Neethiraj
<
madhan@apache.org
>
parent
454eb66a
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
119 additions
and
83 deletions
+119
-83
SaveModalLayoutView_tmpl.html
...ic/js/templates/search/save/SaveModalLayoutView_tmpl.html
+2
-1
SaveSearchItemView_tmpl.html
...lic/js/templates/search/save/SaveSearchItemView_tmpl.html
+0
-0
SaveSearchView_tmpl.html
.../public/js/templates/search/save/SaveSearchView_tmpl.html
+2
-1
CommonViewFunction.js
dashboardv2/public/js/utils/CommonViewFunction.js
+5
-3
SearchLayoutView.js
dashboardv2/public/js/views/search/SearchLayoutView.js
+19
-20
SaveModalLayoutView.js
...oardv2/public/js/views/search/save/SaveModalLayoutView.js
+49
-14
SaveSearchItemView.js
...boardv2/public/js/views/search/save/SaveSearchItemView.js
+16
-4
SaveSearchView.js
dashboardv2/public/js/views/search/save/SaveSearchView.js
+26
-40
No files found.
dashboardv2/public/js/templates/search/
SaveAs
LayoutView_tmpl.html
→
dashboardv2/public/js/templates/search/
save/SaveModal
LayoutView_tmpl.html
View file @
20f2fba7
...
...
@@ -18,7 +18,7 @@
<div
class=
"form-group"
>
<label
class=
"control-label col-sm-2 required"
for=
"name"
>
Name
</label>
<div
class=
"col-sm-10"
>
<input
class=
"form-control"
data-id=
"saveAsName"
placeholder=
"Name(required)"
autofocus
/>
<input
class=
"form-control"
data-id=
"saveAsName"
placeholder=
"Name(required)"
value=
"{{selectedModel.name}}"
autofocus
/>
</div>
</div>
</form>
\ No newline at end of file
dashboardv2/public/js/templates/search/SaveSearchItemView_tmpl.html
→
dashboardv2/public/js/templates/search/
save/
SaveSearchItemView_tmpl.html
View file @
20f2fba7
File moved
dashboardv2/public/js/templates/search/
SaveSearch
_tmpl.html
→
dashboardv2/public/js/templates/search/
save/SaveSearchView
_tmpl.html
View file @
20f2fba7
...
...
@@ -22,7 +22,8 @@
<button
type=
"button"
class=
"btn btn-action btn-sm"
data-id=
"saveAsBtn"
>
Save As
</button>
</div>
</div>
<ul
data-id=
"itemViewContent"
class=
"tag-tree saveSearchList gray-text"
>
<span
class=
"gray-text noFavoriteSearch"
>
You don't have any favorite search.
</span>
<ul
data-id=
"itemViewContent"
class=
"tag-tree saveSearchList"
>
<div
class=
"fontLoader-relative"
style=
"display: block;"
>
<i
class=
"fa fa-refresh fa-spin-custom"
></i>
</div>
...
...
dashboardv2/public/js/utils/CommonViewFunction.js
View file @
20f2fba7
...
...
@@ -447,9 +447,8 @@ define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Enum
CommonViewFunction
.
generateObjectForSaveSearchApi
=
function
(
options
)
{
var
obj
=
{
name
:
options
.
name
,
searchParameters
:
{
excludeDeletedEntities
:
true
}
guid
:
options
.
guid
,
searchParameters
:
{}
};
var
value
=
options
.
value
;
if
(
value
)
{
...
...
@@ -470,6 +469,9 @@ define(['require', 'utils/Utils', 'modules/Modal', 'utils/Messages', 'utils/Enum
}
}
}
if
(
k
==
"includeDE"
)
{
val
=
_
.
isUndefinedNull
(
val
)
?
true
:
val
;
}
obj
.
searchParameters
[
v
]
=
val
;
});
return
obj
;
...
...
dashboardv2/public/js/views/search/SearchLayoutView.js
View file @
20f2fba7
...
...
@@ -102,8 +102,10 @@ define(['require',
type
:
null
,
tag
:
null
,
attributes
:
null
,
tagFilters
:
null
,
pageOffset
:
null
,
pageLimit
:
null
,
entityFilters
:
null
,
includeDE
:
null
}
};
...
...
@@ -119,7 +121,7 @@ define(['require',
},
renderSaveSearch
:
function
()
{
var
that
=
this
;
require
([
'views/search/SaveSearchView'
],
function
(
SaveSearchView
)
{
require
([
'views/search/
save/
SaveSearchView'
],
function
(
SaveSearchView
)
{
var
saveSearchBaiscCollection
=
new
VSearchList
(),
saveSearchAdvanceCollection
=
new
VSearchList
(),
saveSearchCollection
=
new
VSearchList
();
...
...
@@ -137,6 +139,7 @@ define(['require',
tagObj
=
that
.
searchTableFilters
[
'tagFilters'
],
urlObj
=
Utils
.
getUrlState
.
getQueryParams
();
if
(
urlObj
)
{
// includeDE value in because we need to send "true","false" to the server.
if
(
urlObj
.
includeDE
==
"true"
)
{
urlObj
.
includeDE
=
true
;
}
else
{
...
...
@@ -329,8 +332,10 @@ define(['require',
type
:
null
,
tag
:
null
,
attributes
:
null
,
tagFilters
:
null
,
pageOffset
:
null
,
pageLimit
:
null
,
entityFilters
:
null
,
includeDE
:
null
}),
param
);
},
...
...
@@ -439,6 +444,7 @@ define(['require',
this
.
value
=
paramObj
;
}
if
(
this
.
value
)
{
this
.
ui
.
searchInput
.
val
(
this
.
value
.
query
||
""
);
if
(
this
.
value
.
dslChecked
==
"true"
)
{
if
(
!
this
.
ui
.
searchType
.
prop
(
"checked"
))
{
this
.
ui
.
searchType
.
prop
(
"checked"
,
true
).
trigger
(
"change"
);
...
...
@@ -470,7 +476,6 @@ define(['require',
}
}
}
this
.
ui
.
searchInput
.
val
(
this
.
value
.
query
||
""
);
setTimeout
(
function
()
{
that
.
ui
.
searchInput
.
focus
();
},
0
);
...
...
@@ -482,15 +487,11 @@ define(['require',
triggerSearch
:
function
(
value
)
{
var
params
=
{
searchType
:
this
.
type
,
dslChecked
:
this
.
ui
.
searchType
.
is
(
':checked'
),
tagFilters
:
null
,
entityFilters
:
null
,
attributes
:
null
,
includeDE
:
null
dslChecked
:
this
.
ui
.
searchType
.
is
(
':checked'
)
}
this
.
query
[
this
.
type
].
type
=
this
.
ui
.
typeLov
.
select2
(
'val'
)
||
null
;
params
[
'type'
]
=
this
.
ui
.
typeLov
.
select2
(
'val'
)
||
null
;
if
(
!
this
.
dsl
)
{
this
.
query
[
this
.
type
].
tag
=
this
.
ui
.
tagLov
.
select2
(
'val'
)
||
null
;
params
[
'tag'
]
=
this
.
ui
.
tagLov
.
select2
(
'val'
)
||
null
;
var
entityFilterObj
=
this
.
searchTableFilters
[
'entityFilters'
],
tagFilterObj
=
this
.
searchTableFilters
[
'tagFilters'
];
if
(
this
.
value
.
tag
)
{
...
...
@@ -503,25 +504,27 @@ define(['require',
if
(
columnList
)
{
params
[
'attributes'
]
=
columnList
.
join
(
','
);
}
if
(
this
.
value
.
includeDE
)
{
if
(
_
.
isUndefinedNull
(
this
.
value
.
includeDE
))
{
params
[
'includeDE'
]
=
false
;
}
else
{
params
[
'includeDE'
]
=
this
.
value
.
includeDE
;
}
}
if
(
this
.
value
.
pageLimit
)
{
this
.
query
[
this
.
type
].
pageLimit
=
this
.
value
.
pageLimit
;
params
[
'pageLimit'
]
=
this
.
value
.
pageLimit
;
}
if
(
this
.
value
.
pageOffset
)
{
if
(
this
.
query
[
this
.
type
].
query
&&
this
.
query
[
this
.
type
].
query
!=
value
)
{
this
.
query
[
this
.
type
].
pageOffset
=
0
;
params
[
'pageOffset'
]
=
0
;
}
else
{
this
.
query
[
this
.
type
].
pageOffset
=
this
.
value
.
pageOffset
;
params
[
'pageOffset'
]
=
this
.
value
.
pageOffset
;
}
}
this
.
query
[
this
.
type
].
query
=
value
||
null
;
params
[
'query'
]
=
value
||
null
;
_
.
extend
(
this
.
query
[
this
.
type
],
params
);
Utils
.
setUrl
({
url
:
'#!/search/searchResult'
,
urlParams
:
_
.
extend
({},
this
.
query
[
this
.
type
]
,
params
),
urlParams
:
_
.
extend
({},
this
.
query
[
this
.
type
]),
mergeBrowserUrl
:
false
,
trigger
:
true
,
updateTabState
:
true
...
...
@@ -555,10 +558,6 @@ define(['require',
this
.
$
(
'.searchText'
).
text
(
'Search By Text'
);
this
.
ui
.
searchInput
.
attr
(
"placeholder"
,
"Search By Text"
);
}
if
(
paramObj
&&
this
.
type
==
"basic"
)
{
this
.
query
[
this
.
type
].
attributes
=
paramObj
.
attributes
?
paramObj
.
attributes
:
null
;
this
.
query
[
this
.
type
].
includeDE
=
this
.
value
.
includeDE
;
}
if
(
Utils
.
getUrlState
.
isSearchTab
())
{
Utils
.
setUrl
({
url
:
'#!/search/searchResult'
,
...
...
dashboardv2/public/js/views/search/
SaveAs
LayoutView.js
→
dashboardv2/public/js/views/search/
save/SaveModal
LayoutView.js
View file @
20f2fba7
...
...
@@ -18,7 +18,7 @@
define
([
'require'
,
'backbone'
,
'hbs!tmpl/search/
SaveAs
LayoutView_tmpl'
,
'hbs!tmpl/search/
save/SaveModal
LayoutView_tmpl'
,
'utils/Utils'
,
'modules/Modal'
,
'utils/UrlLinks'
,
...
...
@@ -26,31 +26,39 @@ define(['require',
'models/VSearch'
,
'utils/CommonViewFunction'
,
'utils/Messages'
],
function
(
require
,
Backbone
,
Save
As
LayoutViewTmpl
,
Utils
,
Modal
,
UrlLinks
,
platform
,
VSearch
,
CommonViewFunction
,
Messages
)
{
],
function
(
require
,
Backbone
,
Save
Modal
LayoutViewTmpl
,
Utils
,
Modal
,
UrlLinks
,
platform
,
VSearch
,
CommonViewFunction
,
Messages
)
{
var
Save
As
LayoutView
=
Backbone
.
Marionette
.
LayoutView
.
extend
({
_viewName
:
'Save
As
LayoutView'
,
template
:
Save
As
LayoutViewTmpl
,
var
Save
Modal
LayoutView
=
Backbone
.
Marionette
.
LayoutView
.
extend
({
_viewName
:
'Save
Modal
LayoutView'
,
template
:
Save
Modal
LayoutViewTmpl
,
regions
:
{},
ui
:
{
saveAsName
:
"[data-id='saveAsName']"
},
templateHelpers
:
function
()
{
return
{
selectedModel
:
this
.
selectedModel
?
this
.
selectedModel
.
toJSON
()
:
null
};
},
events
:
function
()
{
var
events
=
{};
return
events
;
},
initialize
:
function
(
options
)
{
var
that
=
this
;
_
.
extend
(
this
,
_
.
pick
(
options
,
'
value'
,
'collection'
,
'searchVent'
,
'typeHeaders'
,
'fetchFavioriteCollection'
,
'getValue'
,
'isBasic
'
));
_
.
extend
(
this
,
_
.
pick
(
options
,
'
selectedModel'
,
'collection'
,
'getValue'
,
'isBasic'
,
'saveObj
'
));
this
.
model
=
new
VSearch
();
if
(
this
.
saveObj
)
{
this
.
onCreateButton
();
}
else
{
var
modal
=
new
Modal
({
title
:
'Enter your search name'
,
title
:
(
this
.
selectedModel
?
'Update'
:
'Create'
)
+
' your favorite search '
+
(
this
.
selectedModel
?
'name'
:
''
)
,
content
:
this
,
cancelText
:
"Cancel"
,
okCloses
:
false
,
okText
:
'Create'
,
okText
:
this
.
selectedModel
?
'Update'
:
'Create'
,
allowCancel
:
true
}).
open
();
modal
.
$el
.
find
(
'button.ok'
).
attr
(
"disabled"
,
"true"
);
...
...
@@ -69,29 +77,55 @@ define(['require',
modal
.
on
(
'closeModal'
,
function
()
{
modal
.
trigger
(
'cancel'
);
});
}
},
onCreateButton
:
function
(
modal
)
{
var
that
=
this
,
obj
=
{
value
:
this
.
getValue
(),
name
:
this
.
ui
.
saveAsName
.
val
()
},
saveObj
=
CommonViewFunction
.
generateObjectForSaveSearchApi
(
obj
);
obj
=
{
name
:
this
.
ui
.
saveAsName
.
val
?
this
.
ui
.
saveAsName
.
val
()
:
null
};
if
(
this
.
selectedModel
)
{
// Update Name only.
var
saveObj
=
this
.
selectedModel
.
toJSON
();
saveObj
.
name
=
obj
.
name
;
}
else
{
obj
.
value
=
this
.
getValue
();
if
(
this
.
saveObj
)
{
// Save search Filter
_
.
extend
(
obj
,
this
.
saveObj
);
}
var
saveObj
=
CommonViewFunction
.
generateObjectForSaveSearchApi
(
obj
);
if
(
this
.
isBasic
)
{
saveObj
[
'searchType'
]
=
"BASIC"
;
}
else
{
saveObj
[
'searchType'
]
=
"ADVANCED"
;
}
that
.
model
.
urlRoot
=
UrlLinks
.
saveSearchApiUrl
();
that
.
model
.
save
(
saveObj
,
{
}
this
.
model
.
urlRoot
=
UrlLinks
.
saveSearchApiUrl
();
this
.
model
.
save
(
saveObj
,
{
type
:
(
saveObj
.
guid
?
'PUT'
:
'POST'
),
success
:
function
(
model
,
data
)
{
if
(
that
.
collection
)
{
that
.
collection
.
add
(
data
);
if
(
saveObj
.
guid
)
{
var
collectionRef
=
that
.
collection
.
find
({
guid
:
data
.
guid
});
if
(
collectionRef
)
{
collectionRef
.
set
(
data
);
}
Utils
.
notifySuccess
({
content
:
obj
.
name
+
Messages
.
editSuccessMessage
});
}
else
{
that
.
collection
.
add
(
data
);
Utils
.
notifySuccess
({
content
:
obj
.
name
+
Messages
.
addSuccessMessage
});
}
}
}
});
if
(
modal
)
{
modal
.
trigger
(
'cancel'
);
}
}
});
return
Save
As
LayoutView
;
return
Save
Modal
LayoutView
;
});
\ No newline at end of file
dashboardv2/public/js/views/search/SaveSearchItemView.js
→
dashboardv2/public/js/views/search/
save/
SaveSearchItemView.js
View file @
20f2fba7
...
...
@@ -17,15 +17,15 @@
*/
define
([
'require'
,
'backbone'
,
'hbs!tmpl/search/SaveSearchItemView_tmpl'
,
'hbs!tmpl/search/
save/
SaveSearchItemView_tmpl'
,
'utils/UrlLinks'
,
'utils/Utils'
,
'utils/CommonViewFunction'
,
'utils/Messages'
],
function
(
require
,
Backbone
,
SaveSearchItemView
_t
mpl
,
UrlLinks
,
Utils
,
CommonViewFunction
,
Messages
)
{
],
function
(
require
,
Backbone
,
SaveSearchItemView
T
mpl
,
UrlLinks
,
Utils
,
CommonViewFunction
,
Messages
)
{
'use strict'
;
return
Backbone
.
Marionette
.
ItemView
.
extend
({
template
:
SaveSearchItemView
_t
mpl
,
template
:
SaveSearchItemView
T
mpl
,
tagName
:
'li'
,
className
:
'parent-node'
,
ui
:
{
...
...
@@ -61,6 +61,9 @@ define(['require',
this
.
trigger
(
'item:clicked'
);
this
.
ui
.
stateChange
.
parent
(
'li'
).
addClass
(
'active'
).
siblings
().
removeClass
(
'active'
);
},
modelEvents
:
{
'change'
:
'render'
},
showToolTip
:
function
(
e
)
{
var
that
=
this
;
Utils
.
generatePopover
({
...
...
@@ -69,7 +72,8 @@ define(['require',
popoverOptions
:
{
content
:
function
()
{
return
"<ul class='saveSearchPopoverList'>"
+
"<li class='th' ><i class='fa fa-search'></i> <a href='javascript:void(0)' data-fn='onSearch'>Search </a></li>"
+
"<li class='listTerm' ><i class='fa fa-search'></i> <a href='javascript:void(0)' data-fn='onSearch'>Search </a></li>"
+
"<li class='listTerm' ><i class='fa fa-pencil'></i> <a href='javascript:void(0)' data-fn='onRename'>Rename</a></li>"
+
"<li class='listTerm' ><i class='fa fa-trash-o'></i> <a href='javascript:void(0)' data-fn='onDelete'>Delete</a></li>"
+
"</ul>"
;
}
...
...
@@ -95,6 +99,14 @@ define(['require',
updateTabState
:
true
});
},
onRename
:
function
()
{
var
that
=
this
;
require
([
'views/search/save/SaveModalLayoutView'
],
function
(
SaveModalLayoutView
)
{
new
SaveModalLayoutView
({
'selectedModel'
:
that
.
model
,
'collection'
:
that
.
collection
,
'getValue'
:
that
.
getValue
,
'isBasic'
:
that
.
isBasic
});
});
},
onDelete
:
function
()
{
var
that
=
this
;
var
notifyObj
=
{
...
...
dashboardv2/public/js/views/search/SaveSearchView.js
→
dashboardv2/public/js/views/search/
save/
SaveSearchView.js
View file @
20f2fba7
...
...
@@ -18,18 +18,18 @@
define
([
'require'
,
'backbone'
,
'hbs!tmpl/search/
SaveSearch
_tmpl'
,
'views/search/SaveSearchItemView'
,
'hbs!tmpl/search/
save/SaveSearchView
_tmpl'
,
'views/search/
save/
SaveSearchItemView'
,
'collection/VSearchList'
,
'utils/Utils'
,
'utils/UrlLinks'
,
'utils/CommonViewFunction'
,
'utils/Messages'
],
function
(
require
,
Backbone
,
SaveSearch
_
Tmpl
,
SaveSearchItemView
,
VSearchList
,
Utils
,
UrlLinks
,
CommonViewFunction
,
Messages
)
{
],
function
(
require
,
Backbone
,
SaveSearch
View
Tmpl
,
SaveSearchItemView
,
VSearchList
,
Utils
,
UrlLinks
,
CommonViewFunction
,
Messages
)
{
'use strict'
;
return
Backbone
.
Marionette
.
CompositeView
.
extend
({
template
:
SaveSearch
_
Tmpl
,
template
:
SaveSearch
View
Tmpl
,
childView
:
SaveSearchItemView
,
childViewContainer
:
"[data-id='itemViewContent']"
,
ui
:
{
...
...
@@ -68,23 +68,22 @@ define(['require',
this
.
bindEvents
();
},
bindEvents
:
function
()
{
this
.
listenTo
(
this
.
collection
,
"
reset error"
,
function
(
model
,
response
)
{
this
.
listenTo
(
this
.
collection
,
"
add reset error"
,
function
(
model
,
collection
)
{
this
.
$
(
'.fontLoader-relative'
).
hide
();
if
(
model
&&
model
.
length
)
{
this
.
$
(
"
[data-id='itemViewContent']"
).
text
(
""
);
if
(
this
.
collection
&&
this
.
collection
.
length
)
{
this
.
$
(
"
.noFavoriteSearch"
).
hide
(
);
}
else
{
this
.
$
(
"
[data-id='itemViewContent']"
).
text
(
"You don't have any favorite search."
)
this
.
$
(
"
.noFavoriteSearch"
).
show
();
}
},
this
);
},
saveAs
:
function
(
e
)
{
var
that
=
this
,
value
=
this
.
getValue
();
var
value
=
this
.
getValue
();
if
(
value
&&
(
value
.
type
||
value
.
tag
||
value
.
query
))
{
require
([
'
views/search/SaveAsLayoutView'
],
function
(
SaveAsLayoutView
)
{
new
SaveAsLayoutView
({
'value'
:
that
.
value
,
'searchVent'
:
that
.
searchVent
,
'collection'
:
that
.
collection
,
'getValue'
:
that
.
getValue
,
'isBasic'
:
that
.
isBasic
});
this
.
callSaveModalLayoutView
({
'
collection'
:
this
.
collection
,
'getValue'
:
this
.
getValue
,
'isBasic'
:
this
.
isBasic
});
}
else
{
Utils
.
notifyInfo
({
...
...
@@ -99,45 +98,33 @@ define(['require',
modal
:
true
,
html
:
true
,
ok
:
function
(
argument
)
{
that
.
onSaveNotifyOk
(
obj
);
that
.
callSaveModalLayoutView
({
'saveObj'
:
obj
,
'collection'
:
that
.
collection
,
'getValue'
:
that
.
getValue
,
'isBasic'
:
that
.
isBasic
})
},
cancel
:
function
(
argument
)
{}
},
selectedEl
=
this
.
$
(
'.saveSearchList li.active'
).
find
(
'div.item'
);
obj
.
name
=
selectedEl
.
find
(
'a'
).
text
();
obj
.
id
=
selectedEl
.
data
(
'id'
);
obj
.
gu
id
=
selectedEl
.
data
(
'id'
);
if
(
selectedEl
&&
selectedEl
.
length
)
{
notifyObj
[
'text'
]
=
Messages
.
search
.
favoriteSearch
.
save
+
" <b>"
+
obj
.
name
+
"</b> ?"
;
Utils
.
notifyConfirm
(
notifyObj
);
}
else
{
Utils
.
notifyInfo
({
content
:
Messages
.
search
.
favoriteSearch
.
notSelectedElement
content
:
Messages
.
search
.
favoriteSearch
.
notSelected
Favorite
Element
})
}
},
onSaveNotifyOk
:
function
(
obj
)
{
var
that
=
this
if
(
obj
&&
obj
.
id
)
{
var
model
=
new
this
.
collection
.
model
();
obj
.
value
=
this
.
getValue
();
var
saveObj
=
CommonViewFunction
.
generateObjectForSaveSearchApi
(
obj
);
saveObj
[
'guid'
]
=
obj
.
id
;
model
.
urlRoot
=
UrlLinks
.
saveSearchApiUrl
();
model
.
save
(
saveObj
,
{
type
:
'PUT'
,
success
:
function
(
model
,
data
)
{
if
(
that
.
collection
)
{
var
collectionRef
=
that
.
collection
.
find
({
guid
:
data
.
guid
});
if
(
collectionRef
)
{
collectionRef
.
set
(
data
);
}
}
Utils
.
notifySuccess
({
content
:
obj
.
name
+
Messages
.
editSuccessMessage
});
}
callSaveModalLayoutView
:
function
(
options
)
{
require
([
'views/search/save/SaveModalLayoutView'
],
function
(
SaveModalLayoutView
)
{
new
SaveModalLayoutView
(
options
);
});
}
}
});
});
\ No newline at end of file
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