// Merge -> revision 8952

git-svn-id: http://dev.prestashop.com/svn/v1/branches/1.5.x@8957 b9a71923-0436-4b27-9f14-aed3839534dd
This commit is contained in:
rMalie
2011-10-03 09:14:01 +00:00
parent f1e265fad4
commit ef17d0b7da
44 changed files with 622 additions and 366 deletions
+27 -9
View File
@@ -24,6 +24,7 @@
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
if(!defined('_PS_ADMIN_DIR_'))
define('_PS_ADMIN_DIR_', _PS_ADMIN_DIR_);
if(!defined('_PS_USE_SQL_SLAVE_'))
@@ -480,10 +481,11 @@ $this->standalone = true;
else
{
// echo '<img src="'.$originalCore.'" /> '.
$this->nextParams['msg'] = ($testOrigCore?$this->l('Core files are ok'):sprintf($this->l('%s core files have been modified'), sizeof($changedFileList)));
$this->nextParams['msg'] = ($testOrigCore?$this->l('Core files are ok'):sprintf($this->l('%1$s core files have been modified (%2$s total)'), count($changedFileList['core']), count(array_merge($changedFileList['core'], $changedFileList['mail'], $changedFileList['translation']))));
}
$this->nextParams['result'] = $changedFileList;
}
public function ajaxProcessUpgradeNow()
{
$this->nextDesc = $this->l('Starting upgrade ...');
@@ -1727,6 +1729,9 @@ echo '</script>';
#dbResultCheck{ padding-left:20px;}
#checkPrestaShopFilesVersion{margin-bottom:20px;}
#changedList ul{list-style-type:circle}
.changedFileList {margin-left:20px; padding-left:5px;}
.changedNotice li{color:lightgrey;}
.changedImportant li{color:red;font-weight:bold}
</style>';
$this->displayWarning($this->l('This function is experimental. It\'s highly recommended to make a backup of your files and database before starting the upgrade process.'));
@@ -1870,7 +1875,7 @@ function handleXMLResult(xmlRet, previousParams)
switch(previousParams.upgradeDbStep)
{
case 0: // getVersionFromDb
result = "ok";
resGlobal.result = "ok";
break;
case 1: // getVersionFromDb
result = resGlobal.result;
@@ -2134,6 +2139,19 @@ function handleError(res)
}
';
// ajax to check md5 files
$js .= 'function addModifiedFileList(title, fileList, css_class)
{
subList = $("<ul class=\"changedFileList "+css_class+"\"></ul>");
$(fileList).each(function(k,v){
$(subList).append("<li>"+v+"</li>");
});
$("#changedList").append("<h3><a class=\"toggleSublist\">"+title+"</a></h3>");
$("#changedList").append(subList);
$("#cchangedList").append("<br/>");
}';
$js.= '$(document).ready(function(){
$.ajax({
type:"POST",
@@ -2155,16 +2173,15 @@ $js.= '$(document).ready(function(){
$("#checkPrestaShopFilesVersion").prepend("<img src=\"../img/admin/warning.gif\" /> ");
else
{
$("#checkPrestaShopFilesVersion").prepend(answer.status);
$("#checkPrestaShopFilesVersion").prepend("<img src=\"../img/admin/warning.gif\" /> ");
$("#checkPrestaShopFilesVersion").append("<a id=\"toggleChangedList\" class=\"button\" href=\"\">'.$this->l('See or hide the list').'</a><br/>");
$("#checkPrestaShopFilesVersion").append("<div id=\"changedList\" style=\"display:none \">");
$("#changedList").html("<ul>");
$(answer.result).each(function(k,v){
$("#changedList ul").append("<li>"+v+"</li>");
});
$("#checkPrestaShopFilesVersion").append("<div id=\"changedList\" style=\"display:none \"><br/>");
addModifiedFileList("'.$this->l('Core files').'", answer.result.core, "changedImportant");
addModifiedFileList("'.$this->l('Mail files').'", answer.result.mail, "changedNotice");
addModifiedFileList("'.$this->l('Translation files').'", answer.result.translation, "changedNotice");
$("#toggleChangedList").bind("click",function(e){e.preventDefault();$("#changedList").toggle();});
$(".toggleSublist").live("click",function(e){e.preventDefault();$(this).parent().next().toggle();});
}
}
,
@@ -2183,6 +2200,7 @@ $("#toggleChangedList").bind("click",function(e){e.preventDefault();$("#changedL
});';
return $js;
}
private function _cleanUp($path)
{
// as we need theses files for restore operation, we can't remove them.
+35 -12
View File
@@ -165,18 +165,37 @@ class UpgraderCore
}
return $this->changed_files;
}
/** populate $this->changed_files with $path
* in sub arrays mail, translation and core items
* @param string $path filepath to add, relative to _PS_ROOT_DIR_
*/
protected function addChangedFile($path)
{
$this->version_is_modified = true;
$this->changed_files[] = $path;
//array_unique($this->changed_files);
if (strpos($path, 'mails/') !== false)
$this->changed_files['mail'][] = $path;
else if (
strpos($path, '/en.php') !== false
|| strpos($path, '/fr.php') !== false
|| strpos($path, '/es.php') !== false
|| strpos($path, '/it.php') !== false
|| strpos($path, '/de.php') !== false
|| strpos($path, 'translations/') !== false
)
$this->changed_files['translation'][] = $path;
else
$this->changed_files['core'][] = $path;
}
/** populate $this->missing_files with $path
* @param string $path filepath to add, relative to _PS_ROOT_DIR_
*/
protected function addMissingFile($path)
{
$this->version_is_modified = true;
$this->missing_files[] = $path;
//array_unique($this->missingFile);
}
protected function browseXmlAndCompare($node, &$current_path = array(), $level = 1)
@@ -190,18 +209,22 @@ class UpgraderCore
}
else if (is_object($child) && $child->getName() == 'md5file')
{
$path = _PS_ROOT_DIR_.DIRECTORY_SEPARATOR;
// We will store only relative path.
// absolute path is only used for file_exists and compare
$relative_path = '';
for ($i = 1; $i < $level; $i++)
$path .= $current_path[$i].'/';
$path .= (string)$child['name'];
$path = str_replace('ps_root_dir',_PS_ROOT_DIR_,$path);
$relative_path .= $current_path[$i].'/';
$relative_path .= (string)$child['name'];
$fullpath = _PS_ROOT_DIR_.DIRECTORY_SEPARATOR . $relative_path;
$fullpath = str_replace('ps_root_dir', _PS_ROOT_DIR_, $fullpath);
// replace default admin dir by current one
$path = str_replace(_PS_ROOT_DIR_.'/admin', _PS_ADMIN_DIR_, $path);
if(!file_exists($path))
$this->addMissingFile($path);
else if (!$this->compareChecksum($path, (string)$child))
$this->addChangedFile($path);
$fullpath = str_replace(_PS_ROOT_DIR_.'/admin', _PS_ADMIN_DIR_, $fullpath);
if (!file_exists($fullpath))
$this->addMissingFile($relative_path);
else if (!$this->compareChecksum($fullpath, (string)$child))
$this->addChangedFile($relative_path);
// else, file is original (and ok)
}
}
+42 -17
View File
@@ -1,8 +1,33 @@
<?php
/*
* 2007-2011 PrestaShop
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author PrestaShop SA <contact@prestashop.com>
* @copyright 2007-2011 PrestaShop SA
* @version Release: $Revision$
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
* International Registered Trademark & Property of PrestaShop SA
*/
class Autoupgrade extends Module
{
function __construct()
public function __construct()
{
$this->name = 'autoupgrade';
$this->tab = 'administration';
@@ -25,15 +50,16 @@ class Autoupgrade extends Module
$this->description = $this->l('Provides an automated method to upgrade your shop to the last PrestaShop version');
}
function install()
public function install()
{
$res = true;
// before adding AdminSelfUpgrade, we should remove AdminUpgrade
$idTab = Tab::getIdFromClassName('AdminUpgrade');
$idTab = Tab::getIdFromClassName('AdminUpgrade');
if ($idTab)
{
if ($idTab)
{
$tab = new Tab($idTab);
$res &= $tab->delete();
}
@@ -46,17 +72,17 @@ class Autoupgrade extends Module
$tab->class_name = 'AdminSelfUpgrade';
$tab->module = 'autoupgrade';
$tab->id_parent = 9;
$tab->name = array_fill(1,sizeof(Language::getLanguages(false)), 'Upgrade');
$tab->name = array_fill(1, sizeof(Language::getLanguages(false)), 'Upgrade');
$res &= $tab->save();
}
else
$tab = new Tab($idTab);
Configuration::updateValue('PS_AUTOUPDATE_MODULE_IDTAB',$tab->id);
Configuration::updateValue('PS_AUTOUPDATE_MODULE_IDTAB',$tab->id);
$autoupgradeDir = _PS_ADMIN_DIR_.DIRECTORY_SEPARATOR.'autoupgrade';
if(!file_exists($autoupgradeDir))
if (!file_exists($autoupgradeDir))
$res &= @mkdir($autoupgradeDir);
if(file_exists($autoupgradeDir.DIRECTORY_SEPARATOR.'ajax-upgradetab.php'))
if (file_exists($autoupgradeDir.DIRECTORY_SEPARATOR.'ajax-upgradetab.php'))
$res &= unlink($autoupgradeDir.DIRECTORY_SEPARATOR.'ajax-upgradetab.php');
if (!defined('_PS_MODULE_DIR_'))
{
@@ -67,36 +93,35 @@ class Autoupgrade extends Module
$res &= copy(_PS_MODULE_DIR_.'autoupgrade/logo.gif',_PS_ROOT_DIR_. DIRECTORY_SEPARATOR . 'img/t/AdminSelfUpgrade.gif');
if (!$res
OR !Tab::getIdFromClassName('AdminSelfUpgrade')
OR !parent::install()
|| !Tab::getIdFromClassName('AdminSelfUpgrade')
|| !parent::install()
)
return false;
return true;
}
public function uninstall()
{
$idtab = Configuration::get('PS_AUTOUPDATE_MODULE_IDTAB');
$tab = new Tab($idtab,1);
$res = $tab->delete();
if(file_exists(_PS_ADMIN_DIR_.DIRECTORY_SEPARATOR.'tabs'.'AdminUpgrade.php'))
if (file_exists(_PS_ADMIN_DIR_.DIRECTORY_SEPARATOR.'tabs'.'AdminUpgrade.php'))
{
// Should we create the correct AdminUpgrade tab (not the module)
if($idOldTab = Tab::getIdFromClassName('AdminUpgrade'))
{
$tab = new Tab($idOldTab);
$res &= $tab->delete();
}
}
$res &= unlink(_PS_ADMIN_DIR_.DIRECTORY_SEPARATOR.'tabs'.'AdminUpgrade.php');
}
if (file_exists(_PS_ADMIN_DIR_.DIRECTORY_SEPARATOR.'autoupgrade'.DIRECTORY_SEPARATOR.'ajax-upgradetab.php'))
$res &= @unlink(_PS_ADMIN_DIR_.DIRECTORY_SEPARATOR.'autoupgrade'.DIRECTORY_SEPARATOR.'ajax-upgradetab.php');
if (!$res OR !parent::uninstall())
if (!$res || !parent::uninstall())
return false;
return true;
}
}
}
+3 -3
View File
@@ -116,13 +116,13 @@ class CarrierCompare extends Module
if (!Validate::isInt($id_state))
$errors[] = $this->l('Invalid state ID');
if ($id_state != 0 && !Validate::isLoadedObject(new State($id_state)))
$errors[] = $this->l('Invalid state ID');
$errors[] = $this->l('Please select a state');
if (!Validate::isInt($id_country) || !Validate::isLoadedObject(new Country($id_country)))
$errors[] = $this->l('Invalid country ID');
$errors[] = $this->l('Please select a country');
if (!$this->checkZipcode($zipcode, $id_country))
$errors[] = $this->l('Please use a valid zip/postal code depending on your country selection');
if (!Validate::isInt($id_carrier) || !Validate::isLoadedObject(new Carrier($id_carrier)))
$errors[] = $this->l('Invalid carrier ID');
$errors[] = $this->l('Please select a carrier');
if (sizeof($errors))
return $errors;
+2 -1
View File
@@ -2,10 +2,11 @@
<module>
<name>ebay</name>
<displayName><![CDATA[eBay]]></displayName>
<version><![CDATA[1.2.8]]></version>
<version><![CDATA[1.3]]></version>
<description><![CDATA[Open your shop on the eBay market place !]]></description>
<author><![CDATA[PrestaShop]]></author>
<tab><![CDATA[market_place]]></tab>
<confirmUninstall>Are you sure you want uninstall this module ? All your configuration will be lost.</confirmUninstall>
<is_configurable>1</is_configurable>
<need_instance>1</need_instance>
<limited_countries></limited_countries>
+2 -2
View File
@@ -1163,8 +1163,8 @@ class eBayRequest
'status' => (string)$order->CheckoutStatus->Status,
'date' => substr((string)$order->CreatedTime, 0, 10).' '.substr((string)$order->CreatedTime, 11, 8),
'name' => (string)$order->ShippingAddress->Name,
'firstname' => trim($name[0]),
'familyname' => (isset($name[1]) ? trim($name[1]) : trim($name[0])),
'firstname' => substr(trim($name[0]), 0, 32),
'familyname' => (isset($name[1]) ? substr(trim($name[1]), 0, 32) : substr(trim($name[0]), 0, 32)),
'address1' => (string)$order->ShippingAddress->Street1,
'address2' => (string)$order->ShippingAddress->Street2,
'city' => (string)$order->ShippingAddress->CityName,
+6 -1
View File
@@ -59,7 +59,7 @@ class Ebay extends Module
{
$this->name = 'ebay';
$this->tab = 'market_place';
$this->version = '1.2.8';
$this->version = '1.3';
$this->author = 'PrestaShop';
parent::__construct ();
$this->displayName = $this->l('eBay');
@@ -113,6 +113,9 @@ class Ebay extends Module
// Loading Shipping Method
$this->loadShippingMethod();
// Warning uninstall
$this->confirmUninstall = $this->l('Are you sure you want uninstall this module ? All your configuration will be lost.');
}
}
@@ -434,6 +437,8 @@ class Ebay extends Module
$cartAdd->id_carrier = 1;
$cartAdd->id_lang = $this->id_lang;
$cartAdd->id_currency = Currency::getIdByIsoCode('EUR');
$cartAdd->recyclable = 0;
$cartAdd->gift = 0;
$cartAdd->add();
foreach ($order['product_list'] as $product)
if ($cartAdd->updateQty((int)($product['quantity']), (int)($product['id_product']), ((isset($product['id_product_attribute']) && $product['id_product_attribute'] > 0) ? $product['id_product_attribute'] : NULL)))
+1
View File
@@ -9,6 +9,7 @@ $_MODULE['<{ebay}prestashop>ebay_8acabcc788c316f92b7850c76265b49b'] = 'Vous deve
$_MODULE['<{ebay}prestashop>ebay_d591cbc447d01e5a6165096ebfdf804e'] = 'Vous devez activer l\'extension cURL sur votre serveur si vous désirez utiliser ce module.';
$_MODULE['<{ebay}prestashop>ebay_1b18bd5d9d85f4f667614345ee20e3b2'] = 'Vous devez activer l\'option allow_url_fopen sur votre serveur si vous désirez utiliser ce module.';
$_MODULE['<{ebay}prestashop>ebay_8e64bc164aee46723ed2d70273c6f8ab'] = 'Vous devez enregistrer le module sur eBay';
$_MODULE['<{ebay}prestashop>ebay_55798d71e1cce07f08e25d9a4da60f4a'] = 'Etes-vous sûr de vouloir désinstaller ce module? Toute votre configuration sera perdue.';
$_MODULE['<{ebay}prestashop>ebay_5f8d9c30e0606a91c5d7b6e1bf1217ac'] = 'N\'a pas pu ajouté les produits au panier (peut être votre stock est il à 0)';
$_MODULE['<{ebay}prestashop>ebay_25fff46a6fe5b9512e2f4054f4ff6060'] = 'N\'a pas pu trouvé les produits dans votre catalogue';
$_MODULE['<{ebay}prestashop>ebay_eaa0d1591e6369a298dcd6cb1a8eba8a'] = 'E-mail invalide';
@@ -63,7 +63,7 @@ class MRManagement extends MondialRelay
SET `id_method` = '.(int)$this->_params['id_mr_method'].', ';
if (is_array($this->_params['relayPointInfo']))
foreach($this->_params['relayPointInfo'] as $nameKey => $value)
$query .= '`MR_Selected_'.$nameKey.'` = "'.$value.'", ';
$query .= '`MR_Selected_'.pSQL($nameKey).'` = "'.pSQL($value).'", ';
else // Clean the existing relay point data
$query .= '
MR_Selected_Num = NULL,
@@ -82,14 +82,14 @@ class MRManagement extends MondialRelay
(`id_customer`, `id_method`, `id_cart`, ';
if (is_array($this->_params['relayPointInfo']))
foreach($this->_params['relayPointInfo'] as $nameKey => $value)
$query .= '`MR_Selected_'.$nameKey.'`, ';
$query .= '`MR_Selected_'.pSQL($nameKey).'`, ';
$query = rtrim($query, ', ').') VALUES (
'.$this->_params['id_customer'].',
'.$this->_params['id_mr_method'].',
'.$this->_params['id_cart'].', ';
if (is_array($this->_params['relayPointInfo']))
foreach($this->_params['relayPointInfo'] as $nameKey => $value)
$query .= '"'.$value.'", ';
$query .= '"'.pSQL($value).'", ';
$query = rtrim($query, ', ').')';
}
Db::getInstance()->Execute($query);
+6 -4
View File
@@ -922,12 +922,14 @@ class MondialRelay extends Module
<fieldset class="PS_MRFormStyle">
<legend>
<img src="../modules/mondialrelay/images/logo.gif" />'.$this->l('Advanced Settings'). ' -
<a href="javascript:void(0);" id="PS_MRDisplayPersonalizedOptions"><font style="color:#00b511;">'.$this->l('Click to display / hide the options').'</font> </a>'.
<a href="javascript:void(0);" id="PS_MRDisplayPersonalizedOptions">
<font style="color:#00b511;">'.$this->l('Click to display / hide the options').'</font>
</a>'.
'</legend>
<div id="PS_MRAdvancedSettings">
<p>
'.$this->l('URL Cron Task:').' '.Tools::getHttpHost(true, true)
._MODULE_DIR_.$this->name.'/cron.php?secure_key='.
<p>'.
$this->l('URL Cron Task:').' '.Tools::getHttpHost(true, true).
_MODULE_DIR_.$this->name.'/cron.php?secure_key='.
Configuration::get('MONDIAL_RELAY_SECURE_KEY').
'</p>
</div>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

