Restructure array populated with duplicated keys and not lose data [duplicate] - php

This question already has answers here:
PHP Associative Array Duplicate Keys
(6 answers)
Closed 11 days ago.
I'm trying to post form data to an API using PHP.
The API requires that the screening_questions have duplicate keys, like so:
I've gotten as far as having all fields correctly in the array to send except for the screening questions, which are multiple arrays inside the main array. I need to flatten or merge them so that the screening_questions aren't in nested arrays, but while attempting to do so it returns just the last screening_questions array since they all have the same key.
So this array:
Array
(
[candidate[resume]] => CURLFile Object
(
[name] => /nas/content/live/powerhrg2/wp-content/uploads/gravity_forms/5-51403278f6166fe42c507d417f729fc5/2023/02/Power-HRG-Tech-Spec256.pdf
[mime] => application/pdf
[postname] => Power-HRG-Tech-Spec256.pdf
)
[candidate[first_name]] => John
[candidate[last_name]] => Johnson
[candidate[email]] => john.johnson#email.net
[candidate[phone]] => 555-555-5555
[job_posting_public_id] => 28038a67e70c7e235fa725bbf8b6e167d9e3efee1e43a304d73be8408c6f24a2
[candidate[source_id]] => 7795
[0] => Array
(
[screening_questions[][id]] => 1
[screening_questions[][answer]] => false
)
[1] => Array
(
[screening_questions[][id]] => 6
[screening_questions[][answer]] => true
)
[2] => Array
(
[screening_questions[][id]] => 11
[screening_questions[][answer]] => true
)
[3] => Array
(
[screening_questions[][id]] => 16
[screening_questions[][answer]] => true
)
)
Should be this:
Array
(
[candidate[resume]] => CURLFile Object
(
[name] => /nas/content/live/powerhrg2/wp-content/uploads/gravity_forms/5-51403278f6166fe42c507d417f729fc5/2023/02/Power-HRG-Tech-Spec256.pdf
[mime] => application/pdf
[postname] => Power-HRG-Tech-Spec256.pdf
)
[candidate[first_name]] => John
[candidate[last_name]] => Johnson
[candidate[email]] => john.johnson#email.net
[candidate[phone]] => 555-555-5555
[job_posting_public_id] => 28038a67e70c7e235fa725bbf8b6e167d9e3efee1e43a304d73be8408c6f24a2
[candidate[source_id]] => 7795
[screening_questions[][id]] => 1
[screening_questions[][answer]] => false
[screening_questions[][id]] => 6
[screening_questions[][answer]] => true
[screening_questions[][id]] => 11
[screening_questions[][answer]] => true
[screening_questions[][id]] => 16
[screening_questions[][answer]] => true
)
I can't think of a way to merge or flatten those arrays?
I've only ever dealt with sending array data via POST - is there another way I can format this so that I can have these duplicated screening_questions with unique values?
Any help is greatly appreciated.
Here is my code in it's entirety:
add_action( 'gform_after_submission_5', 'post_to_third_party', 10, 2 );
function post_to_third_party( $entry, $form ) {
// get regular form field data
$first_name = rgar( $entry, '1' );
$last_name = rgar( $entry, '3' );
$phone = rgar( $entry, '11' );
$phone = preg_replace("/[^0-9]/", "", $phone);
$phone = preg_replace('/^1?(\d{3})(\d{3})(\d{4})$/', '$1-$2-$3', $phone);
$email = rgar( $entry, '6' );
$resume = GFFormsModel::get_physical_file_path( rgar( $entry, '7' ) );
$resume = new CURLFile($resume, mime_content_type($resume), basename($resume));
$job = get_job(); // get current job ID & source ID
$job_id = $job['id'];
$source_id = $job['source_id'];
// get screening questions id & answer data
foreach ( $form['fields'] as $field ) {
if ($field['type'] === 'radio') {
$field_values[] = preg_replace('/[^0-9.]/', '', $field->get_value_export( $entry, $field->id )); // i.e. converts 'yes-1' to '1'
$field_names[] = $field->get_value_export( $entry, $field->id, true ); // yes
}
}
// remove empty data
$field_values = array_slice(array_filter($field_values),0);
$field_names = array_slice(array_filter($field_names),0);
// combine screening id & answer
$screening_questions = array_combine($field_values, $field_names);
foreach ($screening_questions as $screening_question=>$sq) {
// value must be true/false, not yes/no
if ($sq == 'yes') {
$sq = 'true';
} else {
$sq = 'false';
}
$question[] = array('screening_questions[][id]' => $screening_question, 'screening_questions[][answer]' => $sq);
}
// create an array of form data to send via the API
$body = array(
'candidate[resume]' => $resume,
'candidate[first_name]' => $first_name,
'candidate[last_name]' => $last_name,
'candidate[email]' => $email,
'candidate[phone]' => $phone,
'job_posting_public_id' => $job_id,
'candidate[source_id]' => $source_id,
);
foreach ($question as $q) {
array_push($body,$q);
}
$post_url = 'https://pr29718.nitro-web.beta.hq.powerapp.cloud/recruiting/api/v2/candidates'; // TEST URL
$pass = 'AMC0HNUK30RTFATWWKBJTW'; // TEST TOKEN
$headers = array(
'Host: pr29718.nitro-web.beta.hq.powerapp.cloud',
'Authorization: '.$pass,
);
// Uncomment the below line to debug, if necessary
GFCommon::log_debug( 'gform_after_submission: body => ' . print_r( $body, true ) );
// Uncomment the below line to debug, if necessary
GFCommon::log_debug( 'gform_after_submission: headers => ' . print_r( $headers, true ) );
// Nitro API request via cURL
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://pr29718.nitro-web.beta.hq.powerapp.cloud/recruiting/api/v2/candidates',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HEADER => true,
CURLOPT_HTTPHEADER => $headers,
));
$response = curl_exec($curl);
curl_close($curl);
// Uncomment below line to debug, if necessary
GFCommon::log_debug( 'gform_after_submission: response => ' . print_r( $response, true ) );
}

