diff --git a/core/lib/Thelia/Core/Template/Element/BaseLoop.php b/core/lib/Thelia/Core/Template/Element/BaseLoop.php index 7e803c43a..1ed7292cf 100755 --- a/core/lib/Thelia/Core/Template/Element/BaseLoop.php +++ b/core/lib/Thelia/Core/Template/Element/BaseLoop.php @@ -23,12 +23,18 @@ namespace Thelia\Core\Template\Element; +use Propel\Runtime\ActiveQuery\Criteria; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Thelia\Core\Template\Element\Exception\SearchLoopException; use Thelia\Core\Template\Loop\Argument\Argument; use Propel\Runtime\ActiveQuery\ModelCriteria; use Thelia\Core\Security\SecurityContext; +use Thelia\Type\AlphaNumStringListType; +use Thelia\Type\EnumListType; +use Thelia\Type\EnumType; +use Thelia\Type\TypeCollection; /** * @@ -84,13 +90,43 @@ abstract class BaseLoop */ protected function getDefaultArgs() { - return array( - Argument::createIntTypeArgument('offset', 0), - Argument::createIntTypeArgument('page'), - Argument::createIntTypeArgument('limit', PHP_INT_MAX), + $defaultArgs = array( Argument::createBooleanTypeArgument('backend_context', false), Argument::createBooleanTypeArgument('force_return', false), ); + + if(true === $this->countable) { + $defaultArgs = array_merge($defaultArgs, array( + Argument::createIntTypeArgument('offset', 0), + Argument::createIntTypeArgument('page'), + Argument::createIntTypeArgument('limit', PHP_INT_MAX), + )); + } + + if($this instanceof SearchLoopInterface) { + $defaultArgs = array_merge($defaultArgs, array( + Argument::createAnyTypeArgument('search_term'), + new Argument( + 'search_in', + new TypeCollection( + new EnumListType($this->getSearchIn()) + ) + ), + new Argument( + 'search_mode', + new TypeCollection( + new EnumType(array( + SearchLoopInterface::MODE_ANY_WORD, + SearchLoopInterface::MODE_SENTENCE, + SearchLoopInterface::MODE_STRICT_SENTENCE, + )) + ), + SearchLoopInterface::MODE_STRICT_SENTENCE + ) + )); + } + + return $defaultArgs; } /** @@ -205,6 +241,30 @@ abstract class BaseLoop */ protected function search(ModelCriteria $search, &$pagination = null) { + if($this instanceof SearchLoopInterface) { + $searchTerm = $this->getSearch_term(); + $searchIn = $this->getSearch_in(); + $searchMode = $this->getSearch_mode(); + if(null !== $searchTerm && null !== $searchIn) { + + switch($searchMode) { + case SearchLoopInterface::MODE_ANY_WORD: + $searchCriteria = Criteria::IN; + $searchTerm = explode(' ', $searchTerm); + break; + case SearchLoopInterface::MODE_SENTENCE: + $searchCriteria = Criteria::LIKE; + $searchTerm = '%' . $searchTerm . '%'; + break; + case SearchLoopInterface::MODE_STRICT_SENTENCE: + $searchCriteria = Criteria::EQUAL; + break; + } + + $this->doSearch($search, $searchTerm, $searchIn, $searchCriteria); + } + } + if ($this->getArgValue('page') !== null) { return $this->searchWithPagination($search, $pagination); } else { diff --git a/core/lib/Thelia/Core/Template/Element/Exception/SearchLoopException.php b/core/lib/Thelia/Core/Template/Element/Exception/SearchLoopException.php new file mode 100644 index 000000000..f1ce6657f --- /dev/null +++ b/core/lib/Thelia/Core/Template/Element/Exception/SearchLoopException.php @@ -0,0 +1,40 @@ +. */ +/* */ +/*************************************************************************************/ + +namespace Thelia\Core\Template\Element\Exception; + +class SearchLoopException extends \RuntimeException +{ + const UNKNOWN_EXCEPTION = 0; + + public function __construct($message, $code = null, $arguments = array(), $previous = null) + { + if (is_array($arguments)) { + $this->arguments = $arguments; + } + if ($code === null) { + $code = self::UNKNOWN_EXCEPTION; + } + parent::__construct($message, $code, $previous); + } +} diff --git a/core/lib/Thelia/Core/Template/Element/SearchLoopInterface.php b/core/lib/Thelia/Core/Template/Element/SearchLoopInterface.php new file mode 100644 index 000000000..48876012f --- /dev/null +++ b/core/lib/Thelia/Core/Template/Element/SearchLoopInterface.php @@ -0,0 +1,44 @@ +. */ +/* */ +/*************************************************************************************/ +namespace Thelia\Core\Template\Element; + +use Symfony\Component\Validator\ExecutionContextInterface; + +/** + * + * @author Etienne Roudeix + * + */ +interface SearchLoopInterface +{ + const MODE_ANY_WORD = 'any_word'; + const MODE_SENTENCE = 'sentence'; + const MODE_STRICT_SENTENCE = 'strict_sentence'; + + /** + * @return array of available field to search in + */ + public function getSearchIn(); + + public function doSearch(&$search, $searchTerm, $searchIn, $searchCriteria); +} diff --git a/core/lib/Thelia/Core/Template/Loop/Customer.php b/core/lib/Thelia/Core/Template/Loop/Customer.php index e811606c0..e49d9ef76 100755 --- a/core/lib/Thelia/Core/Template/Loop/Customer.php +++ b/core/lib/Thelia/Core/Template/Loop/Customer.php @@ -28,6 +28,7 @@ use Thelia\Core\Template\Element\BaseLoop; use Thelia\Core\Template\Element\LoopResult; use Thelia\Core\Template\Element\LoopResultRow; +use Thelia\Core\Template\Element\SearchLoopInterface; use Thelia\Core\Template\Loop\Argument\ArgumentCollection; use Thelia\Core\Template\Loop\Argument\Argument; @@ -44,7 +45,7 @@ use Thelia\Type; * @package Thelia\Core\Template\Loop * @author Etienne Roudeix */ -class Customer extends BaseLoop +class Customer extends BaseLoop implements SearchLoopInterface { public $timestampable = true; @@ -67,6 +68,47 @@ class Customer extends BaseLoop ); } + public function getSearchIn() + { + return array( + "ref", + "firstname", + "lastname", + "email", + ); + } + + /** + * @param CustomerQuery $search + * @param $searchTerm + * @param $searchIn + * @param $searchCriteria + */ + public function doSearch(&$search, $searchTerm, $searchIn, $searchCriteria) + { + + $search->_and(); + foreach($searchIn as $index => $searchInElement) { + if($index > 0) { + $search->_or(); + } + switch($searchInElement) { + case "ref": + $search->filterByRef($searchTerm, $searchCriteria); + break; + case "firstname": + $search->filterByFirstname($searchTerm, $searchCriteria); + break; + case "lastname": + $search->filterByLastname($searchTerm, $searchCriteria); + break; + case "email": + $search->filterByEmail($searchTerm, $searchCriteria); + break; + } + } + } + /** * @param $pagination * diff --git a/core/lib/Thelia/Core/Template/Loop/Order.php b/core/lib/Thelia/Core/Template/Loop/Order.php index 5b1e97cc1..140a64fc3 100755 --- a/core/lib/Thelia/Core/Template/Loop/Order.php +++ b/core/lib/Thelia/Core/Template/Loop/Order.php @@ -28,8 +28,12 @@ use Thelia\Core\Template\Element\BaseLoop; use Thelia\Core\Template\Element\LoopResult; use Thelia\Core\Template\Element\LoopResultRow; +use Thelia\Core\Template\Element\SearchLoopInterface; use Thelia\Core\Template\Loop\Argument\ArgumentCollection; use Thelia\Core\Template\Loop\Argument\Argument; +use Thelia\Model\CustomerQuery; +use Thelia\Model\OrderAddress; +use Thelia\Model\OrderAddressQuery; use Thelia\Model\OrderQuery; use Thelia\Type\TypeCollection; use Thelia\Type; @@ -38,8 +42,9 @@ use Thelia\Type; * @package Thelia\Core\Template\Loop * * @author Franck Allimant + * @author Etienne Roudeix */ -class Order extends BaseLoop +class Order extends BaseLoop implements SearchLoopInterface { public $countable = true; public $timestampable = true; @@ -74,6 +79,59 @@ class Order extends BaseLoop ); } + public function getSearchIn() + { + return array( + "ref", + "customer_ref", + "customer_firstname", + "customer_lastname", + "customer_email", + ); + } + + /** + * @param OrderQuery $search + * @param $searchTerm + * @param $searchIn + * @param $searchCriteria + */ + public function doSearch(&$search, $searchTerm, $searchIn, $searchCriteria) + { + + $search->_and(); + foreach($searchIn as $index => $searchInElement) { + if($index > 0) { + $search->_or(); + } + switch($searchInElement) { + case "ref": + $search->filterByRef($searchTerm, $searchCriteria); + break; + case "customer_ref": + $search->filterByCustomer( + CustomerQuery::create()->filterByRef($searchTerm, $searchCriteria)->find() + ); + break; + case "customer_firstname": + $search->filterByOrderAddressRelatedByInvoiceOrderAddressId( + OrderAddressQuery::create()->filterByFirstname($searchTerm, $searchCriteria)->find() + ); + break; + case "customer_lastname": + $search->filterByOrderAddressRelatedByInvoiceOrderAddressId( + OrderAddressQuery::create()->filterByLastname($searchTerm, $searchCriteria)->find() + ); + break; + case "customer_email": + $search->filterByCustomer( + CustomerQuery::create()->filterByEmail($searchTerm, $searchCriteria)->find() + ); + break; + } + } + } + /** * @param $pagination * diff --git a/core/lib/Thelia/Core/Template/Loop/Product.php b/core/lib/Thelia/Core/Template/Loop/Product.php index 01a5ed15f..4d32b7101 100755 --- a/core/lib/Thelia/Core/Template/Loop/Product.php +++ b/core/lib/Thelia/Core/Template/Loop/Product.php @@ -30,6 +30,7 @@ use Thelia\Core\Template\Element\BaseLoop; use Thelia\Core\Template\Element\LoopResult; use Thelia\Core\Template\Element\LoopResultRow; +use Thelia\Core\Template\Element\SearchLoopInterface; use Thelia\Core\Template\Loop\Argument\ArgumentCollection; use Thelia\Core\Template\Loop\Argument\Argument; @@ -53,7 +54,7 @@ use Thelia\Type; * @package Thelia\Core\Template\Loop * @author Etienne Roudeix */ -class Product extends BaseI18nLoop +class Product extends BaseI18nLoop implements SearchLoopInterface { public $timestampable = true; public $versionable = true; @@ -129,6 +130,39 @@ class Product extends BaseI18nLoop ); } + public function getSearchIn() + { + return array( + "ref", + "title", + ); + } + + /** + * @param ProductQuery $search + * @param $searchTerm + * @param $searchIn + * @param $searchCriteria + */ + public function doSearch(&$search, $searchTerm, $searchIn, $searchCriteria) + { + + $search->_and(); + foreach($searchIn as $index => $searchInElement) { + if($index > 0) { + $search->_or(); + } + switch($searchInElement) { + case "ref": + $search->filterByRef($searchTerm, $searchCriteria); + break; + case "title": + $search->where("CASE WHEN NOT ISNULL(`requested_locale_i18n`.ID) THEN `requested_locale_i18n`.`TITLE` ELSE `default_locale_i18n`.`TITLE` END ".$searchCriteria." ?", $searchTerm, \PDO::PARAM_STR); + break; + } + } + } + /** * @param $pagination * diff --git a/core/lib/Thelia/Model/OrderQuery.php b/core/lib/Thelia/Model/OrderQuery.php index 8d5e4c085..0fff5dba1 100755 --- a/core/lib/Thelia/Model/OrderQuery.php +++ b/core/lib/Thelia/Model/OrderQuery.php @@ -2,6 +2,7 @@ namespace Thelia\Model; +use Propel\Runtime\ActiveQuery\Criteria; use Propel\Runtime\Exception\PropelException; use Propel\Runtime\Propel; use Thelia\Model\Base\OrderQuery as BaseOrderQuery; @@ -52,4 +53,5 @@ class OrderQuery extends BaseOrderQuery return $obj; } + } // OrderQuery diff --git a/templates/admin/default/search.html b/templates/admin/default/search.html new file mode 100644 index 000000000..a39eda982 --- /dev/null +++ b/templates/admin/default/search.html @@ -0,0 +1,238 @@ +{extends file="admin-layout.tpl"} + +{block name="page-title"}{intl l='Modules'}{/block} + +{block name="check-resource"}admin.configuration.search{/block} +{block name="check-access"}view{/block} + +{block name="main-content"} +
+ +
+ + + + {module_include location='modules_top'} + +
+
+ + {* customer search *} +
+
+ + + + + + + + + + + + + + + + + + + + {loop name="customer_list" type="customer" current="false" visible="*" backend_context="1" search_term=$smarty.get.search_term search_in="ref,firstname,lastname,email"} + {assign "lastOrderDate" ''} + {assign "lastOrderAmount" ''} + {assign "lastOrderCurrency" ''} + + {loop type="order" name="last-order" customer=$ID order="create-date-reverse" limit="1"} + {assign "lastOrderDate" "{format_date date=$CREATE_DATE}"} + {assign "lastOrderAmount" "{format_number number=$TOTAL_TAXED_AMOUNT}"} + {loop type="currency" name="order-currency" id=$CURRENCY} + {assign "lastOrderCurrency" $SYMBOL} + {/loop} + {/loop} + + + + + + + + + {module_include location='customer_list_row'} + + + + + + + + {/loop} + +
+ {intl l='Customer'} +
+ {intl l="customer ref"} + + {intl l="company"} + + {intl l="firstname & lastname"} + + {intl l="last order"} + {intl l='order amount'}{intl l="Actions"}
{$REF} + {$COMPANY} + + {$FIRSTNAME} {$LASTNAME} + + {$lastOrderDate} + + {$lastOrderCurrency} {$lastOrderAmount} + +
+ + {loop type="auth" name="can_change" role="ADMIN" resource="admin.customer" access="UPDATE"} + + {/loop} + {loop type="auth" name="can_send_mail" role="ADMIN" resource="admin.customer" access="VIEW"} + + {/loop} + {loop type="auth" name="can_delete" role="ADMIN" resource="admin.customer" access="DELETE"} + + {/loop} +
+
+
+
+ {* end customer search *} + + {* order search *} +
+
+ + + + + + + + + + + + + + + + + {loop type="order" name="order-search" backend_context=1 customer="*" search_term=$smarty.get.search_term search_in="ref,customer_ref,customer_firstname,customer_lastname,customer_email"} + {loop type="order_address" name="order-invoice-address" id=$INVOICE_ADDRESS} + {assign "orderInvoiceFirstName" $FIRSTNAME} + {assign "orderInvoiceLastName" $LASTNAME} + {assign "orderInvoiceCompany" $COMPANY} + {/loop} + + {loop type="order-status" name="order-status" id=$STATUS} + {assign "orderStatus" $TITLE} + {assign "orderStatusLabel" "order_$CODE"} + {/loop} + + + + + + + + + + + + + {/loop} + +
+ {intl l='Orders'} +
{intl l="Order n°"}{intl l="Date & Hour"}{intl l="Company"}{intl l="Name"}{intl l="Amount"}{intl l="Status"}{intl l="Actions"}
{$REF}{format_date date=$CREATE_DATE}{$orderInvoiceCompany}{$orderInvoiceFirstName|ucwords} {$orderInvoiceLastName|upper}{$TOTAL_TAXED_AMOUNT}{$orderStatus} +
+ + {loop type="auth" name="can_change" role="ADMIN" resource="admin.order" access="UPDATE"} + + {/loop} +
+
+
+
+ {* end order search *} + + {* product search *} +
+
+ + + + + + + + + + + + + + + {loop type="product" name="product-search" visible="*" search_mode="sentence" search_term=$smarty.get.search_term search_in="ref,title"} + + + + + + + + + + + + + {/loop} + +
+ {intl l='Product'} +
{intl l="ID"}{intl l="Reference"}{intl l="Product title"}{intl l="Actions"}
{$ID} + {loop type="image" name="cat_image" source="product" source_id="$ID" limit="1" width="50" height="50" resize_mode="crop" backend_context="1"} + + {$TITLE} + + {/loop} + + {$REF}{$TITLE} +
+ {loop type="auth" name="can_change" role="ADMIN" resource="admin.product" access="UPDATE"} + + {/loop} +
+
+
+
+ {* end product search *} + +
+
+ + {module_include location='modules_bottom'} + +
+
+ +{/block} + +{block name="javascript-initialization"} + + + +{/block} \ No newline at end of file