Merge branch 'develop' into standard-product-changes

This commit is contained in:
vishalsingh.qlo325
2025-05-22 10:18:07 +05:30
41 changed files with 1362 additions and 831 deletions

View File

@@ -48,7 +48,11 @@
@include left(1px)
@include box-shadow(white 0 0 0 3px inset)
.table.customer_thread, #MeaningStatus
.text-success
color: #56CE56
.text-warning
color: #FCC94F
.message-item-initial
h2

View File

@@ -84,6 +84,11 @@
.panel-refund-request .refunded_amount
margin-top: 2px
.order-notes
.panel-content
max-height: 240px
overflow: auto
.panel-guest_address
.panel-heading
align-items: center

View File

@@ -443,3 +443,9 @@
@extend .icon-toggle-on
.process-icon-toggle-off
@extend .icon-toggle-off
.process-icon-reply-all
@extend .icon-reply-all
.process-icon-mail-reply
@extend .icon-mail-reply
.process-icon-lock
@extend .icon-lock

View File

@@ -25,65 +25,21 @@
{extends file="helpers/list/list_header.tpl"}
{block name="leadin"}
<div id="CustomerThreadContacts" class="row">
{assign var=nb_categories value=count($categories)}
{foreach $categories as $key => $val}
{assign var=total_thread value=0}
{assign var=id_customer_thread value=0}
{foreach $contacts as $tmp => $tmp2}
{if $val.id_contact == $tmp2.id_contact}
{assign var=total_thread value=$tmp2.total}
{assign var=id_customer_thread value=$tmp2.id_customer_thread}
{/if}
{/foreach}
<div class="col-lg-3">
<div class="panel">
<div class="panel-heading">
{$val.name}
</div>
{if $nb_categories < 6}
<p>{$val.description}</p>
{/if}
{if $total_thread == 0}
<span class="message-mail">{l s='No new messages'}</span>
{else}
<a href="{$currentIndex|escape:'html':'UTF-8'}&amp;token={$token|escape:'html':'UTF-8'}&amp;id_customer_thread={$id_customer_thread}&amp;viewcustomer_thread" class="button">
{$total_thread}
{if $total_thread > 1}{l s='New messages'}{else}{l s='New message'}{/if}
</a>
{/if}
</div>
</div>
{/foreach}
<div class="col-lg-3">
<div id="MeaningStatus" class="panel">
<div class="panel-heading">
{l s='Meaning of status'}
</div>
{block name="list_filter_items" append}
<div class="pull-right col-xs-4 col-sm-3 col-md-2 col-lg-2">
<div class="list_availibility_container">
<button type="button" class="btn btn-default btn-left btn-block dropdown-toggle" data-toggle="dropdown" data-target="MeaningStatus">
<span>{l s='Thread Statuses'}
<i class="icon-caret-down pull-right"></i>
</button>
<div id="MeaningStatus" class="dropdown-menu">
<ul class="list-unstyled">
<li class="text-success"><i class="icon-circle"></i> {l s='Open'}</li>
<li class="text-danger"><i class="icon-circle"></i> {l s='Closed'}</li>
<li class="text-warning"><i class="icon-circle"></i> {l s='Pending 1'}</li>
<li class="text-warning"><i class="icon-circle"></i> {l s='Pending 2'}</li>
</ul>
</div>
</div>
<div class="col-lg-3">
<div id="CustomerService" class="panel">
<div class="panel-heading">
{l s='Statistics'}
</div>
<ul class="list-unstyled">
{assign var=count value=0}
{foreach $params as $key => $val}
{assign var=count value=$count+1}
<li>{$key} <span class="badge">{$val}</span></li>
{/foreach}
<li><p><i class="icon-circle text-success"></i> {l s='Open'}</p></li>
<li><p><i class="icon-circle text-danger"></i> {l s='Closed'}</p></li>
<li><p><i class="icon-circle text-warning"></i> {l s='Pending 1'}</p></li>
<li><p><i class="icon-circle text-warning"></i> {l s='Pending 2'}</p></li>
</ul>
</div>
</div>
</div>
{/block}
{/block}

View File

@@ -45,14 +45,15 @@
{if !$initial}
<h4 class="message-item-heading">
<i class="icon-mail-reply text-muted"></i>
{if $type == 'customer'}
{$message.customer_name|escape:'html':'UTF-8'}
{else}
{$message.employee_name|escape:'html':'UTF-8'}
{/if}
{if $type == 'customer'}
{$message.customer_name|escape:'html':'UTF-8'}
{else}
{$message.employee_name|escape:'html':'UTF-8'}
{/if}
</h4>
{/if}
<span class="message-date">&nbsp;<i class="icon-calendar"></i> - {dateFormat date=$message.date_add full=0} - <i class="icon-time"></i> {$message.date_add|substr:11:5}</span>
{if $message.private}<span class="badge badge-info">{l s='Private'}</span>{/if}
{if isset($message.file_name)} <span class="message-product">&nbsp;<i class="icon-link"></i> <a href="{$message.file_name|escape:'html':'UTF-8'}" class="_blank">{l s="Attachment"}</a></span>{/if}
{if isset($message.product_name)} <span class="message-attachment">&nbsp;<i class="icon-book"></i> <a href="{$message.product_link|escape:'html':'UTF-8'}" class="_blank">{l s="Room type:"} {$message.product_name|escape:'html':'UTF-8'} </a></span>{/if}
<p class="message-item-text">{$message.message|escape:'html':'UTF-8'|nl2br}</p>

View File

