ssimard Posted March 27, 2018 Posted March 27, 2018 Hi all, well I went ahead and did it myself. Here are the steps in order to add predefined messages in the customer service section. I hope that it will be useful for someone. ** Sorry about the french screenshots and strings, I am running a french site so it is what it is :grinning: ** You will have to duplicate the 2 database tables used by the orders predefined messages option. They are "xxordermessage" and "xxordermessagelang". Duplicate the structure only and rename them to "xxclientmessage" and "xxclientmessagelang". In both new tables, change the first column name to "idclientmessage". In the override section, create AdminClientMessageController.php and add the following code. ``` <?php /** * 2007-2016 PrestaShop * * Thirty Bees is an extension to the PrestaShop e-commerce software developed by PrestaShop SA * Copyright (C) 2017 Thirty Bees * * 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 [email protected] 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 https://www.thirtybees.com for more information. * * @author Thirty Bees [email protected] * @author PrestaShop SA [email protected] * @copyright 2017 Thirty Bees * @copyright 2007-2016 PrestaShop SA * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * PrestaShop is an internationally registered trademark & property of PrestaShop SA */ /** * Class AdminClientMessageControllerCore * * @since 1.0.0 */ class AdminClientMessageControllerCore extends AdminController { /** * AdminClintMessageControllerCore constructor. * * @since 1.0.0 */ public function __construct() { $this->bootstrap = true; $this->table = 'client_message'; $this->className = 'ClientMessage'; $this->lang = true; $this->addRowAction('edit'); $this->addRowAction('delete'); $this->context = Context::getContext(); if (!Tools::getValue('realedit')) { $this->deleted = false; } $this->bulk_actions = [ 'delete' => [ 'text' => $this->l('Delete selected'), 'confirm' => $this->l('Delete selected items?'), 'icon' => 'icon-trash', ], ]; $this->fields_list = [ 'id_client_message' => [ 'title' => $this->l('ID'), 'align' => 'center', ], 'name' => [ 'title' => $this->l('Name'), ], 'message' => [ 'title' => $this->l('Message'), 'maxlength' => 300, ], ]; $this->fields_form = [ 'legend' => [ 'title' => $this->l('Client messages'), 'icon' => 'icon-mail', ], 'input' => [ [ 'type' => 'text', 'lang' => true, 'label' => $this->l('Name'), 'name' => 'name', 'size' => 53, 'required' => true, ], [ 'type' => 'textarea', 'lang' => true, 'label' => $this->l('Message'), 'name' => 'message', 'required' => true, ], ], 'submit' => [ 'title' => $this->l('Save'), ], ]; parent::__construct(); } /** * Initialize page header toolbar * * @return void * * @since 1.0.0 */ public function initPageHeaderToolbar() { if (empty($this->display)) { $this->page_header_toolbar_btn['new_client_message'] = [ 'href' => static::$currentIndex.'&addclient_message&token='.$this->token, 'desc' => $this->l('Ajouter un nouveau message client'), 'icon' => 'process-icon-new', ]; } parent::initPageHeaderToolbar(); } } ``` 3. In override, create ClientMessage.php and paste the following code inside. ```<?php /** * 2007-2016 PrestaShop * * Thirty Bees is an extension to the PrestaShop e-commerce software developed by PrestaShop SA * Copyright (C) 2017 Thirty Bees * * 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 [email protected] 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 https://www.thirtybees.com for more information. * * @author Thirty Bees [email protected] * @author PrestaShop SA [email protected] * @copyright 2017 Thirty Bees * @copyright 2007-2016 PrestaShop SA * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * PrestaShop is an internationally registered trademark & property of PrestaShop SA */ /** * Class ClientMessageCore * * @since 1.0.0 */ class ClientMessageCore extends ObjectModel { // @codingStandardsIgnoreStart /** @var string name name */ public $name; /** @var string message content */ public $message; /** @var string Object creation date */ public $date_add; // @codingStandardsIgnoreEnd /** * @see ObjectModel::$definition */ public static $definition = [ 'table' => 'client_message', 'primary' => 'id_client_message', 'multilang' => true, 'fields' => [ 'date_add' => ['type' => self::TYPE_DATE, 'validate' => 'isDate' ], 'name' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isGenericName', 'required' => true, 'size' => 128 ], 'message' => ['type' => self::TYPE_STRING, 'lang' => true, 'validate' => 'isMessage', 'required' => true, 'size' => 1200], ], ]; protected $webserviceParameters = [ 'fields' => [ 'id' => ['sqlId' => 'id_discount_type', 'xlink_resource' => 'client_message_lang'], 'date_add' => ['sqlId' => 'date_add'], ], ]; /** * @param $idLang * * @return array|false|mysqli_result|null|PDOStatement|resource * * @since 1.0.0 * @version 1.0.0 Initial version */ public static function getClientMessages($idLang) { return Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS(' SELECT om.id_client_message, oml.name, oml.message FROM '._DB_PREFIX_.'client_message om LEFT JOIN '._DB_PREFIX_.'client_message_lang oml ON (oml.id_client_message = om.id_client_message) WHERE oml.id_lang = '.(int) $idLang.' ORDER BY name ASC'); } } ``` Next you will have to override AdminCustomerThreadsController.php in order to add the messages list to the template. Do it and add the following code into it. ``` <?php /** * 2007-2016 PrestaShop * * Thirty Bees is an extension to the PrestaShop e-commerce software developed by PrestaShop SA * Copyright (C) 2017 Thirty Bees * * 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 [email protected] 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 https://www.thirtybees.com for more information. * * @author Thirty Bees [email protected] * @author PrestaShop SA [email protected] * @copyright 2017 Thirty Bees * @copyright 2007-2016 PrestaShop SA * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) * PrestaShop is an internationally registered trademark & property of PrestaShop SA */ /** * Class AdminCustomerThreadsControllerCore * * @since 1.0.0 */ class AdminCustomerThreadsController extends AdminCustomerThreadsControllerCore { /** * Render view * * @return string * * @since 1.0.0 */ public function renderView() { if (!$idCustomerThread = (int) Tools::getValue('id_customer_thread')) { return ''; } $this->context = Context::getContext(); if (!($thread = $this->loadObject())) { return ''; } $this->context->cookie->{'customer_threadFilter_cl!id_contact'} = $thread->id_contact; $employees = Employee::getEmployees(); $messages = CustomerThread::getMessageCustomerThreads($idCustomerThread); foreach ($messages as $key => $mess) { if ($mess['id_employee']) { $employee = new Employee($mess['id_employee']); $messages[$key]['employee_image'] = $employee->getImage(); } if (isset($mess['file_name']) && $mess['file_name'] != '') { $messages[$key]['file_name'] = _THEME_PROD_PIC_DIR_.$mess['file_name']; } else { unset($messages[$key]['file_name']); } if ($mess['id_product']) { $product = new Product((int) $mess['id_product'], false, $this->context->language->id); if (Validate::isLoadedObject($product)) { $messages[$key]['product_name'] = $product->name; $messages[$key]['product_link'] = $this->context->link->getAdminLink('AdminProducts').'&updateproduct&id_product='.(int) $product->id; } } } $nextThread = CustomerThread::getNextThread((int) $thread->id); $contacts = Contact::getContacts($this->context->language->id); $actions = []; if ($nextThread) { $nextThread = [ 'href' => static::$currentIndex.'&id_customer_thread='.(int) $nextThread.'&viewcustomer_thread&token='.$this->token, 'name' => $this->l('Reply to the next unanswered message in this thread'), ]; } if ($thread->status != 'closed') { $actions['closed'] = [ 'href' => static::$currentIndex.'&viewcustomer_thread&setstatus=2&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token, 'label' => $this->l('Mark as "handled"'), 'name' => 'setstatus', 'value' => 2, ]; } else { $actions['open'] = [ 'href' => static::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token, 'label' => $this->l('Re-open'), 'name' => 'setstatus', 'value' => 1, ]; } if ($thread->status != 'pending1') { $actions['pending1'] = [ 'href' => static::$currentIndex.'&viewcustomer_thread&setstatus=3&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token, 'label' => $this->l('Mark as "pending 1" (will be answered later)'), 'name' => 'setstatus', 'value' => 3, ]; } else { $actions['pending1'] = [ 'href' => static::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token, 'label' => $this->l('Disable pending status'), 'name' => 'setstatus', 'value' => 1, ]; } if ($thread->status != 'pending2') { $actions['pending2'] = [ 'href' => static::$currentIndex.'&viewcustomer_thread&setstatus=4&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token, 'label' => $this->l('Mark as "pending 2" (will be answered later)'), 'name' => 'setstatus', 'value' => 4, ]; } else { $actions['pending2'] = [ 'href' => static::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int) Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token, 'label' => $this->l('Disable pending status'), 'name' => 'setstatus', 'value' => 1, ]; } if ($thread->id_customer) { $customer = new Customer($thread->id_customer); $orders = Order::getCustomerOrders($customer->id); if ($orders && count($orders)) { $totalOk = 0; $ordersOk = []; foreach ($orders as $key => $order) { if ($order['valid']) { $ordersOk[] = $order; $totalOk += $order['total_paid_real'] / $order['conversion_rate']; } $orders[$key]['date_add'] = Tools::displayDate($order['date_add']); $orders[$key]['total_paid_real'] = Tools::displayPrice($order['total_paid_real'], new Currency((int) $order['id_currency'])); } } $products = $customer->getBoughtProducts(); if ($products && count($products)) { foreach ($products as $key => $product) { $products[$key]['date_add'] = Tools::displayDate($product['date_add'], null, true); } } } $timelineItems = $this->getTimeline($messages, $thread->id_order); $firstMessage = $messages[0]; if (!$messages[0]['id_employee']) { unset($messages[0]); } $contact = ''; foreach ($contacts as $c) { if ($c['id_contact'] == $thread->id_contact) { $contact = $c['name']; } } $this->tpl_view_vars = [ 'id_customer_thread' => $idCustomerThread, 'thread' => $thread, 'actions' => $actions, 'employees' => $employees, 'current_employee' => $this->context->employee, 'messages' => $messages, 'first_message' => $firstMessage, 'contact' => $contact, 'next_thread' => $nextThread, 'orders' => isset($orders) ? $orders : false, 'customer' => isset($customer) ? $customer : false, 'products' => isset($products) ? $products : false, 'total_ok' => isset($totalOk) ? Tools::displayPrice($totalOk, $this->context->currency) : false, 'orders_ok' => isset($ordersOk) ? $ordersOk : false, 'count_ok' => isset($ordersOk) ? count($ordersOk) : false, 'PS_CUSTOMER_SERVICE_SIGNATURE' => str_replace('\r\n', "\n", Configuration::get('PS_CUSTOMER_SERVICE_SIGNATURE', (int) $thread->id_lang)), 'timeline_items' => $timelineItems, 'clientMessages' => ClientMessage::getClientMessages($thread->id_lang), ]; if ($nextThread) { $this->tpl_view_vars['next_thread'] = $nextThread; } return AdminController::renderView(); } } ``` 5. And finally, you will have to override the helper view.tpl with the following code. ``` {* * 2007-2016 PrestaShop * * NOTICE OF LICENSE * * This source file is subject to the Academic Free License (AFL 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/afl-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 [email protected] 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 [email protected] * @copyright 2007-2016 PrestaShop SA * @license http://opensource.org/licenses/afl-3.0.php Academic Free License (AFL 3.0) * International Registered Trademark & Property of PrestaShop SA *} {extends file="helpers/view/view.tpl"} {block name="overridetpl"} {include file="controllers/customerthreads/helpers/view/modal.tpl" } {l s="Thread"}: #{$id_customer_thread|intval} {if isset($next_thread) && $next_thread} {$next_thread.name} {/if} {foreach $actions as $action} {if isset($action.icon)}{/if}{$action.label} {/foreach} {l s="Forward this discussion to another employee"} {if isset($customer->firstname)} {$customer->firstname|escape:'html':'UTF-8'} {$customer->lastname|escape:'html':'UTF-8'} ({$customer->email|escape:'html':'UTF-8'}) {else} {$thread->email|escape:'html':'UTF-8'} {/if} {if isset($contact) && trim($contact) != ''} {l s="To:"} {$contact|escape:'html':'UTF-8'} {/if} {if isset($customer->firstname)} {if $count_ok} {l s='[1]%1$d[/1] order(s) validated for a total amount of [2]%2$s[/2]' sprintf=[$count_ok, $total_ok] tags=['', '']} {else} {l s="No orders validated for the moment"} {/if} {l s="Customer since: %s" sprintf=[{dateFormat date=$customer->date_add full=0}]} {/if} {if !$first_message.id_employee} {include file="controllers/customer_threads/helpers/view/message.tpl" message=$first_message initial=true} {/if} </div> <div class="row"> {foreach $messages as $message} {include file="controllers/customer_threads/helpers/view/message.tpl" message=$message initial=false} {/foreach} </div> {l s="Your answer to"} {if isset($customer->firstname)}{$customer->firstname|escape:'html':'UTF-8'} {$customer->lastname|escape:'html':'UTF-8'} {else} {$thread->email}{/if} {if isset($current_employee->firstname)}{/if} {$PS_CUSTOMER_SERVICE_SIGNATURE|escape:'html':'UTF-8'} {l s='Messages prédéfinis'} - {foreach from=$clientMessages item=clientMessage} {$clientMessage['name']} {/foreach} {l s='Configurer les messages prédéfinis'} <div class="panel-footer"> <!-- <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> <i class="icon-magic icon-2x"></i><br> {l s="Choose a template"} </button> --> <button class="btn btn-default pull-right" name="submitReply"><i class="process-icon-mail-reply"></i> {l s="Send"}</button> <input type="hidden" name="id_customer_thread" value="{$thread->id|intval}" /> <input type="hidden" name="msg_email" value="{$thread->email}" /> </div> </form> {if count($timeline_items)} {l s="Orders and messages timeline"} {foreach $timeline_items as $dates} {foreach from=$dates key=date item=timeline_item} {include file="controllers/customer_threads/helpers/view/timeline_item.tpl" timeline_item=$timeline_item} {/foreach} {/foreach} {/if} {/block} ``` @@@@@ Code is complete, don't forget to delete class_index.php in your cache folder before running it. Your customer service reply page should now look like this: To add the management link to the clients menu, just go to administration-menus and add an item like this: ----> One last important thing, you may notice that the drop-down length is fine at first but does not scale if you resize your window. There is a bug in the chosen implementation (you will notice the same problem in the orders section). To fix it, you will have to edit js/admin.js and add the width parameter and set it to 100%. You should now be able to add as many predefined replies as you wish. Hopefully I did not forget anything but feel free to post if you have problems and I will do my best to help you out. Cheers !
Beeta Posted March 27, 2018 Posted March 27, 2018 WOW!! If this is not against TB code standards and is not breaking anything, how about to include in the core?! how about PR on GitHub?
wakabayashi Posted March 27, 2018 Posted March 27, 2018 Cool stuff! Maybe it's something for the core. But maybe the customer Service should be redesigned in a bigger way!? At least for me the customer service right now, is a big mess :)
ssimard Posted March 27, 2018 Author Posted March 27, 2018 Yeah, our tech support staff wanted a few things added before using it. I'll see what else is missing, we are also adding an option to simply send a message to multiple persons without reassigning it to anybody. Sometimes, our tech support staff are sending me and other colleagues questions that they don't know how to answer. They have to be able to send it to multiple persons at once and also, I don't want it to be 'reassigned' to me in the backend. So basically just a 'forward' option. We are almost done on that too.
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now