I think the parameter sending is wrongly defined. just put the subarray in the screening_questions[] array, what is the API response?
//next time pls define the variable before use.
$question = array();
foreach ($screening_questions as $screening_question=>$sq) {
// value must be true/false, not yes/no
$question[] = array(
'id' => $screening_question,
'answer' => $sq === 'true' ? 'true' : 'false'
);
}
$body = array(
'candidate[resume]' => $resume,
'candidate[first_name]' => $first_name,
'candidate[last_name]' => $last_name,
'candidate[email]' => $email,
'candidate[phone]' => $phone,
'job_posting_public_id' => $job_id,
'candidate[source_id]' => $source_id,
'screening_questions' => $question,
);

Related

PHP group keys in array with multiple values

I've got an array like this:
$sendemail= array(
'apple#yahoo.com' => '123456781234567',
'apple#yahoo.com' => '013881002296968',
'cherry#yahoo.com' => '3553220865206561',
'orange#yahoo.com' => '358805051217453',
'apple#yahoo.com' => '357998054217777',
'cherry#yahoo.com' => '013881002296968',
);
I would like to have an output like this:
'apple#yahoo.com' => 123456781234567, 013881002296968, 357998054217777
'cherry#yahoo.com' => 3553220865206561, 013881002296968
'orange#yahoo.com' => 358805051217453
to be able to use the keys as email address and the values as my email's buddy
$email= 'apple#yahoo.com';
$body= '123456781234567, 013881002296968, 357998054217777';
mail($email, 'Your codes', $body);
And the same for the other email addresses.
PLEASE NOTE that 2 keys might have the same values which is fine (e.g. apple#yahoo.com and cherry#yahoo.com have the same values; the value will be sent to both of them)
I used this 'for loop' but didn't work. first of all I cant group them based on email addresses, second of all the same values will not be assigned to the other email addresses; like '013881002296968' which should be shared with apple#yahoo.com and cherry#yahoo.com
$sendmail= array(
'123456781234567' => 'apple#yahoo.com',
'013881002296968' => 'apple#yahoo.com',
'3553220865206561' => 'cherry#yahoo.com',
'358805051217453' => 'orange#yahoo.com',
'357998054217777' => 'apple#yahoo.com',
'013881002296968' => 'cherry#yahoo.com',
);
$out = array();
foreach($sendmail as $key=>$value)
if(array_key_exists($value, $out)) {
$out[$value][] = $key;
}
else {
$out[$value] = array($key);
}
Output
array (
'apple#yahoo.com' =>
array (
0 => 123456781234567,
1 => 013881002296968,
2 => 357998054217777,
),
'cherry#yahoo.com' =>
array (
0 => 3553220865206561,
),
'orange#yahoo.com' =>
array (
0 => 358805051217453,
),
)
Here is an alternative to Kris' method that has less iterations, but more conditionals:
Input:
$sendmail= array(
'123456781234567' => 'apple#yahoo.com',
'013881002296968' => 'apple#yahoo.com',
'3553220865206561' => 'cherry#yahoo.com',
'358805051217453' => 'orange#yahoo.com',
'357998054217777' => 'apple#yahoo.com',
'013881002296968' => 'cherry#yahoo.com',
);
Method:
foreach($sendmail as $k=>$v){
if(!isset($out[$v])){$out[$v]='';} // initialize new element with empty string
$out[$v].=($out[$v]?',':'').$k; // concat the values with conditional comma
}
var_export($out);
Output:
array (
'apple#yahoo.com' => '123456781234567,357998054217777',
'cherry#yahoo.com' => '013881002296968,3553220865206561',
'orange#yahoo.com' => '358805051217453',
)
Use implode on the element of your result array. live demo
foreach($sendmail as $key=>$value) {
$out[$value][] = $key;
}
$out = array_map(function($v){return implode(',', $v);}, $out);

PHP - Inserting to an array by calling a method that returns both key and value

I'm trying to call methods while building an array. I am building a fairly large config array which contains many re usable blocks.
This is the array that I'd like to get:
array(
"masterKey" => array(
"myKey" => array(
"valueField" => "hi"
),
"anotherKey" => array(
"valueField" => "hi again"
)
....
)
);
This is how I'd like to generate it:
array(
"masterKey" => array(
self::getValueField("myKey", "hi"),
self::getValueField("anotherKey", "hi again"),
...
)
);
private static function getValueField($key, $value)
{
return array($key =>
"valueField" => $value
);
}
But this gives me
array(
"masterKey" => array(
[0] => array(
"myKey" => array(
"valueField" => "hi"
)
),
[1] => array(
"anotherKey" => array(
"valueField => "hi again"
)
)
)
);
Instead of constructing the "masterKey" field as a literal, merge the arrays returned by self::getValueField:
array(
"masterKey" => array_merge(
self::getValueField("myKey", "hi"),
self::getValueField("anotherKey", "hi again"),
...
)
);
Just want to add that, for #giaour answer to work, code for the getValueField function should be:
<?php
private static function getValueField($key, $value)
{
return array(
$key => array(
"valueField" => $value
)
);
}

How to store session array variable in PHP in a MySQL database?

I am having problem with session array variable
$_SESSION ['roomsInfo_' . $intHId] = $strRTypeArr;
It automatically destroy when land on the other page, by using the <form action=”booking_level.php”>
foreach ( $arrRoomInfo as $arrRoomDetails ) {
// occup details
$AdultNum = ( int ) $arrRoomDetails->AdultNum;
$ChildNum = ( int ) $arrRoomDetails->ChildNum;
// child ages details
$strChAgs = '';
if (property_exists ( $arrRoomDetails, 'ChildAges' )) {
$childAgs = $arrRoomDetails->ChildAges->ChildAge;
$arrChAge = array ();
foreach ( $childAgs as $chAgs ) {
$chAgs = $chAgs->attributes ();
$arrChAge [] = $chAgs ['age'];
}
$strChAgs = implode ( ":", $arrChAge );
}
// set array for all the above details
$strRTypeArr [$rtId] [$i] = array (
'myRoomSq' => $i,
'roomSeq' => $arrRmSeq,
'adults' => $AdultNum,
'child' => $ChildNum,
'childAges' => $strChAgs,
'maxGuests' => $maxGuests,
'maxChild' => $maxChild,
'name' => $rmName,
'HotelRoomTypeId' => $rtId,
'roomId' => $roomId,
'isPublish' => $isPublish,
'Occupancy' => array (
"attributes" => array (
'avrNgtlyRtComm' => $avrNgtlyRtComm,
'avrNightPrice' => $avrNtPr,
'bedding' => $bedding
),
"boardBase" => array (
'bbId' => $arrBrdBsId,
'bbName' => $arrBrdBsName,
'bbPrice' => $arrBrdBsPrice,
'bbPublishPrice' => $arrBrdBspBPrce,
'strBBaseExists' => $strBBaseExists
),
'PriceBreakdown' => $arrDay,
"dblBrDownPrTourico" => $dblBrDownPrTourico,
"dblTtlBrDownPr" => $dblTtlBrDownPr,
'Supplements' => $arrSupp
),
'Discount' => $arrDst,
'cancelPolicy' => $strCnlionHtml,
'arrCanPolicy' => $arrCanPolicy,
'arrChcrCanPolicy' => $arrChcrCanPolicy,
'chcr_cancelPolicy' => $chcrCancellation,
'curncy' => $strCurn
);
$i ++;
}
}
}
}
$_SESSION ['roomsInfo_' . $intHId] = $strRTypeArr;
How can I store in Session Array Variable in a MySQL database ?
Just make sure that you assign the array the way you assign a normal value and then store the array in your database.
$_SESSION['dArray'] = $strRTypeArr;
//for example:
$strRTypeArr[0][0] = array (
'myRoomSq' => 2.5,
'roomSeq' => "test this",
'adults' => true,
'child' => 5,
'childAges' => "test"
);
$strRTypeArr[0][1] = array (
'myRoomSq' => 3.5,
'roomSeq' => "this is another test",
'adults' => false,
'child' => 6,
'childAges' => "test"
);
$_SESSION['dArray'] = $strRTypeArr;
echo "<pre>";
print_r($_SESSION['dArray']);
echo "</pre>";
//Store this $_SESSION['dArray'] in your database
You can also use json_encode($_SESSION['dArray']); to store your array in the database.