@@ -30,9 +30,9 @@
<div class="panel-heading">
<i class="icon-comments"></i>
{l s="Thread"}: <span class="badge">#{$id_customer_thread|intval}</span>
{if isset($next_thread) && $next_thread}
<a class="btn btn-default pull-right" href="{$next_thread.href|escape:'html':'UTF-8'}">
{$next_thread.name} <i class="icon-forward"></i>
{if isset($thread->id_customer) && $thread->id_customer}
<a class="pull-right" href="{$link->getAdminLink('AdminCustomers')|escape:'html':'UTF-8'}&amp;id_customer={$customer->id|intval}&amp;viewcustomer&">
{$customer->firstname} {$customer->lastname}<small>({$customer->email|escape:'html':'UTF-8'})</small>
</a>
{/if}
</div>
@@ -50,24 +50,25 @@
</div>
<div class="row">
<div class="message-item-initial media">
<a href="{if isset($customer->id)}{$link->getAdminLink('AdminCustomers')|escape:'html':'UTF-8'}&amp;id_customer={$customer->id|intval}&amp;viewcustomer&{else}#{/if}" class="avatar-lg pull-left"><i class="icon-user icon-3x"></i></a>
<span class="avatar-md pull-left"><i class="icon-user icon-3x"></i></span>
<div class="media-body">
<div class="row">
<div class="col-sm-6">
{if isset($customer->firstname)}
{if isset($thread->user_name) && trim($thread->user_name)}
<h2>
<a href="{$link->getAdminLink('AdminCustomers')|escape:'html':'UTF-8'}&amp;id_customer={$customer->id|intval}&amp;viewcustomer&">
{$customer->firstname|escape:'html':'UTF-8'} {$customer->lastname|escape:'html':'UTF-8'} <small>({$customer->email|escape:'html':'UTF-8'})</small>
</a>
{$thread->user_name|escape:'html':'UTF-8'} <small>({$thread->email|escape:'html':'UTF-8'})</small>
</h2>
{else}
<h2>{$thread->email|escape:'html':'UTF-8'}</h2>
{/if}
{if isset($thread->phone) && trim($thread->phone) != ''}
<p>{l s='Phone:'} {$thread->phone|escape:'html':'UTF-8'}</p>
{/if}
{if isset($contact) && trim($contact) != ''}
<span>{l s="To:"} </span><span class="badge">{$contact|escape:'html':'UTF-8'}</span>
<span>{l s='To:'} </span><span class="badge">{$contact|escape:'html':'UTF-8'}</span>
{/if}
</div>
{if isset($customer->firstname)}
{if isset($customer->email) && $customer->email == $thread->email}
<div class="col-sm-6">
<p>
{if $count_ok}
@@ -116,7 +117,9 @@
{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>
<button class="btn btn-default" name="submitReplyAndClose"><i class="process-icon-reply-all"></i> {l s='Reply And Close'}</button>
<button class="btn btn-default pull-right" name="submitReply"><i class="process-icon-mail-reply"></i> {l s='Reply And Open'}</button>
<button class="btn btn-default pull-right" name="submitReplyAsPrivate"><i class="process-icon-lock"></i> {l s='Add Private Message'}</button>
<input type="hidden" name="id_customer_thread" value="{$thread->id|intval}" />
<input type="hidden" name="msg_email" value="{$thread->email}" />
</div>
@@ -180,5 +183,3 @@
</script>
{/block}

View File

@@ -0,0 +1,39 @@
{**
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License version 3.0
* that is bundled with this package in the file LICENSE.md
* It is also available through the world-wide-web at this URL:
* https://opensource.org/license/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 support@qloapps.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade this module to a newer
* versions in the future. If you wish to customize this module for your needs
* please refer to https://store.webkul.com/customisation-guidelines for more information.
*
* @author Webkul IN
* @copyright Since 2010 Webkul
* @license https://opensource.org/license/osl-3-0-php Open Software License version 3.0
*}
{extends file="page_header_toolbar.tpl"}
{block name=pageTitle}
{if isset($display) && $display == 'view'}
<h2 class="page-title">
{if is_array($title)}
{$title|end|strip_tags}
{else}
<span title="{$title}">
{$title|strip_tags|truncate:40}
</span>
{/if}
</h2>
{else}
{$smarty.block.parent}
{/if}
{/block}

View File

@@ -501,13 +501,22 @@
{if count($messages)}
<table class="table">
<thead>
<th><span class="title_box">{l s='Status'}</span></th>
<th class="fixed-width-sm"><span class="title_box">{l s='Status'}</span></th>
<th><span class="title_box">{l s='Message'}</span></th>
<th><span class="title_box">{l s='Sent on'}</span></th>
<th class="fixed-width-lg"><span class="title_box">{l s='Sent on'}</span></th>
</thead>
{foreach $messages AS $message}
<tr>
<td>{$message['status']}</td>
<td>{if $message['status'] == CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN}
{l s='Open'}
{elseif $message['status'] == CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED}
{l s='Closed'}
{elseif $message['status'] == CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1}
{l s='Pending 1'}
{elseif $message['status'] == CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2}
{l s='Pending 2'}
{/if}
</td>
<td>
<a href="index.php?tab=AdminCustomerThreads&amp;id_customer_thread={$message.id_customer_thread}&amp;viewcustomer_thread&amp;token={getAdminToken tab='AdminCustomerThreads'}">
{$message['message']}...

View File

@@ -319,7 +319,7 @@
id_cart: id_cart,
id_customer: id_customer,
message: $(this).val()
},
},
success : function(res)
{
displaySummary(res);
@@ -2305,6 +2305,15 @@
<textarea name="order_message" id="order_message" rows="3" cols="45"></textarea>
</div>
</div>
<div class="form-group col-lg-12">
<span class="col-lg-3"></span>
<p class="checkbox col-lg-6">
<label class="control-label" for="visibility">
<input type="checkbox" name="visibility" id="visibility" value="1" />
{l s='Display Message to Customer?'}
</label>
</p>
</div>
<div class="form-group" {if $order_total <= 0}style="display: none;"{/if}>
{if !$PS_CATALOG_MODE}
<div class="col-lg-9 col-lg-offset-3">

View File

@@ -704,13 +704,13 @@
</div>
{* Order Internal notes *}
{if (sizeof($messages))}
<div class="panel">
{if isset($messages) && $messages}
<div class="panel order-notes">
<div class="panel-heading">
<i class="icon-undo"></i> &nbsp;{l s='Order Notes'}
<i class="icon-undo"></i> &nbsp;{l s='Order Private Notes'}
</div>
<div class="panel-content">
{foreach from=$messages item=message}
{foreach from=$messages item=message name=customerMessage}
<div class="message-body">
<p class="message-item-text">
{$message['message']|escape:'html':'UTF-8'|nl2br}
@@ -727,6 +727,7 @@
{/if}
</p>
</div>
{if !$smarty.foreach.customerMessage.last}<hr/>{/if}
{* {if ($message['is_new_for_me'])}
<a class="new_message" title="{l s='Mark this message as \'viewed\''}" href="{$smarty.server.REQUEST_URI}&amp;token={$smarty.get.token}&amp;messageReaded={$message['id_message']}">
<i class="icon-ok"></i>
@@ -1214,7 +1215,11 @@
<div class="panel">
<div class="panel-heading">
<i class="icon-envelope"></i> &nbsp;{l s='Messages'} <span class="badge">{sizeof($customer_thread_message)}</span>
<a href="{$link->getAdminLink('AdminCustomerThreads')|escape:'html':'UTF-8'}&amp;id_order={$order->id|intval}" class="pull-right">{l s='Show all messages'}</a>
{if $id_customer_thread}
<a href="{$link->getAdminLink('AdminCustomerThreads')|escape:'html':'UTF-8'}&amp;viewcustomer_thread&id_customer_thread={$id_customer_thread|intval}" class="pull-right ">{l s='Show all messages'}</a>
{else}
<a href="{$link->getAdminLink('AdminCustomerThreads')|escape:'html':'UTF-8'}" class="pull-right">{l s='Show all messages'}</a>
{/if}
</div>
<div id="messages">
{if $can_edit}

View File

@@ -194,237 +194,241 @@
</script>
{block name="updatelist"}
{if $new_list_header_design}
<div class="list_action_wrapper">
<div class="row">
{if $fields_optional|count}
<div class="col-xs-4 col-sm-3 col-md-2">
<div class="list_availibility_container">
<button type="button" class="btn btn-default btn-left btn-block dropdown-toggle" data-toggle="dropdown">
<span>{l s='Available Fields'}
<i class="icon-caret-down pull-right"></i>
</button>
<ul id="optional-list-toggle" class="dropdown-menu">
{foreach $fields_optional as $key => $field}
<li>
<label>
<input type="checkbox" name="list_fields_visibility" value="{$key}" {if isset($field['selected']) && $field['selected']}checked="checked"{/if}>
{$field['title']}
</label>
</li>
{/foreach}
</ul>
</div>
</div>
{/if}
{if !$simple_header && $show_filters}
<div class="col-xs-3 col-xs-offset-{if $fields_optional|count}}5{else}9{/if} col-sm-2 col-sm-offset-{if $fields_optional|count}}7{else}10{/if} col-md-1 col-md-offset-{if $fields_optional|count}9{else}11{/if}">
<div class="list_filter_container">
<button type="button" class="btn btn-default btn-block" data-toggle="collapse" data-target="#list_filters_panel">
<i class="icon-sliders"></i>
<span>{l s='Filters'}
</button>
</div>
</div>
{/if}
</div>
<div class="row">
<div class="col-sm-12">
{if $filters_has_value}
<div id="selected_filter_container">
{l s='Filters: '}
<span class="selected_filters">
{foreach $fields_display AS $key => $params}
{if (!isset($params.search) || $params.search) && $params.value != ''}
{if ($params.type == 'date' || $params.type == 'datetime' || $params.type == 'range') && $params.value|is_array}
{assign var="filter_value" value=''}
{foreach $params.value as $value}
{if (isset($value) && ($value !== '' || $value === 0))}
{if !(isset($filter_value) && ($filter_value !== '' || $filter_value === 0))}
{assign var="filter_value" value=$value}
{else}
{assign var="filter_value" value="`$filter_value` - `$value`"}
{block name="list_action_wrapper"}
<div class="list_action_wrapper">
<div class="row">
{block name="list_filter_items"}
{if $fields_optional|count}
<div class="col-xs-4 col-sm-3 col-md-2">
<div class="list_availibility_container">
<button type="button" class="btn btn-default btn-left btn-block dropdown-toggle" data-toggle="dropdown">
<span>{l s='Available Fields'}
<i class="icon-caret-down pull-right"></i>
</button>
<ul id="optional-list-toggle" class="dropdown-menu">
{foreach $fields_optional as $key => $field}
<li>
<label>
<input type="checkbox" name="list_fields_visibility" value="{$key}" {if isset($field['selected']) && $field['selected']}checked="checked"{/if}>
{$field['title']}
</label>
</li>
{/foreach}
</ul>
</div>
</div>
{/if}
{if !$simple_header && $show_filters}
<div class="col-xs-3 col-sm-2 col-md-1 pull-right">
<div class="list_filter_container">
<button type="button" class="btn btn-default btn-block" data-toggle="collapse" data-target="#list_filters_panel">
<i class="icon-sliders"></i>
<span>{l s='Filters'}
</button>
</div>
</div>
{/if}
{/block}
</div>
<div class="row">
<div class="col-sm-12">
{if $filters_has_value}
<div id="selected_filter_container">
{l s='Filters: '}
<span class="selected_filters">
{foreach $fields_display AS $key => $params}
{if (!isset($params.search) || $params.search) && $params.value != ''}
{if ($params.type == 'date' || $params.type == 'datetime' || $params.type == 'range') && $params.value|is_array}
{assign var="filter_value" value=''}
{foreach $params.value as $value}
{if (isset($value) && ($value !== '' || $value === 0))}
{if !(isset($filter_value) && ($filter_value !== '' || $filter_value === 0))}
{assign var="filter_value" value=$value}
{else}
{assign var="filter_value" value="`$filter_value` - `$value`"}
{/if}
{/if}
{/if}
{/foreach}
{if (isset($filter_value) && ($filter_value !== '' || $filter_value === 0))}
<span data-filter_key="{if isset($params.name_date)}{$params.name_date}{else}{$key}{/if}" data-filter_type="{$params.type}">
{$params['title']|escape:'html':'UTF-8'}: <span class="filter_value">{$filter_value|escape:'html':'UTF-8'}</span>
<i class="icon-times"></i>
</span>
{/if}
{elseif $params.type == 'select'}
{if isset($params.multiple) && $params.multiple}
<span data-filter_key="{if isset($params.filter_key)}{$params.filter_key}{else}{$key}{/if}" data-filter_type="{$params.type}" data-multiple="{$params.multiple|intval}" data-operator="{$params.operator}">
{$params['title']|escape:'html':'UTF-8'}:
<span class="filter_value">
{foreach from=$params.value item=option name=foreachInfo}
{$params.list[$option]}{if !$smarty.foreach.foreachInfo.last}{if $params.operator == 'or'} | {else}, {/if}{/if}
{/foreach}
{/foreach}
{if (isset($filter_value) && ($filter_value !== '' || $filter_value === 0))}
<span data-filter_key="{if isset($params.name_date)}{$params.name_date}{else}{$key}{/if}" data-filter_type="{$params.type}">
{$params['title']|escape:'html':'UTF-8'}: <span class="filter_value">{$filter_value|escape:'html':'UTF-8'}</span>
<i class="icon-times"></i>
</span>
{/if}
{elseif $params.type == 'select'}
{if isset($params.multiple) && $params.multiple}
<span data-filter_key="{if isset($params.filter_key)}{$params.filter_key}{else}{$key}{/if}" data-filter_type="{$params.type}" data-multiple="{$params.multiple|intval}" data-operator="{$params.operator}">
{$params['title']|escape:'html':'UTF-8'}:
<span class="filter_value">
{foreach from=$params.value item=option name=foreachInfo}
{$params.list[$option]}{if !$smarty.foreach.foreachInfo.last}{if $params.operator == 'or'} | {else}, {/if}{/if}
{/foreach}
</span>
<i class="icon-times"></i>
</span>
{else}
<span data-filter_key="{if isset($params.filter_key)}{$params.filter_key}{else}{$key}{/if}" data-filter_type="{$params.type}">
{$params['title']|escape:'html':'UTF-8'}: <span class="filter_value">{$params['list'][$params['value']]|replace: '&nbsp;' : ''}</span>
<i class="icon-times"></i>
</span>
{/if}
{elseif $params.type == 'bool'}
<span data-filter_key="{if isset($params.filter_key)}{$params.filter_key}{else}{$key}{/if}" data-filter_type="{$params.type}">
{$params['title']|escape:'html':'UTF-8'}: <span class="filter_value">{if $params['value'] == 1}{l s='Yes'}{else}{l s='No'}{/if}</span>
<i class="icon-times"></i>
</span>
{else}
<span data-filter_key="{if isset($params.filter_key)}{$params.filter_key}{else}{$key}{/if}" data-filter_type="{$params.type}">
{$params['title']|escape:'html':'UTF-8'}: <span class="filter_value">{$params['list'][$params['value']]|replace: '&nbsp;' : ''}</span>
{$params['title']|escape:'html':'UTF-8'}: <span class="filter_value">{$params['value']|escape:'html':'UTF-8'}</span>
<i class="icon-times"></i>
</span>
{/if}
{elseif $params.type == 'bool'}
<span data-filter_key="{if isset($params.filter_key)}{$params.filter_key}{else}{$key}{/if}" data-filter_type="{$params.type}">
{$params['title']|escape:'html':'UTF-8'}: <span class="filter_value">{if $params['value'] == 1}{l s='Yes'}{else}{l s='No'}{/if}</span>
<i class="icon-times"></i>
</span>
{else}
<span data-filter_key="{if isset($params.filter_key)}{$params.filter_key}{else}{$key}{/if}" data-filter_type="{$params.type}">
{$params['title']|escape:'html':'UTF-8'}: <span class="filter_value">{$params['value']|escape:'html':'UTF-8'}</span>
<i class="icon-times"></i>
</span>
{/if}
{/if}
{/foreach}
</span>
</div>
{/if}
<div class="panel collapse" id="list_filters_panel">
<div class="row">
<div class="col-sm-12">
<div class="list_filters">
{foreach $fields_display AS $key => $params}
{if !isset($params.search) || $params.search}
<div class="row">
<label class="col-xs-3" for="filter_input_{$key}">{$params['title']|escape:'html':'UTF-8'}</label>
<div class="col-xs-9">
{if $params.type == 'range'}
{$field_name="`$list_id`Filter_{if isset($params.filter_key)}`$params.filter_key`{else}`$key`{/if}"}
<div class="input_range">
<input type="text" class="filter form-control" name="{$field_name}[0]" placeholder="{l s='From'}" value="{if isset($smarty.post[$field_name][0]) && $smarty.post[$field_name][0]}{$smarty.post[$field_name][0]}{elseif isset($params.value.0)}{$params.value.0}{/if}">
<input type="text" class="filter form-control" name="{$field_name}[1]" placeholder="{l s='To'}" value="{if isset($smarty.post[$field_name][1]) && $smarty.post[$field_name][1]}{$smarty.post[$field_name][1]}{elseif isset($params.value.1)}{$params.value.1}{/if}">
</div>
{elseif $params.type == 'bool'}
{$field_name="`$list_id`Filter_{if isset($params.filter_key)}`$params.filter_key`{else}`$key`{/if}"}
<select id="filter_input_{$key}" class="filter center" name="{$field_name}">
<option value="">-</option>
<option value="1" {if isset($smarty.post.$field_name) && $smarty.post.$field_name == 1} selected="selected"{elseif $params.value == 1} selected="selected" {/if}>{l s='Yes'}</option>
<option value="0" {if isset($smarty.post.$field_name) && $smarty.post.$field_name != '' && $smarty.post.$field_name == 0} selected="selected"{elseif $params.value == 0 && $params.value != ''} selected="selected" {/if}>{l s='No'}</option>
</select>
{elseif $params.type == 'date' || $params.type == 'datetime'}
<div class="date_range">
<div class="input-group center">
<input type="text" class="filter datepicker date-input form-control" id="local_{$params.id_date}_0" name="local_{$params.name_date}[0]" placeholder="{l s='From'}" autocomplete="off"/>
<input type="hidden" id="{$params.id_date}_0" name="{$params.name_date}[0]" value="{if isset($smarty.post[$params.name_date][0]) && $smarty.post[$params.name_date][0]}{$smarty.post[$params.name_date][0]}{elseif isset($params.value.0)}{$params.value.0}{/if}">
<span class="input-group-addon">
<i class="icon-calendar"></i>
</span>
{/foreach}
</span>
</div>
{/if}
<div class="panel collapse" id="list_filters_panel">
<div class="row">
<div class="col-sm-12">
<div class="list_filters">
{foreach $fields_display AS $key => $params}
{if !isset($params.search) || $params.search}
<div class="row">
<label class="col-xs-3" for="filter_input_{$key}">{$params['title']|escape:'html':'UTF-8'}</label>
<div class="col-xs-9">
{if $params.type == 'range'}
{$field_name="`$list_id`Filter_{if isset($params.filter_key)}`$params.filter_key`{else}`$key`{/if}"}
<div class="input_range">
<input type="text" class="filter form-control" name="{$field_name}[0]" placeholder="{l s='From'}" value="{if isset($smarty.post[$field_name][0]) && $smarty.post[$field_name][0]}{$smarty.post[$field_name][0]}{elseif isset($params.value.0)}{$params.value.0}{/if}">
<input type="text" class="filter form-control" name="{$field_name}[1]" placeholder="{l s='To'}" value="{if isset($smarty.post[$field_name][1]) && $smarty.post[$field_name][1]}{$smarty.post[$field_name][1]}{elseif isset($params.value.1)}{$params.value.1}{/if}">
</div>
<div class="input-group center">
<input type="text" class="filter datepicker date-input form-control" id="local_{$params.id_date}_1" name="local_{$params.name_date}[1]" placeholder="{l s='To'}" autocomplete="off"/>
<input type="hidden" id="{$params.id_date}_1" name="{$params.name_date}[1]" value="{if isset($smarty.post[$params.name_date][1]) && $smarty.post[$params.name_date][1]}{$smarty.post[$params.name_date][1]}{elseif isset($params.value.1)}{$params.value.1}{/if}">
<span class="input-group-addon">
<i class="icon-calendar"></i>
</span>
</div>
<script>
$(function() {
var dateStart = parseDate($("#{$params.id_date}_0").val());
var dateEnd = parseDate($("#{$params.id_date}_1").val());
$("#local_{$params.id_date}_0").datepicker("option", "altField", "#{$params.id_date}_0");
$("#local_{$params.id_date}_1").datepicker("option", "altField", "#{$params.id_date}_1");
if (dateStart !== null){
$("#local_{$params.id_date}_0").datepicker("setDate", dateStart);
}
if (dateEnd !== null){
$("#local_{$params.id_date}_1").datepicker("setDate", dateEnd);
}
$('#local_{$params.id_date}_0').datepicker('option', {
prevText: '',
nextText: '',
dateFormat: 'yy-mm-dd',
onClose: function() {
let dateFrom = $('#local_{$params.id_date}_0').val().trim();
let dateTo = $('#local_{$params.id_date}_1').val().trim();
if ((dateFrom && dateTo) && (dateFrom >= dateTo)) {
let objDateToMin = $.datepicker.parseDate('yy-mm-dd', dateFrom);
objDateToMin.setDate(objDateToMin.getDate());
$('#local_{$params.id_date}_1').datepicker('option', 'minDate', objDateToMin);
}
},
});
$('#local_{$params.id_date}_1').datepicker('option', {
prevText: '',
nextText: '',
dateFormat: 'yy-mm-dd',
beforeShow: function() {
let dateFrom = $('#local_{$params.id_date}_0').val().trim();
if (typeof dateFrom != 'undefined' && dateFrom != '') {
let objDateToMin = $.datepicker.parseDate('yy-mm-dd', dateFrom);
objDateToMin.setDate(objDateToMin.getDate());
$('#local_{$params.id_date}_1').datepicker('option', 'minDate', objDateToMin);
}
},
});
});
</script>
</div>
{elseif $params.type == 'select'}
{$field_name="`$list_id`Filter_`$params.filter_key`"}
{if isset($params.multiple) && $params.multiple}
<select id="filter_input_{$key}" class="filter{if isset($params.align) && $params.align == 'center'}center{/if} select_multiple_{$params.operator} chosen" multiple name="{$field_name}[]" {if isset($params.width)} style="width:{$params.width}px"{/if}>
{if isset($params.list) && is_array($params.list)}
{foreach $params.list AS $option_value => $option_display}
<option value="{$option_value}" {if isset($smarty.post.$field_name) && $smarty.post.$field_name && in_array($option_value, $smarty.post.$field_name)} selected="selected"{elseif isset($params.value) && $params.value}{if in_array($option_value, $params.value)} selected="selected"{/if}{/if}>{$option_display}</option>
{/foreach}
{/if}
{elseif $params.type == 'bool'}
{$field_name="`$list_id`Filter_{if isset($params.filter_key)}`$params.filter_key`{else}`$key`{/if}"}
<select id="filter_input_{$key}" class="filter center" name="{$field_name}">
<option value="">-</option>
<option value="1" {if isset($smarty.post.$field_name) && $smarty.post.$field_name == 1} selected="selected"{elseif $params.value == 1} selected="selected" {/if}>{l s='Yes'}</option>
<option value="0" {if isset($smarty.post.$field_name) && $smarty.post.$field_name != '' && $smarty.post.$field_name == 0} selected="selected"{elseif $params.value == 0 && $params.value != ''} selected="selected" {/if}>{l s='No'}</option>
</select>
{else}
{if isset($params.filter_key)}
<select id="filter_input_{$key}" class="filter{if isset($params.align) && $params.align == 'center'}center{/if} {if isset($params.class)}{$params.class}{/if}" name="{$field_name}" {if isset($params.width)} style="width:{$params.width}px"{/if}>
<option value="" {if $params.value == ''} selected="selected" {/if}>-</option>
{elseif $params.type == 'date' || $params.type == 'datetime'}
<div class="date_range">
<div class="input-group center">
<input type="text" class="filter datepicker date-input form-control" id="local_{$params.id_date}_0" name="local_{$params.name_date}[0]" placeholder="{l s='From'}" autocomplete="off"/>
<input type="hidden" id="{$params.id_date}_0" name="{$params.name_date}[0]" value="{if isset($smarty.post[$params.name_date][0]) && $smarty.post[$params.name_date][0]}{$smarty.post[$params.name_date][0]}{elseif isset($params.value.0)}{$params.value.0}{/if}">
<span class="input-group-addon">
<i class="icon-calendar"></i>
</span>
</div>
<div class="input-group center">
<input type="text" class="filter datepicker date-input form-control" id="local_{$params.id_date}_1" name="local_{$params.name_date}[1]" placeholder="{l s='To'}" autocomplete="off"/>
<input type="hidden" id="{$params.id_date}_1" name="{$params.name_date}[1]" value="{if isset($smarty.post[$params.name_date][1]) && $smarty.post[$params.name_date][1]}{$smarty.post[$params.name_date][1]}{elseif isset($params.value.1)}{$params.value.1}{/if}">
<span class="input-group-addon">
<i class="icon-calendar"></i>
</span>
</div>
<script>
$(function() {
var dateStart = parseDate($("#{$params.id_date}_0").val());
var dateEnd = parseDate($("#{$params.id_date}_1").val());
$("#local_{$params.id_date}_0").datepicker("option", "altField", "#{$params.id_date}_0");
$("#local_{$params.id_date}_1").datepicker("option", "altField", "#{$params.id_date}_1");
if (dateStart !== null){
$("#local_{$params.id_date}_0").datepicker("setDate", dateStart);
}
if (dateEnd !== null){
$("#local_{$params.id_date}_1").datepicker("setDate", dateEnd);
}
$('#local_{$params.id_date}_0').datepicker('option', {
prevText: '',
nextText: '',
dateFormat: 'yy-mm-dd',
onClose: function() {
let dateFrom = $('#local_{$params.id_date}_0').val().trim();
let dateTo = $('#local_{$params.id_date}_1').val().trim();
if ((dateFrom && dateTo) && (dateFrom >= dateTo)) {
let objDateToMin = $.datepicker.parseDate('yy-mm-dd', dateFrom);
objDateToMin.setDate(objDateToMin.getDate());
$('#local_{$params.id_date}_1').datepicker('option', 'minDate', objDateToMin);
}
},
});
$('#local_{$params.id_date}_1').datepicker('option', {
prevText: '',
nextText: '',
dateFormat: 'yy-mm-dd',
beforeShow: function() {
let dateFrom = $('#local_{$params.id_date}_0').val().trim();
if (typeof dateFrom != 'undefined' && dateFrom != '') {
let objDateToMin = $.datepicker.parseDate('yy-mm-dd', dateFrom);
objDateToMin.setDate(objDateToMin.getDate());
$('#local_{$params.id_date}_1').datepicker('option', 'minDate', objDateToMin);
}
},
});
});
</script>
</div>
{elseif $params.type == 'select'}
{$field_name="`$list_id`Filter_`$params.filter_key`"}
{if isset($params.multiple) && $params.multiple}
<select id="filter_input_{$key}" class="filter{if isset($params.align) && $params.align == 'center'}center{/if} select_multiple_{$params.operator} chosen" multiple name="{$field_name}[]" {if isset($params.width)} style="width:{$params.width}px"{/if}>
{if isset($params.list) && is_array($params.list)}
{foreach $params.list AS $option_value => $option_display}
<option value="{$option_value}" {if isset($smarty.post.$field_name) && $smarty.post.$field_name != '' && $smarty.post.$field_name == $option_value} selected="selected"{elseif (string)$option_display === (string)$params.value || (string)$option_value === (string)$params.value} selected="selected"{/if}>{$option_display}</option>
<option value="{$option_value}" {if isset($smarty.post.$field_name) && $smarty.post.$field_name && in_array($option_value, $smarty.post.$field_name)} selected="selected"{elseif isset($params.value) && $params.value}{if in_array($option_value, $params.value)} selected="selected"{/if}{/if}>{$option_display}</option>
{/foreach}
{/if}
</select>
{else}
{if isset($params.filter_key)}
<select id="filter_input_{$key}" class="filter{if isset($params.align) && $params.align == 'center'}center{/if} {if isset($params.class)}{$params.class}{/if}" name="{$field_name}" {if isset($params.width)} style="width:{$params.width}px"{/if}>
<option value="" {if $params.value == ''} selected="selected" {/if}>-</option>
{if isset($params.list) && is_array($params.list)}
{foreach $params.list AS $option_value => $option_display}
<option value="{$option_value}" {if isset($smarty.post.$field_name) && $smarty.post.$field_name != '' && $smarty.post.$field_name == $option_value} selected="selected"{elseif (string)$option_display === (string)$params.value || (string)$option_value === (string)$params.value} selected="selected"{/if}>{$option_display}</option>
{/foreach}
{/if}
</select>
{/if}
{/if}
{else}
{$field_name="`$list_id`Filter_{if isset($params.filter_key)}`$params.filter_key`{else}`$key`{/if}"}
<input type="text" id="filter_input_{$key}" class="filter" name="{$field_name}" value="{if isset($smarty.post.$field_name) && $smarty.post.$field_name}{$smarty.post.$field_name}{else}{$params.value|escape:'html':'UTF-8'}{/if}" {if isset($params.width) && $params.width != 'auto'} style="width:{$params.width}px"{/if} />
{/if}
{else}
{$field_name="`$list_id`Filter_{if isset($params.filter_key)}`$params.filter_key`{else}`$key`{/if}"}
<input type="text" id="filter_input_{$key}" class="filter" name="{$field_name}" value="{if isset($smarty.post.$field_name) && $smarty.post.$field_name}{$smarty.post.$field_name}{else}{$params.value|escape:'html':'UTF-8'}{/if}" {if isset($params.width) && $params.width != 'auto'} style="width:{$params.width}px"{/if} />
{/if}
</div>
</div>
{/if}
{/foreach}
{if $has_actions || $show_filters}
<div class="actions">
<hr>
{if $show_filters}
<span class="pull-right">
<button type="submit" id="submitFilterButton{$list_id}" name="submitFilter" class="btn btn-default" data-list-id="{$list_id}">
<i class="icon-search"></i> {l s='Search'}
</button>
{if $filters_has_value}
<button type="submit" name="submitReset{$list_id}" class="btn btn-warning">
<i class="icon-eraser"></i> {l s='Reset'}
</button>
{/if}
</span>
{/if}
</div>
{/if}
{/foreach}
{if $has_actions || $show_filters}
<div class="actions">
<hr>
{if $show_filters}
<span class="pull-right">
<button type="submit" id="submitFilterButton{$list_id}" name="submitFilter" class="btn btn-default" data-list-id="{$list_id}">
<i class="icon-search"></i> {l s='Search'}
</button>
{if $filters_has_value}
<button type="submit" name="submitReset{$list_id}" class="btn btn-warning">
<i class="icon-eraser"></i> {l s='Reset'}
</button>
{/if}
</span>
{/if}
</div>
{/if}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/block}
{/if}
{/block}
<div class="panel col-lg-12">

View File

@@ -32,6 +32,7 @@ class CustomerMessageCore extends ObjectModel
public $message;
public $file_name;
public $ip_address;
public $id_product;
public $user_agent;
public $private;
public $date_add;
@@ -51,6 +52,7 @@ class CustomerMessageCore extends ObjectModel
'id_employee' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_customer_thread' => array('type' => self::TYPE_INT),
'ip_address' => array('type' => self::TYPE_STRING, 'validate' => 'isIp2Long', 'size' => 15),
'id_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'message' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml', 'required' => true, 'size' => 16777216),
'file_name' => array('type' => self::TYPE_STRING),
'user_agent' => array('type' => self::TYPE_STRING),
@@ -72,7 +74,7 @@ class CustomerMessageCore extends ObjectModel
),
);
public static function getMessagesByOrderId($id_order, $private = true)
public static function getMessagesByOrderId($id_order, $private = null, $limit = false)
{
return Db::getInstance()->executeS('
SELECT cm.*,
@@ -89,29 +91,33 @@ class CustomerMessageCore extends ObjectModel
LEFT OUTER JOIN `'._DB_PREFIX_.'employee` e
ON e.`id_employee` = cm.`id_employee`
WHERE ct.id_order = '.(int)$id_order.'
'.(!$private ? 'AND cm.`private` = 0' : '').'
'.(!is_null($private) ? 'AND cm.`private` = '.(int) $private : '').'
GROUP BY cm.id_customer_message
ORDER BY cm.date_add DESC
');
ORDER BY cm.date_add DESC'.
(($limit) ? ' LIMIT '.(int) $limit: ' ')
);
}
public static function getTotalCustomerMessages($where = null)
{
$sql = 'SELECT COUNT(*)
FROM '._DB_PREFIX_.'customer_message cm
LEFT JOIN `'._DB_PREFIX_.'customer_thread` ct ON (cm.`id_customer_thread` = ct.`id_customer_thread`) ';
if (is_null($where)) {
return (int)Db::getInstance()->getValue('
SELECT COUNT(*)
FROM '._DB_PREFIX_.'customer_message
LEFT JOIN `'._DB_PREFIX_.'customer_thread` ct ON (cm.`id_customer_thread` = ct.`id_customer_thread`)
WHERE 1'.Shop::addSqlRestriction()
);
} else {
return (int)Db::getInstance()->getValue('
SELECT COUNT(*)
FROM '._DB_PREFIX_.'customer_message cm
LEFT JOIN `'._DB_PREFIX_.'customer_thread` ct ON (cm.`id_customer_thread` = ct.`id_customer_thread`)
WHERE '.$where.Shop::addSqlRestriction()
$where = ' 1 ';
}
$employee = Context::getContext()->employee;
if (!$employee->isSuperAdmin()) {
$where .= (($acsHtls = HotelBranchInformation::getProfileAccessedHotels($employee->id_profile, 1, 1)) ?
' AND ct.`id_order` IN (SELECT `id_order` FROM `'._DB_PREFIX_.'htl_booking_detail` hbd WHERE `id_hotel` IN ('.implode(',', $acsHtls).')) ' :
' AND ct.`id_order` = 0 '
);
}
return (int)Db::getInstance()->getValue(
$sql.' WHERE '.$where.Shop::addSqlRestriction()
);
}
public function delete()

View File

@@ -30,15 +30,23 @@ class CustomerThreadCore extends ObjectModel
public $id_shop;
public $id_lang;
public $id_contact;
public $id_employee;
public $id_customer;
public $id_order;
public $id_product;
public $user_name;
public $phone;
public $subject;
public $status;
public $email;
public $token;
public $date_add;
public $date_upd;
const QLO_CUSTOMER_THREAD_STATUS_OPEN = 1;
const QLO_CUSTOMER_THREAD_STATUS_PENDING1 = 2;
const QLO_CUSTOMER_THREAD_STATUS_PENDING2 = 3;
const QLO_CUSTOMER_THREAD_STATUS_CLOSED = 4;
/**
* @see ObjectModel::$definition
*/
@@ -48,10 +56,13 @@ class CustomerThreadCore extends ObjectModel
'fields' => array(
'id_lang' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'id_contact' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true),
'user_name' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName'),
'phone' => array('type' => self::TYPE_STRING, 'validate' => 'isPhoneNumber', 'size' => 32),
'subject' => array('type' => self::TYPE_STRING, 'validate' => 'isCleanHtml'),
'id_shop' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_customer' =>array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_employee' =>array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_order' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'id_product' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId'),
'email' => array('type' => self::TYPE_STRING, 'validate' => 'isEmail', 'size' => 254),
'token' => array('type' => self::TYPE_STRING, 'validate' => 'isGenericName', 'required' => true),
'status' => array('type' => self::TYPE_STRING),
@@ -158,15 +169,38 @@ class CustomerThreadCore extends ObjectModel
return Db::getInstance()->executeS($sql);
}
public static function getIdCustomerThreadByEmailAndIdOrder($email, $id_order, $id_contact = false)
public static function getIdCustomerThreadByEmailAndIdOrder($email, $id_order, $id_contact = false, $query = false)
{
return Db::getInstance()->getValue('
SELECT a.`id_customer_thread`
FROM `'._DB_PREFIX_.'customer_thread` a
WHERE a.`email` = \''.pSQL($email).'\'
AND a.`id_shop` = '.(int)Context::getContext()->shop->id.'
AND a.`id_order` = '.(int)$id_order.' '.
(($id_contact) ? ' AND a.`id_contact`='.(int) $id_contact : ' ').
(($query) ? $query : ' ')
);
}
public static function getIdCustomerThreadByIdOrder($idOrder)
{
return Db::getInstance()->getValue('
SELECT cm.id_customer_thread
FROM '._DB_PREFIX_.'customer_thread cm
WHERE cm.email = \''.pSQL($email).'\'
AND cm.id_shop = '.(int)Context::getContext()->shop->id.'
AND cm.id_order = '.(int)$id_order.' '.
(($id_contact) ? ' AND cm.id_contact='.(int) $id_contact : ' ')
WHERE cm.id_shop = '.(int)Context::getContext()->shop->id.'
AND cm.id_order = '.(int)$idOrder
);
}
public static function getIdCustomerThreadByToken($token)
{
return Db::getInstance()->getValue('
SELECT cm.id_customer_thread
FROM '._DB_PREFIX_.'customer_thread cm
WHERE cm.id_shop = '.(int)Context::getContext()->shop->id.'
AND token = \''.pSQL($token).'\''
);
}
@@ -176,7 +210,8 @@ class CustomerThreadCore extends ObjectModel
SELECT cl.*, COUNT(*) as total, (
SELECT id_customer_thread
FROM '._DB_PREFIX_.'customer_thread ct2
WHERE status = "open" AND ct.id_contact = ct2.id_contact
WHERE status = '.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN.'
AND ct.id_contact = ct2.id_contact
'.Shop::addSqlRestriction().'
ORDER BY date_upd ASC
LIMIT 1
@@ -184,7 +219,7 @@ class CustomerThreadCore extends ObjectModel
FROM '._DB_PREFIX_.'customer_thread ct
LEFT JOIN '._DB_PREFIX_.'contact_lang cl
ON (cl.id_contact = ct.id_contact AND cl.id_lang = '.(int)Context::getContext()->language->id.')
WHERE ct.status = "open"
WHERE ct.status = '.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN.'
AND ct.id_contact IS NOT NULL
AND cl.id_contact IS NOT NULL
'.Shop::addSqlRestriction().'
@@ -194,19 +229,19 @@ class CustomerThreadCore extends ObjectModel
public static function getTotalCustomerThreads($where = null)
{
$sql = 'SELECT COUNT(*) FROM '._DB_PREFIX_.'customer_thread ';
if (is_null($where)) {
return (int)Db::getInstance()->getValue('
SELECT COUNT(*)
FROM '._DB_PREFIX_.'customer_thread
WHERE 1 '.Shop::addSqlRestriction()
);
} else {
return (int)Db::getInstance()->getValue('
SELECT COUNT(*)
FROM '._DB_PREFIX_.'customer_thread
WHERE '.$where.Shop::addSqlRestriction()
$where = ' 1 ';
}
$employee = Context::getContext()->employee;
if (!$employee->isSuperAdmin()) {
$where .= (($acsHtls = HotelBranchInformation::getProfileAccessedHotels($employee->id_profile, 1, 1)) ?
' AND `id_order` IN (SELECT `id_order` FROM `'._DB_PREFIX_.'htl_booking_detail` hbd WHERE `id_hotel` IN ('.implode(',', $acsHtls).')) ' :
' AND `id_order` = 0 '
);
}
return (int)Db::getInstance()->getValue($sql.' WHERE '.$where.Shop::addSqlRestriction());
}
public static function getMessageCustomerThreads($id_customer_thread)
@@ -234,7 +269,7 @@ class CustomerThreadCore extends ObjectModel
return Db::getInstance()->getValue('
SELECT id_customer_thread
FROM '._DB_PREFIX_.'customer_thread ct
WHERE ct.status = "open"
WHERE ct.status = '.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN.'
AND ct.date_upd = (
SELECT date_add FROM '._DB_PREFIX_.'customer_message
WHERE (id_employee IS NULL OR id_employee = 0)

View File

@@ -88,7 +88,7 @@ class MessageCore extends ObjectModel
* @param bool $private return WITH private messages
* @return array Messages
*/
public static function getMessagesByOrderId($id_order, $private = false, Context $context = null)
public static function getMessagesByOrderId($id_order, $private = null, Context $context = null)
{
if (!Validate::isBool($private)) {
die(Tools::displayError());
@@ -108,7 +108,7 @@ class MessageCore extends ObjectModel
AND mr.`id_employee` = '.(isset($context->employee) ? (int)$context->employee->id : '\'\'').'
LEFT OUTER JOIN `'._DB_PREFIX_.'employee` e ON e.`id_employee` = m.`id_employee`
WHERE id_order = '.(int)$id_order.'
'.(!$private ? ' AND m.`private` = 0' : '').'
'.(!is_null($private) ? ' AND m.`private` = '.(int) $private : '').'
GROUP BY m.id_message
ORDER BY m.date_add DESC
');

View File

@@ -716,7 +716,7 @@ abstract class PaymentModuleCore extends Module
$customer_thread->id_order = (int)$order->id;
$customer_thread->id_lang = (int)$this->context->language->id;
$customer_thread->email = $this->context->customer->email;
$customer_thread->status = 'open';
$customer_thread->status = CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN;
$customer_thread->token = Tools::passwdGen(12);
$customer_thread->add();
@@ -724,7 +724,7 @@ abstract class PaymentModuleCore extends Module
$customer_message->id_customer_thread = $customer_thread->id;
$customer_message->id_employee = 0;
$customer_message->message = $update_message->message;
$customer_message->private = 0;
$customer_message->private = !Tools::getValue('visibility');
if (!$customer_message->add()) {
$this->errors[] = Tools::displayError('An error occurred while saving message');

View File

@@ -1063,11 +1063,12 @@ class ProductCore extends ObjectModel
}
if (!$this->booking_product) {
$objHotelRoomTypeBedType = new HotelRoomTypeBedType();
$objHotelRoomTypeBedType->deleteRoomTypeBedTypes(false, $this->id);
if (!$this->deleteServiceInfo()) {
return false;
}
} else {
$objHotelRoomTypeBedType = new HotelRoomTypeBedType();
$objHotelRoomTypeBedType->deleteRoomTypeBedTypes(false, $this->id);
}
Hook::exec('actionProductDelete', array('id_product' => (int)$this->id, 'product' => $this));

View File

@@ -2679,7 +2679,7 @@ class WebserviceSpecificManagementBookingsCore Extends ObjectModel implements We
$message = strip_tags($remark, '<br>');
$saveMessage = true;
$idCart = Cart::getCartIdByOrderId($objOrder->id);
if ($customerMessages = Message::getMessagesByOrderId($objOrder->id, true)) {
if ($customerMessages = Message::getMessagesByOrderId($objOrder->id, false)) {
foreach ($customerMessages as $customerMessage) {
if ($customerMessage['message'] == $message) {
$saveMessage = false;
@@ -3499,7 +3499,7 @@ class WebserviceSpecificManagementBookingsCore Extends ObjectModel implements We
}
$params['associations']['remarks'] = array();
if ($customerMessages = Message::getMessagesByOrderId($objOrder->id, true)) {
if ($customerMessages = Message::getMessagesByOrderId($objOrder->id)) {
foreach ($customerMessages as $customerMessage) {
$message = $customerMessage['message'];
$params['associations']['remarks'][] = $message;

View File

@@ -51,10 +51,22 @@ class AdminCustomerThreadsControllerCore extends AdminController
}
$icon_array = array(
'open' => array('class' => 'icon-circle text-success', 'alt' => $this->l('Open')),
'closed' => array('class' => 'icon-circle text-danger', 'alt' => $this->l('Closed')),
'pending1' => array('class' => 'icon-circle text-warning', 'alt' => $this->l('Pending 1')),
'pending2' => array('class' => 'icon-circle text-warning', 'alt' => $this->l('Pending 2')),
CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN => array(
'class' => 'icon-circle text-success',
'alt' => $this->l('Open')
),
CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED => array(
'class' => 'icon-circle text-danger',
'alt' => $this->l('Closed')
),
CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1 => array(
'class' => 'icon-circle text-warning',
'alt' => $this->l('Pending 1')
),
CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2 => array(
'class' => 'icon-circle text-warning',
'alt' => $this->l('Pending 2')
),
);
$status_array = array();
@@ -67,7 +79,9 @@ class AdminCustomerThreadsControllerCore extends AdminController
$this->access_join = ' LEFT JOIN '._DB_PREFIX_.'orders ord ON (a.id_order = ord.id_order)';
$this->access_join .= ' LEFT JOIN '._DB_PREFIX_.'htl_booking_detail hbd ON (hbd.id_order = ord.id_order)';
if ($acsHtls = HotelBranchInformation::getProfileAccessedHotels($this->context->employee->id_profile, 1, 1)) {
$this->access_where = ' AND IF(a.`id_order`, hbd.`id_hotel` IN ('.implode(',', $acsHtls).'), 1)';
$this->access_where = ' WHERE IF(a.`id_order`, hbd.`id_hotel` IN ('.implode(',', $acsHtls).'), 1)';
} else {
$this->access_where = ' WHERE a.`id_order` = 0';
}
$this->fields_list = array(
@@ -79,25 +93,41 @@ class AdminCustomerThreadsControllerCore extends AdminController
'customer' => array(
'title' => $this->l('Customer'),
'filter_key' => 'customer',
'callback' => 'getCustomerLink',
'tmpTableFilter' => true,
),
'email' => array(
'title' => $this->l('Email'),
'filter_key' => 'a!email',
),
'phone' => array(
'title' => $this->l('Phone'),
'filter_key' => 'a!phone',
'optional' => true,
'visible_default' => true,
),
'contact' => array(
'title' => $this->l('Type'),
'title' => $this->l('Contact'),
'type' => 'select',
'list' => $contact_array,
'filter_key' => 'cl!id_contact',
'filter_type' => 'int',
),
'subject' => array(
'title' => $this->l('Title'),
'filter_key' => 'a!subject',
'tmpTableFilter' => true,
'maxlength' => 30,
'optional' => true,
'visible_default' => true,
),
'language' => array(
'title' => $this->l('Language'),
'type' => 'select',
'list' => $language_array,
'filter_key' => 'l!id_lang',
'filter_type' => 'int',
'optional' => true,
),
'status' => array(
'title' => $this->l('Status'),
@@ -112,29 +142,42 @@ class AdminCustomerThreadsControllerCore extends AdminController
'title' => $this->l('Employee'),
'filter_key' => 'employee',
'tmpTableFilter' => true,
'optional' => true,
'hint' => $this->l('The first employee to reply becomes responsible. They can then forward the message to another employee if needed.')
),
'last_employee' => array(
'title' => $this->l('Last Reply by'),
'filter_key' => 'employee',
'tmpTableFilter' => true,
'optional' => true,
),
'messages' => array(
'title' => $this->l('Messages'),
'filter_key' => 'messages',
'tmpTableFilter' => true,
'maxlength' => 40,
'maxlength' => 30,
'optional' => true,
),
'private' => array(
'title' => $this->l('Private'),
'type' => 'select',
'filter_key' => 'private',
'has_private' => array(
'title' => $this->l('Has Private'),
'type' => 'bool',
'havingFilter' => true,
'filter_key' => 'has_private',
'align' => 'center',
'cast' => 'intval',
'callback' => 'printOptinIcon',
'list' => array(
'0' => $this->l('No'),
'1' => $this->l('Yes')
)
),
'date_upd' => array(
'title' => $this->l('Last message'),
'havingFilter' => true,
'type' => 'datetime',
'optional' => true,
),
'id_order' => array(
'title' => $this->l('ID Order'),
'filter_key' => 'a!id_order',
'optional' => true,
'visible_default' => false,
'displayed' => false,
),
);
@@ -147,22 +190,71 @@ class AdminCustomerThreadsControllerCore extends AdminController
);
$this->shopLinkType = 'shop';
$arr = array();
if ($contacts = Contact::getContacts($this->context->language->id)) {
foreach ($contacts as $contact) {
$arr[] = array('email_message' => $contact['id_contact'], 'name' => $contact['name']);
}
}
$this->fields_options = array(
'contact' => array(
'title' => $this->l('Contact options'),
'fields' => array(
'PS_CUSTOMER_SERVICE_FILE_UPLOAD' => array(
'title' => $this->l('Allow file uploading'),
'hint' => $this->l('Allow customers to upload files using the contact page.'),
'type' => 'bool'
),
'title' => $this->l('Contact options'),
'fields' => array(
'PS_CUSTOMER_SERVICE_SIGNATURE' => array(
'title' => $this->l('Default message'),
'hint' => $this->l('Please fill out the message fields that appear by default when you answer a thread on the customer service page.'),
'type' => 'textareaLang',
'lang' => true
)
'title' => $this->l('Default message'),
'hint' => $this->l('Please fill out the message fields that appear by default when you answer a thread on the customer service page.'),
'type' => 'textareaLang',
'lang' => true
),
'PS_MAIL_EMAIL_MESSAGE' => array(
'title' => $this->l('Send messages from order page to'),
'desc' => $this->l('Where customers send messages from the order page.'),
'validation' => 'isUnsignedId',
'type' => 'select',
'cast' => 'intval',
'identifier' => 'email_message',
'list' => $arr
),
'PS_CUSTOMER_SERVICE_DISPLAY_CONTACT' => array(
'title' => $this->l('Select contact on contact page'),
'hint' => $this->l('Allow guest to select the contact while sending message from the contact page.'),
'type' => 'bool'
),
'PS_CUSTOMER_SERVICE_CONTACT' => array(
'title' => $this->l('Send messages from contact page to'),
'desc' => $this->l('Where customers send messages from the Contact page.'),
'validation' => 'isUnsignedId',
'type' => 'select',
'cast' => 'intval',
'identifier' => 'email_message',
'list' => $arr
),
'PS_CUSTOMER_SERVICE_DISPLAY_NAME' => array(
'title' => $this->l('Display name on contact page'),
'hint' => $this->l('Display name field on contact page.'),
'type' => 'bool',
),
'PS_CUSTOMER_SERVICE_REQUIRED_NAME' => array(
'title' => $this->l('Set name as required'),
'hint' => $this->l('Set name as required while submitting message from the contact page.'),
'type' => 'bool'
),
'PS_CUSTOMER_SERVICE_DISPLAY_PHONE' => array(
'title' => $this->l('Display phone on contact page'),
'hint' => $this->l('Display phone field on contact page.'),
'type' => 'bool',
),
'PS_CUSTOMER_SERVICE_REQUIRED_PHONE' => array(
'title' => $this->l('Set phone as required'),
'hint' => $this->l('Set phone as required while submitting message from the contact page.'),
'type' => 'bool'
),
'PS_CUSTOMER_SERVICE_FILE_UPLOAD' => array(
'title' => $this->l('Allow file uploading'),
'hint' => $this->l('Allow customers to upload files using the contact page.'),
'type' => 'bool'
),
),
'submit' => array(
'title' => $this->l('Save'),
@@ -242,6 +334,25 @@ class AdminCustomerThreadsControllerCore extends AdminController
);
parent::__construct();
$this->_new_list_header_design = true;
$this->list_no_link = true;
}
public function getCustomerLink($customer, $tr)
{
if ($tr['id_customer']) {
return '<a href="'.$this->context->link->getAdminLink('AdminCustomers').'&viewcustomer&id_customer='.$tr['id_customer'].'">'.$customer.' (#'.$tr['id_customer'].')</a>';
}
return $customer;
}
public function setMedia()
{
parent::setMedia();
if (!$this->display || $this->display == 'list') {
$this->addJS(_PS_JS_DIR_.'admin/customer_thread.js');
}
}
public function renderList()
@@ -253,7 +364,8 @@ class AdminCustomerThreadsControllerCore extends AdminController
$this->addRowAction('delete');
$this->_select = '
CONCAT(c.`firstname`," ",c.`lastname`) as customer, cl.`name` as contact, l.`name` as language, group_concat(message) as messages, cm.private,
IF(a.`user_name`="", CONCAT(c.`firstname`," ",c.`lastname`), a.`user_name`) AS customer, cl.`name` as contact, l.`name` as language, group_concat(message) as messages,
cm.private, IF(COUNT(CASE WHEN cm.`private` = 1 THEN 1 END), 1, 0) AS `has_private`,
(
SELECT IFNULL(CONCAT(LEFT(e.`firstname`, 1),". ",e.`lastname`), "--")
FROM `'._DB_PREFIX_.'customer_message` cm2
@@ -262,9 +374,12 @@ class AdminCustomerThreadsControllerCore extends AdminController
WHERE cm2.id_employee > 0
AND cm2.`id_customer_thread` = a.`id_customer_thread`
ORDER BY cm2.`date_add` DESC LIMIT 1
) as employee';
) AS last_employee,
IFNULL(CONCAT(LEFT(emp.`firstname`, 1),". ",emp.`lastname`), "--") AS employee';
$this->_join = '
LEFT JOIN `'._DB_PREFIX_.'employee` emp
ON emp.`id_employee` = a.`id_employee`
LEFT JOIN `'._DB_PREFIX_.'customer` c
ON c.`id_customer` = a.`id_customer`
LEFT JOIN `'._DB_PREFIX_.'customer_message` cm
@@ -274,33 +389,10 @@ class AdminCustomerThreadsControllerCore extends AdminController
LEFT JOIN `'._DB_PREFIX_.'contact_lang` cl
ON (cl.`id_contact` = a.`id_contact` AND cl.`id_lang` = '.(int)$this->context->language->id.')';
if ($id_order = Tools::getValue('id_order')) {
$this->_where .= ' AND id_order = '.(int)$id_order;
}
$this->_group = 'GROUP BY cm.id_customer_thread';
$this->_orderBy = 'id_customer_thread';
$this->_orderWay = 'DESC';
$contacts = CustomerThread::getContacts();
$categories = Contact::getCategoriesContacts();
$params = array(
$this->l('Total threads') => $all = CustomerThread::getTotalCustomerThreads(),
$this->l('Threads pending') => $pending = CustomerThread::getTotalCustomerThreads('status LIKE "%pending%"'),
$this->l('Total number of customer messages') => CustomerMessage::getTotalCustomerMessages('id_employee = 0'),
$this->l('Total number of employee messages') => CustomerMessage::getTotalCustomerMessages('id_employee != 0'),
$this->l('Unread threads') => $unread = CustomerThread::getTotalCustomerThreads('status = "open"'),
$this->l('Closed threads') => $all - ($unread + $pending)
);
$this->tpl_list_vars = array(
'contacts' => $contacts,
'categories' => $categories,
'params' => $params
);
return parent::renderList();
}
@@ -323,6 +415,13 @@ class AdminCustomerThreadsControllerCore extends AdminController
if (Tools::isSubmit('submitOptionsCustomerService')) {
unset($this->fields_options['general']);
$this->processUpdateOptions();
if (!Tools::getValue('PS_CUSTOMER_SERVICE_DISPLAY_NAME')) {
Configuration::updateValue('PS_CUSTOMER_SERVICE_REQUIRED_NAME', 0);
}
if (!Tools::getValue('PS_CUSTOMER_SERVICE_DISPLAY_PHONE')) {
Configuration::updateValue('PS_CUSTOMER_SERVICE_REQUIRED_PHONE', 0);
}
} else if (Tools::isSubmit('submitOptionsIMAPConfig')) {
unset($this->fields_options['contact']);
$this->processUpdateOptions();
@@ -334,6 +433,7 @@ class AdminCustomerThreadsControllerCore extends AdminController
}
if ($id_customer_thread = (int)Tools::getValue('id_customer_thread')) {
$ct = new CustomerThread($id_customer_thread);
if (($id_contact = (int)Tools::getValue('id_contact'))) {
Db::getInstance()->execute('
UPDATE '._DB_PREFIX_.'customer_thread
@@ -342,14 +442,13 @@ class AdminCustomerThreadsControllerCore extends AdminController
);
}
if ($id_status = (int)Tools::getValue('setstatus')) {
$status_array = array(1 => 'open', 2 => 'closed', 3 => 'pending1', 4 => 'pending2');
Db::getInstance()->execute('
UPDATE '._DB_PREFIX_.'customer_thread
SET status = "'.$status_array[$id_status].'"
SET status = "'.(int) $id_status.'"
WHERE id_customer_thread = '.(int)$id_customer_thread.' LIMIT 1
');
}
if (isset($_POST['id_employee_forward'])) {
if ($id_employee = (int) Tools::getValue('id_employee_forward')) {
$messages = Db::getInstance()->getRow('
SELECT ct.*, cm.*, cl.name subject, CONCAT(e.firstname, \' \', e.lastname) employee_name,
CONCAT(c.firstname, \' \', c.lastname) customer_name, c.firstname
@@ -363,7 +462,7 @@ class AdminCustomerThreadsControllerCore extends AdminController
LEFT OUTER JOIN '._DB_PREFIX_.'customer c
ON (c.email = ct.email)
WHERE ct.id_customer_thread = '.(int)Tools::getValue('id_customer_thread').'
ORDER BY cm.date_add DESC
ORDER BY cm.id_employee, cm.private, cm.date_add DESC
');
$output = $this->displayMessage($messages, true, (int)Tools::getValue('id_employee_forward'));
$cm = new CustomerMessage();
@@ -395,7 +494,10 @@ class AdminCustomerThreadsControllerCore extends AdminController
$employee->firstname.' '.$employee->lastname,
$current_employee->email,
$current_employee->firstname.' '.$current_employee->lastname,
null, null, _PS_MAIL_DIR_, true)) {
null, null, _PS_MAIL_DIR_, true)
) {
$ct->id_employee = $employee->id;
$ct->save();
$cm->id_employee = (int)$employee->id;
$cm->private = 1;
$cm->message = $this->l('Message forwarded to').' '.$employee->firstname.' '.$employee->lastname."\n".$this->l('Comment:').' '.$message;
@@ -424,13 +526,14 @@ class AdminCustomerThreadsControllerCore extends AdminController
$this->errors[] = '<div class="alert error">'.Tools::displayError('The email address is invalid.').'</div>';
}
}
if (Tools::isSubmit('submitReply')) {
$ct = new CustomerThread($id_customer_thread);
if (Tools::isSubmit('submitReply') || Tools::isSubmit('submitReplyAndClose')) {
ShopUrl::cacheMainDomainForShop((int)$ct->id_shop);
$cm = new CustomerMessage();
$cm->id_employee = (int)$this->context->employee->id;
if (!$ct->id_employee) {
$ct->id_employee = $this->context->employee->id;
}
$cm->id_customer_thread = $ct->id;
$cm->ip_address = (int)ip2long(Tools::getRemoteAddr());
$cm->message = Tools::getValue('reply_message');
@@ -446,15 +549,7 @@ class AdminCustomerThreadsControllerCore extends AdminController
$file_attachment['mime'] = $_FILES['joinFile']['type'];
}
$customer = new Customer($ct->id_customer);
$params = array(
'{reply}' => Tools::nl2br(Tools::getValue('reply_message')),
'{link}' => Tools::url(
$this->context->link->getPageLink('contact', true, (int)$ct->id_lang, null, false, $ct->id_shop),
'id_customer_thread='.(int)$ct->id.'&token='.$ct->token
),
'{firstname}' => $customer->firstname,
'{lastname}' => $customer->lastname
);
//#ct == id_customer_thread #tc == token of thread <== used in the synchronization imap
$contact = new Contact((int)$ct->id_contact, (int)$ct->id_lang);
@@ -466,13 +561,80 @@ class AdminCustomerThreadsControllerCore extends AdminController
$from_email = null;
}
if ($ct->id_order) {
$link = Tools::url(
$this->context->link->getPageLink('order-detail', true, (int)$ct->id_lang, null, false, $ct->id_shop),
'id_order='.$ct->id_order
);
$objOrder = new Order($ct->id_order);
$message = $cm->message;
if (Configuration::get('PS_MAIL_TYPE', null, null, $objOrder->id_shop) != Mail::TYPE_TEXT) {
$message = Tools::nl2br($cm->message);
}
$objCustomer = new Customer($objOrder->id_customer);
$idShop = $objOrder->id_shop;
$params = array(
'{lastname}' => $objCustomer->lastname,
'{firstname}' => $objCustomer->firstname,
'{id_order}' => $objOrder->id,
'{order_name}' => $objOrder->getUniqReference(),
'{message}' => $message
);
$toEmail = $objCustomer->email;
$toName = $objCustomer->firstname.' '.$objCustomer->lastname;
$title = Mail::l('New message regarding your booking', (int)$objOrder->id_lang);
$template = 'order_merchant_comment';
$idLang = $objOrder->id_lang;
} else {
$link = Tools::url(
$this->context->link->getPageLink('contact', true, (int)$ct->id_lang, null, false, $ct->id_shop),
'token='.$ct->token
);
$headingText = $this->l('Hi').' '.$ct->user_name.',';
$idShop = $ct->id_shop;
$params = array(
'{reply}' => Tools::nl2br(Tools::getValue('reply_message')),
'{link}' => $link,
'{headingtext}' => $headingText,
'{contact_name}' => $contact->name,
);
if (!empty(trim($ct->subject))) {
$params['{subject}'] = $ct->subject;
}
$idLang = $ct->id_lang;
$toEmail = Tools::getValue('msg_email');
$toName = null;
$template = 'reply_msg';
$title = Mail::l('An answer to your message is available', $ct->id_lang);
if ($customer->id) {
$headingText .= ' '.$customer->firstname.' '.$customer->lastname;
}
$headingText .= ',';
}
if (Mail::Send(
(int)$ct->id_lang,
'reply_msg',
sprintf(Mail::l('An answer to your message is available #ct%1$s #tc%2$s', $ct->id_lang), $ct->id, $ct->token),
$params, Tools::getValue('msg_email'), null, $from_email, $from_name, $file_attachment, null,
_PS_MAIL_DIR_, true, $ct->id_shop)) {
$ct->status = 'closed';
(int) $idLang,
$template,
$title,
$params,
$toEmail,
$toName,
$from_email,
$from_name,
$file_attachment,
null,
_PS_MAIL_DIR_,
true,
$idShop
)) {
if (Tools::isSubmit('submitReplyAndClose')) {
$ct->status = CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED;
} else {
$ct->status = CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN;
}
$ct->update();
}
Tools::redirectAdmin(
@@ -481,6 +643,24 @@ class AdminCustomerThreadsControllerCore extends AdminController
} else {
$this->errors[] = Tools::displayError('An error occurred. Your message was not sent. Please contact your system administrator.');
}
} else if (Tools::isSubmit('submitReplyAsPrivate')) {
if (!$ct->id_employee) {
$ct->id_employee = $this->context->employee->id;
$ct->update();
}
ShopUrl::cacheMainDomainForShop((int)$ct->id_shop);
$cm = new CustomerMessage();
$cm->id_employee = (int)$this->context->employee->id;
$cm->id_customer_thread = $ct->id;
$cm->ip_address = (int)ip2long(Tools::getRemoteAddr());
$cm->message = Tools::getValue('reply_message');
$cm->private = 1;
$cm->read = 1;
if (($error = $cm->validateField('message', $cm->message, null, array(), true)) !== true) {
$this->errors[] = $error;
} elseif (!$cm->add()) {
$this->errors[] = Tools::displayError('Some error occured while saving the message');
}
}
}
@@ -539,19 +719,34 @@ class AdminCustomerThreadsControllerCore extends AdminController
$time = time();
$kpis = array();
$all = CustomerThread::getTotalCustomerThreads();
$pending = (int)AdminStatsController::getPendingMessages();
$open = CustomerThread::getTotalCustomerThreads('status='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN);
$helper = new HelperKpi();
$helper->id = 'box-messages';
$helper->icon = 'icon-envelope';
$helper->color = 'color1';
$helper->title = $this->l('Total threads');
$helper->tooltip = $this->l('Total number of threads.');
$helper->value = $all;
$this->kpis[] = $helper;
$helper = new HelperKpi();
$helper->id = 'box-pending-messages';
$helper->icon = 'icon-envelope';
$helper->color = 'color1';
$helper->title = $this->l('Pending Discussion Threads', null, null, false);
$helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=pending_messages';
$helper->color = 'color2';
$helper->title = $this->l('Threads pending', null, null, false);
$helper->value = $pending + $open;
$helper->tooltip = $this->l('Total number of threads having "open", "pending 1" and "pending 2" statuses.');
$this->kpis[] = $helper;
$helper = new HelperKpi();
$helper->id = 'box-age';
$helper->icon = 'icon-time';
$helper->color = 'color2';
$helper->color = 'color3';
$helper->title = $this->l('Average Response Time', null, null, false);
$helper->tooltip = $this->l('The average response time.');
$helper->subtitle = $this->l('30 days', null, null, false);
$helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=avg_msg_response_time';
$this->kpis[] = $helper;
@@ -559,12 +754,79 @@ class AdminCustomerThreadsControllerCore extends AdminController
$helper = new HelperKpi();
$helper->id = 'box-messages-per-thread';
$helper->icon = 'icon-copy';
$helper->color = 'color3';
$helper->title = $this->l('Messages per Thread', null, null, false);
$helper->color = 'color4';
$helper->title = $this->l('Average Messages per Thread', null, null, false);
$helper->subtitle = $this->l('30 day', null, null, false);
$helper->tooltip = $this->l('The average number of messages in each thread.');
$helper->source = $this->context->link->getAdminLink('AdminStats').'&ajax=1&action=getKpi&kpi=messages_per_thread';
$this->kpis[] = $helper;
$helper = new HelperKpi();
$helper->id = 'box-pending-messages';
$helper->icon = 'icon-envelope';
$helper->color = 'color1';
$helper->title = $this->l('Customer messages', null, null, false);
$helper->tooltip = $this->l('Total number of messages sent by customers.');
$helper->value = (int) CustomerMessage::getTotalCustomerMessages('cm.`id_employee` = 0');
$this->kpis[] = $helper;
$helper = new HelperKpi();
$helper->id = 'box-pending-messages';
$helper->icon = 'icon-envelope';
$helper->color = 'color4';
$helper->title = $this->l('Employee messages', null, null, false);
$helper->tooltip = $this->l('Total number of messages sent by employees.');
$helper->value = (int) CustomerMessage::getTotalCustomerMessages('cm.`id_employee` != 0');
$this->kpis[] = $helper;
$helper = new HelperKpi();
$helper->id = 'box-pending-messages';
$helper->icon = 'icon-envelope';
$helper->color = 'color2';
$helper->title = $this->l('Unread threads', null, null, false);
$helper->tooltip = $this->l('Total number of messages having open status.');
$helper->value = (int) $open;
$this->kpis[] = $helper;
$helper = new HelperKpi();
$helper->id = 'box-pending-messages';
$helper->icon = 'icon-envelope';
$helper->color = 'color3';
$helper->title = $this->l('Closed threads', null, null, false);
$helper->value = (int) ($all - ($open + $pending));
$helper->tooltip = $this->l('Total number of messages having closed status.');
$this->kpis[] = $helper;
$contacts = CustomerThread::getContacts();
$categories = Contact::getCategoriesContacts();
if ($contacts) {
$contacts = array_column($contacts, null, 'id_contact');
}
if ($categories) {
foreach ($categories as $idContact => $category) {
$helper = new HelperKpi();
$helper->id = 'box-pending-messages';
$helper->icon = 'icon-envelope';
$helper->color = 'color'.(($idContact+1)%3 ? ($idContact+1)%3 : '4');
$helper->title = $category['name'];
if (isset($contacts[$category['id_contact']])) {
$helper->href = self::$currentIndex.'&token='.$this->token.'&id_customer_thread='.$contacts[$category['id_contact']]['id_customer_thread'].'&viewcustomer_thread';
if ($contacts[$category['id_contact']]['total'] > 1) {
$helper->value = $contacts[$category['id_contact']]['total'].' '.$this->l('New messages');
} else {
$helper->value = $contacts[$category['id_contact']]['total'].' '.$this->l('New message');
}
} else {
$helper->value = $this->l('No new messages');
}
$helper->tooltip = $this->l('Total number of messages sent to').' '.$category['name'];
$this->kpis[] = $helper;
}
}
return parent::renderKpis();
}
@@ -603,64 +865,54 @@ class AdminCustomerThreadsControllerCore extends AdminController
}
}
$next_thread = CustomerThread::getNextThread((int)$thread->id);
$contacts = Contact::getContacts($this->context->language->id);
$actions = array();
if ($next_thread) {
$next_thread = array(
'href' => self::$currentIndex.'&id_customer_thread='.(int)$next_thread.'&viewcustomer_thread&token='.$this->token,
'name' => $this->l('Reply to the next unanswered message in this thread')
);
}
if ($thread->status != 'closed') {
$actions['closed'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=2&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
if ($thread->status != CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED) {
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED.'&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
'label' => $this->l('Mark as "handled"'),
'name' => 'setstatus',
'value' => 2
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED
);
} else {
$actions['open'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN.'&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
'label' => $this->l('Re-open'),
'name' => 'setstatus',
'value' => 1
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN
);
}
if ($thread->status != 'pending1') {
$actions['pending1'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=3&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
if ($thread->status != CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1) {
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1.'&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
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1
);
} else {
$actions['pending1'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN.'&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
'label' => $this->l('Disable pending status'),
'name' => 'setstatus',
'value' => 1
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN
);
}
if ($thread->status != 'pending2') {
$actions['pending2'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=4&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
if ($thread->status != CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2) {
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2.'&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
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2
);
} else {
$actions['pending2'] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus=1&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
$actions[CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2] = array(
'href' => self::$currentIndex.'&viewcustomer_thread&setstatus='.CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN.'&id_customer_thread='.(int)Tools::getValue('id_customer_thread').'&viewmsg&token='.$this->token,
'label' => $this->l('Disable pending status'),
'name' => 'setstatus',
'value' => 1
'value' => CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN
);
}
@@ -701,6 +953,7 @@ class AdminCustomerThreadsControllerCore extends AdminController
}
}
$this->context->smarty->assign('display', 'view');
$this->tpl_view_vars = array(
'id_customer_thread' => $id_customer_thread,
'thread' => $thread,
@@ -710,7 +963,6 @@ class AdminCustomerThreadsControllerCore extends AdminController
'messages' => $messages,
'first_message' => $first_message,
'contact' => $contact,
'next_thread' => $next_thread,
'orders' => isset($orders) ? $orders : false,
'customer' => isset($customer) ? $customer : false,
'products' => isset($products) ? $products : false,
@@ -721,10 +973,6 @@ class AdminCustomerThreadsControllerCore extends AdminController
'timeline_items' => $timeline_items,
);
if ($next_thread) {
$this->tpl_view_vars['next_thread'] = $next_thread;
}
return parent::renderView();
}
@@ -737,10 +985,22 @@ class AdminCustomerThreadsControllerCore extends AdminController
$content = '';
if (!$message['private']) {
$content .= $this->l('Message to: ').' <span class="badge">'.(!$message['id_employee'] ? $message['subject'] : $message['customer_name']).'</span><br/>';
$customerName = false;
if (!$message['id_customer']) {
$customerName = $message['user_name'];
} else if ($message['customer_name']) {
$customerName = $message['customer_name'];
}
if ($message['id_employee'] && $customerName) {
$content .= $this->l('Message to: ').' <span class="badge">'.($customerName).'</span><br/><br/>';
}
} else {
$content .= '<span class="label label-info">'.$this->l('Private').'</span><br/><br/>';
}
if (Validate::isLoadedObject($product)) {
$content .= '<br/>'.$this->l('Room type: ').'<span class="label label-info">'.$product->name.'</span><br/><br/>';
$content .= $this->l('Room type: ').'<span class="label label-info">'.$product->name.'</span><br/><br/>';
}
$content .= Tools::safeOutput($message['message']);
@@ -778,6 +1038,22 @@ class AdminCustomerThreadsControllerCore extends AdminController
return $timeline;
}
public function initToolbarTitle()
{
parent::initToolbarTitle();
switch ($this->display) {
case 'view':
if (!($thread = $this->loadObject())) {
return;
}
if ($thread->id) {
$this->toolbar_title[] = sprintf($this->l('Title: %1$s'), $thread->subject);
}
break;
}
}
protected function displayMessage($message, $email = false, $id_employee = null)
{
$tpl = $this->createTemplate('message.tpl');
@@ -1079,7 +1355,7 @@ class AdminCustomerThreadsControllerCore extends AdminController
$ct->id_contact = $id_contact;
$ct->id_lang = (int)Configuration::get('PS_LANG_DEFAULT');
$ct->id_shop = $this->context->shop->id; //new customer threads for unrecognized mails are not shown without shop id
$ct->status = 'open';
$ct->status = CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN;
$ct->token = Tools::passwdGen(12);
$ct->add();
} else {

View File

@@ -80,25 +80,11 @@ class AdminEmailsControllerCore extends AdminController
}
parent::__construct();
foreach (Contact::getContacts($this->context->language->id) as $contact) {
$arr[] = array('email_message' => $contact['id_contact'], 'name' => $contact['name']);
}
$this->fields_options = array(
'email' => array(
'title' => $this->l('Email'),
'icon' => 'icon-envelope',
'fields' => array(
'PS_MAIL_EMAIL_MESSAGE' => array(
'title' => $this->l('Send email to'),
'desc' => $this->l('Where customers send messages from the order page.'),
'validation' => 'isUnsignedId',
'type' => 'select',
'cast' => 'intval',
'identifier' => 'email_message',
'list' => $arr
),
'fields' => array(
'PS_MAIL_METHOD' => array(
'title' => '',
'validation' => 'isGenericName',

View File

@@ -1722,13 +1722,17 @@ class AdminOrdersControllerCore extends AdminController
$id_customer_thread = CustomerThread::getIdCustomerThreadByEmailAndIdOrder($customer->email, $order->id);
if (!$id_customer_thread) {
$customer_thread = new CustomerThread();
$customer_thread->id_contact = 0;
$customer_thread->id_contact = (int)Configuration::get('PS_MAIL_EMAIL_MESSAGE');
$customer_thread->id_customer = (int)$order->id_customer;
$customer_thread->user_name = $customer->firstname.' '.$customer->lastname;
$customer_thread->phone = $customer->phone;
$customer_thread->subject = $order->reference;
$customer_thread->id_employee = (int)$this->context->employee->id;
$customer_thread->id_shop = (int)$this->context->shop->id;
$customer_thread->id_order = (int)$order->id;
$customer_thread->id_lang = (int)$this->context->language->id;
$customer_thread->email = $customer->email;
$customer_thread->status = 'open';
$customer_thread->status = CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN;
$customer_thread->token = Tools::passwdGen(12);
$customer_thread->add();
} else {
@@ -1761,7 +1765,7 @@ class AdminOrdersControllerCore extends AdminController
if (@Mail::Send(
(int)$order->id_lang,
'order_merchant_comment',
Mail::l('New message regarding your order', (int)$order->id_lang),
Mail::l('New message regarding your booking', (int)$order->id_lang),
$varsTpl,
$customer->email,
$customer->firstname.' '.$customer->lastname,
@@ -2820,13 +2824,15 @@ class AdminOrdersControllerCore extends AdminController
$helper->value = $objOrder->source;
$this->kpis[] = $helper;
$objCustomerThread = new CustomerThread();
$idCustomerThread = $objCustomerThread->getIdCustomerThreadByIdOrder($objOrder->id);
$helper = new HelperKpi();
$helper->id = 'box-messages';
$helper->icon = 'icon-comments';
$helper->color = 'color2';
$helper->title = $this->l('Messages');
$helper->tooltip = $this->l('Messages is the number of customer messages for this order.');
$helper->href = $this->context->link->getAdminLink('AdminCustomerThreads').'&id_order='.$objOrder->id;
$helper->href = $this->context->link->getAdminLink('AdminCustomerThreads').((int) $idCustomerThread ? '&viewcustomer_thread&id_customer_thread='.(int) $idCustomerThread : '');
$helper->value = count(CustomerThread::getCustomerMessages($objOrder->id_customer, null, $objOrder->id, CustomerMessage::QLO_CUSTOMER_MESSAGE_BY_CUSTOMER));
$this->kpis[] = $helper;
@@ -3474,10 +3480,28 @@ class AdminOrdersControllerCore extends AdminController
}
}
$objCustomerThread = new CustomerThread();
$messages = Message::getMessagesByOrderId($order->id, true);
if ($customerMessages = CustomerMessage::getMessagesByOrderId($order->id, true)) {
foreach ($messages as $messageKey => $message) {
foreach ($customerMessages as $customerMessageKey => $customerMessage) {
if (strcmp($message['message'], $customerMessage['message']) == 0) {
unset($messages[$messageKey]);
}
}
}
$messages = array_merge($customerMessages, $messages);
usort($messages, function ($a, $b) {
return strtotime($a['date_add']) < strtotime($b['date_add']);
});
}
// send hotel standalone and standalone products
$objProduct = new Product();
$hotelStandaloneProducts = $objProduct->getServiceProducts(null, Product::SELLING_PREFERENCE_HOTEL_STANDALONE);
$standaloneProducts = $objProduct->getServiceProducts(null, Product::SELLING_PREFERENCE_STANDALONE);
$this->tpl_view_vars = array(
'hotelStandaloneProducts' => $hotelStandaloneProducts,
'standaloneProducts' => $standaloneProducts,
@@ -3527,8 +3551,9 @@ class AdminOrdersControllerCore extends AdminController
'discounts' => $order->getCartRules(),
'total_paid' => $order->getTotalPaid(),
'customer_thread_message' => CustomerThread::getCustomerMessages($order->id_customer, null, $order->id),
'id_customer_thread' => $objCustomerThread->getIdCustomerThreadByIdOrder($order->id),
'orderMessages' => OrderMessage::getOrderMessages($order->id_lang),
'messages' => Message::getMessagesByOrderId($order->id, true),
'messages' => $messages,
'carrier' => new Carrier($order->id_carrier),
'history' => $history,
'order_payment_detail' => $order_payment_detail,

View File

@@ -3000,8 +3000,6 @@ class AdminProductsControllerCore extends AdminController
$objRoomType = new HotelRoomType();
$objRoomType->id_product = $product->id;
$objRoomType->id_hotel = $id_hotel;
$idBedTypes = Tools::getValue('id_bed_types', array());
$objRoomType->id_bed_types = json_encode($idBedTypes);
$objRoomType->save();
}
}

View File

@@ -499,7 +499,14 @@ class AdminStatsControllerCore extends AdminStatsTabController
public static function getPendingMessages()
{
return CustomerThread::getTotalCustomerThreads('status LIKE "%pending%" OR status = "open"'.Shop::addSqlRestriction());
$pendingStatuses = array(
CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING2,
CustomerThread::QLO_CUSTOMER_THREAD_STATUS_PENDING1
);
return CustomerThread::getTotalCustomerThreads(
'`status` IN ('.implode(',', $pendingStatuses).')'.Shop::addSqlRestriction()
);
}
public static function getAverageMessageResponseTime($date_from, $date_to, $return_seconds = false)
@@ -530,13 +537,23 @@ class AdminStatsControllerCore extends AdminStatsTabController
public static function getMessagesPerThread($date_from, $date_to)
{
$accessWhere = '';
$employee = Context::getContext()->employee;
if (!$employee->isSuperAdmin()) {
$idProfile = $employee->id_profile;
if ($acsHtls = HotelBranchInformation::getProfileAccessedHotels($idProfile, 1, 1)) {
$accessWhere = ' AND ct.`id_order` IN (SELECT `id_order` FROM `'._DB_PREFIX_.'htl_booking_detail` hbd WHERE `id_hotel` IN ('.implode(',', $acsHtls).'))';
} else {
$accessWhere = ' AND ct.`id_order` = 0 ';
}
}
$result = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS('
SELECT COUNT(*) as messages
FROM `'._DB_PREFIX_.'customer_thread` ct
LEFT JOIN `'._DB_PREFIX_.'customer_message` cm ON (ct.id_customer_thread = cm.id_customer_thread)
WHERE ct.`date_add` BETWEEN "'.pSQL($date_from).' 00:00:00" AND "'.pSQL($date_to).' 23:59:59"
'.Shop::addSqlRestriction().'
AND status = "closed"
'.Shop::addSqlRestriction().$accessWhere.'
GROUP BY ct.id_customer_thread');
$threads = $messages = 0;
foreach ($result as $row) {

View File

@@ -39,16 +39,40 @@ class ContactControllerCore extends FrontController
$saveContactKey = $this->context->cookie->contactFormKey;
$extension = array('.txt', '.rtf', '.doc', '.docx', '.pdf', '.zip', '.png', '.jpeg', '.gif', '.jpg');
$file_attachment = Tools::fileAttachment('fileUpload');
$message = Tools::getValue('message'); // Html entities is not usefull, iscleanHtml check there is no bad html tags.
$message = trim(Tools::getValue('message')); // Html entities is not usefull, iscleanHtml check there is no bad html tags.
$url = Tools::getValue('url');
$phone = Tools::getValue('phone');
$subject = trim(Tools::getValue('subject'));
$userName = Tools::getValue('user_name');
$id_contact = (int)Tools::getValue('id_contact');
$objCustomerThread = new CustomerThread();
$id_customer_thread = (int)$objCustomerThread->getIdCustomerThreadByToken(Tools::getValue('token'));
$nameRequired = Configuration::get('PS_CUSTOMER_SERVICE_REQUIRED_NAME');
$phoneRequired = Configuration::get('PS_CUSTOMER_SERVICE_REQUIRED_PHONE');
if (!Configuration::get('PS_CUSTOMER_SERVICE_DISPLAY_CONTACT')) {
$id_contact = (int) Configuration::get('PS_CUSTOMER_SERVICE_CONTACT');
}
if (!($from = trim(Tools::getValue('from'))) || !Validate::isEmail($from)) {
$this->errors[] = Tools::displayError('Invalid email address.');
} elseif (!$message) {
$this->errors[] = Tools::displayError('The message cannot be blank.');
} elseif (!Validate::isCleanHtml($message)) {
$this->errors[] = Tools::displayError('Invalid message');
} elseif (!($id_contact = (int)Tools::getValue('id_contact')) || !(Validate::isLoadedObject($contact = new Contact($id_contact, $this->context->language->id)))) {
$this->errors[] = Tools::displayError('Please select a subject from the list provided. ');
} elseif (!$id_customer_thread && !$subject) {
$this->errors[] = Tools::displayError('The title cannot be blank.');
} elseif (!$id_customer_thread && !Validate::isCleanHtml($subject)) {
$this->errors[] = Tools::displayError('Invalid subject$subject');
} else if (!$id_customer_thread && $nameRequired && !trim($userName)) {
$this->errors[] = Tools::displayError('Name is required.');
} else if (!$id_customer_thread && trim($nameRequired) && !Validate::isGenericName($userName)) {
$this->errors[] = Tools::displayError('Invalid name.');
} else if (!$id_customer_thread && $phoneRequired && !trim($phone)) {
$this->errors[] = Tools::displayError('Phone is required.');
} else if (!$id_customer_thread && trim($phone) && !Validate::isPhoneNumber($phone)) {
$this->errors[] = Tools::displayError('Invalid Phone number.');
} elseif (!Validate::isLoadedObject($contact = new Contact($id_contact, $this->context->language->id))) {
$this->errors[] = Tools::displayError('Please choose who to send the message to.');
} elseif (!empty($file_attachment['name']) && $file_attachment['error'] != 0) {
$this->errors[] = Tools::displayError('An error occurred during the file-upload process.');
} elseif (!empty($file_attachment['name']) && !in_array(Tools::strtolower(substr($file_attachment['name'], -4)), $extension) && !in_array(Tools::strtolower(substr($file_attachment['name'], -5)), $extension)) {
@@ -71,44 +95,15 @@ class ContactControllerCore extends FrontController
$id_order = (int) $order->id_customer === (int) $customer->id ? $id_order : 0;
}
if (!((
($id_customer_thread = (int)Tools::getValue('id_customer_thread'))
&& (int)Db::getInstance()->getValue('
SELECT cm.id_customer_thread FROM '._DB_PREFIX_.'customer_thread cm
WHERE cm.id_customer_thread = '.(int)$id_customer_thread.' AND cm.id_shop = '.(int)$this->context->shop->id.' AND token = \''.pSQL(Tools::getValue('token')).'\'')
) || (
$id_customer_thread = CustomerThread::getIdCustomerThreadByEmailAndIdOrder($from, $id_order, $id_contact)
))) {
$fields = Db::getInstance()->executeS('
SELECT cm.id_customer_thread, cm.id_contact, cm.id_customer, cm.id_order, cm.id_product, cm.email
FROM '._DB_PREFIX_.'customer_thread cm
WHERE email = \''.pSQL($from).'\' AND cm.id_shop = '.(int)$this->context->shop->id.' AND ('.
($customer->id ? 'id_customer = '.(int)$customer->id.' OR ' : '').'
id_order = '.(int)$id_order.')');
$score = 0;
foreach ($fields as $key => $row) {
$tmp = 0;
if ((int)$row['id_customer'] && $row['id_customer'] != $customer->id && $row['email'] != $from) {
continue;
}
if ($row['id_order'] != 0 && $id_order != $row['id_order']) {
continue;
}
if ($row['email'] == $from) {
$tmp += 4;
}
if ($row['id_contact'] == $id_contact) {
$tmp++;
}
if (Tools::getValue('id_product') != 0 && $row['id_product'] == Tools::getValue('id_product')) {
$tmp += 2;
}
if ($tmp >= 5 && $tmp >= $score) {
$score = $tmp;
$id_customer_thread = $row['id_customer_thread'];
}
}
if (!$id_customer_thread) {
$id_customer_thread = CustomerThread::getIdCustomerThreadByEmailAndIdOrder(
$from,
$id_order,
$id_contact,
' AND a.`status` !='. CustomerThread::QLO_CUSTOMER_THREAD_STATUS_CLOSED
);
}
$old_message = Db::getInstance()->getValue('
SELECT cm.message FROM '._DB_PREFIX_.'customer_message cm
LEFT JOIN '._DB_PREFIX_.'customer_thread cc on (cm.id_customer_thread = cc.id_customer_thread)
@@ -123,36 +118,31 @@ class ContactControllerCore extends FrontController
if ($contact->customer_service) {
if ((int)$id_customer_thread) {
$ct = new CustomerThread($id_customer_thread);
$ct->status = 'open';
$ct->id_lang = (int)$this->context->language->id;
$ct->id_contact = (int)$id_contact;
$ct->id_order = (int)$id_order;
if ($id_product = (int)Tools::getValue('id_product')) {
$ct->id_product = $id_product;
}
$ct->update();
} else {
$ct = new CustomerThread();
if (isset($customer->id)) {
$ct->id_customer = (int)$customer->id;
}
$ct->id_shop = (int)$this->context->shop->id;
$ct->id_order = (int)$id_order;
if ($id_product = (int)Tools::getValue('id_product')) {
$ct->id_product = $id_product;
}
$ct->id_contact = (int)$id_contact;
$ct->id_lang = (int)$this->context->language->id;
$ct->phone = $phone;
$ct->user_name = $userName;
$ct->subject = $subject;
$ct->email = $from;
$ct->status = 'open';
$ct->token = Tools::passwdGen(12);
$ct->add();
}
if ($ct->id) {
$ct->status = CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN;
$ct->id_lang = (int)$this->context->language->id;
$ct->id_contact = (int)$id_contact;
if ($ct->save()) {
$cm = new CustomerMessage();
$cm->id_customer_thread = $ct->id;
$cm->message = $message;
if ($id_product = (int)Tools::getValue('id_product')) {
$cm->id_product = $id_product;
}
if (isset($file_attachment['rename']) && !empty($file_attachment['rename']) && rename($file_attachment['tmp_name'], _PS_UPLOAD_DIR_.basename($file_attachment['rename']))) {
$cm->file_name = $file_attachment['rename'];
@chmod(_PS_UPLOAD_DIR_.basename($file_attachment['rename']), 0664);
@@ -168,33 +158,22 @@ class ContactControllerCore extends FrontController
}
if (!count($this->errors)) {
$var_list = array(
'{order_name}' => '-',
'{attached_file}' => '-',
'{message}' => Tools::nl2br(stripslashes($message)),
'{email}' => $from,
'{product_name}' => '',
);
$smartyMailVars['message'] = Tools::nl2br(stripslashes($message));
$smartyMailVars['email'] = $ct->email;
$smartyMailVars['subject'] = $ct->subject;
$smartyMailVars['phone'] = $ct->phone;
$smartyMailVars['user_name'] = $ct->user_name;
if (isset($file_attachment['name'])) {
$var_list['{attached_file}'] = $file_attachment['name'];
}
$id_product = (int)Tools::getValue('id_product');
if (isset($ct) && Validate::isLoadedObject($ct) && $ct->id_order) {
$order = new Order((int)$ct->id_order);
$var_list['{order_name}'] = $order->getUniqReference();
$var_list['{id_order}'] = (int)$order->id;
}
if ($id_product) {
$product = new Product((int)$id_product);
if (Validate::isLoadedObject($product) && isset($product->name[Context::getContext()->language->id])) {
$var_list['{product_name}'] = $product->name[Context::getContext()->language->id];
}
$smartyMailVars['attached_file'] = $file_attachment['name'];
}
$contact_content_txt = $this->getEmailTemplateContent('contact_content_txt.tpl', Mail::TYPE_TEXT, $smartyMailVars);
$contact_content_html = $this->getEmailTemplateContent('contact_content_html.tpl', Mail::TYPE_TEXT, $smartyMailVars);
$var_list = array(
'{contact_content_txt}' => $contact_content_txt,
'{contact_content_html}' => $contact_content_html,
);
if (!empty($contact->email)) {
Mail::Send(
$this->context->language->id,
@@ -226,6 +205,30 @@ class ContactControllerCore extends FrontController
}
}
protected function getEmailTemplateContent($template_name, $mail_type, $var)
{
$email_configuration = Configuration::get('PS_MAIL_TYPE');
if ($email_configuration != $mail_type && $email_configuration != Mail::TYPE_BOTH) {
return '';
}
$pathToFindEmail = array(
_PS_THEME_DIR_.'mails'.DIRECTORY_SEPARATOR.$this->context->language->iso_code.DIRECTORY_SEPARATOR.$template_name,
_PS_THEME_DIR_.'mails'.DIRECTORY_SEPARATOR.'en'.DIRECTORY_SEPARATOR.$template_name,
_PS_MAIL_DIR_.$this->context->language->iso_code.DIRECTORY_SEPARATOR.$template_name,
_PS_MAIL_DIR_.'en'.DIRECTORY_SEPARATOR.$template_name,
);
foreach ($pathToFindEmail as $path) {
if (Tools::file_exists_cache($path)) {
$this->context->smarty->assign($var);
return $this->context->smarty->fetch($path);
}
}
return '';
}
public function setMedia()
{
parent::setMedia();
@@ -259,27 +262,30 @@ class ContactControllerCore extends FrontController
$email = Tools::safeOutput(Tools::getValue('from',
((isset($this->context->cookie) && isset($this->context->cookie->email) && Validate::isEmail($this->context->cookie->email)) ? $this->context->cookie->email : '')));
$customerName = isset($this->context->customer) ? $this->context->customer->firstname.' '.$this->context->customer->lastname : '';
$customerPhone = isset($this->context->customer) ? $this->context->customer->phone: '';
$this->context->smarty->assign(array(
'errors' => $this->errors,
'email' => $email,
'customerName' => $customerName,
'customerPhone' => $customerPhone,
'fileupload' => Configuration::get('PS_CUSTOMER_SERVICE_FILE_UPLOAD'),
'max_upload_size' => (int)Tools::getMaxUploadSize()
));
if (($id_customer_thread = (int)Tools::getValue('id_customer_thread')) && $token = Tools::getValue('token')) {
$customer_thread = Db::getInstance()->getRow('
if ($token = Tools::getValue('token')) {
if ($customer_thread = Db::getInstance()->getRow('
SELECT cm.*
FROM '._DB_PREFIX_.'customer_thread cm
WHERE cm.id_customer_thread = '.(int)$id_customer_thread.'
AND cm.id_shop = '.(int)$this->context->shop->id.'
WHERE cm.id_shop = '.(int)$this->context->shop->id.'
AND token = \''.pSQL($token).'\'
');
$order = new Order((int)$customer_thread['id_order']);
if (Validate::isLoadedObject($order)) {
$customer_thread['reference'] = $order->getUniqReference();
')) {
$order = new Order((int)$customer_thread['id_order']);
if (Validate::isLoadedObject($order)) {
$customer_thread['reference'] = $order->getUniqReference();
}
$this->context->smarty->assign('customerThread', $customer_thread);
}
$this->context->smarty->assign('customerThread', $customer_thread);
}
$objShop = new Shop();
@@ -330,6 +336,11 @@ class ContactControllerCore extends FrontController
'contacts' => Contact::getContacts($this->context->language->id),
'message' => html_entity_decode(Tools::getValue('message')),
'contactKey' => $contactKey,
'contactNameRequired' => Configuration::get('PS_CUSTOMER_SERVICE_REQUIRED_NAME'),
'displayContactName' => Configuration::get('PS_CUSTOMER_SERVICE_DISPLAY_NAME'),
'contactPhoneRequired' => Configuration::get('PS_CUSTOMER_SERVICE_REQUIRED_PHONE'),
'displayContactPhone' => Configuration::get('PS_CUSTOMER_SERVICE_DISPLAY_PHONE'),
'allowContactSelection' => Configuration::get('PS_CUSTOMER_SERVICE_DISPLAY_CONTACT')
)
);

View File

@@ -493,7 +493,7 @@ class OrderDetailControllerCore extends FrontController
'deliveryAddressFormatedValues' => $deliveryAddressFormatedValues,
'deliveryState' => (Validate::isLoadedObject($addressDelivery) && $addressDelivery->id_state) ? new State($addressDelivery->id_state) : false,
'is_guest' => false,
'messages' => CustomerMessage::getMessagesByOrderId((int) $order->id, false),
'messages' => CustomerMessage::getMessagesByOrderId((int) $order->id),
'CUSTOMIZE_FILE' => Product::CUSTOMIZE_FILE,
'CUSTOMIZE_TEXTFIELD' => Product::CUSTOMIZE_TEXTFIELD,
'isRecyclable' => Configuration::get('PS_RECYCLABLE_PACK'),
@@ -758,26 +758,32 @@ class OrderDetailControllerCore extends FrontController
$id_customer_thread = CustomerThread::getIdCustomerThreadByEmailAndIdOrder($this->context->customer->email, $order->id);
$id_product = (int)Tools::getValue('id_product');
$cm = new CustomerMessage();
$objCustomer = new Customer($order->id_customer);
if (!$id_customer_thread) {
$ct = new CustomerThread();
$ct->id_contact = 0;
$ct->id_contact = (int)Configuration::get('PS_MAIL_EMAIL_MESSAGE');
$ct->id_customer = (int)$order->id_customer;
$ct->user_name = $objCustomer->firstname.' '.$objCustomer->lastname;
$ct->subject = $order->reference;
$ct->phone = $objCustomer->phone;
$ct->id_shop = (int)$this->context->shop->id;
if ($id_product && $order->orderContainProduct($id_product)) {
$ct->id_product = $id_product;
}
$ct->id_order = (int)$order->id;
$ct->id_lang = (int)$this->context->language->id;
$ct->email = $this->context->customer->email;
$ct->status = 'open';
$ct->status = CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN;
$ct->token = Tools::passwdGen(12);
$ct->add();
} else {
$ct = new CustomerThread((int)$id_customer_thread);
$ct->status = 'open';
$ct->status = CustomerThread::QLO_CUSTOMER_THREAD_STATUS_OPEN;
$ct->update();
}
if ($id_product && $order->orderContainProduct($id_product)) {
$cm->id_product = $id_product;
}
$cm->user_agent = $_SERVER['HTTP_USER_AGENT'];
$cm->id_customer_thread = $ct->id;
$cm->message = $msgText;
$cm->ip_address = (int)ip2long($_SERVER['REMOTE_ADDR']);
@@ -829,7 +835,7 @@ class OrderDetailControllerCore extends FrontController
// send message html in json
$response['status'] = true;
$message = CustomerMessage::getMessagesByOrderId($order->id, false)[0];
$message = CustomerMessage::getMessagesByOrderId($order->id)[0];
$this->context->smarty->assign(array('message' => $message));
$response['message_html'] = $this->context->smarty->fetch(_PS_THEME_DIR_.'_partials/order-message.tpl');
} else {
@@ -887,4 +893,4 @@ class OrderDetailControllerCore extends FrontController
}
}
}
}
}

View File

@@ -634,6 +634,7 @@ CREATE TABLE `PREFIX_customer_message` (
`id_customer_thread` int(11) DEFAULT NULL,
`id_employee` int(10) unsigned DEFAULT NULL,
`message` MEDIUMTEXT NOT NULL,
`id_product` int(10) unsigned DEFAULT NULL,
`file_name` varchar(18) DEFAULT NULL,
`ip_address` varchar(16) DEFAULT NULL,
`user_agent` varchar(128) DEFAULT NULL,
@@ -658,9 +659,12 @@ CREATE TABLE `PREFIX_customer_thread` (
`id_lang` int(10) unsigned NOT NULL,
`id_contact` int(10) unsigned NOT NULL,
`id_customer` int(10) unsigned DEFAULT NULL,
`id_employee` int(10) unsigned DEFAULT NULL,
`id_order` int(10) unsigned DEFAULT NULL,
`id_product` int(10) unsigned DEFAULT NULL,
`status` enum('open','closed','pending1','pending2') NOT NULL DEFAULT 'open',
`user_name` VARCHAR(128) DEFAULT NULL,
`phone` VARCHAR(32) DEFAULT NULL,
`subject` TEXT DEFAULT NULL,
`status` INT(10) NOT NULL DEFAULT 1,
`email` varchar(128) NOT NULL,
`token` varchar(12) DEFAULT NULL,
`date_add` datetime NOT NULL,
@@ -670,8 +674,7 @@ CREATE TABLE `PREFIX_customer_thread` (
KEY `id_lang` (`id_lang`),
KEY `id_contact` (`id_contact`),
KEY `id_customer` (`id_customer`),
KEY `id_order` (`id_order`),
KEY `id_product` (`id_product`)
KEY `id_order` (`id_order`)
) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION;
CREATE TABLE `PREFIX_order_customer_guest_detail` (

View File

@@ -955,6 +955,21 @@
</configuration>
<configuration id="PS_BACKDATE_ORDER_EMPLOYEES" name="PS_BACKDATE_ORDER_EMPLOYEES">
<value>0</value>
</configuration>
<configuration id="PS_CUSTOMER_SERVICE_REQUIRED_NAME" name="PS_CUSTOMER_SERVICE_REQUIRED_NAME">
<value>1</value>
</configuration>
<configuration id="PS_CUSTOMER_SERVICE_DISPLAY_NAME" name="PS_CUSTOMER_SERVICE_DISPLAY_NAME">
<value>1</value>
</configuration>
<configuration id="PS_CUSTOMER_SERVICE_REQUIRED_PHONE" name="PS_CUSTOMER_SERVICE_REQUIRED_PHONE">
<value>1</value>
</configuration>
<configuration id="PS_CUSTOMER_SERVICE_DISPLAY_PHONE" name="PS_CUSTOMER_SERVICE_DISPLAY_PHONE">
<value>1</value>
</configuration>
<configuration id="PS_CUSTOMER_SERVICE_DISPLAY_CONTACT" name="PS_CUSTOMER_SERVICE_DISPLAY_CONTACT">
<value>1</value>
</configuration>
<configuration id="PS_STANDARD_PRODUCT_ORDER_ADDRESS_PREFRENCE" name="PS_STANDARD_PRODUCT_ORDER_ADDRESS_PREFRENCE">
<value>0</value>

View File

@@ -0,0 +1,57 @@
/*
* Copyright since 2010 Webkul.
*
* NOTICE OF LICENSE
*
* All right is reserved,
* Please go through this link for complete license : https://store.webkul.com/license.html
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade this module to newer
* versions in the future. If you wish to customize this module for your
* needs please refer to https://store.webkul.com/customisation-guidelines/ for more information.
*
* @author Webkul IN <support@webkul.com>
* @copyright Since 2010 Webkul IN
* @license https://store.webkul.com/license.html
*/
$(document).ready(function() {
if ($('#PS_CUSTOMER_SERVICE_DISPLAY_CONTACT_on').prop('checked')) {
$('[name="PS_CUSTOMER_SERVICE_CONTACT"]').closest('.form-group').hide();
}
if (!$('#PS_CUSTOMER_SERVICE_DISPLAY_NAME_on').prop('checked')) {
$('[name="PS_CUSTOMER_SERVICE_REQUIRED_NAME"]').closest('.form-group').hide();
}
if (!$('#PS_CUSTOMER_SERVICE_DISPLAY_PHONE_on').prop('checked')) {
$('[name="PS_CUSTOMER_SERVICE_REQUIRED_PHONE"]').closest('.form-group').hide();
}
$(document).on('change', '[name="PS_CUSTOMER_SERVICE_DISPLAY_CONTACT"]', function() {
if ($('#PS_CUSTOMER_SERVICE_DISPLAY_CONTACT_on').prop('checked')) {
$('[name="PS_CUSTOMER_SERVICE_CONTACT"]').closest('.form-group').hide();
} else {
$('[name="PS_CUSTOMER_SERVICE_CONTACT"]').closest('.form-group').show();
}
});
$(document).on('change', '[name="PS_CUSTOMER_SERVICE_DISPLAY_NAME"]', function() {
if ($('#PS_CUSTOMER_SERVICE_DISPLAY_NAME_on').prop('checked')) {
$('[name="PS_CUSTOMER_SERVICE_REQUIRED_NAME"]').closest('.form-group').show();
} else {
$('[name="PS_CUSTOMER_SERVICE_REQUIRED_NAME"]').closest('.form-group').hide();
}
});
$(document).on('change', '[name="PS_CUSTOMER_SERVICE_DISPLAY_PHONE"]', function() {
if ($('#PS_CUSTOMER_SERVICE_DISPLAY_PHONE_on').prop('checked')) {
$('[name="PS_CUSTOMER_SERVICE_REQUIRED_PHONE"]').closest('.form-group').show();
} else {
$('[name="PS_CUSTOMER_SERVICE_REQUIRED_PHONE"]').closest('.form-group').hide();
}
});
});

View File

@@ -66,37 +66,33 @@
</a>
</td>
</tr>
<tr>
<td align="center" class="titleblock" style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<span class="title" style="font-weight:500;font-size:28px;text-transform:uppercase;line-height:33px">Message from a {shop_name} customer</span>
</font>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>
<tr>
<td class="box" style="border:1px solid #D6D4D4;background-color:#f8f8f8;padding:7px 0">
<table class="table" style="width:100%">
<tr>
<td width="10" style="padding:7px 0">&nbsp;</td>
<td style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<span style="color:#777">
<span style="color:#333"><strong>Customer e-mail address: <a href="mailto:{email}" style="color:#337ff1">{email}</a></strong></span><br /><br />
<span style="color:#333"><strong>Customer message:</strong></span> {message}<br /><br />
<span style="color:#333"><strong>Attached file:</strong></span> {attached_file}
</span>
</font>
</td>
<td width="10" style="padding:7px 0">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" class="titleblock" style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<span class="title" style="font-weight:500;font-size:28px;text-transform:uppercase;line-height:33px">Message from a {shop_name} customer</span>
</font>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>
<tr>
<td class="box" style="border:1px solid #D6D4D4;background-color:#f8f8f8;padding:7px 0">
<table class="table" style="width:100%">
<tr>
<td width="10" style="padding:7px 0">&nbsp;</td>
<td style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<span style="color:#777">
{contact_content_html}
</span>
</font>
</td>
<td width="10" style="padding:7px 0">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>

View File

@@ -1,16 +1,10 @@
[/{shop_url}]
[/{shop_url}]
Message from a {shop_name} customer
Message from a {shop_name} customer
CUSTOMER E-MAIL ADDRESS: {email}
CUSTOMER MESSAGE: {message}
ORDER ID: {order_name}
ATTACHED FILE: {attached_file}
{contact_content_txt}
{shop_name} [/{shop_url}] powered by Webkul(tm)
[https://webkul.com]
[https://webkul.com]

View File

@@ -0,0 +1,15 @@
{if isset($user_name) && $user_name}
<span style="color:#333"><strong>Name:</strong> {$user_name}</span><br /><br />
{/if}
{if isset($email) && $email}
<span style="color:#333"><strong>E-mail: <a href="mailto:{$email}" style="color:#337ff1">{$email}</a></strong></span><br /><br />
{/if}
{if isset($subject) && $subject}
<span style="color:#333"><strong>Title:</strong> {$subject}</span><br /><br />
{/if}
{if isset($message) && $message}
<span style="color:#333"><strong>Customer message:</strong></span><br />{$message}<br/><br />
{/if}
{if isset($attached_file) && $attached_file}
<span style="color:#333"><strong>Attached file:</strong></span> {$attached_file}
{/if}

View File

@@ -0,0 +1,18 @@
{if isset($user_name) && $user_name}
Name: {$user_name}
{/if}
{if isset($email) && $email}
E-mail: {$email}
{/if}
{if isset($subject) && $subject}
Title: {$subject}
{/if}
{if isset($message) && $message}
Customer message:</strong></span><br />{$message}<br/><br />
{/if}
{if isset($attached_file) && $attached_file}
Attached file:</strong></span> {$attached_file}
{/if}

View File

@@ -4,51 +4,51 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<title>Message from {shop_name}</title>
<style> @media only screen and (max-width: 300px){
<style> @media only screen and (max-width: 300px){
body {
width:218px !important;
margin:auto !important;
}
.table {width:195px !important;margin:auto !important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto !important;display: block !important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto !important;display: block !important;}
span.title{font-size:20px !important;line-height: 23px !important}
span.subtitle{font-size: 14px !important;line-height: 18px !important;padding-top:10px !important;display:block !important;}
span.subtitle{font-size: 14px !important;line-height: 18px !important;padding-top:10px !important;display:block !important;}
td.box p{font-size: 12px !important;font-weight: bold !important;}
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
}
.table-recap{width: 200px!important;}
.table-recap tr td, .conf_body td{text-align:center !important;}
.table-recap tr td, .conf_body td{text-align:center !important;}
.address{display: block !important;margin-bottom: 10px !important;}
.space_address{display: none !important;}
.space_address{display: none !important;}
}
@media only screen and (min-width: 301px) and (max-width: 500px) {
@media only screen and (min-width: 301px) and (max-width: 500px) {
body {width:308px!important;margin:auto!important;}
.table {width:285px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
.table {width:285px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
}
.table-recap{width: 295px !important;}
.table-recap tr td, .conf_body td{text-align:center !important;}
}
@media only screen and (min-width: 501px) and (max-width: 768px) {
body {width:478px!important;margin:auto!important;}
.table {width:450px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
}
@media only screen and (max-device-width: 480px) {
body {width:308px!important;margin:auto!important;}
.table {width:285px;margin:auto!important;}
.table {width:450px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
}
@media only screen and (max-device-width: 480px) {
body {width:308px!important;margin:auto!important;}
.table {width:285px;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
.table-recap{width: 295px!important;}
.table-recap tr td, .conf_body td{text-align:center!important;}
.table-recap tr td, .conf_body td{text-align:center!important;}
.address{display: block !important;margin-bottom: 10px !important;}
.space_address{display: none !important;}
.space_address{display: none !important;}
}
</style>
@@ -66,39 +66,36 @@
</a>
</td>
</tr>
<tr>
<td align="center" class="titleblock" style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<span class="title" style="font-weight:500;font-size:28px;text-transform:uppercase;line-height:33px">Hi {firstname} {lastname},</span>
</font>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>
<tr>
<td class="box" style="border:1px solid #D6D4D4;background-color:#f8f8f8;padding:7px 0">
<table class="table" style="width:100%">
<tr>
<td width="10" style="padding:7px 0">&nbsp;</td>
<td style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<p data-html-only="1" style="border-bottom:1px solid #D6D4D4;margin:3px 0 7px;text-transform:uppercase;font-weight:500;font-size:18px;padding-bottom:10px">
Message from a customer </p>
<span style="color:#777">
You have received a new message regarding order with the reference <span style="color:#333"><strong>{order_name}</strong></span>.<br /><br />
<span style="color:#333"><strong>Customer:</strong></span> {firstname} {lastname} ({email})<br /><br />
{message}
</span>
</font>
</td>
<td width="10" style="padding:7px 0">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" class="titleblock" style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<span class="title" style="font-weight:500;font-size:28px;text-transform:uppercase;line-height:33px">Message from a customer</span>
</font>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>
<tr>
<td class="box" style="border:1px solid #D6D4D4;background-color:#f8f8f8;padding:7px 0">
<table class="table" style="width:100%">
<tr>
<td width="10" style="padding:7px 0">&nbsp;</td>
<td style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<span style="color:#777">
You have received a new message regarding booking with the reference <span style="color:#333"><strong>{order_name}</strong></span>.<br /><br />
<span style="color:#333"><strong>Name:</strong></span> {firstname} {lastname} <br /><br />
<span style="color:#333"><strong>E-mail: <a href="mailto:{email}" style="color:#337ff1">{email}</a></strong></span><br /><br />
<span style="color:#333"><strong>Customer message:</strong></span><br />{message}<br/><br />
</span>
</font>
</td>
<td width="10" style="padding:7px 0">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>

View File

@@ -1,15 +1,18 @@
[/{shop_url}]
[/{shop_url}]
Hi {firstname} {lastname},
Hi {firstname} {lastname},
You have received a new message regarding order with the reference
You have received a new message regarding booking with the reference
{order_name}.
CUSTOMER: {firstname} {lastname} ({email})
NAME: {firstname} {lastname}
{message}
E-mail: {email}
Customer message:
{message}
{shop_name} [/{shop_url}] powered by Webkul(tm)
[https://webkul.com]
[https://webkul.com]

View File

@@ -4,51 +4,51 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<title>Message from {shop_name}</title>
<style> @media only screen and (max-width: 300px){
<style> @media only screen and (max-width: 300px){
body {
width:218px !important;
margin:auto !important;
}
.table {width:195px !important;margin:auto !important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto !important;display: block !important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto !important;display: block !important;}
span.title{font-size:20px !important;line-height: 23px !important}
span.subtitle{font-size: 14px !important;line-height: 18px !important;padding-top:10px !important;display:block !important;}
span.subtitle{font-size: 14px !important;line-height: 18px !important;padding-top:10px !important;display:block !important;}
td.box p{font-size: 12px !important;font-weight: bold !important;}
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
}
.table-recap{width: 200px!important;}
.table-recap tr td, .conf_body td{text-align:center !important;}
.table-recap tr td, .conf_body td{text-align:center !important;}
.address{display: block !important;margin-bottom: 10px !important;}
.space_address{display: none !important;}
.space_address{display: none !important;}
}
@media only screen and (min-width: 301px) and (max-width: 500px) {
@media only screen and (min-width: 301px) and (max-width: 500px) {
body {width:308px!important;margin:auto!important;}
.table {width:285px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
.table {width:285px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
}
.table-recap{width: 295px !important;}
.table-recap tr td, .conf_body td{text-align:center !important;}
}
@media only screen and (min-width: 501px) and (max-width: 768px) {
body {width:478px!important;margin:auto!important;}
.table {width:450px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
}
@media only screen and (max-device-width: 480px) {
body {width:308px!important;margin:auto!important;}
.table {width:285px;margin:auto!important;}
.table {width:450px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
}
@media only screen and (max-device-width: 480px) {
body {width:308px!important;margin:auto!important;}
.table {width:285px;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
.table-recap{width: 295px!important;}
.table-recap tr td, .conf_body td{text-align:center!important;}
.table-recap tr td, .conf_body td{text-align:center!important;}
.address{display: block !important;margin-bottom: 10px !important;}
.space_address{display: none !important;}
.space_address{display: none !important;}
}
</style>
@@ -66,38 +66,37 @@
</a>
</td>
</tr>
<tr>
<td align="center" class="titleblock" style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<span class="title" style="font-weight:500;font-size:28px;text-transform:uppercase;line-height:33px">Hi {firstname} {lastname},</span>
</font>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>
<tr>
<td class="box" style="border:1px solid #D6D4D4;background-color:#f8f8f8;padding:7px 0">
<table class="table" style="width:100%">
<tr>
<td width="10" style="padding:7px 0">&nbsp;</td>
<td style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<p data-html-only="1" style="border-bottom:1px solid #D6D4D4;margin:3px 0 7px;text-transform:uppercase;font-weight:500;font-size:18px;padding-bottom:10px">
Message from {shop_name} </p>
<span style="color:#777">
You have received a new message from <span style="color:#333"><strong>{shop_name}</strong></span> regarding order with the reference <span style="color:#333"><strong>{order_name}</strong></span>.<br /><br />
<span style="color:#333"><strong>Message:</strong></span> {message}
</span>
</font>
</td>
<td width="10" style="padding:7px 0">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center" class="titleblock" style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<span class="title" style="font-weight:500;font-size:28px;text-transform:uppercase;line-height:33px">Hi {firstname} {lastname},</span>
</font>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>
<tr>
<td class="box" style="border:1px solid #D6D4D4;background-color:#f8f8f8;padding:7px 0">
<table class="table" style="width:100%">
<tr>
<td width="10" style="padding:7px 0">&nbsp;</td>
<td style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<p data-html-only="1" style="border-bottom:1px solid #D6D4D4;margin:3px 0 7px;text-transform:uppercase;font-weight:500;font-size:18px;padding-bottom:10px">
Message from {shop_name}
</p>
<span style="color:#777">
You have received a new message from <span style="color:#333"><strong>{shop_name}</strong></span> regarding booking with the reference <span style="color:#333"><strong>{order_name}</strong></span>.<br /><br />
<span style="color:#333"><strong>Message:</strong></span><br>{message}
</span>
</font>
</td>
<td width="10" style="padding:7px 0">&nbsp;</td>
</tr>
</table>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>

View File

@@ -1,13 +1,14 @@
[/{shop_url}]
[/{shop_url}]
Hi {firstname} {lastname},
Hi {firstname} {lastname},
You have received a new message from {shop_name} regarding order with
You have received a new message from {shop_name} regarding booking with
the reference {order_name}.
MESSAGE: {message}
MESSAGE:
{message}
{shop_name} [/{shop_url}] powered by Webkul(tm)
[https://webkul.com]
[https://webkul.com]

View File

@@ -4,51 +4,51 @@
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<title>Message from {shop_name}</title>
<style> @media only screen and (max-width: 300px){
<style> @media only screen and (max-width: 300px){
body {
width:218px !important;
margin:auto !important;
}
.table {width:195px !important;margin:auto !important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto !important;display: block !important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto !important;display: block !important;}
span.title{font-size:20px !important;line-height: 23px !important}
span.subtitle{font-size: 14px !important;line-height: 18px !important;padding-top:10px !important;display:block !important;}
span.subtitle{font-size: 14px !important;line-height: 18px !important;padding-top:10px !important;display:block !important;}
td.box p{font-size: 12px !important;font-weight: bold !important;}
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
}
.table-recap{width: 200px!important;}
.table-recap tr td, .conf_body td{text-align:center !important;}
.table-recap tr td, .conf_body td{text-align:center !important;}
.address{display: block !important;margin-bottom: 10px !important;}
.space_address{display: none !important;}
.space_address{display: none !important;}
}
@media only screen and (min-width: 301px) and (max-width: 500px) {
@media only screen and (min-width: 301px) and (max-width: 500px) {
body {width:308px!important;margin:auto!important;}
.table {width:285px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
.table {width:285px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
.table-recap table, .table-recap thead, .table-recap tbody, .table-recap th, .table-recap td, .table-recap tr {
display: block !important;
}
.table-recap{width: 295px !important;}
.table-recap tr td, .conf_body td{text-align:center !important;}
}
@media only screen and (min-width: 501px) and (max-width: 768px) {
body {width:478px!important;margin:auto!important;}
.table {width:450px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
}
@media only screen and (max-device-width: 480px) {
body {width:308px!important;margin:auto!important;}
.table {width:285px;margin:auto!important;}
.table {width:450px!important;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
}
@media only screen and (max-device-width: 480px) {
body {width:308px!important;margin:auto!important;}
.table {width:285px;margin:auto!important;}
.logo, .titleblock, .linkbelow, .box, .footer, .space_footer{width:auto!important;display: block!important;}
.table-recap{width: 295px!important;}
.table-recap tr td, .conf_body td{text-align:center!important;}
.table-recap tr td, .conf_body td{text-align:center!important;}
.address{display: block !important;margin-bottom: 10px !important;}
.space_address{display: none !important;}
.space_address{display: none !important;}
}
</style>
@@ -66,31 +66,34 @@
</a>
</td>
</tr>
<tr>
<td align="center" class="titleblock" style="padding:7px 0">
<span class="title" style="font-weight:500;font-size:28px;text-transform:uppercase;line-height:33px">Hi {firstname} {lastname},</span>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>
<tr>
<td class="linkbelow" style="padding:7px 0">
<span>{reply}</span>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>
<tr>
<td class="linkbelow" style="padding:7px 0">
<span>
Please do not reply directly to this email, we will not receive it. <br>
In order to reply, please use the following link: <a href="{link}" target="_blank" style="color:#337ff1">{link}</a> </span>
</td>
</tr>
<tr>
<td align="center" class="titleblock" style="padding:7px 0">
<span class="title" style="font-weight:500;font-size:28px;text-transform:uppercase;line-height:33px">{headingtext}</span>
</td>
</tr>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>
<tr>
<td class="box" style="border:1px solid #D6D4D4;background-color:#f8f8f8;padding:7px 0">
<table class="table" style="width:100%">
<tr>
<td width="10" style="padding:7px 0">&nbsp;</td>
<td style="padding:7px 0">
<font size="2" face="Open-sans, sans-serif" color="#555454">
<p data-html-only="1" style="border-bottom:1px solid #D6D4D4;margin:3px 0 7px;text-transform:uppercase;font-weight:500;font-size:18px;padding-bottom:10px">
Message from {contact_name}</p><br/
<span><span style="color:#333"><strong>Title:</strong></span> {subject}</span></br><br/>
<span><span style="color:#333"><strong>Message:</strong></span> <br/>{reply}</span>
<br/><br/>
<span>
Please do not reply directly to this email, we will not receive it. <br>
In order to reply, you can <a href="{link}" target="_blank" style="color:#337ff1">click here</a>
</span>
</td>
</tr>
</table>
</td>
<tr>
<td class="space_footer" style="padding:0!important">&nbsp;</td>
</tr>

View File

@@ -1,14 +1,17 @@
[/{shop_url}]
[/{shop_url}]
Hi {firstname} {lastname},
{headingtext},
{reply}
Title: {subject}
Please do not reply directly to this email, we will not receive it.
Message:
{reply}
In order to reply, please use the following link: {link} [/{link}]
Please do not reply directly to this email, we will not receive it.
In order to reply, you can click here [/{link}].
{shop_name} [/{shop_url}] powered by Webkul(tm)
[https://webkul.com]
[https://webkul.com]

View File

@@ -70,25 +70,56 @@
{/if}
<div class="col-sm-6 {if !(isset($gblHtlAddress) && $gblHtlAddress) && !(isset($gblHtlPhone) && $gblHtlPhone) && !(isset($gblHtlEmail) && $gblHtlEmail)} col-sm-offset-3 {/if}">
{block name='contact_form_content'}
{if isset($customerThread.token)}
<form action="{$link->getPageLink('contact', null, null, array('token' => $customerThread.token))}" method="post" class="contact-form-box" enctype="multipart/form-data">
{else}
<form action="{$link->getPageLink('contact')}" method="post" class="contact-form-box" enctype="multipart/form-data">
{if isset($customerThread.id_contact) && $customerThread.id_contact && $contacts|count}
{assign var=flag value=true}
{foreach from=$contacts item=contact}
{if $contact.id_contact == $customerThread.id_contact}
<input type="text" class="form-control" id="contact_name" name="contact_name" value="{$contact.name|escape:'html':'UTF-8'}" readonly="readonly" />
<input type="hidden" name="id_contact" value="{$contact.id_contact|intval}" />
{$flag=false}
{/if}
{if isset($displayContactName) && $displayContactName}
<div class="form-group row">
<div class="col-sm-12">
<label for="user_name" class="control-label">
{l s='Name'}{if isset($contactNameRequired) && $contactNameRequired}*{/if}
</label>
<input class="form-control contact_input" type="text" id="user_name" name="user_name" value="{if isset($smarty.post.user_name)}{$smarty.post.user_name}{elseif isset($customerThread.user_name)}{$customerThread.user_name|escape:'html':'UTF-8'}{elseif isset($customerName)}{$customerName}{/if}" {if isset($customerThread.user_name)} readonly{/if}/>
</div>
</div>
{/if}
<div class="form-group row">
<div class="col-sm-12">
<label for="Email" class="control-label">
{l s='Email'}*
</label>
{if isset($customerThread.email)}
<input class="form-control contact_input" type="email" id="email" name="from" value="{if isset($customerThread.email)}{$customerThread.email|escape:'html':'UTF-8'}" readonly="readonly"{/if} />
{else}
<input class="form-control contact_input validate" type="email" id="email" name="from" data-validate="isEmail" value="{if isset($smarty.post.email)}{$smarty.post.email}{else}{$email|escape:'html':'UTF-8'}{/if}" />
{/if}
{/foreach}
{if $flag && isset($contacts.0.id_contact)}
<input type="text" class="form-control" id="contact_name" name="contact_name" value="{$contacts.0.name|escape:'html':'UTF-8'}" readonly="readonly" />
<input type="hidden" name="id_contact" value="{$contacts.0.id_contact|intval}" />
{/if}
{else}
</div>
</div>
{if isset($displayContactPhone) && $displayContactPhone}
<div class="form-group row">
<div class="col-sm-12">
<label for="phone" class="control-label">
{l s='Phone'}{if isset($contactPhoneRequired) && $contactPhoneRequired}*{/if}
</label>
<input class="form-control contact_input" type="text" id="phone" name="phone" value="{if isset($smarty.post.phone)}{$smarty.post.phone}{else if isset($customerThread.phone)}{$customerThread.phone|escape:'html':'UTF-8'}{elseif isset($customerPhone)}{$customerPhone}{/if}" {if isset($customerThread.phone)}readonly="readonly"{/if}/>
</div>
</div>
{/if}
<div class="form-group row">
<div class="col-sm-12">
<label for="subject" class="control-label">
{l s='Title'}*
</label>
<input class="form-control contact_input" type="text" id="subject" name="subject" value="{if isset($smarty.post.subject)}{$smarty.post.subject}{else if isset($customerThread.subject)}{$customerThread.subject|escape:'html':'UTF-8'}{/if}" {if isset($customerThread.subject)}readonly="readonly"{/if}/>
</div>
</div>
{if !isset($customerThread.id_contact) && isset($allowContactSelection) && $allowContactSelection}
<div class="form-group row">
<div class="col-sm-12">
<label for="message" class="control-label">
{l s='Subject'}
{l s='Send To'}*
</label>
<div class="dropdown">
<button class="form-control contact_type_input" type="button" data-toggle="dropdown">
@@ -112,23 +143,13 @@
</div>
</div>
</div>
{elseif isset($customerThread.id_contact) && isset($allowContactSelection) && $allowContactSelection}
<input type="hidden" id="id_contact" name="id_contact" value="{$customerThread.id_contact|escape:'html':'UTF-8'}"/>
{/if}
<div class="form-group row">
<div class="col-sm-12">
<label for="price" class="control-label">
{l s='Email'}
</label>
{if isset($customerThread.email)}
<input class="form-control contact_input" type="email" id="email" name="from" value="{$customerThread.email|escape:'html':'UTF-8'}" readonly="readonly" />
{else}
<input class="form-control contact_input validate" type="email" id="email" name="from" data-validate="isEmail" value="{$email|escape:'html':'UTF-8'}" />
{/if}
</div>
</div>
<div class="form-group row">
<div class="col-sm-12">
<label for="message" class="control-label">
{l s='Message/Query'}
{l s='Message/Query'}*
</label>
<textarea class="form-control contact_textarea" id="message" name="message">{if isset($message)}{$message|escape:'html':'UTF-8'|stripslashes}{/if}</textarea>
</div>
@@ -144,6 +165,9 @@
</div>
</div>
{/if}
<div class="form-group">
{l s='* Required fields'}
</div>
{hook h='displayGDPRConsent' moduleName='contactform'}
{hook h='displayContactFormFieldsAfter'}
<div class="form-group">

View File

@@ -200,13 +200,13 @@
font-size: 16px;
font-weight: 600;
height: 47px;
margin-bottom: 20px;
margin-bottom: 5px;
padding: 10px;
}
.contact_textarea {
color: #9a9a9a;
height: 150px !important;
margin-bottom: 20px;
margin-bottom: 5px;
padding: 10px;
font-size: 16px;
font-weight: 600;

View File

@@ -606,7 +606,7 @@
color: #55c65e;
}
#sendOrderMessage {
#sendOrderMessage, .select-room-type .card-subheader {
margin-bottom: 10px;
}
@@ -1121,4 +1121,4 @@
text-decoration: underline;
text-underline-offset: 3px;
}
/* Guest Tracking page styles - end */
/* Guest Tracking page styles - end */

View File

@@ -596,6 +596,9 @@
<form action="{$link->getPageLink('order-detail', true)|escape:'html':'UTF-8'}" method="post" class="std" id="sendOrderMessage">
<div class="form-group select-room-type">
<label for="id_product">{l s='Room Type'}</label>
<p class="card-subheader text-muted">
{l s='To add a comment about a room type, please select one first.'}
</p>
<select name="id_product" class="form-control">
<option value="0">{l s='-- Choose --'}</option>