Create an admin menu in Opencart 4 - opencart

In Opencart4 I am creating a new module. Installed the module and working fine from accessing Admin-> extensions -> modules -> module name -> (then edit).
Now I would like to add a new menu item under the "Catalogue" menu in the admin part.
I have done the following
In module's controller I added
$this->load->model('setting/event');
$this->model_setting_event->deleteEventByCode('MY_EVENT');
$this->model_setting_event->addEvent('MY_EVENT', "",'admin/view/common/column_left/before', 'extension/testimonials/module/testimonials/ADDTOADMINMENU');
ADDTOADMINMENU function in the module's controller looks like
public function ADDTOADMINMENU(&$route, &$data){
/**
* Check if current logged in user has permission to access that link
* Replace "extension/module/MY_EXTENSION" with your target path
* This check can very well be ignored/deleted...
**/
echo "Here";
exit;
if ($this->user->hasPermission('access', 'extension/testimonials/module/testimonials')) {
$my_menu_entry = array(
'id' => 'menu-MY_EXTENSION',
'icon' => 'fa-check',
'name' => 'My menu entry',
'href' => $this->url->link('extension/testimonials/module/testimonials', 'user_token=' . $this->session->data['user_token'], true),
'children' => array()
);
$target_menu_id = 'menu-catalog';
$target_submenu_href = $this->url->link('catalog/product', 'user_token=' . $this->session->data['user_token'], true);
$new_menu = array();
foreach( $data['menus'] as &$menu ) {
if( $menu['id'] == $target_menu_id ) {
$new_submenu = array();
foreach( $menu['children'] as $submenu ) {
if( $submenu['href'] == $target_submenu_href ) {
$new_submenu[] = $my_menu_entry;
$new_submenu[] = $submenu;
} else {
$new_submenu[] = $submenu;
}
}
$menu['children'] = $new_submenu;
$new_menu[] = $menu;
} else {
$new_menu[] = $menu;
}
}
$data['menus'] = $new_menu;
}
}
But not even entering in this function. Is the correct way I am following ?
Please guide.

try this:
$this->model_setting_event->addEvent('MY_EVENT', "",'admin/view/common/column_left/before', 'extension/testimonials/module/testimonials/ADDTOADMINMENU');
replace with:
$this->model_setting_event->addEvent('MY_EVENT', "",'admin/view/common/column_left/before', 'extension/testimonials/module/testimonials|ADDTOADMINMENU');
I have not checked, but OC have changed they framework a bit.

Solution :
Replace
$this->model_setting_event->addEvent('MY_EVENT', "",'admin/view/common/column_left/before', 'extension/testimonials/module/testimonials/ADDTOADMINMENU');
With
$dataEvent = [
'code' => 'MY_EVENT',
'description' => '',
'trigger' => 'admin/view/common/column_left/before',
'action' => 'extension/testimonials/module/testimonials|ADDTOADMINMENU',
'status' => true,
'sort_order' => 0,
];
if (version_compare(VERSION, '4.0.1.0', '>=')) {
$this->model_setting_event->addEvent($dataEvent);
} elseif (version_compare(VERSION, '4.0.0.0', '>=')) {
$this->model_setting_event->addEvent($dataEvent['code'], $dataEvent['description'], $dataEvent['trigger'], $dataEvent['action'], $dataEvent['status'], $dataEvent['sort_order']);
}else {
/* compatibility Opencart 3 */
$dataEvent['trigger']=str_replace("|","/",$dataEvent['trigger']);
$this->model_setting_event->addEvent($dataEvent['code'], $dataEvent['trigger'], $dataEvent['action'], $dataEvent['status'], $dataEvent['sort_order']);
}
Now you can use your code to update also Admin menu on Opencart 3

Related

Is there a way to get "Product Type" inside product.tpl? - Prestashop 1.6

