Xml to mysql import script

Hi, first time caller and after if a little help if possible.

I have created a script that pulls a product feed from my supplier and I am trying to figure out why it does not insert the products into the database.

The script runs with no errors and the final result says it has inserted 1 product into the database but there is nothing in the tables.

Any help would be greatly appreciated.

The database looks like this

> DROP TABLE IF EXISTS `tbl_temp_products`;
> CREATE TABLE IF NOT EXISTS `tbl_temp_products` (
>   `model` varchar(50) NOT NULL,
>   `ean` varchar(99) NOT NULL,
>   `name` varchar(250) NOT NULL,
>   `description` text NOT NULL,
>   `dimension` varchar(50) NOT NULL,
>   `price` varchar(10) NOT NULL,
>   `delivery` varchar(2) NOT NULL,
>   `quantity` varchar(10) NOT NULL,
>   `min_order_qty` varchar(2) NOT NULL,
>   `image_url` varchar(255) NOT NULL,
>   `categories` varchar(255) NOT NULL,
>   `options` varchar(255) NOT NULL
> ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

and the php as follows

> <?php
> $conn = mysqli_connect("localhost", "root", "", "tbl_temp_products");
> 
> $affectedRow = 0;
> 
> $context  = stream_context_create(array('http' => array('header' => 'Accept: application/xml')));
> $url = 'http://www.<supplier-url>.co.uk/gifts/[email protected]&passwd=1234&action=full';
> 
> $xml = file_get_contents($url, false, $context);
> $xml = simplexml_load_string($xml);
> 
> foreach ($xml->children() as $row) {
> 
>     $item = $row->item;
>     $model = $row->model;
>     $ean = $row->ean;
>     $name = $row->name;
>     $description = $row->description;
>     $dimension = $row->dimension;
>     $price = $row->price;
>     $delivery = $row->delivery;
>     $quantity = $row->quantity;
>     $min_order_qty = $row->min_order_qty;
>     $url = $row->url;
>     $image_url = $row->image_url;
>     $categories = $row->categories;
>     $options = $row->options;
> 
>     $sql = "insert into tbl_temp_products(item,model,ean,name,description,dimension,price,delivery,quantity,min_order_qty,url,image_url,categories,options) VALUES ('" . $item . "','" . $model . "','" . $ean . "','" . $name . "','" . $description . "','" . $dimension . "','" . $price . "','" . $delivery . "','" . $quantity . "','" . $min_order_qty . "','" . $url . "','" . $image_url . "','" . $categories . "','" . $options . "')";
> 
>     $result = mysqli_query($conn, $sql);
> 
>     if (! empty($result)) {
>         $affectedRow ++;
>     } else {
>         $error_message = mysqli_error($conn) . "\n";
>     }
> }
> ?>
> <h2>Insert XML Data to MySql Table Output</h2>
> <?php
> if ($affectedRow > 0) {
>     $message = $affectedRow . " records inserted";
> } else {
>     $message = "No records inserted";
> }
> 
> ?>
> <style>
> body {
>     max-width:550px;
>     font-family: Arial;
> }
> .affected-row {
> 	background: #cae4ca;
> 	padding: 10px;
> 	margin-bottom: 20px;
> 	border: #bdd6bd 1px solid;
> 	border-radius: 2px;
>     color: #6e716e;
> }
> .error-message {
>     background: #eac0c0;
>     padding: 10px;
>     margin-bottom: 20px;
>     border: #dab2b2 1px solid;
>     border-radius: 2px;
>     color: #5d5b5b;
> }
> </style>
> 
> <div class="affected-row"><?php  echo $message; ?></div>
> <?php if (! empty($error_message)) { ?>
> <div class="error-message"><?php echo nl2br($error_message); ?></div>
> <?php } ?>
  1. Build some error handling on mysqli_connect() like the first example on php.net

  2. if (! empty($result)) should be if ($result !== false)

Thanks frankbeen,

I think I done that correctly but not totally sure.

No errors are being thrown up :frowning:

<?php
$conn = mysqli_connect("localhost", "root", "", "tbl_temp_products");

if (!$conn) {
    echo "Error: Unable to connect to MySQL." . PHP_EOL;
    echo "Debugging errno: " . mysqli_connect_errno() . PHP_EOL;
    echo "Debugging error: " . mysqli_connect_error() . PHP_EOL;
    exit;
}

echo "<br/>Success: A proper connection to MySQL was made!<br/><br/>" . PHP_EOL;
echo "Host information: " . mysqli_get_host_info($conn) . PHP_EOL;

$affectedRow = 0;

$context  = stream_context_create(array('http' => array('header' => 'Accept: application/xml')));
$url = 'http://www.<url-removed>.co.uk/gifts/[email protected]&passwd=1234&action=full';

$xml = file_get_contents($url, false, $context);
$xml = simplexml_load_string($xml);

foreach ($xml->children() as $row) {

    $item = $row->item;
    $model = $row->model;
    $ean = $row->ean;
    $name = $row->name;
    $description = $row->description;
    $dimension = $row->dimension;
    $price = $row->price;
    $delivery = $row->delivery;
    $quantity = $row->quantity;
    $min_order_qty = $row->min_order_qty;
    $url = $row->url;
    $image_url = $row->image_url;
    $categories = $row->categories;
    $options = $row->options;

    $sql = "insert into tbl_temp_products(item,model,ean,name,description,dimension,price,delivery,quantity,min_order_qty,url,image_url,categories,options) VALUES ('" . $item . "','" . $model . "','" . $ean . "','" . $name . "','" . $description . "','" . $dimension . "','" . $price . "','" . $delivery . "','" . $quantity . "','" . $min_order_qty . "','" . $url . "','" . $image_url . "','" . $categories . "','" . $options . "')";

    $result = mysqli_query($conn, $sql);

    if ($result !== false) {
        $affectedRow ++;
    } else {
        $error_message = mysqli_error($conn) . "\n";
    }
}

mysqli_close($conn);

?>
<h2>Insert XML Data to MySql Table Output</h2>
<?php
if ($affectedRow > 0) {
    $message = $affectedRow . " records inserted";
} else {
    $message = "No records inserted";
}

?>
<style>
body {
    max-width:550px;
    font-family: Arial;
}
.affected-row {
	background: #cae4ca;
	padding: 10px;
	margin-bottom: 20px;
	border: #bdd6bd 1px solid;
	border-radius: 2px;
    color: #6e716e;
}
.error-message {
    background: #eac0c0;
    padding: 10px;
    margin-bottom: 20px;
    border: #dab2b2 1px solid;
    border-radius: 2px;
    color: #5d5b5b;
}
</style>

<div class="affected-row"><?php  echo $message; ?></div>
<?php if (! empty($error_message)) { ?>
<div class="error-message"><?php echo nl2br($error_message); ?></div>
<?php } ?>

Could you place a short xml sample with just one or two items?

Thanks again for the help.

Here is a snippet of the xml

<?xml version="1.0" encoding="UTF-8" ?>
<DROPSHIPITEMS>
	<CREATED value="Sat Dec 21 12:22:41 UTC 2019">
		<PRODUCT ITEM='13726'>
			<MODEL>TY573</MODEL>
			<EAN>5055071713330</EAN>
			<NAME>Fun Kids Octopus Splash Toy</NAME>
			<DESCRIPTION>
				<![CDATA[<p>Fun Kids Octopus Splash Toy</p><p>Whether it&#39;s bright, colourful, squidgy or disgusting, kids of all ages will love our range of novelty toys.</p><p>Ideal for party bags and stocking fillers, or just as gifts for all the family, our range comes in a variety of fun themed designs.</p><p>CE marked for children 3 years of over.</p>]]>
			</DESCRIPTION>
			<DIMENSION>
				<![CDATA[Height 17cm Width 6cm Depth 6cm ]]>
			</DIMENSION>
			<PRICE>1.90</PRICE>
			<DELIVERY>B</DELIVERY>
			<QUANTITY>7335</QUANTITY>
			<MIN_ORDER_QTY>1</MIN_ORDER_QTY>
			<URL>http://www.<url-removed>.co.uk/gifts/product_info.php?products_id=13726</URL>
			<IMAGE_URL>http://www.<url-removed>.co.uk/gifts/images/TY573_001.jpg</IMAGE_URL>
			<CATEGORIES>9118</CATEGORIES>
			<OPTIONS>
				<![CDATA[Only Available Assorted]]>
			</OPTIONS>
		</PRODUCT>
		<PRODUCT ITEM='13724'>
			<MODEL>TY571</MODEL>
			<EAN>5055071713316</EAN>
			<NAME>Fun Kids Puppet - Shark Head</NAME>
			<DESCRIPTION>
				<![CDATA[<p>Fun Kids Puppet - Shark Head</p><p>Whether it&#39;s bright, colourful, squidgy or disgusting, kids of all ages will love our range of novelty toys.</p><p>Ideal for party bags and stocking fillers, or just as gifts for all the family, our range comes in a variety of fun themed designs.</p><p>CE marked for children 5 years of over.</p>]]>
			</DESCRIPTION>
			<DIMENSION>
				<![CDATA[Height 6cm Width 10cm Depth 16cm ]]>
			</DIMENSION>
			<PRICE>2.13</PRICE>
			<DELIVERY>B</DELIVERY>
			<QUANTITY>2410</QUANTITY>
			<MIN_ORDER_QTY>1</MIN_ORDER_QTY>
			<URL>http://www.<url-removed>.co.uk/gifts/product_info.php?products_id=13724</URL>
			<IMAGE_URL>http://www.<url-removed>.co.uk/gifts/images/TY571_001.jpg</IMAGE_URL>
			<CATEGORIES>9118</CATEGORIES>
			<OPTIONS>
				<![CDATA[Only Available Assorted]]>
			</OPTIONS>
		</PRODUCT>
	</CREATED>
</DROPSHIPITEMS>

a few points:

  • You did still miss some error handling on file_get_contents and simplexml_load_string.
  • You did miss some columns in your table according to the CREATE TABLE query.
  • You did not escape the data with mysqli_escape_string() which broke your query thanks to the word it’s in the description of the first product :slight_smile:
  • I added a auto increment primary key on the table. But if you are sure that the item numbers are always unique you could use that column as primary key too.
  • You still have to look at the column options because the snippet you gave did not contain any options. I think you should set this column as posbible empty (null).
  • Next time try to debug your code. place some echo’s or a print_r() to see what works or not.
<?php
$conn = mysqli_connect("localhost", "root", "", "tbl_temp_products");

if (!$conn) {
    echo "Error: Unable to connect to MySQL." . PHP_EOL;
    echo "Debugging errno: " . mysqli_connect_errno() . PHP_EOL;
    echo "Debugging error: " . mysqli_connect_error() . PHP_EOL;
    exit;
}

echo "<br/>Success: A proper connection to MySQL was made!<br/><br/>" . PHP_EOL;
echo "Host information: " . mysqli_get_host_info($conn) . PHP_EOL;

$affectedRow = 0;

$context  = stream_context_create(array('http' => array('header' => 'Accept: application/xml')));
$url = 'test.xml';

$xml = file_get_contents($url, false, $context);
if($xml === false) {
    echo 'could not read xml file!';
    exit;
}

$xml = simplexml_load_string($xml);
if($xml === false) {
    echo 'could not convert xml file to a php object!';
    exit;
}

foreach ($xml->CREATED->PRODUCT as $row) {
    //print_r($row);
    $item = mysqli_escape_string($conn, $row->attributes()['ITEM']);
    $model = mysqli_escape_string($conn, $row->MODEL);
    $ean = mysqli_escape_string($conn, $row->EAN);
    $name = mysqli_escape_string($conn, $row->NAME);
    $description = mysqli_escape_string($conn, $row->DESCRIPTION);
    $dimension = mysqli_escape_string($conn, $row->DIMENSION);
    $price = mysqli_escape_string($conn, $row->PRICE);
    $delivery = mysqli_escape_string($conn, $row->DELIVERY);
    $quantity = mysqli_escape_string($conn, $row->QUANTITY);
    $min_order_qty = mysqli_escape_string($conn, $row->MIN_ORDER_QTY);
    $url = mysqli_escape_string($conn, $row->URL);
    $image_url = mysqli_escape_string($conn, $row->IMAGE_URL);
    $categories = mysqli_escape_string($conn, $row->CATEGORIES);
    $options = 'no options';

    $sql = "insert into tbl_temp_products(item,model,ean,name,description,dimension,price,delivery,quantity,min_order_qty,url,image_url,categories,options) VALUES ('" . $item . "','" . $model . "','" . $ean . "','" . $name . "','" . $description . "','" . $dimension . "','" . $price . "','" . $delivery . "','" . $quantity . "','" . $min_order_qty . "','" . $url . "','" . $image_url . "','" . $categories . "','" . $options . "')";

    $result = mysqli_query($conn, $sql);

    if ($result !== false) {
        $affectedRow ++;
    } else {
        $error_message = mysqli_error($conn) . "\n";
    }
}

mysqli_close($conn);

?>
<h2>Insert XML Data to MySql Table Output</h2>
<?php
if ($affectedRow > 0) {
    $message = $affectedRow . " records inserted";
} else {
    $message = "No records inserted";
}

And the complete table:

CREATE TABLE `tbl_temp_products` (
  `id` int(11) NOT NULL,
  `item` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `model` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `ean` varchar(99) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `name` varchar(250) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `description` text CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `dimension` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `price` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `delivery` varchar(2) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `quantity` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `min_order_qty` varchar(2) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `url` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `image_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `categories` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
  `options` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

ALTER TABLE `tbl_temp_products`
  ADD PRIMARY KEY (`id`);

ALTER TABLE `tbl_temp_products`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
1 Like

If you are doing this for learning, start with parsing/inserting just a couple of values, one attribute and one data element. Once you can successfully do that, you can deal with the rest of the values.

Next, one of the points of programming is that the computer is a tool to help you accomplish a task. If you have more than about 2-3 inputs, you should not find yourself repeating code for every possible input. Let the computer do that for you, by using a data-driven design, where you define a list of inputs in a data structure (array, database table) and use general-purpose code to produce a result based on that definition. This has the affect of eliminating repetition (Don’t Repeat Yourself - DRY), so that if you have a mistake or need to make a change, you can do it in one place only.

See the following example for a data-driven design for this task -

// define a list(array) of xml elements to parse/insert
// an entry defined as an array [] indicates an element attribute
// an entry defined as a string indicates an element value
$elements = [['ITEM'],'MODEL', 'EAN', 'NAME', 'DESCRIPTION', 'DIMENSION', 'PRICE', 'DELIVERY', 'QUANTITY', 'MIN_ORDER_QTY',
		'URL', 'IMAGE_URL', 'CATEGORIES', 'OPTIONS'];
// the above definition must be in the same order as the columns in the query
// note: you can dynamically produce the insert query using the above definition - left as a programming exercise for you

// change your existing ...load_string() call to the following to keep the CDATA values
$xml = simplexml_load_string($xml, null, LIBXML_NOCDATA);

// prepare the query once
$sql = "insert into tbl_temp_products (item,model,ean,name,description,dimension,price,delivery,quantity,min_order_qty,url,image_url,categories,options)
 VALUES (".implode(',',array_fill(0,count($elements),'?')).")";

// note: this example uses the much simpler PDO extension
$stmt = $pdo->prepare($sql);

// loop over the xml
foreach($xml->CREATED->PRODUCT as $row)
{
	$params = []; // initialize the query input parameter
	// loop over the definition and get each input value
	foreach($elements as $element)
	{
		if(is_array($element))
		{
			// attribute
			$params[] = trim((string)$row[$element[0]]);
		} else {
			// value
			$params[] = trim((string)$row->$element);
		}
	}
	$stmt->execute($params);
}

You can change what xml data gets included or excluded by just changing the defining array and the sql query columns (or dynamically produce the query from the defining array as noted in the code.)

1 Like

Hi phdr, merry xmas and thank you for your response

Although I am still learning and trying to digest everything.

happy xmas frankbeen and thank you that worked perfectly.

I am now trying to incorporate duplicate checking into the code which throws no errors but does not work

<?php
$conn = mysqli_connect("localhost", "root", "", "tbl_temp_products");

if (!$conn) {
    echo "Error: Unable to connect to MySQL." . PHP_EOL;
    echo "Debugging errno: " . mysqli_connect_errno() . PHP_EOL;
    echo "Debugging error: " . mysqli_connect_error() . PHP_EOL;
    exit;
}

echo "<br/>Success: A proper connection to MySQL was made!<br/><br/>" . PHP_EOL;
echo "Host information: " . mysqli_get_host_info($conn) . PHP_EOL;

$affectedRow = 0;

$context  = stream_context_create(array('http' => array('header' => 'Accept: application/xml')));
$url = 'http://www.<url-removed>.co.uk/gifts/feed_xml_products.php?email=<removed>.com&passwd=<removed>&action=full';

$xml = file_get_contents($url, false, $context);
if($xml === false) {
    echo 'could not read xml file!';
    exit;
}

$xml = simplexml_load_string($xml);
if($xml === false) {
    echo 'could not convert xml file to a php object!';
    exit;
}

foreach ($xml->CREATED->PRODUCT as $row) {
    print_r($row);
    $item = mysqli_escape_string($conn, $row->attributes()['ITEM']);
    $model = mysqli_escape_string($conn, $row->MODEL);
    $ean = mysqli_escape_string($conn, $row->EAN);
    $name = mysqli_escape_string($conn, $row->NAME);
    $description = mysqli_escape_string($conn, $row->DESCRIPTION);
    $dimension = mysqli_escape_string($conn, $row->DIMENSION);
    $price = mysqli_escape_string($conn, $row->PRICE);
    $delivery = mysqli_escape_string($conn, $row->DELIVERY);
    $quantity = mysqli_escape_string($conn, $row->QUANTITY);
    $min_order_qty = mysqli_escape_string($conn, $row->MIN_ORDER_QTY);
    $url = mysqli_escape_string($conn, $row->URL);
    $image_url = mysqli_escape_string($conn, $row->IMAGE_URL);
    $categories = mysqli_escape_string($conn, $row->CATEGORIES);
    $options = 'no options';


$sql = mysqli_query($conn, "SELECT item FROM tbl_temp_products WHERE ITEM='$item'");
$result = mysqli_num_rows($sql);

if($result !=="0"){
echo "The product you have chosen already exists!";
exit;
} else {
    $sql = "insert into tbl_temp_products(item,model,ean,name,description,dimension,price,delivery,quantity,min_order_qty,url,image_url,categories,options) VALUES ('" . $item . "','" . $model . "','" . $ean . "','" . $name . "','" . $description . "','" . $dimension . "','" . $price . "','" . $delivery . "','" . $quantity . "','" . $min_order_qty . "','" . $url . "','" . $image_url . "','" . $categories . "','" . $options . "')";
    mysqli_query($query) or die('mysqli error.');
}

    $result = mysqli_query($conn, $sql);

    if ($result !== false) {
        $affectedRow ++;
    } else {
        $error_message = mysqli_error($conn) . "\n";
    }
}

mysqli_close($conn);

?>

<style>
body {
    max-width:550px;
    font-family: Arial;
}
.affected-row {
	background: #cae4ca;
	padding: 10px;
	margin-bottom: 20px;
	border: #bdd6bd 1px solid;
	border-radius: 2px;
    color: #6e716e;
}
.error-message {
    background: #eac0c0;
    padding: 10px;
    margin-bottom: 20px;
    border: #dab2b2 1px solid;
    border-radius: 2px;
    color: #5d5b5b;
}
</style>

<h2>Insert XML Data to MySql Table Output</h2>
<?php
if ($affectedRow > 0) {
    $message = $affectedRow . " records inserted";
} else {
    $message = "No records inserted";
}
?>

<div class="affected-row"><?php  echo $message; ?></div>
<?php if (! empty($error_message)) { ?>
<div class="error-message"><?php echo nl2br($error_message); ?></div>
<?php } ?>

What action do you want to perform if an item number already exists? Do you want to report the duplicate item numbers or a count of the duplicates? Do you want to UPDATE the rest of the values instead of inserting a new row? Do you want to silently ignore the duplicate? I’m betting you don’t want to halt execution on the first duplicate detected, which is what the current code would do.

For each of these cases, the item column should be defined as a unique index and you should just attempt to insert the data (no SELECT query is needed at all.) To report the duplicate item numbers or get a count of the duplicates, the error handling for the insert query would test for a duplicate index error number, then either save the item number (in an array) or increment a counter, then continue to process the rest of the rows. To UPDATE the rest of the values, you would use an INSERT … ON DUPLICATE KEY UPDATE … query. To silently ignore the duplicate, you would add the IGNORE keyword to the insert query.

Your current code should always be outputting the “The product you have chosen already exists!” message. The reason for this is because the num_rows value is a number, but that comparison is testing if it is not exactly the string “0”, which it won’t ever be. Don’t surround numbers with quotes. This problem will go-away when you eliminate the select query.

1 Like

The ultimate goal is to insert all data from the xml feed into the database and then report back with

xxx products inserted into database

xxx products not inserted as already present

ok so after a bit of research and many attempts I have got this far.

I have changed the insert query to check for an existing item code. Now all been said this works like it should by not inserting existing products into the database.

I am now however a little stuck as I would like it report the amount of products not replaced i.e. ‘1000 matching products already exist which have not been replaced’

$sql = "SELECT item from tbl_temp_products WHERE item = '$item'";
$result = mysqli_query($conn, $sql);
 if(mysqli_num_rows($result) > 0) {
  echo $item . ' already exists<br/>';
 }else {
 $sql2 = "insert into tbl_temp_products(item,model,ean,name,description,dimension,price,weight,delivery,quantity,min_order_qty,url,image_url,categories,options,stockwi,assorted) VALUES ('" . $item . "','" . $model . "','" . $ean . "','" . $name . "','" . $description . "','" . $dimension . "','" . $price . "','" . $weight . "','" . $delivery . "','" . $quantity . "','" . $min_order_qty . "','" . $url . "','" . $image_url . "','" . $categories . "','" . $options . "','" . $stockwi . "','" . $assorted . "')";
 $result2 = mysqli_query($conn, $sql2);
}

Further help would be appreciated.

Thanks

Just make a counter variabele and count in your foreach loop. you dont need the database to do that unless you need to know how many rows there are in the table. To do that you can simply run

SELECT COUNT(*) AS total FROM tablename

1 Like

Happy Birthday frankbeen

Lost me now. I have researched the counter variable but have no idea on how to incorporate it into my code.

I have been trying for days to get something to work but have not even come close to anything.

What I would like the script to do is cycle through the xml and compare it to what is already in the database then import the products and display a report as below

No. of products in feed = 12345
No. of products inserted (new) = 12345
No of products skipped (duplicates) = 12345

If anyone can help with this I would appreciate it.

Moved on from the above as I could not get anything to work.

My current issue is that I would like to replace the | seperator character that is in the xml feed and replace it with a ,

The product categories are inserted in the database as

1234|5678

I need them to be inserted as

1234,5678

I have been looking at preg_replace but not sure if this is the correct way of doing it.

$result = preg_replace('/|/', ',', $string);   # Replace all '|' with ','

or if this is even the correct line to add $result to

$categories = mysqli_escape_string($conn, $row->CATEGORIES);

or do I need to add it before the database insert???

Thanks in advance for any help.

ok for those that are following this journey I have managed to solve the above this way

$categories = str_replace("|", ",", $row->CATEGORIES);
Sponsor our Newsletter | Privacy Policy | Terms of Service