I am currently in the development of a module prestashop and I meet a problem. I created a new hook for my module and I add an admin controller in my module that execute this hook I add the function that calls the hook and that uses a template. Except that the template does not appear I have an empty page. Here is the code I did:
My module:
<?php
if (!defined('_PS_VERSION_'))
exit;
/* Checking compatibility with older PrestaShop and fixing it */
if (!defined('_MYSQL_ENGINE_'))
define('_MYSQL_ENGINE_', 'MyISAM');
require_once(_PS_MODULE_DIR_.'blockobjectif/classes/Objectif.php');
class blockobjectif extends Module
{
public function __construct()
{
$this->name = 'blockobjectif';
$this->tab = 'front_office_features';
$this->version = '1.0';
$this->author = 'Athor Athor';
$this->bootstrap = true;
$this->need_instance = 0;
$this->ps_versions_compliancy['min'] = '1.5';
$this->ps_versions_compliancy['max'] = '1.6';
parent::__construct();
$this->displayName = $this->l('Objectifs');
$this->description = $this->l('Définie des objectifs aux clients');
}
public function install()
{
$sql = array();
include(dirname(__FILE__).'/sql/install.php');
foreach ($sql as $s)
if (!Db::getInstance()->execute($s))
return false;
$class = 'AdminObjectif';
$tab = new Tab();
$tab->class_name = $class;
$tab->module = $this->name;
$tab->id_parent = (int) Tab::getIdFromClassName('AdminParentCustomer');
$langs = Language::getLanguages(false);
foreach ($langs as $l) {
$tab->name[$l['id_lang']] = $this->l('Objectifs');
}
$tab->save();
return parent::install()
&& $this->registerHook('displayCustomerAccount')
&& $this->registerHook('displayAdminObjectifs');
}
public function uninstall($delete_params = true)
{
$sql = array();
include(dirname(__FILE__).'/sql/uninstall.php');
foreach ($sql as $s)
if (!Db::getInstance()->execute($s))
return false;
$moduleTabs = Tab::getCollectionFromModule($this->name);
if (!empty($moduleTabs)) {
foreach ($moduleTabs as $moduleTab) {
$moduleTab->delete();
}
}
if (!parent::uninstall())
return false;
return true;
}
public function hookDisplayCustomerAccount($params)
{
ddd($params);
}
public function hookDisplayAdminObjectifs($params)
{
return $this->display(__FILE__, 'admin-obj.tpl');
}
}
My AdminObjectifController:
<?php
class AdminObjectifController extends ModuleAdminController
{
public function __construct()
{
$this->bootstrap = true;
$this->table = 'objectifs';
$this->className = 'Objectif';
parent::__construct();
Hook::exec('displayAdminProductsExtra');
}
}
Result:
I do not see where the problem comes from ...
Thank you for your help
This is not the right method to display a backoffice controller template.
Here is what you should try:
<?php
class AdminObjectifController extends ModuleAdminController
{
public function __construct()
{
$this->bootstrap = true;
$this->table = 'objectifs';
$this->className = 'Objectif';
parent::__construct();
}
public function initContent()
{
$tpl = $this->context->smarty->createTemplate($this->getTemplatePath() . 'admin-obj.tpl', $this->context->smarty);
$tpl->assign(array(
'my_var' => "test"
));
$this->content .= $tpl->fetch();
parent::initContent();
}
}
Note that your admin-obj.tpl file should be placed under views/templates/admin/admin-obj.tpl inside your module.
Related
Is it possible to nest aggregate hydrators? If i have the following classes:
class Appointment{
public date;
public startTime;
public endTime;
public User; //* #var User */
}
class User{
public Location; //* #var Location*/
}
...being populated with the following AggregateHydrator (created from a factory):
class AppointmentModelHydratorFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator) {
$serviceManager = $serviceLocator->getServiceLocator();
$arrayHydrator = new ArraySerializable();
$arrayHydrator->addStrategy('date', new DateTimeStrategy())
->addStrategy('endTime', new TimeStrategy())
->addStrategy('startTime', new TimeStrategy());
$aggregateHydrator = new AggregateHydrator();
$aggregateHydrator->add($arrayHydrator);
$aggregateHydrator->add($serviceLocator->get('Hydrator\User'));
return $aggregateHydrator;
}
}
With the UserHydratorFactory looking like:
class UserHydratorFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator) {
$sm = $serviceLocator->getServiceLocator();
$userHydrator = new UserHydrator($sm->get('User\Mapper'));
$aggregateHydrator = new AggregateHydrator();
$aggregateHydrator->add($userHydrator );
$aggregateHydrator->add($sm->get('HydratorManager')->get('Hydrator\User\Location'));
return $aggregateHydrator;
}
}
This is throwing an expection as the model is being returned as null, but if i comment out adding the Location hydrator to the User hydrator, it works fine (albeit without location data loaded). So i was wondering if aggregate hydrators are able to be nested?
It is not built-in, but doable.
namespace Hydrator;
use Zend\Stdlib\Hydrator\HydratorInterface;
class NestedHydrator implements HydratorInterface
{
protected $inner_hydrator;
private $empty;
public function __construct ($inner_hydrator, $empty)
{
$this->inner_hydrator = $inner_hydrator;
$this->empty = $empty;
}
public function extract ($object)
{
return [
$this->getPath() => $this->inner_hydrator->extract ($object->{$this->getPath()})
];
}
public function hydrate (array $data, $object)
{
$object->{$this->getPath()} = $this->inner_hydrator->hydrate ($data [$this->getPath()], $this->empty);
return $object;
}
protected function getPath ()
{
return get_class ($this->empty);
}
}
And then:
$u = new User();
$u->Location = "4 Clinton Rd.";
$a = new Appointment();
$a->date = "yesterday";
$a->startTime = "7:00";
$a->endTime = "8:00";
$a->User = $u;
$h = new AggregateHydrator();
$h->add (new ObjectProperty());
$nested = new \Hydrator\NestedHydrator(new ObjectProperty(), new User());
$h->add ($nested);
$data = $h->extract ($a);
$b = $h->hydrate ($data, new Appointment());
$this->assertEquals ($a, $b);
My users are required to confirm their email addresses before they have access to the application.
I have a specific route that they are sent to if they log in and their email is not confirmed: "customer/register-landing" this will send an email and the view will explain what they need to do.
I am using bootstrap for brevity.
This is what I have so far and the last bit I am struggling to work out (the re-direct part)
//I run console related queries and this breaks if run
if ( $e->getRequest() instanceof \ZF\ContentNegotiation\Request )
{
//Get a user object
$authService = $sm->get( AuthorizationService::class );
$userObject = $authService->getIdentity();
if (!$userObject instanceof User ) {
return;
}
if ($userObject->getIsEmailConfirmed() == 1) {
return;
}
//So we have a logged in user who needs to confirm their email
$redirect = $em->attach(MvcEvent::EVENT_DISPATCH,
function($e){
$route = $e->getRouteMatch();
if ($route->getMatchedRouteName() != 'customer/register-landing')
{
//Redirect to the route: customer/register-landing
}
}
);
}
What do I need to do to re-direct to the actual page? I had a look around and I found this code:
$em->getSharedManager()->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', function($e) {
$controller = $e->getTarget();
$controller->plugin('redirect')->toRoute('customer/register-landing');
}, 100);
However when I add it to the class it does not work:
$redirect = $em->attach(MvcEvent::EVENT_DISPATCH,
function($e){
$route = $e->getRouteMatch();
if ($route->getMatchedRouteName() != 'customer/register-landing')
{
$em->getSharedManager()->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', function($e) {
$controller = $e->getTarget();
$controller->plugin('redirect')->toRoute('customer/register-landing');
}, 100);
}
}
);
What is the correct way to do this?
i solved this with next code. if you need all code go to https://github.com/Gimalca/piderapido/blob/master/module/Admin/Module.php
class Module {
public function onBootstrap(MvcEvent $e) {
}
public function init(ModuleManager $moduleManager) {
$moduleName = $moduleManager->getEvent()->getModuleName();
if ($moduleName == 'Admin') {
$events = $moduleManager->getEventManager();
$sharedEvents = $events->getSharedManager();
// This define modules need Login
$sharedEvents->attach(array(__NAMESPACE__, 'Admin', 'Account'), 'dispatch', array($this, 'initAuth'), 100);
}
}
public function initAuth(MvcEvent $e) {
//This get router strings
$routerMatch = $e->getRouteMatch();
$module = $routerMatch->getMatchedRouteName();
$controller = $routerMatch->getParam('controller');
$action = $routerMatch->getParam('action');
//This get Authenticate Class
$app = $e->getApplication();
$sm = $app->getServiceManager();
$auth = $sm->get('Admin\Model\LoginAdmin');
// This redirect all. but is login interface not
if ($controller != 'Admin\Controller\Login' && !$auth->isLoggedIn()) {
$controller = $e->getTarget();
return $controller->redirect()->toRoute('admin',array('controller'=>'login','action' => 'index'));
}
if ($auth->isLoggedIn()) {
$viewModel = $e->getViewModel();
$viewModel->userIdentity = $auth->getIdentity();
}
}
I am trying to implement Custom authentication via SOAP webservice
as per Refering links
http://it-cook-de.blogspot.in/2013/07/zend-framework-2-custom-authentication-with-soap-webservice-part-1.html
http://samsonasik.wordpress.com/2012/10/23/zend-framework-2-create-login-authentication-using-authenticationservice-with-rememberme/#comment-6117
Now getting error as
Code :
<?php
namespace Application\Adapter;
use Zend\Authentication\Adapter\AdapterInterface;
use Zend\Authentication\Result;
use Zend\Soap\Client;
use Zend\Soap\Client\DotNet as DotNet;
class SoapAuthenticationAdapter implements AdapterInterface
{
//:TODO: move to configuration
//private $module = 'my_application_name_in_auth_system';
private $module = 'student';
//:TODO: move to configuration
// private $uri = 'full_url_to_soap_webservice_of_auth_system';
private $uri = 'http://ip/Service/student.svc?wsdl';
//:TODO: move to configuration
//private $location = 'full_url_to_soap_webservice_of_auth_system';
private $location = 'http://ip/Service/student.svc?wsdl';
protected $username;
protected $password;
protected $customercode;
function __construct()
{
}
public function authenticate()
{
$client = new DotNet($this->uri);
$param=array("customercode"=>$this->customercode,"username"=>$this->username,"password"=>$this->password);
$result = $client->call('ValidateUser',array($param));
if ($result) {
$this->permissions = $param;
return $this->getResult(Result::SUCCESS, $this->permissions, 'SUCCESS');
} else {
return $this->getResult(Result::FAILURE_CREDENTIAL_INVALID, null, 'FAILURE_CREDENTIAL_INVALID');
}
} catch (\SoapFault $e) {
// switch ($e->getMessage()) {
return $this->getResult(Result::FAILURE_UNCATEGORIZED, null, $e->getMessage());
// }
}
}
public function setIdentity($username)
{
$this->username = $username;
}
public function setCredential($password)
{
$this->password = $password;
}
public function setCustomerCode($customercode)
{
$this->customercode =$customercode;
}
private function getResult($type, $identity, $message)
{
return new Result($type, $identity, array(
$message
));
}
}
/////In IndexController
public function getAuthService()
{
if (! $this->authservice) {
$this->authservice = $this->getServiceLocator()
->get('SoapAuthenticationService');
}
return $this->authservice;
}
public function someAction()
{
$request = $this->getRequest();
if ($request->isPost()){
$this->getAuthService()->getAdapter()
->setIdentity($request->getPost('username'))
->setCredential($request->getPost('password'))
->setCustomerCode($request->getPost('customercode'));
$result = $this->getAuthService()->authenticate();
foreach($result->getMessages() as $message)
{
//save message temporary into flashmessenger
$this->flashmessenger()->addMessage($message);
}
if ($result->isValid()) {
$redirect = 'home';
//check if it has rememberMe :
if ($request->getPost('rememberme') == 1 ) {
$this->getSessionStorage()
->setRememberMe(1);
//set storage again
$this->getAuthService()->setStorage($this->getSessionStorage());
}
$this->getAuthService()->setStorage($this->getSessionStorage());
$this->getAuthService()->getStorage()->write($request->getPost('username'));
}
else
{
$redirect ='login';
}
return $this->redirect()->toRoute($redirect);
}
in module.php
public function getServiceConfig()
{
return array(
'factories' => array(
//Add the following lines
'Application\Model\SoapAuthenticationStorage' => function($sm){
return new SoapAuthenticationStorage('student');
},
'SoapAuthenticationService' => function($sm) {
$authAdapter = new SoapAuthenticationAdapter();
$authService = new AuthenticationService();
$authService->setAdapter($authAdapter);
$authService->setStorage($sm->get('Application\Model\SoapAuthenticationStorage'));
return $authService;
}
)
);
}
Any help to solve this
Each of the set methods in your adapter need to return $this in order to allow you to chain method calls together (this is called a 'fluent interface'). E.g.:
public function setIdentity($username)
{
$this->username = $username;
return $this;
}
That should fix the error you're getting.
How can i implement Customized lookup in Report Dialog box.
for example i have two fields in my report dialog 1) Custgroup 2) CustAccount
if i have selected a particuler cust group in first field then second field lookup should show only customers those come under this cust groups.
//class
public class ReportRun extends ObjectRun
{
DialogField dialogcustGroup,dialogcustaccount ;
CustTable obj_CustTable ;
}
//dialog method
public Object dialog(Object _dialog)
{
DialogRunbase dialog = _dialog;
DialogGroup toFromGroup;
Args _args;
str accountnum,custGroup;
;
// _args = new Args();
// obj_dev_CustTable = _args.record();
//accountnum = obj_dev_CustTable.AccountNum;
dialogcustGroup = dialog.addFieldValue(extendedTypeStr(CustGroup),CustGroup,"");
while select obj_CustTable
where obj_CustTable.AccountNum == dialogcustGroup .value()
{
CID = obj_dev_CustTable.CID;
dialogcustaccount =dialog.addFieldValue(ExtendedTypeStr(AccountNum),accountnum,"CID");
}
return dialog;
}
Any help would be great!!!!
The best way to do it is to override the lookup() method on the specified DialogField. See the example below - it works just fine.
class CustomizedLookup extends RunBase
{
DialogRunbase dialog;
DialogField dFieldCustGroup;
DialogField dFieldCustAccount;
CustGroupId fetchedCustGroup;
CustAccount fetchedAccountNum;
}
protected Object dialog()
{
dialog = super();
FieldCustGroup = dialog.addField(extendedTypeStr(CustGroupId),"sysLabel1");
dFieldCustGroup.allowEdit(true);
dFieldCustAccount = dialog.addField(extendedTypeStr(CustAccount),"sysLabel1");
dFieldCustAccount.allowEdit(false);
return dialog;
}
public void dialogPostRun(DialogRunbase _dialog)
{
super(_dialog);
// allow to call the event methods
// of this class (e.g. Fld1_1_modified() method)
_dialog.dialogForm().formRun().controlMethodOverload(true);
_dialog.dialogForm().formRun().controlMethodOverloadObject(this);
}
private boolean Fld1_1_modified() // dFieldCustGroup
{
FormStringControl control;
boolean isFieldModified;
control = dialog.formRun().controlCallingMethod();
isFieldModified = control.modified();
if(isFieldModified)
{
fetchedCustGroup = dFieldCustGroup.value();
dFieldCustAccount.allowEdit(true);
}
return isFieldModified;
}
private void Fld2_1_lookup() //dFieldCustAccount
{
FormStringControl control = dialog.formRun().controlCallingMethod();
SysTableLookup sysTableLookup = SysTableLookup::newParameters(tablenum(CustTable),control);
Query query = new Query();
QueryBuildDataSource queryBuildDataSource;
QueryBuildRange queryBuildRange;
queryBuildDataSource = query.addDataSource(TableNum(CustTable));
queryBuildRange = queryBuildDataSource.addRange(FieldNum(CustTable, CustGroup));
queryBuildRange.value(fetchedCustGroup);
sysTableLookup.addLookupfield(fieldnum(CustTable, AccountNum));
sysTableLookup.addLookupfield(fieldnum(CustTable, CustGroup));
sysTableLookup.parmQuery(query);
sysTableLookup.performFormLookup();
}
public boolean getFromDialog()
{
boolean ret;
ret = super();
fetchedAccountNum = dFieldCustAccount.value();
return ret;
}
static void main(Args _e)
{
CustomizedLookup customizedLookup;
customizedLookup = new CustomizedLookup();
if (customizedLookup.prompt())
{
customizedLookup.run();
// do some actions with your data
customizedLookup.theAction();
}
}
private void theAction()
{
info(strFmt("Customer Group: %1",fetchedCustGroup));
info(strFmt("Account Number: %1",fetchedAccountNum));
}
Some more methods like pack , unpack and main method should be declared
public class CustAmountCalculation extends RunBase
{
DialogField fieldAccount;
CustAccount custAccount;
}
public Object dialog()
{
Dialog dialog;
DialogGroup groupCustomer;
dialog = super();
fieldAccount = dialog.addField(extendedTypeStr(custAccount), "CustomerAccount");
return dialog;
}
public boolean getFromDialog()
{
custAccount = fieldAccount.value();
return super();
}
public container pack()
{
return conNull();
}
public void run()
{
CustTable custTable;
CustTrans custTrans;
;
select sum(AmountMST) from custTrans where custTrans.AccountNum == custAccount;
info("You have enetered customer information");
info(strfmt("Account: %1", custAccount));
info(strFmt("Amount: %1", custTrans.AmountMST));
}
public boolean unpack(container _packedClass)
{
return true;
}
public static void main(Args _args)
{
CustAmountCalculation custAmountCalculation = new CustAmountCalculation();
if (CustAmountCalculation.prompt())
{
CustAmountCalculation.run();
}
}
I have 2 ComboBoxes on my form. I create the bindings as follows:
TestClass myclass = new TestClass("Instruments");
myclass.Add(instr1 = new TestClass("INSTR1"));
myclass.Add(instr2 = new TestClass("INSTR2"));
myclass.Add(instr3 = new TestClass("INSTR3"));
myclass.Add(instr4 = new TestClass("INSTR4"));
instr1.Add(app1 = new TestClass("app1"));
instr1.Add(app2 = new TestClass("app2"));
instr1.Add(app3 = new TestClass("app3"));
instr1.Add(app4 = new TestClass("app4"));
instr2.Add(app5 = new TestClass("app5"));
instr2.Add(app6 = new TestClass("app6"));
instr2.Add(app7 = new TestClass("app7"));
instr2.Add(app8 = new TestClass("app8"));
mysource = new BindingSource(myclass, null);
selectedComboBox1.DataSource = mysource;
selectedComboBox1.DisplayMember = "NAME";
mysource2 = new BindingSource(selectedComboBox1, "SelectedItem");
selectedComboBox2.DataSource = mysource2;
selectedComboBox2.DisplayMember = "NAME";
The class used for the binding looks as follows
class TestClass : BindingList<TestClass>, INotifyPropertyChanged
{
public event RunTestChanged RunTestChangedEventHandler;
public TestClass()
{
this.test = "";
this.name = "";
this.runTest = true;
}
public TestClass(string name)
{
this.test = "";
this.name = name;
this.runTest = true;
}
public TestClass LIST
{
get
{
return this;
}
}
public string NAME
{
get
{
return this.name;
}
set
{
this.name = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("NAME"));
}
}
}
public string TEST
{
get
{
return this.test;
}
set
{
this.test = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("TEST"));
}
}
}
public bool RUNTEST
{
get
{
return runTest;
}
set
{
runTest = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs("RUNTEST"));
}
RunTestArgs myargs = new RunTestArgs(value);
if (RunTestChangedEventHandler != null)
{
RunTestChangedEventHandler(this, myargs);
}
}
}
private bool runTest;
private string name;
private string test;
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
}
when the form first loads the 2 comboboxes are filled as they should be with the expected items. However, if i change an item in selectedComboBox1, the items in selectedComboBox2 aren't updated. I know that I can subscribe to the selectedComboBox1 SelectedIndexChanged event and then rebind the DataSource on selectedComboBox2 and everything will work as expected.
For example:
void selectedComboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
mysource2.DataSource = selectedComboBox1.SelectedItem;
mysource2.DataMember = null;
}
Another alternative that works is to perform the databinding as follows:
mysource = new BindingSource(myclass, null);
mysource2 = new BindingSource(mysource, "LIST");
mysource3 = new BindingSource(mysource2, "LIST");
selectedComboBox1.DataSource = mysource;
selectedComboBox1.DisplayMember = "NAME";
selectedComboBox2.DataSource = mysource2;
selectedComboBox2.DisplayMember = "NAME";
However I wanted to know if there was a way to avoid having to subscribe to the event or performing the databinding in a different manner and just have the 2nd ComboBox be updated via the BindingSource using the SelectedItem property. In the end I'm curious to know how to get the BindingSource to be updated via the SelectedItem databinding and if it's not possible what is preventing it from working.
Thank you for your help.
i have the same issue and got resolved by binding Name to SelectedValue of combobox and set ValueMember to be "NAME" property
selectedComboBox1.DisplayMember = "NAME";
selectedComboBox1.ValueMember = "NAME";