Prestashop: duplicate Invoice PDF with new tpl - prestashop

How can I duplicate the Generate Invoice PDF Process in Prestashop? I want to use a different tpl file, but the rest should stay the same.
Let me explain, what I already did:
HTMLTemplateInvoice as HTMLTemplateMahnung and changed Class Name.
Added: const TEMPLATE_MAHNUNG = 'Mahnung'; to the file classes/pdf/PDF.php
Created file mahnung.tpl in root/pdf folder
Added to AdminPdfController.php:
public function processGenerateMahnungPdf() {
if (Tools::isSubmit('id_order')) {
$this->generateMahnungPDFByIdOrder(Tools::getValue('id_order'));
} elseif (Tools::isSubmit('id_order_invoice')) {
$this->generateInvoicePDFByIdOrderInvoice(Tools::getValue('id_order_invoice'));
} else {
die(Tools::displayError('The order ID -- or the invoice order ID -- is missing.'));
}}
AND
public function generateMahnungPDFByIdOrder($id_order)
{
$order = new Order((int)$id_order);
if (!Validate::isLoadedObject($order)) {
die(Tools::displayError('The order cannot be found within your database.'));
}
$order_invoice_list = $order->getInvoicesCollection();
Hook::exec('actionPDFInvoiceRender', array('order_invoice_list' => $order_invoice_list));
$this->generatePDF($order_invoice_list, PDF::TEMPLATE_MAHNUNG);
}
But it's not working. It just doesn't generate the PDF.
Any help?
UPDATE
I had to include the class: require_once _PS_ROOT_DIR_ . '/classes/pdf/HTMLTemplateMahnung.php';
Now its working. Anybody knows why I had to this? I don't see any includes of Core Files :S

Pretashop uses the file cache/class_index.php to keep track of the classes it needs.
Everytime you add a new override, or even a class or controller, you need to delete (or rename) this file. If it doesn't find it, Prestashop will recreate indexing all files in set folders (classes, controllers, overrides, and others).

Related

Drupal 8 custom modules - Installing additional configuration from YML files with a hook_update_N

