From 080b08ca7b1445f016322d4a4b15eb7615b9f443 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 13:01:16 -0600 Subject: [PATCH 01/28] adds ProductComment hook --- src/Hook/ProductComment.php | 70 +++++++++++++++++++++++++++++++++++++ src/HookDispatcher.php | 1 + 2 files changed, 71 insertions(+) create mode 100755 src/Hook/ProductComment.php diff --git a/src/Hook/ProductComment.php b/src/Hook/ProductComment.php new file mode 100755 index 000000000..9c01c30d6 --- /dev/null +++ b/src/Hook/ProductComment.php @@ -0,0 +1,70 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + */ + +namespace PrestaShop\Module\FacetedSearch\Hook; + +class ProductComment extends AbstractHook +{ + const AVAILABLE_HOOKS = [ + 'actionObjectProductCommentValidateAfter', + ]; + + /** + * Product Comment Validate After + * + * Smart index after validating a comment + * + * @param array $params + */ + public function actionObjectProductCommentValidateAfter(array $params) + { + $grade = $params['object']; + + $commentLogRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index_log` + WHERE id_comment = ' . $grade->id . ' AND indexed = 1'); + + if (empty($commentLogRow)){ + $gradeCommentRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index` + WHERE id_product =' . $grade->id_product); + + if (empty($gradeCommentRow) || !$gradeCommentRow){ + //insert the first index record for this comment + $this->database->execute('INSERT INTO `'._DB_PREFIX_.'layered_comment_index` (`id_product`, `score`, `avg_score`) VALUES ('.$grade->id_product.', '.$grade->grade.','.$grade->grade.')'); + + $this->addCommentIndexLog($grade); + }else{ + //update index for the comment + $productCommentLogRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index_log` + WHERE id_product = ' . $grade->id_product . ' AND indexed = 1'); + + $newGradeValue = (int)$gradeCommentRow[0]['score'] + (int)$grade->grade; + $avg_score = $newGradeValue/(count($productCommentLogRow)+1); + + $this->database->execute('update ' . _DB_PREFIX_ . 'layered_comment_index set score='.$newGradeValue.', avg_score= '.$avg_score .' where id_product=' . $grade->id_product); + + $this->addCommentIndexLog($grade); + } + } + } + + public function addCommentIndexLog($comment){ + $this->database->execute('INSERT INTO `'._DB_PREFIX_.'layered_comment_index_log` (`id_comment`, `indexed`, `id_product`) VALUES ('.$comment->id.', 1,'. $comment->id_product .')'); + } +} diff --git a/src/HookDispatcher.php b/src/HookDispatcher.php index 459334245..35805c053 100644 --- a/src/HookDispatcher.php +++ b/src/HookDispatcher.php @@ -40,6 +40,7 @@ class HookDispatcher Hook\Product::class, Hook\ProductSearch::class, Hook\SpecificPrice::class, + Hook\ProductComment::class, ]; /** From a029235f3b03d53ae14ff07c1edb552b01cb8a4a Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 13:15:32 -0600 Subject: [PATCH 02/28] methods to index reviews --- ps_facetedsearch.php | 90 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index 5029e7bf9..4df9fb7c7 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -165,6 +165,10 @@ protected function getDefaultFilters() 'label' => 'Product price filter (slider)', 'slider' => true, ], + 'layered_selection_review_star' => [ + 'label' => 'Avg. Customer Review', + 'star' => true + ], ]; } @@ -213,6 +217,8 @@ public function install() $this->rebuildPriceIndexTable(); $this->installIndexableAttributeTable(); $this->installProductAttributeTable(); + $this->rebuildCommentIndexTable(); + $this->indexReviews(true); if ($productsCount < static::LOCK_TOO_MANY_PRODUCTS) { $this->fullPricesIndexProcess(); @@ -880,7 +886,7 @@ public function rebuildLayeredStructure() `id_shop` INT(11) UNSIGNED NOT NULL, `id_category` INT(10) UNSIGNED NOT NULL, `id_value` INT(10) UNSIGNED NULL DEFAULT \'0\', - `type` ENUM(\'category\',\'id_feature\',\'id_attribute_group\',\'quantity\',\'condition\',\'manufacturer\',\'weight\',\'price\') NOT NULL, + `type` ENUM(\'category\',\'id_feature\',\'id_attribute_group\',\'quantity\',\'condition\',\'manufacturer\',\'weight\',\'price\',\'review\') NOT NULL, `position` INT(10) UNSIGNED NOT NULL, `filter_type` int(10) UNSIGNED NOT NULL DEFAULT 0, `filter_show_limit` int(10) UNSIGNED NOT NULL DEFAULT 0, @@ -1162,6 +1168,8 @@ public function buildLayeredCategories() } elseif (substr($key, 0, 23) == 'layered_selection_feat_') { $sqlInsert .= '(' . (int) $idCategory . ', ' . (int) $idShop . ', ' . (int) str_replace('layered_selection_feat_', '', $key) . ', \'id_feature\',' . (int) $n . ', ' . (int) $limit . ', ' . (int) $type . '),'; + } elseif ($key == 'layered_selection_review_star') { + $sqlInsert .= '(' . (int) $idCategory . ', ' . (int) $idShop . ', NULL,\'review\',' . (int) $n . ', ' . (int) $limit . ', ' . (int) $type . '),'; } ++$nbSqlValuesToInsert; @@ -1503,4 +1511,84 @@ public function getWidgetVariables($hookName, array $configuration) { return []; } + + /** + * Install review indexes table + */ + public function rebuildCommentIndexTable() + { + $this->getDatabase()->execute('DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'layered_comment_index`'); + + $this->getDatabase()->execute('DROP TABLE IF EXISTS `' . _DB_PREFIX_ . 'layered_comment_index_log`'); + + $this->getDatabase()->execute( + 'CREATE TABLE `' . _DB_PREFIX_ . 'layered_comment_index` ( + `id_product` INT NOT NULL, + `score` INT NOT NULL, + `avg_score` FLOAT NOT NULL, + PRIMARY KEY (`id_product`), + INDEX `score` (`score`), + INDEX `avg_score` (`score`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;' + ); + + $this->getDatabase()->execute( + 'CREATE TABLE `' . _DB_PREFIX_ . 'layered_comment_index_log` ( + `id_comment` INT NOT NULL, + `id_product` INT NOT NULL, + `indexed` TINYINT(1) NOT NULL, + PRIMARY KEY (`id_comment`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;' + ); + } + + public function indexReviews($full = false) + { + if ($full) { + $query = 'SELECT avg(`grade`) as avg_grade, sum(`grade`) as sum_grade, `id_product` FROM ' . _DB_PREFIX_ . 'product_comment +group by id_product'; + + foreach ($this->getDatabase()->executeS($query) as $comment) { + $this->addCommentIndex($comment); + } + + $query = 'SELECT `id_product_comment`, `id_product` FROM ' . _DB_PREFIX_ . 'product_comment'; + + foreach ($this->getDatabase()->executeS($query) as $commentLog) { + $this->addCommentIndexLog($commentLog); + } + + } else { + $query = 'select pc.id_product_comment, pc.id_product, pc.grade from ps_product_comment as pc +left join ps_layered_comment_index_log as lc +on pc.id_product_comment = lc.id_comment +where lc.indexed is null;'; + + //returns non matching records + foreach ($this->getDatabase()->executeS($query) as $commentLog) { + $gradeCommentRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index` + WHERE id_product =' . $commentLog['id_product']); + + $productCommentLogRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index_log` + WHERE id_product = ' . $commentLog['id_product'] . ' AND indexed = 1'); + + $newGradeValue = (int)$gradeCommentRow[0]['score'] + (int)$commentLog['grade']; + $avg_score = $newGradeValue/(count($productCommentLogRow)+1); + + $this->database->execute('update ' . _DB_PREFIX_ . 'layered_comment_index set score='.$newGradeValue.', avg_score= '.$avg_score .' where id_product=' . $commentLog['id_product']); + + $this->addCommentIndexLog($commentLog); + } + } + + return "true"; + } + + public function addCommentIndex($comment){ + $this->database->execute('INSERT INTO `'._DB_PREFIX_.'layered_comment_index` (`id_product`, `score`, `avg_score`) VALUES ('.$comment['id_product'].', '.$comment['sum_grade'].','.$comment['avg_grade'].')'); + } + + public function addCommentIndexLog($commentLog){ + $this->database->execute('INSERT INTO `'._DB_PREFIX_.'layered_comment_index_log` (`id_comment`, `indexed`, `id_product`) VALUES ('.$commentLog['id_product_comment'].', 1,'. $commentLog['id_product'] .')'); + } } From 9666de21427ebeb5e8c155a89b73215455d8faa7 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 13:21:03 -0600 Subject: [PATCH 03/28] admin items --- views/templates/admin/view.tpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/views/templates/admin/view.tpl b/views/templates/admin/view.tpl index 54ad7a6cd..af4f5a15c 100644 --- a/views/templates/admin/view.tpl +++ b/views/templates/admin/view.tpl @@ -69,6 +69,8 @@ {if !empty($filter['slider'])}