I'm using PS 1.6.1.16 and I'm wondering if there is a way to display "additional" or "different" content on product.tpl if the product type is "Virtual" (set in BO).
Additionally, is there a way to create a category page for all these "Virtual" products?
Thanks in advance.
In tpl condition is :
{if $product->is_virtual} IS VIRTUAL PRODUCT {else} NO VIRTUAL PRODUCT {/if}
To have a dedicated page is different, here is an example of product override to put in ROOT/override/controllers/front/ProductController.php which allows you to load a new TPL for virtual products. It is therefore necessary in the theme to add a product_virtual.tpl file
<?php
class ProductController extends ProductControllerCore
{
public function initContent()
{
parent::initContent();
if (!$this->errors) {
if (Pack::isPack((int)$this->product->id) && !Pack::isInStock((int)$this->product->id)) {
$this->product->quantity = 0;
}
$this->product->description = $this->transformDescriptionWithImg($this->product->description);
// Assign to the template the id of the virtual product. "0" if the product is not downloadable.
$this->context->smarty->assign('virtual', ProductDownload::getIdFromIdProduct((int)$this->product->id));
$this->context->smarty->assign('customizationFormTarget', Tools::safeOutput(urldecode($_SERVER['REQUEST_URI'])));
if (Tools::isSubmit('submitCustomizedDatas')) {
// If cart has not been saved, we need to do it so that customization fields can have an id_cart
// We check that the cookie exists first to avoid ghost carts
if (!$this->context->cart->id && isset($_COOKIE[$this->context->cookie->getName()])) {
$this->context->cart->add();
$this->context->cookie->id_cart = (int)$this->context->cart->id;
}
$this->pictureUpload();
$this->textRecord();
$this->formTargetFormat();
} elseif (Tools::getIsset('deletePicture') && !$this->context->cart->deleteCustomizationToProduct($this->product->id, Tools::getValue('deletePicture'))) {
$this->errors[] = Tools::displayError('An error occurred while deleting the selected picture.');
}
$pictures = array();
$text_fields = array();
if ($this->product->customizable) {
$files = $this->context->cart->getProductCustomization($this->product->id, Product::CUSTOMIZE_FILE, true);
foreach ($files as $file) {
$pictures['pictures_'.$this->product->id.'_'.$file['index']] = $file['value'];
}
$texts = $this->context->cart->getProductCustomization($this->product->id, Product::CUSTOMIZE_TEXTFIELD, true);
foreach ($texts as $text_field) {
$text_fields['textFields_'.$this->product->id.'_'.$text_field['index']] = str_replace('<br />', "\n", $text_field['value']);
}
}
$this->context->smarty->assign(array(
'pictures' => $pictures,
'textFields' => $text_fields));
$this->product->customization_required = false;
$customization_fields = $this->product->customizable ? $this->product->getCustomizationFields($this->context->language->id) : false;
if (is_array($customization_fields)) {
foreach ($customization_fields as $customization_field) {
if ($this->product->customization_required = $customization_field['required']) {
break;
}
}
}
// Assign template vars related to the category + execute hooks related to the category
$this->assignCategory();
// Assign template vars related to the price and tax
$this->assignPriceAndTax();
// Assign template vars related to the images
$this->assignImages();
// Assign attribute groups to the template
$this->assignAttributesGroups();
// Assign attributes combinations to the template
$this->assignAttributesCombinations();
// Pack management
$pack_items = Pack::isPack($this->product->id) ? Pack::getItemTable($this->product->id, $this->context->language->id, true) : array();
$this->context->smarty->assign('packItems', $pack_items);
$this->context->smarty->assign('packs', Pack::getPacksTable($this->product->id, $this->context->language->id, true, 1));
if (isset($this->category->id) && $this->category->id) {
$return_link = Tools::safeOutput($this->context->link->getCategoryLink($this->category));
} else {
$return_link = 'javascript: history.back();';
}
$accessories = $this->product->getAccessories($this->context->language->id);
if ($this->product->cache_is_pack || count($accessories)) {
$this->context->controller->addCSS(_THEME_CSS_DIR_.'product_list.css');
}
if ($this->product->customizable) {
$customization_datas = $this->context->cart->getProductCustomization($this->product->id, null, true);
}
$this->context->smarty->assign(array(
'stock_management' => Configuration::get('PS_STOCK_MANAGEMENT'),
'customizationFields' => $customization_fields,
'id_customization' => empty($customization_datas) ? null : $customization_datas[0]['id_customization'],
'accessories' => $accessories,
'return_link' => $return_link,
'product' => $this->product,
'product_manufacturer' => new Manufacturer((int)$this->product->id_manufacturer, $this->context->language->id),
'token' => Tools::getToken(false),
'features' => $this->product->getFrontFeatures($this->context->language->id),
'attachments' => (($this->product->cache_has_attachments) ? $this->product->getAttachments($this->context->language->id) : array()),
'allow_oosp' => $this->product->isAvailableWhenOutOfStock((int)$this->product->out_of_stock),
'last_qties' => (int)Configuration::get('PS_LAST_QTIES'),
'HOOK_EXTRA_LEFT' => Hook::exec('displayLeftColumnProduct'),
'HOOK_EXTRA_RIGHT' => Hook::exec('displayRightColumnProduct'),
'HOOK_PRODUCT_OOS' => Hook::exec('actionProductOutOfStock', array('product' => $this->product)),
'HOOK_PRODUCT_ACTIONS' => Hook::exec('displayProductButtons', array('product' => $this->product)),
'HOOK_PRODUCT_TAB' => Hook::exec('displayProductTab', array('product' => $this->product)),
'HOOK_PRODUCT_TAB_CONTENT' => Hook::exec('displayProductTabContent', array('product' => $this->product)),
'HOOK_PRODUCT_CONTENT' => Hook::exec('displayProductContent', array('product' => $this->product)),
'display_qties' => (int)Configuration::get('PS_DISPLAY_QTIES'),
'display_ht' => !Tax::excludeTaxeOption(),
'jqZoomEnabled' => Configuration::get('PS_DISPLAY_JQZOOM'),
'ENT_NOQUOTES' => ENT_NOQUOTES,
'outOfStockAllowed' => (int)Configuration::get('PS_ORDER_OUT_OF_STOCK'),
'errors' => $this->errors,
'body_classes' => array(
$this->php_self.'-'.$this->product->id,
$this->php_self.'-'.$this->product->link_rewrite,
'category-'.(isset($this->category) ? $this->category->id : ''),
'category-'.(isset($this->category) ? $this->category->getFieldByLang('link_rewrite') : '')
),
'display_discount_price' => Configuration::get('PS_DISPLAY_DISCOUNT_PRICE'),
));
}
if (ProductDownload::getIdFromIdProduct((int)$this->product->id) == 0)
$this->setTemplate(_PS_THEME_DIR_.'product.tpl');
else
$this->setTemplate(_PS_THEME_DIR_.'product_virtual.tpl');
}
}

