I'm working on a PRESTASHOP override option,
i need to update the function getPackageShippingCost() in override/classes/Cart.php
i want prestashop to take this overide into consideration only if my id_carrier is 5 for example, is it possible ?
Yes you can easily do it like this:
class Cart extends CartCore
{
public function getPackageShippingCost($id_carrier = null, $use_tax = true, Country $default_country = null, $product_list = null, $id_zone = null)
{
if ($id_carrier !== 5)
{
return parent::getPackageShippingCost($id_carrier, $use_tax, $default_country, $product_list, $id_zone);
}
// YOUR CUSTOM CODE
}
}
Do not forget to remove file /cache/class_index.php after adding a new override.
Related
I want to programmatically alter a page title in Drupal 8 so that it will be hard-coded in the theme file.
I'm attempting to use a hook function to preprocess_page_title, but it seems to not understand what page to change the title on.
Here's what I have so far:
function test_preprocess_page_title(&$variables) {
if (arg(0) == 'node/12') {
$variables['title'] = 'New Title';
}
}
I figured the only way to make this change on one specific page is to set the node argument. Has any one figured out a way to override page title on Drupal?
In your template.theme file add the preprocessor and then override page-title.html.twig in your template folder by printing the variable, like below:
function theme_preprocess_page_title(&$variables) {
$node = \Drupal::request()->attributes->get('node');
$nid = $node->id();
if($nid == '14') {
$variables['subtitle'] = 'Subheading';
}
}
then {{ subtitle }}
Here's the method to preprocess your page :
function yourthemename_preprocess_page(&$variables) {
$node = \Drupal::routeMatch()->getParameter('node');
if ($node) {
$variables['title'] = $node->getTitle();
}
}
and in your template page.html.twig
{{title}}
There are a couple of solutions to change the page title
On template
/**
* Implements hook_preprocess_HOOK().
*/
function MYMODULE_preprocess_page_title(&$variables) {
if ($YOUR_LOGIC == TRUE) {
$variables['title'] = 'New Title';
}
}
On the node view page
/**
* Implements hook_ENTITY_TYPE_view_alter().
*/
function mymodule_user_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
if ($YOUR_LOGIC == TRUE) {
$build['#title'] = $entity->get('field_display_name')->getString();
}
}
for a sample if you want to change user title
function mymodule_user_view_alter(array &$build, Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display) {
if ($entity->getEntityTypeId() == 'user') {
$build['#title'] = $entity->get('field_first_name')->getString();
}
}
On Controller or hook_form_alter
if ($YOUR_LOGIC == TRUE) {
$request = \Drupal::request();
if ($route = $request->attributes->get(\Symfony\Cmf\Component\Routing\RouteObjectInterface::ROUTE_OBJECT)) {
$route->setDefault('_title', 'New Title');
}
}
The Page Title is a block in Drupal 8. If you can find the plugin_id of the page title block (which is likely to be page_title_block), then you can override the title directly, with no need to change an existing twig template, using a block preprocessor. Your code may be similar to the following:
function vhs_preprocess_block(&$variables) {
// This example restricts based on the actual URL; you can replace this with any other logic you wish.
$request = \Drupal::request();
$uri = $request->getRequestUri();
if (
isset($variables['elements']['#base_plugin_id']) &&
$variables['elements']['#base_plugin_id'] == 'page_title_block' &&
isset($variables['content']['#title']['#markup']) &&
strpos($uri, '/url-to-match') === 0 // replace with logic that finds the correct page to override
) {
$variables['content']['#title']['#markup'] = 'My Custom Title';
}
}
The example above uses the Drupal request object to grab and compare the actual URL. The initial question asked to match based on the node path; you could get that with something like:
$current_path = \Drupal::service('path.current')->getPath();
Then, in place of the strpos condition above, you could use:
$current_path == 'node/12'
i have changed the page_title block for user/uid to a different custom account field name like this :
function hook_preprocess_block(&$variables) {
$path = \Drupal::request()->getpathInfo();
$arg = explode('/', $path);
if (isset($arg[2]) && $arg[2] == 'user' && isset($arg[3])) {
if (isset($variables['elements']['content']['#type']) && $variables['elements']['content']['#type'] == 'page_title') {
$account = \Drupal\user\Entity\User::load($arg[3]);
if(isset($account) && isset($account->field_mycustomfield->value)){
$variables['content']['#title']['#markup']=$account->field_mycustomfield->value;
}
}
}
}
I'm using TornadoFX 1.7.5 and I can't seem to get bound properties to work. I have the below ItemViewModels
class DynamicMenuViewModel : ItemViewModel<DynamicMenu>(DynamicMenu()) {
val title = bind { item?.title?.toProperty() }
val isBold = bind { item?.isBold?.toProperty() }
val routes = bind { item?.routes?.toProperty() }
}
data class DynamicMenu(var title: String = "", var isBold: Boolean = false, var routes: MutableList<MenuRouteViewModel> = mutableListOf())
class MenuRouteViewModel : ItemViewModel<MenuRoute>(MenuRoute()) {
val url = bind { item?.url?.toProperty() }
val title = bind { item?.title?.toProperty() }
val isBold = bind { item?.isBold?.toProperty() }
val showNew = bind { item?.showNew?.toProperty() }
}
data class MenuRoute(var url: String = "", var title: String = "", var showNew: Boolean = false, var isBold: Boolean = false)
Which are bound like this:
//routesController.dynamicMenu is an instance of DynamicMenuViewModel()
textfield(property = routesController.dynamicMenu.title) {
prefWidth = formWidth * .5
gridpaneConstraints {
columnRowIndex(0, 1)
marginLeft = 10.0
columnSpan = 2
marginBottom = 20.0
}
}
checkbox(property = routesController.dynamicMenu.isBold){
gridpaneConstraints {
columnRowIndex(2, 1)
marginLeft = 15.0
marginBottom = 20.0
}
}
Then the following functions commit the models and prints them to the screen when I click a button:
fun onClick(){
commitModel()
println(dynamicMenu.item.toString())
dynamicMenu.item.routes.forEach {
println(it.item.toString())
}
}
fun commitModel(){
dynamicMenu.item.routes.forEach {
it.commit()
}
dynamicMenu.commit()
}
The problem is that when I run the program and edit the textfields and checkboxes then click the button that runs onClick(), the backing item doesn't seem to be getting updated. So none of the updated values are printed to the console.
What am I doing wrong here?
The ViewModel can as you probably know only bind bidirectionally against JavaFX Properties. Your domain objects doesn't contain JavaFX properties, so you need to convert them. However, the toProperty() function you are using only operates on a value, and turns it into a Property. This property has no way of knowing about it's field owner, and hence cannot write back into the domain object.
Luckily, you can use the observable function to make your domain object properties writable as well:
val url = bind { item?.observable(MenuRoute::url) }
Since the observable function operates on a specific instance of a MenuRoute object, it now has enough information to write back into that instance when you commit() the model.
If you would consider changing the properties in your domain objects to be observable, you could write:
val url = bind(MenuRoute::url)
You can use the TornadoFX IDEA plugin inspection "Convert all properties to TornadoFX Properties" to automatically rework your properties. This would transform your MenuRoute object into:
class MenuRoute {
val isBoldProperty = SimpleBooleanProperty(false)
var isBold by isBoldProperty
val showNewProperty = SimpleBooleanProperty(false)
var showNew by showNewProperty
val urlProperty = SimpleStringProperty("")
var url by urlProperty
val titleProperty = SimpleStringProperty("")
var title by titleProperty
}
(You have to manually remove the data modifier on your class. Also beware that the current version of the plugin has a bug in the conversion function that would leave the old parameters - a new version will be released shortly).
If you don't want to do that for various reasons, I was just able to support that nice syntax even for mutable vars like you have, so from TornadoFX 1.7.6 you can use this syntax in your binding statements even if you don't want to change your data classes:
val url = bind(MenuRoute::url)
I'm searching for a way to create a custom action button which allows me to make a new DataObject with pre-filled content from another DataObject. As a simple example: When I have an email and click the "answer"-button in my email-client, I get a new window with pre-filled content from the email before. I need exactly this functionality for my button. This button should appear next to each DataObject in the GridField.
So I know how to make a button and add it to my GridField (--> https://docs.silverstripe.org/en/3.2/developer_guides/forms/how_tos/create_a_gridfield_actionprovider/) and I know how to go to a new DataObject:
Controller::curr()->redirect($gridField->Link('item/new'));
I also found out that there is a duplicate function for DataObjects:
public function duplicate($doWrite = true) {
$className = $this->class;
$clone = new $className( $this->toMap(), false, $this->model );
$clone->ID = 0;
$clone->invokeWithExtensions('onBeforeDuplicate', $this, $doWrite);
if($doWrite) {
$clone->write();
$this->duplicateManyManyRelations($this, $clone);
}
$clone->invokeWithExtensions('onAfterDuplicate', $this, $doWrite);
return $clone;
}
Perhaps it's easier than I think but at the moment I just don't get how to rewrite this to get what I need. Can somebody give me a hint?
That's for sure not the cleanest solution but I think it should do the trick.
At first let's create the custom gridfield action. Here we will save all accessible records in a session and add a query string to the url so that we'll know which object we want to "clone"
public function getColumnContent($gridField, $record, $columnName) {
if(!$record->canEdit()) return;
$field = GridField_FormAction::create(
$gridField,
'clone'.$record->ID,
'Clone',
'clone',
array('RecordID' => $record->ID)
);
$values = Session::get('ClonedData');
$data = $record->data()->toMap();
if($arr = $values) {
$arr[$record->ID] = $data;
} else {
$arr = array(
$record->ID => $data
);
}
Session::set('ClonedData', $arr);
return $field->Field();
}
public function getActions($gridField) {
return array('clone');
}
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
if($actionName == 'clone') {
$id = $arguments['RecordID'];
Controller::curr()->redirect($gridField->Link("item/new/?cloneID=$id"));
}
}
after adding this new component to our gridfield,
$gridField->getConfig()->addComponent(new GridFieldCustomAction());
we'll need to bring the data into the new form. To do so, add this code directly above "return $fields" on your getCMSFields function so it will be executed every time we'll open this kind of object.
$values = Session::get('ClonedData');
if($values) {
Session::clear('ClonedData');
$json = json_encode($values);
$fields->push(LiteralField::create('ClonedData', "<div id='cloned-data' style='display:none;'>$json</div>"));
}
At the end we need to bring the content back into the fields. We'll do that with a little bit of javascript so at first you need to create a new script.js file and include it in the ss backend (or just use an existing one).
(function($) {
$('#cloned-data').entwine({
onmatch: function() {
var data = JSON.parse($(this).text()),
id = getParameterByName('cloneID');
if(id && data) {
var obj = data[id];
if(obj) {
$.each(obj, function(i, val) {
$('[name=' + i + ']').val(val);
});
}
}
}
});
// http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript#answer-901144
function getParameterByName(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
})(jQuery);
And that's it ... quite tricky. Hope it will solve your problem.
I'm working on a project for creating a prestashop module that creates a custom tab at the back office and by pressing it, it opens an iframe. I have created the tab but at the menu bar at the backoffice. But i dont know how open an iframe by pressing it.
Can you help me out please?
This is my module's code:
<?php
if (!defined('_PS_VERSION_'))
exit;
class Mytab extends Module
{
// PLIROFORIES TOY AYTHOR
public function __construct()
{
$this->name = 'Mytab';
$this->tab = 'Administration';
$this->version = 1.5;
$this->author = 'Sergio Kagiema';
$this->need_instance = 0;
//$this->tabParentName = 'AdminTools';
parent::__construct();
$this->displayName = $this->l('My Tab');
$this->description = $this->l('Module makes a tab at BackEnd');
$this->confirmUninstall = $this->l('Are you sure you want to uninstall?');
if (!Configuration::get('My Tab'))
$this->warning = $this->l('No name provided');
} //END OF PLIROFORIES TOY AYTHOR
//INSTALL TOY MODULE
public function install()
{
$parent_tab = new Tab();
foreach (Language::getLanguages(true) as $lang)
$parent_tab->name [$lang['id_lang']] = 'Tab';
$parent_tab->class_name = 'Tab';
$parent_tab->id_parent = 0;
$parent_tab->module = $this->name;
$parent_tab->add();
if (!parent::install()
|| !$this->installModuleTab('MyTabsController', array((int)(Configuration::get('PS_LANG_DEFAULT'))=>'My Tab'), $parent_tab->id)
)
return false;
return true;
}
//UNISTALL TOY MODULE
public function uninstall()
{
if (!parent::uninstall()
|| !$this->uninstallModuleTab('MyTab')
|| !$this->uninstallModuleTab('MyTabsController'))
return false;
return true;
}
private function installModuleTab($tabClass, $tabName, $idTabParent)
{
$idTab = Tab::getIdFromClassName($idTabParent);
$idTab = $idTabParent;
$pass = true ;
#copy(_PS_MODULE_DIR_.$this->name.'/logo.gif', _PS_IMG_DIR_.'t/'.$tabClass.'.gif');
$tab = new Tab();
$tab->name = $tabName;
$tab->class_name = $tabClass;
$tab->module = $this->name;
$tab->id_parent = $idTab;
$pass = $tab->save();
return($pass);
}
private function uninstallModuleTab($tabClass)
{
$pass = true ;
#unlink(_PS_IMG_DIR_.'t/'.$tabClass.'.gif');
$idTab = Tab::getIdFromClassName($tabClass);
if($idTab != 0)
{
$tab = new Tab($idTab);
$pass = $tab->delete();
}
return($pass);
}
}
?>
This is my controller's code at my contoroller/admin file:
<?php
class AffiliatesTabsController extends AdminController
{
public function init()
{
parent::init();
}
/**
* Assign template vars related to page content
* #see FrontController::initContent()
*/
public function initContent()
{ parent::initContent();
$this->setTemplate(_PS_THEME_DIR_.'/MyTab.tpl');
//$smarty = $this->context->smarty;
//$smarty->assign('test', 'test1');
// include(dirname(__FILE__).'/jQ.tpl');
}
}
?>
Please help me! Thanks!
I'm searching for similar things today and find out.
Controller:
class AffiliatesTabsController extends ModuleAdminController
{
public function initContent()
{
parent::initContent();
$this->setTemplate('MyTab.tpl');
}
}
Then move MyTab.tpl into
prestashop\modules\mytab\views\templates\admin\affiliatestabs\
MyTab.tpl:
<iframe src="webpage url">
You are trying to set an admin template for frontoffice use, that won't work.
This solution here works for me:
How to create a new page in prestashop admin panel?
Edit: additions after comments below:
After you created the the AdminAffiliatesController.php file in controllers\admin folder you create the template file here:
admin\themes\default\template\controllers\adminaffiliates\content.tpl
In that content.tpl file you can create your iframe or whatever.
Every output that you create in the controller or default values that you wan't to use or display in your template do you need to assign in the controller like this:
$title_of_page = "Welcome at this page!";
$smarty->assign('title_of_page', 'title_of_page');
In your template:
<h1>{$title_of_page}</h1>
In Flex I'm using the following code to allow sorting in a DataGrid (the data is paged and sorted serverside).
private function headerReleaseHandler(event:DataGridEvent):void
{
var column:DataGridColumn = DataGridColumn(event.currentTarget.columns[event.columnIndex]);
if(this.count>0)
{
if(this.query.SortField == column.dataField)
{
this.query.SortAscending = !this.query.SortAscending;
}
else
{
this.query.SortField = column.dataField;
this.query.SortAscending = true;
}
this.fill();
}
event.preventDefault();
}
This works perfectly, except that the arrows that indicate sorting isn't shown. How can I accomplish that?
Thanks!
/Niels
There is an example here if this is what you are looking for:
http://blog.flexexamples.com/2008/02/28/displaying-the-sort-arrow-in-a-flex-datagrid-control-without-having-to-click-a-column/
It looks like you need to refresh the collection used by your dataprovider.
I have encountered the same problem and the only solution I found was to override the DataGrid and create a custom one.
Here is the class:
public class DataGridCustomSort extends DataGrid
{
public function DataGridCustomSort()
{
super();
addEventListener(DataGridEvent.HEADER_RELEASE,
headerReleaseHandlerCustomSort,
false, EventPriority.DEFAULT_HANDLER);
}
public function headerReleaseHandlerCustomSort(event:DataGridEvent):void {
mx_internal::sortIndex = event.columnIndex;
if (mx_internal::sortDirection == null || mx_internal::sortDirection == "DESC")
mx_internal::sortDirection = "ASC";
else
mx_internal::sortDirection = "DESC";
placeSortArrow();
}
}
You have to specifically call the placeSortArrow() method when you get the HEADER_RELEASE event and set the column index and direction information.
in the above code what does "this" refer to is it the datagrid because I am confused by this.query.SortField , I am assuming 'this' and "query' are your own custom objects. and why are you checking for count. what count is that.
Regards
-Mohan