Convert multidimensional array into XML

Please read the bolded line below before you comment that this may be a duplicate. This has nothing to do with SimpleXML.
Let me start off by showing how the XML should be laid out. Please ignore the namespaces:
<hot:SearchHotels>
<hot:request>
<hot1:Destination>?</hot1:Destination>
<hot1:HotelCityName>?</hot1:HotelCityName>
<hot1:HotelLocationName>?</hot1:HotelLocationName>
<hot1:HotelName>?</hot1:HotelName>
<hot1:CheckIn>?</hot1:CheckIn>
<hot1:CheckOut>?</hot1:CheckOut>
<hot1:RoomsInformation>
<!--Zero or more repetitions:-->
<hot1:RoomInfo>
<hot1:AdultNum>?</hot1:AdultNum>
<hot1:ChildNum>?</hot1:ChildNum>
<!--Optional:-->
<hot1:ChildAges>
<!--Zero or more repetitions:-->
<hot1:ChildAge age="?"/>
</hot1:ChildAges>
</hot1:RoomInfo>
</hot1:RoomsInformation>
<hot1:MaxPrice>?</hot1:MaxPrice>
<hot1:StarLevel>?</hot1:StarLevel>
<hot1:AvailableOnly>?</hot1:AvailableOnly>
<hot1:PropertyType>?</hot1:PropertyType>
<hot1:ExactDestination>?</hot1:ExactDestination>
</hot:request>
</hot:SearchHotels>
Notice under hot1:RoomsInformation there is RoomInfo. I'm supposed to be able to send multiple RoomInfo nodes. But I'm using a PHP class to convert an array to this object to be submitted via SOAP.
Here's my array before it gets converted to an object:
$param = array(
"Destination" => $destcode,
"HotelCityName" => $city,
"HotelLocationName" => "",
"HotelName" => "",
"CheckIn" => date("Y-m-d", strtotime($checkin)),
"CheckOut" => date("Y-m-d", strtotime($checkout)),
"RoomsInformation" => array (
"RoomInfo" => array(
"AdultNum" => 2,
"ChildNum" => 1,
"ChildAges" => array(
"ChildAge" => array(
"age"=>11
)
)
),
"RoomInfo" => array(
"AdultNum" => 1,
"ChildNum" => 0,
"ChildAges" => array(
"ChildAge" => array(
"age"=>0
)
)
)
),
"MaxPrice" => 0,
"StarLevel" => 0,
"AvailableOnly" => "false",
"PropertyType" => "NotSet",
"ExactDestination" => "false"
);
$param = arrayToObject($param) ;
$obj = new stdClass();
$obj->request=$param;
$result = $test->SearchHotels($obj) ;
The problem is that after converting to an Object, there is only 1 RoomInfo and its the last one. My thought is because the RoomsInformation array has 2 identical KEY names. So how can I make this work?
For your information, here is the SOAP class I use and the arrayToObject function:
http://pastebin.com/SBUN0FAF
The problem is, your array is invalid as you suspected because of the duplicate keys. One way to solve the issue is to wrap each "RoomInfo" in its own array like so:
$param = array(
"Destination" => $destcode,
"HotelCityName" => $city,
"HotelLocationName" => "",
"HotelName" => "",
"CheckIn" => date("Y-m-d", strtotime($checkin)),
"CheckOut" => date("Y-m-d", strtotime($checkout)),
"RoomsInformation" => array (
array(
"RoomInfo" => array(
"AdultNum" => 2,
"ChildNum" => 1,
"ChildAges" => array(
"ChildAge" => array(
"age"=>11
)
)
),
),
array(
"RoomInfo" => array(
"AdultNum" => 1,
"ChildNum" => 0,
"ChildAges" => array(
"ChildAge" => array(
"age"=>0
)
)
)
)
),
"MaxPrice" => 0,
"StarLevel" => 0,
"AvailableOnly" => "false",
"PropertyType" => "NotSet",
"ExactDestination" => "false"
);
And you can generate the XML like this:
// create simpleXML object
$xml = new SimpleXMLElement("<?xml version=\"1.0\"?><SearchHotels></SearchHotels>");
$node = $xml->addChild('request');
// function call to convert array to xml
array_to_xml($param, $node);
// display XML to screen
echo $xml->asXML();
die();
// function to convert an array to XML using SimpleXML
function array_to_xml($array, &$xml) {
foreach($array as $key => $value) {
if(is_array($value)) {
if(!is_numeric($key)){
$subnode = $xml->addChild("$key");
array_to_xml($value, $subnode);
} else {
array_to_xml($value, $xml);
}
} else {
$xml->addChild("$key","$value");
}
}
}
I attribute the array_to_xml function to the wonderful author here: https://stackoverflow.com/a/5965940/2200766
It looks as though you should have your array like this, instead;
$param = array(
"Destination" => $destcode,
"HotelCityName" => $city,
"HotelLocationName" => "",
"HotelName" => "",
"CheckIn" => date("Y-m-d", strtotime($checkin)),
"CheckOut" => date("Y-m-d", strtotime($checkout)),
"RoomsInformation" => array (
"RoomInfo" => array(
array(
"AdultNum" => 2,
"ChildNum" => 1,
"ChildAges" => array(
"ChildAge" => array(
"age"=>11
)
)
),
array(
"AdultNum" => 1,
"ChildNum" => 0,
"ChildAges" => array(
"ChildAge" => array(
"age"=>0
)
)
)
)
),
"MaxPrice" => 0,
"StarLevel" => 0,
"AvailableOnly" => "false",
"PropertyType" => "NotSet",
"ExactDestination" => "false"
);
This will preserve the two RoomInfo array elements.
simply use this function, where $data is multideminsional array
function array_to_xml( $data ) {
$xml_data = new SimpleXMLElement('<?xml version="1.0"?><data></data>');
foreach( $data as $key => $value ) {
$node = $xml_data->addChild('row_' . $key);
foreach( $value as $keyx => $valuex ) {
$node->addChild($keyx,$valuex);
}
}
return $xml_data->asXML();
}

