';
$customFields = is_array( $product['custom_fields'] ?? null ) ? $product['custom_fields'] : [];
@@ -896,9 +919,15 @@ class ShopProductController
public function product_custom_label_suggestions(): void
{
$response = [ 'status' => 'error', 'msg' => 'Podczas pobierania sugestii dla custom label wystąpił błąd. Proszę spróbować ponownie.' ];
+ $labelType = (string) \Shared\Helpers\Helpers::get( 'label_type' );
- $suggestions = $this->repository->customLabelSuggestions( \Shared\Helpers\Helpers::get( 'custom_label' ), \Shared\Helpers\Helpers::get( 'label_type' ) );
- if ( $suggestions ) {
+ if ( !$this->isAllowedCustomLabelType( $labelType ) ) {
+ echo json_encode( $response );
+ exit;
+ }
+
+ $suggestions = $this->repository->customLabelSuggestions( (string) \Shared\Helpers\Helpers::get( 'custom_label' ), $labelType );
+ if ( is_array( $suggestions ) ) {
$response = [ 'status' => 'ok', 'suggestions' => $suggestions ];
}
@@ -912,8 +941,14 @@ class ShopProductController
public function product_custom_label_save(): void
{
$response = [ 'status' => 'error', 'msg' => 'Podczas zapisywania custom label wystąpił błąd. Proszę spróbować ponownie.' ];
+ $labelType = (string) \Shared\Helpers\Helpers::get( 'label_type' );
- if ( $this->repository->saveCustomLabel( (int) \Shared\Helpers\Helpers::get( 'product_id' ), \Shared\Helpers\Helpers::get( 'custom_label' ), \Shared\Helpers\Helpers::get( 'label_type' ) ) ) {
+ if ( !$this->isAllowedCustomLabelType( $labelType ) ) {
+ echo json_encode( $response );
+ exit;
+ }
+
+ if ( $this->repository->saveCustomLabel( (int) \Shared\Helpers\Helpers::get( 'product_id' ), (string) \Shared\Helpers\Helpers::get( 'custom_label' ), $labelType ) ) {
$response = [ 'status' => 'ok' ];
}
@@ -1196,4 +1231,36 @@ class ShopProductController
echo json_encode( [ 'status' => 'ok', 'products' => $products ] );
exit;
}
+
+ private function customLabelsEnabled(): bool
+ {
+ return isset( $_SESSION[ self::CUSTOM_LABELS_SESSION_KEY ] ) && (int) $_SESSION[ self::CUSTOM_LABELS_SESSION_KEY ] === 1;
+ }
+
+ private function isAllowedCustomLabelType(string $labelType): bool
+ {
+ return in_array( $labelType, [ 'custom_label_0', 'custom_label_1', 'custom_label_2', 'custom_label_3', 'custom_label_4' ], true );
+ }
+
+ private function renderCustomLabelsEditor(array $product, int $productId, array $customLabelNames): string
+ {
+ $customLabelsHtml = '
';
+
+ for ( $index = 0; $index < 5; $index++ ) {
+ $fieldName = 'custom_label_' . $index;
+ $labelText = htmlspecialchars( (string) ( $customLabelNames[$fieldName] ?? 'Custom label ' . $index ), ENT_QUOTES, 'UTF-8' );
+ $valueText = htmlspecialchars( (string) ( $product[$fieldName] ?? '' ), ENT_QUOTES, 'UTF-8' );
+
+ $customLabelsHtml .= '
';
+ $customLabelsHtml .= '
' . $labelText . '';
+ $datalistId = 'custom-label-list-' . $productId . '-' . $fieldName;
+ $customLabelsHtml .= '
';
+ $customLabelsHtml .= '
';
+ $customLabelsHtml .= '
';
+ $customLabelsHtml .= '
';
+ }
+
+ $customLabelsHtml .= '
';
+ return $customLabelsHtml;
+ }
}
diff --git a/autoload/front/Controllers/ShopBasketController.php b/autoload/front/Controllers/ShopBasketController.php
index e26e7fa..7477b91 100644
--- a/autoload/front/Controllers/ShopBasketController.php
+++ b/autoload/front/Controllers/ShopBasketController.php
@@ -280,20 +280,71 @@ class ShopBasketController
$client = \Shared\Helpers\Helpers::get_session( 'client' );
$orderSubmitToken = $this->createOrderSubmitToken();
+ $basket = \Shared\Helpers\Helpers::get_session( 'basket' );
+ $coupon = \Shared\Helpers\Helpers::get_session( 'coupon' );
+ $transport = ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->findActiveByIdCached( \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ) );
+
+ $productsSummary = (float)\Domain\Basket\BasketCalculator::summaryPrice( $basket, $coupon );
+ $freeDeliveryThreshold = isset( $settings['free_delivery'] ) ? (float)$settings['free_delivery'] : 0.0;
+ $transportCalc = $this->calculateTransportCostForSummary( $transport, $productsSummary, $freeDeliveryThreshold );
+
return \Shared\Tpl\Tpl::view( 'shop-basket/summary-view', [
'lang_id' => $lang_id,
'client' => \Shared\Helpers\Helpers::get_session( 'client' ),
- 'basket' => \Shared\Helpers\Helpers::get_session( 'basket' ),
- 'transport' => ( new \Domain\Transport\TransportRepository( $GLOBALS['mdb'] ) )->findActiveByIdCached( \Shared\Helpers\Helpers::get_session( 'basket-transport-method-id' ) ),
+ 'basket' => $basket,
+ 'transport' => $transport,
+ 'transport_cost_effective' => $transportCalc['transport_cost_effective'],
+ 'free_delivery_applies' => $transportCalc['free_delivery_applies'],
'payment_method' => $this->paymentMethodRepository->paymentMethodCached( (int)\Shared\Helpers\Helpers::get_session( 'basket-payment-method-id' ) ),
'addresses' => ( new \Domain\Client\ClientRepository( $GLOBALS['mdb'] ) )->clientAddresses( (int)$client['id'] ),
'settings' => $settings,
- 'coupon' => \Shared\Helpers\Helpers::get_session( 'coupon' ),
+ 'coupon' => $coupon,
'basket_message' => \Shared\Helpers\Helpers::get_session( 'basket_message' ),
'order_submit_token' => $orderSubmitToken
] );
}
+ /**
+ * Wylicza efektywny koszt transportu dla widoku /koszyk-podsumowanie.
+ * Koszt spada do 0, gdy transport ma flage delivery_free=1 ORAZ wartosc koszyka
+ * (po kuponie) osiaga prog darmowej dostawy $freeDeliveryThreshold.
+ *
+ * @param array|null $transport Aktywny transport (lub null gdy nie wybrany)
+ * @param float $productsSummary Wartosc koszyka po kuponie
+ * @param float $freeDeliveryThreshold Prog darmowej dostawy z settings.free_delivery
+ * @return array{transport_cost_effective: float, free_delivery_applies: bool}
+ */
+ protected function calculateTransportCostForSummary( $transport, $productsSummary, $freeDeliveryThreshold )
+ {
+ if ( !is_array( $transport ) )
+ {
+ return [
+ 'transport_cost_effective' => 0.0,
+ 'free_delivery_applies' => false,
+ ];
+ }
+
+ $deliveryFree = isset( $transport['delivery_free'] ) ? (int)$transport['delivery_free'] : 0;
+ $cost = isset( $transport['cost'] ) ? (float)$transport['cost'] : 0.0;
+
+ $applies = false;
+ if ( $deliveryFree === 1 && $freeDeliveryThreshold > 0 )
+ {
+ $summaryNormalized = \Shared\Helpers\Helpers::normalize_decimal( $productsSummary );
+ $thresholdNormalized = \Shared\Helpers\Helpers::normalize_decimal( $freeDeliveryThreshold );
+
+ if ( $summaryNormalized >= $thresholdNormalized )
+ {
+ $applies = true;
+ }
+ }
+
+ return [
+ 'transport_cost_effective' => $applies ? 0.0 : $cost,
+ 'free_delivery_applies' => $applies,
+ ];
+ }
+
public function basketSave()
{
$orderSubmitToken = (string)\Shared\Helpers\Helpers::get( 'order_submit_token', true );
diff --git a/autoload/front/LayoutEngine.php b/autoload/front/LayoutEngine.php
index f384f70..5679298 100644
--- a/autoload/front/LayoutEngine.php
+++ b/autoload/front/LayoutEngine.php
@@ -153,15 +153,9 @@ class LayoutEngine
{
$category = $categoryRepo->frontCategoryDetails( (int)\Shared\Helpers\Helpers::get( 'category' ), $lang_id );
- if ( $category['language']['meta_title'] )
- $page['language']['title'] = $category['language']['meta_title'];
- else
- $page['language']['title'] = $category['language']['title'];
-
+ $page = self::applyEntityMetaToPage( $page, isset( $category['language'] ) ? $category['language'] : null, isset( $category['language']['title'] ) ? $category['language']['title'] : '' );
$page['show_title'] = true;
- $page['language']['meta_keywords'] = $category['language']['meta_keywords'];
- $page['language']['meta_description'] = $category['language']['meta_description'];
- $page['language']['page_title'] = $category['language']['category_title'] ? $category['language']['category_title'] : $category['language']['title'];
+ $page['language']['page_title'] = !empty( $category['language']['category_title'] ) ? $category['language']['category_title'] : ( isset( $category['language']['title'] ) ? $category['language']['title'] : '' );
// CANONICAL
$html = str_replace( '[CANONICAL]', '', $html );
@@ -175,14 +169,8 @@ class LayoutEngine
{
$article = $articleRepo->articleDetailsFrontend( (int)\Shared\Helpers\Helpers::get( 'article' ), $lang_id );
- if ( $article['language']['meta_title'] )
- $page['language']['title'] = $article['language']['meta_title'];
- else
- $page['language']['title'] = $article['language']['title'];
-
+ $page = self::applyEntityMetaToPage( $page, isset( $article['language'] ) ? $article['language'] : null, isset( $article['language']['title'] ) ? $article['language']['title'] : '' );
$page['show_title'] = false;
- $page['language']['meta_keywords'] = $article['language']['meta_keywords'];
- $page['language']['meta_description'] = $article['language']['meta_description'];
// CANONICAL
$html = str_replace( '[CANONICAL]', '', $html );
@@ -193,16 +181,11 @@ class LayoutEngine
//
if ( \Shared\Helpers\Helpers::get( 'product' ) )
{
- $product = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCached( \Shared\Helpers\Helpers::get( 'product' ), $lang_id, $_GET['permutation_hash'] ?? null );
-
- if ( $product['language']['meta_title'] )
- $page['language']['title'] = $product['language']['meta_title'];
- else
- $page['language']['title'] = $product['language']['name'];
+ $permutation_hash = isset( $_GET['permutation_hash'] ) ? str_replace( '_', '|', $_GET['permutation_hash'] ) : null;
+ $product = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCached( \Shared\Helpers\Helpers::get( 'product' ), $lang_id, $permutation_hash );
+ $page = self::applyEntityMetaToPage( $page, isset( $product['language'] ) ? $product['language'] : null, isset( $product['language']['name'] ) ? $product['language']['name'] : '' );
$page['show_title'] = false;
- $page['language']['meta_keywords'] = $product['language']['meta_keywords'];
- $page['language']['meta_description'] = $product['language']['meta_description'];
// CANONICAL
if ( $product['language']['canonical'] )
@@ -439,6 +422,35 @@ class LayoutEngine
] );
}
+ /**
+ * Przepisuje meta encji (kategoria/artykuł/produkt) do $page['language'].
+ *
+ * Dlaczego: domyślne $page jest stroną główną CMS. Jeśli nie nadpiszemy
+ * meta_title encji (nawet pustym), meta_title homepage wycieka do
+ * na podstronie kategorii/produktu (linia podstawienia [TITLE]).
+ *
+ * @param array $page obecne $page (z homepage lub session)
+ * @param array|null $entityLanguage wiersz *_langs encji (może być null)
+ * @param string $fallbackTitle nazwa encji używana jako $page.language.title
+ * @return array zmodyfikowany $page
+ */
+ public static function applyEntityMetaToPage( $page, $entityLanguage, $fallbackTitle )
+ {
+ if ( !is_array( $page ) ) {
+ $page = [];
+ }
+ if ( !isset( $page['language'] ) or !is_array( $page['language'] ) ) {
+ $page['language'] = [];
+ }
+
+ $page['language']['title'] = $fallbackTitle;
+ $page['language']['meta_title'] = is_array( $entityLanguage ) && isset( $entityLanguage['meta_title'] ) ? $entityLanguage['meta_title'] : null;
+ $page['language']['meta_keywords'] = is_array( $entityLanguage ) && isset( $entityLanguage['meta_keywords'] ) ? $entityLanguage['meta_keywords'] : null;
+ $page['language']['meta_description'] = is_array( $entityLanguage ) && isset( $entityLanguage['meta_description'] ) ? $entityLanguage['meta_description'] : null;
+
+ return $page;
+ }
+
public static function alert()
{
if ( $alert = \Shared\Helpers\Helpers::get_session( 'alert' ) )