Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
M
mongo-php-library
Project
Project
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
sinan
mongo-php-library
Commits
d1061386
Commit
d1061386
authored
Oct 06, 2017
by
Jeremy Mikola
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #410
parents
5731a023
202bef69
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
262 additions
and
3 deletions
+262
-3
apiargs-MongoDBCollection-method-find-option.yaml
...ncludes/apiargs-MongoDBCollection-method-find-option.yaml
+81
-0
apiargs-MongoDBCollection-method-findOne-option.yaml
...udes/apiargs-MongoDBCollection-method-findOne-option.yaml
+24
-0
Find.php
src/Operation/Find.php
+54
-3
FindOne.php
src/Operation/FindOne.php
+18
-0
FindFunctionalTest.php
tests/Operation/FindFunctionalTest.php
+52
-0
FindTest.php
tests/Operation/FindTest.php
+33
-0
No files found.
docs/includes/apiargs-MongoDBCollection-method-find-option.yaml
View file @
d1061386
...
@@ -80,6 +80,19 @@ interface: phpmethod
...
@@ -80,6 +80,19 @@ interface: phpmethod
operation
:
~
operation
:
~
optional
:
true
optional
:
true
---
---
arg_name
:
option
name
:
hint
type
:
string|array|object
description
:
|
The index to use. Specify either the index name as a string or the index key
pattern as a document. If specified, then the query system will only consider
plans using the hinted index.
.. versionadded:: 1.2
interface
:
phpmethod
operation
:
~
optional
:
true
---
source
:
source
:
file
:
apiargs-common-option.yaml
file
:
apiargs-common-option.yaml
ref
:
maxTimeMS
ref
:
maxTimeMS
...
@@ -93,6 +106,39 @@ source:
...
@@ -93,6 +106,39 @@ source:
ref
:
readPreference
ref
:
readPreference
---
---
arg_name
:
option
arg_name
:
option
name
:
max
type
:
array|object
description
:
|
The exclusive upper bound for a specific index.
.. versionadded:: 1.2
interface
:
phpmethod
operation
:
~
optional
:
true
---
arg_name
:
option
name
:
maxScan
type
:
integer
description
:
|
Maximum number of documents or index keys to scan when executing the query.
.. versionadded:: 1.2
interface
:
phpmethod
operation
:
~
optional
:
true
---
arg_name
:
option
name
:
min
type
:
array|object
description
:
|
The inclusive lower bound for a specific index.
.. versionadded:: 1.2
interface
:
phpmethod
operation
:
~
optional
:
true
---
arg_name
:
option
name
:
oplogReplay
name
:
oplogReplay
type
:
boolean
type
:
boolean
description
:
|
description
:
|
...
@@ -120,6 +166,41 @@ operation: ~
...
@@ -120,6 +166,41 @@ operation: ~
optional
:
true
optional
:
true
---
---
arg_name
:
option
arg_name
:
option
name
:
returnKey
type
:
boolean
description
:
|
If true, returns only the index keys in the resulting documents.
.. versionadded:: 1.2
interface
:
phpmethod
operation
:
~
optional
:
true
---
arg_name
:
option
name
:
showRecordId
type
:
boolean
description
:
|
Determines whether to return the record identifier for each document. If true,
adds a field $recordId to the returned documents.
.. versionadded:: 1.2
interface
:
phpmethod
operation
:
~
optional
:
true
---
arg_name
:
option
name
:
snapshot
type
:
boolean
description
:
|
Prevents the cursor from returning a document more than once because of an
intervening write operation.
.. versionadded:: 1.2
interface
:
phpmethod
operation
:
~
optional
:
true
---
arg_name
:
option
name
:
allowPartialResults
name
:
allowPartialResults
type
:
boolean
type
:
boolean
description
:
|
description
:
|
...
...
docs/includes/apiargs-MongoDBCollection-method-findOne-option.yaml
View file @
d1061386
...
@@ -18,6 +18,10 @@ source:
...
@@ -18,6 +18,10 @@ source:
file
:
apiargs-MongoDBCollection-method-find-option.yaml
file
:
apiargs-MongoDBCollection-method-find-option.yaml
ref
:
comment
ref
:
comment
---
---
source
:
file
:
apiargs-MongoDBCollection-method-find-option.yaml
ref
:
hint
---
source
:
source
:
file
:
apiargs-common-option.yaml
file
:
apiargs-common-option.yaml
ref
:
maxTimeMS
ref
:
maxTimeMS
...
@@ -36,6 +40,26 @@ source:
...
@@ -36,6 +40,26 @@ source:
post
:
|
post
:
|
This will be used for the returned result document.
This will be used for the returned result document.
---
---
source
:
file
:
apiargs-MongoDBCollection-method-find-option.yaml
ref
:
max
---
source
:
file
:
apiargs-MongoDBCollection-method-find-option.yaml
ref
:
maxScan
---
source
:
file
:
apiargs-MongoDBCollection-method-find-option.yaml
ref
:
min
---
source
:
file
:
apiargs-MongoDBCollection-method-find-option.yaml
ref
:
returnKey
---
source
:
file
:
apiargs-MongoDBCollection-method-find-option.yaml
ref
:
showRecordId
---
source
:
source
:
file
:
apiargs-MongoDBCollection-method-find-option.yaml
file
:
apiargs-MongoDBCollection-method-find-option.yaml
ref
:
modifiers
ref
:
modifiers
...
...
src/Operation/Find.php
View file @
d1061386
...
@@ -70,12 +70,23 @@ class Find implements Executable
...
@@ -70,12 +70,23 @@ class Find implements Executable
* NON_TAILABLE, TAILABLE, or TAILABLE_AWAIT. The default is
* NON_TAILABLE, TAILABLE, or TAILABLE_AWAIT. The default is
* NON_TAILABLE.
* NON_TAILABLE.
*
*
* * hint (string|document): The index to use. Specify either the index
* name as a string or the index key pattern as a document. If specified,
* then the query system will only consider plans using the hinted index.
*
* * limit (integer): The maximum number of documents to return.
* * limit (integer): The maximum number of documents to return.
*
*
* * max (document): The exclusive upper bound for a specific index.
*
* * maxScan (integer): Maximum number of documents or index keys to scan
* when executing the query.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. If "$maxTimeMS" also exists in the modifiers document, this
* run. If "$maxTimeMS" also exists in the modifiers document, this
* option will take precedence.
* option will take precedence.
*
*
* * min (document): The inclusive upper bound for a specific index.
*
* * modifiers (document): Meta operators that modify the output or
* * modifiers (document): Meta operators that modify the output or
* behavior of a query. Use of these operators is deprecated in favor of
* behavior of a query. Use of these operators is deprecated in favor of
* named options.
* named options.
...
@@ -97,8 +108,18 @@ class Find implements Executable
...
@@ -97,8 +108,18 @@ class Find implements Executable
*
*
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
*
*
* * returnKey (boolean): If true, returns only the index keys in the
* resulting documents.
*
* * showRecordId (boolean): Determines whether to return the record
* identifier for each document. If true, adds a field $recordId to the
* returned documents.
*
* * skip (integer): The number of documents to skip before returning.
* * skip (integer): The number of documents to skip before returning.
*
*
* * snapshot (boolean): Prevents the cursor from returning a document more
* than once because of an intervening write operation.
*
* * sort (document): The order in which to return matching documents. If
* * sort (document): The order in which to return matching documents. If
* "$orderby" also exists in the modifiers document, this option will
* "$orderby" also exists in the modifiers document, this option will
* take precedence.
* take precedence.
...
@@ -146,14 +167,30 @@ class Find implements Executable
...
@@ -146,14 +167,30 @@ class Find implements Executable
}
}
}
}
if
(
isset
(
$options
[
'hint'
])
&&
!
is_string
(
$options
[
'hint'
])
&&
!
is_array
(
$options
[
'hint'
])
&&
!
is_object
(
$options
[
'hint'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"hint" option'
,
$options
[
'hint'
],
'string or array or object'
);
}
if
(
isset
(
$options
[
'limit'
])
&&
!
is_integer
(
$options
[
'limit'
]))
{
if
(
isset
(
$options
[
'limit'
])
&&
!
is_integer
(
$options
[
'limit'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"limit" option'
,
$options
[
'limit'
],
'integer'
);
throw
InvalidArgumentException
::
invalidType
(
'"limit" option'
,
$options
[
'limit'
],
'integer'
);
}
}
if
(
isset
(
$options
[
'max'
])
&&
!
is_array
(
$options
[
'max'
])
&&
!
is_object
(
$options
[
'max'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"max" option'
,
$options
[
'max'
],
'array or object'
);
}
if
(
isset
(
$options
[
'maxScan'
])
&&
!
is_integer
(
$options
[
'maxScan'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"maxScan" option'
,
$options
[
'maxScan'
],
'integer'
);
}
if
(
isset
(
$options
[
'maxTimeMS'
])
&&
!
is_integer
(
$options
[
'maxTimeMS'
]))
{
if
(
isset
(
$options
[
'maxTimeMS'
])
&&
!
is_integer
(
$options
[
'maxTimeMS'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"maxTimeMS" option'
,
$options
[
'maxTimeMS'
],
'integer'
);
throw
InvalidArgumentException
::
invalidType
(
'"maxTimeMS" option'
,
$options
[
'maxTimeMS'
],
'integer'
);
}
}
if
(
isset
(
$options
[
'min'
])
&&
!
is_array
(
$options
[
'min'
])
&&
!
is_object
(
$options
[
'min'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"min" option'
,
$options
[
'min'
],
'array or object'
);
}
if
(
isset
(
$options
[
'modifiers'
])
&&
!
is_array
(
$options
[
'modifiers'
])
&&
!
is_object
(
$options
[
'modifiers'
]))
{
if
(
isset
(
$options
[
'modifiers'
])
&&
!
is_array
(
$options
[
'modifiers'
])
&&
!
is_object
(
$options
[
'modifiers'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"modifiers" option'
,
$options
[
'modifiers'
],
'array or object'
);
throw
InvalidArgumentException
::
invalidType
(
'"modifiers" option'
,
$options
[
'modifiers'
],
'array or object'
);
}
}
...
@@ -178,10 +215,22 @@ class Find implements Executable
...
@@ -178,10 +215,22 @@ class Find implements Executable
throw
InvalidArgumentException
::
invalidType
(
'"readPreference" option'
,
$options
[
'readPreference'
],
'MongoDB\Driver\ReadPreference'
);
throw
InvalidArgumentException
::
invalidType
(
'"readPreference" option'
,
$options
[
'readPreference'
],
'MongoDB\Driver\ReadPreference'
);
}
}
if
(
isset
(
$options
[
'returnKey'
])
&&
!
is_bool
(
$options
[
'returnKey'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"returnKey" option'
,
$options
[
'returnKey'
],
'boolean'
);
}
if
(
isset
(
$options
[
'showRecordId'
])
&&
!
is_bool
(
$options
[
'showRecordId'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"showRecordId" option'
,
$options
[
'showRecordId'
],
'boolean'
);
}
if
(
isset
(
$options
[
'skip'
])
&&
!
is_integer
(
$options
[
'skip'
]))
{
if
(
isset
(
$options
[
'skip'
])
&&
!
is_integer
(
$options
[
'skip'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"skip" option'
,
$options
[
'skip'
],
'integer'
);
throw
InvalidArgumentException
::
invalidType
(
'"skip" option'
,
$options
[
'skip'
],
'integer'
);
}
}
if
(
isset
(
$options
[
'snapshot'
])
&&
!
is_bool
(
$options
[
'snapshot'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"snapshot" option'
,
$options
[
'snapshot'
],
'boolean'
);
}
if
(
isset
(
$options
[
'sort'
])
&&
!
is_array
(
$options
[
'sort'
])
&&
!
is_object
(
$options
[
'sort'
]))
{
if
(
isset
(
$options
[
'sort'
])
&&
!
is_array
(
$options
[
'sort'
])
&&
!
is_object
(
$options
[
'sort'
]))
{
throw
InvalidArgumentException
::
invalidType
(
'"sort" option'
,
$options
[
'sort'
],
'array or object'
);
throw
InvalidArgumentException
::
invalidType
(
'"sort" option'
,
$options
[
'sort'
],
'array or object'
);
}
}
...
@@ -249,14 +298,16 @@ class Find implements Executable
...
@@ -249,14 +298,16 @@ class Find implements Executable
}
}
}
}
foreach
([
'allowPartialResults'
,
'batchSize'
,
'comment'
,
'
limit'
,
'maxTimeMS'
,
'noCursorTimeout'
,
'oplogReplay'
,
'projection'
,
'readConcern'
,
'skip
'
,
'sort'
]
as
$option
)
{
foreach
([
'allowPartialResults'
,
'batchSize'
,
'comment'
,
'
hint'
,
'limit'
,
'maxScan'
,
'maxTimeMS'
,
'noCursorTimeout'
,
'oplogReplay'
,
'projection'
,
'readConcern'
,
'returnKey'
,
'showRecordId'
,
'skip'
,
'snapshot
'
,
'sort'
]
as
$option
)
{
if
(
isset
(
$this
->
options
[
$option
]))
{
if
(
isset
(
$this
->
options
[
$option
]))
{
$options
[
$option
]
=
$this
->
options
[
$option
];
$options
[
$option
]
=
$this
->
options
[
$option
];
}
}
}
}
if
(
isset
(
$this
->
options
[
'collation'
]))
{
foreach
([
'collation'
,
'max'
,
'min'
]
as
$option
)
{
$options
[
'collation'
]
=
(
object
)
$this
->
options
[
'collation'
];
if
(
isset
(
$this
->
options
[
$option
]))
{
$options
[
$option
]
=
(
object
)
$this
->
options
[
$option
];
}
}
}
$modifiers
=
empty
(
$this
->
options
[
'modifiers'
])
?
[]
:
(
array
)
$this
->
options
[
'modifiers'
];
$modifiers
=
empty
(
$this
->
options
[
'modifiers'
])
?
[]
:
(
array
)
$this
->
options
[
'modifiers'
];
...
...
src/Operation/FindOne.php
View file @
d1061386
...
@@ -48,10 +48,21 @@ class FindOne implements Executable
...
@@ -48,10 +48,21 @@ class FindOne implements Executable
* * comment (string): Attaches a comment to the query. If "$comment" also
* * comment (string): Attaches a comment to the query. If "$comment" also
* exists in the modifiers document, this option will take precedence.
* exists in the modifiers document, this option will take precedence.
*
*
* * hint (string|document): The index to use. Specify either the index
* name as a string or the index key pattern as a document. If specified,
* then the query system will only consider plans using the hinted index.
*
* * max (document): The exclusive upper bound for a specific index.
*
* * maxScan (integer): Maximum number of documents or index keys to scan
* when executing the query.
*
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* * maxTimeMS (integer): The maximum amount of time to allow the query to
* run. If "$maxTimeMS" also exists in the modifiers document, this
* run. If "$maxTimeMS" also exists in the modifiers document, this
* option will take precedence.
* option will take precedence.
*
*
* * min (document): The inclusive upper bound for a specific index.
*
* * modifiers (document): Meta-operators modifying the output or behavior
* * modifiers (document): Meta-operators modifying the output or behavior
* of a query.
* of a query.
*
*
...
@@ -65,6 +76,13 @@ class FindOne implements Executable
...
@@ -65,6 +76,13 @@ class FindOne implements Executable
*
*
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
* * readPreference (MongoDB\Driver\ReadPreference): Read preference.
*
*
* * returnKey (boolean): If true, returns only the index keys in the
* resulting documents.
*
* * showRecordId (boolean): Determines whether to return the record
* identifier for each document. If true, adds a field $recordId to the
* returned documents.
*
* * skip (integer): The number of documents to skip before returning.
* * skip (integer): The number of documents to skip before returning.
*
*
* * sort (document): The order in which to return matching documents. If
* * sort (document): The order in which to return matching documents. If
...
...
tests/Operation/FindFunctionalTest.php
View file @
d1061386
...
@@ -3,6 +3,7 @@
...
@@ -3,6 +3,7 @@
namespace
MongoDB\Tests\Operation
;
namespace
MongoDB\Tests\Operation
;
use
MongoDB\Driver\BulkWrite
;
use
MongoDB\Driver\BulkWrite
;
use
MongoDB\Operation\CreateIndexes
;
use
MongoDB\Operation\Find
;
use
MongoDB\Operation\Find
;
use
MongoDB\Tests\CommandObserver
;
use
MongoDB\Tests\CommandObserver
;
use
stdClass
;
use
stdClass
;
...
@@ -28,6 +29,57 @@ class FindFunctionalTest extends FunctionalTestCase
...
@@ -28,6 +29,57 @@ class FindFunctionalTest extends FunctionalTestCase
);
);
}
}
public
function
testHintOption
()
{
$bulkWrite
=
new
BulkWrite
;
$bulkWrite
->
insert
([
'_id'
=>
1
,
'x'
=>
1
]);
$bulkWrite
->
insert
([
'_id'
=>
2
,
'x'
=>
2
]);
$bulkWrite
->
insert
([
'_id'
=>
3
,
'y'
=>
3
]);
$this
->
manager
->
executeBulkWrite
(
$this
->
getNamespace
(),
$bulkWrite
);
$createIndexes
=
new
CreateIndexes
(
$this
->
getDatabaseName
(),
$this
->
getCollectionName
(),
[
[
'key'
=>
[
'x'
=>
1
],
'sparse'
=>
true
,
'name'
=>
'sparse_x'
],
[
'key'
=>
[
'y'
=>
1
]],
]);
$createIndexes
->
execute
(
$this
->
getPrimaryServer
());
$hintsUsingSparseIndex
=
[
[
'x'
=>
1
],
'sparse_x'
,
];
foreach
(
$hintsUsingSparseIndex
as
$hint
)
{
$operation
=
new
Find
(
$this
->
getDatabaseName
(),
$this
->
getCollectionName
(),
[],
[
'hint'
=>
$hint
]);
$cursor
=
$operation
->
execute
(
$this
->
getPrimaryServer
());
$expectedDocuments
=
[
(
object
)
[
'_id'
=>
1
,
'x'
=>
1
],
(
object
)
[
'_id'
=>
2
,
'x'
=>
2
],
];
$this
->
assertEquals
(
$expectedDocuments
,
$cursor
->
toArray
());
}
$hintsNotUsingSparseIndex
=
[
[
'_id'
=>
1
],
[
'y'
=>
1
],
'y_1'
,
];
foreach
(
$hintsNotUsingSparseIndex
as
$hint
)
{
$operation
=
new
Find
(
$this
->
getDatabaseName
(),
$this
->
getCollectionName
(),
[],
[
'hint'
=>
$hint
]);
$cursor
=
$operation
->
execute
(
$this
->
getPrimaryServer
());
$expectedDocuments
=
[
(
object
)
[
'_id'
=>
1
,
'x'
=>
1
],
(
object
)
[
'_id'
=>
2
,
'x'
=>
2
],
(
object
)
[
'_id'
=>
3
,
'y'
=>
3
],
];
$this
->
assertEquals
(
$expectedDocuments
,
$cursor
->
toArray
());
}
}
/**
/**
* @dataProvider provideTypeMapOptionsAndExpectedDocuments
* @dataProvider provideTypeMapOptionsAndExpectedDocuments
*/
*/
...
...
tests/Operation/FindTest.php
View file @
d1061386
...
@@ -48,14 +48,30 @@ class FindTest extends TestCase
...
@@ -48,14 +48,30 @@ class FindTest extends TestCase
$options
[][]
=
[
'cursorType'
=>
$value
];
$options
[][]
=
[
'cursorType'
=>
$value
];
}
}
foreach
(
$this
->
getInvalidHintValues
()
as
$value
)
{
$options
[][]
=
[
'hint'
=>
$value
];
}
foreach
(
$this
->
getInvalidIntegerValues
()
as
$value
)
{
foreach
(
$this
->
getInvalidIntegerValues
()
as
$value
)
{
$options
[][]
=
[
'limit'
=>
$value
];
$options
[][]
=
[
'limit'
=>
$value
];
}
}
foreach
(
$this
->
getInvalidDocumentValues
()
as
$value
)
{
$options
[][]
=
[
'max'
=>
$value
];
}
foreach
(
$this
->
getInvalidIntegerValues
()
as
$value
)
{
$options
[][]
=
[
'maxScan'
=>
$value
];
}
foreach
(
$this
->
getInvalidIntegerValues
()
as
$value
)
{
foreach
(
$this
->
getInvalidIntegerValues
()
as
$value
)
{
$options
[][]
=
[
'maxTimeMS'
=>
$value
];
$options
[][]
=
[
'maxTimeMS'
=>
$value
];
}
}
foreach
(
$this
->
getInvalidDocumentValues
()
as
$value
)
{
$options
[][]
=
[
'min'
=>
$value
];
}
foreach
(
$this
->
getInvalidDocumentValues
()
as
$value
)
{
foreach
(
$this
->
getInvalidDocumentValues
()
as
$value
)
{
$options
[][]
=
[
'modifiers'
=>
$value
];
$options
[][]
=
[
'modifiers'
=>
$value
];
}
}
...
@@ -76,10 +92,22 @@ class FindTest extends TestCase
...
@@ -76,10 +92,22 @@ class FindTest extends TestCase
$options
[][]
=
[
'readPreference'
=>
$value
];
$options
[][]
=
[
'readPreference'
=>
$value
];
}
}
foreach
(
$this
->
getInvalidBooleanValues
()
as
$value
)
{
$options
[][]
=
[
'returnKey'
=>
$value
];
}
foreach
(
$this
->
getInvalidBooleanValues
()
as
$value
)
{
$options
[][]
=
[
'showRecordId'
=>
$value
];
}
foreach
(
$this
->
getInvalidIntegerValues
()
as
$value
)
{
foreach
(
$this
->
getInvalidIntegerValues
()
as
$value
)
{
$options
[][]
=
[
'skip'
=>
$value
];
$options
[][]
=
[
'skip'
=>
$value
];
}
}
foreach
(
$this
->
getInvalidBooleanValues
()
as
$value
)
{
$options
[][]
=
[
'snapshot'
=>
$value
];
}
foreach
(
$this
->
getInvalidDocumentValues
()
as
$value
)
{
foreach
(
$this
->
getInvalidDocumentValues
()
as
$value
)
{
$options
[][]
=
[
'sort'
=>
$value
];
$options
[][]
=
[
'sort'
=>
$value
];
}
}
...
@@ -91,6 +119,11 @@ class FindTest extends TestCase
...
@@ -91,6 +119,11 @@ class FindTest extends TestCase
return
$options
;
return
$options
;
}
}
private
function
getInvalidHintValues
()
{
return
[
123
,
3.14
,
true
];
}
/**
/**
* @expectedException MongoDB\Exception\InvalidArgumentException
* @expectedException MongoDB\Exception\InvalidArgumentException
* @dataProvider provideInvalidConstructorCursorTypeOptions
* @dataProvider provideInvalidConstructorCursorTypeOptions
...
...
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