I have a custom module which installs a specific set of configurations - these are all stored in the config/install folder, which means they are installed when the module is installed.
The configuration includes a content type, paragraphs, view modes, form modes, field storages and fields attached to both the content type and the paragraphs, etc. The idea is to use this module to install a 'feature' (a blog) and use it across multiple sites, as well as provide updates and extensions when we add more stuff to this feature.
Since upon initial install, you cannot add more configuration through the config/install folder, I've been trying to find a way to import additional configuration files through an update hook, and this is one that works:
<?php
use \Symfony\Component\Yaml\Yaml;
/**
* Installs the file upload element
*/
function MODULE_NAME_update_8002() {
// Is the flaw with this the fact that the order of loading configurations now
// matters and is a little bit more difficult to deal with?
// NOTE: YES. If, for example, you comment out the installing of the
// field_storage for the field_cb_file, but try to add the field_cb_file to
// the paragraph type, the update is successful and no errors are thrown.
// This is basically me trying to re-create the drupal configuration management
// system, without the dependency checks, etc. What is the PROPER way of
// importing additional configuration from a module through an update?
// FIXME:
$configs_to_install = [
'paragraphs.paragraphs_type.cbsf_file_download',
'field.storage.paragraph.field_cb_file',
'field.field.paragraph.cbsf_file_download.field_cb_file',
'field.field.paragraph.cbsf_file_download.field_cb_heading',
'field.field.paragraph.cbsf_file_download.field_cb_icon',
'field.field.paragraph.cbsf_file_download.field_cb_text',
'core.entity_form_display.paragraph.cbsf_file_download.default',
'core.entity_view_display.paragraph.cbsf_file_download.default',
];
foreach ($configs_to_install as $config_to_install) {
$path = drupal_get_path('module', 'MODULE_NAME') . '/config/update_8002/' . $config_to_install . '.yml';
$content = file_get_contents($path);
$parsed_yml = Yaml::parse($content);
$active_storage = \Drupal::service('config.storage');
$active_storage->write($config_to_install, $parsed_yml);
}
}
however, there are flaws with this method since it means you have to order configuration files in the right order if they depend on each other, and any dependencies that are present in the config file are not checked.
Is there a way to utilise configuration management to import config properly, in this same, 'loop over the files' way? Or to point to a folder that contains all of the config files and install them?
EDIT: There are further issues with this method - even if you've ordered the files correctly in terms of dependencies, no database tables are created. The configuration is simply 'written in' as is, and no other part of Drupal seems to be made aware that new entities were created, so they cannot run any of the functions that are otherwise ran if you were to create the entities through Drupal GUI. Definitely not the recommended way of transferring more complex configuration.
I've pushed this a step further - there is a way to use the EntityTypeManager class to create / update configurations.
2 links have helped me with this largely:
https://drupal.stackexchange.com/questions/164713/how-do-i-update-the-configuration-of-a-module
pwolanins answer at the bottom provides a function that either updates the configuration if it exists, or creates the configuration outright.
https://www.metaltoad.com/blog/programmatically-importing-drupal-8-field-configurations
the code on this page gives a clearer idea of what is happening - for each configuration that you'd like to install, you run the YML file through the respective storage manager, and then create the appropriate entity configurations, which creates all of the required DB tables.
What I ended up doing was:
Utilised a slightly modified version of pwolanins code and create a generic config updater function -
function _update_or_install_config( String $prefix, String $update_id, String $module) {
$updated = [];
$created = [];
/** #var \Drupal\Core\Config\ConfigManagerInterface $config_manager */
$config_manager = \Drupal::service('config.manager');
$files = glob(drupal_get_path('module', $module) . '/config/update_' . $update_id. '/' . $prefix . '*.yml') ;
foreach ($files as $file) {
$raw = file_get_contents($file);
$value = \Drupal\Component\Serialization\Yaml::decode($raw);
if(!is_array($value)) {
throw new \RuntimeException(sprintf('Invalid YAML file %s'), $file);
}
$type = $config_manager->getEntityTypeIdByName(basename($file));
$entity_manager = $config_manager->getEntityManager();
$definition = $entity_manager->getDefinition($type);
$id_key = $definition->getKey('id');
$id = $value[$id_key];
/** #var \Drupal\Core\Config\Entity\ConfigEntityStorage $entity_storage */
$entity_storage = $entity_manager->getStorage($type);
$entity = $entity_storage->load($id);
if ($entity) {
$entity = $entity_storage->updateFromStorageRecord($entity, $value);
$entity->save();
$updated[] = $id;
}
else {
$entity = $entity_storage->createFromStorageRecord($value);
$entity->save();
$created[] = $id;
}
}
return [
'udpated' => $updated,
'created' => $created,
];
}
I placed all of my yml files in folder config/update_8002, then utilised this function to loop over the config files in a hook_update_N function:
function MODULE_NAME_update_8002() {
$configs_to_install = [
'paragraphs.paragraphs_type.cbsf_file_download',
'core.entity_form_display.paragraph.cbsf_file_download.default',
'core.entity_view_display.paragraph.cbsf_file_download.default',
'field.storage.paragraph.field_cb_file',
'field.field.paragraph.cbsf_file_download.field_cb_file',
'field.field.paragraph.cbsf_file_download.field_cb_heading',
'field.field.paragraph.cbsf_file_download.field_cb_icon',
'field.field.paragraph.cbsf_file_download.field_cb_text',
];
foreach ($configs_to_install as $config_to_install) {
_update_or_install_config('paragraphs.paragraphs_type', '8002', 'MODULE_NAME');
_update_or_install_config('field.storage.paragraph', '8002', 'MODULE_NAME');
_update_or_install_config('field.field.paragraph', '8002', 'MODULE_NAME');
_update_or_install_config('core.entity_view_display.paragraph', '8002', 'MODULE_NAME');
_update_or_install_config('core.entity_form_display.paragraph', '8002', 'MODULE_NAME');
}
}
Note that the _update_or_install_config function loops over all of the configs in the folder that match a specific entity type manager - thus you should just include the prefix in the function, and all of the YML files that import configuration of the same type will be included.

