Jump to content
thirty bees forum

ssimard

Members
  • Posts

    162
  • Joined

  • Last visited

  • Days Won

    1

Posts posted by ssimard

  1. Hi everyone, I am now using TB on a production site and would like to keep it as-is, without updating the modules since I have a lot of overrides.

    Is there a way to turn the module update warnings OFF ? Especially for the non-admin employees.

    Right-now I have permissions restricted for a few people but they should be able to update some modules configs and therefore, they are seing the modules lists. I don't want them to click on update buttons thou !

    Any ideas ?

    Thanks. Sylvain

  2. @mockob you don't have to overwrite anything if you are only uploading your images via the backend, it will work.

    The only thing you have to change is in the template itself, in the Smarty files.

    @mdekker was supposed to fix it in the core for the CSV and Webservice upload.

  3. Take a look at my replies in this particular thread: https://forum.thirtybees.com/topic/40/retina-images-please-help

    Also be aware that the 2x images are not created if you use the csv import and neither are they if you are using the API.

    You will have to edit the template yourself and for the csv and API import there are fixes I will dig my old thread for you ..

    Here: https://forum.thirtybees.com/topic/798/retina-images-are-not-created-when-uploading-through-the-webservice

    This was supposed to be added to the core so i don't know if it has been yet.

  4. 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.

  5. 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: **

    1. 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".

    0_1522160311886_Capture d’écran 2018-03-27 à 10.13.40.png

    0_1522160321902_Capture d’écran 2018-03-27 à 10.16.30.png

    1. In the override section, create AdminClientMessageController.php and add the following code. 0_1522160530797_Capture d’écran 2018-03-27 à 10.20.30.png

    ``` <?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 license@thirtybees.com 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 contact@thirtybees.com * @author PrestaShop SA contact@prestashop.com * @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. 0_1522160734336_Capture d’écran 2018-03-27 à 10.24.38.png

    ```<?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 license@thirtybees.com 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 contact@thirtybees.com * @author PrestaShop SA contact@prestashop.com * @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');
    }
    

    }

    ```

    1. 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. 0_1522161051645_Capture d’écran 2018-03-27 à 10.30.16.png

    ``` <?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 license@thirtybees.com 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 contact@thirtybees.com * @author PrestaShop SA contact@prestashop.com * @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. 0_1522161190650_Capture d’écran 2018-03-27 à 10.32.27.png

    ``` {* * 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 license@prestashop.com 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 contact@prestashop.com * @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} {/foreach}
    {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}

    {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: 0_1522161552204_Capture d’écran 2018-03-27 à 10.36.01.png

    To add the management link to the clients menu, just go to administration-menus and add an item like this: 0_1522161617283_Capture d’écran 2018-03-27 à 10.36.58.png

    ----> 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%. 0_1522161931317_Capture d’écran 2018-03-27 à 10.43.43.png

    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 !

  6. Not really what I am looking for but thanks ;-)

    Our customer support staff are already using predefined messages in gmail in order to quickly reply to the most common issues but I don't want them to copy/paste an answer in the back office module. I would really like them to use it thou...

    I might have to do it myself if it doesn't exist..

  7. I can confirm that's it's a lot of work to do. Not sure if you would be better starting from another theme or not, I pretty much jumped in TB at the first public release so the version of the theme I used was a bit older than what you have now.

    For me the hardest has been to hack the suppliers into authors. Also selling PDF and ePubs of my book as variations was a nice puzzle.

    If you like retina images, be aware that the default theme does not support them (or at least did not at first). You can search the forum for my threads on retina images, good advices and code examples for you to fix it.

  8. I'm not sure why it's not working, I haven't posted the full override code for product.php but I assume that you've done it properly ?

    ``` class Product extends ProductCore{ public function getAllTags($idlang = 0, $qty = 10){ if(!$idlang){ $context = Context::getContext(); $id_lang = $context->language->id; }

        return Tag::getTags($id_lang, $qty);
    }
    

    } ```

    I'm really sorry, I fail to see why it is not working on your end :-/

  9. You have to override the Tag class for this to work. Look in my accepted answer.

    Basically, it is just an SQL request that retrieves entries in the tag database. It is called by the getAllTags function that you have to add to product.php class as well.

    Make sure that you have datas in that table.

    I can't read German, what does the full error means ?

  10. @zimmer-media

    You should be good if you modify the 3 files I pointed out in my previous post.

    You can do a quick test and just pass a manual list of keywords to the autocomplete function in Informations.tpl. -> /override/controllers/admin/templates/products/informations.tpl

    Find the code in the original file(between line 438 and 448) and replace it with this:

    ``` {literal}

    {/literal} ```

  11. @lathaneo said in Keywords autocomplete in the products page:

    Hi,

    Tagify doesn't work with jquery autocomplete. Tagify use only the widget factory of JqueryUI.

    Regards.

    I only wanted to make it easier to choose keywords while typing. I have several keywords that are the same for several books (kinda like bisac codes if you're familiar).

    It is working now with JQuery autocomplete (see the code I posted). The only downside is that you have to click once to select it then ENTER in order to confirm.

    0_1517407994201_Capture d’écran 2018-01-31 à 09.12.38.png

  12. There was a conflict between JQueryUI and standard JQuery it seems.. here's what I did in the end using standard JQuery autocomplete:

    Informations.tpl ``` {literal}

    {/literal} ```

    Tag.php class Tag extends TagCore { public static function getTags($id_lang, $nb = 10) { return Db::getInstance(_PS_USE_SQL_SLAVE_)->ExecuteS(' SELECT t.`name` FROM `'._DB_PREFIX_.'tag` t WHERE t.`id_lang` = '.(int)$id_lang.' ORDER BY t.`name` ASC'); //I commented this out since I need them all and not only the first 10. Could be useful to someone, I don't know. //LIMIT 0, '.(int)$nb); } }

    Product.php, added this function: ``` class Product extends ProductCore{ public function getAllTags($idlang = 0, $qty = 10){ if(!$idlang){ $context = Context::getContext(); $id_lang = $context->language->id; }

        return Tag::getTags($id_lang, $qty);
    }
    

    } ```

    • Thanks 1
×
×
  • Create New...