return array of arrays nusoap php web service

i have a problem returning an array of arrays, i making a web service in php using nusoap, here is my code:
server.php
<?php
//libreria de nusoap
require_once ('nusoap/lib/nusoap.php');
$miURL = 'http://localhost/webservice/';
$server = new soap_server();
$server->configureWSDL('Web Service de Pruebas', $miURL);
$server->wsdl->schemaTargetNamespace = $miURL;
$server->wsdl->addComplexType(
'array_php',
'complexType',
'struct',
'all',
'',
array(
'pk' => array('name' => 'pk', 'type' =>'xsd:int'),
'rol' => array('name' => 'rol', 'type' =>'xsd:string'),
'descripcion' => array('name' => 'descripcion', 'type' =>'xsd:string')
)
);
$server->wsdl->addComplexType(
'array_array',
'complexType',
'array',
'',
'SOAP-ENC:Array',
array(),
array(
array('ref' => 'SOAP-ENC:arrayType',
'wsdl:arrayType' => 'tns:array_php[]'
)
),
'tns:array_php'
);
$server->register('prueba', // method name
array(), // input parameters
array('return' => 'tns:array_array'), // output parameters
$miURL, // namespace
$miURL . '#prueba', // soapaction
'rpc', // style
'encoded', // use
'Get Specific array_php' // documentation
);
function prueba()
{
$con = mysql_connect('localhost', 'root', '1234');
mysql_selectdb('laboral', $con);
$sql = "SELECT * FROM roles";
$q = mysql_query($sql);
$item = array();
while($r = mysql_fetch_assoc($q)){
$item[] = $r;
}
return $item;
}
if( !isset($HTTP_RAW_POST_DATA) )
$HTTP_RAW_POST_DATA = file_get_contents( 'php://input' );
$server->service($HTTP_RAW_POST_DATA);
?>
client.php
<?php
//libreria nusoap
require_once ('nusoap/lib/nusoap.php');
//lineas de configuracion
$serverURL = 'http://localhost/webservice/ws2.php?wsdl';
$cliente = new nusoap_client($serverURL);
//sí error de conexión:
$error = $cliente->getError();
if($error){
echo "<p> '.$error.' </p>";
echo '<p style="color:red;'>htmlspecialchars($cliente->getDebug(), ENT_QUOTES).'</p>';
die();
}
echo "<br/>";
$arreglo2 = $cliente->call('prueba');
echo "<br/>";
for( $i=0; $i<3; $i++ ){
print_r( $arreglo2[$i]['pk'] );
print_r( $arreglo2[$i]['rol'] );
print_r( $arreglo2[$i]['descripcion'] );
echo "<br/>";
}
?>
the problem is the return, is returning nothing to my client and i dont know what happens, i been reading a lot of forums but i cant find a answer, please if some one know about help me here
ty and sorry for my english
This is what I did:
// Complex Array Keys and Types ++++++++++++++++++++++++++++++++++++++++++
$server->wsdl->addComplexType('notaryConnectionData','complexType','struct','all','',
array(
'id' => array('name'=>'id','type'=>'xsd:int'),
'name' => array('name'=>'name','type'=>'xsd:string')
)
);
// *************************************************************************
// Complex Array ++++++++++++++++++++++++++++++++++++++++++
$server->wsdl->addComplexType('notaryConnectionArray','complexType','array','','SOAP-ENC:Array',
array(),
array(
array(
'ref' => 'SOAP-ENC:arrayType',
'wsdl:arrayType' => 'tns:notaryConnectionData[]'
)
)
);
// *************************************************************************
// This is where I register my method and use the notaryConnectionArray
$server->register("listNotaryConnections",
array('token' => 'xsd:string'),
array('result' => 'xsd:bool', 'notary_array' => 'tns:notaryConnectionArray', 'error' => 'xsd:string'),
'urn:closingorder',
'urn:closingorder#listNotaryConnections',
'rpc',
'encoded',
'Use this service to list notaries connected to the signed-in title company.');
// In my function, I query the data and do:
$list = array();
$results = mysql_query($query);
while($row = mysql_fetch_assoc($results)) {
array_push($list, array('id' => intval($row['na_id']), 'name' => $row['agency_name']));
}
return array("result" => true, "notary_array" => $list);
// The output is:
Array
(
[result] => 1
[notary_array] => Array
(
[0] => Array
(
[id] => 1
[name] => Agency 1
)
[1] => Array
(
[id] => 3
[name] => Agency 3
)
[2] => Array
(
[id] => 4
[name] => Agency 4
)
)
[error] =>
)
Hope this helps.

Categories