+1
View File
@@ -96,6 +96,7 @@ class Ogone extends PaymentModule
<li><a href="../modules/'.$this->name.'/docs/en2.png">'.$this->l('See the screenshot for step').' 2</a></li>
<li><a href="../modules/'.$this->name.'/docs/en3.png">'.$this->l('See the screenshot for step').' 3</a></li>
<li><a href="../modules/'.$this->name.'/docs/en4.png">'.$this->l('See the screenshot for step').' 4</a></li>
<li><a href="../modules/'.$this->name.'/docs/en5.png">'.$this->l('See the screenshot for step').' 5</a></li>
</ol>
</li>
</ol>
+3 -3
View File
@@ -297,10 +297,10 @@ abstract class PSCPrepaidServices extends PaymentModule
foreach ($this->_getAllowedCurrencies() AS $currency)
{
$mid = Configuration::get($this->prefix.'MERCHANT_ID_'.$currency['iso_code']);
$certificateExiste = '';
$certificate = '';
$passwordExist = '';
if (file_exists($this->certificat_dir.$mid.'.pem'))
$certificateExiste = '<div style="color:#00b511; text-align:center;">'.$this->l('A certificate has been found for this configuration').' : '.$mid.'.pem'.'</div><br />';
$certificate = '<div style="color:#00b511; text-align:center;">'.$this->l('A certificate has been found for this configuration').' : '.$mid.'.pem'.'</div><br />';
if (Configuration::get($this->prefix.'KEYRING_PW_'.$currency['iso_code']))
$passwordExist = '<div style="color:#00b511; text-align:center;">'.$this->l('A password has already been saved');
@@ -317,7 +317,7 @@ abstract class PSCPrepaidServices extends PaymentModule
<div class="margin-form">
<input type="file" name="ct_keyring_certificate_'.$currency['iso_code'].'" />
</div>
'.$certificateExiste.'
'.$certificate.'
<label>'.$this->getL('keyring_pw').'</label>
<div class="margin-form">
<input type="password" name="ct_keyring_pw_'.$currency['iso_code'].'" value=""/>
+13 -10
View File
@@ -6,21 +6,24 @@
</head>
<body>
<table style="font-family: Verdana,sans-serif; font-size: 11px; color: #374953; width: 550px;">
<tbody>
<tr>
<td align="left"><a title="{shop_name}" href="{shop_url}"><img style="border: none;" src="{shop_logo}" alt="{shop_name}" /></a></td>
<td align="left">
<a href="{shop_url}" title="{shop_name}"><img alt="{shop_name}" src="{shop_logo}" style="border:none;" ></a>
</td>
</tr>
<tr>
<td align="left">Danke, dass Sie bei {shop_name} eingekauft haben. Waren Sie mit Ihrem Einkauf und unserem Service zufrieden? Hier können Sie {shop_name} bewerten, wir freuen uns auf Ihr Feedback.
<td align="left">
Danke, dass Sie bei {shop_name} eingekauft haben. Waren Sie mit Ihrem Einkauf und unserem Service zufrieden? Hier können Sie {shop_name} bewerten, wir freuen uns auf Ihr Feedback.
<br /><br />
<center><a href="{rating_url}"><img border="0" src="{button_url}/apply_en.gif" alt="Wir freuen uns auf Ihr Feedback" /></a></center>
</td>
</tr>
<tr><td>&nbsp;</td></tr>
<tr>
<td>&nbsp;</td>
<td align="center" style="font-size:10px; border-top: 1px solid #D9DADE;">
<a href="{shop_url}" style="color:#DB3484; font-weight:bold; text-decoration:none;">{shop_name}</a> powered with <a href="http://www.prestashop.com/" style="text-decoration:none; color:#374953;">PrestaShop™</a>
</td>
</tr>
<tr>
<td style="font-size: 10px; border-top: 1px solid #D9DADE;" align="center"><a style="color: #db3484; font-weight: bold; text-decoration: none;" href="{shop_url}">{shop_name}</a> powered with <a style="text-decoration: none; color: #374953;" href="http://www.prestashop.com/">PrestaShop&trade;</a></td>
</tr>
</tbody>
</table>
</body>
</html>
</html>
@@ -13,7 +13,7 @@
</tr>
<tr>
<td align="left">
Merci pour votre achat chez {shop_name}. Etes vous satisfait du service chez {shop_name}? Vous pouvez donner votre avis et nous évaluer.
Merci pour votre achat chez {shop_name}. Etes-vous satisfait du service chez {shop_name}? Vous pouvez donner votre avis et nous évaluer.
<br /><br />
<center><a href="{rating_url}"><img border="0" src="{button_url}/apply_fr.gif" alt="Evaluez nous" /></a></center>
</td>
@@ -1,3 +1,3 @@
Merci pour votre achat chez {shop_name}. Etes vous satisfait du service chez {shop_name}? Vous pouvez donner votre avis et nous évaluer sur {rating_url}
Merci pour votre achat chez {shop_name}. Etes-vous satisfait du service chez {shop_name}? Vous pouvez donner votre avis et nous évaluer sur {rating_url}
{shop_url} réalisé par PrestaShop™