{l s='List of ranges' d='Modules.Facetedsearch.Admin'}

+ {elseif !empty($filter['star'])} +

{l s='List of stars' d='Modules.Facetedsearch.Admin'}

{else}
+ + {l s='Yes' d='Admin.Global'} + {l s='No' d='Admin.Global'} + + + +
+
+ {l s='Avg. Customer Reviews' d='Modules.Facetedsearch.Admin'} +
+
+
+
+ +
+ +

{l s='List of ranges' d='Modules.Facetedsearch.Admin'}

+
+
+ {if $attribute_groups|count > 0} {foreach $attribute_groups as $attribute_group}
  • From f9f96a682f66f934e36f46953eb3325cc96b3682 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 13:32:50 -0600 Subject: [PATCH 05/28] adds review block --- src/Filters/Block.php | 68 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/Filters/Block.php b/src/Filters/Block.php index fe582b66a..8889e1891 100644 --- a/src/Filters/Block.php +++ b/src/Filters/Block.php @@ -115,6 +115,9 @@ public function getFilterBlock( case 'price': $filterBlocks[] = $this->getPriceRangeBlock($filter, $selectedFilters, $nbProducts); break; + case 'review': + $filterBlocks[] = $this->getReviweBlock($filter, $selectedFilters); + break; case 'weight': $filterBlocks[] = $this->getWeightRangeBlock($filter, $selectedFilters, $nbProducts); break; @@ -979,4 +982,69 @@ private function preparePriceSpecifications() 'currencySymbol' => $currency->sign, ]; } + + /** + * Get the reviews filter block + * + * @param array $filter + * @param array $selectedFilters + * + * @return array + */ + private function getReviweBlock($filter, $selectedFilters) + { + $values = [ + '4' => ['name'=>"4"], + '3' => ['name'=>"3"], + '2' => ['name'=>"2"], + '1' => ['name'=>"1"], + ]; + + $query = 'SELECT count(g.grade) as count ,g.grade FROM (SELECT t.id_product, case +when t.avg_grade between 1 and 1.99 then "1" +when t.avg_grade between 2 and 2.99 then "2" +when t.avg_grade between 3 and 3.99 then "3" +when t.avg_grade between 4 and 5 then "4" +end as grade + FROM (SELECT avg(h.grade) as avg_grade, + h.id_product FROM + (select pc.id_product, pc.grade from ' . _DB_PREFIX_ . 'product_comment as pc +inner join (SELECT * FROM ' . _DB_PREFIX_ . 'category_product where id_category = '.$_GET['id_category'].') cp +on pc.id_product = cp.id_product) h +group by id_product) t) g +group by g.grade +order by g.grade ASC'; + $gradeCounts = $this->database->executeS($query); + + if (!empty($gradeCounts)){ + foreach ($values as $value){ + $nbr = 0; + foreach ($gradeCounts as $count){ + if ((int)$value['name'] <= $count['grade']){ + $nbr += $count['count']; + } + } + $values[$value['name']]['nbr'] = $nbr; + } + } + + if (isset($selectedFilters['review'])){ + $reviewValues = $selectedFilters['review']; + foreach ($reviewValues as $rv){ + $values[$rv]['checked'] = true; + } + } + + $reviewBlock = [ + 'type_lite' => 'review', + 'type' => 'review', + 'id_key' => 0, + 'name' => $this->context->getTranslator()->trans('Avg. Customer Reviews', [], 'Modules.Facetedsearch.Shop'), + 'values' => $values, + 'filter_show_limit' => (int) $filter['filter_show_limit'], + 'filter_type' => Converter::WIDGET_TYPE_STAR, + ]; + + return $reviewBlock; + } } From 1c99bff2a4d08efca70814f6f7f6c6a1eeda4328 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 13:34:52 -0600 Subject: [PATCH 06/28] adds review type to converter --- src/Filters/Converter.php | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/Filters/Converter.php b/src/Filters/Converter.php index 45a35d31d..15ffb4f12 100644 --- a/src/Filters/Converter.php +++ b/src/Filters/Converter.php @@ -38,6 +38,7 @@ class Converter const WIDGET_TYPE_RADIO = 1; const WIDGET_TYPE_DROPDOWN = 2; const WIDGET_TYPE_SLIDER = 3; + const WIDGET_TYPE_STAR = 4; const TYPE_ATTRIBUTE_GROUP = 'id_attribute_group'; const TYPE_AVAILABILITY = 'availability'; @@ -48,6 +49,7 @@ class Converter const TYPE_MANUFACTURER = 'manufacturer'; const TYPE_PRICE = 'price'; const TYPE_WEIGHT = 'weight'; + const TYPE_REVIEW = 'review'; const PROPERTY_URL_NAME = 'url_name'; const PROPERTY_COLOR = 'color'; @@ -195,6 +197,34 @@ public function getFacetsFromFilterBlocks(array $filterBlocks) $facet->addFilter($filter); + break; + case self::TYPE_REVIEW: + $type = $filterBlock['type']; + + $facet + ->setType($type) + ->setMultipleSelectionAllowed(false); + + $filters = []; + foreach ($filterBlock['values'] as $id => $filterArray) { + $filter = new Filter(); + $filter + ->setType($type) + ->setLabel($filterArray['name']) + ->setMagnitude($filterArray['nbr']) + ->setValue($id); + + if (array_key_exists('checked', $filterArray)) { + $filter->setActive($filterArray['checked']); + } + + $filters[] = $filter; + } + + foreach ($filters as $filter) { + $facet->addFilter($filter); + } + break; } @@ -215,6 +245,10 @@ public function getFacetsFromFilterBlocks(array $filterBlocks) $facet->setMultipleSelectionAllowed(false); $facet->setWidgetType('slider'); break; + case self::WIDGET_TYPE_STAR: + $facet->setMultipleSelectionAllowed(false); + $facet->setWidgetType('star'); + break; } $facets[] = $facet; @@ -438,6 +472,8 @@ public function createFacetedSearchFiltersFromQuery(ProductSearchQuery $query) private function convertFilterTypeToLabel($filterType) { switch ($filterType) { + case self::TYPE_REVIEW: + return $this->context->getTranslator()->trans('Avg. Customer Reviews', [], 'Modules.Facetedsearch.Shop'); case self::TYPE_PRICE: return $this->context->getTranslator()->trans('Price', [], 'Modules.Facetedsearch.Shop'); case self::TYPE_WEIGHT: From d50e526c949d6a87a1233b28090052a2b3fff23e Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 13:36:38 -0600 Subject: [PATCH 07/28] adds review sort --- src/Product/SearchProvider.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Product/SearchProvider.php b/src/Product/SearchProvider.php index 54366f921..1778e8111 100644 --- a/src/Product/SearchProvider.php +++ b/src/Product/SearchProvider.php @@ -86,6 +86,7 @@ private function getAvailableSortOrders() $sortNameDesc = new SortOrder('product', 'name', 'desc'); $sortPriceAsc = new SortOrder('product', 'price', 'asc'); $sortPriceDesc = new SortOrder('product', 'price', 'desc'); + $sortBestRated = new SortOrder('product', 'avg_score', 'desc'); $translator = $this->module->getTranslator(); return [ @@ -107,6 +108,9 @@ private function getAvailableSortOrders() $sortPriceDesc->setLabel( $translator->trans('Price, high to low', [], 'Shop.Theme.Catalog') ), + $sortBestRated->setLabel( + $translator->trans('Sort Best Rated', [], 'Shop.Theme.Catalog') + ), ]; } From a4b11d7e8136734c2f0b97a5333a572b37a570f9 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 13:40:37 -0600 Subject: [PATCH 08/28] updates css and js --- views/dist/front.css | 4 ++-- views/dist/front.js | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/views/dist/front.css b/views/dist/front.css index 0746a873e..3275ad6ba 100644 --- a/views/dist/front.css +++ b/views/dist/front.css @@ -2,7 +2,7 @@ #search_filters .ui-slider-horizontal .ui-slider-handle{margin-left:-1px;cursor:pointer}#search_filters .ui-widget-header{background:#555}#search_filters .ui-slider .ui-slider-handle{top:-.45em;width:0.4em;background:#fff;border:1px solid #555}#search_filters .ui-slider-horizontal{height:.4em} -#search_filters .facet .title{display:flex}#search_filters .facet .title .collapse-icons{margin-left:auto}#search_filters .facet .facet-title{width:calc(100% - 30px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#search_filters .facet .facet-label{width:100%;text-align:left}#search_filters .facet .facet-label .custom-checkbox,#search_filters .facet .facet-label .custom-radio{top:-7px;margin-right:0}#search_filters .facet .facet-label .color{margin-left:0}#search_filters .facet .facet-label a{width:calc(100% - 30px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap} +#search_filters .facet .title{display:flex}#search_filters .facet .title .collapse-icons{margin-left:auto}#search_filters .facet .facet-title{width:calc(100% - 30px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#search_filters .facet .facet-label{width:100%;text-align:left}#search_filters .facet .facet-label .custom-checkbox,#search_filters .facet .facet-label .custom-radio{top:-7px;margin-right:0}#search_filters .facet .facet-label .color{margin-left:0}#search_filters .facet .facet-label a{width:calc(100% - 30px);overflow:hidden;text-overflow:ellipsis;white-space:nowrap} #search_filters .facet .facet-label a.star-link{width: 100%} -/*# sourceMappingURL=front.css.map*/ \ No newline at end of file +/*# sourceMappingURL=front.css.map*/ diff --git a/views/dist/front.js b/views/dist/front.js index 64d4ffc3b..2db5e4945 100644 --- a/views/dist/front.js +++ b/views/dist/front.js @@ -1,3 +1,18 @@ /*! For license information please see front.js.LICENSE.txt */ (()=>{var t={557:()=>{!function(t){if(t.support.touch="ontouchend"in document,t.support.touch){var e,n=t.ui.mouse.prototype,r=n._mouseInit,i=n._mouseDestroy;n._touchStart=function(t){!e&&this._mouseCapture(t.originalEvent.changedTouches[0])&&(e=!0,this._touchMoved=!1,o(t,"mouseover"),o(t,"mousemove"),o(t,"mousedown"))},n._touchMove=function(t){e&&(this._touchMoved=!0,o(t,"mousemove"))},n._touchEnd=function(t){e&&(o(t,"mouseup"),o(t,"mouseout"),this._touchMoved||o(t,"click"),e=!1)},n._mouseInit=function(){var e=this;e.element.bind({touchstart:t.proxy(e,"_touchStart"),touchmove:t.proxy(e,"_touchMove"),touchend:t.proxy(e,"_touchEnd")}),r.call(e)},n._mouseDestroy=function(){var e=this;e.element.unbind({touchstart:t.proxy(e,"_touchStart"),touchmove:t.proxy(e,"_touchMove"),touchend:t.proxy(e,"_touchEnd")}),i.call(e)}}function o(t,e){if(!(t.originalEvent.touches.length>1)){t.preventDefault();var n=t.originalEvent.changedTouches[0],r=document.createEvent("MouseEvents");r.initMouseEvent(e,!0,!0,window,1,n.screenX,n.screenY,n.clientX,n.clientY,!1,!1,!1,!1,0,null),t.target.dispatchEvent(r)}}}(jQuery)},658:(t,e,n)=>{var r=/[\\^$.*+?()[\]{}|]/g,i=RegExp(r.source),o="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,a="object"==typeof self&&self&&self.Object===Object&&self,u=o||a||Function("return this")(),c=Object.prototype.toString,s=u.Symbol,l=s?s.prototype:void 0,f=l?l.toString:void 0;t.exports=function(t){var e;return(t=null==(e=t)?"":function(t){if("string"==typeof t)return t;if(function(t){return"symbol"==typeof t||function(t){return!!t&&"object"==typeof t}(t)&&"[object Symbol]"==c.call(t)}(t))return f?f.call(t):"";var e=t+"";return"0"==e&&1/t==-1/0?"-0":e}(e))&&i.test(t)?t.replace(r,"\\$&"):t}},741:()=>{},580:()=>{},765:()=>{},379:(t,e,n)=>{"use strict";var r,i=function(){var t={};return function(e){if(void 0===t[e]){var n=document.querySelector(e);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(t){n=null}t[e]=n}return t[e]}}(),o=[];function a(t){for(var e=-1,n=0;n{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var r in e)n.o(e,r)&&!n.o(t,r)&&Object.defineProperty(t,r,{enumerable:!0,get:e[r]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(t){if("object"==typeof window)return window}}(),n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),(()=>{"use strict";function t(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);nt.length)&&(e=t.length);for(var n=0,r=new Array(e);nthis.numberSpecification.getMaxFractionDigits()&&(e=e.replace(/0+$/,"")),e.length1&&(o=e(i[1]));var a=!1;o.forEach((function(t){"q"===t.name&&(a=!0)})),a||o.push({name:"q",value:""}),o.forEach((function(e){"q"===e.name&&(e.value+=[e.value.length>0?"/":"",t.data("slider-label"),"-",t.data("slider-unit"),"-",r.values[0],"-",r.values[1]].join(""))}));var u=[i[0],"?",$.param(o)].join("");prestashop.emit("updateFacets",u)},slide:function(e,n){E(t.data("slider-id"),$("#facet_label_".concat(t.data("slider-id"))),n.values[0],n.values[1])}})}))};var O=n(379),_=n.n(O),C=n(580),D=n.n(C);_()(D(),{insert:"head",singleton:!1}),D().locals,$(document).ready((function(){prestashop.on("updateProductList",(function(){$(".faceted-overlay").remove(),I()})),I(),prestashop.on("updateFacets",(function(){1!==$(".faceted-overlay").length&&$("body").append('
    \n
    \n
    \n
    \n
    ')}))}));var F=n(765),G=n.n(F);_()(G(),{insert:"head",singleton:!1}),G().locals;var T=n(741),A=n.n(T);_()(A(),{insert:"head",singleton:!1}),A().locals})()})(); -//# sourceMappingURL=front.js.map \ No newline at end of file +//# sourceMappingURL=front.js.map + +$('.grade-stars-filter').rating(); + +var oldXHR = window.XMLHttpRequest; + +function newXHR() { + var realXHR = new oldXHR(); + realXHR.addEventListener("readystatechange", function() { + if(realXHR.readyState===4 && realXHR.status===200){ + setTimeout(function(){ $('.grade-stars-filter').rating(); }, 50); + } + }, false); + return realXHR; +} +window.XMLHttpRequest = newXHR; From 9a70fda998dc900bc3eb30c56cc158706c33320a Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 13:46:56 -0600 Subject: [PATCH 09/28] updates facets front view --- views/templates/front/catalog/facets.tpl | 47 +++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/views/templates/front/catalog/facets.tpl b/views/templates/front/catalog/facets.tpl index 72de2874f..3e9f47430 100644 --- a/views/templates/front/catalog/facets.tpl +++ b/views/templates/front/catalog/facets.tpl @@ -168,13 +168,42 @@ {$filter.label}

    -
    -
  • - - {/foreach} - {/block} - {/if} - - {/foreach} - +
    + + + {/foreach} + {/block} + + {elseif $facet.widgetType == 'star'} + {block name='facet_item_star'} + + {/block} + {/if} + + {/foreach} + {/if} From 21a9f79598e5a51e3b2500b6c2637288b6240116 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 13:51:16 -0600 Subject: [PATCH 10/28] adds avg_score to mysql table mapping --- src/Adapter/MySQL.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Adapter/MySQL.php b/src/Adapter/MySQL.php index 8e3a33d64..f5bf7b3f8 100644 --- a/src/Adapter/MySQL.php +++ b/src/Adapter/MySQL.php @@ -301,6 +301,12 @@ protected function getFieldMapping() 'joinCondition' => '(psales.id_product = p.id_product)', 'joinType' => self::LEFT_JOIN, ], + 'avg_score' => [ + 'tableName' => 'layered_comment_index', + 'tableAlias' => 'coms', + 'joinCondition' => '(coms.id_product = p.id_product)', + 'joinType' => self::INNER_JOIN, + ], ]; return $filterToTableMapping; From 0d6b378c7e03071ece1ccc818c6c071d2e7385e8 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 13:56:31 -0600 Subject: [PATCH 11/28] updates search --- src/Product/Search.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/Product/Search.php b/src/Product/Search.php index 74e5512ce..ca2653619 100644 --- a/src/Product/Search.php +++ b/src/Product/Search.php @@ -280,6 +280,18 @@ private function addSearchFilters($selectedFilters, $parent, $idShop) ); } break; + + case 'review': + if (isset($selectedFilters['review']) + && ( + $selectedFilters['review'][0] !== '' + ) + ) { + $this->addReviewFilter( + (float) $selectedFilters['review'][0] + ); + } + break; } } @@ -329,4 +341,14 @@ private function addPriceFilter($minPrice, $maxPrice) $this->getSearchAdapter()->addFilter('price_min', [$maxPrice], '<='); $this->getSearchAdapter()->addFilter('price_max', [$minPrice], '>='); } + + /** + * Add a review filter + * + * @param float $scoreUp + */ + private function addReviewFilter($scoreUp) + { + $this->getSearchAdapter()->addFilter('avg_score', [$scoreUp], '>='); + } } From 8ee9a3a96bcccb51631e93bf1c665b1a2a310d0d Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 14:24:31 -0600 Subject: [PATCH 12/28] adds review indexer file --- ps_facetedsearch-review-indexer.php | 36 +++++++++++++++++++++++++++++ ps_facetedsearch.php | 2 ++ 2 files changed, 38 insertions(+) create mode 100755 ps_facetedsearch-review-indexer.php diff --git a/ps_facetedsearch-review-indexer.php b/ps_facetedsearch-review-indexer.php new file mode 100755 index 000000000..dd14418ff --- /dev/null +++ b/ps_facetedsearch-review-indexer.php @@ -0,0 +1,36 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + */ +require_once __DIR__ . '/../../config/config.inc.php'; +require_once __DIR__ . '/ps_facetedsearch.php'; + +if (substr(Tools::encrypt('ps_facetedsearch/index'), 0, 10) != Tools::getValue('token') || !Module::isInstalled('ps_facetedsearch')) { + exit('Bad token'); +} + +Shop::setContext(Shop::CONTEXT_ALL); + +$module = new Ps_Facetedsearch(); + +if (Tools::getValue('full')){ + $module->rebuildCommentIndexTable(); + echo $module->indexReviews(true); +}else{ + echo $module->indexReviews(); +} diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index 4df9fb7c7..ac3bfdefe 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -827,6 +827,8 @@ public function getContent() 'full_price_indexer_url' => $moduleUrl . 'ps_facetedsearch-price-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10) . '&full=1', 'attribute_indexer_url' => $moduleUrl . 'ps_facetedsearch-attribute-indexer.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10), 'clear_cache_url' => $moduleUrl . 'ps_facetedsearch-clear-cache.php' . '?token=' . substr(Tools::hash('ps_facetedsearch/index'), 0, 10), + 'index_reviews_full' => $moduleUrl . 'ps_facetedsearch-review-indexer.php' . '?full=true&token=' . substr(Tools::encrypt('ps_facetedsearch/index'), 0, 10), + 'index_reviews_missing' => $moduleUrl . 'ps_facetedsearch-review-indexer.php' . '?token=' . substr(Tools::encrypt('ps_facetedsearch/index'), 0, 10), 'filters_templates' => $this->getDatabase()->executeS('SELECT * FROM ' . _DB_PREFIX_ . 'layered_filter ORDER BY date_add DESC'), 'show_quantities' => Configuration::get('PS_LAYERED_SHOW_QTIES'), 'cache_enabled' => Configuration::get('PS_LAYERED_CACHE_ENABLED'), From e0494006a21f9af2a40ef9ed4f5831e19ee4827d Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 14:40:41 -0600 Subject: [PATCH 13/28] admin manage view --- views/templates/admin/manage.tpl | 76 ++++++++++++++++---------------- 1 file changed, 39 insertions(+), 37 deletions(-) mode change 100644 => 100755 views/templates/admin/manage.tpl diff --git a/views/templates/admin/manage.tpl b/views/templates/admin/manage.tpl old mode 100644 new mode 100755 index 9290d0aac..cdd983c60 --- a/views/templates/admin/manage.tpl +++ b/views/templates/admin/manage.tpl @@ -19,33 +19,35 @@ {include file='./_partials/messages.tpl'}
    -

    {l s='Indexes and caches' d='Modules.Facetedsearch.Admin'}

    - - -
    -
    - {l s='You can set a cron job that will rebuild price index using the following URL:' d='Modules.Facetedsearch.Admin'} -
    - {$price_indexer_url} -
    -
    - {l s='You can set a cron job that will rebuild attribute index using the following URL:' d='Modules.Facetedsearch.Admin'} -
    - {$attribute_indexer_url} +

    {l s='Indexes and caches' d='Modules.Facetedsearch.Admin'}

    + + +
    +
    + {l s='You can set a cron job that will rebuild price index using the following URL:' d='Modules.Facetedsearch.Admin'} +
    + {$price_indexer_url} +
    +
    + {l s='You can set a cron job that will rebuild attribute index using the following URL:' d='Modules.Facetedsearch.Admin'} +
    + {$attribute_indexer_url} +
    +
    +
    +
    {l s='A nightly rebuild is recommended.' d='Modules.Facetedsearch.Admin'}
    -
    -
    -
    {l s='A nightly rebuild is recommended.' d='Modules.Facetedsearch.Admin'}
    -

    {l s='Filters templates' d='Modules.Facetedsearch.Admin'}{$filters_templates|count}

    @@ -252,15 +254,15 @@ var base_folder = '{$base_folder}'; var translations = new Object(); - translations.in_progress = '{l s='(in progress)' js=1 d='Modules.Facetedsearch.Admin'}'; - translations.url_indexation_finished = '{l s='URL indexing finished' js=1 d='Modules.Facetedsearch.Admin'}'; - translations.attribute_indexation_finished = '{l s='Attribute indexing finished' js=1 d='Modules.Facetedsearch.Admin'}'; - translations.url_indexation_failed = '{l s='URL indexing failed' js=1 d='Modules.Facetedsearch.Admin'}'; - translations.attribute_indexation_failed = '{l s='Attribute indexing failed' js=1 d='Modules.Facetedsearch.Admin'}'; - translations.price_indexation_finished = '{l s='Price indexing finished' js=1 d='Modules.Facetedsearch.Admin'}'; - translations.price_indexation_failed = '{l s='Price indexing failed' js=1 d='Modules.Facetedsearch.Admin'}'; - translations.price_indexation_in_progress = '{l s='(in progress, %s products price to index)' js=1 d='Modules.Facetedsearch.Admin'}'; - translations.loading = '{l s='Loading...' js=1 d='Modules.Facetedsearch.Admin'}'; - translations.delete_all_filters_templates = '{l s='You selected -All categories-: all existing filter templates will be deleted. Is it OK?' js=1 d='Modules.Facetedsearch.Admin'}'; - translations.no_selected_categories = '{l s='You must select at least one category' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.in_progress = '{l s='(in progress)' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.url_indexation_finished = '{l s='URL indexing finished' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.attribute_indexation_finished = '{l s='Indexing finished' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.url_indexation_failed = '{l s='URL indexing failed' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.attribute_indexation_failed = '{l s='Indexing failed' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.price_indexation_finished = '{l s='Price indexing finished' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.price_indexation_failed = '{l s='Price indexing failed' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.price_indexation_in_progress = '{l s='(in progress, %s products price to index)' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.loading = '{l s='Loading...' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.delete_all_filters_templates = '{l s='You selected -All categories-: all existing filter templates will be deleted. Is it OK?' js=1 d='Modules.Facetedsearch.Admin'}'; + translations.no_selected_categories = '{l s='You must select at least one category' js=1 d='Modules.Facetedsearch.Admin'}'; From 90354ff183d628ade9d2ed48a8bf6d7ae2927a2f Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 17:20:33 -0600 Subject: [PATCH 14/28] index valid comments --- ps_facetedsearch.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index ac3bfdefe..3b020a46d 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -1547,14 +1547,18 @@ public function rebuildCommentIndexTable() public function indexReviews($full = false) { if ($full) { - $query = 'SELECT avg(`grade`) as avg_grade, sum(`grade`) as sum_grade, `id_product` FROM ' . _DB_PREFIX_ . 'product_comment -group by id_product'; + $validateCondition = ''; + if (Configuration::get('PRODUCT_COMMENTS_MODERATE')){ + $validateCondition = ' where validate = 1'; + } + + $query = 'SELECT avg(`grade`) as avg_grade, sum(`grade`) as sum_grade, `id_product` FROM ' . _DB_PREFIX_ . 'product_comment '. $validateCondition .' group by id_product'; foreach ($this->getDatabase()->executeS($query) as $comment) { $this->addCommentIndex($comment); } - $query = 'SELECT `id_product_comment`, `id_product` FROM ' . _DB_PREFIX_ . 'product_comment'; + $query = 'SELECT `id_product_comment`, `id_product` FROM ' . _DB_PREFIX_ . 'product_comment' . $validateCondition; foreach ($this->getDatabase()->executeS($query) as $commentLog) { $this->addCommentIndexLog($commentLog); From aca5aa1536986014a4a453eb2f3ffb3e2cb124cc Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 12 Dec 2021 20:06:33 -0600 Subject: [PATCH 15/28] invalidate block cache after adding comment --- src/Hook/ProductComment.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Hook/ProductComment.php b/src/Hook/ProductComment.php index 9c01c30d6..441257934 100755 --- a/src/Hook/ProductComment.php +++ b/src/Hook/ProductComment.php @@ -62,6 +62,7 @@ public function actionObjectProductCommentValidateAfter(array $params) $this->addCommentIndexLog($grade); } } + $this->module->invalidateLayeredFilterBlockCache(); } public function addCommentIndexLog($comment){ From 9191b0ff49491ba8f7a01b573611872eaf886927 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 13 Dec 2021 17:04:02 -0600 Subject: [PATCH 16/28] Update ps_facetedsearch.php spaces Co-authored-by: GoT --- ps_facetedsearch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index 3b020a46d..1dba9958f 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -1171,7 +1171,7 @@ public function buildLayeredCategories() $sqlInsert .= '(' . (int) $idCategory . ', ' . (int) $idShop . ', ' . (int) str_replace('layered_selection_feat_', '', $key) . ', \'id_feature\',' . (int) $n . ', ' . (int) $limit . ', ' . (int) $type . '),'; } elseif ($key == 'layered_selection_review_star') { - $sqlInsert .= '(' . (int) $idCategory . ', ' . (int) $idShop . ', NULL,\'review\',' . (int) $n . ', ' . (int) $limit . ', ' . (int) $type . '),'; + $sqlInsert .= '(' . (int) $idCategory . ', ' . (int) $idShop . ', NULL, \'review\', ' . (int) $n . ', ' . (int) $limit . ', ' . (int) $type . '),'; } ++$nbSqlValuesToInsert; From c13edac6f378a8883f0c7fb0aa02942c42b6a450 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 13 Dec 2021 17:04:46 -0600 Subject: [PATCH 17/28] Update ps_facetedsearch.php float avg_score Co-authored-by: GoT --- ps_facetedsearch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index 1dba9958f..16b23a8d9 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -1527,7 +1527,7 @@ public function rebuildCommentIndexTable() 'CREATE TABLE `' . _DB_PREFIX_ . 'layered_comment_index` ( `id_product` INT NOT NULL, `score` INT NOT NULL, - `avg_score` FLOAT NOT NULL, + `avg_score` FLOAT(5, 4) NOT NULL, PRIMARY KEY (`id_product`), INDEX `score` (`score`), INDEX `avg_score` (`score`) From e79531f5acca6d3529a42af100f27f5831c8c9f6 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 13 Dec 2021 17:05:08 -0600 Subject: [PATCH 18/28] Update ps_facetedsearch.php space sql create Co-authored-by: GoT --- ps_facetedsearch.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index 16b23a8d9..1a63efcc3 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -1525,7 +1525,7 @@ public function rebuildCommentIndexTable() $this->getDatabase()->execute( 'CREATE TABLE `' . _DB_PREFIX_ . 'layered_comment_index` ( - `id_product` INT NOT NULL, + `id_product` INT NOT NULL, `score` INT NOT NULL, `avg_score` FLOAT(5, 4) NOT NULL, PRIMARY KEY (`id_product`), From 501fa80c287bc82adabf68b541919f90aae21a92 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 13 Dec 2021 17:05:23 -0600 Subject: [PATCH 19/28] Update ps_facetedsearch.php space Co-authored-by: GoT --- ps_facetedsearch.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index 1a63efcc3..4dc3d5603 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -1536,8 +1536,8 @@ public function rebuildCommentIndexTable() $this->getDatabase()->execute( 'CREATE TABLE `' . _DB_PREFIX_ . 'layered_comment_index_log` ( - `id_comment` INT NOT NULL, - `id_product` INT NOT NULL, + `id_comment` INT NOT NULL, + `id_product` INT NOT NULL, `indexed` TINYINT(1) NOT NULL, PRIMARY KEY (`id_comment`) ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8;' From b8cb6a1ebc25c1c5f8e25dd84d56d7373ef6817f Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 13 Dec 2021 17:06:48 -0600 Subject: [PATCH 20/28] ps prefix and beautify sql query Co-authored-by: GoT --- ps_facetedsearch.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index 4dc3d5603..88f5b1bbe 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -1565,10 +1565,11 @@ public function indexReviews($full = false) } } else { - $query = 'select pc.id_product_comment, pc.id_product, pc.grade from ps_product_comment as pc -left join ps_layered_comment_index_log as lc -on pc.id_product_comment = lc.id_comment -where lc.indexed is null;'; + $query = 'SELECT pc.id_product_comment, pc.id_product, pc.grade ' . + 'FROM ' . _DB_PREFIX_ . '_product_comment as pc ' . + 'LEFT JOIN ' . _DB_PREFIX_ . '_layered_comment_index_log as lc ' . + 'pc.id_product_comment = lc.id_comment ' . + 'WHERE lc.indexed IS NULL'; //returns non matching records foreach ($this->getDatabase()->executeS($query) as $commentLog) { From ba9c0201274ee95a250a7b65a3a9637cd5410f77 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 13 Dec 2021 17:10:23 -0600 Subject: [PATCH 21/28] removes useless if condition Co-authored-by: GoT --- src/Hook/ProductComment.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Hook/ProductComment.php b/src/Hook/ProductComment.php index 441257934..181bb03f9 100755 --- a/src/Hook/ProductComment.php +++ b/src/Hook/ProductComment.php @@ -44,7 +44,7 @@ public function actionObjectProductCommentValidateAfter(array $params) $gradeCommentRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index` WHERE id_product =' . $grade->id_product); - if (empty($gradeCommentRow) || !$gradeCommentRow){ + if (empty($gradeCommentRow)){ //insert the first index record for this comment $this->database->execute('INSERT INTO `'._DB_PREFIX_.'layered_comment_index` (`id_product`, `score`, `avg_score`) VALUES ('.$grade->id_product.', '.$grade->grade.','.$grade->grade.')'); From c3eeabe669a558e4d60f4f1d6902f2d4b24ad451 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 13 Dec 2021 17:27:51 -0600 Subject: [PATCH 22/28] get category id with getValue --- src/Filters/Block.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Filters/Block.php b/src/Filters/Block.php index 8889e1891..1bf8ef655 100644 --- a/src/Filters/Block.php +++ b/src/Filters/Block.php @@ -1000,6 +1000,11 @@ private function getReviweBlock($filter, $selectedFilters) '1' => ['name'=>"1"], ]; + $idParent = (int) Tools::getValue( + 'id_category', + Tools::getValue('id_category_layered', Configuration::get('PS_HOME_CATEGORY')) + ); + $query = 'SELECT count(g.grade) as count ,g.grade FROM (SELECT t.id_product, case when t.avg_grade between 1 and 1.99 then "1" when t.avg_grade between 2 and 2.99 then "2" @@ -1009,7 +1014,7 @@ private function getReviweBlock($filter, $selectedFilters) FROM (SELECT avg(h.grade) as avg_grade, h.id_product FROM (select pc.id_product, pc.grade from ' . _DB_PREFIX_ . 'product_comment as pc -inner join (SELECT * FROM ' . _DB_PREFIX_ . 'category_product where id_category = '.$_GET['id_category'].') cp +inner join (SELECT * FROM ' . _DB_PREFIX_ . 'category_product where id_category = '.$idParent.') cp on pc.id_product = cp.id_product) h group by id_product) t) g group by g.grade From e3754c194d238e0da0610519b367bfb257e3ffb7 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 15 Dec 2021 14:50:33 -0600 Subject: [PATCH 23/28] removes review filters when product comments module is disabled --- ps_facetedsearch-review-indexer.php | 4 +++ ps_facetedsearch.php | 16 ++++++++--- views/templates/admin/add.tpl | 42 +++++++++++++++-------------- views/templates/admin/manage.tpl | 6 +++-- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/ps_facetedsearch-review-indexer.php b/ps_facetedsearch-review-indexer.php index dd14418ff..36792af4b 100755 --- a/ps_facetedsearch-review-indexer.php +++ b/ps_facetedsearch-review-indexer.php @@ -24,6 +24,10 @@ exit('Bad token'); } +if (!Module::isEnabled('productcomments')){ + exit('Product Comment module is not installed or is disabled.'); +} + Shop::setContext(Shop::CONTEXT_ALL); $module = new Ps_Facetedsearch(); diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index 88f5b1bbe..b72b4e781 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -144,7 +144,7 @@ public function getContext() protected function getDefaultFilters() { - return [ + $defaultFilters = [ 'layered_selection_subcategories' => [ 'label' => 'Sub-categories filter', ], @@ -165,11 +165,16 @@ protected function getDefaultFilters() 'label' => 'Product price filter (slider)', 'slider' => true, ], - 'layered_selection_review_star' => [ + ]; + + if (Module::isEnabled('productcomments')){ + $defaultFilters['layered_selection_review_star'] = [ 'label' => 'Avg. Customer Review', 'star' => true - ], - ]; + ]; + } + + return $defaultFilters; } public function install() @@ -778,6 +783,7 @@ public function getContent() ]); $this->context->smarty->assign('categories_tree', $treeCategoriesHelper->render()); + $this->context->smarty->assign('comment_module_enabled', Module::isEnabled('productcomments')); return $this->display(__FILE__, 'views/templates/admin/add.tpl'); } @@ -841,6 +847,8 @@ public function getContent() 'filter_by_default_category' => (bool) Configuration::get('PS_LAYERED_FILTER_BY_DEFAULT_CATEGORY'), ]); + $this->context->smarty->assign('comment_module_enabled', Module::isEnabled('productcomments')); + return $this->display(__FILE__, 'views/templates/admin/manage.tpl'); } diff --git a/views/templates/admin/add.tpl b/views/templates/admin/add.tpl index 9b90a4fe4..e194c486b 100755 --- a/views/templates/admin/add.tpl +++ b/views/templates/admin/add.tpl @@ -220,30 +220,32 @@
    -
  • -
    -
  • +
    + -
    -
    - {l s='Avg. Customer Reviews' d='Modules.Facetedsearch.Admin'} -
    -
    -
    -
    - -
    - -

    {l s='List of ranges' d='Modules.Facetedsearch.Admin'}

    + +
    -
    -
  • +
    + {l s='Avg. Customer Reviews' d='Modules.Facetedsearch.Admin'} +
    +
    +
    +
    + +
    + +

    {l s='Star' d='Modules.Facetedsearch.Admin'}

    +
    +
    + + {/if} {if $attribute_groups|count > 0} {foreach $attribute_groups as $attribute_group}
  • diff --git a/views/templates/admin/manage.tpl b/views/templates/admin/manage.tpl index cdd983c60..d45c86f65 100755 --- a/views/templates/admin/manage.tpl +++ b/views/templates/admin/manage.tpl @@ -29,8 +29,10 @@ {l s='Rebuild entire price index' d='Modules.Facetedsearch.Admin'} {l s='Build attributes and features indexes' d='Modules.Facetedsearch.Admin'} {l s='Clear cache' d='Modules.Facetedsearch.Admin'} - {l s='Rebuild entire review index' d='Modules.Facetedsearch.Admin'} - {l s='Index missing reviews' d='Modules.Facetedsearch.Admin'} + {if $comment_module_enabled} + {l s='Rebuild entire review index' d='Modules.Facetedsearch.Admin'} + {l s='Index missing reviews' d='Modules.Facetedsearch.Admin'} + {/if}

    From 745d289a214a4d347c7a9630b5fe34fc9df046a0 Mon Sep 17 00:00:00 2001 From: Sam Berry Date: Mon, 20 Jun 2022 09:16:11 -0400 Subject: [PATCH 24/28] feat: support for add after and delete after comment indexing --- src/Hook/ProductComment.php | 105 ++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 17 deletions(-) diff --git a/src/Hook/ProductComment.php b/src/Hook/ProductComment.php index 181bb03f9..25355dc4a 100755 --- a/src/Hook/ProductComment.php +++ b/src/Hook/ProductComment.php @@ -23,49 +23,120 @@ class ProductComment extends AbstractHook { const AVAILABLE_HOOKS = [ - 'actionObjectProductCommentValidateAfter', + 'actionObjectProductCommentAddAfter', + 'actionObjectProductCommentDeleteAfter', + 'actionObjectProductCommentValidateAfter' ]; /** - * Product Comment Validate After + * Product Add After * - * Smart index after validating a comment + * Smart index after adding a comment * * @param array $params */ - public function actionObjectProductCommentValidateAfter(array $params) + public function actionObjectProductCommentAddAfter(array $params) { - $grade = $params['object']; + if (!\Configuration::get('PRODUCT_COMMENTS_MODERATE')){ + $this->updateCommentIndex($params['array']); + } + } + + /** + * Product Validate After + * + * Smart index after validating a comment + * + * @param array $params + */ + public function actionObjectProductCommentValidateAfter(array $params){ + if (\Configuration::get('PRODUCT_COMMENTS_MODERATE')){ + $this->updateCommentIndex($params['object']); + } + } + + /** + * Product Delete After + * + * Smart index after deleting a comment + * + * @param array $params + */ + public function actionObjectProductCommentDeleteAfter(array $params){ + $comment = $params['object']; $skip = false; + + if (\Configuration::get('PRODUCT_COMMENTS_MODERATE')){ + if ((int)$comment->validate){ + $skip = true; + } + } + + if ($skip){ + $gradeCommentRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index` + WHERE id_product =' . $comment->id_product); + + //update index for the comment + $productCommentLogRowCount = $this->database->executeS('SELECT count(id_comment) as count FROM `' . _DB_PREFIX_ . 'layered_comment_index_log` + WHERE id_product = ' . $comment->id_product . ' AND indexed = 1'); + + $count = $productCommentLogRowCount[0]['count']; + $count--; + + $newGradeValue = (int)$gradeCommentRow[0]['score'] - (int)$comment->grade; + $avg_score = $newGradeValue/(((int)$count)); + + $this->database->execute('update ' . _DB_PREFIX_ . 'layered_comment_index set score='.$newGradeValue.', avg_score= '.$avg_score .' where id_product=' . $comment->id_product); + + $this->deleteCommentIndexLog($comment->id, $comment->id_product); + } + } + + public function updateCommentIndex($param){ + if (is_array($param)){ + $grade = $param['grade']; + $id_product = $param['id_product']; + $id_comment = $param['id_product_comment']; + }else{ + $grade = $param->grade; + $id_product = $param->id_product; + $id_comment = $param->id; + } $commentLogRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index_log` - WHERE id_comment = ' . $grade->id . ' AND indexed = 1'); + WHERE id_comment = ' . $id_comment . ' AND indexed = 1'); if (empty($commentLogRow)){ $gradeCommentRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index` - WHERE id_product =' . $grade->id_product); + WHERE id_product =' . $id_product); if (empty($gradeCommentRow)){ //insert the first index record for this comment - $this->database->execute('INSERT INTO `'._DB_PREFIX_.'layered_comment_index` (`id_product`, `score`, `avg_score`) VALUES ('.$grade->id_product.', '.$grade->grade.','.$grade->grade.')'); + $this->database->execute('INSERT INTO `'._DB_PREFIX_.'layered_comment_index` (`id_product`, `score`, `avg_score`) VALUES ('.$id_product.', '.$grade.','.$grade.')'); - $this->addCommentIndexLog($grade); + $this->addCommentIndexLog($id_comment, $id_product); }else{ //update index for the comment - $productCommentLogRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index_log` - WHERE id_product = ' . $grade->id_product . ' AND indexed = 1'); + $productCommentLogRowCount = $this->database->executeS('SELECT count(id_comment) as count FROM `' . _DB_PREFIX_ . 'layered_comment_index_log` + WHERE id_product = ' . $id_product . ' AND indexed = 1'); + + $count = $productCommentLogRowCount[0]['count']; - $newGradeValue = (int)$gradeCommentRow[0]['score'] + (int)$grade->grade; - $avg_score = $newGradeValue/(count($productCommentLogRow)+1); + $newGradeValue = (int)$gradeCommentRow[0]['score'] + (int)$grade; + $avg_score = $newGradeValue/(((int)$count)+1); - $this->database->execute('update ' . _DB_PREFIX_ . 'layered_comment_index set score='.$newGradeValue.', avg_score= '.$avg_score .' where id_product=' . $grade->id_product); + $this->database->execute('update ' . _DB_PREFIX_ . 'layered_comment_index set score='.$newGradeValue.', avg_score= '.$avg_score .' where id_product=' . $id_product); - $this->addCommentIndexLog($grade); + $this->addCommentIndexLog($id_comment, $id_product); } } $this->module->invalidateLayeredFilterBlockCache(); } - public function addCommentIndexLog($comment){ - $this->database->execute('INSERT INTO `'._DB_PREFIX_.'layered_comment_index_log` (`id_comment`, `indexed`, `id_product`) VALUES ('.$comment->id.', 1,'. $comment->id_product .')'); + public function addCommentIndexLog($id_comment, $id_product){ + $this->database->execute('INSERT INTO `'._DB_PREFIX_.'layered_comment_index_log` (`id_comment`, `indexed`, `id_product`) VALUES ('.$id_comment.', 1,'. $id_product .')'); + } + + public function deleteCommentIndexLog($id_comment, $id_product){ + $this->database->execute('DELETE FROM `'._DB_PREFIX_.'layered_comment_index_log` WHERE id_comment = ' . $id_comment . ' AND id_product = ' . $id_product); } } From 0073146afd28a44f397991ec241a50ff798d83fc Mon Sep 17 00:00:00 2001 From: Sam Berry Date: Mon, 20 Jun 2022 12:56:07 -0400 Subject: [PATCH 25/28] fix: a little fix in if condition --- src/Hook/ProductComment.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Hook/ProductComment.php b/src/Hook/ProductComment.php index 25355dc4a..1ba324912 100755 --- a/src/Hook/ProductComment.php +++ b/src/Hook/ProductComment.php @@ -66,12 +66,12 @@ public function actionObjectProductCommentDeleteAfter(array $params){ $comment = $params['object']; $skip = false; if (\Configuration::get('PRODUCT_COMMENTS_MODERATE')){ - if ((int)$comment->validate){ + if (!(int)$comment->validate){ $skip = true; } } - if ($skip){ + if (!$skip){ $gradeCommentRow = $this->database->executeS('SELECT * FROM `' . _DB_PREFIX_ . 'layered_comment_index` WHERE id_product =' . $comment->id_product); From 0a022417a0ab6574f19a1ccb16423b63f3c56156 Mon Sep 17 00:00:00 2001 From: Sam Berry Date: Mon, 20 Jun 2022 15:14:47 -0400 Subject: [PATCH 26/28] fix: added check for comment approval --- ps_facetedsearch.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index b72b4e781..211ed4e2f 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -1573,11 +1573,16 @@ public function indexReviews($full = false) } } else { + $validateCondition = ''; + if (Configuration::get('PRODUCT_COMMENTS_MODERATE')){ + $validateCondition = ' AND WHERE validate = 1'; + } + $query = 'SELECT pc.id_product_comment, pc.id_product, pc.grade ' . 'FROM ' . _DB_PREFIX_ . '_product_comment as pc ' . 'LEFT JOIN ' . _DB_PREFIX_ . '_layered_comment_index_log as lc ' . 'pc.id_product_comment = lc.id_comment ' . - 'WHERE lc.indexed IS NULL'; + 'WHERE lc.indexed IS NULL' . $validateCondition; //returns non matching records foreach ($this->getDatabase()->executeS($query) as $commentLog) { From 3160ebd75b768920111d55a0c63be28ff26f9ea2 Mon Sep 17 00:00:00 2001 From: Sam Berry Date: Tue, 12 Jul 2022 17:59:17 -0400 Subject: [PATCH 27/28] fix: removed the index record on last comment removal for a product --- src/Hook/ProductComment.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Hook/ProductComment.php b/src/Hook/ProductComment.php index 1ba324912..5640a5b91 100755 --- a/src/Hook/ProductComment.php +++ b/src/Hook/ProductComment.php @@ -82,10 +82,14 @@ public function actionObjectProductCommentDeleteAfter(array $params){ $count = $productCommentLogRowCount[0]['count']; $count--; - $newGradeValue = (int)$gradeCommentRow[0]['score'] - (int)$comment->grade; - $avg_score = $newGradeValue/(((int)$count)); + if ($count === 0 ){ + $this->database->execute('DELETE FROM `'._DB_PREFIX_.'layered_comment_index` WHERE id_product = ' . $comment->id_product ); + } else{ + $newGradeValue = (int)$gradeCommentRow[0]['score'] - (int)$comment->grade; + $avg_score = $newGradeValue/(((int)$count)); - $this->database->execute('update ' . _DB_PREFIX_ . 'layered_comment_index set score='.$newGradeValue.', avg_score= '.$avg_score .' where id_product=' . $comment->id_product); + $this->database->execute('update ' . _DB_PREFIX_ . 'layered_comment_index set score='.$newGradeValue.', avg_score= '.$avg_score .' where id_product=' . $comment->id_product); + } $this->deleteCommentIndexLog($comment->id, $comment->id_product); } From 4f79cb81b52cc5fb780c48098c6ed41b90779f6f Mon Sep 17 00:00:00 2001 From: Sam Berry Date: Mon, 5 Sep 2022 08:19:27 -0400 Subject: [PATCH 28/28] chore: change version to 3.9.0 --- ps_facetedsearch.php | 2 +- upgrade/upgrade-3.9.0.php | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 upgrade/upgrade-3.9.0.php diff --git a/ps_facetedsearch.php b/ps_facetedsearch.php index 211ed4e2f..1fb71c774 100644 --- a/ps_facetedsearch.php +++ b/ps_facetedsearch.php @@ -91,7 +91,7 @@ public function __construct() { $this->name = 'ps_facetedsearch'; $this->tab = 'front_office_features'; - $this->version = '3.8.0'; + $this->version = '3.9.0'; $this->author = 'PrestaShop'; $this->need_instance = 0; $this->bootstrap = true; diff --git a/upgrade/upgrade-3.9.0.php b/upgrade/upgrade-3.9.0.php new file mode 100644 index 000000000..bc4e0b248 --- /dev/null +++ b/upgrade/upgrade-3.9.0.php @@ -0,0 +1,32 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + */ +if (!defined('_PS_VERSION_')) { + exit; +} + +function upgrade_module_3_9_0(Ps_Facetedsearch $module) +{ + $module->rebuildCommentIndexTable(); + $module->indexReviews(true); + $module->rebuildLayeredStructure(); + $module->invalidateLayeredFilterBlockCache(); + + return $module->registerHook($module->getHookDispatcher()->getAvailableHooks()); +}