Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
customQueries
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
Bolt Extensions
customQueries
Commits
179d70d4
Commit
179d70d4
authored
Dec 04, 2018
by
Van
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix(all) : autocomplete first item fixed. Exclude and include empty . All fields bt contenttypes
parent
fe66bb39
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
107 additions
and
80 deletions
+107
-80
CustomQueriesExtension.php
src/CustomQueriesExtension.php
+16
-5
app.css
web/css/app.css
+5
-3
app.js
web/js/app.js
+86
-72
No files found.
src/CustomQueriesExtension.php
View file @
179d70d4
...
...
@@ -77,6 +77,7 @@ class CustomQueriesExtension extends SimpleExtension
}
$json
=
json_decode
(
$input
,
true
);
if
(
!
$json
||
empty
(
$json
))
{
return
''
;
}
...
...
@@ -85,7 +86,11 @@ class CustomQueriesExtension extends SimpleExtension
$storage
=
$this
->
getContainer
()[
'storage'
];
foreach
(
$json
as
$contentType
=>
$fields
)
{
$query
=
$this
->
_buildQuery
(
$contentType
,
$fields
);
$query
=
false
;
if
(
$contentType
!=
'all'
){
$query
=
$this
->
_buildQuery
(
$json
,
$contentType
,
$fields
);
}
if
(
!
empty
(
$query
))
{
...
...
@@ -97,12 +102,12 @@ class CustomQueriesExtension extends SimpleExtension
$contents
=
$storage
->
getContent
(
$contentType
,
[
'id'
=>
implode
(
' || '
,
$ids
),
'paging'
=>
true
,
'limit'
=>
99999
]);
if
(
is_array
(
$contents
))
{
$records
=
array_merge
(
$records
,
$contents
);
}
else
{
$records
=
array_merge
(
$records
,
[
$contents
]);
}
}
}
return
$this
->
getContainer
()[
'twig'
]
->
render
(
"custom_queries.twig"
,
[
'records'
=>
$records
]);
...
...
@@ -191,26 +196,32 @@ class CustomQueriesExtension extends SimpleExtension
}
/**
* @param array $json
* @param $contentType
* @param array $fields
* @return string
*/
protected
function
_buildQuery
(
$contentType
,
array
$fields
=
[])
{
protected
function
_buildQuery
(
array
$json
,
$contentType
,
array
$fields
=
[])
{
if
(
!
$contentType
)
{
return
''
;
}
$storage
=
$this
->
getContainer
()[
'storage'
];
$tableName
=
$storage
->
getContenttypeTablename
(
$contentType
);
if
(
!
$tableName
)
{
return
''
;
}
$query
=
'SELECT id FROM '
.
$tableName
;
if
(
!
isset
(
$json
[
'all'
])
||
!
\in_array
(
$contentType
,
$json
[
'all'
]))
{
$where
=
$this
->
_buildWhere
(
$fields
);
if
(
$where
)
{
$query
.=
$where
;
}
}
return
$query
;
}
...
...
web/css/app.css
View file @
179d70d4
...
...
@@ -15,13 +15,15 @@
margin-right
:
1em
;
}
/* Autocomplete css */
.chooseField
{
height
:
26px
!important
;
width
:
20em
!important
;
text-align
:
center
!important
;
}
.select
span
{
width
:
20em
;
text-align
:
center
;
.select2-container
{
text-align
:
left
;
}
.addContentTypeContainer
{
...
...
web/js/app.js
View file @
179d70d4
...
...
@@ -25,20 +25,21 @@ class CustomQueries {
this
.
name
=
element
.
attr
(
'name'
);
this
.
usedContentType
=
[];
this
.
selectors
=
{
addContentTypeContainer
:
'.addContentTypeContainer-'
+
this
.
name
,
addContentTypeBtn
:
'#addContentType-'
+
this
.
name
,
validateContentTypeBtn
:
'#validateContentType-'
+
this
.
name
,
cancelContentTypeBtn
:
'#cancelContentType-'
+
this
.
name
,
selectContentType
:
'.addContentTypeContainer-'
+
this
.
name
+
' select'
,
listContentTypeContainer
:
'.listContentTypeContainer-'
+
this
.
name
,
removeContentTypeBtn
:
'#removeContentType-'
+
this
.
name
,
addFieldBtn
:
'#addField-'
+
this
.
name
,
allFields
:
'#allFields-'
+
this
.
name
,
validateFieldBtn
:
'#validateField-'
+
this
.
name
,
cancelFieldBtn
:
'#cancelField-'
+
this
.
name
,
removeFieldBtn
:
'#removeField-'
+
this
.
name
,
addContentTypeContainer
:
'.addContentTypeContainer-'
+
this
.
name
,
addContentTypeBtn
:
'#addContentType-'
+
this
.
name
,
validateContentTypeBtn
:
'#validateContentType-'
+
this
.
name
,
cancelContentTypeBtn
:
'#cancelContentType-'
+
this
.
name
,
selectContentType
:
'.addContentTypeContainer-'
+
this
.
name
+
' select'
,
listContentTypeContainer
:
'.listContentTypeContainer-'
+
this
.
name
,
removeContentTypeBtn
:
'#removeContentType-'
+
this
.
name
,
addFieldBtn
:
'#addField-'
+
this
.
name
,
allFields
:
'#allFields-'
+
this
.
name
,
validateFieldBtn
:
'#validateField-'
+
this
.
name
,
cancelFieldBtn
:
'#cancelField-'
+
this
.
name
,
removeFieldBtn
:
'#removeField-'
+
this
.
name
,
}
}
/**
* Init
*/
...
...
@@ -54,13 +55,14 @@ class CustomQueries {
/*##########################################
Load original content
##########################################*/
/**
* INIT JSON content for content type
*/
loadOriginalContent
()
{
let
content
=
this
.
json
;
for
(
let
index
in
content
)
{
if
(
index
!=
'all'
){
for
(
let
index
in
content
)
{
if
(
index
!=
'all'
)
{
this
.
createContentTypeBlock
(
index
);
this
.
loadOriginalField
(
index
,
content
[
index
],
content
)
}
...
...
@@ -73,14 +75,14 @@ class CustomQueries {
* @param fields
*/
loadOriginalField
(
contentType
,
fields
,
json
)
{
for
(
let
index
in
fields
)
{
for
(
let
index
in
fields
)
{
let
text
=
index
;
let
joinField
=
'and'
;
if
(
this
.
config
[
contentType
]
&&
this
.
config
[
contentType
][
'fields'
]
&&
this
.
config
[
contentType
][
'fields'
][
index
]
&&
this
.
config
[
contentType
][
'fields'
][
index
].
label
)
{
if
(
this
.
config
[
contentType
]
&&
this
.
config
[
contentType
][
'fields'
]
&&
this
.
config
[
contentType
][
'fields'
][
index
]
&&
this
.
config
[
contentType
][
'fields'
][
index
].
label
)
{
text
=
this
.
config
[
contentType
][
'fields'
][
index
].
label
;
}
if
(
json
[
contentType
]
&&
json
[
contentType
][
index
]
&&
json
[
contentType
][
index
][
'joinFields'
])
{
if
(
json
[
contentType
]
&&
json
[
contentType
][
index
]
&&
json
[
contentType
][
index
][
'joinFields'
])
{
joinField
=
json
[
contentType
][
index
][
'joinFields'
]
}
...
...
@@ -96,9 +98,9 @@ class CustomQueries {
* @param fieldContent
*/
loadOriginalFieldContent
(
contentType
,
field
,
fieldContent
)
{
for
(
let
type
in
fieldContent
)
{
this
.
createInput
(
contentType
,
field
,
type
,
fieldContent
[
type
]);
}
this
.
createInput
(
contentType
,
field
,
'joinFields'
,
fieldContent
[
'joinFields'
]);
this
.
createInput
(
contentType
,
field
,
'include'
,
fieldContent
[
'include'
]);
this
.
createInput
(
contentType
,
field
,
'exclude'
,
fieldContent
[
'exclude'
]);
}
/*##########################################
...
...
@@ -129,7 +131,7 @@ class CustomQueries {
* @param contentType
*/
createContentTypeBlock
(
contentType
)
{
if
(
!
this
.
usedContentType
[
contentType
]
&&
contentType
)
{
if
(
!
this
.
usedContentType
[
contentType
]
&&
contentType
)
{
this
.
usedContentType
[
contentType
]
=
[];
let
checked
=
""
;
if
(
jQuery
.
inArray
(
contentType
,
this
.
json
[
'all'
])
!==
-
1
)
{
...
...
@@ -137,7 +139,7 @@ class CustomQueries {
}
$
(
this
.
selectors
.
listContentTypeContainer
).
append
(
`<fieldset class="bolt-field-repeater">
<div class="repeater-slot contentType
${
contentType
}
">
<div class="repeater-slot contentType
${
contentType
}
"
data-content-type="
${
contentType
}
"
>
<div class="repeater-group panel panel-default">
<div class="panel-heading text-right">
<div class="pull-left title">
...
...
@@ -197,7 +199,7 @@ class CustomQueries {
* @param value
*/
fillContentTypeSelector
(
value
)
{
$
(
this
.
selectors
.
selectContentType
).
append
(
'<option value="'
+
value
+
'">'
+
value
+
'</option>'
);
$
(
this
.
selectors
.
selectContentType
).
append
(
'<option value="'
+
value
+
'">'
+
value
+
'</option>'
);
}
/**
...
...
@@ -206,13 +208,13 @@ class CustomQueries {
* @param field
*/
createFieldLine
(
contentType
,
field
,
fieldText
,
joinField
)
{
if
(
!
joinField
||
(
joinField
!=
'or'
&&
joinField
!=
'and'
))
{
if
(
!
joinField
||
(
joinField
!=
'or'
&&
joinField
!=
'and'
))
{
joinField
=
'and'
;
}
let
andChecked
=
(
joinField
==
'and'
)
?
'checked'
:
''
;
let
orChecked
=
(
joinField
==
'or'
)
?
'checked'
:
''
;
let
andChecked
=
(
joinField
==
'and'
)
?
'checked'
:
''
;
let
orChecked
=
(
joinField
==
'or'
)
?
'checked'
:
''
;
let
container
=
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' tbody'
);
let
container
=
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' tbody'
);
container
.
append
(
`
<tr class="field-
${
field
}
">
<td>
${
fieldText
}
</td>
...
...
@@ -238,25 +240,26 @@ class CustomQueries {
this
.
initEventClickBtnRemoveField
(
contentType
,
field
);
this
.
initEventChaneToggleField
(
contentType
,
field
);
}
/**
* Create Input in Field line
* @param contentType
* @param field
*/
createInput
(
contentType
,
field
,
type
,
values
)
{
let
container
=
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' tbody .field-'
+
field
+
' .'
+
type
);
let
input
=
'<select class="tags form-control" id="field-'
+
contentType
+
'-'
+
field
+
'-'
+
type
+
'" multiple="true">'
;
let
container
=
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' tbody .field-'
+
field
+
' .'
+
type
);
let
input
=
'<select class="tags form-control" id="field-'
+
contentType
+
'-'
+
field
+
'-'
+
type
+
'" multiple="true">'
;
if
(
values
)
{
for
(
let
index
in
values
)
{
input
+=
'<option value="'
+
values
[
index
]
+
'" selected>'
+
values
[
index
]
+
'</option>'
;
if
(
values
)
{
for
(
let
index
in
values
)
{
input
+=
'<option value="'
+
values
[
index
]
+
'" selected>'
+
values
[
index
]
+
'</option>'
;
}
}
input
+=
'</select>'
;
container
.
append
(
input
);
let
selector
=
$
(
'#field-'
+
contentType
+
'-'
+
field
+
'-'
+
type
);
let
selector
=
$
(
'#field-'
+
contentType
+
'-'
+
field
+
'-'
+
type
);
this
.
initEventSelectTags
(
selector
);
}
...
...
@@ -267,8 +270,8 @@ class CustomQueries {
* @param text
*/
fillFieldSelector
(
contentType
,
value
,
text
)
{
if
(
this
.
usedContentType
[
contentType
].
indexOf
(
value
)
===
-
1
)
{
$
(
'#chooseField-'
+
this
.
name
+
'-'
+
contentType
).
append
(
'<option value="'
+
value
+
'">'
+
text
+
'</option>'
);
if
(
this
.
usedContentType
[
contentType
].
indexOf
(
value
)
===
-
1
)
{
$
(
'#chooseField-'
+
this
.
name
+
'-'
+
contentType
).
append
(
'<option value="'
+
value
+
'">'
+
text
+
'</option>'
);
}
}
...
...
@@ -292,9 +295,9 @@ class CustomQueries {
$
(
this
.
selectors
.
addContentTypeBtn
).
on
(
'click'
,
(
e
)
=>
{
e
.
preventDefault
();
$
(
this
.
selectors
.
addContentTypeBtn
).
hide
();
$
(
this
.
selectors
.
selectContentType
+
' option'
).
remove
();
for
(
let
index
in
this
.
config
)
{
if
(
!
this
.
usedContentType
[
index
])
{
$
(
this
.
selectors
.
selectContentType
+
' option'
).
remove
();
for
(
let
index
in
this
.
config
)
{
if
(
!
this
.
usedContentType
[
index
])
{
this
.
fillContentTypeSelector
(
index
);
}
}
...
...
@@ -307,6 +310,7 @@ class CustomQueries {
$
(
this
.
selectors
.
selectContentType
).
parent
().
hide
();
$
(
this
.
selectors
.
addContentTypeBtn
).
show
();
this
.
createContentTypeBlock
(
$
(
this
.
selectors
.
selectContentType
).
val
());
this
.
rebuildJson
();
});
// Content type cancel btn
...
...
@@ -322,13 +326,13 @@ class CustomQueries {
* @param contentType
*/
initEventClickBtnRemoveContentType
(
contentType
)
{
$
(
this
.
selectors
.
removeContentTypeBtn
+
'-'
+
contentType
).
on
(
'click'
,
(
e
)
=>
{
$
(
this
.
selectors
.
removeContentTypeBtn
+
'-'
+
contentType
).
on
(
'click'
,
(
e
)
=>
{
e
.
preventDefault
();
this
.
usedContentType
=
this
.
usedContentType
.
slice
(
this
.
usedContentType
.
indexOf
(
contentType
));
let
index
=
this
.
usedContentType
.
indexOf
(
contentType
);
this
.
usedContentType
.
splice
(
index
,
1
);
$
(
'.listContentTypeContainer-'
+
this
.
name
+
' .contentType.'
+
contentType
).
remove
();
$
(
'.listContentTypeContainer-'
+
this
.
name
+
' .contentType.'
+
contentType
).
remove
();
this
.
rebuildJson
();
});
...
...
@@ -337,8 +341,8 @@ class CustomQueries {
/**
* @param contentType
*/
initEventClickAllFields
(
contentType
){
$
(
this
.
selectors
.
allFields
+
'-'
+
contentType
).
on
(
'click'
,
(
e
)
=>
{
initEventClickAllFields
(
contentType
)
{
$
(
this
.
selectors
.
allFields
+
'-'
+
contentType
).
on
(
'click'
,
(
e
)
=>
{
this
.
rebuildJson
();
});
}
...
...
@@ -349,32 +353,33 @@ class CustomQueries {
*/
initEventClickBtnAddField
(
contentType
)
{
// Field add btn
$
(
this
.
selectors
.
addFieldBtn
+
'-'
+
contentType
).
on
(
'click'
,
(
e
)
=>
{
$
(
this
.
selectors
.
addFieldBtn
+
'-'
+
contentType
).
on
(
'click'
,
(
e
)
=>
{
e
.
preventDefault
();
$
(
this
.
selectors
.
addFieldBtn
+
'-'
+
contentType
).
hide
();
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' .headField select option'
).
remove
();
if
(
this
.
config
[
contentType
]
&&
this
.
config
[
contentType
][
'fields'
])
{
/*console.log(this.config[contentType]['fields']);*/
for
(
let
index
in
this
.
config
[
contentType
][
'fields'
])
{
$
(
this
.
selectors
.
addFieldBtn
+
'-'
+
contentType
).
hide
();
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' .headField select option'
).
remove
();
if
(
this
.
config
[
contentType
]
&&
this
.
config
[
contentType
][
'fields'
])
{
for
(
let
index
in
this
.
config
[
contentType
][
'fields'
])
{
let
text
=
index
;
if
(
this
.
config
[
contentType
][
'fields'
][
index
].
label
)
{
if
(
this
.
config
[
contentType
][
'fields'
][
index
].
label
)
{
text
=
this
.
config
[
contentType
][
'fields'
][
index
].
label
;
}
this
.
fillFieldSelector
(
contentType
,
index
,
text
);
}
}
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' .headField'
).
show
();
});
$
(
'.chooseField'
).
select2
();
setTimeout
(
function
()
{
$
(
'.chooseField'
).
select2
();
},
1
);
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' .headField'
).
show
();
});
// Field validate btn
$
(
this
.
selectors
.
validateFieldBtn
+
'-'
+
contentType
).
on
(
'click'
,
(
e
)
=>
{
$
(
this
.
selectors
.
validateFieldBtn
+
'-'
+
contentType
).
on
(
'click'
,
(
e
)
=>
{
e
.
preventDefault
();
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' .headField'
).
hide
();
$
(
this
.
selectors
.
addFieldBtn
+
'-'
+
contentType
).
show
();
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' .headField'
).
hide
();
$
(
this
.
selectors
.
addFieldBtn
+
'-'
+
contentType
).
show
();
let
field
=
$
(
'#chooseField-'
+
this
.
name
+
'-'
+
contentType
).
val
();
let
fieldText
=
$
(
'#chooseField-'
+
this
.
name
+
'-'
+
contentType
+
' option:selected'
).
text
();
let
field
=
$
(
'#chooseField-'
+
this
.
name
+
'-'
+
contentType
).
val
();
let
fieldText
=
$
(
'#chooseField-'
+
this
.
name
+
'-'
+
contentType
+
' option:selected'
).
text
();
this
.
createFieldLine
(
contentType
,
field
,
fieldText
);
this
.
createInput
(
contentType
,
field
,
'include'
);
...
...
@@ -384,10 +389,10 @@ class CustomQueries {
});
// Field cancel btn
$
(
this
.
selectors
.
cancelFieldBtn
+
'-'
+
contentType
).
on
(
'click'
,
(
e
)
=>
{
$
(
this
.
selectors
.
cancelFieldBtn
+
'-'
+
contentType
).
on
(
'click'
,
(
e
)
=>
{
e
.
preventDefault
();
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' .headField'
).
hide
();
$
(
this
.
selectors
.
addFieldBtn
+
'-'
+
contentType
).
show
();
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' .headField'
).
hide
();
$
(
this
.
selectors
.
addFieldBtn
+
'-'
+
contentType
).
show
();
});
}
...
...
@@ -397,9 +402,9 @@ class CustomQueries {
* @param field
*/
initEventClickBtnRemoveField
(
contentType
,
field
)
{
$
(
this
.
selectors
.
removeFieldBtn
+
'-'
+
contentType
+
'-'
+
field
).
on
(
'click'
,
(
e
)
=>
{
$
(
this
.
selectors
.
removeFieldBtn
+
'-'
+
contentType
+
'-'
+
field
).
on
(
'click'
,
(
e
)
=>
{
e
.
preventDefault
();
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' .field-'
+
field
).
remove
();
$
(
this
.
selectors
.
listContentTypeContainer
+
' .contentType.'
+
contentType
+
' .field-'
+
field
).
remove
();
// removed used field
this
.
rebuildJson
();
...
...
@@ -411,7 +416,7 @@ class CustomQueries {
* @param selector
*/
initEventSelectTags
(
selector
)
{
selector
.
select2
({
width
:
"100%"
,
tags
:
true
,
minimumInputLength
:
1
,
tokenSeparators
:
[
','
]});
selector
.
select2
({
width
:
"100%"
,
tags
:
true
,
minimumInputLength
:
1
,
tokenSeparators
:
[
','
]});
selector
.
on
(
"select2:select"
,
(
e
)
=>
{
this
.
rebuildJson
();
...
...
@@ -428,10 +433,10 @@ class CustomQueries {
* @param field
*/
initEventChaneToggleField
(
contentType
,
field
)
{
$
(
'#or-'
+
contentType
+
'-'
+
field
).
on
(
'click'
,
(
e
)
=>
{
$
(
'#or-'
+
contentType
+
'-'
+
field
).
on
(
'click'
,
(
e
)
=>
{
this
.
rebuildJson
();
});
$
(
'#and-'
+
contentType
+
'-'
+
field
).
on
(
'click'
,
(
e
)
=>
{
$
(
'#and-'
+
contentType
+
'-'
+
field
).
on
(
'click'
,
(
e
)
=>
{
this
.
rebuildJson
();
});
}
...
...
@@ -441,20 +446,28 @@ class CustomQueries {
*/
rebuildJson
()
{
let
json
=
{
all
:
{}
all
:
[]
};
$
(
'.contentType'
).
each
(
function
()
{
let
contentType
=
$
(
this
).
data
(
'contentType'
);
if
(
!
json
[
contentType
])
{
json
[
contentType
]
=
{};
}
});
$
(
'.tags'
).
each
(
function
()
{
let
id
=
$
(
this
).
attr
(
'id'
).
split
(
'-'
);
let
value
=
$
(
this
).
select2
(
'val'
);
if
(
value
)
{
if
(
value
)
{
let
joinFields
=
'and'
;
if
(
$
(
'#or-'
+
id
[
1
]
+
'-'
+
id
[
2
]).
is
(
":checked"
))
{
if
(
$
(
'#or-'
+
id
[
1
]
+
'-'
+
id
[
2
]).
is
(
":checked"
))
{
joinFields
=
'or'
;
}
if
(
!
json
[
id
[
1
]])
{
if
(
!
json
[
id
[
1
]])
{
json
[
id
[
1
]]
=
{};
}
if
(
!
json
[
id
[
1
]][
id
[
2
]])
{
if
(
!
json
[
id
[
1
]][
id
[
2
]])
{
json
[
id
[
1
]][
id
[
2
]]
=
{
joinFields
:
joinFields
};
...
...
@@ -462,12 +475,13 @@ class CustomQueries {
json
[
id
[
1
]][
id
[
2
]][
id
[
3
]]
=
value
;
}
});
$
(
'.allCheckbox input'
).
each
(
function
()
{
if
(
$
(
this
).
is
(
':checked'
))
{
$
(
'.allCheckbox input'
).
each
(
function
(
e
)
{
if
(
$
(
this
).
is
(
':checked'
))
{
let
contentType
=
$
(
this
).
attr
(
'id'
).
replace
(
'allFields-list-'
,
''
);
json
.
all
.
push
(
contentType
);
}
});
/*console.log(json);*/
this
.
element
.
val
(
JSON
.
stringify
(
json
));
}
...
...
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