diff --git a/admin-dev/themes/new-theme/js/fos_js_routes.json b/admin-dev/themes/new-theme/js/fos_js_routes.json index 12be03b467378..0794440e53245 100644 --- a/admin-dev/themes/new-theme/js/fos_js_routes.json +++ b/admin-dev/themes/new-theme/js/fos_js_routes.json @@ -1 +1 @@ -{"base_url":"","routes":{"admin_common_notifications":{"tokens":[["text","\/common\/notifications"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_product_form":{"tokens":[["variable","\/","\\d+","id"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":{"id":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_feature_get_feature_values":{"tokens":[["variable","\/","\\d+","idFeature"],["text","\/sell\/catalog\/products\/features"]],"defaults":{"idFeature":0},"requirements":{"idFeature":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_combinations":{"tokens":[["text","\/combinations"],["variable","\/","[^\/]++","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_combinations_ids":{"tokens":[["text","\/combinations\/ids"],["variable","\/","[^\/]++","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_combinations_update_combination_from_listing":{"tokens":[["text","\/update-combination-from-listing"],["variable","\/","[^\/]++","productId"],["text","\/sell\/catalog\/products-v2\/combinations"]],"defaults":[],"requirements":{"combinationId":"\\d+"},"hosttokens":[],"methods":["PATCH"],"schemes":[]},"admin_products_combinations_edit_combination":{"tokens":[["text","\/edit"],["variable","\/","\\d+","combinationId"],["text","\/sell\/catalog\/products-v2\/combinations"]],"defaults":[],"requirements":{"combinationId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_products_combinations_bulk_edit_combination":{"tokens":[["text","\/combinations\/bulk-edit"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["PATCH"],"schemes":[]},"admin_products_combinations_delete_combination":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/delete"],["variable","\/","\\d+","combinationId"],["text","\/sell\/catalog\/products-v2\/combinations"]],"defaults":{"shopId":null},"requirements":{"combinationId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["DELETE"],"schemes":[]},"admin_products_combinations_bulk_delete":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/combinations\/bulk-delete"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":{"shopId":null},"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_attribute_groups":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/attribute-groups"],["variable","\/","[^\/]++","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":{"shopId":null},"requirements":{"shopId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_all_attribute_groups":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/sell\/catalog\/products-v2\/all-attribute-groups"]],"defaults":{"shopId":null},"requirements":{"shopId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_combinations_generate":{"tokens":[["variable","\/","\\d+","shopId"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2\/generate-combinations"]],"defaults":{"shopId":null},"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_images_for_shop":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/images-for-shop"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_product_shop_images":{"tokens":[["text","\/shopImages"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_products_add_image":{"tokens":[["text","\/sell\/catalog\/products-v2\/images\/add"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_update_image":{"tokens":[["text","\/update"],["variable","\/","\\d+","productImageId"],["text","\/sell\/catalog\/products-v2\/images"]],"defaults":[],"requirements":{"productImageId":"\\d+"},"hosttokens":[],"methods":["PATCH"],"schemes":[]},"admin_products_delete_image":{"tokens":[["text","\/delete"],["variable","\/","\\d+","productImageId"],["text","\/sell\/catalog\/products-v2\/images"]],"defaults":[],"requirements":{"productImageId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_specific_prices_list":{"tokens":[["text","\/specific-prices\/list"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_specific_prices_create":{"tokens":[["text","\/specific-prices\/create"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_products_specific_prices_edit":{"tokens":[["text","\/edit"],["variable","\/","\\d+","specificPriceId"],["text","\/sell\/catalog\/products-v2\/specific-prices"]],"defaults":[],"requirements":{"specificPriceId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_products_specific_prices_delete":{"tokens":[["text","\/delete"],["variable","\/","\\d+","specificPriceId"],["text","\/sell\/catalog\/products-v2\/specific-prices"]],"defaults":[],"requirements":{"specificPriceId":"\\d+"},"hosttokens":[],"methods":["DELETE"],"schemes":[]},"admin_products_edit":{"tokens":[["text","\/edit"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["GET","POST","PATCH"],"schemes":[]},"admin_products_select_shops":{"tokens":[["text","\/shops"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["GET","POST","PATCH"],"schemes":[]},"admin_products_bulk_enable_all_shops":{"tokens":[["text","\/sell\/catalog\/products-v2\/bulk-enable-all-shops"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_enable_shop":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/sell\/catalog\/products-v2\/bulk-enable-shop"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_enable_shop_group":{"tokens":[["variable","\/","\\d+","shopGroupId"],["text","\/sell\/catalog\/products-v2\/bulk-enable-shop-group"]],"defaults":[],"requirements":{"productId":"\\d+","shopGroupId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_disable_all_shops":{"tokens":[["text","\/sell\/catalog\/products-v2\/bulk-disable-for-all-shops"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_disable_shop":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/sell\/catalog\/products-v2\/bulk-disable-shop"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_disable_shop_group":{"tokens":[["variable","\/","\\d+","shopGroupId"],["text","\/sell\/catalog\/products-v2\/bulk-disable-shop-group"]],"defaults":[],"requirements":{"productId":"\\d+","shopGroupId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_duplicate_all_shops":{"tokens":[["text","\/sell\/catalog\/products-v2\/bulk-duplicate-all-shops"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_duplicate_shop":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/sell\/catalog\/products-v2\/bulk-duplicate-shop"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_duplicate_shop_group":{"tokens":[["variable","\/","\\d+","shopGroupId"],["text","\/sell\/catalog\/products-v2\/bulk-duplicate-shop-group"]],"defaults":[],"requirements":{"productId":"\\d+","shopGroupId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_delete_from_all_shops":{"tokens":[["text","\/sell\/catalog\/products-v2\/bulk-delete-from-all-shops"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["POST","DELETE"],"schemes":[]},"admin_products_bulk_delete_from_shop":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/sell\/catalog\/products-v2\/bulk-delete-from-shop"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST","DELETE"],"schemes":[]},"admin_products_bulk_delete_from_shop_group":{"tokens":[["variable","\/","\\d+","shopGroupId"],["text","\/sell\/catalog\/products-v2\/bulk-delete-from-shop-group"]],"defaults":[],"requirements":{"productId":"\\d+","shopGroupId":"\\d+"},"hosttokens":[],"methods":["POST","DELETE"],"schemes":[]},"admin_products_search_product_combinations":{"tokens":[["variable","\/","\\d+","languageId"],["variable","\/","\\d+","shopId"],["text","\/search-product-combinations"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":{"languageId":null,"shopId":null},"requirements":{"productId":"\\d+","shopId":"\\d+","languageId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_quantity":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/quantity"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products-v2"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_categories_get_categories_tree":{"tokens":[["text","\/sell\/catalog\/categories\/tree"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_catalog_price_rules_list_for_product":{"tokens":[["variable","\/","[^\/]++","productId"],["text","\/sell\/catalog\/catalog-price-rules\/list-for-product"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_view":{"tokens":[["text","\/view"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_addresses_create":{"tokens":[["text","\/sell\/addresses\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_addresses_edit":{"tokens":[["text","\/edit"],["variable","\/","\\d+","addressId"],["text","\/sell\/addresses"]],"defaults":[],"requirements":{"addressId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_order_addresses_edit":{"tokens":[["text","\/edit"],["variable","\/","delivery|invoice","addressType"],["variable","\/","\\d+","orderId"],["text","\/sell\/addresses\/order"]],"defaults":[],"requirements":{"orderId":"\\d+","addressType":"delivery|invoice"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_cart_addresses_edit":{"tokens":[["text","\/edit"],["variable","\/","delivery|invoice","addressType"],["variable","\/","\\d+","cartId"],["text","\/sell\/addresses\/cart"]],"defaults":[],"requirements":{"cartId":"\\d+","addressType":"delivery|invoice"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_customer_threads_view":{"tokens":[["text","\/view"],["variable","\/","\\d+","customerThreadId"],["text","\/sell\/customer-service\/customer-threads"]],"defaults":[],"requirements":{"customerThreadId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_view":{"tokens":[["text","\/view"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_currency":{"tokens":[["text","\/currency"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_language":{"tokens":[["text","\/language"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_delivery_settings":{"tokens":[["text","\/rules\/delivery-settings"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_cart_rule":{"tokens":[["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_cart_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_product":{"tokens":[["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_product_price":{"tokens":[["text","\/price"],["variable","\/","\\d+","productId"],["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+","productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_product_quantity":{"tokens":[["text","\/quantity"],["variable","\/","\\d+","productId"],["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+","productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_product":{"tokens":[["text","\/delete-product"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_place":{"tokens":[["text","\/sell\/orders\/place"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_view":{"tokens":[["text","\/view"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_update_product":{"tokens":[["variable","\/","\\d+","orderDetailId"],["text","\/products"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+","orderDetailId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_partial_refund":{"tokens":[["text","\/partial-refund"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_standard_refund":{"tokens":[["text","\/standard-refund"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_return_product":{"tokens":[["text","\/return-product"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_send_process_order_email":{"tokens":[["text","\/sell\/orders\/process-order-email"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_add_product":{"tokens":[["text","\/products"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_delete_product":{"tokens":[["text","\/delete"],["variable","\/","\\d+","orderDetailId"],["text","\/products"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+","orderDetailId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_get_discounts":{"tokens":[["text","\/discounts"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_prices":{"tokens":[["text","\/prices"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_payments":{"tokens":[["text","\/payments"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_products":{"tokens":[["text","\/products"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_invoices":{"tokens":[["text","\/invoices"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_documents":{"tokens":[["text","\/documents"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_shipping":{"tokens":[["text","\/shipping"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_cancellation":{"tokens":[["text","\/cancellation"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_configure_product_pagination":{"tokens":[["text","\/sell\/orders\/configure-product-pagination"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_product_prices":{"tokens":[["text","\/products\/prices"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_products_search":{"tokens":[["text","\/sell\/orders\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_attachments_attachment_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","attachmentId"],["text","\/sell\/attachments"]],"defaults":[],"requirements":{"attachmentId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_attachments_search":{"tokens":[["variable","\/","[^\/]++","searchPhrase"],["text","\/sell\/attachments\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_shops_search":{"tokens":[["variable","\/","[^\/]++","searchTerm"],["text","\/configure\/advanced\/shops\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":""} \ No newline at end of file +{"base_url":"","routes":{"admin_common_notifications":{"tokens":[["text","\/common\/notifications"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_combinations":{"tokens":[["text","\/combinations"],["variable","\/","[^\/]++","productId"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_combinations_ids":{"tokens":[["text","\/combinations\/ids"],["variable","\/","[^\/]++","productId"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_combinations_update_combination_from_listing":{"tokens":[["text","\/update-combination-from-listing"],["variable","\/","[^\/]++","productId"],["text","\/sell\/catalog\/products\/combinations"]],"defaults":[],"requirements":{"combinationId":"\\d+"},"hosttokens":[],"methods":["PATCH"],"schemes":[]},"admin_products_combinations_edit_combination":{"tokens":[["text","\/edit"],["variable","\/","\\d+","combinationId"],["text","\/sell\/catalog\/products\/combinations"]],"defaults":[],"requirements":{"combinationId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_products_combinations_bulk_edit_combination":{"tokens":[["text","\/combinations\/bulk-edit"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["PATCH"],"schemes":[]},"admin_products_combinations_delete_combination":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/delete"],["variable","\/","\\d+","combinationId"],["text","\/sell\/catalog\/products\/combinations"]],"defaults":{"shopId":null},"requirements":{"combinationId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["DELETE"],"schemes":[]},"admin_products_combinations_bulk_delete":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/combinations\/bulk-delete"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products"]],"defaults":{"shopId":null},"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_attribute_groups":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/attribute-groups"],["variable","\/","[^\/]++","productId"],["text","\/sell\/catalog\/products"]],"defaults":{"shopId":null},"requirements":{"shopId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_all_attribute_groups":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/sell\/catalog\/products\/all-attribute-groups"]],"defaults":{"shopId":null},"requirements":{"shopId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_combinations_generate":{"tokens":[["variable","\/","\\d+","shopId"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products\/generate-combinations"]],"defaults":{"shopId":null},"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_images_for_shop":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/images-for-shop"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_product_shop_images":{"tokens":[["text","\/shopImages"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_products_add_image":{"tokens":[["text","\/sell\/catalog\/products\/images\/add"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_update_image":{"tokens":[["text","\/update"],["variable","\/","\\d+","productImageId"],["text","\/sell\/catalog\/products\/images"]],"defaults":[],"requirements":{"productImageId":"\\d+"},"hosttokens":[],"methods":["PATCH"],"schemes":[]},"admin_products_delete_image":{"tokens":[["text","\/delete"],["variable","\/","\\d+","productImageId"],["text","\/sell\/catalog\/products\/images"]],"defaults":[],"requirements":{"productImageId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_specific_prices_list":{"tokens":[["text","\/specific-prices\/list"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_specific_prices_create":{"tokens":[["text","\/specific-prices\/create"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_products_specific_prices_edit":{"tokens":[["text","\/edit"],["variable","\/","\\d+","specificPriceId"],["text","\/sell\/catalog\/products\/specific-prices"]],"defaults":[],"requirements":{"specificPriceId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_products_specific_prices_delete":{"tokens":[["text","\/delete"],["variable","\/","\\d+","specificPriceId"],["text","\/sell\/catalog\/products\/specific-prices"]],"defaults":[],"requirements":{"specificPriceId":"\\d+"},"hosttokens":[],"methods":["DELETE"],"schemes":[]},"admin_products_edit":{"tokens":[["text","\/edit"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["GET","POST","PATCH"],"schemes":[]},"admin_product_form":{"tokens":[["variable","\/","\\d+","id"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":{"id":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_select_shops":{"tokens":[["text","\/shops"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["GET","POST","PATCH"],"schemes":[]},"admin_products_bulk_enable_all_shops":{"tokens":[["text","\/sell\/catalog\/products\/bulk-enable-all-shops"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_enable_shop":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/sell\/catalog\/products\/bulk-enable-shop"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_enable_shop_group":{"tokens":[["variable","\/","\\d+","shopGroupId"],["text","\/sell\/catalog\/products\/bulk-enable-shop-group"]],"defaults":[],"requirements":{"productId":"\\d+","shopGroupId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_disable_all_shops":{"tokens":[["text","\/sell\/catalog\/products\/bulk-disable-for-all-shops"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_disable_shop":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/sell\/catalog\/products\/bulk-disable-shop"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_disable_shop_group":{"tokens":[["variable","\/","\\d+","shopGroupId"],["text","\/sell\/catalog\/products\/bulk-disable-shop-group"]],"defaults":[],"requirements":{"productId":"\\d+","shopGroupId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_duplicate_all_shops":{"tokens":[["text","\/sell\/catalog\/products\/bulk-duplicate-all-shops"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_duplicate_shop":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/sell\/catalog\/products\/bulk-duplicate-shop"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_duplicate_shop_group":{"tokens":[["variable","\/","\\d+","shopGroupId"],["text","\/sell\/catalog\/products\/bulk-duplicate-shop-group"]],"defaults":[],"requirements":{"productId":"\\d+","shopGroupId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_products_bulk_delete_from_all_shops":{"tokens":[["text","\/sell\/catalog\/products\/bulk-delete-from-all-shops"]],"defaults":[],"requirements":{"productId":"\\d+"},"hosttokens":[],"methods":["POST","DELETE"],"schemes":[]},"admin_products_bulk_delete_from_shop":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/sell\/catalog\/products\/bulk-delete-from-shop"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["POST","DELETE"],"schemes":[]},"admin_products_bulk_delete_from_shop_group":{"tokens":[["variable","\/","\\d+","shopGroupId"],["text","\/sell\/catalog\/products\/bulk-delete-from-shop-group"]],"defaults":[],"requirements":{"productId":"\\d+","shopGroupId":"\\d+"},"hosttokens":[],"methods":["POST","DELETE"],"schemes":[]},"admin_products_search_product_combinations":{"tokens":[["variable","\/","\\d+","languageId"],["variable","\/","\\d+","shopId"],["text","\/search-product-combinations"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products"]],"defaults":{"languageId":null,"shopId":null},"requirements":{"productId":"\\d+","shopId":"\\d+","languageId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_products_quantity":{"tokens":[["variable","\/","\\d+","shopId"],["text","\/quantity"],["variable","\/","\\d+","productId"],["text","\/sell\/catalog\/products"]],"defaults":[],"requirements":{"productId":"\\d+","shopId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_categories_get_categories_tree":{"tokens":[["text","\/sell\/catalog\/categories\/tree"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_catalog_price_rules_list_for_product":{"tokens":[["variable","\/","[^\/]++","productId"],["text","\/sell\/catalog\/catalog-price-rules\/list-for-product"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_feature_get_feature_values":{"tokens":[["variable","\/","\\d+","featureId"],["text","\/sell\/catalog\/features\/values"]],"defaults":[],"requirements":{"featureId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_cart_rules_search":{"tokens":[["text","\/sell\/catalog\/cart-rules\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_view":{"tokens":[["text","\/view"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_customers_search":{"tokens":[["text","\/sell\/customers\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_carts":{"tokens":[["text","\/carts"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_customers_orders":{"tokens":[["text","\/orders"],["variable","\/","\\d+","customerId"],["text","\/sell\/customers"]],"defaults":[],"requirements":{"customerId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_addresses_create":{"tokens":[["text","\/sell\/addresses\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_addresses_edit":{"tokens":[["text","\/edit"],["variable","\/","\\d+","addressId"],["text","\/sell\/addresses"]],"defaults":[],"requirements":{"addressId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_order_addresses_edit":{"tokens":[["text","\/edit"],["variable","\/","delivery|invoice","addressType"],["variable","\/","\\d+","orderId"],["text","\/sell\/addresses\/order"]],"defaults":[],"requirements":{"orderId":"\\d+","addressType":"delivery|invoice"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_cart_addresses_edit":{"tokens":[["text","\/edit"],["variable","\/","delivery|invoice","addressType"],["variable","\/","\\d+","cartId"],["text","\/sell\/addresses\/cart"]],"defaults":[],"requirements":{"cartId":"\\d+","addressType":"delivery|invoice"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_customer_threads_view":{"tokens":[["text","\/view"],["variable","\/","\\d+","customerThreadId"],["text","\/sell\/customer-service\/customer-threads"]],"defaults":[],"requirements":{"customerThreadId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_view":{"tokens":[["text","\/view"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_carts_create":{"tokens":[["text","\/sell\/orders\/carts\/new"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_addresses":{"tokens":[["text","\/addresses"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_carrier":{"tokens":[["text","\/carrier"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_currency":{"tokens":[["text","\/currency"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_language":{"tokens":[["text","\/language"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_set_delivery_settings":{"tokens":[["text","\/rules\/delivery-settings"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_cart_rule":{"tokens":[["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_cart_rule":{"tokens":[["text","\/delete"],["variable","\/","[^\/]++","cartRuleId"],["text","\/cart-rules"],["variable","\/","[^\/]++","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_add_product":{"tokens":[["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_product_price":{"tokens":[["text","\/price"],["variable","\/","\\d+","productId"],["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+","productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_edit_product_quantity":{"tokens":[["text","\/quantity"],["variable","\/","\\d+","productId"],["text","\/products"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+","productId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_carts_delete_product":{"tokens":[["text","\/delete-product"],["variable","\/","\\d+","cartId"],["text","\/sell\/orders\/carts"]],"defaults":[],"requirements":{"cartId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_place":{"tokens":[["text","\/sell\/orders\/place"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_view":{"tokens":[["text","\/view"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET","POST"],"schemes":[]},"admin_orders_duplicate_cart":{"tokens":[["text","\/duplicate-cart"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_update_product":{"tokens":[["variable","\/","\\d+","orderDetailId"],["text","\/products"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+","orderDetailId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_partial_refund":{"tokens":[["text","\/partial-refund"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_standard_refund":{"tokens":[["text","\/standard-refund"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_return_product":{"tokens":[["text","\/return-product"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_send_process_order_email":{"tokens":[["text","\/sell\/orders\/process-order-email"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_add_product":{"tokens":[["text","\/products"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_delete_product":{"tokens":[["text","\/delete"],["variable","\/","\\d+","orderDetailId"],["text","\/products"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+","orderDetailId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_get_discounts":{"tokens":[["text","\/discounts"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_prices":{"tokens":[["text","\/prices"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_payments":{"tokens":[["text","\/payments"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_products":{"tokens":[["text","\/products"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_invoices":{"tokens":[["text","\/invoices"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_documents":{"tokens":[["text","\/documents"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_get_shipping":{"tokens":[["text","\/shipping"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_cancellation":{"tokens":[["text","\/cancellation"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_configure_product_pagination":{"tokens":[["text","\/sell\/orders\/configure-product-pagination"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["POST"],"schemes":[]},"admin_orders_product_prices":{"tokens":[["text","\/products\/prices"],["variable","\/","\\d+","orderId"],["text","\/sell\/orders"]],"defaults":[],"requirements":{"orderId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_orders_products_search":{"tokens":[["text","\/sell\/orders\/products\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_attachments_attachment_info":{"tokens":[["text","\/info"],["variable","\/","\\d+","attachmentId"],["text","\/sell\/attachments"]],"defaults":[],"requirements":{"attachmentId":"\\d+"},"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_attachments_search":{"tokens":[["variable","\/","[^\/]++","searchPhrase"],["text","\/sell\/attachments\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]},"admin_shops_search":{"tokens":[["variable","\/","[^\/]++","searchTerm"],["text","\/configure\/advanced\/shops\/search"]],"defaults":[],"requirements":[],"hosttokens":[],"methods":["GET"],"schemes":[]}},"prefix":"","host":"localhost","port":"","scheme":"http","locale":""} \ No newline at end of file diff --git a/admin-dev/themes/new-theme/js/pages/order/view/order-view-page.ts b/admin-dev/themes/new-theme/js/pages/order/view/order-view-page.ts index 328de3ffd8a65..861e09a051eda 100644 --- a/admin-dev/themes/new-theme/js/pages/order/view/order-view-page.ts +++ b/admin-dev/themes/new-theme/js/pages/order/view/order-view-page.ts @@ -183,7 +183,7 @@ export default class OrderViewPage { $item.find(OrderViewPageMap.productPackModal.product.name).html(item.name); $item.find(OrderViewPageMap.productPackModal.product.link).attr( 'href', - this.router.generate('admin_product_form', {id: item.id}), + this.router.generate('admin_products_edit', {productId: item.id}), ); if (item.reference !== '') { $item.find(OrderViewPageMap.productPackModal.product.ref).append(item.reference); diff --git a/admin-dev/themes/new-theme/js/pages/product/edit/manager/feature-values-manager.ts b/admin-dev/themes/new-theme/js/pages/product/edit/manager/feature-values-manager.ts index 37e3871aebc09..11c78f2c2ce21 100644 --- a/admin-dev/themes/new-theme/js/pages/product/edit/manager/feature-values-manager.ts +++ b/admin-dev/themes/new-theme/js/pages/product/edit/manager/feature-values-manager.ts @@ -120,7 +120,7 @@ export default class FeatureValuesManager { private watchFeatureSelectors(): void { $(this.$collectionContainer).on('change', ProductMap.featureValues.featureSelect, (event) => { const $selector = $(event.target); - const idFeature = Number($selector.val()); + const featureId = Number($selector.val()); const $collectionRow = $selector.closest(ProductMap.featureValues.collectionRow); const $featureValueSelector = $(ProductMap.featureValues.featureValueSelect, $collectionRow).first(); const $customValueInputs = $(ProductMap.featureValues.customValueInput, $collectionRow); @@ -131,18 +131,18 @@ export default class FeatureValuesManager { $featureValueSelector.val('0'); $customFeatureIdInput.val(''); - this.renderFeatureValueChoices($featureValueSelector, idFeature); + this.renderFeatureValueChoices($featureValueSelector, featureId); }); } - private renderFeatureValueChoices($featureValueSelector: JQuery, idFeature: number): void { - if (!idFeature) { + private renderFeatureValueChoices($featureValueSelector: JQuery, featureId: number): void { + if (!featureId) { $featureValueSelector.prop('disabled', true); return; } - $.get(this.router.generate('admin_feature_get_feature_values', {idFeature})) + $.get(this.router.generate('admin_feature_get_feature_values', {featureId})) .then((featureValuesData) => { $featureValueSelector.prop('disabled', featureValuesData.length === 0); $featureValueSelector.empty(); diff --git a/app/config/config.yml b/app/config/config.yml index 572235e5ade87..0aea8baa47a83 100644 --- a/app/config/config.yml +++ b/app/config/config.yml @@ -92,7 +92,6 @@ twig: form_themes: - '@PrestaShop/Admin/TwigTemplateForm/bootstrap_4_horizontal_layout.html.twig' paths: - '%admin_page%/Product': Product '%admin_page%/TwigTemplateForm': Twig '%admin_page%/Common': Common '%admin_page%/Configure/AdvancedParameters': AdvancedParameters diff --git a/classes/Link.php b/classes/Link.php index 317b51eb2fd38..d4d064c51fb7c 100644 --- a/classes/Link.php +++ b/classes/Link.php @@ -27,7 +27,6 @@ use PrestaShop\PrestaShop\Core\Exception\CoreException; use PrestaShop\PrestaShop\Core\Feature\TokenInUrls; use PrestaShopBundle\Routing\Converter\LegacyUrlConverter; -use PrestaShopBundle\Service\TransitionalBehavior\AdminPagePreferenceInterface; use Symfony\Component\Routing\Exception\InvalidParameterException; use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; use Symfony\Component\Routing\Exception\RouteNotFoundException; @@ -760,44 +759,6 @@ public function getAdminLink($controller, $withToken = true, $sfRouteParams = [] $routeName = ''; switch ($controller) { - case 'AdminProducts': - // New architecture modification: temporary behavior to switch between old and new controllers. - /** @var AdminPagePreferenceInterface $pagePreference */ - $pagePreference = $sfContainer->get('prestashop.core.admin.page_preference_interface'); - $redirectLegacy = $pagePreference->getTemporaryShouldUseLegacyPage('product'); - if (!$redirectLegacy) { - if (array_key_exists('id_product', $sfRouteParams)) { - if (array_key_exists('deleteproduct', $sfRouteParams)) { - return $sfRouter->generate( - 'admin_product_unit_action', - ['action' => 'delete', 'id' => $sfRouteParams['id_product']] - ); - } - //default: if (array_key_exists('updateproduct', $sfRouteParams)) - return $sfRouter->generate( - 'admin_product_form', - ['id' => $sfRouteParams['id_product']] - ); - } - if (array_key_exists('submitFilterproduct', $sfRouteParams)) { - $routeParams = []; - if (array_key_exists('filter_column_sav_quantity', $sfRouteParams)) { - $routeParams['quantity'] = $sfRouteParams['filter_column_sav_quantity']; - } - if (array_key_exists('filter_column_active', $sfRouteParams)) { - $routeParams['active'] = $sfRouteParams['filter_column_active']; - } - - return $sfRouter->generate('admin_product_catalog_filters', $routeParams); - } - - return $sfRouter->generate('admin_product_catalog', $sfRouteParams); - } else { - $params = array_merge($params, $sfRouteParams); - } - - break; - case 'AdminTranslations': // In case of email body translations we want to get a link to legacy controller, // in other cases - it's the migrated controller diff --git a/classes/QuickAccess.php b/classes/QuickAccess.php index a0b0a75f0e168..d4607ba19af90 100644 --- a/classes/QuickAccess.php +++ b/classes/QuickAccess.php @@ -24,10 +24,6 @@ * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) */ -use PrestaShop\PrestaShop\Adapter\SymfonyContainer; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; - /** * Class QuickAccessCore. */ @@ -50,7 +46,7 @@ class QuickAccessCore extends ObjectModel /** * link to new product creation form for product v2 */ - private const NEW_PRODUCT_V2_LINK = 'index.php/sell/catalog/products-v2/create'; + private const NEW_PRODUCT_V2_LINK = 'index.php/sell/catalog/products/create'; /** * @see ObjectModel::$definition @@ -127,11 +123,9 @@ public static function getQuickAccessesWithToken($idLang, $idEmployee) unset($quickAccess[$index]); continue; } - // if new product page feature is enabled we create new product v2 modal popup - if (self::productPageV2Enabled()) { - $quickAccess[$index]['link'] = $context->link->getAdminBaseLink() . basename(_PS_ADMIN_DIR_) . '/' . self::NEW_PRODUCT_V2_LINK; - $quickAccess[$index]['class'] = 'new-product-button'; - } + // We create new product v2 modal popup link + $quickAccess[$index]['link'] = $context->link->getAdminBaseLink() . basename(_PS_ADMIN_DIR_) . '/' . self::NEW_PRODUCT_V2_LINK; + $quickAccess[$index]['class'] = 'new-product-button'; } } @@ -163,12 +157,4 @@ public function toggleNewWindow() return $this->update(false); } - - /** - * @return bool - */ - private static function productPageV2Enabled(): bool - { - return SymfonyContainer::getInstance()->get(FeatureFlagStateCheckerInterface::class)->isEnabled(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2); - } } diff --git a/classes/helper/HelperList.php b/classes/helper/HelperList.php index 96a2b797d89a0..cb66585f136ba 100644 --- a/classes/helper/HelperList.php +++ b/classes/helper/HelperList.php @@ -25,7 +25,6 @@ */ use PrestaShop\PrestaShop\Adapter\Routing\AdminLinkBuilder; use PrestaShop\PrestaShop\Adapter\Routing\LegacyHelperLinkBuilder; -use PrestaShop\PrestaShop\Adapter\SymfonyContainer; use PrestaShop\PrestaShop\Core\Routing\EntityLinkBuilderFactory; use PrestaShop\PrestaShop\Core\Routing\Exception\BuilderNotFoundException; @@ -612,10 +611,7 @@ public function displayDeleteLink($token, $id, $name = null) switch ($this->currentIndex) { case 'index.php?controller=AdminProducts': case 'index.php?tab=AdminProducts': - // New architecture modification: temporary behavior to switch between old and new controllers. - $pagePreference = SymfonyContainer::getInstance()->get('prestashop.core.admin.page_preference_interface'); - $redirectLegacy = $pagePreference->getTemporaryShouldUseLegacyPage('product'); - if (!$redirectLegacy && $this->identifier == 'id_product') { + if ($this->identifier == 'id_product') { $href = Context::getContext()->link->getAdminLink('AdminProducts', true, ['id_product' => $id, 'deleteproduct' => 1]); } diff --git a/classes/lang/KeysReference/FeatureFlagLang.php b/classes/lang/KeysReference/FeatureFlagLang.php index 87b6e0ec49cc3..6ee1f43f74a37 100644 --- a/classes/lang/KeysReference/FeatureFlagLang.php +++ b/classes/lang/KeysReference/FeatureFlagLang.php @@ -24,23 +24,6 @@ * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) */ -// Product feature flag in 1.7.8 -trans('Experimental product page', 'Admin.Advparameters.Feature'); -trans( - 'This page benefits from increased performance and includes new features such as a new combination management system. Please note this is a work in progress and some features are not available yet.', - 'Admin.Advparameters.Help' -); - -// Product feature flag in 8.0 -trans('New product page - Single store', 'Admin.Advparameters.Feature'); -// Product feature flag in 8.1 -trans('New product page', 'Admin.Advparameters.Feature'); -trans('This page benefits from increased performance and includes new features such as a new combination management system.', 'Admin.Advparameters.Help'); - -// Product multi store feature flag in 8.0 -trans('New product page - Multistore', 'Admin.Advparameters.Feature'); -trans('Access the new product page, even in a multistore context. This is a work in progress and some features are not available.', 'Admin.Advparameters.Help'); - // Legacy link feature flags trans('Attribute groups', 'Admin.Advparameters.Feature'); trans('Enable or disable the migrated attribute page.', 'Admin.Advparameters.Help'); diff --git a/controllers/admin/AdminProductsController.php b/controllers/admin/AdminProductsController.php deleted file mode 100644 index 5678409605ea0..0000000000000 --- a/controllers/admin/AdminProductsController.php +++ /dev/null @@ -1,3233 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -use PrestaShop\PrestaShop\Adapter\Product\SpecificPrice\Update\SpecificPricePriorityUpdater; -use PrestaShop\PrestaShop\Adapter\SymfonyContainer; -use PrestaShop\PrestaShop\Core\Exception\CoreException; -use PrestaShop\PrestaShop\Core\Image\ImageFormatConfiguration; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * @property Product|null $object - */ -class AdminProductsControllerCore extends AdminController -{ - /** - * @var int Max image size for upload - * As of 1.5 it is recommended to not set a limit to max image size - */ - protected $max_file_size = null; - - protected $_category; - /** - * @var string name of the tab to display - */ - protected $tab_display; - protected $tab_display_module; - - /** - * The order in the array decides the order in the list of tab. If an element's value is a number, it will be preloaded. - * The tabs are preloaded from the smallest to the highest number. - * - * @var array product tabs - */ - protected $available_tabs = []; - - protected $default_tab = 'Informations'; - - protected $available_tabs_lang = []; - - /** @var string */ - protected $position_identifier = 'id_product'; - - protected $submitted_tabs; - - protected $id_current_category; - - public function __construct($theme_name = 'default') - { - $this->bootstrap = true; - $this->table = 'product'; - $this->className = 'Product'; - parent::__construct('', $theme_name); - } - - public function init() - { - if (Tools::getIsset('id_product')) { - if (Tools::getIsset('addproduct') || Tools::getIsset('updateproduct')) { - $sfContainer = SymfonyContainer::getInstance(); - if (null !== $sfContainer) { - $sfRouter = $sfContainer->get('router'); - Tools::redirectAdmin($sfRouter->generate( - 'admin_product_form', - ['id' => Tools::getValue('id_product')] - )); - } - } - } - - return parent::init(); - } - - public static function getQuantities($echo, $tr) - { - if ((int) $tr['is_virtual'] == 1 && $tr['nb_downloadable'] == 0) { - return '∞'; - } else { - return $echo; - } - } - - protected function _cleanMetaKeywords(string $keywords) - { - if (!empty($keywords)) { - $out = []; - $words = explode(',', $keywords); - foreach ($words as $word_item) { - $word_item = trim($word_item); - if (!empty($word_item)) { - $out[] = $word_item; - } - } - - return (count($out) > 0) ? implode(',', $out) : ''; - } else { - return ''; - } - } - - /** - * @param Product|ObjectModel $object - * @param string $table - */ - protected function copyFromPost(&$object, $table) - { - parent::copyFromPost($object, $table); - if (get_class($object) != 'Product') { - return; - } - - /* Additional fields */ - foreach (Language::getIDs(false) as $id_lang) { - if (isset($_POST['meta_keywords_' . $id_lang])) { - $_POST['meta_keywords_' . $id_lang] = $this->_cleanMetaKeywords(Tools::strtolower($_POST['meta_keywords_' . $id_lang])); - $object->meta_keywords[$id_lang] = $_POST['meta_keywords_' . $id_lang]; - } - } - $_POST['width'] = empty($_POST['width']) ? '0' : str_replace(',', '.', $_POST['width']); - $_POST['height'] = empty($_POST['height']) ? '0' : str_replace(',', '.', $_POST['height']); - $_POST['depth'] = empty($_POST['depth']) ? '0' : str_replace(',', '.', $_POST['depth']); - $_POST['weight'] = empty($_POST['weight']) ? '0' : str_replace(',', '.', $_POST['weight']); - - if (Tools::getIsset('unit_price') != null) { - $object->unit_price = (float) str_replace(',', '.', Tools::getValue('unit_price')); - } - if (Tools::getIsset('ecotax') != null) { - $object->ecotax = (float) str_replace(',', '.', Tools::getValue('ecotax')); - } - - if ($this->isTabSubmitted('Informations')) { - if ($this->checkMultishopBox('available_for_order', $this->context)) { - $object->available_for_order = (bool) Tools::getValue('available_for_order'); - } - - if ($this->checkMultishopBox('show_price', $this->context)) { - $object->show_price = $object->available_for_order || (bool) Tools::getValue('show_price'); - } - - if ($this->checkMultishopBox('online_only', $this->context)) { - $object->online_only = (bool) Tools::getValue('online_only'); - } - - if ($this->checkMultishopBox('show_condition', $this->context)) { - $object->show_condition = (bool) Tools::getValue('show_condition'); - } - } - if ($this->isTabSubmitted('Prices')) { - $object->on_sale = (bool) Tools::getValue('on_sale'); - } - } - - public function checkMultishopBox($field, $context = null) - { - static $checkbox = null; - static $shop_context = null; - - if ($context == null && $shop_context == null) { - $context = Context::getContext(); - } - - if ($shop_context == null) { - $shop_context = $context->shop->getContext(); - } - - if ($checkbox == null) { - $checkbox = Tools::getValue('multishop_check', []); - } - - if ($shop_context == Shop::CONTEXT_SHOP) { - return true; - } - - if (isset($checkbox[$field]) && $checkbox[$field] == 1) { - return true; - } - - return false; - } - - /** - * @param int $id_lang - * @param string $orderBy - * @param string $orderWay - * @param int $start - * @param int $limit - * @param int|null $id_lang_shop - * - * @throws PrestaShopDatabaseException - * @throws PrestaShopException - * - * @deprecated - */ - public function getList($id_lang, $orderBy = null, $orderWay = null, $start = 0, $limit = null, $id_lang_shop = null) - { - $orderByPriceFinal = (empty($orderBy) ? ($this->context->cookie->__get($this->table . 'Orderby') ? $this->context->cookie->__get($this->table . 'Orderby') : 'id_' . $this->table) : $orderBy); - $orderWayPriceFinal = (empty($orderWay) ? ($this->context->cookie->__get($this->table . 'Orderway') ? $this->context->cookie->__get($this->table . 'Orderby') : 'ASC') : $orderWay); - if ($orderByPriceFinal == 'price_final') { - $orderBy = 'id_' . $this->table; - $orderWay = 'ASC'; - } - parent::getList($id_lang, $orderBy, $orderWay, $start, $limit, $this->context->shop->id); - - /* update product quantity with attributes ...*/ - $nb = count($this->_list); - if ($this->_list) { - $context = $this->context->cloneContext(); - $context->shop = clone $context->shop; - /* update product final price */ - for ($i = 0; $i < $nb; ++$i) { - if (Context::getContext()->shop->getContext() != Shop::CONTEXT_SHOP) { - $context->shop = new Shop((int) $this->_list[$i]['id_shop_default']); - } - - // convert price with the currency from context - $this->_list[$i]['price'] = Tools::convertPrice($this->_list[$i]['price'], $this->context->currency, true, $this->context); - $this->_list[$i]['price_tmp'] = (float) Product::getPriceStatic( - $this->_list[$i]['id_product'], - true, - null, - (int) Configuration::get('PS_PRICE_DISPLAY_PRECISION'), - null, - false, - true, - 1, - true, - null, - null, - null, - $nothing, - true, - true, - $context - ); - } - } - - if ($orderByPriceFinal == 'price_final') { - if (strtolower($orderWayPriceFinal) == 'desc') { - uasort($this->_list, 'cmpPriceDesc'); - } else { - uasort($this->_list, 'cmpPriceAsc'); - } - } - for ($i = 0; $this->_list && $i < $nb; ++$i) { - $this->_list[$i]['price_final'] = $this->_list[$i]['price_tmp']; - unset($this->_list[$i]['price_tmp']); - } - } - - protected function loadObject($opt = false) - { - $result = parent::loadObject($opt); - if ($result && Validate::isLoadedObject($this->object)) { - if (Shop::getContext() == Shop::CONTEXT_SHOP && Shop::isFeatureActive() && !$this->object->isAssociatedToShop()) { - $default_product = new Product((int) $this->object->id, false, null, (int) $this->object->id_shop_default); - $def = ObjectModel::getDefinition($this->object); - foreach ($def['fields'] as $field_name => $row) { - if (is_array($default_product->$field_name)) { - foreach ($default_product->$field_name as $key => $value) { - $this->object->{$field_name}[$key] = $value; - } - } else { - $this->object->$field_name = $default_product->$field_name; - } - } - } - $this->object->loadStockData(); - } - - return $result; - } - - public function ajaxProcessGetCategoryTree() - { - $category = Tools::getValue('category', Category::getRootCategory()->id); - $full_tree = Tools::getValue('fullTree', 0); - $use_check_box = Tools::getValue('useCheckBox', 1); - $selected = Tools::getValue('selected', []); - $id_tree = Tools::getValue('type'); - $input_name = str_replace(['[', ']'], '', Tools::getValue('inputName', null)); - - $tree = new HelperTreeCategories('subtree_associated_categories'); - $tree->setTemplate('subtree_associated_categories.tpl') - ->setUseCheckBox($use_check_box) - ->setUseSearch(true) - ->setIdTree($id_tree) - ->setSelectedCategories($selected) - ->setFullTree($full_tree) - ->setChildrenOnly(true) - ->setNoJS(true) - ->setRootCategory($category); - - if ($input_name) { - $tree->setInputName($input_name); - } - - die($tree->render()); - } - - public function ajaxProcessGetCountriesOptions() - { - if (!$res = Country::getCountriesByIdShop((int) Tools::getValue('id_shop'), (int) $this->context->language->id)) { - return; - } - - $tpl = $this->createTemplate('specific_prices_shop_update.tpl'); - $tpl->assign( - [ - 'option_list' => $res, - 'key_id' => 'id_country', - 'key_value' => 'name', - ] - ); - - $this->content = $tpl->fetch(); - } - - public function ajaxProcessGetCurrenciesOptions() - { - if (!$res = Currency::getCurrenciesByIdShop((int) Tools::getValue('id_shop'))) { - return; - } - - $tpl = $this->createTemplate('specific_prices_shop_update.tpl'); - $tpl->assign( - [ - 'option_list' => $res, - 'key_id' => 'id_currency', - 'key_value' => 'name', - ] - ); - - $this->content = $tpl->fetch(); - } - - public function ajaxProcessGetGroupsOptions() - { - if (!$res = Group::getGroups((int) $this->context->language->id, (int) Tools::getValue('id_shop'))) { - return; - } - - $tpl = $this->createTemplate('specific_prices_shop_update.tpl'); - $tpl->assign( - [ - 'option_list' => $res, - 'key_id' => 'id_group', - 'key_value' => 'name', - ] - ); - - $this->content = $tpl->fetch(); - } - - public function processDeleteVirtualProduct() - { - if (!($id_product_download = ProductDownload::getIdFromIdProduct((int) Tools::getValue('id_product')))) { - $this->errors[] = $this->trans('Cannot retrieve file.', [], 'Admin.Notifications.Error'); - } else { - $product_download = new ProductDownload((int) $id_product_download); - - if (!$product_download->deleteFile((int) $id_product_download)) { - $this->errors[] = $this->trans('Cannot delete file', [], 'Admin.Notifications.Error'); - } else { - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) Tools::getValue('id_product') . '&updateproduct&key_tab=VirtualProduct&conf=1&token=' . $this->token; - } - } - - $this->display = 'edit'; - $this->tab_display = 'VirtualProduct'; - } - - public function ajaxProcessAddAttachment() - { - if (!$this->access('edit')) { - return die(json_encode(['error' => 'You do not have the right permission'])); - } - if (isset($_FILES['attachment_file'])) { - if ((int) $_FILES['attachment_file']['error'] === 1) { - $_FILES['attachment_file']['error'] = []; - - $max_upload = (int) ini_get('upload_max_filesize'); - $max_post = (int) ini_get('post_max_size'); - $upload_mb = min($max_upload, $max_post); - $_FILES['attachment_file']['error'][] = sprintf( - 'File %1$s exceeds the size allowed by the server. The limit is set to %2$d MB.', - '' . $_FILES['attachment_file']['name'] . ' ', - '' . $upload_mb . '' - ); - } - - $_FILES['attachment_file']['error'] = []; - - $is_attachment_name_valid = false; - $attachment_names = Tools::getValue('attachment_name'); - $attachment_descriptions = Tools::getValue('attachment_description'); - - if (!isset($attachment_names) || !$attachment_names) { - $attachment_names = []; - } - - if (!isset($attachment_descriptions) || !$attachment_descriptions) { - $attachment_descriptions = []; - } - - foreach ($attachment_names as $lang => $name) { - $language = Language::getLanguage((int) $lang); - - if (Tools::strlen($name) > 0) { - $is_attachment_name_valid = true; - } - - if (!Validate::isGenericName($name)) { - $_FILES['attachment_file']['error'][] = $this->trans('Invalid name for %s language', [$language['name']], 'Admin.Notifications.Error'); - } elseif (Tools::strlen($name) > 32) { - $_FILES['attachment_file']['error'][] = $this->trans('The name for %1s language is too long (%2d chars max).', [$language['name'], 32], 'Admin.Notifications.Error'); - } - } - - foreach ($attachment_descriptions as $lang => $description) { - $language = Language::getLanguage((int) $lang); - - if (!Validate::isCleanHtml($description)) { - $_FILES['attachment_file']['error'][] = $this->trans('Invalid description for %s language.', [$language['name']], 'Admin.Catalog.Notification'); - } - } - - if (!$is_attachment_name_valid) { - $_FILES['attachment_file']['error'][] = $this->trans('An attachment name is required.', [], 'Admin.Catalog.Notification'); - } - - if (empty($_FILES['attachment_file']['error'])) { - if (is_uploaded_file($_FILES['attachment_file']['tmp_name'])) { - if ($_FILES['attachment_file']['size'] > (Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE') * 1024 * 1024)) { - $_FILES['attachment_file']['error'][] = sprintf( - 'The file is too large. Maximum size allowed is: %1$d kB. The file you are trying to upload is %2$d kB.', - (Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE') * 1024), - number_format(($_FILES['attachment_file']['size'] / 1024), 2, '.', '') - ); - } else { - do { - $uniqid = sha1(microtime()); - } while (file_exists(_PS_DOWNLOAD_DIR_ . $uniqid)); - if (!copy($_FILES['attachment_file']['tmp_name'], _PS_DOWNLOAD_DIR_ . $uniqid)) { - $_FILES['attachment_file']['error'][] = 'File copy failed'; - } - @unlink($_FILES['attachment_file']['tmp_name']); - } - } else { - $_FILES['attachment_file']['error'][] = $this->trans('The file is missing.', [], 'Admin.Notifications.Error'); - } - - if (empty($_FILES['attachment_file']['error']) && isset($uniqid)) { - $attachment = new Attachment(); - - foreach ($attachment_names as $lang => $name) { - $attachment->name[(int) $lang] = $name; - } - - foreach ($attachment_descriptions as $lang => $description) { - $attachment->description[(int) $lang] = $description; - } - - $attachment->file = $uniqid; - $attachment->mime = $_FILES['attachment_file']['type']; - $attachment->file_name = $_FILES['attachment_file']['name']; - - if (empty($attachment->mime) || Tools::strlen($attachment->mime) > 128) { - $_FILES['attachment_file']['error'][] = $this->trans('Invalid file extension', [], 'Admin.Notifications.Error'); - } - if (!Validate::isGenericName($attachment->file_name)) { - $_FILES['attachment_file']['error'][] = $this->trans('Invalid file name', [], 'Admin.Notifications.Error'); - } - if (Tools::strlen($attachment->file_name) > 128) { - $_FILES['attachment_file']['error'][] = $this->trans('The file name is too long.', [], 'Admin.Notifications.Error'); - } - if (empty($this->errors)) { - $res = $attachment->add(); - if (!$res) { - $_FILES['attachment_file']['error'][] = $this->trans('This attachment was unable to be loaded into the database.', [], 'Admin.Catalog.Notification'); - } else { - $_FILES['attachment_file']['id_attachment'] = $attachment->id; - $_FILES['attachment_file']['filename'] = $attachment->name[$this->context->employee->id_lang]; - $id_product = (int) Tools::getValue($this->identifier); - $res = $attachment->attachProduct($id_product); - if (!$res) { - $_FILES['attachment_file']['error'][] = $this->trans('We were unable to associate this attachment to a product.', [], 'Admin.Catalog.Notification'); - } - } - } else { - $_FILES['attachment_file']['error'][] = $this->trans('Invalid file', [], 'Admin.Notifications.Error'); - } - } - } - - die(json_encode($_FILES)); - } - } - - /** - * Attach an existing attachment to the product. - */ - public function processAttachments() - { - if ($id = (int) Tools::getValue($this->identifier)) { - $attachments = trim(Tools::getValue('arrayAttachments'), ','); - $attachments = explode(',', $attachments); - if (!Attachment::attachToProduct($id, $attachments)) { - $this->errors[] = $this->trans('An error occurred while saving product attachments.', [], 'Admin.Catalog.Notification'); - } - } - } - - public function processDuplicate() - { - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $id_product_old = $product->id; - if (empty($product->price) && Shop::getContext() == Shop::CONTEXT_GROUP) { - $shops = ShopGroup::getShopsFromGroup(Shop::getContextShopGroupID()); - foreach ($shops as $shop) { - if ($product->isAssociatedToShop($shop['id_shop'])) { - $product_price = new Product($id_product_old, false, null, $shop['id_shop']); - $product->price = $product_price->price; - } - } - } - unset( - $product->id, - $product->id_product - ); - - $product->indexed = false; - $product->active = false; - if ($product->add() - && Category::duplicateProductCategories($id_product_old, $product->id) - && Product::duplicateSuppliers($id_product_old, $product->id) - && ($combination_images = Product::duplicateAttributes($id_product_old, $product->id)) !== false - && GroupReduction::duplicateReduction($id_product_old, $product->id) - && Product::duplicateAccessories($id_product_old, $product->id) - && Product::duplicateFeatures($id_product_old, $product->id) - && Product::duplicateSpecificPrices($id_product_old, $product->id) - && Pack::duplicate($id_product_old, $product->id) - && Product::duplicateCustomizationFields($id_product_old, $product->id) - && Product::duplicateTags($id_product_old, $product->id) - && Product::duplicateDownload($id_product_old, $product->id)) { - if ($product->hasAttributes()) { - Product::updateDefaultAttribute($product->id); - } - - if (!Tools::getValue('noimage') && !Image::duplicateProductImages($id_product_old, $product->id, $combination_images)) { - $this->errors[] = $this->trans('An error occurred while copying the image.', [], 'Admin.Notifications.Error'); - } else { - Hook::exec('actionProductAdd', ['id_product_old' => $id_product_old, 'id_product' => (int) $product->id, 'product' => $product]); - if (in_array($product->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $product->id); - } - $this->redirect_after = self::$currentIndex . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&conf=19&token=' . $this->token; - } - } else { - $this->errors[] = $this->trans('An error occurred while creating an object.', [], 'Admin.Notifications.Error'); - } - } - } - - /** - * @return bool|ObjectModel|void|null - * - * @throws PrestaShopException - */ - public function processDelete() - { - $object = $this->loadObject(); - if (Validate::isLoadedObject($object)) { - /** @var Product $object */ - // check if request at least one object with noZeroObject - if (isset($object->noZeroObject) && count($taxes = call_user_func([$this->className, $object->noZeroObject])) <= 1) { - $this->errors[] = $this->trans('You need at least one object.', [], 'Admin.Notifications.Error') . ' ' . $this->table . '
' . $this->trans('You cannot delete all of the items.', [], 'Admin.Notifications.Error'); - } else { - if (!count($this->errors)) { - if ($object->delete()) { - $id_category = (int) Tools::getValue('id_category'); - $category_url = empty($id_category) ? '' : '&id_category=' . (int) $id_category; - PrestaShopLogger::addLog(sprintf('%s deletion', $this->className), 1, null, $this->className, (int) $object->id, true, (int) $this->context->employee->id); - $this->redirect_after = self::$currentIndex . '&conf=1&token=' . $this->token . $category_url; - } else { - $this->errors[] = $this->trans('An error occurred during deletion.', [], 'Admin.Notifications.Error'); - } - } - } - } else { - $this->errors[] = $this->trans('An error occurred while deleting the object.', [], 'Admin.Notifications.Error') . ' ' . $this->table . ' ' . $this->trans('(cannot load object)', [], 'Admin.Notifications.Error'); - } - } - - public function processImage() - { - $id_image = (int) Tools::getValue('id_image'); - $image = new Image((int) $id_image); - if (Validate::isLoadedObject($image)) { - /* Update product image/legend */ - // @todo : move in processEditProductImage - if (Tools::getIsset('editImage')) { - if ($image->cover) { - $_POST['cover'] = 1; - } - - $_POST['id_image'] = $image->id; - } elseif (Tools::getIsset('coverImage')) { - /* Choose product cover image */ - Image::deleteCover($image->id_product); - $image->cover = true; - if (!$image->update()) { - $this->errors[] = $this->trans('You cannot change the product\'s cover image.', [], 'Admin.Catalog.Notification'); - } else { - $productId = (int) Tools::getValue('id_product'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . $productId . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . $productId . '_' . $this->context->shop->id . '.jpg'); - $this->redirect_after = self::$currentIndex . '&id_product=' . $image->id_product . '&id_category=' . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&action=Images&addproduct' . '&token=' . $this->token; - } - } elseif (Tools::getIsset('imgPosition') && Tools::getIsset('imgDirection')) { - /* Choose product image position */ - $image->updatePosition(Tools::getValue('imgDirection'), Tools::getValue('imgPosition')); - $this->redirect_after = self::$currentIndex . '&id_product=' . $image->id_product . '&id_category=' . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&add' . $this->table . '&action=Images&token=' . $this->token; - } - } else { - $this->errors[] = $this->trans('The image could not be found. ', [], 'Admin.Catalog.Notification'); - } - } - - /** - * @return bool|void - * - * @throws PrestaShopException - */ - protected function processBulkDelete() - { - if ($this->access('delete')) { - if (is_array($this->boxes) && !empty($this->boxes)) { - $object = new $this->className(); - - if (isset($object->noZeroObject) && - // Check if all object will be deleted - (count(call_user_func([$this->className, $object->noZeroObject])) <= 1 || count($_POST[$this->table . 'Box']) == count(call_user_func([$this->className, $object->noZeroObject])))) { - $this->errors[] = $this->trans('You need at least one object.', [], 'Admin.Notifications.Error') . ' ' . $this->table . '
' . $this->trans('You cannot delete all of the items.', [], 'Admin.Notifications.Error'); - } else { - $success = 1; - $products = Tools::getValue($this->table . 'Box'); - if (is_array($products) && ($count = count($products))) { - // Deleting products can be quite long on a cheap server. Let's say 1.5 seconds by product (I've seen it!). - if ((int) (ini_get('max_execution_time')) < round($count * 1.5)) { - ini_set('max_execution_time', (string) round($count * 1.5)); - } - - foreach ($products as $id_product) { - $product = new Product((int) $id_product); - if (!count($this->errors)) { - if ($product->delete()) { - PrestaShopLogger::addLog(sprintf('%s deletion', $this->className), 1, null, $this->className, (int) $product->id, true, (int) $this->context->employee->id); - } else { - $success = false; - } - } else { - $success = 0; - } - } - } - - if ($success) { - $id_category = (int) Tools::getValue('id_category'); - $category_url = empty($id_category) ? '' : '&id_category=' . (int) $id_category; - $this->redirect_after = self::$currentIndex . '&conf=2&token=' . $this->token . $category_url; - } else { - $this->errors[] = $this->trans('An error occurred while deleting this selection.', [], 'Admin.Notifications.Error'); - } - } - } else { - $this->errors[] = $this->trans('You must select at least one element to delete.', [], 'Admin.Notifications.Error'); - } - } else { - $this->errors[] = $this->trans('You do not have permission to delete this.', [], 'Admin.Notifications.Error'); - } - } - - public function processProductAttribute() - { - // Don't process if the combination fields have not been submitted - if (!Combination::isFeatureActive() || !Tools::getValue('attribute_combination_list')) { - return; - } - - if (Validate::isLoadedObject($product = $this->object)) { - if ($this->isProductFieldUpdated('attribute_price') && !Tools::getIsset('attribute_price')) { - $this->errors[] = $this->trans('The price attribute is required.', [], 'Admin.Catalog.Notification'); - } - if (!Tools::getIsset('attribute_combination_list') || Tools::isEmpty(Tools::getValue('attribute_combination_list'))) { - $this->errors[] = $this->trans('You must add at least one attribute.', [], 'Admin.Catalog.Notification'); - } - - $array_checks = [ - 'reference' => 'isReference', - 'supplier_reference' => 'isReference', - 'location' => 'isReference', - 'ean13' => 'isEan13', - 'isbn' => 'isIsbn', - 'upc' => 'isUpc', - 'mpn' => 'isMpn', - 'wholesale_price' => 'isPrice', - 'price' => 'isPrice', - 'ecotax' => 'isPrice', - 'quantity' => 'isInt', - 'weight' => 'isUnsignedFloat', - 'unit_price_impact' => 'isPrice', - 'default_on' => 'isBool', - 'minimal_quantity' => 'isUnsignedInt', - 'available_date' => 'isDateFormat', - ]; - foreach ($array_checks as $property => $check) { - if (Tools::getValue('attribute_' . $property) !== false && !call_user_func(['Validate', $check], Tools::getValue('attribute_' . $property))) { - $this->errors[] = $this->trans('The %s field is not valid', [$property], 'Admin.Notifications.Error'); - } - } - - if (!count($this->errors)) { - if (!isset($_POST['attribute_wholesale_price'])) { - $_POST['attribute_wholesale_price'] = 0; - } - if (!isset($_POST['attribute_price_impact'])) { - $_POST['attribute_price_impact'] = 0; - } - if (!isset($_POST['attribute_weight_impact'])) { - $_POST['attribute_weight_impact'] = 0; - } - if (!isset($_POST['attribute_ecotax'])) { - $_POST['attribute_ecotax'] = 0; - } - if (Tools::getValue('attribute_default')) { - $product->deleteDefaultAttributes(); - } - - // Change existing one - if (($id_product_attribute = (int) Tools::getValue('id_product_attribute')) || ($id_product_attribute = $product->productAttributeExists(Tools::getValue('attribute_combination_list'), false, null, true, true))) { - if ($this->access('edit')) { - if ($this->isProductFieldUpdated('available_date_attribute') && (Tools::getValue('available_date_attribute') != '' && !Validate::isDateFormat(Tools::getValue('available_date_attribute')))) { - $this->errors[] = $this->trans('Invalid date format.', [], 'Admin.Notifications.Error'); - } else { - $product->updateAttribute( - (int) $id_product_attribute, - $this->isProductFieldUpdated('attribute_wholesale_price') ? Tools::getValue('attribute_wholesale_price') : null, - $this->isProductFieldUpdated('attribute_price_impact') ? Tools::getValue('attribute_price') * Tools::getValue('attribute_price_impact') : null, - $this->isProductFieldUpdated('attribute_weight_impact') ? Tools::getValue('attribute_weight') * Tools::getValue('attribute_weight_impact') : null, - $this->isProductFieldUpdated('attribute_unit_impact') ? Tools::getValue('attribute_unity') * Tools::getValue('attribute_unit_impact') : null, - $this->isProductFieldUpdated('attribute_ecotax') ? Tools::getValue('attribute_ecotax') : null, - Tools::getValue('id_image_attr'), - Tools::getValue('attribute_reference'), - Tools::getValue('attribute_ean13'), - $this->isProductFieldUpdated('attribute_default') ? Tools::getValue('attribute_default') : null, - Tools::getValue('attribute_location'), - Tools::getValue('attribute_upc'), - $this->isProductFieldUpdated('attribute_minimal_quantity') ? Tools::getValue('attribute_minimal_quantity') : null, - $this->isProductFieldUpdated('available_date_attribute') ? Tools::getValue('available_date_attribute') : null, - false, - [], - Tools::getValue('attribute_isbn'), - Tools::getValue('attribute_low_stock_threshold'), - Tools::getValue('attribute_low_stock_alert'), - Tools::getValue('attribute_mpn') - ); - StockAvailable::setProductOutOfStock((int) $product->id, $product->out_of_stock, null, (int) $id_product_attribute); - } - } else { - $this->errors[] = $this->trans('You do not have permission to add this.', [], 'Admin.Notifications.Error'); - } - } else { - // Add new - if ($this->access('add')) { - if ($product->productAttributeExists(Tools::getValue('attribute_combination_list'))) { - $this->errors[] = $this->trans('This combination already exists.', [], 'Admin.Catalog.Notification'); - } else { - $id_product_attribute = $product->addCombinationEntity( - Tools::getValue('attribute_wholesale_price'), - Tools::getValue('attribute_price') * Tools::getValue('attribute_price_impact'), - Tools::getValue('attribute_weight') * Tools::getValue('attribute_weight_impact'), - Tools::getValue('attribute_unity') * Tools::getValue('attribute_unit_impact'), - Tools::getValue('attribute_ecotax'), - 0, - Tools::getValue('id_image_attr'), - Tools::getValue('attribute_reference'), - 0, - Tools::getValue('attribute_ean13'), - Tools::getValue('attribute_default'), - Tools::getValue('attribute_location'), - Tools::getValue('attribute_upc'), - Tools::getValue('attribute_minimal_quantity'), - [], - Tools::getValue('available_date_attribute'), - Tools::getValue('attribute_isbn'), - Tools::getValue('attribute_low_stock_threshold'), - Tools::getValue('attribute_low_stock_alert'), - Tools::getValue('attribute_mpn') - ); - StockAvailable::setProductOutOfStock((int) $product->id, $product->out_of_stock, null, (int) $id_product_attribute); - } - } else { - $this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error'); - } - } - if (!count($this->errors)) { - $combination = new Combination((int) $id_product_attribute); - $combination->setAttributes(Tools::getValue('attribute_combination_list')); - - // images could be deleted before - $id_images = Tools::getValue('id_image_attr'); - if (!empty($id_images)) { - $combination->setImages($id_images); - } - - $product->checkDefaultAttributes(); - if (Tools::getValue('attribute_default')) { - Product::updateDefaultAttribute((int) $product->id); - $product->cache_default_attribute = (int) $id_product_attribute; - - if ($available_date = Tools::getValue('available_date_attribute')) { - $product->setAvailableDate($available_date); - } else { - $product->setAvailableDate(); - } - } - } - } - } - } - - public function processFeatures($id_product = null) - { - if (!Feature::isFeatureActive()) { - return; - } - - $id_product = (int) $id_product ? $id_product : (int) Tools::getValue('id_product'); - - if (Validate::isLoadedObject($product = new Product($id_product))) { - // delete all objects - $product->deleteFeatures(); - - // add new objects - $languages = Language::getLanguages(false); - $form = Tools::getValue('form', false); - if (false !== $form) { - $features = isset($form['step1']['features']) ? $form['step1']['features'] : []; - if (is_array($features)) { - foreach ($features as $feature) { - if (!empty($feature['value'])) { - $product->addFeaturesToDB($feature['feature'], $feature['value']); - } elseif ($defaultValue = $this->checkFeatures($languages, $feature)) { - $idValue = $product->addFeaturesToDB($feature['feature'], 0, 1); - foreach ($languages as $language) { - $valueToAdd = (isset($feature['custom_value'][$language['id_lang']])) - ? $feature['custom_value'][$language['id_lang']] - : $defaultValue; - - $product->addFeaturesCustomToDB($idValue, (int) $language['id_lang'], $valueToAdd); - } - } - } - } - } - } else { - $this->errors[] = $this->trans('A product must be created before adding features.', [], 'Admin.Catalog.Notification'); - } - } - - /** - * This function is never called at the moment (specific prices cannot be edited). - */ - public function processPricesModification() - { - $id_specific_prices = Tools::getValue('spm_id_specific_price'); - $id_combinations = Tools::getValue('spm_id_product_attribute'); - $id_shops = Tools::getValue('spm_id_shop'); - $id_currencies = Tools::getValue('spm_id_currency'); - $id_countries = Tools::getValue('spm_id_country'); - $id_groups = Tools::getValue('spm_id_group'); - $id_customers = Tools::getValue('spm_id_customer'); - $prices = Tools::getValue('spm_price'); - $from_quantities = Tools::getValue('spm_from_quantity'); - $reductions = Tools::getValue('spm_reduction'); - $reduction_types = Tools::getValue('spm_reduction_type'); - $froms = Tools::getValue('spm_from'); - $tos = Tools::getValue('spm_to'); - - foreach ($id_specific_prices as $key => $id_specific_price) { - if ($reduction_types[$key] == 'percentage' && ((float) $reductions[$key] <= 0 || (float) $reductions[$key] > 100)) { - $this->errors[] = $this->trans('The submitted reduction value (0-100) is out-of-range.', [], 'Admin.Catalog.Notification'); - } elseif ($this->_validateSpecificPrice($id_shops[$key], $id_currencies[$key], $id_countries[$key], $id_groups[$key], $id_customers[$key], $prices[$key], $from_quantities[$key], $reductions[$key], $reduction_types[$key], $froms[$key], $tos[$key], $id_combinations[$key])) { - $specific_price = new SpecificPrice((int) ($id_specific_price)); - $specific_price->id_shop = (int) $id_shops[$key]; - $specific_price->id_product_attribute = (int) $id_combinations[$key]; - $specific_price->id_currency = (int) ($id_currencies[$key]); - $specific_price->id_country = (int) ($id_countries[$key]); - $specific_price->id_group = (int) ($id_groups[$key]); - $specific_price->id_customer = (int) $id_customers[$key]; - $specific_price->price = (float) ($prices[$key]); - $specific_price->from_quantity = (int) ($from_quantities[$key]); - $specific_price->reduction = (float) ($reduction_types[$key] == 'percentage' ? ($reductions[$key] / 100) : $reductions[$key]); - $specific_price->reduction_type = !$reductions[$key] ? 'amount' : $reduction_types[$key]; - $specific_price->from = !$froms[$key] ? '0000-00-00 00:00:00' : $froms[$key]; - $specific_price->to = !$tos[$key] ? '0000-00-00 00:00:00' : $tos[$key]; - if (!$specific_price->update()) { - $this->errors[] = $this->trans('An error occurred while updating the specific price.', [], 'Admin.Catalog.Notification'); - } - } - } - if (!count($this->errors)) { - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) (Tools::getValue('id_product')) . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&update' . $this->table . '&action=Prices&token=' . $this->token; - } - } - - public function processPriceAddition() - { - // Check if a specific price has been submitted - if (!Tools::getIsset('submitPriceAddition')) { - return; - } - - $id_product = Tools::getValue('id_product'); - $id_product_attribute = Tools::getValue('sp_id_product_attribute'); - $id_shop = Tools::getValue('sp_id_shop'); - $id_currency = Tools::getValue('sp_id_currency'); - $id_country = Tools::getValue('sp_id_country'); - $id_group = Tools::getValue('sp_id_group'); - $id_customer = Tools::getValue('sp_id_customer'); - $price = Tools::getValue('leave_bprice') ? '-1' : Tools::getValue('sp_price'); - $from_quantity = Tools::getValue('sp_from_quantity'); - $reduction = (float) (Tools::getValue('sp_reduction')); - $reduction_tax = Tools::getValue('sp_reduction_tax'); - $reduction_type = !$reduction ? 'amount' : Tools::getValue('sp_reduction_type'); - $reduction_type = $reduction_type == '-' ? 'amount' : $reduction_type; - $from = Tools::getValue('sp_from'); - if (!$from) { - $from = '0000-00-00 00:00:00'; - } - $to = Tools::getValue('sp_to'); - if (!$to) { - $to = '0000-00-00 00:00:00'; - } - - if (($price == '-1') && ((float) $reduction == '0')) { - $this->errors[] = $this->trans('No reduction value has been submitted.', [], 'Admin.Catalog.Notification'); - } elseif ($to != '0000-00-00 00:00:00' && strtotime($to) < strtotime($from)) { - $this->errors[] = $this->trans('Invalid date range', [], 'Admin.Notifications.Error'); - } elseif ($reduction_type == 'percentage' && ((float) $reduction <= 0 || (float) $reduction > 100)) { - $this->errors[] = $this->trans('The submitted reduction value (0-100) is out-of-range.', [], 'Admin.Catalog.Notification'); - } elseif ($this->_validateSpecificPrice($id_shop, $id_currency, $id_country, $id_group, $id_customer, $price, $from_quantity, $reduction, $reduction_type, $from, $to, $id_product_attribute)) { - $specificPrice = new SpecificPrice(); - $specificPrice->id_product = (int) $id_product; - $specificPrice->id_product_attribute = (int) $id_product_attribute; - $specificPrice->id_shop = (int) $id_shop; - $specificPrice->id_currency = (int) ($id_currency); - $specificPrice->id_country = (int) ($id_country); - $specificPrice->id_group = (int) ($id_group); - $specificPrice->id_customer = (int) $id_customer; - $specificPrice->price = (float) ($price); - $specificPrice->from_quantity = (int) ($from_quantity); - $specificPrice->reduction = (float) ($reduction_type == 'percentage' ? $reduction / 100 : $reduction); - $specificPrice->reduction_tax = $reduction_tax; - $specificPrice->reduction_type = $reduction_type; - $specificPrice->from = $from; - $specificPrice->to = $to; - if (!$specificPrice->add()) { - $this->errors[] = $this->trans('An error occurred while updating the specific price.', [], 'Admin.Catalog.Notification'); - } - } - } - - public function ajaxProcessDeleteSpecificPrice() - { - if ($this->access('delete')) { - $id_specific_price = (int) Tools::getValue('id_specific_price'); - if (!$id_specific_price || !Validate::isUnsignedId($id_specific_price)) { - $error = $this->trans('The specific price ID is invalid.', [], 'Admin.Catalog.Notification'); - } else { - $specificPrice = new SpecificPrice((int) $id_specific_price); - if (!$specificPrice->delete()) { - $error = $this->trans('An error occurred while attempting to delete the specific price.', [], 'Admin.Catalog.Notification'); - } - } - } else { - $error = $this->trans('You do not have permission to delete this.', [], 'Admin.Notifications.Error'); - } - - if (isset($error)) { - $json = [ - 'status' => 'error', - 'message' => $error, - ]; - } else { - $json = [ - 'status' => 'ok', - 'message' => $this->_conf[1], - ]; - } - - die(json_encode($json)); - } - - public function processSpecificPricePriorities() - { - if (!($obj = $this->loadObject())) { - return; - } - if (!$priorities = Tools::getValue('specificPricePriority')) { - $this->errors[] = $this->trans('Please specify priorities.', [], 'Admin.Catalog.Notification'); - } elseif (Tools::isSubmit('specificPricePriorityToAll') && Tools::getValue('specificPricePriorityToAll')) { - $sfContainer = SymfonyContainer::getInstance(); - /** @var SpecificPricePriorityUpdater $specificPricePriorityUpdater */ - $specificPricePriorityUpdater = $sfContainer->get(SpecificPricePriorityUpdater::class); - - try { - $specificPricePriorityUpdater->updateDefaultPriorities($priorities); - $this->confirmations[] = 'The price rule has successfully updated'; - } catch (CoreException $e) { - $this->errors[] = $this->trans('An error occurred while updating priorities.', [], 'Admin.Catalog.Notification'); - } - } elseif (!SpecificPrice::setSpecificPriority((int) $obj->id, $priorities)) { - $this->errors[] = $this->trans('An error occurred while setting priorities.', [], 'Admin.Catalog.Notification'); - } - } - - public function processCustomizationConfiguration() - { - $product = $this->object; - // Get the number of existing customization fields ($product->text_fields is the updated value, not the existing value) - $current_customization = $product->getCustomizationFieldIds(); - $files_count = 0; - $text_count = 0; - if (is_array($current_customization)) { - foreach ($current_customization as $field) { - if ($field['type'] == Product::CUSTOMIZE_TEXTFIELD) { - ++$text_count; - } else { - ++$files_count; - } - } - } - - if (!$product->createLabels((int) $product->uploadable_files - $files_count, (int) $product->text_fields - $text_count)) { - $this->errors[] = $this->trans('An error occurred while creating customization fields.', [], 'Admin.Catalog.Notification'); - } - if (!count($this->errors) && !$product->updateLabels()) { - $this->errors[] = $this->trans('An error occurred while updating customization fields.', [], 'Admin.Catalog.Notification'); - } - $product->customizable = ($product->uploadable_files > 0 || $product->text_fields > 0) ? 1 : 0; - if (($product->uploadable_files != $files_count || $product->text_fields != $text_count) && !count($this->errors) && !$product->update()) { - $this->errors[] = $this->trans('An error occurred while updating the custom configuration.', [], 'Admin.Catalog.Notification'); - } - } - - public function processProductCustomization() - { - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - foreach ($_POST as $field => $value) { - if (strncmp($field, 'label_', 6) == 0 && !Validate::isLabel($value)) { - $this->errors[] = $this->trans('The label fields defined are invalid.', [], 'Admin.Catalog.Notification'); - } - } - if (empty($this->errors) && !$product->updateLabels()) { - $this->errors[] = $this->trans('An error occurred while updating customization fields.', [], 'Admin.Catalog.Notification'); - } - if (empty($this->errors)) { - $this->confirmations[] = 'Update successful'; - } - } else { - $this->errors[] = $this->trans('A product must be created before adding customization.', [], 'Admin.Catalog.Notification'); - } - } - - /** - * Overrides parent for custom redirect link. - * - * @return bool|ObjectModel|void|null - * - * @throws PrestaShopException - */ - public function processPosition() - { - $object = $this->loadObject(); - /** @var Product $object */ - if (!Validate::isLoadedObject($object)) { - $this->errors[] = $this->trans('An error occurred while updating the status for an object.', [], 'Admin.Notifications.Error') . - ' ' . $this->table . ' ' . $this->trans('(cannot load object)', [], 'Admin.Notifications.Error'); - } elseif (!$object->updatePosition((bool) Tools::getValue('way'), (int) Tools::getValue('position'))) { - $this->errors[] = $this->trans('Failed to update the position.', [], 'Admin.Notifications.Error'); - } else { - $category = new Category((int) Tools::getValue('id_category')); - if (Validate::isLoadedObject($category)) { - Hook::exec('actionCategoryUpdate', ['category' => $category]); - } - $this->redirect_after = self::$currentIndex . '&' . $this->table . 'Orderby=position&' . $this->table . 'Orderway=asc&action=Customization&conf=5' . (($id_category = (Tools::getIsset('id_category') ? (int) Tools::getValue('id_category') : '')) ? ('&id_category=' . $id_category) : '') . '&token=' . Tools::getAdminTokenLite('AdminProducts'); - } - } - - public function initProcess() - { - if (Tools::isSubmit('submitAddproductAndStay') || Tools::isSubmit('submitAddproduct')) { - $this->id_object = (int) Tools::getValue('id_product'); - $this->object = new Product($this->id_object); - - if ($this->isTabSubmitted('Informations') && $this->object->is_virtual && (int) Tools::getValue('type_product') != 2) { - if ($id_product_download = (int) ProductDownload::getIdFromIdProduct($this->id_object)) { - $product_download = new ProductDownload($id_product_download); - if (!$product_download->deleteFile($id_product_download)) { - $this->errors[] = $this->trans('Cannot delete file', [], 'Admin.Notifications.Error'); - } - } - } - } - - // Delete a product in the download folder - if (Tools::getValue('deleteVirtualProduct')) { - if ($this->access('delete')) { - $this->action = 'deleteVirtualProduct'; - } else { - $this->errors[] = $this->trans('You do not have permission to delete this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::isSubmit('submitAddProductAndPreview')) { - // Product preview - $this->display = 'edit'; - $this->action = 'save'; - if (Tools::getValue('id_product')) { - $this->id_object = Tools::getValue('id_product'); - $this->object = new Product((int) Tools::getValue('id_product')); - } - } elseif (Tools::isSubmit('submitAttachments')) { - if ($this->access('edit')) { - $this->action = 'attachments'; - $this->tab_display = 'attachments'; - } else { - $this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::getIsset('duplicate' . $this->table)) { - // Product duplication - if ($this->access('add')) { - $this->action = 'duplicate'; - } else { - $this->errors[] = $this->trans('You do not have permission to add this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::getValue('id_image') && Tools::getValue('ajax')) { - // Product images management - if ($this->access('edit')) { - $this->action = 'image'; - } else { - $this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::isSubmit('submitProductAttribute')) { - // Product attributes management - if ($this->access('edit')) { - $this->action = 'productAttribute'; - } else { - $this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::isSubmit('submitFeatures') || Tools::isSubmit('submitFeaturesAndStay')) { - // Product features management - if ($this->access('edit')) { - $this->action = 'features'; - } else { - $this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::isSubmit('submitPricesModification')) { - // Product specific prices management NEVER USED - if ($this->access('add')) { - $this->action = 'pricesModification'; - } else { - $this->errors[] = $this->trans('You do not have permission to add this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::isSubmit('deleteSpecificPrice')) { - if ($this->access('delete')) { - $this->action = 'deleteSpecificPrice'; - } else { - $this->errors[] = $this->trans('You do not have permission to delete this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::isSubmit('submitSpecificPricePriorities')) { - if ($this->access('edit')) { - $this->action = 'specificPricePriorities'; - $this->tab_display = 'prices'; - } else { - $this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::isSubmit('submitCustomizationConfiguration')) { - // Customization management - if ($this->access('edit')) { - $this->action = 'customizationConfiguration'; - $this->tab_display = 'customization'; - $this->display = 'edit'; - } else { - $this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::isSubmit('submitProductCustomization')) { - if ($this->access('edit')) { - $this->action = 'productCustomization'; - $this->tab_display = 'customization'; - $this->display = 'edit'; - } else { - $this->errors[] = $this->trans('You do not have permission to edit this.', [], 'Admin.Notifications.Error'); - } - } elseif (Tools::isSubmit('id_product')) { - $post_max_size = Tools::getMaxUploadSize(Configuration::get('PS_LIMIT_UPLOAD_FILE_VALUE') * 1024 * 1024); - if ($post_max_size && isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] && $_SERVER['CONTENT_LENGTH'] > $post_max_size) { - $this->errors[] = $this->trans( - 'The uploaded file exceeds the "Maximum size for a downloadable product" set in preferences (%1$dMB) or the post_max_size/ directive in php.ini (%2$dMB).', - [ - number_format((float) Configuration::get('PS_LIMIT_UPLOAD_FILE_VALUE')), - ($post_max_size / 1024 / 1024), - ], - 'Admin.Catalog.Notification' - ); - } - } - - if (!$this->action) { - parent::initProcess(); - } else { - $this->id_object = (int) Tools::getValue($this->identifier); - } - - if (isset($this->available_tabs[Tools::getValue('key_tab')])) { - $this->tab_display = Tools::getValue('key_tab'); - } - - // Set tab to display if not decided already - if (!$this->tab_display && $this->action) { - if (in_array($this->action, array_keys($this->available_tabs))) { - $this->tab_display = $this->action; - } - } - - // And if still not set, use default - if (!$this->tab_display) { - if (in_array($this->default_tab, $this->available_tabs)) { - $this->tab_display = $this->default_tab; - } else { - $this->tab_display = key($this->available_tabs); - } - } - } - - /** - * postProcess for new form archi (need object return). - * - * @return ObjectModel|false - */ - public function postCoreProcess() - { - return parent::postProcess(); - } - - /** - * postProcess handle every checks before saving products information. - */ - public function postProcess() - { - if (!$this->redirect_after) { - parent::postProcess(); - } - - if ($this->display == 'edit' || $this->display == 'add') { - $this->addJqueryUI([ - 'ui.core', - 'ui.widget', - ]); - - $this->addjQueryPlugin([ - 'autocomplete', - 'tablednd', - 'thickbox', - 'ajaxfileupload', - 'date', - 'tagify', - 'select2', - 'validate', - ]); - - $this->addJS([ - _PS_JS_DIR_ . 'admin/products.js', - _PS_JS_DIR_ . 'admin/price.js', - _PS_JS_DIR_ . 'tiny_mce/tiny_mce.js', - _PS_JS_DIR_ . 'admin/tinymce.inc.js', - _PS_JS_DIR_ . 'admin/dnd.js', - _PS_JS_DIR_ . 'jquery/ui/jquery.ui.progressbar.min.js', - _PS_JS_DIR_ . 'vendor/spin.js', - _PS_JS_DIR_ . 'vendor/ladda.js', - ]); - - $this->addJS(_PS_JS_DIR_ . 'jquery/plugins/select2/select2_locale_' . $this->context->language->iso_code . '.js'); - $this->addJS(_PS_JS_DIR_ . 'jquery/plugins/validate/localization/messages_' . $this->context->language->iso_code . '.js'); - - $this->addCSS([ - _PS_JS_DIR_ . 'jquery/plugins/timepicker/jquery-ui-timepicker-addon.css', - ]); - } - } - - public function ajaxProcessDeleteProductAttribute() - { - if (!Combination::isFeatureActive()) { - return; - } - - if ($this->access('delete')) { - $id_product = (int) Tools::getValue('id_product'); - $id_product_attribute = (int) Tools::getValue('id_product_attribute'); - - if ($id_product && Validate::isUnsignedId($id_product) && Validate::isLoadedObject($product = new Product($id_product))) { - $product->deleteAttributeCombination((int) $id_product_attribute); - $product->checkDefaultAttributes(); - if (!$product->hasAttributes()) { - $product->cache_default_attribute = 0; - $product->update(); - } else { - Product::updateDefaultAttribute($id_product); - } - - $json = [ - 'status' => 'ok', - 'message' => $this->_conf[1], - 'id_product_attribute' => (int) $id_product_attribute, - ]; - } else { - $json = [ - 'status' => 'error', - 'message' => 'You cannot delete this attribute.', - ]; - } - } else { - $json = [ - 'status' => 'error', - 'message' => 'You do not have permission to delete this.', - ]; - } - - die(json_encode($json)); - } - - public function ajaxProcessDefaultProductAttribute() - { - if ($this->access('edit')) { - if (!Combination::isFeatureActive()) { - return; - } - - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $product->deleteDefaultAttributes(); - $product->setDefaultAttribute((int) Tools::getValue('id_product_attribute')); - $json = [ - 'status' => 'ok', - 'message' => $this->_conf[4], - ]; - } else { - $json = [ - 'status' => 'error', - 'message' => 'You cannot make this the default attribute.', - ]; - } - - die(json_encode($json)); - } - } - - public function ajaxProcessEditProductAttribute() - { - if ($this->access('edit')) { - $id_product = (int) Tools::getValue('id_product'); - $id_product_attribute = (int) Tools::getValue('id_product_attribute'); - if ($id_product && Validate::isUnsignedId($id_product) && Validate::isLoadedObject($product = new Product((int) $id_product))) { - $combinations = $product->getAttributeCombinationsById($id_product_attribute, $this->context->language->id); - foreach ($combinations as $key => $combination) { - $combinations[$key]['attributes'][] = [$combination['group_name'], $combination['attribute_name'], $combination['id_attribute']]; - } - - die(json_encode($combinations)); - } - } - } - - public function ajaxPreProcess() - { - if (Tools::getIsset('update' . $this->table) && Tools::getIsset('id_' . $this->table)) { - $this->display = 'edit'; - $this->action = Tools::getValue('action'); - } - } - - public function ajaxProcessUpdateProductImageShopAsso() - { - $id_product = (int) Tools::getValue('id_product'); - $id_image = (int) Tools::getValue('id_image'); - $id_shop = (int) Tools::getValue('id_shop'); - - if ($id_image && $id_shop) { - if (Tools::getValue('active') == 'true') { - $res = Db::getInstance()->execute('INSERT INTO ' . _DB_PREFIX_ . 'image_shop (`id_product`, `id_image`, `id_shop`, `cover`) VALUES(' . $id_product . ', ' . $id_image . ', ' . $id_shop . ', NULL)'); - } else { - $res = Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'image_shop WHERE `id_image` = ' . $id_image . ' AND `id_shop` = ' . $id_shop); - } - } - - // Clean covers in image table - $count_cover_image = Db::getInstance()->getValue(' - SELECT COUNT(*) FROM ' . _DB_PREFIX_ . 'image i - INNER JOIN ' . _DB_PREFIX_ . 'image_shop ish ON (i.id_image = ish.id_image AND ish.id_shop = ' . $id_shop . ') - WHERE i.cover = 1 AND i.`id_product` = ' . $id_product); - - if (!$id_image) { - $id_image = Db::getInstance()->getValue(' - SELECT i.`id_image` FROM ' . _DB_PREFIX_ . 'image i - INNER JOIN ' . _DB_PREFIX_ . 'image_shop ish ON (i.id_image = ish.id_image AND ish.id_shop = ' . $id_shop . ') - WHERE i.`id_product` = ' . $id_product); - } - - if ($count_cover_image < 1) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image i SET i.cover = 1 WHERE i.id_image = ' . $id_image . ' AND i.`id_product` = ' . $id_product . ' LIMIT 1'); - } - - // Clean covers in image_shop table - $count_cover_image_shop = Db::getInstance()->getValue(' - SELECT COUNT(*) - FROM ' . _DB_PREFIX_ . 'image_shop ish - WHERE ish.`id_product` = ' . $id_product . ' AND ish.id_shop = ' . $id_shop . ' AND ish.cover = 1'); - - if ($count_cover_image_shop < 1) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image_shop ish SET ish.cover = 1 WHERE ish.id_image = ' . $id_image . ' AND ish.`id_product` = ' . $id_product . ' AND ish.id_shop = ' . (int) $id_shop . ' LIMIT 1'); - } - - if (isset($res) && $res) { - $this->jsonConfirmation($this->_conf[27]); - } else { - $this->jsonError($this->trans('An error occurred while attempting to associate this image with your shop. ', [], 'Admin.Catalog.Notification')); - } - } - - public function ajaxProcessUpdateImagePosition() - { - if (!$this->access('edit')) { - return die(json_encode(['error' => 'You do not have the right permission'])); - } - $res = false; - if ($json = Tools::getValue('json')) { - $res = true; - $json = stripslashes($json); - $images = json_decode($json, true); - foreach ($images as $id => $position) { - $img = new Image((int) $id); - $img->position = (int) $position; - $res &= $img->update(); - } - } - if ($res) { - $this->jsonConfirmation($this->_conf[25]); - } else { - $this->jsonError($this->trans('An error occurred while attempting to move this picture.', [], 'Admin.Catalog.Notification')); - } - } - - public function ajaxProcessUpdateCover() - { - if (!$this->access('edit')) { - return die(json_encode(['error' => 'You do not have the right permission'])); - } - Image::deleteCover((int) Tools::getValue('id_product')); - $img = new Image((int) Tools::getValue('id_image')); - $img->cover = true; - - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . (int) $img->id_product . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $img->id_product . '_' . $this->context->shop->id . '.jpg'); - - if ($img->update()) { - $this->jsonConfirmation($this->_conf[26]); - } else { - $this->jsonError($this->trans('An error occurred while attempting to update the cover picture.', [], 'Admin.Catalog.Notification')); - } - } - - public function ajaxProcessDeleteProductImage($id_image = null) - { - $this->display = 'content'; - $res = true; - /* Delete product image */ - $id_image = $id_image ? $id_image : (int) Tools::getValue('id_image'); - - $image = new Image($id_image); - $res &= $image->delete(); - // if deleted image was the cover, change it to the first one - if (!Image::getCover($image->id_product)) { - $res &= Db::getInstance()->execute(' - UPDATE `' . _DB_PREFIX_ . 'image_shop` image_shop - SET image_shop.`cover` = 1 - WHERE image_shop.`id_product` = ' . (int) $image->id_product . ' - AND id_shop=' . (int) $this->context->shop->id . ' LIMIT 1'); - } - - if (!Image::getGlobalCover($image->id_product)) { - $res &= Db::getInstance()->execute(' - UPDATE `' . _DB_PREFIX_ . 'image` i - SET i.`cover` = 1 - WHERE i.`id_product` = ' . (int) $image->id_product . ' LIMIT 1'); - } - - if (file_exists(_PS_TMP_IMG_DIR_ . 'product_' . $image->id_product . '.jpg')) { - $res &= @unlink(_PS_TMP_IMG_DIR_ . 'product_' . $image->id_product . '.jpg'); - } - if (file_exists(_PS_TMP_IMG_DIR_ . 'product_mini_' . $image->id_product . '_' . $this->context->shop->id . '.jpg')) { - $res &= @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . $image->id_product . '_' . $this->context->shop->id . '.jpg'); - } - - if ($res) { - $this->jsonConfirmation($this->_conf[7]); - } else { - $this->jsonError($this->trans('An error occurred while attempting to delete the product image.', [], 'Admin.Catalog.Notification')); - } - } - - protected function _validateSpecificPrice($id_shop, $id_currency, $id_country, $id_group, $id_customer, $price, $from_quantity, $reduction, $reduction_type, $from, $to, $id_combination = 0) - { - if (!Validate::isUnsignedId($id_shop) || !Validate::isUnsignedId($id_currency) || !Validate::isUnsignedId($id_country) || !Validate::isUnsignedId($id_group) || !Validate::isUnsignedId($id_customer)) { - $this->errors[] = $this->trans('Wrong IDs', [], 'Admin.Catalog.Notification'); - } elseif ((!isset($price) && !isset($reduction)) || (isset($price) && !Validate::isNegativePrice($price)) || (isset($reduction) && !Validate::isPrice($reduction))) { - $this->errors[] = $this->trans('Invalid price/discount amount', [], 'Admin.Catalog.Notification'); - } elseif (!Validate::isUnsignedInt($from_quantity)) { - $this->errors[] = $this->trans('Invalid quantity', [], 'Admin.Catalog.Notification'); - } elseif ($reduction && !Validate::isReductionType($reduction_type)) { - $this->errors[] = $this->trans('Please select a discount type (amount or percentage).', [], 'Admin.Catalog.Notification'); - } elseif ($from && $to && (!Validate::isDateFormat($from) || !Validate::isDateFormat($to))) { - $this->errors[] = $this->trans('The from/to date is invalid.', [], 'Admin.Catalog.Notification'); - } elseif (SpecificPrice::exists((int) $this->object->id, $id_combination, $id_shop, $id_group, $id_country, $id_currency, $id_customer, $from_quantity, $from, $to, false)) { - $this->errors[] = $this->trans('A specific price already exists for these parameters.', [], 'Admin.Catalog.Notification'); - } else { - return true; - } - - return false; - } - - /** - * Checking customs feature. - * - * @param array $languages - * @param array $featureInfo - * - * @return int|string - */ - protected function checkFeatures($languages, $featureInfo) - { - $rules = call_user_func(['FeatureValue', 'getValidationRules'], 'FeatureValue'); - $feature = Feature::getFeature((int) Configuration::get('PS_LANG_DEFAULT'), $featureInfo['feature']); - - foreach ($languages as $language) { - if (isset($featureInfo['custom_value'][$language['id_lang']])) { - $val = $featureInfo['custom_value'][$language['id_lang']]; - $current_language = new Language($language['id_lang']); - if (Tools::strlen($val) > $rules['sizeLang']['value']) { - $this->errors[] = $this->trans( - 'The name for feature %1$s is too long in %2$s.', - [ - ' ' . $feature['name'] . '', - $current_language->name, - ], - 'Admin.Catalog.Notification' - ); - } elseif (!call_user_func(['Validate', $rules['validateLang']['value']], $val)) { - $this->errors[] = $this->trans( - 'A valid name required for feature. %1$s in %2$s.', - [ - ' ' . $feature['name'] . '', - $current_language->name, - ], - 'Admin.Catalog.Notification' - ); - } - if (count($this->errors)) { - return 0; - } - // Getting default language - if ($language['id_lang'] == Configuration::get('PS_LANG_DEFAULT')) { - return $val; - } - } - } - - return 0; - } - - /** - * Add or update a product image. - * - * @param Product $product Product object to add image - * @param string $method - * - * @return int|false - */ - public function addProductImage($product, $method = 'auto') - { - /* Updating an existing product image */ - if ($id_image = (int) Tools::getValue('id_image')) { - $image = new Image((int) $id_image); - if (!Validate::isLoadedObject($image)) { - $this->errors[] = $this->trans('An error occurred while uploading the image.', [], 'Admin.Notifications.Error'); - } else { - if (($cover = Tools::getValue('cover')) == 1) { - Image::deleteCover($product->id); - } - $image->cover = $cover; - $this->validateRules('Image'); - $this->copyFromPost($image, 'image'); - if (count($this->errors) || !$image->update()) { - $this->errors[] = $this->trans('An error occurred while updating the image.', [], 'Admin.Notifications.Error'); - } elseif (isset($_FILES['image_product']['tmp_name']) && $_FILES['image_product']['tmp_name'] != null) { - $this->copyImage($product->id, $image->id, $method); - } - } - } - if (isset($image) && Validate::isLoadedObject($image) && !file_exists(_PS_PRODUCT_IMG_DIR_ . $image->getExistingImgPath() . '.' . $image->image_format)) { - $image->delete(); - } - if (count($this->errors)) { - return false; - } - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . $product->id . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . $product->id . '_' . $this->context->shop->id . '.jpg'); - - return (is_int($id_image) && $id_image) ? $id_image : false; - } - - /** - * Copy a product image. - * - * @param int $id_product Product Id for product image filename - * @param int $id_image Image Id for product image filename - * @param string $method - * - * @return void|false - * - * @throws PrestaShopException - */ - public function copyImage($id_product, $id_image, $method = 'auto') - { - if (!isset($_FILES['image_product']['tmp_name'])) { - return false; - } - if ($error = ImageManager::validateUpload($_FILES['image_product'])) { - $this->errors[] = $error; - } else { - $image = new Image($id_image); - - if (!$new_path = $image->getPathForCreation()) { - $this->errors[] = $this->trans('An error occurred while attempting to create a new folder.', [], 'Admin.Notifications.Error'); - } - if (!($tmpName = tempnam(_PS_TMP_IMG_DIR_, 'PS')) || !move_uploaded_file($_FILES['image_product']['tmp_name'], $tmpName)) { - $this->errors[] = $this->trans('An error occurred while uploading the image.', [], 'Admin.Notifications.Error'); - } elseif (!ImageManager::resize($tmpName, $new_path . '.' . $image->image_format)) { - $this->errors[] = $this->trans('An error occurred while copying the image.', [], 'Admin.Notifications.Error'); - } elseif ($method == 'auto') { - $imagesTypes = ImageType::getImagesTypes('products'); - foreach ($imagesTypes as $k => $image_type) { - if (!ImageManager::resize($tmpName, $new_path . '-' . stripslashes($image_type['name']) . '.' . $image->image_format, $image_type['width'], $image_type['height'], $image->image_format)) { - $this->errors[] = $this->trans('An error occurred while copying this image: %s', [stripslashes($image_type['name'])], 'Admin.Notifications.Error'); - } - } - } - - @unlink($tmpName); - Hook::exec('actionWatermark', ['id_image' => $id_image, 'id_product' => $id_product]); - } - } - - protected function updateAssoShop($id_object) - { - //override AdminController::updateAssoShop() specifically for products because shop association is set with the context in ObjectModel - } - - public function processAdd() - { - $this->checkProduct(); - - if (!empty($this->errors)) { - $this->display = 'add'; - - return false; - } - - $this->object = new $this->className(); - $this->_removeTaxFromEcotax(); - $this->copyFromPost($this->object, $this->table); - if ($this->object->add()) { - PrestaShopLogger::addLog(sprintf('%s addition', $this->className), 1, null, $this->className, (int) $this->object->id, true, (int) $this->context->employee->id); - $this->addCarriers($this->object); - $this->updateAccessories($this->object); - $this->updatePackItems($this->object); - $this->updateDownloadProduct($this->object); - - if (empty($this->errors)) { - $languages = Language::getLanguages(false); - if ($this->isProductFieldUpdated('category_box') && !$this->object->updateCategories(Tools::getValue('categoryBox'))) { - $this->errors[] = $this->trans( - 'An error occurred while linking the object %table_name% to categories.', - [ - '%table_name%' => ' ' . $this->table . ' ', - ], - 'Admin.Notifications.Error' - ); - } elseif (!$this->updateTags($languages, $this->object)) { - $this->errors[] = $this->trans('An error occurred while adding tags.', [], 'Admin.Catalog.Notification'); - } else { - Hook::exec('actionProductAdd', ['id_product_old' => null, 'id_product' => (int) $this->object->id, 'product' => $this->object]); - if (in_array($this->object->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $this->object->id); - } - } - - // Apply groups reductions - $this->object->setGroupReduction(); - - // Save and preview - if (Tools::isSubmit('submitAddProductAndPreview')) { - $this->redirect_after = $this->getPreviewUrl($this->object); - } - - // Save and stay on same form - if ($this->display == 'edit') { - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) $this->object->id - . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') - . '&updateproduct&conf=3&key_tab=' . Tools::safeOutput(Tools::getValue('key_tab')) . '&token=' . $this->token; - } else { - // Default behavior (save and back) - $this->redirect_after = self::$currentIndex - . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') - . '&conf=3&token=' . $this->token; - } - } else { - $this->object->delete(); - // if errors : stay on edit page - $this->display = 'edit'; - } - } else { - $this->errors[] = $this->trans('An error occurred while creating an object.', [], 'Admin.Notifications.Error') . ' ' . $this->table . ''; - } - - return $this->object; - } - - protected function isTabSubmitted($tab_name) - { - if (!is_array($this->submitted_tabs)) { - $this->submitted_tabs = Tools::getValue('submitted_tabs'); - } - - if (is_array($this->submitted_tabs) && in_array($tab_name, $this->submitted_tabs)) { - return true; - } - - return false; - } - - public function processStatus() - { - $this->loadObject(true); - if (!Validate::isLoadedObject($this->object)) { - return false; - } - if (($error = $this->object->validateFields(false, true)) !== true) { - $this->errors[] = $error; - } - if (($error = $this->object->validateFieldsLang(false, true)) !== true) { - $this->errors[] = $error; - } - - if (count($this->errors)) { - return false; - } - - $res = parent::processStatus(); - - $query = trim(Tools::getValue('bo_query')); - $searchType = (int) Tools::getValue('bo_search_type'); - - if ($query) { - $this->redirect_after = preg_replace('/[\?|&](bo_query|bo_search_type)=([^&]*)/i', '', $this->redirect_after); - $this->redirect_after .= '&bo_query=' . $query . '&bo_search_type=' . $searchType; - } - - return $res; - } - - public function processUpdate() - { - $existing_product = $this->object; - - $this->checkProduct(); - - if (!empty($this->errors)) { - $this->display = 'edit'; - - return false; - } - - $id = (int) Tools::getValue('id_' . $this->table); - /* Update an existing product */ - if (!empty($id)) { - /** @var Product $object */ - $object = new $this->className((int) $id); - $this->object = $object; - - if (Validate::isLoadedObject($object)) { - $this->_removeTaxFromEcotax(); - $product_type_before = $object->getType(); - $this->copyFromPost($object, $this->table); - $object->indexed = 0; - - if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP) { - $values = (array) Tools::getValue('multishop_check', []); - $values['state'] = Product::STATE_SAVED; - $values['id_manufacturer'] = true; - $values['ean13'] = true; - $values['mpn'] = true; - $values['isbn'] = true; - $values['upc'] = true; - $values['reference'] = true; - $values['weight'] = true; - $values['depth'] = true; - $values['width'] = true; - $values['height'] = true; - - $object->setFieldsToUpdate($values); - } - - // Duplicate combinations if not associated to shop - if ($this->context->shop->getContext() == Shop::CONTEXT_SHOP && !$object->isAssociatedToShop()) { - $is_associated_to_shop = false; - $combinations = Product::getProductAttributesIds($object->id); - if ($combinations) { - foreach ($combinations as $id_combination) { - $combination = new Combination((int) $id_combination['id_product_attribute']); - $default_combination = new Combination((int) $id_combination['id_product_attribute'], null, (int) $this->object->id_shop_default); - - $def = ObjectModel::getDefinition($default_combination); - foreach ($def['fields'] as $field_name => $row) { - $combination->$field_name = ObjectModel::formatValue($default_combination->$field_name, $def['fields'][$field_name]['type']); - } - - $combination->save(); - } - } - } else { - $is_associated_to_shop = true; - } - - if ($object->update()) { - // If the product doesn't exist in the current shop but exists in another shop - if (Shop::getContext() == Shop::CONTEXT_SHOP && !$existing_product->isAssociatedToShop($this->context->shop->id)) { - $out_of_stock = StockAvailable::outOfStock($existing_product->id, $existing_product->id_shop_default); - StockAvailable::setProductOutOfStock((int) $this->object->id, $out_of_stock, $this->context->shop->id); - } - - PrestaShopLogger::addLog(sprintf('%s modification', $this->className), 1, null, $this->className, (int) $this->object->id, true, (int) $this->context->employee->id); - if (in_array($this->context->shop->getContext(), [Shop::CONTEXT_SHOP, Shop::CONTEXT_ALL])) { - if ($this->isTabSubmitted('Shipping')) { - $this->addCarriers(); - } - if ($this->isTabSubmitted('Associations')) { - $this->updateAccessories($object); - } - if ($this->isTabSubmitted('Suppliers')) { - $this->processSuppliers(); - } - if ($this->isTabSubmitted('Features')) { - $this->processFeatures(); - } - if ($this->isTabSubmitted('Combinations')) { - $this->processProductAttribute(); - } - if ($this->isTabSubmitted('Prices')) { - $this->processPriceAddition(); - $this->processSpecificPricePriorities(); - } - if ($this->isTabSubmitted('Customization')) { - $this->processCustomizationConfiguration(); - } - if ($this->isTabSubmitted('Attachments')) { - $this->processAttachments(); - } - if ($this->isTabSubmitted('Images')) { - $this->processImageLegends(); - } - - $this->updatePackItems($object); - $this->updateDownloadProduct($object, 1); - $this->updateTags(Language::getLanguages(false), $object); - - if ($this->isProductFieldUpdated('category_box') && !$object->updateCategories(Tools::getValue('categoryBox'))) { - $this->errors[] = $this->trans( - 'An error occurred while linking the object %table_name% to categories.', - [ - '%table_name%' => ' ' . $this->table . ' ', - ], - 'Admin.Notifications.Error' - ); - } - } - - if (empty($this->errors)) { - if (in_array($object->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $object->id); - } - - // Save and preview - if (Tools::isSubmit('submitAddProductAndPreview')) { - $this->redirect_after = $this->getPreviewUrl($object); - } else { - $page = (int) Tools::getValue('page'); - // Save and stay on same form - if ($this->display == 'edit') { - $this->confirmations[] = 'Update successful'; - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) $this->object->id - . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') - . '&updateproduct&conf=4&key_tab=' . Tools::safeOutput(Tools::getValue('key_tab')) . ($page > 1 ? '&page=' . (int) $page : '') . '&token=' . $this->token; - } else { - // Default behavior (save and back) - $this->redirect_after = self::$currentIndex . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&conf=4' . ($page > 1 ? '&submitFilterproduct=' . (int) $page : '') . '&token=' . $this->token; - } - } - } else { - // if errors: stay on edit page - $this->display = 'edit'; - } - } else { - if (!$is_associated_to_shop && $combinations) { - foreach ($combinations as $id_combination) { - $combination = new Combination((int) $id_combination['id_product_attribute']); - $combination->delete(); - } - } - $this->errors[] = $this->trans('An error occurred while updating an object.', [], 'Admin.Notifications.Error') . ' ' . $this->table . ' (' . Db::getInstance()->getMsgError() . ')'; - } - } else { - $this->errors[] = $this->trans('An error occurred while updating an object.', [], 'Admin.Notifications.Error') . ' ' . $this->table . ' (' . $this->trans('The object cannot be loaded. ', [], 'Admin.Notifications.Error') . ')'; - } - - return $object; - } - } - - /** - * Check that a saved product is valid. - */ - public function checkProduct() - { - /** @todo : the call_user_func seems to contains only statics values (className = 'Product') */ - $rules = call_user_func([$this->className, 'getValidationRules'], $this->className); - $default_language = new Language((int) Configuration::get('PS_LANG_DEFAULT')); - $languages = Language::getLanguages(false); - - // Check required fields - foreach ($rules['required'] as $field) { - if (!$this->isProductFieldUpdated($field)) { - continue; - } - - if (($value = Tools::getValue($field)) == false && $value != '0') { - if (Tools::getValue('id_' . $this->table) && $field == 'passwd') { - continue; - } - $this->errors[] = $this->trans('The %name% field is required.', ['%name%' => call_user_func([$this->className, 'displayFieldName'], $field, $this->className)], 'Admin.Notifications.Error'); - } - } - - // Check multilingual required fields - foreach ($rules['requiredLang'] as $fieldLang) { - if ($this->isProductFieldUpdated($fieldLang, $default_language->id) && !Tools::getValue($fieldLang . '_' . $default_language->id)) { - $this->errors[] = $this->trans( - 'This %1$s field is required at least in %2$s', - [ - call_user_func([$this->className, 'displayFieldName'], $fieldLang, $this->className), - $default_language->name, - ], - 'Admin.Catalog.Notification' - ); - } - } - - // Check fields sizes - foreach ($rules['size'] as $field => $maxLength) { - if ($this->isProductFieldUpdated($field) && ($value = Tools::getValue($field)) && Tools::strlen($value) > $maxLength) { - $this->errors[] = $this->trans( - 'The %1$s field is too long (%2$d chars max).', - [ - call_user_func([$this->className, 'displayFieldName'], $field, $this->className), - $maxLength, - ], - 'Admin.Catalog.Notification' - ); - } - } - - if (Tools::getIsset('description_short') && $this->isProductFieldUpdated('description_short')) { - $saveShort = Tools::getValue('description_short'); - $_POST['description_short'] = strip_tags(Tools::getValue('description_short')); - } - - // Check description short size without html - $limit = (int) Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT'); - if ($limit <= 0) { - $limit = 400; - } - foreach ($languages as $language) { - if ($this->isProductFieldUpdated('description_short', $language['id_lang']) && ($value = Tools::getValue('description_short_' . $language['id_lang']))) { - // This validation computation actually comes from TinyMceMaxLengthValidator if you modify it here you - // should keep the validator in sync (along with other parts of the code, more info in the - // TinyMceMaxLengthValidator comments). - $replaceArray = [ - "\n", - "\r", - "\n\r", - "\r\n", - ]; - $str = str_replace($replaceArray, [''], strip_tags($value)); - $shortDescriptionLength = iconv_strlen($str); - if ($shortDescriptionLength > $limit) { - $this->errors[] = $this->trans( - 'This %1$s field (%2$s) is too long: %3$d chars max (current count %4$d).', - [ - call_user_func([$this->className, 'displayFieldName'], 'description_short'), - $language['name'], - $limit, - Tools::strlen(strip_tags($value)), - ], - 'Admin.Catalog.Notification' - ); - } - } - } - - // Check multilingual fields sizes - foreach ($rules['sizeLang'] as $fieldLang => $maxLength) { - foreach ($languages as $language) { - $value = Tools::getValue($fieldLang . '_' . $language['id_lang']); - if ($value && Tools::strlen($value) > $maxLength) { - $this->errors[] = $this->trans( - 'The %1$s field is too long (%2$d chars max).', - [ - call_user_func([$this->className, 'displayFieldName'], $fieldLang, $this->className), - $maxLength, - ], - 'Admin.Catalog.Notification' - ); - } - } - } - - if ($this->isProductFieldUpdated('description_short') && isset($_POST['description_short'], $saveShort)) { - $_POST['description_short'] = $saveShort; - } - - // Check fields validity - foreach ($rules['validate'] as $field => $function) { - if ($this->isProductFieldUpdated($field) && ($value = Tools::getValue($field))) { - $res = true; - if (Tools::strtolower($function) == 'iscleanhtml') { - if (!Validate::$function($value, (int) Configuration::get('PS_ALLOW_HTML_IFRAME'))) { - $res = false; - } - } elseif (!Validate::$function($value)) { - $res = false; - } - - if (!$res) { - $this->errors[] = $this->trans( - 'The %s field is invalid.', - [ - call_user_func([$this->className, 'displayFieldName'], $field, $this->className), - ], - 'Admin.Notifications.Error' - ); - } - } - } - // Check multilingual fields validity - foreach ($rules['validateLang'] as $fieldLang => $function) { - foreach ($languages as $language) { - if ($this->isProductFieldUpdated($fieldLang, $language['id_lang']) && ($value = Tools::getValue($fieldLang . '_' . $language['id_lang']))) { - if (!Validate::$function($value, (int) Configuration::get('PS_ALLOW_HTML_IFRAME'))) { - $this->errors[] = $this->trans( - 'The %1$s field (%2$s) is invalid.', - [ - call_user_func([$this->className, 'displayFieldName'], $fieldLang, $this->className), - $language['name'], - ], - 'Admin.Notifications.Error' - ); - } - } - } - } - - // Categories - if ($this->isProductFieldUpdated('id_category_default') && (!Tools::isSubmit('categoryBox') || !count(Tools::getValue('categoryBox')))) { - $this->errors[] = 'Products must be in at least one category.'; - } - - if ($this->isProductFieldUpdated('id_category_default') && (!is_array(Tools::getValue('categoryBox')) || !in_array(Tools::getValue('id_category_default'), Tools::getValue('categoryBox')))) { - $this->errors[] = 'This product must be in the default category.'; - } - - // Tags - foreach ($languages as $language) { - if ($value = Tools::getValue('tags_' . $language['id_lang'])) { - if (!Validate::isTagsList($value)) { - $this->errors[] = $this->trans( - 'The tags list (%s) is invalid.', - [ - $language['name'], - ], - 'Admin.Notifications.Error' - ); - } - } - } - } - - /** - * Check if a field is edited (if the checkbox is checked) - * This method will do something only for multishop with a context all / group. - * - * @param string $field Name of field - * @param int $id_lang - * - * @return bool - */ - protected function isProductFieldUpdated($field, $id_lang = null) - { - // Cache this condition to improve performances - static $is_activated = null; - if (null === $is_activated) { - $is_activated = Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP && $this->id_object; - } - - if (!$is_activated) { - return true; - } - - $def = ObjectModel::getDefinition($this->object); - if (!$this->object->isMultiShopField($field) && null === $id_lang && isset($def['fields'][$field])) { - return true; - } - - if (null === $id_lang) { - return !empty($_POST['multishop_check'][$field]); - } else { - return !empty($_POST['multishop_check'][$field][$id_lang]); - } - } - - protected function _removeTaxFromEcotax() - { - if ($ecotax = Tools::getValue('ecotax')) { - $_POST['ecotax'] = Tools::ps_round($ecotax / (1 + Tax::getProductEcotaxRate() / 100), 6); - } - } - - protected function _applyTaxToEcotax($product) - { - if ($product->ecotax) { - $product->ecotax = Tools::ps_round($product->ecotax * (1 + Tax::getProductEcotaxRate() / 100), 2); - } - } - - /** - * Update product download. - * - * @param Product $product - * @param int $edit - * - * @return bool - */ - public function updateDownloadProduct($product, $edit = 0) - { - //legacy/sf2 form workaround - //if is_virtual_file parameter was not send (SF2 form case), don't process virtual file - if (Tools::getValue('is_virtual_file') === false) { - return false; - } - - if ((int) Tools::getValue('is_virtual_file') == 1) { - if (isset($_FILES['virtual_product_file_uploader']) && $_FILES['virtual_product_file_uploader']['size'] > 0) { - $virtual_product_filename = ProductDownload::getNewFilename(); - $helper = new HelperUploader('virtual_product_file_uploader'); - $helper->setPostMaxSize(Tools::getOctets(ini_get('upload_max_filesize'))) - ->setSavePath(_PS_DOWNLOAD_DIR_)->upload($_FILES['virtual_product_file_uploader'], $virtual_product_filename); - } else { - $virtual_product_filename = Tools::getValue('virtual_product_filename', ProductDownload::getNewFilename()); - } - - $product->setDefaultAttribute(0); //reset cache_default_attribute - - // Trick's - if ($edit == 1) { - $id_product_download = (int) ProductDownload::getIdFromIdProduct((int) $product->id, false); - if (!$id_product_download) { - $id_product_download = (int) Tools::getValue('virtual_product_id'); - } - } else { - $id_product_download = Tools::getValue('virtual_product_id'); - } - - $is_shareable = (bool) Tools::getValue('virtual_product_is_shareable'); - $virtual_product_name = Tools::getValue('virtual_product_name'); - $virtual_product_nb_days = Tools::getValue('virtual_product_nb_days'); - $virtual_product_nb_downloable = Tools::getValue('virtual_product_nb_downloable'); - $virtual_product_expiration_date = Tools::getValue('virtual_product_expiration_date'); - - $download = new ProductDownload((int) $id_product_download); - $download->id_product = (int) $product->id; - $download->display_filename = $virtual_product_name; - $download->filename = $virtual_product_filename; - $download->date_add = date('Y-m-d H:i:s'); - $download->date_expiration = $virtual_product_expiration_date ? $virtual_product_expiration_date . ' 23:59:59' : ''; - $download->nb_days_accessible = (int) $virtual_product_nb_days; - $download->nb_downloadable = (int) $virtual_product_nb_downloable; - $download->active = true; - $download->is_shareable = $is_shareable; - if ($download->save()) { - return true; - } - } else { - /* unactive download product if checkbox not checked */ - if ($edit == 1) { - $id_product_download = (int) ProductDownload::getIdFromIdProduct((int) $product->id); - if (!$id_product_download) { - $id_product_download = (int) Tools::getValue('virtual_product_id'); - } - } else { - $id_product_download = ProductDownload::getIdFromIdProduct($product->id); - } - - if (!empty($id_product_download)) { - $product_download = new ProductDownload((int) $id_product_download); - $product_download->date_expiration = date('Y-m-d H:i:s', time() - 1); - $product_download->active = false; - - return $product_download->save(); - } - } - - return false; - } - - /** - * Update product accessories. - * - * @param object $product Product - */ - public function updateAccessories($product) - { - $product->deleteAccessories(); - if ($accessories = Tools::getValue('inputAccessories')) { - $accessories_id = array_unique(explode('-', $accessories)); - array_pop($accessories_id); - $product->changeAccessories($accessories_id); - } - } - - /** - * Update product tags. - * - * @param array $languages Array languages - * @param object $product Product - * - * @return bool Update result - */ - public function updateTags($languages, $product) - { - $tag_success = true; - /* Reset all tags for THIS product */ - if (!Tag::deleteTagsForProduct((int) $product->id)) { - $this->errors[] = $this->trans('An error occurred while attempting to delete previous tags.', [], 'Admin.Catalog.Notification'); - } - /* Assign tags to this product */ - foreach ($languages as $language) { - if ($value = Tools::getValue('tags_' . $language['id_lang'])) { - $tag_success &= Tag::addTags($language['id_lang'], (int) $product->id, $value); - } - } - - if (!$tag_success) { - $this->errors[] = $this->trans('An error occurred while adding tags.', [], 'Admin.Catalog.Notification'); - } - - return $tag_success; - } - - public function ajaxProcessProductManufacturers() - { - $manufacturers = Manufacturer::getManufacturers(false, 0, true, false, false, false, true); - $jsonArray = []; - - if ($manufacturers) { - foreach ($manufacturers as $manufacturer) { - $tmp = ['optionValue' => $manufacturer['id_manufacturer'], 'optionDisplay' => htmlspecialchars(trim($manufacturer['name']))]; - $jsonArray[] = json_encode($tmp); - } - } - - die('[' . implode(',', $jsonArray) . ']'); - } - - public function getPreviewUrl(Product $product) - { - $id_lang = (int) Configuration::get('PS_LANG_DEFAULT', null, null, Context::getContext()->shop->id); - - if (!ShopUrl::getMainShopDomain()) { - return false; - } - - $is_rewrite_active = (bool) Configuration::get('PS_REWRITING_SETTINGS'); - $preview_url = $this->context->link->getProductLink( - $product, - $this->getFieldValue($product, 'link_rewrite', $this->context->language->id), - Category::getLinkRewrite($this->getFieldValue($product, 'id_category_default'), $this->context->language->id), - null, - $id_lang, - (int) Context::getContext()->shop->id, - 0, - $is_rewrite_active - ); - - if (!$product->active) { - $admin_dir = dirname($_SERVER['PHP_SELF']); - $admin_dir = substr($admin_dir, strrpos($admin_dir, '/') + 1); - $preview_url .= ((strpos($preview_url, '?') === false) ? '?' : '&') . 'adtoken=' . $this->token . '&ad=' . $admin_dir . '&id_employee=' . (int) $this->context->employee->id; - } - - return $preview_url; - } - - /** - * Post treatment for suppliers. - * - * @param int|null $id_product - */ - public function processSuppliers($id_product = null) - { - $id_product = (int) $id_product ? $id_product : (int) Tools::getValue('id_product'); - - if ((int) Tools::getValue('supplier_loaded') === 1 && Validate::isLoadedObject($product = new Product($id_product))) { - // Get all id_product_attribute - $attributes = $product->getAttributesResume($this->context->language->id); - if (empty($attributes)) { - $attributes[] = [ - 'id_product_attribute' => 0, - 'attribute_designation' => '', - ]; - } - - // Get all available suppliers - $suppliers = Supplier::getSuppliers(); - - // Get already associated suppliers - $associated_suppliers = ProductSupplier::getSupplierCollection($product->id); - - $suppliers_to_associate = []; - $new_default_supplier = 0; - $defaultWholeslePrice = (float) 0; - $defaultReference = ''; - - if (Tools::isSubmit('default_supplier')) { - $new_default_supplier = (int) Tools::getValue('default_supplier'); - } - - // Get new associations - foreach ($suppliers as $supplier) { - if (Tools::isSubmit('check_supplier_' . $supplier['id_supplier'])) { - $suppliers_to_associate[] = $supplier['id_supplier']; - } - } - - // Delete already associated suppliers if needed - foreach ($associated_suppliers as $key => $associated_supplier) { - /** @var ProductSupplier $associated_supplier */ - if (!in_array($associated_supplier->id_supplier, $suppliers_to_associate)) { - // Code taken from https://github.com/PrestaShop/PrestaShop/pull/26609/commits/e966aa7d3c2204ddb7318dd7203639845739137b - // ProductSupplier objectModel is shared between v1 & v2 product pages. - // This code ensures keeping old behavior in v1 product page without breaking v2 product page. - - $res = $associated_supplier->delete(); - - if ($res && $associated_supplier->id_product_attribute == 0) { - $items = ProductSupplier::getSupplierCollection($associated_supplier->id_product, false); - foreach ($items as $item) { - /** @var ProductSupplier $item */ - if ($item->id_product_attribute > 0) { - $item->delete(); - } - } - } - - unset($associated_suppliers[$key]); - } - } - - // Associate suppliers - foreach ($suppliers_to_associate as $id) { - $to_add = true; - foreach ($associated_suppliers as $as) { - /** @var ProductSupplier $as */ - if ($id == $as->id_supplier) { - $to_add = false; - } - } - - if ($to_add) { - $product_supplier = new ProductSupplier(); - $product_supplier->id_product = $product->id; - $product_supplier->id_product_attribute = 0; - $product_supplier->id_supplier = $id; - if ($this->context->currency->id) { - $product_supplier->id_currency = (int) $this->context->currency->id; - } else { - $product_supplier->id_currency = Currency::getDefaultCurrencyId(); - } - $product_supplier->save(); - - $associated_suppliers[] = $product_supplier; - foreach ($attributes as $attribute) { - if ((int) $attribute['id_product_attribute'] > 0) { - $product_supplier = new ProductSupplier(); - $product_supplier->id_product = $product->id; - $product_supplier->id_product_attribute = (int) $attribute['id_product_attribute']; - $product_supplier->id_supplier = $id; - $product_supplier->save(); - } - } - } - } - - // Manage references and prices - foreach ($attributes as $attribute) { - foreach ($associated_suppliers as $supplier) { - /** @var ProductSupplier $supplier */ - if (Tools::isSubmit('supplier_reference_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier) || - (Tools::isSubmit('product_price_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier) && - Tools::isSubmit('product_price_currency_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier))) { - $reference = pSQL( - Tools::getValue( - 'supplier_reference_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier, - '' - ) - ); - - $price = (float) str_replace( - [' ', ','], - ['', '.'], - Tools::getValue( - 'product_price_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier, - 0 - ) - ); - - $price = Tools::ps_round($price, 6); - - $id_currency = (int) Tools::getValue( - 'product_price_currency_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier, - 0 - ); - - if ($id_currency <= 0 || !($result = Currency::getCurrency($id_currency))) { - $this->errors[] = $this->trans('The selected currency is not valid', [], 'Admin.Catalog.Notification'); - } - - // Save product-supplier data - $product_supplier_id = (int) ProductSupplier::getIdByProductAndSupplier($product->id, $attribute['id_product_attribute'], $supplier->id_supplier); - - if (!$product_supplier_id) { - $product->addSupplierReference($supplier->id_supplier, (int) $attribute['id_product_attribute'], $reference, (float) $price, (int) $id_currency); - } else { - $product_supplier = new ProductSupplier($product_supplier_id); - $product_supplier->id_currency = (int) $id_currency; - $product_supplier->product_supplier_price_te = (float) $price; - $product_supplier->product_supplier_reference = pSQL($reference); - $product_supplier->update(); - } - - if ($new_default_supplier == $supplier->id_supplier) { - if ((int) $attribute['id_product_attribute'] > 0) { - $data = [ - 'supplier_reference' => pSQL($reference), - 'wholesale_price' => (float) Tools::convertPrice($price, $id_currency), - ]; - $where = ' - a.id_product = ' . (int) $product->id . ' - AND a.id_product_attribute = ' . (int) $attribute['id_product_attribute']; - ObjectModel::updateMultishopTable('Combination', $data, $where); - } else { - // @deprecated 1.7.7.0 This condition block will be remove in the next major, use ProductSupplier instead - $defaultWholeslePrice = (float) Tools::convertPrice($price, $id_currency); - $defaultReference = $reference; - } - } - } - } - } - - if ($this->object) { - $product->updateDefaultSupplierData( - $new_default_supplier, - $defaultReference, - $defaultWholeslePrice - ); - } - } - } - - /** - * Get an array of pack items for display from the product object if specified, else from POST/GET values. - * - * @param Product $product - * - * @return array of pack items - */ - public function getPackItems($product = null) - { - $pack_items = []; - - if (!$product) { - $names_input = Tools::getValue('namePackItems'); - $ids_input = Tools::getValue('inputPackItems'); - if (!$names_input || !$ids_input) { - return []; - } - // ids is an array of string with format : QTYxID - $ids = array_unique(explode('-', $ids_input)); - $names = array_unique(explode('¤', $names_input)); - - $length = count($ids); - for ($i = 0; $i < $length; ++$i) { - if (isset($ids[$i]) && !empty($names[$i])) { - list($pack_items[$i]['pack_quantity'], $pack_items[$i]['id']) = explode('x', $ids[$i]); - $exploded_name = explode('x', $names[$i]); - $pack_items[$i]['name'] = $exploded_name[1]; - } - } - } else { - $i = 0; - foreach ($product->packItems as $pack_item) { - $pack_items[$i]['id'] = $pack_item->id; - $pack_items[$i]['pack_quantity'] = $pack_item->pack_quantity; - $pack_items[$i]['name'] = $pack_item->name; - $pack_items[$i]['reference'] = $pack_item->reference; - $pack_items[$i]['id_product_attribute'] = isset($pack_item->id_pack_product_attribute) && $pack_item->id_pack_product_attribute ? $pack_item->id_pack_product_attribute : 0; - $cover = $pack_item->id_pack_product_attribute ? Product::getCombinationImageById($pack_item->id_pack_product_attribute, Context::getContext()->language->id) : Product::getCover($pack_item->id); - $pack_items[$i]['image'] = Context::getContext()->link->getImageLink($pack_item->link_rewrite, $cover['id_image'], 'home_default'); - // @todo: don't rely on 'home_default' - //$path_to_image = _PS_IMG_DIR_.'p/'.Image::getImgFolderStatic($cover['id_image']).(int)$cover['id_image'].'.jpg'; - //$pack_items[$i]['image'] = ImageManager::thumbnail($path_to_image, 'pack_mini_'.$pack_item->id.'_'.$this->context->shop->id.'.jpg', 120); - ++$i; - } - } - - return $pack_items; - } - - protected function _getFinalPrice($specific_price, $product_price, $tax_rate) - { - return $this->object->getPrice(false, $specific_price['id_product_attribute'], 2); - } - - protected function _getCustomizationFieldIds($labels, $alreadyGenerated, $obj) - { - $customizableFieldIds = []; - if (isset($labels[Product::CUSTOMIZE_FILE])) { - foreach ($labels[Product::CUSTOMIZE_FILE] as $id_customization_field => $label) { - $customizableFieldIds[] = 'label_' . Product::CUSTOMIZE_FILE . '_' . (int) ($id_customization_field); - } - } - if (isset($labels[Product::CUSTOMIZE_TEXTFIELD])) { - foreach ($labels[Product::CUSTOMIZE_TEXTFIELD] as $id_customization_field => $label) { - $customizableFieldIds[] = 'label_' . Product::CUSTOMIZE_TEXTFIELD . '_' . (int) ($id_customization_field); - } - } - $j = 0; - for ($i = $alreadyGenerated[Product::CUSTOMIZE_FILE]; $i < (int) ($this->getFieldValue($obj, 'uploadable_files')); ++$i) { - $customizableFieldIds[] = 'newLabel_' . Product::CUSTOMIZE_FILE . '_' . $j++; - } - $j = 0; - for ($i = $alreadyGenerated[Product::CUSTOMIZE_TEXTFIELD]; $i < (int) ($this->getFieldValue($obj, 'text_fields')); ++$i) { - $customizableFieldIds[] = 'newLabel_' . Product::CUSTOMIZE_TEXTFIELD . '_' . $j++; - } - - return implode('¤', $customizableFieldIds); - } - - protected function getCarrierList() - { - $carrier_list = Carrier::getCarriers($this->context->language->id, false, false, false, null, Carrier::ALL_CARRIERS); - - if ($product = $this->loadObject(true)) { - /** @var Product $product */ - $carrier_selected_list = $product->getCarriers(); - foreach ($carrier_list as &$carrier) { - foreach ($carrier_selected_list as $carrier_selected) { - if ($carrier_selected['id_reference'] == $carrier['id_reference']) { - $carrier['selected'] = true; - - continue; - } - } - } - } - - return $carrier_list; - } - - protected function addCarriers($product = null) - { - if (!isset($product)) { - $product = new Product((int) Tools::getValue('id_product')); - } - - if (Validate::isLoadedObject($product)) { - $carriers = []; - - if (Tools::getValue('selectedCarriers')) { - $carriers = Tools::getValue('selectedCarriers'); - } - - $product->setCarriers($carriers); - } - } - - /** - * Ajax process upload images. - * - * @param int|null $idProduct - * @param string $inputFileName - * @param bool $die If method must die or return values - * - * @return array - */ - public function ajaxProcessaddProductImage($idProduct = null, $inputFileName = 'file', $die = true) - { - $idProduct = $idProduct ? $idProduct : Tools::getValue('id_product'); - - self::$currentIndex = 'index.php?tab=AdminProducts'; - $product = new Product((int) $idProduct); - $legends = Tools::getValue('legend'); - - if (!is_array($legends)) { - $legends = (array) $legends; - } - - if (!Validate::isLoadedObject($product)) { - $files = []; - $files[0]['error'] = $this->trans('Cannot add image because product creation failed.', [], 'Admin.Catalog.Notification'); - } - - $image_uploader = new HelperImageUploader($inputFileName); - $image_uploader->setAcceptTypes(['jpeg', 'gif', 'png', 'jpg', 'webp'])->setMaxSize($this->max_image_size); - $files = $image_uploader->process(); - - foreach ($files as &$file) { - $image = new Image(); - $image->id_product = (int) ($product->id); - $image->position = Image::getHighestPosition($product->id) + 1; - - foreach ($legends as $key => $legend) { - if (!empty($legend)) { - $image->legend[(int) $key] = $legend; - } - } - - $image->cover = !Image::getCover($image->id_product); - - if (($validate = $image->validateFieldsLang(false, true)) !== true) { - $file['error'] = $validate; - } - - if (isset($file['error']) && (!is_numeric($file['error']) || $file['error'] != 0)) { - continue; - } - - if (!$image->add()) { - $file['error'] = $this->trans('Error while creating additional image', [], 'Admin.Catalog.Notification'); - } else { - if (!$new_path = $image->getPathForCreation()) { - $file['error'] = $this->trans('An error occurred while attempting to create a new folder.', [], 'Admin.Notifications.Error'); - - continue; - } - - $error = 0; - - if (!ImageManager::resize($file['save_path'], $new_path . '.' . $image->image_format, null, null, 'jpg', false, $error)) { - switch ($error) { - case ImageManager::ERROR_FILE_NOT_EXIST: - $file['error'] = $this->trans('An error occurred while copying image, the file does not exist anymore.', [], 'Admin.Catalog.Notification'); - - break; - - case ImageManager::ERROR_FILE_WIDTH: - $file['error'] = $this->trans('An error occurred while copying image, the file width is 0px.', [], 'Admin.Catalog.Notification'); - - break; - - case ImageManager::ERROR_MEMORY_LIMIT: - $file['error'] = $this->trans('An error occurred while copying image, check your memory limit.', [], 'Admin.Catalog.Notification'); - - break; - - default: - $file['error'] = $this->trans('An error occurred while copying the image.', [], 'Admin.Catalog.Notification'); - - break; - } - - continue; - } else { - $imagesTypes = ImageType::getImagesTypes('products'); - - // Should we generate high DPI images? - $generate_hight_dpi_images = (bool) Configuration::get('PS_HIGHT_DPI'); - - /* - * Let's resolve which formats we will use for image generation. - * - * In case of .jpg images, the actual format inside is decided by ImageManager. - */ - $configuredImageFormats = SymfonyContainer::getInstance()->get(ImageFormatConfiguration::class)->getGenerationFormats(); - - foreach ($imagesTypes as $imageType) { - foreach ($configuredImageFormats as $imageFormat) { - // For JPG images, we let Imagemanager decide what to do and choose between JPG/PNG. - // For webp and avif extensions, we want it to follow our command and ignore the original format. - $forceFormat = ($imageFormat !== 'jpg'); - if (!ImageManager::resize( - $file['save_path'], - $new_path . '-' . stripslashes($imageType['name']) . '.' . $imageFormat, - $imageType['width'], - $imageType['height'], - $imageFormat, - $forceFormat - )) { - $file['error'] = $this->trans('An error occurred while copying this image:', [], 'Admin.Notifications.Error') . ' ' . stripslashes($imageType['name']); - - continue; - } - } - } - } - - unlink($file['save_path']); - //Necesary to prevent hacking - unset($file['save_path']); - Hook::exec('actionWatermark', ['id_image' => $image->id, 'id_product' => $product->id]); - - if (!$image->update()) { - $file['error'] = $this->trans('Error while updating the status.', [], 'Admin.Notifications.Error'); - - continue; - } - - // Associate image to shop from context - $shops = Shop::getContextListShopID(); - $image->associateTo($shops); - $json_shops = []; - - foreach ($shops as $id_shop) { - $json_shops[$id_shop] = true; - } - - $file['status'] = 'ok'; - $file['id'] = $image->id; - $file['position'] = $image->position; - $file['cover'] = $image->cover; - $file['legend'] = $image->legend; - $file['path'] = $image->getExistingImgPath(); - $file['shops'] = $json_shops; - - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . (int) $product->id . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $product->id . '_' . $this->context->shop->id . '.jpg'); - } - } - - if ($die) { - die(json_encode([$image_uploader->getName() => $files])); - } else { - return $files; - } - } - - public function ajaxProcessProductQuantity() - { - if (!$this->access('edit')) { - return die(json_encode(['error' => 'You do not have the right permission'])); - } - if (!Tools::getValue('actionQty')) { - return json_encode(['error' => 'Undefined action']); - } - - $product = new Product((int) Tools::getValue('id_product'), true); - switch (Tools::getValue('actionQty')) { - case 'pack_stock_type': - $value = Tools::getValue('value'); - if ($value === false) { - die(json_encode(['error' => 'Undefined value'])); - } - if ((int) $value != 0 && (int) $value != 1 - && (int) $value != 2 && (int) $value != 3) { - die(json_encode(['error' => 'Incorrect value'])); - } - - Product::setPackStockType($product->id, $value); - - break; - - case 'out_of_stock': - if (Tools::getValue('value') === false) { - die(json_encode(['error' => 'Undefined value'])); - } - if (!in_array((int) Tools::getValue('value'), [0, 1, 2])) { - die(json_encode(['error' => 'Incorrect value'])); - } - - StockAvailable::setProductOutOfStock($product->id, (int) Tools::getValue('value')); - - break; - - case 'set_qty': - if (Tools::getValue('value') === false || (!is_numeric(trim(Tools::getValue('value'))))) { - die(json_encode(['error' => 'Undefined value'])); - } - if (Tools::getValue('id_product_attribute') === false) { - die(json_encode(['error' => 'Undefined id product attribute'])); - } - - StockAvailable::setQuantity($product->id, (int) Tools::getValue('id_product_attribute'), (int) Tools::getValue('value')); - Hook::exec('actionProductUpdate', ['id_product' => (int) $product->id, 'product' => $product]); - - // Catch potential echo from modules - // This echoed error is kept for legacy controllers, but is dropped during sf refactoring of the hook. - $error = ob_get_contents(); - if (!empty($error)) { - ob_end_clean(); - die(json_encode(['error' => $error])); - } - - break; - } - die(json_encode(['error' => false])); - } - - public function getCombinationImagesJS() - { - if (!($obj = $this->loadObject(true))) { - return; - } - /** @var Product $obj */ - $content = 'var combination_images = new Array();'; - if (!$allCombinationImages = $obj->getCombinationImages($this->context->language->id)) { - return $content; - } - foreach ($allCombinationImages as $id_product_attribute => $combination_images) { - $i = 0; - $content .= 'combination_images[' . (int) $id_product_attribute . '] = new Array();'; - foreach ($combination_images as $combination_image) { - $content .= 'combination_images[' . (int) $id_product_attribute . '][' . $i++ . '] = ' . (int) $combination_image['id_image'] . ';'; - } - } - - return $content; - } - - public function haveThisAccessory($accessory_id, $accessories) - { - foreach ($accessories as $accessory) { - if ((int) $accessory['id_product'] == (int) $accessory_id) { - return true; - } - } - - return false; - } - - protected function initPack(Product $product) - { - $this->tpl_form_vars['is_pack'] = ($product->id && Pack::isPack($product->id)) || Tools::getValue('type_product') == Product::PTYPE_PACK; - $product->packItems = Pack::getItems($product->id, $this->context->language->id); - - $input_pack_items = ''; - if (Tools::getValue('inputPackItems')) { - $input_pack_items = Tools::getValue('inputPackItems'); - } else { - foreach ($product->packItems as $pack_item) { - $input_pack_items .= $pack_item->pack_quantity . 'x' . $pack_item->id . '-'; - } - } - $this->tpl_form_vars['input_pack_items'] = $input_pack_items; - - $input_namepack_items = ''; - if (Tools::getValue('namePackItems')) { - $input_namepack_items = Tools::getValue('namePackItems'); - } else { - foreach ($product->packItems as $pack_item) { - $input_namepack_items .= $pack_item->pack_quantity . ' x ' . $pack_item->name . '¤'; - } - } - $this->tpl_form_vars['input_namepack_items'] = $input_namepack_items; - } - - /** - * delete all items in pack, then check if type_product value is 2. - * if yes, add the pack items from input "inputPackItems". - * - * @param Product $product - */ - public function updatePackItems($product) - { - Pack::deleteItems($product->id); - // lines format: QTY x ID-QTY x ID - if (Tools::getValue('type_product') == Product::PTYPE_PACK) { - $product->setDefaultAttribute(0); //reset cache_default_attribute - $items = Tools::getValue('inputPackItems'); - $lines = array_unique(explode('-', $items)); - - // lines is an array of string with format : QTYxIDxID_PRODUCT_ATTRIBUTE - foreach ($lines as $line) { - if (!empty($line)) { - $item_id_attribute = 0; - count($array = explode('x', $line)) == 3 ? list($qty, $item_id, $item_id_attribute) = $array : list($qty, $item_id) = $array; - if ($qty > 0) { - if (Pack::isPack((int) $item_id)) { - $this->errors[] = $this->trans('You can\'t add product packs into a pack', [], 'Admin.Catalog.Notification'); - } elseif (!Pack::addItem((int) $product->id, (int) $item_id, (int) $qty, (int) $item_id_attribute)) { - $this->errors[] = $this->trans('An error occurred while attempting to add products to the pack.', [], 'Admin.Catalog.Notification'); - } - } - } - } - } - } - - public function ajaxProcessCheckProductName() - { - if ($this->access('view')) { - $search = Tools::getValue('q'); - $id_lang = Tools::getValue('id_lang'); - $limit = Tools::getValue('limit'); - if (Context::getContext()->shop->getContext() != Shop::CONTEXT_SHOP) { - $result = false; - } else { - $result = Db::getInstance()->executeS(' - SELECT DISTINCT pl.`name`, p.`id_product`, pl.`id_shop` - FROM `' . _DB_PREFIX_ . 'product` p - LEFT JOIN `' . _DB_PREFIX_ . 'product_shop` ps ON (ps.id_product = p.id_product AND ps.id_shop =' . (int) Context::getContext()->shop->id . ') - LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl - ON (pl.`id_product` = p.`id_product` AND pl.`id_lang` = ' . (int) $id_lang . ') - WHERE pl.`name` LIKE "%' . pSQL($search) . '%" AND ps.id_product IS NULL - GROUP BY pl.`id_product` - LIMIT ' . (int) $limit); - } - die(json_encode($result)); - } - } - - public function ajaxProcessUpdatePositions() - { - if ($this->access('edit')) { - $way = (bool) (Tools::getValue('way')); - $id_product = (int) Tools::getValue('id_product'); - $id_category = (int) Tools::getValue('id_category'); - $positions = Tools::getValue('product'); - $page = (int) Tools::getValue('page'); - $selected_pagination = (int) Tools::getValue('selected_pagination'); - - if (is_array($positions)) { - foreach ($positions as $position => $value) { - $pos = explode('_', $value); - - if ((isset($pos[1], $pos[2])) && ($pos[1] == $id_category && (int) $pos[2] === $id_product)) { - if ($page > 1) { - $position = $position + (($page - 1) * $selected_pagination); - } - - $product = new Product((int) $pos[2]); - if (Validate::isLoadedObject($product)) { - if ($product->updatePosition($way, $position)) { - $category = new Category((int) $id_category); - if (Validate::isLoadedObject($category)) { - Hook::exec('actionCategoryUpdate', ['category' => $category]); - } - echo 'ok position ' . (int) $position . ' for product ' . (int) $pos[2] . "\r\n"; - } else { - echo '{"hasError" : true, "errors" : "Can not update product ' . (int) $id_product . ' to position ' . (int) $position . ' "}'; - } - } else { - echo '{"hasError" : true, "errors" : "This product (' . (int) $id_product . ') can t be loaded"}'; - } - - break; - } - } - } - } - } - - public function ajaxProcessPublishProduct() - { - if ($this->access('edit')) { - if ($id_product = (int) Tools::getValue('id_product')) { - $bo_product_url = dirname($_SERVER['PHP_SELF']) . '/index.php?tab=AdminProducts&id_product=' . $id_product . '&updateproduct&token=' . $this->token; - - if (Tools::getValue('redirect')) { - die($bo_product_url); - } - - $product = new Product((int) $id_product); - if (!Validate::isLoadedObject($product)) { - die('error: invalid id'); - } - - $product->active = true; - - if ($product->save()) { - die($bo_product_url); - } else { - die('error: saving'); - } - } - } - } - - public function processImageLegends() - { - if (Tools::getValue('key_tab') == 'Images' && Tools::getValue('submitAddproductAndStay') == 'update_legends' && Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $id_image = (int) Tools::getValue('id_caption'); - $language_ids = Language::getIDs(false); - foreach ($_POST as $key => $val) { - if (preg_match('/^legend_([0-9]+)/i', $key, $match)) { - foreach ($language_ids as $id_lang) { - if ($val && $id_lang == $match[1]) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image_lang SET legend = "' . pSQL($val) . '" WHERE ' . ($id_image ? 'id_image = ' . (int) $id_image : 'EXISTS (SELECT 1 FROM ' . _DB_PREFIX_ . 'image WHERE ' . _DB_PREFIX_ . 'image.id_image = ' . _DB_PREFIX_ . 'image_lang.id_image AND id_product = ' . (int) $product->id . ')') . ' AND id_lang = ' . (int) $id_lang); - } - } - } - } - } - } - - /** - * Returns in an homemade JSON with the content of a products pack. - */ - public function displayAjaxProductPackItems() - { - $jsonArray = []; - $products = Db::getInstance()->executeS(' - SELECT p.`id_product`, pl.`name` - FROM `' . _DB_PREFIX_ . 'product` p - NATURAL LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl - WHERE pl.`id_lang` = ' . (int) (Tools::getValue('id_lang')) . ' - ' . Shop::addSqlRestrictionOnLang('pl') . ' - AND NOT EXISTS (SELECT 1 FROM `' . _DB_PREFIX_ . 'pack` WHERE `id_product_pack` = p.`id_product`) - AND p.`id_product` != ' . (int) (Tools::getValue('id_product'))); - - foreach ($products as $packItem) { - $jsonArray[] = '{"value": "' . (int) ($packItem['id_product']) . '-' . addslashes($packItem['name']) - . '", "text":"' . (int) ($packItem['id_product']) . ' - ' . addslashes($packItem['name']) . '"}'; - } - $this->ajaxRender('[' . implode(',', $jsonArray) . ']'); - } - - /** - * Displays a list of products when their name matches a given query - * Optional parameters allow products to be excluded from the results. - */ - public function displayAjaxProductsList() - { - $query = Tools::getValue('q', false); - if (empty($query)) { - return; - } - - /* - * In the SQL request the "q" param is used entirely to match result in database. - * In this way if string:"(ref : #ref_pattern#)" is displayed on the return list, - * they are no return values just because string:"(ref : #ref_pattern#)" - * is not write in the name field of the product. - * So the ref pattern will be cut for the search request. - */ - if ($pos = strpos($query, ' (ref:')) { - $query = substr($query, 0, $pos); - } - - $excludeIds = Tools::getValue('excludeIds', false); - if ($excludeIds && $excludeIds != 'NaN') { - $excludeIds = implode(',', array_map('intval', explode(',', $excludeIds))); - } else { - $excludeIds = ''; - } - - // Excluding downloadable products from packs because download from pack is not supported - $forceJson = Tools::getValue('forceJson', false); - $disableCombination = Tools::getValue('disableCombination', false); - $excludeVirtuals = (bool) Tools::getValue('excludeVirtuals', true); - $exclude_packs = (bool) Tools::getValue('exclude_packs', true); - - $context = Context::getContext(); - - $sql = 'SELECT p.`id_product`, pl.`link_rewrite`, p.`reference`, pl.`name`, image_shop.`id_image` id_image, il.`legend`, p.`cache_default_attribute` - FROM `' . _DB_PREFIX_ . 'product` p - ' . Shop::addSqlAssociation('product', 'p') . ' - LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl ON (pl.id_product = p.id_product AND pl.id_lang = ' . (int) $context->language->id . Shop::addSqlRestrictionOnLang('pl') . ') - LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop - ON (image_shop.`id_product` = p.`id_product` AND image_shop.cover=1 AND image_shop.id_shop=' . (int) $context->shop->id . ') - LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (image_shop.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $context->language->id . ') - WHERE (pl.name LIKE \'%' . pSQL($query) . '%\' OR p.reference LIKE \'%' . pSQL($query) . '%\')' . - (!empty($excludeIds) ? ' AND p.id_product NOT IN (' . $excludeIds . ') ' : ' ') . - ($excludeVirtuals ? 'AND NOT EXISTS (SELECT 1 FROM `' . _DB_PREFIX_ . 'product_download` pd WHERE (pd.id_product = p.id_product))' : '') . - ($exclude_packs ? 'AND (p.cache_is_pack IS NULL OR p.cache_is_pack = 0)' : '') . - ' GROUP BY p.id_product'; - - $items = Db::getInstance()->executeS($sql); - - if ($items && ($disableCombination || $excludeIds)) { - $results = $resultsJson = []; - /** @var array{id_product: int, link_rewrite: string, reference: string, name: string, id_image: string} $item */ - foreach ($items as $item) { - if (!$forceJson) { - $item['name'] = str_replace('|', '|', $item['name']); - $results[] = trim($item['name']) . (!empty($item['reference']) ? ' (ref: ' . $item['reference'] . ')' : '') . '|' . (int) ($item['id_product']); - } else { - $resultsJson[] = [ - 'id' => $item['id_product'], - 'name' => $item['name'] . (!empty($item['reference']) ? ' (ref: ' . $item['reference'] . ')' : ''), - 'ref' => (!empty($item['reference']) ? $item['reference'] : ''), - 'image' => str_replace('http://', Tools::getShopProtocol(), $context->link->getImageLink($item['link_rewrite'], $item['id_image'], 'home_default')), - ]; - } - } - - if (!$forceJson) { - return $this->ajaxRender(implode(PHP_EOL, $results)); - } - - return $this->ajaxRender(json_encode($resultsJson)); - } - if ($items) { - // packs - $results = []; - foreach ($items as $item) { - // check if product have combination - if (Combination::isFeatureActive() && $item['cache_default_attribute']) { - $sql = 'SELECT pa.`id_product_attribute`, pa.`reference`, ag.`id_attribute_group`, pai.`id_image`, agl.`name` AS group_name, al.`name` AS attribute_name, - a.`id_attribute` - FROM `' . _DB_PREFIX_ . 'product_attribute` pa - ' . Shop::addSqlAssociation('product_attribute', 'pa') . ' - LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_combination` pac ON pac.`id_product_attribute` = pa.`id_product_attribute` - LEFT JOIN `' . _DB_PREFIX_ . 'attribute` a ON a.`id_attribute` = pac.`id_attribute` - LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group` ag ON ag.`id_attribute_group` = a.`id_attribute_group` - LEFT JOIN `' . _DB_PREFIX_ . 'attribute_lang` al ON (a.`id_attribute` = al.`id_attribute` AND al.`id_lang` = ' . (int) $context->language->id . ') - LEFT JOIN `' . _DB_PREFIX_ . 'attribute_group_lang` agl ON (ag.`id_attribute_group` = agl.`id_attribute_group` AND agl.`id_lang` = ' . (int) $context->language->id . ') - LEFT JOIN `' . _DB_PREFIX_ . 'product_attribute_image` pai ON pai.`id_product_attribute` = pa.`id_product_attribute` - WHERE pa.`id_product` = ' . (int) $item['id_product'] . ' - GROUP BY pa.`id_product_attribute`, ag.`id_attribute_group` - ORDER BY pa.`id_product_attribute`'; - - $combinations = Db::getInstance()->executeS($sql); - if (!empty($combinations)) { - foreach ($combinations as $k => $combination) { - $results[$combination['id_product_attribute']]['id'] = $item['id_product']; - $results[$combination['id_product_attribute']]['id_product_attribute'] = $combination['id_product_attribute']; - !empty($results[$combination['id_product_attribute']]['name']) ? $results[$combination['id_product_attribute']]['name'] .= ' ' . $combination['group_name'] . '-' . $combination['attribute_name'] - : $results[$combination['id_product_attribute']]['name'] = $item['name'] . ' ' . $combination['group_name'] . '-' . $combination['attribute_name']; - if (!empty($combination['reference'])) { - $results[$combination['id_product_attribute']]['ref'] = $combination['reference']; - } else { - $results[$combination['id_product_attribute']]['ref'] = !empty($item['reference']) ? $item['reference'] : ''; - } - if (empty($results[$combination['id_product_attribute']]['image'])) { - $results[$combination['id_product_attribute']]['image'] = str_replace('http://', Tools::getShopProtocol(), $context->link->getImageLink($item['link_rewrite'], $combination['id_image'], 'home_default')); - } - } - } else { - $results[] = [ - 'id' => $item['id_product'], - 'name' => $item['name'], - 'ref' => (!empty($item['reference']) ? $item['reference'] : ''), - 'image' => str_replace('http://', Tools::getShopProtocol(), $context->link->getImageLink($item['link_rewrite'], $item['id_image'], 'home_default')), - ]; - } - } else { - $results[] = [ - 'id' => $item['id_product'], - 'name' => $item['name'], - 'ref' => (!empty($item['reference']) ? $item['reference'] : ''), - 'image' => str_replace('http://', Tools::getShopProtocol(), $context->link->getImageLink($item['link_rewrite'], $item['id_image'], 'home_default')), - ]; - } - } - - return $this->ajaxRender(json_encode(array_values($results))); - } - - return $this->ajaxRender(json_encode([])); - } -} diff --git a/controllers/admin/DummyAdminController.php b/controllers/admin/DummyAdminController.php index 6ea1b1a2a7d28..95c4a4ec204ca 100644 --- a/controllers/admin/DummyAdminController.php +++ b/controllers/admin/DummyAdminController.php @@ -30,7 +30,7 @@ /** * This class is used only because some parts of the Back Office require a Controller - * to function (like the NullDispatcher). + * to function (like the NullDispatcher). It is also used in integration tests for override. */ class DummyAdminControllerCore extends AdminController { diff --git a/install-dev/data/xml/feature_flag.xml b/install-dev/data/xml/feature_flag.xml index 50624bd0f29ae..c8d1ca246c3ae 100644 --- a/install-dev/data/xml/feature_flag.xml +++ b/install-dev/data/xml/feature_flag.xml @@ -11,7 +11,6 @@ - diff --git a/install-dev/data/xml/quick_access.xml b/install-dev/data/xml/quick_access.xml index 739cfa4c4dcdd..1e1200063cd8a 100644 --- a/install-dev/data/xml/quick_access.xml +++ b/install-dev/data/xml/quick_access.xml @@ -12,7 +12,7 @@ index.php?controller=AdminCartRules&addcart_rule - index.php/sell/catalog/products/new + index.php/sell/catalog/products/create index.php/sell/catalog/categories/new diff --git a/src/Adapter/Admin/AbstractAdminQueryBuilder.php b/src/Adapter/Admin/AbstractAdminQueryBuilder.php deleted file mode 100644 index d1e00bebf5c35..0000000000000 --- a/src/Adapter/Admin/AbstractAdminQueryBuilder.php +++ /dev/null @@ -1,221 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Admin; - -use PrestaShop\PrestaShop\Adapter\Validate; -use Symfony\Component\Process\Exception\LogicException; - -/** - * Base class for data provider, to give common Adapter functions. - * - * Contains methods to compile SQL from parseable arrays of select, tables, joins, where, etc... - */ -abstract class AbstractAdminQueryBuilder -{ - public const FILTERING_LIKE_BOTH = 'LIKE \'%%%s%%\''; - public const FILTERING_LIKE_LEFT = 'LIKE \'%%%s\''; - public const FILTERING_LIKE_RIGHT = 'LIKE \'%s%%\''; - public const FILTERING_EQUAL_NUMERIC = '= %s'; - public const FILTERING_EQUAL_STRING = '= \'%s\''; - - /** - * @var string|null - */ - private $lastCompiledSql = null; - - /** - * @param array $whereArray - * - * @return mixed|string - */ - private function compileSqlWhere(array $whereArray) - { - $operator = 'AND'; - $s = []; - while ($item = array_shift($whereArray)) { - if ($item == 'OR') { - $operator = 'OR'; - } elseif ($item == 'AND') { - $operator = 'AND'; - } else { - $s[] = (is_array($item) ? $this->compileSqlWhere($item) : $item); - } - } - if (count($s) == 1) { - return $s[0]; - } - - return '(' . implode(' ' . $operator . ' ', $s) . ')'; - } - - /** - * Compiles a SQL query (SELECT), from a group of associative arrays. - * - * @see \PrestaShop\PrestaShop\Adapter\Product\AdminProductDataProvider::getCatalogProductList() for an example. - * - * Format example for $table: - * $table = array( - * 'p' => 'product', // First table: a simple name - * 'pl' => array( // Next: arrays to set join properly - * 'table' => 'product_lang', - * 'join' => 'LEFT JOIN', - * 'on' => 'pl.`id_product` = p.`id_product` AND pl.`id_lang` = '.$idLang.' AND pl.`id_shop` = '.$idShop - * ), - * 'sav' => array( - * 'table' => 'stock_available', - * 'join' => 'LEFT JOIN', - * 'on' => 'sav.`id_product` = p.`id_product` AND sav.`id_product_attribute` = 0 AND sav.id_shop_group = 1 AND sav.id_shop = 0' - * ), - * ... - * ); - * - * Format example for $select: - * $select = array( - * 'id_product' => array('table' => 'p', 'field' => 'id_product', 'filtering' => self::FILTERING_EQUAL_NUMERIC), - * 'reference' => array('table' => 'p', 'field' => 'reference', 'filtering' => self::FILTERING_LIKE_BOTH), - * ... - * ); - * - * Format example for $where: - * $where = array( - * 'AND', // optional if AND, mandatory if OR. - * 1, // First condition: let 1 here if there is no condition, then "WHERE 1;" will work better than "WHERE ;" - * array('OR', '2', '3'), - * array( - * 'AND', - * array('OR', '4', '5'), - * array('6', '7') - * ) - * ); - * In the WHERE, it's up to you to build each condition string. You can use the 'filtering' data in the $select array to help you: - * $where[] = $select[$field]['table'].'.`'.$select[$field]['field'].'` '.sprintf($select[$field]['filtering'], $filterValue); - * - * Format example for $order: - * $order = array('name ASC', 'id_product DESC'); - * - * @param array|string> $select - * @param array $table - * @param array $where - * @param array $groupBy - * @param array $order - * @param string $limit - * - * @throws LogicException if SQL elements cannot be joined - * - * @return string the SQL query ready to be executed - */ - protected function compileSqlQuery(array $select, array $table, array $where = [], array $groupBy = [], array $order = [], $limit = null) - { - $sql = []; - - // SELECT - $s = []; - foreach ($select as $alias => $field) { - $a = ' AS `' . $alias . '`'; - if (is_array($field)) { - if (isset($field['table'])) { - $s[] = ' ' . $field['table'] . '.`' . $field['field'] . '` ' . $a; - } elseif (isset($field['select'])) { - $s[] = ' ' . $field['select'] . $a; - } - } else { - $s[] = ' ' . $field . $a; - } - } - if (count($s) === 0) { - throw new LogicException('Compile SQL failed: No field to SELECT!'); - } - $sql[] = 'SELECT SQL_CALC_FOUND_ROWS' . implode(',' . PHP_EOL, $s); - - // FROM / JOIN - $s = []; - foreach ($table as $alias => $join) { - if (!is_array($join)) { - if (count($s) > 0) { - throw new LogicException('Compile SQL failed: cannot join the table ' . $join . ' into SQL query without JOIN sepcs.'); - } - $s[0] = ' `' . _DB_PREFIX_ . $join . '` ' . $alias; - } else { - if (count($s) === 0) { - throw new LogicException('Compile SQL failed: cannot join the table alias ' . $alias . ' into SQL query before to insert initial table.'); - } - $s[] = ' ' . $join['join'] . ' `' . _DB_PREFIX_ . $join['table'] . '` ' . $alias . ((isset($join['on'])) ? ' ON (' . $join['on'] . ')' : ''); - } - } - if (count($s) === 0) { - throw new LogicException('Compile SQL failed: No table to insert into FROM!'); - } - $sql[] = 'FROM ' . implode(' ' . PHP_EOL, $s); - - // WHERE (recursive call) - if (count($where)) { - $s = $this->compileSqlWhere($where); - if (strlen($s) > 0) { - $sql[] = 'WHERE ' . $s . PHP_EOL; - } - } - - // GROUP BY - if (!empty($groupBy)) { - $sql[] = 'GROUP BY ' . implode(', ', array_map('pSQL', $groupBy)) . PHP_EOL; - } - - // ORDER - if (count($order) > 0) { - $goodOrder = []; - foreach ($order as $o) { - $value = explode(' ', $o); - if (2 === count($value) && Validate::isOrderBy($value[0]) && Validate::isOrderWay($value[1])) { - $goodOrder[] = ' `' . bqSQL($value[0]) . '` ' . $value[1]; - } - } - - if (count($goodOrder) > 0) { - $sql[] = 'ORDER BY ' . implode(', ', $goodOrder) . PHP_EOL; - } - } - - // LIMIT - if ($limit) { - $sql[] = 'LIMIT ' . $limit . PHP_EOL; - } - - $this->lastCompiledSql = implode(' ' . PHP_EOL, $sql) . ';'; - - return $this->lastCompiledSql; - } - - /** - * Returns the last SQL query that was compiled on this Provider. - * - * @return string The last SQL query that was compiled with $this->compileSqlQuery() - */ - public function getLastCompiledSql() - { - return $this->lastCompiledSql; - } -} diff --git a/src/Adapter/Admin/PagePreference.php b/src/Adapter/Admin/PagePreference.php deleted file mode 100644 index 16c770058b243..0000000000000 --- a/src/Adapter/Admin/PagePreference.php +++ /dev/null @@ -1,131 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Admin; - -use Db; -use PrestaShop\PrestaShop\Core\Version; -use PrestaShopBundle\Service\TransitionalBehavior\AdminPagePreferenceInterface; -use Symfony\Component\HttpFoundation\Session\SessionInterface; -use Symfony\Component\HttpFoundation\Session\Storage\PhpBridgeSessionStorage; -use Symfony\Component\Routing\Exception\InvalidParameterException; - -/** - * Adapter to know which page's version to display. - * - * This implementation gives methods to use to take decision: - * - if we should display the new refactored page, or the old legacy one. - * - if we should display the switch on the admin layout to change this setting. - * - * Data is stored in the cookie, as legacy does. - */ -class PagePreference implements AdminPagePreferenceInterface -{ - /** - * @var bool - */ - private $isDebug; - - /** - * @var SessionInterface - */ - private $session; - - public function __construct(SessionInterface $session, bool $isDebug = _PS_MODE_DEV_) - { - if ($session->isStarted()) { - $this->session = $session; - } else { - $sessionClass = $session::class; - $this->session = new $sessionClass(new PhpBridgeSessionStorage()); - } - $this->isDebug = $isDebug; - } - - /** - * {@inheritdoc} - */ - public function getTemporaryShouldUseLegacyPage($page) - { - if (!$page) { - throw new InvalidParameterException('$page parameter missing'); - } - - return $this->session->has('should_use_legacy_page_for_' . $page) && $this->session->get('should_use_legacy_page_for_' . $page, 0) == 1; - } - - /** - * {@inheritdoc} - */ - public function setTemporaryShouldUseLegacyPage($page, $useLegacy) - { - if (!$page) { - throw new InvalidParameterException('$page parameter missing'); - } - - if ((bool) $useLegacy) { - $this->session->set('should_use_legacy_page_for_' . $page, 1); - } else { - $this->session->remove('should_use_legacy_page_for_' . $page); - } - } - - /** - * {@inheritdoc} - */ - public function getTemporaryShouldAllowUseLegacyPage($page = null) - { - // Dev mode: always shown - if ($this->isDebug) { - return true; - } - - $version = Db::getInstance()->getValue('SELECT `value` FROM `' . _DB_PREFIX_ . 'configuration` WHERE `name` = "PS_INSTALL_VERSION"'); - if (!$version) { - return false; - } - $installVersion = explode('.', $version); - $currentVersion = explode('.', Version::VERSION); - - // Prod mode, depends on the page - switch ($page) { - case 'product': - // never show it for Product page in production mode. - return false; - default: - // show only for 1.7.x - if ($currentVersion[0] != '1' || $currentVersion[1] != '7') { - return false; - } - // show only if upgrade from older version than current one - if ($installVersion[0] >= $currentVersion[0] || $installVersion[1] >= $currentVersion[1]) { - return false; - } - } - - return true; - } -} diff --git a/src/Adapter/Attribute/AdminAttributeGeneratorControllerWrapper.php b/src/Adapter/Attribute/AdminAttributeGeneratorControllerWrapper.php deleted file mode 100644 index 04954a8574e3f..0000000000000 --- a/src/Adapter/Attribute/AdminAttributeGeneratorControllerWrapper.php +++ /dev/null @@ -1,133 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Attribute; - -use Combination; -use Context; -use PrestaShopBundle\Translation\TranslatorComponent; -use Product; -use SpecificPriceRule; -use Validate; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller wrapper for new Architecture, about Category admin controller. - */ -class AdminAttributeGeneratorControllerWrapper -{ - /** - * @var TranslatorComponent - */ - private $translator; - - public function __construct() - { - $context = Context::getContext(); - $this->translator = $context->getTranslator(); - } - - /** - * Generate product attributes. - * - * @param object $product The product - * @param array $options The array with all attributes combinations - */ - public function processGenerate($product, $options) - { - SpecificPriceRule::disableAnyApplication(); - - //add combination if not already exists - $combinations = array_values($this->createCombinations(array_values($options))); - $combinationsValues = array_values(array_map(function () use ($product) { - return [ - 'id_product' => $product->id, - ]; - }, $combinations)); - - $product->generateMultipleCombinations($combinationsValues, $combinations, false); - - Product::updateDefaultAttribute($product->id); - SpecificPriceRule::enableAnyApplication(); - SpecificPriceRule::applyAllRules([(int) $product->id]); - } - - public function createCombinations(array $list): array - { - if (count($list) <= 1) { - return count($list) ? array_map(function ($v) { - return [$v]; - }, $list[0]) : $list; - } - $res = []; - $first = array_pop($list); - foreach ($first as $attribute) { - $tab = $this->createCombinations($list); - foreach ($tab as $to_add) { - $res[] = is_array($to_add) ? array_merge($to_add, [$attribute]) : [$to_add, $attribute]; - } - } - - return $res; - } - - /** - * Delete a product attribute. - * - * @param int $idAttribute The attribute ID - * @param int $idProduct The product ID - * - * @return array|bool - */ - public function ajaxProcessDeleteProductAttribute($idAttribute, $idProduct) - { - if (!Combination::isFeatureActive()) { - return false; - } - - if ($idProduct && Validate::isUnsignedId($idProduct) && Validate::isLoadedObject($product = new Product($idProduct))) { - $product->deleteAttributeCombination((int) $idAttribute); - $product->checkDefaultAttributes(); - if (!$product->hasAttributes()) { - $product->cache_default_attribute = 0; - $product->update(); - } else { - Product::updateDefaultAttribute($idProduct); - } - - return [ - 'status' => 'ok', - 'message' => $this->translator->trans('Successful deletion', [], 'Admin.Catalog.Notification'), - ]; - } else { - return [ - 'status' => 'error', - 'message' => $this->translator->trans('You cannot delete this attribute.', [], 'Admin.Catalog.Notification'), - ]; - } - } -} diff --git a/src/Adapter/CombinationDataProvider.php b/src/Adapter/CombinationDataProvider.php deleted file mode 100644 index 583e7c27926f6..0000000000000 --- a/src/Adapter/CombinationDataProvider.php +++ /dev/null @@ -1,204 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter; - -use Combination; -use PrestaShop\Decimal\DecimalNumber; -use PrestaShop\PrestaShop\Adapter\Product\ProductDataProvider; -use PrestaShop\PrestaShop\Core\Localization\Locale; -use PrestaShopBundle\Form\FormHelper; -use Product; -use Tax; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This class will provide data from DB / ORM about product combination. - */ -class CombinationDataProvider -{ - /** - * @var LegacyContext - */ - private $context; - - /** - * @var ProductDataProvider - */ - private $productAdapter; - - /** - * @var Locale - */ - private $locale; - - /** - * @param Locale $locale - */ - public function __construct(Locale $locale) - { - $this->context = new LegacyContext(); - $this->productAdapter = new ProductDataProvider(); - $this->locale = $locale; - } - - /** - * Retrieve combinations data for a specific language id. - * - * @param array $combinationIds - * @param int $languageId - * - * @return array a list of formatted combinations - * - * @throws \PrestaShopDatabaseException - * @throws \PrestaShopException - */ - public function getFormCombinations(array $combinationIds, $languageId) - { - $productId = (new Combination($combinationIds[0]))->id_product; - $product = new Product($productId); - $combinations = []; - - foreach ($combinationIds as $combinationId) { - $combinations[$combinationId] = $this->completeCombination( - $product->getAttributeCombinationsById( - $combinationId, - $languageId - ), - $product - ); - } - - return $combinations; - } - - /** - * @param array $attributesCombinations - * @param Product $product - * - * @return array - */ - public function completeCombination($attributesCombinations, $product) - { - $combination = $attributesCombinations[0]; - - $attribute_price_impact = 0; - if ($combination['price'] > 0) { - $attribute_price_impact = 1; - } elseif ($combination['price'] < 0) { - $attribute_price_impact = -1; - } - - $attribute_weight_impact = 0; - if ($combination['weight'] > 0) { - $attribute_weight_impact = 1; - } elseif ($combination['weight'] < 0) { - $attribute_weight_impact = -1; - } - - $attribute_unity_price_impact = 0; - if ($combination['unit_price_impact'] > 0) { - $attribute_unity_price_impact = 1; - } elseif ($combination['unit_price_impact'] < 0) { - $attribute_unity_price_impact = -1; - } - - $productTaxRate = $product->getTaxesRate(); - - // Get product basic prices - $productPrice = new DecimalNumber((string) $product->price); - $productPriceIncluded = $productPrice->times(new DecimalNumber((string) (1 + ($productTaxRate / 100)))); - $productEcotax = new DecimalNumber((string) $product->ecotax); - $productEcotaxIncluded = $productEcotax->times(new DecimalNumber((string) (1 + (Tax::getProductEcotaxRate() / 100)))); - - // Get combination prices and impacts - $combinationEcotax = new DecimalNumber((string) $combination['ecotax_tax_excluded']); - $combinationEcotaxIncluded = new DecimalNumber((string) $combination['ecotax_tax_included']); - $combinationImpactTaxExcluded = new DecimalNumber((string) $combination['price']); - $combinationImpactTaxIncluded = $combinationImpactTaxExcluded->times(new DecimalNumber((string) (1 + ($productTaxRate / 100)))); - - $ecotax = $combinationEcotax->equalsZero() ? $productEcotax : $combinationEcotax; - $finalPrice = $productPrice - ->plus($ecotax) - ->plus($combinationImpactTaxExcluded) - ->toPrecision(FormHelper::DEFAULT_PRICE_PRECISION); - - $ecotaxIncluded = $combinationEcotaxIncluded->equalsZero() ? $productEcotaxIncluded : $combinationEcotaxIncluded; - $finalPriceIncluded = $productPriceIncluded - ->plus($ecotaxIncluded) - ->plus($combinationImpactTaxIncluded) - ->toPrecision(FormHelper::DEFAULT_PRICE_PRECISION); - - return [ - 'id_product_attribute' => $combination['id_product_attribute'], - 'attribute_reference' => $combination['reference'], - 'attribute_ean13' => $combination['ean13'], - 'attribute_isbn' => $combination['isbn'], - 'attribute_upc' => $combination['upc'], - 'attribute_mpn' => $combination['mpn'], - 'attribute_wholesale_price' => $combination['wholesale_price'], - 'attribute_price_impact' => $attribute_price_impact, - 'attribute_price' => $combinationImpactTaxExcluded->toPrecision(FormHelper::DEFAULT_PRICE_PRECISION), - 'attribute_price_display' => $this->locale->formatPrice((string) $combinationImpactTaxExcluded, $this->context->getContext()->currency->iso_code), - 'final_price' => $finalPrice, - 'final_price_tax_included' => $finalPriceIncluded, - 'attribute_priceTI' => '', - // The value is displayed with tax included - 'product_ecotax' => $productEcotaxIncluded->toPrecision(FormHelper::DEFAULT_PRICE_PRECISION), - 'attribute_ecotax' => $combination['ecotax_tax_included'], - 'attribute_weight_impact' => $attribute_weight_impact, - 'attribute_weight' => $combination['weight'], - 'attribute_unit_impact' => $attribute_unity_price_impact, - 'attribute_unity' => $combination['unit_price_impact'], - 'attribute_minimal_quantity' => $combination['minimal_quantity'], - 'attribute_low_stock_threshold' => $combination['low_stock_threshold'], - 'attribute_low_stock_alert' => (bool) $combination['low_stock_alert'], - 'available_date_attribute' => $combination['available_date'], - 'attribute_default' => (bool) $combination['default_on'], - 'attribute_location' => $this->productAdapter->getLocation($product->id, $combination['id_product_attribute']), - 'attribute_quantity' => $this->productAdapter->getQuantity($product->id, $combination['id_product_attribute']), - 'name' => $this->getCombinationName($attributesCombinations), - 'id_product' => $product->id, - ]; - } - - /** - * @param array $attributesCombinations - * - * @return string - */ - private function getCombinationName($attributesCombinations) - { - $name = []; - - foreach ($attributesCombinations as $attribute) { - $name[] = $attribute['group_name'] . ' - ' . $attribute['attribute_name']; - } - - return implode(', ', $name); - } -} diff --git a/src/Adapter/Feature/FeatureDataProvider.php b/src/Adapter/Feature/FeatureDataProvider.php deleted file mode 100644 index 2e547dad5b6f7..0000000000000 --- a/src/Adapter/Feature/FeatureDataProvider.php +++ /dev/null @@ -1,77 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Feature; - -use Feature; -use FeatureValue; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This class will provide data from DB / ORM about Feature. - */ -class FeatureDataProvider -{ - /** - * Get all features for a given language. - * - * @param int $id_lang Language id - * @param bool $with_shop - * - * @return array Multiple arrays with feature's data - */ - public static function getFeatures($id_lang, $with_shop = true) - { - return Feature::getFeatures($id_lang, $with_shop); - } - - /** - * Get all values for a given feature and language. - * - * @param int $id_lang Language id - * @param int $id_feature Feature id - * @param bool $custom - * - * @return array Array with feature's values - */ - public static function getFeatureValuesWithLang($id_lang, $id_feature, $custom = false) - { - return FeatureValue::getFeatureValuesWithLang($id_lang, $id_feature, $custom); - } - - /** - * Get all language for a given value. - * - * @param int $id_feature_value Feature value id - * - * @return array Array with value's languages - */ - public static function getFeatureValueLang($id_feature_value) - { - return FeatureValue::getFeatureValueLang($id_feature_value); - } -} diff --git a/src/Adapter/Pack/PackDataProvider.php b/src/Adapter/Pack/PackDataProvider.php deleted file mode 100644 index 2c9b07a24c1fe..0000000000000 --- a/src/Adapter/Pack/PackDataProvider.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Pack; - -use Context; -use Pack; -use Product; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This class will provide data from DB / ORM about product pack. - */ -class PackDataProvider -{ - /** - * Get product pack items. - * - * @param int $id_product - * @param int $id_lang - */ - public function getItems($id_product, $id_lang) - { - $packItems = Pack::getItems($id_product, $id_lang); - - foreach ($packItems as $packItem) { - $cover = $packItem->id_pack_product_attribute ? Product::getCombinationImageById($packItem->id_pack_product_attribute, $id_lang) : Product::getCover($packItem->id); - $packItem->image = Context::getContext()->link->getImageLink($packItem->link_rewrite, $cover ? $cover['id_image'] : '', 'home_default'); - } - - return $packItems; - } -} diff --git a/src/Adapter/Product/AdminProductDataProvider.php b/src/Adapter/Product/AdminProductDataProvider.php deleted file mode 100644 index 838cfef7ccee2..0000000000000 --- a/src/Adapter/Product/AdminProductDataProvider.php +++ /dev/null @@ -1,470 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Product; - -use Configuration; -use Context; -use Currency; -use Db; -use DbQuery; -use Doctrine\ORM\EntityManager; -use Hook; -use PrestaShop\PrestaShop\Adapter\Admin\AbstractAdminQueryBuilder; -use PrestaShop\PrestaShop\Adapter\ImageManager; -use PrestaShop\PrestaShop\Adapter\Validate; -use PrestaShop\PrestaShop\Core\Version; -use PrestaShopBundle\Entity\AdminFilter; -use PrestaShopBundle\Service\DataProvider\Admin\ProductInterface; -use Product; -use Psr\Cache\CacheItemPoolInterface; -use StockAvailable; -use Tools; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Data provider for new Architecture, about Product object model. - * - * This class will provide data from DB / ORM about Products for the Admin interface. - * This is an Adapter that works with the Legacy code and persistence behaviors. - */ -class AdminProductDataProvider extends AbstractAdminQueryBuilder implements ProductInterface -{ - /** - * @var EntityManager - */ - private $entityManager; - - /** - * @var ImageManager - */ - private $imageManager; - - /** - * @var CacheItemPoolInterface - */ - private $cache; - - public function __construct( - EntityManager $entityManager, - ImageManager $imageManager, - CacheItemPoolInterface $cache - ) { - $this->entityManager = $entityManager; - $this->imageManager = $imageManager; - $this->cache = $cache; - } - - /** - * {@inheritdoc} - */ - public function getPersistedFilterParameters() - { - $employee = Context::getContext()->employee; - $employeeId = $employee->id ?: 0; - - $cachedFilters = $this->cache->getItem("app.product_filters_{$employeeId}"); - - if (!$cachedFilters->isHit()) { - $shop = Context::getContext()->shop; - /** @var AdminFilter|null $filter */ - $filter = $this->entityManager->getRepository(AdminFilter::class)->findOneBy([ - 'employee' => $employeeId, - 'shop' => $shop->id ?: 0, - 'controller' => 'ProductController', - 'action' => 'catalogAction', - ]); - - if (null === $filter) { - $filters = AdminFilter::getProductCatalogEmptyFilter(); - } else { - $filters = $filter->getProductCatalogFilter(); - } - - $cachedFilters->set($filters); - $this->cache->save($cachedFilters); - } - - return $cachedFilters->get(); - } - - /** - * {@inheritdoc} - */ - public function isCategoryFiltered() - { - $filters = $this->getPersistedFilterParameters(); - - return !empty($filters['filter_category']) && $filters['filter_category'] > 0; - } - - /** - * {@inheritdoc} - */ - public function isColumnFiltered() - { - $filters = $this->getPersistedFilterParameters(); - foreach ($filters as $filterKey => $filterValue) { - if (str_starts_with($filterKey, 'filter_column_') && $filterValue !== '') { - return true; // break at first column filter found - } - } - - return false; - } - - /** - * {@inheritdoc} - */ - public function persistFilterParameters(array $parameters) - { - $employee = Context::getContext()->employee; - $shop = Context::getContext()->shop; - /** @var AdminFilter|null $filter */ - $filter = $this->entityManager->getRepository(AdminFilter::class)->findOneBy([ - 'employee' => $employee->id ?: 0, - 'shop' => $shop->id ?: 0, - 'controller' => 'ProductController', - 'action' => 'catalogAction', - ]); - - if (!$filter) { - $filter = new AdminFilter(); - $filter->setEmployee($employee->id ?: 0)->setShop($shop->id ?: 0)->setController('ProductController')->setAction('catalogAction'); - } - - $filter->setProductCatalogFilter($parameters); - $this->entityManager->persist($filter); - - // if each filter is == '', then remove item from DB :) - if (count(array_diff($filter->getProductCatalogFilter(), [''])) == 0) { - $this->entityManager->remove($filter); - } - - $this->entityManager->flush(); - - //Flush cache - $employee = Context::getContext()->employee; - $employeeId = $employee->id ?: 0; - - $this->cache->deleteItem("app.product_filters_{$employeeId}"); - } - - /** - * {@inheritdoc} - */ - public function combinePersistentCatalogProductFilter($paramsIn = [], $avoidPersistence = false) - { - // retrieve persisted filter parameters - $persistedParams = $this->getPersistedFilterParameters(); - // merge with new values - $paramsOut = array_merge($persistedParams, (array) $paramsIn); - // persist new values - if (!$avoidPersistence) { - $this->persistFilterParameters($paramsOut); - } - - // return new values - return $paramsOut; - } - - /** - * {@inheritdoc} - */ - public function getCatalogProductList( - $offset, - $limit, - $orderBy, - $sortOrder, - $post = [], - $avoidPersistence = false, - $formatCldr = true - ) { - $offset = (int) $offset; - $limit = (int) $limit; - $orderBy = Validate::isOrderBy($orderBy) ? $orderBy : 'id_product'; - $sortOrder = Validate::isOrderWay($sortOrder) ? $sortOrder : 'desc'; - - $filterParams = $this->combinePersistentCatalogProductFilter(array_merge( - $post, - ['last_offset' => $offset, 'last_limit' => $limit, 'last_orderBy' => $orderBy, 'last_sortOrder' => $sortOrder] - ), $avoidPersistence); - $filterParams = AdminFilter::sanitizeFilterParameters($filterParams); - - $showPositionColumn = $this->isCategoryFiltered(); - if ($orderBy == 'position_ordering' && $showPositionColumn) { - foreach ($filterParams as $key => $param) { - if (str_starts_with($key, 'filter_column_')) { - $filterParams[$key] = ''; - } - } - } - if ($orderBy == 'position_ordering') { - $orderBy = 'position'; - } - - $idShop = Context::getContext()->shop->id; - $idLang = Context::getContext()->language->id; - - $sqlSelect = [ - 'id_product' => ['table' => 'p', 'field' => 'id_product', 'filtering' => ' %s '], - 'reference' => ['table' => 'p', 'field' => 'reference', 'filtering' => self::FILTERING_LIKE_BOTH], - 'price' => ['table' => 'sa', 'field' => 'price', 'filtering' => ' %s '], - 'id_shop_default' => ['table' => 'p', 'field' => 'id_shop_default'], - 'is_virtual' => ['table' => 'p', 'field' => 'is_virtual'], - 'name' => ['table' => 'pl', 'field' => 'name', 'filtering' => self::FILTERING_LIKE_BOTH], - 'link_rewrite' => ['table' => 'pl', 'field' => 'link_rewrite', 'filtering' => self::FILTERING_LIKE_BOTH], - 'active' => ['table' => 'sa', 'field' => 'active', 'filtering' => self::FILTERING_EQUAL_NUMERIC], - 'shopname' => ['table' => 'shop', 'field' => 'name'], - 'id_image' => ['table' => 'image_shop', 'field' => 'id_image'], - 'name_category' => ['table' => 'cl', 'field' => 'name', 'filtering' => self::FILTERING_LIKE_BOTH], - 'price_final' => '0', - 'nb_downloadable' => ['table' => 'pd', 'field' => 'nb_downloadable'], - 'sav_quantity' => ['table' => 'sav', 'field' => 'quantity', 'filtering' => ' %s '], - 'badge_danger' => ['select' => 'IF(sav.`quantity`<=0, 1, 0)', 'filtering' => 'IF(sav.`quantity`<=0, 1, 0) = %s'], - ]; - $sqlTable = [ - 'p' => 'product', - 'pl' => [ - 'table' => 'product_lang', - 'join' => 'LEFT JOIN', - 'on' => 'pl.`id_product` = p.`id_product` AND pl.`id_lang` = ' . $idLang . ' AND pl.`id_shop` = ' . $idShop, - ], - 'sav' => [ - 'table' => 'stock_available', - 'join' => 'LEFT JOIN', - 'on' => 'sav.`id_product` = p.`id_product` AND sav.`id_product_attribute` = 0' . - StockAvailable::addSqlShopRestriction(null, $idShop, 'sav'), - ], - 'sa' => [ - 'table' => 'product_shop', - 'join' => 'JOIN', - 'on' => 'p.`id_product` = sa.`id_product` AND sa.id_shop = ' . $idShop, - ], - 'cl' => [ - 'table' => 'category_lang', - 'join' => 'LEFT JOIN', - 'on' => 'sa.`id_category_default` = cl.`id_category` AND cl.`id_lang` = ' . $idLang . ' AND cl.id_shop = ' . $idShop, - ], - 'c' => [ - 'table' => 'category', - 'join' => 'LEFT JOIN', - 'on' => 'c.`id_category` = cl.`id_category`', - ], - 'shop' => [ - 'table' => 'shop', - 'join' => 'LEFT JOIN', - 'on' => 'shop.id_shop = ' . $idShop, - ], - 'image_shop' => [ - 'table' => 'image_shop', - 'join' => 'LEFT JOIN', - 'on' => 'image_shop.`id_product` = p.`id_product` AND image_shop.`cover` = 1 AND image_shop.id_shop = ' . $idShop, - ], - 'i' => [ - 'table' => 'image', - 'join' => 'LEFT JOIN', - 'on' => 'i.`id_image` = image_shop.`id_image`', - ], - 'pd' => [ - 'table' => 'product_download', - 'join' => 'LEFT JOIN', - 'on' => 'pd.`id_product` = p.`id_product`', - ], - ]; - $sqlWhere = ['AND', 1]; - $sqlOrder = [$orderBy . ' ' . $sortOrder]; - if ($orderBy != 'id_product') { - $sqlOrder[] = 'id_product asc'; // secondary order by (useful when ordering by active, quantity, price, etc...) - } - $sqlLimit = $offset . ', ' . $limit; - - // Column 'position' added if filtering by category - if ($showPositionColumn) { - $filteredCategoryId = (int) $filterParams['filter_category']; - $sqlSelect['position'] = ['table' => 'cp', 'field' => 'position']; - $sqlTable['cp'] = [ - 'table' => 'category_product', - 'join' => 'INNER JOIN', - 'on' => 'cp.`id_product` = p.`id_product` AND cp.`id_category` = ' . $filteredCategoryId, - ]; - } elseif ($orderBy == 'position') { - // We do not show position column, so we do not join the table, so we do not order by position! - $sqlOrder = ['id_product ASC']; - } - - $sqlGroupBy = []; - foreach ($filterParams as $filterParam => $filterValue) { - if (!$filterValue && $filterValue !== '0') { - continue; - } - if (str_starts_with($filterParam, 'filter_column_')) { - $filterValue = Db::getInstance()->escape($filterValue, in_array($filterParam, [ - 'filter_column_id_product', - 'filter_column_sav_quantity', - 'filter_column_price', - ]), true); - $field = substr($filterParam, 14); // 'filter_column_' takes 14 chars - if (isset($sqlSelect[$field]['table'])) { - $sqlWhere[] = $sqlSelect[$field]['table'] . '.`' . $sqlSelect[$field]['field'] . '` ' . sprintf($sqlSelect[$field]['filtering'], $filterValue); - } else { - $sqlWhere[] = '(' . sprintf($sqlSelect[$field]['filtering'], $filterValue) . ')'; - } - } - // for 'filter_category', see next if($showPositionColumn) block. - } - $sqlWhere[] = 'state = ' . Product::STATE_SAVED; - - // exec legacy hook but with different parameters (retro-compat < 1.7 is broken here) - Hook::exec('actionAdminProductsListingFieldsModifier', [ - '_ps_version' => Version::VERSION, - 'sql_select' => &$sqlSelect, - 'sql_table' => &$sqlTable, - 'sql_where' => &$sqlWhere, - 'sql_group_by' => &$sqlGroupBy, - 'sql_order' => &$sqlOrder, - 'sql_limit' => &$sqlLimit, - ]); - - $sql = $this->compileSqlQuery($sqlSelect, $sqlTable, $sqlWhere, $sqlGroupBy, $sqlOrder, $sqlLimit); - $products = Db::getInstance()->executeS($sql, true, false); - $total = Db::getInstance()->executeS('SELECT FOUND_ROWS();', true, false); - $total = $total[0]['FOUND_ROWS()']; - - // post treatment - $currency = Currency::getDefaultCurrency(); - $localeCldr = Tools::getContextLocale(Context::getContext()); - - /** - * @var array{id_product: int, reference: string, price: string, id_shop_default: int, link_rewrite: string, id_image: int} $product - */ - foreach ($products as &$product) { - $product['total'] = $total; // total product count (filtered) - $product['price_final'] = Product::getPriceStatic( - $product['id_product'], - true, - null, - Context::getContext()->getComputingPrecision(), - null, - false, - false, - 1, - true, - null, - null, - null, - $nothing, - true, - true - ); - - if ($formatCldr) { - $product['price'] = $localeCldr->formatPrice($product['price'], $currency->iso_code); - $product['price_final'] = $localeCldr->formatPrice($product['price_final'], $currency->iso_code); - } - $product['image'] = $this->imageManager->getThumbnailForListing($product['id_image']); - $product['image_link'] = Context::getContext()->link->getImageLink( - $product['link_rewrite'], - (string) $product['id_image'] - ); - } - - // post treatment by hooks - // exec legacy hook but with different parameters (retro-compat < 1.7 is broken here) - Hook::exec('actionAdminProductsListingResultsModifier', [ - '_ps_version' => Version::VERSION, - 'products' => &$products, - 'total' => $total, - ]); - - return $products; - } - - /** - * {@inheritdoc} - */ - public function countAllProducts() - { - $idShop = Context::getContext()->shop->id; - - $query = new DbQuery(); - $query->select('COUNT(ps.id_product)'); - $query->from('product_shop', 'ps'); - $query->where('ps.id_shop = ' . (int) $idShop); - - $total = Db::getInstance()->getValue($query); - - return (int) $total; - } - - /** - * Translates new Core route parameters into their Legacy equivalent. - * - * @param string[] $coreParameters The new Core route parameters - * - * @return array The URL parameters for Legacy URL (GETs) - */ - public function mapLegacyParametersProductForm($coreParameters = []) - { - $params = []; - if ($coreParameters['id'] == '0') { - $params['addproduct'] = 1; - } else { - $params['updateproduct'] = 1; - $params['id_product'] = $coreParameters['id']; - } - - return $params; - } - - /** - * {@inheritdoc} - */ - public function getPaginationLimitChoices() - { - $paginationLimitChoices = [20, 50, 100]; - - $memory = Tools::getMemoryLimit(); - - if ($memory >= 512 * 1024 * 1024) { - $paginationLimitChoices[] = 300; - } - if ($memory >= 1536 * 1024 * 1024) { - $paginationLimitChoices[] = 1000; - } - - return $paginationLimitChoices; - } - - /** - * {@inheritdoc} - */ - public function isNewProductDefaultActivated() - { - return (bool) Configuration::get('PS_PRODUCT_ACTIVATION_DEFAULT'); - } -} diff --git a/src/Adapter/Product/AdminProductDataUpdater.php b/src/Adapter/Product/AdminProductDataUpdater.php deleted file mode 100644 index 3576a313b9ec5..0000000000000 --- a/src/Adapter/Product/AdminProductDataUpdater.php +++ /dev/null @@ -1,303 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Product; - -use Category; -use Configuration; -use Db; -use GroupReduction; -use Image; -use Pack; -use PrestaShop\PrestaShop\Core\Hook\HookDispatcherInterface; -use PrestaShopBundle\Exception\UpdateProductException; -use PrestaShopBundle\Service\DataUpdater\Admin\ProductInterface; -use Product; -use Search; -use Shop; -use ShopGroup; -use StockAvailable; -use Validate; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This class will update/insert/delete data from DB / ORM about Product, for both Front and Admin interfaces. - */ -class AdminProductDataUpdater implements ProductInterface -{ - /** - * @var HookDispatcherInterface - */ - private $hookDispatcher; - - /** - * Constructor. HookDispatcher is injected by Sf container. - * - * @param HookDispatcherInterface $hookDispatcher - */ - public function __construct(HookDispatcherInterface $hookDispatcher) - { - $this->hookDispatcher = $hookDispatcher; - } - - /** - * {@inheritdoc} - */ - public function activateProductIdList(array $productListId, $activate = true) - { - if (count($productListId) < 1) { - throw new \Exception('AdminProductDataUpdater->activateProductIdList() should always receive at least one ID. Zero given.', 5003); - } - - $failedIdList = []; - foreach ($productListId as $productId) { - $product = new Product($productId); - if (!Validate::isLoadedObject($product) - || $product->validateFields(false, true) !== true - || $product->validateFieldsLang(false, true) !== true) { - $failedIdList[] = $productId; - - continue; - } - $product->active = (bool) $activate; - $product->update(); - if (in_array($product->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $product->id); - } - $this->hookDispatcher->dispatchWithParameters('actionProductActivation', ['id_product' => (int) $product->id, 'product' => $product, 'activated' => $activate]); - } - - if (count($failedIdList) > 0) { - throw new UpdateProductException('Cannot change activation state on many requested products', 5004); - } - - return true; - } - - /** - * {@inheritdoc} - */ - public function deleteProductIdList(array $productIdList) - { - if (count($productIdList) < 1) { - throw new \Exception('AdminProductDataUpdater->deleteProductIdList() should always receive at least one ID. Zero given.', 5005); - } - - $failedIdList = $productIdList; // Since we have just one call to delete all, cannot have distinctive fails. - // Hooks: will trigger actionProductDelete multiple times - $result = (new Product())->deleteSelection($productIdList); - - if ($result === 0) { - throw new UpdateProductException('Cannot delete many requested products.', 5006); - } - - return true; - } - - /** - * {@inheritdoc} - */ - public function duplicateProductIdList(array $productIdList) - { - if (count($productIdList) < 1) { - throw new \Exception('AdminProductDataUpdater->duplicateProductIdList() should always receive at least one ID. Zero given.', 5005); - } - - $failedIdList = []; - foreach ($productIdList as $productId) { - try { - $this->duplicateProduct($productId); - } catch (\Exception $e) { - $failedIdList[] = $productId; - - continue; - } - } - - if (count($failedIdList) > 0) { - throw new UpdateProductException('Cannot duplicate many requested products', 5004); - } - - return true; - } - - /** - * {@inheritdoc} - */ - public function deleteProduct($productId) - { - $product = new Product($productId); - if (!Validate::isLoadedObject($product)) { - throw new \Exception('AdminProductDataUpdater->deleteProduct() received an unknown ID.', 5005); - } - - // dumb? no: delete() makes a lot of things, and can reject deletion in specific cases. - // Hooks: will trigger actionProductDelete - $result = $product->delete(); - - if ($result === false) { - throw new UpdateProductException('Cannot delete the requested product.', 5007); - } - - return true; - } - - /** - * {@inheritdoc} - */ - public function duplicateProduct($productId, $namePattern = 'copy of %s') - { - //TODO : use the $namePattern var to input translated version of 'copy of %s', if translation requested. - $product = new Product($productId); - if (!Validate::isLoadedObject($product)) { - throw new \Exception('AdminProductDataUpdater->duplicateProduct() received an unknown ID.', 5005); - } - - if (($error = $product->validateFields(false, true)) !== true - || ($error = $product->validateFieldsLang(false, true)) !== true) { - throw new UpdateProductException(sprintf('Cannot duplicate this product: %s', $error)); - } - - $id_product_old = $product->id; - if (empty($product->price) && Shop::getContext() == Shop::CONTEXT_GROUP) { - $shops = ShopGroup::getShopsFromGroup(Shop::getContextShopGroupID()); - foreach ($shops as $shop) { - if ($product->isAssociatedToShop($shop['id_shop'])) { - $product_price = new Product($id_product_old, false, null, $shop['id_shop']); - $product->price = $product_price->price; - } - } - } - - unset( - $product->id, - $product->id_product - ); - - $product->indexed = false; - $product->active = false; - - // change product name to prefix it - foreach ($product->name as $langKey => $oldName) { - if (!preg_match('/^' . str_replace('%s', '.*', preg_quote($namePattern, '/') . '$/'), $oldName)) { - $newName = sprintf($namePattern, $oldName); - if (mb_strlen($newName, 'UTF-8') <= 127) { - $product->name[$langKey] = $newName; - } - } - } - - if ($product->add() - && Category::duplicateProductCategories($id_product_old, $product->id) - && Product::duplicateSuppliers($id_product_old, $product->id) - && ($combination_images = Product::duplicateAttributes($id_product_old, $product->id)) !== false - && GroupReduction::duplicateReduction($id_product_old, $product->id) - && Product::duplicateAccessories($id_product_old, $product->id) - && Product::duplicateFeatures($id_product_old, $product->id) - && Product::duplicateSpecificPrices($id_product_old, $product->id) - && Pack::duplicate($id_product_old, $product->id) - && Product::duplicateCustomizationFields($id_product_old, $product->id) - && Product::duplicatePrices($id_product_old, $product->id) - && Product::duplicateTags($id_product_old, $product->id) - && Product::duplicateTaxes($id_product_old, $product->id) - && Product::duplicateDownload($id_product_old, $product->id)) { - if ($product->hasAttributes()) { - Product::updateDefaultAttribute($product->id); - } - - if (!Image::duplicateProductImages($id_product_old, $product->id, $combination_images)) { - throw new UpdateProductException('An error occurred while copying images.', 5008); - } else { - $this->hookDispatcher->dispatchWithParameters('actionProductAdd', ['id_product_old' => $id_product_old, 'id_product' => (int) $product->id, 'product' => $product]); - if (in_array($product->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $product->id); - } - StockAvailable::setProductOutOfStock($product->id, StockAvailable::outOfStock($id_product_old)); - - return $product->id; - } - } else { - if ($product->id !== null) { - $product->delete(); - } - throw new \Exception('An error occurred while creating an object.', 5009); - } - } - - /** - * {@inheritdoc} - */ - public function sortProductIdList(array $productList, $filterParams) - { - if (count($productList) < 2) { - return false; - } - - if (!isset($filterParams['filter_category'])) { - throw new \Exception('Cannot sort when filterParams does not contains \'filter_category\'.', 5010); - } - - foreach ($filterParams as $k => $v) { - if ($v == '' || !str_starts_with($k, 'filter_')) { - continue; - } - if ($k == 'filter_category') { - continue; - } - - throw new \Exception('Cannot sort when filterParams contains other filter than \'filter_category\'.', 5010); - } - - $categoryId = $filterParams['filter_category']; - $minPosition = min(array_values($productList)); - $productsIds = implode(',', array_map('intval', array_keys($productList))); - - /* - * First request to update position on category_product - */ - Db::getInstance()->query('SET @i := ' . (((int) $minPosition) - 1)); - $updatePositions = 'UPDATE `' . _DB_PREFIX_ . 'category_product` cp ' . - 'SET cp.`position` = (SELECT @i := @i + 1) ' . - 'WHERE cp.`id_category` = ' . (int) $categoryId . ' AND cp.`id_product` IN (' . $productsIds . ') ' . - 'ORDER BY FIELD(cp.`id_product`, ' . $productsIds . ')'; - Db::getInstance()->query($updatePositions); - - /** - * Second request to update date_upd because - * ORDER BY is not working on multi-tables update - */ - $updateProducts = 'UPDATE `' . _DB_PREFIX_ . 'product` p ' . - '' . Shop::addSqlAssociation('product', 'p') . ' ' . - 'SET ' . - ' p.`date_upd` = "' . date('Y-m-d H:i:s') . '", ' . - ' product_shop.`date_upd` = "' . date('Y-m-d H:i:s') . '" ' . - 'WHERE p.`id_product` IN (' . $productsIds . ') '; - Db::getInstance()->query($updateProducts); - - return true; - } -} diff --git a/src/Adapter/Product/AdminProductWrapper.php b/src/Adapter/Product/AdminProductWrapper.php deleted file mode 100644 index 88750d9a7adf2..0000000000000 --- a/src/Adapter/Product/AdminProductWrapper.php +++ /dev/null @@ -1,949 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Product; - -use AdminProductsController; -use Attachment; -use Category; -use Combination; -use Configuration; -use Context; -use Customer; -use Db; -use Hook; -use Image; -use Language; -use ObjectModel; -use PrestaShop\PrestaShop\Adapter\Entity\Customization; -use PrestaShop\PrestaShop\Core\Foundation\Database\EntityNotFoundException; -use PrestaShop\PrestaShop\Core\Localization\Locale; -use PrestaShopBundle\Form\FormHelper; -use PrestaShopBundle\Utils\FloatParser; -use Product; -use ProductDownload; -use Shop; -use ShopUrl; -use SpecificPrice; -use SpecificPriceRule; -use StockAvailable; -use Symfony\Contracts\Translation\TranslatorInterface; -use Tax; -use Tools; -use Validate; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller wrapper for new Architecture, about Product admin controller. - */ -class AdminProductWrapper -{ - /** - * @var array - */ - private $errors = []; - - /** - * @var Locale - */ - private $locale; - - /** - * @var TranslatorInterface - */ - private $translator; - - /** - * @var array - */ - private $employeeAssociatedShops; - - /** - * @var FloatParser - */ - private $floatParser; - - /** - * Constructor : Inject Symfony\Component\Translation Translator. - * - * @param object $translator - * @param array $employeeAssociatedShops - * @param Locale $locale - * @param FloatParser|null $floatParser - */ - public function __construct($translator, array $employeeAssociatedShops, Locale $locale, FloatParser $floatParser = null) - { - $this->translator = $translator; - $this->employeeAssociatedShops = $employeeAssociatedShops; - $this->locale = $locale; - $this->floatParser = $floatParser ?? new FloatParser(); - } - - /** - * getInstance - * Get the legacy AdminProductsControllerCore instance. - * - * @return AdminProductsController instance - */ - public function getInstance() - { - return new AdminProductsController(); - } - - /** - * processProductAttribute - * Update a combination. - * - * @param object $product - * @param array $combinationValues the posted values - * - * @return void - */ - public function processProductAttribute($product, $combinationValues) - { - $id_product_attribute = (int) $combinationValues['id_product_attribute']; - $images = []; - - if (!Combination::isFeatureActive() || $id_product_attribute == 0) { - return; - } - - if (!isset($combinationValues['attribute_wholesale_price'])) { - $combinationValues['attribute_wholesale_price'] = 0; - } - if (!isset($combinationValues['attribute_price_impact'])) { - $combinationValues['attribute_price_impact'] = 0; - } - if (!isset($combinationValues['attribute_weight_impact'])) { - $combinationValues['attribute_weight_impact'] = 0; - } - - // This is VERY UGLY, but since ti ComputingPrecision can never return enough decimals for now we have no - // choice but to hard code this one to make sure enough precision is saved in the DB or it results in errors - // of 1 cent in the shop - $computingPrecision = FormHelper::DEFAULT_PRICE_PRECISION; - if (!isset($combinationValues['attribute_ecotax']) || 0.0 === (float) $combinationValues['attribute_ecotax']) { - $combinationValues['attribute_ecotax'] = 0; - } else { - // Value is displayed tax included but must be saved tax excluded - $combinationValues['attribute_ecotax'] = Tools::ps_round( - $combinationValues['attribute_ecotax'] / (1 + Tax::getProductEcotaxRate() / 100), - $computingPrecision - ); - } - if ((isset($combinationValues['attribute_default']) && $combinationValues['attribute_default'] == 1)) { - $product->deleteDefaultAttributes(); - } - if (!empty($combinationValues['id_image_attr'])) { - $images = $combinationValues['id_image_attr']; - } else { - $combination = new Combination($id_product_attribute); - $combination->setImages([]); - } - if (!isset($combinationValues['attribute_low_stock_threshold'])) { - $combinationValues['attribute_low_stock_threshold'] = null; - } - if (!isset($combinationValues['attribute_low_stock_alert'])) { - $combinationValues['attribute_low_stock_alert'] = false; - } - - $product->updateAttribute( - $id_product_attribute, - $combinationValues['attribute_wholesale_price'], - $combinationValues['attribute_price'] * $combinationValues['attribute_price_impact'], - $combinationValues['attribute_weight'] * $combinationValues['attribute_weight_impact'], - $combinationValues['attribute_unity'] * $combinationValues['attribute_unit_impact'], - $combinationValues['attribute_ecotax'], - $images, - $combinationValues['attribute_reference'], - $combinationValues['attribute_ean13'], - (isset($combinationValues['attribute_default']) && $combinationValues['attribute_default'] == 1), - isset($combinationValues['attribute_location']) ? $combinationValues['attribute_location'] : null, - $combinationValues['attribute_upc'], - $combinationValues['attribute_minimal_quantity'], - $combinationValues['available_date_attribute'], - false, - [], - $combinationValues['attribute_isbn'], - $combinationValues['attribute_low_stock_threshold'], - $combinationValues['attribute_low_stock_alert'], - $combinationValues['attribute_mpn'] - ); - - StockAvailable::setProductOutOfStock((int) $product->id, $product->out_of_stock, null, $id_product_attribute); - StockAvailable::setLocation((int) $product->id, $combinationValues['attribute_location'], null, $id_product_attribute); - - $product->checkDefaultAttributes(); - - if ((isset($combinationValues['attribute_default']) && $combinationValues['attribute_default'] == 1)) { - Product::updateDefaultAttribute((int) $product->id); - $product->cache_default_attribute = (int) $id_product_attribute; - - // We need to reload the product because some other calls have modified the database - // It's done just for the setAvailableDate to avoid side effects - Product::disableCache(); - $consistentProduct = new Product($product->id); - if ($available_date = $combinationValues['available_date_attribute']) { - $consistentProduct->setAvailableDate($available_date); - } else { - $consistentProduct->setAvailableDate(); - } - Product::enableCache(); - } - - if (isset($combinationValues['attribute_quantity'])) { - $this->processQuantityUpdate($product, $combinationValues['attribute_quantity'], $id_product_attribute); - } - } - - /** - * Update a quantity for a product or a combination. - * - * @param Product $product - * @param int $quantity - * @param int $forAttributeId - */ - public function processQuantityUpdate(Product $product, $quantity, $forAttributeId = 0) - { - // Hook triggered by legacy code below: actionUpdateQuantity('id_product', 'id_product_attribute', 'quantity') - StockAvailable::setQuantity((int) $product->id, $forAttributeId, $quantity); - Hook::exec('actionProductUpdate', ['id_product' => (int) $product->id, 'product' => $product]); - } - - /** - * Update the out of stock strategy. - * - * @param Product $product - * @param int $out_of_stock - */ - public function processProductOutOfStock(Product $product, $out_of_stock) - { - StockAvailable::setProductOutOfStock((int) $product->id, (int) $out_of_stock); - } - - /** - * @param Product $product - * @param string $location - */ - public function processLocation(Product $product, $location) - { - StockAvailable::setLocation($product->id, $location); - } - - /** - * Add/Update a SpecificPrice object. - * - * @param int $id_product - * @param array $specificPriceValues the posted values - * @param int|null $idSpecificPrice if this is an update of an existing specific price, null else - * - * @return AdminProductsController|array - */ - public function processProductSpecificPrice($id_product, $specificPriceValues, $idSpecificPrice = null) - { - // ---- data formatting ---- - $id_product_attribute = $specificPriceValues['sp_id_product_attribute'] ?? 0; - $id_shop = $specificPriceValues['sp_id_shop'] ? $specificPriceValues['sp_id_shop'] : 0; - $id_currency = $specificPriceValues['sp_id_currency'] ? $specificPriceValues['sp_id_currency'] : 0; - $id_country = $specificPriceValues['sp_id_country'] ? $specificPriceValues['sp_id_country'] : 0; - $id_group = $specificPriceValues['sp_id_group'] ? $specificPriceValues['sp_id_group'] : 0; - $id_customer = !empty($specificPriceValues['sp_id_customer']['data']) ? $specificPriceValues['sp_id_customer']['data'][0] : 0; - $price = isset($specificPriceValues['leave_bprice']) ? '-1' : $this->floatParser->fromString($specificPriceValues['sp_price']); - $from_quantity = $specificPriceValues['sp_from_quantity']; - $reduction = $this->floatParser->fromString($specificPriceValues['sp_reduction']); - $reduction_tax = $specificPriceValues['sp_reduction_tax']; - $reduction_type = !$reduction ? 'amount' : $specificPriceValues['sp_reduction_type']; - $reduction_type = $reduction_type == '-' ? 'amount' : $reduction_type; - $from = $specificPriceValues['sp_from']; - if (!$from) { - $from = '0000-00-00 00:00:00'; - } - $to = $specificPriceValues['sp_to']; - if (!$to) { - $to = '0000-00-00 00:00:00'; - } - $isThisAnUpdate = (null !== $idSpecificPrice); - - // ---- validation ---- - if (($price == '-1') && ((float) $reduction == '0')) { - $this->errors[] = $this->translator->trans('No reduction value has been submitted.', [], 'Admin.Catalog.Notification'); - } elseif ($to != '0000-00-00 00:00:00' && strtotime($to) < strtotime($from)) { - $this->errors[] = $this->translator->trans('Invalid date range', [], 'Admin.Catalog.Notification'); - } elseif ($reduction_type == 'percentage' && ((float) $reduction <= 0 || (float) $reduction > 100)) { - $this->errors[] = $this->translator->trans('The submitted reduction value (0-100) is out-of-range.', [], 'Admin.Catalog.Notification'); - } - $validationResult = $this->validateSpecificPrice( - $id_product, - $id_shop, - $id_currency, - $id_country, - $id_group, - $id_customer, - $price, - $from_quantity, - $reduction, - $reduction_type, - $from, - $to, - $id_product_attribute, - $isThisAnUpdate - ); - - if (false === $validationResult || count($this->errors)) { - return $this->errors; - } - - // ---- data modification ---- - if ($isThisAnUpdate) { - $specificPrice = new SpecificPrice($idSpecificPrice); - } else { - $specificPrice = new SpecificPrice(); - } - - $specificPrice->id_product = (int) $id_product; - $specificPrice->id_product_attribute = (int) $id_product_attribute; - $specificPrice->id_shop = (int) $id_shop; - $specificPrice->id_currency = (int) ($id_currency); - $specificPrice->id_country = (int) ($id_country); - $specificPrice->id_group = (int) ($id_group); - $specificPrice->id_customer = (int) $id_customer; - $specificPrice->price = (float) ($price); - $specificPrice->from_quantity = (int) ($from_quantity); - $specificPrice->reduction = (float) ($reduction_type == 'percentage' ? $reduction / 100 : $reduction); - $specificPrice->reduction_tax = $reduction_tax; - $specificPrice->reduction_type = $reduction_type; - $specificPrice->from = $from; - $specificPrice->to = $to; - - if ($isThisAnUpdate) { - $dataSavingResult = $specificPrice->save(); - } else { - $dataSavingResult = $specificPrice->add(); - } - - if (false === $dataSavingResult) { - $this->errors[] = $this->translator->trans('An error occurred while updating the specific price.', [], 'Admin.Catalog.Notification'); - } - - return $this->errors; - } - - /** - * Validate a specific price. - */ - private function validateSpecificPrice( - $id_product, - $id_shop, - $id_currency, - $id_country, - $id_group, - $id_customer, - $price, - $from_quantity, - $reduction, - $reduction_type, - $from, - $to, - $id_combination = 0, - $isThisAnUpdate = false - ) { - if (!Validate::isUnsignedId($id_shop) || !Validate::isUnsignedId($id_currency) || !Validate::isUnsignedId($id_country) || !Validate::isUnsignedId($id_group) || !Validate::isUnsignedId($id_customer)) { - $this->errors[] = 'Wrong IDs'; - } elseif ((!isset($price) && !isset($reduction)) || (isset($price) && !Validate::isNegativePrice($price)) || (isset($reduction) && !Validate::isPrice($reduction))) { - $this->errors[] = 'Invalid price/discount amount'; - } elseif (!Validate::isUnsignedInt($from_quantity)) { - $this->errors[] = 'Invalid quantity'; - } elseif ($reduction && !Validate::isReductionType($reduction_type)) { - $this->errors[] = 'Please select a discount type (amount or percentage).'; - } elseif ($from && $to && (!Validate::isDateFormat($from) || !Validate::isDateFormat($to))) { - $this->errors[] = 'The from/to date is invalid.'; - } elseif (!$isThisAnUpdate && SpecificPrice::exists((int) $id_product, $id_combination, $id_shop, $id_group, $id_country, $id_currency, $id_customer, $from_quantity, $from, $to, false)) { - $this->errors[] = 'A specific price already exists for these parameters.'; - } else { - return true; - } - - return false; - } - - /** - * Get specific prices list for a product. - * - * @param object $product - * @param object $defaultCurrency - * @param array $shops Available shops - * @param array $currencies Available currencies - * @param array $countries Available countries - * @param array $groups Available users groups - * - * @return array - */ - public function getSpecificPricesList($product, $defaultCurrency, $shops, $currencies, $countries, $groups) - { - $content = []; - $specific_prices = array_merge( - SpecificPrice::getByProductId((int) $product->id), - SpecificPrice::getByProductId(0) - ); - - $tmp = []; - foreach ($shops as $shop) { - $tmp[$shop['id_shop']] = $shop; - } - $shops = $tmp; - $tmp = []; - foreach ($currencies as $currency) { - $tmp[$currency['id_currency']] = $currency; - } - $currencies = $tmp; - - $tmp = []; - foreach ($countries as $country) { - $tmp[$country['id_country']] = $country; - } - $countries = $tmp; - - $tmp = []; - foreach ($groups as $group) { - $tmp[$group['id_group']] = $group; - } - $groups = $tmp; - - if (is_array($specific_prices) && count($specific_prices)) { - foreach ($specific_prices as $specific_price) { - $id_currency = $specific_price['id_currency'] ? $specific_price['id_currency'] : $defaultCurrency->id; - if (!isset($currencies[$id_currency])) { - continue; - } - - $current_specific_currency = $currencies[$id_currency]; - if ($specific_price['reduction_type'] == 'percentage') { - $impact = '- ' . ($specific_price['reduction'] * 100) . ' %'; - } elseif ($specific_price['reduction'] > 0) { - $impact = '- ' . $this->locale->formatPrice($specific_price['reduction'], $current_specific_currency['iso_code']) . ' '; - if ($specific_price['reduction_tax']) { - $impact .= '(' . $this->translator->trans('Tax incl.', [], 'Admin.Global') . ')'; - } else { - $impact .= '(' . $this->translator->trans('Tax excl.', [], 'Admin.Global') . ')'; - } - } else { - $impact = '--'; - } - - if ($specific_price['from'] == '0000-00-00 00:00:00' && $specific_price['to'] == '0000-00-00 00:00:00') { - $period = $this->translator->trans('Unlimited', [], 'Admin.Global'); - } else { - $period = $this->translator->trans('From', [], 'Admin.Global') . ' ' . ($specific_price['from'] != '0000-00-00 00:00:00' ? $specific_price['from'] : '0000-00-00 00:00:00') . '
' . $this->translator->trans('to', [], 'Admin.Global') . ' ' . ($specific_price['to'] != '0000-00-00 00:00:00' ? $specific_price['to'] : '0000-00-00 00:00:00'); - } - if ($specific_price['id_product_attribute']) { - $combination = new Combination((int) $specific_price['id_product_attribute']); - $attributes = $combination->getAttributesName(1); - $attributes_name = ''; - foreach ($attributes as $attribute) { - $attributes_name .= $attribute['name'] . ' - '; - } - $attributes_name = rtrim($attributes_name, ' - '); - } else { - $attributes_name = $this->translator->trans('All combinations', [], 'Admin.Catalog.Feature'); - } - - $rule = new SpecificPriceRule((int) $specific_price['id_specific_price_rule']); - $rule_name = ($rule->id ? $rule->name : '--'); - - if ($specific_price['id_customer']) { - $customer = new Customer((int) $specific_price['id_customer']); - if (Validate::isLoadedObject($customer)) { - $customer_full_name = $customer->firstname . ' ' . $customer->lastname; - } - unset($customer); - } - - if (!$specific_price['id_shop'] || in_array($specific_price['id_shop'], Shop::getContextListShopID())) { - $can_delete_specific_prices = true; - if (Shop::isFeatureActive()) { - $can_delete_specific_prices = (count($this->employeeAssociatedShops) > 1 && !$specific_price['id_shop']) || $specific_price['id_shop']; - } - - $price = Tools::ps_round($specific_price['price'], 2); - $fixed_price = (($price == Tools::ps_round($product->price, 2) && $current_specific_currency['id_currency'] == $defaultCurrency->id) || $specific_price['price'] == -1) ? '--' : $this->locale->formatPrice($price, $current_specific_currency['iso_code']); - - $content[] = [ - 'id_specific_price' => $specific_price['id_specific_price'], - 'id_product' => $product->id, - 'rule_name' => $rule_name, - 'attributes_name' => $attributes_name, - 'shop' => ($specific_price['id_shop'] ? $shops[$specific_price['id_shop']]['name'] : $this->translator->trans('All stores', [], 'Admin.Global')), - 'currency' => ($specific_price['id_currency'] ? $currencies[$specific_price['id_currency']]['name'] : $this->translator->trans('All currencies', [], 'Admin.Global')), - 'country' => ($specific_price['id_country'] ? $countries[$specific_price['id_country']]['name'] : $this->translator->trans('All countries', [], 'Admin.Global')), - 'group' => ($specific_price['id_group'] ? $groups[$specific_price['id_group']]['name'] : $this->translator->trans('All groups', [], 'Admin.Global')), - 'customer' => (isset($customer_full_name) ? $customer_full_name : $this->translator->trans('All customers', [], 'Admin.Global')), - 'fixed_price' => $fixed_price, - 'impact' => $impact, - 'period' => $period, - 'from_quantity' => $specific_price['from_quantity'], - 'can_delete' => (!$rule->id && $can_delete_specific_prices) ? true : false, - 'can_edit' => (!$rule->id && $can_delete_specific_prices) ? true : false, - ]; - - unset($customer_full_name); - } - } - } - - return $content; - } - - /** - * @param int $id - * - * @return SpecificPrice - * - * @throws EntityNotFoundException - */ - public function getSpecificPriceDataById($id) - { - $price = new SpecificPrice($id); - if (null === $price->id) { - throw new EntityNotFoundException(sprintf('Cannot find specific price with id %d', $id)); - } - - return $price; - } - - /** - * Delete a specific price. - * - * @param int $id_specific_price - * - * @return array error & status - */ - public function deleteSpecificPrice($id_specific_price) - { - if (!$id_specific_price || !Validate::isUnsignedId($id_specific_price)) { - $error = $this->translator->trans('The specific price ID is invalid.', [], 'Admin.Catalog.Notification'); - } else { - $specificPrice = new SpecificPrice((int) $id_specific_price); - if (!$specificPrice->delete()) { - $error = $this->translator->trans('An error occurred while attempting to delete the specific price.', [], 'Admin.Catalog.Notification'); - } - } - - if (isset($error)) { - return [ - 'status' => 'error', - 'message' => $error, - ]; - } - - return [ - 'status' => 'ok', - 'message' => $this->translator->trans('Successful deletion', [], 'Admin.Notifications.Success'), - ]; - } - - /** - * Get price priority. - * - * @param int|null $idProduct - * - * @return array - */ - public function getPricePriority($idProduct = null) - { - if (!$idProduct) { - return [ - 0 => 'id_shop', - 1 => 'id_currency', - 2 => 'id_country', - 3 => 'id_group', - ]; - } - - $specific_price_priorities = SpecificPrice::getPriority((int) $idProduct); - - // Not use id_customer - if ($specific_price_priorities[0] == 'id_customer') { - unset($specific_price_priorities[0]); - } - - return array_values($specific_price_priorities); - } - - /** - * Process customization collection. - * - * @param object $product - * @param array $data - * - * @return array - */ - public function processProductCustomization($product, $data) - { - $customization_ids = []; - if ($data) { - foreach ($data as $customization) { - $customization_ids[] = (int) $customization['id_customization_field']; - } - } - - $shopList = Shop::getContextListShopID(); - - /* Update the customization fields to be deleted in the next step if not used */ - $product->softDeleteCustomizationFields($customization_ids); - - $usedCustomizationIds = $product->getUsedCustomizationFieldsIds(); - $usedCustomizationIds = array_column($usedCustomizationIds, 'index'); - $usedCustomizationIds = array_map('intval', $usedCustomizationIds); - $usedCustomizationIds = array_unique(array_merge($usedCustomizationIds, $customization_ids), SORT_REGULAR); - - //remove customization field langs for current context shops - $productCustomization = $product->getCustomizationFieldIds(); - $toDeleteCustomizationIds = []; - foreach ($productCustomization as $customizationFiled) { - if (!in_array((int) $customizationFiled['id_customization_field'], $usedCustomizationIds)) { - $toDeleteCustomizationIds[] = (int) $customizationFiled['id_customization_field']; - } - //if the customization_field is still in use, only delete the current context shops langs, - if (in_array((int) $customizationFiled['id_customization_field'], $customization_ids)) { - Customization::deleteCustomizationFieldLangByShop($customizationFiled['id_customization_field'], $shopList); - } - } - - //remove unused customization for the product - $product->deleteUnusedCustomizationFields($toDeleteCustomizationIds); - - //create new customizations - $countFieldText = 0; - $countFieldFile = 0; - $productCustomizableValue = 0; - $hasRequiredField = false; - - $new_customization_fields_ids = []; - - if ($data) { - foreach ($data as $key => $customization) { - if ($customization['require']) { - $hasRequiredField = true; - } - - //create label - if (isset($customization['id_customization_field'])) { - $id_customization_field = (int) $customization['id_customization_field']; - Db::getInstance()->execute('UPDATE `' . _DB_PREFIX_ . 'customization_field` - SET `required` = ' . ($customization['require'] ? 1 : 0) . ', `type` = ' . (int) $customization['type'] . ' - WHERE `id_customization_field` = ' . $id_customization_field); - } else { - Db::getInstance()->execute( - 'INSERT INTO `' . _DB_PREFIX_ . 'customization_field` (`id_product`, `type`, `required`) - VALUES (' - . (int) $product->id . ', ' - . (int) $customization['type'] . ', ' - . ($customization['require'] ? 1 : 0) - . ')' - ); - $id_customization_field = (int) Db::getInstance()->Insert_ID(); - } - - $new_customization_fields_ids[$key] = $id_customization_field; - - // Create multilingual label name - $langValues = ''; - foreach (Language::getLanguages() as $language) { - $name = $customization['label'][$language['id_lang']]; - foreach ($shopList as $id_shop) { - $langValues .= '(' - . (int) $id_customization_field . ', ' - . (int) $language['id_lang'] . ', ' - . (int) $id_shop . ',\'' - . pSQL($name) - . '\'), '; - } - } - Db::getInstance()->execute( - 'INSERT INTO `' . _DB_PREFIX_ . 'customization_field_lang` (`id_customization_field`, `id_lang`, `id_shop`, `name`) VALUES ' - . rtrim( - $langValues, - ', ' - ) - ); - - if ($customization['type'] == Product::CUSTOMIZE_FILE) { - ++$countFieldFile; - } else { - ++$countFieldText; - } - } - - $productCustomizableValue = $hasRequiredField ? 2 : 1; - } - - //update product count fields labels - Db::getInstance()->execute('UPDATE `' . _DB_PREFIX_ . 'product` SET `customizable` = ' . $productCustomizableValue . ', `uploadable_files` = ' . (int) $countFieldFile . ', `text_fields` = ' . (int) $countFieldText . ' WHERE `id_product` = ' . (int) $product->id); - - //update product_shop count fields labels - ObjectModel::updateMultishopTable('product', [ - 'customizable' => $productCustomizableValue, - 'uploadable_files' => (int) $countFieldFile, - 'text_fields' => (int) $countFieldText, - ], 'a.id_product = ' . (int) $product->id); - - Configuration::updateGlobalValue('PS_CUSTOMIZATION_FEATURE_ACTIVE', '1'); - - return $new_customization_fields_ids; - } - - /** - * Update product download. - * - * @param object $product - * @param array $data - * - * @return ProductDownload - */ - public function updateDownloadProduct($product, $data) - { - $id_product_download = ProductDownload::getIdFromIdProduct((int) $product->id, false); - $download = new ProductDownload($id_product_download ? $id_product_download : null); - - if ((int) $data['is_virtual_file'] == 1) { - $fileName = null; - $file = $data['file']; - - if (!empty($file)) { - $fileName = ProductDownload::getNewFilename(); - $file->move(_PS_DOWNLOAD_DIR_, $fileName); - } - - $product->setDefaultAttribute(0); //reset cache_default_attribute - - $download->id_product = (int) $product->id; - $download->display_filename = $data['name']; - $download->filename = $fileName ? $fileName : $download->filename; - $download->date_add = date('Y-m-d H:i:s'); - $download->date_expiration = $data['expiration_date'] ? $data['expiration_date'] . ' 23:59:59' : ''; - $download->nb_days_accessible = (int) $data['nb_days']; - $download->nb_downloadable = (int) $data['nb_downloadable']; - $download->active = true; - $download->is_shareable = false; - - if (!$id_product_download) { - $download->save(); - } else { - $download->update(); - } - } else { - if (!empty($id_product_download)) { - $download->date_expiration = date('Y-m-d H:i:s', time() - 1); - $download->active = false; - $download->update(); - } - } - - return $download; - } - - /** - * Delete file from a virtual product. - * - * @param object $product - */ - public function processDeleteVirtualProductFile($product) - { - $id_product_download = ProductDownload::getIdFromIdProduct((int) $product->id, false); - $download = new ProductDownload($id_product_download ? $id_product_download : null); - - if (!empty($download->filename)) { - unlink(_PS_DOWNLOAD_DIR_ . $download->filename); - Db::getInstance()->execute('UPDATE `' . _DB_PREFIX_ . 'product_download` SET filename = "" WHERE `id_product_download` = ' . (int) $download->id); - } - } - - /** - * Delete a virtual product. - * - * @param object $product - */ - public function processDeleteVirtualProduct($product) - { - $id_product_download = ProductDownload::getIdFromIdProduct((int) $product->id, false); - $download = new ProductDownload($id_product_download ? $id_product_download : null); - if (Validate::isLoadedObject($download)) { - $download->delete(true); - } - } - - /** - * Add attachement file. - * - * @param object $product - * @param array $data - * @param array $locales - * - * @return object|null Attachement - */ - public function processAddAttachment($product, $data, $locales) - { - $attachment = null; - $file = $data['file']; - if (!empty($file)) { - $fileName = sha1(microtime()); - $attachment = new Attachment(); - - foreach ($locales as $locale) { - $attachment->name[(int) $locale['id_lang']] = $data['name']; - $attachment->description[(int) $locale['id_lang']] = $data['description']; - } - - $attachment->file = $fileName; - $attachment->mime = $file->getMimeType(); - $attachment->file_name = $file->getClientOriginalName(); - - $file->move(_PS_DOWNLOAD_DIR_, $fileName); - - if ($attachment->add()) { - $attachment->attachProduct($product->id); - } - } - - return $attachment; - } - - /** - * Process product attachments. - * - * @param object $product - * @param array $data - */ - public function processAttachments($product, $data) - { - Attachment::attachToProduct($product->id, $data); - } - - /** - * Update images positions. - * - * @param array $data Indexed array with id product/position - */ - public function ajaxProcessUpdateImagePosition($data) - { - foreach ($data as $id => $position) { - $img = new Image((int) $id); - $img->position = (int) $position; - $img->update(); - } - } - - /** - * Update image legend and cover. - * - * @param int $idImage - * @param array $data - * - * @return object image - */ - public function ajaxProcessUpdateImage($idImage, $data) - { - $img = new Image((int) $idImage); - if ($data['cover']) { - Image::deleteCover((int) $img->id_product); - $img->cover = true; - } - $img->legend = $data['legend']; - $img->update(); - - return $img; - } - - /** - * Generate preview URL. - * - * @param object $product - * @param bool $preview - * - * @return string|bool Preview url - */ - public function getPreviewUrl($product, $preview = true) - { - $context = Context::getContext(); - $id_lang = (int) Configuration::get('PS_LANG_DEFAULT', null, null, $context->shop->id); - - if (!ShopUrl::getMainShopDomain()) { - return false; - } - - $is_rewrite_active = (bool) Configuration::get('PS_REWRITING_SETTINGS'); - $preview_url = $context->link->getProductLink( - $product, - $product->link_rewrite[$context->language->id], - Category::getLinkRewrite($product->id_category_default, $context->language->id), - null, - $id_lang, - (int) $context->shop->id, - 0, - $is_rewrite_active - ); - - if (!$product->active && $preview) { - $preview_url = $this->getPreviewUrlDeactivate($preview_url); - } - - return $preview_url; - } - - /** - * Generate preview URL deactivate. - * - * @param string $preview_url - * - * @return string preview url deactivate - */ - public function getPreviewUrlDeactivate($preview_url) - { - $context = Context::getContext(); - $token = Tools::getAdminTokenLite('AdminProducts'); - - $admin_dir = dirname($_SERVER['PHP_SELF']); - $admin_dir = substr($admin_dir, strrpos($admin_dir, '/') + 1); - $preview_url_deactivate = $preview_url . ((!str_contains($preview_url, '?')) ? '?' : '&') . 'adtoken=' . $token . '&ad=' . $admin_dir . '&id_employee=' . (int) $context->employee->id . '&preview=1'; - - return $preview_url_deactivate; - } - - /** - * Generate preview URL. - * - * @param int $productId - * - * @return string preview url - */ - public function getPreviewUrlFromId($productId) - { - $product = new Product($productId, false); - - return $this->getPreviewUrl($product); - } -} diff --git a/src/Adapter/Product/AttachmentDataProvider.php b/src/Adapter/Product/AttachmentDataProvider.php deleted file mode 100644 index 0f6835c5dc4bd..0000000000000 --- a/src/Adapter/Product/AttachmentDataProvider.php +++ /dev/null @@ -1,54 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Product; - -use Db; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This class will provide data from DB / ORM about attachment. - */ -class AttachmentDataProvider -{ - /** - * Get all attachments. - * - * @param int $id_lang - * - * @return array Attachment - */ - public function getAllAttachments($id_lang) - { - return Db::getInstance()->executeS(' - SELECT * - FROM ' . _DB_PREFIX_ . 'attachment a - LEFT JOIN ' . _DB_PREFIX_ . 'attachment_lang al - ON (a.id_attachment = al.id_attachment AND al.id_lang = ' . (int) $id_lang . ') - '); - } -} diff --git a/src/Adapter/Product/FilterCategoriesRequestPurifier.php b/src/Adapter/Product/FilterCategoriesRequestPurifier.php deleted file mode 100644 index c9c5718b81f34..0000000000000 --- a/src/Adapter/Product/FilterCategoriesRequestPurifier.php +++ /dev/null @@ -1,59 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Product; - -use Symfony\Component\HttpFoundation\Request; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Extracted from Product Controller, used to cleanup the request. - * For internal use only. - */ -final class FilterCategoriesRequestPurifier -{ - public const CATEGORY = 'filter_category'; - - /** - * Changes the filter category values in case it is not numeric or signed. - * - * @param Request $request - * - * @return Request - */ - public function purify(Request $request) - { - if ($request->isMethod('POST')) { - $value = $request->request->get(self::CATEGORY); - if (null !== $value && (!is_numeric($value) || $value < 0)) { - $request->request->set(self::CATEGORY, ''); - } - } - - return $request; - } -} diff --git a/src/Adapter/Product/ListParametersUpdater.php b/src/Adapter/Product/ListParametersUpdater.php deleted file mode 100644 index bb194e588af7a..0000000000000 --- a/src/Adapter/Product/ListParametersUpdater.php +++ /dev/null @@ -1,145 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Product; - -use PrestaShop\PrestaShop\Core\Exception\ProductException; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Can manage filter parameters from request in Product Catalogue Page. - * For internal use only. - */ -final class ListParametersUpdater -{ - /** - * In case of position ordering all the filters should be reset. - * - * @param array $filterParameters - * @param string $orderBy - * @param bool $hasCategoryFilter - * - * @return array $filterParameters - */ - public function cleanFiltersForPositionOrdering($filterParameters, $orderBy, $hasCategoryFilter) - { - if ($orderBy == 'position_ordering' && $hasCategoryFilter) { - foreach (array_keys($filterParameters) as $key) { - if (str_starts_with($key, 'filter_column_')) { - $filterParameters[$key] = ''; - } - } - } - - return $filterParameters; - } - - /** - * @param array $queryFilterParameters - * @param array $persistedFilterParameters - * @param array $defaultFilterParameters - * - * @return array - * - * @throws ProductException - */ - public function buildListParameters( - array $queryFilterParameters, - array $persistedFilterParameters, - array $defaultFilterParameters - ) { - $filters = [ - 'offset' => (int) $this->getParameter( - 'offset', - $queryFilterParameters, - $persistedFilterParameters, - $defaultFilterParameters - ), - 'limit' => (int) $this->getParameter( - 'limit', - $queryFilterParameters, - $persistedFilterParameters, - $defaultFilterParameters - ), - 'orderBy' => (string) $this->getParameter( - 'orderBy', - $queryFilterParameters, - $persistedFilterParameters, - $defaultFilterParameters - ), - 'sortOrder' => (string) $this->getParameter( - 'sortOrder', - $queryFilterParameters, - $persistedFilterParameters, - $defaultFilterParameters - ), - ]; - - /* - * We need to force the sort order when the order by - * is set to position_ordering - */ - if ('position_ordering' === $filters['orderBy']) { - $filters['sortOrder'] = 'asc'; - } - - return $filters; - } - - /** - * @param string $parameterName - * @param array $queryFilterParameters - * @param array $persistedFilterParameters - * @param array $defaultFilterParameters - * - * @return string|int - * - * @throws ProductException - */ - private function getParameter( - $parameterName, - array $queryFilterParameters, - array $persistedFilterParameters, - array $defaultFilterParameters - ) { - if (isset($queryFilterParameters[$parameterName])) { - $value = $queryFilterParameters[$parameterName]; - } elseif (isset($persistedFilterParameters[$parameterName])) { - $value = $persistedFilterParameters[$parameterName]; - } elseif (isset($defaultFilterParameters[$parameterName])) { - $value = $defaultFilterParameters[$parameterName]; - } else { - throw new ProductException('Could not find the parameter %s', 'Admin.Notifications.Error', [$parameterName]); - } - - if ($value === 'last' && isset($persistedFilterParameters['last_' . $parameterName])) { - $value = $persistedFilterParameters['last_' . $parameterName]; - } - - return $value; - } -} diff --git a/src/Adapter/Product/ProductDataProvider.php b/src/Adapter/Product/ProductDataProvider.php deleted file mode 100644 index 7a08afb17cf77..0000000000000 --- a/src/Adapter/Product/ProductDataProvider.php +++ /dev/null @@ -1,167 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Product; - -use Context; -use Image; -use Product; -use StockAvailable; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This class will provide data from DB / ORM about Product, for both Front and Admin interfaces. - */ -class ProductDataProvider -{ - /** - * Get a new ProductCore instance. - * - * @param int|null $idProduct - * - * @return Product - */ - public function getProductInstance($idProduct = null) - { - if ($idProduct) { - return new Product($idProduct); - } - - return new Product(); - } - - /** - * Get a product. - * - * @param int $id_product - * @param bool $full - * @param int|null $id_lang - * @param int|null $id_shop - * @param object|null $context - * - * @throws \LogicException If the product id is not set - * - * @return Product $product - */ - public function getProduct($id_product, $full = false, $id_lang = null, $id_shop = null, $context = null) - { - if (!$id_product) { - throw new \LogicException('You need to provide a product id', 5002); - } - - $product = new Product($id_product, $full, $id_lang, $id_shop, $context); - - if (!is_array($product->link_rewrite)) { - $linkRewrite = $product->link_rewrite; - } else { - $linkRewrite = $product->link_rewrite[$id_lang ? $id_lang : key($product->link_rewrite)]; - } - - $cover = Product::getCover($product->id); - $product->image = Context::getContext()->link->getImageLink($linkRewrite, $cover ? $cover['id_image'] : '', 'home_default'); - - return $product; - } - - /** - * Get default taxe rate product. - * - * @return int id tax rule group - */ - public function getIdTaxRulesGroup() - { - $product = new Product(); - - return $product->getIdTaxRulesGroup(); - } - - /** - * Get product quantity. - * - * @param int $id_product - * @param int|null $id_product_attribute - * @param bool|null $cache_is_pack - * - * @return int stock - */ - public function getQuantity($id_product, $id_product_attribute = null, $cache_is_pack = null) - { - return Product::getQuantity($id_product, $id_product_attribute, $cache_is_pack); - } - - /** - * @param int $id_product - * @param int $id_product_attribute Optional - * - * @return string - */ - public function getLocation($id_product, $id_product_attribute = 0) - { - return StockAvailable::getLocation($id_product, $id_product_attribute); - } - - /** - * Get associated images to product. - * - * @param int $id_product - * @param int $id_lang - * - * @return array - */ - public function getImages($id_product, $id_lang) - { - $id_shop = (int) Context::getContext()->shop->id; - $data = []; - foreach (Image::getImages($id_lang, $id_product, null, $id_shop) as $image) { - $data[] = $this->getImage($image['id_image']); - } - - return $data; - } - - /** - * Get an image. - * - * @param int $id_image - * - * @return array() - */ - public function getImage($id_image) - { - $imageData = new Image((int) $id_image); - - return [ - 'id' => $imageData->id, - 'id_product' => $imageData->id_product, - 'position' => $imageData->position, - 'cover' => $imageData->cover ? true : false, - 'legend' => $imageData->legend, - 'format' => $imageData->image_format, - 'base_image_url' => _THEME_PROD_DIR_ . $imageData->getImgPath(), - ]; - } -} diff --git a/src/Adapter/StockManager.php b/src/Adapter/StockManager.php index 889f493d4b8ab..52bdf0731572a 100644 --- a/src/Adapter/StockManager.php +++ b/src/Adapter/StockManager.php @@ -28,7 +28,6 @@ use Db; use PrestaShop\PrestaShop\Adapter\Shop\Context as ShopAdapter; -use PrestaShopBundle\Service\DataProvider\StockInterface; use StockAvailable; /** @@ -36,7 +35,7 @@ * * This class will provide data from DB / ORM about Product stocks. */ -class StockManager implements StockInterface +class StockManager { /** * Gets available stock for a given product / combination / shop. @@ -117,7 +116,7 @@ public function updatePhysicalProductQuantity($shopId, $errorState, $cancellatio SELECT product_id FROM {table_prefix}order_detail WHERE id_order = ' . (int) $idOrder . ' - ) od + ) od ON sa.id_product = od.product_id '; } diff --git a/src/Adapter/Supplier/SupplierDataProvider.php b/src/Adapter/Supplier/SupplierDataProvider.php deleted file mode 100644 index 6f689b228fff2..0000000000000 --- a/src/Adapter/Supplier/SupplierDataProvider.php +++ /dev/null @@ -1,96 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Supplier; - -use ProductSupplier; -use Supplier; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This class will provide data from DB / ORM about Supplier. - */ -class SupplierDataProvider -{ - /** - * Get all suppliers. - * - * @param bool $get_nb_products - * @param int $id_lang - * @param bool $active - * @param bool $p - * @param bool $n - * @param bool $all_groups - * - * @return array Suppliers - */ - public function getSuppliers($get_nb_products = false, $id_lang = 0, $active = true, $p = false, $n = false, $all_groups = false) - { - return Supplier::getSuppliers($get_nb_products, $id_lang, $active, $p, $n, $all_groups); - } - - /** - * Get product suppliers. - * - * @param int $id_product - * @param bool $group_by_supplier - * - * @return array Suppliers - */ - public function getProductSuppliers($id_product, $group_by_supplier = true) - { - $suppliersCollection = ProductSupplier::getSupplierCollection($id_product, $group_by_supplier); - - return $suppliersCollection->getResults(); - } - - /** - * For a given product and supplier, gets the product supplier data. - * - * @param int $id_product - * @param int $id_product_attribute - * @param int $id_supplier - * - * @return array - */ - public function getProductSupplierData($id_product, $id_product_attribute, $id_supplier) - { - return ProductSupplier::getProductSupplierData($id_product, $id_product_attribute, $id_supplier); - } - - /** - * Get supplier name by id. - * - * @param int $id_supplier - * - * @return string - */ - public function getNameById($id_supplier) - { - return Supplier::getNameById($id_supplier); - } -} diff --git a/src/Adapter/Tax/TaxRuleDataProvider.php b/src/Adapter/Tax/TaxRuleDataProvider.php deleted file mode 100644 index 2fd3121c6750e..0000000000000 --- a/src/Adapter/Tax/TaxRuleDataProvider.php +++ /dev/null @@ -1,132 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Adapter\Tax; - -use Address; -use Context; -use Product; -use Tax; -use TaxManagerFactory; -use TaxRulesGroup; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This class will provide data from DB / ORM about tax rules. - */ -class TaxRuleDataProvider -{ - /** - * Get all Tax Rules Groups. - * - * @param bool $only_active - * - * @return array TaxRulesGroup - */ - public function getTaxRulesGroups($only_active = true) - { - return TaxRulesGroup::getTaxRulesGroups($only_active); - } - - /** - * Get most used Tax. - * - * @return int - */ - public function getIdTaxRulesGroupMostUsed() - { - return (int) Product::getIdTaxRulesGroupMostUsed(); - } - - /** - * Get all Tax Rules Groups with rates. - * - * @return array TaxRulesGroup - */ - public function getTaxRulesGroupWithRates() - { - $address = new Address(); - $address->id_country = (int) Context::getContext()->country->id; - $tax_rules_groups = $this->getTaxRulesGroups(); - $tax_rates = [ - 0 => [ - 'id_tax_rules_group' => 0, - 'rates' => [0], - 'computation_method' => 0, - ], - ]; - - foreach ($tax_rules_groups as $tax_rules_group) { - $id_tax_rules_group = (int) $tax_rules_group['id_tax_rules_group']; - $tax_calculator = TaxManagerFactory::getManager($address, $id_tax_rules_group)->getTaxCalculator(); - $tax_rates[$id_tax_rules_group] = [ - 'id_tax_rules_group' => $id_tax_rules_group, - 'rates' => [], - 'computation_method' => (int) $tax_calculator->computation_method, - ]; - - if (!empty($tax_calculator->taxes)) { - foreach ($tax_calculator->taxes as $tax) { - $tax_rates[$id_tax_rules_group]['rates'][] = (float) $tax->rate; - } - } else { - $tax_rates[$id_tax_rules_group]['rates'][] = 0; - } - } - - return $tax_rates; - } - - /** - * Get product eco taxe rate. - * - * @return float tax - */ - public function getProductEcotaxRate() - { - return Tax::getProductEcotaxRate(); - } - - /** - * Gets a list of tax rules groups for choice type. - * - * @param bool $onlyActive if true, returns only active tax rules groups - * - * @return array - */ - public function getTaxRulesGroupChoices($onlyActive = true) - { - $taxRulesGroups = $this->getTaxRulesGroups($onlyActive); - $choices = []; - - foreach ($taxRulesGroups as $taxRulesGroup) { - $choices[$taxRulesGroup['name']] = (int) $taxRulesGroup['id_tax_rules_group']; - } - - return $choices; - } -} diff --git a/src/Core/FeatureFlag/FeatureFlagSettings.php b/src/Core/FeatureFlag/FeatureFlagSettings.php index f322b817493e1..f3999ae6db796 100644 --- a/src/Core/FeatureFlag/FeatureFlagSettings.php +++ b/src/Core/FeatureFlag/FeatureFlagSettings.php @@ -50,16 +50,6 @@ class FeatureFlagSettings */ public const PREFIX = 'PS_FF_'; - /** - * @deprecated Deprecated in 8.1 will be removed in next major. - */ - public const FEATURE_FLAG_PRODUCT_PAGE_V2 = 'product_page_v2'; - - /** - * @deprecated Deprecated in 8.1 will be removed in next major. - */ - public const FEATURE_FLAG_PRODUCT_PAGE_V2_MULTI_SHOP = 'product_page_v2_multi_shop'; - public const FEATURE_FLAG_AUTHORIZATION_SERVER = 'authorization_server'; public const FEATURE_FLAG_MULTIPLE_IMAGE_FORMAT = 'multiple_image_format'; public const FEATURE_FLAG_SYMFONY_LAYOUT = 'symfony_layout'; diff --git a/src/Core/Grid/Definition/Factory/Monitoring/AbstractProductGridDefinitionFactory.php b/src/Core/Grid/Definition/Factory/Monitoring/AbstractProductGridDefinitionFactory.php index e715e7d4351fe..2892b2bb47a20 100644 --- a/src/Core/Grid/Definition/Factory/Monitoring/AbstractProductGridDefinitionFactory.php +++ b/src/Core/Grid/Definition/Factory/Monitoring/AbstractProductGridDefinitionFactory.php @@ -106,7 +106,7 @@ protected function getColumns() ->setOptions([ 'field' => 'active', 'primary_field' => 'id_product', - 'route' => 'admin_product_toggle_status', + 'route' => 'admin_products_toggle_status_for_all_shops', 'route_param_name' => 'productId', ]) ) @@ -199,18 +199,17 @@ protected function getRowActions() ->setName($this->trans('Edit', [], 'Admin.Actions')) ->setIcon('edit') ->setOptions([ - 'route' => 'admin_product_form', - 'route_param_name' => 'id', + 'route' => 'admin_products_edit', + 'route_param_name' => 'productId', 'route_param_field' => 'id_product', ]) ) ->add( $this->buildDeleteAction( - 'admin_product_unit_action', - 'id', + 'admin_products_delete_from_all_shops', + 'productId', 'id_product', Request::METHOD_POST, - ['action' => 'delete'] ) ); } diff --git a/src/Core/Product/ProductCsvExporter.php b/src/Core/Product/ProductCsvExporter.php deleted file mode 100644 index 16d1724a0ffe8..0000000000000 --- a/src/Core/Product/ProductCsvExporter.php +++ /dev/null @@ -1,115 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Core\Product; - -use PrestaShopBundle\Component\CsvResponse; -use PrestaShopBundle\Service\DataProvider\Admin\ProductInterface as ProductDataProviderInterface; -use Symfony\Component\Translation\Exception\InvalidArgumentException; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Used to export list of Products in CSV in the Product list page. - * For internal use only. - */ -final class ProductCsvExporter implements ProductExporterInterface -{ - /** - * @var TranslatorInterface - */ - private $translator; - - /** - * @var ProductDataProviderInterface - */ - private $productProvider; - - public function __construct(TranslatorInterface $translator, ProductDataProviderInterface $productProvider) - { - $this->translator = $translator; - $this->productProvider = $productProvider; - } - - /** - * In this specific case, we don't need to pass a products list. - * - * @param array $products - * - * @return CsvResponse - * - * @throws \InvalidArgumentException - * @throws \Symfony\Component\Translation\Exception\InvalidArgumentException - */ - public function export(array $products = []) - { - $productProvider = $this->productProvider; - $persistedFilterParameters = $productProvider->getPersistedFilterParameters(); - $orderBy = $persistedFilterParameters['last_orderBy']; - $sortOrder = $persistedFilterParameters['last_sortOrder']; - - // prepare callback to fetch data from DB - $dataCallback = function ($offset, $limit) use ($productProvider, $orderBy, $sortOrder) { - return $productProvider->getCatalogProductList($offset, $limit, $orderBy, $sortOrder, [], true, false); - }; - - $headersData = [ - 'id_product' => 'Product ID', - 'image_link' => $this->trans('Image', 'Admin.Global'), - 'name' => $this->trans('Name', 'Admin.Global'), - 'reference' => $this->trans('Reference', 'Admin.Global'), - 'name_category' => $this->trans('Category', 'Admin.Global'), - 'price' => $this->trans('Price (tax excl.)', 'Admin.Catalog.Feature'), - 'price_final' => $this->trans('Price (tax incl.)', 'Admin.Catalog.Feature'), - 'sav_quantity' => $this->trans('Quantity', 'Admin.Global'), - 'badge_danger' => $this->trans('Status', 'Admin.Global'), - 'position' => $this->trans('Position', 'Admin.Global'), - ]; - - return (new CsvResponse()) - ->setData($dataCallback) - ->setHeadersData($headersData) - ->setModeType(CsvResponse::MODE_OFFSET) - ->setLimit(5000) - ->setFileName('product_' . date('Y-m-d_His') . '.csv'); - } - - /** - * Translator helper. - * - * @param string $key - * @param string $domain - * - * @return string - * - * @throws InvalidArgumentException - */ - private function trans($key, $domain) - { - return $this->translator->trans($key, [], $domain); - } -} diff --git a/src/Core/Product/ProductExporterInterface.php b/src/Core/Product/ProductExporterInterface.php deleted file mode 100644 index e4eab915a7fd2..0000000000000 --- a/src/Core/Product/ProductExporterInterface.php +++ /dev/null @@ -1,44 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Core\Product; - -use Symfony\Component\HttpFoundation\Response; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Allow the export of a list of products. - */ -interface ProductExporterInterface -{ - /** - * @param array $products - * - * @return Response - */ - public function export(array $products); -} diff --git a/src/Core/Product/ProductInterface.php b/src/Core/Product/ProductInterface.php deleted file mode 100644 index 45e2612be34d4..0000000000000 --- a/src/Core/Product/ProductInterface.php +++ /dev/null @@ -1,47 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShop\PrestaShop\Core\Product; - -use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\RedirectType; - -/** - * @deprecated - * @see RedirectType instead - */ -interface ProductInterface -{ - public const REDIRECT_TYPE_CATEGORY_MOVED_PERMANENTLY = RedirectType::TYPE_CATEGORY_PERMANENT; - public const REDIRECT_TYPE_CATEGORY_FOUND = RedirectType::TYPE_CATEGORY_TEMPORARY; - public const REDIRECT_TYPE_PRODUCT_MOVED_PERMANENTLY = RedirectType::TYPE_PRODUCT_PERMANENT; - public const REDIRECT_TYPE_PRODUCT_FOUND = RedirectType::TYPE_PRODUCT_TEMPORARY; - public const REDIRECT_TYPE_NOT_FOUND = RedirectType::TYPE_NOT_FOUND; - public const REDIRECT_TYPE_GONE = RedirectType::TYPE_GONE; - public const REDIRECT_TYPE_DEFAULT = RedirectType::TYPE_DEFAULT; - public const REDIRECT_TYPE_NOT_FOUND_DISPLAYED = RedirectType::TYPE_NOT_FOUND_DISPLAYED; - public const REDIRECT_TYPE_GONE_DISPLAYED = RedirectType::TYPE_GONE_DISPLAYED; - public const REDIRECT_TYPE_SUCCESS_DISPLAYED = RedirectType::TYPE_SUCCESS_DISPLAYED; -} diff --git a/src/PrestaShopBundle/Command/LegacyLinkLinterCommand.php b/src/PrestaShopBundle/Command/LegacyLinkLinterCommand.php index 1eb048af045f2..6fc09d7f84ef8 100644 --- a/src/PrestaShopBundle/Command/LegacyLinkLinterCommand.php +++ b/src/PrestaShopBundle/Command/LegacyLinkLinterCommand.php @@ -57,40 +57,10 @@ class LegacyLinkLinterCommand extends Command 'admin_common_sidebar', 'admin_common_reset_search', 'admin_common_reset_search_by_filter_id', - 'admin_product_new', 'admin_product_form', - 'admin_product_virtual_save_action', - 'admin_product_virtual_remove_file_action', - 'admin_product_virtual_download_file_action', - 'admin_product_virtual_remove_action', - 'admin_product_attachement_add_action', - 'admin_product_image_upload', - 'admin_product_image_positions', - 'admin_product_image_form', - 'admin_product_image_delete', - 'admin_get_product_combinations', 'admin_product_catalog', - 'admin_product_catalog_filters', - 'admin_product_list', - 'admin_product_bulk_action', - 'admin_product_unit_action', - 'admin_product_mass_edit_action', - 'admin_product_export_action', 'admin_security_compromised', - 'admin_attribute_get_all', - 'admin_delete_attribute', - 'admin_delete_all_attributes', - 'admin_get_form_images_combination', - 'admin_category_simple_add_form', - 'admin_get_ajax_categories', - 'admin_combination_generate_form', 'admin_feature_get_feature_values', - 'admin_specific_price_list', - 'admin_get_specific_price_update_form', - 'admin_specific_price_add', - 'admin_specific_price_update', - 'admin_delete_specific_price', - 'admin_supplier_refresh_product_supplier_combination_form', 'admin_warehouse_refresh_product_warehouse_combination_form', 'admin_products_combinations', 'admin_products_combinations_ids', @@ -126,6 +96,7 @@ class LegacyLinkLinterCommand extends Command 'admin_categories_get_categories_tree', 'admin_catalog_price_rules_list_for_product', 'admin_features_horizontal_index', + 'admin_products_toggle_status_for_shop', ]; public function __construct(LegacyLinkLinter $legacyLinkLinter, AdminRouteProvider $adminRouteProvider) diff --git a/src/PrestaShopBundle/Command/SecurityAnnotationLinterCommand.php b/src/PrestaShopBundle/Command/SecurityAnnotationLinterCommand.php index 5c86ade93163e..05993de3c5aeb 100644 --- a/src/PrestaShopBundle/Command/SecurityAnnotationLinterCommand.php +++ b/src/PrestaShopBundle/Command/SecurityAnnotationLinterCommand.php @@ -66,11 +66,6 @@ final class SecurityAnnotationLinterCommand extends Command 'admin_common_reset_search', 'admin_common_reset_search_by_filter_id', 'admin_security_compromised', - 'admin_get_ajax_categories', - 'admin_product_list', // Back-office product page v1 has its own security system - 'admin_product_bulk_action', // Back-office product page v1 has its own security system - 'admin_product_unit_action', // Back-office product page v1 has its own security system - 'admin_product_mass_edit_action', // Back-office product page v1 has its own security system 'admin_import_data_configuration_index_redirect', 'admin_country_states', 'admin_mail_theme_save_configuration_deprecated', // Deprecated diff --git a/src/PrestaShopBundle/Controller/Admin/AttachementProductController.php b/src/PrestaShopBundle/Controller/Admin/AttachementProductController.php deleted file mode 100644 index 2cef2a0de6a59..0000000000000 --- a/src/PrestaShopBundle/Controller/Admin/AttachementProductController.php +++ /dev/null @@ -1,87 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Controller\Admin; - -use PrestaShop\PrestaShop\Adapter\Product\AdminProductWrapper; -use PrestaShopBundle\Security\Annotation\AdminSecurity; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller for product attachments (in /product/form page). - */ -class AttachementProductController extends FrameworkBundleAdminController -{ - /** - * Manage form add product attachment. - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) && is_granted('update', request.get('_legacy_controller'))") - * - * @param int $idProduct - * @param Request $request - * - * @return JsonResponse - */ - public function addAction($idProduct, Request $request) - { - $response = new JsonResponse(); - $legacyContext = $this->get('prestashop.adapter.legacy.context'); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - - //get product - $product = $productAdapter->getProduct((int) $idProduct); - - if (!$product || !$request->isXmlHttpRequest()) { - return $response; - } - - $form = $this->createForm( - 'PrestaShopBundle\Form\Admin\Product\ProductAttachement', - null, - ['csrf_protection' => false] - ); - - $form->handleRequest($request); - - if ($form->isValid()) { - $data = $form->getData(); - $res = $adminProductWrapper->processAddAttachment($product, $data, $legacyContext->getLanguages()); - if ($res) { - $res->real_name = $data['name']; - $response->setData($res); - } - } else { - $response->setStatusCode(400); - $response->setData($this->getFormErrorsForJS($form)); - } - - return $response; - } -} diff --git a/src/PrestaShopBundle/Controller/Admin/AttributeController.php b/src/PrestaShopBundle/Controller/Admin/AttributeController.php deleted file mode 100644 index 4faa6ea95e522..0000000000000 --- a/src/PrestaShopBundle/Controller/Admin/AttributeController.php +++ /dev/null @@ -1,339 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Controller\Admin; - -use PrestaShop\PrestaShop\Adapter\Attribute\AdminAttributeGeneratorControllerWrapper; -use PrestaShopBundle\Security\Annotation\AdminSecurity; -use Product; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller for the attribute / attribute group. - */ -class AttributeController extends FrameworkBundleAdminController -{ - /** - * get All Attributes as json. - * - * @AdminSecurity("is_granted('read', request.get('_legacy_controller'))") - * - * @return JsonResponse - */ - public function getAllAttributesAction() - { - $response = new JsonResponse(); - $locales = $this->get('prestashop.adapter.legacy.context')->getLanguages(); - $attributes = $this->get('prestashop.adapter.data_provider.attribute')->getAttributes($locales[0]['id_lang'], true); - - $dataGroupAttributes = []; - $data = []; - foreach ($attributes as $attribute) { - /* Construct attribute group selector. Ex : Color : All */ - $dataGroupAttributes[$attribute['id_attribute_group']] = [ - 'value' => 'group-' . $attribute['id_attribute_group'], - 'label' => $attribute['public_name'] . ' : ' . $this->trans('All', 'Admin.Global'), - 'data' => [ - 'id_group' => $attribute['id_attribute_group'], - 'name' => $attribute['public_name'], - ], - ]; - - $data[] = [ - 'value' => $attribute['id_attribute'], - 'label' => $attribute['public_name'] . ' : ' . $attribute['name'], - 'data' => [ - 'id_group' => $attribute['id_attribute_group'], - 'name' => $attribute['name'], - ], - ]; - } - - $data = array_merge($dataGroupAttributes, $data); - - $response->setData($data); - - return $response; - } - - /** - * Attributes generator. - * - * @AdminSecurity( - * "is_granted('create', request.get('_legacy_controller')) && is_granted('update', request.get('_legacy_controller'))" - * ) - * - * @param Request $request The request - * - * @return JsonResponse - */ - public function attributesGeneratorAction(Request $request) - { - $response = new JsonResponse(); - $locales = $this->get('prestashop.adapter.legacy.context')->getLanguages(); - $options = $request->get('options'); - $idProduct = isset($request->get('form')['id_product']) ? $request->get('form')['id_product'] : null; - - //get product - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - /** @var Product $product */ - $product = $productAdapter->getProduct((int) $idProduct); - - if (!is_object($product) || empty($product->id) || empty($options) || !is_array($options)) { - $response->setStatusCode(400); - - return $response; - } - - //store exisiting product combinations - $existingCombinationsIds = array_map(function ($o) { - return $o['id_product_attribute']; - }, $product->getAttributeCombinations(1, false)); - - //get clean attributes ids - $newOptions = []; - foreach ($options as $idGroup => $attributes) { - foreach ($attributes as $attribute) { - //If attribute is a group attribute, replace group data by all attributes group - if (str_contains($attribute, 'group')) { - $allGroupAttributes = $this->get('prestashop.adapter.data_provider.attribute')->getAttributeIdsByGroup((int) $idGroup, true); - foreach ($allGroupAttributes as $groupAttribute) { - $newOptions[$idGroup][$groupAttribute] = $groupAttribute; - } - } else { - $newOptions[$idGroup][$attribute] = $attribute; - } - } - } - - //create attributes - $this->get(AdminAttributeGeneratorControllerWrapper::class)->processGenerate($product, $newOptions); - - //get all product combinations - $allCombinations = $product->getAttributeCombinations(1, false); - - $allCombinationsIds = array_map(function ($o) { - return $o['id_product_attribute']; - }, $allCombinations); - - //get new created combinations Ids - $newCombinationIds = array_diff($allCombinationsIds, $existingCombinationsIds); - - $attributes = $product->sortCombinationByAttributePosition($newCombinationIds, $locales[0]['id_lang']); - $this->ensureProductHasDefaultCombination($product, $attributes); - - $combinationDataProvider = $this->get('prestashop.adapter.data_provider.combination'); - $result = [ - 'ids_product_attribute' => [], - 'form' => '', - ]; - - foreach ($attributes as $attribute) { - foreach ($attribute as $combination) { - $formCombinations = $combinationDataProvider->getFormCombinations( - [$combination['id_product_attribute']], - $this->getContext()->language->id - ); - - $form = $this->get('form.factory') - ->createNamed( - 'combination_' . $combination['id_product_attribute'], - 'PrestaShopBundle\Form\Admin\Product\ProductCombination', - $formCombinations[$combination['id_product_attribute']] - ); - $result['form'] .= $this->renderView( - '@Product/ProductPage/Forms/form_combination.html.twig', - [ - 'form' => $form->createView(), - ] - ); - $result['ids_product_attribute'][] = $combination['id_product_attribute']; - } - } - - return $response->create($result); - } - - /** - * @param Product $product - * @param array $combinations - */ - public function ensureProductHasDefaultCombination(Product $product, array $combinations) - { - if (count($combinations)) { - $defaultProductAttributeId = $product->getDefaultIdProductAttribute(); - if (!$defaultProductAttributeId) { - /* - * Combinations indexed by position, then attribute id - * ex: $combinations = [ - * 3 => [ //4th position attribute - * 45 => [ //product_attribute id - * ] - * ] - * ] - */ - $firstPosition = array_keys($combinations)[0]; - if (!empty($combinations[$firstPosition])) { - $firstAttributeId = array_keys($combinations[$firstPosition])[0]; - $product->setDefaultAttribute($firstAttributeId); - } - } - } - } - - /** - * Delete a product attribute. - * - * @AdminSecurity("is_granted('delete', request.get('_legacy_controller'))") - * - * @param int $idProduct The product ID - * @param Request $request The request - * - * @return JsonResponse - */ - public function deleteAttributeAction($idProduct, Request $request) - { - $response = new JsonResponse(); - - if (!$request->isXmlHttpRequest()) { - return $response; - } - - $legacyResponse = false; - - if ($request->request->has('attribute-ids')) { - $attributeIds = $request->request->all('attribute-ids'); - foreach ($attributeIds as $attributeId) { - $legacyResponse = $this->get(AdminAttributeGeneratorControllerWrapper::class) - ->ajaxProcessDeleteProductAttribute($attributeId, $idProduct); - } - - if ($legacyResponse['status'] == 'error') { - $response->setStatusCode(400); - } - - $response->setData(['message' => $legacyResponse['message']]); - } - - return $response; - } - - /** - * Delete all product attributes. - * - * @AdminSecurity("is_granted('delete', request.get('_legacy_controller'))") - * - * @param int $idProduct The product ID - * @param Request $request The request - * - * @return JsonResponse - */ - public function deleteAllAttributeAction($idProduct, Request $request) - { - $attributeAdapter = $this->get('prestashop.adapter.data_provider.attribute'); - $response = new JsonResponse(); - - //get all attribute for a product - $combinations = $attributeAdapter->getProductCombinations($idProduct); - - if (!$combinations || !$request->isXmlHttpRequest()) { - return $response; - } - - $res = false; - - foreach ($combinations as $combination) { - $res = $this->get(AdminAttributeGeneratorControllerWrapper::class) - ->ajaxProcessDeleteProductAttribute($combination['id_product_attribute'], $idProduct); - - if ($res['status'] == 'error') { - $response->setStatusCode(400); - - break; - } - } - - $response->setData(['message' => $res['message']]); - - return $response; - } - - /** - * get the images form for a product combinations. - * - * @AdminSecurity("is_granted('read', request.get('_legacy_controller'))") - * - * @param int $idProduct The product id - * @param Request $request The request - * - * @return JsonResponse - */ - public function getFormImagesAction($idProduct, Request $request) - { - $response = new JsonResponse(); - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - $attributeAdapter = $this->get('prestashop.adapter.data_provider.attribute'); - $locales = $this->get('prestashop.adapter.legacy.context')->getLanguages(); - - //get product - $product = $productAdapter->getProduct((int) $idProduct); - - //get product images - $productImages = $productAdapter->getImages($idProduct, $locales[0]['id_lang']); - - if (!$request->isXmlHttpRequest() || !is_object($product) || empty($product->id)) { - $response->setStatusCode(400); - - return $response; - } - - $data = []; - $combinations = $attributeAdapter->getProductCombinations($idProduct); - foreach ($combinations as $combination) { - //get combination images - $combinationImages = array_map(function ($o) { - return $o['id']; - }, $attributeAdapter->getImages($combination['id_product_attribute'])); - - $newProductImages = $productImages; - foreach ($newProductImages as $k => $image) { - $newProductImages[$k]['id_image_attr'] = false; - if (in_array($image['id'], $combinationImages)) { - $newProductImages[$k]['id_image_attr'] = true; - } - } - - $data[$combination['id_product_attribute']] = $newProductImages; - } - - $response->setData($data); - - return $response; - } -} diff --git a/src/PrestaShopBundle/Controller/Admin/CategoryController.php b/src/PrestaShopBundle/Controller/Admin/CategoryController.php deleted file mode 100644 index 124c3003ee86c..0000000000000 --- a/src/PrestaShopBundle/Controller/Admin/CategoryController.php +++ /dev/null @@ -1,166 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Controller\Admin; - -use PrestaShop\PrestaShop\Adapter\Tools; -use PrestaShop\PrestaShop\Core\Domain\Category\Command\AddCategoryCommand; -use PrestaShop\PrestaShop\Core\Domain\Category\Exception\CategoryException; -use PrestaShop\PrestaShop\Core\Domain\Category\ValueObject\CategoryId; -use PrestaShop\PrestaShop\Core\Domain\Product\Command\AssignProductToCategoryCommand; -use PrestaShop\PrestaShop\Core\Domain\Product\Exception\CannotAssignProductToCategoryException; -use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductException; -use PrestaShopBundle\Form\Admin\Category\SimpleCategory; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller for the Category pages. - */ -class CategoryController extends FrameworkBundleAdminController -{ - /** - * Process Ajax Form to add a simple category (name and parent category). - * - * @param Request $request - * - * @return string - */ - public function addSimpleCategoryFormAction(Request $request) - { - $response = new JsonResponse(); - $commandBus = $this->getCommandBus(); - $tools = $this->get(Tools::class); - $shopContext = $this->get('prestashop.adapter.shop.context'); - $shopList = $shopContext->getShops(false, true); - $currentIdShop = $shopContext->getContextShopID(); - $defaultLanguageId = $this->getConfiguration()->getInt('PS_LANG_DEFAULT'); - - $form = $this->createFormBuilder() - ->add('category', SimpleCategory::class) - ->getForm(); - - $form->handleRequest($request); - - if ($form->isValid()) { - $data = $form->getData(); - - $localizedName = [ - $defaultLanguageId => $data['category']['name'], - ]; - - $command = new AddCategoryCommand( - $localizedName, - [$defaultLanguageId => $tools->linkRewrite($data['category']['name'])], - true, - (int) $data['category']['id_parent'] - ); - - $command->setAssociatedShopIds($currentIdShop ? [$currentIdShop => $currentIdShop] : $shopList); - - try { - /** @var CategoryId $categoryId */ - $categoryId = $commandBus->handle($command); - - if ($categoryId->getValue()) { - $response->setData( - [ - 'category' => [ - 'id' => $categoryId->getValue(), - 'id_parent' => $data['category']['id_parent'], - 'name' => $localizedName, - ], - ] - ); - if ($request->query->has('id_product')) { - $assignProductToCategoryCommand = new AssignProductToCategoryCommand( - $categoryId->getValue(), - $request->query->getInt('id_product') - ); - $commandBus->handle($assignProductToCategoryCommand); - } - } - } catch (CategoryException $e) { - // TODO: do some frontend work to display this error message from ajax query - $response->setStatusCode(Response::HTTP_BAD_REQUEST); - $response->setData(['error' => $this->getErrorMessageForException($e, $this->getErrorMessages($data['category']['name']))]); - } catch (ProductException $e) { - // TODO: do some frontend work to display this error message from ajax query - $response->setStatusCode(Response::HTTP_BAD_REQUEST); - $response->setData(['error' => $this->getErrorMessageForException($e, $this->getErrorMessages($data['category']['name']))]); - } - } else { - $response->setStatusCode(Response::HTTP_BAD_REQUEST); - $response->setData($this->getFormErrorsForJS($form)); - } - - return $response; - } - - /** - * Get Categories formatted like ajax_product_file.php. - * - * @param int $limit - * @param Request $request - * - * @return JsonResponse - */ - public function getAjaxCategoriesAction($limit, Request $request) - { - if (!$request->isXmlHttpRequest()) { - throw new NotFoundHttpException('Should be ajax request.'); - } - - return new JsonResponse( - $this->get('prestashop.adapter.data_provider.category')->getAjaxCategories($request->get('query'), $limit, true) - ); - } - - /** - * @param string $categoryName - * - * @return array - */ - private function getErrorMessages(string $categoryName): array - { - return [ - CategoryException::class => $this->trans( - 'Category "%s" could not be created.', - 'Admin.Notifications.Error', - [$categoryName] - ), - CannotAssignProductToCategoryException::class => $this->trans( - 'This product could not be assigned to category "%s".', - 'Admin.Notifications.Error', - [$categoryName] - ), - ]; - } -} diff --git a/src/PrestaShopBundle/Controller/Admin/CombinationController.php b/src/PrestaShopBundle/Controller/Admin/CombinationController.php deleted file mode 100644 index 7e7bf01fcc66c..0000000000000 --- a/src/PrestaShopBundle/Controller/Admin/CombinationController.php +++ /dev/null @@ -1,110 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Controller\Admin; - -use PrestaShopBundle\Form\Admin\Product\ProductCombination; -use PrestaShopBundle\Security\Annotation\AdminSecurity; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Response; - -/** - * @deprecated since 8.1 and will be removed in next major. - */ -class CombinationController extends FrameworkBundleAdminController -{ - /** - * Generate combination - * - * @AdminSecurity( - * "is_granted('create', 'ADMINPRODUCTS_') && is_granted('update', 'ADMINPRODUCTS_')" - * ) - * - * @return Response - */ - public function generateCombinationFormAction($combinationIds) - { - $response = new Response(); - - $combinationIds = explode('-', $combinationIds); - - $combinationDataProvider = $this->get('prestashop.adapter.data_provider.combination'); - $combinations = $combinationDataProvider->getFormCombinations($combinationIds, (int) $this->getContext()->language->id); - - $formFactory = $this->get('form.factory'); - $forms = []; - foreach ($combinations as $combinationId => $combination) { - $forms[] = $formFactory->createNamed( - "combination_$combinationId", - ProductCombination::class, - $combination - )->createView(); - } - - return $response->setContent($this->renderView( - '@Product/ProductPage/Forms/form_combination_collection.html.twig', - [ - 'combinationForms' => $forms, - ] - )); - } - - /** - * Get all Combinations for a product. - * - * @AdminSecurity("is_granted('read', 'ADMINPRODUCTS_')") - * - * @param int $idProduct The product id - * - * @return JsonResponse - */ - public function getProductCombinationsAction($idProduct) - { - $response = new JsonResponse(); - - //get product - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - $product = $productAdapter->getProduct((int) $idProduct); - - //get combinations - - $modelMapper = $this->get('prestashop.adapter.admin.model.product'); - - $combinations = $modelMapper->getAttributesResume($product); - - $combinationList = []; - - if (is_array($combinations)) { - foreach ($combinations as $combination) { - $combinationList[] = ['id' => $combination['id_product_attribute'], 'name' => $combination['attribute_designation']]; - } - } - - $response->setData($combinationList); - - return $response; - } -} diff --git a/src/PrestaShopBundle/Controller/Admin/Configure/AdvancedParameters/BackupController.php b/src/PrestaShopBundle/Controller/Admin/Configure/AdvancedParameters/BackupController.php index 95eb4f2bfe166..e210fff116508 100644 --- a/src/PrestaShopBundle/Controller/Admin/Configure/AdvancedParameters/BackupController.php +++ b/src/PrestaShopBundle/Controller/Admin/Configure/AdvancedParameters/BackupController.php @@ -51,7 +51,7 @@ class BackupController extends FrameworkBundleAdminController * * @AdminSecurity("is_granted('read', request.get('_legacy_controller'))", * message="You do not have permission to update this.", - * redirectRoute="admin_product_catalog" + * redirectRoute="admin_products_index" * ) * * @param Request $request diff --git a/src/PrestaShopBundle/Controller/Admin/FeatureController.php b/src/PrestaShopBundle/Controller/Admin/FeatureController.php deleted file mode 100644 index ad66b6ba041a0..0000000000000 --- a/src/PrestaShopBundle/Controller/Admin/FeatureController.php +++ /dev/null @@ -1,78 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Controller\Admin; - -use Symfony\Component\HttpFoundation\JsonResponse; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller for the Feature pages. - */ -class FeatureController extends FrameworkBundleAdminController -{ - /** - * Get all values for a given feature. - * - * @param int $idFeature The feature Id - * - * @return JsonResponse features list - */ - public function getFeatureValuesAction($idFeature) - { - $response = new JsonResponse(); - $locales = $this->get('prestashop.adapter.legacy.context')->getLanguages(); - $data = []; - - if ($idFeature == 0) { - return $response; - } - - $featuresValues = $this->get('prestashop.adapter.data_provider.feature')->getFeatureValuesWithLang($locales[0]['id_lang'], $idFeature); - - if (count($featuresValues) !== 0) { - $data['0'] = [ - 'id' => 0, - 'value' => $this->trans('Choose a value', 'Admin.Catalog.Feature'), - ]; - } - - foreach ($featuresValues as $featureValue) { - if (isset($featureValue['custom']) && $featureValue['custom'] == 1) { - continue; - } - $data[] = [ - 'id' => $featureValue['id_feature_value'], - 'value' => $featureValue['value'], - ]; - } - - $response->setData($data); - - return $response; - } -} diff --git a/src/PrestaShopBundle/Controller/Admin/ProductController.php b/src/PrestaShopBundle/Controller/Admin/ProductController.php deleted file mode 100644 index 45c42f4f588ba..0000000000000 --- a/src/PrestaShopBundle/Controller/Admin/ProductController.php +++ /dev/null @@ -1,1292 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Controller\Admin; - -use Category; -use Exception; -use LogicException; -use PrestaShop\PrestaShop\Adapter\Product\AdminProductWrapper; -use PrestaShop\PrestaShop\Adapter\Product\FilterCategoriesRequestPurifier; -use PrestaShop\PrestaShop\Adapter\Product\ListParametersUpdater; -use PrestaShop\PrestaShop\Adapter\Tax\TaxRuleDataProvider; -use PrestaShop\PrestaShop\Core\Domain\Product\Command\UpdateProductCommand; -use PrestaShop\PrestaShop\Core\Domain\Product\Exception\CannotUpdateProductException; -use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductConstraintException; -use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductException; -use PrestaShop\PrestaShop\Core\Domain\Product\Exception\ProductNotFoundException; -use PrestaShop\PrestaShop\Core\Domain\Product\Query\GetProductForEditing; -use PrestaShop\PrestaShop\Core\Domain\Product\QueryResult\ProductForEditing; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; -use PrestaShop\PrestaShop\Core\Hook\HookDispatcher; -use PrestaShop\PrestaShop\Core\Product\ProductCsvExporter; -use PrestaShop\PrestaShop\Core\Security\Permission; -use PrestaShopBundle\Component\CsvResponse; -use PrestaShopBundle\Entity\AdminFilter; -use PrestaShopBundle\Entity\Attribute; -use PrestaShopBundle\Entity\Repository\AttributeRepository; -use PrestaShopBundle\Exception\UpdateProductException; -use PrestaShopBundle\Form\Admin\Product\ProductCategories; -use PrestaShopBundle\Form\Admin\Product\ProductCombination; -use PrestaShopBundle\Form\Admin\Product\ProductCombinationBulk; -use PrestaShopBundle\Form\Admin\Product\ProductInformation; -use PrestaShopBundle\Form\Admin\Product\ProductOptions; -use PrestaShopBundle\Form\Admin\Product\ProductPrice; -use PrestaShopBundle\Form\Admin\Product\ProductQuantity; -use PrestaShopBundle\Form\Admin\Product\ProductSeo; -use PrestaShopBundle\Form\Admin\Product\ProductShipping; -use PrestaShopBundle\Model\Product\AdminModelAdapter; -use PrestaShopBundle\Security\Annotation\AdminSecurity; -use PrestaShopBundle\Service\DataProvider\Admin\ProductInterface as ProductInterfaceProvider; -use PrestaShopBundle\Service\DataUpdater\Admin\ProductInterface as ProductInterfaceUpdater; -use PrestaShopBundle\Service\Hook\HookFinder; -use Product; -use Psr\Log\LoggerInterface; -use Symfony\Component\Form\Extension\Core\Type\HiddenType; -use Symfony\Component\Form\Form; -use Symfony\Component\Form\FormInterface; -use Symfony\Component\HttpFoundation\File\UploadedFile; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Tools as LegacyTools; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller for the Product pages using the Symfony architecture: - * - categories - * - product list - * - product details - * - product attributes - * - ... - * - * This controller is the first one to be refactored to the new Symfony Architecture. - * The retro-compatibility is dropped for the corresponding Admin pages. - * A set of hooks are integrated and an Adapter is made to wrap the new EventDispatcher - * component to the existing hook system. So existing hooks are always triggered, but from the new - * code (and so needs to be adapted on the module side ton comply on the new parameters formats, - * the new UI style, etc...). - */ -class ProductController extends FrameworkBundleAdminController -{ - /** - * Used to validate connected user authorizations. - */ - public const PRODUCT_OBJECT = 'ADMINPRODUCTS_'; - - /** - * Get the Catalog page with KPI banner, product list, bulk actions, filters, search, etc... - * - * URL example: /product/catalog/40/20/id_product/asc - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller')) || is_granted('read', request.get('_legacy_controller'))") - * - * @param Request $request - * @param int $limit The size of the listing - * @param int $offset The offset of the listing - * @param string $orderBy To order product list - * @param string $sortOrder To order product list - * - * @return Response - * - * @throws \Symfony\Component\Translation\Exception\InvalidArgumentException - * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException - * @throws LogicException - * @throws \Symfony\Component\Routing\Exception\MissingMandatoryParametersException - * @throws \Symfony\Component\Routing\Exception\InvalidParameterException - * @throws \Symfony\Component\Form\Exception\LogicException - * @throws \Symfony\Component\Form\Exception\AlreadySubmittedException - */ - public function catalogAction( - Request $request, - $limit = 10, - $offset = 0, - $orderBy = 'id_product', - $sortOrder = 'desc' - ) { - if ($this->shouldRedirectToV2()) { - return $this->redirectToRoute('admin_products_index'); - } - - $language = $this->getContext()->language; - $request->getSession()->set('_locale', $language->locale); - $request = $this->get(FilterCategoriesRequestPurifier::class)->purify($request); - - /** @var ProductInterfaceProvider $productProvider */ - $productProvider = $this->get('prestashop.core.admin.data_provider.product_interface'); - - // Set values from persistence and replace in the request - $persistedFilterParameters = $productProvider->getPersistedFilterParameters(); - /** @var ListParametersUpdater $listParametersUpdater */ - $listParametersUpdater = $this->get(ListParametersUpdater::class); - $listParameters = $listParametersUpdater->buildListParameters( - $request->query->all(), - $persistedFilterParameters, - compact('offset', 'limit', 'orderBy', 'sortOrder') - ); - $offset = $listParameters['offset']; - $limit = $listParameters['limit']; - $orderBy = $listParameters['orderBy']; - $sortOrder = $listParameters['sortOrder']; - - //The product provider performs the same merge internally, so we do the same so that the displayed filters are - //consistent with the request ones - $combinedFilterParameters = array_replace($persistedFilterParameters, $request->request->all()); - - $toolbarButtons = $this->getToolbarButtons(); - - // Fetch product list (and cache it into view subcall to listAction) - $products = $productProvider->getCatalogProductList( - $offset, - $limit, - $orderBy, - $sortOrder, - $request->request->all() - ); - $lastSql = $productProvider->getLastCompiledSql(); - - $hasCategoryFilter = $productProvider->isCategoryFiltered(); - $hasColumnFilter = $productProvider->isColumnFiltered(); - $totalFilteredProductCount = (count($products) > 0) ? $products[0]['total'] : 0; - // Alternative layout for empty list - if ((!$hasCategoryFilter && !$hasColumnFilter && $totalFilteredProductCount === 0) - || ($totalProductCount = $productProvider->countAllProducts()) === 0 - ) { - // no filter, total filtered == 0, and then total count == 0 too. - $legacyUrlGenerator = $this->get('prestashop.core.admin.url_generator_legacy'); - - return $this->render( - '@PrestaShop/Admin/Product/CatalogPage/catalog_empty.html.twig', - [ - 'layoutHeaderToolbarBtn' => $toolbarButtons, - 'import_url' => $legacyUrlGenerator->generate('AdminImport'), - ] - ); - } - - // Pagination - $paginationParameters = $request->attributes->all(); - $paginationParameters['_route'] = 'admin_product_catalog'; - $categoriesForm = $this->createForm(ProductCategories::class); - if (!empty($combinedFilterParameters['filter_category'])) { - $categoriesForm->setData( - [ - 'categories' => [ - 'tree' => [0 => $combinedFilterParameters['filter_category']], - ], - ] - ); - } - - $cleanFilterParameters = $listParametersUpdater->cleanFiltersForPositionOrdering( - $combinedFilterParameters, - $orderBy, - $hasCategoryFilter - ); - - $permissionError = null; - if ($this->get('session')->getFlashBag()->has('permission_error')) { - $permissionError = $this->get('session')->getFlashBag()->get('permission_error')[0]; - } - - $categoriesFormView = $categoriesForm->createView(); - $selectedCategory = !empty($combinedFilterParameters['filter_category']) - ? new Category((int) $combinedFilterParameters['filter_category']) - : null; - - //Drag and drop is ONLY activated when EXPLICITLY requested by the user - //Meaning a category is selected and the user clicks on REORDER button - $activateDragAndDrop = 'position_ordering' === $orderBy && $hasCategoryFilter; - - // Template vars injection - return $this->render('@PrestaShop/Admin/Product/CatalogPage/catalog.html.twig', array_merge( - $cleanFilterParameters, - [ - 'limit' => $limit, - 'offset' => $offset, - 'orderBy' => $orderBy, - 'sortOrder' => $sortOrder, - 'has_filter' => $hasCategoryFilter || $hasColumnFilter, - 'has_category_filter' => $hasCategoryFilter, - 'selected_category' => $selectedCategory, - 'has_column_filter' => $hasColumnFilter, - 'products' => $products, - 'last_sql' => $lastSql, - 'product_count_filtered' => $totalFilteredProductCount, - 'product_count' => $totalProductCount, - 'activate_drag_and_drop' => $activateDragAndDrop, - 'pagination_parameters' => $paginationParameters, - 'layoutHeaderToolbarBtn' => $toolbarButtons, - 'categories' => $categoriesFormView, - 'pagination_limit_choices' => $productProvider->getPaginationLimitChoices(), - 'import_link' => $this->generateUrl('admin_import', ['import_type' => 'products']), - 'sql_manager_add_link' => $this->generateUrl('admin_sql_requests_create'), - 'enableSidebar' => true, - 'help_link' => $this->generateSidebarLink('AdminProducts'), - 'is_shop_context' => $this->get('prestashop.adapter.shop.context')->isShopContext(), - 'permission_error' => $permissionError, - 'layoutTitle' => $this->trans('Products', 'Admin.Navigation.Menu'), - ] - )); - } - - /** - * Get only the list of products to display on the main Admin Product page. - * The full page that shows products list will subcall this action (from catalogAction). - * URL example: /product/list/html/40/20/id_product/asc. - * - * @param Request $request - * @param int $limit The size of the listing - * @param int $offset The offset of the listing - * @param string $orderBy To order product list - * @param string $sortOrder To order product list - * @param string $view full|quicknav To change default template used to render the content - * - * @return Response - */ - public function listAction( - Request $request, - $limit = 10, - $offset = 0, - $orderBy = 'id_product', - $sortOrder = 'asc', - $view = 'full' - ): Response { - if (!$this->isGranted(Permission::READ, self::PRODUCT_OBJECT)) { - return $this->redirect('admin_dashboard'); - } - - /** @var ProductInterfaceProvider $productProvider */ - $productProvider = $this->get('prestashop.core.admin.data_provider.product_interface'); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $totalCount = 0; - - $this->get('prestashop.service.product')->cleanupOldTempProducts(); - - $products = $request->attributes->get('products', null); // get from action subcall data, if any - $lastSql = $request->attributes->get('last_sql', null); // get from action subcall data, if any - - if ($products === null) { - // get old values from persistence (before the current update) - $persistedFilterParameters = $productProvider->getPersistedFilterParameters(); - /** @var ListParametersUpdater $listParametersUpdater */ - $listParametersUpdater = $this->get(ListParametersUpdater::class); - $listParameters = $listParametersUpdater->buildListParameters( - $request->query->all(), - $persistedFilterParameters, - compact('offset', 'limit', 'orderBy', 'sortOrder') - ); - $offset = $listParameters['offset']; - $limit = $listParameters['limit']; - $orderBy = $listParameters['orderBy']; - $sortOrder = $listParameters['sortOrder']; - - /** - * 2 hooks are triggered here: - * - actionAdminProductsListingFieldsModifier - * - actionAdminProductsListingResultsModifier. - */ - $products = $productProvider->getCatalogProductList($offset, $limit, $orderBy, $sortOrder); - $lastSql = $productProvider->getLastCompiledSql(); - } - - $hasCategoryFilter = $productProvider->isCategoryFiltered(); - - // Adds controller info (URLs, etc...) to product list - foreach ($products as &$product) { - $totalCount = isset($product['total']) ? $product['total'] : $totalCount; - $product['url'] = $this->generateUrl( - 'admin_product_form', - ['id' => $product['id_product']] - ); - $product['unit_action_url'] = $this->generateUrl( - 'admin_product_unit_action', - [ - 'action' => 'duplicate', - 'id' => $product['id_product'], - ] - ); - $product['preview_url'] = $adminProductWrapper->getPreviewUrlFromId($product['id_product']); - $product['url_v2'] = $this->generateUrl('admin_products_edit', ['productId' => $product['id_product']]); - } - - //Drag and drop is ONLY activated when EXPLICITLY requested by the user - //Meaning a category is selected and the user clicks on REORDER button - $activateDragAndDrop = 'position_ordering' === $orderBy && $hasCategoryFilter; - - // Template vars injection - $vars = [ - 'activate_drag_and_drop' => $activateDragAndDrop, - 'products' => $products, - 'product_count' => $totalCount, - 'last_sql_query' => $lastSql, - 'has_category_filter' => $productProvider->isCategoryFiltered(), - 'is_shop_context' => $this->get('prestashop.adapter.shop.context')->isShopContext(), - ]; - if ($view !== 'full') { - return $this->render( - '@Product/CatalogPage/Lists/list_' . $view . '.html.twig', - array_merge( - $vars, - [ - 'limit' => $limit, - 'offset' => $offset, - 'total' => $totalCount, - ] - ) - ); - } - - return $this->render('@PrestaShop/Admin/Product/CatalogPage/Lists/list.html.twig', $vars); - } - - /** - * Gets the header toolbar buttons. - * - * @return array - */ - private function getToolbarButtons() - { - $toolbarButtons = []; - $toolbarButtons['add'] = [ - 'href' => $this->generateUrl('admin_product_new'), - 'desc' => $this->trans('Add new product', 'Admin.Actions'), - 'icon' => 'add_circle_outline', - 'help' => $this->trans('Create a new product: CTRL+P', 'Admin.Catalog.Help'), - ]; - - return $toolbarButtons; - } - - /** - * Create a new basic product - * Then return to form action. - * - * @return RedirectResponse - * - * @throws LogicException - * @throws \PrestaShopException - */ - public function newAction() - { - if (!$this->isGranted(Permission::CREATE, self::PRODUCT_OBJECT)) { - $errorMessage = $this->trans('You do not have permission to add this.', 'Admin.Notifications.Error'); - $this->get('session')->getFlashBag()->add('permission_error', $errorMessage); - - return $this->redirectToRoute('admin_product_catalog'); - } - - $productProvider = $this->get('prestashop.core.admin.data_provider.product_interface'); - $languages = $this->get('prestashop.adapter.legacy.context')->getLanguages(); - - /** @var ProductInterfaceProvider $productProvider */ - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - $productShopCategory = $this->getContext()->shop->id_category; - - /** @var Product $product */ - $product = $productAdapter->getProductInstance(); - $product->id_category_default = $productShopCategory; - - /** @var TaxRuleDataProvider $taxRuleDataProvider */ - $taxRuleDataProvider = $this->get('prestashop.adapter.data_provider.tax'); - $product->id_tax_rules_group = $taxRuleDataProvider->getIdTaxRulesGroupMostUsed(); - $product->active = $productProvider->isNewProductDefaultActivated(); - $product->state = Product::STATE_TEMP; - - //set name and link_rewrite in each lang - foreach ($languages as $lang) { - $product->name[$lang['id_lang']] = ''; - $product->link_rewrite[$lang['id_lang']] = ''; - } - - $product->save(); - $product->addToCategories([$productShopCategory]); - - return $this->redirectToRoute('admin_product_form', ['id' => $product->id]); - } - - /** - * Product form. - * - * @param int $id The product ID - * @param Request $request - * - * @return Response Template vars - * - * @throws Exception - */ - public function formAction($id, Request $request): Response - { - if ($this->shouldRedirectToV2()) { - return $this->redirectToRoute('admin_products_edit', ['productId' => $id]); - } - - gc_disable(); - - foreach ([Permission::READ, Permission::UPDATE, Permission::CREATE] as $permission) { - if (!$this->isGranted($permission, self::PRODUCT_OBJECT)) { - return $this->redirect('admin_dashboard'); - } - } - - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - try { - $product = $productAdapter->getProduct($id); - } catch (LogicException $e) { - $product = null; - } - - if (!$product || empty($product->id)) { - $this->addFlash( - 'warning', - $this->trans('The product you are trying to access doesn\'t exist.', 'Admin.Catalog.Notification') - ); - - return $this->redirectToRoute('admin_product_catalog'); - } - - $shopContext = $this->get('prestashop.adapter.shop.context'); - $legacyContextService = $this->get('prestashop.adapter.legacy.context'); - $isMultiShopContext = count($shopContext->getContextListShopID()) > 1; - - $modelMapper = $this->get('prestashop.adapter.admin.model.product'); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - - $form = $this->createProductForm($product, $modelMapper); - - $formBulkCombinations = $this->createForm( - ProductCombinationBulk::class, - null, - [ - 'iso_code' => $this - ->get('prestashop.adapter.legacy.context') - ->getContext()->currency->iso_code, - ] - ); - - // Legacy code. To fix when Object model will change. But report Hooks. - $postData = $request->request->all(); - $combinationsList = []; - if (!empty($postData)) { - foreach ($postData as $postKey => $postValue) { - if (preg_match('/^combination_.*/', $postKey)) { - $combinationsList[$postKey] = $postValue; - $postData['form'][$postKey] = $postValue; // need to validate the form - } - } - - // Duplicate Request to be a valid form (like it was real) with postData modified .. - $request = $request->duplicate( - $request->query->all(), - $postData, - $request->attributes->all(), - $request->cookies->all(), - $request->files->all(), - $request->server->all() - ); - } - - /* @var Form $form */ - $form->handleRequest($request); - $formData = $form->getData(); - $formData['step3']['combinations'] = $combinationsList; - - try { - if ($form->isSubmitted()) { - if ($this->isDemoModeEnabled() && $request->isXmlHttpRequest()) { - $errorMessage = $this->getDemoModeErrorMessage(); - - return $this->returnErrorJsonResponse( - ['error' => [$errorMessage]], - Response::HTTP_SERVICE_UNAVAILABLE - ); - } - - if ($form->isValid()) { - //define POST values for keeping legacy adminController skills - $_POST = $modelMapper->getModelData($formData, $isMultiShopContext) + $_POST; - $_POST['form'] = $formData; - $_POST['state'] = Product::STATE_SAVED; - - $adminProductController = $adminProductWrapper->getInstance(); - $adminProductController->setIdObject($formData['id_product']); - $adminProductController->setAction('save'); - - // Hooks: this will trigger legacy AdminProductController, postProcess(): - // actionAdminSaveBefore; actionAdminProductsControllerSaveBefore - // actionProductAdd or actionProductUpdate (from processSave() -> processAdd() or processUpdate()) - // actionAdminSaveAfter; actionAdminProductsControllerSaveAfter - $productSaveResult = $adminProductController->postCoreProcess(); - - if (false == $productSaveResult) { - return $this->returnErrorJsonResponse( - ['error' => $adminProductController->errors], - Response::HTTP_BAD_REQUEST - ); - } - - $product = $productSaveResult; - - /* @var Product $product */ - $adminProductController->processSuppliers($product->id); - $adminProductController->processFeatures($product->id); - $adminProductController->processSpecificPricePriorities(); - foreach ($_POST['combinations'] as $combinationValues) { - $adminProductWrapper->processProductAttribute($product, $combinationValues); - } - - // If there is no combination, then quantity and location are managed for the whole product (as combination ID 0) - // In all cases, legacy hooks are triggered: actionProductUpdate and actionUpdateQuantity - if (count($_POST['combinations']) === 0 && isset($_POST['qty_0'])) { - $adminProductWrapper->processQuantityUpdate($product, $_POST['qty_0']); - $adminProductWrapper->processLocation($product, (string) $_POST['location']); - } - // else quantities are managed from $adminProductWrapper->processProductAttribute() above. - - $adminProductWrapper->processProductOutOfStock($product, $_POST['out_of_stock']); - - $customizationFieldsIds = $adminProductWrapper - ->processProductCustomization($product, $_POST['custom_fields']); - - $adminProductWrapper->processAttachments($product, $_POST['attachments']); - - $response = new JsonResponse(); - $response->setData([ - 'product' => $product, - 'customization_fields_ids' => $customizationFieldsIds, - ]); - - if ($request->isXmlHttpRequest()) { - return $response; - } - } elseif ($request->isXmlHttpRequest()) { - return $this->returnErrorJsonResponse( - $this->getFormErrorsForJS($form), - Response::HTTP_BAD_REQUEST - ); - } - } - } catch (Exception $e) { - // this controller can be called as an AJAX JSON route or an HTML page - // so we need to return the right type of response if an exception is thrown - if ($request->isXmlHttpRequest()) { - return $this->returnErrorJsonResponse( - [], - Response::HTTP_INTERNAL_SERVER_ERROR - ); - } - - throw $e; - } - - //If context shop is define to a group shop, disable the form - if ($shopContext->isGroupShopContext()) { - return $this->render('@Product/ProductPage/disabled_form_alert.html.twig', ['showContentHeader' => false]); - } - - // languages for switch dropdown - $languages = $legacyContextService->getLanguages(); - - // generate url preview - if ($product->active) { - $preview_url = $adminProductWrapper->getPreviewUrl($product); - $preview_url_deactive = $adminProductWrapper->getPreviewUrlDeactivate($preview_url); - } else { - $preview_url_deactive = $adminProductWrapper->getPreviewUrl($product, false); - $preview_url = $adminProductWrapper->getPreviewUrlDeactivate($preview_url_deactive); - } - - $doctrine = $this->getDoctrine()->getManager(); - $language = empty($languages[0]) ? ['id_lang' => 1, 'id_shop' => 1] : $languages[0]; - /** @var AttributeRepository $attributeRepository */ - $attributeRepository = $doctrine->getRepository(Attribute::class); - $attributeGroups = $attributeRepository->findByLangAndShop((int) $language['id_lang'], (int) $language['id_shop']); - - $drawerModules = (new HookFinder())->setHookName('displayProductPageDrawer') - ->setParams(['product' => $product]) - ->addExpectedInstanceClasses('PrestaShop\PrestaShop\Core\Product\ProductAdminDrawer') - ->present(); - - return $this->render('@PrestaShop/Admin/Product/ProductPage/product.html.twig', [ - 'form' => $form->createView(), - 'formCombinations' => $formBulkCombinations->createView(), - 'categories' => $this->get('prestashop.adapter.data_provider.category')->getCategoriesWithBreadCrumb(), - 'id_product' => $id, - 'ids_product_attribute' => (isset($formData['step3']['id_product_attributes']) ? implode(',', $formData['step3']['id_product_attributes']) : ''), - 'has_combinations' => (isset($formData['step3']['id_product_attributes']) && count($formData['step3']['id_product_attributes']) > 0), - 'combinations_count' => isset($formData['step3']['id_product_attributes']) ? count($formData['step3']['id_product_attributes']) : 0, - 'is_multishop_context' => $isMultiShopContext, - 'is_combination_active' => $this->getConfiguration()->getBoolean('PS_COMBINATION_FEATURE_ACTIVE'), - 'showContentHeader' => false, - 'seo_link' => $adminProductWrapper->getPreviewUrl($product, false), - 'preview_link' => $preview_url, - 'preview_link_deactivate' => $preview_url_deactive, - 'stats_link' => $this->getAdminLink('AdminStats', ['module' => 'statsproduct', 'id_product' => $id]), - 'help_link' => $this->generateSidebarLink('AdminProducts'), - 'languages' => $languages, - 'default_language_iso' => $languages[0]['iso_code'], - 'attribute_groups' => $attributeGroups, - 'max_upload_size' => LegacyTools::formatBytes(UploadedFile::getMaxFilesize()), - 'is_shop_context' => $this->get('prestashop.adapter.shop.context')->isShopContext(), - 'editable' => $this->isGranted(Permission::UPDATE, self::PRODUCT_OBJECT), - 'drawerModules' => $drawerModules, - 'layoutTitle' => $this->trans('Product', 'Admin.Navigation.Menu'), - 'isCreationMode' => (int) $product->state === Product::STATE_TEMP, - ]); - } - - /** - * Builds the product form. - * - * @param Product $product - * @param AdminModelAdapter $modelMapper - * - * @return FormInterface - * - * @throws \Symfony\Component\Process\Exception\LogicException - */ - private function createProductForm(Product $product, AdminModelAdapter $modelMapper) - { - $formBuilder = $this->createFormBuilder( - $modelMapper->getFormData($product), - ['allow_extra_fields' => true] - ) - ->add('id_product', HiddenType::class) - ->add('step1', ProductInformation::class) - ->add('step2', ProductPrice::class, ['id_product' => $product->id]) - ->add('step3', ProductQuantity::class) - ->add('step4', ProductShipping::class) - ->add('step5', ProductSeo::class, [ - 'mapping_type' => $product->getRedirectType(), - ]) - ->add('step6', ProductOptions::class); - - // Prepare combination form (fake but just to validate the form) - $combinations = $product->getAttributesResume( - $this->getContext()->language->id - ); - - if (is_array($combinations)) { - $maxInputVars = (int) ini_get('max_input_vars'); - $combinationsCount = count($combinations) * 25; - $combinationsInputs = ceil($combinationsCount / 1000) * 1000; - - if ($combinationsInputs > $maxInputVars) { - $this->addFlash( - 'error', - $this->trans( - 'The value of the PHP.ini setting "max_input_vars" must be increased to %value% in order to be able to submit the product form.', - 'Admin.Notifications.Error', - ['%value%' => $combinationsInputs] - ) - ); - } - - foreach ($combinations as $combination) { - $formBuilder->add( - 'combination_' . $combination['id_product_attribute'], - ProductCombination::class, - ['allow_extra_fields' => true] - ); - } - } - - return $formBuilder->getForm(); - } - - /** - * Do bulk action on a list of Products. Used with the 'selection action' dropdown menu on the Catalog page. - * - * @param Request $request - * @param string $action The action to apply on the selected products - * - * @throws Exception if action not properly set or unknown - * - * @return \Symfony\Component\HttpFoundation\Response - */ - public function bulkAction(Request $request, $action) - { - if (!$this->actionIsAllowed($action, self::PRODUCT_OBJECT, '_all')) { - $this->addFlash('permission_error', $this->getForbiddenActionMessage($action)); - - return $this->redirectToRoute('admin_product_catalog'); - } - - $productIdList = $request->request->all('bulk_action_selected_products'); - /** @var ProductInterfaceUpdater $productUpdater */ - $productUpdater = $this->get('prestashop.core.admin.data_updater.product_interface'); - - /** @var LoggerInterface $logger */ - $logger = $this->get('logger'); - - $hookEventParameters = ['product_list_id' => $productIdList]; - /** @var HookDispatcher $hookDispatcher */ - $hookDispatcher = $this->get('prestashop.core.hook.dispatcher'); - - try { - $hasMessages = $this->get('session')->getFlashBag()->has('success'); - - if ($this->isDemoModeEnabled()) { - throw new UpdateProductException($this->getDemoModeErrorMessage()); - } - - switch ($action) { - case 'activate_all': - $hookDispatcher->dispatchWithParameters( - 'actionAdminActivateBefore', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerActivateBefore', - $hookEventParameters - ); - // Hooks: managed in ProductUpdater - $productUpdater->activateProductIdList($productIdList); - if (empty($hasMessages)) { - $this->addFlash( - 'success', - $this->trans('Product(s) successfully activated.', 'Admin.Catalog.Notification') - ); - } - - $logger->info('Products activated: (' . implode(',', $productIdList) . ').', $this->getLogDataContext()); - $hookDispatcher->dispatchWithParameters( - 'actionAdminActivateAfter', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerActivateAfter', - $hookEventParameters - ); - - break; - case 'deactivate_all': - $hookDispatcher->dispatchWithParameters( - 'actionAdminDeactivateBefore', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDeactivateBefore', - $hookEventParameters - ); - // Hooks: managed in ProductUpdater - $productUpdater->activateProductIdList($productIdList, false); - if (empty($hasMessages)) { - $this->addFlash( - 'success', - $this->trans('Product(s) successfully deactivated.', 'Admin.Catalog.Notification') - ); - } - - $logger->info('Products deactivated: (' . implode(',', $productIdList) . ').', $this->getLogDataContext()); - $hookDispatcher->dispatchWithParameters( - 'actionAdminDeactivateAfter', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDeactivateAfter', - $hookEventParameters - ); - - break; - case 'delete_all': - $hookDispatcher->dispatchWithParameters( - 'actionAdminDeleteBefore', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDeleteBefore', - $hookEventParameters - ); - // Hooks: managed in ProductUpdater - $productUpdater->deleteProductIdList($productIdList); - if (empty($hasMessages)) { - $this->addFlash( - 'success', - $this->trans('Product(s) successfully deleted.', 'Admin.Catalog.Notification') - ); - } - - $logger->info('Products deleted: (' . implode(',', $productIdList) . ').', $this->getLogDataContext()); - $hookDispatcher->dispatchWithParameters( - 'actionAdminDeleteAfter', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDeleteAfter', - $hookEventParameters - ); - - break; - case 'duplicate_all': - $hookDispatcher->dispatchWithParameters( - 'actionAdminDuplicateBefore', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDuplicateBefore', - $hookEventParameters - ); - // Hooks: managed in ProductUpdater - $productUpdater->duplicateProductIdList($productIdList); - if (empty($hasMessages)) { - $this->addFlash( - 'success', - $this->trans('Product(s) successfully duplicated.', 'Admin.Catalog.Notification') - ); - } - - $logger->info('Products duplicated: (' . implode(',', $productIdList) . ').', $this->getLogDataContext()); - $hookDispatcher->dispatchWithParameters( - 'actionAdminDuplicateAfter', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDuplicateAfter', - $hookEventParameters - ); - - break; - default: - /* - * should never happens since the route parameters are - * restricted to a set of action values in YML file. - */ - $logger->error('Bulk action from ProductController received a bad parameter.', $this->getLogDataContext()); - - throw new Exception('Bad action received from call to ProductController::bulkAction: "' . $action . '"', 2001); - } - } catch (UpdateProductException $due) { - //TODO : need to translate this with an domain name - $message = $due->getMessage(); - $this->addFlash('failure', $message); - $logger->warning($message, $this->getLogDataContext()); - } - - return new Response(json_encode(['result' => 'ok'])); - } - - /** - * Do mass edit action on the current page of products. - * Used with the 'grouped action' dropdown menu on the Catalog page. - * - * @param Request $request - * @param string $action The action to apply on the selected products - * - * @throws Exception if action not properly set or unknown - * - * @return \Symfony\Component\HttpFoundation\Response - */ - public function massEditAction(Request $request, $action) - { - if (!$this->isGranted(Permission::UPDATE, self::PRODUCT_OBJECT)) { - $errorMessage = $this->trans( - 'You do not have permission to edit this.', - 'Admin.Notifications.Error' - ); - $this->get('session')->getFlashBag()->add('permission_error', $errorMessage); - - return $this->redirectToRoute('admin_product_catalog'); - } - - /** @var ProductInterfaceProvider $productProvider */ - $productProvider = $this->get('prestashop.core.admin.data_provider.product_interface'); - - /** @var ProductInterfaceUpdater $productUpdater */ - $productUpdater = $this->get('prestashop.core.admin.data_updater.product_interface'); - - /** @var LoggerInterface $logger */ - $logger = $this->get('logger'); - - /* @var HookDispatcher $hookDispatcher */ - $hookDispatcher = $this->get('prestashop.core.hook.dispatcher'); - - /* Initialize router params variable. */ - $routerParams = []; - - try { - switch ($action) { - case 'sort': - /* Change position_ordering to position */ - $routerParams['orderBy'] = 'position'; - - $productIdList = $request->request->all('mass_edit_action_sorted_products'); - $productPositionList = $request->request->all('mass_edit_action_sorted_positions'); - $hookEventParameters = [ - 'product_list_id' => $productIdList, - 'product_list_position' => $productPositionList, - ]; - - $hookDispatcher->dispatchWithParameters( - 'actionAdminSortBefore', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerSortBefore', - $hookEventParameters - ); - - // Hooks: managed in ProductUpdater - $persistedFilterParams = $productProvider->getPersistedFilterParameters(); - $productList = array_combine($productIdList, $productPositionList); - $productUpdater->sortProductIdList( - $productList, - ['filter_category' => $persistedFilterParams['filter_category']] - ); - - $this->addFlash( - 'success', - $this->trans('Products successfully sorted.', 'Admin.Catalog.Notification') - ); - $logger->info( - 'Products sorted: (' . implode(',', $productIdList) . - ') with positions (' . implode(',', $productPositionList) . ').', $this->getLogDataContext() - ); - $hookEventParameters = [ - 'product_list_id' => $productIdList, - 'product_list_position' => $productPositionList, - ]; - $hookDispatcher->dispatchWithParameters( - 'actionAdminSortAfter', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerSortAfter', - $hookEventParameters - ); - - break; - default: - /* - * should never happens since the route parameters are - * restricted to a set of action values in YML file. - */ - $logger->error('Mass edit action from ProductController received a bad parameter.', $this->getLogDataContext()); - - throw new Exception('Bad action received from call to ProductController::massEditAction: "' . $action . '"', 2001); - } - } catch (UpdateProductException $due) { - //TODO : need to translate with domain name - $message = $due->getMessage(); - $this->addFlash('failure', $message); - $logger->warning($message, $this->getLogDataContext()); - } - - $urlGenerator = $this->get('prestashop.core.admin.url_generator'); - - return $this->redirect($urlGenerator->generate('admin_product_catalog', $routerParams)); - } - - /** - * Do action on one product at a time. Can be used at many places in the controller's page. - * - * @param string $action The action to apply on the selected product - * @param int $id the product ID to apply the action on - * - * @throws Exception if action not properly set or unknown - * - * @return \Symfony\Component\HttpFoundation\Response - */ - public function unitAction($action, $id) - { - if (!$this->actionIsAllowed($action, self::PRODUCT_OBJECT)) { - $this->addFlash('permission_error', $this->getForbiddenActionMessage($action)); - - return $this->redirectToRoute('admin_product_catalog'); - } - - /** @var ProductInterfaceUpdater $productUpdater */ - $productUpdater = $this->get('prestashop.core.admin.data_updater.product_interface'); - - /** @var LoggerInterface $logger */ - $logger = $this->get('logger'); - - $hookEventParameters = ['product_id' => $id]; - /** @var HookDispatcher $hookDispatcher */ - $hookDispatcher = $this->get('prestashop.core.hook.dispatcher'); - - try { - if ($this->isDemoModeEnabled()) { - throw new UpdateProductException($this->getDemoModeErrorMessage()); - } - - switch ($action) { - case 'delete': - $hookDispatcher->dispatchWithParameters( - 'actionAdminDeleteBefore', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDeleteBefore', - $hookEventParameters - ); - // Hooks: managed in ProductUpdater - $productUpdater->deleteProduct($id); - $this->addFlash( - 'success', - $this->trans('Product successfully deleted.', 'Admin.Catalog.Notification') - ); - $logger->info('Product deleted: (' . $id . ').', $this->getLogDataContext($id)); - $hookDispatcher->dispatchWithParameters( - 'actionAdminDeleteAfter', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDeleteAfter', - $hookEventParameters - ); - - break; - case 'duplicate': - $hookDispatcher->dispatchWithParameters( - 'actionAdminDuplicateBefore', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDuplicateBefore', - $hookEventParameters - ); - // Hooks: managed in ProductUpdater - $duplicateProductId = $productUpdater->duplicateProduct($id); - $this->addFlash( - 'success', - $this->trans('Product successfully duplicated.', 'Admin.Catalog.Notification') - ); - $logger->info('Product duplicated: (from ' . $id . ' to ' . $duplicateProductId . ').', $this->getLogDataContext($id)); - $hookDispatcher->dispatchWithParameters( - 'actionAdminDuplicateAfter', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDuplicateAfter', - $hookEventParameters - ); - // stops here and redirect to the new product's page. - return $this->redirectToRoute('admin_product_form', ['id' => $duplicateProductId]); - case 'activate': - $hookDispatcher->dispatchWithParameters( - 'actionAdminActivateBefore', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerActivateBefore', - $hookEventParameters - ); - // Hooks: managed in ProductUpdater - $productUpdater->activateProductIdList([$id]); - $this->addFlash( - 'success', - $this->trans('Product successfully activated.', 'Admin.Catalog.Notification') - ); - $logger->info('Product activated: ' . $id, $this->getLogDataContext($id)); - $hookDispatcher->dispatchWithParameters( - 'actionAdminActivateAfter', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerActivateAfter', - $hookEventParameters - ); - - break; - case 'deactivate': - $hookDispatcher->dispatchWithParameters( - 'actionAdminDeactivateBefore', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDeactivateBefore', - $hookEventParameters - ); - // Hooks: managed in ProductUpdater - $productUpdater->activateProductIdList([$id], false); - $this->addFlash( - 'success', - $this->trans('Product successfully deactivated.', 'Admin.Catalog.Notification') - ); - $logger->info('Product deactivated: ' . $id, $this->getLogDataContext($id)); - $hookDispatcher->dispatchWithParameters( - 'actionAdminDeactivateAfter', - $hookEventParameters - ); - $hookDispatcher->dispatchWithParameters( - 'actionAdminProductsControllerDeactivateAfter', - $hookEventParameters - ); - - break; - default: - /* - * should never happens since the route parameters are - * restricted to a set of action values in YML file. - */ - $logger->error('Unit action from ProductController received a bad parameter.', $this->getLogDataContext($id)); - - throw new Exception('Bad action received from call to ProductController::unitAction: "' . $action . '"', 2002); - } - } catch (UpdateProductException $due) { - //TODO : need to translate with a domain name - $message = $due->getMessage(); - $this->addFlash('failure', $message); - $logger->warning($message, $this->getLogDataContext($id)); - } - - return $this->redirect($this->get('prestashop.core.admin.url_generator')->generate('admin_product_catalog')); - } - - /** - * Toggle product status - * - * @AdminSecurity( - * "is_granted('update', request.get('_legacy_controller'))", - * message="You do not have permission to update this." - * ) - * - * @param int $productId - * - * @return JsonResponse - */ - public function toggleStatusAction(Request $request, $productId) - { - if ($this->isDemoModeEnabled()) { - return $this->json([ - 'status' => false, - 'message' => $this->getDemoModeErrorMessage(), - ]); - } - - $shopConstraint = $request->attributes->get('shopConstraint'); - /** @var ProductForEditing $productForEditing */ - $productForEditing = $this->getQueryBus()->handle(new GetProductForEditing( - $productId, - $shopConstraint, - $this->getContextLangId() - )); - - try { - $command = new UpdateProductCommand($productId, $request->attributes->get('shopConstraint')); - $command->setActive(!$productForEditing->isActive()); - $this->getCommandBus()->handle($command); - $response = [ - 'status' => true, - 'message' => $this->trans('The status has been successfully updated.', 'Admin.Notifications.Success'), - ]; - } catch (ProductException $e) { - $response = [ - 'status' => false, - 'message' => $this->getErrorMessageForException($e, $this->getErrorMessages()), - ]; - } - - return $this->json($response); - } - - /** - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller')) || is_granted('read', request.get('_legacy_controller'))") - * - * @return CsvResponse - * - * @throws \Symfony\Component\Translation\Exception\InvalidArgumentException - */ - public function exportAction() - { - return $this->get(ProductCsvExporter::class)->export(); - } - - /** - * Set the Catalog filters values and redirect to the catalogAction. - * - * URL example: /product/catalog_filters/42/last/32 - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller')) || is_granted('read', request.get('_legacy_controller'))") - * - * @param int|string $quantity the quantity to set on the catalog filters persistence - * @param string $active the activation state to set on the catalog filters persistence - * - * @return RedirectResponse - */ - public function catalogFiltersAction($quantity = 'none', $active = 'none') - { - $quantity = urldecode($quantity); - - /** @var ProductInterfaceProvider $productProvider */ - $productProvider = $this->get('prestashop.core.admin.data_provider.product_interface'); - - // we merge empty filter set with given values, to reset the other filters! - $productProvider->persistFilterParameters( - array_merge( - AdminFilter::getProductCatalogEmptyFilter(), - [ - 'filter_column_sav_quantity' => ($quantity == 'none') ? '' : $quantity, - 'filter_column_active' => ($active == 'none') ? '' : $active, - ] - ) - ); - - return $this->redirectToRoute('admin_product_catalog'); - } - - /** - * @return array - */ - private function getErrorMessages(): array - { - return [ - ProductNotFoundException::class => $this->trans('The object cannot be loaded (or found).', 'Admin.Notifications.Error'), - CannotUpdateProductException::class => $this->trans('An error occurred while updating the status for an object.', 'Admin.Notifications.Error'), - ProductConstraintException::class => [ - ProductConstraintException::INVALID_ONLINE_DATA => $this->trans( - 'To put this product online, please enter a name.', - 'Admin.Catalog.Notification' - ), - ], - ]; - } - - /** - * @return array - */ - private function getLogDataContext($id_product = null, $error_code = null, $allow_duplicate = null): array - { - return [ - 'object_type' => 'Product', - 'object_id' => $id_product, - 'error_code' => $error_code, - 'allow_duplicate' => $allow_duplicate, - ]; - } - - /** - * @return bool - */ - private function shouldRedirectToV2(): bool - { - return $this->get(FeatureFlagStateCheckerInterface::class)->isEnabled(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2); - } -} diff --git a/src/PrestaShopBundle/Controller/Admin/ProductImageController.php b/src/PrestaShopBundle/Controller/Admin/ProductImageController.php deleted file mode 100644 index 2b96cd37ab8e8..0000000000000 --- a/src/PrestaShopBundle/Controller/Admin/ProductImageController.php +++ /dev/null @@ -1,220 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Controller\Admin; - -use ImageManager; -use PrestaShop\PrestaShop\Adapter\Product\AdminProductWrapper; -use PrestaShopBundle\Security\Annotation\AdminSecurity; -use Symfony\Component\Form\Extension\Core\Type\FormType; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Validator\Constraints as Assert; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller for product images. - */ -class ProductImageController extends FrameworkBundleAdminController -{ - /** - * Manage upload for product image. - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller'))") - * - * @param int $idProduct - * @param Request $request - * - * @return string - */ - public function uploadImageAction($idProduct, Request $request) - { - $response = new JsonResponse(); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $return_data = []; - - if ($idProduct == 0 || !$request->isXmlHttpRequest()) { - return $response; - } - - $form = $this->createFormBuilder(null, ['csrf_protection' => false]) - ->add('file', 'Symfony\Component\Form\Extension\Core\Type\FileType', [ - 'error_bubbling' => true, - 'constraints' => [ - new Assert\NotNull(['message' => $this->trans('Please select a file', 'Admin.Catalog.Feature')]), - new Assert\Image(['maxSize' => $this->getConfiguration()->get('PS_ATTACHMENT_MAXIMUM_SIZE') . 'M']), - new Assert\File([ - 'mimeTypes' => [ - 'image/gif', - 'image/jpeg', - 'image/png', - 'image/webp', - ], - 'mimeTypesMessage' => $this->trans( - 'Image format not recognized, allowed formats are: %s', - 'Admin.Notifications.Error', - [ - implode(', ', ImageManager::EXTENSIONS_SUPPORTED), - ] - ), - ]), - ], - ]) - ->getForm(); - - $form->handleRequest($request); - - if ($request->isMethod('POST')) { - if ($form->isValid()) { - $return_data = $adminProductWrapper->getInstance()->ajaxProcessaddProductImage($idProduct, 'form', false)[0]; - $return_data = array_merge($return_data, [ - 'url_update' => $this->generateUrl('admin_product_image_form', ['idImage' => $return_data['id']]), - 'url_delete' => $this->generateUrl('admin_product_image_delete', ['idImage' => $return_data['id']]), - ]); - } else { - $error_msg = []; - foreach ($form->getErrors() as $error) { - $error_msg[] = $error->getMessage(); - } - $return_data = ['message' => implode(' ', $error_msg)]; - $response->setStatusCode(400); - } - } - - return $response->setData($return_data); - } - - /** - * Update images positions. - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller'))") - * - * @param Request $request - * - * @return JsonResponse - */ - public function updateImagePositionAction(Request $request) - { - $response = new JsonResponse(); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $json = $request->request->get('json'); - - if (!empty($json) && $request->isXmlHttpRequest()) { - $adminProductWrapper->ajaxProcessUpdateImagePosition(json_decode($json, true)); - } - - return $response; - } - - /** - * Manage form image. - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller'))") - * - * @param string|int $idImage - * @param Request $request - * - * @return Response - */ - public function formAction($idImage, Request $request): Response - { - $locales = $this->get('prestashop.adapter.legacy.context')->getLanguages(); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - - if ($idImage == 0 || !$request->isXmlHttpRequest()) { - return new Response(); - } - - $image = $productAdapter->getImage((int) $idImage); - - $form = $this->get('form.factory')->createNamedBuilder('form_image', FormType::class, $image, ['csrf_protection' => false]) - ->add('legend', 'PrestaShopBundle\Form\Admin\Type\TranslateType', [ - 'type' => 'Symfony\Component\Form\Extension\Core\Type\TextareaType', - 'options' => [], - 'locales' => $locales, - 'hideTabs' => true, - 'label' => $this->trans('Caption', 'Admin.Catalog.Feature'), - 'required' => false, - ]) - ->add('cover', 'Symfony\Component\Form\Extension\Core\Type\CheckboxType', [ - 'label' => $this->trans('Cover image', 'Admin.Catalog.Feature'), - 'required' => false, - ]) - ->getForm(); - - $form->handleRequest($request); - - if ($request->isMethod('POST')) { - $jsonResponse = new JsonResponse(); - - if ($form->isValid()) { - $jsonResponse->setData($adminProductWrapper->ajaxProcessUpdateImage($idImage, $form->getData())); - } else { - $error_msg = []; - foreach ($form->getErrors() as $error) { - $error_msg[] = $error->getMessage(); - } - - $jsonResponse->setData(['message' => implode(' ', $error_msg)]); - $jsonResponse->setStatusCode(400); - } - - return $jsonResponse; - } - - return $this->render('@PrestaShop/Admin/ProductImage/form.html.twig', [ - 'image' => $image, - 'form' => $form->createView(), - ]); - } - - /** - * Delete an image from its ID. - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller'))") - * - * @param int $idImage - * @param Request $request - * - * @return JsonResponse - */ - public function deleteAction($idImage, Request $request) - { - $response = new JsonResponse(); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - - if (!$request->isXmlHttpRequest()) { - return $response; - } - - $adminProductWrapper->getInstance()->ajaxProcessDeleteProductImage($idImage); - - return $response; - } -} diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Catalog/FeatureValueController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Catalog/FeatureValueController.php index 9e825ae7c8590..b9392690dd2c0 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Catalog/FeatureValueController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Catalog/FeatureValueController.php @@ -29,6 +29,7 @@ namespace PrestaShopBundle\Controller\Admin\Sell\Catalog; use Exception; +use PrestaShop\PrestaShop\Adapter\Form\ChoiceProvider\FeatureValuesChoiceProvider; use PrestaShop\PrestaShop\Core\Domain\Feature\Command\BulkDeleteFeatureValueCommand; use PrestaShop\PrestaShop\Core\Domain\Feature\Command\DeleteFeatureValueCommand; use PrestaShop\PrestaShop\Core\Domain\Feature\Exception\BulkFeatureValueException; @@ -43,6 +44,7 @@ use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController; use PrestaShopBundle\Controller\BulkActionsTrait; use PrestaShopBundle\Security\Annotation\AdminSecurity; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -266,6 +268,41 @@ public function bulkDeleteAction(int $featureId, Request $request): Response ]); } + /** + * Get all values for a given feature. + * + * @AdminSecurity("is_granted('read', request.get('_legacy_controller')) || is_granted('read', 'AdminProducts')") + * + * @param int $featureId The feature Id + * + * @return JsonResponse features list + */ + public function getFeatureValuesAction($featureId) + { + if ($featureId == 0) { + return new JsonResponse(); + } + + $featuresChoices = $this->get(FeatureValuesChoiceProvider::class)->getChoices(['feature_id' => $featureId, 'custom' => false]); + + $data = []; + if (count($featuresChoices) !== 0) { + $data[] = [ + 'id' => 0, + 'value' => $this->trans('Choose a value', 'Admin.Catalog.Feature'), + ]; + } + + foreach ($featuresChoices as $featureName => $featureId) { + $data[] = [ + 'id' => $featureId, + 'value' => $featureName, + ]; + } + + return new JsonResponse($data); + } + /** * @return array> */ diff --git a/src/PrestaShopBundle/Controller/Admin/Sell/Catalog/Product/ProductController.php b/src/PrestaShopBundle/Controller/Admin/Sell/Catalog/Product/ProductController.php index e8a7125961183..0991e5ce13126 100644 --- a/src/PrestaShopBundle/Controller/Admin/Sell/Catalog/Product/ProductController.php +++ b/src/PrestaShopBundle/Controller/Admin/Sell/Catalog/Product/ProductController.php @@ -57,8 +57,6 @@ use PrestaShop\PrestaShop\Core\Domain\Shop\Exception\ShopAssociationNotFound; use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint; use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopId; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; use PrestaShop\PrestaShop\Core\Form\IdentifiableObject\Builder\FormBuilderInterface; use PrestaShop\PrestaShop\Core\Form\IdentifiableObject\Handler\FormHandlerInterface; use PrestaShop\PrestaShop\Core\Grid\Definition\Factory\GridDefinitionFactoryInterface; @@ -137,10 +135,6 @@ public function __construct(ProductRepository $productRepository) */ public function indexAction(Request $request, ProductFilters $filters): Response { - if ($this->shouldRedirectToV1()) { - return $this->redirectToRoute('admin_product_catalog'); - } - $productGridFactory = $this->get('prestashop.core.grid.factory.product'); $productGrid = $productGridFactory->getGrid($filters); @@ -162,6 +156,22 @@ public function indexAction(Request $request, ProductFilters $filters): Response ]); } + /** + * This action is only used to allow backward compatible use of the former route admin_product_catalog + * It is added out of courtesy to give time for module to change and use the new admin_products_index route, + * but it will be removed in version 10.0 and its only usable via GET method. + * + * @deprecated Will be removed in 10.0 + * + * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller')) || is_granted('read', request.get('_legacy_controller'))") + * + * @return RedirectResponse + */ + public function backwardCompatibleListAction(): RedirectResponse + { + return $this->redirectToRoute('admin_products_index'); + } + /** * Process Grid search, but we need to add the category filter which is handled independently. * @@ -437,10 +447,6 @@ public function createAction(Request $request): Response */ public function editAction(Request $request, int $productId): Response { - if ($this->shouldRedirectToV1()) { - return $this->redirectToRoute('admin_product_form', ['id' => $productId]); - } - if ($request->query->get('switchToShop')) { $this->addFlash('success', $this->trans('Your store context has been automatically modified.', 'Admin.Notifications.Success')); @@ -502,6 +508,24 @@ public function editAction(Request $request, int $productId): Response return $this->renderEditProductForm($productForm, $productId); } + /** + * This action is only used to allow backward compatible use of the former route admin_product_form + * It is added out of courtesy to give time for module to change and use the new admin_products_edit route, + * but it will be removed in version 10.0 and its only usable via GET method. + * + * @deprecated Will be removed in 10.0 + * + * @AdminSecurity("is_granted('update', request.get('_legacy_controller'))", message="You do not have permission to update this.") + * + * @param int $id + * + * @return RedirectResponse + */ + public function backwardCompatibleEditAction(int $id): RedirectResponse + { + return $this->redirectToRoute('admin_products_edit', ['productId' => $id]); + } + /** * @AdminSecurity("is_granted('delete', request.get('_legacy_controller'))", message="You do not have permission to delete this.") * @@ -633,7 +657,7 @@ public function duplicateShopGroupAction(int $productId, int $shopGroupId): Resp } /** - * Toggles product status + * Toggles product status for specific shop * * @AdminSecurity("is_granted('update', request.get('_legacy_controller'))", redirectRoute="admin_products_index") * @@ -644,29 +668,21 @@ public function duplicateShopGroupAction(int $productId, int $shopGroupId): Resp */ public function toggleStatusForShopAction(int $productId, int $shopId): JsonResponse { - $shopConstraint = ShopConstraint::shop($shopId); - /** @var ProductForEditing $productForEditing */ - $productForEditing = $this->getQueryBus()->handle(new GetProductForEditing( - $productId, - $shopConstraint, - $this->getContextLangId() - )); - - try { - $command = new UpdateProductCommand($productId, $shopConstraint); - $command->setActive(!$productForEditing->isActive()); - $this->getCommandBus()->handle($command); - } catch (Exception $e) { - return $this->json([ - 'status' => false, - 'message' => $this->getErrorMessageForException($e, $this->getErrorMessages($e)), - ]); - } + return $this->toggleProductStatusByShopConstraint($productId, ShopConstraint::shop($shopId)); + } - return $this->json([ - 'status' => true, - 'message' => $this->trans('The status has been successfully updated.', 'Admin.Notifications.Success'), - ]); + /** + * Toggles product status for all shops + * + * @AdminSecurity("is_granted('update', request.get('_legacy_controller'))", redirectRoute="admin_products_index") + * + * @param int $productId + * + * @return JsonResponse + */ + public function toggleStatusForAllShopsAction(int $productId): JsonResponse + { + return $this->toggleProductStatusByShopConstraint($productId, ShopConstraint::allShops()); } /** @@ -1281,6 +1297,32 @@ private function updateProductStatusByShopConstraint(int $productId, bool $isEna return $this->redirectToRoute('admin_products_index'); } + private function toggleProductStatusByShopConstraint(int $productId, ShopConstraint $shopConstraint): JsonResponse + { + /** @var ProductForEditing $productForEditing */ + $productForEditing = $this->getQueryBus()->handle(new GetProductForEditing( + $productId, + $shopConstraint, + $this->getContextLangId() + )); + + try { + $command = new UpdateProductCommand($productId, $shopConstraint); + $command->setActive(!$productForEditing->isActive()); + $this->getCommandBus()->handle($command); + } catch (Exception $e) { + return $this->json([ + 'status' => false, + 'message' => $this->getErrorMessageForException($e, $this->getErrorMessages($e)), + ]); + } + + return $this->json([ + 'status' => true, + 'message' => $this->trans('The status has been successfully updated.', 'Admin.Notifications.Success'), + ]); + } + /** * Helper private method to bulk update a product's status. * @@ -1509,14 +1551,6 @@ private function getGridAdminFilter(): ?AdminFilter return $adminFiltersRepository->findByEmployeeAndFilterId($employeeId, $shopId, ProductGridDefinitionFactory::GRID_ID); } - /** - * @return bool - */ - private function shouldRedirectToV1(): bool - { - return $this->get(FeatureFlagStateCheckerInterface::class)->isDisabled(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2); - } - /** * @return int|null */ diff --git a/src/PrestaShopBundle/Controller/Admin/SpecificPriceController.php b/src/PrestaShopBundle/Controller/Admin/SpecificPriceController.php deleted file mode 100644 index 301a51272558a..0000000000000 --- a/src/PrestaShopBundle/Controller/Admin/SpecificPriceController.php +++ /dev/null @@ -1,290 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Controller\Admin; - -use DateTime; -use Exception; -use PrestaShop\PrestaShop\Adapter\Product\AdminProductWrapper; -use PrestaShop\PrestaShop\Core\Foundation\Database\EntityDataInconsistencyException; -use PrestaShop\PrestaShop\Core\Foundation\Database\EntityNotFoundException; -use PrestaShopBundle\Form\Admin\Product\ProductSpecificPrice as SpecificPriceFormType; -use PrestaShopBundle\Security\Annotation\AdminSecurity; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller for the attribute / attribute group. - */ -class SpecificPriceController extends FrameworkBundleAdminController -{ - /** - * Get specific price list for a product. - * - * @AdminSecurity("is_granted('read', 'ADMINPRODUCTS_')") - * - * @param string|int $idProduct The product ID - * - * @return JsonResponse - */ - public function listAction($idProduct) - { - $response = new JsonResponse(); - - $contextAdapter = $this->get('prestashop.adapter.legacy.context'); - $locales = $contextAdapter->getLanguages(); - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $shopContextAdapter = $this->get('prestashop.adapter.shop.context'); - $shops = $shopContextAdapter->getShops(); - $countries = $this->get('prestashop.adapter.data_provider.country')->getCountries($locales[0]['id_lang']); - $currencies = $this->get('prestashop.adapter.data_provider.currency')->getCurrencies(); - $groups = $this->get('prestashop.adapter.data_provider.group')->getGroups($locales[0]['id_lang']); - - //get product - $product = $productAdapter->getProduct((int) $idProduct); - if (!is_object($product) || empty($product->id)) { - $response->setStatusCode(Response::HTTP_BAD_REQUEST); - - return $response; - } - - $response->setData($adminProductWrapper->getSpecificPricesList( - $product, - $contextAdapter->getContext()->currency, - $shops, - $currencies, - $countries, - $groups - )); - - return $response; - } - - /** - * Add specific price Form process. - * - * @AdminSecurity( - * "is_granted('create', 'ADMINPRODUCTS_') && is_granted('update', 'ADMINPRODUCTS_')" - * ) - * - * @param Request $request The request - * - * @return JsonResponse - */ - public function addAction(Request $request) - { - $response = new JsonResponse(); - $idProduct = isset($request->get('form')['id_product']) ? $request->get('form')['id_product'] : null; - - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $errors = $adminProductWrapper->processProductSpecificPrice($idProduct, $request->get('form')['step2']['specific_price']); - - if (!empty($errors)) { - $response->setData(implode(', ', $errors)); - $response->setStatusCode(Response::HTTP_BAD_REQUEST); - } - - return $response; - } - - /** - * Get one specific price list for a product. - * - * @AdminSecurity( - * "is_granted('create', 'ADMINPRODUCTS_') && is_granted('update', 'ADMINPRODUCTS_')" - * ) - * - * @param int $idSpecificPrice - * - * @return Response - */ - public function getUpdateFormAction($idSpecificPrice): Response - { - /** @var AdminProductWrapper $adminProductWrapper */ - $adminProductWrapper = $this->get(AdminProductWrapper::class); - - try { - $price = $adminProductWrapper->getSpecificPriceDataById($idSpecificPrice); - } catch (EntityNotFoundException $e) { - $message = $this->trans( - 'Cannot find specific price %price%', - 'Admin.Catalog.Notification', - ['price' => $idSpecificPrice] - ); - - return new Response($message, Response::HTTP_BAD_REQUEST); - } - $formData = $this->formatSpecificPriceToPrefillForm($idSpecificPrice, $price); - - $options = [ - 'id_product' => $price->id_product, - 'selected_product_attribute' => $price->id_product_attribute, - ]; - - $formBuilder = $this->createFormBuilder(); - $formBuilder->add('modal', SpecificPriceFormType::class, $options); - - $form = $formBuilder->getForm(); - $form->setData($formData); - - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - $product = $productAdapter->getProduct((int) $price->id_product); - - return $this->render('@PrestaShop/Admin/Product/ProductPage/Forms/form_specific_price.html.twig', [ - 'form' => $form->createView()->offsetGet('modal'), - 'has_combinations' => ($product->hasCombinations()), - 'is_modal' => true, - ]); - } - - /** - * Update specific price Form process. - * - * @AdminSecurity( - * "is_granted('create', 'ADMINPRODUCTS_') && is_granted('update', 'ADMINPRODUCTS_')" - * ) - * - * @param int $idSpecificPrice - * @param Request $request - * - * @return JsonResponse - */ - public function updateAction($idSpecificPrice, Request $request) - { - $response = new JsonResponse(); - $formData = $request->get('form'); - - $idProduct = isset($formData['id_product']) ? $formData['id_product'] : null; - $formValues = $formData['modal']; - - /** @var AdminProductWrapper $adminProductWrapper */ - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $errors = $adminProductWrapper->processProductSpecificPrice($idProduct, $formValues, $idSpecificPrice); - - if (!empty($errors)) { - $response->setData(implode(', ', $errors)); - $response->setStatusCode(Response::HTTP_BAD_REQUEST); - } - - return $response; - } - - /** - * Delete a specific price. - * - * @AdminSecurity("is_granted('delete', 'ADMINPRODUCTS_')") - * - * @param int $idSpecificPrice The specific price ID - * @param Request $request The request - * - * @return JsonResponse - */ - public function deleteAction($idSpecificPrice, Request $request) - { - $response = new JsonResponse(); - - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $res = $adminProductWrapper->deleteSpecificPrice((int) $idSpecificPrice); - - if ($res['status'] == 'error') { - $response->setStatusCode(Response::HTTP_BAD_REQUEST); - } - - $response->setData($res['message']); - - return $response; - } - - /** - * @param int $id - * @param \SpecificPrice $price - * - * @return array - */ - private function formatSpecificPriceToPrefillForm($id, $price) - { - if ($price->reduction_type === 'percentage') { - $reduction = $price->reduction * 100; - } else { - $reduction = $price->reduction; - } - $formattedFormData = [ - 'sp_update_id' => $id, - 'sp_id_shop' => $price->id_shop, - 'sp_id_currency' => $price->id_currency, - 'sp_id_country' => $price->id_country, - 'sp_id_group' => $price->id_group, - 'sp_id_customer' => null, - 'sp_id_product_attribute' => $price->id_product_attribute, - 'sp_from' => self::formatForDatePicker($price->from), - 'sp_to' => self::formatForDatePicker($price->to), - 'sp_from_quantity' => $price->from_quantity, - 'sp_price' => ($price->price !== '-1.000000') ? $price->price : '', - 'leave_bprice' => ($price->price === '-1.000000'), - 'sp_reduction' => $reduction, - 'sp_reduction_type' => $price->reduction_type, - 'sp_reduction_tax' => $price->reduction_tax, - ]; - if ($price->id_customer !== '0') { - $formattedFormData['sp_id_customer'] = ['data' => [$price->id_customer]]; - } - $cleanedFormData = array_map(function ($item) { - if (!$item) { - return null; - } - - return $item; - }, $formattedFormData); - - return ['modal' => $cleanedFormData]; - } - - /** - * @param string $dateAsString - * - * @return string|null If date is 0000-00-00 00:00:00, null is returned - * - * @throws \PrestaShopDatabaseExceptionCore if date is not valid - */ - private static function formatForDatePicker($dateAsString) - { - if ('0000-00-00 00:00:00' === $dateAsString) { - return null; - } - - try { - $dateTime = new DateTime($dateAsString); - } catch (Exception $e) { - throw new EntityDataInconsistencyException(sprintf('Found bad date for specific price: %s', $dateAsString)); - } - - return $dateTime->format('Y-m-d H:i:s'); - } -} diff --git a/src/PrestaShopBundle/Controller/Admin/SupplierController.php b/src/PrestaShopBundle/Controller/Admin/SupplierController.php deleted file mode 100644 index 4b93ee475633c..0000000000000 --- a/src/PrestaShopBundle/Controller/Admin/SupplierController.php +++ /dev/null @@ -1,126 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Controller\Admin; - -use PrestaShop\PrestaShop\Adapter\Product\AdminProductWrapper; -use PrestaShop\PrestaShop\Adapter\Tools; -use PrestaShopBundle\Form\Admin\Product\ProductSupplierCombination; -use PrestaShopBundle\Model\Product\AdminModelAdapter as ProductAdminModelAdapter; -use PrestaShopBundle\Security\Annotation\AdminSecurity; -use Symfony\Component\Form\Extension\Core\Type\CollectionType; -use Symfony\Component\Form\Extension\Core\Type\FormType; -use Symfony\Component\HttpFoundation\Response; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller for suppliers page. - */ -class SupplierController extends FrameworkBundleAdminController -{ - /** - * refreshProductSupplierCombinationFormAction. - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller'))") - * - * @param int $idProduct - * @param int|string $supplierIds The suppliers ids separate by "-" - * - * @return string|Response - */ - public function refreshProductSupplierCombinationFormAction($idProduct, $supplierIds) - { - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - $response = new Response(); - - //get product - $product = $productAdapter->getProduct((int) $idProduct); - - $suppliers = explode('-', $supplierIds); - if ($supplierIds == 0) { - return $response; - } - - if (!is_object($product) || empty($product->id)) { - $response->setStatusCode(400); - - return $response; - } - - //Pre-save of supplier product, needed for well form generation - $_POST['supplier_loaded'] = 1; - foreach ($suppliers as $idSupplier) { - $_POST['check_supplier_' . $idSupplier] = 1; - } - $adminProductController = $adminProductWrapper->getInstance(); - $adminProductController->processSuppliers($idProduct); - - $modelMapper = new ProductAdminModelAdapter( - $this->get('prestashop.adapter.legacy.context'), - $this->get(AdminProductWrapper::class), - $this->get(Tools::class), - $this->get('prestashop.adapter.data_provider.product'), - $this->get('prestashop.adapter.data_provider.supplier'), - $this->get('prestashop.adapter.data_provider.feature'), - $this->get('prestashop.adapter.data_provider.pack'), - $this->get('prestashop.adapter.shop.context'), - $this->get('prestashop.adapter.data_provider.tax'), - $this->get('prestashop.adapter.legacy.configuration'), - $this->get('router') - ); - $allFormData = $modelMapper->getFormData($product); - - $form = $this->createFormBuilder($allFormData); - $simpleSubForm = $form->create('step6', FormType::class); - - foreach ($suppliers as $idSupplier) { - if ($idSupplier == 0 || !is_numeric($idSupplier)) { - continue; - } - - $simpleSubForm->add('supplier_combination_' . $idSupplier, CollectionType::class, [ - 'entry_type' => ProductSupplierCombination::class, - 'entry_options' => [ - 'id_supplier' => $idSupplier, - 'id_currency' => $this->getContext()->currency->id, - ], - 'prototype' => true, - 'allow_add' => true, - 'required' => false, - 'label' => $this->get('prestashop.adapter.data_provider.supplier')->getNameById($idSupplier), - ]); - } - - $form->add($simpleSubForm); - - return $this->render('@Product/ProductPage/Forms/form_supplier_combination.html.twig', [ - 'suppliers' => $suppliers, - 'form' => $form->getForm()['step6']->createView(), - ]); - } -} diff --git a/src/PrestaShopBundle/Controller/Admin/VirtualProductController.php b/src/PrestaShopBundle/Controller/Admin/VirtualProductController.php deleted file mode 100644 index e9d1d262f12ca..0000000000000 --- a/src/PrestaShopBundle/Controller/Admin/VirtualProductController.php +++ /dev/null @@ -1,185 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Controller\Admin; - -use PrestaShop\PrestaShop\Adapter\Product\AdminProductWrapper; -use PrestaShopBundle\Entity\ProductDownload; -use PrestaShopBundle\Form\Admin\Product\ProductVirtual; -use PrestaShopBundle\Security\Annotation\AdminSecurity; -use Symfony\Component\HttpFoundation\BinaryFileResponse; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\ResponseHeaderBag; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Admin controller for the virtual product on the /product/form page. - */ -class VirtualProductController extends FrameworkBundleAdminController -{ - /** - * Process Ajax Form to create/update virtual product. - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller'))") - * - * @param string|int $idProduct - * @param Request $request - * - * @return JsonResponse - */ - public function saveAction($idProduct, Request $request) - { - $response = new JsonResponse(); - $legacyContext = $this->get('prestashop.adapter.legacy.context'); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - $router = $this->get('router'); - - //get product - $product = $productAdapter->getProduct((int) $idProduct); - - if (!$product || !$request->isXmlHttpRequest()) { - return $response; - } - - $form = $this->createForm( - ProductVirtual::class, - null, - ['csrf_protection' => false] - ); - - $form->handleRequest($request); - if ($form->isValid()) { - $data = $form->getData(); - $res = $adminProductWrapper->updateDownloadProduct($product, $data); - $res->file_download_link = - $res->filename ? - $router->generate( - 'admin_product_virtual_download_file_action', - ['idProduct' => $idProduct] - ) : - ''; - - $product->is_virtual = 1; - $product->save(); - - $response->setData($res); - } else { - $response->setStatusCode(400); - $response->setData($this->getFormErrorsForJS($form)); - } - - return $response; - } - - /** - * Download the content of the virtual product. - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller')) || is_granted('read', request.get('_legacy_controller'))") - * - * @param int $idProduct - * - * @return BinaryFileResponse - */ - public function downloadFileAction($idProduct) - { - $configuration = $this->getConfiguration(); - $download = $this->getDoctrine() - ->getRepository(ProductDownload::class) - ->findOneBy([ - 'idProduct' => $idProduct, - ]); - - $response = new BinaryFileResponse( - $configuration->get('_PS_DOWNLOAD_DIR_') . $download->getFilename() - ); - - $response->setContentDisposition( - ResponseHeaderBag::DISPOSITION_ATTACHMENT, - $download->getDisplayFilename() - ); - - return $response; - } - - /** - * Process Ajax Form to remove attached file. - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller'))") - * - * @param string|int $idProduct - * @param Request $request - * - * @return JsonResponse - */ - public function removeFileAction($idProduct, Request $request) - { - $response = new JsonResponse(); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - - //get product - $product = $productAdapter->getProduct((int) $idProduct); - - if (!$product || !$request->isXmlHttpRequest()) { - return $response; - } - - $adminProductWrapper->processDeleteVirtualProductFile($product); - - return $response; - } - - /** - * Process Ajax remove action. - * - * @AdminSecurity("is_granted('create', request.get('_legacy_controller')) || is_granted('update', request.get('_legacy_controller'))") - * - * @param string|int $idProduct - * @param Request $request - * - * @return JsonResponse - */ - public function removeAction($idProduct, Request $request) - { - $response = new JsonResponse(); - $adminProductWrapper = $this->get(AdminProductWrapper::class); - $productAdapter = $this->get('prestashop.adapter.data_provider.product'); - - //get product - $product = $productAdapter->getProduct((int) $idProduct); - - if (!$product || !$request->isXmlHttpRequest()) { - return $response; - } - - $adminProductWrapper->processDeleteVirtualProduct($product); - - return $response; - } -} diff --git a/src/PrestaShopBundle/Entity/Repository/FeatureFlagRepository.php b/src/PrestaShopBundle/Entity/Repository/FeatureFlagRepository.php index 46ed4b9a2318f..4bb99523d1124 100644 --- a/src/PrestaShopBundle/Entity/Repository/FeatureFlagRepository.php +++ b/src/PrestaShopBundle/Entity/Repository/FeatureFlagRepository.php @@ -29,7 +29,6 @@ namespace PrestaShopBundle\Entity\Repository; use Doctrine\ORM\EntityRepository; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; use PrestaShopBundle\Entity\FeatureFlag; class FeatureFlagRepository extends EntityRepository @@ -43,13 +42,6 @@ class FeatureFlagRepository extends EntityRepository */ public function getByName(string $featureFlagName): ?FeatureFlag { - // Temporary automatic conversion of the feature flag since now only one value is used - // will be removed when the FEATURE_FLAG_PRODUCT_PAGE_V2_MULTI_SHOP is removed and/or the - // feature is officially released - if ($featureFlagName === FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2_MULTI_SHOP) { - $featureFlagName = FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2; - } - return $this->findOneBy(['name' => $featureFlagName]); } diff --git a/src/PrestaShopBundle/Form/Admin/Category/SimpleCategory.php b/src/PrestaShopBundle/Form/Admin/Category/SimpleCategory.php deleted file mode 100644 index 56f6144fafb69..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Category/SimpleCategory.php +++ /dev/null @@ -1,122 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Category; - -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use Symfony\Component\Form\Extension\Core\Type\ChoiceType; -use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the basic category form - * Name (not translated), and parent category selector. - */ -class SimpleCategory extends CommonAbstractType -{ - private $translator; - private $categories; - - /** - * Constructor. - * - * @param TranslatorInterface $translator - * @param object $categoryDataProvider - */ - public function __construct(TranslatorInterface $translator, $categoryDataProvider) - { - $this->translator = $translator; - $this->formatValidList($categoryDataProvider->getNestedCategories()); - } - - /** - * Create and format a valid array keys categories that can be validate by the choice SF2 cform component. - * - * @param array $list The nested array categories - */ - protected function formatValidList($list) - { - foreach ($list as $item) { - $this->categories[$item['name']] = $item['id_category']; - - if (isset($item['children'])) { - $this->formatValidList($item['children']); - } - } - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add('name', TextType::class, [ - 'label' => $this->translator->trans('Name', [], 'Admin.Global'), - 'required' => false, - 'attr' => [ - 'placeholder' => $this->translator->trans('Category name', [], 'Admin.Catalog.Feature'), - 'class' => 'ajax', - ], - 'constraints' => $options['ajax'] ? [] : [ - new Assert\NotBlank(), - new Assert\Length(['min' => 1, 'max' => 128]), - ], - ]) - ->add('id_parent', ChoiceType::class, [ - 'choices' => $this->categories, - 'required' => true, - 'autocomplete' => true, - 'label' => $this->translator->trans('Parent of the category', [], 'Admin.Catalog.Feature'), - ]); - } - - /** - * {@inheritdoc} - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults([ - 'ajax' => false, - ]); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'new_simple_category'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Feature/ProductFeature.php b/src/PrestaShopBundle/Form/Admin/Feature/ProductFeature.php deleted file mode 100644 index bf112ad1b3f01..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Feature/ProductFeature.php +++ /dev/null @@ -1,165 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Feature; - -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\TranslateType; -use PrestaShopBundle\Form\FormHelper; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; -use Symfony\Component\Form\FormInterface; - -/** - * This Class is responsible to generate the product Features form. - */ -class ProductFeature extends CommonAbstractType -{ - private $featureDataProvider; - private $translator; - private $locales; - private $router; - private $features; - - /** - * Constructor. - * - * @param object $translator - * @param object $legacyContext - * @param object $router - * @param object $featureDataProvider - */ - public function __construct($translator, $legacyContext, $router, $featureDataProvider) - { - $this->translator = $translator; - $this->locales = $legacyContext->getLanguages(); - $this->router = $router; - $this->featureDataProvider = $featureDataProvider; - $this->features = FormHelper::formatDataChoicesList( - $this->featureDataProvider->getFeatures($this->locales[0]['id_lang']), - 'id_feature' - ); - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add('feature', FormType\ChoiceType::class, [ - 'label' => $this->translator->trans('Feature', [], 'Admin.Catalog.Feature'), - 'choices' => $this->features, - 'required' => false, - 'autocomplete' => true, - 'attr' => [ - 'data-action' => $this->router->generate('admin_feature_get_feature_values', ['idFeature' => 1]), - 'class' => 'feature-selector', - ], - 'placeholder' => $this->translator->trans('Choose a feature', [], 'Admin.Catalog.Feature'), - ]) - ->add('value', FormType\ChoiceType::class, [ - 'label' => $this->translator->trans('Pre-defined value', [], 'Admin.Catalog.Feature'), - 'required' => false, - 'autocomplete' => true, - 'placeholder' => $this->translator->trans('Choose a value', [], 'Admin.Catalog.Feature'), - 'disabled' => true, - ]) - ->add('custom_value', TranslateType::class, [ - 'type' => FormType\TextType::class, - 'options' => [], - 'locales' => $this->locales, - 'hideTabs' => true, - 'required' => false, - 'label' => $this->translator->trans('OR Customized value', [], 'Admin.Catalog.Feature'), - ]); - - $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { - $form = $event->getForm(); - $data = $event->getData(); - - if (!$data || !$data['feature'] || $data['custom_value']) { - return; - } - - $choices = FormHelper::formatDataChoicesList( - $this->featureDataProvider->getFeatureValuesWithLang($this->locales[0]['id_lang'], $data['feature']), - 'id_feature_value', - 'value' - ); - - $this->updateValueField($form, $choices); - }); - - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { - $data = $event->getData(); - $form = $event->getForm(); - - if (empty($data['value'])) { - return; - } - - $choices = FormHelper::formatDataChoicesList( - $this->featureDataProvider->getFeatureValuesWithLang($this->locales[0]['id_lang'], $data['feature']), - 'id_feature_value', - 'value' - ); - - $this->updateValueField($form, $choices); - }); - } - - /** - * @param FormInterface $form - * @param array $choices - */ - private function updateValueField(FormInterface $form, array $choices) - { - $form->add('value', FormType\ChoiceType::class, [ - 'label' => $this->translator->trans('Pre-defined value', [], 'Admin.Catalog.Feature'), - 'required' => false, - 'autocomplete' => true, - 'attr' => [ - 'class' => 'feature-value-selector', - ], - 'choices' => $choices, - 'placeholder' => $this->translator->trans('Choose a value', [], 'Admin.Catalog.Feature'), - ]); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_feature'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductAttachement.php b/src/PrestaShopBundle/Form/Admin/Product/ProductAttachement.php deleted file mode 100644 index 494c04cd3e5ce..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductAttachement.php +++ /dev/null @@ -1,114 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use PrestaShop\PrestaShop\Core\ConfigurationInterface; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use Symfony\Component\Form\Extension\Core\Type\ButtonType; -use Symfony\Component\Form\Extension\Core\Type\FileType; -use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the product attachments. - */ -class ProductAttachement extends CommonAbstractType -{ - private $translator; - private $configuration; - - public function __construct(TranslatorInterface $translator, ConfigurationInterface $configuration) - { - $this->translator = $translator; - $this->configuration = $configuration; - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add('file', FileType::class, [ - 'required' => false, - 'label' => $this->translator->trans('File', [], 'Admin.Global'), - 'constraints' => [ - new Assert\NotNull(['message' => $this->translator->trans('Please select a file', [], 'Admin.Catalog.Feature')]), - new Assert\File(['maxSize' => $this->configuration->get('PS_ATTACHMENT_MAXIMUM_SIZE') . 'M']), - ], - ]) - ->add('name', TextType::class, [ - 'label' => $this->translator->trans('Filename', [], 'Admin.Global'), - 'attr' => ['placeholder' => $this->translator->trans('Title', [], 'Admin.Global')], - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Length(['min' => 2]), - ], - ]) - ->add('description', TextType::class, [ - 'label' => $this->translator->trans('Description', [], 'Admin.Global'), - 'attr' => ['placeholder' => $this->translator->trans('Description', [], 'Admin.Global')], - 'empty_data' => '', - ]) - ->add('add', ButtonType::class, [ - 'label' => $this->translator->trans('Add', [], 'Admin.Actions'), - 'attr' => ['class' => 'btn-outline-primary pull-right'], - ]) - ->add('cancel', ButtonType::class, [ - 'label' => $this->translator->trans('Cancel', [], 'Admin.Actions'), - 'attr' => ['class' => 'btn-outline-secondary pull-right mr-2', 'data-toggle' => 'collapse', 'data-target' => '#collapsedForm'], - ]); - - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { - $form = $event->getForm(); - - //if this partial form is submit from a parent form, disable it - if ($form->getParent()) { - $event->setData([]); - $form->add('file', FileType::class, ['mapped' => false]); - $form->add('name', TextType::class, ['mapped' => false]); - } - }); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_attachment'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductCategories.php b/src/PrestaShopBundle/Form/Admin/Product/ProductCategories.php deleted file mode 100644 index 2ec882b932c74..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductCategories.php +++ /dev/null @@ -1,90 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use PrestaShop\PrestaShop\Adapter\Category\CategoryDataProvider; -use PrestaShopBundle\Form\Admin\Type\ChoiceCategoriesTreeType; -use PrestaShopBundle\Form\Admin\Type\TranslatorAwareType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This class render Product Categories Form in Product List Page. - */ -class ProductCategories extends TranslatorAwareType -{ - /** - * @var CategoryDataProvider - */ - private $categoryProvider; - - /** - * @var int - */ - private $contextLangId; - - /** - * @param TranslatorInterface $translator - * @param CategoryDataProvider $categoryDataProvider - * @param array $locales - * @param int $contextLangId - */ - public function __construct( - TranslatorInterface $translator, - CategoryDataProvider $categoryDataProvider, - array $locales, - int $contextLangId - ) { - $this->categoryProvider = $categoryDataProvider; - $this->contextLangId = $contextLangId; - parent::__construct($translator, $locales); - } - - /** - * {@inheritdoc} - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add('categories', ChoiceCategoriesTreeType::class, [ - 'label' => $this->trans('Categories', 'Admin.Catalog.Feature'), - 'list' => $this->categoryProvider->getNestedCategories(null, $this->contextLangId, false), - 'valid_list' => [], - 'multiple' => false, - 'expanded' => false, - ]); - } - - /** - * {@inheritdoc} - */ - public function getBlockPrefix() - { - return 'product_categories'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductCombination.php b/src/PrestaShopBundle/Form/Admin/Product/ProductCombination.php deleted file mode 100644 index cc6d7f6d241a0..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductCombination.php +++ /dev/null @@ -1,280 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShop\PrestaShop\Core\ConfigurationInterface; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\DatePickerType; -use PrestaShopBundle\Form\FormHelper; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\ChoiceType; -use Symfony\Component\Form\Extension\Core\Type\HiddenType; -use Symfony\Component\Form\Extension\Core\Type\MoneyType; -use Symfony\Component\Form\Extension\Core\Type\NumberType; -use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the product combination form. - */ -class ProductCombination extends CommonAbstractType -{ - /** - * @var ConfigurationInterface - */ - private $configuration; - - /** - * @var TranslatorInterface - */ - public $translator; - - /** - * @var LegacyContext - */ - private $legacyContext; - - /** - * Constructor. - * - * @param TranslatorInterface $translator - * @param LegacyContext $legacyContext - * @param ConfigurationInterface $configuration - */ - public function __construct(TranslatorInterface $translator, LegacyContext $legacyContext, ConfigurationInterface $configuration) - { - $this->translator = $translator; - $this->configuration = $configuration; - $this->legacyContext = $legacyContext; - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $currencyIsoCode = $this->legacyContext->getContext()->currency->iso_code; - $is_stock_management = $this->configuration->get('PS_STOCK_MANAGEMENT'); - - $builder->add('id_product_attribute', HiddenType::class, [ - 'required' => false, - ]) - ->add('attribute_reference', TextType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Reference', [], 'Admin.Global'), - 'empty_data' => '', - ]) - ->add('attribute_ean13', TextType::class, [ - 'required' => false, - 'error_bubbling' => true, - 'label' => $this->translator->trans('EAN-13 or JAN barcode', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Regex('/^[0-9]{0,13}$/'), - ], - 'empty_data' => '', - ]) - ->add('attribute_isbn', TextType::class, [ - 'required' => false, - 'label' => $this->translator->trans('ISBN code', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Regex('/^[0-9-]{0,32}$/'), - ], - 'empty_data' => '', - ]) - ->add('attribute_upc', TextType::class, [ - 'required' => false, - 'label' => $this->translator->trans('UPC barcode', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Regex('/^[0-9]{0,12}$/'), - ], - 'empty_data' => '', - ]) - ->add('attribute_mpn', TextType::class, [ - 'required' => false, - 'label' => $this->translator->trans('MPN', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Length(['max' => 40]), - ], - 'empty_data' => '', - ]) - ->add('attribute_wholesale_price', MoneyType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Cost price', [], 'Admin.Catalog.Feature'), - 'currency' => $currencyIsoCode, - 'attr' => ['class' => 'attribute_wholesale_price'], - ]) - ->add('attribute_price', MoneyType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Impact on price (tax excl.)', [], 'Admin.Catalog.Feature'), - 'currency' => $currencyIsoCode, - 'attr' => ['class' => 'attribute_priceTE'], - ]) - ->add('attribute_priceTI', MoneyType::class, [ - 'required' => false, - 'mapped' => false, - 'label' => $this->translator->trans('Impact on price (tax incl.)', [], 'Admin.Catalog.Feature'), - 'currency' => $currencyIsoCode, - 'attr' => ['class' => 'attribute_priceTI'], - ]) - ->add('attribute_ecotax', MoneyType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Ecotax (tax incl.)', [], 'Admin.Catalog.Feature'), - 'currency' => $currencyIsoCode, - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'float']), - ], - 'attr' => [ - 'class' => 'attribute_ecotaxTi', - ], - ]) - ->add('attribute_weight', NumberType::class, [ - 'prepend_unit' => true, - 'unit' => $this->configuration->get('PS_WEIGHT_UNIT'), - 'scale' => FormHelper::DEFAULT_WEIGHT_PRECISION, - 'required' => false, - 'label' => $this->translator->trans('Impact on weight', [], 'Admin.Catalog.Feature'), - 'attr' => ['class' => 'attribute_weight'], - ]) - ->add('attribute_unity', MoneyType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Impact on price per unit (tax excl.)', [], 'Admin.Catalog.Feature'), - 'currency' => $currencyIsoCode, - 'attr' => ['class' => 'attribute_unity'], - ]) - ->add('attribute_minimal_quantity', NumberType::class, [ - 'required' => false, - 'default_empty_data' => 1, - 'label' => $this->translator->trans('Min. quantity for sale', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Positive(), - new Assert\Type(['type' => 'numeric']), - ], - 'attr' => [ - 'min' => 1, - ], - 'html5' => true, - ]) - ->add('attribute_location', TextType::class, [ - 'label' => $this->translator->trans('Stock location', [], 'Admin.Catalog.Feature'), - ]) - ->add('attribute_low_stock_threshold', NumberType::class, [ - 'label' => $this->translator->trans('Low stock level', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Type(['type' => 'numeric']), - ], - 'attr' => [ - 'placeholder' => $this->translator->trans( - 'Leave empty to disable', - [], - 'Admin.Catalog.Help' - ), - ], - ]) - ->add('attribute_low_stock_alert', CheckboxType::class, [ - 'label' => $this->translator->trans('Send me an email when the quantity is below or equals this level', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Type(['type' => 'bool']), - ], - ]) - ->add('available_date_attribute', DatePickerType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Availability date', [], 'Admin.Catalog.Feature'), - 'attr' => ['class' => 'date', 'placeholder' => 'YYYY-MM-DD'], - ]) - ->add('attribute_default', CheckboxType::class, [ - 'label' => $this->translator->trans('Set as default combination', [], 'Admin.Catalog.Feature'), - 'required' => false, - 'attr' => ['class' => 'attribute_default_checkbox'], - ]); - if ($is_stock_management) { - $builder->add( - 'attribute_quantity', - NumberType::class, - [ - 'required' => true, - 'label' => $this->translator->trans('Quantity', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'numeric']), - ], - ] - ); - } - $builder->add('id_image_attr', ChoiceType::class, [ - 'choices' => [], - 'required' => false, - 'expanded' => true, - 'multiple' => true, - 'label' => $this->translator->trans('Select images of this combination:', [], 'Admin.Catalog.Feature'), - 'attr' => ['class' => 'images'], - ]) - ->add('final_price', MoneyType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Final price', [], 'Admin.Catalog.Feature'), - 'currency' => $currencyIsoCode, - ]); - - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { - $form = $event->getForm(); - $data = $event->getData(); - - $choices = []; - if (!empty($data['id_image_attr'])) { - foreach ($data['id_image_attr'] as $id) { - $choices[$id] = $id; - } - } - - $form->add('id_image_attr', ChoiceType::class, [ - 'choices' => $choices, - 'required' => false, - 'expanded' => true, - 'multiple' => true, - ]); - }); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_combination'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductCombinationBulk.php b/src/PrestaShopBundle/Form/Admin/Product/ProductCombinationBulk.php deleted file mode 100644 index f4306687cbdec..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductCombinationBulk.php +++ /dev/null @@ -1,127 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use PrestaShop\PrestaShop\Core\Domain\Configuration\ShopConfigurationInterface; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\DatePickerType; -use PrestaShopBundle\Form\FormHelper; -use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\MoneyType; -use Symfony\Component\Form\Extension\Core\Type\NumberType; -use Symfony\Component\Form\Extension\Core\Type\TextType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the form for bulk combination feature - * Note this form is not validated from the server side. - */ -class ProductCombinationBulk extends CommonAbstractType -{ - private $translator; - private $configuration; - - public function __construct(TranslatorInterface $translator, ShopConfigurationInterface $configuration) - { - $this->translator = $translator; - $this->configuration = $configuration; - } - - public function buildForm(FormBuilderInterface $builder, array $options) - { - $is_stock_management = $this->configuration->getBoolean('PS_STOCK_MANAGEMENT'); - $isoCode = $options['iso_code']; - - if ($is_stock_management) { - $builder->add('quantity', NumberType::class, [ - 'required' => true, - 'label' => $this->translator->trans('Quantity', [], 'Admin.Catalog.Feature'), - ]); - } - - $builder->add('cost_price', MoneyType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Cost Price', [], 'Admin.Catalog.Feature'), - 'attr' => ['data-display-price-precision' => FormHelper::DEFAULT_PRICE_PRECISION], - 'currency' => $isoCode, - ]) - ->add('impact_on_weight', NumberType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Impact on weight', [], 'Admin.Catalog.Feature'), - ]) - ->add('impact_on_price_te', MoneyType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Impact on price (tax excl.)', [], 'Admin.Catalog.Feature'), - 'currency' => $isoCode, - ]) - ->add('impact_on_price_ti', MoneyType::class, [ - 'required' => false, - 'mapped' => false, - 'label' => $this->translator->trans('Impact on price (tax incl.)', [], 'Admin.Catalog.Feature'), - 'currency' => $isoCode, - ]) - ->add('date_availability', DatePickerType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Availability date', [], 'Admin.Catalog.Feature'), - 'attr' => ['class' => 'date', 'placeholder' => 'YYYY-MM-DD'], - ]) - ->add('reference', TextType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Reference', [], 'Admin.Catalog.Feature'), - 'empty_data' => '', - ]) - ->add('minimal_quantity', NumberType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Minimum quantity', [], 'Admin.Catalog.Feature'), - ]) - ->add('low_stock_threshold', NumberType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Low stock level', [], 'Admin.Catalog.Feature'), - ]) - ->add('low_stock_alert', CheckboxType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Send me an email when the quantity is below or equals this level', [], 'Admin.Catalog.Feature'), - ]); - } - - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults([ - 'validation_groups' => false, - 'iso_code' => '', - ]); - } - - public function getBlockPrefix() - { - return 'product_combination_bulk'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductCustomField.php b/src/PrestaShopBundle/Form/Admin/Product/ProductCustomField.php deleted file mode 100644 index 00136b2b7e936..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductCustomField.php +++ /dev/null @@ -1,112 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\TranslateType; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the product custom fields configuration form. - */ -class ProductCustomField extends CommonAbstractType -{ - private $translator; - /** - * @var object|LegacyContext - */ - private $legacyContext; - - /** - * Constructor. - * - * @param TranslatorInterface $translator - * @param LegacyContext $legacyContext - */ - public function __construct(TranslatorInterface $translator, LegacyContext $legacyContext) - { - $this->translator = $translator; - $this->legacyContext = $legacyContext; - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add( - 'id_customization_field', - FormType\HiddenType::class, - [ - 'required' => false, - ] - ) - ->add('label', TranslateType::class, [ - 'type' => FormType\TextType::class, - 'options' => ['constraints' => [ - new Assert\NotBlank(), - new Assert\Length(['min' => 2]), - ]], - 'locales' => $this->legacyContext->getLanguages(), - 'hideTabs' => true, - 'label' => $this->translator->trans('Label', [], 'Admin.Global'), - ]) - ->add('type', FormType\ChoiceType::class, [ - 'label' => $this->translator->trans('Type', [], 'Admin.Catalog.Feature'), - 'choices' => [ - $this->translator->trans('Text', [], 'Admin.Global') => 1, - $this->translator->trans('File', [], 'Admin.Global') => 0, - ], - 'attr' => [ - 'class' => 'c-select', - ], - 'required' => true, - ]) - ->add('require', FormType\CheckboxType::class, [ - 'label' => $this->translator->trans('Required', [], 'Admin.Global'), - 'required' => false, - ]); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_custom_field'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductInformation.php b/src/PrestaShopBundle/Form/Admin/Product/ProductInformation.php deleted file mode 100644 index b82ec524102e9..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductInformation.php +++ /dev/null @@ -1,416 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use Currency; -use Language; -use PrestaShop\PrestaShop\Adapter\Category\CategoryDataProvider; -use PrestaShop\PrestaShop\Adapter\Configuration; -use PrestaShop\PrestaShop\Adapter\Feature\FeatureDataProvider; -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShop\PrestaShop\Adapter\Manufacturer\ManufacturerDataProvider; -use PrestaShop\PrestaShop\Adapter\Product\ProductDataProvider; -use PrestaShop\PrestaShop\Core\Domain\Product\ProductSettings; -use PrestaShopBundle\Form\Admin\Category\SimpleCategory; -use PrestaShopBundle\Form\Admin\Feature\ProductFeature; -use PrestaShopBundle\Form\Admin\Type\ChoiceCategoriesTreeType; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\FormattedTextareaType; -use PrestaShopBundle\Form\Admin\Type\TranslateType; -use PrestaShopBundle\Form\Admin\Type\TypeaheadProductCollectionType; -use PrestaShopBundle\Form\Admin\Type\TypeaheadProductPackCollectionType; -use PrestaShopBundle\Form\FormHelper; -use PrestaShopBundle\Form\Validator\Constraints\TinyMceMaxLength; -use PrestaShopBundle\Service\Routing\Router; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormError; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the basic product information form. - */ -class ProductInformation extends CommonAbstractType -{ - /** - * @var array - */ - public $categories; - /** - * @var CategoryDataProvider - */ - public $categoryDataProvider; - /** - * @var Configuration - */ - public $configuration; - /** - * @var LegacyContext - */ - private $context; - /** - * @var Currency - */ - public $currency; - /** - * @var FeatureDataProvider - */ - public $featureDataProvider; - /** - * @var array - */ - private $locales; - /** - * @var ManufacturerDataProvider - */ - private $manufacturerDataProvider; - /** - * @var array - */ - private $manufacturers; - /** - * @var array - */ - public $nested_categories; - /** - * @var ProductDataProvider - */ - public $productDataProvider; - /** - * @var Router - */ - public $router; - /** - * @var TranslatorInterface - */ - public $translator; - - /** - * Constructor. - * - * @param TranslatorInterface $translator - * @param LegacyContext $legacyContext - * @param Router $router - * @param CategoryDataProvider $categoryDataProvider - * @param ProductDataProvider $productDataProvider - * @param FeatureDataProvider $featureDataProvider - * @param ManufacturerDataProvider $manufacturerDataProvider - */ - public function __construct( - $translator, - $legacyContext, - $router, - $categoryDataProvider, - $productDataProvider, - $featureDataProvider, - $manufacturerDataProvider - ) { - $this->context = $legacyContext; - $this->translator = $translator; - $this->router = $router; - $this->productDataProvider = $productDataProvider; - $this->categoryDataProvider = $categoryDataProvider; - $this->manufacturerDataProvider = $manufacturerDataProvider; - $this->featureDataProvider = $featureDataProvider; - - $this->configuration = new Configuration(); - $this->locales = $this->context->getLanguages(); - $this->currency = $this->context->getContext()->currency; - - $this->categories = FormHelper::formatDataChoicesList( - $this->categoryDataProvider->getAllCategoriesName( - $root_category = null, - $id_lang = false, - $active = false - ), - 'id_category' - ); - - $this->nested_categories = $this->categoryDataProvider->getNestedCategories( - $root_category = null, - $id_lang = false, - $active = false - ); - - $this->manufacturers = FormHelper::formatDataChoicesList( - $this->manufacturerDataProvider->getManufacturers( - $get_nb_products = false, - $id_lang = 0, - $active = true, - $p = false, - $n = false, - $all_group = false, - $group_by = true - ), - 'id_manufacturer' - ); - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $is_stock_management = $this->configuration->get('PS_STOCK_MANAGEMENT'); - $shortDescriptionLimit = (int) $this->configuration->get('PS_PRODUCT_SHORT_DESC_LIMIT'); - if ($shortDescriptionLimit <= 0) { - $shortDescriptionLimit = ProductSettings::MAX_DESCRIPTION_SHORT_LENGTH; - } - - $builder->add('type_product', FormType\ChoiceType::class, [ - 'choices' => [ - $this->translator->trans('Standard product', [], 'Admin.Catalog.Feature') => 0, - $this->translator->trans('Pack of products', [], 'Admin.Catalog.Feature') => 1, - $this->translator->trans('Virtual product', [], 'Admin.Catalog.Feature') => 2, - ], - 'attr' => [ - 'class' => 'custom-select', - ], - 'label' => $this->translator->trans('Type', [], 'Admin.Catalog.Feature'), - 'required' => true, - ]) - ->add('inputPackItems', TypeaheadProductPackCollectionType::class, [ - 'remote_url' => $this->context->getLegacyAdminLink('AdminProducts', true, ['ajax' => 1, 'action' => 'productsList', 'forceJson' => 1, 'excludeVirtuals' => 1, 'limit' => 20]) . '&q=%QUERY', - 'mapping_value' => 'id', - 'mapping_name' => 'name', - 'placeholder' => $this->translator->trans('Search for a product', [], 'Admin.Catalog.Help'), - 'template_collection' => ' -

%s

-
REF: %s
-
x%s
- - ', - 'required' => false, - 'label' => $this->translator->trans('Add products to your pack', [], 'Admin.Catalog.Feature'), - ]) - ->add('name', TranslateType::class, [ - 'type' => FormType\TextType::class, - 'help' => $this->translator->trans( - 'Invalid characters are: %invalidCharacters%', - ['%invalidCharacters%' => '<>;=#{}'], - 'Admin.Catalog.Feature' - ), - 'options' => [ - 'constraints' => [ - new Assert\Regex([ - 'pattern' => '/[<>;=#{}]/', - 'match' => false, - 'message' => $this->translator->trans( - 'This field contains invalid characters: %invalidCharacters%', - ['%invalidCharacters%' => '<>;=#{}'], - 'Admin.Catalog.Feature' - ), - ]), - new Assert\NotBlank(), - new Assert\Length(['min' => 3, 'max' => 128]), - ], - 'attr' => [ - 'placeholder' => $this->translator->trans('Enter your product name', [], 'Admin.Catalog.Help'), - 'class' => 'edit js-edit serp-default-title', - ], - ], - 'locales' => $this->locales, - 'hideTabs' => true, - 'label' => $this->translator->trans('Name', [], 'Admin.Global'), - ]) - ->add('description', TranslateType::class, [ - 'type' => FormattedTextareaType::class, - 'options' => [ - 'required' => false, - 'attr' => [ - 'class' => 'serp-default-description', - ], - 'constraints' => [ - new TinyMceMaxLength([ - 'max' => FormattedTextareaType::LIMIT_TEXT_UTF8, - ]), - ], - ], - 'locales' => $this->locales, - 'hideTabs' => true, - 'label' => $this->translator->trans('Description', [], 'Admin.Global'), - 'required' => false, - ]) - ->add('description_short', TranslateType::class, [ - 'type' => FormType\TextareaType::class, // https://github.com/symfony/symfony/issues/5906 - 'options' => [ - 'attr' => [ - 'class' => 'autoload_rte', - 'placeholder' => $this->translator->trans('The summary is a short sentence describing your product.
It will appears at the top of your shop\'s product page, in product lists, and in search engines\' results page (so it\'s important for SEO). To give more details about your product, use the "Description" tab.', [], 'Admin.Catalog.Help'), - 'counter' => $shortDescriptionLimit, - ], - 'constraints' => [ - new TinyMceMaxLength([ - 'max' => $shortDescriptionLimit, - ]), - ], - 'required' => false, - ], - 'locales' => $this->locales, - 'hideTabs' => true, - 'label' => $this->translator->trans('Short description', [], 'Admin.Catalog.Feature'), - 'required' => false, - ]) - //FEATURES & ATTRIBUTES - ->add('features', FormType\CollectionType::class, [ - 'entry_type' => ProductFeature::class, - 'prototype' => true, - 'allow_add' => true, - 'allow_delete' => true, - ]) - ->add('id_manufacturer', FormType\ChoiceType::class, [ - 'choices' => $this->manufacturers, - 'required' => false, - 'autocomplete' => true, - 'label' => $this->translator->trans('Brand', [], 'Admin.Catalog.Feature'), - 'placeholder' => $this->translator->trans('Choose a brand', [], 'Admin.Catalog.Feature'), - ]) - //RIGHT COL - ->add('active', FormType\CheckboxType::class, [ - 'label' => $this->translator->trans('Enabled', [], 'Admin.Global'), - 'required' => false, - ]) - ->add('price_shortcut', FormType\MoneyType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Pre-tax retail price', [], 'Admin.Catalog.Feature'), - 'currency' => $this->currency->iso_code, - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'float']), - ], - 'attr' => [], - ]) - ->add('price_ttc_shortcut', FormType\MoneyType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Retail price with tax', [], 'Admin.Catalog.Feature'), - 'mapped' => false, - 'currency' => $this->currency->iso_code, - ]); - if ($is_stock_management) { - $builder->add('qty_0_shortcut', FormType\NumberType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Quantity', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'numeric']), - ], - ]); - } - $builder->add('categories', ChoiceCategoriesTreeType::class, [ - 'label' => $this->translator->trans('Associated categories', [], 'Admin.Catalog.Feature'), - 'list' => $this->nested_categories, - 'valid_list' => $this->categories, - 'multiple' => true, - ]) - ->add('id_category_default', FormType\ChoiceType::class, [ - 'choices' => $this->categories, - 'expanded' => true, - 'multiple' => false, - 'required' => true, - 'label' => $this->translator->trans('Default category', [], 'Admin.Catalog.Feature'), - ]) - ->add('new_category', SimpleCategory::class, [ - 'ajax' => true, - 'required' => false, - 'mapped' => false, - 'constraints' => [], - 'label' => $this->translator->trans('Add a new category', [], 'Admin.Catalog.Feature'), - ]) - ->add('ignore', null, [ - 'mapped' => false, - ]) - ->add('related_products', TypeaheadProductCollectionType::class, [ - 'remote_url' => $this->context->getLegacyAdminLink('AdminProducts', true, ['ajax' => 1, 'action' => 'productsList', 'forceJson' => 1, 'disableCombination' => 1, 'exclude_packs' => 0, 'excludeVirtuals' => 0, 'limit' => 20]) . '&q=%QUERY', - 'mapping_value' => 'id', - 'mapping_name' => 'name', - 'placeholder' => $this->translator->trans('Search and add a related product', [], 'Admin.Catalog.Help'), - 'template_collection' => '%sclear', - 'required' => false, - 'label' => $this->translator->trans('Accessories', [], 'Admin.Catalog.Feature'), - ]); - - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { - $data = $event->getData(); - - if (!isset($data['type_product'])) { - $data['type_product'] = 0; - $event->setData($data); - } - - /* - * Remove duplicates to prevent SQL errors. - */ - if (isset($data['features'])) { - $ids = []; - foreach ($data['features'] as $idx => $feature) { - if (empty($feature['value'])) { - continue; - } - - $id = sprintf('%d-%d', $feature['feature'], $feature['value']); - if (in_array($id, $ids)) { - unset($data['features'][$idx]); - } else { - $ids[] = $id; - } - } - - $event->setData($data); - } - - //if product type is pack, check if inputPackItems is not empty - if ($data['type_product'] == 1) { - if (!isset($data['inputPackItems']) || empty($data['inputPackItems']['data'])) { - $form = $event->getForm(); - $error = $this->translator->trans( - 'This pack is empty. You must add at least one product item.', - [], - 'Admin.Catalog.Notification' - ); - $form->get('inputPackItems')->addError(new FormError($error)); - } - } - }); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_step1'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductOptions.php b/src/PrestaShopBundle/Form/Admin/Product/ProductOptions.php deleted file mode 100644 index e1066532698cc..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductOptions.php +++ /dev/null @@ -1,307 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\Isbn; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\TranslateType; -use PrestaShopBundle\Form\FormHelper; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; -use Symfony\Component\Validator\Constraints as Assert; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the product options form. - */ -class ProductOptions extends CommonAbstractType -{ - private $translator; - private $suppliers; - private $context; - private $router; - private $locales; - private $fullAttachmentList; - private $attachmentList; - - /** - * Constructor. - * - * @param object $translator - * @param object $legacyContext - * @param object $supplierDataProvider - * @param object $attachmentDataprovider - * @param object $router - */ - public function __construct( - $translator, - $legacyContext, - $supplierDataProvider, - $attachmentDataprovider, - $router - ) { - $this->context = $legacyContext; - $this->translator = $translator; - $this->locales = $legacyContext->getLanguages(); - $this->router = $router; - - $this->suppliers = FormHelper::formatDataChoicesList( - $supplierDataProvider->getSuppliers(), - 'id_supplier' - ); - - $this->fullAttachmentList = $attachmentDataprovider->getAllAttachments( - $this->context->getLanguages()[0]['id_lang'] - ); - $this->attachmentList = FormHelper::formatDataChoicesList( - $this->fullAttachmentList, - 'id_attachment', - 'file' - ); - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add('visibility', FormType\ChoiceType::class, [ - 'choices' => [ - $this->translator->trans('Everywhere', [], 'Admin.Catalog.Feature') => 'both', - $this->translator->trans('Catalog only', [], 'Admin.Catalog.Feature') => 'catalog', - $this->translator->trans('Search only', [], 'Admin.Catalog.Feature') => 'search', - $this->translator->trans('Nowhere', [], 'Admin.Catalog.Feature') => 'none', - ], - 'attr' => [ - 'class' => 'custom-select', - ], - 'required' => true, - 'label' => $this->translator->trans('Visibility', [], 'Admin.Catalog.Feature'), - ]) - ->add('tags', TranslateType::class, [ - 'type' => FormType\TextType::class, - 'options' => [ - 'attr' => [ - 'class' => 'tokenfield', - 'placeholder' => $this->translator->trans('Use a comma to create separate tags. E.g.: dress, cotton, party dresses.', [], 'Admin.Catalog.Help'), - ], - ], - 'locales' => $this->locales, - 'label' => $this->translator->trans('Tags', [], 'Admin.Catalog.Feature'), - ]) - ->add( - $builder->create( - 'display_options', - FormType\FormType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Display options', [], 'Admin.Catalog.Feature'), - ] - ) - ->add( - 'available_for_order', - FormType\CheckboxType::class, - [ - 'label' => $this->translator->trans('Available for order', [], 'Admin.Catalog.Feature'), - 'required' => false, - ] - ) - ->add( - 'show_price', - FormType\CheckboxType::class, - [ - 'label' => $this->translator->trans('Show price', [], 'Admin.Catalog.Feature'), - 'required' => false, - ] - ) - ->add( - 'online_only', - FormType\CheckboxType::class, - [ - 'label' => $this->translator->trans( - 'Web only (not sold in your retail store)', - [], - 'Admin.Catalog.Feature' - ), - 'required' => false, - ] - ) - ) - ->add('mpn', FormType\TextType::class, [ - 'required' => false, - 'label' => $this->translator->trans('MPN', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Length(['max' => 40]), - ], - 'empty_data' => '', - ]) - ->add('upc', FormType\TextType::class, [ - 'required' => false, - 'label' => $this->translator->trans('UPC barcode', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Regex('/^[0-9]{0,12}$/'), - ], - 'empty_data' => '', - ]) - ->add('ean13', FormType\TextType::class, [ - 'required' => false, - 'error_bubbling' => true, - 'label' => $this->translator->trans('EAN-13 or JAN barcode', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Regex('/^[0-9]{0,13}$/'), - ], - 'empty_data' => '', - ]) - ->add('isbn', FormType\TextType::class, [ - 'required' => false, - 'label' => $this->translator->trans('ISBN', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Regex(Isbn::VALID_PATTERN), - ], - 'empty_data' => '', - ]) - ->add('reference', FormType\TextType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Reference', [], 'Admin.Global'), - 'empty_data' => '', - ]) - ->add('show_condition', FormType\CheckboxType::class, [ - 'required' => false, - 'label' => $this->translator->trans('Display condition on product page', [], 'Admin.Catalog.Feature'), - ]) - ->add('condition', FormType\ChoiceType::class, [ - 'choices' => [ - $this->translator->trans('New', [], 'Admin.Catalog.Feature') => 'new', - $this->translator->trans('Used', [], 'Admin.Catalog.Feature') => 'used', - $this->translator->trans('Refurbished', [], 'Admin.Catalog.Feature') => 'refurbished', - ], - 'attr' => [ - 'class' => 'custom-select', - ], - 'required' => true, - 'label' => $this->translator->trans('Condition', [], 'Admin.Catalog.Feature'), - ]) - ->add('suppliers', FormType\ChoiceType::class, [ - 'choices' => $this->suppliers, - 'expanded' => true, - 'multiple' => true, - 'required' => false, - 'attr' => [ - 'class' => 'custom-select', - ], - 'label' => $this->translator->trans('Suppliers', [], 'Admin.Global'), - ]) - ->add('default_supplier', FormType\ChoiceType::class, [ - 'choices' => $this->suppliers, - 'expanded' => true, - 'multiple' => false, - 'required' => true, - 'attr' => [ - 'class' => 'custom-select', - ], - 'label' => $this->translator->trans('Default suppliers', [], 'Admin.Catalog.Feature'), - ]); - - foreach ($this->suppliers as $supplier => $id) { - $builder->add( - 'supplier_combination_' . $id, - FormType\CollectionType::class, - [ - 'entry_type' => ProductSupplierCombination::class, - 'entry_options' => [ - 'id_supplier' => $id, - 'id_currency' => $this->context->getContext()->currency->id, - ], - 'prototype' => true, - 'allow_add' => true, - 'required' => false, - 'label' => $supplier, - ] - ); - } - - $builder->add('custom_fields', FormType\CollectionType::class, [ - 'entry_type' => ProductCustomField::class, - 'label' => $this->translator->trans('Customization', [], 'Admin.Catalog.Feature'), - 'prototype' => true, - 'allow_add' => true, - 'allow_delete' => true, - ]); - - //Add product attachment form - $builder->add('attachment_product', ProductAttachement::class, [ - 'required' => false, - 'label' => $this->translator->trans('Attachment', [], 'Admin.Catalog.Feature'), - 'attr' => ['data-action' => $this->router->generate('admin_product_attachement_add_action', ['idProduct' => 1])], - ]); - - //Add attachment selectors - $builder->add('attachments', FormType\ChoiceType::class, [ - 'expanded' => true, - 'multiple' => true, - 'choices' => $this->attachmentList, - 'choice_label' => function ($choice, $key, $value) { - $attachmentKey = array_search($key, array_column($this->fullAttachmentList, 'file')); - - return $this->fullAttachmentList[$attachmentKey]['name']; - }, - 'required' => false, - 'attr' => [ - 'class' => 'custom-select', - 'data' => $this->fullAttachmentList, - ], - 'label' => $this->translator->trans('Attachments for this product:', [], 'Admin.Catalog.Feature'), - ]); - - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { - $data = $event->getData(); - - //If not supplier selected, remove all supplier combinations collection form - if (!isset($data['suppliers']) || count($data['suppliers']) == 0) { - $form = $event->getForm(); - foreach ($this->suppliers as $supplier => $id) { - $form->remove('supplier_combination_' . $id); - } - } - }); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_options'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductPrice.php b/src/PrestaShopBundle/Form/Admin/Product/ProductPrice.php deleted file mode 100644 index 76f8090f6cedf..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductPrice.php +++ /dev/null @@ -1,293 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use Currency; -use PrestaShop\PrestaShop\Adapter\Country\CountryDataProvider; -use PrestaShop\PrestaShop\Adapter\Currency\CurrencyDataProvider; -use PrestaShop\PrestaShop\Adapter\Group\GroupDataProvider; -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShop\PrestaShop\Adapter\Shop\Context; -use PrestaShop\PrestaShop\Adapter\Tax\TaxRuleDataProvider; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\FormHelper; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Routing\Router; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the product price form. - */ -class ProductPrice extends CommonAbstractType -{ - // When the form is used to create, the product does not yet exists - // however the ID is required for some fields so we use a default one: - public const DEFAULT_PRODUCT_ID_FOR_FORM_CREATION = 1; - - /** - * @var CountryDataProvider - */ - public $countryDataprovider; - /** - * @var Currency - */ - public $currency; - /** - * @var CurrencyDataProvider - */ - public $currencyDataprovider; - /** - * @var float - */ - public $eco_tax_rate; - /** - * @var GroupDataProvider - */ - public $groupDataprovider; - /** - * @var LegacyContext - */ - public $legacyContext; - /** - * @var Router - */ - public $router; - /** - * @var Context - */ - public $shopContextAdapter; - /** - * @var array - */ - public $tax_rules; - /** - * @var array[] - */ - public $tax_rules_rates; - /** - * @var TranslatorInterface - */ - public $translator; - - /** - * Constructor. - * - * @param TranslatorInterface $translator - * @param TaxRuleDataProvider $taxDataProvider - * @param Router $router - * @param Context $shopContextAdapter - * @param CountryDataProvider $countryDataprovider - * @param CurrencyDataProvider $currencyDataprovider - * @param GroupDataProvider $groupDataprovider - * @param LegacyContext $legacyContext - */ - public function __construct( - $translator, - $taxDataProvider, - $router, - $shopContextAdapter, - $countryDataprovider, - $currencyDataprovider, - $groupDataprovider, - $legacyContext - ) { - $this->translator = $translator; - $this->router = $router; - $this->shopContextAdapter = $shopContextAdapter; - $this->countryDataprovider = $countryDataprovider; - $this->currencyDataprovider = $currencyDataprovider; - $this->groupDataprovider = $groupDataprovider; - $this->legacyContext = $legacyContext; - $this->tax_rules_rates = $taxDataProvider->getTaxRulesGroupWithRates(); - $this->eco_tax_rate = $taxDataProvider->getProductEcotaxRate(); - $this->currency = $legacyContext->getContext()->currency; - $this->tax_rules = FormHelper::formatDataChoicesList( - $taxDataProvider->getTaxRulesGroups(true), - 'id_tax_rules_group' - ); - } - - /** - * {@inheritdoc} - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $this->tax_rules = [$this->translator->trans('No tax', [], 'Admin.Catalog.Feature') => 0] + $this->tax_rules; - - $builder->add( - 'price', - FormType\MoneyType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Retail price (tax excl.)', [], 'Admin.Catalog.Feature'), - 'attr' => ['data-display-price-precision' => FormHelper::DEFAULT_PRICE_PRECISION], - 'currency' => $this->currency->iso_code, - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'float']), - ], - ] - ) - ->add( - 'price_ttc', - FormType\MoneyType::class, - [ - 'required' => false, - 'mapped' => false, - 'label' => $this->translator->trans('Retail price (tax incl.)', [], 'Admin.Catalog.Feature'), - 'currency' => $this->currency->iso_code, - ] - ) - ->add( - 'ecotax', - FormType\MoneyType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Ecotax (tax incl.)', [], 'Admin.Catalog.Feature'), - 'currency' => $this->currency->iso_code, - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'float']), - ], - 'attr' => ['data-eco-tax-rate' => $this->eco_tax_rate], - ] - ) - ->add( - 'id_tax_rules_group', - FormType\ChoiceType::class, - [ - 'choices' => $this->tax_rules, - 'required' => true, - 'choice_attr' => function ($val) { - return [ - 'data-rates' => implode(',', $this->tax_rules_rates[$val]['rates']), - 'data-computation-method' => $this->tax_rules_rates[$val]['computation_method'], - ]; - }, - 'autocomplete' => true, - 'label' => $this->translator->trans('Tax rule', [], 'Admin.Catalog.Feature'), - ] - ) - ->add( - 'on_sale', - FormType\CheckboxType::class, - [ - 'required' => false, - 'label' => $this->translator->trans( - 'Display the "On sale!" flag on the product page, and on product listings.', - [], - 'Admin.Catalog.Feature' - ), - ] - ) - ->add( - 'wholesale_price', - FormType\MoneyType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Cost price (tax excl.)', [], 'Admin.Catalog.Feature'), - 'currency' => $this->currency->iso_code, - ] - ) - ->add( - 'unit_price', - FormType\MoneyType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Retail price per unit (tax excl.)', [], 'Admin.Catalog.Feature'), - 'currency' => $this->currency->iso_code, - ] - ) - ->add( - 'unity', - FormType\TextType::class, - [ - 'required' => false, - 'attr' => ['placeholder' => $this->translator->trans('Per kilo, per litre', [], 'Admin.Catalog.Help')], - ] - ) - ->add( - 'specific_price', - ProductSpecificPrice::class, - [ - 'id_product' => $options['id_product'], - ] - ) - ->add( - 'specificPricePriorityToAll', - FormType\CheckboxType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Apply to all products', [], 'Admin.Catalog.Feature'), - ] - ); - - //generates fields for price priority - $specificPricePriorityChoices = [ - $this->translator->trans('Store', [], 'Admin.Global') => 'id_shop', - $this->translator->trans('Currency', [], 'Admin.Global') => 'id_currency', - $this->translator->trans('Country', [], 'Admin.Global') => 'id_country', - $this->translator->trans('Group', [], 'Admin.Global') => 'id_group', - ]; - - for ($i = 0, $iMax = count($specificPricePriorityChoices); $i < $iMax; ++$i) { - $builder->add( - 'specificPricePriority_' . $i, - FormType\ChoiceType::class, - [ - 'choices' => $specificPricePriorityChoices, - 'required' => true, - ] - ); - } - } - - /** - * {@inheritdoc} - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults([ - 'id_product' => self::DEFAULT_PRODUCT_ID_FOR_FORM_CREATION, - ]); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_price'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductQuantity.php b/src/PrestaShopBundle/Form/Admin/Product/ProductQuantity.php deleted file mode 100644 index 874ee43ef56a2..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductQuantity.php +++ /dev/null @@ -1,339 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use Language; -use Pack; -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShop\PrestaShop\Core\ConfigurationInterface; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\DatePickerType; -use PrestaShopBundle\Form\Admin\Type\TranslateType; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; -use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the product quantity form. - */ -class ProductQuantity extends CommonAbstractType -{ - /** - * @var ConfigurationInterface - */ - public $configuration; - /** - * @var LegacyContext - */ - public $legacyContext; - /** - * @var array - */ - public $locales; - /** - * @var UrlGeneratorInterface - */ - private $router; - /** - * @var TranslatorInterface - */ - private $translator; - - /** - * Constructor. - * - * @param TranslatorInterface $translator - * @param UrlGeneratorInterface $router - * @param LegacyContext $legacyContext - * @param ConfigurationInterface $configuration - */ - public function __construct( - TranslatorInterface $translator, - UrlGeneratorInterface $router, - LegacyContext $legacyContext, - ConfigurationInterface $configuration - ) { - $this->router = $router; - $this->translator = $translator; - $this->legacyContext = $legacyContext; - $this->configuration = $configuration; - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $this->locales = $this->legacyContext->getLanguages(); - $is_stock_management = $this->configuration->get('PS_STOCK_MANAGEMENT'); - $builder - ->add( - 'attributes', - FormType\TextType::class, - [ - 'attr' => [ - 'class' => 'tokenfield', - 'data-minLength' => 1, - 'placeholder' => $this->translator->trans( - 'Combine several attributes, e.g.: "Size: all", "Color: red".', - [], - 'Admin.Catalog.Help' - ), - 'data-prefetch' => $this->router->generate('admin_attribute_get_all'), - 'data-action' => $this->router->generate('admin_attribute_generator'), - ], - 'label' => $this->translator->trans('Create combinations', [], 'Admin.Catalog.Feature'), - 'empty_data' => '', - ] - ) - ->add( - 'pack_stock_type', - FormType\ChoiceType::class - ); - - if ($is_stock_management) { - $builder->add( - 'qty_0', - FormType\NumberType::class, - [ - 'required' => true, - 'label' => $this->translator->trans('Quantity', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'numeric']), - ], - ] - ); - } - $builder - ->add( - 'out_of_stock', - FormType\ChoiceType::class - ) - ->add( - 'minimal_quantity', - FormType\NumberType::class, - [ - 'required' => true, - 'default_empty_data' => 1, - 'label' => $this->translator->trans('Minimum quantity for sale', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Positive(), - new Assert\Type(['type' => 'numeric']), - ], - 'attr' => [ - 'min' => 1, - ], - 'html5' => true, - ] - ) - ->add( - 'location', - FormType\TextType::class, - [ - 'label' => $this->translator->trans('Stock location', [], 'Admin.Catalog.Feature'), - ] - ) - ->add( - 'low_stock_threshold', - FormType\NumberType::class, - [ - 'label' => $this->translator->trans('Low stock level', [], 'Admin.Catalog.Feature'), - 'attr' => [ - 'placeholder' => $this->translator->trans('Leave empty to disable', [], 'Admin.Catalog.Help'), - ], - 'constraints' => [ - new Assert\Type(['type' => 'numeric']), - ], - ] - ) - ->add( - 'low_stock_alert', - FormType\CheckboxType::class, - [ - 'label' => $this->translator->trans( - 'Send me an email when the quantity is below or equals this level', - [], - 'Admin.Catalog.Feature' - ), - 'constraints' => [ - new Assert\Type(['type' => 'bool']), - ], - ] - ) - ->add( - 'available_now', - TranslateType::class, - [ - 'type' => FormType\TextType::class, - 'options' => [], - 'locales' => $this->locales, - 'hideTabs' => true, - 'label' => $this->translator->trans('Label when in stock', [], 'Admin.Catalog.Feature'), - ] - ) - ->add( - 'available_later', - TranslateType::class, - [ - 'type' => FormType\TextType::class, - 'options' => [], - 'locales' => $this->locales, - 'hideTabs' => true, - 'label' => $this->translator->trans( - 'Label when out of stock (and backorders allowed)', - [], - 'Admin.Catalog.Feature' - ), - ] - ) - ->add( - 'available_date', - DatePickerType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Availability date', [], 'Admin.Catalog.Feature'), - 'attr' => ['placeholder' => 'YYYY-MM-DD'], - ] - ) - ->add( - 'virtual_product', - ProductVirtual::class, - [ - 'required' => false, - 'label' => $this->translator->trans( - 'Add downloadable file', - [], - 'Admin.Catalog.Feature' - ), - ] - ); - - $builder->addEventListener( - FormEvents::PRE_SET_DATA, - function (FormEvent $event) { - $form = $event->getForm(); - - //Manage out_of_stock field with contextual values/label - $defaultChoiceLabel = $this->translator->trans( - 'Use default behavior', - [], - 'Admin.Catalog.Feature' - ) . ' ('; - $defaultChoiceLabel .= $this->configuration->get('PS_ORDER_OUT_OF_STOCK') == 1 ? - $this->translator->trans('Allow orders', [], 'Admin.Catalog.Feature') : - $this->translator->trans('Deny orders', [], 'Admin.Catalog.Feature'); - $defaultChoiceLabel .= ')'; - - $form->add( - 'out_of_stock', - FormType\ChoiceType::class, - [ - 'choices' => [ - $this->translator->trans('Deny orders', [], 'Admin.Catalog.Feature') => '0', - $this->translator->trans('Allow orders', [], 'Admin.Catalog.Feature') => '1', - $defaultChoiceLabel => '2', - ], - 'expanded' => true, - 'required' => false, - 'placeholder' => false, - 'label' => $this->translator->trans('When out of stock', [], 'Admin.Catalog.Feature'), - ] - ); - - //Manage out_of_stock field with contextual values/label - $pack_stock_type = $this->configuration->get('PS_PACK_STOCK_TYPE'); - $defaultChoiceLabel = $this->translator->trans('Default', [], 'Admin.Global') . ': '; - if ($pack_stock_type == Pack::STOCK_TYPE_PACK_ONLY) { - $defaultChoiceLabel .= $this->translator->trans( - 'Decrement pack only.', - [], - 'Admin.Catalog.Feature' - ); - } elseif ($pack_stock_type == Pack::STOCK_TYPE_PRODUCTS_ONLY) { - $defaultChoiceLabel .= $this->translator->trans( - 'Decrement products in pack only.', - [], - 'Admin.Catalog.Feature' - ); - } else { - $defaultChoiceLabel .= $this->translator->trans('Decrement both.', [], 'Admin.Catalog.Feature'); - } - - $form->add( - 'pack_stock_type', - FormType\ChoiceType::class, - [ - 'choices' => [ - $this->translator->trans('Decrement pack only.', [], 'Admin.Catalog.Feature') => 0, - $this->translator->trans('Decrement products in pack only.', [], 'Admin.Catalog.Feature') => 1, - $this->translator->trans('Decrement both.', [], 'Admin.Catalog.Feature') => 2, - $defaultChoiceLabel => 3, - ], - 'expanded' => false, - 'required' => true, - 'placeholder' => false, - 'label' => $this->translator->trans('Pack quantities', [], 'Admin.Catalog.Feature'), - ] - ); - } - ); - } - - /** - * {@inheritdoc} - * - * Configure options - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults( - [ - 'allow_extra_fields' => true, - ] - ); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_quantity'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductSeo.php b/src/PrestaShopBundle/Form/Admin/Product/ProductSeo.php deleted file mode 100644 index 7d1735de0ec50..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductSeo.php +++ /dev/null @@ -1,224 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\RedirectType; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\TranslateType; -use PrestaShopBundle\Form\Admin\Type\TypeaheadProductCollectionType; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Routing\Router; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the product SEO form. - */ -class ProductSeo extends CommonAbstractType -{ - /** - * @var LegacyContext - */ - public $context; - /** - * @var UrlGeneratorInterface - */ - private $router; - /** - * @var TranslatorInterface - */ - public $translator; - - /** - * Constructor. - * - * @param TranslatorInterface $translator - * @param LegacyContext $legacyContext - * @param Router $router - */ - public function __construct(TranslatorInterface $translator, LegacyContext $legacyContext, UrlGeneratorInterface $router) - { - $this->translator = $translator; - $this->context = $legacyContext; - $this->router = $router; - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $locales = $this->context->getLanguages(); - $remoteUrls = [ - RedirectType::TYPE_PRODUCT_PERMANENT => $this->context->getLegacyAdminLink('AdminProducts', true, ['ajax' => 1, 'action' => 'productsList', 'forceJson' => 1, 'disableCombination' => 1, 'exclude_packs' => 0, 'excludeVirtuals' => 0, 'limit' => 20]) . '&q=%QUERY', - RedirectType::TYPE_PRODUCT_TEMPORARY => $this->context->getLegacyAdminLink('AdminProducts', true, ['ajax' => 1, 'action' => 'productsList', 'forceJson' => 1, 'disableCombination' => 1, 'exclude_packs' => 0, 'excludeVirtuals' => 0, 'limit' => 20]) . '&q=%QUERY', - RedirectType::TYPE_CATEGORY_PERMANENT => $this->router->generate('admin_get_ajax_categories') . '&query=%QUERY', - RedirectType::TYPE_CATEGORY_TEMPORARY => $this->router->generate('admin_get_ajax_categories') . '&query=%QUERY', - ]; - - $builder->add( - 'meta_title', - TranslateType::class, - [ - 'type' => FormType\TextType::class, - 'options' => [ - 'attr' => [ - 'placeholder' => $this->translator->trans('To have a different title from the product name, enter it here.', [], 'Admin.Catalog.Help'), - 'counter' => 70, - 'counter_type' => 'recommended', - 'class' => 'serp-watched-title', - ], - 'required' => false, - ], - 'locales' => $this->context->getLanguages(), - 'hideTabs' => true, - 'label' => $this->translator->trans('Meta title', [], 'Admin.Catalog.Feature'), - 'label_attr' => [ - 'popover' => $this->translator->trans('Public title for the product\'s page, and for search engines. Leave blank to use the product name. The number of remaining characters is displayed to the left of the field.', [], 'Admin.Catalog.Help'), - 'popover_placement' => 'right', - 'class' => 'px-0', - ], - 'required' => false, - ] - ) - ->add( - 'meta_description', - TranslateType::class, - [ - 'type' => FormType\TextareaType::class, - 'options' => [ - 'attr' => [ - 'placeholder' => $this->translator->trans('To have a different description than your product summary in search results pages, write it here.', [], 'Admin.Catalog.Help'), - 'counter' => 160, - 'counter_type' => 'recommended', - 'class' => 'serp-watched-description', - ], - 'required' => false, - ], - 'locales' => $locales, - 'hideTabs' => true, - 'label' => $this->translator->trans('Meta description', [], 'Admin.Catalog.Feature'), - 'label_attr' => [ - 'popover' => $this->translator->trans('This description will appear in search engines. You need a single sentence, shorter than 160 characters (including spaces)', [], 'Admin.Catalog.Help'), - 'popover_placement' => 'right', - 'class' => 'px-0', - ], - 'required' => false, - ] - ) - ->add( - 'link_rewrite', - TranslateType::class, - [ - 'type' => FormType\TextType::class, - 'options' => [ - 'attr' => [ - 'class' => 'serp-watched-url', - ], - ], - 'locales' => $locales, - 'hideTabs' => true, - 'label' => $this->translator->trans('Friendly URL', [], 'Admin.Catalog.Feature'), - ] - ) - ->add( - 'redirect_type', - FormType\ChoiceType::class, - [ - 'choices' => [ - $this->translator->trans('Default behavior from configuration', [], 'Admin.Catalog.Feature') => RedirectType::TYPE_DEFAULT, - $this->translator->trans('No redirection (200), display product', [], 'Admin.Catalog.Feature') => RedirectType::TYPE_SUCCESS_DISPLAYED, - $this->translator->trans('No redirection (404), display product', [], 'Admin.Catalog.Feature') => RedirectType::TYPE_NOT_FOUND_DISPLAYED, - $this->translator->trans('No redirection (410), display product', [], 'Admin.Catalog.Feature') => RedirectType::TYPE_GONE_DISPLAYED, - $this->translator->trans('No redirection (404), display error page', [], 'Admin.Catalog.Feature') => RedirectType::TYPE_NOT_FOUND, - $this->translator->trans('No redirection (410), display error page', [], 'Admin.Catalog.Feature') => RedirectType::TYPE_GONE, - $this->translator->trans('Permanent redirection to a category (301)', [], 'Admin.Catalog.Feature') => RedirectType::TYPE_CATEGORY_PERMANENT, - $this->translator->trans('Temporary redirection to a category (302)', [], 'Admin.Catalog.Feature') => RedirectType::TYPE_CATEGORY_TEMPORARY, - $this->translator->trans('Permanent redirection to a product (301)', [], 'Admin.Catalog.Feature') => RedirectType::TYPE_PRODUCT_PERMANENT, - $this->translator->trans('Temporary redirection to a product (302)', [], 'Admin.Catalog.Feature') => RedirectType::TYPE_PRODUCT_TEMPORARY, - ], - 'choice_attr' => function ($val, $key, $index) use ($remoteUrls) { - if (array_key_exists($index, $remoteUrls)) { - return ['data-remoteurl' => $remoteUrls[$index]]; - } - - return []; - }, - 'required' => true, - 'label' => $this->translator->trans('Redirection when offline', [], 'Admin.Catalog.Feature'), - 'attr' => [ - 'data-labelproduct' => $this->translator->trans('Target product', [], 'Admin.Catalog.Feature'), - 'data-placeholderproduct' => $this->translator->trans('To which product should the page redirect?', [], 'Admin.Catalog.Help'), - 'data-labelcategory' => $this->translator->trans('Target category', [], 'Admin.Catalog.Feature'), - 'data-placeholdercategory' => $this->translator->trans('To which category should the page redirect?', [], 'Admin.Catalog.Help'), - 'data-hintcategory' => $this->translator->trans('By default, the main category will be used if no category is selected.', [], 'Admin.Catalog.Help'), - ], - ] - ) - ->add( - 'id_type_redirected', - TypeaheadProductCollectionType::class, - [ - 'remote_url' => $this->context->getLegacyAdminLink('AdminProducts', true, ['ajax' => 1, 'action' => 'productsList', 'forceJson' => 1, 'disableCombination' => 1, 'exclude_packs' => 0, 'excludeVirtuals' => 0, 'limit' => 20]) . '&q=%QUERY', - 'mapping_value' => 'id', - 'mapping_name' => 'name', - 'mapping_type' => $options['mapping_type'], - 'template_collection' => '%sclear', - 'limit' => 1, - 'required' => false, - 'label' => $this->translator->trans('Target', [], 'Admin.Catalog.Feature'), - ] - ); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_seo'; - } - - /** - * {@inheritdoc} - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults([ - 'mapping_type' => 'product', - ]); - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php b/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php deleted file mode 100644 index 510ba033c2f92..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductShipping.php +++ /dev/null @@ -1,266 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use Currency; -use PrestaShop\PrestaShop\Adapter\Carrier\CarrierDataProvider; -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\TranslateType; -use PrestaShopBundle\Form\FormHelper; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the product shipping form. - */ -class ProductShipping extends CommonAbstractType -{ - /** - * @var array - */ - private $carriersChoices; - /** - * @var Currency - */ - public $currency; - /** - * @var LegacyContext - */ - public $legacyContext; - /** - * @var array - */ - public $locales; - /** - * @var TranslatorInterface - */ - public $translator; - /** - * @var string - */ - private $dimensionUnit; - /** - * @var string - */ - private $weightUnit; - - /** - * Constructor. - * - * @param TranslatorInterface $translator - * @param LegacyContext $legacyContext - * @param CarrierDataProvider $carrierDataProvider - * @param string $dimensionUnit - * @param string $weightUnit - */ - public function __construct( - TranslatorInterface $translator, - LegacyContext $legacyContext, - CarrierDataProvider $carrierDataProvider, - string $dimensionUnit, - string $weightUnit - ) { - $this->translator = $translator; - $this->legacyContext = $legacyContext; - $this->currency = $legacyContext->getContext()->currency; - $this->locales = $this->legacyContext->getLanguages(); - - $carriers = $carrierDataProvider->getCarriers( - $this->locales[0]['id_lang'], - false, - false, - false, - null, - $carrierDataProvider->getAllCarriersConstant() - ); - $this->carriersChoices = []; - foreach ($carriers as $carrier) { - $choiceId = $carrier['id_carrier'] . ' - ' . $carrier['name']; - if (!empty($carrier['delay'])) { - $choiceId .= ' (' . $carrier['delay'] . ')'; - } - - $this->carriersChoices[$choiceId] = $carrier['id_reference']; - } - $this->dimensionUnit = $dimensionUnit; - $this->weightUnit = $weightUnit; - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add( - 'width', - FormType\NumberType::class, - [ - 'unit' => $this->dimensionUnit, - 'required' => false, - 'label' => $this->translator->trans('Width', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'numeric']), - ], - ] - ) - ->add( - 'height', - FormType\NumberType::class, - [ - 'unit' => $this->dimensionUnit, - 'required' => false, - 'label' => $this->translator->trans('Height', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'numeric']), - ], - ] - ) - ->add( - 'depth', - FormType\NumberType::class, - [ - 'unit' => $this->dimensionUnit, - 'required' => false, - 'label' => $this->translator->trans('Depth', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'numeric']), - ], - ] - ) - ->add( - 'weight', - FormType\NumberType::class, - [ - 'unit' => $this->weightUnit, - 'scale' => FormHelper::DEFAULT_WEIGHT_PRECISION, - 'required' => false, - 'label' => $this->translator->trans('Weight', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'numeric']), - ], - ] - ) - ->add( - 'additional_shipping_cost', - FormType\MoneyType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Shipping fees', [], 'Admin.Catalog.Feature'), - 'currency' => $this->currency->iso_code, - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'float']), - ], - ] - ) - ->add( - 'selectedCarriers', - FormType\ChoiceType::class, - [ - 'choices' => $this->carriersChoices, - 'expanded' => true, - 'multiple' => true, - 'required' => false, - 'label' => $this->translator->trans('Available carriers', [], 'Admin.Catalog.Feature'), - ] - ) - ->add( - 'additional_delivery_times', - FormType\ChoiceType::class, - [ - 'choices' => [ - $this->translator->trans('None', [], 'Admin.Catalog.Feature') => 0, - $this->translator->trans('Default delivery time', [], 'Admin.Catalog.Feature') => 1, - $this->translator->trans('Specific delivery time for this product', [], 'Admin.Catalog.Feature') => 2, - ], - 'expanded' => true, - 'multiple' => false, - 'required' => false, - 'placeholder' => null, - 'preferred_choices' => ['default'], - 'label' => $this->translator->trans('Delivery time', [], 'Admin.Catalog.Feature'), - ] - ) - ->add( - 'delivery_out_stock', - TranslateType::class, - [ - 'type' => FormType\TextType::class, - 'options' => [ - 'attr' => [ - 'placeholder' => $this->translator->trans('Delivered within 5-7 days', [], 'Admin.Catalog.Feature'), - ], - ], - 'locales' => $this->locales, - 'hideTabs' => true, - 'required' => false, - 'label' => $this->translator->trans( - 'Delivery time of out-of-stock products with allowed orders:', - [], - 'Admin.Catalog.Feature' - ), - ] - ) - ->add( - 'delivery_in_stock', - TranslateType::class, - [ - 'type' => FormType\TextType::class, - 'options' => [ - 'attr' => [ - 'placeholder' => $this->translator->trans('Delivered within 3-4 days', [], 'Admin.Catalog.Feature'), - ], - ], - 'locales' => $this->locales, - 'hideTabs' => true, - 'required' => false, - 'label' => $this->translator->trans('Delivery time of in-stock products:', [], 'Admin.Catalog.Feature'), - ] - ); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_shipping'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductSpecificPrice.php b/src/PrestaShopBundle/Form/Admin/Product/ProductSpecificPrice.php deleted file mode 100644 index 3fef041fe81ba..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductSpecificPrice.php +++ /dev/null @@ -1,380 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use Currency; -use PrestaShop\PrestaShop\Adapter\Country\CountryDataProvider; -use PrestaShop\PrestaShop\Adapter\Customer\CustomerDataProvider; -use PrestaShop\PrestaShop\Adapter\Group\GroupDataProvider; -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShop\PrestaShop\Adapter\Shop\Context; -use PrestaShop\PrestaShop\Core\Currency\CurrencyDataProviderInterface; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\DatePickerType; -use PrestaShopBundle\Form\Admin\Type\TypeaheadCustomerCollectionType; -use PrestaShopBundle\Form\FormHelper; -use Symfony\Bundle\FrameworkBundle\Routing\Router; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; -use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the basic product specific prices form. - */ -class ProductSpecificPrice extends CommonAbstractType -{ - /** - * @var LegacyContext - */ - public $context; - /** - * @var array - */ - private $countries; - /** - * @var array - */ - private $currencies; - /** - * @var Currency - */ - public $currency; - /** - * @var CustomerDataProvider - */ - public $customerDataProvider; - /** - * @var array - */ - private $groups; - /** - * @var array - */ - public $locales; - /** - * @var Router - */ - public $router; - /** - * @var array - */ - public $shops; - /** - * @var TranslatorInterface - */ - public $translator; - - /** - * Constructor. - * - * @param Router $router - * @param TranslatorInterface $translator - * @param Context $shopContextAdapter - * @param CountryDataProvider $countryDataprovider - * @param CurrencyDataProviderInterface $currencyDataprovider - * @param GroupDataProvider $groupDataprovider - * @param LegacyContext $legacyContext - * @param CustomerDataProvider $customerDataProvider - */ - public function __construct( - $router, - $translator, - $shopContextAdapter, - $countryDataprovider, - $currencyDataprovider, - $groupDataprovider, - $legacyContext, - $customerDataProvider - ) { - $this->router = $router; - $this->translator = $translator; - $this->context = $legacyContext; - $this->locales = $legacyContext->getLanguages(); - $this->shops = FormHelper::formatDataChoicesList($shopContextAdapter->getShops(), 'id_shop'); - $this->countries = FormHelper::formatDataChoicesList( - $countryDataprovider->getCountries($this->locales[0]['id_lang']), - 'id_country' - ); - $this->currencies = FormHelper::formatDataChoicesList( - $currencyDataprovider->getCurrencies(), - 'id_currency' - ); - $this->groups = FormHelper::formatDataChoicesList( - $groupDataprovider->getGroups($this->locales[0]['id_lang']), - 'id_group' - ); - $this->currency = $legacyContext->getContext()->currency; - $this->customerDataProvider = $customerDataProvider; - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - //If context multi-shop, hide shop selector - //Else show selector - if (count($this->shops) == 1) { - $builder->add( - 'sp_id_shop', - FormType\HiddenType::class, - [ - 'required' => false, - ] - ); - } else { - $builder->add( - 'sp_id_shop', - FormType\ChoiceType::class, - [ - 'choices' => $this->shops, - 'required' => false, - 'label' => false, - 'placeholder' => $this->translator->trans('All stores', [], 'Admin.Global'), - ] - ); - } - - $builder->add( - 'sp_id_currency', - FormType\ChoiceType::class, - [ - 'choices' => $this->currencies, - 'required' => false, - 'label' => false, - 'autocomplete' => true, - 'placeholder' => $this->translator->trans('All currencies', [], 'Admin.Global'), - ] - ) - ->add( - 'sp_id_country', - FormType\ChoiceType::class, - [ - 'choices' => $this->countries, - 'required' => false, - 'label' => false, - 'autocomplete' => true, - 'placeholder' => $this->translator->trans('All countries', [], 'Admin.Global'), - ] - ) - ->add( - 'sp_id_group', - FormType\ChoiceType::class, - [ - 'choices' => $this->groups, - 'required' => false, - 'label' => false, - 'autocomplete' => true, - 'placeholder' => $this->translator->trans('All groups', [], 'Admin.Global'), - ] - ) - ->add( - 'sp_id_customer', - TypeaheadCustomerCollectionType::class, - [ - // "%QUERY" is appended to url in order to avoid "%" sign being encoded into "%25", - // it used as a placeholder to replace with actual query in JS - 'remote_url' => $this->router->generate('admin_customers_search', ['sf2' => 1]) . '&customer_search=%QUERY', - 'mapping_value' => 'id_customer', - 'mapping_name' => 'fullname_and_email', - 'placeholder' => $this->translator->trans('All customers', [], 'Admin.Global'), - 'template_collection' => '
%s
clear
', - 'limit' => 1, - 'required' => false, - 'label' => $this->translator->trans('Add customer', [], 'Admin.Catalog.Feature'), - ] - ) - ->add( - 'sp_id_product_attribute', - FormType\ChoiceType::class, - [ - 'choices' => [], - 'required' => false, - 'placeholder' => $this->translator->trans('Apply to all combinations', [], 'Admin.Catalog.Feature'), - 'label' => $this->translator->trans('Combinations', [], 'Admin.Catalog.Feature'), - 'attr' => [ - 'data-action' => $this->router->generate('admin_get_product_combinations', ['idProduct' => $options['id_product']]), - // used to force selected select option after options have been loaded - 'data-selected-attribute' => (array_keys($options, 'selected_product_attribute')) ? $options['selected_product_attribute'] : '0', - ], - ] - ) - ->add( - 'sp_from', - DatePickerType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Available from', [], 'Admin.Catalog.Feature'), - 'attr' => ['placeholder' => 'YYYY-MM-DD HH:mm:ss'], - 'date_format' => 'YYYY-MM-DD HH:mm:ss', - ] - ) - ->add( - 'sp_to', - DatePickerType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('to', [], 'Admin.Global'), - 'attr' => ['placeholder' => 'YYYY-MM-DD HH:mm:ss'], - 'date_format' => 'YYYY-MM-DD HH:mm:ss', - ] - ) - ->add( - 'sp_from_quantity', - FormType\NumberType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Starting at', [], 'Admin.Catalog.Feature'), - 'constraints' => [ - new Assert\Type(['type' => 'numeric']), - ], - 'unit' => $this->translator->trans('Unit(s)', [], 'Admin.Catalog.Feature'), - ] - ) - ->add( - 'sp_price', - FormType\MoneyType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('Product price (tax excl.)', [], 'Admin.Catalog.Feature'), - 'attr' => ['class' => 'price'], - 'currency' => $this->currency->iso_code, - 'disabled' => true, - ] - ) - ->add( - 'leave_bprice', - FormType\CheckboxType::class, - [ - 'label' => $this->translator->trans('Leave initial price', [], 'Admin.Catalog.Feature'), - 'required' => false, - ] - ) - ->add( - 'sp_reduction', - FormType\NumberType::class, - [ - 'label' => $this->translator->trans('Reduction', [], 'Admin.Catalog.Feature'), - 'required' => false, - ] - ) - ->add( - 'sp_reduction_type', - FormType\ChoiceType::class, - [ - 'label' => $this->translator->trans('Reduction type', [], 'Admin.Catalog.Feature'), - 'choices' => [ - $this->currency->getSign() => 'amount', - $this->translator->trans('%', [], 'Admin.Global') => 'percentage', - ], - 'required' => true, - ] - ) - ->add( - 'sp_reduction_tax', - FormType\ChoiceType::class, - [ - 'label' => $this->translator->trans('Reduction tax', [], 'Admin.Catalog.Feature'), - 'choices' => [ - $this->translator->trans('Tax excluded', [], 'Admin.Catalog.Feature') => '0', - $this->translator->trans('Tax included', [], 'Admin.Catalog.Feature') => '1', - ], - 'required' => true, - ] - ) - ->add( - 'save', - FormType\ButtonType::class, - [ - 'label' => $this->translator->trans('Apply', [], 'Admin.Actions'), - 'attr' => ['class' => 'btn-outline-primary js-save'], - ] - ) - ->add( - 'cancel', - FormType\ButtonType::class, - [ - 'label' => $this->translator->trans('Cancel', [], 'Admin.Actions'), - 'attr' => ['class' => 'btn-outline-secondary js-cancel'], - ] - ); - // - // ResetType can't be used because the product page is wrapped - // inside a big form: reset a specific price form the "right" way - // will reset the global form. - // - - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { - $data = $event->getData(); - - if (empty($data['sp_id_product_attribute'])) { - return; - } - - $form = $event->getForm(); - - //bypass SF validation, define submitted value in choice list - $form->add( - 'sp_id_product_attribute', - FormType\ChoiceType::class, - [ - 'choices' => [$data['sp_id_product_attribute'] => ''], - 'required' => false, - ] - ); - }); - } - - /** - * {@inheritdoc} - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults([ - 'id_product' => 1, // 1 is default value for new form - 'selected_product_attribute' => '0', // used to force selected select option after options have been loaded - ]); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_combination'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductSupplierCombination.php b/src/PrestaShopBundle/Form/Admin/Product/ProductSupplierCombination.php deleted file mode 100644 index c6e8021a0ddb5..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductSupplierCombination.php +++ /dev/null @@ -1,128 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use PrestaShop\PrestaShop\Core\Currency\CurrencyDataProviderInterface; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\FormHelper; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\OptionsResolver\OptionsResolver; -use Symfony\Component\Validator\Constraints as Assert; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the basic product suppliers form. - */ -class ProductSupplierCombination extends CommonAbstractType -{ - /** - * @var CurrencyDataProviderInterface - */ - private $currencyDataProvider; - - /** - * @param CurrencyDataProviderInterface $currencyDataProvider - */ - public function __construct(CurrencyDataProviderInterface $currencyDataProvider) - { - $this->currencyDataProvider = $currencyDataProvider; - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add( - 'supplier_reference', - FormType\TextType::class, - [ - 'required' => false, - 'label' => null, - 'empty_data' => '', - ] - ) - ->add( - 'product_price', - FormType\MoneyType::class, - [ - 'required' => false, - 'constraints' => [ - new Assert\NotBlank(), - new Assert\Type(['type' => 'float']), - ], - ] - ) - ->add( - 'product_price_currency', - FormType\ChoiceType::class, - [ - 'choices' => FormHelper::formatDataChoicesList($this->currencyDataProvider->getCurrencies(), 'id_currency'), - 'required' => true, - 'attr' => [ - 'class' => 'custom-select', - ], - ] - ) - ->add('id_product_attribute', FormType\HiddenType::class) - ->add('product_id', FormType\HiddenType::class) - ->add('supplier_id', FormType\HiddenType::class); - - //set default minimal values for collection prototype - $builder->setData([ - 'product_price' => 0, - 'supplier_id' => $options['id_supplier'], - 'product_price_currency' => $options['id_currency'], - ]); - } - - /** - * {@inheritdoc} - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults([ - 'id_supplier' => null, - ]); - - $resolver->setRequired('id_currency'); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_supplier_combination'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Product/ProductVirtual.php b/src/PrestaShopBundle/Form/Admin/Product/ProductVirtual.php deleted file mode 100644 index 98dac92866c82..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Product/ProductVirtual.php +++ /dev/null @@ -1,169 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Product; - -use PrestaShop\PrestaShop\Core\ConfigurationInterface; -use PrestaShopBundle\Form\Admin\Type\CommonAbstractType; -use PrestaShopBundle\Form\Admin\Type\DatePickerType; -use Symfony\Component\Form\Extension\Core\Type as FormType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormEvent; -use Symfony\Component\Form\FormEvents; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to generate the virtual product. - */ -class ProductVirtual extends CommonAbstractType -{ - private $translator; - /** - * @var ConfigurationInterface - */ - private $configuration; - - /** - * Constructor. - * - * @param TranslatorInterface $translator - * @param ConfigurationInterface $configuration - */ - public function __construct(TranslatorInterface $translator, ConfigurationInterface $configuration) - { - $this->translator = $translator; - $this->configuration = $configuration; - } - - /** - * {@inheritdoc} - * - * Builds form - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add( - 'is_virtual_file', - FormType\ChoiceType::class, - [ - 'choices' => [ - $this->translator->trans('Yes', [], 'Admin.Global') => 1, - $this->translator->trans('No', [], 'Admin.Global') => 0, - ], - 'expanded' => true, - 'required' => true, - 'multiple' => false, - ] - ) - ->add( - 'file', - FormType\FileType::class, - [ - 'required' => false, - 'label' => $this->translator->trans('File', [], 'Admin.Global'), - 'constraints' => [ - new Assert\File(['maxSize' => $this->configuration->get('PS_ATTACHMENT_MAXIMUM_SIZE') . 'M']), - ], - ] - ) - ->add( - 'name', - FormType\TextType::class, - [ - 'label' => $this->translator->trans('Filename', [], 'Admin.Global'), - 'constraints' => [ - new Assert\NotBlank(), - ], - ] - ) - ->add( - 'nb_downloadable', - FormType\NumberType::class, - [ - 'label' => $this->translator->trans('Number of allowed downloads', [], 'Admin.Catalog.Feature'), - 'required' => false, - 'constraints' => [ - new Assert\Type(['type' => 'numeric']), - ], - ] - ) - ->add( - 'expiration_date', - DatePickerType::class, - [ - 'label' => $this->translator->trans('Expiration date', [], 'Admin.Catalog.Feature'), - 'required' => false, - 'attr' => ['placeholder' => 'YYYY-MM-DD'], - ] - ) - ->add( - 'nb_days', - FormType\NumberType::class, - [ - 'label' => $this->translator->trans('Number of days', [], 'Admin.Catalog.Feature'), - 'required' => false, - 'constraints' => [ - new Assert\Type(['type' => 'numeric']), - ], - ] - ) - ->add( - 'save', - FormType\ButtonType::class, - [ - 'label' => $this->translator->trans('Save', [], 'Admin.Actions'), - 'attr' => ['class' => 'btn-primary pull-right'], - ] - ); - - $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { - $form = $event->getForm(); - $data = $event->getData(); - - //if this partial form is submit from a parent form, disable it - if ($form->getParent()) { - $event->setData([]); - $form->add('name', FormType\TextType::class, ['mapped' => false]); - } elseif ($data['is_virtual_file'] == 0) { - //disable name mapping when is virtual not defined to yes - $form->add('name', FormType\TextType::class, ['mapped' => false]); - } - }); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'product_virtual'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Type/TypeaheadProductCollectionType.php b/src/PrestaShopBundle/Form/Admin/Type/TypeaheadProductCollectionType.php deleted file mode 100644 index 6d5067be8fc20..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Type/TypeaheadProductCollectionType.php +++ /dev/null @@ -1,155 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Type; - -use Symfony\Component\Form\Extension\Core\Type\CollectionType; -use Symfony\Component\Form\Extension\Core\Type\HiddenType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormInterface; -use Symfony\Component\Form\FormView; -use Symfony\Component\OptionsResolver\OptionsResolver; - -/** - * This form class is responsible to create a product, with or without attribute field. - */ -class TypeaheadProductCollectionType extends CommonAbstractType -{ - protected $productAdapter; - protected $categoryAdapter; - - /** - * {@inheritdoc} - * - * @param object $productAdapter - */ - public function __construct($productAdapter, $categoryAdapter) - { - $this->productAdapter = $productAdapter; - $this->categoryAdapter = $categoryAdapter; - } - - /** - * {@inheritdoc} - * - * Add the vars to the view - * Inject collection products - */ - public function buildView(FormView $view, FormInterface $form, array $options) - { - $view->vars['placeholder'] = $options['placeholder']; - $view->vars['remote_url'] = $options['remote_url']; - $view->vars['mapping_value'] = $options['mapping_value']; - $view->vars['mapping_name'] = $options['mapping_name']; - $view->vars['mapping_type'] = $options['mapping_type']; - $view->vars['template_collection'] = $options['template_collection']; - $view->vars['limit'] = $options['limit']; - - //if form is submitted, inject datas to display collection - if (!empty($view->vars['value']) && !empty($view->vars['value']['data'])) { - $collection = []; - - $i = 0; - foreach ($view->vars['value']['data'] as $id) { - if (!$id) { - continue; - } - - switch ($view->vars['mapping_type']) { - case 'category': - $category = $this->categoryAdapter->getCategory($id); - $collection[] = [ - 'id' => $id, - 'name' => $this->categoryAdapter->getBreadCrumb($category->id), - 'image' => $category->image, - ]; - - break; - - default: - $product = $this->productAdapter->getProduct($id); - $collection[] = [ - 'id' => $id, - 'name' => reset($product->name) . ' (ref:' . $product->reference . ')', - 'image' => $product->image, - ]; - - break; - } - ++$i; - - //if collection length is up to limit, break - if ($options['limit'] != 0 && $i >= $options['limit']) { - break; - } - } - $view->vars['collection'] = $collection; - } - } - - /** - * {@inheritdoc} - * - * Builds the form. - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add('data', CollectionType::class, [ - 'entry_type' => HiddenType::class, - 'allow_add' => true, - 'allow_delete' => true, - 'label' => false, - 'required' => false, - 'prototype' => true, - ]); - } - - /** - * {@inheritdoc} - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults([ - 'remote_url' => '', - 'mapping_value' => 'id', - 'mapping_name' => 'name', - 'mapping_type' => 'product', - 'placeholder' => '', - 'template_collection' => '%s', - 'limit' => 0, - ]); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'typeahead_product_collection'; - } -} diff --git a/src/PrestaShopBundle/Form/Admin/Type/TypeaheadProductPackCollectionType.php b/src/PrestaShopBundle/Form/Admin/Type/TypeaheadProductPackCollectionType.php deleted file mode 100644 index 27ab931c1efbb..0000000000000 --- a/src/PrestaShopBundle/Form/Admin/Type/TypeaheadProductPackCollectionType.php +++ /dev/null @@ -1,101 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Form\Admin\Type; - -use Symfony\Component\Form\Extension\Core\Type\CollectionType; -use Symfony\Component\Form\Extension\Core\Type\HiddenType; -use Symfony\Component\Form\FormBuilderInterface; -use Symfony\Component\Form\FormInterface; -use Symfony\Component\Form\FormView; -use Symfony\Component\OptionsResolver\OptionsResolver; - -/** - * This form class is responsible to create a product, with or without attribute field. - */ -class TypeaheadProductPackCollectionType extends CommonAbstractType -{ - /** - * {@inheritdoc} - * - * Add the vars to the view - * Inject collection products - */ - public function buildView(FormView $view, FormInterface $form, array $options) - { - $view->vars['placeholder'] = $options['placeholder']; - $view->vars['remote_url'] = $options['remote_url']; - $view->vars['mapping_value'] = $options['mapping_value']; - $view->vars['mapping_name'] = $options['mapping_name']; - $view->vars['template_collection'] = $options['template_collection']; - - //if form is submitted, inject datas to display collection - if (!empty($view->vars['value']) && !empty($view->vars['value']['data'])) { - $view->vars['collection'] = $view->vars['value']['data']; - } - } - - /** - * {@inheritdoc} - * - * Builds the form. - */ - public function buildForm(FormBuilderInterface $builder, array $options) - { - $builder->add('data', CollectionType::class, [ - 'entry_type' => HiddenType::class, - 'allow_add' => true, - 'allow_delete' => true, - 'label' => false, - 'required' => false, - 'prototype' => true, - ]); - } - - /** - * {@inheritdoc} - */ - public function configureOptions(OptionsResolver $resolver) - { - $resolver->setDefaults([ - 'remote_url' => '', - 'mapping_value' => 'id', - 'mapping_name' => 'name', - 'placeholder' => '', - 'template_collection' => '', - ]); - } - - /** - * Returns the block prefix of this type. - * - * @return string The prefix name - */ - public function getBlockPrefix() - { - return 'typeahead_product_pack_collection'; - } -} diff --git a/src/PrestaShopBundle/Model/AdminModelAdapter.php b/src/PrestaShopBundle/Model/AdminModelAdapter.php deleted file mode 100644 index bd16c9396fefa..0000000000000 --- a/src/PrestaShopBundle/Model/AdminModelAdapter.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Model; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to map the form data to the posted object. - * - * For this parent class, only hook sub fields are handled. - */ -class AdminModelAdapter -{ - /** - * modelMapper - * Map form data to object model. - * - * This parent method will return only hook sub array. - * - * @return array Transformed form data to model attempt - */ - public function getHookData() - { - // Hook fields are kept. - if (array_key_exists('hook', $_POST)) { - $hookFields = $_POST['hook']; - - return ['hook' => $hookFields]; - } - - return []; - } -} diff --git a/src/PrestaShopBundle/Model/Product/AdminModelAdapter.php b/src/PrestaShopBundle/Model/Product/AdminModelAdapter.php deleted file mode 100644 index 8b374c8300e7b..0000000000000 --- a/src/PrestaShopBundle/Model/Product/AdminModelAdapter.php +++ /dev/null @@ -1,933 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Model\Product; - -use Attachment; -use PrestaShop\PrestaShop\Adapter\Configuration; -use PrestaShop\PrestaShop\Adapter\Feature\FeatureDataProvider; -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShop\PrestaShop\Adapter\Pack\PackDataProvider; -use PrestaShop\PrestaShop\Adapter\Product\AdminProductWrapper; -use PrestaShop\PrestaShop\Adapter\Product\ProductDataProvider; -use PrestaShop\PrestaShop\Adapter\Shop\Context as ShopContext; -use PrestaShop\PrestaShop\Adapter\Supplier\SupplierDataProvider; -use PrestaShop\PrestaShop\Adapter\Tax\TaxRuleDataProvider; -use PrestaShop\PrestaShop\Adapter\Tools; -use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\RedirectType; -use PrestaShopBundle\Utils\FloatParser; -use Product; -use ProductDownload; -use Symfony\Component\Routing\Router; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * This form class is responsible to map the form data to the product object. - */ -class AdminModelAdapter extends \PrestaShopBundle\Model\AdminModelAdapter -{ - /** @var LegacyContext */ - private $context; - /** @var \Context */ - private $contextShop; - /** @var AdminProductWrapper */ - private $adminProductWrapper; - /** @var array */ - private $locales; - /** @var Tools */ - private $tools; - /** @var ProductDataProvider */ - private $productAdapter; - /** @var SupplierDataProvider */ - private $supplierAdapter; - /** @var FeatureDataProvider */ - private $featureAdapter; - /** @var PackDataProvider */ - private $packAdapter; - /** @var ShopContext */ - private $shopContext; - /** @var TaxRuleDataProvider */ - private $taxRuleDataProvider; - /** @var array */ - private $productPricePriority; - /** @var Configuration */ - private $configuration; - /** @var Router */ - private $router; - /** @var FloatParser */ - private $floatParser; - - /** @var array */ - private $multiShopKeys = [ - 'category_box', - 'id_category_default', - 'attribute_wholesale_price', - 'attribute_price_impact', - 'attribute_weight_impact', - 'attribute_unit_impact', - 'attribute_ecotax', - 'attribute_minimal_quantity', - 'attribute_low_stock_threshold', - 'attribute_low_stock_alert', - 'available_date_attribute', - 'attribute_default', - 'uploadable_files', - 'text_fields', - 'active', - 'redirect_type', - 'id_type_redirected', - 'visibility', - 'available_for_order', - 'show_price', - 'online_only', - 'show_condition', - 'condition', - 'wholesale_price', - 'price', - 'id_tax_rules_group', - 'ecotax', - 'unit_price', - 'on_sale', - 'minimal_quantity', - 'low_stock_threshold', - 'low_stock_alert', - 'available_date', - 'ecotax', - 'additional_shipping_cost', - 'additional_delivery_times', - ]; - - /** - * Defines translatable key. - * - * @var array - */ - private $translatableKeys = [ - 'name', - 'description', - 'description_short', - 'link_rewrite', - 'meta_title', - 'meta_description', - 'available_now', - 'available_later', - 'tags', - 'delivery_in_stock', - 'delivery_out_stock', - ]; - - /** - * Defines unused key for manual binding. - * - * @var array - */ - private $unmapKeys = [ - 'name', - 'description', - 'description_short', - 'images', - 'related_products', - 'categories', - 'suppliers', - 'display_options', - 'features', - 'specific_price', - 'virtual_product', - 'attachment_product', - ]; - - /** - * Array containing all the data to be mapped with the form. - * - * @var array - */ - private $formData; - - /** - * Constructor - * Set all adapters needed and get product. - * - * @param LegacyContext $legacyContext - * @param AdminProductWrapper $adminProductWrapper - * @param Tools $toolsAdapter - * @param ProductDataProvider $productDataProvider - * @param SupplierDataProvider $supplierDataProvider - * @param FeatureDataProvider $featureDataProvider - * @param PackDataProvider $packDataProvider - * @param ShopContext $shopContext - * @param TaxRuleDataProvider $taxRuleDataProvider - * @param Configuration $configuration - * @param Router $router - * @param FloatParser|null $floatParser - */ - public function __construct( - LegacyContext $legacyContext, - AdminProductWrapper $adminProductWrapper, - Tools $toolsAdapter, - ProductDataProvider $productDataProvider, - SupplierDataProvider $supplierDataProvider, - FeatureDataProvider $featureDataProvider, - PackDataProvider $packDataProvider, - ShopContext $shopContext, - TaxRuleDataProvider $taxRuleDataProvider, - Configuration $configuration, - Router $router, - FloatParser $floatParser = null - ) { - $this->context = $legacyContext; - $this->contextShop = $this->context->getContext(); - $this->adminProductWrapper = $adminProductWrapper; - $this->locales = $this->context->getLanguages(); - $this->tools = $toolsAdapter; - $this->productAdapter = $productDataProvider; - $this->supplierAdapter = $supplierDataProvider; - $this->featureAdapter = $featureDataProvider; - $this->packAdapter = $packDataProvider; - $this->shopContext = $shopContext; - $this->taxRuleDataProvider = $taxRuleDataProvider; - $this->configuration = $configuration; - $this->router = $router; - $this->floatParser = $floatParser ?? new FloatParser(); - } - - /** - * modelMapper - * Map form data to object model. - * - * @param array $form_data - * @param bool $isMultiShopContext If the context is define to multishop, force data to be apply on all shops - * - * @return array Transformed form data to model attempt - */ - public function getModelData($form_data, $isMultiShopContext = false) - { - //merge all form steps - $form_data = array_merge( - ['id_product' => $form_data['id_product']], - $form_data['step1'], - $form_data['step2'], - $form_data['step3'], - $form_data['step4'], - $form_data['step5'], - $form_data['step6'] - ); - - //add some legacy field to execute some add/update methods - $form_data['submitted_tabs'] = ['Shipping']; - $form_data['submitted_tabs'][] = 'Associations'; - - //map translatable - foreach ($this->translatableKeys as $field) { - foreach ($form_data[$field] as $lang_id => $translate_value) { - $form_data[$field . '_' . $lang_id] = $translate_value; - } - } - - //Product type - if ($form_data['type_product'] == 2) { - $form_data['condition'] = 'new'; - $form_data['is_virtual'] = 1; - } else { - $form_data['is_virtual'] = 0; - } - - // Product redirection type and object ID - $form_data['redirect_type'] = (string) $form_data['redirect_type']; - - /* - * In case of categories, we will use the category ID, but if it's missing, - * it can be assigned to zero. Product default category will be used. - */ - if (RedirectType::TYPE_CATEGORY_PERMANENT == $form_data['redirect_type'] || - RedirectType::TYPE_CATEGORY_TEMPORARY == $form_data['redirect_type']) { - if (!empty($form_data['id_type_redirected']['data'][0])) { - $form_data['id_type_redirected'] = $form_data['id_type_redirected']['data'][0]; - } else { - $form_data['id_type_redirected'] = 0; - } - /* - * For redirects to products, we need that ID. If its missing and it was still submitted - * somehow, we will fall back to default category redirect with no object ID. - */ - } elseif (RedirectType::TYPE_PRODUCT_PERMANENT == $form_data['redirect_type'] || - RedirectType::TYPE_PRODUCT_TEMPORARY == $form_data['redirect_type']) { - if (!empty($form_data['id_type_redirected']['data'][0])) { - $form_data['id_type_redirected'] = $form_data['id_type_redirected']['data'][0]; - } else { - $form_data['id_type_redirected'] = 0; - $form_data['redirect_type'] = RedirectType::TYPE_CATEGORY_PERMANENT; - } - /* - * For all other redirection types that don't need any other object ID. - */ - } else { - $form_data['id_type_redirected'] = 0; - } - - // If redirection is set to category and we have proper data, we assign it - if ((RedirectType::TYPE_CATEGORY_PERMANENT == $form_data['redirect_type'] || - RedirectType::TYPE_CATEGORY_TEMPORARY == $form_data['redirect_type']) && - !empty($form_data['id_type_redirected']['data'][0])) { - $form_data['id_type_redirected'] = $form_data['id_type_redirected']['data'][0]; - } - - // If redirection is set to product and we have proper data, we assign it - // Otherwise, we fallback to category permament redirect - if (RedirectType::TYPE_PRODUCT_PERMANENT == $form_data['redirect_type'] || - RedirectType::TYPE_PRODUCT_TEMPORARY == $form_data['redirect_type']) { - if (!empty($form_data['id_type_redirected']['data'][0])) { - $form_data['id_type_redirected'] = $form_data['id_type_redirected']['data'][0]; - } else { - $form_data['redirect_type'] = RedirectType::TYPE_CATEGORY_PERMANENT; - } - } - - //map inputPackItems - if ($form_data['type_product'] == 1 - && !empty($form_data['inputPackItems']) - && !empty($form_data['inputPackItems']['data']) - ) { - $inputPackItems = ''; - foreach ($form_data['inputPackItems']['data'] as $productIds) { - $inputPackItems .= $productIds . '-'; - } - $form_data['inputPackItems'] = $inputPackItems; - } else { - $form_data['inputPackItems'] = ''; - } - - //map categories - foreach ($form_data['categories']['tree'] as $category) { - $form_data['categoryBox'][] = $category; - } - - //if empty categories, set default one - if (empty($form_data['categoryBox'])) { - $form_data['categoryBox'][] = $this->contextShop->shop->id_category; - } - - //if default category not define, set the default one - if (empty($form_data['id_category_default'])) { - $form_data['id_category_default'] = $this->contextShop->shop->id_category; - } - - //map combinations and impact price/weight/unit price - foreach ($form_data['combinations'] as $k => $combination) { - $form_data['combinations'][$k]['attribute_price_impact'] = 0; - $form_data['combinations'][$k]['attribute_weight_impact'] = 0; - $form_data['combinations'][$k]['attribute_unit_impact'] = 0; - - if ($this->floatParser->fromString($combination['attribute_price']) > 0) { - $form_data['combinations'][$k]['attribute_price_impact'] = 1; - } elseif ($this->floatParser->fromString($combination['attribute_price']) < 0) { - $form_data['combinations'][$k]['attribute_price_impact'] = -1; - } - - if ($this->floatParser->fromString($combination['attribute_weight']) > 0) { - $form_data['combinations'][$k]['attribute_weight_impact'] = 1; - } elseif ($this->floatParser->fromString($combination['attribute_weight']) < 0) { - $form_data['combinations'][$k]['attribute_weight_impact'] = -1; - } - - if ($this->floatParser->fromString($combination['attribute_unity']) > 0) { - $form_data['combinations'][$k]['attribute_unit_impact'] = 1; - } elseif ($this->floatParser->fromString($combination['attribute_unity']) < 0) { - $form_data['combinations'][$k]['attribute_unit_impact'] = -1; - } - - $form_data['combinations'][$k]['attribute_price'] = abs( - $this->floatParser->fromString($combination['attribute_price']) - ); - $form_data['combinations'][$k]['attribute_weight'] = abs( - $this->floatParser->fromString($combination['attribute_weight']) - ); - $form_data['combinations'][$k]['attribute_unity'] = abs( - $this->floatParser->fromString($combination['attribute_unity']) - ); - if ($this->configuration->getBoolean('PS_STOCK_MANAGEMENT')) { - $form_data['combinations'][$k]['attribute_quantity'] = $this->floatParser->fromString($combination['attribute_quantity']); - } - - $form_data['combinations'][$k]['attribute_wholesale_price'] = abs( - $this->floatParser->fromString($combination['attribute_wholesale_price']) - ); - } - - //map suppliers - $form_data['supplier_loaded'] = 1; - if (!empty($form_data['suppliers'])) { - foreach ($form_data['suppliers'] as $id_supplier) { - $form_data['check_supplier_' . $id_supplier] = 1; - - //map supplier combinations - foreach ($form_data['supplier_combination_' . $id_supplier] as $combination) { - $key = $form_data['id_product'] . '_' . $combination['id_product_attribute'] . '_' . $id_supplier; - $form_data['supplier_reference_' . $key] = $combination['supplier_reference']; - $form_data['product_price_' . $key] = $combination['product_price']; - $form_data['product_price_currency_' . $key] = $combination['product_price_currency']; - - unset($form_data['supplier_combination_' . $id_supplier]); - } - } - } - - //map display options - foreach ($form_data['display_options'] as $option => $value) { - $form_data[$option] = $value; - } - - //if empty, set link_rewrite for default locale - $linkRewriteKey = 'link_rewrite_' . $this->locales[0]['id_lang']; - if (empty($form_data[$linkRewriteKey])) { - $form_data[$linkRewriteKey] = $this->tools->linkRewrite($form_data['name_' . $this->locales[0]['id_lang']]); - } - - //map inputAccessories - if (!empty($form_data['related_products']) && !empty($form_data['related_products']['data'])) { - $inputAccessories = ''; - foreach ($form_data['related_products']['data'] as $accessoryIds) { - $accessoryIds = explode(',', $accessoryIds); - $inputAccessories .= $accessoryIds[0] . '-'; - } - $form_data['inputAccessories'] = $inputAccessories; - } - - //force customization fields values - $form_data['uploadable_files'] = 0; - $form_data['text_fields'] = 0; - - //map all - $new_form_data = []; - foreach ($form_data as $k => $v) { - if (in_array($k, $this->unmapKeys) || in_array($k, $this->translatableKeys)) { - continue; - } - $new_form_data[$k] = $v; - } - - //map specific price priority - $new_form_data['specificPricePriority'] = [ - $new_form_data['specificPricePriority_0'], - $new_form_data['specificPricePriority_1'], - $new_form_data['specificPricePriority_2'], - $new_form_data['specificPricePriority_3'], - ]; - - $new_form_data = array_merge(parent::getHookData(), $new_form_data); - - //if multiShop context is defined, simulate multiShop checkbox for all POST DATA - if ($isMultiShopContext) { - foreach ($this->multiShopKeys as $multishopKey) { - $new_form_data['multishop_check'][$multishopKey] = 1; - } - - //apply multishop rules for translatables fields - foreach ($this->translatableKeys as $field) { - foreach ($form_data[$field] as $lang_id => $translate_value) { - $new_form_data['multishop_check'][$field][$lang_id] = 1; - } - } - } - - return $new_form_data; - } - - /** - * formMapper - * Map object model to form data. - * - * @param Product $product - * - * @return array Transformed model data to form attempt - */ - public function getFormData(Product $product) - { - $product->loadStockData(); - $this->productPricePriority = $this->adminProductWrapper->getPricePriority($product->id); - - $this->formData['id_product'] = $product->id; - $this->formData['step1'] = $this->mapStep1FromData($product); - $this->formData['step2'] = $this->mapStep2FormData($product); - $this->formData['step3'] = $this->mapStep3FormData($product); - $this->formData['step4'] = $this->mapStep4FormData($product); - $this->formData['step5'] = $this->mapStep5FormData($product); - $this->formData['step6'] = $this->mapStep6FormData($product); - - //Inject data form for supplier combinations - $this->formData['step6'] = array_merge($this->formData['step6'], $this->getDataSuppliersCombinations($product)); - - return $this->formData; - } - - /** - * Maps the existing products data to the form for Step 1. - * - * @param Product $product - * - * @return array - */ - private function mapStep1FromData(Product $product) - { - return [ - 'type_product' => $product->getType(), - 'inputPackItems' => [ - 'data' => array_map( - function ($p) { - return [ - 'id' => $p->id, - 'id_product_attribute' => isset($p->id_pack_product_attribute) - ? $p->id_pack_product_attribute - : 0, - 'name' => $p->name, - 'ref' => $p->reference, - 'quantity' => $p->pack_quantity, - 'image' => $p->image, - ]; - }, - $this->packAdapter->getItems($product->id, $this->locales[0]['id_lang']) - ), - ], - 'name' => $product->name, - 'description' => $product->description, - 'description_short' => $product->description_short, - 'active' => $product->active == 0 ? false : true, - 'price_shortcut' => $product->price, - 'qty_0_shortcut' => $product->getQuantity($product->id), - 'categories' => ['tree' => $product->getCategories()], - 'id_category_default' => $product->id_category_default, - 'related_products' => [ - 'data' => array_map( - function ($p) { - return $p['id_product']; - }, - call_user_func_array( - [$product, 'getAccessoriesLight'], - [$this->locales[0]['id_lang'], $product->id] - ) - ), - ], - 'id_manufacturer' => $product->id_manufacturer, - 'features' => $this->getFormFeatures($product), - 'images' => $this->productAdapter->getImages($product->id, $this->locales[0]['id_lang']), - ]; - } - - /** - * Maps the existing products data to the form for Step 2. - * - * @param Product $product - * - * @return array - */ - private function mapStep2FormData(Product $product) - { - // ecotax is stored with tax included but form uses the tax excluded value - // using a precision of 6 digits as `AdminProductsController::_removeTaxFromEcotax()` - // which does the opposite uses 6 digits too - $ecotax = $this->tools->round( - $product->ecotax * (1 + $this->taxRuleDataProvider->getProductEcotaxRate() / 100), - 6 - ); - - return [ - 'price' => $product->price, - 'ecotax' => $ecotax, - 'id_tax_rules_group' => isset($product->id_tax_rules_group) - ? (int) $product->id_tax_rules_group - : $this->taxRuleDataProvider->getIdTaxRulesGroupMostUsed(), - 'on_sale' => (bool) $product->on_sale, - 'wholesale_price' => $product->wholesale_price, - 'unit_price' => $product->unit_price, - 'unity' => $product->unity, - 'specific_price' => [ // extra form to be saved separately. Here this is the default form values. - 'sp_from_quantity' => 1, - 'sp_reduction' => 0, - 'sp_reduction_tax' => 1, - 'leave_bprice' => true, - 'sp_id_shop' => $this->shopContext->getContextShopID(), - ], - 'specificPricePriority_0' => $this->productPricePriority[0], - 'specificPricePriority_1' => $this->productPricePriority[1], - 'specificPricePriority_2' => $this->productPricePriority[2], - 'specificPricePriority_3' => $this->productPricePriority[3], - ]; - } - - /** - * Maps the existing products data to the form for Step 3. - * - * @param Product $product - * - * @return array - */ - private function mapStep3FormData(Product $product) - { - return [ - 'qty_0' => $product::getQuantity($product->id), - 'id_product_attributes' => $this->getProductAttributes($product), - 'out_of_stock' => $product->out_of_stock, - 'minimal_quantity' => $product->minimal_quantity, - 'location' => $product->location, - 'low_stock_threshold' => $product->low_stock_threshold, - 'low_stock_alert' => (bool) $product->low_stock_alert, - 'available_now' => $product->available_now, - 'available_later' => $product->available_later, - 'available_date' => $product->available_date, - 'pack_stock_type' => $product->pack_stock_type, - 'virtual_product' => $this->getVirtualProductData($product), - ]; - } - - /** - * Maps the existing products data to the form for Step 4. - * - * @param Product $product - * - * @return array - */ - private function mapStep4FormData(Product $product) - { - return [ - 'width' => $product->width, - 'height' => $product->height, - 'depth' => $product->depth, - 'weight' => $product->weight, - 'additional_shipping_cost' => $product->additional_shipping_cost, - 'selectedCarriers' => $this->getFormProductCarriers($product), - 'additional_delivery_times' => $product->additional_delivery_times, - 'delivery_in_stock' => $product->delivery_in_stock, - 'delivery_out_stock' => $product->delivery_out_stock, - ]; - } - - /** - * Maps the existing products data to the form for Step 5. - * - * @param Product $product - * - * @return array - */ - private function mapStep5FormData(Product $product) - { - return [ - 'link_rewrite' => $product->link_rewrite, - 'meta_title' => $product->meta_title, - 'meta_description' => $product->meta_description, - 'redirect_type' => $product->redirect_type, - 'id_type_redirected' => [ - 'data' => [$product->id_type_redirected], - ], - ]; - } - - /** - * Maps the existing products data to the form for Step 6. - * - * @param Product $product - * - * @return array - */ - private function mapStep6FormData(Product $product) - { - return [ - 'visibility' => $product->visibility, - 'tags' => $this->getTags($product), - 'display_options' => [ - 'available_for_order' => (bool) $product->available_for_order, - 'show_price' => (bool) $product->show_price, - 'online_only' => (bool) $product->online_only, - ], - 'upc' => $product->upc, - 'mpn' => $product->mpn, - 'ean13' => $product->ean13, - 'isbn' => $product->isbn, - 'reference' => $product->reference, - 'show_condition' => (bool) $product->show_condition, - 'condition' => $product->condition, - 'suppliers' => array_map( - function ($s) { - return $s->id_supplier; - }, - $this->supplierAdapter->getProductSuppliers($product->id) - ), - 'default_supplier' => $product->id_supplier, - 'custom_fields' => $this->getCustomFields($product), - 'attachments' => $this->getProductAttachments($product), - ]; - } - - /** - * Get all available product attributes resume. - * - * @param Product $product - * - * @return array Product attributes combinations - */ - public function getAttributesResume(Product $product) - { - return $product->getAttributesResume($this->context->getContext()->language->id); - } - - /** - * Get product attachments. - * - * @param Product $product - * - * @return array - */ - private function getProductAttachments(Product $product) - { - return array_map( - function ($attachment) { - return $attachment['id_attachment']; - }, - Attachment::getAttachments($this->locales[0]['id_lang'], $product->id) - ); - } - - /** - * Get virtual product data. - * - * @param Product $product - * - * @return array - */ - private function getVirtualProductData(Product $product) - { - //force virtual product feature - $this->configuration->set('PS_VIRTUAL_PROD_FEATURE_ACTIVE', '1'); - - $id_product_download = ProductDownload::getIdFromIdProduct((int) $product->id, false); - if ($id_product_download) { - $download = new ProductDownload($id_product_download); - $dateValue = $download->date_expiration == '0000-00-00 00:00:00' - ? '' - : date('Y-m-d', strtotime($download->date_expiration)); - - $res = [ - 'is_virtual_file' => $download->active, - 'name' => $download->display_filename, - 'nb_downloadable' => $download->nb_downloadable, - 'expiration_date' => $dateValue, - 'nb_days' => $download->nb_days_accessible, - ]; - - if ($download->filename) { - $res['filename'] = $download->filename; - $res['file_download_link'] = $this->router->generate( - 'admin_product_virtual_download_file_action', - ['idProduct' => $download->id_product] - ); - } - - return $res; - } - - return [ - 'is_virtual_file' => 0, - 'nb_days' => 0, - ]; - } - - /** - * Generate form custom fields configuration. - * - * @param Product $product - * - * @return array - */ - private function getCustomFields(Product $product) - { - $finalCustomFields = []; - $customizationFields = []; - $productCustomizationFields = $product->getCustomizationFields(); - - if (!$productCustomizationFields) { - return []; - } - - foreach ($productCustomizationFields as $customizationField) { - $customizationFields = array_merge($customizationFields, $customizationField); - } - - foreach ($customizationFields as $customizationField) { - $baseObject = [ - 'id_customization_field' => $customizationField[$this->locales[0]['id_lang']]['id_customization_field'], - 'label' => [], - 'type' => $customizationField[$this->locales[0]['id_lang']]['type'], - 'require' => $customizationField[$this->locales[0]['id_lang']]['required'] == 1 ? true : false, - ]; - - //add translation name - foreach ($this->locales as $locale) { - $baseObject['label'][$locale['id_lang']] = $customizationField[$locale['id_lang']]['name']; - } - $finalCustomFields[] = $baseObject; - } - - return $finalCustomFields; - } - - /** - * Generate form supplier/combinations references. - * - * @param Product $product - * - * @return array filled data form references combinations - */ - private function getDataSuppliersCombinations(Product $product) - { - $combinations = $product->getAttributesResume($this->locales[0]['id_lang']) ?: []; - if (empty($combinations)) { - $combinations[] = [ - 'id_product' => $product->id, - 'id_product_attribute' => 0, - 'attribute_designation' => $product->name[$this->locales[0]['id_lang']], - ]; - } - - //for each supplier, generate combinations list - $dataSuppliersCombinations = []; - - foreach ($this->supplierAdapter->getProductSuppliers($product->id) as $supplier) { - foreach ($combinations as $combination) { - $productSupplierData = $this->supplierAdapter->getProductSupplierData( - $product->id, - $combination['id_product_attribute'], - $supplier->id_supplier - ); - $dataSuppliersCombinations['supplier_combination_' . $supplier->id_supplier][] = [ - 'label' => $combination['attribute_designation'], - 'supplier_reference' => isset($productSupplierData['product_supplier_reference']) - ? $productSupplierData['product_supplier_reference'] - : '', - 'product_price' => isset($productSupplierData['price']) - ? $productSupplierData['price'] - : 0, - 'product_price_currency' => isset($productSupplierData['id_currency']) - ? $productSupplierData['id_currency'] - : 1, - 'supplier_id' => $supplier->id_supplier, - 'product_id' => $product->id, - 'id_product_attribute' => $combination['id_product_attribute'], - ]; - } - } - - return $dataSuppliersCombinations; - } - - /** - * get form product features. - * - * @param Product $product - * - * @return array features with translation - */ - private function getFormFeatures(Product $product) - { - $formFeaturesData = []; - foreach ($product->getFeatures() as $featureData) { - $itemForm = [ - 'feature' => $featureData['id_feature'], - 'value' => null, - 'custom_value' => null, - ]; - - if ($featureData['custom'] == 0) { - // use id_feature_value only if this value isn't custom - $itemForm['value'] = $featureData['id_feature_value']; - } else { - $customLangs = []; - $featureLangsData = $this->featureAdapter->getFeatureValueLang($featureData['id_feature_value']); - foreach ($featureLangsData as $langData) { - $customLangs[$langData['id_lang']] = $langData['value']; - } - $itemForm['custom_value'] = $customLangs; - } - - $formFeaturesData[] = $itemForm; - } - - return $formFeaturesData; - } - - /** - * get product carrier. - * - * @param Product $product - * - * @return array carrier - */ - private function getFormProductCarriers(Product $product) - { - $formDataCarriers = []; - foreach ($product->getCarriers() as $carrier) { - $formDataCarriers[] = $carrier['id_reference']; - } - - return $formDataCarriers; - } - - /** - * Get all product id_product_attribute. - * - * @param Product $product - * - * @return array id_product_attribute - */ - private function getProductAttributes(Product $product) - { - $combinations = $product->getAttributesResume($this->context->getContext()->language->id); - $idsProductAttribute = []; - - if (is_array($combinations)) { - foreach ($combinations as $combination) { - $idsProductAttribute[] = $combination['id_product_attribute']; - } - } - - return $idsProductAttribute; - } - - /** - * Get a localized tags for product. - * - * @param Product $product - * - * @return array - */ - private function getTags(Product $product) - { - $tags = []; - foreach ($this->locales as $locale) { - $tags[$locale['id_lang']] = $product->getTags($locale['id_lang']); - } - - return $tags; - } -} diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/_catalog.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/_catalog.yml index af41be21acb94..987bd028f27bf 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/_catalog.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/_catalog.yml @@ -1,10 +1,6 @@ -_products: - resource: "products/products.yml" - prefix: /products/ - _products_v2: - resource: "products_v2/product.yml" - prefix: /products-v2/ + resource: "products/product.yml" + prefix: /products/ _categories: resource: "categories.yml" diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/features.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/features.yml index 6ffa69b7e82eb..b51e03636380a 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/features.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/features.yml @@ -154,3 +154,14 @@ admin_feature_values_bulk_delete: requirements: featureId: \d+ featureValueId: \d+ + +admin_feature_get_feature_values: + path: /values/{featureId} + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\FeatureValueController::getFeatureValuesAction + _legacy_controller: AdminFeatures + requirements: + featureId: \d+ + options: + expose: true diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/attributes.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/attributes.yml deleted file mode 100644 index a18895006dc72..0000000000000 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/attributes.yml +++ /dev/null @@ -1,42 +0,0 @@ -admin_attribute_get_all: - path: /get-all - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\AttributeController::getAllAttributesAction - _format: json - _legacy_controller: AdminProducts - -admin_attribute_generator: - path: /generator - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\AttributeController::attributesGeneratorAction - _legacy_controller: AdminProducts - -admin_delete_attribute: - path: /{idProduct} - methods: [ DELETE ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\AttributeController::deleteAttributeAction - _legacy_controller: AdminProducts - -admin_delete_all_attributes: - path: /delete-all/{idProduct} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\AttributeController::deleteAllAttributeAction - _legacy_controller: AdminProducts - idProduct: 0 - requirements: - idProduct: \d+ - -admin_get_form_images_combination: - path: /form-images/{idProduct} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\AttributeController::getFormImagesAction - _format: json - _legacy_controller: AdminProducts - idProduct: 0 - requirements: - idProduct: \d+ diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/categories.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/categories.yml deleted file mode 100644 index badb65e92dc8d..0000000000000 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/categories.yml +++ /dev/null @@ -1,15 +0,0 @@ -admin_category_simple_add_form: - path: /add/simple - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\CategoryController::addSimpleCategoryFormAction - -admin_get_ajax_categories: - path: /list/{limit} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\CategoryController::getAjaxCategoriesAction - _format: json - limit: 20 - requirements: - limit: \d+ diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products_v2/combination.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/combination.yml similarity index 100% rename from src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products_v2/combination.yml rename to src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/combination.yml diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/combinations.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/combinations.yml deleted file mode 100644 index 648f66123627b..0000000000000 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/combinations.yml +++ /dev/null @@ -1,16 +0,0 @@ -admin_combination_generate_form: - path: /form/{combinationIds} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\CombinationController::generateCombinationFormAction - combinationIds: 0 - -admin_get_product_combinations: - path: /{idProduct} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\CombinationController::getProductCombinationsAction - _format: json - idProduct: 0 - requirements: - idProduct: \d+ diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/features.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/features.yml deleted file mode 100644 index d537605b000d6..0000000000000 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/features.yml +++ /dev/null @@ -1,10 +0,0 @@ -admin_feature_get_feature_values: - path: /{idFeature} - methods: [ GET ] - defaults: - idFeature: 0 - _controller: PrestaShopBundle\Controller\Admin\FeatureController::getFeatureValuesAction - requirements: - idFeature: \d+ - options: - expose: true diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products_v2/image.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/image.yml similarity index 100% rename from src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products_v2/image.yml rename to src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/image.yml diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/product.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/product.yml index 25b791ed195f2..e718ca1e2d41f 100644 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/product.yml +++ b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/product.yml @@ -1,112 +1,514 @@ -# Product Form -admin_product_new: - path: /new +_combinations: + resource: 'combination.yml' + +_images: + resource: 'image.yml' + +_specific_prices: + resource: 'specific_price.yml' + +admin_products_index: + path: / + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::indexAction + _legacy_controller: AdminProducts + _legacy_link: AdminProducts + +# Old route temporarily kept for backward compatibility +admin_product_catalog: + path: /legacy-list methods: [ GET ] defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductController::newAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::backwardCompatibleListAction _legacy_controller: AdminProducts + limit: last + offset: 0 + orderBy: last + sortOrder: last +admin_products_light_list: + path: /light-list + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::lightListAction + _legacy_controller: AdminProducts + +admin_products_preview: + path: /{productId}/preview/{shopId} + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::previewAction + _legacy_controller: AdminProducts + _legacy_link: AdminProducts:preview + _legacy_parameters: + id_product: productId + shopId: null + requirements: + productId: \d+ + +admin_products_search: + path: / + methods: [ POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::searchGridAction + _legacy_controller: AdminProducts + +admin_products_reset_grid_search: + path: /reset_grid_search + methods: [ POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::resetGridSearchAction + _legacy_controller: AdminProducts + +admin_products_grid_category_filter: + path: /grid_category_filter + methods: [ POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::gridCategoryFilterAction + _legacy_controller: AdminProducts + +admin_products_grid_shop_previews: + path: /shop_previews/{productId}/{shopGroupId} + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::productShopPreviewsAction + _legacy_controller: AdminProducts + shopGroupId: null + requirements: + productId: \d+ + shopGroupId: \d+ + +admin_products_create: + path: /create + methods: [ GET, POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::createAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:addproduct + - AdminProducts:add + +admin_products_edit: + path: /{productId}/edit + methods: [ GET, POST, PATCH ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::editAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:updateproduct + - AdminProducts:update + _legacy_parameters: + id_product: productId + requirements: + productId: \d+ + options: + expose: true + +# Old route temporarily kept for backward compatibility admin_product_form: path: /{id} - methods: [ GET, POST ] + methods: [ GET ] defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductController::formAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::backwardCompatibleEditAction _legacy_controller: AdminProducts - _legacy_param_mapper_class: PrestaShop\PrestaShop\Adapter\Product\AdminProductDataProvider - _legacy_param_mapper_method: mapLegacyParametersProductForm requirements: id: \d+ options: expose: true -admin_product_virtual_save_action: - path: /virtual/save/{idProduct} +admin_products_select_shops: + path: /{productId}/shops + methods: [ GET, POST, PATCH ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::selectProductShopsAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:assignshops + _legacy_parameters: + id_product: productId + requirements: + productId: \d+ + options: + expose: true + +admin_products_download_virtual_product_file: + path: /virtual-product-file/{virtualProductFileId} + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::downloadVirtualFileAction + _legacy_controller: AdminProducts + requirements: + virtualProductFileId: \d+ + +admin_products_delete_from_all_shops: + path: /{productId}/delete-from-all-shops + methods: [ POST, DELETE ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::deleteFromAllShopsAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:delete + _legacy_parameters: + id_product: productId + requirements: + productId: \d+ + +admin_products_delete_from_shop_group: + path: /{productId}/delete-from-shop-group/{shopGroupId} + methods: [ POST, DELETE ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::deleteFromShopGroupAction + _legacy_controller: AdminProducts + requirements: + productId: \d+ + shopGroupId: \d+ + +admin_products_delete_from_shop: + path: /{productId}/delete-from-shop/{shopId} + methods: [ POST, DELETE ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::deleteFromShopAction + _legacy_controller: AdminProducts + requirements: + productId: \d+ + shopId: \d+ + +admin_products_duplicate_all_shops: + path: /{productId}/duplicate-all-shops methods: [ POST ] defaults: - _controller: PrestaShopBundle\Controller\Admin\VirtualProductController::saveAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::duplicateAllShopsAction _legacy_controller: AdminProducts - idProduct: 0 + _legacy_link: + - AdminProducts:duplicate + _legacy_parameters: + id_product: productId requirements: - idProduct: \d+ + productId: \d+ -admin_product_virtual_remove_file_action: - path: /virtual/remove-file/{idProduct} +admin_products_duplicate_shop: + path: /{productId}/duplicate-shop/{shopId} + methods: [ POST ] defaults: - _controller: PrestaShopBundle\Controller\Admin\VirtualProductController::removeFileAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::duplicateShopAction _legacy_controller: AdminProducts - idProduct: 0 + _legacy_link: + - AdminProducts:duplicate + _legacy_parameters: + id_product: productId requirements: - idProduct: \d+ + productId: \d+ + shopId: \d+ -admin_product_virtual_download_file_action: - path: /virtual/download-file/{idProduct} +admin_products_duplicate_shop_group: + path: /{productId}/duplicate-shop-group/{shopGroupId} + methods: [ POST ] defaults: - _controller: PrestaShopBundle\Controller\Admin\VirtualProductController::downloadFileAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::duplicateShopGroupAction _legacy_controller: AdminProducts - idProduct: 0 + _legacy_link: + - AdminProducts:duplicate + _legacy_parameters: + id_product: productId requirements: - idProduct: \d+ + productId: \d+ + shopGroupId: \d+ -admin_product_virtual_remove_action: - path: /virtual/remove/{idProduct} +admin_products_toggle_status_for_shop: + path: /{productId}/toggle-status-for-shop/{shopId} + methods: [ POST ] defaults: - _controller: PrestaShopBundle\Controller\Admin\VirtualProductController::removeAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::toggleStatusForShopAction _legacy_controller: AdminProducts - idProduct: 0 + _legacy_parameters: + id_product: productId requirements: - idProduct: \d+ + productId: \d+ + shopId: \d+ -admin_product_attachement_add_action: - path: /attachment/form/add/{idProduct} +admin_products_toggle_status_for_all_shops: + path: /{productId}/toggle-status-for-all-shops methods: [ POST ] defaults: - _controller: PrestaShopBundle\Controller\Admin\AttachementProductController::addAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::toggleStatusForAllShopsAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:statusproduct + _legacy_parameters: + id_product: productId + requirements: + productId: \d+ + shopId: \d+ + +admin_products_enable_for_all_shops: + path: /{productId}/enable-for-all-shops + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::enableForAllShopsAction _legacy_controller: AdminProducts - idProduct: 0 requirements: - idProduct: \d+ + productId: \d+ -admin_product_image_upload: - path: /image/upload/{idProduct} +admin_products_enable_for_shop_group: + path: /{productId}/enable-for-shop-group/{shopGroupId} + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::enableForShopGroupAction + _legacy_controller: AdminProducts + requirements: + productId: \d+ + shopGroupId: \d+ + +admin_products_disable_for_all_shops: + path: /{productId}/disable-for-all-shops + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::disableForAllShopsAction + _legacy_controller: AdminProducts + requirements: + productId: \d+ + +admin_products_disable_for_shop_group: + path: /{productId}/disable-for-shop-group/{shopGroupId} + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::disableForShopGroupAction + _legacy_controller: AdminProducts + shopGroupId: null + requirements: + productId: \d+ + shopGroupId: \d+ + +admin_products_update_position: + path: /update_position methods: [ POST ] defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductImageController::uploadImageAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::updatePositionAction _legacy_controller: AdminProducts - idProduct: 0 + _legacy_link: + - AdminProducts:positionproduct + _legacy_parameters: + id_product: productId requirements: - idProduct: \d+ + productId: \d+ -admin_product_image_positions: - path: /image/positions +admin_products_bulk_enable_all_shops: + path: /bulk-enable-all-shops methods: [ POST ] defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductImageController::updateImagePositionAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkEnableAllShopsAction _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:submitBulkdisableSelectionproduct + requirements: + productId: \d+ + options: + expose: true -admin_product_image_form: - path: image/form/{idImage} +admin_products_bulk_enable_shop: + path: /bulk-enable-shop/{shopId} + methods: [ POST ] defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductImageController::formAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkEnableShopAction _legacy_controller: AdminProducts - idImage: 0 + _legacy_link: + - AdminProducts:submitBulkdisableSelectionproduct requirements: - idImage: \d+ + productId: \d+ + shopId: \d+ + options: + expose: true -admin_product_image_delete: - path: /image/delete/{idImage} +admin_products_bulk_enable_shop_group: + path: /bulk-enable-shop-group/{shopGroupId} + methods: [ POST ] defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductImageController::deleteAction + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkEnableShopGroupAction _legacy_controller: AdminProducts - idImage: 0 + _legacy_link: + - AdminProducts:submitBulkdisableSelectionproduct requirements: - idImage: \d+ + productId: \d+ + shopGroupId: \d+ + options: + expose: true -admin_product_toggle_status: - path: /{productId}/toggle-status +admin_products_bulk_disable_all_shops: + path: /bulk-disable-for-all-shops methods: [ POST ] defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductController::toggleStatusAction - _legacy_controller: AdminTracking - _legacy_link: AdminTracking:statusproduct + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDisableAllShopsAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:submitBulkenableSelectionproduct + requirements: + productId: \d+ + options: + expose: true + +admin_products_bulk_disable_shop: + path: /bulk-disable-shop/{shopId} + methods: [ POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDisableShopAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:submitBulkenableSelectionproduct + requirements: + productId: \d+ + shopId: \d+ + options: + expose: true + +admin_products_bulk_disable_shop_group: + path: /bulk-disable-shop-group/{shopGroupId} + methods: [ POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDisableShopGroupAction + _legacy_controller: AdminProducts + _legacy_link: AdminProducts:submitBulkdisableSelectionproduct + requirements: + productId: \d+ + shopGroupId: \d+ + options: + expose: true + +admin_products_bulk_duplicate_all_shops: + path: /bulk-duplicate-all-shops + methods: [ POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDuplicateAllShopsAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:submitBulkduplicateSelectionproduct + _legacy_parameters: + id_product: productId + requirements: + productId: \d+ + options: + expose: true + +admin_products_bulk_duplicate_shop: + path: /bulk-duplicate-shop/{shopId} + methods: [ POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDuplicateShopAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:submitBulkduplicateSelectionproduct _legacy_parameters: id_product: productId + requirements: + productId: \d+ + shopId: \d+ + options: + expose: true + +admin_products_bulk_duplicate_shop_group: + path: /bulk-duplicate-shop-group/{shopGroupId} + methods: [ POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDuplicateShopGroupAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:submitBulkduplicateSelectionproduct + _legacy_parameters: + id_product: productId + requirements: + productId: \d+ + shopGroupId: \d+ + options: + expose: true + +admin_products_bulk_delete_from_all_shops: + path: /bulk-delete-from-all-shops + methods: [ POST, DELETE ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDeleteFromAllShopsAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:submitBulkdeleteproduct + _legacy_parameters: + id_product: productId + requirements: + productId: \d+ + options: + expose: true + +admin_products_bulk_delete_from_shop: + path: /bulk-delete-from-shop/{shopId} + methods: [ POST, DELETE ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDeleteFromShopAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:submitBulkdeleteproduct + _legacy_parameters: + id_product: productId + requirements: + productId: \d+ + shopId: \d+ + options: + expose: true + +admin_products_bulk_delete_from_shop_group: + path: /bulk-delete-from-shop-group/{shopGroupId} + methods: [ POST, DELETE ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDeleteFromShopGroupAction + _legacy_controller: AdminProducts + _legacy_link: + - AdminProducts:submitBulkdeleteproduct + _legacy_parameters: + id_product: productId + requirements: + productId: \d+ + shopGroupId: \d+ + options: + expose: true + +admin_products_search_products_for_association: + path: /search/{languageCode} + methods: [ GET, POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::searchProductsForAssociationAction + _legacy_controller: AdminProducts + requirements: + languageCode: !php/const PrestaShop\PrestaShop\Core\Domain\Language\ValueObject\TagIETF::IETF_TAG_REGEXP + +admin_products_search_combinations_for_association: + path: /search/combination/{languageCode} + methods: [ GET, POST ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\CombinationController::searchCombinationsForAssociationAction + _legacy_controller: AdminProducts + requirements: + languageCode: !php/const PrestaShop\PrestaShop\Core\Domain\Language\ValueObject\TagIETF::IETF_TAG_REGEXP + +admin_products_search_product_combinations: + path: /{productId}/search-product-combinations/{shopId}/{languageId} + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\CombinationController::searchProductCombinationsAction + _legacy_controller: AdminProducts + languageId: null + shopId: null + requirements: + productId: \d+ + shopId: \d+ + languageId: \d+ + options: + expose: true + +admin_products_quantity: + path: /{productId}/quantity/{shopId} + methods: [ GET ] + defaults: + _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::quantityAction + _legacy_controller: AdminProducts + requirements: + productId: \d+ + shopId: \d+ + options: + expose: true diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/products.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/products.yml deleted file mode 100644 index d3f0258c3264b..0000000000000 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/products.yml +++ /dev/null @@ -1,107 +0,0 @@ -_product: - resource: "product.yml" - -_product_attributes: - resource: "attributes.yml" - prefix: /attributes/ - -_product_categories: - resource: "categories.yml" - prefix: /categories/ - -_product_combinations: - resource: "combinations.yml" - prefix: /combinations/ - -_product_features: - resource: "features.yml" - prefix: /features/ - -_product_specific_prices: - resource: "specific_prices.yml" - prefix: /specific-prices/ - -_product_suppliers: - resource: "suppliers.yml" - prefix: /suppliers/ - -# Product List -admin_product_catalog: - path: /{offset}/{limit}/{orderBy}/{sortOrder} - methods: [ GET, POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductController::catalogAction - _legacy_controller: AdminProducts - limit: last - offset: 0 - orderBy: last - sortOrder: last - requirements: - limit: _limit|last|\d+ - orderBy: last|id_product|name|reference|name_category|price|sav_quantity|position|active|position_ordering - offset: last|\d+ - sortOrder: last|asc|desc - -admin_product_catalog_filters: - path: /filters/{quantity}/{active} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductController::catalogFiltersAction - _legacy_controller: AdminProducts - quantity: 'none' - active: 'none' - requirements: - quantity: none|<=\d+|<\d+|>\d+|>=\d+ - active: none|1|0 - -admin_product_list: - path: /list/{offset}/{limit}/{orderBy}/{sortOrder}/{view} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductController::listAction - limit: last - offset: 0 - orderBy: last - sortOrder: last - view: full - requirements: - limit: _limit|last|\d+ - orderBy: last|id_product|name|reference|name_category|price|sav_quantity|position|active|position_ordering - offset: last|\d+ - sortOrder: last|asc|desc - view: full|quicknav - -admin_product_bulk_action: - path: /bulk/{action} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductController::bulkAction - requirements: - action: activate_all|deactivate_all|delete_all|duplicate_all - -admin_product_unit_action: - path: /unit/{action}/{id} - methods: [ POST|GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductController::unitAction - requirements: - action: delete|duplicate|activate|deactivate - id: \d+ - -admin_product_mass_edit_action: - path: /massedit/{action} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductController::massEditAction - requirements: - action: sort - -admin_product_export_action: - path: /export.{_format} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\ProductController::exportAction - _legacy_controller: AdminProducts - _format: csv - requirements: - _format: csv diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products_v2/specific_price.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/specific_price.yml similarity index 100% rename from src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products_v2/specific_price.yml rename to src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/specific_price.yml diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/specific_prices.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/specific_prices.yml deleted file mode 100644 index 8321386b274ca..0000000000000 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/specific_prices.yml +++ /dev/null @@ -1,42 +0,0 @@ -admin_specific_price_list: - path: /list/{idProduct} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\SpecificPriceController::listAction - _format: json - idProduct: 0 - requirements: - idProduct: \d+ - -admin_get_specific_price_update_form: - path: /form/{idSpecificPrice}/ - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\SpecificPriceController::getUpdateFormAction - idSpecificPrice: 0 - requirements: - idSpecificPrice: \d+ - -admin_specific_price_add: - path: /add - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\SpecificPriceController::addAction - -admin_specific_price_update: - path: /update/{idSpecificPrice}/ - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\SpecificPriceController::updateAction - idSpecificPrice: 0 - requirements: - idSpecificPrice: \d+ - -admin_delete_specific_price: - path: /delete/{idSpecificPrice} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\SpecificPriceController::deleteAction - idSpecificPrice: 0 - requirements: - idSpecificPrice: \d+ diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/suppliers.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/suppliers.yml deleted file mode 100644 index 7b0c1aaffcc31..0000000000000 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products/suppliers.yml +++ /dev/null @@ -1,10 +0,0 @@ -admin_supplier_refresh_product_supplier_combination_form: - path: /refresh-product-supplier-combination-form/{idProduct}/{supplierIds} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\SupplierController::refreshProductSupplierCombinationFormAction - _legacy_controller: AdminProducts - supplierIds: 0 - idProduct: 0 - requirements: - idProduct: \d+ diff --git a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products_v2/product.yml b/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products_v2/product.yml deleted file mode 100644 index 8c417497d013b..0000000000000 --- a/src/PrestaShopBundle/Resources/config/routing/admin/sell/catalog/products_v2/product.yml +++ /dev/null @@ -1,478 +0,0 @@ -_combinations: - resource: 'combination.yml' - -_images: - resource: 'image.yml' - -_specific_prices: - resource: 'specific_price.yml' - -admin_products_index: - path: / - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::indexAction - _legacy_controller: AdminProducts - _legacy_link: AdminProducts - -admin_products_light_list: - path: /light-list - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::lightListAction - _legacy_controller: AdminProducts - -admin_products_preview: - path: /{productId}/preview/{shopId} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::previewAction - _legacy_controller: AdminProducts - _legacy_link: AdminProducts:preview - _legacy_parameters: - id_product: productId - shopId: null - requirements: - productId: \d+ - -admin_products_search: - path: / - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::searchGridAction - _legacy_controller: AdminProducts - -admin_products_reset_grid_search: - path: /reset_grid_search - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::resetGridSearchAction - _legacy_controller: AdminProducts - -admin_products_grid_category_filter: - path: /grid_category_filter - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::gridCategoryFilterAction - _legacy_controller: AdminProducts - -admin_products_grid_shop_previews: - path: /shop_previews/{productId}/{shopGroupId} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::productShopPreviewsAction - _legacy_controller: AdminProducts - shopGroupId: null - requirements: - productId: \d+ - shopGroupId: \d+ - -admin_products_create: - path: /create - methods: [ GET, POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::createAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:addproduct - - AdminProducts:add - -admin_products_edit: - path: /{productId}/edit - methods: [ GET, POST, PATCH ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::editAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:updateproduct - - AdminProducts:update - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - options: - expose: true - -admin_products_select_shops: - path: /{productId}/shops - methods: [ GET, POST, PATCH ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::selectProductShopsAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:assignshops - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - options: - expose: true - -admin_products_download_virtual_product_file: - path: /virtual-product-file/{virtualProductFileId} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::downloadVirtualFileAction - _legacy_controller: AdminProducts - requirements: - virtualProductFileId: \d+ - -admin_products_delete_from_all_shops: - path: /{productId}/delete-from-all-shops - methods: [ POST, DELETE ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::deleteFromAllShopsAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:delete - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - -admin_products_delete_from_shop_group: - path: /{productId}/delete-from-shop-group/{shopGroupId} - methods: [ POST, DELETE ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::deleteFromShopGroupAction - _legacy_controller: AdminProducts - requirements: - productId: \d+ - shopGroupId: \d+ - -admin_products_delete_from_shop: - path: /{productId}/delete-from-shop/{shopId} - methods: [ POST, DELETE ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::deleteFromShopAction - _legacy_controller: AdminProducts - requirements: - productId: \d+ - shopId: \d+ - -admin_products_duplicate_all_shops: - path: /{productId}/duplicate-all-shops - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::duplicateAllShopsAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:duplicate - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - -admin_products_duplicate_shop: - path: /{productId}/duplicate-shop/{shopId} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::duplicateShopAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:duplicate - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - shopId: \d+ - -admin_products_duplicate_shop_group: - path: /{productId}/duplicate-shop-group/{shopGroupId} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::duplicateShopGroupAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:duplicate - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - shopGroupId: \d+ - -admin_products_toggle_status_for_shop: - path: /{productId}/toggle-status-for-shop/{shopId} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::toggleStatusForShopAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:statusproduct - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - shopId: \d+ - -admin_products_enable_for_all_shops: - path: /{productId}/enable-for-all-shops - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::enableForAllShopsAction - _legacy_controller: AdminProducts - requirements: - productId: \d+ - -admin_products_enable_for_shop_group: - path: /{productId}/enable-for-shop-group/{shopGroupId} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::enableForShopGroupAction - _legacy_controller: AdminProducts - requirements: - productId: \d+ - shopGroupId: \d+ - -admin_products_disable_for_all_shops: - path: /{productId}/disable-for-all-shops - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::disableForAllShopsAction - _legacy_controller: AdminProducts - requirements: - productId: \d+ - -admin_products_disable_for_shop_group: - path: /{productId}/disable-for-shop-group/{shopGroupId} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::disableForShopGroupAction - _legacy_controller: AdminProducts - shopGroupId: null - requirements: - productId: \d+ - shopGroupId: \d+ - -admin_products_update_position: - path: /update_position - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::updatePositionAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:positionproduct - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - -admin_products_bulk_enable_all_shops: - path: /bulk-enable-all-shops - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkEnableAllShopsAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkdisableSelectionproduct - requirements: - productId: \d+ - options: - expose: true - -admin_products_bulk_enable_shop: - path: /bulk-enable-shop/{shopId} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkEnableShopAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkdisableSelectionproduct - requirements: - productId: \d+ - shopId: \d+ - options: - expose: true - -admin_products_bulk_enable_shop_group: - path: /bulk-enable-shop-group/{shopGroupId} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkEnableShopGroupAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkdisableSelectionproduct - requirements: - productId: \d+ - shopGroupId: \d+ - options: - expose: true - -admin_products_bulk_disable_all_shops: - path: /bulk-disable-for-all-shops - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDisableAllShopsAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkenableSelectionproduct - requirements: - productId: \d+ - options: - expose: true - -admin_products_bulk_disable_shop: - path: /bulk-disable-shop/{shopId} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDisableShopAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkenableSelectionproduct - requirements: - productId: \d+ - shopId: \d+ - options: - expose: true - -admin_products_bulk_disable_shop_group: - path: /bulk-disable-shop-group/{shopGroupId} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDisableShopGroupAction - _legacy_controller: AdminProducts - _legacy_link: AdminProducts:submitBulkdisableSelectionproduct - requirements: - productId: \d+ - shopGroupId: \d+ - options: - expose: true - -admin_products_bulk_duplicate_all_shops: - path: /bulk-duplicate-all-shops - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDuplicateAllShopsAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkduplicateSelectionproduct - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - options: - expose: true - -admin_products_bulk_duplicate_shop: - path: /bulk-duplicate-shop/{shopId} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDuplicateShopAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkduplicateSelectionproduct - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - shopId: \d+ - options: - expose: true - -admin_products_bulk_duplicate_shop_group: - path: /bulk-duplicate-shop-group/{shopGroupId} - methods: [ POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDuplicateShopGroupAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkduplicateSelectionproduct - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - shopGroupId: \d+ - options: - expose: true - -admin_products_bulk_delete_from_all_shops: - path: /bulk-delete-from-all-shops - methods: [ POST, DELETE ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDeleteFromAllShopsAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkdeleteproduct - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - options: - expose: true - -admin_products_bulk_delete_from_shop: - path: /bulk-delete-from-shop/{shopId} - methods: [ POST, DELETE ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDeleteFromShopAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkdeleteproduct - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - shopId: \d+ - options: - expose: true - -admin_products_bulk_delete_from_shop_group: - path: /bulk-delete-from-shop-group/{shopGroupId} - methods: [ POST, DELETE ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::bulkDeleteFromShopGroupAction - _legacy_controller: AdminProducts - _legacy_link: - - AdminProducts:submitBulkdeleteproduct - _legacy_parameters: - id_product: productId - requirements: - productId: \d+ - shopGroupId: \d+ - options: - expose: true - -admin_products_search_products_for_association: - path: /search/{languageCode} - methods: [ GET, POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::searchProductsForAssociationAction - _legacy_controller: AdminProducts - requirements: - languageCode: !php/const PrestaShop\PrestaShop\Core\Domain\Language\ValueObject\TagIETF::IETF_TAG_REGEXP - -admin_products_search_combinations_for_association: - path: /search/combination/{languageCode} - methods: [ GET, POST ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\CombinationController::searchCombinationsForAssociationAction - _legacy_controller: AdminProducts - requirements: - languageCode: !php/const PrestaShop\PrestaShop\Core\Domain\Language\ValueObject\TagIETF::IETF_TAG_REGEXP - -admin_products_search_product_combinations: - path: /{productId}/search-product-combinations/{shopId}/{languageId} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\CombinationController::searchProductCombinationsAction - _legacy_controller: AdminProducts - languageId: null - shopId: null - requirements: - productId: \d+ - shopId: \d+ - languageId: \d+ - options: - expose: true - -admin_products_quantity: - path: /{productId}/quantity/{shopId} - methods: [ GET ] - defaults: - _controller: PrestaShopBundle\Controller\Admin\Sell\Catalog\Product\ProductController::quantityAction - _legacy_controller: AdminProducts - requirements: - productId: \d+ - shopId: \d+ - options: - expose: true diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/data_provider.yml b/src/PrestaShopBundle/Resources/config/services/adapter/data_provider.yml index 6162d07c11f7f..16254aeae39c2 100644 --- a/src/PrestaShopBundle/Resources/config/services/adapter/data_provider.yml +++ b/src/PrestaShopBundle/Resources/config/services/adapter/data_provider.yml @@ -16,10 +16,6 @@ services: decorates: prestashop.core.admin.data_provider.module_interface public: false - prestashop.adapter.data_provider.supplier: - class: PrestaShop\PrestaShop\Adapter\Supplier\SupplierDataProvider - deprecated: ~ - prestashop.adapter.data_provider.category: class: PrestaShop\PrestaShop\Adapter\Category\CategoryDataProvider arguments: [ "@=service('prestashop.adapter.legacy.context')" ] @@ -27,25 +23,9 @@ services: prestashop.adapter.data_provider.group: class: PrestaShop\PrestaShop\Adapter\Group\GroupDataProvider - prestashop.adapter.data_provider.tax: - class: PrestaShop\PrestaShop\Adapter\Tax\TaxRuleDataProvider - deprecated: ~ - prestashop.adapter.data_provider.manufacturer: class: PrestaShop\PrestaShop\Adapter\Manufacturer\ManufacturerDataProvider - prestashop.adapter.data_provider.product: - class: PrestaShop\PrestaShop\Adapter\Product\ProductDataProvider - deprecated: ~ - - prestashop.adapter.data_provider.attachment: - class: PrestaShop\PrestaShop\Adapter\Product\AttachmentDataProvider - deprecated: ~ - - prestashop.adapter.data_provider.feature: - class: PrestaShop\PrestaShop\Adapter\Feature\FeatureDataProvider - deprecated: ~ - prestashop.adapter.data_provider.carrier: class: PrestaShop\PrestaShop\Adapter\Carrier\CarrierDataProvider arguments: [ '@prestashop.adapter.legacy.configuration' ] @@ -65,19 +45,9 @@ services: prestashop.adapter.data_provider.customer: class: PrestaShop\PrestaShop\Adapter\Customer\CustomerDataProvider - prestashop.adapter.data_provider.combination: - class: PrestaShop\PrestaShop\Adapter\CombinationDataProvider - arguments: - - "@prestashop.core.localization.locale.context_locale" - deprecated: ~ - prestashop.adapter.data_provider.cms: class: PrestaShop\PrestaShop\Adapter\CMS\CMSDataProvider - prestashop.adapter.data_provider.pack: - class: PrestaShop\PrestaShop\Adapter\Pack\PackDataProvider - deprecated: ~ - PrestaShop\PrestaShop\Adapter\Tab\TabDataProvider: class: PrestaShop\PrestaShop\Adapter\Tab\TabDataProvider public: false diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/form.yml b/src/PrestaShopBundle/Resources/config/services/adapter/form.yml index dc3494f2ed792..102963fa610fb 100644 --- a/src/PrestaShopBundle/Resources/config/services/adapter/form.yml +++ b/src/PrestaShopBundle/Resources/config/services/adapter/form.yml @@ -71,12 +71,14 @@ services: arguments: - '@=service("prestashop.adapter.legacy.context").getLanguage().id' - prestashop.adapter.form.choice_provider.feature_values_choice_provider: - class: 'PrestaShop\PrestaShop\Adapter\Form\ChoiceProvider\FeatureValuesChoiceProvider' + PrestaShop\PrestaShop\Adapter\Form\ChoiceProvider\FeatureValuesChoiceProvider: arguments: - '@PrestaShop\PrestaShop\Adapter\Feature\Repository\FeatureValueRepository' - '@prestashop.adapter.legacy.context' + prestashop.adapter.form.choice_provider.feature_values_choice_provider: + alias: 'PrestaShop\PrestaShop\Adapter\Form\ChoiceProvider\FeatureValuesChoiceProvider' + prestashop.adapter.form.choice_provider.supplier_name_by_id_choice_provider: class: 'PrestaShop\PrestaShop\Adapter\Form\ChoiceProvider\SupplierNameByIdChoiceProvider' diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/product.yml b/src/PrestaShopBundle/Resources/config/services/adapter/product.yml index 2e7021bb8727c..57b76bfa61ed2 100644 --- a/src/PrestaShopBundle/Resources/config/services/adapter/product.yml +++ b/src/PrestaShopBundle/Resources/config/services/adapter/product.yml @@ -18,42 +18,6 @@ services: - '@PrestaShop\PrestaShop\Core\Currency\CurrencyDataProviderInterface' - '@PrestaShop\PrestaShop\Adapter\ContextStateManager' - # Deprecated in 8.1 - PrestaShop\PrestaShop\Adapter\Product\AdminProductDataProvider: - decorates: prestashop.core.admin.data_provider.product_interface - arguments: - - "@doctrine.orm.entity_manager" - - '@PrestaShop\PrestaShop\Adapter\ImageManager' - - "@prestashop.static_cache.adapter" - public: false - deprecated: ~ - - PrestaShop\PrestaShop\Adapter\Product\AdminProductWrapper: - arguments: - - "@translator" - - "@=service('prestashop.adapter.legacy.context').getContext().employee.getAssociatedShops()" - - '@PrestaShop\PrestaShop\Core\Localization\LocaleInterface' - - '@PrestaShopBundle\Utils\FloatParser' - deprecated: ~ - - PrestaShop\PrestaShop\Adapter\Attribute\AdminAttributeGeneratorControllerWrapper: - deprecated: ~ - - PrestaShop\PrestaShop\Adapter\Product\FilterCategoriesRequestPurifier: - deprecated: ~ - - PrestaShop\PrestaShop\Adapter\Product\ListParametersUpdater: - deprecated: ~ - - PrestaShop\PrestaShop\Adapter\Product\AdminProductDataUpdater: - class: PrestaShop\PrestaShop\Adapter\Product\AdminProductDataUpdater - decorates: prestashop.core.admin.data_updater.product_interface - public: false - arguments: - - '@PrestaShop\PrestaShop\Core\Hook\HookDispatcherInterface' - - "@prestashop.static_cache.adapter" - deprecated: ~ - PrestaShop\PrestaShop\Adapter\Product\QueryHandler\SearchProductsForAssociationHandler: autowire: true public: false diff --git a/src/PrestaShopBundle/Resources/config/services/adapter/services.yml b/src/PrestaShopBundle/Resources/config/services/adapter/services.yml index f69fb0c4ed789..73fffed69b418 100644 --- a/src/PrestaShopBundle/Resources/config/services/adapter/services.yml +++ b/src/PrestaShopBundle/Resources/config/services/adapter/services.yml @@ -93,23 +93,6 @@ services: prestashop.core.module.updater: class: PrestaShop\PrestaShop\Adapter\Module\ModuleDataUpdater - prestashop.adapter.admin.model.product: - class: PrestaShopBundle\Model\Product\AdminModelAdapter - arguments: - - '@PrestaShop\PrestaShop\Adapter\LegacyContext' - - '@PrestaShop\PrestaShop\Adapter\Product\AdminProductWrapper' - - '@PrestaShop\PrestaShop\Adapter\Tools' - - "@prestashop.adapter.data_provider.product" - - "@prestashop.adapter.data_provider.supplier" - - "@prestashop.adapter.data_provider.feature" - - "@prestashop.adapter.data_provider.pack" - - "@prestashop.adapter.shop.context" - - "@prestashop.adapter.data_provider.tax" - - '@prestashop.adapter.legacy.configuration' - - "@router" - - '@PrestaShopBundle\Utils\FloatParser' - deprecated: ~ - prestashop.adapter.translation_route_finder: class: PrestaShop\PrestaShop\Adapter\Translations\TranslationRouteFinder arguments: diff --git a/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml b/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml index 2fc91cbea23fb..06d621c4fe01a 100644 --- a/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml +++ b/src/PrestaShopBundle/Resources/config/services/bundle/form/form_type.yml @@ -46,8 +46,6 @@ services: resource: "%kernel.project_dir%/src/PrestaShopBundle/Form/Admin/Sell/CartRule/*" PrestaShopBundle\Form\Admin\Sell\Product\: resource: "%kernel.project_dir%/src/PrestaShopBundle/Form/Admin/Sell/Product/*" - PrestaShopBundle\Form\Admin\Category\: - resource: "%kernel.project_dir%/src/PrestaShopBundle/Form/Admin/Category/*" PrestaShopBundle\Form\Admin\Type\: resource: "%kernel.project_dir%/src/PrestaShopBundle/Form/Admin/Type/*" PrestaShopBundle\Form\Admin\AdvancedParameters\: @@ -73,92 +71,13 @@ services: PrestaShopBundle\Form\Admin\Type\LocaleChoiceType: PrestaShopBundle\Form\Admin\Type\DateRangeType: - PrestaShopBundle\Form\Admin\Category\SimpleCategory: - deprecated: ~ PrestaShopBundle\Form\Admin\Type\ChoiceCategoriesTreeType: PrestaShopBundle\Form\Admin\Type\TranslateType: - PrestaShopBundle\Form\Admin\Feature\ProductFeature: - arguments: - - "@translator" - - "@prestashop.adapter.legacy.context" - - "@router" - - "@prestashop.adapter.data_provider.feature" - deprecated: ~ - - PrestaShopBundle\Form\Admin\Product\ProductAttachement: - deprecated: ~ - PrestaShopBundle\Form\Admin\Product\ProductCombination: - deprecated: ~ - PrestaShopBundle\Form\Admin\Product\ProductCustomField: - deprecated: ~ - PrestaShopBundle\Form\Admin\Product\ProductInformation: - arguments: - - "@translator" - - "@prestashop.adapter.legacy.context" - - "@router" - - "@prestashop.adapter.data_provider.category" - - "@prestashop.adapter.data_provider.product" - - "@prestashop.adapter.data_provider.feature" - - "@prestashop.adapter.data_provider.manufacturer" - deprecated: ~ - - PrestaShopBundle\Form\Admin\Product\ProductOptions: - arguments: - - "@translator" - - "@prestashop.adapter.legacy.context" - - "@prestashop.adapter.data_provider.supplier" - - "@prestashop.adapter.data_provider.attachment" - - "@router" - - PrestaShopBundle\Form\Admin\Product\ProductPrice: - arguments: - - "@translator" - - "@prestashop.adapter.data_provider.tax" - - "@router" - - "@prestashop.adapter.shop.context" - - "@prestashop.adapter.data_provider.country" - - "@prestashop.adapter.data_provider.currency" - - "@prestashop.adapter.data_provider.group" - - "@prestashop.adapter.legacy.context" - deprecated: ~ - - PrestaShopBundle\Form\Admin\Product\ProductQuantity: - deprecated: ~ - PrestaShopBundle\Form\Admin\Product\ProductSeo: - deprecated: ~ - PrestaShopBundle\Form\Admin\Product\ProductShipping: - arguments: - $carrierDataProvider: "@prestashop.adapter.data_provider.carrier" - deprecated: ~ - PrestaShopBundle\Form\Admin\Product\ProductSpecificPrice: - arguments: - - "@router" - - "@translator" - - "@prestashop.adapter.shop.context" - - "@prestashop.adapter.data_provider.country" - - "@prestashop.adapter.data_provider.currency" - - "@prestashop.adapter.data_provider.group" - - "@prestashop.adapter.legacy.context" - - "@prestashop.adapter.data_provider.customer" - deprecated: ~ - - PrestaShopBundle\Form\Admin\Product\ProductSupplierCombination: - deprecated: ~ - PrestaShopBundle\Form\Admin\Product\ProductVirtual: - deprecated: ~ - PrestaShopBundle\Form\Admin\Type\TypeaheadProductCollectionType: - arguments: - - "@prestashop.adapter.data_provider.product" - - "@prestashop.adapter.data_provider.category" PrestaShopBundle\Form\Admin\Type\TypeaheadCustomerCollectionType: arguments: - "@prestashop.adapter.data_provider.customer" - PrestaShopBundle\Form\Admin\Product\ProductCombinationBulk: - deprecated: ~ - PrestaShopBundle\Form\Admin\Product\ProductCategories: - deprecated: ~ PrestaShopBundle\Form\Admin\Sell\Order\Invoices\GenerateByDateType: PrestaShopBundle\Form\Admin\Sell\Order\Invoices\GenerateByStatusType: @@ -188,7 +107,7 @@ services: PrestaShopBundle\Form\Admin\Configure\ShopParameters\OrderPreferences\GiftOptionsType: arguments: - $taxChoices: '@=service("prestashop.adapter.data_provider.tax").getTaxRulesGroupChoices()' + $taxChoices: '@=service("prestashop.core.form.choice_provider.tax_rule_group_choice_provider").getChoices()' PrestaShopBundle\Form\Admin\Configure\AdvancedParameters\Import\ImportType: arguments: diff --git a/src/PrestaShopBundle/Resources/config/services/bundle/services.yml b/src/PrestaShopBundle/Resources/config/services/bundle/services.yml index 9f661a2772e0b..1a83dc64189a5 100644 --- a/src/PrestaShopBundle/Resources/config/services/bundle/services.yml +++ b/src/PrestaShopBundle/Resources/config/services/bundle/services.yml @@ -13,10 +13,6 @@ services: public: true # Interfaced services to decorate - prestashop.core.admin.data_provider.product_interface: - class: PrestaShopBundle\Service\DataProvider\Admin\ProductInterface - prestashop.core.admin.data_updater.product_interface: - class: PrestaShopBundle\Service\DataUpdater\Admin\ProductInterface prestashop.core.admin.page_preference_interface: class: PrestaShopBundle\Service\TransitionalBehavior\AdminPagePreferenceInterface prestashop.core.admin.data_provider.module_interface: @@ -42,12 +38,6 @@ services: class: PrestaShopBundle\Service\Database\DoctrineNamingStrategy arguments: [ "%database_prefix%" ] - prestashop.service.product: - class: PrestaShopBundle\Service\ProductService - arguments: - - "@prestashop.adapter.data_provider.product" - deprecated: ~ - prestashop.service.translation: class: PrestaShopBundle\Service\TranslationService properties: diff --git a/src/PrestaShopBundle/Resources/config/services/core/product.yml b/src/PrestaShopBundle/Resources/config/services/core/product.yml index 304f8bff077d1..4bd52a488d777 100644 --- a/src/PrestaShopBundle/Resources/config/services/core/product.yml +++ b/src/PrestaShopBundle/Resources/config/services/core/product.yml @@ -2,13 +2,6 @@ services: _defaults: public: true - # Product CSV Exporter - PrestaShop\PrestaShop\Core\Product\ProductCsvExporter: - arguments: - - '@translator' - - '@prestashop.core.admin.data_provider.product_interface' - deprecated: ~ - PrestaShop\PrestaShop\Core\Product\Combination\Generator\CombinationGenerator: ~ PrestaShop\PrestaShop\Core\Product\Combination\Generator\CombinationGeneratorInterface: '@PrestaShop\PrestaShop\Core\Product\Combination\Generator\CombinationGenerator' diff --git a/src/PrestaShopBundle/Resources/views/Admin/Category/categories.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Category/categories.html.twig deleted file mode 100644 index e3e3786ddbca2..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Category/categories.html.twig +++ /dev/null @@ -1,31 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
- -
- -
- {{ categories|json_encode }} -
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Blocks/filters.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Blocks/filters.html.twig deleted file mode 100644 index 9124c2f665603..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Blocks/filters.html.twig +++ /dev/null @@ -1,136 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
- {% block product_catalog_filter_by_categories %} - - {% endblock %} - - {% block product_catalog_filter_bulk_actions %} -
- {% set buttons_action = [ - { - "onclick": "bulkProductAction(this, 'activate_all');", - "icon": "radio_button_checked", - "label": "Activate selection"|trans({}, 'Admin.Actions') - }, { - "onclick": "bulkProductAction(this, 'deactivate_all');", - "icon": "radio_button_unchecked", - "label": "Deactivate selection"|trans({}, 'Admin.Actions') - } - ] %} - - {% set buttons_action = buttons_action|merge([ - { - "divider": true - }, { - "onclick": "bulkProductAction(this, 'duplicate_all');", - "icon": "content_copy", - "label": "Duplicate selection"|trans({}, 'Admin.Actions') - } - ]) %} - - - {% set buttons_action = buttons_action|merge([ - { - "divider": true - }, { - "onclick": "bulkProductAction(this, 'delete_all');", - "icon": "delete", - "label": "Delete selection"|trans({}, 'Admin.Actions') - } - ]) %} - - {% include '@PrestaShop/Admin/Helpers/dropdown_menu.html.twig' with { - 'div_style': "btn-group dropdown bulk-catalog", - 'button_id': "product_bulk_menu", - 'disabled': true, - 'menu_label': "Bulk actions"|trans({}, 'Admin.Global'), - 'buttonType': "outline-secondary", - 'menu_icon': "icon-caret-up", - 'items': buttons_action - } %} -
- {% endblock %} -
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Blocks/tools.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Blocks/tools.html.twig deleted file mode 100644 index 2792054d71c8e..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Blocks/tools.html.twig +++ /dev/null @@ -1,52 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} - diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Forms/form_edit_dropdown.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Forms/form_edit_dropdown.html.twig deleted file mode 100644 index 98a080621fa5d..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Forms/form_edit_dropdown.html.twig +++ /dev/null @@ -1,67 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% set buttonType = buttonType|default('primary') %} -{% set right = right|default(false) %} - -
- - {% if default_item is defined %} - - {% if default_item.icon %} - {{ default_item.icon }} - {% endif %} - {{ default_item.label|default('') }} - - {% endif %} - - - - - -
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Forms/form_products.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Forms/form_products.html.twig deleted file mode 100644 index 588e384d7953f..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Forms/form_products.html.twig +++ /dev/null @@ -1,68 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
- - - - {% block product_catalog_form_table %} - {{ include('@Product/CatalogPage/Lists/products_table.html.twig', { - 'limit': limit, - 'orderBy': orderBy, - 'offset': offset, - 'sortOrder': sortOrder, - 'filter_category': filter_category, - 'filter_column_id_product': filter_column_id_product, - 'filter_column_name': filter_column_name, - 'filter_column_reference': filter_column_reference, - 'filter_column_name_category': filter_column_name_category, - 'filter_column_price': filter_column_price, - 'filter_column_sav_quantity': filter_column_sav_quantity, - 'filter_column_active':filter_column_active, - 'has_category_filter': has_category_filter, - 'activate_drag_and_drop': activate_drag_and_drop, - 'products': products, - 'last_sql': last_sql - }) - }} - {% endblock %} - - {% if product_count_filtered > 20 %} - {{ render(controller('PrestaShopBundle\\Controller\\Admin\\CommonController:paginationAction', { - 'limit': limit, - 'offset': offset, - 'total': product_count_filtered, - 'caller_route': app.request.attributes.get('_route'), - 'caller_parameters': pagination_parameters, - 'limit_choices': pagination_limit_choices - })) }} - {% endif %} -
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Lists/list.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Lists/list.html.twig deleted file mode 100644 index 09319ebc63556..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Lists/list.html.twig +++ /dev/null @@ -1,145 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} - - {% for product in products %} - {% block product_catalog_form_table_row %} - - -
- -
- - - - - - {{ product.image|raw }} - - - {{ product.name|default('N/A'|trans({}, 'Admin.Global')) }} - - - {{ product.reference|default('') }} - - - {{ product.name_category|default('') }} - - - {{ product.price|default('N/A'|trans({}, 'Admin.Global')) }} - - - {{ product.price_final|default('N/A'|trans({}, 'Admin.Global')) }} - - - {% if configuration('PS_STOCK_MANAGEMENT') %} - - - {% if product.sav_quantity is defined and product.sav_quantity > 0 %} - {{ product.sav_quantity }} - {% else %} - {{ product.sav_quantity|default('N/A'|trans({}, 'Admin.Global')) }} - {% endif %} - - - {% else %} - - {% endif %} - - -
- - - - - -
- - {% if product.position is defined %} - - {% if activate_drag_and_drop %} - - {% endif %} - {{ product.position }} - - - - {% endif %} - -
- - {% set buttons_action = [ - { - "href": product.preview_url|default('#'), - "target": "_blank", - "icon": "remove_red_eye", - "label": "Preview"|trans({}, 'Admin.Actions') - } - ] %} - - {% set buttons_action = buttons_action|merge([ - { - "onclick": "unitProductAction(this, 'duplicate');", - "icon": "content_copy", - "label": "Duplicate"|trans({}, 'Admin.Actions') - } - ]) %} - - {% set buttons_action = buttons_action|merge([ - { - "onclick": "unitProductAction(this, 'delete');", - "icon": "delete", - "label": "Delete"|trans({}, 'Admin.Actions') - } - ]) %} - - {% include '@Product/CatalogPage/Forms/form_edit_dropdown.html.twig' with { - 'button_id': "product_list_id_" ~ product.id_product ~ "_menu", - 'default_item': { - "href": product.url|default('#'), - "icon": "mode_edit" - }, - 'right': true, - 'items': buttons_action - } %} -
- - - {% endblock %} -{% else %} - - {{ "There is no result for this search. Update your filters to view other products."|trans({}, 'Admin.Catalog.Notification') }} - -{% endfor %} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Lists/list_quicknav.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Lists/list_quicknav.html.twig deleted file mode 100644 index e68cdee7271a0..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Lists/list_quicknav.html.twig +++ /dev/null @@ -1,99 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
-
- -

{{ "Quick navigation"|trans({}, 'Admin.Global') }}

-
-
- - - - - - - {% if configuration('PS_STOCK_MANAGEMENT') %} - - {% endif %} - - - - {% for product in products %} - - - - - {% if configuration('PS_STOCK_MANAGEMENT') %} - - {% endif %} - - {% else %} - - {% endfor %} - -
- {{ "Name"|trans({}, 'Admin.Global') }} -
- {{ product.name|default('N/A'|trans({}, 'Admin.Global')) }} -
- {{ "There is no result for this search. Update your filters to view other products."|trans({}, 'Admin.Catalog.Notification') }} -
-
- -
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Lists/products_table.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Lists/products_table.html.twig deleted file mode 100644 index ed86772192008..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/Lists/products_table.html.twig +++ /dev/null @@ -1,229 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% import '@PrestaShop/Admin/macros.html.twig' as ps %} -
- - - {% block product_catalog_form_table_header %} - - - - - - - - - - - {% if configuration('PS_STOCK_MANAGEMENT') %} - - {% else %} - - {% endif %} - - - {% if has_category_filter == true %} - - {% endif %} - - - {% endblock %} - - {% block product_catalog_form_table_filters %} - {% set filters_disabled = activate_drag_and_drop %} - - - - - - - - - - {% if configuration('PS_STOCK_MANAGEMENT') %} - - {% else %} - - {% endif %} - - - {% if has_category_filter == true %} - - {% endif %} - - - {% endblock %} - - {% block product_catalog_form_table_items %} - {{ render(controller('PrestaShopBundle\\Controller\\Admin\\ProductController::listAction', { - 'limit': limit, - 'offset': offset, - 'orderBy': orderBy, - 'sortOrder': sortOrder, - 'products': products, - 'last_sql': last_sql - })) }} - {% endblock %} -
- {{ ps.sortable_column_header("ID"|trans({}, 'Admin.Global'), 'id_product', orderBy, sortOrder) }} - - {{ "Image"|trans({}, 'Admin.Global') }} - - {{ ps.sortable_column_header("Name"|trans({}, 'Admin.Global'), 'name', orderBy, sortOrder) }} - - {{ ps.sortable_column_header("Reference"|trans({}, 'Admin.Global'), 'reference', orderBy, sortOrder) }} - - {{ ps.sortable_column_header("Category"|trans({}, 'Admin.Catalog.Feature'), 'name_category', orderBy, sortOrder) }} - - {{ ps.sortable_column_header("Price (tax excl.)"|trans({}, 'Admin.Catalog.Feature'), 'price', orderBy, sortOrder) }} - - {{ "Price (tax incl.)"|trans({}, 'Admin.Catalog.Feature') }} - - {{ ps.sortable_column_header("Quantity"|trans({}, 'Admin.Catalog.Feature'), 'sav_quantity', orderBy, sortOrder) }} - - {{ ps.sortable_column_header("Status"|trans({}, 'Admin.Global'), 'active', orderBy, sortOrder) }} - - {{ ps.sortable_column_header("Position"|trans({}, 'Admin.Global'), 'position', orderBy, sortOrder) }} - - {{ "Actions"|trans({}, 'Admin.Global') }} -
- {% block product_catalog_filter_select_all %} -
- -
- {% endblock %} -
- {% include '@PrestaShop/Admin/Helpers/range_inputs.html.twig' with { - 'input_name': "filter_column_id_product", - 'min': '0', - 'minLabel': "Min"|trans({}, 'Admin.Global'), - 'maxLabel': "Max"|trans({}, 'Admin.Global'), - 'value': filter_column_id_product, - 'disabled': filters_disabled, - } %} -   - - - - - - - {% include '@PrestaShop/Admin/Helpers/range_inputs.html.twig' with { - 'input_name': "filter_column_price", - 'min': '0', - 'max': '1000000', - 'minLabel': "Min"|trans({}, 'Admin.Global'), - 'maxLabel': "Max"|trans({}, 'Admin.Global'), - 'value': filter_column_price, - 'disabled': filters_disabled, - } %} - - {% include '@PrestaShop/Admin/Helpers/range_inputs.html.twig' with { - 'input_name': "filter_column_sav_quantity", - 'min': '-1000000', - 'max': '1000000', - 'minLabel': "Min"|trans({}, 'Admin.Global'), - 'maxLabel': "Max"|trans({}, 'Admin.Global'), - 'value': filter_column_sav_quantity, - 'disabled': filters_disabled, - } %} - -
- -
-
- {% if not(activate_drag_and_drop) %} - - {% else %} - - {% endif %} - - - -
-
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/catalog.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/catalog.html.twig deleted file mode 100644 index b7a3d598686da..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/catalog.html.twig +++ /dev/null @@ -1,232 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% extends '@PrestaShop/Admin/layout.html.twig' %} -{% form_theme categories '@PrestaShop/Admin/Product/Themes/categories_theme.html.twig' %} - -{% block stylesheets %} - {{ parent() }} - -{% endblock %} - -{% block javascripts %} - {{ parent() }} - - - - - -{% endblock %} - -{% block content %} -
- - {{ renderhook('legacy_block_kpi', {'kpi_controller': 'AdminProductsController'}) }} - -
- - {% if permission_error|length %} - - {% endif %} - -
- {% block product_catalog_filters %} - {{ include('@Product/CatalogPage/Blocks/filters.html.twig', { - 'limit': limit, - 'offset': offset, - 'orderBy': orderBy, - 'sortOrder': sortOrder - }) - }} - {% endblock %} - {% block product_catalog_tools %} - {{ include('@Product/CatalogPage/Blocks/tools.html.twig', {'import_link': import_link }) }} - {% endblock %} -
- - {% block product_catalog_form %} - {{ include('@Product/CatalogPage/Forms/form_products.html.twig', { - 'limit': limit, - 'orderBy': orderBy, - 'offset': offset, - 'sortOrder': sortOrder, - 'filter_category': filter_category, - 'filter_column_id_product': filter_column_id_product, - 'filter_column_name': filter_column_name, - 'filter_column_reference': filter_column_reference, - 'filter_column_name_category': filter_column_name_category, - 'filter_column_price': filter_column_price, - 'filter_column_sav_quantity': filter_column_sav_quantity, - 'filter_column_active': filter_column_active, - 'has_category_filter': has_category_filter, - 'activate_drag_and_drop': activate_drag_and_drop, - 'products': products, - 'last_sql': last_sql, - 'product_count_filtered': product_count_filtered, - 'pagination_parameters': pagination_parameters, - 'pagination_limit_choices': pagination_limit_choices - }) - }} - {% endblock %} - -
-
- - {# Duplication product modal #} - {% embed '@PrestaShop/Admin/Helpers/bootstrap_popup.html.twig' with { - 'id': "catalog_duplicate_all_modal", - 'title': "Duplicating products"|trans({}, 'Admin.Catalog.Notification'), - 'closable': true, - 'progressbar': { - 'id': "catalog_duplicate_all_progression", - 'label': "Duplicating..."|trans({}, 'Admin.Catalog.Notification') - }, - 'actions': [], - }%} - {% block content %} - - {% endblock %} - {% endembed %} - - - {# Activation product modal #} - {% embed '@PrestaShop/Admin/Helpers/bootstrap_popup.html.twig' with { - 'id': "catalog_activate_all_modal", - 'title': "Activating products"|trans({}, 'Admin.Catalog.Notification'), - 'closable': true, - 'progressbar': { - 'id': "catalog_activate_all_progression", - 'label': "Activating..."|trans({}, 'Admin.Catalog.Notification') - }, - 'actions': [], - }%} - {% block content %} - - {% endblock %} - {% endembed %} - - - {# Desactivation product modal #} - {% embed '@PrestaShop/Admin/Helpers/bootstrap_popup.html.twig' with { - 'id': "catalog_deactivate_all_modal", - 'title': "Deactivating products"|trans({}, 'Admin.Catalog.Notification'), - 'closable': true, - 'progressbar': { - 'id': "catalog_deactivate_all_progression", - 'label': "Deactivating..."|trans({}, 'Admin.Catalog.Notification') - }, - 'actions': [], - }%} - {% block content %} - - {% endblock %} - {% endembed %} - - - {# Deletion product modal #} - {% embed '@PrestaShop/Admin/Helpers/bootstrap_popup.html.twig' with { - 'id': "catalog_delete_all_modal", - 'title': "Deleting products"|trans({}, 'Admin.Catalog.Notification'), - 'closable': true, - 'progressbar': { - 'id': "catalog_delete_all_progression", - 'label': "Deleting..."|trans({}, 'Admin.Catalog.Notification') - }, - 'actions': [], - }%} - {% block content %} - - {% endblock %} - {% endembed %} - - - {# Confirmation deletion product modal #} - {% embed '@PrestaShop/Admin/Helpers/bootstrap_popup.html.twig' with { - 'id': "catalog_deletion_modal", - 'title': 'Delete products?'|trans({}, 'Admin.Catalog.Feature'), - 'closable': true, - 'closeLabel': 'Cancel'|trans({}, 'Admin.Actions'), - 'actions': [{ - 'type': 'button', - 'label': 'Delete'|trans({}, 'Admin.Actions'), - 'value': 'confirm', - 'class': 'btn btn-danger btn-lg', - }], - }%} - {% block content %} - - {% endblock %} - {% endembed %} - - {% embed '@PrestaShop/Admin/Helpers/bootstrap_popup.html.twig' with { - 'id': "catalog_sql_query_modal", - 'title': "SQL query"|trans({}, 'Admin.Global'), - 'closable': true, - 'actions': [{ - 'type': 'button', - 'label': "Export to SQL Manager"|trans({}, 'Admin.Actions'), - 'value': 'sql_manager', - 'class': 'btn btn-primary btn-lg', - }], - } %} - {% block content %} -
- -
- {% endblock %} - {% endembed %} - -{% endblock %} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/catalog_empty.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/catalog_empty.html.twig deleted file mode 100644 index 66df64c507074..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/CatalogPage/catalog_empty.html.twig +++ /dev/null @@ -1,43 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% extends '@PrestaShop/Admin/layout.html.twig' %} - -{% block content %} -
-
- add_shopping_cart -
- - {{ '[1]Add your first product[/1][2]or import a list of products (filetypes: .csv, .xls, .xlsx, .xlst, .ods, .ots).[/2]'|trans({}, 'Admin.Catalog.Feature')| - replace({ - '[1]': '', - '[2]': '', - })|raw - }} - -
-{% endblock %} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Blocks/footer.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Blocks/footer.html.twig deleted file mode 100644 index 09077c2db44e0..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Blocks/footer.html.twig +++ /dev/null @@ -1,150 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% block product_footer %} - -{% endblock %} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Blocks/header.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Blocks/header.html.twig deleted file mode 100644 index f5332c5545df1..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Blocks/header.html.twig +++ /dev/null @@ -1,81 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
-
- {% if is_multishop_context %} -
- -
- {% endif %} - -
-
-
- {{ form_widget(formName) }} -
-
- {{ form_widget(formType) }} - -
-
-
- -
-
- -
- {{ form_errors(formName) }} - {{ form_errors(formType) }} -
-
-
- diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Blocks/tabs.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Blocks/tabs.html.twig deleted file mode 100644 index bad8b2614aa60..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Blocks/tabs.html.twig +++ /dev/null @@ -1,44 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} - diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_categories.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_categories.html.twig deleted file mode 100644 index 76828975db9c3..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_categories.html.twig +++ /dev/null @@ -1,83 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -

{{ "Categories"|trans({}, 'Admin.Catalog.Feature') }} - - -

-
-
-
- - - {{ include('@PrestaShop/Admin/Category/categories.html.twig', {'categories': categories }) }} - {{ form_errors(form.id_category_default) }} - {{ form_widget(form.id_category_default) }} -
- expand_more{{ "Expand"|trans({}, 'Admin.Actions') }} - expand_less{{ "Collapse"|trans({}, 'Admin.Actions') }} -
- {{ form_errors(form.categories) }} - {{ form_widget(form.categories, {'defaultCategory': true, defaultHidden: true}) }} {# see bootstrap_4_layout.html.twig #} -
-
-
-
-

- {{ "Create a new category"|trans({}, 'Admin.Catalog.Feature') }} - - -

-
-
-
- - {{ form_errors(form.new_category) }} - {{ form_widget(form.new_category.name) }} -
- -
- -
- - {{ form_widget(form.new_category.id_parent) }} -
- -
- - -
-
- - - add_circle - {{ 'Create a category'|trans({}, 'Admin.Catalog.Feature') }} - -
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combination.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combination.html.twig deleted file mode 100644 index 982ad926f9ced..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combination.html.twig +++ /dev/null @@ -1,282 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} - - -
- {{ form.vars.value.name }} - -
-
- {{ default_currency_symbol }} -
- -
- - -
- {{ form.vars.value.final_price }} {{ default_currency_symbol }} -
- {% if 'PS_USE_ECOTAX'|configuration != 0 %} - {% set attributeEcotax = form.vars.value.attribute_ecotax %} - {% if attributeEcotax == 0 %} - {% set attributeEcotax = form.vars.value.product_ecotax %} - {% endif %} -
- {{ 'Ecotax'|trans({}, 'Admin.Catalog.Feature') }} - {{ attributeEcotax|round(2) }} {{ default_currency_symbol }} -
- {% endif %} - - {% if configuration('PS_STOCK_MANAGEMENT') %} - -
- -
- - {% endif %} - - -
- mode_edit -
-
- -
-
- -
-

- {{ "Combination details"|trans({}, 'Admin.Catalog.Feature') }} - - {{ form.vars.value.name }} -

- {{ form_widget(form.attribute_default) }} -
- {% if configuration('PS_STOCK_MANAGEMENT') %} -
-
- - {{ form_errors(form.attribute_quantity) }} - {{ form_widget(form.attribute_quantity) }} -
-
- {% endif %} -
-
- - {{ form_errors(form.available_date_attribute) }} - {{ form_widget(form.available_date_attribute) }} -
-
-
-
- - {{ form_errors(form.attribute_minimal_quantity) }} - {{ form_widget(form.attribute_minimal_quantity) }} -
-
-
-
- - {{ form_errors(form.attribute_reference) }} - {{ form_widget(form.attribute_reference) }} -
-
-
-

- {{ 'Stock'|trans({}, 'Admin.Catalog.Feature') }} -

-
-
-
- - {{ form_errors(form.attribute_location) }} - {{ form_widget(form.attribute_location) }} -
-
-
-
- - {{ form_errors(form.attribute_low_stock_threshold) }} - {{ form_widget(form.attribute_low_stock_threshold) }} -
-
-
-
- -
- {{ form_errors(form.attribute_low_stock_alert) }} - {{ form_widget(form.attribute_low_stock_alert) }} - Team[/1]"|trans({'[1]':'','[/1]':''}, 'Admin.Catalog.Help')|raw }}" > - -
-
-
-
-

- {{ "Price and impact"|trans({}, 'Admin.Catalog.Feature') }} -

-
-
-
- - {{ form_errors(form.attribute_wholesale_price) }} - {{ form_widget(form.attribute_wholesale_price) }} -
-
-
-
- - {{ form_errors(form.attribute_price) }} - {{ form_widget(form.attribute_price) }} -
-
-
-
- - {{ form_errors(form.attribute_priceTI) }} - {{ form_widget(form.attribute_priceTI) }} -
-
-
-
{{ "Final retail price (tax excl.) will be"|trans({}, 'Admin.Catalog.Feature') }} {{ form.vars.value.final_price }} {{ default_currency_symbol }}
-
{{ "Final retail price (tax incl.) will be"|trans({}, 'Admin.Catalog.Feature') }} {{ form.vars.value.final_price_tax_included }} {{ default_currency_symbol }}
-
-
-
-
-
- - {{ form_errors(form.attribute_ecotax) }} - {{ form_widget(form.attribute_ecotax) }} -
-
-
-
- - {{ form_errors(form.attribute_unity) }} - {{ form_widget(form.attribute_unity) }} -
-
-
-
- - {{ form_errors(form.attribute_weight) }} - {{ form_widget(form.attribute_weight) }} -
-
-
-

- {{ "Specific references"|trans({}, 'Admin.Catalog.Feature') }} -

-
-
-
- - {{ form_errors(form.attribute_isbn) }} - {{ form_widget(form.attribute_isbn) }} -
-
-
-
- - {{ form_errors(form.attribute_ean13) }} - {{ form_widget(form.attribute_ean13) }} -
-
-
-
- - {{ form_errors(form.attribute_upc) }} - {{ form_widget(form.attribute_upc) }} -
-
-
-
- - {{ form_errors(form.attribute_mpn) }} - {{ form_widget(form.attribute_mpn) }} -
-
-
-

- {{ "Images"|trans({}, 'Admin.Catalog.Feature') }} -

- -
- - {{ form_errors(form.id_image_attr) }} - {{ form_widget(form.id_image_attr) }} -
- - {{ renderhook('displayAdminProductsCombinationBottom', { 'id_product': form.vars.value.id_product, 'id_product_attribute': form.vars.value.id_product_attribute }) }} - - {{ form_widget(form.id_product_attribute) }} -
-
- - - delete - - - {% set checked = form.vars.value.attribute_default == 1 ? 'checked' : '' %} - - - diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combinations.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combinations.html.twig deleted file mode 100644 index f46b0d21c2356..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combinations.html.twig +++ /dev/null @@ -1,203 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
-
-

- {{ 'Manage your product combinations'|trans({}, 'Admin.Catalog.Feature') }} - Attributes & Features for this!"|trans({}, 'Admin.Catalog.Help') }}" - > -

-
- -
-
-
- {{ form_errors(form.attributes) }} - {{ form_widget(form.attributes) }} -
-
-
- -
-
-
- -
- -
-
- {{ include('@Product/ProductPage/Forms/form_combinations_bulk.html.twig', { 'form': form_combination_bulk }) }} -
-
-
- -
- - - - - - - - - {% if configuration('PS_STOCK_MANAGEMENT') %} - - {% endif %} - - - - - {% if has_combinations %} - - - - - - - {% if configuration('PS_STOCK_MANAGEMENT') %} - - {% endif %} - - - {% endif %} - -
- - {{ 'Combinations'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Impact on price (tax excl.)'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Final price (tax excl.)'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Quantity'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Default combination'|trans({}, 'Admin.Catalog.Feature') }}
- -
-
-
-
-
-
-
-
-
-
-
-
- -
- {% for attribute_group in attribute_groups %} -
- -
-
- {% for attribute in attribute_group.attributes %} -
- - -
- {% endfor %} -
-
-
- {% endfor %} -
-
- -
-
- -
-

{{ 'Availability preferences'|trans({}, 'Admin.Catalog.Feature') }}

-
- {% if configuration('PS_STOCK_MANAGEMENT') %} -
- - {{ form_errors(form.out_of_stock) }} - {{ form_widget(form.out_of_stock) }} -
- -
- - {{ form_errors(form.available_now) }} - {{ form_widget(form.available_now) }} -
- -
- - {{ form_errors(form.available_later) }} - {{ form_widget(form.available_later) }} -
- {% else %} -
-

{{ 'Stock management is disabled'|trans({}, 'Admin.Catalog.Feature') }}

-
- {% endif %} - - {% if not has_combinations %} -
- - {{ form_errors(form.available_date) }} - {{ form_widget(form.available_date) }} -
- {% endif %} - -
-
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combinations_bulk.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combinations_bulk.html.twig deleted file mode 100644 index 3263b1ea6836f..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combinations_bulk.html.twig +++ /dev/null @@ -1,106 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
- {% if configuration('PS_STOCK_MANAGEMENT') %} -
- - {{ form_errors(form.quantity) }} - {{ form_widget(form.quantity) }} -
- {% endif %} - -
- - {{ form_errors(form.cost_price) }} - {{ form_widget(form.cost_price) }} -
- -
- - {{ form_errors(form.impact_on_weight) }} - {{ form_widget(form.impact_on_weight) }} -
- -
- - {{ form_errors(form.impact_on_price_te) }} - {{ form_widget(form.impact_on_price_te) }} -
- -
- - {{ form_errors(form.impact_on_price_ti) }} - {{ form_widget(form.impact_on_price_ti) }} -
- -
- - {{ form_errors(form.date_availability) }} - {{ form_widget(form.date_availability) }} -
- -
- - {{ form_errors(form.reference) }} - {{ form_widget(form.reference) }} -
- -
- - {{ form_errors(form.minimal_quantity) }} - {{ form_widget(form.minimal_quantity) }} -
- -
- - {{ form_errors(form.low_stock_threshold) }} - {{ form_widget(form.low_stock_threshold) }} -
- -
-
- {{ form_errors(form.low_stock_alert) }} - {{ form_widget(form.low_stock_alert) }} - - -
-
-
-
- - -
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_custom_fields.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_custom_fields.html.twig deleted file mode 100644 index a0a0ad798d289..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_custom_fields.html.twig +++ /dev/null @@ -1,57 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
- {{ form_widget(form.id_customization_field) }} -
-
- - {{ form_errors(form.label) }} - {{ form_widget(form.label) }} -
-
-
-
- - {{ form_errors(form.type) }} - {{ form_widget(form.type) }} -
-
-
-
- - delete -
-
-
-
-
- -
-
- {{ form_widget(form.require, {'switch': 'tiny'}) }} -
-
-
-
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_edit_specific_price_modal.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_edit_specific_price_modal.html.twig deleted file mode 100644 index 7e11d43173914..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_edit_specific_price_modal.html.twig +++ /dev/null @@ -1,44 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} - diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_feature.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_feature.html.twig deleted file mode 100644 index a492b4d2b866a..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_feature.html.twig +++ /dev/null @@ -1,48 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
-
-
- - {{ form_widget(form.feature) }} -
-
-
-
- - {{ form_widget(form.value) }} -
-
-
-
- - {{ form_widget(form.custom_value) }} -
-
-
- delete -
-
-
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_manufacturer.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_manufacturer.html.twig deleted file mode 100644 index 7f9fdea189fe8..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_manufacturer.html.twig +++ /dev/null @@ -1,47 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
-

{{ "Brand"|trans({}, 'Admin.Catalog.Feature') }}

-
-
-
- {{ form_widget(form) }} -
- {{ form_errors(form) }} -
-
-
-
- delete -
-
-
-
-
- -
-
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_related_products.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_related_products.html.twig deleted file mode 100644 index bb8cdde3cd7b5..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_related_products.html.twig +++ /dev/null @@ -1,44 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} - - - - - diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_seo.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_seo.html.twig deleted file mode 100644 index 6abb4009175ff..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_seo.html.twig +++ /dev/null @@ -1,142 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} - -

{{ 'Search Engine Optimization'|trans({}, 'Admin.Catalog.Feature') }}

-

{{ 'Improve your ranking and how your product page will appear in search engines results.'|trans({}, 'Admin.Catalog.Feature') }}

- -{% block product_catalog_tool_serp %} -

{{ "Here is a preview of your search engine result, play with it!"|trans({}, 'Admin.Catalog.Feature') }}

- {# Div targetted by the SERP component in VueJs. It displays a Google search result preview. #} -
-{% endblock %} - -
-
-
- {{ form_label(seoForm.meta_title) }} - {{ form_errors(seoForm.meta_title) }} - {{ form_widget(seoForm.meta_title) }} -
-
-
- -
-
-
- {{ form_label(seoForm.meta_description) }} - {{ form_errors(seoForm.meta_description) }} - {{ form_widget(seoForm.meta_description) }} -
-
-
-
- - {{ form_errors(seoForm.link_rewrite) }} -
-
- {{ form_widget(seoForm.link_rewrite) }} -
-
- -
-
-
- -
-
- -
-
- -

- {{ 'Redirection page'|trans({}, 'Admin.Catalog.Feature') }} - - -

- -
- -
-
- - {{ form_errors(seoForm.redirect_type) }} - {{ form_widget(seoForm.redirect_type) }} -
-
- -
-
- - {{ form_errors(seoForm.id_type_redirected) }} - {{ form_widget(seoForm.id_type_redirected) }} -
- -
-
-
-
- -
-
- -{{ form_rest(seoForm) }} - -{{ renderhook('displayAdminProductsSeoStepBottom', { 'id_product': productId }) }} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_shipping.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_shipping.html.twig deleted file mode 100644 index d0b914fc10da0..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_shipping.html.twig +++ /dev/null @@ -1,144 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} - -
-

{{ 'Package dimension'|trans({}, 'Admin.Catalog.Feature') }}

-

{{ 'Adjust your shipping costs by filling in the product dimensions.'|trans({}, 'Admin.Catalog.Feature') }}

-
- -
-
- - {{ form_errors(form.width) }} -
- {{ form_widget(form.width) }} -
-
-
-
-
- - {{ form_errors(form.height) }} -
- {{ form_widget(form.height) }} -
-
-
-
-
- - {{ form_errors(form.depth) }} -
- {{ form_widget(form.depth) }} -
-
-
-
-
- - {{ form_errors(form.weight) }} -
- {{ form_widget(form.weight) }} -
-
-
- -
-
-

- {{ form.additional_delivery_times.vars.label }} - - -

-
-
- {% for child in form.additional_delivery_times %} - {% if child.vars.value == 1 %} - - {% else %} - {{ form_widget(child) }} - {% endif %} - {% endfor %} -
-
-
-
- -
-
- - {{ form_errors(form.delivery_in_stock) }} - {{ form_widget(form.delivery_in_stock) }} -

{{ 'Leave empty to disable.'|trans({}, 'Admin.Catalog.Feature') }}

-
-
-
-
- - {{ form_errors(form.delivery_out_stock) }} - {{ form_widget(form.delivery_out_stock) }} -

{{ 'Leave empty to disable.'|trans({}, 'Admin.Catalog.Feature') }}

-
-
- -
-
-

- {{ form.additional_shipping_cost.vars.label }} - - -

- -
-
- {{ form_widget(form.additional_shipping_cost) }} -
-
-
-
- -
-
-

{{ form.selectedCarriers.vars.label }}

- {{ form_widget(form.selectedCarriers) }} -
-
- -
- -
- -{{ renderhook('displayAdminProductsShippingStepBottom', { 'id_product': id_product }) }} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_specific_price.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_specific_price.html.twig deleted file mode 100644 index 21a472473ba75..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_specific_price.html.twig +++ /dev/null @@ -1,169 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% if is_modal is not defined %} - {% set is_modal = false %} -{% endif %} - -{% if is_modal == false %} - {% set column_default_md_3 = 'col-md-3' %} - {% set column_default_md_2 = 'col-md-2' %} - {% set column_default_xl_3 = 'col-xl-3' %} -{% else %} - {% set column_default_md_3 = 'col-md-9' %} - {% set column_default_md_2 = 'col-md-4' %} - {% set column_default_xl_3 = 'col-xl-4' %} -{% endif %} - -
-

{{ 'Specific price conditions'|trans({}, 'Admin.Catalog.Feature') }}

- {{ form_errors(form) }} - - {% if form.sp_id_shop.vars.choices is defined %} -
-
-
- - {{ form_errors(form.sp_id_shop) }} - {{ form_widget(form.sp_id_shop) }} -
-
-
- {% else %} - {{ form_widget(form.sp_id_shop) }} - {% endif %} - -
-
-
- - {{ form_errors(form.sp_id_currency) }} - {{ form_widget(form.sp_id_currency) }} -
-
-
-
- - {{ form_errors(form.sp_id_country) }} - {{ form_widget(form.sp_id_country) }} -
-
-
-
- - {{ form_errors(form.sp_id_group) }} - {{ form_widget(form.sp_id_group) }} -
-
-
-
- - {{ form_errors(form.sp_id_customer) }} - {{ form_widget(form.sp_id_customer) }} -
-
-
-
-
-
- - {{ form_errors(form.sp_id_product_attribute) }} - {{ form_widget(form.sp_id_product_attribute) }} -
-
-
-
-
- - {{ form_errors(form.sp_from) }} - {{ form_widget(form.sp_from) }} -
-
-
-
- - {{ form_errors(form.sp_to) }} - {{ form_widget(form.sp_to) }} -
-
- {% if is_modal == true %} -
-
- {% endif %} -
-
- - {{ form_errors(form.sp_from_quantity) }} - {{ form_widget(form.sp_from_quantity) }} -
-
-
-
- -

{{ 'Impact on price'|trans({}, 'Admin.Catalog.Feature') }}

-
-
-
- - {{ form_errors(form.sp_price) }} - {{ form_widget(form.sp_price) }} -
-
-
-
- - {{ form_errors(form.leave_bprice) }} - {{ form_widget(form.leave_bprice) }} -
-
-
-
-
-
- - {{ form_errors(form.sp_reduction) }} - {{ form_widget(form.sp_reduction) }} -
-
-
-
- - {{ form_errors(form.sp_reduction_type) }} - {{ form_widget(form.sp_reduction_type) }} -
-
-
-
- - {{ form_errors(form.sp_reduction_tax) }} - {{ form_widget(form.sp_reduction_tax) }} -
-
-
-
- {{ form_widget(form.cancel) }} - {{ form_widget(form.save) }} -
-
-
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_supplier_choice.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_supplier_choice.html.twig deleted file mode 100644 index c81a1f7e28a1b..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_supplier_choice.html.twig +++ /dev/null @@ -1,62 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% if form.suppliers|length > 0 %} -
-

{{ form.suppliers.vars.label }}

-
- -
- -
-
-
- {{ form_errors(form.suppliers) }} - - - - - - - - - {% for key, supplier in form.suppliers %} - - - - - {% endfor %} - -
{{ 'Choose the suppliers associated with this product'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Default supplier'|trans({}, 'Admin.Catalog.Feature') }}
{{ form_widget(supplier) }}{{ form_widget(form.default_supplier[key]) }}
-
-
-
- -
-{% endif %} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_supplier_combination.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_supplier_combination.html.twig deleted file mode 100644 index 14410aa637a59..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_supplier_combination.html.twig +++ /dev/null @@ -1,70 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% if suppliers|length > 0 %} -

{{ 'Supplier reference(s)'|trans({}, 'Admin.Catalog.Feature') }}

- -{% endif %} - -{% for supplierId in suppliers %} - {% set collectionName = 'supplier_combination_' ~ supplierId %} -
-
- {{ form[collectionName].vars.label }} -
-
-
- {{ form_errors(form[collectionName]) }} - - - - - - - - - - - {% for supplier_combination in form[collectionName] %} - - - - - - - {% endfor %} - -
{{ 'Product name'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Supplier reference'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Cost price (tax excl.)'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Currency'|trans({}, 'Admin.Global') }}
{{ supplier_combination.vars.value.label }}{{ form_widget(supplier_combination.supplier_reference) }}{{ form_widget(supplier_combination.product_price) }} - {{ form_widget(supplier_combination.product_price_currency) }} - {{ form_widget(supplier_combination.id_product_attribute) }} - {{ form_widget(supplier_combination.supplier_id) }} -
-
-
-
-{% endfor %} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/combinations.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/combinations.html.twig deleted file mode 100644 index f0b7fdb7387a5..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/combinations.html.twig +++ /dev/null @@ -1,184 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
-
- -
-

{{ 'Quantities'|trans({}, 'Admin.Catalog.Feature') }}

-
-
- {% if configuration('PS_STOCK_MANAGEMENT') %} -
- - {{ form_errors(formStockQuantity) }} - {{ form_widget(formStockQuantity) }} -
- {% endif %} -
- - {{ form_errors(formStockMinimalQuantity) }} - {{ form_widget(formStockMinimalQuantity) }} -
-
-
- -

{{ 'Stock'|trans({}, 'Admin.Catalog.Feature') }}

-
-
-
- - {{ form_errors(formLocation) }} - {{ form_widget(formLocation) }} -
-
-
-
- - {{ form_errors(formLowStockThreshold) }} - {{ form_widget(formLowStockThreshold) }} -
-
- -
- {{ form_errors(formLowStockAlert) }} - {{ form_widget(formLowStockAlert) }} - Team[/1]"|trans({'[1]':'','[/1]':''}, 'Admin.Catalog.Help')|raw }}"> - -
-
-
-
-
- -
- -

{{ formVirtualProduct.vars.label }}

-
- {{ form_widget(formVirtualProduct.is_virtual_file) }} -
- -
-
-
-
- {{ form_errors(formVirtualProduct) }} -
-
- -
- {{ form_widget(formVirtualProduct.file) }} -
- -
-
-
-
- - {{ form_errors(formVirtualProduct.name) }} - {{ form_widget(formVirtualProduct.name) }} -
-
-
-
- - {{ form_errors(formVirtualProduct.nb_downloadable) }} - {{ form_widget(formVirtualProduct.nb_downloadable) }} -
-
-
-
- - {{ form_errors(formVirtualProduct.expiration_date) }} - {{ form_widget(formVirtualProduct.expiration_date) }} -
-
-
-
- - {{ form_errors(formVirtualProduct.nb_days) }} - {{ form_widget(formVirtualProduct.nb_days) }} -
-
-
- {{ form_widget(formVirtualProduct.save) }} -
-
-
-
-
- -
- - {% if configuration('PS_STOCK_MANAGEMENT') %} -
-

{{ formPackStockType.vars.label }}

-
-
-
- {{ form_errors(formPackStockType) }} - {{ form_widget(formPackStockType) }} -
-
-
-
- {% endif %} - {{ include('@Product/ProductPage/Forms/form_combinations.html.twig', {'form': formStep3, 'form_combination_bulk': formCombinations}) }} - - {{ renderhook('displayAdminProductsQuantitiesStepBottom', { 'id_product': productId }) }} - -
-
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/essentials.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/essentials.html.twig deleted file mode 100644 index 8c5abff6c0988..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/essentials.html.twig +++ /dev/null @@ -1,276 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
-
-
- - {# LEFT #} -
- -
- {{ form_errors(formPackItems) }} - {{ form_widget(formPackItems) }} -
- -
-
-
-
- add_a_photo
- {{js_translatable['Drop images here']}}
- {{js_translatable['or select files']}}
- - {{js_translatable['files recommandations']}}
- {{js_translatable['files recommandations2']}} -
-
- {% if images is defined %} - {% if editable %} -
-
- + -
-
- {% endif %} - {% for image in images %} -
-
-
-
- -
-
- -
-
-
- -
-
- -
-
-
- {% if image.cover %} -
{{ 'Cover'|trans({}, 'Admin.Catalog.Feature') }}
- {% endif %} -
- {% endfor %} - {% endif %} -
-
-
-
-
- {{ 'View all images'|trans({}, 'Admin.Catalog.Feature') }} - {{ 'View less'|trans({}, 'Admin.Catalog.Feature') }} -
- -
- -
-

{{ 'Summary'|trans({}, 'Admin.Catalog.Feature') }}

-
- {{ form_widget(formShortDescription) }} -
- -

{{ 'Description'|trans({}, 'Admin.Global') }}

-
- {{ form_widget(formDescription) }} -
-
- - {{ renderhook('displayAdminProductsMainStepLeftColumnMiddle', { 'id_product': productId }) }} - -
-
-

{{ 'Features'|trans({}, 'Admin.Catalog.Feature') }}

- {{ form_errors(formFeatures) }} -
- {% for feature in formFeatures %} - {{ include('@Product/ProductPage/Forms/form_feature.html.twig', { 'form': feature }) }} - {% endfor %} -
-
-
-
- -
-
-
- -
- {{ include('@Product/ProductPage/Forms/form_manufacturer.html.twig', { 'form': formManufacturer }) }} -
- - - - {{ renderhook('displayAdminProductsMainStepLeftColumnBottom', { 'id_product': productId }) }} - -
- - {# RIGHT #} -
- - {% if is_combination_active %} -
-

- {{ "Combinations"|trans({}, 'Admin.Catalog.Feature') }} - - -

-
- -
-
- -
- - {# First tag [1][/1] is for a HTML link. Second tag [2] is an icon (no closing tag needed). #} - {{ "Advanced settings in [1][2]Combinations[/1]"|trans({}, 'Admin.Catalog.Help')|replace({'[1]': '', '[/1]': '', '[2]': 'open_in_new'})|raw }} - -
-
-
- {% endif %} - -
-

- {{ "Reference"|trans({}, 'Admin.Catalog.Feature') }} - - -

- {{ form_errors(formReference) }} -
-
- {{ form_widget(formReference) }} -
-
-
- - {% if configuration('PS_STOCK_MANAGEMENT') %} -
-

- {{ "Quantity"|trans({}, 'Admin.Catalog.Feature') }} - - -

- {{ form_errors(formQuantityShortcut) }} -
-
- {{ form_widget(formQuantityShortcut) }} -
-
- - {# First tag [1][/1] is for a HTML link. Second tag [2] is an icon (no closing tag needed). #} - {{ "Advanced settings in [1][2]Quantities[/1]"|trans({}, 'Admin.Catalog.Help')|replace({'[1]': '', '[/1]': '', '[2]': 'open_in_new'})|raw }} - -
- {% endif %} - -
-

- {{ "Price"|trans({}, 'Admin.Global') }} - - -

-
-
- - {{ form_widget(formPriceShortcut) }} - {{ form_errors(formPriceShortcut) }} -
-
- - {{ form_widget(formPriceShortcutTTC) }} - {{ form_errors(formPriceShortcutTTC) }} -
-
- - {{ render( - controller('PrestaShopBundle\\Controller\\Admin\\CommonController:renderFieldAction', { - 'formName': 'step2', - 'formType': 'PrestaShopBundle\\Form\\Admin\\Product\\ProductPrice', - 'fieldName': 'id_tax_rules_group', - 'fieldData' : form.step2.id_tax_rules_group.vars.value - } - ) - ) - }} -
-
- - {# First tag [1][/1] is for a HTML link. Second tag [2] is an icon (no closing tag needed). #} - {{ "Advanced settings in [1][2]Pricing[/1]"|trans({}, 'Admin.Catalog.Help')|replace({'[1]': '', '[/1]': '', '[2]': 'open_in_new'})|raw }} - -
-
-
-
- -
-
-
- × -
-
- -
- {{ include('@Product/ProductPage/Forms/form_categories.html.twig', { 'form': formCategories, 'productId': productId }) }} -
- - {{ renderhook('displayAdminProductsMainStepRightColumnBottom', { 'id_product': productId }) }} - -
-
-
-
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/options.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/options.html.twig deleted file mode 100644 index b3ece12102ff7..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/options.html.twig +++ /dev/null @@ -1,214 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
-
- - {{ renderhook('displayAdminProductsOptionsStepTop', { 'id_product': productId }) }} - -

{{ 'Visibility'|trans({}, 'Admin.Catalog.Feature') }}

-

{{ 'Where do you want your product to appear?'|trans({}, 'Admin.Catalog.Feature') }}

- -
-
- {{ form_errors(optionsForm.visibility) }} - {{ form_widget(optionsForm.visibility) }} -
-
- -
-
- {{ form_errors(optionsForm.display_options) }} -
-
- {{ form_widget(optionsForm.display_options.available_for_order) }} -
-
- {{ form_widget(optionsForm.display_options.show_price) }} -
-
- {{ form_widget(optionsForm.display_options.online_only) }} -
-
-
-
-
-
- - {{ form_errors(optionsForm.tags) }} - {{ form_widget(optionsForm.tags) }} - -
-
- -

{{ 'Condition & References'|trans({}, 'Admin.Catalog.Feature')|raw }}

- -
-
- - {{ form_errors(optionsForm.condition) }} - {{ form_widget(optionsForm.condition) }} -
-
- - {{ form_widget(optionsForm.show_condition) }} -
-
-
-
- - {{ form_errors(optionsForm.isbn) }} - {{ form_widget(optionsForm.isbn) }} -
-
- - {{ form_errors(optionsForm.ean13) }} - {{ form_widget(optionsForm.ean13) }} -
-
-
-
- - {{ form_errors(optionsForm.upc) }} - {{ form_widget(optionsForm.upc) }} -
-
- - {{ form_errors(optionsForm.mpn) }} - {{ form_widget(optionsForm.mpn) }} -
-
- -
-

{{ optionsForm.custom_fields.vars.label }}

-

{{ 'Customers can personalize the product by entering some text or by providing custom image files.'|trans({}, 'Admin.Catalog.Feature') }}

- {{ form_errors(optionsForm.custom_fields) }} -
    - {% for field in optionsForm.custom_fields %} -
  • - {{ include('@Product/ProductPage/Forms/form_custom_fields.html.twig', { 'form': field }) }} -
  • - {% endfor %} -
- - add_circle - {{ 'Add a customization field'|trans({}, 'Admin.Catalog.Feature') }} - -
- -
-
-

{{ 'Attached files'|trans({}, 'Admin.Catalog.Feature') }}

-

{{ 'Select the files (instructions, documentation, recipes, etc.) your customers can directly download on this product page.'|trans({}, 'Admin.Catalog.Feature') }} -
- {{ 'Need to browse all files? Go to [1]Catalog > Files[/1]'|trans({'[1]':'','[/1]':''}, 'Admin.Catalog.Feature')|raw }} -

- {{ form_widget(optionsForm.attachments) }} -
-
-
-
- -
- {{ form_errors(optionsForm.attachment_product) }} -
-
{{ form_widget(optionsForm.attachment_product.file) }}
-
{{ form_widget(optionsForm.attachment_product.name) }}
-
{{ form_widget(optionsForm.attachment_product.description) }}
-
- {{ form_widget(optionsForm.attachment_product.add) }} - {{ form_widget(optionsForm.attachment_product.cancel) }} -
-
-
-
-
- -
-
- {{ include('@Product/ProductPage/Forms/form_supplier_choice.html.twig', { 'form': optionsForm }) }} -
-
-
- {{ include('@Product/ProductPage/Forms/form_supplier_combination.html.twig', { 'suppliers': optionsForm.suppliers.vars.value, 'form': optionsForm }) }} -
- - {{ renderhook('displayAdminProductsOptionsStepBottom', { 'id_product': productId }) }} - -
-
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/pricing.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/pricing.html.twig deleted file mode 100644 index c27fc77e1fa5e..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/pricing.html.twig +++ /dev/null @@ -1,214 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
-
- -

{{ 'Retail price'|trans({}, 'Admin.Catalog.Feature') }} - - -

- -
-
- -
- - {{ form_errors(pricingForm.price) }} - {{ form_widget(pricingForm.price) }} -
-
- - {{ form_errors(pricingForm.price_ttc) }} - {{ form_widget(pricingForm.price_ttc) }} -
- -
- -
-
- {{ form_errors(pricingForm.unit_price) }} - {{ form_widget(pricingForm.unit_price) }} -
-
- {{ form_errors(pricingForm.unity) }} - {{ form_widget(pricingForm.unity) }} -
-
-
-
- - {{ form_errors(pricingForm.ecotax) }} - {{ form_widget(pricingForm.ecotax) }} -
-
-
- -
-
- - {{ form_errors(pricingForm.id_tax_rules_group) }} - {{ form_widget(pricingForm.id_tax_rules_group) }} -
- -
- {{ form_widget(pricingForm.on_sale) }} -
-
-
-
- -
-
-
-
- -
-
-

- {{ 'Cost price'|trans({}, 'Admin.Catalog.Feature') }} - - -

-
-
- - {{ form_errors(pricingForm.wholesale_price) }} - {{ form_widget(pricingForm.wholesale_price) }} -
-
- -
-
-

- {{ 'Specific prices'|trans({}, 'Admin.Catalog.Feature') }} - - -

-
-
-
- -
- {{ include('@Product/ProductPage/Forms/form_specific_price.html.twig', {'form': pricingForm.specific_price, 'is_multishop_context': is_multishop_context}) }} -
- - - - - - - - - - - - - - - - - - - -
{{ 'ID'|trans({}, 'Admin.Global') }}{{ 'Rule'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Combination'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Currency'|trans({}, 'Admin.Global') }}{{ 'Country'|trans({}, 'Admin.Global') }}{{ 'Group'|trans({}, 'Admin.Global') }}{{ 'Customer'|trans({}, 'Admin.Global') }}{{ 'Fixed price'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Impact'|trans({}, 'Admin.Catalog.Feature') }}{{ 'Period'|trans({}, 'Admin.Global') }}{{ 'From'|trans({}, 'Admin.Catalog.Feature') }}
-
-
-
- -
- {{ include('@Product/ProductPage/Forms/form_edit_specific_price_modal.html.twig') }} -
- -
-
-

- {{ 'Priority management'|trans({}, 'Admin.Catalog.Feature') }} - - -

-
-
-
- - {{ form_errors(pricingForm.specificPricePriority_0) }} - {{ form_widget(pricingForm.specificPricePriority_0) }} -
-
-
-
- - {{ form_errors(pricingForm.specificPricePriority_1) }} - {{ form_widget(pricingForm.specificPricePriority_1) }} -
-
-
-
- - {{ form_errors(pricingForm.specificPricePriority_2) }} - {{ form_widget(pricingForm.specificPricePriority_2) }} -
-
-
-
- - {{ form_errors(pricingForm.specificPricePriority_3) }} - {{ form_widget(pricingForm.specificPricePriority_3) }} -
-
-
- {{ form_widget(pricingForm.specificPricePriorityToAll) }} -
-
- - {{ renderhook('displayAdminProductsPriceStepBottom', { 'id_product': productId }) }} - -
-
diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/disabled_form_alert.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/disabled_form_alert.html.twig deleted file mode 100644 index 8254ec261a54b..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/disabled_form_alert.html.twig +++ /dev/null @@ -1,38 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% extends '@PrestaShop/Admin/layout.html.twig' %} - -{% block content %} -
- {{ multistoreHeader() }} -
-
- -
-{% endblock %} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/product.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/product.html.twig deleted file mode 100644 index f3da756947d7f..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/product.html.twig +++ /dev/null @@ -1,329 +0,0 @@ - {#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% extends '@PrestaShop/Admin/layout.html.twig' %} - -{% block stylesheets %} - {{ parent() }} - -{% endblock %} - -{% block content %} - {% set hooks = renderhooksarray('displayAdminProductsExtra', { 'id_product': id_product }) %} -
- {{ multistoreHeader() }} -
-
- - {% if not editable %}
{% endif %} - {# PRODUCT HEADER #} - {% block product_header %} - {{ include('@Product/ProductPage/Blocks/header.html.twig', { - 'formName': form.step1.name, - 'formType': form.step1.type_product, - 'is_multishop_context': is_multishop_context, - 'languages': languages, - 'help_link': help_link, - 'stats_link': stats_link, - 'isCreationMode': isCreationMode, - }) - }} - {% endblock %} - -
-
- {{ form_errors(form) }} -
-
- -
- {# FORM TABS CONTAINER #} - {% block product_tabs_container %} - {{ include('@Product/ProductPage/Blocks/tabs.html.twig', { 'hooks': hooks }) }} - {% endblock %} -
- - {# PANEL ESSENTIALS #} - {% block product_panel_essentials %} - {% set formQuantityShortcut = form.step1.qty_0_shortcut is defined ? form.step1.qty_0_shortcut : null %} - {{ include('@Product/ProductPage/Panels/essentials.html.twig', { - 'formPackItems': form.step1.inputPackItems, - 'productId': id_product, - 'images': form.step1.vars.value.images, - 'formShortDescription': form.step1.description_short, - 'formDescription': form.step1.description, - 'formFeatures': form.step1.features, - 'formManufacturer': form.step1.id_manufacturer, - 'formRelatedProducts': form.step1.related_products, - 'is_combination_active': is_combination_active, - 'has_combinations': has_combinations, - 'formReference': form.step6.reference, - 'formQuantityShortcut': formQuantityShortcut, - 'formPriceShortcut': form.step1.price_shortcut, - 'formPriceShortcutTTC': form.step1.price_ttc_shortcut, - 'formCategories': form.step1, - }) - }} - {% endblock %} - - {# PANEL COMBINATIONS #} - {% block product_panel_combinations %} - {% set formStockQuantity = form.step3.qty_0 is defined ? form.step3.qty_0 : null %} - {{ include('@Product/ProductPage/Panels/combinations.html.twig', { - 'productId': id_product, - 'formStockQuantity': formStockQuantity, - 'formStockMinimalQuantity': form.step3.minimal_quantity, - 'formLowStockThreshold': form.step3.low_stock_threshold, - 'formLocation': form.step3.location, - 'formLowStockAlert': form.step3.low_stock_alert, - 'formVirtualProduct': form.step3.virtual_product, - 'formType': form.step1.type_product, - 'formPackStockType': form.step3.pack_stock_type, - 'formStep3': form.step3, - 'formCombinations': formCombinations, - 'has_combinations': has_combinations, - 'max_upload_size': max_upload_size - }) - }} - {% endblock %} - - {# PANEL SHIPPING #} - {% block product_panel_shipping %} -
-
-
- {{ include('@Product/ProductPage/Forms/form_shipping.html.twig', { - 'form' : form.step4, - 'isNotVirtual': form.step1.type_product.vars.value != "2" - }) }} -
-
-
- {% endblock %} - - {# PANEL PRICING #} - {% block product_panel_pricing %} - {{ include('@Product/ProductPage/Panels/pricing.html.twig', { - 'pricingForm': form.step2, - 'is_multishop_context': is_multishop_context, - 'productId': id_product - }) }} - {% endblock %} - - {# PANEL SEO #} - {% block product_panel_seo %} - {{ include('@Product/ProductPage/Panels/seo.html.twig', { - 'seoForm': form.step5, - 'productId': id_product - }) }} - {% endblock %} - - {# PANEL OPTIONS #} - {% block product_panel_options %} - {{ include('@Product/ProductPage/Panels/options.html.twig', { - 'optionsForm': form.step6, - 'productId': id_product - }) }} - {% endblock %} - - {# PANEL HOOKED MODULES #} - {% block product_panel_modules %} - {% if hooksarraycontent(hooks) is not empty %} -
-
- - -
-
-

{{ 'Choose a module to configure'|trans({}, 'Admin.Catalog.Feature') }}

-

{{ 'These modules are relative to the product page of your store.'|trans({}, 'Admin.Catalog.Feature') }}
- {{ 'To manage all your modules go to the [1]Installed module page[/1]'|trans({}, 'Admin.Catalog.Feature')|replace({'[1]': '', '[/1]': ''})|raw }}

-
-
- {% for module in hooks %} -
-
-
- {{ module.attributes.displayName }} -

- {{ module.attributes.displayName }} -

-
- {{ module.attributes.version }} by {{ module.attributes.author }} -
-
-
- {{ module.attributes.description }} -
-
-
- -
-
-
-
- {% endfor %} -
-
- - {% for module in hooks %} - - {% endfor %} - -
-
- {% endif %} - {% endblock %} -
- - {{ form_widget(form.id_product) }} - {{ form_widget(form._token) }} - -
- {# FOOTER #} - {{ include('@Product/ProductPage/Blocks/footer.html.twig', { - 'preview_link': preview_link, - 'preview_link_deactivate': preview_link_deactivate, - 'is_shop_context': is_shop_context, - 'editable': editable, - 'is_active': form.step1.vars.value.active, - 'productId': id_product - }) }} - {% if not editable %}
{% endif %} -
- - - {% embed '@PrestaShop/Admin/Helpers/bootstrap_popup.html.twig' with { - 'id': 'confirmation_modal', - 'title': "Warning"|trans({}, 'Admin.Notifications.Warning'), - 'closable': false, - 'actions': [ - { - 'type': 'button', - 'label': "No"|trans({}, 'Admin.Global'), - 'class': 'btn btn-outline-secondary btn-lg cancel' - }, - { - 'type': 'button', - 'label': "Yes"|trans({}, 'Admin.Global'), - 'class': 'btn btn-primary btn-lg continue' - } - ], - } %} - {% block content %} - - {% endblock %} - {% endembed %} - -{% endblock %} - -{% block javascripts %} - {{ parent() }} - - - - - - - - - - - - - - - - -{% endblock %} - -{% set js_translatable = { -"Are you sure to disable variations ? they will all be deleted": "This will delete all the combinations. Do you wish to proceed?"|trans({}, 'Admin.Catalog.Notification'), -}|merge(js_translatable) %} - -{% set js_translatable = { -"Form update success": "Settings updated."|trans({}, 'Admin.Notifications.Success'), -"Form update errors": "Unable to update settings."|trans({}, 'Admin.Notifications.Error'), -"Delete": "Delete"|trans({}, 'Admin.Actions'), -"ToLargeFile": "The file is too large. Maximum size allowed is: [1] MB. The file you are trying to upload is [2] MB."|trans({}, 'Admin.Notifications.Error')|replace({ '[1]': '{{maxFilesize}}', '[2]': '{{filesize}}' }), -"Drop images here": "Drop images here"|trans({}, 'Admin.Catalog.Feature'), -"or select files": "or select files"|trans({}, 'Admin.Catalog.Feature'), -"files recommandations": "Recommended size 800 x 800px for default theme."|trans({}, 'Admin.Catalog.Feature'), -"files recommandations2": "JPG, GIF, PNG or WebP format."|trans({}, 'Admin.Catalog.Feature'), -"Cover": "Cover"|trans({}, 'Admin.Catalog.Feature'), -"Are you sure you want to delete this item?": "Are you sure you want to delete this item?"|trans({}, 'Admin.Notifications.Warning'), -"Quantities": "Quantities"|trans({}, 'Admin.Catalog.Feature'), -"Combinations": "Combinations"|trans({}, 'Admin.Catalog.Feature'), -"Virtual product": "Virtual product"|trans({}, 'Admin.Catalog.Feature'), -"tax incl.": "tax incl."|trans({}, 'Admin.Catalog.Feature'), -"tax excl.": "tax excl."|trans({}, 'Admin.Catalog.Feature'), -"You can't create pack product with variations. Are you sure to disable variations ? they will all be deleted.": "A pack of products can't have combinations."|trans({}, "Admin.Catalog.Notification") ~ ' ' ~ js_translatable['Are you sure to disable variations ? they will all be deleted'], -"You can't create virtual product with variations. Are you sure to disable variations ? they will all be deleted.": "A virtual product can't have combinations."|trans({}, "Admin.Catalog.Notification") ~ ' ' ~ js_translatable['Are you sure to disable variations ? they will all be deleted'], -"Are you sure you want to delete the selected item(s)?": "Are you sure you want to delete the selected item(s)?"|trans({}, 'Admin.Global'), -}|merge(js_translatable) %} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/Themes/categories_theme.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Product/Themes/categories_theme.html.twig deleted file mode 100644 index dff07596c1f07..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/Themes/categories_theme.html.twig +++ /dev/null @@ -1,62 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% block choice_tree_widget -%} -
-
    - - {%- for child in choices %} - {{ block('choice_tree_item_widget') }} - {% endfor -%} -
-
- -{%- endblock choice_tree_widget %} - -{% block choice_tree_item_widget -%} -
  • - {% set checked = (form.vars.submitted_values is defined and submitted_values[child.id_category] is defined) %} - -
    - -
    - - {% if child.children is defined %} -
      - {% for item in child.children %} - {% set child = item %} - {{ block('choice_tree_item_widget') }} - {% endfor -%} -
    - {% endif %} -
  • -{%- endblock choice_tree_item_widget %} diff --git a/src/PrestaShopBundle/Resources/views/Admin/ProductImage/form.html.twig b/src/PrestaShopBundle/Resources/views/Admin/ProductImage/form.html.twig deleted file mode 100644 index 8418bab32ce4e..0000000000000 --- a/src/PrestaShopBundle/Resources/views/Admin/ProductImage/form.html.twig +++ /dev/null @@ -1,45 +0,0 @@ -{#** - * Copyright since 2007 PrestaShop SA and Contributors - * PrestaShop is an International Registered Trademark & Property of PrestaShop SA - * - * NOTICE OF LICENSE - * - * This source file is subject to the Open Software License (OSL 3.0) - * that is bundled with this package in the file LICENSE.md. - * It is also available through the world-wide-web at this URL: - * https://opensource.org/licenses/OSL-3.0 - * If you did not receive a copy of the license and are unable to - * obtain it through the world-wide-web, please send an email - * to license@prestashop.com so we can send you a copy immediately. - * - * DISCLAIMER - * - * Do not edit or add to this file if you wish to upgrade PrestaShop to newer - * versions in the future. If you wish to customize PrestaShop for your - * needs please refer to https://devdocs.prestashop.com/ for more information. - * - * @author PrestaShop SA and Contributors - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} - - -
    -
    - {{ form_widget(form.cover) }} -
    - -
    - - -{{ form_widget(form.legend) }} -{{ form_errors(form.legend) }} - -
    - - -
    diff --git a/src/PrestaShopBundle/Resources/views/Admin/Sell/Catalog/Manufacturer/Blocks/View/products.html.twig b/src/PrestaShopBundle/Resources/views/Admin/Sell/Catalog/Manufacturer/Blocks/View/products.html.twig index 21213d4db0efe..55eeb1a799257 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Sell/Catalog/Manufacturer/Blocks/View/products.html.twig +++ b/src/PrestaShopBundle/Resources/views/Admin/Sell/Catalog/Manufacturer/Blocks/View/products.html.twig @@ -32,7 +32,7 @@ {% for product in viewableManufacturer.manufacturerProducts %}
    - {{ product.name }} + {{ product.name }}
    @@ -41,13 +41,13 @@ -
    -
    - - -
    -{% endblock %} - -{% block typeahead_customer_collection_widget %} - {{ form_errors(form) }} - -
      - {% if collection is defined and collection|length > 0 %} - {% for item in collection %} -
    • -
      - {{ template_collection|format(item.name)|raw }} -
      - -
    • - {% endfor %} - {% endif %} -
    - -{% endblock %} diff --git a/src/PrestaShopBundle/Service/DataProvider/Admin/ProductInterface.php b/src/PrestaShopBundle/Service/DataProvider/Admin/ProductInterface.php deleted file mode 100644 index 337f3b3945ebf..0000000000000 --- a/src/PrestaShopBundle/Service/DataProvider/Admin/ProductInterface.php +++ /dev/null @@ -1,126 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Service\DataProvider\Admin; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Data provider for new Architecture, about Product object model. - * - * This class will provide data from DB / ORM about Products for the Admin interface. - */ -interface ProductInterface -{ - /** - * Will retrieve set of parameters from persistence, for product filters. - * - * @return string[] The old filter parameters values - */ - public function getPersistedFilterParameters(); - - /** - * Is there a specific category selected to filter product? - * - * @return bool true if a category is selected - */ - public function isCategoryFiltered(); - - /** - * Is there any column filter value? - * - * A filter with empty string '' is considered as not filtering, but 0 or '0' is a filter value! - * - * @return bool True if at least one column is filtered (except categories) - */ - public function isColumnFiltered(); - - /** - * Will persist set of parameters for product filters. - * - * @param string[] $parameters - */ - public function persistFilterParameters(array $parameters); - - /** - * Combines new filter values with old ones (persisted), then persists the combination and returns it. - * - * @param string[]|null $paramsIn New filter params values to take into account. If not given, the method will simply return persisted values. - * @param bool $avoidPersistence true to avoid persisting these preferences (for an export for example) - * - * @return string[] The new filter params values - */ - public function combinePersistentCatalogProductFilter($paramsIn = [], $avoidPersistence = false); - - /** - * Returns a collection of products, using default language, currency and others, from Context. - * - * @param int|string $offset an offset, or the 'last' token - * @param int|string $limit a limit, or the 'last' token - * @param string $orderBy Field name to sort during SQL query - * @param string $sortOrder 'asc' or 'desc' - * @param string[] $post filter params values to take into acount (often comes from POST data) - * @param bool $avoidPersistence True to avoid persisting these preferences (for an export for example) - * @param bool $formatCldr False to avoid CLDR formatting (heavy memory usage) - * - * @return array[mixed[]] A list of products, as an array of arrays of raw data - */ - public function getCatalogProductList($offset, $limit, $orderBy, $sortOrder, $post = [], $avoidPersistence = false, $formatCldr = true); - - /** - * Retrieve global product count (for the current shop). - * - * No filtering/limit/offset is applied to give this count. - * - * @return int The product count on the current shop - */ - public function countAllProducts(); - - /** - * Because pagination is tied to memory available on the server side, the choices can vary from one to another platform, - * depending on the memory_limit allocated to PHP. - * This will return Elements per page that are recommended to fetch products on the Admin catalog page. - * - * @return array[int] A list of pagination limits to show in the select dropbox in paginator component - */ - public function getPaginationLimitChoices(); - - /** - * Returns the last SQL query that was compiled on this Provider. - * - * @return string The last SQL query that was compiled with $this->compileSqlQuery() - */ - public function getLastCompiledSql(); - - /** - * Returns default activation state for new product. - * - * Duplication process could be different since duplicated product is always deactivated after duplication. - * - * @return bool true if a newly created product should be activated by default - */ - public function isNewProductDefaultActivated(); -} diff --git a/src/PrestaShopBundle/Service/DataProvider/StockInterface.php b/src/PrestaShopBundle/Service/DataProvider/StockInterface.php deleted file mode 100644 index 4b5509d79f126..0000000000000 --- a/src/PrestaShopBundle/Service/DataProvider/StockInterface.php +++ /dev/null @@ -1,46 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Service\DataProvider; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Data provider for new Architecture, about Product stocks. - * - * This class will provide data from DB / ORM about Product stocks. - */ -interface StockInterface -{ - /** - * Returns True if Stocks are managed by a module (or by legacy ASM). - * - * @return bool True if Stocks are managed by a module (or by legacy ASM) - * - * @deprecated Since 9.0 and will be removed in 10.0 - */ - public function isAsmGloballyActivated(); -} diff --git a/src/PrestaShopBundle/Service/DataUpdater/Admin/ProductInterface.php b/src/PrestaShopBundle/Service/DataUpdater/Admin/ProductInterface.php deleted file mode 100644 index f7bb0b3551f75..0000000000000 --- a/src/PrestaShopBundle/Service/DataUpdater/Admin/ProductInterface.php +++ /dev/null @@ -1,108 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Service\DataUpdater\Admin; - -/** - * @deprecated since 8.1 and will be removed in next major. - * - * Data updater for new Architecture, about Product object model. - * - * This class will provide data from DB / ORM about Products for the Admin interface. - */ -interface ProductInterface -{ - /** - * Activate or deactivate a list of products. - * - * @param array $productListId The ID list of products to (de)activate - * @param bool $activate true to activate, false to deactivate - * - * @throws \PrestaShopBundle\Exception\UpdateProductException If an error occured during update (not really blocking since its just activation flag) - * - * @return bool true when succeed - */ - public function activateProductIdList(array $productListId, $activate = true); - - /** - * Do a safe delete on given product IDs. - * - * @param array $productIdList The ID list of products to delete - * - * @throws \PrestaShopBundle\Exception\UpdateProductException If deletion failed (some normal cases can brings this, it's not a Development error) - * - * @return bool true when succeed - */ - public function deleteProductIdList(array $productIdList); - - /** - * Duplicates the given product IDs. - * - * @param array $productIdList The ID list of products to delete - * - * @throws \PrestaShopBundle\Exception\UpdateProductException if duplication failed - * - * @return bool true when succeed - */ - public function duplicateProductIdList(array $productIdList); - - /** - * Do a safe delete on given product ID. - * - * @param int $productId The product ID to delete - * - * @throws \PrestaShopBundle\Exception\UpdateProductException If deletion failed (some normal cases can brings this, it's not a Development error) - * - * @return bool - */ - public function deleteProduct($productId); - - /** - * Duplicates the given product, and returns the new ID. - * - * Code comes from Legacy controller! - * - * @param int $productId The product ID to duplicate - * - * @return int The new product ID (duplicate) - */ - public function duplicateProduct($productId); - - /** - * Do a sort on a page of products. - * - * Since the sort can be partial (only one page, with offset and limit), we MUST sort only the given IDs, - * and keep the others safely sorted without any functional change (even if we can bulk shift positions to fix gaps and duplicates). - * - * @param array $productList the list of products to sort (keys: ID, values: old positions) The natural order of the array is the new order to update - * @param array $filterParams Contains the ID of the category to sort. Take it from AdminProductDataProvider::getPersistedFilterParameters(). - * - * @throws \PrestaShopBundle\Exception\UpdateProductException If deletion failed (some normal cases can brings this, it's not a Development error) - * - * @return bool true when succeed - */ - public function sortProductIdList(array $productList, $filterParams); -} diff --git a/src/PrestaShopBundle/Service/ProductService.php b/src/PrestaShopBundle/Service/ProductService.php deleted file mode 100644 index ccbc6689674f3..0000000000000 --- a/src/PrestaShopBundle/Service/ProductService.php +++ /dev/null @@ -1,57 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Service; - -use PrestaShop\PrestaShop\Adapter\Product\ProductDataProvider; -use Product; - -/** - * @deprecated since 8.1 and will be removed in next major. - */ -class ProductService -{ - /** - * @var ProductDataProvider - */ - protected $dataProvider; - - public function __construct(ProductDataProvider $dataProvider) - { - $this->dataProvider = $dataProvider; - } - - public function cleanupOldTempProducts() - { - $oldProducts = Product::getOldTempProducts(); - - foreach ($oldProducts as $oldProduct) { - $id_product = $oldProduct['id_product']; - $product = $this->dataProvider->getProduct($id_product); - $product->delete(); - } - } -} diff --git a/src/PrestaShopBundle/Service/TransitionalBehavior/AdminPagePreferenceInterface.php b/src/PrestaShopBundle/Service/TransitionalBehavior/AdminPagePreferenceInterface.php deleted file mode 100644 index aa7f95f3f6738..0000000000000 --- a/src/PrestaShopBundle/Service/TransitionalBehavior/AdminPagePreferenceInterface.php +++ /dev/null @@ -1,64 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace PrestaShopBundle\Service\TransitionalBehavior; - -/** - * Contract to know which page's version to display. - * - * This interface gives methods to use to take decision: - * - if we should display the new refactored page, or the old legacy one. - * - if we should display the switch on the admin layout to change this setting. - */ -interface AdminPagePreferenceInterface -{ - /** - * Use it to know if we need to redirect to legacy Controllers or not. - * - * @param string $page the page to look for - * - * @return bool true to redirect to legacy - */ - public function getTemporaryShouldUseLegacyPage($page); - - /** - * Set the temporary behavior of the new/old page on Admin interface. - * - * @param string $page the page to look for - * @param bool $useLegacy true to redirect to old legacy pages for Product controller - */ - public function setTemporaryShouldUseLegacyPage($page, $useLegacy); - - /** - * Use it to know if we need to display the 'switch to legacy page' button or not. - * In debug mode, always shown. - * - * @param string $page the page to look for - * - * @return bool true to show the switch to legacy page button - */ - public function getTemporaryShouldAllowUseLegacyPage($page = null); -} diff --git a/src/PrestaShopBundle/Twig/Component/QuickAccess.php b/src/PrestaShopBundle/Twig/Component/QuickAccess.php index bcb26bbae91b7..743e5d014018b 100644 --- a/src/PrestaShopBundle/Twig/Component/QuickAccess.php +++ b/src/PrestaShopBundle/Twig/Component/QuickAccess.php @@ -30,8 +30,6 @@ use PrestaShop\PrestaShop\Adapter\LegacyContext; use PrestaShop\PrestaShop\Core\Domain\Language\ValueObject\LanguageId; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagStateCheckerInterface; use PrestaShop\PrestaShop\Core\QuickAccess\QuickAccessRepositoryInterface; use PrestaShop\PrestaShop\Core\Util\Url\UrlCleaner; use PrestaShopBundle\Entity\Repository\TabRepository; @@ -52,7 +50,7 @@ class QuickAccess /** * link to new product creation form for product v2 */ - private const NEW_PRODUCT_V2_LINK = 'index.php/sell/catalog/products-v2/create'; + private const NEW_PRODUCT_V2_LINK = 'index.php/sell/catalog/products/create'; /** * List of Quick Accesses to display @@ -86,7 +84,6 @@ public function __construct( private readonly CsrfTokenManagerInterface $tokenManager, private readonly UserProvider $userProvider, private readonly RequestStack $requestStack, - private readonly FeatureFlagStateCheckerInterface $featureFlagStateChecker, private readonly MenuBuilder $menuBuilder ) { } @@ -127,11 +124,9 @@ public function getQuickAccesses(): array unset($quickAccesses[$index]); continue; } - // if new product page feature is enabled we create new product v2 modal popup - if ($this->featureFlagStateChecker->isEnabled(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2)) { - $quick['link'] = self::NEW_PRODUCT_V2_LINK; - $quick['class'] = 'new-product-button'; - } + // We create new product v2 modal popup link + $quick['link'] = self::NEW_PRODUCT_V2_LINK; + $quick['class'] = 'new-product-button'; } // Preparation of the link to display in component view. diff --git a/src/PrestaShopBundle/Twig/Layout/TemplateVariablesBuilder.php b/src/PrestaShopBundle/Twig/Layout/TemplateVariablesBuilder.php index a70d739e99360..47b9d3bece484 100644 --- a/src/PrestaShopBundle/Twig/Layout/TemplateVariablesBuilder.php +++ b/src/PrestaShopBundle/Twig/Layout/TemplateVariablesBuilder.php @@ -39,6 +39,7 @@ use PrestaShopBundle\Entity\Repository\TabRepository; use PrestaShopBundle\Entity\Tab; use PrestaShopBundle\Service\DataProvider\UserProvider; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface; /** @@ -61,6 +62,7 @@ public function __construct( private readonly ShopContext $shopContext, private readonly LegacyControllerContext $legacyControllerContext, private readonly MultistoreFeature $multistoreFeature, + private readonly RequestStack $requestStack, ) { } @@ -100,7 +102,7 @@ private function getJsRouterMetadata(): array { return [ // base url for javascript router - 'base_url' => $this->shopContext->getBaseURL(), + 'base_url' => $this->requestStack->getCurrentRequest()->getBaseUrl(), //security token for javascript router 'token' => $this->tokenManager->getToken($this->userProvider->getUsername())->getValue(), ]; diff --git a/tests/Integration/Classes/module/ModuleTest.php b/tests/Integration/Classes/module/ModuleTest.php index 494497144a172..5a73b63ebd40f 100644 --- a/tests/Integration/Classes/module/ModuleTest.php +++ b/tests/Integration/Classes/module/ModuleTest.php @@ -102,7 +102,7 @@ public function testRealOverrideInModuleDir(): void $overrides = $module->getOverrides(); $this->assertContains('Cart', $overrides); - $this->assertContains('AdminProductsController', $overrides); + $this->assertContains('DummyAdminController', $overrides); $this->assertCount(2, $overrides); HelperModule::removeModule('pscsx3241'); diff --git a/tests/Integration/Core/Addon/Module/ModuleManagerBuilderTest.php b/tests/Integration/Core/Addon/Module/ModuleManagerBuilderTest.php index 3f92739f0fabf..cb8209ae3a4ed 100644 --- a/tests/Integration/Core/Addon/Module/ModuleManagerBuilderTest.php +++ b/tests/Integration/Core/Addon/Module/ModuleManagerBuilderTest.php @@ -91,7 +91,7 @@ public static function tearDownAfterClass(): void } // Remove overrides - @unlink(_PS_ROOT_DIR_ . '/override/controllers/admin/AdminProductsController.php'); + @unlink(_PS_ROOT_DIR_ . '/override/controllers/admin/DummyAdminController.php'); @unlink(_PS_ROOT_DIR_ . '/override/classes/Cart.php'); // Reset modules folder @@ -129,21 +129,29 @@ public function testInstall(): void */ $resource_path = dirname(__DIR__, 4) . '/Resources/modules_tests/override/'; + $overrideCart = _PS_ROOT_DIR_ . '/override/classes/Cart.php'; $actual_override_cart = file_get_contents(_PS_ROOT_DIR_ . '/override/classes/Cart.php'); $expected_override_cart = file_get_contents($resource_path . '/Cart.php'); $actual_override_cart = $this->cleanup($actual_override_cart); $expected_override_cart = $this->cleanup($expected_override_cart); - $this->assertEquals($expected_override_cart, $actual_override_cart); + $this->assertEquals( + $expected_override_cart, + $actual_override_cart, + 'Cart.php file different' + ); + + $actual_override_admin_product = file_get_contents(_PS_ROOT_DIR_ . '/override/controllers/admin/DummyAdminController.php'); + $expected_override_admin_product = file_get_contents($resource_path . '/DummyAdminController.php'); - $actual_override_admin_product = file_get_contents(_PS_ROOT_DIR_ . '/override/controllers/admin/AdminProductsController.php'); - $expected_override_admin_product = file_get_contents($resource_path . '/AdminProductsController.php'); + $actual_override_admin_product = $this->cleanup($actual_override_admin_product); + $expected_override_admin_product = $this->cleanup($expected_override_admin_product); $this->assertEquals( - $this->cleanup($expected_override_admin_product), - $this->cleanup($actual_override_admin_product), - 'AdminProductsController.php file different' + $expected_override_admin_product, + $actual_override_admin_product, + 'DummyAdminController.php file different' ); // Then it checks that the overrides are removed once the modules are uninstalled. diff --git a/tests/Integration/PrestaShopBundle/Controller/Admin/ProductControllerTest.php b/tests/Integration/PrestaShopBundle/Controller/Admin/ProductControllerTest.php deleted file mode 100644 index 34518c2e4e535..0000000000000 --- a/tests/Integration/PrestaShopBundle/Controller/Admin/ProductControllerTest.php +++ /dev/null @@ -1,302 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -declare(strict_types=1); - -namespace Tests\Integration\PrestaShopBundle\Controller\Admin; - -use Context; -use Country; -use Currency; -use Employee; -use Language; -use Link; -use PrestaShop\PrestaShop\Adapter\Currency\CurrencyDataProvider; -use PrestaShop\PrestaShop\Adapter\LegacyContext; -use PrestaShop\PrestaShop\Core\Addon\Theme\Theme; -use PrestaShop\PrestaShop\Core\Kpi\Row\KpiRowPresenterInterface; -use Psr\Log\NullLogger; -use Shop; -use Symfony\Bundle\FrameworkBundle\KernelBrowser; -use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; -use Symfony\Component\Routing\Router; - -class ProductControllerTest extends WebTestCase -{ - /** - * @var KernelBrowser - */ - protected $client; - - /** - * @var Router - */ - protected $router; - - protected function setUp(): void - { - parent::setUp(); - - $this->client = self::createClient(); - $this->router = self::$kernel->getContainer()->get('router'); - $translator = self::$kernel->getContainer()->get('translator'); - - $employeeMock = $this->getMockBuilder(Employee::class) - ->getMock(); - $employeeMock->id_profile = 1; - $employeeMock->id_lang = 1; - - $contextMock = $this->getMockBuilder(Context::class) - ->setMethods(['getTranslator', 'getContext']) - ->disableOriginalConstructor() - ->getMock(); - - $contextMock->method('getTranslator') - ->will($this->returnValue($translator)); - - $contextMock->method('getContext') - ->will($this->returnValue($contextMock)); - - $contextMock->employee = $employeeMock; - - $shopMock = $this->getMockBuilder(Shop::class) - ->setMethods(['getBaseURL']) - ->disableOriginalConstructor() - ->getMock(); - - $shopMock->id = 1; - $shopMock->method('getBaseURL') - ->willReturn('my-awesome-url.com'); - - $contextMock->shop = $shopMock; - - $contextMock->shop->theme = new Theme([ - 'name' => 'classic', - 'directory' => _PS_ROOT_DIR_ . '/themes/', - ]); - - $countryMock = $this->getMockBuilder(Country::class) - ->getMock(); - $countryMock->iso_code = 'en'; - - $contextMock->country = $countryMock; - - $languageFixture = new Language(1); - - $contextMock->language = $languageFixture; - - $currencyMock = $this->getMockBuilder(Currency::class) - ->disableOriginalConstructor() - ->getMock(); - - $contextMock->currency = $currencyMock; - - $currencyDataProviderMock = $this->getMockBuilder(CurrencyDataProvider::class) - ->disableOriginalConstructor() - ->setMethods(['getDefaultCurrencyIsoCode', 'getDefaultCurrency']) - ->getMock(); - - $linkMock = $this - ->getMockBuilder(Link::class) - ->disableOriginalConstructor() - ->getMock(); - - // If getCMSLink() is not mocked, - // it returns null, thus breaking code that expects it to return string, - // as string is the only valid return type for this method. - $linkMock->method('getCMSLink')->willReturn(''); - - $contextMock->link = $linkMock; - - $currencyDataProviderMock->method('getDefaultCurrencyIsoCode') - ->will($this->returnValue('en')); - $currencyDataProviderMock->method('getDefaultCurrency') - ->will($this->returnValue($currencyMock)); - - $kpiRowPresenterMock = $this->getMockBuilder(KpiRowPresenterInterface::class) - ->disableOriginalConstructor() - ->setMethods(['present']) - ->getMock(); - - $kpiRowPresenterMock->method('present') - ->will($this->returnValue(['allowRefresh' => false, 'kpis' => ['a', 'b', 'c']])); - - $smartyMock = $this - ->getMockBuilder(\Smarty::class) - ->disableOriginalConstructor() - ->getMock() - ; - - $legacyContextMock = $this->getMockBuilder(LegacyContext::class) - ->setMethods([ - 'getContext', - 'getEmployeeLanguageIso', - 'getEmployeeCurrency', - 'getRootUrl', - 'getLanguages', - 'getLanguage', - 'getAdminLink', - 'getAvailableLanguages', - 'getSmarty', - ]) - ->disableAutoload() - ->disableOriginalConstructor() - ->getMock(); - - $legacyContextMock->method('getSmarty') - ->willReturn($smartyMock); - - $legacyContextMock->method('getContext') - ->willReturn($contextMock); - - $legacyContextMock->method('getLanguages') - ->will( - $this->returnValue( - [ - [ - 'id_lang' => '1', - 'name' => 'English (English)', - 'iso_code' => 'en', - 'language_code' => 'en-us', - 'locale' => 'en-US', - ], - [ - 'id_lang' => '2', - 'name' => 'Français (French)', - 'iso_code' => 'fr', - 'language_code' => 'fr', - 'locale' => 'fr-FR', - ], - ] - ) - ); - - $legacyContextMock->method('getLanguage') - ->will( - $this->returnValue($languageFixture) - ); - - $legacyContextMock->method('getAvailableLanguages') - ->willReturn( - [ - [ - 'id_lang' => '1', - 'name' => 'English (English)', - 'iso_code' => 'en', - 'language_code' => 'en-us', - 'locale' => 'en-US', - 'active' => true, - ], - [ - 'id_lang' => '2', - 'name' => 'Français (French)', - 'iso_code' => 'fr', - 'language_code' => 'fr', - 'locale' => 'fr-FR', - 'active' => false, - ], - ] - ); - - self::$kernel->getContainer()->set('prestashop.adapter.data_provider.currency', $currencyDataProviderMock); - self::$kernel->getContainer()->set('prestashop.adapter.legacy.context', $legacyContextMock); - self::$kernel->getContainer()->set('prestashop.core.kpi_row.presenter', $kpiRowPresenterMock); - self::$kernel->getContainer()->set('logger', new NullLogger()); - - // Enable debug mode - $configurationMock = $this->getMockBuilder('\PrestaShop\PrestaShop\Adapter\Configuration') - ->setMethods(['get']) - ->disableOriginalConstructor() - ->disableAutoload() - ->getMock(); - - $values = [ - ['_PS_MODE_DEMO_', null, null, true], - ['_PS_ROOT_DIR_', null, null, _PS_ROOT_DIR_ . '/'], - ['_PS_MODULE_DIR_', null, null, _PS_ROOT_DIR_ . '/tests/Resources/modules/'], - ]; - - $configurationMock->method('get') - ->will($this->returnValueMap($values)); - - self::$kernel->getContainer()->set('prestashop.adapter.legacy.configuration', $configurationMock); - } - - /** - * @dataProvider getUnitActions - * - * @param string $action - */ - public function testUnitAction(string $action): void - { - $actionUrl = $this->router->generate('admin_product_unit_action', [ - 'action' => $action, - 'id' => 1, - ]); - $this->client->request('POST', $actionUrl); - $this->assertSessionFlagBagContainsFailureMessage(); - } - - public function getUnitActions(): array - { - return [ - ['delete'], - ['duplicate'], - ['activate'], - ['deactivate'], - ]; - } - - /** - * @dataProvider getBulkActions - * - * @param string $action - */ - public function testBulkAction(string $action): void - { - $actionUrl = $this->router->generate('admin_product_bulk_action', [ - 'action' => $action, - ]); - $this->client->request('POST', $actionUrl); - $this->assertSessionFlagBagContainsFailureMessage(); - } - - public function getBulkActions(): array - { - return [ - ['activate_all'], - ['deactivate_all'], - ['duplicate_all'], - ['delete_all'], - ]; - } - - protected function assertSessionFlagBagContainsFailureMessage(): void - { - $all = self::$kernel->getContainer()->get('session')->getFlashBag()->all(); - $this->assertArrayHasKey('failure', $all); - } -} diff --git a/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/CombinationControllerTest.php b/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/CombinationControllerTest.php index 526fe3aad1467..b58d6d832cb2c 100644 --- a/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/CombinationControllerTest.php +++ b/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/CombinationControllerTest.php @@ -31,8 +31,6 @@ use Cache; use DOMElement; use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductType; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; -use PrestaShopBundle\Entity\Repository\FeatureFlagRepository; use RuntimeException; use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Form; @@ -43,11 +41,6 @@ class CombinationControllerTest extends FormGridControllerTestCase { - /** - * @var bool - */ - private $changedProductFeatureFlag = false; - public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); @@ -61,27 +54,6 @@ public static function tearDownAfterClass(): void ProductResetter::resetProducts(); } - public function setUp(): void - { - parent::setUp(); - $featureFlagRepository = $this->client->getContainer()->get(FeatureFlagRepository::class); - if (!$featureFlagRepository->isEnabled(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2)) { - $featureFlagRepository->enable(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2); - $this->changedProductFeatureFlag = true; - } - } - - public function tearDown(): void - { - if ($this->changedProductFeatureFlag) { - $featureFlagRepository = $this->client->getContainer()->get(FeatureFlagRepository::class); - $featureFlagRepository->disable(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2); - } - - // Call parent tear down later or the kernel will be shut down - parent::tearDown(); - } - /** * @return int */ diff --git a/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/MultiShopProductControllerTest.php b/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/MultiShopProductControllerTest.php index c5512350f3f90..7a6e98540b4fb 100644 --- a/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/MultiShopProductControllerTest.php +++ b/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/MultiShopProductControllerTest.php @@ -37,9 +37,7 @@ use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductId; use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductType; use PrestaShop\PrestaShop\Core\Domain\Shop\ValueObject\ShopConstraint; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; use PrestaShop\PrestaShop\Core\Multistore\MultistoreConfig; -use PrestaShopBundle\Entity\Repository\FeatureFlagRepository; use Product; use Shop; use ShopGroup; @@ -153,22 +151,6 @@ public static function tearDownAfterClass(): void self::forceMultiShopCookie([]); } - public function setUp(): void - { - parent::setUp(); - $featureFlagRepository = $this->client->getContainer()->get(FeatureFlagRepository::class); - $featureFlagRepository->enable(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2); - } - - public function tearDown(): void - { - $featureFlagRepository = $this->client->getContainer()->get(FeatureFlagRepository::class); - $featureFlagRepository->disable(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2); - - // Call parent tear down later or the kernel will be shut down - parent::tearDown(); - } - /** * @dataProvider getMultiShopListParameters */ diff --git a/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/ProductControllerTest.php b/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/ProductControllerTest.php index c10594b2804cb..f77dabfd29c25 100644 --- a/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/ProductControllerTest.php +++ b/tests/Integration/PrestaShopBundle/Controller/Sell/Catalog/ProductControllerTest.php @@ -34,8 +34,6 @@ use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductType; use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\ProductVisibility; use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\RedirectType; -use PrestaShop\PrestaShop\Core\FeatureFlag\FeatureFlagSettings; -use PrestaShopBundle\Entity\Repository\FeatureFlagRepository; use Symfony\Component\DomCrawler\Crawler; use Tests\Integration\Core\Form\IdentifiableObject\Handler\FormHandlerChecker; use Tests\Integration\PrestaShopBundle\Controller\FormGridControllerTestCase; @@ -49,11 +47,6 @@ class ProductControllerTest extends FormGridControllerTestCase private const TEST_MINIMAL_QUANTITY = 2; private const TEST_RETAIL_PRICE_TAX_EXCLUDED = 87.7; - /** - * @var bool - */ - private $changedProductFeatureFlag = false; - public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); @@ -67,27 +60,6 @@ public static function tearDownAfterClass(): void ProductResetter::resetProducts(); } - public function setUp(): void - { - parent::setUp(); - $featureFlagRepository = $this->client->getContainer()->get(FeatureFlagRepository::class); - if (!$featureFlagRepository->isEnabled(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2)) { - $featureFlagRepository->enable(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2); - $this->changedProductFeatureFlag = true; - } - } - - public function tearDown(): void - { - if ($this->changedProductFeatureFlag) { - $featureFlagRepository = $this->client->getContainer()->get(FeatureFlagRepository::class); - $featureFlagRepository->disable(FeatureFlagSettings::FEATURE_FLAG_PRODUCT_PAGE_V2); - } - - // Call parent tear down later or the kernel will be shut down - parent::tearDown(); - } - public function testIndex(): int { $products = $this->getEntitiesFromGrid(); diff --git a/tests/Integration/PrestaShopBundle/Form/AdvancedFormType.php b/tests/Integration/PrestaShopBundle/Form/AdvancedFormType.php index 4662011f1d69e..0c8c36b65b04d 100644 --- a/tests/Integration/PrestaShopBundle/Form/AdvancedFormType.php +++ b/tests/Integration/PrestaShopBundle/Form/AdvancedFormType.php @@ -30,7 +30,6 @@ use PrestaShop\PrestaShop\Core\Domain\Product\ValueObject\RedirectType; use PrestaShopBundle\Form\Admin\Type\TranslatorAwareType; -use PrestaShopBundle\Form\Admin\Type\TypeaheadProductCollectionType; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Form\DataTransformerInterface; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; @@ -69,11 +68,9 @@ public function buildForm(FormBuilderInterface $builder, array $options) $this->trans('Temporary redirection to a product (302)', 'Admin.Catalog.Feature') => RedirectType::TYPE_PRODUCT_TEMPORARY, ], ]) - ->add('target', TypeaheadProductCollectionType::class, [ + ->add('target', ChoiceType::class, [ 'required' => false, 'error_bubbling' => false, - 'template_collection' => '%s', - 'limit' => 1, ]) ; diff --git a/tests/Integration/PrestaShopBundle/Model/Product/AdminModelAdapterTest.php b/tests/Integration/PrestaShopBundle/Model/Product/AdminModelAdapterTest.php deleted file mode 100644 index 55cba878f1891..0000000000000 --- a/tests/Integration/PrestaShopBundle/Model/Product/AdminModelAdapterTest.php +++ /dev/null @@ -1,279 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -namespace Tests\Integration\PrestaShopBundle\Model\Product; - -use PrestaShop\PrestaShop\Adapter\Product\AdminProductWrapper; -use PrestaShop\PrestaShop\Adapter\Tools; -use PrestaShopBundle\Model\Product\AdminModelAdapter; -use PrestaShopBundle\Utils\FloatParser; -use Product; -use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; -use Tests\Integration\Utility\ContextMockerTrait; - -class AdminModelAdapterTest extends KernelTestCase -{ - use ContextMockerTrait; - - /** @var AdminModelAdapter */ - private $adminModelAdapter; - - /** @var Product */ - private $product; - - private function fakeFormData(): array - { - return [ - 'id_product' => '', - 'step1' => [ - 'type_product' => '', - 'inputPackItems' => [], - 'name' => [], - 'name_1' => 'Amazing product', - 'name_2' => 'Amazing product', - 'description' => [], - 'description_short' => [], - 'active' => '', - 'price_shortcut' => '', - 'qty_0_shortcut' => '', - 'categories' => ['tree' => []], - 'id_category_default' => '', - 'related_products' => [], - 'id_manufacturer' => '', - 'features' => [], - 'images' => [], - ], - 'step2' => [ - 'price' => '', - 'ecotax' => '', - 'id_tax_rules_group' => '', - 'on_sale' => '', - 'wholesale_price' => '', - 'unit_price' => '', - 'unity' => '', - 'specific_price' => [], - 'specificPricePriority_0' => '', - 'specificPricePriority_1' => '', - 'specificPricePriority_2' => '', - 'specificPricePriority_3' => '', - ], - 'step3' => [ - 'qty_0' => '', - 'combinations' => [], - 'out_of_stock' => '', - 'minimal_quantity' => '', - 'low_stock_threshold' => '', - 'low_stock_alert' => '', - 'available_now' => [], - 'available_later' => [], - 'available_date' => '', - 'pack_stock_type' => '', - 'virtual_product' => [], - ], - 'step4' => [ - 'width' => '', - 'height' => '', - 'depth' => '', - 'weight' => '', - 'additional_shipping_cost' => '', - 'selectedCarriers' => [], - 'additional_delivery_times' => '', - 'delivery_in_stock' => [], - 'delivery_out_stock' => [], - ], - 'step5' => [ - 'link_rewrite' => [], - 'meta_title' => [], - 'meta_description' => [], - ], - 'step6' => [ - 'redirect_type' => '', - 'id_type_redirected' => [], - 'visibility' => '', - 'tags' => [], - 'display_options' => [], - 'upc' => '', - 'mpn' => '', - 'ean13' => '', - 'isbn' => '', - 'reference' => '', - 'condition' => '', - 'suppliers' => [], - 'default_supplier' => '', - 'custom_fields' => [], - 'attachments' => [], - 'supplier_combination_1' => [], - ], - ]; - } - - private function fakeCombination(): array - { - return ['0' => [ - 'id_product_attribute' => '6', - 'id_product' => '1', - 'reference' => '', - 'supplier_reference' => '', - 'location' => '', - 'ean13' => '', - 'isbn' => '', - 'upc' => '', - 'mpn' => '', - 'wholesale_price' => '1.250000', - 'price' => '0.000000', - 'ecotax' => '0.000000', - 'ecotax_tax_excluded' => '0.000000', - 'ecotax_tax_included' => '0.000000', - 'quantity' => 300, - 'weight' => '0.000000', - 'unit_price_impact' => '0.000000', - 'default_on' => null, - 'minimal_quantity' => '1', - 'low_stock_threshold' => '2', - 'low_stock_alert' => '1', - 'available_date' => '0000-00-00', - 'id_shop' => '1', - 'id_attribute_group' => '1', - 'is_color_group' => '0', - 'group_name' => 'Taille', - 'attribute_name' => 'L', - 'id_attribute' => '3', - ]]; - } - - private function fakeProduct(): Product - { - $product = new Product(); - $product->name = 'Product name'; - - return $product; - } - - protected function setUp(): void - { - self::bootKernel(); - - $this->product = $this->fakeProduct(); - $this->adminModelAdapter = new AdminModelAdapter( - self::$kernel->getContainer()->get('prestashop.adapter.legacy.context'), - self::$kernel->getContainer()->get(AdminProductWrapper::class), - self::$kernel->getContainer()->get(Tools::class), - self::$kernel->getContainer()->get('prestashop.adapter.data_provider.product'), - self::$kernel->getContainer()->get('prestashop.adapter.data_provider.supplier'), - self::$kernel->getContainer()->get('prestashop.adapter.data_provider.feature'), - self::$kernel->getContainer()->get('prestashop.adapter.data_provider.pack'), - self::$kernel->getContainer()->get('prestashop.adapter.shop.context'), - self::$kernel->getContainer()->get('prestashop.adapter.data_provider.tax'), - self::$kernel->getContainer()->get('prestashop.adapter.legacy.configuration'), - self::$kernel->getContainer()->get('router'), - self::$kernel->getContainer()->get(FloatParser::class) - ); - } - - protected function tearDown(): void - { - parent::tearDown(); - unset($this->product, $this->adminModelAdapter); - } - - /** - * Checks that the construction of object still works as expected - */ - public function testConstruct(): void - { - $this->assertInstanceOf(AdminModelAdapter::class, $this->adminModelAdapter); - } - - public function testGetFormData(): void - { - $this->assertIsArray($this->adminModelAdapter->getFormData($this->product)); - $expectedArrayStructure = $this->fakeFormData(); - - foreach ($expectedArrayStructure as $property => $value) { - $this->assertArrayHasKey( - $property, - $this->adminModelAdapter->getFormData($this->product), - sprintf('The expected key %s was not found', $property) - ); - } - } - - public function testGetModelData(): void - { - $this->assertIsArray($this->adminModelAdapter->getModelData($this->fakeFormData())); - } - - /** - * @todo find a way to check the value of `attribute_quantity` and `name` and `attribute_price_display` that depend on database - */ - public function testGetFormCombination(): void - { - $expectedStructureReturn = [ - 'id_product_attribute' => '6', - 'attribute_reference' => '', - 'attribute_ean13' => '', - 'attribute_isbn' => '', - 'attribute_upc' => '', - 'attribute_mpn' => '', - 'attribute_wholesale_price' => '1.250000', - 'attribute_price_impact' => 0, - 'attribute_price' => '0.000000', - 'attribute_price_display' => '$0.00', - 'final_price' => '0.000000', - 'final_price_tax_included' => '0.000000', - 'attribute_priceTI' => '', - 'product_ecotax' => '0.000000', - 'attribute_ecotax' => '0.000000', - 'attribute_weight_impact' => 0, - 'attribute_weight' => '0.000000', - 'attribute_unit_impact' => 0, - 'attribute_unity' => '0.000000', - 'attribute_minimal_quantity' => '1', - 'attribute_low_stock_threshold' => '2', - 'attribute_low_stock_alert' => '1', - 'available_date_attribute' => '0000-00-00', - 'attribute_default' => false, - 'attribute_location' => false, - 'attribute_quantity' => 300, - 'name' => 'Taille - L', - 'id_product' => null, - ]; - $combinationDataProvider = self::$kernel->getContainer()->get('prestashop.adapter.data_provider.combination'); - $actualReturn = $combinationDataProvider->completeCombination($this->fakeCombination(), $this->product); - - foreach ($expectedStructureReturn as $property => $value) { - $this->assertArrayHasKey($property, $actualReturn, sprintf('The expected key %s was not found', $property)); - $this->assertEquals( - $value, - $actualReturn[$property], - sprintf('The expected value for property %s is wrong', $property) - ); - } - - // Test full equals to check if there are additional unexpected fields - $this->assertEquals($expectedStructureReturn, $actualReturn); - } -} diff --git a/tests/Resources/modules_tests/override/AdminProductsController.php b/tests/Resources/modules_tests/override/AdminProductsController.php deleted file mode 100644 index ebef7ac042f5d..0000000000000 --- a/tests/Resources/modules_tests/override/AdminProductsController.php +++ /dev/null @@ -1,4530 +0,0 @@ -context->employee) - && $this->context->employee->bo_theme) ? $this->context->employee->bo_theme : 'default'); - if (!file_exists(_PS_BO_ALL_THEMES_DIR_ . $bo_theme . DIRECTORY_SEPARATOR - . 'template')) { - $bo_theme = 'default'; - } - $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.iframe-transport.js'); - $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.fileupload.js'); - $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.fileupload-process.js'); - $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.fileupload-validate.js'); - $this->addJs(__PS_BASE_URI__ . 'js/vendor/spin.js'); - $this->addJs(__PS_BASE_URI__ . 'js/vendor/ladda.js'); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _cleanMetaKeywords($keywords) - { - if (!empty($keywords) && $keywords != '') { - $out = []; - $words = explode(',', $keywords); - foreach ($words as $word_item) { - $word_item = trim($word_item); - if (!empty($word_item) && $word_item != '') { - $out[] = $word_item; - } - } - - return (count($out) > 0) ? implode(',', $out) : ''; - } else { - return ''; - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function copyFromPost(&$object, $table) - { - parent::copyFromPost($object, $table); - if (get_class($object) != 'Product') { - return; - } - - foreach (Language::getIDs(false) as $id_lang) { - if (isset($_POST['meta_keywords_' . $id_lang])) { - $_POST['meta_keywords_' . $id_lang] = $this->_cleanMetaKeywords(Tools::strtolower($_POST['meta_keywords_' . $id_lang])); - $object->meta_keywords[$id_lang] = $_POST['meta_keywords_' . $id_lang]; - } - } - $_POST['width'] = empty($_POST['width']) ? '0' : str_replace(',', '.', $_POST['width']); - $_POST['height'] = empty($_POST['height']) ? '0' : str_replace(',', '.', $_POST['height']); - $_POST['depth'] = empty($_POST['depth']) ? '0' : str_replace(',', '.', $_POST['depth']); - $_POST['weight'] = empty($_POST['weight']) ? '0' : str_replace(',', '.', $_POST['weight']); - if (Tools::getIsset('unit_price') != null) { - $object->unit_price = str_replace(',', '.', Tools::getValue('unit_price')); - } - if (Tools::getIsset('ecotax') != null) { - $object->ecotax = str_replace(',', '.', Tools::getValue('ecotax')); - } - if ($this->isTabSubmitted('Informations')) { - $object->available_for_order = (int) Tools::getValue('available_for_order'); - $object->show_price = $object->available_for_order ? 1 : (int) Tools::getValue('show_price'); - $object->online_only = (int) Tools::getValue('online_only'); - } - if ($this->isTabSubmitted('Prices')) { - $object->on_sale = (int) Tools::getValue('on_sale'); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function getList($id_lang, $orderBy = null, $orderWay = null, $start = 0, $limit = null, $id_lang_shop = null) - { - $orderByPriceFinal = (empty($orderBy) ? ($this->context->cookie->__get($this->table . 'Orderby') ? $this->context->cookie->__get($this->table . 'Orderby') : 'id_' . $this->table) : $orderBy); - $orderWayPriceFinal = (empty($orderWay) ? ($this->context->cookie->__get($this->table . 'Orderway') ? $this->context->cookie->__get($this->table . 'Orderby') : 'ASC') : $orderWay); - if ($orderByPriceFinal == 'price_final') { - $orderBy = 'id_' . $this->table; - $orderWay = 'ASC'; - } - parent::getList($id_lang, $orderBy, $orderWay, $start, $limit, $this->context->shop->id); - - $nb = count($this->_list); - if ($this->_list) { - $context = $this->context->cloneContext(); - $context->shop = clone $context->shop; - - for ($i = 0; $i < $nb; ++$i) { - if (Context::getContext()->shop->getContext() != Shop::CONTEXT_SHOP) { - $context->shop = new Shop((int) $this->_list[$i]['id_shop_default']); - } - $this->_list[$i]['price'] = Tools::convertPrice($this->_list[$i]['price'], $this->context->currency, true, $this->context); - $this->_list[$i]['price_tmp'] = (float) Product::getPriceStatic($this->_list[$i]['id_product'], true, null, 2, null, false, true, 1, true, null, null, null, $nothing, true, true, $context); - } - } - if ($orderByPriceFinal == 'price_final') { - if (strtolower($orderWayPriceFinal) == 'desc') { - uasort($this->_list, 'cmpPriceDesc'); - } else { - uasort($this->_list, 'cmpPriceAsc'); - } - } - for ($i = 0; $this->_list && $i < $nb; ++$i) { - $this->_list[$i]['price_final'] = $this->_list[$i]['price_tmp']; - unset($this->_list[$i]['price_tmp']); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function loadObject($opt = false) - { - $result = parent::loadObject($opt); - if ($result && Validate::isLoadedObject($this->object)) { - if (Shop::getContext() == Shop::CONTEXT_SHOP && Shop::isFeatureActive() && !$this->object->isAssociatedToShop()) { - $default_product = new Product((int) $this->object->id, false, null, (int) $this->object->id_shop_default); - $def = ObjectModel::getDefinition($this->object); - foreach ($def['fields'] as $field_name => $row) { - if (is_array($default_product->$field_name)) { - foreach ($default_product->$field_name as $key => $value) { - $this->object->{$field_name}[$key] = $value; - } - } else { - $this->object->$field_name = $default_product->$field_name; - } - } - } - $this->object->loadStockData(); - } - - return $result; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessGetCountriesOptions() - { - if (!$res = Country::getCountriesByIdShop((int) Tools::getValue('id_shop'), (int) $this->context->language->id)) { - return; - } - $tpl = $this->createTemplate('specific_prices_shop_update.tpl'); - $tpl->assign( - [ - 'option_list' => $res, - 'key_id' => 'id_country', - 'key_value' => 'name', - ] - ); - $this->content = $tpl->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessGetCurrenciesOptions() - { - if (!$res = Currency::getCurrenciesByIdShop((int) Tools::getValue('id_shop'))) { - return; - } - $tpl = $this->createTemplate('specific_prices_shop_update.tpl'); - $tpl->assign( - [ - 'option_list' => $res, - 'key_id' => 'id_currency', - 'key_value' => 'name', - ] - ); - $this->content = $tpl->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessGetGroupsOptions() - { - if (!$res = Group::getGroups((int) $this->context->language->id, (int) Tools::getValue('id_shop'))) { - return; - } - $tpl = $this->createTemplate('specific_prices_shop_update.tpl'); - $tpl->assign( - [ - 'option_list' => $res, - 'key_id' => 'id_group', - 'key_value' => 'name', - ] - ); - $this->content = $tpl->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processDeleteVirtualProduct() - { - if (!($id_product_download = ProductDownload::getIdFromIdProduct((int) Tools::getValue('id_product')))) { - $this->errors[] = Tools::displayError('Cannot retrieve file'); - } else { - $product_download = new ProductDownload((int) $id_product_download); - if (!$product_download->deleteFile((int) $id_product_download)) { - $this->errors[] = Tools::displayError('Cannot delete file'); - } else { - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) Tools::getValue('id_product') . '&updateproduct&key_tab=VirtualProduct&conf=1&token=' . $this->token; - } - } - $this->display = 'edit'; - $this->tab_display = 'VirtualProduct'; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessAddAttachment() - { - if (isset($_FILES['attachment_file'])) { - if ((int) $_FILES['attachment_file']['error'] === 1) { - $_FILES['attachment_file']['error'] = []; - $max_upload = (int) ini_get('upload_max_filesize'); - $max_post = (int) ini_get('post_max_size'); - $upload_mb = min($max_upload, $max_post); - $_FILES['attachment_file']['error'][] = sprintf( - $this->l('File %1$s exceeds the size allowed by the server. The limit is set to %2$d MB.'), - '' . $_FILES['attachment_file']['name'] . ' ', - '' . $upload_mb . '' - ); - } - $_FILES['attachment_file']['error'] = []; - $is_attachment_name_valid = false; - $attachment_names = Tools::getValue('attachment_name'); - $attachment_descriptions = Tools::getValue('attachment_description'); - if (!isset($attachment_names) || !$attachment_names) { - $attachment_names = []; - } - if (!isset($attachment_descriptions) || !$attachment_descriptions) { - $attachment_descriptions = []; - } - foreach ($attachment_names as $lang => $name) { - $language = Language::getLanguage((int) $lang); - if (Tools::strlen($name) > 0) { - $is_attachment_name_valid = true; - } - if (!Validate::isGenericName($name)) { - $_FILES['attachment_file']['error'][] = sprintf(Tools::displayError('Invalid name for %s language'), $language['name']); - } elseif (Tools::strlen($name) > 32) { - $_FILES['attachment_file']['error'][] = sprintf(Tools::displayError('The name for %1s language is too long (%2d chars max).'), $language['name'], 32); - } - } - foreach ($attachment_descriptions as $lang => $description) { - $language = Language::getLanguage((int) $lang); - if (!Validate::isCleanHtml($description)) { - $_FILES['attachment_file']['error'][] = sprintf(Tools::displayError('Invalid description for %s language.'), $language['name']); - } - } - if (!$is_attachment_name_valid) { - $_FILES['attachment_file']['error'][] = Tools::displayError('An attachment name is required.'); - } - if (empty($_FILES['attachment_file']['error'])) { - if (is_uploaded_file($_FILES['attachment_file']['tmp_name'])) { - if ($_FILES['attachment_file']['size'] > (Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE') * 1024 * 1024)) { - $_FILES['attachment_file']['error'][] = sprintf( - $this->l('The file is too large. Maximum size allowed is: %1$d kB. The file you are trying to upload is %2$d kB.'), - (Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE') * 1024), - number_format(($_FILES['attachment_file']['size'] / 1024), 2, '.', '') - ); - } else { - do { - $uniqid = sha1(microtime()); - } while (file_exists(_PS_DOWNLOAD_DIR_ . $uniqid)); - if (!copy($_FILES['attachment_file']['tmp_name'], _PS_DOWNLOAD_DIR_ . $uniqid)) { - $_FILES['attachment_file']['error'][] = $this->l('File copy failed'); - } - @unlink($_FILES['attachment_file']['tmp_name']); - } - } else { - $_FILES['attachment_file']['error'][] = Tools::displayError('The file is missing.'); - } - if (empty($_FILES['attachment_file']['error']) && isset($uniqid)) { - $attachment = new Attachment(); - foreach ($attachment_names as $lang => $name) { - $attachment->name[(int) $lang] = $name; - } - foreach ($attachment_descriptions as $lang => $description) { - $attachment->description[(int) $lang] = $description; - } - $attachment->file = $uniqid; - $attachment->mime = $_FILES['attachment_file']['type']; - $attachment->file_name = $_FILES['attachment_file']['name']; - if (empty($attachment->mime) || Tools::strlen($attachment->mime) > 128) { - $_FILES['attachment_file']['error'][] = Tools::displayError('Invalid file extension'); - } - if (!Validate::isGenericName($attachment->file_name)) { - $_FILES['attachment_file']['error'][] = Tools::displayError('Invalid file name'); - } - if (Tools::strlen($attachment->file_name) > 128) { - $_FILES['attachment_file']['error'][] = Tools::displayError('The file name is too long.'); - } - if (empty($this->errors)) { - $res = $attachment->add(); - if (!$res) { - $_FILES['attachment_file']['error'][] = Tools::displayError('This attachment was unable to be loaded into the database.'); - } else { - $_FILES['attachment_file']['id_attachment'] = $attachment->id; - $_FILES['attachment_file']['filename'] = $attachment->name[$this->context->employee->id_lang]; - $id_product = (int) Tools::getValue($this->identifier); - $res = $attachment->attachProduct($id_product); - if (!$res) { - $_FILES['attachment_file']['error'][] = Tools::displayError('We were unable to associate this attachment to a product.'); - } - } - } else { - $_FILES['attachment_file']['error'][] = Tools::displayError('Invalid file'); - } - } - } - die(json_encode($_FILES)); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processAttachments() - { - if ($id = (int) Tools::getValue($this->identifier)) { - $attachments = trim(Tools::getValue('arrayAttachments'), ','); - $attachments = explode(',', $attachments); - if (!Attachment::attachToProduct($id, $attachments)) { - $this->errors[] = Tools::displayError('An error occurred while saving product attachments.'); - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processDuplicate() - { - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $id_product_old = $product->id; - if (empty($product->price) && Shop::getContext() == Shop::CONTEXT_GROUP) { - $shops = ShopGroup::getShopsFromGroup(Shop::getContextShopGroupID()); - foreach ($shops as $shop) { - if ($product->isAssociatedToShop($shop['id_shop'])) { - $product_price = new Product($id_product_old, false, null, $shop['id_shop']); - $product->price = $product_price->price; - } - } - } - unset( - $product->id, - $product->id_product - ); - $product->indexed = 0; - $product->active = 0; - if ($product->add() - && Category::duplicateProductCategories($id_product_old, $product->id) - && ($combination_images = Product::duplicateAttributes($id_product_old, $product->id)) !== false - && GroupReduction::duplicateReduction($id_product_old, $product->id) - && Product::duplicateAccessories($id_product_old, $product->id) - && Product::duplicateFeatures($id_product_old, $product->id) - && Product::duplicateSpecificPrices($id_product_old, $product->id) - && Pack::duplicate($id_product_old, $product->id) - && Product::duplicateCustomizationFields($id_product_old, $product->id) - && Product::duplicateTags($id_product_old, $product->id) - && Product::duplicateDownload($id_product_old, $product->id)) { - if ($product->hasAttributes()) { - Product::updateDefaultAttribute($product->id); - } - if (!Tools::getValue('noimage') && !Image::duplicateProductImages($id_product_old, $product->id, $combination_images)) { - $this->errors[] = Tools::displayError('An error occurred while copying images.'); - } else { - Hook::exec('actionProductAdd', ['id_product' => (int) $product->id, 'product' => $product]); - if (in_array($product->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $product->id); - } - $this->redirect_after = self::$currentIndex . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&conf=19&token=' . $this->token; - } - } else { - $this->errors[] = Tools::displayError('An error occurred while creating an object.'); - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processDelete() - { - if (Validate::isLoadedObject($object = $this->loadObject()) && isset($this->fieldImageSettings)) { - if (isset($object->noZeroObject) && count($taxes = call_user_func([$this->className, $object->noZeroObject])) <= 1) { - $this->errors[] = Tools::displayError('You need at least one object.') . ' ' . $this->table . '
    ' . Tools::displayError('You cannot delete all of the items.'); - } else { - if (!count($this->errors)) { - if ($object->delete()) { - $id_category = (int) Tools::getValue('id_category'); - $category_url = empty($id_category) ? '' : '&id_category=' . (int) $id_category; - PrestaShopLogger::addLog(sprintf($this->l('%s deletion', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int) $object->id, true, (int) $this->context->employee->id); - $this->redirect_after = self::$currentIndex . '&conf=1&token=' . $this->token . $category_url; - } else { - $this->errors[] = Tools::displayError('An error occurred during deletion.'); - } - } - } - } else { - $this->errors[] = Tools::displayError('An error occurred while deleting the object.') . ' ' . $this->table . ' ' . Tools::displayError('(cannot load object)'); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processImage() - { - $id_image = (int) Tools::getValue('id_image'); - $image = new Image((int) $id_image); - if (Validate::isLoadedObject($image)) { - if (Tools::getIsset('editImage')) { - if ($image->cover) { - $_POST['cover'] = 1; - } - $_POST['id_image'] = $image->id; - } elseif (Tools::getIsset('coverImage')) { - Image::deleteCover($image->id_product); - $image->cover = 1; - if (!$image->update()) { - $this->errors[] = Tools::displayError('You cannot change the product\'s cover image.'); - } else { - $productId = (int) Tools::getValue('id_product'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . $productId . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . $productId . '_' . $this->context->shop->id . '.jpg'); - $this->redirect_after = self::$currentIndex . '&id_product=' . $image->id_product . '&id_category=' . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&action=Images&addproduct' . '&token=' . $this->token; - } - } elseif (Tools::getIsset('imgPosition') && Tools::getIsset('imgDirection')) { - $image->updatePosition(Tools::getValue('imgDirection'), Tools::getValue('imgPosition')); - $this->redirect_after = self::$currentIndex . '&id_product=' . $image->id_product . '&id_category=' . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&add' . $this->table . '&action=Images&token=' . $this->token; - } - } else { - $this->errors[] = Tools::displayError('The image could not be found. '); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function processBulkDelete() - { - if ($this->access('delete')) { - if (is_array($this->boxes) && !empty($this->boxes)) { - $object = new $this->className(); - if (isset($object->noZeroObject) && - (count(call_user_func([$this->className, $object->noZeroObject])) <= 1 || count($_POST[$this->table . 'Box']) == count(call_user_func([$this->className, $object->noZeroObject])))) { - $this->errors[] = Tools::displayError('You need at least one object.') . ' ' . $this->table . '
    ' . Tools::displayError('You cannot delete all of the items.'); - } else { - $success = 1; - $products = Tools::getValue($this->table . 'Box'); - if (is_array($products) && ($count = count($products))) { - if ((int) (ini_get('max_execution_time')) < round($count * 1.5)) { - ini_set('max_execution_time', round($count * 1.5)); - } - foreach ($products as $id_product) { - $product = new Product((int) $id_product); - if (!count($this->errors)) { - if ($product->delete()) { - PrestaShopLogger::addLog(sprintf($this->l('%s deletion', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int) $product->id, true, (int) $this->context->employee->id); - } else { - $success = false; - } - } else { - $success = 0; - } - } - } - if ($success) { - $id_category = (int) Tools::getValue('id_category'); - $category_url = empty($id_category) ? '' : '&id_category=' . (int) $id_category; - $this->redirect_after = self::$currentIndex . '&conf=2&token=' . $this->token . $category_url; - } else { - $this->errors[] = Tools::displayError('An error occurred while deleting this selection.'); - } - } - } else { - $this->errors[] = Tools::displayError('You must select at least one element to delete.'); - } - } else { - $this->errors[] = Tools::displayError('You do not have permission to delete this.'); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processProductAttribute() - { - if (!Combination::isFeatureActive() || !Tools::getValue('attribute_combination_list')) { - return; - } - if (Validate::isLoadedObject($product = $this->object)) { - if ($this->isProductFieldUpdated('attribute_price') && (!Tools::getIsset('attribute_price') || Tools::getIsset('attribute_price') == null)) { - $this->errors[] = Tools::displayError('The price attribute is required.'); - } - if (!Tools::getIsset('attribute_combination_list') || Tools::isEmpty(Tools::getValue('attribute_combination_list'))) { - $this->errors[] = Tools::displayError('You must add at least one attribute.'); - } - $array_checks = [ - 'reference' => 'isReference', - 'supplier_reference' => 'isReference', - 'location' => 'isReference', - 'ean13' => 'isEan13', - 'upc' => 'isUpc', - 'wholesale_price' => 'isPrice', - 'price' => 'isPrice', - 'ecotax' => 'isPrice', - 'quantity' => 'isInt', - 'weight' => 'isUnsignedFloat', - 'unit_price_impact' => 'isPrice', - 'default_on' => 'isBool', - 'minimal_quantity' => 'isUnsignedInt', - 'available_date' => 'isDateFormat', - ]; - foreach ($array_checks as $property => $check) { - if (Tools::getValue('attribute_' . $property) !== false && !call_user_func(['Validate', $check], Tools::getValue('attribute_' . $property))) { - $this->errors[] = sprintf(Tools::displayError('Field %s is not valid'), $property); - } - } - if (!count($this->errors)) { - if (!isset($_POST['attribute_wholesale_price'])) { - $_POST['attribute_wholesale_price'] = 0; - } - if (!isset($_POST['attribute_price_impact'])) { - $_POST['attribute_price_impact'] = 0; - } - if (!isset($_POST['attribute_weight_impact'])) { - $_POST['attribute_weight_impact'] = 0; - } - if (!isset($_POST['attribute_ecotax'])) { - $_POST['attribute_ecotax'] = 0; - } - if (Tools::getValue('attribute_default')) { - $product->deleteDefaultAttributes(); - } - if (($id_product_attribute = (int) Tools::getValue('id_product_attribute')) || ($id_product_attribute = $product->productAttributeExists(Tools::getValue('attribute_combination_list'), false, null, true, true))) { - if ($this->access('edit')) { - if ($this->isProductFieldUpdated('available_date_attribute') && (Tools::getValue('available_date_attribute') != '' && !Validate::isDateFormat(Tools::getValue('available_date_attribute')))) { - $this->errors[] = Tools::displayError('Invalid date format.'); - } else { - $product->updateAttribute( - (int) $id_product_attribute, - $this->isProductFieldUpdated('attribute_wholesale_price') ? Tools::getValue('attribute_wholesale_price') : null, - $this->isProductFieldUpdated('attribute_price_impact') ? Tools::getValue('attribute_price') * Tools::getValue('attribute_price_impact') : null, - $this->isProductFieldUpdated('attribute_weight_impact') ? Tools::getValue('attribute_weight') * Tools::getValue('attribute_weight_impact') : null, - $this->isProductFieldUpdated('attribute_unit_impact') ? Tools::getValue('attribute_unity') * Tools::getValue('attribute_unit_impact') : null, - $this->isProductFieldUpdated('attribute_ecotax') ? Tools::getValue('attribute_ecotax') : null, - Tools::getValue('id_image_attr'), - Tools::getValue('attribute_reference'), - Tools::getValue('attribute_ean13'), - $this->isProductFieldUpdated('attribute_default') ? Tools::getValue('attribute_default') : null, - Tools::getValue('attribute_location'), - Tools::getValue('attribute_upc'), - $this->isProductFieldUpdated('attribute_minimal_quantity') ? Tools::getValue('attribute_minimal_quantity') : null, - $this->isProductFieldUpdated('available_date_attribute') ? Tools::getValue('available_date_attribute') : null, - false - ); - StockAvailable::setProductOutOfStock((int) $product->id, $product->out_of_stock, null, (int) $id_product_attribute); - } - } else { - $this->errors[] = Tools::displayError('You do not have permission to add this.'); - } - } else { - if ($this->access('add')) { - if ($product->productAttributeExists(Tools::getValue('attribute_combination_list'))) { - $this->errors[] = Tools::displayError('This combination already exists.'); - } else { - $id_product_attribute = $product->addCombinationEntity( - Tools::getValue('attribute_wholesale_price'), - Tools::getValue('attribute_price') * Tools::getValue('attribute_price_impact'), - Tools::getValue('attribute_weight') * Tools::getValue('attribute_weight_impact'), - Tools::getValue('attribute_unity') * Tools::getValue('attribute_unit_impact'), - Tools::getValue('attribute_ecotax'), - 0, - Tools::getValue('id_image_attr'), - Tools::getValue('attribute_reference'), - null, - Tools::getValue('attribute_ean13'), - Tools::getValue('attribute_default'), - Tools::getValue('attribute_location'), - Tools::getValue('attribute_upc'), - Tools::getValue('attribute_minimal_quantity'), - [], - Tools::getValue('available_date_attribute'), - Tools::getValue('attribute_isbn') - ); - StockAvailable::setProductOutOfStock((int) $product->id, $product->out_of_stock, null, (int) $id_product_attribute); - } - } else { - $this->errors[] = Tools::displayError('You do not have permission to') . '
    ' . Tools::displayError('edit here.'); - } - } - if (!count($this->errors)) { - $combination = new Combination((int) $id_product_attribute); - $combination->setAttributes(Tools::getValue('attribute_combination_list')); - $id_images = Tools::getValue('id_image_attr'); - if (!empty($id_images)) { - $combination->setImages($id_images); - } - $product->checkDefaultAttributes(); - if (Tools::getValue('attribute_default')) { - Product::updateDefaultAttribute((int) $product->id); - if (isset($id_product_attribute)) { - $product->cache_default_attribute = (int) $id_product_attribute; - } - if ($available_date = Tools::getValue('available_date_attribute')) { - $product->setAvailableDate($available_date); - } else { - $product->setAvailableDate(); - } - } - } - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processFeatures() - { - if (!Feature::isFeatureActive()) { - return; - } - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $product->deleteFeatures(); - $languages = Language::getLanguages(false); - foreach ($_POST as $key => $val) { - if (preg_match('/^feature_([0-9]+)_value/i', $key, $match)) { - if ($val) { - $product->addFeaturesToDB($match[1], $val); - } else { - if ($default_value = $this->checkFeatures($languages, $match[1])) { - $id_value = $product->addFeaturesToDB($match[1], 0, 1); - foreach ($languages as $language) { - if ($cust = Tools::getValue('custom_' . $match[1] . '_' . (int) $language['id_lang'])) { - $product->addFeaturesCustomToDB($id_value, (int) $language['id_lang'], $cust); - } else { - $product->addFeaturesCustomToDB($id_value, (int) $language['id_lang'], $default_value); - } - } - } - } - } - } - } else { - $this->errors[] = Tools::displayError('A product must be created before adding features.'); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processPricesModification() - { - $id_specific_prices = Tools::getValue('spm_id_specific_price'); - $id_combinations = Tools::getValue('spm_id_product_attribute'); - $id_shops = Tools::getValue('spm_id_shop'); - $id_currencies = Tools::getValue('spm_id_currency'); - $id_countries = Tools::getValue('spm_id_country'); - $id_groups = Tools::getValue('spm_id_group'); - $id_customers = Tools::getValue('spm_id_customer'); - $prices = Tools::getValue('spm_price'); - $from_quantities = Tools::getValue('spm_from_quantity'); - $reductions = Tools::getValue('spm_reduction'); - $reduction_types = Tools::getValue('spm_reduction_type'); - $froms = Tools::getValue('spm_from'); - $tos = Tools::getValue('spm_to'); - foreach ($id_specific_prices as $key => $id_specific_price) { - if ($reduction_types[$key] == 'percentage' && ((float) $reductions[$key] <= 0 || (float) $reductions[$key] > 100)) { - $this->errors[] = Tools::displayError('The submitted reduction value (0-100) is out-of-range.'); - } elseif ($this->_validateSpecificPrice($id_shops[$key], $id_currencies[$key], $id_countries[$key], $id_groups[$key], $id_customers[$key], $prices[$key], $from_quantities[$key], $reductions[$key], $reduction_types[$key], $froms[$key], $tos[$key], $id_combinations[$key])) { - $specific_price = new SpecificPrice((int) ($id_specific_price)); - $specific_price->id_shop = (int) $id_shops[$key]; - $specific_price->id_product_attribute = (int) $id_combinations[$key]; - $specific_price->id_currency = (int) ($id_currencies[$key]); - $specific_price->id_country = (int) ($id_countries[$key]); - $specific_price->id_group = (int) ($id_groups[$key]); - $specific_price->id_customer = (int) $id_customers[$key]; - $specific_price->price = (float) ($prices[$key]); - $specific_price->from_quantity = (int) ($from_quantities[$key]); - $specific_price->reduction = (float) ($reduction_types[$key] == 'percentage' ? ($reductions[$key] / 100) : $reductions[$key]); - $specific_price->reduction_type = !$reductions[$key] ? 'amount' : $reduction_types[$key]; - $specific_price->from = !$froms[$key] ? '0000-00-00 00:00:00' : $froms[$key]; - $specific_price->to = !$tos[$key] ? '0000-00-00 00:00:00' : $tos[$key]; - if (!$specific_price->update()) { - $this->errors[] = Tools::displayError('An error occurred while updating the specific price.'); - } - } - } - if (!count($this->errors)) { - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) (Tools::getValue('id_product')) . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&update' . $this->table . '&action=Prices&token=' . $this->token; - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processPriceAddition() - { - if (!Tools::getIsset('submitPriceAddition')) { - return; - } - $id_product = Tools::getValue('id_product'); - $id_product_attribute = Tools::getValue('sp_id_product_attribute'); - $id_shop = Tools::getValue('sp_id_shop'); - $id_currency = Tools::getValue('sp_id_currency'); - $id_country = Tools::getValue('sp_id_country'); - $id_group = Tools::getValue('sp_id_group'); - $id_customer = Tools::getValue('sp_id_customer'); - $price = Tools::getValue('leave_bprice') ? '-1' : Tools::getValue('sp_price'); - $from_quantity = Tools::getValue('sp_from_quantity'); - $reduction = (float) (Tools::getValue('sp_reduction')); - $reduction_tax = Tools::getValue('sp_reduction_tax'); - $reduction_type = !$reduction ? 'amount' : Tools::getValue('sp_reduction_type'); - $reduction_type = $reduction_type == '-' ? 'amount' : $reduction_type; - $from = Tools::getValue('sp_from'); - if (!$from) { - $from = '0000-00-00 00:00:00'; - } - $to = Tools::getValue('sp_to'); - if (!$to) { - $to = '0000-00-00 00:00:00'; - } - if (($price == '-1') && ((float) $reduction == '0')) { - $this->errors[] = Tools::displayError('No reduction value has been submitted.'); - } elseif (strtotime($to) < strtotime($from)) { - $this->errors[] = Tools::displayError('Invalid date range'); - } elseif ($reduction_type == 'percentage' && ((float) $reduction <= 0 || (float) $reduction > 100)) { - $this->errors[] = Tools::displayError('The submitted reduction value (0-100) is out-of-range.'); - } elseif ($this->_validateSpecificPrice($id_shop, $id_currency, $id_country, $id_group, $id_customer, $price, $from_quantity, $reduction, $reduction_type, $from, $to, $id_product_attribute)) { - $specificPrice = new SpecificPrice(); - $specificPrice->id_product = (int) $id_product; - $specificPrice->id_product_attribute = (int) $id_product_attribute; - $specificPrice->id_shop = (int) $id_shop; - $specificPrice->id_currency = (int) ($id_currency); - $specificPrice->id_country = (int) ($id_country); - $specificPrice->id_group = (int) ($id_group); - $specificPrice->id_customer = (int) $id_customer; - $specificPrice->price = (float) ($price); - $specificPrice->from_quantity = (int) ($from_quantity); - $specificPrice->reduction = (float) ($reduction_type == 'percentage' ? $reduction / 100 : $reduction); - $specificPrice->reduction_tax = $reduction_tax; - $specificPrice->reduction_type = $reduction_type; - $specificPrice->from = $from; - $specificPrice->to = $to; - if (!$specificPrice->add()) { - $this->errors[] = Tools::displayError('An error occurred while updating the specific price.'); - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessDeleteSpecificPrice() - { - if ($this->access('delete')) { - $id_specific_price = (int) Tools::getValue('id_specific_price'); - if (!$id_specific_price || !Validate::isUnsignedId($id_specific_price)) { - $error = Tools::displayError('The specific price ID is invalid.'); - } else { - $specificPrice = new SpecificPrice((int) $id_specific_price); - if (!$specificPrice->delete()) { - $error = Tools::displayError('An error occurred while attempting to delete the specific price.'); - } - } - } else { - $error = Tools::displayError('You do not have permission to delete this.'); - } - if (isset($error)) { - $json = [ - 'status' => 'error', - 'message' => $error, - ]; - } else { - $json = [ - 'status' => 'ok', - 'message' => $this->_conf[1], - ]; - } - die(json_encode($json)); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processSpecificPricePriorities() - { - if (!($obj = $this->loadObject())) { - return; - } - if (!$priorities = Tools::getValue('specificPricePriority')) { - $this->errors[] = Tools::displayError('Please specify priorities.'); - } elseif (Tools::isSubmit('specificPricePriorityToAll')) { - $sfContainer = SymfonyContainer::getInstance(); - $specificPricePriorityUpdater = $sfContainer->get(SpecificPricePriorityUpdater::class); - - try { - $specificPricePriorityUpdater->updateDefaultPriorities($priorities); - $this->confirmations[] = 'The price rule has successfully updated'; - } catch (CoreException $e) { - $this->errors[] = $this->trans('An error occurred while updating priorities.', [], 'Admin.Catalog.Notification'); - } - } elseif (!SpecificPrice::setSpecificPriority((int) $obj->id, $priorities)) { - $this->errors[] = Tools::displayError('An error occurred while setting priorities.'); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processCustomizationConfiguration() - { - $product = $this->object; - $current_customization = $product->getCustomizationFieldIds(); - $files_count = 0; - $text_count = 0; - if (is_array($current_customization)) { - foreach ($current_customization as $field) { - if ($field['type'] == 1) { - ++$text_count; - } else { - ++$files_count; - } - } - } - if (!$product->createLabels((int) $product->uploadable_files - $files_count, (int) $product->text_fields - $text_count)) { - $this->errors[] = Tools::displayError('An error occurred while creating customization fields.'); - } - if (!count($this->errors) && !$product->updateLabels()) { - $this->errors[] = Tools::displayError('An error occurred while updating customization fields.'); - } - $product->customizable = ($product->uploadable_files > 0 || $product->text_fields > 0) ? 1 : 0; - if (($product->uploadable_files != $files_count || $product->text_fields != $text_count) && !count($this->errors) && !$product->update()) { - $this->errors[] = Tools::displayError('An error occurred while updating the custom configuration.'); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processProductCustomization() - { - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - foreach ($_POST as $field => $value) { - if (strncmp($field, 'label_', 6) == 0 && !Validate::isLabel($value)) { - $this->errors[] = Tools::displayError('The label fields defined are invalid.'); - } - } - if (empty($this->errors) && !$product->updateLabels()) { - $this->errors[] = Tools::displayError('An error occurred while updating customization fields.'); - } - if (empty($this->errors)) { - $this->confirmations[] = $this->l('Update successful'); - } - } else { - $this->errors[] = Tools::displayError('A product must be created before adding customization.'); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processPosition() - { - if (!Validate::isLoadedObject($object = $this->loadObject())) { - $this->errors[] = Tools::displayError('An error occurred while updating the status for an object.') . - ' ' . $this->table . ' ' . Tools::displayError('(cannot load object)'); - } elseif (!$object->updatePosition((int) Tools::getValue('way'), (int) Tools::getValue('position'))) { - $this->errors[] = Tools::displayError('Failed to update the position.'); - } else { - $category = new Category((int) Tools::getValue('id_category')); - if (Validate::isLoadedObject($category)) { - Hook::exec('actionCategoryUpdate', ['category' => $category]); - } - $this->redirect_after = self::$currentIndex . '&' . $this->table . 'Orderby=position&' . $this->table . 'Orderway=asc&action=Customization&conf=5' . (($id_category = (Tools::getIsset('id_category') ? (int) Tools::getValue('id_category') : '')) ? ('&id_category=' . $id_category) : '') . '&token=' . Tools::getAdminTokenLite('AdminProducts'); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initProcess() - { - if (Tools::isSubmit('submitAddproductAndStay') || Tools::isSubmit('submitAddproduct')) { - $this->id_object = (int) Tools::getValue('id_product'); - $this->object = new Product($this->id_object); - if ($this->isTabSubmitted('Informations') && $this->object->is_virtual && (int) Tools::getValue('type_product') != 2) { - if ($id_product_download = (int) ProductDownload::getIdFromIdProduct($this->id_object)) { - $product_download = new ProductDownload($id_product_download); - if (!$product_download->deleteFile($id_product_download)) { - $this->errors[] = Tools::displayError('Cannot delete file'); - } - } - } - } - if (Tools::getValue('deleteVirtualProduct')) { - if ($this->access('delete')) { - $this->action = 'deleteVirtualProduct'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to delete this.'); - } - } elseif (Tools::isSubmit('submitAddProductAndPreview')) { - $this->display = 'edit'; - $this->action = 'save'; - if (Tools::getValue('id_product')) { - $this->id_object = Tools::getValue('id_product'); - $this->object = new Product((int) Tools::getValue('id_product')); - } - } elseif (Tools::isSubmit('submitAttachments')) { - if ($this->access('edit')) { - $this->action = 'attachments'; - $this->tab_display = 'attachments'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::getIsset('duplicate' . $this->table)) { - if ($this->access('add')) { - $this->action = 'duplicate'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to add this.'); - } - } elseif (Tools::getValue('id_image') && Tools::getValue('ajax')) { - if ($this->access('edit')) { - $this->action = 'image'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('submitProductAttribute')) { - if ($this->access('edit')) { - $this->action = 'productAttribute'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('submitFeatures') || Tools::isSubmit('submitFeaturesAndStay')) { - if ($this->access('edit')) { - $this->action = 'features'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('submitPricesModification')) { - if ($this->access('add')) { - $this->action = 'pricesModification'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to add this.'); - } - } elseif (Tools::isSubmit('deleteSpecificPrice')) { - if ($this->access('delete')) { - $this->action = 'deleteSpecificPrice'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to delete this.'); - } - } elseif (Tools::isSubmit('submitSpecificPricePriorities')) { - if ($this->access('edit')) { - $this->action = 'specificPricePriorities'; - $this->tab_display = 'prices'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('submitCustomizationConfiguration')) { - if ($this->access('edit')) { - $this->action = 'customizationConfiguration'; - $this->tab_display = 'customization'; - $this->display = 'edit'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('submitProductCustomization')) { - if ($this->access('edit')) { - $this->action = 'productCustomization'; - $this->tab_display = 'customization'; - $this->display = 'edit'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('id_product')) { - $post_max_size = Tools::getMaxUploadSize(Configuration::get('PS_LIMIT_UPLOAD_FILE_VALUE') * 1024 * 1024); - if ($post_max_size && isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] && $_SERVER['CONTENT_LENGTH'] > $post_max_size) { - $this->errors[] = sprintf(Tools::displayError('The uploaded file exceeds the "Maximum size for a downloadable product" set in preferences (%1$dMB) or the post_max_size/ directive in php.ini (%2$dMB).'), number_format((Configuration::get('PS_LIMIT_UPLOAD_FILE_VALUE'))), ($post_max_size / 1024 / 1024)); - } - } - if (!$this->action) { - parent::initProcess(); - } else { - $this->id_object = (int) Tools::getValue($this->identifier); - } - if (isset($this->available_tabs[Tools::getValue('key_tab')])) { - $this->tab_display = Tools::getValue('key_tab'); - } - if (!$this->tab_display && $this->action) { - if (in_array($this->action, array_keys($this->available_tabs))) { - $this->tab_display = $this->action; - } - } - if (!$this->tab_display) { - if (in_array($this->default_tab, $this->available_tabs)) { - $this->tab_display = $this->default_tab; - } else { - $this->tab_display = key($this->available_tabs); - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function postProcess() - { - if (!$this->redirect_after) { - parent::postProcess(); - } - if ($this->display == 'edit' || $this->display == 'add') { - $this->addJqueryUI([ - 'ui.core', - 'ui.widget', - ]); - $this->addjQueryPlugin([ - 'autocomplete', - 'tablednd', - 'thickbox', - 'ajaxfileupload', - 'date', - 'tagify', - 'select2', - 'validate', - ]); - $this->addJS([ - _PS_JS_DIR_ . 'admin/products.js', - _PS_JS_DIR_ . 'admin/attributes.js', - _PS_JS_DIR_ . 'admin/price.js', - _PS_JS_DIR_ . 'tiny_mce/tiny_mce.js', - _PS_JS_DIR_ . 'admin/tinymce.inc.js', - _PS_JS_DIR_ . 'admin/dnd.js', - _PS_JS_DIR_ . 'jquery/ui/jquery.ui.progressbar.min.js', - _PS_JS_DIR_ . 'vendor/spin.js', - _PS_JS_DIR_ . 'vendor/ladda.js', - ]); - $this->addJS(_PS_JS_DIR_ . 'jquery/plugins/select2/select2_locale_' . $this->context->language->iso_code . '.js'); - $this->addJS(_PS_JS_DIR_ . 'jquery/plugins/validate/localization/messages_' . $this->context->language->iso_code . '.js'); - $this->addCSS([ - _PS_JS_DIR_ . 'jquery/plugins/timepicker/jquery-ui-timepicker-addon.css', - ]); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessDeleteProductAttribute() - { - if (!Combination::isFeatureActive()) { - return; - } - if ($this->access('delete')) { - $id_product = (int) Tools::getValue('id_product'); - $id_product_attribute = (int) Tools::getValue('id_product_attribute'); - if ($id_product && Validate::isUnsignedId($id_product) && Validate::isLoadedObject($product = new Product($id_product))) { - $product->deleteAttributeCombination((int) $id_product_attribute); - $product->checkDefaultAttributes(); - if (!$product->hasAttributes()) { - $product->cache_default_attribute = 0; - $product->update(); - } else { - Product::updateDefaultAttribute($id_product); - } - $json = [ - 'status' => 'ok', - 'message' => $this->_conf[1], - 'id_product_attribute' => (int) $id_product_attribute, - ]; - } else { - $json = [ - 'status' => 'error', - 'message' => $this->l('You cannot delete this attribute.'), - ]; - } - } else { - $json = [ - 'status' => 'error', - 'message' => $this->l('You do not have permission to delete this.'), - ]; - } - die(json_encode($json)); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessDefaultProductAttribute() - { - if ($this->access('edit')) { - if (!Combination::isFeatureActive()) { - return; - } - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $product->deleteDefaultAttributes(); - $product->setDefaultAttribute((int) Tools::getValue('id_product_attribute')); - $json = [ - 'status' => 'ok', - 'message' => $this->_conf[4], - ]; - } else { - $json = [ - 'status' => 'error', - 'message' => $this->l('You cannot make this the default attribute.'), - ]; - } - die(json_encode($json)); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessEditProductAttribute() - { - if ($this->access('edit')) { - $id_product = (int) Tools::getValue('id_product'); - $id_product_attribute = (int) Tools::getValue('id_product_attribute'); - if ($id_product && Validate::isUnsignedId($id_product) && Validate::isLoadedObject($product = new Product((int) $id_product))) { - $combinations = $product->getAttributeCombinationsById($id_product_attribute, $this->context->language->id); - foreach ($combinations as $key => $combination) { - $combinations[$key]['attributes'][] = [$combination['group_name'], $combination['attribute_name'], $combination['id_attribute']]; - } - die(json_encode($combinations)); - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxPreProcess() - { - if (Tools::getIsset('update' . $this->table) && Tools::getIsset('id_' . $this->table)) { - $this->display = 'edit'; - $this->action = Tools::getValue('action'); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessUpdateProductImageShopAsso() - { - $id_product = Tools::getValue('id_product'); - if (($id_image = Tools::getValue('id_image')) && ($id_shop = (int) Tools::getValue('id_shop'))) { - if (Tools::getValue('active') == 'true') { - $res = Db::getInstance()->execute('INSERT INTO ' . _DB_PREFIX_ . 'image_shop (`id_image`, `id_shop`, `cover`) VALUES(' . (int) $id_image . ', ' . (int) $id_shop . ', \'0\')'); - } else { - $res = Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'image_shop WHERE `id_image` = ' . (int) $id_image . ' AND `id_shop` = ' . (int) $id_shop); - } - } - $count_cover_image = Db::getInstance()->getValue(' - SELECT COUNT(*) FROM ' . _DB_PREFIX_ . 'image i - INNER JOIN ' . _DB_PREFIX_ . 'image_shop ish ON (i.id_image = ish.id_image AND ish.id_shop = ' . (int) $id_shop . ') - WHERE i.cover = 1 AND `id_product` = ' . (int) $id_product); - $id_image = Db::getInstance()->getValue(' - SELECT i.`id_image` FROM ' . _DB_PREFIX_ . 'image i - INNER JOIN ' . _DB_PREFIX_ . 'image_shop ish ON (i.id_image = ish.id_image AND ish.id_shop = ' . (int) $id_shop . ') - WHERE `id_product` = ' . (int) $id_product); - if ($count_cover_image < 1) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image i SET i.cover = 1 WHERE i.id_image = ' . (int) $id_image . ' AND i.`id_product` = ' . (int) $id_product . ' LIMIT 1'); - } - if ($count_cover_image > 1) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image i SET i.cover = 0 WHERE i.id_image <> ' . (int) $id_image . ' AND i.`id_product` = ' . (int) $id_product); - } - $count_cover_image_shop = Db::getInstance()->getValue(' - SELECT COUNT(*) - FROM ' . _DB_PREFIX_ . 'image_shop ish - INNER JOIN ' . _DB_PREFIX_ . 'image i ON (i.id_image = ish.id_image AND i.`id_product` = ' . (int) $id_product . ') - WHERE ish.id_shop = ' . (int) $id_shop . ' AND ish.cover = 1'); - if ($count_cover_image_shop < 1) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image_shop ish SET ish.cover = 1 WHERE ish.id_image = ' . (int) $id_image . ' AND ish.id_shop = ' . (int) $id_shop . ' LIMIT 1'); - } - if ($count_cover_image_shop > 1) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image_shop ish SET ish.cover = 0 WHERE ish.id_image <> ' . (int) $id_image . ' AND ish.cover = 1 AND ish.id_shop = ' . (int) $id_shop . ' LIMIT ' . (int) ($count_cover_image_shop - 1)); - } - if ($res) { - $this->jsonConfirmation($this->_conf[27]); - } else { - $this->jsonError(Tools::displayError('An error occurred while attempting to associate this image with your shop. ')); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessUpdateImagePosition() - { - $res = false; - if ($json = Tools::getValue('json')) { - $res = true; - $json = stripslashes($json); - $images = json_decode($json, true); - foreach ($images as $id => $position) { - $img = new Image((int) $id); - $img->position = (int) $position; - $res &= $img->update(); - } - } - if ($res) { - $this->jsonConfirmation($this->_conf[25]); - } else { - $this->jsonError(Tools::displayError('An error occurred while attempting to move this picture.')); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessUpdateCover() - { - Image::deleteCover((int) Tools::getValue('id_product')); - $img = new Image((int) Tools::getValue('id_image')); - $img->cover = 1; - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . (int) $img->id_product . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $img->id_product . '_' . $this->context->shop->id . '.jpg'); - if ($img->update()) { - $this->jsonConfirmation($this->_conf[26]); - } else { - $this->jsonError(Tools::displayError('An error occurred while attempting to move this picture.')); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessDeleteProductImage() - { - $this->display = 'content'; - $res = true; - - $image = new Image((int) Tools::getValue('id_image')); - $this->content['id'] = $image->id; - $res &= $image->delete(); - if (!Image::getCover($image->id_product)) { - $res &= Db::getInstance()->execute(' - UPDATE `' . _DB_PREFIX_ . 'image_shop` image_shop, ' . _DB_PREFIX_ . 'image i - SET image_shop.`cover` = 1, - i.cover = 1 - WHERE image_shop.`id_image` = (SELECT id_image FROM - (SELECT image_shop.id_image - FROM ' . _DB_PREFIX_ . 'image i' . - Shop::addSqlAssociation('image', 'i') . ' - WHERE i.id_product =' . (int) $image->id_product . ' LIMIT 1 - ) tmpImage) - AND id_shop=' . (int) $this->context->shop->id . ' - AND i.id_image = image_shop.id_image - '); - } - if (file_exists(_PS_TMP_IMG_DIR_ . 'product_' . $image->id_product . '.jpg')) { - $res &= @unlink(_PS_TMP_IMG_DIR_ . 'product_' . $image->id_product . '.jpg'); - } - if (file_exists(_PS_TMP_IMG_DIR_ . 'product_mini_' . $image->id_product . '_' . $this->context->shop->id . '.jpg')) { - $res &= @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . $image->id_product . '_' . $this->context->shop->id . '.jpg'); - } - if ($res) { - $this->jsonConfirmation($this->_conf[7]); - } else { - $this->jsonError(Tools::displayError('An error occurred while attempting to delete the product image.')); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _validateSpecificPrice($id_shop, $id_currency, $id_country, $id_group, $id_customer, $price, $from_quantity, $reduction, $reduction_type, $from, $to, $id_combination = 0) - { - if (!Validate::isUnsignedId($id_shop) || !Validate::isUnsignedId($id_currency) || !Validate::isUnsignedId($id_country) || !Validate::isUnsignedId($id_group) || !Validate::isUnsignedId($id_customer)) { - $this->errors[] = Tools::displayError('Wrong IDs'); - } elseif ((!isset($price) && !isset($reduction)) || (isset($price) && !Validate::isNegativePrice($price)) || (isset($reduction) && !Validate::isPrice($reduction))) { - $this->errors[] = Tools::displayError('Invalid price/discount amount'); - } elseif (!Validate::isUnsignedInt($from_quantity)) { - $this->errors[] = Tools::displayError('Invalid quantity'); - } elseif ($reduction && !Validate::isReductionType($reduction_type)) { - $this->errors[] = Tools::displayError('Please select a discount type (amount or percentage).'); - } elseif ($from && $to && (!Validate::isDateFormat($from) || !Validate::isDateFormat($to))) { - $this->errors[] = Tools::displayError('The from/to date is invalid.'); - } elseif (SpecificPrice::exists((int) $this->object->id, $id_combination, $id_shop, $id_group, $id_country, $id_currency, $id_customer, $from_quantity, $from, $to, false)) { - $this->errors[] = Tools::displayError('A specific price already exists for these parameters.'); - } else { - return true; - } - - return false; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function checkFeatures($languages, $feature_id) - { - $rules = call_user_func(['FeatureValue', 'getValidationRules'], 'FeatureValue'); - $feature = Feature::getFeature((int) Configuration::get('PS_LANG_DEFAULT'), $feature_id); - foreach ($languages as $language) { - if ($val = Tools::getValue('custom_' . $feature_id . '_' . $language['id_lang'])) { - $current_language = new Language($language['id_lang']); - if (Tools::strlen($val) > $rules['sizeLang']['value']) { - $this->errors[] = sprintf( - Tools::displayError('The name for feature %1$s is too long in %2$s.'), - ' ' . $feature['name'] . '', - $current_language->name - ); - } elseif (!call_user_func(['Validate', $rules['validateLang']['value']], $val)) { - $this->errors[] = sprintf( - Tools::displayError('A valid name required for feature. %1$s in %2$s.'), - ' ' . $feature['name'] . '', - $current_language->name - ); - } - if (count($this->errors)) { - return 0; - } - if ($language['id_lang'] == Configuration::get('PS_LANG_DEFAULT')) { - return $val; - } - } - } - - return 0; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function addProductImage($product, $method = 'auto') - { - if ($id_image = (int) Tools::getValue('id_image')) { - $image = new Image((int) $id_image); - if (!Validate::isLoadedObject($image)) { - $this->errors[] = Tools::displayError('An error occurred while loading the object image.'); - } else { - if (($cover = Tools::getValue('cover')) == 1) { - Image::deleteCover($product->id); - } - $image->cover = $cover; - $this->validateRules('Image'); - $this->copyFromPost($image, 'image'); - if (count($this->errors) || !$image->update()) { - $this->errors[] = Tools::displayError('An error occurred while updating the image.'); - } elseif (isset($_FILES['image_product']['tmp_name']) && $_FILES['image_product']['tmp_name'] != null) { - $this->copyImage($product->id, $image->id, $method); - } - } - } - if (isset($image) && Validate::isLoadedObject($image) && !file_exists(_PS_PRODUCT_IMG_DIR_ . $image->getExistingImgPath() . '.' . $image->image_format)) { - $image->delete(); - } - if (count($this->errors)) { - return false; - } - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . $product->id . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . $product->id . '_' . $this->context->shop->id . '.jpg'); - - return (isset($id_image) && is_int($id_image) && $id_image) ? $id_image : false; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function copyImage($id_product, $id_image, $method = 'auto') - { - if (!isset($_FILES['image_product']['tmp_name'])) { - return false; - } - if ($error = ImageManager::validateUpload($_FILES['image_product'])) { - $this->errors[] = $error; - } else { - $image = new Image($id_image); - if (!$new_path = $image->getPathForCreation()) { - $this->errors[] = Tools::displayError('An error occurred while attempting to create a new folder.'); - } - if (!($tmpName = tempnam(_PS_TMP_IMG_DIR_, 'PS')) || !move_uploaded_file($_FILES['image_product']['tmp_name'], $tmpName)) { - $this->errors[] = Tools::displayError('An error occurred during the image upload process.'); - } elseif (!ImageManager::resize($tmpName, $new_path . '.' . $image->image_format)) { - $this->errors[] = Tools::displayError('An error occurred while copying the image.'); - } elseif ($method == 'auto') { - $imagesTypes = ImageType::getImagesTypes('products'); - foreach ($imagesTypes as $k => $image_type) { - if (!ImageManager::resize($tmpName, $new_path . '-' . stripslashes($image_type['name']) . '.' . $image->image_format, $image_type['width'], $image_type['height'], $image->image_format)) { - $this->errors[] = Tools::displayError('An error occurred while copying this image:') . ' ' . stripslashes($image_type['name']); - } - } - } - @unlink($tmpName); - Hook::exec('actionWatermark', ['id_image' => $id_image, 'id_product' => $id_product]); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function updateAssoShop($id_object) - { - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processAdd() - { - $this->checkProduct(); - if (!empty($this->errors)) { - $this->display = 'add'; - - return false; - } - $this->object = new $this->className(); - $this->_removeTaxFromEcotax(); - $this->copyFromPost($this->object, $this->table); - if ($this->object->add()) { - PrestaShopLogger::addLog(sprintf($this->l('%s addition', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int) $this->object->id, true, (int) $this->context->employee->id); - $this->addCarriers($this->object); - $this->updateAccessories($this->object); - $this->updatePackItems($this->object); - $this->updateDownloadProduct($this->object); - if (empty($this->errors)) { - $languages = Language::getLanguages(false); - if ($this->isProductFieldUpdated('category_box') && !$this->object->updateCategories(Tools::getValue('categoryBox'))) { - $this->errors[] = Tools::displayError('An error occurred while linking the object.') . ' ' . $this->table . ' ' . Tools::displayError('To categories'); - } elseif (!$this->updateTags($languages, $this->object)) { - $this->errors[] = Tools::displayError('An error occurred while adding tags.'); - } else { - Hook::exec('actionProductAdd', ['id_product' => (int) $this->object->id, 'product' => $this->object]); - if (in_array($this->object->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $this->object->id); - } - } - $this->object->setGroupReduction(); - if (Tools::isSubmit('submitAddProductAndPreview')) { - $this->redirect_after = $this->getPreviewUrl($this->object); - } - if ($this->display == 'edit') { - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) $this->object->id - . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') - . '&updateproduct&conf=3&key_tab=' . Tools::safeOutput(Tools::getValue('key_tab')) . '&token=' . $this->token; - } else { - $this->redirect_after = self::$currentIndex - . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') - . '&conf=3&token=' . $this->token; - } - } else { - $this->object->delete(); - $this->display = 'edit'; - } - } else { - $this->errors[] = Tools::displayError('An error occurred while creating an object.') . ' ' . $this->table . ''; - } - - return $this->object; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function isTabSubmitted($tab_name) - { - if (!is_array($this->submitted_tabs)) { - $this->submitted_tabs = Tools::getValue('submitted_tabs'); - } - if (is_array($this->submitted_tabs) && in_array($tab_name, $this->submitted_tabs)) { - return true; - } - - return false; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processStatus() - { - $this->loadObject(true); - if (!Validate::isLoadedObject($this->object)) { - return false; - } - if (($error = $this->object->validateFields(false, true)) !== true) { - $this->errors[] = $error; - } - if (($error = $this->object->validateFieldsLang(false, true)) !== true) { - $this->errors[] = $error; - } - if (count($this->errors)) { - return false; - } - $res = parent::processStatus(); - - return $res; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processUpdate() - { - $existing_product = $this->object; - $this->checkProduct(); - if (!empty($this->errors)) { - $this->display = 'edit'; - - return false; - } - $id = (int) Tools::getValue('id_' . $this->table); - - if (isset($id) && !empty($id)) { - $object = new $this->className((int) $id); - $this->object = $object; - if (Validate::isLoadedObject($object)) { - $this->_removeTaxFromEcotax(); - $product_type_before = $object->getType(); - $this->copyFromPost($object, $this->table); - $object->indexed = 0; - if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP) { - $object->setFieldsToUpdate((array) Tools::getValue('multishop_check', [])); - } - if ($this->context->shop->getContext() == Shop::CONTEXT_SHOP && !$object->isAssociatedToShop()) { - $is_associated_to_shop = false; - $combinations = Product::getProductAttributesIds($object->id); - if ($combinations) { - foreach ($combinations as $id_combination) { - $combination = new Combination((int) $id_combination['id_product_attribute']); - $default_combination = new Combination((int) $id_combination['id_product_attribute'], null, (int) $this->object->id_shop_default); - $def = ObjectModel::getDefinition($default_combination); - foreach ($def['fields'] as $field_name => $row) { - $combination->$field_name = ObjectModel::formatValue($default_combination->$field_name, $def['fields'][$field_name]['type']); - } - $combination->save(); - } - } - } else { - $is_associated_to_shop = true; - } - if ($object->update()) { - if (Shop::getContext() == Shop::CONTEXT_SHOP && !$existing_product->isAssociatedToShop($this->context->shop->id)) { - $out_of_stock = StockAvailable::outOfStock($existing_product->id, $existing_product->id_shop_default); - StockAvailable::setProductOutOfStock((int) $this->object->id, $out_of_stock, $this->context->shop->id); - } - PrestaShopLogger::addLog(sprintf($this->l('%s modification', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int) $this->object->id, true, (int) $this->context->employee->id); - if (in_array($this->context->shop->getContext(), [Shop::CONTEXT_SHOP, Shop::CONTEXT_ALL])) { - if ($this->isTabSubmitted('Shipping')) { - $this->addCarriers(); - } - if ($this->isTabSubmitted('Associations')) { - $this->updateAccessories($object); - } - if ($this->isTabSubmitted('Suppliers')) { - $this->processSuppliers(); - } - if ($this->isTabSubmitted('Features')) { - $this->processFeatures(); - } - if ($this->isTabSubmitted('Combinations')) { - $this->processProductAttribute(); - } - if ($this->isTabSubmitted('Prices')) { - $this->processPriceAddition(); - $this->processSpecificPricePriorities(); - } - if ($this->isTabSubmitted('Customization')) { - $this->processCustomizationConfiguration(); - } - if ($this->isTabSubmitted('Attachments')) { - $this->processAttachments(); - } - if ($this->isTabSubmitted('Images')) { - $this->processImageLegends(); - } - $this->updatePackItems($object); - $this->updateDownloadProduct($object, 1); - $this->updateTags(Language::getLanguages(false), $object); - if ($this->isProductFieldUpdated('category_box') && !$object->updateCategories(Tools::getValue('categoryBox'))) { - $this->errors[] = Tools::displayError('An error occurred while linking the object.') . ' ' . $this->table . ' ' . Tools::displayError('To categories'); - } - } - - if (empty($this->errors)) { - if (in_array($object->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $object->id); - } - if (Tools::isSubmit('submitAddProductAndPreview')) { - $this->redirect_after = $this->getPreviewUrl($object); - } else { - if ($this->display == 'edit') { - $this->confirmations[] = $this->l('Update successful'); - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) $this->object->id - . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') - . '&updateproduct&conf=4&key_tab=' . Tools::safeOutput(Tools::getValue('key_tab')) . '&token=' . $this->token; - } else { - $this->redirect_after = self::$currentIndex . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&conf=4&token=' . $this->token; - } - } - } else { - $this->display = 'edit'; - } - } else { - if (!$is_associated_to_shop && $combinations) { - foreach ($combinations as $id_combination) { - $combination = new Combination((int) $id_combination['id_product_attribute']); - $combination->delete(); - } - } - $this->errors[] = Tools::displayError('An error occurred while updating an object.') . ' ' . $this->table . ' (' . Db::getInstance()->getMsgError() . ')'; - } - } else { - $this->errors[] = Tools::displayError('An error occurred while updating an object.') . ' ' . $this->table . ' (' . Tools::displayError('The object cannot be loaded. ') . ')'; - } - - return $object; - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function checkProduct() - { - $className = 'Product'; - - $rules = call_user_func([$this->className, 'getValidationRules'], $this->className); - $default_language = new Language((int) Configuration::get('PS_LANG_DEFAULT')); - $languages = Language::getLanguages(false); - foreach ($rules['required'] as $field) { - if (!$this->isProductFieldUpdated($field)) { - continue; - } - if (($value = Tools::getValue($field)) == false && $value != '0') { - if (Tools::getValue('id_' . $this->table) && $field == 'passwd') { - continue; - } - $this->errors[] = sprintf( - Tools::displayError('The %s field is required.'), - call_user_func([$className, 'displayFieldName'], $field, $className) - ); - } - } - foreach ($rules['requiredLang'] as $fieldLang) { - if ($this->isProductFieldUpdated($fieldLang, $default_language->id) && !Tools::getValue($fieldLang . '_' . $default_language->id)) { - $this->errors[] = sprintf( - Tools::displayError('This %1$s field is required at least in %2$s'), - call_user_func([$className, 'displayFieldName'], $fieldLang, $className), - $default_language->name - ); - } - } - foreach ($rules['size'] as $field => $maxLength) { - if ($this->isProductFieldUpdated($field) && ($value = Tools::getValue($field)) && Tools::strlen($value) > $maxLength) { - $this->errors[] = sprintf( - Tools::displayError('The %1$s field is too long (%2$d chars max).'), - call_user_func([$className, 'displayFieldName'], $field, $className), - $maxLength - ); - } - } - if (Tools::getIsset('description_short') && $this->isProductFieldUpdated('description_short')) { - $saveShort = Tools::getValue('description_short'); - $_POST['description_short'] = strip_tags(Tools::getValue('description_short')); - } - $limit = (int) Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT'); - if ($limit <= 0) { - $limit = 400; - } - foreach ($languages as $language) { - if ($this->isProductFieldUpdated('description_short', $language['id_lang']) && ($value = Tools::getValue('description_short_' . $language['id_lang']))) { - if (Tools::strlen(strip_tags($value)) > $limit) { - $this->errors[] = sprintf( - Tools::displayError('This %1$s field (%2$s) is too long: %3$d chars max (current count %4$d).'), - call_user_func([$className, 'displayFieldName'], 'description_short'), - $language['name'], - $limit, - Tools::strlen(strip_tags($value)) - ); - } - } - } - foreach ($rules['sizeLang'] as $fieldLang => $maxLength) { - foreach ($languages as $language) { - $value = Tools::getValue($fieldLang . '_' . $language['id_lang']); - if ($value && Tools::strlen($value) > $maxLength) { - $this->errors[] = sprintf( - Tools::displayError('The %1$s field is too long (%2$d chars max).'), - call_user_func([$className, 'displayFieldName'], $fieldLang, $className), - $maxLength - ); - } - } - } - if ($this->isProductFieldUpdated('description_short') && isset($_POST['description_short'])) { - $_POST['description_short'] = $saveShort; - } - foreach ($rules['validate'] as $field => $function) { - if ($this->isProductFieldUpdated($field) && ($value = Tools::getValue($field))) { - $res = true; - if (Tools::strtolower($function) == 'iscleanhtml') { - if (!Validate::$function($value, (int) Configuration::get('PS_ALLOW_HTML_IFRAME'))) { - $res = false; - } - } elseif (!Validate::$function($value)) { - $res = false; - } - if (!$res) { - $this->errors[] = sprintf( - Tools::displayError('The %s field is invalid.'), - call_user_func([$className, 'displayFieldName'], $field, $className) - ); - } - } - } - foreach ($rules['validateLang'] as $fieldLang => $function) { - foreach ($languages as $language) { - if ($this->isProductFieldUpdated($fieldLang, $language['id_lang']) && ($value = Tools::getValue($fieldLang . '_' . $language['id_lang']))) { - if (!Validate::$function($value, (int) Configuration::get('PS_ALLOW_HTML_IFRAME'))) { - $this->errors[] = sprintf( - Tools::displayError('The %1$s field (%2$s) is invalid.'), - call_user_func([$className, 'displayFieldName'], $fieldLang, $className), - $language['name'] - ); - } - } - } - } - if ($this->isProductFieldUpdated('id_category_default') && (!Tools::isSubmit('categoryBox') || !count(Tools::getValue('categoryBox')))) { - $this->errors[] = $this->l('Products must be in at least one category.'); - } - if ($this->isProductFieldUpdated('id_category_default') && (!is_array(Tools::getValue('categoryBox')) || !in_array(Tools::getValue('id_category_default'), Tools::getValue('categoryBox')))) { - $this->errors[] = $this->l('This product must be in the default category.'); - } - foreach ($languages as $language) { - if ($value = Tools::getValue('tags_' . $language['id_lang'])) { - if (!Validate::isTagsList($value)) { - $this->errors[] = sprintf( - Tools::displayError('The tags list (%s) is invalid.'), - $language['name'] - ); - } - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function isProductFieldUpdated($field, $id_lang = null) - { - static $is_activated = null; - if (null === $is_activated) { - $is_activated = Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP && $this->id_object; - } - if (!$is_activated) { - return true; - } - if (null === $id_lang) { - return !empty($_POST['multishop_check'][$field]); - } else { - return !empty($_POST['multishop_check'][$field][$id_lang]); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _removeTaxFromEcotax() - { - if ($ecotax = Tools::getValue('ecotax')) { - $_POST['ecotax'] = Tools::ps_round($ecotax / (1 + Tax::getProductEcotaxRate() / 100), 6); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _applyTaxToEcotax($product) - { - if ($product->ecotax) { - $product->ecotax = Tools::ps_round($product->ecotax * (1 + Tax::getProductEcotaxRate() / 100), 2); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function updateDownloadProduct($product, $edit = 0) - { - if ((int) Tools::getValue('is_virtual_file') == 1) { - if (isset($_FILES['virtual_product_file_uploader']) && $_FILES['virtual_product_file_uploader']['size'] > 0) { - $virtual_product_filename = ProductDownload::getNewFilename(); - $helper = new HelperUploader('virtual_product_file_uploader'); - $helper->setPostMaxSize(Tools::getOctets(ini_get('upload_max_filesize'))) - ->setSavePath(_PS_DOWNLOAD_DIR_)->upload($_FILES['virtual_product_file_uploader'], $virtual_product_filename); - } else { - $virtual_product_filename = Tools::getValue('virtual_product_filename', ProductDownload::getNewFilename()); - } - $product->setDefaultAttribute(0); //reset cache_default_attribute - if (Tools::getValue('virtual_product_expiration_date') && !Validate::isDate(Tools::getValue('virtual_product_expiration_date'))) { - if (!Tools::getValue('virtual_product_expiration_date')) { - $this->errors[] = Tools::displayError('The expiration-date attribute is required.'); - - return false; - } - } - if ($edit == 1) { - $id_product_download = (int) ProductDownload::getIdFromIdProduct((int) $product->id); - if (!$id_product_download) { - $id_product_download = (int) Tools::getValue('virtual_product_id'); - } - } else { - $id_product_download = Tools::getValue('virtual_product_id'); - } - $is_shareable = Tools::getValue('virtual_product_is_shareable'); - $virtual_product_name = Tools::getValue('virtual_product_name'); - $virtual_product_nb_days = Tools::getValue('virtual_product_nb_days'); - $virtual_product_nb_downloable = Tools::getValue('virtual_product_nb_downloable'); - $virtual_product_expiration_date = Tools::getValue('virtual_product_expiration_date'); - $download = new ProductDownload((int) $id_product_download); - $download->id_product = (int) $product->id; - $download->display_filename = $virtual_product_name; - $download->filename = $virtual_product_filename; - $download->date_add = date('Y-m-d H:i:s'); - $download->date_expiration = $virtual_product_expiration_date ? $virtual_product_expiration_date . ' 23:59:59' : ''; - $download->nb_days_accessible = (int) $virtual_product_nb_days; - $download->nb_downloadable = (int) $virtual_product_nb_downloable; - $download->active = 1; - $download->is_shareable = (int) $is_shareable; - if ($download->save()) { - return true; - } - } else { - if ($edit == 1) { - $id_product_download = (int) ProductDownload::getIdFromIdProduct((int) $product->id); - if (!$id_product_download) { - $id_product_download = (int) Tools::getValue('virtual_product_id'); - } - } else { - $id_product_download = ProductDownload::getIdFromIdProduct($product->id); - } - if (!empty($id_product_download)) { - $product_download = new ProductDownload((int) $id_product_download); - $product_download->date_expiration = date('Y-m-d H:i:s', time() - 1); - $product_download->active = 0; - - return $product_download->save(); - } - } - - return false; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function updateAccessories($product) - { - $product->deleteAccessories(); - if ($accessories = Tools::getValue('inputAccessories')) { - $accessories_id = array_unique(explode('-', $accessories)); - if (count($accessories_id)) { - array_pop($accessories_id); - $product->changeAccessories($accessories_id); - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function updateTags($languages, $product) - { - $tag_success = true; - - if (!Tag::deleteTagsForProduct((int) $product->id)) { - $this->errors[] = Tools::displayError('An error occurred while attempting to delete previous tags.'); - } - - foreach ($languages as $language) { - if ($value = Tools::getValue('tags_' . $language['id_lang'])) { - $tag_success &= Tag::addTags($language['id_lang'], (int) $product->id, $value); - } - } - if (!$tag_success) { - $this->errors[] = Tools::displayError('An error occurred while adding tags.'); - } - - return $tag_success; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initContent($token = null) - { - if ($this->display == 'edit' || $this->display == 'add') { - $this->fields_form = []; - if (substr($this->tab_display, 0, 6) == 'Module') { - $this->tab_display_module = strtolower(substr($this->tab_display, 6, Tools::strlen($this->tab_display) - 6)); - $this->tab_display = 'Modules'; - } - if (method_exists($this, 'initForm' . $this->tab_display)) { - $this->tpl_form = strtolower($this->tab_display) . '.tpl'; - } - if ($this->ajax) { - $this->content_only = true; - } else { - $product_tabs = []; - if (!method_exists($this, 'initForm' . $this->tab_display)) { - $this->tab_display = $this->default_tab; - } - foreach ($this->available_tabs as $product_tab => $value) { - $product_tabs[$product_tab] = [ - 'id' => $product_tab, - 'selected' => (strtolower($product_tab) == strtolower($this->tab_display) || (isset($this->tab_display_module) && 'module' . $this->tab_display_module == Tools::strtolower($product_tab))), - 'name' => $this->available_tabs_lang[$product_tab], - 'href' => $this->context->link->getAdminLink('AdminProducts') . '&id_product=' . (int) Tools::getValue('id_product') . '&action=' . $product_tab, - ]; - } - $this->tpl_form_vars['product_tabs'] = $product_tabs; - } - } else { - if ($id_category = (int) $this->id_current_category) { - self::$currentIndex .= '&id_category=' . (int) $this->id_current_category; - } - if (!$id_category) { - $this->_defaultOrderBy = $this->identifier; - if ($this->context->cookie->{$this->table . 'Orderby'} == 'position') { - unset( - $this->context->cookie->{$this->table . 'Orderby'}, - $this->context->cookie->{$this->table . 'Orderway'} - ); - } - } - if (!$id_category) { - $id_category = Configuration::get('PS_ROOT_CATEGORY'); - } - $this->tpl_list_vars['is_category_filter'] = (bool) $this->id_current_category; - $tree = new HelperTreeCategories('categories-tree', $this->l('Filter by category')); - $tree->setAttribute('is_category_filter', (bool) $this->id_current_category) - ->setAttribute('base_url', preg_replace('#&id_category=[0-9]*#', '', self::$currentIndex) . '&token=' . $this->token) - ->setInputName('id-category') - ->setSelectedCategories([(int) $id_category]); - $this->tpl_list_vars['category_tree'] = $tree->render(); - $this->tpl_list_vars['base_url'] = preg_replace('#&id_category=[0-9]*#', '', self::$currentIndex) . '&token=' . $this->token; - } - $this->tpl_form_vars['vat_number'] = file_exists(_PS_MODULE_DIR_ . 'vatnumber/ajax.php'); - parent::initContent(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function renderKpis() - { - $time = time(); - $kpis = []; - - if (Configuration::get('PS_STOCK_MANAGEMENT')) { - $helper = new HelperKpi(); - $helper->id = 'box-products-stock'; - $helper->icon = 'icon-archive'; - $helper->color = 'color1'; - $helper->title = $this->l('Out of stock items', null, null, false); - if (ConfigurationKPI::get('PERCENT_PRODUCT_OUT_OF_STOCK') !== false) { - $helper->value = ConfigurationKPI::get('PERCENT_PRODUCT_OUT_OF_STOCK'); - } - $helper->source = $this->context->link->getAdminLink('AdminStats') . '&ajax=1&action=getKpi&kpi=percent_product_out_of_stock'; - $helper->tooltip = $this->l('X% of your products for sale are out of stock.', null, null, false); - $helper->refresh = (bool) (ConfigurationKPI::get('PERCENT_PRODUCT_OUT_OF_STOCK_EXPIRE') < $time); - $helper->href = Context::getContext()->link->getAdminLink('AdminProducts') . '&productFilter_sav!quantity=0&productFilter_active=1&submitFilterproduct=1'; - $kpis[] = $helper->generate(); - } - $helper = new HelperKpi(); - $helper->id = 'box-avg-gross-margin'; - $helper->icon = 'icon-tags'; - $helper->color = 'color2'; - $helper->title = $this->l('Average Gross Margin', null, null, false); - if (ConfigurationKPI::get('PRODUCT_AVG_GROSS_MARGIN') !== false) { - $helper->value = ConfigurationKPI::get('PRODUCT_AVG_GROSS_MARGIN'); - } - $helper->source = $this->context->link->getAdminLink('AdminStats') . '&ajax=1&action=getKpi&kpi=product_avg_gross_margin'; - $helper->tooltip = $this->l('The gross margin is the difference between the retail price and the wholesale price, on all your products for sale.', null, null, false); - $helper->refresh = (bool) (ConfigurationKPI::get('PRODUCT_AVG_GROSS_MARGIN_EXPIRE') < $time); - $kpis[] = $helper->generate(); - $helper = new HelperKpi(); - $helper->id = 'box-8020-sales-catalog'; - $helper->icon = 'icon-beaker'; - $helper->color = 'color3'; - $helper->title = $this->l('Purchased references', null, null, false); - $helper->subtitle = $this->l('30 days', null, null, false); - if (ConfigurationKPI::get('8020_SALES_CATALOG') !== false) { - $helper->value = ConfigurationKPI::get('8020_SALES_CATALOG'); - } - $helper->source = $this->context->link->getAdminLink('AdminStats') . '&ajax=1&action=getKpi&kpi=8020_sales_catalog'; - $helper->tooltip = $this->l('X% of your references have been purchased for the past 30 days', null, null, false); - $helper->refresh = (bool) (ConfigurationKPI::get('8020_SALES_CATALOG_EXPIRE') < $time); - $moduleManagerBuilder = ModuleManagerBuilder::getInstance(); - $moduleManager = $moduleManagerBuilder->build(); - if ($moduleManager->isInstalled('statsbestproducts')) { - $helper->href = Context::getContext()->link->getAdminLink('AdminStats') . '&module=statsbestproducts&datepickerFrom=' . date('Y-m-d', strtotime('-30 days')) . '&datepickerTo=' . date('Y-m-d'); - } - $kpis[] = $helper->generate(); - $helper = new HelperKpi(); - $helper->id = 'box-disabled-products'; - $helper->icon = 'icon-off'; - $helper->color = 'color4'; - $helper->href = $this->context->link->getAdminLink('AdminProducts'); - $helper->title = $this->l('Disabled Products', null, null, false); - if (ConfigurationKPI::get('DISABLED_PRODUCTS') !== false) { - $helper->value = ConfigurationKPI::get('DISABLED_PRODUCTS'); - } - $helper->source = $this->context->link->getAdminLink('AdminStats') . '&ajax=1&action=getKpi&kpi=disabled_products'; - $helper->refresh = (bool) (ConfigurationKPI::get('DISABLED_PRODUCTS_EXPIRE') < $time); - $helper->tooltip = $this->l('X% of your products are disabled and not visible to your customers', null, null, false); - $helper->href = Context::getContext()->link->getAdminLink('AdminProducts') . '&productFilter_active=0&submitFilterproduct=1'; - $kpis[] = $helper->generate(); - $helper = new HelperKpiRow(); - $helper->kpis = $kpis; - - return $helper->generate(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function renderList() - { - $this->addRowAction('edit'); - $this->addRowAction('duplicate'); - $this->addRowAction('delete'); - - return parent::renderList(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessProductManufacturers() - { - $manufacturers = Manufacturer::getManufacturers(false, 0, true, false, false, false, true); - $jsonArray = []; - if ($manufacturers) { - foreach ($manufacturers as $manufacturer) { - $jsonArray[] = '{"optionValue": "' . (int) $manufacturer['id_manufacturer'] . '", "optionDisplay": "' . htmlspecialchars(trim($manufacturer['name'])) . '"}'; - } - } - die('[' . implode(',', $jsonArray) . ']'); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public static function recurseCategoryForInclude($id_obj, $indexedCategories, $categories, $current, $id_category = null, $id_category_default = null, $has_suite = []) - { - global $done; - static $irow; - $content = ''; - if (!$id_category) { - $id_category = (int) Configuration::get('PS_ROOT_CATEGORY'); - } - if (!isset($done[$current['infos']['id_parent']])) { - $done[$current['infos']['id_parent']] = 0; - } - ++$done[$current['infos']['id_parent']]; - $todo = count($categories[$current['infos']['id_parent']]); - $doneC = $done[$current['infos']['id_parent']]; - $level = $current['infos']['level_depth'] + 1; - $content .= ' - - - - - - ' . $id_category . ' - - '; - for ($i = 2; $i < $level; ++$i) { - $content .= ''; - } - $content .= '   - - '; - if ($level > 1) { - $has_suite[] = ($todo == $doneC ? 0 : 1); - } - if (isset($categories[$id_category])) { - foreach ($categories[$id_category] as $key => $row) { - if ($key != 'infos') { - $content .= static::recurseCategoryForInclude($id_obj, $indexedCategories, $categories, $categories[$id_category][$key], $key, $id_category_default, $has_suite); - } - } - } - - return $content; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _displayDraftWarning($active) - { - $content = '
    - ' . $this->l('Your product will be saved as a draft.') . ' - ' . $this->l('Save and preview') . ' - -
    '; - $this->tpl_form_vars['draft_warning'] = $content; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initPageHeaderToolbar() - { - if (empty($this->display)) { - $this->page_header_toolbar_btn['new_product'] = [ - 'href' => self::$currentIndex . '&addproduct&token=' . $this->token, - 'desc' => $this->l('Add new product', null, null, false), - 'icon' => 'process-icon-new', - ]; - } - if ($this->display == 'edit') { - if (($product = $this->loadObject(true)) && $product->isAssociatedToShop()) { - if ($url_preview = $this->getPreviewUrl($product)) { - $this->page_header_toolbar_btn['preview'] = [ - 'short' => $this->l('Preview', null, null, false), - 'href' => $url_preview, - 'desc' => $this->l('Preview', null, null, false), - 'target' => true, - 'class' => 'previewUrl', - ]; - } - $js = (bool) Image::getImages($this->context->language->id, (int) $product->id) ? - 'confirm_link(\'\', \'' . $this->l('This will copy the images too. If you wish to proceed, click "Yes". If not, click "No".', null, true, false) . '\', \'' . $this->l('Yes', null, true, false) . '\', \'' . $this->l('No', null, true, false) . '\', \'' . $this->context->link->getAdminLink('AdminProducts', true) . '&id_product=' . (int) $product->id . '&duplicateproduct' . '\', \'' . $this->context->link->getAdminLink('AdminProducts', true) . '&id_product=' . (int) $product->id . '&duplicateproduct&noimage=1' . '\')' - : - 'document.location = \'' . $this->context->link->getAdminLink('AdminProducts', true) . '&id_product=' . (int) $product->id . '&duplicateproduct&noimage=1' . '\''; - if ($this->access('add')) { - $this->page_header_toolbar_btn['duplicate'] = [ - 'short' => $this->l('Duplicate', null, null, false), - 'desc' => $this->l('Duplicate', null, null, false), - 'confirm' => 1, - 'js' => $js, - ]; - } - if (file_exists(_PS_MODULE_DIR_ . 'statsproduct/statsproduct.php')) { - $this->page_header_toolbar_btn['stats'] = [ - 'short' => $this->l('Statistics', null, null, false), - 'href' => $this->context->link->getAdminLink('AdminStats') . '&module=statsproduct&id_product=' . (int) $product->id, - 'desc' => $this->l('Product sales', null, null, false), - ]; - } - if ($this->access('delete')) { - $this->page_header_toolbar_btn['delete'] = [ - 'short' => $this->l('Delete', null, null, false), - 'href' => $this->context->link->getAdminLink('AdminProducts') . '&id_product=' . (int) $product->id . '&deleteproduct', - 'desc' => $this->l('Delete this product', null, null, false), - 'confirm' => 1, - 'js' => 'if (confirm(\'' . $this->l('Delete product?', null, true, false) . '\')){return true;}else{event.preventDefault();}', - ]; - } - } - } - parent::initPageHeaderToolbar(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initToolbar() - { - parent::initToolbar(); - if ($this->display == 'edit' || $this->display == 'add') { - $this->toolbar_btn['save'] = [ - 'short' => 'Save', - 'href' => '#', - 'desc' => $this->l('Save'), - ]; - $this->toolbar_btn['save-and-stay'] = [ - 'short' => 'SaveAndStay', - 'href' => '#', - 'desc' => $this->l('Save and stay'), - ]; - $this->toolbar_btn['newCombination'] = [ - 'short' => 'New combination', - 'desc' => $this->l('New combination'), - 'class' => 'toolbar-new', - ]; - } else { - $this->toolbar_btn['import'] = [ - 'href' => $this->context->link->getAdminLink('AdminImport', true) . '&import_type=products', - 'desc' => $this->l('Import'), - ]; - } - $this->context->smarty->assign('toolbar_scroll', 1); - $this->context->smarty->assign('show_toolbar', 1); - $this->context->smarty->assign('toolbar_btn', $this->toolbar_btn); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function renderForm() - { - $this->product_name = $this->object->name[$this->context->language->id]; - if (!method_exists($this, 'initForm' . $this->tab_display)) { - return; - } - $product = $this->object; - $this->context->smarty->assign('bullet_common_field', ''); - if (Shop::isFeatureActive() && $this->display == 'edit') { - if (Shop::getContext() != Shop::CONTEXT_SHOP) { - $this->context->smarty->assign([ - 'display_multishop_checkboxes' => true, - 'multishop_check' => Tools::getValue('multishop_check'), - ]); - } - if (Shop::getContext() != Shop::CONTEXT_ALL) { - $this->context->smarty->assign('bullet_common_field', ''); - $this->context->smarty->assign('display_common_field', true); - } - } - $this->tpl_form_vars['tabs_preloaded'] = $this->available_tabs; - $this->tpl_form_vars['product_type'] = (int) Tools::getValue('type_product', $product->getType()); - $this->getLanguages(); - $this->tpl_form_vars['id_lang_default'] = Configuration::get('PS_LANG_DEFAULT'); - $this->tpl_form_vars['currentIndex'] = self::$currentIndex; - $this->tpl_form_vars['display_multishop_checkboxes'] = (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP && $this->display == 'edit'); - $this->fields_form = ['']; - $this->tpl_form_vars['token'] = $this->token; - $this->tpl_form_vars['combinationImagesJs'] = $this->getCombinationImagesJs(); - $this->tpl_form_vars['PS_ALLOW_ACCENTED_CHARS_URL'] = (int) Configuration::get('PS_ALLOW_ACCENTED_CHARS_URL'); - $this->tpl_form_vars['post_data'] = json_encode($_POST); - $this->tpl_form_vars['save_error'] = !empty($this->errors); - $this->tpl_form_vars['mod_evasive'] = Tools::apacheModExists('evasive'); - $this->tpl_form_vars['mod_security'] = Tools::apacheModExists('security'); - $this->tpl_form_vars['ps_force_friendly_product'] = Configuration::get('PS_FORCE_FRIENDLY_PRODUCT'); - $this->tpl_form_vars['tinymce'] = true; - $iso = $this->context->language->iso_code; - $this->tpl_form_vars['iso'] = file_exists(_PS_CORE_DIR_ . '/js/tiny_mce/langs/' . $iso . '.js') ? $iso : 'en'; - $this->tpl_form_vars['path_css'] = _THEME_CSS_DIR_; - $this->tpl_form_vars['ad'] = __PS_BASE_URI__ . basename(_PS_ADMIN_DIR_); - if (Validate::isLoadedObject(($this->object))) { - $id_product = (int) $this->object->id; - } else { - $id_product = (int) Tools::getvalue('id_product'); - } - $this->tpl_form_vars['form_action'] = $this->context->link->getAdminLink('AdminProducts') . '&' . ($id_product ? 'id_product=' . (int) $id_product : 'addproduct'); - $this->tpl_form_vars['id_product'] = $id_product; - $upload_max_filesize = Tools::getOctets(ini_get('upload_max_filesize')); - $upload_max_filesize = ($upload_max_filesize / 1024) / 1024; - $this->tpl_form_vars['upload_max_filesize'] = $upload_max_filesize; - $this->tpl_form_vars['country_display_tax_label'] = $this->context->country->display_tax_label; - $this->tpl_form_vars['has_combinations'] = $this->object->hasAttributes(); - $this->product_exists_in_shop = true; - if ($this->display == 'edit' && Validate::isLoadedObject($product) && Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_SHOP && !$product->isAssociatedToShop($this->context->shop->id)) { - $this->product_exists_in_shop = false; - if ($this->tab_display == 'Informations') { - $this->displayWarning($this->l('Warning: The product does not exist in this shop')); - } - $default_product = new Product(); - $definition = ObjectModel::getDefinition($product); - foreach ($definition['fields'] as $field_name => $field) { - if (isset($field['shop']) && $field['shop']) { - $product->$field_name = ObjectModel::formatValue($default_product->$field_name, $field['type']); - } - } - } - if (!Validate::isLoadedObject($this->object) && Tools::getValue('id_product')) { - $this->errors[] = 'Unable to load object'; - } else { - $this->_displayDraftWarning($this->object->active); - if (!empty($this->errors)) { - $this->copyFromPost($this->object, $this->table); - } - $this->initPack($this->object); - $this->{'initForm' . $this->tab_display}($this->object); - $this->tpl_form_vars['product'] = $this->object; - if ($this->ajax) { - if (!isset($this->tpl_form_vars['custom_form'])) { - throw new PrestaShopException('custom_form empty for action ' . $this->tab_display); - } else { - return $this->tpl_form_vars['custom_form']; - } - } - } - $parent = parent::renderForm(); - $this->addJqueryPlugin(['autocomplete', 'fancybox', 'typewatch']); - - return $parent; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function getPreviewUrl(Product $product) - { - $id_lang = Configuration::get('PS_LANG_DEFAULT', null, null, Context::getContext()->shop->id); - if (!ShopUrl::getMainShopDomain()) { - return false; - } - $is_rewrite_active = (bool) Configuration::get('PS_REWRITING_SETTINGS'); - $preview_url = $this->context->link->getProductLink( - $product, - $this->getFieldValue($product, 'link_rewrite', $this->context->language->id), - Category::getLinkRewrite($this->getFieldValue($product, 'id_category_default'), $this->context->language->id), - null, - $id_lang, - (int) Context::getContext()->shop->id, - 0, - $is_rewrite_active - ); - if (!$product->active) { - $admin_dir = dirname($_SERVER['PHP_SELF']); - $admin_dir = substr($admin_dir, strrpos($admin_dir, '/') + 1); - $preview_url .= ((strpos($preview_url, '?') === false) ? '?' : '&') . 'adtoken=' . $this->token . '&ad=' . $admin_dir . '&id_employee=' . (int) $this->context->employee->id; - } - - return $preview_url; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processSuppliers() - { - if ((int) Tools::getValue('supplier_loaded') === 1 && Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $attributes = $product->getAttributesResume($this->context->language->id); - if (empty($attributes)) { - $attributes[] = [ - 'id_product_attribute' => 0, - 'attribute_designation' => '', - ]; - } - $suppliers = Supplier::getSuppliers(); - $associated_suppliers = ProductSupplier::getSupplierCollection($product->id); - $suppliers_to_associate = []; - $new_default_supplier = 0; - if (Tools::isSubmit('default_supplier')) { - $new_default_supplier = (int) Tools::getValue('default_supplier'); - } - foreach ($suppliers as $supplier) { - if (Tools::isSubmit('check_supplier_' . $supplier['id_supplier'])) { - $suppliers_to_associate[] = $supplier['id_supplier']; - } - } - foreach ($associated_suppliers as $key => $associated_supplier) { - if (!in_array($associated_supplier->id_supplier, $suppliers_to_associate)) { - $associated_supplier->delete(); - unset($associated_suppliers[$key]); - } - } - foreach ($suppliers_to_associate as $id) { - $to_add = true; - foreach ($associated_suppliers as $as) { - if ($id == $as->id_supplier) { - $to_add = false; - } - } - if ($to_add) { - $product_supplier = new ProductSupplier(); - $product_supplier->id_product = $product->id; - $product_supplier->id_product_attribute = 0; - $product_supplier->id_supplier = $id; - if ($this->context->currency->id) { - $product_supplier->id_currency = (int) $this->context->currency->id; - } else { - $product_supplier->id_currency = Currency::getDefaultCurrencyId(); - } - $product_supplier->save(); - $associated_suppliers[] = $product_supplier; - foreach ($attributes as $attribute) { - if ((int) $attribute['id_product_attribute'] > 0) { - $product_supplier = new ProductSupplier(); - $product_supplier->id_product = $product->id; - $product_supplier->id_product_attribute = (int) $attribute['id_product_attribute']; - $product_supplier->id_supplier = $id; - $product_supplier->save(); - } - } - } - } - foreach ($attributes as $attribute) { - foreach ($associated_suppliers as $supplier) { - if (Tools::isSubmit('supplier_reference_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier) || - (Tools::isSubmit('product_price_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier) && - Tools::isSubmit('product_price_currency_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier))) { - $reference = pSQL( - Tools::getValue( - 'supplier_reference_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier, - '' - ) - ); - $price = (float) str_replace( - [' ', ','], - ['', '.'], - Tools::getValue( - 'product_price_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier, - 0 - ) - ); - $price = Tools::ps_round($price, 6); - $id_currency = (int) Tools::getValue( - 'product_price_currency_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier, - 0 - ); - if ($id_currency <= 0 || (!($result = Currency::getCurrency($id_currency)) || empty($result))) { - $this->errors[] = Tools::displayError('The selected currency is not valid'); - } - $product_supplier_id = (int) ProductSupplier::getIdByProductAndSupplier($product->id, $attribute['id_product_attribute'], $supplier->id_supplier); - if (!$product_supplier_id) { - $product->addSupplierReference($supplier->id_supplier, (int) $attribute['id_product_attribute'], $reference, (float) $price, (int) $id_currency); - if ($product->id_supplier == $supplier->id_supplier) { - if ((int) $attribute['id_product_attribute'] > 0) { - $data = [ - 'supplier_reference' => pSQL($reference), - 'wholesale_price' => (float) Tools::convertPrice($price, $id_currency), - ]; - $where = ' - a.id_product = ' . (int) $product->id . ' - AND a.id_product_attribute = ' . (int) $attribute['id_product_attribute']; - ObjectModel::updateMultishopTable('Combination', $data, $where); - } else { - $product->wholesale_price = (float) Tools::convertPrice($price, $id_currency); //converted in the default currency - $product->supplier_reference = pSQL($reference); - $product->update(); - } - } - } else { - $product_supplier = new ProductSupplier($product_supplier_id); - $product_supplier->id_currency = (int) $id_currency; - $product_supplier->product_supplier_price_te = (float) $price; - $product_supplier->product_supplier_reference = pSQL($reference); - $product_supplier->update(); - } - } elseif (Tools::isSubmit('supplier_reference_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier)) { - if ((int) $attribute['id_product_attribute'] > 0) { - $product_supplier = new ProductSupplier(); - $product_supplier->id_product = $product->id; - $product_supplier->id_product_attribute = (int) $attribute['id_product_attribute']; - $product_supplier->id_supplier = $supplier->id_supplier; - $product_supplier->save(); - } - } - } - } - if ($new_default_supplier != $product->id_supplier) { - $this->object->id_supplier = $new_default_supplier; - $this->object->update(); - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormAssociations($obj) - { - $product = $obj; - $data = $this->createTemplate($this->tpl_form); - $root = Category::getRootCategory(); - $default_category = $this->context->cookie->id_category_products_filter ? $this->context->cookie->id_category_products_filter : Context::getContext()->shop->id_category; - if (!$product->id || !$product->isAssociatedToShop()) { - $selected_cat = Category::getCategoryInformation(Tools::getValue('categoryBox', [$default_category]), $this->default_form_language); - } else { - if (Tools::isSubmit('categoryBox')) { - $selected_cat = Category::getCategoryInformation(Tools::getValue('categoryBox', [$default_category]), $this->default_form_language); - } else { - $selected_cat = Product::getProductCategoriesFull($product->id, $this->default_form_language); - } - } - $data->assign('feature_shop_active', Shop::isFeatureActive()); - $helper = new HelperForm(); - if ($this->object && $this->object->id) { - $helper->id = $this->object->id; - } else { - $helper->id = null; - } - $helper->table = $this->table; - $helper->identifier = $this->identifier; - $accessories = Product::getAccessoriesLight($this->context->language->id, $product->id); - if ($post_accessories = Tools::getValue('inputAccessories')) { - $post_accessories_tab = explode('-', $post_accessories); - foreach ($post_accessories_tab as $accessory_id) { - if (!$this->haveThisAccessory($accessory_id, $accessories) && $accessory = Product::getAccessoryById($accessory_id)) { - $accessories[] = $accessory; - } - } - } - $data->assign('accessories', $accessories); - $product->manufacturer_name = Manufacturer::getNameById($product->id_manufacturer); - $categories = []; - foreach ($selected_cat as $key => $category) { - $categories[] = $key; - } - $tree = new HelperTreeCategories('associated-categories-tree', 'Associated categories'); - $tree->setTemplate('tree_associated_categories.tpl') - ->setHeaderTemplate('tree_associated_header.tpl') - ->setRootCategory($root->id) - ->setUseCheckBox(true) - ->setUseSearch(true) - ->setSelectedCategories($categories); - $data->assign(['default_category' => $default_category, - 'selected_cat_ids' => implode(',', array_keys($selected_cat)), - 'selected_cat' => $selected_cat, - 'id_category_default' => $product->getDefaultCategory(), - 'category_tree' => $tree->render(), - 'product' => $product, - 'link' => $this->context->link, - 'is_shop_context' => Shop::getContext() == Shop::CONTEXT_SHOP, - ]); - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormPrices($obj) - { - $data = $this->createTemplate($this->tpl_form); - $product = $obj; - if ($obj->id) { - $shops = Shop::getShops(); - $countries = Country::getCountries($this->context->language->id); - $groups = Group::getGroups($this->context->language->id); - $currencies = Currency::getCurrencies(); - $attributes = $obj->getAttributesGroups((int) $this->context->language->id); - $combinations = []; - foreach ($attributes as $attribute) { - $combinations[$attribute['id_product_attribute']]['id_product_attribute'] = $attribute['id_product_attribute']; - if (!isset($combinations[$attribute['id_product_attribute']]['attributes'])) { - $combinations[$attribute['id_product_attribute']]['attributes'] = ''; - } - $combinations[$attribute['id_product_attribute']]['attributes'] .= $attribute['attribute_name'] . ' - '; - $combinations[$attribute['id_product_attribute']]['price'] = $this->context->getContextLocale()->formatPrice( - Tools::convertPrice( - Product::getPriceStatic((int) $obj->id, false, $attribute['id_product_attribute']), - $this->context->currency - ), - $this->context->currency->iso_code - ); - } - foreach ($combinations as &$combination) { - $combination['attributes'] = rtrim($combination['attributes'], ' - '); - } - $data->assign( - 'specificPriceModificationForm', - $this->_displaySpecificPriceModificationForm( - $this->context->currency, - $shops, - $currencies, - $countries, - $groups - ) - ); - $data->assign('ecotax_tax_excl', $obj->ecotax); - $this->_applyTaxToEcotax($obj); - $data->assign([ - 'shops' => $shops, - 'admin_one_shop' => count($this->context->employee->getAssociatedShops()) == 1, - 'currencies' => $currencies, - 'countries' => $countries, - 'groups' => $groups, - 'combinations' => $combinations, - 'multi_shop' => Shop::isFeatureActive(), - 'link' => new Link(), - 'pack' => new Pack(), - ]); - } else { - $this->displayWarning($this->l('You must save this product before adding specific pricing')); - $product->id_tax_rules_group = (int) Product::getIdTaxRulesGroupMostUsed(); - $data->assign('ecotax_tax_excl', 0); - } - $address = new Address(); - $address->id_country = (int) $this->context->country->id; - $tax_rules_groups = TaxRulesGroup::getTaxRulesGroups(true); - $tax_rates = [ - 0 => [ - 'id_tax_rules_group' => 0, - 'rates' => [0], - 'computation_method' => 0, - ], - ]; - foreach ($tax_rules_groups as $tax_rules_group) { - $id_tax_rules_group = (int) $tax_rules_group['id_tax_rules_group']; - $tax_calculator = TaxManagerFactory::getManager($address, $id_tax_rules_group)->getTaxCalculator(); - $tax_rates[$id_tax_rules_group] = [ - 'id_tax_rules_group' => $id_tax_rules_group, - 'rates' => [], - 'computation_method' => (int) $tax_calculator->computation_method, - ]; - if (isset($tax_calculator->taxes) && count($tax_calculator->taxes)) { - foreach ($tax_calculator->taxes as $tax) { - $tax_rates[$id_tax_rules_group]['rates'][] = (float) $tax->rate; - } - } else { - $tax_rates[$id_tax_rules_group]['rates'][] = 0; - } - } - $data->assign([ - 'link' => $this->context->link, - 'currency' => $currency = $this->context->currency, - 'tax_rules_groups' => $tax_rules_groups, - 'taxesRatesByGroup' => $tax_rates, - 'ecotaxTaxRate' => Tax::getProductEcotaxRate(), - 'tax_exclude_taxe_option' => Tax::excludeTaxeOption(), - 'ps_use_ecotax' => Configuration::get('PS_USE_ECOTAX'), - ]); - $product->price = Tools::convertPrice($product->price, $this->context->currency, true, $this->context); - if ($product->unit_price_ratio != 0) { - $data->assign('unit_price', Tools::ps_round($product->price / $product->unit_price_ratio, 6)); - } else { - $data->assign('unit_price', 0); - } - $data->assign('ps_tax', Configuration::get('PS_TAX')); - $data->assign('country_display_tax_label', $this->context->country->display_tax_label); - $data->assign([ - 'currency', $this->context->currency, - 'product' => $product, - 'token' => $this->token, - ]); - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormSeo($product) - { - if (!$this->default_form_language) { - $this->getLanguages(); - } - $data = $this->createTemplate($this->tpl_form); - $context = Context::getContext(); - $rewritten_links = []; - foreach ($this->_languages as $language) { - $category = Category::getLinkRewrite((int) $product->id_category_default, (int) $language['id_lang']); - $rewritten_links[(int) $language['id_lang']] = explode( - '[REWRITE]', - $context->link->getProductLink($product, '[REWRITE]', $category, null, (int) $language['id_lang']) - ); - } - $data->assign([ - 'product' => $product, - 'languages' => $this->_languages, - 'id_lang' => $this->context->language->id, - 'ps_ssl_enabled' => Configuration::get('PS_SSL_ENABLED'), - 'curent_shop_url' => $this->context->shop->getBaseURL(), - 'default_form_language' => $this->default_form_language, - 'rewritten_links' => $rewritten_links, - ]); - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function getPackItems($product = null) - { - $pack_items = []; - if (!$product) { - $names_input = Tools::getValue('namePackItems'); - $ids_input = Tools::getValue('inputPackItems'); - if (!$names_input || !$ids_input) { - return []; - } - $ids = array_unique(explode('-', $ids_input)); - $names = array_unique(explode('¤', $names_input)); - if (!empty($ids)) { - $length = count($ids); - for ($i = 0; $i < $length; ++$i) { - if (!empty($ids[$i]) && !empty($names[$i])) { - list($pack_items[$i]['pack_quantity'], $pack_items[$i]['id']) = explode('x', $ids[$i]); - $exploded_name = explode('x', $names[$i]); - $pack_items[$i]['name'] = $exploded_name[1]; - } - } - } - } else { - $i = 0; - foreach ($product->packItems as $pack_item) { - $pack_items[$i]['id'] = $pack_item->id; - $pack_items[$i]['pack_quantity'] = $pack_item->pack_quantity; - $pack_items[$i]['name'] = $pack_item->name; - $pack_items[$i]['reference'] = $pack_item->reference; - $pack_items[$i]['id_product_attribute'] = isset($pack_item->id_pack_product_attribute) && $pack_item->id_pack_product_attribute ? $pack_item->id_pack_product_attribute : 0; - $cover = $pack_item->id_pack_product_attribute ? Product::getCombinationImageById($pack_item->id_pack_product_attribute, Context::getContext()->language->id) : Product::getCover($pack_item->id); - $pack_items[$i]['image'] = Context::getContext()->link->getImageLink($pack_item->link_rewrite, $cover['id_image'], 'home_default'); - ++$i; - } - } - - return $pack_items; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormPack($product) - { - $data = $this->createTemplate($this->tpl_form); - if (Tools::getValue('namePackItems')) { - $input_pack_items = Tools::getValue('inputPackItems'); - $input_namepack_items = Tools::getValue('namePackItems'); - $pack_items = $this->getPackItems(); - } else { - $product->packItems = Pack::getItems($product->id, $this->context->language->id); - $pack_items = $this->getPackItems($product); - $input_namepack_items = ''; - $input_pack_items = ''; - foreach ($pack_items as $pack_item) { - $input_pack_items .= $pack_item['pack_quantity'] . 'x' . $pack_item['id'] . 'x' . $pack_item['id_product_attribute'] . '-'; - $input_namepack_items .= $pack_item['pack_quantity'] . ' x ' . $pack_item['name'] . '¤'; - } - } - $data->assign([ - 'input_pack_items' => $input_pack_items, - 'input_namepack_items' => $input_namepack_items, - 'pack_items' => $pack_items, - 'product_type' => (int) Tools::getValue('type_product', $product->getType()), - ]); - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormVirtualProduct($product) - { - $data = $this->createTemplate($this->tpl_form); - $currency = $this->context->currency; - - $product_download = new ProductDownload(); - if ($id_product_download = $product_download->getIdFromIdProduct($this->getFieldValue($product, 'id'))) { - $product_download = new ProductDownload($id_product_download); - } - $product->{'productDownload'} = $product_download; - if ($product->productDownload->id && empty($product->productDownload->display_filename)) { - $this->errors[] = Tools::displayError('A file name is required in order to associate a file'); - $this->tab_display = 'VirtualProduct'; - } - - $exists_file = realpath(_PS_DOWNLOAD_DIR_) . '/' . $product->productDownload->filename; - $data->assign('product_downloaded', $product->productDownload->id); - if (!file_exists($exists_file) - && !empty($product->productDownload->display_filename) - && empty($product->cache_default_attribute)) { - $msg = sprintf( - Tools::displayError('File "%s" is missing'), - $product->productDownload->display_filename - ); - } else { - $msg = ''; - } - $virtual_product_file_uploader = new HelperUploader('virtual_product_file_uploader'); - $virtual_product_file_uploader->setMultiple(false)->setUrl( - Context::getContext()->link->getAdminLink('AdminProducts') . '&ajax=1&id_product=' . (int) $product->id - . '&action=AddVirtualProductFile' - )->setPostMaxSize(Tools::getOctets(ini_get('upload_max_filesize'))) - ->setTemplate('virtual_product.tpl'); - $data->assign([ - 'download_product_file_missing' => $msg, - 'download_dir_writable' => ProductDownload::checkWritableDir(), - 'up_filename' => (string) (Tools::getValue('virtual_product_filename')), - ]); - $product->productDownload->nb_downloadable = ($product->productDownload->id > 0) ? $product->productDownload->nb_downloadable : htmlentities(Tools::getValue('virtual_product_nb_downloable'), ENT_COMPAT, 'UTF-8'); - $product->productDownload->date_expiration = ($product->productDownload->id > 0) ? ((!empty($product->productDownload->date_expiration) && $product->productDownload->date_expiration != '0000-00-00 00:00:00') ? date('Y-m-d', strtotime($product->productDownload->date_expiration)) : '') : htmlentities(Tools::getValue('virtual_product_expiration_date'), ENT_COMPAT, 'UTF-8'); - $product->productDownload->nb_days_accessible = ($product->productDownload->id > 0) ? $product->productDownload->nb_days_accessible : htmlentities(Tools::getValue('virtual_product_nb_days'), ENT_COMPAT, 'UTF-8'); - $product->productDownload->is_shareable = $product->productDownload->id > 0 && $product->productDownload->is_shareable; - $iso_tiny_mce = $this->context->language->iso_code; - $iso_tiny_mce = (file_exists(_PS_JS_DIR_ . 'tiny_mce/langs/' . $iso_tiny_mce . '.js') ? $iso_tiny_mce : 'en'); - $data->assign([ - 'ad' => __PS_BASE_URI__ . basename(_PS_ADMIN_DIR_), - 'iso_tiny_mce' => $iso_tiny_mce, - 'product' => $product, - 'token' => $this->token, - 'currency' => $currency, - 'link' => $this->context->link, - 'is_file' => $product->productDownload->checkFile(), - 'virtual_product_file_uploader' => $virtual_product_file_uploader->render(), - ]); - $data->assign($this->tpl_form_vars); - $this->tpl_form_vars['product'] = $product; - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _getFinalPrice($specific_price, $product_price, $tax_rate) - { - return $this->object->getPrice(false, $specific_price['id_product_attribute'], 2); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _displaySpecificPriceModificationForm($defaultCurrency, $shops, $currencies, $countries, $groups) - { - if (!($obj = $this->loadObject())) { - return; - } - $content = ''; - $specific_prices = SpecificPrice::getByProductId((int) $obj->id); - $specific_price_priorities = SpecificPrice::getPriority((int) $obj->id); - $tmp = []; - foreach ($shops as $shop) { - $tmp[$shop['id_shop']] = $shop; - } - $shops = $tmp; - $tmp = []; - foreach ($currencies as $currency) { - $tmp[$currency['id_currency']] = $currency; - } - $currencies = $tmp; - $tmp = []; - foreach ($countries as $country) { - $tmp[$country['id_country']] = $country; - } - $countries = $tmp; - $tmp = []; - foreach ($groups as $group) { - $tmp[$group['id_group']] = $group; - } - $groups = $tmp; - $length_before = strlen($content); - if (is_array($specific_prices) && count($specific_prices)) { - $i = 0; - foreach ($specific_prices as $specific_price) { - $id_currency = $specific_price['id_currency'] ? $specific_price['id_currency'] : $defaultCurrency->id; - if (!isset($currencies[$id_currency])) { - continue; - } - $current_specific_currency = $currencies[$id_currency]; - if ($specific_price['reduction_type'] == 'percentage') { - $impact = '- ' . ($specific_price['reduction'] * 100) . ' %'; - } elseif ($specific_price['reduction'] > 0) { - $impact = '- ' . $this->context->getContextLocale()->formatPrice( - Tools::ps_round($specific_price['reduction'], 2), - $current_specific_currency['iso_code'] - ) . ' '; - if ($specific_price['reduction_tax']) { - $impact .= '(' . $this->l('Tax incl.') . ')'; - } else { - $impact .= '(' . $this->l('Tax excl.') . ')'; - } - } else { - $impact = '--'; - } - if ($specific_price['from'] == '0000-00-00 00:00:00' && $specific_price['to'] == '0000-00-00 00:00:00') { - $period = $this->l('Unlimited'); - } else { - $period = $this->l('From') . ' ' . ($specific_price['from'] != '0000-00-00 00:00:00' ? $specific_price['from'] : '0000-00-00 00:00:00') . '
    ' . $this->l('To') . ' ' . ($specific_price['to'] != '0000-00-00 00:00:00' ? $specific_price['to'] : '0000-00-00 00:00:00'); - } - if ($specific_price['id_product_attribute']) { - $combination = new Combination((int) $specific_price['id_product_attribute']); - $attributes = $combination->getAttributesName((int) $this->context->language->id); - $attributes_name = ''; - foreach ($attributes as $attribute) { - $attributes_name .= $attribute['name'] . ' - '; - } - $attributes_name = rtrim($attributes_name, ' - '); - } else { - $attributes_name = $this->l('All combinations'); - } - $rule = new SpecificPriceRule((int) $specific_price['id_specific_price_rule']); - $rule_name = ($rule->id ? $rule->name : '--'); - if ($specific_price['id_customer']) { - $customer = new Customer((int) $specific_price['id_customer']); - if (Validate::isLoadedObject($customer)) { - $customer_full_name = $customer->firstname . ' ' . $customer->lastname; - } - unset($customer); - } - if (!$specific_price['id_shop'] || in_array($specific_price['id_shop'], Shop::getContextListShopID())) { - $content .= ' - - ' . $rule_name . ' - ' . $attributes_name . ''; - $can_delete_specific_prices = true; - if (Shop::isFeatureActive()) { - $id_shop_sp = $specific_price['id_shop']; - $can_delete_specific_prices = (count($this->context->employee->getAssociatedShops()) > 1 && !$id_shop_sp) || $id_shop_sp; - $content .= ' - ' . ($id_shop_sp ? $shops[$id_shop_sp]['name'] : $this->l('All shops')) . ''; - } - $price = Tools::ps_round($specific_price['price'], 2); - $fixed_price = ($price == Tools::ps_round($obj->price, 2) || $specific_price['price'] == -1) ? '--' : $this->context->getContextLocale()->formatPrice($price, $current_specific_currency['iso_code']); - $content .= ' - ' . ($specific_price['id_currency'] ? $currencies[$specific_price['id_currency']]['name'] : $this->l('All currencies')) . ' - ' . ($specific_price['id_country'] ? $countries[$specific_price['id_country']]['name'] : $this->l('All countries')) . ' - ' . ($specific_price['id_group'] ? $groups[$specific_price['id_group']]['name'] : $this->l('All groups')) . ' - ' . (isset($customer_full_name) ? $customer_full_name : $this->l('All customers')) . ' - ' . $fixed_price . ' - ' . $impact . ' - ' . $period . ' - ' . $specific_price['from_quantity'] . ' - ' . ((!$rule->id && $can_delete_specific_prices) ? '' : '') . ' - '; - ++$i; - unset($customer_full_name); - } - } - } - if ($length_before === strlen($content)) { - $content .= ' - -  ' . $this->l('No specific prices.') . ' - '; - } - $content .= ' - - -
    - -
    '; - $content .= ' - - '; - if ($specific_price_priorities[0] == 'id_customer') { - unset($specific_price_priorities[0]); - } - $specific_price_priorities = array_values($specific_price_priorities); - $content .= '
    -

    ' . $this->l('Priority management') . '

    -
    - ' . $this->l('Sometimes one customer can fit into multiple price rules. Priorities allow you to define which rule applies to the customer.') . ' -
    '; - $content .= ' -
    - -
    - - - - - - - -
    -
    -
    -
    -

    - -

    -
    -
    - -
    - '; - - return $content; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _getCustomizationFieldIds($labels, $alreadyGenerated, $obj) - { - $customizableFieldIds = []; - if (isset($labels[Product::CUSTOMIZE_FILE])) { - foreach ($labels[Product::CUSTOMIZE_FILE] as $id_customization_field => $label) { - $customizableFieldIds[] = 'label_' . Product::CUSTOMIZE_FILE . '_' . (int) ($id_customization_field); - } - } - if (isset($labels[Product::CUSTOMIZE_TEXTFIELD])) { - foreach ($labels[Product::CUSTOMIZE_TEXTFIELD] as $id_customization_field => $label) { - $customizableFieldIds[] = 'label_' . Product::CUSTOMIZE_TEXTFIELD . '_' . (int) ($id_customization_field); - } - } - $j = 0; - for ($i = $alreadyGenerated[Product::CUSTOMIZE_FILE]; $i < (int) ($this->getFieldValue($obj, 'uploadable_files')); ++$i) { - $customizableFieldIds[] = 'newLabel_' . Product::CUSTOMIZE_FILE . '_' . $j++; - } - $j = 0; - for ($i = $alreadyGenerated[Product::CUSTOMIZE_TEXTFIELD]; $i < (int) ($this->getFieldValue($obj, 'text_fields')); ++$i) { - $customizableFieldIds[] = 'newLabel_' . Product::CUSTOMIZE_TEXTFIELD . '_' . $j++; - } - - return implode('¤', $customizableFieldIds); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _displayLabelField(&$label, $languages, $default_language, $type, $fieldIds, $id_customization_field) - { - foreach ($languages as $language) { - $input_value[$language['id_lang']] = (isset($label[(int) ($language['id_lang'])])) ? $label[(int) ($language['id_lang'])]['name'] : ''; - } - $required = (isset($label[(int) ($language['id_lang'])])) ? $label[(int) ($language['id_lang'])]['required'] : false; - $template = $this->context->smarty->createTemplate( - 'controllers/products/input_text_lang.tpl', - $this->context->smarty - ); - - return '
    ' - . '
    ' - . $template->assign([ - 'languages' => $languages, - 'input_name' => 'label_' . $type . '_' . (int) ($id_customization_field), - 'input_value' => $input_value, - ])->fetch() - . '
    ' - . '
    ' - . '
    ' - . '' - . '
    ' - . '
    ' - . '
    '; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _displayLabelFields(&$obj, &$labels, $languages, $default_language, $type) - { - $content = ''; - $type = (int) ($type); - $labelGenerated = [Product::CUSTOMIZE_FILE => (isset($labels[Product::CUSTOMIZE_FILE]) ? count($labels[Product::CUSTOMIZE_FILE]) : 0), Product::CUSTOMIZE_TEXTFIELD => (isset($labels[Product::CUSTOMIZE_TEXTFIELD]) ? count($labels[Product::CUSTOMIZE_TEXTFIELD]) : 0)]; - $fieldIds = $this->_getCustomizationFieldIds($labels, $labelGenerated, $obj); - if (isset($labels[$type])) { - foreach ($labels[$type] as $id_customization_field => $label) { - $content .= $this->_displayLabelField($label, $languages, $default_language, $type, $fieldIds, (int) ($id_customization_field)); - } - } - - return $content; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormCustomization($obj) - { - $data = $this->createTemplate($this->tpl_form); - if ((bool) $obj->id) { - if ($this->product_exists_in_shop) { - $labels = $obj->getCustomizationFields(); - $has_file_labels = (int) $this->getFieldValue($obj, 'uploadable_files'); - $has_text_labels = (int) $this->getFieldValue($obj, 'text_fields'); - $data->assign([ - 'obj' => $obj, - 'table' => $this->table, - 'languages' => $this->_languages, - 'has_file_labels' => $has_file_labels, - 'display_file_labels' => $this->_displayLabelFields($obj, $labels, $this->_languages, Configuration::get('PS_LANG_DEFAULT'), Product::CUSTOMIZE_FILE), - 'has_text_labels' => $has_text_labels, - 'display_text_labels' => $this->_displayLabelFields($obj, $labels, $this->_languages, Configuration::get('PS_LANG_DEFAULT'), Product::CUSTOMIZE_TEXTFIELD), - 'uploadable_files' => (int) ($this->getFieldValue($obj, 'uploadable_files') ? (int) $this->getFieldValue($obj, 'uploadable_files') : '0'), - 'text_fields' => (int) ($this->getFieldValue($obj, 'text_fields') ? (int) $this->getFieldValue($obj, 'text_fields') : '0'), - ]); - } else { - $this->displayWarning($this->l('You must save the product in this shop before adding customization.')); - } - } else { - $this->displayWarning($this->l('You must save this product before adding customization.')); - } - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormAttachments($obj) - { - if (!$this->default_form_language) { - $this->getLanguages(); - } - $data = $this->createTemplate($this->tpl_form); - $data->assign('default_form_language', $this->default_form_language); - if ((bool) $obj->id) { - if ($this->product_exists_in_shop) { - $attachment_name = []; - $attachment_description = []; - foreach ($this->_languages as $language) { - $attachment_name[$language['id_lang']] = ''; - $attachment_description[$language['id_lang']] = ''; - } - $iso_tiny_mce = $this->context->language->iso_code; - $iso_tiny_mce = (file_exists(_PS_JS_DIR_ . 'tiny_mce/langs/' . $iso_tiny_mce . '.js') ? $iso_tiny_mce : 'en'); - $attachment_uploader = new HelperUploader('attachment_file'); - $attachment_uploader->setMultiple(false)->setUseAjax(true)->setUrl( - Context::getContext()->link->getAdminLink('AdminProducts') . '&ajax=1&id_product=' . (int) $obj->id - . '&action=AddAttachment' - )->setPostMaxSize((Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE') * 1024 * 1024)) - ->setTemplate('attachment_ajax.tpl'); - $data->assign([ - 'obj' => $obj, - 'table' => $this->table, - 'ad' => __PS_BASE_URI__ . basename(_PS_ADMIN_DIR_), - 'iso_tiny_mce' => $iso_tiny_mce, - 'languages' => $this->_languages, - 'id_lang' => $this->context->language->id, - 'attach1' => Attachment::getAttachments($this->context->language->id, $obj->id, true), - 'attach2' => Attachment::getAttachments($this->context->language->id, $obj->id, false), - 'default_form_language' => (int) Configuration::get('PS_LANG_DEFAULT'), - 'attachment_name' => $attachment_name, - 'attachment_description' => $attachment_description, - 'attachment_uploader' => $attachment_uploader->render(), - ]); - } else { - $this->displayWarning($this->l('You must save the product in this shop before adding attachements.')); - } - } else { - $this->displayWarning($this->l('You must save this product before adding attachements.')); - } - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormInformations($product) - { - if (!$this->default_form_language) { - $this->getLanguages(); - } - $data = $this->createTemplate($this->tpl_form); - $currency = $this->context->currency; - $data->assign('languages', $this->_languages); - $data->assign('default_form_language', $this->default_form_language); - $data->assign('currency', $currency); - $this->object = $product; - $data->assign('product_name_redirected', Product::getProductName((int) $product->id_type_redirected, null, (int) $this->context->language->id)); - - $product_download = new ProductDownload(); - if ($id_product_download = $product_download->getIdFromIdProduct($this->getFieldValue($product, 'id'))) { - $product_download = new ProductDownload($id_product_download); - } - $product->{'productDownload'} = $product_download; - $product_props = []; - array_push( - $product_props, - 'reference', - 'ean13', - 'upc', - 'available_for_order', - 'show_price', - 'online_only', - 'id_manufacturer' - ); - array_push( - $product_props, - 'width', - 'height', - 'weight', - 'active', - 'is_virtual', - 'cache_default_attribute', - 'uploadable_files', - 'text_fields' - ); - array_push( - $product_props, - 'price', - 'wholesale_price', - 'id_tax_rules_group', - 'unit_price_ratio', - 'on_sale', - 'unity', - 'minimum_quantity', - 'additional_shipping_cost', - 'available_now', - 'available_later', - 'available_date' - ); - if (Configuration::get('PS_USE_ECOTAX')) { - array_push($product_props, 'ecotax'); - } - foreach ($product_props as $prop) { - $product->$prop = $this->getFieldValue($product, $prop); - } - $product->name['class'] = 'updateCurrentText'; - if (!$product->id || Configuration::get('PS_FORCE_FRIENDLY_PRODUCT')) { - $product->name['class'] .= ' copy2friendlyUrl'; - } - $images = Image::getImages($this->context->language->id, $product->id); - if (is_array($images)) { - foreach ($images as $k => $image) { - $images[$k]['src'] = $this->context->link->getImageLink($product->link_rewrite[$this->context->language->id], $image['id_image'], ImageType::getFormattedName('small')); - } - $data->assign('images', $images); - } - $data->assign('imagesTypes', ImageType::getImagesTypes('products')); - $product->tags = Tag::getProductTags($product->id); - $data->assign('product_type', (int) Tools::getValue('type_product', $product->getType())); - $data->assign('is_in_pack', (int) Pack::isPacked($product->id)); - $check_product_association_ajax = false; - if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_ALL) { - $check_product_association_ajax = true; - } - $iso_tiny_mce = $this->context->language->iso_code; - $iso_tiny_mce = (file_exists(_PS_ROOT_DIR_ . '/js/tiny_mce/langs/' . $iso_tiny_mce . '.js') ? $iso_tiny_mce : 'en'); - $data->assign('ad', dirname($_SERVER['PHP_SELF'])); - $data->assign('iso_tiny_mce', $iso_tiny_mce); - $data->assign('check_product_association_ajax', $check_product_association_ajax); - $data->assign('id_lang', $this->context->language->id); - $data->assign('product', $product); - $data->assign('token', $this->token); - $data->assign('currency', $currency); - $data->assign($this->tpl_form_vars); - $data->assign('link', $this->context->link); - $data->assign('PS_PRODUCT_SHORT_DESC_LIMIT', Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT') ? Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT') : 400); - $this->tpl_form_vars['product'] = $product; - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormShipping($obj) - { - $data = $this->createTemplate($this->tpl_form); - $data->assign([ - 'product' => $obj, - 'ps_dimension_unit' => Configuration::get('PS_DIMENSION_UNIT'), - 'ps_weight_unit' => Configuration::get('PS_WEIGHT_UNIT'), - 'carrier_list' => $this->getCarrierList(), - 'currency' => $this->context->currency, - 'country_display_tax_label' => $this->context->country->display_tax_label, - ]); - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function getCarrierList() - { - $carrier_list = Carrier::getCarriers($this->context->language->id, false, false, false, null, Carrier::ALL_CARRIERS); - if ($product = $this->loadObject(true)) { - $carrier_selected_list = $product->getCarriers(); - foreach ($carrier_list as &$carrier) { - foreach ($carrier_selected_list as $carrier_selected) { - if ($carrier_selected['id_reference'] == $carrier['id_reference']) { - $carrier['selected'] = true; - continue; - } - } - } - } - - return $carrier_list; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function addCarriers($product = null) - { - if (!isset($product)) { - $product = new Product((int) Tools::getValue('id_product')); - } - if (Validate::isLoadedObject($product)) { - $carriers = []; - if (Tools::getValue('selectedCarriers')) { - $carriers = Tools::getValue('selectedCarriers'); - } - $product->setCarriers($carriers); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessaddProductImage() - { - self::$currentIndex = 'index.php?tab=AdminProducts'; - $product = new Product((int) Tools::getValue('id_product')); - $legends = Tools::getValue('legend'); - if (!is_array($legends)) { - $legends = (array) $legends; - } - if (!Validate::isLoadedObject($product)) { - $files = []; - $files[0]['error'] = Tools::displayError('Cannot add image because product creation failed.'); - } - $image_uploader = new HelperImageUploader('file'); - $image_uploader->setAcceptTypes(['jpeg', 'gif', 'png', 'jpg', 'webp'])->setMaxSize($this->max_image_size); - $files = $image_uploader->process(); - foreach ($files as &$file) { - $image = new Image(); - $image->id_product = (int) ($product->id); - $image->position = Image::getHighestPosition($product->id) + 1; - foreach ($legends as $key => $legend) { - if (!empty($legend)) { - $image->legend[(int) $key] = $legend; - } - } - if (!Image::getCover($image->id_product)) { - $image->cover = 1; - } else { - $image->cover = 0; - } - if (($validate = $image->validateFieldsLang(false, true)) !== true) { - $file['error'] = Tools::displayError($validate); - } - if (isset($file['error']) && (!is_numeric($file['error']) || $file['error'] != 0)) { - continue; - } - if (!$image->add()) { - $file['error'] = Tools::displayError('Error while creating additional image'); - } else { - if (!$new_path = $image->getPathForCreation()) { - $file['error'] = Tools::displayError('An error occurred during new folder creation'); - continue; - } - $error = 0; - if (!ImageManager::resize($file['save_path'], $new_path . '.' . $image->image_format, null, null, 'jpg', false, $error)) { - switch ($error) { - case ImageManager::ERROR_FILE_NOT_EXIST: - $file['error'] = Tools::displayError('An error occurred while copying image, the file does not exist anymore.'); - break; - case ImageManager::ERROR_FILE_WIDTH: - $file['error'] = Tools::displayError('An error occurred while copying image, the file width is 0px.'); - break; - case ImageManager::ERROR_MEMORY_LIMIT: - $file['error'] = Tools::displayError('An error occurred while copying image, check your memory limit.'); - break; - default: - $file['error'] = Tools::displayError('An error occurred while copying image.'); - break; - } - continue; - } else { - $imagesTypes = ImageType::getImagesTypes('products'); - foreach ($imagesTypes as $imageType) { - if (!ImageManager::resize($file['save_path'], $new_path . '-' . stripslashes($imageType['name']) . '.' . $image->image_format, $imageType['width'], $imageType['height'], $image->image_format)) { - $file['error'] = Tools::displayError('An error occurred while copying image:') . ' ' . stripslashes($imageType['name']); - continue; - } - } - } - unlink($file['save_path']); - unset($file['save_path']); - Hook::exec('actionWatermark', ['id_image' => $image->id, 'id_product' => $product->id]); - if (!$image->update()) { - $file['error'] = Tools::displayError('Error while updating status'); - continue; - } - $shops = Shop::getContextListShopID(); - $image->associateTo($shops); - $json_shops = []; - foreach ($shops as $id_shop) { - $json_shops[$id_shop] = true; - } - $file['status'] = 'ok'; - $file['id'] = $image->id; - $file['position'] = $image->position; - $file['cover'] = $image->cover; - $file['legend'] = $image->legend; - $file['path'] = $image->getExistingImgPath(); - $file['shops'] = $json_shops; - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . (int) $product->id . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $product->id . '_' . $this->context->shop->id . '.jpg'); - } - } - die(json_encode([$image_uploader->getName() => $files])); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormImages($obj) - { - $data = $this->createTemplate($this->tpl_form); - if ((bool) $obj->id) { - if ($this->product_exists_in_shop) { - $data->assign('product', $this->loadObject()); - $shops = false; - if (Shop::isFeatureActive()) { - $shops = Shop::getShops(); - } - if ($shops) { - foreach ($shops as $key => $shop) { - if (!$obj->isAssociatedToShop($shop['id_shop'])) { - unset($shops[$key]); - } - } - } - $data->assign('shops', $shops); - $count_images = Db::getInstance()->getValue( - ' - SELECT COUNT(id_product) - FROM ' . _DB_PREFIX_ . 'image - WHERE id_product = ' . (int) $obj->id - ); - $images = Image::getImages($this->context->language->id, $obj->id); - foreach ($images as $k => $image) { - $images[$k] = new Image($image['id_image']); - } - if ($this->context->shop->getContext() == Shop::CONTEXT_SHOP) { - $current_shop_id = (int) $this->context->shop->id; - } else { - $current_shop_id = 0; - } - $languages = Language::getLanguages(true); - $image_uploader = new HelperImageUploader('file'); - $image_uploader->setMultiple(!(Tools::getUserBrowser() == 'Apple Safari' && Tools::getUserPlatform() == 'Windows')) - ->setUseAjax(true)->setUrl( - Context::getContext()->link->getAdminLink('AdminProducts') . '&ajax=1&id_product=' . (int) $obj->id - . '&action=addProductImage' - ); - $data->assign([ - 'countImages' => $count_images, - 'id_product' => (int) Tools::getValue('id_product'), - 'id_category_default' => (int) $this->_category->id, - 'images' => $images, - 'iso_lang' => $languages[0]['iso_code'], - 'token' => $this->token, - 'table' => $this->table, - 'max_image_size' => $this->max_image_size / 1024 / 1024, - 'up_filename' => (string) Tools::getValue('virtual_product_filename_attribute'), - 'currency' => $this->context->currency, - 'current_shop_id' => $current_shop_id, - 'languages' => $this->_languages, - 'default_language' => (int) Configuration::get('PS_LANG_DEFAULT'), - 'image_uploader' => $image_uploader->render(), - ]); - $type = ImageType::getByNameNType('%', 'products', 'height'); - if (isset($type['name'])) { - $data->assign('imageType', $type['name']); - } else { - $data->assign('imageType', ImageType::getFormattedName('small')); - } - } else { - $this->displayWarning($this->l('You must save the product in this shop before adding images.')); - } - } else { - $this->displayWarning($this->l('You must save this product before adding images.')); - } - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormCombinations($obj) - { - return $this->initFormAttributes($obj); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormAttributes($product) - { - $data = $this->createTemplate($this->tpl_form); - if (!Combination::isFeatureActive()) { - $this->displayWarning($this->l('This feature has been disabled. ') . - ' ' . $this->l('Performances') . ''); - } elseif (Validate::isLoadedObject($product)) { - if ($this->product_exists_in_shop) { - if ($product->is_virtual) { - $data->assign('product', $product); - $this->displayWarning($this->l('A virtual product cannot have combinations.')); - } else { - $attribute_js = []; - $attributes = ProductAttribute::getAttributes($this->context->language->id, true); - foreach ($attributes as $k => $attribute) { - $attribute_js[$attribute['id_attribute_group']][$attribute['id_attribute']] = $attribute['name']; - natsort($attribute_js[$attribute['id_attribute_group']]); - } - $currency = $this->context->currency; - $data->assign('attributeJs', $attribute_js); - $data->assign('attributes_groups', AttributeGroup::getAttributesGroups($this->context->language->id)); - $data->assign('currency', $currency); - $images = Image::getImages($this->context->language->id, $product->id); - $data->assign('tax_exclude_option', Tax::excludeTaxeOption()); - $data->assign('ps_weight_unit', Configuration::get('PS_WEIGHT_UNIT')); - $data->assign('ps_use_ecotax', Configuration::get('PS_USE_ECOTAX')); - $data->assign('field_value_unity', $this->getFieldValue($product, 'unity')); - $data->assign('minimal_quantity', $this->getFieldValue($product, 'minimal_quantity') ? $this->getFieldValue($product, 'minimal_quantity') : 1); - $data->assign('available_date', ($this->getFieldValue($product, 'available_date') != 0) ? stripslashes(htmlentities($this->getFieldValue($product, 'available_date'), $this->context->language->id)) : '0000-00-00'); - $i = 0; - $type = ImageType::getByNameNType('%', 'products', 'height'); - if (isset($type['name'])) { - $data->assign('imageType', $type['name']); - } else { - $data->assign('imageType', ImageType::getFormattedName('small')); - } - $data->assign('imageWidth', (isset($image_type['width']) ? (int) ($image_type['width']) : 64) + 25); - foreach ($images as $k => $image) { - $images[$k]['obj'] = new Image($image['id_image']); - ++$i; - } - $data->assign('images', $images); - $data->assign($this->tpl_form_vars); - $data->assign([ - 'list' => $this->renderListAttributes($product, $currency), - 'product' => $product, - 'id_category' => $product->getDefaultCategory(), - 'token_generator' => Tools::getAdminTokenLite('AdminAttributeGenerator'), - 'combination_exists' => (Shop::isFeatureActive() && (Shop::getContextShopGroup()->share_stock) && count(AttributeGroup::getAttributesGroups($this->context->language->id)) > 0 && $product->hasAttributes()), - ]); - } - } else { - $this->displayWarning($this->l('You must save the product in this shop before adding combinations.')); - } - } else { - $data->assign('product', $product); - $this->displayWarning($this->l('You must save this product before adding combinations.')); - } - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function renderListAttributes($product, $currency) - { - $this->bulk_actions = ['delete' => ['text' => $this->l('Delete selected'), 'confirm' => $this->l('Delete selected items?')]]; - $this->addRowAction('edit'); - $this->addRowAction('default'); - $this->addRowAction('delete'); - $default_class = 'highlighted'; - $this->fields_list = [ - 'attributes' => ['title' => $this->l('Attribute - value pair'), 'align' => 'left'], - 'price' => ['title' => $this->l('Impact on price'), 'type' => 'price', 'align' => 'left'], - 'weight' => ['title' => $this->l('Impact on weight'), 'align' => 'left'], - 'reference' => ['title' => $this->l('Reference'), 'align' => 'left'], - 'ean13' => ['title' => $this->l('EAN-13'), 'align' => 'left'], - 'upc' => ['title' => $this->l('UPC'), 'align' => 'left'], - ]; - $comb_array = []; - if ($product->id) { - $combinations = $product->getAttributeCombinations($this->context->language->id); - $groups = []; - if (is_array($combinations)) { - $combination_images = $product->getCombinationImages($this->context->language->id); - foreach ($combinations as $k => $combination) { - $price_to_convert = Tools::convertPrice($combination['price'], $currency); - $price = $this->context->getContextLocale()->formatPrice($price_to_convert, $currency['iso_code']); - $container['id_product_attribute'] = $combination['id_product_attribute']; - $container['attributes'][] = [$combination['group_name'], $combination['attribute_name'], $combination['id_attribute']]; - $container['wholesale_price'] = $combination['wholesale_price']; - $container['price'] = $price; - $container['weight'] = $combination['weight'] . Configuration::get('PS_WEIGHT_UNIT'); - $container['unit_impact'] = $combination['unit_price_impact']; - $container['reference'] = $combination['reference']; - $container['ean13'] = $combination['ean13']; - $container['upc'] = $combination['upc']; - $container['id_image'] = isset($combination_images[$combination['id_product_attribute']][0]['id_image']) ? $combination_images[$combination['id_product_attribute']][0]['id_image'] : 0; - $container['available_date'] = strftime($combination['available_date']); - $container['default_on'] = $combination['default_on']; - if ($combination['is_color_group']) { - $groups[$combination['id_attribute_group']] = $combination['group_name']; - } - $comb_array[$combination['id_product_attribute']] = $container; - } - } - foreach ($comb_array as $id_product_attribute => $product_attribute) { - $list = ''; - - asort($product_attribute['attributes']); - foreach ($product_attribute['attributes'] as $attribute) { - $list .= $attribute[0] . ' - ' . $attribute[1] . ', '; - } - $list = rtrim($list, ', '); - $comb_array[$id_product_attribute]['image'] = $product_attribute['id_image'] ? new Image($product_attribute['id_image']) : false; - $comb_array[$id_product_attribute]['available_date'] = $product_attribute['available_date'] != 0 ? date('Y-m-d', strtotime($product_attribute['available_date'])) : '0000-00-00'; - $comb_array[$id_product_attribute]['attributes'] = $list; - $comb_array[$id_product_attribute]['name'] = $list; - if ($product_attribute['default_on']) { - $comb_array[$id_product_attribute]['class'] = $default_class; - } - } - } - foreach ($this->actions_available as $action) { - if (!in_array($action, $this->actions) && isset($this->$action) && $this->$action) { - $this->actions[] = $action; - } - } - $helper = new HelperList(); - $helper->identifier = 'id_product_attribute'; - $helper->table_id = 'combinations-list'; - $helper->token = $this->token; - $helper->currentIndex = self::$currentIndex; - $helper->no_link = true; - $helper->simple_header = true; - $helper->show_toolbar = false; - $helper->shopLinkType = $this->shopLinkType; - $helper->actions = $this->actions; - $helper->list_skip_actions = $this->list_skip_actions; - $helper->colorOnBackground = true; - $helper->override_folder = $this->tpl_folder . 'combination/'; - - return $helper->generateList($comb_array, $this->fields_list); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormSuppliers($obj) - { - $data = $this->createTemplate($this->tpl_form); - if ($obj->id) { - if ($this->product_exists_in_shop) { - $attributes = $obj->getAttributesResume($this->context->language->id); - if (empty($attributes)) { - $attributes[] = [ - 'id_product' => $obj->id, - 'id_product_attribute' => 0, - 'attribute_designation' => '', - ]; - } - $product_designation = []; - foreach ($attributes as $attribute) { - $product_designation[$attribute['id_product_attribute']] = rtrim( - $obj->name[$this->context->language->id] . ' - ' . $attribute['attribute_designation'], - ' - ' - ); - } - $suppliers = Supplier::getSuppliers(); - $associated_suppliers = ProductSupplier::getSupplierCollection($obj->id); - $product_supplier_collection = ProductSupplier::getSupplierCollection($obj->id, false); - $default_supplier = 0; - foreach ($suppliers as &$supplier) { - $supplier['is_selected'] = false; - $supplier['is_default'] = false; - foreach ($associated_suppliers as $associated_supplier) { - if ($associated_supplier->id_supplier == $supplier['id_supplier']) { - $associated_supplier->name = $supplier['name']; - $supplier['is_selected'] = true; - if ($obj->id_supplier == $supplier['id_supplier']) { - $supplier['is_default'] = true; - $default_supplier = $supplier['id_supplier']; - } - } - } - } - $data->assign([ - 'attributes' => $attributes, - 'suppliers' => $suppliers, - 'default_supplier' => $default_supplier, - 'associated_suppliers' => $associated_suppliers, - 'associated_suppliers_collection' => $product_supplier_collection, - 'product_designation' => $product_designation, - 'currencies' => Currency::getCurrencies(), - 'product' => $obj, - 'link' => $this->context->link, - 'token' => $this->token, - 'id_default_currency' => Currency::getDefaultCurrencyId(), - ]); - } else { - $this->displayWarning($this->l('You must save the product in this shop before managing suppliers.')); - } - } else { - $this->displayWarning($this->l('You must save this product before managing suppliers.')); - } - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormFeatures($obj) - { - if (!$this->default_form_language) { - $this->getLanguages(); - } - $data = $this->createTemplate($this->tpl_form); - $data->assign('default_form_language', $this->default_form_language); - $data->assign('languages', $this->_languages); - if (!Feature::isFeatureActive()) { - $this->displayWarning($this->l('This feature has been disabled. ') . ' ' . $this->l('Performances') . ''); - } else { - if ($obj->id) { - if ($this->product_exists_in_shop) { - $features = Feature::getFeatures($this->context->language->id, (Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_SHOP)); - foreach ($features as $k => $tab_features) { - $features[$k]['current_item'] = false; - $features[$k]['val'] = []; - $custom = true; - foreach ($obj->getFeatures() as $tab_products) { - if ($tab_products['id_feature'] == $tab_features['id_feature']) { - $features[$k]['current_item'] = $tab_products['id_feature_value']; - } - } - $features[$k]['featureValues'] = FeatureValue::getFeatureValuesWithLang($this->context->language->id, (int) $tab_features['id_feature']); - if (count($features[$k]['featureValues'])) { - foreach ($features[$k]['featureValues'] as $value) { - if ($features[$k]['current_item'] == $value['id_feature_value']) { - $custom = false; - } - } - } - if ($custom) { - $features[$k]['val'] = FeatureValue::getFeatureValueLang($features[$k]['current_item']); - } - } - $data->assign('available_features', $features); - $data->assign('product', $obj); - $data->assign('link', $this->context->link); - $data->assign('default_form_language', $this->default_form_language); - } else { - $this->displayWarning($this->l('You must save the product in this shop before adding features.')); - } - } else { - $this->displayWarning($this->l('You must save this product before adding features.')); - } - } - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessProductQuantity() - { - if (!Tools::getValue('actionQty')) { - return json_encode(['error' => $this->l('Undefined action')]); - } - $product = new Product((int) Tools::getValue('id_product'), true); - switch (Tools::getValue('actionQty')) { - case 'pack_stock_type': - $value = Tools::getValue('value'); - if ($value === false) { - die(json_encode(['error' => $this->l('Undefined value')])); - } - if ((int) $value != 0 && (int) $value != 1 - && (int) $value != 2 && (int) $value != 3) { - die(json_encode(['error' => $this->l('Incorrect value')])); - } - Product::setPackStockType($product->id, $value); - break; - case 'out_of_stock': - if (Tools::getValue('value') === false) { - die(json_encode(['error' => $this->l('Undefined value')])); - } - if (!in_array((int) Tools::getValue('value'), [0, 1, 2])) { - die(json_encode(['error' => $this->l('Incorrect value')])); - } - StockAvailable::setProductOutOfStock($product->id, (int) Tools::getValue('value')); - break; - case 'set_qty': - if (Tools::getValue('value') === false || (!is_numeric(trim(Tools::getValue('value'))))) { - die(json_encode(['error' => $this->l('Undefined value')])); - } - if (Tools::getValue('id_product_attribute') === false) { - die(json_encode(['error' => $this->l('Undefined id product attribute')])); - } - StockAvailable::setQuantity($product->id, (int) Tools::getValue('id_product_attribute'), (int) Tools::getValue('value')); - Hook::exec('actionProductUpdate', ['id_product' => (int) $product->id, 'product' => $product]); - $error = ob_get_contents(); - if (!empty($error)) { - ob_end_clean(); - die(json_encode(['error' => $error])); - } - break; - } - die(json_encode(['error' => false])); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function getCombinationImagesJS() - { - if (!($obj = $this->loadObject(true))) { - return; - } - $content = 'var combination_images = new Array();'; - if (!$allCombinationImages = $obj->getCombinationImages($this->context->language->id)) { - return $content; - } - foreach ($allCombinationImages as $id_product_attribute => $combination_images) { - $i = 0; - $content .= 'combination_images[' . (int) $id_product_attribute . '] = new Array();'; - foreach ($combination_images as $combination_image) { - $content .= 'combination_images[' . (int) $id_product_attribute . '][' . $i++ . '] = ' . (int) $combination_image['id_image'] . ';'; - } - } - - return $content; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function haveThisAccessory($accessory_id, $accessories) - { - foreach ($accessories as $accessory) { - if ((int) $accessory['id_product'] == (int) $accessory_id) { - return true; - } - } - - return false; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function initPack(Product $product) - { - $this->tpl_form_vars['is_pack'] = ($product->id && Pack::isPack($product->id)) || Tools::getValue('type_product') == Product::PTYPE_PACK; - $product->packItems = Pack::getItems($product->id, $this->context->language->id); - $input_pack_items = ''; - if (Tools::getValue('inputPackItems')) { - $input_pack_items = Tools::getValue('inputPackItems'); - } else { - foreach ($product->packItems as $pack_item) { - $input_pack_items .= $pack_item->pack_quantity . 'x' . $pack_item->id . '-'; - } - } - $this->tpl_form_vars['input_pack_items'] = $input_pack_items; - $input_namepack_items = ''; - if (Tools::getValue('namePackItems')) { - $input_namepack_items = Tools::getValue('namePackItems'); - } else { - foreach ($product->packItems as $pack_item) { - $input_namepack_items .= $pack_item->pack_quantity . ' x ' . $pack_item->name . '¤'; - } - } - $this->tpl_form_vars['input_namepack_items'] = $input_namepack_items; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function initFormModules($obj) - { - $id_module = Db::getInstance()->getValue('SELECT `id_module` FROM `' . _DB_PREFIX_ . 'module` WHERE `name` = \'' . pSQL($this->tab_display_module) . '\''); - $this->tpl_form_vars['custom_form'] = Hook::exec('displayAdminProductsExtra', [], (int) $id_module); - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function updatePackItems($product) - { - Pack::deleteItems($product->id); - if (Tools::getValue('type_product') == Product::PTYPE_PACK) { - $product->setDefaultAttribute(0); //reset cache_default_attribute - $items = Tools::getValue('inputPackItems'); - $lines = array_unique(explode('-', $items)); - if (count($lines)) { - foreach ($lines as $line) { - if (!empty($line)) { - $item_id_attribute = 0; - count($array = explode('x', $line)) == 3 ? list($qty, $item_id, $item_id_attribute) = $array : list($qty, $item_id) = $array; - if ($qty > 0 && isset($item_id)) { - if (Pack::isPack((int) $item_id)) { - $this->errors[] = Tools::displayError('You can\'t add product packs into a pack'); - } elseif (!Pack::addItem((int) $product->id, (int) $item_id, (int) $qty, (int) $item_id_attribute)) { - $this->errors[] = Tools::displayError('An error occurred while attempting to add products to the pack.'); - } - } - } - } - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function getL($key) - { - $trad = [ - 'Default category:' => $this->l('Default category'), - 'Catalog:' => $this->l('Catalog'), - 'Consider changing the default category.' => $this->l('Consider changing the default category.'), - 'ID' => $this->l('ID'), - 'Name' => $this->l('Name'), - 'Mark all checkbox(es) of categories in which product is to appear' => $this->l('Mark the checkbox of each categories in which this product will appear.'), - ]; - - return $trad[$key]; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - protected function _displayUnavailableProductWarning() - { - $content = '
    - ' . $this->l('Your product will be saved as a draft.') . ' - ' . $this->l('Save and preview') . ' - -
    '; - $this->tpl_form_vars['warning_unavailable_product'] = $content; - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessCheckProductName() - { - if ($this->access('view')) { - $search = Tools::getValue('q'); - $id_lang = Tools::getValue('id_lang'); - $limit = Tools::getValue('limit'); - if (Context::getContext()->shop->getContext() != Shop::CONTEXT_SHOP) { - $result = false; - } else { - $result = Db::getInstance()->executeS(' - SELECT DISTINCT pl.`name`, p.`id_product`, pl.`id_shop` - FROM `' . _DB_PREFIX_ . 'product` p - LEFT JOIN `' . _DB_PREFIX_ . 'product_shop` ps ON (ps.id_product = p.id_product AND ps.id_shop =' . (int) Context::getContext()->shop->id . ') - LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl - ON (pl.`id_product` = p.`id_product` AND pl.`id_lang` = ' . (int) $id_lang . ') - WHERE pl.`name` LIKE "%' . pSQL($search) . '%" AND ps.id_product IS NULL - GROUP BY pl.`id_product` - LIMIT ' . (int) $limit); - } - die(json_encode($result)); - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessUpdatePositions() - { - if ($this->access('edit')) { - $way = (int) (Tools::getValue('way')); - $id_product = (int) (Tools::getValue('id_product')); - $id_category = (int) (Tools::getValue('id_category')); - $positions = Tools::getValue('product'); - if (is_array($positions)) { - foreach ($positions as $position => $value) { - $pos = explode('_', $value); - if ((isset($pos[1], $pos[2])) && ($pos[1] == $id_category && (int) $pos[2] === $id_product)) { - if ($product = new Product((int) $pos[2])) { - if (isset($position) && $product->updatePosition($way, $position)) { - $category = new Category((int) $id_category); - if (Validate::isLoadedObject($category)) { - Hook::exec('actionCategoryUpdate', ['category' => $category]); - } - echo 'ok position ' . (int) $position . ' for product ' . (int) $pos[2] . "\r\n"; - } else { - echo '{"hasError" : true, "errors" : "Can not update product ' . (int) $id_product . ' to position ' . (int) $position . ' "}'; - } - } else { - echo '{"hasError" : true, "errors" : "This product (' . (int) $id_product . ') can t be loaded"}'; - } - break; - } - } - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function ajaxProcessPublishProduct() - { - if ($this->access('edit')) { - if ($id_product = (int) Tools::getValue('id_product')) { - $bo_product_url = dirname($_SERVER['PHP_SELF']) . '/index.php?tab=AdminProducts&id_product=' . $id_product . '&updateproduct&token=' . $this->token; - if (Tools::getValue('redirect')) { - die($bo_product_url); - } - $product = new Product((int) $id_product); - if (!Validate::isLoadedObject($product)) { - die('error: invalid id'); - } - $product->active = 1; - if ($product->save()) { - die($bo_product_url); - } else { - die('error: saving'); - } - } - } - } - - /* - * module: pscsx32412 - * date: 2018-12-26 14:14:05 - * version: 1 - */ - public function processImageLegends() - { - if (Tools::getValue('key_tab') == 'Images' && Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $language_ids = Language::getIDs(false); - foreach ($_POST as $key => $val) { - if (preg_match('/^legend_([0-9]+)/i', $key, $match)) { - foreach ($language_ids as $id_lang) { - if ($val && $id_lang == $match[1]) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image_lang SET legend = "' . pSQL($val) . '" WHERE id_image IN (SELECT id_image FROM ' . _DB_PREFIX_ . 'image WHERE id_product = ' . (int) $product->id . ') AND id_lang = ' . (int) $id_lang); - } - } - } - } - } - } - - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $max_file_size = null; - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $max_image_size = null; - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $_category; - - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $tab_display; - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $tab_display_module; - - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $available_tabs = []; - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $default_tab = 'Informations'; - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $available_tabs_lang = []; - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $position_identifier = 'id_product'; - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $submitted_tabs; - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - protected $id_current_category; - - /* - * module: pscsx3241 - * date: 2018-12-26 14:14:06 - * version: 1 - */ - public function __construct() - { - $this->bootstrap = true; - $this->table = 'product'; - $this->className = 'Product'; - $this->lang = true; - $this->explicitSelect = true; - $this->bulk_actions = [ - 'delete' => [ - 'text' => $this->l('Delete selected'), - 'icon' => 'icon-trash', - 'confirm' => $this->l('Delete selected items?'), - ], - ]; - if (!Tools::getValue('id_product')) { - $this->multishop_context_group = false; - } - parent::__construct(); - $this->imageType = 'jpg'; - $this->_defaultOrderBy = 'position'; - $this->max_file_size = (int) (Configuration::get('PS_LIMIT_UPLOAD_FILE_VALUE') * 1000000); - $this->max_image_size = (int) Configuration::get('PS_PRODUCT_PICTURE_MAX_SIZE'); - $this->allow_export = true; - $this->available_tabs_lang = [ - 'Informations' => $this->l('Information'), - 'Pack' => $this->l('Pack'), - 'VirtualProduct' => $this->l('Virtual Product'), - 'Prices' => $this->l('Prices'), - 'Seo' => $this->l('SEO'), - 'Images' => $this->l('Images'), - 'Associations' => $this->l('Associations'), - 'Shipping' => $this->l('Shipping'), - 'Combinations' => $this->l('Combinations'), - 'Features' => $this->l('Features'), - 'Customization' => $this->l('Customization'), - 'Attachments' => $this->l('Attachments'), - 'Quantities' => $this->l('Quantities'), - 'Suppliers' => $this->l('Suppliers'), - ]; - $this->available_tabs = ['Quantities' => 6]; - if ($this->context->shop->getContext() != Shop::CONTEXT_GROUP) { - $this->available_tabs = array_merge($this->available_tabs, [ - 'Informations' => 0, - 'Pack' => 7, - 'VirtualProduct' => 8, - 'Prices' => 1, - 'Seo' => 2, - 'Associations' => 3, - 'Images' => 9, - 'Shipping' => 4, - 'Combinations' => 5, - 'Features' => 10, - 'Customization' => 11, - 'Attachments' => 12, - 'Suppliers' => 13, - ]); - } - asort($this->available_tabs, SORT_NUMERIC); - - $modules_list = Hook::getHookModuleExecList('displayAdminProductsExtra'); - if (is_array($modules_list) && count($modules_list) > 0) { - foreach ($modules_list as $m) { - $this->available_tabs['Module' . ucfirst($m['module'])] = 23; - $this->available_tabs_lang['Module' . ucfirst($m['module'])] = Module::getModuleName($m['module']); - } - } - if (Tools::getValue('reset_filter_category')) { - $this->context->cookie->id_category_products_filter = false; - } - if (Shop::isFeatureActive() && $this->context->cookie->id_category_products_filter) { - $category = new Category((int) $this->context->cookie->id_category_products_filter); - if (!$category->inShop()) { - $this->context->cookie->id_category_products_filter = false; - Tools::redirectAdmin($this->context->link->getAdminLink('AdminProducts')); - } - } - - if ($id_category = (int) Tools::getValue('productFilter_cl!name')) { - $this->_category = new Category((int) $id_category); - $_POST['productFilter_cl!name'] = $this->_category->name[$this->context->language->id]; - } else { - if ($id_category = (int) Tools::getValue('id_category')) { - $this->id_current_category = $id_category; - $this->context->cookie->id_category_products_filter = $id_category; - } elseif ($id_category = $this->context->cookie->id_category_products_filter) { - $this->id_current_category = $id_category; - } - if ($this->id_current_category) { - $this->_category = new Category((int) $this->id_current_category); - } else { - $this->_category = new Category(); - } - } - $join_category = false; - if (Validate::isLoadedObject($this->_category) && empty($this->_filter)) { - $join_category = true; - } - $this->_join .= ' - LEFT JOIN `' . _DB_PREFIX_ . 'image` i ON (i.`id_product` = a.`id_product`) - LEFT JOIN `' . _DB_PREFIX_ . 'stock_available` sav ON (sav.`id_product` = a.`id_product` AND sav.`id_product_attribute` = 0 - ' . StockAvailable::addSqlShopRestriction(null, null, 'sav') . ') '; - $alias = 'sa'; - $alias_image = 'image_shop'; - $id_shop = Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_SHOP ? (int) $this->context->shop->id : 'a.id_shop_default'; - $this->_join .= ' JOIN `' . _DB_PREFIX_ . 'product_shop` sa ON (a.`id_product` = sa.`id_product` AND sa.id_shop = ' . $id_shop . ') - LEFT JOIN `' . _DB_PREFIX_ . 'category_lang` cl ON (' . $alias . '.`id_category_default` = cl.`id_category` AND b.`id_lang` = cl.`id_lang` AND cl.id_shop = ' . $id_shop . ') - LEFT JOIN `' . _DB_PREFIX_ . 'shop` shop ON (shop.id_shop = ' . $id_shop . ') - LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop ON (image_shop.`id_image` = i.`id_image` AND image_shop.`cover` = 1 AND image_shop.id_shop = ' . $id_shop . ') - LEFT JOIN `' . _DB_PREFIX_ . 'product_download` pd ON (pd.`id_product` = a.`id_product`)'; - $this->_select .= 'shop.name as shopname, a.id_shop_default, '; - $this->_select .= 'MAX(' . $alias_image . '.id_image) id_image, cl.name `name_category`, ' . $alias . '.`price`, 0 AS price_final, a.`is_virtual`, pd.`nb_downloadable`, sav.`quantity` as sav_quantity, ' . $alias . '.`active`, IF(sav.`quantity`<=0, 1, 0) badge_danger'; - if ($join_category) { - $this->_join .= ' INNER JOIN `' . _DB_PREFIX_ . 'category_product` cp ON (cp.`id_product` = a.`id_product` AND cp.`id_category` = ' . (int) $this->_category->id . ') '; - $this->_select .= ' , cp.`position`, '; - } - $this->_group = 'GROUP BY ' . $alias . '.id_product'; - $this->fields_list = []; - $this->fields_list['id_product'] = [ - 'title' => $this->l('ID'), - 'align' => 'center', - 'class' => 'fixed-width-xs', - 'type' => 'int', - ]; - $this->fields_list['image'] = [ - 'title' => $this->l('Image'), - 'align' => 'center', - 'image' => 'p', - 'orderby' => false, - 'filter' => false, - 'search' => false, - ]; - $this->fields_list['name'] = [ - 'title' => $this->l('Name'), - 'filter_key' => 'b!name', - ]; - $this->fields_list['reference'] = [ - 'title' => $this->l('Reference'), - 'align' => 'left', - ]; - if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP) { - $this->fields_list['shopname'] = [ - 'title' => $this->l('Default shop'), - 'filter_key' => 'shop!name', - ]; - } else { - $this->fields_list['name_category'] = [ - 'title' => $this->l('Category'), - 'filter_key' => 'cl!name', - ]; - } - $this->fields_list['price'] = [ - 'title' => $this->l('Base price'), - 'type' => 'price', - 'align' => 'text-right', - 'filter_key' => 'a!price', - ]; - $this->fields_list['price_final'] = [ - 'title' => $this->l('Final price'), - 'type' => 'price', - 'align' => 'text-right', - 'havingFilter' => true, - 'orderby' => false, - 'search' => false, - ]; - if (Configuration::get('PS_STOCK_MANAGEMENT')) { - $this->fields_list['sav_quantity'] = [ - 'title' => $this->l('Quantity'), - 'type' => 'int', - 'align' => 'text-right', - 'filter_key' => 'sav!quantity', - 'orderby' => true, - 'badge_danger' => true, - ]; - } - $this->fields_list['active'] = [ - 'title' => $this->l('Status'), - 'active' => 'status', - 'filter_key' => $alias . '!active', - 'align' => 'text-center', - 'type' => 'bool', - 'class' => 'fixed-width-sm', - 'orderby' => false, - ]; - if ($join_category && (int) $this->id_current_category) { - $this->fields_list['position'] = [ - 'title' => $this->l('Position'), - 'filter_key' => 'cp!position', - 'align' => 'center', - 'position' => 'position', - ]; - } - } -} diff --git a/tests/Resources/modules_tests/override/DummyAdminController.php b/tests/Resources/modules_tests/override/DummyAdminController.php new file mode 100644 index 0000000000000..d8c79837996b4 --- /dev/null +++ b/tests/Resources/modules_tests/override/DummyAdminController.php @@ -0,0 +1,25 @@ + - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ -class AdminProductsController extends AdminProductsControllerCore -{ - /** @var int Max image size for upload - * As of 1.5 it is recommended to not set a limit to max image size - */ - protected $max_file_size = null; - protected $max_image_size = null; - - protected $_category; - /** - * @var string name of the tab to display - */ - protected $tab_display; - protected $tab_display_module; - - /** - * The order in the array decides the order in the list of tab. If an element's value is a number, it will be preloaded. - * The tabs are preloaded from the smallest to the highest number. - * - * @var array product tabs - */ - protected $available_tabs = []; - - protected $default_tab = 'Informations'; - - protected $available_tabs_lang = []; - - protected $position_identifier = 'id_product'; - - protected $submitted_tabs; - - protected $id_current_category; - - public function __construct() - { - $this->bootstrap = true; - $this->table = 'product'; - $this->className = 'Product'; - $this->lang = true; - $this->explicitSelect = true; - $this->bulk_actions = [ - 'delete' => [ - 'text' => $this->l('Delete selected'), - 'icon' => 'icon-trash', - 'confirm' => $this->l('Delete selected items?'), - ], - ]; - if (!Tools::getValue('id_product')) { - $this->multishop_context_group = false; - } - - parent::__construct(); - - $this->imageType = 'jpg'; - $this->_defaultOrderBy = 'position'; - $this->max_file_size = (int) (Configuration::get('PS_LIMIT_UPLOAD_FILE_VALUE') * 1000000); - $this->max_image_size = (int) Configuration::get('PS_PRODUCT_PICTURE_MAX_SIZE'); - $this->allow_export = true; - - // @since 1.5 : translations for tabs - $this->available_tabs_lang = [ - 'Informations' => $this->l('Information'), - 'Pack' => $this->l('Pack'), - 'VirtualProduct' => $this->l('Virtual Product'), - 'Prices' => $this->l('Prices'), - 'Seo' => $this->l('SEO'), - 'Images' => $this->l('Images'), - 'Associations' => $this->l('Associations'), - 'Shipping' => $this->l('Shipping'), - 'Combinations' => $this->l('Combinations'), - 'Features' => $this->l('Features'), - 'Customization' => $this->l('Customization'), - 'Attachments' => $this->l('Attachments'), - 'Quantities' => $this->l('Quantities'), - 'Suppliers' => $this->l('Suppliers'), - ]; - - $this->available_tabs = ['Quantities' => 6]; - if ($this->context->shop->getContext() != Shop::CONTEXT_GROUP) { - $this->available_tabs = array_merge($this->available_tabs, [ - 'Informations' => 0, - 'Pack' => 7, - 'VirtualProduct' => 8, - 'Prices' => 1, - 'Seo' => 2, - 'Associations' => 3, - 'Images' => 9, - 'Shipping' => 4, - 'Combinations' => 5, - 'Features' => 10, - 'Customization' => 11, - 'Attachments' => 12, - 'Suppliers' => 13, - ]); - } - - // Sort the tabs that need to be preloaded by their priority number - asort($this->available_tabs, SORT_NUMERIC); - - /* Adding tab if modules are hooked */ - $modules_list = Hook::getHookModuleExecList('displayAdminProductsExtra'); - if (is_array($modules_list) && count($modules_list) > 0) { - foreach ($modules_list as $m) { - $this->available_tabs['Module' . ucfirst($m['module'])] = 23; - $this->available_tabs_lang['Module' . ucfirst($m['module'])] = Module::getModuleName($m['module']); - } - } - - if (Tools::getValue('reset_filter_category')) { - $this->context->cookie->id_category_products_filter = false; - } - if (Shop::isFeatureActive() && $this->context->cookie->id_category_products_filter) { - $category = new Category((int) $this->context->cookie->id_category_products_filter); - if (!$category->inShop()) { - $this->context->cookie->id_category_products_filter = false; - Tools::redirectAdmin($this->context->link->getAdminLink('AdminProducts')); - } - } - /* Join categories table */ - if ($id_category = (int) Tools::getValue('productFilter_cl!name')) { - $this->_category = new Category((int) $id_category); - $_POST['productFilter_cl!name'] = $this->_category->name[$this->context->language->id]; - } else { - if ($id_category = (int) Tools::getValue('id_category')) { - $this->id_current_category = $id_category; - $this->context->cookie->id_category_products_filter = $id_category; - } elseif ($id_category = $this->context->cookie->id_category_products_filter) { - $this->id_current_category = $id_category; - } - if ($this->id_current_category) { - $this->_category = new Category((int) $this->id_current_category); - } else { - $this->_category = new Category(); - } - } - - $join_category = false; - if (Validate::isLoadedObject($this->_category) && empty($this->_filter)) { - $join_category = true; - } - - $this->_join .= ' - LEFT JOIN `' . _DB_PREFIX_ . 'image` i ON (i.`id_product` = a.`id_product`) - LEFT JOIN `' . _DB_PREFIX_ . 'stock_available` sav ON (sav.`id_product` = a.`id_product` AND sav.`id_product_attribute` = 0 - ' . StockAvailable::addSqlShopRestriction(null, null, 'sav') . ') '; - - $alias = 'sa'; - $alias_image = 'image_shop'; - - $id_shop = Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_SHOP ? (int) $this->context->shop->id : 'a.id_shop_default'; - $this->_join .= ' JOIN `' . _DB_PREFIX_ . 'product_shop` sa ON (a.`id_product` = sa.`id_product` AND sa.id_shop = ' . $id_shop . ') - LEFT JOIN `' . _DB_PREFIX_ . 'category_lang` cl ON (' . $alias . '.`id_category_default` = cl.`id_category` AND b.`id_lang` = cl.`id_lang` AND cl.id_shop = ' . $id_shop . ') - LEFT JOIN `' . _DB_PREFIX_ . 'shop` shop ON (shop.id_shop = ' . $id_shop . ') - LEFT JOIN `' . _DB_PREFIX_ . 'image_shop` image_shop ON (image_shop.`id_image` = i.`id_image` AND image_shop.`cover` = 1 AND image_shop.id_shop = ' . $id_shop . ') - LEFT JOIN `' . _DB_PREFIX_ . 'product_download` pd ON (pd.`id_product` = a.`id_product`)'; - - $this->_select .= 'shop.name as shopname, a.id_shop_default, '; - $this->_select .= 'MAX(' . $alias_image . '.id_image) id_image, cl.name `name_category`, ' . $alias . '.`price`, 0 AS price_final, a.`is_virtual`, pd.`nb_downloadable`, sav.`quantity` as sav_quantity, ' . $alias . '.`active`, IF(sav.`quantity`<=0, 1, 0) badge_danger'; - - if ($join_category) { - $this->_join .= ' INNER JOIN `' . _DB_PREFIX_ . 'category_product` cp ON (cp.`id_product` = a.`id_product` AND cp.`id_category` = ' . (int) $this->_category->id . ') '; - $this->_select .= ' , cp.`position`, '; - } - - $this->_group = 'GROUP BY ' . $alias . '.id_product'; - - $this->fields_list = []; - $this->fields_list['id_product'] = [ - 'title' => $this->l('ID'), - 'align' => 'center', - 'class' => 'fixed-width-xs', - 'type' => 'int', - ]; - $this->fields_list['image'] = [ - 'title' => $this->l('Image'), - 'align' => 'center', - 'image' => 'p', - 'orderby' => false, - 'filter' => false, - 'search' => false, - ]; - $this->fields_list['name'] = [ - 'title' => $this->l('Name'), - 'filter_key' => 'b!name', - ]; - $this->fields_list['reference'] = [ - 'title' => $this->l('Reference'), - 'align' => 'left', - ]; - - if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP) { - $this->fields_list['shopname'] = [ - 'title' => $this->l('Default shop'), - 'filter_key' => 'shop!name', - ]; - } else { - $this->fields_list['name_category'] = [ - 'title' => $this->l('Category'), - 'filter_key' => 'cl!name', - ]; - } - $this->fields_list['price'] = [ - 'title' => $this->l('Base price'), - 'type' => 'price', - 'align' => 'text-right', - 'filter_key' => 'a!price', - ]; - $this->fields_list['price_final'] = [ - 'title' => $this->l('Final price'), - 'type' => 'price', - 'align' => 'text-right', - 'havingFilter' => true, - 'orderby' => false, - 'search' => false, - ]; - - if (Configuration::get('PS_STOCK_MANAGEMENT')) { - $this->fields_list['sav_quantity'] = [ - 'title' => $this->l('Quantity'), - 'type' => 'int', - 'align' => 'text-right', - 'filter_key' => 'sav!quantity', - 'orderby' => true, - 'badge_danger' => true, - //'hint' => $this->l('This is the quantity available in the current shop/group.'), - ]; - } - - $this->fields_list['active'] = [ - 'title' => $this->l('Status'), - 'active' => 'status', - 'filter_key' => $alias . '!active', - 'align' => 'text-center', - 'type' => 'bool', - 'class' => 'fixed-width-sm', - 'orderby' => false, - ]; - - if ($join_category && (int) $this->id_current_category) { - $this->fields_list['position'] = [ - 'title' => $this->l('Position'), - 'filter_key' => 'cp!position', - 'align' => 'center', - 'position' => 'position', - ]; - } - } -} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/seo.html.twig b/tests/Resources/modules_tests/pscsx3241/override/controllers/admin/DummyAdminController.php similarity index 78% rename from src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/seo.html.twig rename to tests/Resources/modules_tests/pscsx3241/override/controllers/admin/DummyAdminController.php index 925893556a05b..d4b190df26b5f 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Panels/seo.html.twig +++ b/tests/Resources/modules_tests/pscsx3241/override/controllers/admin/DummyAdminController.php @@ -1,4 +1,5 @@ -{#** + * @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -
    -
    - {{ include('@Product/ProductPage/Forms/form_seo.html.twig', { - 'seoForm' : seoForm, - 'productId': productId - }) - }} -
    -
    + */ +declare(strict_types=1); + +use Symfony\Component\DependencyInjection\ContainerInterface; + +class DummyAdminController extends DummyAdminControllerCore +{ + /** + * @return ContainerInterface|null + */ + protected function buildContainer() + { + return null; + } +} diff --git a/tests/Resources/modules_tests/pscsx32412/override/controllers/admin/AdminProductsController.php b/tests/Resources/modules_tests/pscsx32412/override/controllers/admin/AdminProductsController.php deleted file mode 100644 index 8e95e80b9768a..0000000000000 --- a/tests/Resources/modules_tests/pscsx32412/override/controllers/admin/AdminProductsController.php +++ /dev/null @@ -1,4396 +0,0 @@ - - * @copyright Since 2007 PrestaShop SA and Contributors - * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - */ - -use PrestaShop\PrestaShop\Adapter\Product\SpecificPrice\Update\SpecificPricePriorityUpdater; -use PrestaShop\PrestaShop\Adapter\SymfonyContainer; -use PrestaShop\PrestaShop\Core\Addon\Module\ModuleManagerBuilder; -use PrestaShop\PrestaShop\Core\Exception\CoreException; - -class AdminProductsController extends AdminProductsControllerCore -{ - public static function getQuantities($echo, $tr) - { - if ((int) $tr['is_virtual'] == 1 && $tr['nb_downloadable'] == 0) { - return '∞'; - } else { - return $echo; - } - } - - public function setMedia() - { - parent::setMedia(); - - $bo_theme = ((Validate::isLoadedObject($this->context->employee) - && $this->context->employee->bo_theme) ? $this->context->employee->bo_theme : 'default'); - - if (!file_exists(_PS_BO_ALL_THEMES_DIR_ . $bo_theme . DIRECTORY_SEPARATOR - . 'template')) { - $bo_theme = 'default'; - } - - $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.iframe-transport.js'); - $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.fileupload.js'); - $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.fileupload-process.js'); - $this->addJs(__PS_BASE_URI__ . $this->admin_webpath . '/themes/' . $bo_theme . '/js/jquery.fileupload-validate.js'); - $this->addJs(__PS_BASE_URI__ . 'js/vendor/spin.js'); - $this->addJs(__PS_BASE_URI__ . 'js/vendor/ladda.js'); - } - - protected function _cleanMetaKeywords($keywords) - { - if (!empty($keywords) && $keywords != '') { - $out = []; - $words = explode(',', $keywords); - foreach ($words as $word_item) { - $word_item = trim($word_item); - if (!empty($word_item) && $word_item != '') { - $out[] = $word_item; - } - } - - return (count($out) > 0) ? implode(',', $out) : ''; - } else { - return ''; - } - } - - protected function copyFromPost(&$object, $table) - { - parent::copyFromPost($object, $table); - if (get_class($object) != 'Product') { - return; - } - - /* Additional fields */ - foreach (Language::getIDs(false) as $id_lang) { - if (isset($_POST['meta_keywords_' . $id_lang])) { - $_POST['meta_keywords_' . $id_lang] = $this->_cleanMetaKeywords(Tools::strtolower($_POST['meta_keywords_' . $id_lang])); - // preg_replace('/ *,? +,* /', ',', strtolower($_POST['meta_keywords_'.$id_lang])); - $object->meta_keywords[$id_lang] = $_POST['meta_keywords_' . $id_lang]; - } - } - $_POST['width'] = empty($_POST['width']) ? '0' : str_replace(',', '.', $_POST['width']); - $_POST['height'] = empty($_POST['height']) ? '0' : str_replace(',', '.', $_POST['height']); - $_POST['depth'] = empty($_POST['depth']) ? '0' : str_replace(',', '.', $_POST['depth']); - $_POST['weight'] = empty($_POST['weight']) ? '0' : str_replace(',', '.', $_POST['weight']); - - if (Tools::getIsset('unit_price') != null) { - $object->unit_price = str_replace(',', '.', Tools::getValue('unit_price')); - } - if (Tools::getIsset('ecotax') != null) { - $object->ecotax = str_replace(',', '.', Tools::getValue('ecotax')); - } - - if ($this->isTabSubmitted('Informations')) { - $object->available_for_order = (int) Tools::getValue('available_for_order'); - $object->show_price = $object->available_for_order ? 1 : (int) Tools::getValue('show_price'); - $object->online_only = (int) Tools::getValue('online_only'); - } - if ($this->isTabSubmitted('Prices')) { - $object->on_sale = (int) Tools::getValue('on_sale'); - } - } - - public function getList($id_lang, $orderBy = null, $orderWay = null, $start = 0, $limit = null, $id_lang_shop = null) - { - $orderByPriceFinal = (empty($orderBy) ? ($this->context->cookie->__get($this->table . 'Orderby') ? $this->context->cookie->__get($this->table . 'Orderby') : 'id_' . $this->table) : $orderBy); - $orderWayPriceFinal = (empty($orderWay) ? ($this->context->cookie->__get($this->table . 'Orderway') ? $this->context->cookie->__get($this->table . 'Orderby') : 'ASC') : $orderWay); - if ($orderByPriceFinal == 'price_final') { - $orderBy = 'id_' . $this->table; - $orderWay = 'ASC'; - } - parent::getList($id_lang, $orderBy, $orderWay, $start, $limit, $this->context->shop->id); - - /* update product quantity with attributes ...*/ - $nb = count($this->_list); - if ($this->_list) { - $context = $this->context->cloneContext(); - $context->shop = clone $context->shop; - /* update product final price */ - for ($i = 0; $i < $nb; ++$i) { - if (Context::getContext()->shop->getContext() != Shop::CONTEXT_SHOP) { - $context->shop = new Shop((int) $this->_list[$i]['id_shop_default']); - } - - // convert price with the currency from context - $this->_list[$i]['price'] = Tools::convertPrice($this->_list[$i]['price'], $this->context->currency, true, $this->context); - $this->_list[$i]['price_tmp'] = (float) Product::getPriceStatic($this->_list[$i]['id_product'], true, null, 2, null, false, true, 1, true, null, null, null, $nothing, true, true, $context); - } - } - - if ($orderByPriceFinal == 'price_final') { - if (strtolower($orderWayPriceFinal) == 'desc') { - uasort($this->_list, 'cmpPriceDesc'); - } else { - uasort($this->_list, 'cmpPriceAsc'); - } - } - for ($i = 0; $this->_list && $i < $nb; ++$i) { - $this->_list[$i]['price_final'] = $this->_list[$i]['price_tmp']; - unset($this->_list[$i]['price_tmp']); - } - } - - protected function loadObject($opt = false) - { - $result = parent::loadObject($opt); - if ($result && Validate::isLoadedObject($this->object)) { - if (Shop::getContext() == Shop::CONTEXT_SHOP && Shop::isFeatureActive() && !$this->object->isAssociatedToShop()) { - $default_product = new Product((int) $this->object->id, false, null, (int) $this->object->id_shop_default); - $def = ObjectModel::getDefinition($this->object); - foreach ($def['fields'] as $field_name => $row) { - if (is_array($default_product->$field_name)) { - foreach ($default_product->$field_name as $key => $value) { - $this->object->{$field_name}[$key] = $value; - } - } else { - $this->object->$field_name = $default_product->$field_name; - } - } - } - $this->object->loadStockData(); - } - - return $result; - } - - public function ajaxProcessGetCountriesOptions() - { - if (!$res = Country::getCountriesByIdShop((int) Tools::getValue('id_shop'), (int) $this->context->language->id)) { - return; - } - - $tpl = $this->createTemplate('specific_prices_shop_update.tpl'); - $tpl->assign( - [ - 'option_list' => $res, - 'key_id' => 'id_country', - 'key_value' => 'name', - ] - ); - - $this->content = $tpl->fetch(); - } - - public function ajaxProcessGetCurrenciesOptions() - { - if (!$res = Currency::getCurrenciesByIdShop((int) Tools::getValue('id_shop'))) { - return; - } - - $tpl = $this->createTemplate('specific_prices_shop_update.tpl'); - $tpl->assign( - [ - 'option_list' => $res, - 'key_id' => 'id_currency', - 'key_value' => 'name', - ] - ); - - $this->content = $tpl->fetch(); - } - - public function ajaxProcessGetGroupsOptions() - { - if (!$res = Group::getGroups((int) $this->context->language->id, (int) Tools::getValue('id_shop'))) { - return; - } - - $tpl = $this->createTemplate('specific_prices_shop_update.tpl'); - $tpl->assign( - [ - 'option_list' => $res, - 'key_id' => 'id_group', - 'key_value' => 'name', - ] - ); - - $this->content = $tpl->fetch(); - } - - public function processDeleteVirtualProduct() - { - if (!($id_product_download = ProductDownload::getIdFromIdProduct((int) Tools::getValue('id_product')))) { - $this->errors[] = Tools::displayError('Cannot retrieve file'); - } else { - $product_download = new ProductDownload((int) $id_product_download); - - if (!$product_download->deleteFile((int) $id_product_download)) { - $this->errors[] = Tools::displayError('Cannot delete file'); - } else { - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) Tools::getValue('id_product') . '&updateproduct&key_tab=VirtualProduct&conf=1&token=' . $this->token; - } - } - - $this->display = 'edit'; - $this->tab_display = 'VirtualProduct'; - } - - public function ajaxProcessAddAttachment() - { - if (isset($_FILES['attachment_file'])) { - if ((int) $_FILES['attachment_file']['error'] === 1) { - $_FILES['attachment_file']['error'] = []; - - $max_upload = (int) ini_get('upload_max_filesize'); - $max_post = (int) ini_get('post_max_size'); - $upload_mb = min($max_upload, $max_post); - $_FILES['attachment_file']['error'][] = sprintf( - $this->l('File %1$s exceeds the size allowed by the server. The limit is set to %2$d MB.'), - '' . $_FILES['attachment_file']['name'] . ' ', - '' . $upload_mb . '' - ); - } - - $_FILES['attachment_file']['error'] = []; - - $is_attachment_name_valid = false; - $attachment_names = Tools::getValue('attachment_name'); - $attachment_descriptions = Tools::getValue('attachment_description'); - - if (!isset($attachment_names) || !$attachment_names) { - $attachment_names = []; - } - - if (!isset($attachment_descriptions) || !$attachment_descriptions) { - $attachment_descriptions = []; - } - - foreach ($attachment_names as $lang => $name) { - $language = Language::getLanguage((int) $lang); - - if (Tools::strlen($name) > 0) { - $is_attachment_name_valid = true; - } - - if (!Validate::isGenericName($name)) { - $_FILES['attachment_file']['error'][] = sprintf(Tools::displayError('Invalid name for %s language'), $language['name']); - } elseif (Tools::strlen($name) > 32) { - $_FILES['attachment_file']['error'][] = sprintf(Tools::displayError('The name for %1s language is too long (%2d chars max).'), $language['name'], 32); - } - } - - foreach ($attachment_descriptions as $lang => $description) { - $language = Language::getLanguage((int) $lang); - - if (!Validate::isCleanHtml($description)) { - $_FILES['attachment_file']['error'][] = sprintf(Tools::displayError('Invalid description for %s language.'), $language['name']); - } - } - - if (!$is_attachment_name_valid) { - $_FILES['attachment_file']['error'][] = Tools::displayError('An attachment name is required.'); - } - - if (empty($_FILES['attachment_file']['error'])) { - if (is_uploaded_file($_FILES['attachment_file']['tmp_name'])) { - if ($_FILES['attachment_file']['size'] > (Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE') * 1024 * 1024)) { - $_FILES['attachment_file']['error'][] = sprintf( - $this->l('The file is too large. Maximum size allowed is: %1$d kB. The file you are trying to upload is %2$d kB.'), - (Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE') * 1024), - number_format(($_FILES['attachment_file']['size'] / 1024), 2, '.', '') - ); - } else { - do { - $uniqid = sha1(microtime()); - } while (file_exists(_PS_DOWNLOAD_DIR_ . $uniqid)); - if (!copy($_FILES['attachment_file']['tmp_name'], _PS_DOWNLOAD_DIR_ . $uniqid)) { - $_FILES['attachment_file']['error'][] = $this->l('File copy failed'); - } - @unlink($_FILES['attachment_file']['tmp_name']); - } - } else { - $_FILES['attachment_file']['error'][] = Tools::displayError('The file is missing.'); - } - - if (empty($_FILES['attachment_file']['error']) && isset($uniqid)) { - $attachment = new Attachment(); - - foreach ($attachment_names as $lang => $name) { - $attachment->name[(int) $lang] = $name; - } - - foreach ($attachment_descriptions as $lang => $description) { - $attachment->description[(int) $lang] = $description; - } - - $attachment->file = $uniqid; - $attachment->mime = $_FILES['attachment_file']['type']; - $attachment->file_name = $_FILES['attachment_file']['name']; - - if (empty($attachment->mime) || Tools::strlen($attachment->mime) > 128) { - $_FILES['attachment_file']['error'][] = Tools::displayError('Invalid file extension'); - } - if (!Validate::isGenericName($attachment->file_name)) { - $_FILES['attachment_file']['error'][] = Tools::displayError('Invalid file name'); - } - if (Tools::strlen($attachment->file_name) > 128) { - $_FILES['attachment_file']['error'][] = Tools::displayError('The file name is too long.'); - } - if (empty($this->errors)) { - $res = $attachment->add(); - if (!$res) { - $_FILES['attachment_file']['error'][] = Tools::displayError('This attachment was unable to be loaded into the database.'); - } else { - $_FILES['attachment_file']['id_attachment'] = $attachment->id; - $_FILES['attachment_file']['filename'] = $attachment->name[$this->context->employee->id_lang]; - $id_product = (int) Tools::getValue($this->identifier); - $res = $attachment->attachProduct($id_product); - if (!$res) { - $_FILES['attachment_file']['error'][] = Tools::displayError('We were unable to associate this attachment to a product.'); - } - } - } else { - $_FILES['attachment_file']['error'][] = Tools::displayError('Invalid file'); - } - } - } - - die(json_encode($_FILES)); - } - } - - /** - * Attach an existing attachment to the product - * - * @return void - */ - public function processAttachments() - { - if ($id = (int) Tools::getValue($this->identifier)) { - $attachments = trim(Tools::getValue('arrayAttachments'), ','); - $attachments = explode(',', $attachments); - if (!Attachment::attachToProduct($id, $attachments)) { - $this->errors[] = Tools::displayError('An error occurred while saving product attachments.'); - } - } - } - - public function processDuplicate() - { - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $id_product_old = $product->id; - if (empty($product->price) && Shop::getContext() == Shop::CONTEXT_GROUP) { - $shops = ShopGroup::getShopsFromGroup(Shop::getContextShopGroupID()); - foreach ($shops as $shop) { - if ($product->isAssociatedToShop($shop['id_shop'])) { - $product_price = new Product($id_product_old, false, null, $shop['id_shop']); - $product->price = $product_price->price; - } - } - } - unset( - $product->id, - $product->id_product - ); - $product->indexed = 0; - $product->active = 0; - if ($product->add() - && Category::duplicateProductCategories($id_product_old, $product->id) - && ($combination_images = Product::duplicateAttributes($id_product_old, $product->id)) !== false - && GroupReduction::duplicateReduction($id_product_old, $product->id) - && Product::duplicateAccessories($id_product_old, $product->id) - && Product::duplicateFeatures($id_product_old, $product->id) - && Product::duplicateSpecificPrices($id_product_old, $product->id) - && Pack::duplicate($id_product_old, $product->id) - && Product::duplicateCustomizationFields($id_product_old, $product->id) - && Product::duplicateTags($id_product_old, $product->id) - && Product::duplicateDownload($id_product_old, $product->id)) { - if ($product->hasAttributes()) { - Product::updateDefaultAttribute($product->id); - } - - if (!Tools::getValue('noimage') && !Image::duplicateProductImages($id_product_old, $product->id, $combination_images)) { - $this->errors[] = Tools::displayError('An error occurred while copying images.'); - } else { - Hook::exec('actionProductAdd', ['id_product' => (int) $product->id, 'product' => $product]); - if (in_array($product->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $product->id); - } - $this->redirect_after = self::$currentIndex . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&conf=19&token=' . $this->token; - } - } else { - $this->errors[] = Tools::displayError('An error occurred while creating an object.'); - } - } - } - - public function processDelete() - { - if (Validate::isLoadedObject($object = $this->loadObject()) && isset($this->fieldImageSettings)) { - // check if request at least one object with noZeroObject - if (isset($object->noZeroObject) && count($taxes = call_user_func([$this->className, $object->noZeroObject])) <= 1) { - $this->errors[] = Tools::displayError('You need at least one object.') . ' ' . $this->table . '
    ' . Tools::displayError('You cannot delete all of the items.'); - } else { - if (!count($this->errors)) { - if ($object->delete()) { - $id_category = (int) Tools::getValue('id_category'); - $category_url = empty($id_category) ? '' : '&id_category=' . (int) $id_category; - PrestaShopLogger::addLog(sprintf($this->l('%s deletion', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int) $object->id, true, (int) $this->context->employee->id); - $this->redirect_after = self::$currentIndex . '&conf=1&token=' . $this->token . $category_url; - } else { - $this->errors[] = Tools::displayError('An error occurred during deletion.'); - } - } - } - } else { - $this->errors[] = Tools::displayError('An error occurred while deleting the object.') . ' ' . $this->table . ' ' . Tools::displayError('(cannot load object)'); - } - } - - public function processImage() - { - $id_image = (int) Tools::getValue('id_image'); - $image = new Image((int) $id_image); - if (Validate::isLoadedObject($image)) { - /* Update product image/legend */ - // @todo : move in processEditProductImage - if (Tools::getIsset('editImage')) { - if ($image->cover) { - $_POST['cover'] = 1; - } - - $_POST['id_image'] = $image->id; - } elseif (Tools::getIsset('coverImage')) { - /* Choose product cover image */ - Image::deleteCover($image->id_product); - $image->cover = 1; - if (!$image->update()) { - $this->errors[] = Tools::displayError('You cannot change the product\'s cover image.'); - } else { - $productId = (int) Tools::getValue('id_product'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . $productId . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . $productId . '_' . $this->context->shop->id . '.jpg'); - $this->redirect_after = self::$currentIndex . '&id_product=' . $image->id_product . '&id_category=' . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&action=Images&addproduct' . '&token=' . $this->token; - } - } elseif (Tools::getIsset('imgPosition') && Tools::getIsset('imgDirection')) { - /* Choose product image position */ - $image->updatePosition(Tools::getValue('imgDirection'), Tools::getValue('imgPosition')); - $this->redirect_after = self::$currentIndex . '&id_product=' . $image->id_product . '&id_category=' . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&add' . $this->table . '&action=Images&token=' . $this->token; - } - } else { - $this->errors[] = Tools::displayError('The image could not be found. '); - } - } - - protected function processBulkDelete() - { - if ($this->access('delete')) { - if (is_array($this->boxes) && !empty($this->boxes)) { - $object = new $this->className(); - - if (isset($object->noZeroObject) && - // Check if all object will be deleted - (count(call_user_func([$this->className, $object->noZeroObject])) <= 1 || count($_POST[$this->table . 'Box']) == count(call_user_func([$this->className, $object->noZeroObject])))) { - $this->errors[] = Tools::displayError('You need at least one object.') . ' ' . $this->table . '
    ' . Tools::displayError('You cannot delete all of the items.'); - } else { - $success = 1; - $products = Tools::getValue($this->table . 'Box'); - if (is_array($products) && ($count = count($products))) { - // Deleting products can be quite long on a cheap server. Let's say 1.5 seconds by product (I've seen it!). - if ((int) (ini_get('max_execution_time')) < round($count * 1.5)) { - ini_set('max_execution_time', round($count * 1.5)); - } - - foreach ($products as $id_product) { - $product = new Product((int) $id_product); - if (!count($this->errors)) { - if ($product->delete()) { - PrestaShopLogger::addLog(sprintf($this->l('%s deletion', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int) $product->id, true, (int) $this->context->employee->id); - } else { - $success = false; - } - } else { - $success = 0; - } - } - } - - if ($success) { - $id_category = (int) Tools::getValue('id_category'); - $category_url = empty($id_category) ? '' : '&id_category=' . (int) $id_category; - $this->redirect_after = self::$currentIndex . '&conf=2&token=' . $this->token . $category_url; - } else { - $this->errors[] = Tools::displayError('An error occurred while deleting this selection.'); - } - } - } else { - $this->errors[] = Tools::displayError('You must select at least one element to delete.'); - } - } else { - $this->errors[] = Tools::displayError('You do not have permission to delete this.'); - } - } - - public function processProductAttribute() - { - // Don't process if the combination fields have not been submitted - if (!Combination::isFeatureActive() || !Tools::getValue('attribute_combination_list')) { - return; - } - - if (Validate::isLoadedObject($product = $this->object)) { - if ($this->isProductFieldUpdated('attribute_price') && (!Tools::getIsset('attribute_price') || Tools::getIsset('attribute_price') == null)) { - $this->errors[] = Tools::displayError('The price attribute is required.'); - } - if (!Tools::getIsset('attribute_combination_list') || Tools::isEmpty(Tools::getValue('attribute_combination_list'))) { - $this->errors[] = Tools::displayError('You must add at least one attribute.'); - } - - $array_checks = [ - 'reference' => 'isReference', - 'supplier_reference' => 'isReference', - 'location' => 'isReference', - 'ean13' => 'isEan13', - 'upc' => 'isUpc', - 'wholesale_price' => 'isPrice', - 'price' => 'isPrice', - 'ecotax' => 'isPrice', - 'quantity' => 'isInt', - 'weight' => 'isUnsignedFloat', - 'unit_price_impact' => 'isPrice', - 'default_on' => 'isBool', - 'minimal_quantity' => 'isUnsignedInt', - 'available_date' => 'isDateFormat', - ]; - foreach ($array_checks as $property => $check) { - if (Tools::getValue('attribute_' . $property) !== false && !call_user_func(['Validate', $check], Tools::getValue('attribute_' . $property))) { - $this->errors[] = sprintf(Tools::displayError('Field %s is not valid'), $property); - } - } - - if (!count($this->errors)) { - if (!isset($_POST['attribute_wholesale_price'])) { - $_POST['attribute_wholesale_price'] = 0; - } - if (!isset($_POST['attribute_price_impact'])) { - $_POST['attribute_price_impact'] = 0; - } - if (!isset($_POST['attribute_weight_impact'])) { - $_POST['attribute_weight_impact'] = 0; - } - if (!isset($_POST['attribute_ecotax'])) { - $_POST['attribute_ecotax'] = 0; - } - if (Tools::getValue('attribute_default')) { - $product->deleteDefaultAttributes(); - } - - // Change existing one - if (($id_product_attribute = (int) Tools::getValue('id_product_attribute')) || ($id_product_attribute = $product->productAttributeExists(Tools::getValue('attribute_combination_list'), false, null, true, true))) { - if ($this->access('edit')) { - if ($this->isProductFieldUpdated('available_date_attribute') && (Tools::getValue('available_date_attribute') != '' && !Validate::isDateFormat(Tools::getValue('available_date_attribute')))) { - $this->errors[] = Tools::displayError('Invalid date format.'); - } else { - $product->updateAttribute( - (int) $id_product_attribute, - $this->isProductFieldUpdated('attribute_wholesale_price') ? Tools::getValue('attribute_wholesale_price') : null, - $this->isProductFieldUpdated('attribute_price_impact') ? Tools::getValue('attribute_price') * Tools::getValue('attribute_price_impact') : null, - $this->isProductFieldUpdated('attribute_weight_impact') ? Tools::getValue('attribute_weight') * Tools::getValue('attribute_weight_impact') : null, - $this->isProductFieldUpdated('attribute_unit_impact') ? Tools::getValue('attribute_unity') * Tools::getValue('attribute_unit_impact') : null, - $this->isProductFieldUpdated('attribute_ecotax') ? Tools::getValue('attribute_ecotax') : null, - Tools::getValue('id_image_attr'), - Tools::getValue('attribute_reference'), - Tools::getValue('attribute_ean13'), - $this->isProductFieldUpdated('attribute_default') ? Tools::getValue('attribute_default') : null, - Tools::getValue('attribute_location'), - Tools::getValue('attribute_upc'), - $this->isProductFieldUpdated('attribute_minimal_quantity') ? Tools::getValue('attribute_minimal_quantity') : null, - $this->isProductFieldUpdated('available_date_attribute') ? Tools::getValue('available_date_attribute') : null, - false - ); - StockAvailable::setProductOutOfStock((int) $product->id, $product->out_of_stock, null, (int) $id_product_attribute); - } - } else { - $this->errors[] = Tools::displayError('You do not have permission to add this.'); - } - } else { - // Add new - if ($this->access('add')) { - if ($product->productAttributeExists(Tools::getValue('attribute_combination_list'))) { - $this->errors[] = Tools::displayError('This combination already exists.'); - } else { - $id_product_attribute = $product->addCombinationEntity( - Tools::getValue('attribute_wholesale_price'), - Tools::getValue('attribute_price') * Tools::getValue('attribute_price_impact'), - Tools::getValue('attribute_weight') * Tools::getValue('attribute_weight_impact'), - Tools::getValue('attribute_unity') * Tools::getValue('attribute_unit_impact'), - Tools::getValue('attribute_ecotax'), - 0, - Tools::getValue('id_image_attr'), - Tools::getValue('attribute_reference'), - null, - Tools::getValue('attribute_ean13'), - Tools::getValue('attribute_default'), - Tools::getValue('attribute_location'), - Tools::getValue('attribute_upc'), - Tools::getValue('attribute_minimal_quantity'), - [], - Tools::getValue('available_date_attribute'), - Tools::getValue('attribute_isbn') - ); - StockAvailable::setProductOutOfStock((int) $product->id, $product->out_of_stock, null, (int) $id_product_attribute); - } - } else { - $this->errors[] = Tools::displayError('You do not have permission to') . '
    ' . Tools::displayError('edit here.'); - } - } - if (!count($this->errors)) { - $combination = new Combination((int) $id_product_attribute); - $combination->setAttributes(Tools::getValue('attribute_combination_list')); - - // images could be deleted before - $id_images = Tools::getValue('id_image_attr'); - if (!empty($id_images)) { - $combination->setImages($id_images); - } - - $product->checkDefaultAttributes(); - if (Tools::getValue('attribute_default')) { - Product::updateDefaultAttribute((int) $product->id); - if (isset($id_product_attribute)) { - $product->cache_default_attribute = (int) $id_product_attribute; - } - - if ($available_date = Tools::getValue('available_date_attribute')) { - $product->setAvailableDate($available_date); - } else { - $product->setAvailableDate(); - } - } - } - } - } - } - - public function processFeatures() - { - if (!Feature::isFeatureActive()) { - return; - } - - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - // delete all objects - $product->deleteFeatures(); - - // add new objects - $languages = Language::getLanguages(false); - foreach ($_POST as $key => $val) { - if (preg_match('/^feature_([0-9]+)_value/i', $key, $match)) { - if ($val) { - $product->addFeaturesToDB($match[1], $val); - } else { - if ($default_value = $this->checkFeatures($languages, $match[1])) { - $id_value = $product->addFeaturesToDB($match[1], 0, 1); - foreach ($languages as $language) { - if ($cust = Tools::getValue('custom_' . $match[1] . '_' . (int) $language['id_lang'])) { - $product->addFeaturesCustomToDB($id_value, (int) $language['id_lang'], $cust); - } else { - $product->addFeaturesCustomToDB($id_value, (int) $language['id_lang'], $default_value); - } - } - } - } - } - } - } else { - $this->errors[] = Tools::displayError('A product must be created before adding features.'); - } - } - - /** - * This function is never called at the moment (specific prices cannot be edited) - */ - public function processPricesModification() - { - $id_specific_prices = Tools::getValue('spm_id_specific_price'); - $id_combinations = Tools::getValue('spm_id_product_attribute'); - $id_shops = Tools::getValue('spm_id_shop'); - $id_currencies = Tools::getValue('spm_id_currency'); - $id_countries = Tools::getValue('spm_id_country'); - $id_groups = Tools::getValue('spm_id_group'); - $id_customers = Tools::getValue('spm_id_customer'); - $prices = Tools::getValue('spm_price'); - $from_quantities = Tools::getValue('spm_from_quantity'); - $reductions = Tools::getValue('spm_reduction'); - $reduction_types = Tools::getValue('spm_reduction_type'); - $froms = Tools::getValue('spm_from'); - $tos = Tools::getValue('spm_to'); - - foreach ($id_specific_prices as $key => $id_specific_price) { - if ($reduction_types[$key] == 'percentage' && ((float) $reductions[$key] <= 0 || (float) $reductions[$key] > 100)) { - $this->errors[] = Tools::displayError('The submitted reduction value (0-100) is out-of-range.'); - } elseif ($this->_validateSpecificPrice($id_shops[$key], $id_currencies[$key], $id_countries[$key], $id_groups[$key], $id_customers[$key], $prices[$key], $from_quantities[$key], $reductions[$key], $reduction_types[$key], $froms[$key], $tos[$key], $id_combinations[$key])) { - $specific_price = new SpecificPrice((int) ($id_specific_price)); - $specific_price->id_shop = (int) $id_shops[$key]; - $specific_price->id_product_attribute = (int) $id_combinations[$key]; - $specific_price->id_currency = (int) ($id_currencies[$key]); - $specific_price->id_country = (int) ($id_countries[$key]); - $specific_price->id_group = (int) ($id_groups[$key]); - $specific_price->id_customer = (int) $id_customers[$key]; - $specific_price->price = (float) ($prices[$key]); - $specific_price->from_quantity = (int) ($from_quantities[$key]); - $specific_price->reduction = (float) ($reduction_types[$key] == 'percentage' ? ($reductions[$key] / 100) : $reductions[$key]); - $specific_price->reduction_type = !$reductions[$key] ? 'amount' : $reduction_types[$key]; - $specific_price->from = !$froms[$key] ? '0000-00-00 00:00:00' : $froms[$key]; - $specific_price->to = !$tos[$key] ? '0000-00-00 00:00:00' : $tos[$key]; - if (!$specific_price->update()) { - $this->errors[] = Tools::displayError('An error occurred while updating the specific price.'); - } - } - } - if (!count($this->errors)) { - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) (Tools::getValue('id_product')) . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&update' . $this->table . '&action=Prices&token=' . $this->token; - } - } - - public function processPriceAddition() - { - // Check if a specific price has been submitted - if (!Tools::getIsset('submitPriceAddition')) { - return; - } - - $id_product = Tools::getValue('id_product'); - $id_product_attribute = Tools::getValue('sp_id_product_attribute'); - $id_shop = Tools::getValue('sp_id_shop'); - $id_currency = Tools::getValue('sp_id_currency'); - $id_country = Tools::getValue('sp_id_country'); - $id_group = Tools::getValue('sp_id_group'); - $id_customer = Tools::getValue('sp_id_customer'); - $price = Tools::getValue('leave_bprice') ? '-1' : Tools::getValue('sp_price'); - $from_quantity = Tools::getValue('sp_from_quantity'); - $reduction = (float) (Tools::getValue('sp_reduction')); - $reduction_tax = Tools::getValue('sp_reduction_tax'); - $reduction_type = !$reduction ? 'amount' : Tools::getValue('sp_reduction_type'); - $reduction_type = $reduction_type == '-' ? 'amount' : $reduction_type; - $from = Tools::getValue('sp_from'); - if (!$from) { - $from = '0000-00-00 00:00:00'; - } - $to = Tools::getValue('sp_to'); - if (!$to) { - $to = '0000-00-00 00:00:00'; - } - - if (($price == '-1') && ((float) $reduction == '0')) { - $this->errors[] = Tools::displayError('No reduction value has been submitted.'); - } elseif (strtotime($to) < strtotime($from)) { - $this->errors[] = Tools::displayError('Invalid date range'); - } elseif ($reduction_type == 'percentage' && ((float) $reduction <= 0 || (float) $reduction > 100)) { - $this->errors[] = Tools::displayError('The submitted reduction value (0-100) is out-of-range.'); - } elseif ($this->_validateSpecificPrice($id_shop, $id_currency, $id_country, $id_group, $id_customer, $price, $from_quantity, $reduction, $reduction_type, $from, $to, $id_product_attribute)) { - $specificPrice = new SpecificPrice(); - $specificPrice->id_product = (int) $id_product; - $specificPrice->id_product_attribute = (int) $id_product_attribute; - $specificPrice->id_shop = (int) $id_shop; - $specificPrice->id_currency = (int) ($id_currency); - $specificPrice->id_country = (int) ($id_country); - $specificPrice->id_group = (int) ($id_group); - $specificPrice->id_customer = (int) $id_customer; - $specificPrice->price = (float) ($price); - $specificPrice->from_quantity = (int) ($from_quantity); - $specificPrice->reduction = (float) ($reduction_type == 'percentage' ? $reduction / 100 : $reduction); - $specificPrice->reduction_tax = $reduction_tax; - $specificPrice->reduction_type = $reduction_type; - $specificPrice->from = $from; - $specificPrice->to = $to; - if (!$specificPrice->add()) { - $this->errors[] = Tools::displayError('An error occurred while updating the specific price.'); - } - } - } - - public function ajaxProcessDeleteSpecificPrice() - { - if ($this->access('delete')) { - $id_specific_price = (int) Tools::getValue('id_specific_price'); - if (!$id_specific_price || !Validate::isUnsignedId($id_specific_price)) { - $error = Tools::displayError('The specific price ID is invalid.'); - } else { - $specificPrice = new SpecificPrice((int) $id_specific_price); - if (!$specificPrice->delete()) { - $error = Tools::displayError('An error occurred while attempting to delete the specific price.'); - } - } - } else { - $error = Tools::displayError('You do not have permission to delete this.'); - } - - if (isset($error)) { - $json = [ - 'status' => 'error', - 'message' => $error, - ]; - } else { - $json = [ - 'status' => 'ok', - 'message' => $this->_conf[1], - ]; - } - - die(json_encode($json)); - } - - public function processSpecificPricePriorities() - { - if (!($obj = $this->loadObject())) { - return; - } - if (!$priorities = Tools::getValue('specificPricePriority')) { - $this->errors[] = Tools::displayError('Please specify priorities.'); - } elseif (Tools::isSubmit('specificPricePriorityToAll')) { - $sfContainer = SymfonyContainer::getInstance(); - $specificPricePriorityUpdater = $sfContainer->get(SpecificPricePriorityUpdater::class); - - try { - $specificPricePriorityUpdater->updateDefaultPriorities($priorities); - $this->confirmations[] = 'The price rule has successfully updated'; - } catch (CoreException $e) { - $this->errors[] = $this->trans('An error occurred while updating priorities.', [], 'Admin.Catalog.Notification'); - } - } elseif (!SpecificPrice::setSpecificPriority((int) $obj->id, $priorities)) { - $this->errors[] = Tools::displayError('An error occurred while setting priorities.'); - } - } - - public function processCustomizationConfiguration() - { - $product = $this->object; - // Get the number of existing customization fields ($product->text_fields is the updated value, not the existing value) - $current_customization = $product->getCustomizationFieldIds(); - $files_count = 0; - $text_count = 0; - if (is_array($current_customization)) { - foreach ($current_customization as $field) { - if ($field['type'] == 1) { - ++$text_count; - } else { - ++$files_count; - } - } - } - - if (!$product->createLabels((int) $product->uploadable_files - $files_count, (int) $product->text_fields - $text_count)) { - $this->errors[] = Tools::displayError('An error occurred while creating customization fields.'); - } - if (!count($this->errors) && !$product->updateLabels()) { - $this->errors[] = Tools::displayError('An error occurred while updating customization fields.'); - } - $product->customizable = ($product->uploadable_files > 0 || $product->text_fields > 0) ? 1 : 0; - if (($product->uploadable_files != $files_count || $product->text_fields != $text_count) && !count($this->errors) && !$product->update()) { - $this->errors[] = Tools::displayError('An error occurred while updating the custom configuration.'); - } - } - - public function processProductCustomization() - { - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - foreach ($_POST as $field => $value) { - if (strncmp($field, 'label_', 6) == 0 && !Validate::isLabel($value)) { - $this->errors[] = Tools::displayError('The label fields defined are invalid.'); - } - } - if (empty($this->errors) && !$product->updateLabels()) { - $this->errors[] = Tools::displayError('An error occurred while updating customization fields.'); - } - if (empty($this->errors)) { - $this->confirmations[] = $this->l('Update successful'); - } - } else { - $this->errors[] = Tools::displayError('A product must be created before adding customization.'); - } - } - - /** - * Overrides parent for custom redirect link - */ - public function processPosition() - { - if (!Validate::isLoadedObject($object = $this->loadObject())) { - $this->errors[] = Tools::displayError('An error occurred while updating the status for an object.') . - ' ' . $this->table . ' ' . Tools::displayError('(cannot load object)'); - } elseif (!$object->updatePosition((int) Tools::getValue('way'), (int) Tools::getValue('position'))) { - $this->errors[] = Tools::displayError('Failed to update the position.'); - } else { - $category = new Category((int) Tools::getValue('id_category')); - if (Validate::isLoadedObject($category)) { - Hook::exec('actionCategoryUpdate', ['category' => $category]); - } - $this->redirect_after = self::$currentIndex . '&' . $this->table . 'Orderby=position&' . $this->table . 'Orderway=asc&action=Customization&conf=5' . (($id_category = (Tools::getIsset('id_category') ? (int) Tools::getValue('id_category') : '')) ? ('&id_category=' . $id_category) : '') . '&token=' . Tools::getAdminTokenLite('AdminProducts'); - } - } - - public function initProcess() - { - if (Tools::isSubmit('submitAddproductAndStay') || Tools::isSubmit('submitAddproduct')) { - $this->id_object = (int) Tools::getValue('id_product'); - $this->object = new Product($this->id_object); - - if ($this->isTabSubmitted('Informations') && $this->object->is_virtual && (int) Tools::getValue('type_product') != 2) { - if ($id_product_download = (int) ProductDownload::getIdFromIdProduct($this->id_object)) { - $product_download = new ProductDownload($id_product_download); - if (!$product_download->deleteFile($id_product_download)) { - $this->errors[] = Tools::displayError('Cannot delete file'); - } - } - } - } - - // Delete a product in the download folder - if (Tools::getValue('deleteVirtualProduct')) { - if ($this->access('delete')) { - $this->action = 'deleteVirtualProduct'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to delete this.'); - } - } elseif (Tools::isSubmit('submitAddProductAndPreview')) { - // Product preview - $this->display = 'edit'; - $this->action = 'save'; - if (Tools::getValue('id_product')) { - $this->id_object = Tools::getValue('id_product'); - $this->object = new Product((int) Tools::getValue('id_product')); - } - } elseif (Tools::isSubmit('submitAttachments')) { - if ($this->access('edit')) { - $this->action = 'attachments'; - $this->tab_display = 'attachments'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::getIsset('duplicate' . $this->table)) { - // Product duplication - if ($this->access('add')) { - $this->action = 'duplicate'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to add this.'); - } - } elseif (Tools::getValue('id_image') && Tools::getValue('ajax')) { - // Product images management - if ($this->access('edit')) { - $this->action = 'image'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('submitProductAttribute')) { - // Product attributes management - if ($this->access('edit')) { - $this->action = 'productAttribute'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('submitFeatures') || Tools::isSubmit('submitFeaturesAndStay')) { - // Product features management - if ($this->access('edit')) { - $this->action = 'features'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('submitPricesModification')) { - // Product specific prices management NEVER USED - if ($this->access('add')) { - $this->action = 'pricesModification'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to add this.'); - } - } elseif (Tools::isSubmit('deleteSpecificPrice')) { - if ($this->access('delete')) { - $this->action = 'deleteSpecificPrice'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to delete this.'); - } - } elseif (Tools::isSubmit('submitSpecificPricePriorities')) { - if ($this->access('edit')) { - $this->action = 'specificPricePriorities'; - $this->tab_display = 'prices'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('submitCustomizationConfiguration')) { - // Customization management - if ($this->access('edit')) { - $this->action = 'customizationConfiguration'; - $this->tab_display = 'customization'; - $this->display = 'edit'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('submitProductCustomization')) { - if ($this->access('edit')) { - $this->action = 'productCustomization'; - $this->tab_display = 'customization'; - $this->display = 'edit'; - } else { - $this->errors[] = Tools::displayError('You do not have permission to edit this.'); - } - } elseif (Tools::isSubmit('id_product')) { - $post_max_size = Tools::getMaxUploadSize(Configuration::get('PS_LIMIT_UPLOAD_FILE_VALUE') * 1024 * 1024); - if ($post_max_size && isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] && $_SERVER['CONTENT_LENGTH'] > $post_max_size) { - $this->errors[] = sprintf(Tools::displayError('The uploaded file exceeds the "Maximum size for a downloadable product" set in preferences (%1$dMB) or the post_max_size/ directive in php.ini (%2$dMB).'), number_format((Configuration::get('PS_LIMIT_UPLOAD_FILE_VALUE'))), ($post_max_size / 1024 / 1024)); - } - } - - if (!$this->action) { - parent::initProcess(); - } else { - $this->id_object = (int) Tools::getValue($this->identifier); - } - - if (isset($this->available_tabs[Tools::getValue('key_tab')])) { - $this->tab_display = Tools::getValue('key_tab'); - } - - // Set tab to display if not decided already - if (!$this->tab_display && $this->action) { - if (in_array($this->action, array_keys($this->available_tabs))) { - $this->tab_display = $this->action; - } - } - - // And if still not set, use default - if (!$this->tab_display) { - if (in_array($this->default_tab, $this->available_tabs)) { - $this->tab_display = $this->default_tab; - } else { - $this->tab_display = key($this->available_tabs); - } - } - } - - /** - * postProcess handle every checks before saving products information - * - * @return void - */ - public function postProcess() - { - if (!$this->redirect_after) { - parent::postProcess(); - } - - if ($this->display == 'edit' || $this->display == 'add') { - $this->addJqueryUI([ - 'ui.core', - 'ui.widget', - ]); - - $this->addjQueryPlugin([ - 'autocomplete', - 'tablednd', - 'thickbox', - 'ajaxfileupload', - 'date', - 'tagify', - 'select2', - 'validate', - ]); - - $this->addJS([ - _PS_JS_DIR_ . 'admin/products.js', - _PS_JS_DIR_ . 'admin/attributes.js', - _PS_JS_DIR_ . 'admin/price.js', - _PS_JS_DIR_ . 'tiny_mce/tiny_mce.js', - _PS_JS_DIR_ . 'admin/tinymce.inc.js', - _PS_JS_DIR_ . 'admin/dnd.js', - _PS_JS_DIR_ . 'jquery/ui/jquery.ui.progressbar.min.js', - _PS_JS_DIR_ . 'vendor/spin.js', - _PS_JS_DIR_ . 'vendor/ladda.js', - ]); - - $this->addJS(_PS_JS_DIR_ . 'jquery/plugins/select2/select2_locale_' . $this->context->language->iso_code . '.js'); - $this->addJS(_PS_JS_DIR_ . 'jquery/plugins/validate/localization/messages_' . $this->context->language->iso_code . '.js'); - - $this->addCSS([ - _PS_JS_DIR_ . 'jquery/plugins/timepicker/jquery-ui-timepicker-addon.css', - ]); - } - } - - public function ajaxProcessDeleteProductAttribute() - { - if (!Combination::isFeatureActive()) { - return; - } - - if ($this->access('delete')) { - $id_product = (int) Tools::getValue('id_product'); - $id_product_attribute = (int) Tools::getValue('id_product_attribute'); - - if ($id_product && Validate::isUnsignedId($id_product) && Validate::isLoadedObject($product = new Product($id_product))) { - $product->deleteAttributeCombination((int) $id_product_attribute); - $product->checkDefaultAttributes(); - if (!$product->hasAttributes()) { - $product->cache_default_attribute = 0; - $product->update(); - } else { - Product::updateDefaultAttribute($id_product); - } - - $json = [ - 'status' => 'ok', - 'message' => $this->_conf[1], - 'id_product_attribute' => (int) $id_product_attribute, - ]; - } else { - $json = [ - 'status' => 'error', - 'message' => $this->l('You cannot delete this attribute.'), - ]; - } - } else { - $json = [ - 'status' => 'error', - 'message' => $this->l('You do not have permission to delete this.'), - ]; - } - - die(json_encode($json)); - } - - public function ajaxProcessDefaultProductAttribute() - { - if ($this->access('edit')) { - if (!Combination::isFeatureActive()) { - return; - } - - if (Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $product->deleteDefaultAttributes(); - $product->setDefaultAttribute((int) Tools::getValue('id_product_attribute')); - $json = [ - 'status' => 'ok', - 'message' => $this->_conf[4], - ]; - } else { - $json = [ - 'status' => 'error', - 'message' => $this->l('You cannot make this the default attribute.'), - ]; - } - - die(json_encode($json)); - } - } - - public function ajaxProcessEditProductAttribute() - { - if ($this->access('edit')) { - $id_product = (int) Tools::getValue('id_product'); - $id_product_attribute = (int) Tools::getValue('id_product_attribute'); - if ($id_product && Validate::isUnsignedId($id_product) && Validate::isLoadedObject($product = new Product((int) $id_product))) { - $combinations = $product->getAttributeCombinationsById($id_product_attribute, $this->context->language->id); - foreach ($combinations as $key => $combination) { - $combinations[$key]['attributes'][] = [$combination['group_name'], $combination['attribute_name'], $combination['id_attribute']]; - } - - die(json_encode($combinations)); - } - } - } - - public function ajaxPreProcess() - { - if (Tools::getIsset('update' . $this->table) && Tools::getIsset('id_' . $this->table)) { - $this->display = 'edit'; - $this->action = Tools::getValue('action'); - } - } - - public function ajaxProcessUpdateProductImageShopAsso() - { - $id_product = Tools::getValue('id_product'); - if (($id_image = Tools::getValue('id_image')) && ($id_shop = (int) Tools::getValue('id_shop'))) { - if (Tools::getValue('active') == 'true') { - $res = Db::getInstance()->execute('INSERT INTO ' . _DB_PREFIX_ . 'image_shop (`id_image`, `id_shop`, `cover`) VALUES(' . (int) $id_image . ', ' . (int) $id_shop . ', \'0\')'); - } else { - $res = Db::getInstance()->execute('DELETE FROM ' . _DB_PREFIX_ . 'image_shop WHERE `id_image` = ' . (int) $id_image . ' AND `id_shop` = ' . (int) $id_shop); - } - } - - // Clean covers in image table - $count_cover_image = Db::getInstance()->getValue(' - SELECT COUNT(*) FROM ' . _DB_PREFIX_ . 'image i - INNER JOIN ' . _DB_PREFIX_ . 'image_shop ish ON (i.id_image = ish.id_image AND ish.id_shop = ' . (int) $id_shop . ') - WHERE i.cover = 1 AND `id_product` = ' . (int) $id_product); - - $id_image = Db::getInstance()->getValue(' - SELECT i.`id_image` FROM ' . _DB_PREFIX_ . 'image i - INNER JOIN ' . _DB_PREFIX_ . 'image_shop ish ON (i.id_image = ish.id_image AND ish.id_shop = ' . (int) $id_shop . ') - WHERE `id_product` = ' . (int) $id_product); - - if ($count_cover_image < 1) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image i SET i.cover = 1 WHERE i.id_image = ' . (int) $id_image . ' AND i.`id_product` = ' . (int) $id_product . ' LIMIT 1'); - } - - if ($count_cover_image > 1) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image i SET i.cover = 0 WHERE i.id_image <> ' . (int) $id_image . ' AND i.`id_product` = ' . (int) $id_product); - } - - // Clean covers in image_shop table - $count_cover_image_shop = Db::getInstance()->getValue(' - SELECT COUNT(*) - FROM ' . _DB_PREFIX_ . 'image_shop ish - INNER JOIN ' . _DB_PREFIX_ . 'image i ON (i.id_image = ish.id_image AND i.`id_product` = ' . (int) $id_product . ') - WHERE ish.id_shop = ' . (int) $id_shop . ' AND ish.cover = 1'); - - if ($count_cover_image_shop < 1) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image_shop ish SET ish.cover = 1 WHERE ish.id_image = ' . (int) $id_image . ' AND ish.id_shop = ' . (int) $id_shop . ' LIMIT 1'); - } - if ($count_cover_image_shop > 1) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image_shop ish SET ish.cover = 0 WHERE ish.id_image <> ' . (int) $id_image . ' AND ish.cover = 1 AND ish.id_shop = ' . (int) $id_shop . ' LIMIT ' . (int) ($count_cover_image_shop - 1)); - } - - if ($res) { - $this->jsonConfirmation($this->_conf[27]); - } else { - $this->jsonError(Tools::displayError('An error occurred while attempting to associate this image with your shop. ')); - } - } - - public function ajaxProcessUpdateImagePosition() - { - $res = false; - if ($json = Tools::getValue('json')) { - $res = true; - $json = stripslashes($json); - $images = json_decode($json, true); - foreach ($images as $id => $position) { - $img = new Image((int) $id); - $img->position = (int) $position; - $res &= $img->update(); - } - } - if ($res) { - $this->jsonConfirmation($this->_conf[25]); - } else { - $this->jsonError(Tools::displayError('An error occurred while attempting to move this picture.')); - } - } - - public function ajaxProcessUpdateCover() - { - Image::deleteCover((int) Tools::getValue('id_product')); - $img = new Image((int) Tools::getValue('id_image')); - $img->cover = 1; - - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . (int) $img->id_product . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $img->id_product . '_' . $this->context->shop->id . '.jpg'); - - if ($img->update()) { - $this->jsonConfirmation($this->_conf[26]); - } else { - $this->jsonError(Tools::displayError('An error occurred while attempting to move this picture.')); - } - } - - public function ajaxProcessDeleteProductImage() - { - $this->display = 'content'; - $res = true; - /* Delete product image */ - $image = new Image((int) Tools::getValue('id_image')); - $this->content['id'] = $image->id; - $res &= $image->delete(); - // if deleted image was the cover, change it to the first one - if (!Image::getCover($image->id_product)) { - $res &= Db::getInstance()->execute(' - UPDATE `' . _DB_PREFIX_ . 'image_shop` image_shop, ' . _DB_PREFIX_ . 'image i - SET image_shop.`cover` = 1, - i.cover = 1 - WHERE image_shop.`id_image` = (SELECT id_image FROM - (SELECT image_shop.id_image - FROM ' . _DB_PREFIX_ . 'image i' . - Shop::addSqlAssociation('image', 'i') . ' - WHERE i.id_product =' . (int) $image->id_product . ' LIMIT 1 - ) tmpImage) - AND id_shop=' . (int) $this->context->shop->id . ' - AND i.id_image = image_shop.id_image - '); - } - - if (file_exists(_PS_TMP_IMG_DIR_ . 'product_' . $image->id_product . '.jpg')) { - $res &= @unlink(_PS_TMP_IMG_DIR_ . 'product_' . $image->id_product . '.jpg'); - } - if (file_exists(_PS_TMP_IMG_DIR_ . 'product_mini_' . $image->id_product . '_' . $this->context->shop->id . '.jpg')) { - $res &= @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . $image->id_product . '_' . $this->context->shop->id . '.jpg'); - } - - if ($res) { - $this->jsonConfirmation($this->_conf[7]); - } else { - $this->jsonError(Tools::displayError('An error occurred while attempting to delete the product image.')); - } - } - - protected function _validateSpecificPrice($id_shop, $id_currency, $id_country, $id_group, $id_customer, $price, $from_quantity, $reduction, $reduction_type, $from, $to, $id_combination = 0) - { - if (!Validate::isUnsignedId($id_shop) || !Validate::isUnsignedId($id_currency) || !Validate::isUnsignedId($id_country) || !Validate::isUnsignedId($id_group) || !Validate::isUnsignedId($id_customer)) { - $this->errors[] = Tools::displayError('Wrong IDs'); - } elseif ((!isset($price) && !isset($reduction)) || (isset($price) && !Validate::isNegativePrice($price)) || (isset($reduction) && !Validate::isPrice($reduction))) { - $this->errors[] = Tools::displayError('Invalid price/discount amount'); - } elseif (!Validate::isUnsignedInt($from_quantity)) { - $this->errors[] = Tools::displayError('Invalid quantity'); - } elseif ($reduction && !Validate::isReductionType($reduction_type)) { - $this->errors[] = Tools::displayError('Please select a discount type (amount or percentage).'); - } elseif ($from && $to && (!Validate::isDateFormat($from) || !Validate::isDateFormat($to))) { - $this->errors[] = Tools::displayError('The from/to date is invalid.'); - } elseif (SpecificPrice::exists((int) $this->object->id, $id_combination, $id_shop, $id_group, $id_country, $id_currency, $id_customer, $from_quantity, $from, $to, false)) { - $this->errors[] = Tools::displayError('A specific price already exists for these parameters.'); - } else { - return true; - } - - return false; - } - - /* Checking customs feature */ - protected function checkFeatures($languages, $feature_id) - { - $rules = call_user_func(['FeatureValue', 'getValidationRules'], 'FeatureValue'); - $feature = Feature::getFeature((int) Configuration::get('PS_LANG_DEFAULT'), $feature_id); - - foreach ($languages as $language) { - if ($val = Tools::getValue('custom_' . $feature_id . '_' . $language['id_lang'])) { - $current_language = new Language($language['id_lang']); - if (Tools::strlen($val) > $rules['sizeLang']['value']) { - $this->errors[] = sprintf( - Tools::displayError('The name for feature %1$s is too long in %2$s.'), - ' ' . $feature['name'] . '', - $current_language->name - ); - } elseif (!call_user_func(['Validate', $rules['validateLang']['value']], $val)) { - $this->errors[] = sprintf( - Tools::displayError('A valid name required for feature. %1$s in %2$s.'), - ' ' . $feature['name'] . '', - $current_language->name - ); - } - if (count($this->errors)) { - return 0; - } - // Getting default language - if ($language['id_lang'] == Configuration::get('PS_LANG_DEFAULT')) { - return $val; - } - } - } - - return 0; - } - - /** - * Add or update a product image - * - * @param object $product Product object to add image - */ - public function addProductImage($product, $method = 'auto') - { - /* Updating an existing product image */ - if ($id_image = (int) Tools::getValue('id_image')) { - $image = new Image((int) $id_image); - if (!Validate::isLoadedObject($image)) { - $this->errors[] = Tools::displayError('An error occurred while loading the object image.'); - } else { - if (($cover = Tools::getValue('cover')) == 1) { - Image::deleteCover($product->id); - } - $image->cover = $cover; - $this->validateRules('Image'); - $this->copyFromPost($image, 'image'); - if (count($this->errors) || !$image->update()) { - $this->errors[] = Tools::displayError('An error occurred while updating the image.'); - } elseif (isset($_FILES['image_product']['tmp_name']) && $_FILES['image_product']['tmp_name'] != null) { - $this->copyImage($product->id, $image->id, $method); - } - } - } - if (isset($image) && Validate::isLoadedObject($image) && !file_exists(_PS_PRODUCT_IMG_DIR_ . $image->getExistingImgPath() . '.' . $image->image_format)) { - $image->delete(); - } - if (count($this->errors)) { - return false; - } - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . $product->id . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . $product->id . '_' . $this->context->shop->id . '.jpg'); - - return (isset($id_image) && is_int($id_image) && $id_image) ? $id_image : false; - } - - /** - * Copy a product image - * - * @param int $id_product Product Id for product image filename - * @param int $id_image Image Id for product image filename - */ - public function copyImage($id_product, $id_image, $method = 'auto') - { - if (!isset($_FILES['image_product']['tmp_name'])) { - return false; - } - if ($error = ImageManager::validateUpload($_FILES['image_product'])) { - $this->errors[] = $error; - } else { - $image = new Image($id_image); - - if (!$new_path = $image->getPathForCreation()) { - $this->errors[] = Tools::displayError('An error occurred while attempting to create a new folder.'); - } - if (!($tmpName = tempnam(_PS_TMP_IMG_DIR_, 'PS')) || !move_uploaded_file($_FILES['image_product']['tmp_name'], $tmpName)) { - $this->errors[] = Tools::displayError('An error occurred during the image upload process.'); - } elseif (!ImageManager::resize($tmpName, $new_path . '.' . $image->image_format)) { - $this->errors[] = Tools::displayError('An error occurred while copying the image.'); - } elseif ($method == 'auto') { - $imagesTypes = ImageType::getImagesTypes('products'); - foreach ($imagesTypes as $k => $image_type) { - if (!ImageManager::resize($tmpName, $new_path . '-' . stripslashes($image_type['name']) . '.' . $image->image_format, $image_type['width'], $image_type['height'], $image->image_format)) { - $this->errors[] = Tools::displayError('An error occurred while copying this image:') . ' ' . stripslashes($image_type['name']); - } - } - } - - @unlink($tmpName); - Hook::exec('actionWatermark', ['id_image' => $id_image, 'id_product' => $id_product]); - } - } - - protected function updateAssoShop($id_object) - { - //override AdminController::updateAssoShop() specifically for products because shop association is set with the context in ObjectModel - } - - public function processAdd() - { - $this->checkProduct(); - - if (!empty($this->errors)) { - $this->display = 'add'; - - return false; - } - - $this->object = new $this->className(); - $this->_removeTaxFromEcotax(); - $this->copyFromPost($this->object, $this->table); - - if ($this->object->add()) { - PrestaShopLogger::addLog(sprintf($this->l('%s addition', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int) $this->object->id, true, (int) $this->context->employee->id); - $this->addCarriers($this->object); - $this->updateAccessories($this->object); - $this->updatePackItems($this->object); - $this->updateDownloadProduct($this->object); - - if (empty($this->errors)) { - $languages = Language::getLanguages(false); - if ($this->isProductFieldUpdated('category_box') && !$this->object->updateCategories(Tools::getValue('categoryBox'))) { - $this->errors[] = Tools::displayError('An error occurred while linking the object.') . ' ' . $this->table . ' ' . Tools::displayError('To categories'); - } elseif (!$this->updateTags($languages, $this->object)) { - $this->errors[] = Tools::displayError('An error occurred while adding tags.'); - } else { - Hook::exec('actionProductAdd', ['id_product' => (int) $this->object->id, 'product' => $this->object]); - if (in_array($this->object->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $this->object->id); - } - } - - // Apply groups reductions - $this->object->setGroupReduction(); - - // Save and preview - if (Tools::isSubmit('submitAddProductAndPreview')) { - $this->redirect_after = $this->getPreviewUrl($this->object); - } - - // Save and stay on same form - if ($this->display == 'edit') { - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) $this->object->id - . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') - . '&updateproduct&conf=3&key_tab=' . Tools::safeOutput(Tools::getValue('key_tab')) . '&token=' . $this->token; - } else { - // Default behavior (save and back) - $this->redirect_after = self::$currentIndex - . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') - . '&conf=3&token=' . $this->token; - } - } else { - $this->object->delete(); - // if errors : stay on edit page - $this->display = 'edit'; - } - } else { - $this->errors[] = Tools::displayError('An error occurred while creating an object.') . ' ' . $this->table . ''; - } - - return $this->object; - } - - protected function isTabSubmitted($tab_name) - { - if (!is_array($this->submitted_tabs)) { - $this->submitted_tabs = Tools::getValue('submitted_tabs'); - } - - if (is_array($this->submitted_tabs) && in_array($tab_name, $this->submitted_tabs)) { - return true; - } - - return false; - } - - public function processStatus() - { - $this->loadObject(true); - if (!Validate::isLoadedObject($this->object)) { - return false; - } - if (($error = $this->object->validateFields(false, true)) !== true) { - $this->errors[] = $error; - } - if (($error = $this->object->validateFieldsLang(false, true)) !== true) { - $this->errors[] = $error; - } - - if (count($this->errors)) { - return false; - } - - $res = parent::processStatus(); - - return $res; - } - - public function processUpdate() - { - $existing_product = $this->object; - - $this->checkProduct(); - - if (!empty($this->errors)) { - $this->display = 'edit'; - - return false; - } - - $id = (int) Tools::getValue('id_' . $this->table); - /* Update an existing product */ - if (isset($id) && !empty($id)) { - $object = new $this->className((int) $id); - $this->object = $object; - - if (Validate::isLoadedObject($object)) { - $this->_removeTaxFromEcotax(); - $product_type_before = $object->getType(); - $this->copyFromPost($object, $this->table); - $object->indexed = 0; - - if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP) { - $object->setFieldsToUpdate((array) Tools::getValue('multishop_check', [])); - } - - // Duplicate combinations if not associated to shop - if ($this->context->shop->getContext() == Shop::CONTEXT_SHOP && !$object->isAssociatedToShop()) { - $is_associated_to_shop = false; - $combinations = Product::getProductAttributesIds($object->id); - if ($combinations) { - foreach ($combinations as $id_combination) { - $combination = new Combination((int) $id_combination['id_product_attribute']); - $default_combination = new Combination((int) $id_combination['id_product_attribute'], null, (int) $this->object->id_shop_default); - - $def = ObjectModel::getDefinition($default_combination); - foreach ($def['fields'] as $field_name => $row) { - $combination->$field_name = ObjectModel::formatValue($default_combination->$field_name, $def['fields'][$field_name]['type']); - } - - $combination->save(); - } - } - } else { - $is_associated_to_shop = true; - } - - if ($object->update()) { - // If the product doesn't exist in the current shop but exists in another shop - if (Shop::getContext() == Shop::CONTEXT_SHOP && !$existing_product->isAssociatedToShop($this->context->shop->id)) { - $out_of_stock = StockAvailable::outOfStock($existing_product->id, $existing_product->id_shop_default); - StockAvailable::setProductOutOfStock((int) $this->object->id, $out_of_stock, $this->context->shop->id); - } - - PrestaShopLogger::addLog(sprintf($this->l('%s modification', 'AdminTab', false, false), $this->className), 1, null, $this->className, (int) $this->object->id, true, (int) $this->context->employee->id); - if (in_array($this->context->shop->getContext(), [Shop::CONTEXT_SHOP, Shop::CONTEXT_ALL])) { - if ($this->isTabSubmitted('Shipping')) { - $this->addCarriers(); - } - if ($this->isTabSubmitted('Associations')) { - $this->updateAccessories($object); - } - if ($this->isTabSubmitted('Suppliers')) { - $this->processSuppliers(); - } - if ($this->isTabSubmitted('Features')) { - $this->processFeatures(); - } - if ($this->isTabSubmitted('Combinations')) { - $this->processProductAttribute(); - } - if ($this->isTabSubmitted('Prices')) { - $this->processPriceAddition(); - $this->processSpecificPricePriorities(); - } - if ($this->isTabSubmitted('Customization')) { - $this->processCustomizationConfiguration(); - } - if ($this->isTabSubmitted('Attachments')) { - $this->processAttachments(); - } - if ($this->isTabSubmitted('Images')) { - $this->processImageLegends(); - } - - $this->updatePackItems($object); - $this->updateDownloadProduct($object, 1); - $this->updateTags(Language::getLanguages(false), $object); - - if ($this->isProductFieldUpdated('category_box') && !$object->updateCategories(Tools::getValue('categoryBox'))) { - $this->errors[] = Tools::displayError('An error occurred while linking the object.') . ' ' . $this->table . ' ' . Tools::displayError('To categories'); - } - } - - if (empty($this->errors)) { - if (in_array($object->visibility, ['both', 'search']) && Configuration::get('PS_SEARCH_INDEXATION')) { - Search::indexation(false, $object->id); - } - - // Save and preview - if (Tools::isSubmit('submitAddProductAndPreview')) { - $this->redirect_after = $this->getPreviewUrl($object); - } else { - // Save and stay on same form - if ($this->display == 'edit') { - $this->confirmations[] = $this->l('Update successful'); - $this->redirect_after = self::$currentIndex . '&id_product=' . (int) $this->object->id - . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') - . '&updateproduct&conf=4&key_tab=' . Tools::safeOutput(Tools::getValue('key_tab')) . '&token=' . $this->token; - } else { - // Default behavior (save and back) - $this->redirect_after = self::$currentIndex . (Tools::getIsset('id_category') ? '&id_category=' . (int) Tools::getValue('id_category') : '') . '&conf=4&token=' . $this->token; - } - } - } else { - // if errors : stay on edit page - $this->display = 'edit'; - } - } else { - if (!$is_associated_to_shop && $combinations) { - foreach ($combinations as $id_combination) { - $combination = new Combination((int) $id_combination['id_product_attribute']); - $combination->delete(); - } - } - $this->errors[] = Tools::displayError('An error occurred while updating an object.') . ' ' . $this->table . ' (' . Db::getInstance()->getMsgError() . ')'; - } - } else { - $this->errors[] = Tools::displayError('An error occurred while updating an object.') . ' ' . $this->table . ' (' . Tools::displayError('The object cannot be loaded. ') . ')'; - } - - return $object; - } - } - - /** - * Check that a saved product is valid - */ - public function checkProduct() - { - $className = 'Product'; - /** @todo : the call_user_func seems to contains only statics values (className = 'Product') */ - $rules = call_user_func([$this->className, 'getValidationRules'], $this->className); - $default_language = new Language((int) Configuration::get('PS_LANG_DEFAULT')); - $languages = Language::getLanguages(false); - - // Check required fields - foreach ($rules['required'] as $field) { - if (!$this->isProductFieldUpdated($field)) { - continue; - } - - if (($value = Tools::getValue($field)) == false && $value != '0') { - if (Tools::getValue('id_' . $this->table) && $field == 'passwd') { - continue; - } - $this->errors[] = sprintf( - Tools::displayError('The %s field is required.'), - call_user_func([$className, 'displayFieldName'], $field, $className) - ); - } - } - - // Check multilingual required fields - foreach ($rules['requiredLang'] as $fieldLang) { - if ($this->isProductFieldUpdated($fieldLang, $default_language->id) && !Tools::getValue($fieldLang . '_' . $default_language->id)) { - $this->errors[] = sprintf( - Tools::displayError('This %1$s field is required at least in %2$s'), - call_user_func([$className, 'displayFieldName'], $fieldLang, $className), - $default_language->name - ); - } - } - - // Check fields sizes - foreach ($rules['size'] as $field => $maxLength) { - if ($this->isProductFieldUpdated($field) && ($value = Tools::getValue($field)) && Tools::strlen($value) > $maxLength) { - $this->errors[] = sprintf( - Tools::displayError('The %1$s field is too long (%2$d chars max).'), - call_user_func([$className, 'displayFieldName'], $field, $className), - $maxLength - ); - } - } - - if (Tools::getIsset('description_short') && $this->isProductFieldUpdated('description_short')) { - $saveShort = Tools::getValue('description_short'); - $_POST['description_short'] = strip_tags(Tools::getValue('description_short')); - } - - // Check description short size without html - $limit = (int) Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT'); - if ($limit <= 0) { - $limit = 400; - } - foreach ($languages as $language) { - if ($this->isProductFieldUpdated('description_short', $language['id_lang']) && ($value = Tools::getValue('description_short_' . $language['id_lang']))) { - if (Tools::strlen(strip_tags($value)) > $limit) { - $this->errors[] = sprintf( - Tools::displayError('This %1$s field (%2$s) is too long: %3$d chars max (current count %4$d).'), - call_user_func([$className, 'displayFieldName'], 'description_short'), - $language['name'], - $limit, - Tools::strlen(strip_tags($value)) - ); - } - } - } - - // Check multilingual fields sizes - foreach ($rules['sizeLang'] as $fieldLang => $maxLength) { - foreach ($languages as $language) { - $value = Tools::getValue($fieldLang . '_' . $language['id_lang']); - if ($value && Tools::strlen($value) > $maxLength) { - $this->errors[] = sprintf( - Tools::displayError('The %1$s field is too long (%2$d chars max).'), - call_user_func([$className, 'displayFieldName'], $fieldLang, $className), - $maxLength - ); - } - } - } - - if ($this->isProductFieldUpdated('description_short') && isset($_POST['description_short'])) { - $_POST['description_short'] = $saveShort; - } - - // Check fields validity - foreach ($rules['validate'] as $field => $function) { - if ($this->isProductFieldUpdated($field) && ($value = Tools::getValue($field))) { - $res = true; - if (Tools::strtolower($function) == 'iscleanhtml') { - if (!Validate::$function($value, (int) Configuration::get('PS_ALLOW_HTML_IFRAME'))) { - $res = false; - } - } elseif (!Validate::$function($value)) { - $res = false; - } - - if (!$res) { - $this->errors[] = sprintf( - Tools::displayError('The %s field is invalid.'), - call_user_func([$className, 'displayFieldName'], $field, $className) - ); - } - } - } - // Check multilingual fields validity - foreach ($rules['validateLang'] as $fieldLang => $function) { - foreach ($languages as $language) { - if ($this->isProductFieldUpdated($fieldLang, $language['id_lang']) && ($value = Tools::getValue($fieldLang . '_' . $language['id_lang']))) { - if (!Validate::$function($value, (int) Configuration::get('PS_ALLOW_HTML_IFRAME'))) { - $this->errors[] = sprintf( - Tools::displayError('The %1$s field (%2$s) is invalid.'), - call_user_func([$className, 'displayFieldName'], $fieldLang, $className), - $language['name'] - ); - } - } - } - } - - // Categories - if ($this->isProductFieldUpdated('id_category_default') && (!Tools::isSubmit('categoryBox') || !count(Tools::getValue('categoryBox')))) { - $this->errors[] = $this->l('Products must be in at least one category.'); - } - - if ($this->isProductFieldUpdated('id_category_default') && (!is_array(Tools::getValue('categoryBox')) || !in_array(Tools::getValue('id_category_default'), Tools::getValue('categoryBox')))) { - $this->errors[] = $this->l('This product must be in the default category.'); - } - - // Tags - foreach ($languages as $language) { - if ($value = Tools::getValue('tags_' . $language['id_lang'])) { - if (!Validate::isTagsList($value)) { - $this->errors[] = sprintf( - Tools::displayError('The tags list (%s) is invalid.'), - $language['name'] - ); - } - } - } - } - - /** - * Check if a field is edited (if the checkbox is checked) - * This method will do something only for multishop with a context all / group - * - * @param string $field Name of field - * @param int $id_lang - * - * @return bool - */ - protected function isProductFieldUpdated($field, $id_lang = null) - { - // Cache this condition to improve performances - static $is_activated = null; - if (null === $is_activated) { - $is_activated = Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP && $this->id_object; - } - - if (!$is_activated) { - return true; - } - - if (null === $id_lang) { - return !empty($_POST['multishop_check'][$field]); - } else { - return !empty($_POST['multishop_check'][$field][$id_lang]); - } - } - - protected function _removeTaxFromEcotax() - { - if ($ecotax = Tools::getValue('ecotax')) { - $_POST['ecotax'] = Tools::ps_round($ecotax / (1 + Tax::getProductEcotaxRate() / 100), 6); - } - } - - protected function _applyTaxToEcotax($product) - { - if ($product->ecotax) { - $product->ecotax = Tools::ps_round($product->ecotax * (1 + Tax::getProductEcotaxRate() / 100), 2); - } - } - - /** - * Update product download - * - * @param object $product Product - * - * @return bool - */ - public function updateDownloadProduct($product, $edit = 0) - { - if ((int) Tools::getValue('is_virtual_file') == 1) { - if (isset($_FILES['virtual_product_file_uploader']) && $_FILES['virtual_product_file_uploader']['size'] > 0) { - $virtual_product_filename = ProductDownload::getNewFilename(); - $helper = new HelperUploader('virtual_product_file_uploader'); - $helper->setPostMaxSize(Tools::getOctets(ini_get('upload_max_filesize'))) - ->setSavePath(_PS_DOWNLOAD_DIR_)->upload($_FILES['virtual_product_file_uploader'], $virtual_product_filename); - } else { - $virtual_product_filename = Tools::getValue('virtual_product_filename', ProductDownload::getNewFilename()); - } - - $product->setDefaultAttribute(0); //reset cache_default_attribute - if (Tools::getValue('virtual_product_expiration_date') && !Validate::isDate(Tools::getValue('virtual_product_expiration_date'))) { - if (!Tools::getValue('virtual_product_expiration_date')) { - $this->errors[] = Tools::displayError('The expiration-date attribute is required.'); - - return false; - } - } - - // Trick's - if ($edit == 1) { - $id_product_download = (int) ProductDownload::getIdFromIdProduct((int) $product->id); - if (!$id_product_download) { - $id_product_download = (int) Tools::getValue('virtual_product_id'); - } - } else { - $id_product_download = Tools::getValue('virtual_product_id'); - } - - $is_shareable = Tools::getValue('virtual_product_is_shareable'); - $virtual_product_name = Tools::getValue('virtual_product_name'); - $virtual_product_nb_days = Tools::getValue('virtual_product_nb_days'); - $virtual_product_nb_downloable = Tools::getValue('virtual_product_nb_downloable'); - $virtual_product_expiration_date = Tools::getValue('virtual_product_expiration_date'); - - $download = new ProductDownload((int) $id_product_download); - $download->id_product = (int) $product->id; - $download->display_filename = $virtual_product_name; - $download->filename = $virtual_product_filename; - $download->date_add = date('Y-m-d H:i:s'); - $download->date_expiration = $virtual_product_expiration_date ? $virtual_product_expiration_date . ' 23:59:59' : ''; - $download->nb_days_accessible = (int) $virtual_product_nb_days; - $download->nb_downloadable = (int) $virtual_product_nb_downloable; - $download->active = 1; - $download->is_shareable = (int) $is_shareable; - - if ($download->save()) { - return true; - } - } else { - /* unactive download product if checkbox not checked */ - if ($edit == 1) { - $id_product_download = (int) ProductDownload::getIdFromIdProduct((int) $product->id); - if (!$id_product_download) { - $id_product_download = (int) Tools::getValue('virtual_product_id'); - } - } else { - $id_product_download = ProductDownload::getIdFromIdProduct($product->id); - } - - if (!empty($id_product_download)) { - $product_download = new ProductDownload((int) $id_product_download); - $product_download->date_expiration = date('Y-m-d H:i:s', time() - 1); - $product_download->active = 0; - - return $product_download->save(); - } - } - - return false; - } - - /** - * Update product accessories - * - * @param object $product Product - */ - public function updateAccessories($product) - { - $product->deleteAccessories(); - if ($accessories = Tools::getValue('inputAccessories')) { - $accessories_id = array_unique(explode('-', $accessories)); - if (count($accessories_id)) { - array_pop($accessories_id); - $product->changeAccessories($accessories_id); - } - } - } - - /** - * Update product tags - * - * @param array Languages - * @param object $product Product - * - * @return bool Update result - */ - public function updateTags($languages, $product) - { - $tag_success = true; - /* Reset all tags for THIS product */ - if (!Tag::deleteTagsForProduct((int) $product->id)) { - $this->errors[] = Tools::displayError('An error occurred while attempting to delete previous tags.'); - } - /* Assign tags to this product */ - foreach ($languages as $language) { - if ($value = Tools::getValue('tags_' . $language['id_lang'])) { - $tag_success &= Tag::addTags($language['id_lang'], (int) $product->id, $value); - } - } - - if (!$tag_success) { - $this->errors[] = Tools::displayError('An error occurred while adding tags.'); - } - - return $tag_success; - } - - public function initContent($token = null) - { - if ($this->display == 'edit' || $this->display == 'add') { - $this->fields_form = []; - - // Check if Module - if (substr($this->tab_display, 0, 6) == 'Module') { - $this->tab_display_module = strtolower(substr($this->tab_display, 6, Tools::strlen($this->tab_display) - 6)); - $this->tab_display = 'Modules'; - } - if (method_exists($this, 'initForm' . $this->tab_display)) { - $this->tpl_form = strtolower($this->tab_display) . '.tpl'; - } - - if ($this->ajax) { - $this->content_only = true; - } else { - $product_tabs = []; - - // tab_display defines which tab to display first - if (!method_exists($this, 'initForm' . $this->tab_display)) { - $this->tab_display = $this->default_tab; - } - - foreach ($this->available_tabs as $product_tab => $value) { - $product_tabs[$product_tab] = [ - 'id' => $product_tab, - 'selected' => (strtolower($product_tab) == strtolower($this->tab_display) || (isset($this->tab_display_module) && 'module' . $this->tab_display_module == Tools::strtolower($product_tab))), - 'name' => $this->available_tabs_lang[$product_tab], - 'href' => $this->context->link->getAdminLink('AdminProducts') . '&id_product=' . (int) Tools::getValue('id_product') . '&action=' . $product_tab, - ]; - } - $this->tpl_form_vars['product_tabs'] = $product_tabs; - } - } else { - if ($id_category = (int) $this->id_current_category) { - self::$currentIndex .= '&id_category=' . (int) $this->id_current_category; - } - - // If products from all categories are displayed, we don't want to use sorting by position - if (!$id_category) { - $this->_defaultOrderBy = $this->identifier; - if ($this->context->cookie->{$this->table . 'Orderby'} == 'position') { - unset( - $this->context->cookie->{$this->table . 'Orderby'}, - $this->context->cookie->{$this->table . 'Orderway'} - ); - } - } - if (!$id_category) { - $id_category = Configuration::get('PS_ROOT_CATEGORY'); - } - $this->tpl_list_vars['is_category_filter'] = (bool) $this->id_current_category; - - // Generate category selection tree - $tree = new HelperTreeCategories('categories-tree', $this->l('Filter by category')); - $tree->setAttribute('is_category_filter', (bool) $this->id_current_category) - ->setAttribute('base_url', preg_replace('#&id_category=[0-9]*#', '', self::$currentIndex) . '&token=' . $this->token) - ->setInputName('id-category') - ->setSelectedCategories([(int) $id_category]); - $this->tpl_list_vars['category_tree'] = $tree->render(); - - // used to build the new url when changing category - $this->tpl_list_vars['base_url'] = preg_replace('#&id_category=[0-9]*#', '', self::$currentIndex) . '&token=' . $this->token; - } - // @todo module free - $this->tpl_form_vars['vat_number'] = file_exists(_PS_MODULE_DIR_ . 'vatnumber/ajax.php'); - - parent::initContent(); - } - - public function renderKpis() - { - $time = time(); - $kpis = []; - - /* The data generation is located in AdminStatsControllerCore */ - - if (Configuration::get('PS_STOCK_MANAGEMENT')) { - $helper = new HelperKpi(); - $helper->id = 'box-products-stock'; - $helper->icon = 'icon-archive'; - $helper->color = 'color1'; - $helper->title = $this->l('Out of stock items', null, null, false); - if (ConfigurationKPI::get('PERCENT_PRODUCT_OUT_OF_STOCK') !== false) { - $helper->value = ConfigurationKPI::get('PERCENT_PRODUCT_OUT_OF_STOCK'); - } - $helper->source = $this->context->link->getAdminLink('AdminStats') . '&ajax=1&action=getKpi&kpi=percent_product_out_of_stock'; - $helper->tooltip = $this->l('X% of your products for sale are out of stock.', null, null, false); - $helper->refresh = (bool) (ConfigurationKPI::get('PERCENT_PRODUCT_OUT_OF_STOCK_EXPIRE') < $time); - $helper->href = Context::getContext()->link->getAdminLink('AdminProducts') . '&productFilter_sav!quantity=0&productFilter_active=1&submitFilterproduct=1'; - $kpis[] = $helper->generate(); - } - - $helper = new HelperKpi(); - $helper->id = 'box-avg-gross-margin'; - $helper->icon = 'icon-tags'; - $helper->color = 'color2'; - $helper->title = $this->l('Average Gross Margin', null, null, false); - if (ConfigurationKPI::get('PRODUCT_AVG_GROSS_MARGIN') !== false) { - $helper->value = ConfigurationKPI::get('PRODUCT_AVG_GROSS_MARGIN'); - } - $helper->source = $this->context->link->getAdminLink('AdminStats') . '&ajax=1&action=getKpi&kpi=product_avg_gross_margin'; - $helper->tooltip = $this->l('The gross margin is the difference between the retail price and the wholesale price, on all your products for sale.', null, null, false); - $helper->refresh = (bool) (ConfigurationKPI::get('PRODUCT_AVG_GROSS_MARGIN_EXPIRE') < $time); - $kpis[] = $helper->generate(); - - $helper = new HelperKpi(); - $helper->id = 'box-8020-sales-catalog'; - $helper->icon = 'icon-beaker'; - $helper->color = 'color3'; - $helper->title = $this->l('Purchased references', null, null, false); - $helper->subtitle = $this->l('30 days', null, null, false); - if (ConfigurationKPI::get('8020_SALES_CATALOG') !== false) { - $helper->value = ConfigurationKPI::get('8020_SALES_CATALOG'); - } - $helper->source = $this->context->link->getAdminLink('AdminStats') . '&ajax=1&action=getKpi&kpi=8020_sales_catalog'; - $helper->tooltip = $this->l('X% of your references have been purchased for the past 30 days', null, null, false); - $helper->refresh = (bool) (ConfigurationKPI::get('8020_SALES_CATALOG_EXPIRE') < $time); - $moduleManagerBuilder = ModuleManagerBuilder::getInstance(); - $moduleManager = $moduleManagerBuilder->build(); - - if ($moduleManager->isInstalled('statsbestproducts')) { - $helper->href = Context::getContext()->link->getAdminLink('AdminStats') . '&module=statsbestproducts&datepickerFrom=' . date('Y-m-d', strtotime('-30 days')) . '&datepickerTo=' . date('Y-m-d'); - } - $kpis[] = $helper->generate(); - - $helper = new HelperKpi(); - $helper->id = 'box-disabled-products'; - $helper->icon = 'icon-off'; - $helper->color = 'color4'; - $helper->href = $this->context->link->getAdminLink('AdminProducts'); - $helper->title = $this->l('Disabled Products', null, null, false); - if (ConfigurationKPI::get('DISABLED_PRODUCTS') !== false) { - $helper->value = ConfigurationKPI::get('DISABLED_PRODUCTS'); - } - $helper->source = $this->context->link->getAdminLink('AdminStats') . '&ajax=1&action=getKpi&kpi=disabled_products'; - $helper->refresh = (bool) (ConfigurationKPI::get('DISABLED_PRODUCTS_EXPIRE') < $time); - $helper->tooltip = $this->l('X% of your products are disabled and not visible to your customers', null, null, false); - $helper->href = Context::getContext()->link->getAdminLink('AdminProducts') . '&productFilter_active=0&submitFilterproduct=1'; - $kpis[] = $helper->generate(); - - $helper = new HelperKpiRow(); - $helper->kpis = $kpis; - - return $helper->generate(); - } - - public function renderList() - { - $this->addRowAction('edit'); - $this->addRowAction('duplicate'); - $this->addRowAction('delete'); - - return parent::renderList(); - } - - public function ajaxProcessProductManufacturers() - { - $manufacturers = Manufacturer::getManufacturers(false, 0, true, false, false, false, true); - $jsonArray = []; - if ($manufacturers) { - foreach ($manufacturers as $manufacturer) { - $jsonArray[] = '{"optionValue": "' . (int) $manufacturer['id_manufacturer'] . '", "optionDisplay": "' . htmlspecialchars(trim($manufacturer['name'])) . '"}'; - } - } - die('[' . implode(',', $jsonArray) . ']'); - } - - /** - * Build a categories tree - * - * @param array $indexedCategories Array with categories where product is indexed (in order to check checkbox) - * @param array $categories Categories to list - * @param array $current Current category - * @param int $id_category Current category id - */ - public static function recurseCategoryForInclude($id_obj, $indexedCategories, $categories, $current, $id_category = null, $id_category_default = null, $has_suite = []) - { - global $done; - static $irow; - $content = ''; - - if (!$id_category) { - $id_category = (int) Configuration::get('PS_ROOT_CATEGORY'); - } - - if (!isset($done[$current['infos']['id_parent']])) { - $done[$current['infos']['id_parent']] = 0; - } - ++$done[$current['infos']['id_parent']]; - - $todo = count($categories[$current['infos']['id_parent']]); - $doneC = $done[$current['infos']['id_parent']]; - - $level = $current['infos']['level_depth'] + 1; - - $content .= ' - - - - - - ' . $id_category . ' - - '; - for ($i = 2; $i < $level; ++$i) { - $content .= ''; - } - $content .= '   - - '; - - if ($level > 1) { - $has_suite[] = ($todo == $doneC ? 0 : 1); - } - if (isset($categories[$id_category])) { - foreach ($categories[$id_category] as $key => $row) { - if ($key != 'infos') { - $content .= static::recurseCategoryForInclude($id_obj, $indexedCategories, $categories, $categories[$id_category][$key], $key, $id_category_default, $has_suite); - } - } - } - - return $content; - } - - protected function _displayDraftWarning($active) - { - $content = '
    - ' . $this->l('Your product will be saved as a draft.') . ' - ' . $this->l('Save and preview') . ' - -
    '; - $this->tpl_form_vars['draft_warning'] = $content; - } - - public function initPageHeaderToolbar() - { - if (empty($this->display)) { - $this->page_header_toolbar_btn['new_product'] = [ - 'href' => self::$currentIndex . '&addproduct&token=' . $this->token, - 'desc' => $this->l('Add new product', null, null, false), - 'icon' => 'process-icon-new', - ]; - } - if ($this->display == 'edit') { - if (($product = $this->loadObject(true)) && $product->isAssociatedToShop()) { - // adding button for preview this product - if ($url_preview = $this->getPreviewUrl($product)) { - $this->page_header_toolbar_btn['preview'] = [ - 'short' => $this->l('Preview', null, null, false), - 'href' => $url_preview, - 'desc' => $this->l('Preview', null, null, false), - 'target' => true, - 'class' => 'previewUrl', - ]; - } - - $js = (bool) Image::getImages($this->context->language->id, (int) $product->id) ? - 'confirm_link(\'\', \'' . $this->l('This will copy the images too. If you wish to proceed, click "Yes". If not, click "No".', null, true, false) . '\', \'' . $this->l('Yes', null, true, false) . '\', \'' . $this->l('No', null, true, false) . '\', \'' . $this->context->link->getAdminLink('AdminProducts', true) . '&id_product=' . (int) $product->id . '&duplicateproduct' . '\', \'' . $this->context->link->getAdminLink('AdminProducts', true) . '&id_product=' . (int) $product->id . '&duplicateproduct&noimage=1' . '\')' - : - 'document.location = \'' . $this->context->link->getAdminLink('AdminProducts', true) . '&id_product=' . (int) $product->id . '&duplicateproduct&noimage=1' . '\''; - - // adding button for duplicate this product - if ($this->access('add')) { - $this->page_header_toolbar_btn['duplicate'] = [ - 'short' => $this->l('Duplicate', null, null, false), - 'desc' => $this->l('Duplicate', null, null, false), - 'confirm' => 1, - 'js' => $js, - ]; - } - - // adding button for preview this product statistics - if (file_exists(_PS_MODULE_DIR_ . 'statsproduct/statsproduct.php')) { - $this->page_header_toolbar_btn['stats'] = [ - 'short' => $this->l('Statistics', null, null, false), - 'href' => $this->context->link->getAdminLink('AdminStats') . '&module=statsproduct&id_product=' . (int) $product->id, - 'desc' => $this->l('Product sales', null, null, false), - ]; - } - - // adding button for delete this product - if ($this->access('delete')) { - $this->page_header_toolbar_btn['delete'] = [ - 'short' => $this->l('Delete', null, null, false), - 'href' => $this->context->link->getAdminLink('AdminProducts') . '&id_product=' . (int) $product->id . '&deleteproduct', - 'desc' => $this->l('Delete this product', null, null, false), - 'confirm' => 1, - 'js' => 'if (confirm(\'' . $this->l('Delete product?', null, true, false) . '\')){return true;}else{event.preventDefault();}', - ]; - } - } - } - parent::initPageHeaderToolbar(); - } - - public function initToolbar() - { - parent::initToolbar(); - if ($this->display == 'edit' || $this->display == 'add') { - $this->toolbar_btn['save'] = [ - 'short' => 'Save', - 'href' => '#', - 'desc' => $this->l('Save'), - ]; - - $this->toolbar_btn['save-and-stay'] = [ - 'short' => 'SaveAndStay', - 'href' => '#', - 'desc' => $this->l('Save and stay'), - ]; - - // adding button for adding a new combination in Combination tab - $this->toolbar_btn['newCombination'] = [ - 'short' => 'New combination', - 'desc' => $this->l('New combination'), - 'class' => 'toolbar-new', - ]; - } else { - $this->toolbar_btn['import'] = [ - 'href' => $this->context->link->getAdminLink('AdminImport', true) . '&import_type=products', - 'desc' => $this->l('Import'), - ]; - } - - $this->context->smarty->assign('toolbar_scroll', 1); - $this->context->smarty->assign('show_toolbar', 1); - $this->context->smarty->assign('toolbar_btn', $this->toolbar_btn); - } - - /** - * renderForm contains all necessary initialization needed for all tabs - * - * @return string|void - */ - public function renderForm() - { - // This nice code (irony) is here to store the product name, because the row after will erase product name in multishop context - $this->product_name = $this->object->name[$this->context->language->id]; - - if (!method_exists($this, 'initForm' . $this->tab_display)) { - return; - } - - $product = $this->object; - - // Product for multishop - $this->context->smarty->assign('bullet_common_field', ''); - if (Shop::isFeatureActive() && $this->display == 'edit') { - if (Shop::getContext() != Shop::CONTEXT_SHOP) { - $this->context->smarty->assign([ - 'display_multishop_checkboxes' => true, - 'multishop_check' => Tools::getValue('multishop_check'), - ]); - } - - if (Shop::getContext() != Shop::CONTEXT_ALL) { - $this->context->smarty->assign('bullet_common_field', ''); - $this->context->smarty->assign('display_common_field', true); - } - } - - $this->tpl_form_vars['tabs_preloaded'] = $this->available_tabs; - - $this->tpl_form_vars['product_type'] = (int) Tools::getValue('type_product', $product->getType()); - - $this->getLanguages(); - - $this->tpl_form_vars['id_lang_default'] = Configuration::get('PS_LANG_DEFAULT'); - - $this->tpl_form_vars['currentIndex'] = self::$currentIndex; - $this->tpl_form_vars['display_multishop_checkboxes'] = (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_SHOP && $this->display == 'edit'); - $this->fields_form = ['']; - - $this->tpl_form_vars['token'] = $this->token; - $this->tpl_form_vars['combinationImagesJs'] = $this->getCombinationImagesJs(); - $this->tpl_form_vars['PS_ALLOW_ACCENTED_CHARS_URL'] = (int) Configuration::get('PS_ALLOW_ACCENTED_CHARS_URL'); - $this->tpl_form_vars['post_data'] = json_encode($_POST); - $this->tpl_form_vars['save_error'] = !empty($this->errors); - $this->tpl_form_vars['mod_evasive'] = Tools::apacheModExists('evasive'); - $this->tpl_form_vars['mod_security'] = Tools::apacheModExists('security'); - $this->tpl_form_vars['ps_force_friendly_product'] = Configuration::get('PS_FORCE_FRIENDLY_PRODUCT'); - - // autoload rich text editor (tiny mce) - $this->tpl_form_vars['tinymce'] = true; - $iso = $this->context->language->iso_code; - $this->tpl_form_vars['iso'] = file_exists(_PS_CORE_DIR_ . '/js/tiny_mce/langs/' . $iso . '.js') ? $iso : 'en'; - $this->tpl_form_vars['path_css'] = _THEME_CSS_DIR_; - $this->tpl_form_vars['ad'] = __PS_BASE_URI__ . basename(_PS_ADMIN_DIR_); - - if (Validate::isLoadedObject(($this->object))) { - $id_product = (int) $this->object->id; - } else { - $id_product = (int) Tools::getvalue('id_product'); - } - - $this->tpl_form_vars['form_action'] = $this->context->link->getAdminLink('AdminProducts') . '&' . ($id_product ? 'id_product=' . (int) $id_product : 'addproduct'); - $this->tpl_form_vars['id_product'] = $id_product; - - // Transform configuration option 'upload_max_filesize' in octets - $upload_max_filesize = Tools::getOctets(ini_get('upload_max_filesize')); - - // Transform configuration option 'upload_max_filesize' in MegaOctets - $upload_max_filesize = ($upload_max_filesize / 1024) / 1024; - - $this->tpl_form_vars['upload_max_filesize'] = $upload_max_filesize; - $this->tpl_form_vars['country_display_tax_label'] = $this->context->country->display_tax_label; - $this->tpl_form_vars['has_combinations'] = $this->object->hasAttributes(); - $this->product_exists_in_shop = true; - - if ($this->display == 'edit' && Validate::isLoadedObject($product) && Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_SHOP && !$product->isAssociatedToShop($this->context->shop->id)) { - $this->product_exists_in_shop = false; - if ($this->tab_display == 'Informations') { - $this->displayWarning($this->l('Warning: The product does not exist in this shop')); - } - - $default_product = new Product(); - $definition = ObjectModel::getDefinition($product); - foreach ($definition['fields'] as $field_name => $field) { - if (isset($field['shop']) && $field['shop']) { - $product->$field_name = ObjectModel::formatValue($default_product->$field_name, $field['type']); - } - } - } - - // let's calculate this once for all - if (!Validate::isLoadedObject($this->object) && Tools::getValue('id_product')) { - $this->errors[] = 'Unable to load object'; - } else { - $this->_displayDraftWarning($this->object->active); - - // if there was an error while saving, we don't want to lose posted data - if (!empty($this->errors)) { - $this->copyFromPost($this->object, $this->table); - } - - $this->initPack($this->object); - $this->{'initForm' . $this->tab_display}($this->object); - $this->tpl_form_vars['product'] = $this->object; - - if ($this->ajax) { - if (!isset($this->tpl_form_vars['custom_form'])) { - throw new PrestaShopException('custom_form empty for action ' . $this->tab_display); - } else { - return $this->tpl_form_vars['custom_form']; - } - } - } - - $parent = parent::renderForm(); - $this->addJqueryPlugin(['autocomplete', 'fancybox', 'typewatch']); - - return $parent; - } - - public function getPreviewUrl(Product $product) - { - $id_lang = Configuration::get('PS_LANG_DEFAULT', null, null, Context::getContext()->shop->id); - - if (!ShopUrl::getMainShopDomain()) { - return false; - } - - $is_rewrite_active = (bool) Configuration::get('PS_REWRITING_SETTINGS'); - $preview_url = $this->context->link->getProductLink( - $product, - $this->getFieldValue($product, 'link_rewrite', $this->context->language->id), - Category::getLinkRewrite($this->getFieldValue($product, 'id_category_default'), $this->context->language->id), - null, - $id_lang, - (int) Context::getContext()->shop->id, - 0, - $is_rewrite_active - ); - - if (!$product->active) { - $admin_dir = dirname($_SERVER['PHP_SELF']); - $admin_dir = substr($admin_dir, strrpos($admin_dir, '/') + 1); - $preview_url .= ((strpos($preview_url, '?') === false) ? '?' : '&') . 'adtoken=' . $this->token . '&ad=' . $admin_dir . '&id_employee=' . (int) $this->context->employee->id; - } - - return $preview_url; - } - - /** - * Post treatment for suppliers - */ - public function processSuppliers() - { - if ((int) Tools::getValue('supplier_loaded') === 1 && Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - // Get all id_product_attribute - $attributes = $product->getAttributesResume($this->context->language->id); - if (empty($attributes)) { - $attributes[] = [ - 'id_product_attribute' => 0, - 'attribute_designation' => '', - ]; - } - - // Get all available suppliers - $suppliers = Supplier::getSuppliers(); - - // Get already associated suppliers - $associated_suppliers = ProductSupplier::getSupplierCollection($product->id); - - $suppliers_to_associate = []; - $new_default_supplier = 0; - - if (Tools::isSubmit('default_supplier')) { - $new_default_supplier = (int) Tools::getValue('default_supplier'); - } - - // Get new associations - foreach ($suppliers as $supplier) { - if (Tools::isSubmit('check_supplier_' . $supplier['id_supplier'])) { - $suppliers_to_associate[] = $supplier['id_supplier']; - } - } - - // Delete already associated suppliers if needed - foreach ($associated_suppliers as $key => $associated_supplier) { - if (!in_array($associated_supplier->id_supplier, $suppliers_to_associate)) { - $associated_supplier->delete(); - unset($associated_suppliers[$key]); - } - } - - // Associate suppliers - foreach ($suppliers_to_associate as $id) { - $to_add = true; - foreach ($associated_suppliers as $as) { - if ($id == $as->id_supplier) { - $to_add = false; - } - } - - if ($to_add) { - $product_supplier = new ProductSupplier(); - $product_supplier->id_product = $product->id; - $product_supplier->id_product_attribute = 0; - $product_supplier->id_supplier = $id; - if ($this->context->currency->id) { - $product_supplier->id_currency = (int) $this->context->currency->id; - } else { - $product_supplier->id_currency = Currency::getDefaultCurrencyId(); - } - $product_supplier->save(); - - $associated_suppliers[] = $product_supplier; - foreach ($attributes as $attribute) { - if ((int) $attribute['id_product_attribute'] > 0) { - $product_supplier = new ProductSupplier(); - $product_supplier->id_product = $product->id; - $product_supplier->id_product_attribute = (int) $attribute['id_product_attribute']; - $product_supplier->id_supplier = $id; - $product_supplier->save(); - } - } - } - } - - // Manage references and prices - foreach ($attributes as $attribute) { - foreach ($associated_suppliers as $supplier) { - if (Tools::isSubmit('supplier_reference_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier) || - (Tools::isSubmit('product_price_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier) && - Tools::isSubmit('product_price_currency_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier))) { - $reference = pSQL( - Tools::getValue( - 'supplier_reference_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier, - '' - ) - ); - - $price = (float) str_replace( - [' ', ','], - ['', '.'], - Tools::getValue( - 'product_price_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier, - 0 - ) - ); - - $price = Tools::ps_round($price, 6); - - $id_currency = (int) Tools::getValue( - 'product_price_currency_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier, - 0 - ); - - if ($id_currency <= 0 || (!($result = Currency::getCurrency($id_currency)) || empty($result))) { - $this->errors[] = Tools::displayError('The selected currency is not valid'); - } - - // Save product-supplier data - $product_supplier_id = (int) ProductSupplier::getIdByProductAndSupplier($product->id, $attribute['id_product_attribute'], $supplier->id_supplier); - - if (!$product_supplier_id) { - $product->addSupplierReference($supplier->id_supplier, (int) $attribute['id_product_attribute'], $reference, (float) $price, (int) $id_currency); - if ($product->id_supplier == $supplier->id_supplier) { - if ((int) $attribute['id_product_attribute'] > 0) { - $data = [ - 'supplier_reference' => pSQL($reference), - 'wholesale_price' => (float) Tools::convertPrice($price, $id_currency), - ]; - $where = ' - a.id_product = ' . (int) $product->id . ' - AND a.id_product_attribute = ' . (int) $attribute['id_product_attribute']; - ObjectModel::updateMultishopTable('Combination', $data, $where); - } else { - $product->wholesale_price = (float) Tools::convertPrice($price, $id_currency); //converted in the default currency - $product->supplier_reference = pSQL($reference); - $product->update(); - } - } - } else { - $product_supplier = new ProductSupplier($product_supplier_id); - $product_supplier->id_currency = (int) $id_currency; - $product_supplier->product_supplier_price_te = (float) $price; - $product_supplier->product_supplier_reference = pSQL($reference); - $product_supplier->update(); - } - } elseif (Tools::isSubmit('supplier_reference_' . $product->id . '_' . $attribute['id_product_attribute'] . '_' . $supplier->id_supplier)) { - //int attribute with default values if possible - if ((int) $attribute['id_product_attribute'] > 0) { - $product_supplier = new ProductSupplier(); - $product_supplier->id_product = $product->id; - $product_supplier->id_product_attribute = (int) $attribute['id_product_attribute']; - $product_supplier->id_supplier = $supplier->id_supplier; - $product_supplier->save(); - } - } - } - } - // Manage defaut supplier for product - if ($new_default_supplier != $product->id_supplier) { - $this->object->id_supplier = $new_default_supplier; - $this->object->update(); - } - } - } - - public function initFormAssociations($obj) - { - $product = $obj; - $data = $this->createTemplate($this->tpl_form); - // Prepare Categories tree for display in Associations tab - $root = Category::getRootCategory(); - $default_category = $this->context->cookie->id_category_products_filter ? $this->context->cookie->id_category_products_filter : Context::getContext()->shop->id_category; - if (!$product->id || !$product->isAssociatedToShop()) { - $selected_cat = Category::getCategoryInformation(Tools::getValue('categoryBox', [$default_category]), $this->default_form_language); - } else { - if (Tools::isSubmit('categoryBox')) { - $selected_cat = Category::getCategoryInformation(Tools::getValue('categoryBox', [$default_category]), $this->default_form_language); - } else { - $selected_cat = Product::getProductCategoriesFull($product->id, $this->default_form_language); - } - } - - // Multishop block - $data->assign('feature_shop_active', Shop::isFeatureActive()); - $helper = new HelperForm(); - if ($this->object && $this->object->id) { - $helper->id = $this->object->id; - } else { - $helper->id = null; - } - $helper->table = $this->table; - $helper->identifier = $this->identifier; - - // Accessories block - $accessories = Product::getAccessoriesLight($this->context->language->id, $product->id); - - if ($post_accessories = Tools::getValue('inputAccessories')) { - $post_accessories_tab = explode('-', $post_accessories); - foreach ($post_accessories_tab as $accessory_id) { - if (!$this->haveThisAccessory($accessory_id, $accessories) && $accessory = Product::getAccessoryById($accessory_id)) { - $accessories[] = $accessory; - } - } - } - $data->assign('accessories', $accessories); - - $product->manufacturer_name = Manufacturer::getNameById($product->id_manufacturer); - - $categories = []; - foreach ($selected_cat as $key => $category) { - $categories[] = $key; - } - - $tree = new HelperTreeCategories('associated-categories-tree', 'Associated categories'); - $tree->setTemplate('tree_associated_categories.tpl') - ->setHeaderTemplate('tree_associated_header.tpl') - ->setRootCategory($root->id) - ->setUseCheckBox(true) - ->setUseSearch(true) - ->setSelectedCategories($categories); - - $data->assign(['default_category' => $default_category, - 'selected_cat_ids' => implode(',', array_keys($selected_cat)), - 'selected_cat' => $selected_cat, - 'id_category_default' => $product->getDefaultCategory(), - 'category_tree' => $tree->render(), - 'product' => $product, - 'link' => $this->context->link, - 'is_shop_context' => Shop::getContext() == Shop::CONTEXT_SHOP, - ]); - - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - public function initFormPrices($obj) - { - $data = $this->createTemplate($this->tpl_form); - $product = $obj; - if ($obj->id) { - $shops = Shop::getShops(); - $countries = Country::getCountries($this->context->language->id); - $groups = Group::getGroups($this->context->language->id); - $currencies = Currency::getCurrencies(); - $attributes = $obj->getAttributesGroups((int) $this->context->language->id); - $combinations = []; - foreach ($attributes as $attribute) { - $combinations[$attribute['id_product_attribute']]['id_product_attribute'] = $attribute['id_product_attribute']; - if (!isset($combinations[$attribute['id_product_attribute']]['attributes'])) { - $combinations[$attribute['id_product_attribute']]['attributes'] = ''; - } - $combinations[$attribute['id_product_attribute']]['attributes'] .= $attribute['attribute_name'] . ' - '; - - $combinations[$attribute['id_product_attribute']]['price'] = $this->context->getContextLocale()->formatPrice( - Tools::convertPrice( - Product::getPriceStatic((int) $obj->id, false, $attribute['id_product_attribute']), - $this->context->currency - ), - $this->context->currency->iso_code - ); - } - foreach ($combinations as &$combination) { - $combination['attributes'] = rtrim($combination['attributes'], ' - '); - } - $data->assign( - 'specificPriceModificationForm', - $this->_displaySpecificPriceModificationForm( - $this->context->currency, - $shops, - $currencies, - $countries, - $groups - ) - ); - - $data->assign('ecotax_tax_excl', $obj->ecotax); - $this->_applyTaxToEcotax($obj); - - $data->assign([ - 'shops' => $shops, - 'admin_one_shop' => count($this->context->employee->getAssociatedShops()) == 1, - 'currencies' => $currencies, - 'countries' => $countries, - 'groups' => $groups, - 'combinations' => $combinations, - 'multi_shop' => Shop::isFeatureActive(), - 'link' => new Link(), - 'pack' => new Pack(), - ]); - } else { - $this->displayWarning($this->l('You must save this product before adding specific pricing')); - $product->id_tax_rules_group = (int) Product::getIdTaxRulesGroupMostUsed(); - $data->assign('ecotax_tax_excl', 0); - } - - $address = new Address(); - $address->id_country = (int) $this->context->country->id; - $tax_rules_groups = TaxRulesGroup::getTaxRulesGroups(true); - $tax_rates = [ - 0 => [ - 'id_tax_rules_group' => 0, - 'rates' => [0], - 'computation_method' => 0, - ], - ]; - - foreach ($tax_rules_groups as $tax_rules_group) { - $id_tax_rules_group = (int) $tax_rules_group['id_tax_rules_group']; - $tax_calculator = TaxManagerFactory::getManager($address, $id_tax_rules_group)->getTaxCalculator(); - $tax_rates[$id_tax_rules_group] = [ - 'id_tax_rules_group' => $id_tax_rules_group, - 'rates' => [], - 'computation_method' => (int) $tax_calculator->computation_method, - ]; - - if (isset($tax_calculator->taxes) && count($tax_calculator->taxes)) { - foreach ($tax_calculator->taxes as $tax) { - $tax_rates[$id_tax_rules_group]['rates'][] = (float) $tax->rate; - } - } else { - $tax_rates[$id_tax_rules_group]['rates'][] = 0; - } - } - - // prices part - $data->assign([ - 'link' => $this->context->link, - 'currency' => $currency = $this->context->currency, - 'tax_rules_groups' => $tax_rules_groups, - 'taxesRatesByGroup' => $tax_rates, - 'ecotaxTaxRate' => Tax::getProductEcotaxRate(), - 'tax_exclude_taxe_option' => Tax::excludeTaxeOption(), - 'ps_use_ecotax' => Configuration::get('PS_USE_ECOTAX'), - ]); - - $product->price = Tools::convertPrice($product->price, $this->context->currency, true, $this->context); - if ($product->unit_price_ratio != 0) { - $data->assign('unit_price', Tools::ps_round($product->price / $product->unit_price_ratio, 6)); - } else { - $data->assign('unit_price', 0); - } - $data->assign('ps_tax', Configuration::get('PS_TAX')); - - $data->assign('country_display_tax_label', $this->context->country->display_tax_label); - $data->assign([ - 'currency', $this->context->currency, - 'product' => $product, - 'token' => $this->token, - ]); - - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - public function initFormSeo($product) - { - if (!$this->default_form_language) { - $this->getLanguages(); - } - - $data = $this->createTemplate($this->tpl_form); - - $context = Context::getContext(); - $rewritten_links = []; - foreach ($this->_languages as $language) { - $category = Category::getLinkRewrite((int) $product->id_category_default, (int) $language['id_lang']); - $rewritten_links[(int) $language['id_lang']] = explode( - '[REWRITE]', - $context->link->getProductLink($product, '[REWRITE]', $category, null, (int) $language['id_lang']) - ); - } - - $data->assign([ - 'product' => $product, - 'languages' => $this->_languages, - 'id_lang' => $this->context->language->id, - 'ps_ssl_enabled' => Configuration::get('PS_SSL_ENABLED'), - 'curent_shop_url' => $this->context->shop->getBaseURL(), - 'default_form_language' => $this->default_form_language, - 'rewritten_links' => $rewritten_links, - ]); - - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - /** - * Get an array of pack items for display from the product object if specified, else from POST/GET values - * - * @param Product $product - * - * @return array of pack items - */ - public function getPackItems($product = null) - { - $pack_items = []; - - if (!$product) { - $names_input = Tools::getValue('namePackItems'); - $ids_input = Tools::getValue('inputPackItems'); - if (!$names_input || !$ids_input) { - return []; - } - // ids is an array of string with format : QTYxID - $ids = array_unique(explode('-', $ids_input)); - $names = array_unique(explode('¤', $names_input)); - - if (!empty($ids)) { - $length = count($ids); - for ($i = 0; $i < $length; ++$i) { - if (!empty($ids[$i]) && !empty($names[$i])) { - list($pack_items[$i]['pack_quantity'], $pack_items[$i]['id']) = explode('x', $ids[$i]); - $exploded_name = explode('x', $names[$i]); - $pack_items[$i]['name'] = $exploded_name[1]; - } - } - } - } else { - $i = 0; - foreach ($product->packItems as $pack_item) { - $pack_items[$i]['id'] = $pack_item->id; - $pack_items[$i]['pack_quantity'] = $pack_item->pack_quantity; - $pack_items[$i]['name'] = $pack_item->name; - $pack_items[$i]['reference'] = $pack_item->reference; - $pack_items[$i]['id_product_attribute'] = isset($pack_item->id_pack_product_attribute) && $pack_item->id_pack_product_attribute ? $pack_item->id_pack_product_attribute : 0; - $cover = $pack_item->id_pack_product_attribute ? Product::getCombinationImageById($pack_item->id_pack_product_attribute, Context::getContext()->language->id) : Product::getCover($pack_item->id); - $pack_items[$i]['image'] = Context::getContext()->link->getImageLink($pack_item->link_rewrite, $cover['id_image'], 'home_default'); - // @todo: don't rely on 'home_default' - //$path_to_image = _PS_IMG_DIR_.'p/'.Image::getImgFolderStatic($cover['id_image']).(int)$cover['id_image'].'.jpg'; - //$pack_items[$i]['image'] = ImageManager::thumbnail($path_to_image, 'pack_mini_'.$pack_item->id.'_'.$this->context->shop->id.'.jpg', 120); - ++$i; - } - } - - return $pack_items; - } - - public function initFormPack($product) - { - $data = $this->createTemplate($this->tpl_form); - - // If pack items have been submitted, we want to display them instead of the actuel content of the pack - // in database. In case of a submit error, the posted data is not lost and can be sent again. - if (Tools::getValue('namePackItems')) { - $input_pack_items = Tools::getValue('inputPackItems'); - $input_namepack_items = Tools::getValue('namePackItems'); - $pack_items = $this->getPackItems(); - } else { - $product->packItems = Pack::getItems($product->id, $this->context->language->id); - $pack_items = $this->getPackItems($product); - $input_namepack_items = ''; - $input_pack_items = ''; - foreach ($pack_items as $pack_item) { - $input_pack_items .= $pack_item['pack_quantity'] . 'x' . $pack_item['id'] . 'x' . $pack_item['id_product_attribute'] . '-'; - $input_namepack_items .= $pack_item['pack_quantity'] . ' x ' . $pack_item['name'] . '¤'; - } - } - - $data->assign([ - 'input_pack_items' => $input_pack_items, - 'input_namepack_items' => $input_namepack_items, - 'pack_items' => $pack_items, - 'product_type' => (int) Tools::getValue('type_product', $product->getType()), - ]); - - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - public function initFormVirtualProduct($product) - { - $data = $this->createTemplate($this->tpl_form); - - $currency = $this->context->currency; - - /* - * Form for adding a virtual product like software, mp3, etc... - */ - $product_download = new ProductDownload(); - if ($id_product_download = $product_download->getIdFromIdProduct($this->getFieldValue($product, 'id'))) { - $product_download = new ProductDownload($id_product_download); - } - $product->{'productDownload'} = $product_download; - - if ($product->productDownload->id && empty($product->productDownload->display_filename)) { - $this->errors[] = Tools::displayError('A file name is required in order to associate a file'); - $this->tab_display = 'VirtualProduct'; - } - - /** @todo handle is_virtual with the value of the product */ - $exists_file = realpath(_PS_DOWNLOAD_DIR_) . '/' . $product->productDownload->filename; - $data->assign('product_downloaded', $product->productDownload->id); - - if (!file_exists($exists_file) - && !empty($product->productDownload->display_filename) - && empty($product->cache_default_attribute)) { - $msg = sprintf( - Tools::displayError('File "%s" is missing'), - $product->productDownload->display_filename - ); - } else { - $msg = ''; - } - - $virtual_product_file_uploader = new HelperUploader('virtual_product_file_uploader'); - $virtual_product_file_uploader->setMultiple(false)->setUrl( - Context::getContext()->link->getAdminLink('AdminProducts') . '&ajax=1&id_product=' . (int) $product->id - . '&action=AddVirtualProductFile' - )->setPostMaxSize(Tools::getOctets(ini_get('upload_max_filesize'))) - ->setTemplate('virtual_product.tpl'); - - $data->assign([ - 'download_product_file_missing' => $msg, - 'download_dir_writable' => ProductDownload::checkWritableDir(), - 'up_filename' => (string) (Tools::getValue('virtual_product_filename')), - ]); - - $product->productDownload->nb_downloadable = ($product->productDownload->id > 0) ? $product->productDownload->nb_downloadable : htmlentities(Tools::getValue('virtual_product_nb_downloable'), ENT_COMPAT, 'UTF-8'); - $product->productDownload->date_expiration = ($product->productDownload->id > 0) ? ((!empty($product->productDownload->date_expiration) && $product->productDownload->date_expiration != '0000-00-00 00:00:00') ? date('Y-m-d', strtotime($product->productDownload->date_expiration)) : '') : htmlentities(Tools::getValue('virtual_product_expiration_date'), ENT_COMPAT, 'UTF-8'); - $product->productDownload->nb_days_accessible = ($product->productDownload->id > 0) ? $product->productDownload->nb_days_accessible : htmlentities(Tools::getValue('virtual_product_nb_days'), ENT_COMPAT, 'UTF-8'); - $product->productDownload->is_shareable = $product->productDownload->id > 0 && $product->productDownload->is_shareable; - - $iso_tiny_mce = $this->context->language->iso_code; - $iso_tiny_mce = (file_exists(_PS_JS_DIR_ . 'tiny_mce/langs/' . $iso_tiny_mce . '.js') ? $iso_tiny_mce : 'en'); - $data->assign([ - 'ad' => __PS_BASE_URI__ . basename(_PS_ADMIN_DIR_), - 'iso_tiny_mce' => $iso_tiny_mce, - 'product' => $product, - 'token' => $this->token, - 'currency' => $currency, - 'link' => $this->context->link, - 'is_file' => $product->productDownload->checkFile(), - 'virtual_product_file_uploader' => $virtual_product_file_uploader->render(), - ]); - $data->assign($this->tpl_form_vars); - $this->tpl_form_vars['product'] = $product; - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - protected function _getFinalPrice($specific_price, $product_price, $tax_rate) - { - return $this->object->getPrice(false, $specific_price['id_product_attribute'], 2); - } - - protected function _displaySpecificPriceModificationForm($defaultCurrency, $shops, $currencies, $countries, $groups) - { - if (!($obj = $this->loadObject())) { - return; - } - - $content = ''; - $specific_prices = SpecificPrice::getByProductId((int) $obj->id); - $specific_price_priorities = SpecificPrice::getPriority((int) $obj->id); - - $tmp = []; - foreach ($shops as $shop) { - $tmp[$shop['id_shop']] = $shop; - } - $shops = $tmp; - $tmp = []; - foreach ($currencies as $currency) { - $tmp[$currency['id_currency']] = $currency; - } - $currencies = $tmp; - - $tmp = []; - foreach ($countries as $country) { - $tmp[$country['id_country']] = $country; - } - $countries = $tmp; - - $tmp = []; - foreach ($groups as $group) { - $tmp[$group['id_group']] = $group; - } - $groups = $tmp; - - $length_before = strlen($content); - if (is_array($specific_prices) && count($specific_prices)) { - $i = 0; - foreach ($specific_prices as $specific_price) { - $id_currency = $specific_price['id_currency'] ? $specific_price['id_currency'] : $defaultCurrency->id; - if (!isset($currencies[$id_currency])) { - continue; - } - - $current_specific_currency = $currencies[$id_currency]; - if ($specific_price['reduction_type'] == 'percentage') { - $impact = '- ' . ($specific_price['reduction'] * 100) . ' %'; - } elseif ($specific_price['reduction'] > 0) { - $impact = '- ' . $this->context->getContextLocale()->formatPrice( - Tools::ps_round($specific_price['reduction'], 2), - $current_specific_currency['iso_code'] - ) . ' '; - if ($specific_price['reduction_tax']) { - $impact .= '(' . $this->l('Tax incl.') . ')'; - } else { - $impact .= '(' . $this->l('Tax excl.') . ')'; - } - } else { - $impact = '--'; - } - - if ($specific_price['from'] == '0000-00-00 00:00:00' && $specific_price['to'] == '0000-00-00 00:00:00') { - $period = $this->l('Unlimited'); - } else { - $period = $this->l('From') . ' ' . ($specific_price['from'] != '0000-00-00 00:00:00' ? $specific_price['from'] : '0000-00-00 00:00:00') . '
    ' . $this->l('To') . ' ' . ($specific_price['to'] != '0000-00-00 00:00:00' ? $specific_price['to'] : '0000-00-00 00:00:00'); - } - if ($specific_price['id_product_attribute']) { - $combination = new Combination((int) $specific_price['id_product_attribute']); - $attributes = $combination->getAttributesName((int) $this->context->language->id); - $attributes_name = ''; - foreach ($attributes as $attribute) { - $attributes_name .= $attribute['name'] . ' - '; - } - $attributes_name = rtrim($attributes_name, ' - '); - } else { - $attributes_name = $this->l('All combinations'); - } - - $rule = new SpecificPriceRule((int) $specific_price['id_specific_price_rule']); - $rule_name = ($rule->id ? $rule->name : '--'); - - if ($specific_price['id_customer']) { - $customer = new Customer((int) $specific_price['id_customer']); - if (Validate::isLoadedObject($customer)) { - $customer_full_name = $customer->firstname . ' ' . $customer->lastname; - } - unset($customer); - } - - if (!$specific_price['id_shop'] || in_array($specific_price['id_shop'], Shop::getContextListShopID())) { - $content .= ' - - ' . $rule_name . ' - ' . $attributes_name . ''; - - $can_delete_specific_prices = true; - if (Shop::isFeatureActive()) { - $id_shop_sp = $specific_price['id_shop']; - $can_delete_specific_prices = (count($this->context->employee->getAssociatedShops()) > 1 && !$id_shop_sp) || $id_shop_sp; - $content .= ' - ' . ($id_shop_sp ? $shops[$id_shop_sp]['name'] : $this->l('All shops')) . ''; - } - $price = Tools::ps_round($specific_price['price'], 2); - $fixed_price = ($price == Tools::ps_round($obj->price, 2) || $specific_price['price'] == -1) ? '--' : $this->context->getContextLocale()->formatPrice($price, $current_specific_currency['iso_code']); - $content .= ' - ' . ($specific_price['id_currency'] ? $currencies[$specific_price['id_currency']]['name'] : $this->l('All currencies')) . ' - ' . ($specific_price['id_country'] ? $countries[$specific_price['id_country']]['name'] : $this->l('All countries')) . ' - ' . ($specific_price['id_group'] ? $groups[$specific_price['id_group']]['name'] : $this->l('All groups')) . ' - ' . (isset($customer_full_name) ? $customer_full_name : $this->l('All customers')) . ' - ' . $fixed_price . ' - ' . $impact . ' - ' . $period . ' - ' . $specific_price['from_quantity'] . ' - ' . ((!$rule->id && $can_delete_specific_prices) ? '' : '') . ' - '; - ++$i; - unset($customer_full_name); - } - } - } - - if ($length_before === strlen($content)) { - $content .= ' - -  ' . $this->l('No specific prices.') . ' - '; - } - - $content .= ' - - - - - '; - - $content .= ' - - '; - - // Not use id_customer - if ($specific_price_priorities[0] == 'id_customer') { - unset($specific_price_priorities[0]); - } - // Reindex array starting from 0 - $specific_price_priorities = array_values($specific_price_priorities); - - $content .= '
    -

    ' . $this->l('Priority management') . '

    -
    - ' . $this->l('Sometimes one customer can fit into multiple price rules. Priorities allow you to define which rule applies to the customer.') . ' -
    '; - - $content .= ' -
    - -
    - - - - - - - -
    -
    -
    -
    -

    - -

    -
    -
    - -
    - '; - - return $content; - } - - protected function _getCustomizationFieldIds($labels, $alreadyGenerated, $obj) - { - $customizableFieldIds = []; - if (isset($labels[Product::CUSTOMIZE_FILE])) { - foreach ($labels[Product::CUSTOMIZE_FILE] as $id_customization_field => $label) { - $customizableFieldIds[] = 'label_' . Product::CUSTOMIZE_FILE . '_' . (int) ($id_customization_field); - } - } - if (isset($labels[Product::CUSTOMIZE_TEXTFIELD])) { - foreach ($labels[Product::CUSTOMIZE_TEXTFIELD] as $id_customization_field => $label) { - $customizableFieldIds[] = 'label_' . Product::CUSTOMIZE_TEXTFIELD . '_' . (int) ($id_customization_field); - } - } - $j = 0; - for ($i = $alreadyGenerated[Product::CUSTOMIZE_FILE]; $i < (int) ($this->getFieldValue($obj, 'uploadable_files')); ++$i) { - $customizableFieldIds[] = 'newLabel_' . Product::CUSTOMIZE_FILE . '_' . $j++; - } - $j = 0; - for ($i = $alreadyGenerated[Product::CUSTOMIZE_TEXTFIELD]; $i < (int) ($this->getFieldValue($obj, 'text_fields')); ++$i) { - $customizableFieldIds[] = 'newLabel_' . Product::CUSTOMIZE_TEXTFIELD . '_' . $j++; - } - - return implode('¤', $customizableFieldIds); - } - - protected function _displayLabelField(&$label, $languages, $default_language, $type, $fieldIds, $id_customization_field) - { - foreach ($languages as $language) { - $input_value[$language['id_lang']] = (isset($label[(int) ($language['id_lang'])])) ? $label[(int) ($language['id_lang'])]['name'] : ''; - } - - $required = (isset($label[(int) ($language['id_lang'])])) ? $label[(int) ($language['id_lang'])]['required'] : false; - - $template = $this->context->smarty->createTemplate( - 'controllers/products/input_text_lang.tpl', - $this->context->smarty - ); - - return '
    ' - . '
    ' - . $template->assign([ - 'languages' => $languages, - 'input_name' => 'label_' . $type . '_' . (int) ($id_customization_field), - 'input_value' => $input_value, - ])->fetch() - . '
    ' - . '
    ' - . '
    ' - . '' - . '
    ' - . '
    ' - . '
    '; - } - - protected function _displayLabelFields(&$obj, &$labels, $languages, $default_language, $type) - { - $content = ''; - $type = (int) ($type); - $labelGenerated = [Product::CUSTOMIZE_FILE => (isset($labels[Product::CUSTOMIZE_FILE]) ? count($labels[Product::CUSTOMIZE_FILE]) : 0), Product::CUSTOMIZE_TEXTFIELD => (isset($labels[Product::CUSTOMIZE_TEXTFIELD]) ? count($labels[Product::CUSTOMIZE_TEXTFIELD]) : 0)]; - - $fieldIds = $this->_getCustomizationFieldIds($labels, $labelGenerated, $obj); - if (isset($labels[$type])) { - foreach ($labels[$type] as $id_customization_field => $label) { - $content .= $this->_displayLabelField($label, $languages, $default_language, $type, $fieldIds, (int) ($id_customization_field)); - } - } - - return $content; - } - - public function initFormCustomization($obj) - { - $data = $this->createTemplate($this->tpl_form); - - if ((bool) $obj->id) { - if ($this->product_exists_in_shop) { - $labels = $obj->getCustomizationFields(); - - $has_file_labels = (int) $this->getFieldValue($obj, 'uploadable_files'); - $has_text_labels = (int) $this->getFieldValue($obj, 'text_fields'); - - $data->assign([ - 'obj' => $obj, - 'table' => $this->table, - 'languages' => $this->_languages, - 'has_file_labels' => $has_file_labels, - 'display_file_labels' => $this->_displayLabelFields($obj, $labels, $this->_languages, Configuration::get('PS_LANG_DEFAULT'), Product::CUSTOMIZE_FILE), - 'has_text_labels' => $has_text_labels, - 'display_text_labels' => $this->_displayLabelFields($obj, $labels, $this->_languages, Configuration::get('PS_LANG_DEFAULT'), Product::CUSTOMIZE_TEXTFIELD), - 'uploadable_files' => (int) ($this->getFieldValue($obj, 'uploadable_files') ? (int) $this->getFieldValue($obj, 'uploadable_files') : '0'), - 'text_fields' => (int) ($this->getFieldValue($obj, 'text_fields') ? (int) $this->getFieldValue($obj, 'text_fields') : '0'), - ]); - } else { - $this->displayWarning($this->l('You must save the product in this shop before adding customization.')); - } - } else { - $this->displayWarning($this->l('You must save this product before adding customization.')); - } - - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - public function initFormAttachments($obj) - { - if (!$this->default_form_language) { - $this->getLanguages(); - } - - $data = $this->createTemplate($this->tpl_form); - $data->assign('default_form_language', $this->default_form_language); - - if ((bool) $obj->id) { - if ($this->product_exists_in_shop) { - $attachment_name = []; - $attachment_description = []; - foreach ($this->_languages as $language) { - $attachment_name[$language['id_lang']] = ''; - $attachment_description[$language['id_lang']] = ''; - } - - $iso_tiny_mce = $this->context->language->iso_code; - $iso_tiny_mce = (file_exists(_PS_JS_DIR_ . 'tiny_mce/langs/' . $iso_tiny_mce . '.js') ? $iso_tiny_mce : 'en'); - - $attachment_uploader = new HelperUploader('attachment_file'); - $attachment_uploader->setMultiple(false)->setUseAjax(true)->setUrl( - Context::getContext()->link->getAdminLink('AdminProducts') . '&ajax=1&id_product=' . (int) $obj->id - . '&action=AddAttachment' - )->setPostMaxSize((Configuration::get('PS_ATTACHMENT_MAXIMUM_SIZE') * 1024 * 1024)) - ->setTemplate('attachment_ajax.tpl'); - - $data->assign([ - 'obj' => $obj, - 'table' => $this->table, - 'ad' => __PS_BASE_URI__ . basename(_PS_ADMIN_DIR_), - 'iso_tiny_mce' => $iso_tiny_mce, - 'languages' => $this->_languages, - 'id_lang' => $this->context->language->id, - 'attach1' => Attachment::getAttachments($this->context->language->id, $obj->id, true), - 'attach2' => Attachment::getAttachments($this->context->language->id, $obj->id, false), - 'default_form_language' => (int) Configuration::get('PS_LANG_DEFAULT'), - 'attachment_name' => $attachment_name, - 'attachment_description' => $attachment_description, - 'attachment_uploader' => $attachment_uploader->render(), - ]); - } else { - $this->displayWarning($this->l('You must save the product in this shop before adding attachements.')); - } - } else { - $this->displayWarning($this->l('You must save this product before adding attachements.')); - } - - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - public function initFormInformations($product) - { - if (!$this->default_form_language) { - $this->getLanguages(); - } - - $data = $this->createTemplate($this->tpl_form); - - $currency = $this->context->currency; - $data->assign('languages', $this->_languages); - $data->assign('default_form_language', $this->default_form_language); - $data->assign('currency', $currency); - $this->object = $product; - //$this->display = 'edit'; - $data->assign('product_name_redirected', Product::getProductName((int) $product->id_type_redirected, null, (int) $this->context->language->id)); - /* - * Form for adding a virtual product like software, mp3, etc... - */ - $product_download = new ProductDownload(); - if ($id_product_download = $product_download->getIdFromIdProduct($this->getFieldValue($product, 'id'))) { - $product_download = new ProductDownload($id_product_download); - } - - $product->{'productDownload'} = $product_download; - - $product_props = []; - // global informations - array_push( - $product_props, - 'reference', - 'ean13', - 'upc', - 'available_for_order', - 'show_price', - 'online_only', - 'id_manufacturer' - ); - - // specific / detailled information - array_push( - $product_props, - // physical product - 'width', - 'height', - 'weight', - 'active', - // virtual product - 'is_virtual', - 'cache_default_attribute', - // customization - 'uploadable_files', - 'text_fields' - ); - // prices - array_push( - $product_props, - 'price', - 'wholesale_price', - 'id_tax_rules_group', - 'unit_price_ratio', - 'on_sale', - 'unity', - 'minimum_quantity', - 'additional_shipping_cost', - 'available_now', - 'available_later', - 'available_date' - ); - - if (Configuration::get('PS_USE_ECOTAX')) { - array_push($product_props, 'ecotax'); - } - - foreach ($product_props as $prop) { - $product->$prop = $this->getFieldValue($product, $prop); - } - - $product->name['class'] = 'updateCurrentText'; - if (!$product->id || Configuration::get('PS_FORCE_FRIENDLY_PRODUCT')) { - $product->name['class'] .= ' copy2friendlyUrl'; - } - - $images = Image::getImages($this->context->language->id, $product->id); - - if (is_array($images)) { - foreach ($images as $k => $image) { - $images[$k]['src'] = $this->context->link->getImageLink($product->link_rewrite[$this->context->language->id], $image['id_image'], ImageType::getFormattedName('small')); - } - $data->assign('images', $images); - } - $data->assign('imagesTypes', ImageType::getImagesTypes('products')); - - $product->tags = Tag::getProductTags($product->id); - - $data->assign('product_type', (int) Tools::getValue('type_product', $product->getType())); - $data->assign('is_in_pack', (int) Pack::isPacked($product->id)); - - $check_product_association_ajax = false; - if (Shop::isFeatureActive() && Shop::getContext() != Shop::CONTEXT_ALL) { - $check_product_association_ajax = true; - } - - // TinyMCE - $iso_tiny_mce = $this->context->language->iso_code; - $iso_tiny_mce = (file_exists(_PS_ROOT_DIR_ . '/js/tiny_mce/langs/' . $iso_tiny_mce . '.js') ? $iso_tiny_mce : 'en'); - $data->assign('ad', dirname($_SERVER['PHP_SELF'])); - $data->assign('iso_tiny_mce', $iso_tiny_mce); - $data->assign('check_product_association_ajax', $check_product_association_ajax); - $data->assign('id_lang', $this->context->language->id); - $data->assign('product', $product); - $data->assign('token', $this->token); - $data->assign('currency', $currency); - $data->assign($this->tpl_form_vars); - $data->assign('link', $this->context->link); - $data->assign('PS_PRODUCT_SHORT_DESC_LIMIT', Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT') ? Configuration::get('PS_PRODUCT_SHORT_DESC_LIMIT') : 400); - $this->tpl_form_vars['product'] = $product; - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - public function initFormShipping($obj) - { - $data = $this->createTemplate($this->tpl_form); - $data->assign([ - 'product' => $obj, - 'ps_dimension_unit' => Configuration::get('PS_DIMENSION_UNIT'), - 'ps_weight_unit' => Configuration::get('PS_WEIGHT_UNIT'), - 'carrier_list' => $this->getCarrierList(), - 'currency' => $this->context->currency, - 'country_display_tax_label' => $this->context->country->display_tax_label, - ]); - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - protected function getCarrierList() - { - $carrier_list = Carrier::getCarriers($this->context->language->id, false, false, false, null, Carrier::ALL_CARRIERS); - if ($product = $this->loadObject(true)) { - $carrier_selected_list = $product->getCarriers(); - foreach ($carrier_list as &$carrier) { - foreach ($carrier_selected_list as $carrier_selected) { - if ($carrier_selected['id_reference'] == $carrier['id_reference']) { - $carrier['selected'] = true; - - continue; - } - } - } - } - - return $carrier_list; - } - - protected function addCarriers($product = null) - { - if (!isset($product)) { - $product = new Product((int) Tools::getValue('id_product')); - } - - if (Validate::isLoadedObject($product)) { - $carriers = []; - - if (Tools::getValue('selectedCarriers')) { - $carriers = Tools::getValue('selectedCarriers'); - } - - $product->setCarriers($carriers); - } - } - - public function ajaxProcessaddProductImage() - { - self::$currentIndex = 'index.php?tab=AdminProducts'; - $product = new Product((int) Tools::getValue('id_product')); - $legends = Tools::getValue('legend'); - - if (!is_array($legends)) { - $legends = (array) $legends; - } - - if (!Validate::isLoadedObject($product)) { - $files = []; - $files[0]['error'] = Tools::displayError('Cannot add image because product creation failed.'); - } - - $image_uploader = new HelperImageUploader('file'); - $image_uploader->setAcceptTypes(['jpeg', 'gif', 'png', 'jpg', 'webp'])->setMaxSize($this->max_image_size); - $files = $image_uploader->process(); - - foreach ($files as &$file) { - $image = new Image(); - $image->id_product = (int) ($product->id); - $image->position = Image::getHighestPosition($product->id) + 1; - - foreach ($legends as $key => $legend) { - if (!empty($legend)) { - $image->legend[(int) $key] = $legend; - } - } - - if (!Image::getCover($image->id_product)) { - $image->cover = 1; - } else { - $image->cover = 0; - } - - if (($validate = $image->validateFieldsLang(false, true)) !== true) { - $file['error'] = Tools::displayError($validate); - } - - if (isset($file['error']) && (!is_numeric($file['error']) || $file['error'] != 0)) { - continue; - } - - if (!$image->add()) { - $file['error'] = Tools::displayError('Error while creating additional image'); - } else { - if (!$new_path = $image->getPathForCreation()) { - $file['error'] = Tools::displayError('An error occurred during new folder creation'); - - continue; - } - - $error = 0; - - if (!ImageManager::resize($file['save_path'], $new_path . '.' . $image->image_format, null, null, 'jpg', false, $error)) { - switch ($error) { - case ImageManager::ERROR_FILE_NOT_EXIST: - $file['error'] = Tools::displayError('An error occurred while copying image, the file does not exist anymore.'); - - break; - - case ImageManager::ERROR_FILE_WIDTH: - $file['error'] = Tools::displayError('An error occurred while copying image, the file width is 0px.'); - - break; - - case ImageManager::ERROR_MEMORY_LIMIT: - $file['error'] = Tools::displayError('An error occurred while copying image, check your memory limit.'); - - break; - - default: - $file['error'] = Tools::displayError('An error occurred while copying image.'); - - break; - } - - continue; - } else { - $imagesTypes = ImageType::getImagesTypes('products'); - foreach ($imagesTypes as $imageType) { - if (!ImageManager::resize($file['save_path'], $new_path . '-' . stripslashes($imageType['name']) . '.' . $image->image_format, $imageType['width'], $imageType['height'], $image->image_format)) { - $file['error'] = Tools::displayError('An error occurred while copying image:') . ' ' . stripslashes($imageType['name']); - - continue; - } - } - } - - unlink($file['save_path']); - //Necesary to prevent hacking - unset($file['save_path']); - Hook::exec('actionWatermark', ['id_image' => $image->id, 'id_product' => $product->id]); - - if (!$image->update()) { - $file['error'] = Tools::displayError('Error while updating status'); - - continue; - } - - // Associate image to shop from context - $shops = Shop::getContextListShopID(); - $image->associateTo($shops); - $json_shops = []; - - foreach ($shops as $id_shop) { - $json_shops[$id_shop] = true; - } - - $file['status'] = 'ok'; - $file['id'] = $image->id; - $file['position'] = $image->position; - $file['cover'] = $image->cover; - $file['legend'] = $image->legend; - $file['path'] = $image->getExistingImgPath(); - $file['shops'] = $json_shops; - - @unlink(_PS_TMP_IMG_DIR_ . 'product_' . (int) $product->id . '.jpg'); - @unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $product->id . '_' . $this->context->shop->id . '.jpg'); - } - } - - die(json_encode([$image_uploader->getName() => $files])); - } - - public function initFormImages($obj) - { - $data = $this->createTemplate($this->tpl_form); - - if ((bool) $obj->id) { - if ($this->product_exists_in_shop) { - $data->assign('product', $this->loadObject()); - - $shops = false; - if (Shop::isFeatureActive()) { - $shops = Shop::getShops(); - } - - if ($shops) { - foreach ($shops as $key => $shop) { - if (!$obj->isAssociatedToShop($shop['id_shop'])) { - unset($shops[$key]); - } - } - } - - $data->assign('shops', $shops); - - $count_images = Db::getInstance()->getValue( - ' - SELECT COUNT(id_product) - FROM ' . _DB_PREFIX_ . 'image - WHERE id_product = ' . (int) $obj->id - ); - - $images = Image::getImages($this->context->language->id, $obj->id); - foreach ($images as $k => $image) { - $images[$k] = new Image($image['id_image']); - } - - if ($this->context->shop->getContext() == Shop::CONTEXT_SHOP) { - $current_shop_id = (int) $this->context->shop->id; - } else { - $current_shop_id = 0; - } - - $languages = Language::getLanguages(true); - $image_uploader = new HelperImageUploader('file'); - $image_uploader->setMultiple(!(Tools::getUserBrowser() == 'Apple Safari' && Tools::getUserPlatform() == 'Windows')) - ->setUseAjax(true)->setUrl( - Context::getContext()->link->getAdminLink('AdminProducts') . '&ajax=1&id_product=' . (int) $obj->id - . '&action=addProductImage' - ); - - $data->assign([ - 'countImages' => $count_images, - 'id_product' => (int) Tools::getValue('id_product'), - 'id_category_default' => (int) $this->_category->id, - 'images' => $images, - 'iso_lang' => $languages[0]['iso_code'], - 'token' => $this->token, - 'table' => $this->table, - 'max_image_size' => $this->max_image_size / 1024 / 1024, - 'up_filename' => (string) Tools::getValue('virtual_product_filename_attribute'), - 'currency' => $this->context->currency, - 'current_shop_id' => $current_shop_id, - 'languages' => $this->_languages, - 'default_language' => (int) Configuration::get('PS_LANG_DEFAULT'), - 'image_uploader' => $image_uploader->render(), - ]); - - $type = ImageType::getByNameNType('%', 'products', 'height'); - if (isset($type['name'])) { - $data->assign('imageType', $type['name']); - } else { - $data->assign('imageType', ImageType::getFormattedName('small')); - } - } else { - $this->displayWarning($this->l('You must save the product in this shop before adding images.')); - } - } else { - $this->displayWarning($this->l('You must save this product before adding images.')); - } - - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - public function initFormCombinations($obj) - { - return $this->initFormAttributes($obj); - } - - public function initFormAttributes($product) - { - $data = $this->createTemplate($this->tpl_form); - if (!Combination::isFeatureActive()) { - $this->displayWarning($this->l('This feature has been disabled. ') . - ' ' . $this->l('Performances') . ''); - } elseif (Validate::isLoadedObject($product)) { - if ($this->product_exists_in_shop) { - if ($product->is_virtual) { - $data->assign('product', $product); - $this->displayWarning($this->l('A virtual product cannot have combinations.')); - } else { - $attribute_js = []; - $attributes = ProductAttribute::getAttributes($this->context->language->id, true); - foreach ($attributes as $k => $attribute) { - $attribute_js[$attribute['id_attribute_group']][$attribute['id_attribute']] = $attribute['name']; - natsort($attribute_js[$attribute['id_attribute_group']]); - } - - $currency = $this->context->currency; - - $data->assign('attributeJs', $attribute_js); - $data->assign('attributes_groups', AttributeGroup::getAttributesGroups($this->context->language->id)); - - $data->assign('currency', $currency); - - $images = Image::getImages($this->context->language->id, $product->id); - - $data->assign('tax_exclude_option', Tax::excludeTaxeOption()); - $data->assign('ps_weight_unit', Configuration::get('PS_WEIGHT_UNIT')); - - $data->assign('ps_use_ecotax', Configuration::get('PS_USE_ECOTAX')); - $data->assign('field_value_unity', $this->getFieldValue($product, 'unity')); - - $data->assign('minimal_quantity', $this->getFieldValue($product, 'minimal_quantity') ? $this->getFieldValue($product, 'minimal_quantity') : 1); - $data->assign('available_date', ($this->getFieldValue($product, 'available_date') != 0) ? stripslashes(htmlentities($this->getFieldValue($product, 'available_date'), $this->context->language->id)) : '0000-00-00'); - - $i = 0; - $type = ImageType::getByNameNType('%', 'products', 'height'); - if (isset($type['name'])) { - $data->assign('imageType', $type['name']); - } else { - $data->assign('imageType', ImageType::getFormattedName('small')); - } - $data->assign('imageWidth', (isset($image_type['width']) ? (int) ($image_type['width']) : 64) + 25); - foreach ($images as $k => $image) { - $images[$k]['obj'] = new Image($image['id_image']); - ++$i; - } - $data->assign('images', $images); - - $data->assign($this->tpl_form_vars); - $data->assign([ - 'list' => $this->renderListAttributes($product, $currency), - 'product' => $product, - 'id_category' => $product->getDefaultCategory(), - 'token_generator' => Tools::getAdminTokenLite('AdminAttributeGenerator'), - 'combination_exists' => (Shop::isFeatureActive() && (Shop::getContextShopGroup()->share_stock) && count(AttributeGroup::getAttributesGroups($this->context->language->id)) > 0 && $product->hasAttributes()), - ]); - } - } else { - $this->displayWarning($this->l('You must save the product in this shop before adding combinations.')); - } - } else { - $data->assign('product', $product); - $this->displayWarning($this->l('You must save this product before adding combinations.')); - } - - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - public function renderListAttributes($product, $currency) - { - $this->bulk_actions = ['delete' => ['text' => $this->l('Delete selected'), 'confirm' => $this->l('Delete selected items?')]]; - $this->addRowAction('edit'); - $this->addRowAction('default'); - $this->addRowAction('delete'); - - $default_class = 'highlighted'; - - $this->fields_list = [ - 'attributes' => ['title' => $this->l('Attribute - value pair'), 'align' => 'left'], - 'price' => ['title' => $this->l('Impact on price'), 'type' => 'price', 'align' => 'left'], - 'weight' => ['title' => $this->l('Impact on weight'), 'align' => 'left'], - 'reference' => ['title' => $this->l('Reference'), 'align' => 'left'], - 'ean13' => ['title' => $this->l('EAN-13'), 'align' => 'left'], - 'upc' => ['title' => $this->l('UPC'), 'align' => 'left'], - ]; - - $comb_array = []; - if ($product->id) { - /* Build attributes combinations */ - $combinations = $product->getAttributeCombinations($this->context->language->id); - $groups = []; - if (is_array($combinations)) { - $combination_images = $product->getCombinationImages($this->context->language->id); - foreach ($combinations as $k => $combination) { - $price_to_convert = Tools::convertPrice($combination['price'], $currency); - $price = $this->context->getContextLocale()->formatPrice($price_to_convert, $currency['iso_code']); - - $container['id_product_attribute'] = $combination['id_product_attribute']; - $container['attributes'][] = [$combination['group_name'], $combination['attribute_name'], $combination['id_attribute']]; - $container['wholesale_price'] = $combination['wholesale_price']; - $container['price'] = $price; - $container['weight'] = $combination['weight'] . Configuration::get('PS_WEIGHT_UNIT'); - $container['unit_impact'] = $combination['unit_price_impact']; - $container['reference'] = $combination['reference']; - $container['ean13'] = $combination['ean13']; - $container['upc'] = $combination['upc']; - $container['id_image'] = isset($combination_images[$combination['id_product_attribute']][0]['id_image']) ? $combination_images[$combination['id_product_attribute']][0]['id_image'] : 0; - $container['available_date'] = strftime($combination['available_date']); - $container['default_on'] = $combination['default_on']; - if ($combination['is_color_group']) { - $groups[$combination['id_attribute_group']] = $combination['group_name']; - } - $comb_array[$combination['id_product_attribute']] = $container; - } - } - - foreach ($comb_array as $id_product_attribute => $product_attribute) { - $list = ''; - - /* In order to keep the same attributes order */ - asort($product_attribute['attributes']); - - foreach ($product_attribute['attributes'] as $attribute) { - $list .= $attribute[0] . ' - ' . $attribute[1] . ', '; - } - - $list = rtrim($list, ', '); - $comb_array[$id_product_attribute]['image'] = $product_attribute['id_image'] ? new Image($product_attribute['id_image']) : false; - $comb_array[$id_product_attribute]['available_date'] = $product_attribute['available_date'] != 0 ? date('Y-m-d', strtotime($product_attribute['available_date'])) : '0000-00-00'; - $comb_array[$id_product_attribute]['attributes'] = $list; - $comb_array[$id_product_attribute]['name'] = $list; - - if ($product_attribute['default_on']) { - $comb_array[$id_product_attribute]['class'] = $default_class; - } - } - } - - foreach ($this->actions_available as $action) { - if (!in_array($action, $this->actions) && isset($this->$action) && $this->$action) { - $this->actions[] = $action; - } - } - - $helper = new HelperList(); - $helper->identifier = 'id_product_attribute'; - $helper->table_id = 'combinations-list'; - $helper->token = $this->token; - $helper->currentIndex = self::$currentIndex; - $helper->no_link = true; - $helper->simple_header = true; - $helper->show_toolbar = false; - $helper->shopLinkType = $this->shopLinkType; - $helper->actions = $this->actions; - $helper->list_skip_actions = $this->list_skip_actions; - $helper->colorOnBackground = true; - $helper->override_folder = $this->tpl_folder . 'combination/'; - - return $helper->generateList($comb_array, $this->fields_list); - } - - public function initFormSuppliers($obj) - { - $data = $this->createTemplate($this->tpl_form); - - if ($obj->id) { - if ($this->product_exists_in_shop) { - // Get all id_product_attribute - $attributes = $obj->getAttributesResume($this->context->language->id); - if (empty($attributes)) { - $attributes[] = [ - 'id_product' => $obj->id, - 'id_product_attribute' => 0, - 'attribute_designation' => '', - ]; - } - - $product_designation = []; - - foreach ($attributes as $attribute) { - $product_designation[$attribute['id_product_attribute']] = rtrim( - $obj->name[$this->context->language->id] . ' - ' . $attribute['attribute_designation'], - ' - ' - ); - } - - // Get all available suppliers - $suppliers = Supplier::getSuppliers(); - - // Get already associated suppliers - $associated_suppliers = ProductSupplier::getSupplierCollection($obj->id); - - // Get already associated suppliers and force to retreive product declinaisons - $product_supplier_collection = ProductSupplier::getSupplierCollection($obj->id, false); - - $default_supplier = 0; - - foreach ($suppliers as &$supplier) { - $supplier['is_selected'] = false; - $supplier['is_default'] = false; - - foreach ($associated_suppliers as $associated_supplier) { - if ($associated_supplier->id_supplier == $supplier['id_supplier']) { - $associated_supplier->name = $supplier['name']; - $supplier['is_selected'] = true; - - if ($obj->id_supplier == $supplier['id_supplier']) { - $supplier['is_default'] = true; - $default_supplier = $supplier['id_supplier']; - } - } - } - } - - $data->assign([ - 'attributes' => $attributes, - 'suppliers' => $suppliers, - 'default_supplier' => $default_supplier, - 'associated_suppliers' => $associated_suppliers, - 'associated_suppliers_collection' => $product_supplier_collection, - 'product_designation' => $product_designation, - 'currencies' => Currency::getCurrencies(), - 'product' => $obj, - 'link' => $this->context->link, - 'token' => $this->token, - 'id_default_currency' => Currency::getDefaultCurrencyId(), - ]); - } else { - $this->displayWarning($this->l('You must save the product in this shop before managing suppliers.')); - } - } else { - $this->displayWarning($this->l('You must save this product before managing suppliers.')); - } - - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - public function initFormFeatures($obj) - { - if (!$this->default_form_language) { - $this->getLanguages(); - } - - $data = $this->createTemplate($this->tpl_form); - $data->assign('default_form_language', $this->default_form_language); - $data->assign('languages', $this->_languages); - - if (!Feature::isFeatureActive()) { - $this->displayWarning($this->l('This feature has been disabled. ') . ' ' . $this->l('Performances') . ''); - } else { - if ($obj->id) { - if ($this->product_exists_in_shop) { - $features = Feature::getFeatures($this->context->language->id, (Shop::isFeatureActive() && Shop::getContext() == Shop::CONTEXT_SHOP)); - - foreach ($features as $k => $tab_features) { - $features[$k]['current_item'] = false; - $features[$k]['val'] = []; - - $custom = true; - foreach ($obj->getFeatures() as $tab_products) { - if ($tab_products['id_feature'] == $tab_features['id_feature']) { - $features[$k]['current_item'] = $tab_products['id_feature_value']; - } - } - - $features[$k]['featureValues'] = FeatureValue::getFeatureValuesWithLang($this->context->language->id, (int) $tab_features['id_feature']); - if (count($features[$k]['featureValues'])) { - foreach ($features[$k]['featureValues'] as $value) { - if ($features[$k]['current_item'] == $value['id_feature_value']) { - $custom = false; - } - } - } - - if ($custom) { - $features[$k]['val'] = FeatureValue::getFeatureValueLang($features[$k]['current_item']); - } - } - - $data->assign('available_features', $features); - $data->assign('product', $obj); - $data->assign('link', $this->context->link); - $data->assign('default_form_language', $this->default_form_language); - } else { - $this->displayWarning($this->l('You must save the product in this shop before adding features.')); - } - } else { - $this->displayWarning($this->l('You must save this product before adding features.')); - } - } - $this->tpl_form_vars['custom_form'] = $data->fetch(); - } - - public function ajaxProcessProductQuantity() - { - if (!Tools::getValue('actionQty')) { - return json_encode(['error' => $this->l('Undefined action')]); - } - - $product = new Product((int) Tools::getValue('id_product'), true); - switch (Tools::getValue('actionQty')) { - case 'pack_stock_type': - $value = Tools::getValue('value'); - if ($value === false) { - die(json_encode(['error' => $this->l('Undefined value')])); - } - if ((int) $value != 0 && (int) $value != 1 - && (int) $value != 2 && (int) $value != 3) { - die(json_encode(['error' => $this->l('Incorrect value')])); - } - - Product::setPackStockType($product->id, $value); - - break; - - case 'out_of_stock': - if (Tools::getValue('value') === false) { - die(json_encode(['error' => $this->l('Undefined value')])); - } - if (!in_array((int) Tools::getValue('value'), [0, 1, 2])) { - die(json_encode(['error' => $this->l('Incorrect value')])); - } - - StockAvailable::setProductOutOfStock($product->id, (int) Tools::getValue('value')); - - break; - - case 'set_qty': - if (Tools::getValue('value') === false || (!is_numeric(trim(Tools::getValue('value'))))) { - die(json_encode(['error' => $this->l('Undefined value')])); - } - if (Tools::getValue('id_product_attribute') === false) { - die(json_encode(['error' => $this->l('Undefined id product attribute')])); - } - - StockAvailable::setQuantity($product->id, (int) Tools::getValue('id_product_attribute'), (int) Tools::getValue('value')); - Hook::exec('actionProductUpdate', ['id_product' => (int) $product->id, 'product' => $product]); - - // Catch potential echo from modules - $error = ob_get_contents(); - if (!empty($error)) { - ob_end_clean(); - die(json_encode(['error' => $error])); - } - - break; - } - die(json_encode(['error' => false])); - } - - public function getCombinationImagesJS() - { - if (!($obj = $this->loadObject(true))) { - return; - } - - $content = 'var combination_images = new Array();'; - if (!$allCombinationImages = $obj->getCombinationImages($this->context->language->id)) { - return $content; - } - foreach ($allCombinationImages as $id_product_attribute => $combination_images) { - $i = 0; - $content .= 'combination_images[' . (int) $id_product_attribute . '] = new Array();'; - foreach ($combination_images as $combination_image) { - $content .= 'combination_images[' . (int) $id_product_attribute . '][' . $i++ . '] = ' . (int) $combination_image['id_image'] . ';'; - } - } - - return $content; - } - - public function haveThisAccessory($accessory_id, $accessories) - { - foreach ($accessories as $accessory) { - if ((int) $accessory['id_product'] == (int) $accessory_id) { - return true; - } - } - - return false; - } - - protected function initPack(Product $product) - { - $this->tpl_form_vars['is_pack'] = ($product->id && Pack::isPack($product->id)) || Tools::getValue('type_product') == Product::PTYPE_PACK; - $product->packItems = Pack::getItems($product->id, $this->context->language->id); - - $input_pack_items = ''; - if (Tools::getValue('inputPackItems')) { - $input_pack_items = Tools::getValue('inputPackItems'); - } else { - foreach ($product->packItems as $pack_item) { - $input_pack_items .= $pack_item->pack_quantity . 'x' . $pack_item->id . '-'; - } - } - $this->tpl_form_vars['input_pack_items'] = $input_pack_items; - - $input_namepack_items = ''; - if (Tools::getValue('namePackItems')) { - $input_namepack_items = Tools::getValue('namePackItems'); - } else { - foreach ($product->packItems as $pack_item) { - $input_namepack_items .= $pack_item->pack_quantity . ' x ' . $pack_item->name . '¤'; - } - } - $this->tpl_form_vars['input_namepack_items'] = $input_namepack_items; - } - - /** - * AdminProducts display hook - */ - public function initFormModules($obj) - { - $id_module = Db::getInstance()->getValue('SELECT `id_module` FROM `' . _DB_PREFIX_ . 'module` WHERE `name` = \'' . pSQL($this->tab_display_module) . '\''); - $this->tpl_form_vars['custom_form'] = Hook::exec('displayAdminProductsExtra', [], (int) $id_module); - } - - /** - * delete all items in pack, then check if type_product value is 2. - * if yes, add the pack items from input "inputPackItems" - * - * @param Product $product - * - * @return bool - */ - public function updatePackItems($product) - { - Pack::deleteItems($product->id); - // lines format: QTY x ID-QTY x ID - if (Tools::getValue('type_product') == Product::PTYPE_PACK) { - $product->setDefaultAttribute(0); //reset cache_default_attribute - $items = Tools::getValue('inputPackItems'); - $lines = array_unique(explode('-', $items)); - - // lines is an array of string with format : QTYxIDxID_PRODUCT_ATTRIBUTE - if (count($lines)) { - foreach ($lines as $line) { - if (!empty($line)) { - $item_id_attribute = 0; - count($array = explode('x', $line)) == 3 ? list($qty, $item_id, $item_id_attribute) = $array : list($qty, $item_id) = $array; - if ($qty > 0 && isset($item_id)) { - if (Pack::isPack((int) $item_id)) { - $this->errors[] = Tools::displayError('You can\'t add product packs into a pack'); - } elseif (!Pack::addItem((int) $product->id, (int) $item_id, (int) $qty, (int) $item_id_attribute)) { - $this->errors[] = Tools::displayError('An error occurred while attempting to add products to the pack.'); - } - } - } - } - } - } - } - - public function getL($key) - { - $trad = [ - 'Default category:' => $this->l('Default category'), - 'Catalog:' => $this->l('Catalog'), - 'Consider changing the default category.' => $this->l('Consider changing the default category.'), - 'ID' => $this->l('ID'), - 'Name' => $this->l('Name'), - 'Mark all checkbox(es) of categories in which product is to appear' => $this->l('Mark the checkbox of each categories in which this product will appear.'), - ]; - - return $trad[$key]; - } - - protected function _displayUnavailableProductWarning() - { - $content = '
    - ' . $this->l('Your product will be saved as a draft.') . ' - ' . $this->l('Save and preview') . ' - -
    '; - $this->tpl_form_vars['warning_unavailable_product'] = $content; - } - - public function ajaxProcessCheckProductName() - { - if ($this->access('view')) { - $search = Tools::getValue('q'); - $id_lang = Tools::getValue('id_lang'); - $limit = Tools::getValue('limit'); - if (Context::getContext()->shop->getContext() != Shop::CONTEXT_SHOP) { - $result = false; - } else { - $result = Db::getInstance()->executeS(' - SELECT DISTINCT pl.`name`, p.`id_product`, pl.`id_shop` - FROM `' . _DB_PREFIX_ . 'product` p - LEFT JOIN `' . _DB_PREFIX_ . 'product_shop` ps ON (ps.id_product = p.id_product AND ps.id_shop =' . (int) Context::getContext()->shop->id . ') - LEFT JOIN `' . _DB_PREFIX_ . 'product_lang` pl - ON (pl.`id_product` = p.`id_product` AND pl.`id_lang` = ' . (int) $id_lang . ') - WHERE pl.`name` LIKE "%' . pSQL($search) . '%" AND ps.id_product IS NULL - GROUP BY pl.`id_product` - LIMIT ' . (int) $limit); - } - die(json_encode($result)); - } - } - - public function ajaxProcessUpdatePositions() - { - if ($this->access('edit')) { - $way = (int) (Tools::getValue('way')); - $id_product = (int) (Tools::getValue('id_product')); - $id_category = (int) (Tools::getValue('id_category')); - $positions = Tools::getValue('product'); - - if (is_array($positions)) { - foreach ($positions as $position => $value) { - $pos = explode('_', $value); - - if ((isset($pos[1], $pos[2])) && ($pos[1] == $id_category && (int) $pos[2] === $id_product)) { - if ($product = new Product((int) $pos[2])) { - if (isset($position) && $product->updatePosition($way, $position)) { - $category = new Category((int) $id_category); - if (Validate::isLoadedObject($category)) { - Hook::exec('actionCategoryUpdate', ['category' => $category]); - } - echo 'ok position ' . (int) $position . ' for product ' . (int) $pos[2] . "\r\n"; - } else { - echo '{"hasError" : true, "errors" : "Can not update product ' . (int) $id_product . ' to position ' . (int) $position . ' "}'; - } - } else { - echo '{"hasError" : true, "errors" : "This product (' . (int) $id_product . ') can t be loaded"}'; - } - - break; - } - } - } - } - } - - public function ajaxProcessPublishProduct() - { - if ($this->access('edit')) { - if ($id_product = (int) Tools::getValue('id_product')) { - $bo_product_url = dirname($_SERVER['PHP_SELF']) . '/index.php?tab=AdminProducts&id_product=' . $id_product . '&updateproduct&token=' . $this->token; - - if (Tools::getValue('redirect')) { - die($bo_product_url); - } - - $product = new Product((int) $id_product); - if (!Validate::isLoadedObject($product)) { - die('error: invalid id'); - } - - $product->active = 1; - - if ($product->save()) { - die($bo_product_url); - } else { - die('error: saving'); - } - } - } - } - - public function processImageLegends() - { - if (Tools::getValue('key_tab') == 'Images' && Validate::isLoadedObject($product = new Product((int) Tools::getValue('id_product')))) { - $language_ids = Language::getIDs(false); - foreach ($_POST as $key => $val) { - if (preg_match('/^legend_([0-9]+)/i', $key, $match)) { - foreach ($language_ids as $id_lang) { - if ($val && $id_lang == $match[1]) { - Db::getInstance()->execute('UPDATE ' . _DB_PREFIX_ . 'image_lang SET legend = "' . pSQL($val) . '" WHERE id_image IN (SELECT id_image FROM ' . _DB_PREFIX_ . 'image WHERE id_product = ' . (int) $product->id . ') AND id_lang = ' . (int) $id_lang); - } - } - } - } - } - } -} diff --git a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combination_collection.html.twig b/tests/Resources/modules_tests/pscsx32412/override/controllers/admin/DummyAdminController.php similarity index 86% rename from src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combination_collection.html.twig rename to tests/Resources/modules_tests/pscsx32412/override/controllers/admin/DummyAdminController.php index 76aa484bc16ca..c9143c974cd16 100644 --- a/src/PrestaShopBundle/Resources/views/Admin/Product/ProductPage/Forms/form_combination_collection.html.twig +++ b/tests/Resources/modules_tests/pscsx32412/override/controllers/admin/DummyAdminController.php @@ -1,4 +1,5 @@ -{#** + * @copyright Since 2007 PrestaShop SA and Contributors * @license https://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0) - *#} -{% for combinationForm in combinationForms %} - {{ include('@Product/ProductPage/Forms/form_combination.html.twig', {'form': combinationForm}) }} -{% endfor %} + */ +declare(strict_types=1); + +class DummyAdminController extends DummyAdminControllerCore +{ + public function checkAccess() + { + return false; + } +} diff --git a/tests/UI/campaigns/functional/BO/03_catalog/03_monitoring/02_monitoringProducts.ts b/tests/UI/campaigns/functional/BO/03_catalog/03_monitoring/02_monitoringProducts.ts index 1af2b901760b4..b258c772a1200 100644 --- a/tests/UI/campaigns/functional/BO/03_catalog/03_monitoring/02_monitoringProducts.ts +++ b/tests/UI/campaigns/functional/BO/03_catalog/03_monitoring/02_monitoringProducts.ts @@ -271,8 +271,7 @@ describe('BO - Catalog - Monitoring : Create different products and delete them ); const textResult = await monitoringPage.deleteProductInGrid(page, test.gridName, 1); - expect(textResult).to.equal('Product successfully deleted.'); - // productsPage.successfulDeleteMessage + expect(textResult).to.equal(productsPage.successfulDeleteMessage); const pageTitle = await productsPage.getPageTitle(page); expect(pageTitle).to.contains(productsPage.pageTitle); diff --git a/tests/UI/pages/BO/catalog/products/add/combinationsTab.ts b/tests/UI/pages/BO/catalog/products/add/combinationsTab.ts index 35aae90960148..a5358815e2d43 100644 --- a/tests/UI/pages/BO/catalog/products/add/combinationsTab.ts +++ b/tests/UI/pages/BO/catalog/products/add/combinationsTab.ts @@ -569,7 +569,7 @@ class CombinationsTab extends BOBasePage { * @returns {Promise} */ async getCombinationNameFromModal(page: Page): Promise { - const combinationFrame: Frame | null = await page.frame({url: /sell\/catalog\/products-v2\/combinations/gmi}); + const combinationFrame: Frame | null = await page.frame({url: /sell\/catalog\/products\/combinations/gmi}); expect(combinationFrame).not.eq(null); return this.getTextContent(combinationFrame!, this.editCombinationNameValue); @@ -606,7 +606,7 @@ class CombinationsTab extends BOBasePage { await page.waitForTimeout(2000); await this.waitForVisibleSelector(page, this.editCombinationIframe); - const combinationFrame: Frame|null = page.frame({url: /sell\/catalog\/products-v2\/combinations/gmi}); + const combinationFrame: Frame|null = page.frame({url: /sell\/catalog\/products\/combinations/gmi}); expect(combinationFrame).to.not.equal(null); await this.setValue(combinationFrame!, this.editCombinationModalQuantityInput, combinationData.quantity); @@ -636,7 +636,7 @@ class CombinationsTab extends BOBasePage { * @returns {Promise} */ async getRecentStockMovements(page: Page, row: number = 1): Promise { - const combinationFrame: Frame|null = page.frame({url: /sell\/catalog\/products-v2\/combinations/gmi}); + const combinationFrame: Frame|null = page.frame({url: /sell\/catalog\/products\/combinations/gmi}); expect(combinationFrame).to.not.equal(null); return { @@ -652,7 +652,7 @@ class CombinationsTab extends BOBasePage { * @returns {Promise} */ async closeEditCombinationModal(page: Page): Promise { - const combinationFrame: Frame|null = page.frame({url: /sell\/catalog\/products-v2\/combinations/gmi}); + const combinationFrame: Frame|null = page.frame({url: /sell\/catalog\/products\/combinations/gmi}); expect(combinationFrame).to.not.equal(null); await this.waitForSelectorAndClick(page, this.editCombinationModalCloseButton); diff --git a/tests/UI/pages/BO/catalog/products/index.ts b/tests/UI/pages/BO/catalog/products/index.ts index 0cdddbe8ce84c..2791479c69b13 100644 --- a/tests/UI/pages/BO/catalog/products/index.ts +++ b/tests/UI/pages/BO/catalog/products/index.ts @@ -342,7 +342,7 @@ class Products extends BOBasePage { await this.waitForVisibleSelector(page, `${this.modalCreateProduct} iframe`); await this.waitForHiddenSelector(page, this.modalCreateProductLoader); - const createProductFrame: Frame | null = page.frame({url: /sell\/catalog\/products-v2\/create/gmi}); + const createProductFrame: Frame | null = page.frame({url: /sell\/catalog\/products\/create/gmi}); expect(createProductFrame).to.be.not.equal(null); return this.elementVisible(createProductFrame!, this.addNewProductButton, 2000); @@ -379,7 +379,7 @@ class Products extends BOBasePage { await this.waitForVisibleSelector(page, `${this.modalCreateProduct} iframe`); await this.waitForHiddenSelector(page, this.modalCreateProductLoader); - const createProductFrame: Frame | null = page.frame({url: /sell\/catalog\/products-v2\/create/gmi}); + const createProductFrame: Frame | null = page.frame({url: /sell\/catalog\/products\/create/gmi}); expect(createProductFrame).to.be.not.eq(null); return this.getTextContent(createProductFrame!, this.productTypeDescription); @@ -404,7 +404,7 @@ class Products extends BOBasePage { await this.waitForVisibleSelector(page, `${this.modalCreateProduct} iframe`); await this.waitForHiddenSelector(page, this.modalCreateProductLoader); - const createProductFrame: Frame | null = page.frame({url: /sell\/catalog\/products-v2\/create/gmi}); + const createProductFrame: Frame | null = page.frame({url: /sell\/catalog\/products\/create/gmi}); expect(createProductFrame).to.be.not.eq(null); await this.waitForSelectorAndClick(createProductFrame!, this.productType(productType)); @@ -416,7 +416,7 @@ class Products extends BOBasePage { * @returns {Promise} */ async clickOnAddNewProduct(page: Page): Promise { - const createProductFrame: Frame | null = page.frame({url: /sell\/catalog\/products-v2\/create/gmi}); + const createProductFrame: Frame | null = page.frame({url: /sell\/catalog\/products\/create/gmi}); expect(createProductFrame).to.be.not.eq(null); await this.waitForSelectorAndClick(createProductFrame!, this.addNewProductButton); @@ -524,7 +524,7 @@ class Products extends BOBasePage { (action === 'enable' || action === 'disable') ? `${action}_selection` : `bulk_${action}`, ); await this.waitForSelectorAndClick(page, modalBulkActionsProductsCloseButton); - await page.waitForURL('**/sell/catalog/products-v2/**'); + await page.waitForURL('**/sell/catalog/products/**'); return this.elementNotVisible(page, modalBulkActionsProductsProgress, 1000); } diff --git a/tests/Unit/Adapter/Admin/UrlGeneratorTest.php b/tests/Unit/Adapter/Admin/UrlGeneratorTest.php index e5a53a3985af7..d6d467eceeeab 100644 --- a/tests/Unit/Adapter/Admin/UrlGeneratorTest.php +++ b/tests/Unit/Adapter/Admin/UrlGeneratorTest.php @@ -40,7 +40,7 @@ public function testGenerateEquivalentRoute(): void $generator = new UrlGenerator($this->getMockLegacyContext(), $this->getMockRouter()); // the following route contains a "_legacy" equivalent - list($controller, $parameters) = $generator->getLegacyOptions('admin_product_catalog'); + list($controller, $parameters) = $generator->getLegacyOptions('admin_products_index'); $this->assertEquals('AdminProducts', $controller); $this->assertCount(0, $parameters); } @@ -54,11 +54,11 @@ private function getMockLegacyContext(): LegacyContext private function getMockRouter(): Router { - $route = new Route('/{offset}/{limit}/{orderBy}/{sortOrder}'); + $route = new Route('/'); $route->setDefault('_legacy_controller', 'AdminProducts'); $routeCollection = new RouteCollection(); - $routeCollection->add('admin_product_catalog', $route); + $routeCollection->add('admin_products_index', $route); $mock = $this->createMock(Router::class); $mock->method('getRouteCollection')->willReturn($routeCollection); diff --git a/tests/Unit/PrestaShopBundle/Twig/Locator/ModuleTemplateLoaderTest.php b/tests/Unit/PrestaShopBundle/Twig/Locator/ModuleTemplateLoaderTest.php index af8457ab2bb12..02c86ebeb474f 100644 --- a/tests/Unit/PrestaShopBundle/Twig/Locator/ModuleTemplateLoaderTest.php +++ b/tests/Unit/PrestaShopBundle/Twig/Locator/ModuleTemplateLoaderTest.php @@ -44,7 +44,8 @@ class ModuleTemplateLoaderTest extends TestCase protected function setUp(): void { $namespaces = [ - 'Product' => 'Admin/Product', + 'AdvancedParameters' => 'Admin/Configure/AdvancedParameters', + 'ShopParameters' => 'Admin/Configure/ShopParameters', 'PrestaShop' => '', ]; @@ -69,8 +70,8 @@ public function testGetPaths(): void { $this->assertCount( 2, - $this->loader->getPaths('Product'), - 'Two templates for the namespace "Product" should be found.' + $this->loader->getPaths('ShopParameters'), + 'Two templates for the namespace "ShopParameters" should be found.' ); $this->assertCount( @@ -102,9 +103,9 @@ public function testGetSourceContext(string $sourceContent, string $twigPathAske public function getSourceContextsProvider(): array { return [ - ['module1', '@Product/test.html.twig', 'Module 1 wins as Module 3 is loaded after.'], - ['module1', '@PrestaShop/Admin/Product/test.html.twig', 'PrestaShop is the main namespace.'], - ['List from module 3', '@Product/ProductPage/Lists/list.html.twig', 'Module 3 templates are available.'], + ['module1', '@ShopParameters/test.html.twig', 'Module 1 wins as Module 3 is loaded after.'], + ['module1', '@PrestaShop/Admin/Configure/ShopParameters/test.html.twig', 'PrestaShop is the main namespace.'], + ['List from module 3', '@AdvancedParameters/list.html.twig', 'Module 3 templates are available.'], ['module2', '@PrestaShop/test.html.twig', 'Module 2 templates are availables.'], ]; } diff --git a/tests/Unit/Resources/twig/module1/views/PrestaShop/Admin/Product/test.html.twig b/tests/Unit/Resources/twig/module1/views/PrestaShop/Admin/Configure/ShopParameters/test.html.twig similarity index 100% rename from tests/Unit/Resources/twig/module1/views/PrestaShop/Admin/Product/test.html.twig rename to tests/Unit/Resources/twig/module1/views/PrestaShop/Admin/Configure/ShopParameters/test.html.twig diff --git a/tests/Unit/Resources/twig/module3/views/PrestaShop/Admin/Product/ProductPage/Lists/list.html.twig b/tests/Unit/Resources/twig/module3/views/PrestaShop/Admin/Configure/AdvancedParameters/list.html.twig similarity index 100% rename from tests/Unit/Resources/twig/module3/views/PrestaShop/Admin/Product/ProductPage/Lists/list.html.twig rename to tests/Unit/Resources/twig/module3/views/PrestaShop/Admin/Configure/AdvancedParameters/list.html.twig diff --git a/tests/Unit/Resources/twig/module3/views/PrestaShop/Admin/Product/test.html.twig b/tests/Unit/Resources/twig/module3/views/PrestaShop/Admin/Configure/ShopParameters/test.html.twig similarity index 100% rename from tests/Unit/Resources/twig/module3/views/PrestaShop/Admin/Product/test.html.twig rename to tests/Unit/Resources/twig/module3/views/PrestaShop/Admin/Configure/ShopParameters/test.html.twig