Symfony ignore mock in test service

I'm trying to test a service using phpunit. When i mock a function in my StatServiceTest, my service ignore the mock i did.
with an example will be more clear :
my service.yml
my_service:
class: '%my_service.class%'
parent: parent_service
arguments:
- '#service.repository.commutation'
- '#service.repository.stat'
- '#service.repository.switchboard'
- '%sunrise_host%'
my service : StatService.php
class StatService extends AbstractService
{
protected $commutationRepository;
protected $statRepository;
protected $switchboardRepository;
protected $sunrise_host;
public function __construct(CommutationRepository $commutationRepository, StatRepository $statRepository, SwitchboardRepository $switchboardRepository, $sunrise_host)
{
$this->commutationRepository = $commutationRepository;
$this->statRepository = $statRepository;
$this->switchboardRepository = $switchboardRepository;
$this->sunrise_host = $sunrise_host;
}
public function getNightsWithSunService($id, $start, $end)
{
$switchboard = $this->switchboardRepository->getById($id);
$parameters = array(
'begin' => (int) $start,
'end' => (int) $end,
'lat' => (float) $switchboard->getElement()->getCoordinate()->getLat(),
'lng' => (float) $switchboard->getElement()->getCoordinate()->getLng(),
'timezone' => $switchboard->getElement()->getCoordinate()->getTimezone(),
);
$buzz = new Buzz();
$result = $buzz->post(
$this->sunrise_host.'/nights',
array(
'Content-Type' => 'application/json',
),
json_encode($parameters)
);
return json_decode($result->getContent(), true);
}
}
and finally my StatServiceTest.php
class StatServiceTest extends WebTestCase
{
public function testGetNightsWithSunService()
{
$dic = $this->_client->getKernel()->getContainer();
$sunriseHost = $dic->getParameter('sunrise_host');
$id = 426;
$start = 1538400421;
$end = 1538569621;
$mapNights = $this->getNights();
$mockStatService = $this->getMockBuilder("StatService")
->disableOriginalConstructor()
->getMock();
$mockStatService
->expects($this->any())
->method('getNightsWithSunService')
->withConsecutive(array($id, $start, $end))
->willReturnOnConsecutiveCalls($mapNights)
;
$statService = new StatService($mockCommutationRepository,
$mockStatRepository, $mockSwitchboardRepository, $sunriseHost);
$result = $statService->getNightsWithSunService($id, $start, $end);
$nights = array(
array(
'start' => 1538414415,
'end' => 1538458643,
),
array(
'start' => 1538500702,
'end' => 1538545117,
),
);
$this->assertTrue($this->arrays_are_similar($nights, $result));
}
public function getNights()
{
$nights = array(
array(
'start' => 1538414415,
'end' => 1538458643,
),
array(
'start' => 1538500702,
'end' => 1538545117,
),
);
return $nights;
}
public function arrays_are_similar($a, $b)
{
// we know that the indexes, but maybe not values, match.
// compare the values between the two arrays
foreach ($a as $k => $v) {
if ($v !== $b[$k]) {
return false;
}
}
// we have identical indexes, and no unequal values
return true;
}
}
the error is :
testGetNightsWithSunService
Buzz\Exception\RequestException:
file_get_contents(http://localhost:4244/nights): failed to open
stream: Connection refused
i instantiate my service and i inject in it repositories that i mocked, i just put the part of code that concerns the problem.
What i did wrong ? please any advice will be helpful
The solution i founded is to do another service => toolsService with a function callback for the buzz part, and then we can inject this service and will be more easy to mock it.
I hope that will help you

Yii php unit test action create

I have installed php unit in my local server, but I dont understand (reading the php unit help) how to test my action create. My action is this, and the only thing I want to test is if it saves on database.
/**
* Creates a new model.
* If creation is successful, the browser will be redirected to the 'view' page.
*/
public function actionCreate() {
$_class = $this->getClassName();
$model = new $_class;
if (isset($_POST)) {
$model->attributes = $_POST;
$this->armaMensajeABMGrilla($model->save(), $model);
}
$this->renderPartial('create', array(
'model' => $model,), false, true);
}
protected function armaMensajeABMGrilla($guardoOk, $modelo = null) {
if ($guardoOk == true) {
$this->respuestaJSON = array('success' => true, 'mensaje' => 'ok');
} else {
$erroresMensaje = 'Listado de Errores: <br/><ul>';
$i = 0;
if (isset($modelo->errors)) {
foreach ($modelo->errors as $error) {
$erroresMensaje .= '<li>Error(' . $i . '): ' . $error[0] . '</li>';
$i++;
}
$erroresMensaje.='</ul>';
}
$this->respuestaJSON = array('success' => false, 'mensaje' => $erroresMensaje);
}
$this->renderJSON($this->respuestaJSON);
}
How will be the test method? something like this?
public function actionCreateTest(){
$model = new Model;
$this->asserttrue($model->save());
}
write functional tests for testing controllers functionality instead of unit tests,also
the thing that you are asserting here
$this->assertEquals(true,$controller->actionCreate());
if the outcome of $controller->actionCreate() is the value true, which is not!
you are $this->renderPartial() in that and returning nothing, so that statement will never be true.

getting products and their attributes from prestashop webservice

I'm going to use Prestashop 1.5.4 web-service to get all products with their attributes such as description, name and etc. My problem is whenever I call web-service it returns to me only the products Ids. How can I get attributes too?
Edited :
code :
class ShopApi
{
public $client;
public function __construct()
{
$this->getClient();
}
public function getClient()
{
try {
// creating web service access
$this->client = new PrestaShopWebservice('http://wikibazaar.ir/', 'A38L095W0RHRXE8PM9CM01CZW7KIU4PX', false);
} catch (PrestaShopWebserviceException $ex) {
// Shows a message related to the error
echo 'error: <br />' . $ex->getMessage();
}
}
}
class ProductApi extends ShopApi
{
public function findAll()
{
$products = array();
/// The key-value array
$opt['resource'] = 'products';
$opt['display'] = '[description]';
$opt['limit'] = 1;
$xml = $this->client->get($opt);
$resources = $xml->products->children();
foreach ($resources as $resource)
$products[] = $resource->attributes();
return $products;
}
}
EDIT :
I've found that the response from webservice is ok. but there is a problem during parsing xml with simplexml_load_string() function. any idea?
it's $product var_dump :
SimpleXMLElement#1 ( [products] => SimpleXMLElement#2 ( [product] => SimpleXMLElement#3 ( [description] => SimpleXMLElement#4 ( [language] => SimpleXMLElement#5 ( [#attributes] => array ( 'id' => '1' ) ) ) ) ) )
I think that $opt['display'] = 'full'; would do the job
You can also select only some specific attribute e.g.
$opt['display'] = '[id,name]';
Take a look at the official documentation, you might find it interesting

zf2 phpunit controller dependency

I'm unit testing my controller called IndexController (used the http://framework.zend.com/manual/2.1/en/user-guide/modules.html tutorial).
My IndexController has a dependency:Translator $translator.
<?php
// module/Application/src/Application/Controller/IndexController.php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\I18n\Translator\Translator;
class IndexController extends AbstractActionController
{
protected $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
public function indexAction()
{
$steeringWheelMapper = $this->getServiceLocator()->get('SupplierName\Mapper\SteeringWheel');
$carBrandList = $steeringWheelMapper ->fetchCarBrandList();
return new ViewModel();
}
}
My unit test setup:
module
Application
test
ApplicationTest
Controller
IndexControllerTest.php
Bootstrap.php
phpunit.xml.dist
TestConfig.php.dist
Testing now gives the following error:
Argument 1 passed to Application\Controller\IndexController::__construct() must be an instance of Zend\I18n\Translator\Translator, none given.
Nothing strange here, but I can't find out where to add the translator object..
Where should I add the translator object?
The code
<?php
// module/Application/test/Testconfig.php.dist
return array(
'modules' => array(
'Application',
),
'module_listener_options' => array(
'config_glob_paths' => array(
'../../../config/autoload/{,*.}{global,local}.php',
),
'module_paths' => array(
'module',
'vendor',
),
),
);
<?php
// module/Application/test/Bootstrap.php
namespace ApplicationTest;//Change this namespace for your test
use Zend\Loader\AutoloaderFactory;
use Zend\Mvc\Service\ServiceManagerConfig;
use Zend\ServiceManager\ServiceManager;
use Zend\Stdlib\ArrayUtils;
use RuntimeException;
error_reporting(E_ALL | E_STRICT);
chdir(__DIR__);
class Bootstrap
{
protected static $serviceManager;
protected static $config;
protected static $bootstrap;
public static function init()
{
// Load the user-defined test configuration file, if it exists; otherwise, load
if (is_readable(__DIR__ . '/TestConfig.php')) {
$testConfig = include __DIR__ . '/TestConfig.php';
} else {
$testConfig = include __DIR__ . '/TestConfig.php.dist';
}
$zf2ModulePaths = array();
if (isset($testConfig['module_listener_options']['module_paths'])) {
$modulePaths = $testConfig['module_listener_options']['module_paths'];
foreach ($modulePaths as $modulePath) {
if (($path = static::findParentPath($modulePath)) ) {
$zf2ModulePaths[] = $path;
}
}
}
$zf2ModulePaths = implode(PATH_SEPARATOR, $zf2ModulePaths) . PATH_SEPARATOR;
$zf2ModulePaths .= getenv('ZF2_MODULES_TEST_PATHS') ?: (defined('ZF2_MODULES_TEST_PATHS') ? ZF2_MODULES_TEST_PATHS : '');
static::initAutoloader();
// use ModuleManager to load this module and it's dependencies
$baseConfig = array(
'module_listener_options' => array(
'module_paths' => explode(PATH_SEPARATOR, $zf2ModulePaths),
),
);
$config = ArrayUtils::merge($baseConfig, $testConfig);
$serviceManager = new ServiceManager(new ServiceManagerConfig());
$serviceManager->setService('ApplicationConfig', $config);
$serviceManager->get('ModuleManager')->loadModules();
static::$serviceManager = $serviceManager;
static::$config = $config;
}
public static function getServiceManager()
{
return static::$serviceManager;
}
public static function getConfig()
{
return static::$config;
}
protected static function initAutoloader()
{
$vendorPath = static::findParentPath('vendor');
if (is_readable($vendorPath . '/autoload.php')) {
$loader = include $vendorPath . '/autoload.php';
} else {
$zf2Path = getenv('ZF2_PATH') ?: (defined('ZF2_PATH') ? ZF2_PATH : (is_dir($vendorPath . '/ZF2/library') ? $vendorPath . '/ZF2/library' : false));
if (!$zf2Path) {
throw new RuntimeException('Unable to load ZF2. Run `php composer.phar install` or define a ZF2_PATH environment variable.');
}
include $zf2Path . '/Zend/Loader/AutoloaderFactory.php';
}
AutoloaderFactory::factory(array(
'Zend\Loader\StandardAutoloader' => array(
'autoregister_zf' => true,
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/' . __NAMESPACE__,
),
),
));
}
protected static function findParentPath($path)
{
$dir = __DIR__;
$previousDir = '.';
while (!is_dir($dir . '/' . $path)) {
$dir = dirname($dir);
if ($previousDir === $dir) return false;
$previousDir = $dir;
}
return $dir . '/' . $path;
}
}
Bootstrap::init();
<?php
// module/Application/test/ApplicationTest/Controller/IndexControllerTest.php
namespace ApplicationTest\Controller;
use ApplicationTest\Bootstrap;
use Zend\Mvc\Router\Http\TreeRouteStack as HttpRouter;
use Application\Controller\IndexController;
use Zend\Http\Request;
use Zend\Http\Response;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Router\RouteMatch;
use PHPUnit_Framework_TestCase;
class IndexControllerTest extends \PHPUnit_Framework_TestCase
{
protected $controller;
protected $request;
protected $response;
protected $routeMatch;
protected $event;
protected function setUp()
{
$serviceManager = Bootstrap::getServiceManager();
$this->controller = new IndexController();
$this->request = new Request();
$this->routeMatch = new RouteMatch(array('controller' => 'index'));
$this->event = new MvcEvent();
$config = $serviceManager->get('Config');
$routerConfig = isset($config['router']) ? $config['router'] : array();
$router = HttpRouter::factory($routerConfig);
$this->event->setRouter($router);
$this->event->setRouteMatch($this->routeMatch);
$this->controller->setEvent($this->event);
$this->controller->setServiceLocator($serviceManager);
}
public function testIndexActionCanBeAccessed()
{
$this->routeMatch->setParam('action', 'index');
$result = $this->controller->dispatch($this->request);
$response = $this->controller->getResponse();
$this->assertEquals(200, $response->getStatusCode());
}
}
Inject it in your setUp method
protected function setUp()
{
// ...
$translator = new \Zend\I18n\Translator;
$this->controller = new IndexController($translator);
// ....
}