Prestashop: How to check if product is on a wishlist

Does anyone know how to take advantage of any of the functions of the wishlist module to have a variable to use in tpl to know if a product is already added to a wishlist?
If I do not exist, I imagine that some function can be used because when you add a product, if you already have it in the default list, it adds a nity instead of adding it as a new product.
Ideally, for the function you would need, it would be to make a tour not only in the list by default but in all, it is necessary to know if it is already added to a list or not.
The function would be to show an icon or another according to whether a product is already on a list or not.
Has someone developed it or could you help me out?
Thanks!
Prestashop 1.6.1.16
Thank you very much for the contribution.
I gave the code that I put below and it always gives me a "1" (and if you notice, I invented the product id (which does not exist, since I only have 8 products at the moment).
I made an override of the php of the module:
class BlockWishListOverride extends BlockWishList
{
function checkProductIsInWishlist()
{
$customer_wishlists = getByIdCustomer($this->context->customer->id);
$id_product_to_check = 424554224; // Replace by the current product id
foreach ($customer_wishlists as $cw) {
$check_product_wl = WishList::getProductByIdCustomer((int)$cw['id_wishlist'], (int)$this->context->customer->id,
(int)$this->context->language->id, (int)$id_product_to_check);
if (count($check_product_wl)) {
$this->smarty->assign('is_in_wishlist', true);
}
}
}
}
And in my product.tpl:
<p>Is in whislist: {$is_in_wishlist|print_r}</p>
Result on Front:
Result $is_in_wishlist on front
Thanks
You can use the following methods:
public static function getByIdCustomer($id_customer)
public static function getProductByIdCustomer($id_wishlist, $id_customer, $id_lang, $id_product = null, $quantity = false)
which are available in /modules/blockwishlist/WishList.php
In your controller file (on the front-end), you could do something like this:
$customer_wishlists = WishList::getByIdCustomer($this->context->customer->id);
$id_product_to_check = 42; // Replace by the current product id
foreach ($customer_wishlists as $cw) {
$check_product_wl = WishList::getProductByIdCustomer((int)$cw['id_wishlist'], (int)$this->context->customer->id,
(int)$this->context->language->id, (int)$id_product_to_check);
if (count($check_product_wl)) {
$this->smarty->assign('is_in_wishlist', true);
}
}
And then check the {$is_in_wishlist} variable in your Smarty product.tpl template.

What is module's "themes" folder used for?

PS documentation says that when developing a module you can create the /themes/[theme_name]/modules subfolder and that it is used for:
"Sub-folder for overriding .tpl files and languages files, if necessary."
and that it:
"enables you to handle the module's template files in various ways, depending on the current theme.",
but i don't really understand its practical usage. What would be a use case of it?
Thanks
When you develop a Prestashop website you should never change core files. This mean that you can only create new modules in the /modules/ folder but not alter existing one. Because if you update a module you altered, all your changes will be gone.
Each time Prestashop needs to load a module template file it will first look in the current theme /themes/your_theme/modules/the_module/path_to_tpl.tpl if an override of this template exists. If not it will load the template from the /modules directory.
This recommandation is also valid for .css and .js files.
The documentation you mentioned in the comment below is wrong and should be updated. You can't put a themes folder inside a module.
Here is the _isTemplateOverloadedStatic() method from Module class called everytime we need a module template:
/*
** Template management (display, overload, cache)
*/
protected static function _isTemplateOverloadedStatic($module_name, $template)
{
if (Tools::file_exists_cache(_PS_THEME_DIR_.'modules/'.$module_name.'/'.$template)) {
return _PS_THEME_DIR_.'modules/'.$module_name.'/'.$template;
} elseif (Tools::file_exists_cache(_PS_THEME_DIR_.'modules/'.$module_name.'/views/templates/hook/'.$template)) {
return _PS_THEME_DIR_.'modules/'.$module_name.'/views/templates/hook/'.$template;
} elseif (Tools::file_exists_cache(_PS_THEME_DIR_.'modules/'.$module_name.'/views/templates/front/'.$template)) {
return _PS_THEME_DIR_.'modules/'.$module_name.'/views/templates/front/'.$template;
} elseif (Tools::file_exists_cache(_PS_MODULE_DIR_.$module_name.'/views/templates/hook/'.$template)) {
return false;
} elseif (Tools::file_exists_cache(_PS_MODULE_DIR_.$module_name.'/views/templates/front/'.$template)) {
return false;
} elseif (Tools::file_exists_cache(_PS_MODULE_DIR_.$module_name.'/'.$template)) {
return false;
}
return null;
}
As you can see in this code, Prestashop will never look into a themes folder inside your module when loading a template.

module creation in suiteCRM

I am using SuiteCRM ( Sugar CRM 6.x community edition ) & want to create a custom login page and after successful login I want to redirect based on user type
tried to create some modules but there is no clear documentation except few of useful links, below are my queries :
Can we create custom modules without using module builder, if yes then what would be steps?
Do we need to write module in /module folder or /custom/module folder or on both place?
any link is also appreciated.
You can create a custom Login Page by modfying "modules/Users/login.tpl"
Custom Modules can be created through Modulebuilder or manually.
When creating modules manually it's important to use the right names.
The easiest way is a Plural name for the folder, table and Module and a singular name for the class.
Manual steps:
You need a Folder in modules/ named like you module (i.e. CAccounts)
In this folder you need a file named like the class (i.e CAccount.php) with something like that as content:
require_once('data/SugarBean.php');
require_once('include/utils.php');
class CAccount extends SugarBean{
var $table_name = 'caccounts';
var $object_name = 'CAccount';
var $module_dir = 'CAccounts';
var $new_schema = true;
var $name;
var $created_by;
var $id;
var $deleted;
var $date_entered;
var $date_modified;
var $modified_user_id;
var $modified_by_name;
function CAccount (){
parent::SugarBean();
}
function get_summary_text(){
return $this->name;
}
function bean_implements($interface)
{
switch($interface)
{
case 'ACL':return true;
}
return false;
}
}
In this folder you need a vardefs.php file:
$dictionary['CAccount'] = array(
'table'=>'caccounts',
'audited'=>false,
'fields'=>array (
//Your fielddefs here
)
);
require_once('include/SugarObjects/VardefManager.php');
VardefManager::createVardef('CAccounts','CAccount', array('basic'));
For the language and metadata folders look at any other module.
Next is a file at "custom/Extension/application/Ext/Include/CAccounts.include.php"
$moduleList[] = 'CAccounts';
$beanList['CAccounts'] = 'CAccount';
$beanFiles['CAccount'] = 'modules/CAccounts/CAccount.php';
A language file for the module name must be in "custom/Extension/application/Ext/Language/"
$app_list_strings['moduleList']['CAccounts'] = 'Custom Accounts';
To display the Module in your tabs you need to use "rebuild and repair" and then the "Display Modules and Subpanels" option in the admin menu.
For a custom module you don't need the "custom/" folder structure. Files there will be used by sugar if provided, but often there's no need for that in a custom module.
Guides about the Module Framework can be found on the sugarcrm support site:
http://support.sugarcrm.com/02_Documentation/04_Sugar_Developer/Sugar_Developer_Guide_6.5/03_Module_Framework

UploadField for Files in SilverStripe

I want to insert a new field, below the content textarea, to hold a link/connection to one file (moastly a zip file from my assets folder).
I couldn't find any documentation nor a tutorial for files. Only images. Does anybody know where I can find some help files or can give me some basic code to start from?
In general, there's the API Docs for UploadField.
As well as the docs,
although they're a bit hidden - I'm just fixing the search on doc.silverstripe.org.
This should do the trick:
<?php
class Page extends SiteTree {
static $has_one = array('MyFile', 'File');
function getCMSFields() {
$fields = parent::getCMSFields();
$upload = new UploadField('MyFile');
$upload->setConfig('allowedMaxFileNumber', 1);
$upload->getValidator()->setAllowedExtensions(array('zip'));
$fields->addFieldToTab('Root.Main', $upload);
return $fields;
}
}