diff --git a/cgi/product.pl b/cgi/product.pl deleted file mode 100755 index 374b4901d7a3f..0000000000000 --- a/cgi/product.pl +++ /dev/null @@ -1,1536 +0,0 @@ -#!/usr/bin/perl -w - -# This file is part of Product Opener. -# -# Product Opener -# Copyright (C) 2011-2021 Association Open Food Facts -# Contact: contact@openfoodfacts.org -# Address: 21 rue des Iles, 94100 Saint-Maur des Fossés, France -# -# Product Opener is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -use ProductOpener::PerlStandards; - -use CGI::Carp qw(fatalsToBrowser); - -use ProductOpener::Config qw/:all/; -use ProductOpener::Store qw/:all/; -use ProductOpener::Index qw/:all/; -use ProductOpener::Display qw/:all/; -use ProductOpener::Tags qw/:all/; -use ProductOpener::Users qw/:all/; -use ProductOpener::Images qw/:all/; -use ProductOpener::Lang qw/:all/; -use ProductOpener::Mail qw/:all/; -use ProductOpener::Products qw/:all/; -use ProductOpener::Food qw/:all/; -use ProductOpener::Ingredients qw/:all/; -use ProductOpener::Images qw/:all/; -use ProductOpener::URL qw/:all/; -use ProductOpener::DataQuality qw/:all/; -use ProductOpener::Ecoscore qw/:all/; -use ProductOpener::Packaging qw/:all/; -use ProductOpener::ForestFootprint qw/:all/; -use ProductOpener::Web qw(get_languages_options_list); -use ProductOpener::Text qw/:all/; - -use Apache2::RequestRec (); -use Apache2::Const (); - -use CGI qw/:cgi :form escapeHTML :cgi-lib/; -use URI::Escape::XS; -use Storable qw/dclone/; -use Encode; -use JSON::PP; -use Log::Any qw($log); -use File::Copy qw(move); -use Data::Dumper; - -my $request_ref = ProductOpener::Display::init_request(); - -if ($User_id eq 'unwanted-user-french') { - display_error( - "Il y a des problèmes avec les modifications de produits que vous avez effectuées. Ce compte est temporairement bloqué, merci de nous contacter.", - 403 - ); -} - -my $type = single_param('type') || 'search_or_add'; -my $action = single_param('action') || 'display'; - -my $comment = 'Modification : '; - -my @errors = (); - -my $html = ''; -my $code = normalize_code(single_param('code')); -my $product_id; - -my $product_ref = undef; - -my $interface_version = '20190830'; - -local $log->context->{type} = $type; -local $log->context->{action} = $action; - -my $template_data_ref = {}; - -# Search or add product -if ($type eq 'search_or_add') { - - # barcode in image? - my $filename; - if ((not defined $code) or ($code eq "")) { - $code = process_search_image_form(\$filename); - } - elsif ($code !~ /^\d{4,24}$/) { - display_error($Lang{invalid_barcode}{$lang}, 403); - } - - my $r = Apache2::RequestUtil->request(); - my $method = $r->method(); - if ( (not defined $code) - and ((not defined single_param("imgupload_search")) or (single_param("imgupload_search") eq '')) - and ($method eq 'POST')) - { - - ($code, $product_id) = assign_new_code(); - } - - my %data = (); - my $location; - - if (defined $code) { - $data{code} = $code; - $product_id = product_id_for_owner($Owner_id, $code); - $log->debug("we have a code", {code => $code, product_id => $product_id}) if $log->is_debug(); - - $product_ref = product_exists($product_id); # returns 0 if not - - if ($product_ref) { - $log->info("product exists, redirecting to page", {code => $code}) if $log->is_info(); - $location = product_url($product_ref); - - # jquery.fileupload ? - if (single_param('jqueryfileupload')) { - - $type = 'show'; - } - else { - my $r = shift; - $r->headers_out->set(Location => $location); - $r->status(301); - return 301; - } - } - else { - $log->info("product does not exist, creating product", {code => $code, product_id => $product_id}) - if $log->is_info(); - $product_ref = init_product($User_id, $Org_id, $code, $country); - $product_ref->{interface_version_created} = $interface_version; - store_product($User_id, $product_ref, 'product_created'); - - $type = 'add'; - $action = 'display'; - $location = "/cgi/product.pl?type=add&code=$code"; - - # If we got a barcode image, upload it - if (defined $filename) { - my $imgid; - my $debug; - process_image_upload($product_ref->{_id}, $filename, $User_id, time(), - 'image with barcode from web site Add product button', - \$imgid, \$debug); - } - } - } - else { - if (defined single_param("imgupload_search")) { - $log->info("no code found in image") if $log->is_info(); - $data{error} = lang("image_upload_error_no_barcode_found_in_image_short"); - } - else { - $log->info("no code found in text") if $log->is_info(); - } - } - - $data{type} = $type; - $data{location} = $location; - - # jquery.fileupload ? - if (single_param('jqueryfileupload')) { - - my $data = encode_json(\%data); - - $log->debug("jqueryfileupload JSON data output", {data => $data}) if $log->is_debug(); - - print header(-type => 'application/json', -charset => 'utf-8') . $data; - exit(); - } - - $template_data_ref->{param_imgupload_search} = single_param("imgupload_search"); - -} - -else { - # We should have a code - if ((not defined $code) or ($code eq '')) { - display_error($Lang{missing_barcode}{$lang}, 403); - } - elsif ($code !~ /^\d{4,24}$/) { - display_error($Lang{invalid_barcode}{$lang}, 403); - } - else { - if ( ((defined $server_options{private_products}) and ($server_options{private_products})) - and (not defined $Owner_id)) - { - - display_error(lang("no_owner_defined"), 200); - } - $product_id = product_id_for_owner($Owner_id, $code); - $product_ref = retrieve_product_or_deleted_product($product_id, $User{moderator}); - if (not defined $product_ref) { - display_error(sprintf(lang("no_product_for_barcode"), $code), 404); - } - } -} - -if (($type eq 'delete') and (not $User{moderator})) { - display_error($Lang{error_no_permission}{$lang}, 403); -} - -if ($User_id eq 'unwanted-bot-id') { - my $r = Apache2::RequestUtil->request(); - $r->status(500); - return 500; -} - -if (($type eq 'add') or ($type eq 'edit') or ($type eq 'delete')) { - - if (not defined $User_id) { - - my $submit_label = "login_and_" . $type . "_product"; - $action = 'login'; - $template_data_ref->{type} = $type; - } -} - -$template_data_ref->{user_id} = $User_id; -$template_data_ref->{code} = $code; -process_template('web/pages/product_edit/product_edit_form.tt.html', $template_data_ref, \$html) - or $html = "

" . $tt->error() . "

"; - -my @fields = @ProductOpener::Config::product_fields; - -if ($admin) { - push @fields, "environment_impact_level"; - - # Let admins edit any other fields - if (defined single_param("fields")) { - push @fields, split(/,/, single_param("fields")); - } - -} - -if (($action eq 'process') and (($type eq 'add') or ($type eq 'edit'))) { - - # Process edit rules - - $log->debug("phase 0 - checking edit rules", {code => $code, type => $type}) if $log->is_debug(); - - my $proceed_with_edit = process_product_edit_rules($product_ref); - - $log->debug("phase 0", {code => $code, type => $type, proceed_with_edit => $proceed_with_edit}) if $log->is_debug(); - - if (not $proceed_with_edit) { - - display_error("Edit against edit rules", 403); - } - - $log->debug("phase 1", {code => $code, type => $type}) if $log->is_debug(); - - exists $product_ref->{new_server} and delete $product_ref->{new_server}; - - # 26/01/2017 - disallow barcode changes until we fix bug #677 - if ($User{moderator} and (defined single_param("new_code")) and (single_param("new_code") ne "")) { - - change_product_server_or_code($product_ref, single_param("new_code"), \@errors); - $code = $product_ref->{code}; - } - - my @param_fields = (); - - my @param_sorted_langs = (); - my %param_sorted_langs = (); - if (defined single_param("sorted_langs")) { - foreach my $display_lc (split(/,/, single_param("sorted_langs"))) { - if ($display_lc =~ /^\w\w$/) { - push @param_sorted_langs, $display_lc; - $param_sorted_langs{$display_lc} = 1; - } - } - } - else { - push @param_sorted_langs, $product_ref->{lc}; - } - - # Make sure we have the main language of the product (which could be new) - # needed if we are moving data from one language to the main language - if ( (defined single_param("lang")) - and (single_param("lang") =~ /^\w\w$/) - and (not defined $param_sorted_langs{single_param("lang")})) - { - push @param_sorted_langs, single_param("lang"); - } - - $product_ref->{"debug_param_sorted_langs"} = \@param_sorted_langs; - - foreach my $field ('product_name', 'generic_name', @fields, 'nutrition_data_per', 'nutrition_data_prepared_per', - 'serving_size', 'allergens', 'traces', 'ingredients_text', 'origin', 'packaging_text', 'lang') - { - - if (defined $language_fields{$field}) { - foreach my $display_lc (@param_sorted_langs) { - push @param_fields, $field . "_" . $display_lc; - } - } - else { - push @param_fields, $field; - } - } - - # Move all data and photos from one language to another? - if ($User{moderator}) { - - my $product_lc = single_param("lang"); - - foreach my $from_lc (@param_sorted_langs) { - - my $moveid = "move_" . $from_lc . "_data_and_images_to_main_language"; - - if (($from_lc ne $product_lc) and (defined single_param($moveid)) and (single_param($moveid) eq "on")) { - - my $mode = single_param($moveid . "_mode") || "replace"; - - $log->debug( - "moving all data and photos from one language to another", - {from_lc => $from_lc, product_lc => $product_lc, mode => $mode} - ) if $log->is_debug(); - - # Text fields - - foreach my $field (sort keys %language_fields) { - - my $from_field = $field . "_" . $from_lc; - my $to_field = $field . "_" . $product_lc; - - my $from_value = single_param($from_field); - - $log->debug("moving field value?", - {from_field => $from_field, from_value => $from_value, to_field => $to_field}) - if $log->is_debug(); - - if ((defined $from_value) and ($from_value ne "")) { - - my $to_value = single_param($to_field); - - $log->debug( - "moving field value", - { - from_field => $from_field, - from_value => $from_value, - to_field => $to_field, - to_value => $to_value, - mode => $mode - } - ) if $log->is_debug(); - - if (($mode eq "replace") or ((not defined $to_value) or ($to_value eq ""))) { - - $log->debug( - "replacing to field value", - { - from_field => $from_field, - from_value => $from_value, - to_field => $to_field, - to_value => $to_value, - mode => $mode - } - ) if $log->is_debug(); - - param($to_field, $from_value); - } - - $log->debug( - "deleting from field value", - { - from_field => $from_field, - from_value => $from_value, - to_field => $to_field, - to_value => $to_value, - mode => $mode - } - ) if $log->is_debug(); - - param($from_field, ""); - } - } - - # Selected photos - - foreach my $imageid ("front", "ingredients", "nutrition", "packaging") { - - my $from_imageid = $imageid . "_" . $from_lc; - my $to_imageid = $imageid . "_" . $product_lc; - - if ((defined $product_ref->{images}) and (defined $product_ref->{images}{$from_imageid})) { - - $log->debug("moving selected image", {from_imageid => $from_imageid, to_imageid => $to_imageid}) - if $log->is_debug(); - - if (($mode eq "replace") or (not defined $product_ref->{images}{$to_imageid})) { - - $product_ref->{images}{$to_imageid} = $product_ref->{images}{$from_imageid}; - my $rev = $product_ref->{images}{$from_imageid}{rev}; - - # Rename the images - - my $path = product_path($product_ref); - - foreach my $max ($thumb_size, $small_size, $display_size, "full") { - my $from_file - = "$www_root/images/products/$path/" - . $from_imageid . "." - . $rev . "." - . $max . ".jpg"; - my $to_file - = "$www_root/images/products/$path/" . $to_imageid . "." . $rev . "." . $max . ".jpg"; - File::Copy::move($from_file, $to_file); - } - } - - delete $product_ref->{images}{$from_imageid}; - } - } - } - } - } - - foreach my $field (@param_fields) { - - if (defined single_param($field)) { - - # If we are on the public platform, and the field data has been imported from the producer platform - # ignore the field changes for non tag fields, unless made by a moderator - if ( ((not defined $server_options{private_products}) or (not $server_options{private_products})) - and (defined $product_ref->{owner_fields}) - and (defined $product_ref->{owner_fields}{$field}) - and (not $User{moderator})) - { - $log->debug( - "skipping field with a value set by the owner", - { - code => $code, - field_name => $field, - existing_field_value => $product_ref->{$field}, - new_field_value => remove_tags_and_quote(decode utf8 => single_param($field)) - } - ) if $log->is_debug(); - } - - if ($field eq "lang") { - my $value = remove_tags_and_quote(decode utf8 => single_param($field)); - - # strip variants fr-BE fr_BE - $value =~ s/^([a-z][a-z])(-|_).*$/$1/i; - $value = lc($value); - - # skip unrecognized languages (keep the existing lang & lc value) - if (defined $lang_lc{$value}) { - $product_ref->{lang} = $value; - $product_ref->{lc} = $value; - } - - } - else { - # infocards set by admins can contain HTML - if (($admin) and ($field =~ /infocard/)) { - $product_ref->{$field} = decode utf8 => single_param($field); - } - else { - $product_ref->{$field} = remove_tags_and_quote(decode utf8 => single_param($field)); - } - } - - $log->debug("before compute field_tags", - {code => $code, field_name => $field, field_value => $product_ref->{$field}}) - if $log->is_debug(); - if ($field =~ /ingredients_text/) { - # the ingredients_text_with_allergens[_$lc] will be recomputed after - my $ingredients_text_with_allergens = $field; - $ingredients_text_with_allergens =~ s/ingredients_text/ingredients_text_with_allergens/; - delete $product_ref->{$ingredients_text_with_allergens}; - } - - compute_field_tags($product_ref, $lc, $field); - - } - else { - $log->debug("could not find field in params", {field => $field}) if $log->is_debug(); - } - } - - if ( (defined $product_ref->{nutriments}{"carbon-footprint"}) - and ($product_ref->{nutriments}{"carbon-footprint"} ne '')) - { - push @{$product_ref->{"labels_hierarchy"}}, "en:carbon-footprint"; - push @{$product_ref->{"labels_tags"}}, "en:carbon-footprint"; - } - - if ((defined $product_ref->{nutriments}{"glycemic-index"}) and ($product_ref->{nutriments}{"glycemic-index"} ne '')) - { - push @{$product_ref->{"labels_hierarchy"}}, "en:glycemic-index"; - push @{$product_ref->{"labels_tags"}}, "en:glycemic-index"; - } - - # For fields that can have different values in different languages, copy the main language value to the non suffixed field - - foreach my $field (keys %language_fields) { - if ($field !~ /_image/) { - if (defined $product_ref->{$field . "_$product_ref->{lc}"}) { - $product_ref->{$field} = $product_ref->{$field . "_$product_ref->{lc}"}; - } - } - } - - $log->debug("compute_languages") if $log->is_debug(); - - compute_languages($product_ref); # need languages for allergens detection and cleaning ingredients - $log->debug("clean_ingredients") if $log->is_debug(); - - # Ingredients classes - clean_ingredients_text($product_ref); - $log->debug("extract_ingredients_from_text") if $log->is_debug(); - extract_ingredients_from_text($product_ref); - $log->debug("extract_ingredients_classes_from_text") if $log->is_debug(); - extract_ingredients_classes_from_text($product_ref); - $log->debug("detect_allergens_from_text") if $log->is_debug(); - detect_allergens_from_text($product_ref); - compute_carbon_footprint_from_ingredients($product_ref); - compute_carbon_footprint_from_meat_or_fish($product_ref); - - # Food category rules for sweetened/sugared beverages - # French PNNS groups from categories - - if ((defined $options{product_type}) and ($options{product_type} eq "food")) { - $log->debug("Food::special_process_product") if $log->is_debug(); - ProductOpener::Food::special_process_product($product_ref); - } - - # Obsolete products - - if (($User{moderator} or $Owner_id) and (defined single_param('obsolete_since_date'))) { - $product_ref->{obsolete} = remove_tags_and_quote(decode utf8 => single_param("obsolete")); - $product_ref->{obsolete_since_date} = remove_tags_and_quote(decode utf8 => single_param("obsolete_since_date")); - } - - # Update the nutrients - - assign_nutriments_values_from_request_parameters($product_ref, $nutriment_table); - - # product check - - if ($User{moderator}) { - - my $checked = remove_tags_and_quote(decode utf8 => single_param("photos_and_data_checked")); - if ((defined $checked) and ($checked eq 'on')) { - if ((defined $product_ref->{checked}) and ($product_ref->{checked} eq 'on')) { - my $rechecked = remove_tags_and_quote(decode utf8 => single_param("photos_and_data_rechecked")); - if ((defined $rechecked) and ($rechecked eq 'on')) { - $product_ref->{last_checker} = $User_id; - $product_ref->{last_checked_t} = time(); - } - } - else { - $product_ref->{checked} = 'on'; - $product_ref->{last_checker} = $User_id; - $product_ref->{last_checked_t} = time(); - } - } - else { - delete $product_ref->{checked}; - delete $product_ref->{last_checker}; - delete $product_ref->{last_checked_t}; - } - - } - - # Compute nutrition data per 100g and per serving - - $log->debug("compute nutrition data") if $log->is_debug(); - - $log->trace("compute_serving_size_date - start") if $log->is_trace(); - - fix_salt_equivalent($product_ref); - - compute_serving_size_data($product_ref); - - compute_nutrition_score($product_ref); - - compute_nova_group($product_ref); - - compute_nutrient_levels($product_ref); - - compute_unknown_nutrients($product_ref); - - # Until we provide an interface to directly change the packaging data structure - # erase it before reconstructing it - # (otherwise there is no way to remove incorrect entries) - $product_ref->{packagings} = []; - - analyze_and_combine_packaging_data($product_ref); - - if ((defined $options{product_type}) and ($options{product_type} eq "food")) { - compute_ecoscore($product_ref); - compute_forest_footprint($product_ref); - } - - ProductOpener::DataQuality::check_quality($product_ref); - - $log->trace("end compute_serving_size_date - end") if $log->is_trace(); - - if ($#errors >= 0) { - $action = 'display'; - } -} - -# Display the product edit form - -my %remember_fields = ('purchase_places' => 1, 'stores' => 1); - -# Display each field - -sub display_input_field ($product_ref, $field, $language) { - # $field can be in %language_fields and suffixed by _[lc] - - my $fieldtype = $field; - my $display_lc = $lc; - my @field_notes; - - my $template_data_ref_field = {}; - - if (($field =~ /^(.*?)_(..|new_lc)$/) and (defined $language_fields{$1})) { - $fieldtype = $1; - $display_lc = $2; - } - - my $autocomplete = ""; - my $class = ""; - if (defined $tags_fields{$fieldtype}) { - $class = "tagify-me"; - if ((defined $taxonomy_fields{$fieldtype}) or ($fieldtype eq 'emb_codes')) { - $autocomplete = "$formatted_subdomain/cgi/suggest.pl?tagtype=$fieldtype&"; - } - } - - my $value = $product_ref->{$field}; - - if ( - (defined $value) - and (defined $taxonomy_fields{$field}) - # if the field was previously not taxonomized, the $field_hierarchy field does not exist - and (defined $product_ref->{$field . "_hierarchy"}) - ) - { - $value = display_tags_hierarchy_taxonomy($lc, $field, $product_ref->{$field . "_hierarchy"}); - # Remove tags - $value =~ s/<(([^>]|\n)*)>//g; - } - if (not defined $value) { - $value = ""; - } - - $template_data_ref_field->{language} = $language; - $template_data_ref_field->{field} = $field; - $template_data_ref_field->{class} = $class; - $template_data_ref_field->{value} = $value; - $template_data_ref_field->{display_lc} = $display_lc; - $template_data_ref_field->{autocomplete} = $autocomplete; - $template_data_ref_field->{fieldtype} = $Lang{$fieldtype}{$lang}; - - my $html_field = ''; - - if (($field =~ /infocard/) or ($field =~ /^packaging_text/)) { - - } - else { - # Line feeds will be removed in text inputs, convert them to spaces - $value =~ s/\n/ /g; - } - - foreach my $note ("_note", "_note_2") { - if (defined $Lang{$fieldtype . $note}{$lang}) { - - push( - @field_notes, - { - note => $Lang{$fieldtype . $note}{$lang}, - } - ); - - } - } - - $template_data_ref_field->{field_notes} = \@field_notes; - - if (defined $Lang{$fieldtype . "_example"}{$lang}) { - - my $examples = $Lang{example}{$lang}; - if ($Lang{$fieldtype . "_example"}{$lang} =~ /,/) { - $examples = $Lang{examples}{$lang}; - } - $template_data_ref_field->{examples} = $examples; - $template_data_ref_field->{field_type_examples} = $Lang{$fieldtype . "_example"}{$lang}; - } - - process_template('web/pages/product_edit/display_input_field.tt.html', $template_data_ref_field, \$html_field) - or $html_field = "

" . $tt->error() . "

"; - - return $html_field; -} - -if (($action eq 'display') and (($type eq 'add') or ($type eq 'edit'))) { - - # Populate the energy-kcal or energy-kj field from the energy field if it exists - compute_serving_size_data($product_ref); - - my $template_data_ref_display = {}; - my $js; - - $log->debug("displaying product", {code => $code}) if $log->is_debug(); - - # Lang strings for product.js - - my $moderator = 0; - if ($User{moderator}) { - $moderator = 1; - } - - $header .= < - - -HTML - ; - - $scripts .= < - - - - - - - - - - - -HTML - ; - - my $thumb_selectable_size = $thumb_size + 20; - - $styles .= <{producers_platform_url} = $producers_platform_url; - } - - $template_data_ref_display->{errors_index} = $#errors; - $template_data_ref_display->{errors} = \@errors; - - my $label_new_code = $Lang{new_code}{$lang}; - - # 26/01/2017 - disallow barcode changes until we fix bug #677 - if ($User{moderator}) { - } - - $template_data_ref_display->{server_options_private_products} = $server_options{private_products}; - $template_data_ref_display->{org_id} = $Org_id; - $template_data_ref_display->{label_new_code} = $label_new_code; - $template_data_ref_display->{owner_id} = $Owner_id; - - # obsolete products: restrict to admin on public site - # authorize owners on producers platform - if ($User{moderator} or $Owner_id) { - - my $checked = ''; - if ((defined $product_ref->{obsolete}) and ($product_ref->{obsolete} eq 'on')) { - $checked = 'checked="checked"'; - } - - $template_data_ref_display->{obsolete_checked} = $checked; - $template_data_ref_display->{display_field_obsolete} - = display_input_field($product_ref, "obsolete_since_date", undef); - - } - - # Main language - my $lang_value = $lang; - if (defined $product_ref->{lc}) { - $lang_value = $product_ref->{lc}; - } - - $template_data_ref_display->{product_lang_value} = $lang_value; - # List of all languages for the template to display a dropdown for fields that are language specific - $template_data_ref_display->{lang_options} = get_languages_options_list($lc); - $template_data_ref_display->{display_select_manage} = display_select_manage($product_ref); - - # sort function to put main language first, other languages by alphabetical order, then add new language tab - - defined $product_ref->{lc} or $product_ref->{lc} = $lc; - defined $product_ref->{languages_codes} or $product_ref->{languages_codes} = {}; - - $product_ref->{sorted_langs} = [$product_ref->{lc}]; - - foreach my $olc (sort keys %{$product_ref->{languages_codes}}) { - if ($olc ne $product_ref->{lc}) { - push @{$product_ref->{sorted_langs}}, $olc; - } - } - - $template_data_ref_display->{product_ref_sorted_langs} = join(',', @{$product_ref->{sorted_langs}}); - - sub display_input_tabs ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref) { - - my $template_data_ref_tab = {}; - my @display_tabs; - - $template_data_ref_tab->{tabsid} = $tabsid; - - my $active = " active"; - - foreach my $tabid (@$tabsids_array_ref, 'new_lc', 'new') { - - my $new_lc = ''; - if ($tabid eq 'new_lc') { - $new_lc = ' new_lc hide'; - } - elsif ($tabid eq 'new') { - $new_lc = ' new'; - } - - # We will create an array of fields for each language - my @fields_arr = (); - - my $display_tab_ref = { - tabid => $tabid, - active => $active, - new_lc => $new_lc, - }; - - my $language; - - if ($tabid ne 'new') { - - $language = display_taxonomy_tag($lc, 'languages', $language_codes{$tabid}) - ; # instead of $tabsids_hash_ref->{$tabid} - $display_tab_ref->{language} = $language; - - my $display_lc = $tabid; - $template_data_ref_tab->{display_lc} = $display_lc; - - foreach my $field (@{$fields_array_ref}) { - - # For the ingredient_text field, we will output a div above to display the image of the ingredients - my $image_full_id; - my $display_div; - - if ($field =~ /^(.*)_image/) { - - my $image_field = $1 . "_" . $display_lc; - $display_div = display_select_crop($product_ref, $image_field, $language); - } - elsif ($field eq 'ingredients_text') { - $image_full_id = "ingredients_" . ${display_lc} . "_image_full"; - $display_div = display_input_field($product_ref, $field . "_" . $display_lc, $language); - } - else { - $log->debug("display_field", {field_name => $field, field_value => $product_ref->{$field}}) - if $log->is_debug(); - $display_div = display_input_field($product_ref, $field . "_" . $display_lc, $language); - } - - push( - @fields_arr, - { - image_full_id => $image_full_id, - field => $field, - display_div => $display_div, - } - ); - } - - $display_tab_ref->{fields} = \@fields_arr; - } - - # For moderators, add a checkbox to move all data and photos to the main language - # this needs to be below the "add (language name) in all field labels" above, so that it does not change this label. - if (($User{moderator}) and ($tabsid eq "front_image")) { - - my $msg = f_lang( - "f_move_data_and_photos_to_main_language", - { - language => '' . $language . '', - main_language => '' - . lang("lang_" . $product_ref->{lc}) - . '' - } - ); - - my $moveid = "move_" . $tabid . "_data_and_images_to_main_language"; - - $display_tab_ref->{moveid} = $moveid; - $display_tab_ref->{msg} = $msg; - } - - push(@display_tabs, $display_tab_ref); - - # Only the first tab is active - $active = ""; - - } - - $template_data_ref_tab->{display_tabs} = \@display_tabs; - - my $html_tab = ''; - process_template('web/pages/product_edit/display_input_tabs.tt.html', $template_data_ref_tab, \$html_tab) - or $html_tab = "

" . $tt->error() . "

"; - - return $html_tab; - } - - $template_data_ref_display->{display_tab_product_picture} - = display_input_tabs($product_ref, "front_image", $product_ref->{sorted_langs}, \%Langs, ["front_image"]); - $template_data_ref_display->{display_tab_product_characteristics} - = display_input_tabs($product_ref, "product", $product_ref->{sorted_langs}, - \%Langs, ["product_name", "generic_name"]); - - my @display_fields_arr; - foreach my $field (@fields) { - next if $field eq "origins"; # now displayed below allergens and traces in the ingredients section - $log->debug("display_field", {field_name => $field, field_value => $product_ref->{$field}}) if $log->is_debug(); - my $display_field = display_input_field($product_ref, $field, undef); - push(@display_fields_arr, $display_field); - } - - $template_data_ref_display->{display_fields_arr} = \@display_fields_arr; - my @ingredients_fields = ("ingredients_image", "ingredients_text", "origin"); - - my $checked = ''; - my $tablestyle = 'display: table;'; - my $disabled = ''; - if ((defined $product_ref->{no_nutrition_data}) and ($product_ref->{no_nutrition_data} eq 'on')) { - $checked = 'checked="checked"'; - $tablestyle = 'display: none;'; - $disabled = 'disabled="disabled"'; - } - - $template_data_ref_display->{nutrition_checked} = $checked; - $template_data_ref_display->{display_tab_ingredients_image} - = display_input_tabs($product_ref, "ingredients_image", $product_ref->{sorted_langs}, \%Langs, - \@ingredients_fields); - $template_data_ref_display->{display_field_allergens} = display_input_field($product_ref, "allergens", undef); - $template_data_ref_display->{display_field_traces} = display_input_field($product_ref, "traces", undef); - $template_data_ref_display->{display_field_origins} = display_input_field($product_ref, "origins", undef); - $template_data_ref_display->{display_tab_nutrition_image} - = display_input_tabs($product_ref, "nutrition_image", $product_ref->{sorted_langs}, \%Langs, ["nutrition_image"]); - $template_data_ref_display->{display_field_serving_size} = display_input_field($product_ref, "serving_size", undef); - - $initjs .= display_select_crop_init($product_ref); - - my $hidden_inputs = ''; - - #

→ $Lang{nutrition_data_table_note}{$lang}

- - # Display 2 checkbox to indicate the nutrition values present on the product - - if (not defined $product_ref->{nutrition_data}) { - # by default, display the nutrition data entry column for the product as sold - $product_ref->{nutrition_data} = "on"; - } - if (not defined $product_ref->{nutrition_data_prepared}) { - # by default, do not display the nutrition data entry column for the prepared product - $product_ref->{nutrition_data_prepared} = ""; - } - - my %column_display_style = (); - my %nutrition_data_per_display_style = (); - my @nutrition_products; - - # keep existing field ids for the product as sold, and append _prepared_product for the product after it has been prepared - foreach my $product_type ("", "_prepared") { - - my $nutrition_data = "nutrition_data" . $product_type; - my $nutrition_data_exists = "nutrition_data" . $product_type . "_exists"; - my $nutrition_data_instructions = "nutrition_data" . $product_type . "_instructions"; - - my $checked = ''; - $column_display_style{$nutrition_data} = ''; - my $hidden = ''; - if (($product_ref->{$nutrition_data} eq 'on')) { - $checked = 'checked="checked"'; - } - else { - $column_display_style{$nutrition_data} = 'style="display:none"'; - $hidden = 'style="display:none"'; - } - - my $checked_per_serving = ''; - my $checked_per_100g = 'checked="checked"'; - $nutrition_data_per_display_style{$nutrition_data . "_serving"} = ' style="display:none"'; - $nutrition_data_per_display_style{$nutrition_data . "_100g"} = ''; - - my $nutrition_data_per = "nutrition_data" . $product_type . "_per"; - - if ( - ($product_ref->{$nutrition_data_per} eq 'serving') - # display by serving by default for the prepared product - or (($product_type eq '_prepared') and (not defined $product_ref->{nutrition_data_prepared_per})) - ) - { - $checked_per_serving = 'checked="checked"'; - $checked_per_100g = ''; - $nutrition_data_per_display_style{$nutrition_data . "_serving"} = ''; - $nutrition_data_per_display_style{$nutrition_data . "_100g"} = ' style="display:none"'; - } - - my $nutriment_col_class = "nutriment_col" . $product_type; - - my $product_type_as_sold_or_prepared = "as_sold"; - if ($product_type eq "_prepared") { - $product_type_as_sold_or_prepared = "prepared"; - } - - push( - @nutrition_products, - { - checked => $checked, - nutrition_data => $nutrition_data, - nutrition_data_exists => $Lang{$nutrition_data_exists}{$lang}, - nutrition_data_per => $nutrition_data_per, - checked_per_100g => $checked_per_100g, - checked_per_serving => $checked_per_serving, - nutrition_data_instructions => $nutrition_data_instructions, - nutrition_data_instructions_check => $Lang{$nutrition_data_instructions}, - nutrition_data_instructions_lang => $Lang{$nutrition_data_instructions}{$lang}, - hidden => $hidden, - nutriment_col_class => $nutriment_col_class, - product_type_as_sold_or_prepared => $product_type_as_sold_or_prepared, - checkmate => $product_ref->{$nutrition_data_per}, - } - ); - - } - - $template_data_ref_display->{nutrition_products} = \@nutrition_products; - - $template_data_ref_display->{column_display_style_nutrition_data} = $column_display_style{"nutrition_data"}; - $template_data_ref_display->{column_display_style_nutrition_data_prepared} - = $column_display_style{"nutrition_data_prepared"}; - $template_data_ref_display->{nutrition_data_100g_style} = $nutrition_data_per_display_style{"nutrition_data_100g"}; - $template_data_ref_display->{nutrition_data_serving_style} - = $nutrition_data_per_display_style{"nutrition_data_serving"}; - $template_data_ref_display->{nutrition_data_prepared_100g_style} - = $nutrition_data_per_display_style{"nutrition_data_prepared_100g"}; - $template_data_ref_display->{nutrition_data_prepared_serving_style} - = $nutrition_data_per_display_style{"nutrition_data_prepared_serving"}; - - $template_data_ref_display->{tablestyle} = $tablestyle; - - defined $product_ref->{nutriments} or $product_ref->{nutriments} = {}; - - my @unknown_nutriments = (); - my %seen_unknown_nutriments = (); - foreach my $nid (keys %{$product_ref->{nutriments}}) { - - next if (($nid =~ /_/) and ($nid !~ /_prepared$/)); - - $nid =~ s/_prepared$//; - - $log->trace("detect unknown nutriment", {nid => $nid}) if $log->is_trace(); - - if ( (not exists_taxonomy_tag("nutrients", "zz:$nid")) - and (defined $product_ref->{nutriments}{$nid . "_label"}) - and (not defined $seen_unknown_nutriments{$nid})) - { - push @unknown_nutriments, $nid; - $log->debug("unknown nutriment detected", {nid => $nid}) if $log->is_debug(); - } - } - - my @nutriments; - foreach my $nutriment (@{$nutriments_tables{$nutriment_table}}, @unknown_nutriments, 'new_0', 'new_1') { - - my $nutriment_ref = {}; - - next if $nutriment =~ /^\#/; - my $nid = $nutriment; - $nid =~ s/^(-|!)+//g; - $nid =~ s/-$//g; - - next if $nid =~ /^nutrition-score/; - - # Do not display the energy field without a unit, display energy-kcal or energy-kj instead - next if $nid eq "energy"; - - my $class = 'main'; - my $prefix = ''; - - my $shown = 0; - - if ( - ($nutriment !~ /-$/) - or ((defined $product_ref->{nutriments}{$nid}) and ($product_ref->{nutriments}{$nid} ne '')) - or ( (defined $product_ref->{nutriments}{$nid . "_prepared"}) - and ($product_ref->{nutriments}{$nid . "_prepared"} ne '')) - or ( (defined $product_ref->{nutriments}{$nid . "_modifier"}) - and ($product_ref->{nutriments}{$nid . "_modifier"} eq '-')) - or ( (defined $product_ref->{nutriments}{$nid . "_prepared_modifier"}) - and ($product_ref->{nutriments}{$nid . "_prepared_modifier"} eq '-')) - or ($nid eq 'new_0') - or ($nid eq 'new_1') - ) - { - $shown = 1; - } - - if (($shown) and ($nutriment =~ /^-/)) { - $class = 'sub'; - $prefix = $Lang{nutrition_data_table_sub}{$lang} . " "; - if ($nutriment =~ /^--/) { - $prefix = "  " . $prefix; - } - } - - my $display = ''; - if ($nid eq 'new_0') { - $display = ' style="display:none"'; - } - - my $enid = encodeURIComponent($nid); - - # for prepared product - my $nidp = $nid . "_prepared"; - my $enidp = encodeURIComponent($nidp); - - $nutriment_ref->{label_value} = $product_ref->{nutriments}{$nid . "_label"}; - $nutriment_ref->{product_add_nutrient} = $Lang{product_add_nutrient}{$lang}; - $nutriment_ref->{prefix} = $prefix; - - my $unit = "g"; - - if (exists_taxonomy_tag("nutrients", "zz:$nid")) { - $nutriment_ref->{name} = display_taxonomy_tag($lc, "nutrients", "zz:$nid"); - # We may have a unit specific to the country (e.g. US nutrition facts table using the International Unit for this nutrient, and Europe using mg) - $unit = get_property("nutrients", "zz:$nid", "unit_$cc:en") - // get_property("nutrients", "zz:$nid", "unit:en") // 'g'; - } - else { - if (defined $product_ref->{nutriments}{$nid . "_unit"}) { - $unit = $product_ref->{nutriments}{$nid . "_unit"}; - } - } - - my $value; # product as sold - my $valuep; # prepared product - - if ($nid eq 'water-hardness') { - $value = mmoll_to_unit($product_ref->{nutriments}{$nid}, $unit); - $valuep = mmoll_to_unit($product_ref->{nutriments}{$nidp}, $unit); - } - elsif ($nid eq 'energy-kcal') { - # energy-kcal is already in kcal - $value = $product_ref->{nutriments}{$nid}; - $valuep = $product_ref->{nutriments}{$nidp}; - } - else { - $value = g_to_unit($product_ref->{nutriments}{$nid}, $unit); - $valuep = g_to_unit($product_ref->{nutriments}{$nidp}, $unit); - } - - # If we have a user specified unit and value, use it instead of the default unit of the field - if (defined $product_ref->{nutriments}{$nid . "_unit"}) { - $unit = $product_ref->{nutriments}{$nid . "_unit"}; - if (defined $product_ref->{nutriments}{$nid . "_value"}) { - $value = $product_ref->{nutriments}{$nid . "_value"}; - } - - if (defined $product_ref->{nutriments}{$nidp . "_value"}) { - $valuep = $product_ref->{nutriments}{$nidp . "_value"}; - } - } - - # Add modifiers - if (defined $product_ref->{nutriments}{$nid . "_modifier"}) { - - if ($value ne '') { - $product_ref->{nutriments}{$nid . "_modifier"} eq '<' and $value = "< $value"; - $product_ref->{nutriments}{$nid . "_modifier"} eq "\N{U+2264}" and $value = "≤ $value"; - $product_ref->{nutriments}{$nid . "_modifier"} eq '>' and $value = "> $value"; - $product_ref->{nutriments}{$nid . "_modifier"} eq "\N{U+2265}" and $value = "≥ $value"; - $product_ref->{nutriments}{$nid . "_modifier"} eq '~' and $value = "~ $value"; - } - elsif ($product_ref->{nutriments}{$nid . "_modifier"} eq '-') { - # The - minus sign indicates that there is no value specified on the product - $value = '-'; - } - } - - if (defined $product_ref->{nutriments}{$nidp . "_modifier"}) { - - if ($valuep ne '') { - $product_ref->{nutriments}{$nidp . "_modifier"} eq '<' and $valuep = "< $valuep"; - $product_ref->{nutriments}{$nidp . "_modifier"} eq "\N{U+2264}" and $valuep = "≤ $valuep"; - $product_ref->{nutriments}{$nidp . "_modifier"} eq '>' and $valuep = "> $valuep"; - $product_ref->{nutriments}{$nidp . "_modifier"} eq "\N{U+2265}" and $valuep = "≥ $valuep"; - $product_ref->{nutriments}{$nidp . "_modifier"} eq '~' and $valuep = "~ $valuep"; - } - elsif ($product_ref->{nutriments}{$nidp . "_modifier"} eq '-') { - # The - minus sign indicates that there is no value specified on the product - $valuep = '-'; - } - } - - if (lc($unit) eq "mcg") { - $unit = "µg"; - } - - my $disabled_backup = $disabled; - if ($nid eq 'carbon-footprint') { - # Workaround, so that the carbon footprint, that could be in a location different from actual nutrition facts, - # will never be disabled. - $disabled = ''; - } - - if (($nid eq 'alcohol') or ($nid eq 'energy-kj') or ($nid eq 'energy-kcal')) { - my $unit = ''; - - if (($nid eq 'alcohol')) {$unit = '% vol / °';} # alcohol in % vol / ° - elsif (($nid eq 'energy-kj')) {$unit = 'kJ';} - elsif (($nid eq 'energy-kcal')) {$unit = 'kcal';} - - $nutriment_ref->{nutriment_unit} = $unit; - - } - else { - - my @units = ('g', 'mg', 'µg'); - my @units_arr; - - if ($nid =~ /^energy/) { - @units = ('kJ', 'kcal'); - } - elsif ($nid eq 'water-hardness') { - @units = ( - 'mol/l', 'mmol/l', 'mval/l', 'ppm', "\N{U+00B0}rH", "\N{U+00B0}fH", - "\N{U+00B0}e", "\N{U+00B0}dH", 'gpg' - ); - } - - if ( (defined get_property("nutrients", "zz:$nid", "dv_value:en")) - or ($nid =~ /^new_/) - or (uc($unit) eq '% DV')) - { - push @units, '% DV'; - } - if ( (defined get_property("nutrients", "zz:$nid", "iu_value:en")) - or ($nid =~ /^new_/) - or (uc($unit) eq 'IU') - or (uc($unit) eq 'UI')) - { - push @units, 'IU'; - } - - my $hide_percent = ''; - my $hide_select = ''; - - if ($unit eq '') { - $hide_percent = ' style="display:none"'; - $hide_select = ' style="display:none"'; - - } - elsif ($unit eq '%') { - $hide_select = ' style="display:none"'; - } - else { - $hide_percent = ' style="display:none"'; - } - - $nutriment_ref->{hide_select} = $hide_select; - $nutriment_ref->{hide_percent} = $hide_percent; - $nutriment_ref->{nutriment_unit_disabled} = $disabled; - - $disabled = $disabled_backup; - - foreach my $u (@units) { - my $selected = ''; - if (lc($unit) eq lc($u)) { - $selected = 'selected="selected" '; - } - my $label = $u; - # Display both mcg and µg as different food labels show the unit differently - if ($u eq 'µg') { - $label = "mcg/µg"; - } - - push( - @units_arr, - { - u => $u, - label => $label, - selected => $selected, - } - ); - } - - $nutriment_ref->{units_arr} = \@units_arr; - - } - - $nutriment_ref->{shown} = $shown; - $nutriment_ref->{enid} = $enid; - $nutriment_ref->{enidp} = $enidp; - $nutriment_ref->{nid} = $nid; - $nutriment_ref->{class} = $class; - $nutriment_ref->{value} = $value; - $nutriment_ref->{valuep} = $valuep; - $nutriment_ref->{display} = $display; - $nutriment_ref->{disabled} = $disabled; - - push(@nutriments, $nutriment_ref); - } - - $template_data_ref_display->{nutriments} = \@nutriments; - - # Compute a list of nutrients that will not be displayed in the nutrition facts table in the product edit form - # because they are not set for the product, and are not displayed by default in the user's country. - # Users will be allowed to add those nutrients, and this list will be used for nutrient name autocompletion. - - my $other_nutriments = ''; - my $nutriments = ''; - foreach my $nid (@{$other_nutriments_lists{$nutriment_table}}) { - my $other_nutriment_value = display_taxonomy_tag($lc, "nutrients", "zz:$nid"); - - # Some nutrients cannot be entered directly by users, so don't suggest them - next if (get_property("nutrients", "zz:$nid", "automatically_computed:en") eq "yes"); - - if ((not defined $product_ref->{nutriments}{$nid}) or ($product_ref->{nutriments}{$nid} eq '')) { - my $supports_iu = "false"; - if (defined get_property("nutrients", "zz:$nid", "iu_value:en")) { - $supports_iu = "true"; - } - - my $other_nutriment_unit = get_property("nutrients", "zz:$nid", "unit:en"); - $other_nutriments - .= '{ "value" : "' - . $other_nutriment_value - . '", "unit" : "' - . $other_nutriment_unit - . '", "iu": ' - . $supports_iu . ' },' . "\n"; - } - $nutriments .= '"' . $other_nutriment_value . '" : "' . $nid . '",' . "\n"; - } - $nutriments =~ s/,\n$//s; - $other_nutriments =~ s/,\n$//s; - - $scripts .= < -var nutriments = { -$nutriments -}; - -var otherNutriments = [ -$other_nutriments -]; - - -HTML - ; - - # Packaging photo and data - my @packaging_fields = ("packaging_image", "packaging_text"); - - $template_data_ref_display->{display_tab_packaging} - = display_input_tabs($product_ref, "packaging_image", $product_ref->{sorted_langs}, \%Langs, \@packaging_fields); - - # Product check - - if ($User{moderator}) { - my $checked = ''; - my $label = $Lang{i_checked_the_photos_and_data}{$lang}; - my $recheck_html = ""; - - if ((defined $product_ref->{checked}) and ($product_ref->{checked} eq 'on')) { - $checked = 'checked="checked"'; - $label = $Lang{photos_and_data_checked}{$lang}; - } - - $template_data_ref_display->{product_ref_checked} = $product_ref->{checked}; - $template_data_ref_display->{product_check_label} = $label; - $template_data_ref_display->{product_check_checked} = $checked; - - } - - $template_data_ref_display->{param_fields} = single_param("fields"); - $template_data_ref_display->{type} = $type; - $template_data_ref_display->{code} = $code; - $template_data_ref_display->{display_product_history} = display_product_history($code, $product_ref); - - process_template('web/pages/product_edit/product_edit_form_display.tt.html', $template_data_ref_display, \$html) - or $html = "

" . $tt->error() . "

"; - process_template('web/pages/product_edit/product_edit_form_display.tt.js', $template_data_ref_display, \$js); - $initjs .= $js; - -} -elsif (($action eq 'display') and ($type eq 'delete') and ($User{moderator})) { - - my $template_data_ref_moderator = {}; - - $log->debug("display product", {code => $code}) if $log->is_debug(); - - $template_data_ref_moderator->{product_name} = $product_ref->{product_name}; - $template_data_ref_moderator->{type} = $type; - $template_data_ref_moderator->{code} = $code; - - process_template('web/pages/product_edit/product_edit_form_display_user-moderator.tt.html', - $template_data_ref_moderator, \$html) - or $html = "

" . $tt->error() . "

"; - -} -elsif ($action eq 'process') { - - my $template_data_ref_process = {type => $type}; - - $log->debug("phase 2", {code => $code}) if $log->is_debug(); - - $product_ref->{interface_version_modified} = $interface_version; - - if (($User{moderator}) and ($type eq 'delete')) { - $product_ref->{deleted} = 'on'; - $comment = lang("deleting_product") . separator_before_colon($lc) . ":"; - } - elsif (($User{moderator}) and (exists $product_ref->{deleted})) { - delete $product_ref->{deleted}; - } - - my $time = time(); - $comment = $comment . remove_tags_and_quote(decode utf8 => single_param('comment')); - store_product($User_id, $product_ref, $comment); - - my $edited_product_url = product_url($product_ref); - - if (defined $product_ref->{server}) { - # product that was moved to OBF from OFF etc. - $edited_product_url - = "https://" - . $subdomain . "." - . $options{other_servers}{$product_ref->{server}}{domain} - . product_url($product_ref); - } - elsif ($type eq 'delete') { - - # Notify robotoff - send_notification_for_product_change($product_ref, "deleted"); - - my $email = <{display_random_sample_of_products_after_edits_options} - = $options{display_random_sample_of_products_after_edits}; - - # warning: this option is very slow - if ( (defined $options{display_random_sample_of_products_after_edits}) - and ($options{display_random_sample_of_products_after_edits})) - { - - my %request = ( - 'titleid' => get_string_id_for_lang($lc, product_name_brand($product_ref)), - 'query_string' => $ENV{QUERY_STRING}, - 'referer' => referer(), - 'code' => $code, - 'product_changes_saved' => 1, - 'sample_size' => 10 - ); - - display_product(\%request); - - } - } - - $template_data_ref_process->{edited_product_url} = $edited_product_url; - process_template('web/pages/product_edit/product_edit_form_process.tt.html', $template_data_ref_process, \$html) - or $html = "

" . $tt->error() . "

"; - -} - -$request_ref->{title} = lang($type . '_product'); -$request_ref->{content_ref} = \$html; -$request_ref->{full_width} = 1; -display_page($request_ref); - -exit(0); diff --git a/cgi/product.pl b/cgi/product.pl new file mode 120000 index 0000000000000..8b481d0b16dac --- /dev/null +++ b/cgi/product.pl @@ -0,0 +1 @@ +product_multilingual.pl \ No newline at end of file diff --git a/cgi/product_jqm.pl b/cgi/product_jqm.pl deleted file mode 100755 index bc6cd72e04a8e..0000000000000 --- a/cgi/product_jqm.pl +++ /dev/null @@ -1,496 +0,0 @@ -#!/usr/bin/perl -w - -# This file is part of Product Opener. -# -# Product Opener -# Copyright (C) 2011-2019 Association Open Food Facts -# Contact: contact@openfoodfacts.org -# Address: 21 rue des Iles, 94100 Saint-Maur des Fossés, France -# -# Product Opener is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -use ProductOpener::PerlStandards; - -use CGI::Carp qw(fatalsToBrowser); - -use ProductOpener::Config qw/:all/; -use ProductOpener::Store qw/:all/; -use ProductOpener::Index qw/:all/; -use ProductOpener::Display qw/:all/; -use ProductOpener::Tags qw/:all/; -use ProductOpener::Users qw/:all/; -use ProductOpener::Images qw/:all/; -use ProductOpener::Lang qw/:all/; -use ProductOpener::Mail qw/:all/; -use ProductOpener::Products qw/:all/; -use ProductOpener::Food qw/:all/; -use ProductOpener::Ingredients qw/:all/; -use ProductOpener::Images qw/:all/; -use ProductOpener::DataQuality qw/:all/; -use ProductOpener::Ecoscore qw/:all/; -use ProductOpener::Packaging qw/:all/; -use ProductOpener::ForestFootprint qw/:all/; -use ProductOpener::Text qw/:all/; - -use Apache2::RequestRec (); -use Apache2::Const (); - -use CGI qw/:cgi :form :cgi-lib escapeHTML/; -use URI::Escape::XS; -use Storable qw/dclone/; -use Encode; -use JSON::PP; -use Log::Any qw($log); - -my $request_ref = ProductOpener::Display::init_request(); - -my $comment = '(app)'; - -my $interface_version = '20150316.jqm2'; - -my %response = (); - -my $code = single_param('code'); -my $product_id; - -$log->debug("start", {code => $code, lc => $lc}) if $log->is_debug(); - -# Allow apps to create products without barcodes -# Assign a code and return it in the response. -if ($code eq "new") { - - ($code, $product_id) = assign_new_code(); - $response{code} = $code . ""; # Make sure the code is returned as a string -} - -my $original_code = $code; - -$code = normalize_code($code); - -if ($code !~ /^\d{4,24}$/) { - - $log->info("invalid code", {code => $code, original_code => $original_code}) if $log->is_info(); - $response{status} = 0; - $response{status_verbose} = 'no code or invalid code'; -} -else { - - my $product_id = product_id_for_owner($Owner_id, $code); - my $product_ref = retrieve_product($product_id); - - if (not defined $product_ref) { - $product_ref = init_product($User_id, $Org_id, $code, $country); - $product_ref->{interface_version_created} = $interface_version; - } - - # Process edit rules - - $log->debug("phase 0 - checking edit rules", {code => $code}) if $log->is_debug(); - - my $proceed_with_edit = process_product_edit_rules($product_ref); - - $log->debug("phase 0", {code => $code, proceed_with_edit => $proceed_with_edit}) if $log->is_debug(); - - if (not $proceed_with_edit) { - - $response{status} = 0; - $response{status_verbose} = 'Edit against edit rules'; - - my $data = encode_json(\%response); - - print header(-type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*') . $data; - - exit(0); - } - - exists $product_ref->{new_server} and delete $product_ref->{new_server}; - - my @errors = (); - - # Store parameters for debug purposes - (-e "$data_root/debug") or mkdir("$data_root/debug", 0755); - open(my $out, ">", "$data_root/debug/product_jqm_multilingual." . time() . "." . $code); - print $out encode_json(Vars()); - close $out; - - # Fix too low salt values - # 2020/02/25 - https://github.com/openfoodfacts/openfoodfacts-server/issues/2945 - if ((defined $User_id) and ($User_id eq 'kiliweb') and (defined single_param("nutriment_salt"))) { - - my $salt = single_param("nutriment_salt"); - - if ((defined $product_ref->{nutriments}) and (defined $product_ref->{nutriments}{salt_100g})) { - - my $existing_salt = $product_ref->{nutriments}{salt_100g}; - - $log->debug( - "yuka - kiliweb : changing salt value of existing product", - {salt => $salt, existing_salt => $existing_salt} - ) if $log->is_debug(); - - # Salt value may have been divided by 1000 by the calling app - if ($salt < $existing_salt / 100) { - # Float issue, we can get things like 0.18000001, convert back to string and remove extra digit - $salt = $salt . ''; - if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $` . '.' . $1; - } - if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $` . '.' . $1 . ($2 + 1); - } - $salt = $salt * 1000; - # The divided by 1000 value may have been of the form 9.99999925e-06: try again - if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $` . '.' . $1; - } - if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $` . '.' . $1 . ($2 + 1); - } - $log->debug("yuka - kiliweb : changing salt value - multiplying too low salt value by 1000", - {salt => $salt, existing_salt => $existing_salt}) - if $log->is_debug(); - param(-name => "nutriment_salt", -value => $salt); - } - } - else { - $log->debug("yuka - kiliweb : adding salt value", {salt => $salt}) if $log->is_debug(); - - # Salt value may have been divided by 1000 by the calling app - if ($salt < 0.001) { - # Float issue, we can get things like 0.18000001, convert back to string and remove extra digit - $salt = $salt . ''; - if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $` . '.' . $1; - } - if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $` . '.' . $1 . ($2 + 1); - } - $salt = $salt * 1000; - # The divided by 1000 value may have been of the form 9.99999925e-06: try again - if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $` . '.' . $1; - } - if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $` . '.' . $1 . ($2 + 1); - } - $log->debug("yuka - kiliweb : adding salt value - multiplying too low salt value by 1000", - {salt => $salt}) - if $log->is_debug(); - param(-name => "nutriment_salt", -value => $salt); - } - elsif ($salt < 0.1) { - $log->debug("yuka - kiliweb : adding salt value - removing potentially too low salt value", - {salt => $salt}) - if $log->is_debug(); - param(-name => "nutriment_salt", -value => ""); - } - } - } - - # 26/01/2017 - disallow barcode changes until we fix bug #677 - if ($User{moderator} and (defined single_param('new_code'))) { - - change_product_server_or_code($product_ref, single_param('new_code'), \@errors); - $code = $product_ref->{code}; - - if ($#errors >= 0) { - $response{status} = 0; - $response{status_verbose} = 'new code is invalid'; - - my $data = encode_json(\%response); - - print header(-type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*') . $data; - - exit(0); - } - } - - #my @app_fields = qw(product_name brands quantity); - my @app_fields - = qw(product_name generic_name quantity packaging brands categories labels origins manufacturing_places emb_codes link expiration_date purchase_places stores countries ); - - # admin field to set a creator - if ($admin) { - push @app_fields, "creator"; - } - - if ($admin or ($User_id eq "ecoscore-impact-estimator")) { - push @app_fields, ("ecoscore_extended_data", "ecoscore_extended_data_version"); - } - - # generate a list of potential languages for language specific fields - my %param_langs = (); - foreach my $param (multi_param()) { - if ($param =~ /^(.*)_(\w\w)$/) { - if (defined $language_fields{$1}) { - $param_langs{$2} = 1; - } - } - # some apps may send pt-BR - elsif ($param =~ /^(.*)_(\w\w)-(\w\w)$/) { - if (defined $language_fields{$1}) { - $param_langs{$2} = 1; - # set the product_name_pt value to the value of product_name_pt-BR - param($1 . "_" . $2, single_param($1 . "_" . $2 . "-" . $3)); - } - } - } - my @param_langs = keys %param_langs; - - # 01/06/2019 --> Yuka always sends fr fields even for Spanish products, try to correct it - - if ((defined $User_id) and ($User_id eq 'kiliweb') and (defined single_param('cc'))) { - - my $param_cc = lc(single_param('cc')); - $param_cc =~ s/^en://; - - my %lc_overrides = ( - au => "en", - br => "pt", - co => "es", - es => "es", - it => "it", - de => "de", - uk => "en", - gb => "en", - pt => "pt", - nl => "nl", - no => "no", - us => "en", - ie => "en", - nz => "en", - il => "he", - mx => "es", - tr => "tr", - ru => "ru", - th => "th", - dk => "da", - at => "de", - se => "sv", - bg => "bg", - pl => "pl", - - ); - - if (defined $lc_overrides{$param_cc}) { - $lc = $lc_overrides{$param_cc}; - } - } - - # Do not allow edits / removal through API for data provided by producers (only additions for non existing fields) - # when the corresponding organization has the protect_data checkbox checked - my $protected_data = product_data_is_protected($product_ref); - - foreach my $field (@app_fields, 'nutrition_data_per', 'serving_size', 'traces', 'ingredients_text', 'origin', - 'packaging_text', 'lang') - { - - # 11/6/2018 --> force add_brands and add_countries for yuka / kiliweb - if ( (defined $User_id) - and ($User_id eq 'kiliweb') - and (defined single_param($field)) - and (($field eq 'brands') or ($field eq 'countries'))) - { - - param(-name => "add_" . $field, -value => single_param($field)); - $log->debug("yuka - kiliweb : force add_field", {field => $field, code => $code}) if $log->is_debug(); - - } - - # add_brands=additional brand : only add if it does not exist yet - if ((defined $tags_fields{$field}) and (defined single_param("add_$field"))) { - - my $additional_fields = remove_tags_and_quote(decode utf8 => single_param("add_$field")); - - add_tags_to_field($product_ref, $lc, $field, $additional_fields); - - $log->debug( - "add_field", - { - field => $field, - code => $code, - additional_fields => $additional_fields, - existing_value => $product_ref->{$field} - } - ) if $log->is_debug(); - } - - elsif (defined single_param($field)) { - - # Do not allow edits / removal through API for data provided by producers (only additions for non existing fields) - if (($protected_data) and (defined $product_ref->{$field}) and ($product_ref->{$field} ne "")) { - $log->debug("producer data already exists for field, skip empty value", - {field => $field, code => $code, existing_value => $product_ref->{$field}}) - if $log->is_debug(); - - } - else { - if ($field eq "lang") { - my $value = remove_tags_and_quote(decode utf8 => single_param($field)); - - # strip variants fr-BE fr_BE - $value =~ s/^([a-z][a-z])(-|_).*$/$1/i; - $value = lc($value); - - # skip unrecognized languages (keep the existing lang & lc value) - if (defined $lang_lc{$value}) { - $product_ref->{lang} = $value; - $product_ref->{lc} = $value; - } - - } - elsif ($field eq "ecoscore_extended_data") { - # we expect a JSON value - if (defined single_param($field)) { - $product_ref->{$field} = decode_json(single_param($field)); - } - } - else { - $product_ref->{$field} = remove_tags_and_quote(decode utf8 => single_param($field)); - - if ((defined $language_fields{$field}) and (defined $product_ref->{lc})) { - my $field_lc = $field . "_" . $product_ref->{lc}; - $product_ref->{$field_lc} = $product_ref->{$field}; - } - - compute_field_tags($product_ref, $lc, $field); - } - } - } - - if (defined $language_fields{$field}) { - - foreach my $param_lang (@param_langs) { - my $field_lc = $field . '_' . $param_lang; - if (defined single_param($field_lc)) { - - # Do not allow edits / removal through API for data provided by producers (only additions for non existing fields) - if (($protected_data) and (defined $product_ref->{$field_lc}) and ($product_ref->{$field_lc} ne "")) - { - $log->debug("producer data already exists for field, skip empty value", - {field_lc => $field_lc, code => $code, existing_value => $product_ref->{$field_lc}}) - if $log->is_debug(); - } - else { - - $product_ref->{$field_lc} = remove_tags_and_quote(decode utf8 => single_param($field_lc)); - compute_field_tags($product_ref, $lc, $field_lc); - } - } - } - } - } - - if ( (defined $product_ref->{nutriments}{"carbon-footprint"}) - and ($product_ref->{nutriments}{"carbon-footprint"} ne '')) - { - push @{$product_ref->{"labels_hierarchy"}}, "en:carbon-footprint"; - push @{$product_ref->{"labels_tags"}}, "en:carbon-footprint"; - } - - # For fields that can have different values in different languages, copy the main language value to the non suffixed field - - foreach my $field (keys %language_fields) { - if ($field !~ /_image/) { - if (defined $product_ref->{$field . "_$product_ref->{lc}"}) { - $product_ref->{$field} = $product_ref->{$field . "_$product_ref->{lc}"}; - } - } - } - - compute_languages($product_ref); # need languages for allergens detection and cleaning ingredients - - # Ingredients classes - clean_ingredients_text($product_ref); - extract_ingredients_from_text($product_ref); - extract_ingredients_classes_from_text($product_ref); - detect_allergens_from_text($product_ref); - compute_carbon_footprint_from_ingredients($product_ref); - compute_carbon_footprint_from_meat_or_fish($product_ref); - - # Food category rules for sweeetened/sugared beverages - # French PNNS groups from categories - - if ((defined $options{product_type}) and ($options{product_type} eq "food")) { - ProductOpener::Food::special_process_product($product_ref); - } - - # Nutrition data - - # Do not allow nutrition edits through API for data provided by producers - if (($protected_data) and (defined $product_ref->{"nutriments"})) { - print STDERR - "product_jqm_multilingual.pm - code: $code - nutrition data provided by producer exists, skip nutrients\n"; - } - else { - assign_nutriments_values_from_request_parameters($product_ref, $nutriment_table); - } - - # Compute nutrition data per 100g and per serving - - $log->trace("compute_serving_size_date") if ($admin and $log->is_trace()); - - fix_salt_equivalent($product_ref); - - compute_serving_size_data($product_ref); - - compute_nutrition_score($product_ref); - - compute_nova_group($product_ref); - - compute_nutrient_levels($product_ref); - - compute_unknown_nutrients($product_ref); - - # Until we provide an interface to directly change the packaging data structure - # erase it before reconstructing it - # (otherwise there is no way to remove incorrect entries) - $product_ref->{packagings} = []; - - analyze_and_combine_packaging_data($product_ref); - - if ((defined $options{product_type}) and ($options{product_type} eq "food")) { - compute_ecoscore($product_ref); - compute_forest_footprint($product_ref); - } - - ProductOpener::DataQuality::check_quality($product_ref); - - $log->info("saving product", {code => $code}) if ($log->is_info() and not $log->is_debug()); - $log->debug("saving product", {code => $code, product => $product_ref}) - if ($log->is_debug() and not $log->is_info()); - - $product_ref->{interface_version_modified} = $interface_version; - - my $time = time(); - $comment = $comment . remove_tags_and_quote(decode utf8 => single_param('comment')); - if (store_product($User_id, $product_ref, $comment)) { - # Notify robotoff - send_notification_for_product_change($product_ref, "updated"); - - $response{status} = 1; - $response{status_verbose} = 'fields saved'; - } - else { - $response{status} = 0; - $response{status_verbose} = 'not modified'; - } -} - -my $data = encode_json(\%response); - -print header(-type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*') . $data; - -exit(0); - diff --git a/cgi/product_jqm.pl b/cgi/product_jqm.pl new file mode 120000 index 0000000000000..96d447fa8c68f --- /dev/null +++ b/cgi/product_jqm.pl @@ -0,0 +1 @@ +product_jqm_multilingual.pl \ No newline at end of file diff --git a/cgi/product_jqm2.pl b/cgi/product_jqm2.pl deleted file mode 100755 index bc6cd72e04a8e..0000000000000 --- a/cgi/product_jqm2.pl +++ /dev/null @@ -1,496 +0,0 @@ -#!/usr/bin/perl -w - -# This file is part of Product Opener. -# -# Product Opener -# Copyright (C) 2011-2019 Association Open Food Facts -# Contact: contact@openfoodfacts.org -# Address: 21 rue des Iles, 94100 Saint-Maur des Fossés, France -# -# Product Opener is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -use ProductOpener::PerlStandards; - -use CGI::Carp qw(fatalsToBrowser); - -use ProductOpener::Config qw/:all/; -use ProductOpener::Store qw/:all/; -use ProductOpener::Index qw/:all/; -use ProductOpener::Display qw/:all/; -use ProductOpener::Tags qw/:all/; -use ProductOpener::Users qw/:all/; -use ProductOpener::Images qw/:all/; -use ProductOpener::Lang qw/:all/; -use ProductOpener::Mail qw/:all/; -use ProductOpener::Products qw/:all/; -use ProductOpener::Food qw/:all/; -use ProductOpener::Ingredients qw/:all/; -use ProductOpener::Images qw/:all/; -use ProductOpener::DataQuality qw/:all/; -use ProductOpener::Ecoscore qw/:all/; -use ProductOpener::Packaging qw/:all/; -use ProductOpener::ForestFootprint qw/:all/; -use ProductOpener::Text qw/:all/; - -use Apache2::RequestRec (); -use Apache2::Const (); - -use CGI qw/:cgi :form :cgi-lib escapeHTML/; -use URI::Escape::XS; -use Storable qw/dclone/; -use Encode; -use JSON::PP; -use Log::Any qw($log); - -my $request_ref = ProductOpener::Display::init_request(); - -my $comment = '(app)'; - -my $interface_version = '20150316.jqm2'; - -my %response = (); - -my $code = single_param('code'); -my $product_id; - -$log->debug("start", {code => $code, lc => $lc}) if $log->is_debug(); - -# Allow apps to create products without barcodes -# Assign a code and return it in the response. -if ($code eq "new") { - - ($code, $product_id) = assign_new_code(); - $response{code} = $code . ""; # Make sure the code is returned as a string -} - -my $original_code = $code; - -$code = normalize_code($code); - -if ($code !~ /^\d{4,24}$/) { - - $log->info("invalid code", {code => $code, original_code => $original_code}) if $log->is_info(); - $response{status} = 0; - $response{status_verbose} = 'no code or invalid code'; -} -else { - - my $product_id = product_id_for_owner($Owner_id, $code); - my $product_ref = retrieve_product($product_id); - - if (not defined $product_ref) { - $product_ref = init_product($User_id, $Org_id, $code, $country); - $product_ref->{interface_version_created} = $interface_version; - } - - # Process edit rules - - $log->debug("phase 0 - checking edit rules", {code => $code}) if $log->is_debug(); - - my $proceed_with_edit = process_product_edit_rules($product_ref); - - $log->debug("phase 0", {code => $code, proceed_with_edit => $proceed_with_edit}) if $log->is_debug(); - - if (not $proceed_with_edit) { - - $response{status} = 0; - $response{status_verbose} = 'Edit against edit rules'; - - my $data = encode_json(\%response); - - print header(-type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*') . $data; - - exit(0); - } - - exists $product_ref->{new_server} and delete $product_ref->{new_server}; - - my @errors = (); - - # Store parameters for debug purposes - (-e "$data_root/debug") or mkdir("$data_root/debug", 0755); - open(my $out, ">", "$data_root/debug/product_jqm_multilingual." . time() . "." . $code); - print $out encode_json(Vars()); - close $out; - - # Fix too low salt values - # 2020/02/25 - https://github.com/openfoodfacts/openfoodfacts-server/issues/2945 - if ((defined $User_id) and ($User_id eq 'kiliweb') and (defined single_param("nutriment_salt"))) { - - my $salt = single_param("nutriment_salt"); - - if ((defined $product_ref->{nutriments}) and (defined $product_ref->{nutriments}{salt_100g})) { - - my $existing_salt = $product_ref->{nutriments}{salt_100g}; - - $log->debug( - "yuka - kiliweb : changing salt value of existing product", - {salt => $salt, existing_salt => $existing_salt} - ) if $log->is_debug(); - - # Salt value may have been divided by 1000 by the calling app - if ($salt < $existing_salt / 100) { - # Float issue, we can get things like 0.18000001, convert back to string and remove extra digit - $salt = $salt . ''; - if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $` . '.' . $1; - } - if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $` . '.' . $1 . ($2 + 1); - } - $salt = $salt * 1000; - # The divided by 1000 value may have been of the form 9.99999925e-06: try again - if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $` . '.' . $1; - } - if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $` . '.' . $1 . ($2 + 1); - } - $log->debug("yuka - kiliweb : changing salt value - multiplying too low salt value by 1000", - {salt => $salt, existing_salt => $existing_salt}) - if $log->is_debug(); - param(-name => "nutriment_salt", -value => $salt); - } - } - else { - $log->debug("yuka - kiliweb : adding salt value", {salt => $salt}) if $log->is_debug(); - - # Salt value may have been divided by 1000 by the calling app - if ($salt < 0.001) { - # Float issue, we can get things like 0.18000001, convert back to string and remove extra digit - $salt = $salt . ''; - if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $` . '.' . $1; - } - if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $` . '.' . $1 . ($2 + 1); - } - $salt = $salt * 1000; - # The divided by 1000 value may have been of the form 9.99999925e-06: try again - if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $` . '.' . $1; - } - if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $` . '.' . $1 . ($2 + 1); - } - $log->debug("yuka - kiliweb : adding salt value - multiplying too low salt value by 1000", - {salt => $salt}) - if $log->is_debug(); - param(-name => "nutriment_salt", -value => $salt); - } - elsif ($salt < 0.1) { - $log->debug("yuka - kiliweb : adding salt value - removing potentially too low salt value", - {salt => $salt}) - if $log->is_debug(); - param(-name => "nutriment_salt", -value => ""); - } - } - } - - # 26/01/2017 - disallow barcode changes until we fix bug #677 - if ($User{moderator} and (defined single_param('new_code'))) { - - change_product_server_or_code($product_ref, single_param('new_code'), \@errors); - $code = $product_ref->{code}; - - if ($#errors >= 0) { - $response{status} = 0; - $response{status_verbose} = 'new code is invalid'; - - my $data = encode_json(\%response); - - print header(-type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*') . $data; - - exit(0); - } - } - - #my @app_fields = qw(product_name brands quantity); - my @app_fields - = qw(product_name generic_name quantity packaging brands categories labels origins manufacturing_places emb_codes link expiration_date purchase_places stores countries ); - - # admin field to set a creator - if ($admin) { - push @app_fields, "creator"; - } - - if ($admin or ($User_id eq "ecoscore-impact-estimator")) { - push @app_fields, ("ecoscore_extended_data", "ecoscore_extended_data_version"); - } - - # generate a list of potential languages for language specific fields - my %param_langs = (); - foreach my $param (multi_param()) { - if ($param =~ /^(.*)_(\w\w)$/) { - if (defined $language_fields{$1}) { - $param_langs{$2} = 1; - } - } - # some apps may send pt-BR - elsif ($param =~ /^(.*)_(\w\w)-(\w\w)$/) { - if (defined $language_fields{$1}) { - $param_langs{$2} = 1; - # set the product_name_pt value to the value of product_name_pt-BR - param($1 . "_" . $2, single_param($1 . "_" . $2 . "-" . $3)); - } - } - } - my @param_langs = keys %param_langs; - - # 01/06/2019 --> Yuka always sends fr fields even for Spanish products, try to correct it - - if ((defined $User_id) and ($User_id eq 'kiliweb') and (defined single_param('cc'))) { - - my $param_cc = lc(single_param('cc')); - $param_cc =~ s/^en://; - - my %lc_overrides = ( - au => "en", - br => "pt", - co => "es", - es => "es", - it => "it", - de => "de", - uk => "en", - gb => "en", - pt => "pt", - nl => "nl", - no => "no", - us => "en", - ie => "en", - nz => "en", - il => "he", - mx => "es", - tr => "tr", - ru => "ru", - th => "th", - dk => "da", - at => "de", - se => "sv", - bg => "bg", - pl => "pl", - - ); - - if (defined $lc_overrides{$param_cc}) { - $lc = $lc_overrides{$param_cc}; - } - } - - # Do not allow edits / removal through API for data provided by producers (only additions for non existing fields) - # when the corresponding organization has the protect_data checkbox checked - my $protected_data = product_data_is_protected($product_ref); - - foreach my $field (@app_fields, 'nutrition_data_per', 'serving_size', 'traces', 'ingredients_text', 'origin', - 'packaging_text', 'lang') - { - - # 11/6/2018 --> force add_brands and add_countries for yuka / kiliweb - if ( (defined $User_id) - and ($User_id eq 'kiliweb') - and (defined single_param($field)) - and (($field eq 'brands') or ($field eq 'countries'))) - { - - param(-name => "add_" . $field, -value => single_param($field)); - $log->debug("yuka - kiliweb : force add_field", {field => $field, code => $code}) if $log->is_debug(); - - } - - # add_brands=additional brand : only add if it does not exist yet - if ((defined $tags_fields{$field}) and (defined single_param("add_$field"))) { - - my $additional_fields = remove_tags_and_quote(decode utf8 => single_param("add_$field")); - - add_tags_to_field($product_ref, $lc, $field, $additional_fields); - - $log->debug( - "add_field", - { - field => $field, - code => $code, - additional_fields => $additional_fields, - existing_value => $product_ref->{$field} - } - ) if $log->is_debug(); - } - - elsif (defined single_param($field)) { - - # Do not allow edits / removal through API for data provided by producers (only additions for non existing fields) - if (($protected_data) and (defined $product_ref->{$field}) and ($product_ref->{$field} ne "")) { - $log->debug("producer data already exists for field, skip empty value", - {field => $field, code => $code, existing_value => $product_ref->{$field}}) - if $log->is_debug(); - - } - else { - if ($field eq "lang") { - my $value = remove_tags_and_quote(decode utf8 => single_param($field)); - - # strip variants fr-BE fr_BE - $value =~ s/^([a-z][a-z])(-|_).*$/$1/i; - $value = lc($value); - - # skip unrecognized languages (keep the existing lang & lc value) - if (defined $lang_lc{$value}) { - $product_ref->{lang} = $value; - $product_ref->{lc} = $value; - } - - } - elsif ($field eq "ecoscore_extended_data") { - # we expect a JSON value - if (defined single_param($field)) { - $product_ref->{$field} = decode_json(single_param($field)); - } - } - else { - $product_ref->{$field} = remove_tags_and_quote(decode utf8 => single_param($field)); - - if ((defined $language_fields{$field}) and (defined $product_ref->{lc})) { - my $field_lc = $field . "_" . $product_ref->{lc}; - $product_ref->{$field_lc} = $product_ref->{$field}; - } - - compute_field_tags($product_ref, $lc, $field); - } - } - } - - if (defined $language_fields{$field}) { - - foreach my $param_lang (@param_langs) { - my $field_lc = $field . '_' . $param_lang; - if (defined single_param($field_lc)) { - - # Do not allow edits / removal through API for data provided by producers (only additions for non existing fields) - if (($protected_data) and (defined $product_ref->{$field_lc}) and ($product_ref->{$field_lc} ne "")) - { - $log->debug("producer data already exists for field, skip empty value", - {field_lc => $field_lc, code => $code, existing_value => $product_ref->{$field_lc}}) - if $log->is_debug(); - } - else { - - $product_ref->{$field_lc} = remove_tags_and_quote(decode utf8 => single_param($field_lc)); - compute_field_tags($product_ref, $lc, $field_lc); - } - } - } - } - } - - if ( (defined $product_ref->{nutriments}{"carbon-footprint"}) - and ($product_ref->{nutriments}{"carbon-footprint"} ne '')) - { - push @{$product_ref->{"labels_hierarchy"}}, "en:carbon-footprint"; - push @{$product_ref->{"labels_tags"}}, "en:carbon-footprint"; - } - - # For fields that can have different values in different languages, copy the main language value to the non suffixed field - - foreach my $field (keys %language_fields) { - if ($field !~ /_image/) { - if (defined $product_ref->{$field . "_$product_ref->{lc}"}) { - $product_ref->{$field} = $product_ref->{$field . "_$product_ref->{lc}"}; - } - } - } - - compute_languages($product_ref); # need languages for allergens detection and cleaning ingredients - - # Ingredients classes - clean_ingredients_text($product_ref); - extract_ingredients_from_text($product_ref); - extract_ingredients_classes_from_text($product_ref); - detect_allergens_from_text($product_ref); - compute_carbon_footprint_from_ingredients($product_ref); - compute_carbon_footprint_from_meat_or_fish($product_ref); - - # Food category rules for sweeetened/sugared beverages - # French PNNS groups from categories - - if ((defined $options{product_type}) and ($options{product_type} eq "food")) { - ProductOpener::Food::special_process_product($product_ref); - } - - # Nutrition data - - # Do not allow nutrition edits through API for data provided by producers - if (($protected_data) and (defined $product_ref->{"nutriments"})) { - print STDERR - "product_jqm_multilingual.pm - code: $code - nutrition data provided by producer exists, skip nutrients\n"; - } - else { - assign_nutriments_values_from_request_parameters($product_ref, $nutriment_table); - } - - # Compute nutrition data per 100g and per serving - - $log->trace("compute_serving_size_date") if ($admin and $log->is_trace()); - - fix_salt_equivalent($product_ref); - - compute_serving_size_data($product_ref); - - compute_nutrition_score($product_ref); - - compute_nova_group($product_ref); - - compute_nutrient_levels($product_ref); - - compute_unknown_nutrients($product_ref); - - # Until we provide an interface to directly change the packaging data structure - # erase it before reconstructing it - # (otherwise there is no way to remove incorrect entries) - $product_ref->{packagings} = []; - - analyze_and_combine_packaging_data($product_ref); - - if ((defined $options{product_type}) and ($options{product_type} eq "food")) { - compute_ecoscore($product_ref); - compute_forest_footprint($product_ref); - } - - ProductOpener::DataQuality::check_quality($product_ref); - - $log->info("saving product", {code => $code}) if ($log->is_info() and not $log->is_debug()); - $log->debug("saving product", {code => $code, product => $product_ref}) - if ($log->is_debug() and not $log->is_info()); - - $product_ref->{interface_version_modified} = $interface_version; - - my $time = time(); - $comment = $comment . remove_tags_and_quote(decode utf8 => single_param('comment')); - if (store_product($User_id, $product_ref, $comment)) { - # Notify robotoff - send_notification_for_product_change($product_ref, "updated"); - - $response{status} = 1; - $response{status_verbose} = 'fields saved'; - } - else { - $response{status} = 0; - $response{status_verbose} = 'not modified'; - } -} - -my $data = encode_json(\%response); - -print header(-type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*') . $data; - -exit(0); - diff --git a/cgi/product_jqm2.pl b/cgi/product_jqm2.pl new file mode 120000 index 0000000000000..96d447fa8c68f --- /dev/null +++ b/cgi/product_jqm2.pl @@ -0,0 +1 @@ +product_jqm_multilingual.pl \ No newline at end of file diff --git a/cgi/product_jqm_multilingual.pl b/cgi/product_jqm_multilingual.pl index f25d1fb586d4b..bc6cd72e04a8e 100755 --- a/cgi/product_jqm_multilingual.pl +++ b/cgi/product_jqm_multilingual.pl @@ -64,14 +64,14 @@ my $code = single_param('code'); my $product_id; -$log->debug("start", { code => $code, lc => $lc }) if $log->is_debug(); +$log->debug("start", {code => $code, lc => $lc}) if $log->is_debug(); # Allow apps to create products without barcodes # Assign a code and return it in the response. if ($code eq "new") { ($code, $product_id) = assign_new_code(); - $response{code} = $code . ""; # Make sure the code is returned as a string + $response{code} = $code . ""; # Make sure the code is returned as a string } my $original_code = $code; @@ -80,7 +80,7 @@ if ($code !~ /^\d{4,24}$/) { - $log->info("invalid code", { code => $code, original_code => $original_code }) if $log->is_info(); + $log->info("invalid code", {code => $code, original_code => $original_code}) if $log->is_info(); $response{status} = 0; $response{status_verbose} = 'no code or invalid code'; } @@ -94,23 +94,22 @@ $product_ref->{interface_version_created} = $interface_version; } - # Process edit rules - $log->debug("phase 0 - checking edit rules", { code => $code}) if $log->is_debug(); + $log->debug("phase 0 - checking edit rules", {code => $code}) if $log->is_debug(); my $proceed_with_edit = process_product_edit_rules($product_ref); - $log->debug("phase 0", { code => $code, proceed_with_edit => $proceed_with_edit }) if $log->is_debug(); + $log->debug("phase 0", {code => $code, proceed_with_edit => $proceed_with_edit}) if $log->is_debug(); if (not $proceed_with_edit) { $response{status} = 0; $response{status_verbose} = 'Edit against edit rules'; - my $data = encode_json(\%response); + my $data = encode_json(\%response); - print header( -type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*' ) . $data; + print header(-type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*') . $data; exit(0); } @@ -121,8 +120,8 @@ # Store parameters for debug purposes (-e "$data_root/debug") or mkdir("$data_root/debug", 0755); - open (my $out, ">", "$data_root/debug/product_jqm_multilingual." . time() . "." . $code); - print $out encode_json( Vars() ); + open(my $out, ">", "$data_root/debug/product_jqm_multilingual." . time() . "." . $code); + print $out encode_json(Vars()); close $out; # Fix too low salt values @@ -135,56 +134,65 @@ my $existing_salt = $product_ref->{nutriments}{salt_100g}; - $log->debug("yuka - kiliweb : changing salt value of existing product", { salt => $salt, existing_salt => $existing_salt }) if $log->is_debug(); + $log->debug( + "yuka - kiliweb : changing salt value of existing product", + {salt => $salt, existing_salt => $existing_salt} + ) if $log->is_debug(); # Salt value may have been divided by 1000 by the calling app if ($salt < $existing_salt / 100) { # Float issue, we can get things like 0.18000001, convert back to string and remove extra digit $salt = $salt . ''; if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $`. '.' . $1; + $salt = $` . '.' . $1; } if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $`. '.' . $1 . ($2 + 1); + $salt = $` . '.' . $1 . ($2 + 1); } $salt = $salt * 1000; # The divided by 1000 value may have been of the form 9.99999925e-06: try again if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $`. '.' . $1; + $salt = $` . '.' . $1; } if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $`. '.' . $1 . ($2 + 1); + $salt = $` . '.' . $1 . ($2 + 1); } - $log->debug("yuka - kiliweb : changing salt value - multiplying too low salt value by 1000", { salt => $salt, existing_salt => $existing_salt }) if $log->is_debug(); + $log->debug("yuka - kiliweb : changing salt value - multiplying too low salt value by 1000", + {salt => $salt, existing_salt => $existing_salt}) + if $log->is_debug(); param(-name => "nutriment_salt", -value => $salt); } } else { - $log->debug("yuka - kiliweb : adding salt value", { salt => $salt }) if $log->is_debug(); + $log->debug("yuka - kiliweb : adding salt value", {salt => $salt}) if $log->is_debug(); # Salt value may have been divided by 1000 by the calling app if ($salt < 0.001) { # Float issue, we can get things like 0.18000001, convert back to string and remove extra digit $salt = $salt . ''; if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $`. '.' . $1; + $salt = $` . '.' . $1; } if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $`. '.' . $1 . ($2 + 1); + $salt = $` . '.' . $1 . ($2 + 1); } $salt = $salt * 1000; # The divided by 1000 value may have been of the form 9.99999925e-06: try again if ($salt =~ /\.(\d*?[1-9]\d*?)0{2}/) { - $salt = $`. '.' . $1; + $salt = $` . '.' . $1; } if ($salt =~ /\.(\d+)([0-8]+)9999/) { - $salt = $`. '.' . $1 . ($2 + 1); + $salt = $` . '.' . $1 . ($2 + 1); } - $log->debug("yuka - kiliweb : adding salt value - multiplying too low salt value by 1000", { salt => $salt }) if $log->is_debug(); + $log->debug("yuka - kiliweb : adding salt value - multiplying too low salt value by 1000", + {salt => $salt}) + if $log->is_debug(); param(-name => "nutriment_salt", -value => $salt); } elsif ($salt < 0.1) { - $log->debug("yuka - kiliweb : adding salt value - removing potentially too low salt value", { salt => $salt }) if $log->is_debug(); + $log->debug("yuka - kiliweb : adding salt value - removing potentially too low salt value", + {salt => $salt}) + if $log->is_debug(); param(-name => "nutriment_salt", -value => ""); } } @@ -195,21 +203,22 @@ change_product_server_or_code($product_ref, single_param('new_code'), \@errors); $code = $product_ref->{code}; - + if ($#errors >= 0) { $response{status} = 0; $response{status_verbose} = 'new code is invalid'; - my $data = encode_json(\%response); + my $data = encode_json(\%response); - print header( -type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*' ) . $data; + print header(-type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*') . $data; - exit(0); + exit(0); } } #my @app_fields = qw(product_name brands quantity); - my @app_fields = qw(product_name generic_name quantity packaging brands categories labels origins manufacturing_places emb_codes link expiration_date purchase_places stores countries ); + my @app_fields + = qw(product_name generic_name quantity packaging brands categories labels origins manufacturing_places emb_codes link expiration_date purchase_places stores countries ); # admin field to set a creator if ($admin) { @@ -247,31 +256,31 @@ $param_cc =~ s/^en://; my %lc_overrides = ( - au => "en", - br => "pt", - co => "es", - es => "es", - it => "it", - de => "de", - uk => "en", - gb => "en", - pt => "pt", - nl => "nl", - no => "no", - us => "en", - ie => "en", - nz => "en", - il => "he", - mx => "es", - tr => "tr", - ru => "ru", - th => "th", - dk => "da", - at => "de", - se => "sv", - bg => "bg", - pl => "pl", - + au => "en", + br => "pt", + co => "es", + es => "es", + it => "it", + de => "de", + uk => "en", + gb => "en", + pt => "pt", + nl => "nl", + no => "no", + us => "en", + ie => "en", + nz => "en", + il => "he", + mx => "es", + tr => "tr", + ru => "ru", + th => "th", + dk => "da", + at => "de", + se => "sv", + bg => "bg", + pl => "pl", + ); if (defined $lc_overrides{$param_cc}) { @@ -283,15 +292,19 @@ # when the corresponding organization has the protect_data checkbox checked my $protected_data = product_data_is_protected($product_ref); - foreach my $field (@app_fields, 'nutrition_data_per', 'serving_size', 'traces', 'ingredients_text', 'origin', 'packaging_text', 'lang') { + foreach my $field (@app_fields, 'nutrition_data_per', 'serving_size', 'traces', 'ingredients_text', 'origin', + 'packaging_text', 'lang') + { # 11/6/2018 --> force add_brands and add_countries for yuka / kiliweb - if ((defined $User_id) and ($User_id eq 'kiliweb') + if ( (defined $User_id) + and ($User_id eq 'kiliweb') and (defined single_param($field)) - and (($field eq 'brands') or ($field eq 'countries'))) { + and (($field eq 'brands') or ($field eq 'countries'))) + { param(-name => "add_" . $field, -value => single_param($field)); - $log->debug("yuka - kiliweb : force add_field", { field => $field, code => $code }) if $log->is_debug(); + $log->debug("yuka - kiliweb : force add_field", {field => $field, code => $code}) if $log->is_debug(); } @@ -302,30 +315,40 @@ add_tags_to_field($product_ref, $lc, $field, $additional_fields); - $log->debug("add_field", { field => $field, code => $code, additional_fields => $additional_fields, existing_value => $product_ref->{$field} }) if $log->is_debug(); + $log->debug( + "add_field", + { + field => $field, + code => $code, + additional_fields => $additional_fields, + existing_value => $product_ref->{$field} + } + ) if $log->is_debug(); } elsif (defined single_param($field)) { # Do not allow edits / removal through API for data provided by producers (only additions for non existing fields) if (($protected_data) and (defined $product_ref->{$field}) and ($product_ref->{$field} ne "")) { - $log->debug("producer data already exists for field, skip empty value", { field => $field, code => $code, existing_value => $product_ref->{$field} }) if $log->is_debug(); + $log->debug("producer data already exists for field, skip empty value", + {field => $field, code => $code, existing_value => $product_ref->{$field}}) + if $log->is_debug(); } else { if ($field eq "lang") { my $value = remove_tags_and_quote(decode utf8 => single_param($field)); - + # strip variants fr-BE fr_BE $value =~ s/^([a-z][a-z])(-|_).*$/$1/i; $value = lc($value); - + # skip unrecognized languages (keep the existing lang & lc value) if (defined $lang_lc{$value}) { $product_ref->{lang} = $value; $product_ref->{lc} = $value; - } - + } + } elsif ($field eq "ecoscore_extended_data") { # we expect a JSON value @@ -353,8 +376,11 @@ if (defined single_param($field_lc)) { # Do not allow edits / removal through API for data provided by producers (only additions for non existing fields) - if (($protected_data) and (defined $product_ref->{$field_lc}) and ($product_ref->{$field_lc} ne "")) { - $log->debug("producer data already exists for field, skip empty value", { field_lc => $field_lc, code => $code, existing_value => $product_ref->{$field_lc} }) if $log->is_debug(); + if (($protected_data) and (defined $product_ref->{$field_lc}) and ($product_ref->{$field_lc} ne "")) + { + $log->debug("producer data already exists for field, skip empty value", + {field_lc => $field_lc, code => $code, existing_value => $product_ref->{$field_lc}}) + if $log->is_debug(); } else { @@ -366,9 +392,11 @@ } } - if ((defined $product_ref->{nutriments}{"carbon-footprint"}) and ($product_ref->{nutriments}{"carbon-footprint"} ne '')) { - push @{$product_ref->{"labels_hierarchy" }}, "en:carbon-footprint"; - push @{$product_ref->{"labels_tags" }}, "en:carbon-footprint"; + if ( (defined $product_ref->{nutriments}{"carbon-footprint"}) + and ($product_ref->{nutriments}{"carbon-footprint"} ne '')) + { + push @{$product_ref->{"labels_hierarchy"}}, "en:carbon-footprint"; + push @{$product_ref->{"labels_tags"}}, "en:carbon-footprint"; } # For fields that can have different values in different languages, copy the main language value to the non suffixed field @@ -381,7 +409,7 @@ } } - compute_languages($product_ref); # need languages for allergens detection and cleaning ingredients + compute_languages($product_ref); # need languages for allergens detection and cleaning ingredients # Ingredients classes clean_ingredients_text($product_ref); @@ -390,25 +418,25 @@ detect_allergens_from_text($product_ref); compute_carbon_footprint_from_ingredients($product_ref); compute_carbon_footprint_from_meat_or_fish($product_ref); - + # Food category rules for sweeetened/sugared beverages # French PNNS groups from categories if ((defined $options{product_type}) and ($options{product_type} eq "food")) { ProductOpener::Food::special_process_product($product_ref); - } + } # Nutrition data # Do not allow nutrition edits through API for data provided by producers if (($protected_data) and (defined $product_ref->{"nutriments"})) { - print STDERR "product_jqm_multilingual.pm - code: $code - nutrition data provided by producer exists, skip nutrients\n"; + print STDERR + "product_jqm_multilingual.pm - code: $code - nutrition data provided by producer exists, skip nutrients\n"; } else { assign_nutriments_values_from_request_parameters($product_ref, $nutriment_table); } - # Compute nutrition data per 100g and per serving $log->trace("compute_serving_size_date") if ($admin and $log->is_trace()); @@ -424,14 +452,14 @@ compute_nutrient_levels($product_ref); compute_unknown_nutrients($product_ref); - + # Until we provide an interface to directly change the packaging data structure # erase it before reconstructing it # (otherwise there is no way to remove incorrect entries) - $product_ref->{packagings} = []; - + $product_ref->{packagings} = []; + analyze_and_combine_packaging_data($product_ref); - + if ((defined $options{product_type}) and ($options{product_type} eq "food")) { compute_ecoscore($product_ref); compute_forest_footprint($product_ref); @@ -439,13 +467,12 @@ ProductOpener::DataQuality::check_quality($product_ref); - - $log->info("saving product", { code => $code }) if ($log->is_info() and not $log->is_debug()); - $log->debug("saving product", { code => $code, product => $product_ref }) if ($log->is_debug() and not $log->is_info()); + $log->info("saving product", {code => $code}) if ($log->is_info() and not $log->is_debug()); + $log->debug("saving product", {code => $code, product => $product_ref}) + if ($log->is_debug() and not $log->is_info()); $product_ref->{interface_version_modified} = $interface_version; - my $time = time(); $comment = $comment . remove_tags_and_quote(decode utf8 => single_param('comment')); if (store_product($User_id, $product_ref, $comment)) { @@ -461,10 +488,9 @@ } } -my $data = encode_json(\%response); - -print header( -type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*' ) . $data; +my $data = encode_json(\%response); +print header(-type => 'application/json', -charset => 'utf-8', -access_control_allow_origin => '*') . $data; exit(0); diff --git a/cgi/product_multilingual.pl b/cgi/product_multilingual.pl index fc6497c922bd3..374b4901d7a3f 100755 --- a/cgi/product_multilingual.pl +++ b/cgi/product_multilingual.pl @@ -60,10 +60,12 @@ my $request_ref = ProductOpener::Display::init_request(); if ($User_id eq 'unwanted-user-french') { - display_error("Il y a des problèmes avec les modifications de produits que vous avez effectuées. Ce compte est temporairement bloqué, merci de nous contacter.", 403); + display_error( + "Il y a des problèmes avec les modifications de produits que vous avez effectuées. Ce compte est temporairement bloqué, merci de nous contacter.", + 403 + ); } - my $type = single_param('type') || 'search_or_add'; my $action = single_param('action') || 'display'; @@ -98,7 +100,10 @@ my $r = Apache2::RequestUtil->request(); my $method = $r->method(); - if ((not defined $code) and ((not defined single_param("imgupload_search")) or ( single_param("imgupload_search") eq '')) and ($method eq 'POST')) { + if ( (not defined $code) + and ((not defined single_param("imgupload_search")) or (single_param("imgupload_search") eq '')) + and ($method eq 'POST')) + { ($code, $product_id) = assign_new_code(); } @@ -109,12 +114,12 @@ if (defined $code) { $data{code} = $code; $product_id = product_id_for_owner($Owner_id, $code); - $log->debug("we have a code", { code => $code, product_id => $product_id }) if $log->is_debug(); + $log->debug("we have a code", {code => $code, product_id => $product_id}) if $log->is_debug(); - $product_ref = product_exists($product_id); # returns 0 if not + $product_ref = product_exists($product_id); # returns 0 if not if ($product_ref) { - $log->info("product exists, redirecting to page", { code => $code }) if $log->is_info(); + $log->info("product exists, redirecting to page", {code => $code}) if $log->is_info(); $location = product_url($product_ref); # jquery.fileupload ? @@ -124,13 +129,14 @@ } else { my $r = shift; - $r->headers_out->set(Location =>$location); + $r->headers_out->set(Location => $location); $r->status(301); return 301; } } else { - $log->info("product does not exist, creating product", { code => $code, product_id => $product_id }) if $log->is_info(); + $log->info("product does not exist, creating product", {code => $code, product_id => $product_id}) + if $log->is_info(); $product_ref = init_product($User_id, $Org_id, $code, $country); $product_ref->{interface_version_created} = $interface_version; store_product($User_id, $product_ref, 'product_created'); @@ -143,7 +149,9 @@ if (defined $filename) { my $imgid; my $debug; - process_image_upload($product_ref->{_id},$filename,$User_id, time(),'image with barcode from web site Add product button',\$imgid, \$debug); + process_image_upload($product_ref->{_id}, $filename, $User_id, time(), + 'image with barcode from web site Add product button', + \$imgid, \$debug); } } } @@ -165,9 +173,9 @@ my $data = encode_json(\%data); - $log->debug("jqueryfileupload JSON data output", { data => $data }) if $log->is_debug(); + $log->debug("jqueryfileupload JSON data output", {data => $data}) if $log->is_debug(); - print header( -type => 'application/json', -charset => 'utf-8' ) . $data; + print header(-type => 'application/json', -charset => 'utf-8') . $data; exit(); } @@ -184,8 +192,9 @@ display_error($Lang{invalid_barcode}{$lang}, 403); } else { - if ( ((defined $server_options{private_products}) and ($server_options{private_products})) - and (not defined $Owner_id)) { + if ( ((defined $server_options{private_products}) and ($server_options{private_products})) + and (not defined $Owner_id)) + { display_error(lang("no_owner_defined"), 200); } @@ -211,15 +220,16 @@ if (not defined $User_id) { - my $submit_label = "login_and_" .$type . "_product"; + my $submit_label = "login_and_" . $type . "_product"; $action = 'login'; $template_data_ref->{type} = $type; } } -$template_data_ref->{user_id} = $User_id; +$template_data_ref->{user_id} = $User_id; $template_data_ref->{code} = $code; -process_template('web/pages/product_edit/product_edit_form.tt.html', $template_data_ref, \$html) or $html = "

" . $tt->error() . "

"; +process_template('web/pages/product_edit/product_edit_form.tt.html', $template_data_ref, \$html) + or $html = "

" . $tt->error() . "

"; my @fields = @ProductOpener::Config::product_fields; @@ -237,18 +247,18 @@ # Process edit rules - $log->debug("phase 0 - checking edit rules", { code => $code, type => $type }) if $log->is_debug(); + $log->debug("phase 0 - checking edit rules", {code => $code, type => $type}) if $log->is_debug(); my $proceed_with_edit = process_product_edit_rules($product_ref); - $log->debug("phase 0", { code => $code, type => $type, proceed_with_edit => $proceed_with_edit }) if $log->is_debug(); + $log->debug("phase 0", {code => $code, type => $type, proceed_with_edit => $proceed_with_edit}) if $log->is_debug(); if (not $proceed_with_edit) { display_error("Edit against edit rules", 403); } - $log->debug("phase 1", { code => $code, type => $type }) if $log->is_debug(); + $log->debug("phase 1", {code => $code, type => $type}) if $log->is_debug(); exists $product_ref->{new_server} and delete $product_ref->{new_server}; @@ -277,13 +287,18 @@ # Make sure we have the main language of the product (which could be new) # needed if we are moving data from one language to the main language - if ((defined single_param("lang")) and (single_param("lang") =~ /^\w\w$/) and (not defined $param_sorted_langs{single_param("lang")} )) { + if ( (defined single_param("lang")) + and (single_param("lang") =~ /^\w\w$/) + and (not defined $param_sorted_langs{single_param("lang")})) + { push @param_sorted_langs, single_param("lang"); } $product_ref->{"debug_param_sorted_langs"} = \@param_sorted_langs; - foreach my $field ('product_name', 'generic_name', @fields, 'nutrition_data_per', 'nutrition_data_prepared_per', 'serving_size', 'allergens', 'traces', 'ingredients_text', 'origin', 'packaging_text', 'lang') { + foreach my $field ('product_name', 'generic_name', @fields, 'nutrition_data_per', 'nutrition_data_prepared_per', + 'serving_size', 'allergens', 'traces', 'ingredients_text', 'origin', 'packaging_text', 'lang') + { if (defined $language_fields{$field}) { foreach my $display_lc (@param_sorted_langs) { @@ -308,8 +323,10 @@ my $mode = single_param($moveid . "_mode") || "replace"; - $log->debug("moving all data and photos from one language to another", - { from_lc => $from_lc, product_lc => $product_lc, mode => $mode }) if $log->is_debug(); + $log->debug( + "moving all data and photos from one language to another", + {from_lc => $from_lc, product_lc => $product_lc, mode => $mode} + ) if $log->is_debug(); # Text fields @@ -321,25 +338,50 @@ my $from_value = single_param($from_field); $log->debug("moving field value?", - { from_field => $from_field, from_value => $from_value, to_field => $to_field }) if $log->is_debug(); + {from_field => $from_field, from_value => $from_value, to_field => $to_field}) + if $log->is_debug(); if ((defined $from_value) and ($from_value ne "")) { my $to_value = single_param($to_field); - $log->debug("moving field value", - { from_field => $from_field, from_value => $from_value, to_field => $to_field, to_value => $to_value, mode => $mode }) if $log->is_debug(); + $log->debug( + "moving field value", + { + from_field => $from_field, + from_value => $from_value, + to_field => $to_field, + to_value => $to_value, + mode => $mode + } + ) if $log->is_debug(); if (($mode eq "replace") or ((not defined $to_value) or ($to_value eq ""))) { - $log->debug("replacing to field value", - { from_field => $from_field, from_value => $from_value, to_field => $to_field, to_value => $to_value, mode => $mode }) if $log->is_debug(); + $log->debug( + "replacing to field value", + { + from_field => $from_field, + from_value => $from_value, + to_field => $to_field, + to_value => $to_value, + mode => $mode + } + ) if $log->is_debug(); param($to_field, $from_value); } - $log->debug("deleting from field value", - { from_field => $from_field, from_value => $from_value, to_field => $to_field, to_value => $to_value, mode => $mode }) if $log->is_debug(); + $log->debug( + "deleting from field value", + { + from_field => $from_field, + from_value => $from_value, + to_field => $to_field, + to_value => $to_value, + mode => $mode + } + ) if $log->is_debug(); param($from_field, ""); } @@ -354,9 +396,8 @@ if ((defined $product_ref->{images}) and (defined $product_ref->{images}{$from_imageid})) { - $log->debug("moving selected image", - { from_imageid => $from_imageid, to_imageid => $to_imageid }) if $log->is_debug(); - + $log->debug("moving selected image", {from_imageid => $from_imageid, to_imageid => $to_imageid}) + if $log->is_debug(); if (($mode eq "replace") or (not defined $product_ref->{images}{$to_imageid})) { @@ -368,8 +409,13 @@ my $path = product_path($product_ref); foreach my $max ($thumb_size, $small_size, $display_size, "full") { - my $from_file = "$www_root/images/products/$path/" . $from_imageid . "." . $rev . "." . $max . ".jpg"; - my $to_file = "$www_root/images/products/$path/" . $to_imageid . "." . $rev . "." . $max . ".jpg"; + my $from_file + = "$www_root/images/products/$path/" + . $from_imageid . "." + . $rev . "." + . $max . ".jpg"; + my $to_file + = "$www_root/images/products/$path/" . $to_imageid . "." . $rev . "." . $max . ".jpg"; File::Copy::move($from_file, $to_file); } } @@ -381,19 +427,26 @@ } } - foreach my $field (@param_fields) { if (defined single_param($field)) { # If we are on the public platform, and the field data has been imported from the producer platform # ignore the field changes for non tag fields, unless made by a moderator - if (((not defined $server_options{private_products}) or (not $server_options{private_products})) - and (defined $product_ref->{owner_fields}) and (defined $product_ref->{owner_fields}{$field}) - and (not $User{moderator})) { - $log->debug("skipping field with a value set by the owner", - { code => $code, field_name => $field, existing_field_value => $product_ref->{$field}, - new_field_value => remove_tags_and_quote(decode utf8 => single_param($field))}) if $log->is_debug(); + if ( ((not defined $server_options{private_products}) or (not $server_options{private_products})) + and (defined $product_ref->{owner_fields}) + and (defined $product_ref->{owner_fields}{$field}) + and (not $User{moderator})) + { + $log->debug( + "skipping field with a value set by the owner", + { + code => $code, + field_name => $field, + existing_field_value => $product_ref->{$field}, + new_field_value => remove_tags_and_quote(decode utf8 => single_param($field)) + } + ) if $log->is_debug(); } if ($field eq "lang") { @@ -420,7 +473,9 @@ } } - $log->debug("before compute field_tags", { code => $code, field_name => $field, field_value => $product_ref->{$field}}) if $log->is_debug(); + $log->debug("before compute field_tags", + {code => $code, field_name => $field, field_value => $product_ref->{$field}}) + if $log->is_debug(); if ($field =~ /ingredients_text/) { # the ingredients_text_with_allergens[_$lc] will be recomputed after my $ingredients_text_with_allergens = $field; @@ -432,18 +487,21 @@ } else { - $log->debug("could not find field in params", { field => $field }) if $log->is_debug(); + $log->debug("could not find field in params", {field => $field}) if $log->is_debug(); } } - if ((defined $product_ref->{nutriments}{"carbon-footprint"}) and ($product_ref->{nutriments}{"carbon-footprint"} ne '')) { - push @{$product_ref->{"labels_hierarchy" }}, "en:carbon-footprint"; - push @{$product_ref->{"labels_tags" }}, "en:carbon-footprint"; + if ( (defined $product_ref->{nutriments}{"carbon-footprint"}) + and ($product_ref->{nutriments}{"carbon-footprint"} ne '')) + { + push @{$product_ref->{"labels_hierarchy"}}, "en:carbon-footprint"; + push @{$product_ref->{"labels_tags"}}, "en:carbon-footprint"; } - if ((defined $product_ref->{nutriments}{"glycemic-index"}) and ($product_ref->{nutriments}{"glycemic-index"} ne '')) { - push @{$product_ref->{"labels_hierarchy" }}, "en:glycemic-index"; - push @{$product_ref->{"labels_tags" }}, "en:glycemic-index"; + if ((defined $product_ref->{nutriments}{"glycemic-index"}) and ($product_ref->{nutriments}{"glycemic-index"} ne '')) + { + push @{$product_ref->{"labels_hierarchy"}}, "en:glycemic-index"; + push @{$product_ref->{"labels_tags"}}, "en:glycemic-index"; } # For fields that can have different values in different languages, copy the main language value to the non suffixed field @@ -458,7 +516,7 @@ $log->debug("compute_languages") if $log->is_debug(); - compute_languages($product_ref); # need languages for allergens detection and cleaning ingredients + compute_languages($product_ref); # need languages for allergens detection and cleaning ingredients $log->debug("clean_ingredients") if $log->is_debug(); # Ingredients classes @@ -471,7 +529,7 @@ detect_allergens_from_text($product_ref); compute_carbon_footprint_from_ingredients($product_ref); compute_carbon_footprint_from_meat_or_fish($product_ref); - + # Food category rules for sweetened/sugared beverages # French PNNS groups from categories @@ -557,14 +615,13 @@ } } - # Display the product edit form -my %remember_fields = ('purchase_places'=>1, 'stores'=>1); +my %remember_fields = ('purchase_places' => 1, 'stores' => 1); # Display each field -sub display_input_field($product_ref, $field, $language) { +sub display_input_field ($product_ref, $field, $language) { # $field can be in %language_fields and suffixed by _[lc] my $fieldtype = $field; @@ -589,9 +646,13 @@ ($product_ref, $field, $language) my $value = $product_ref->{$field}; - if ((defined $value) and (defined $taxonomy_fields{$field}) + if ( + (defined $value) + and (defined $taxonomy_fields{$field}) # if the field was previously not taxonomized, the $field_hierarchy field does not exist - and (defined $product_ref->{$field . "_hierarchy"})) { + and (defined $product_ref->{$field . "_hierarchy"}) + ) + { $value = display_tags_hierarchy_taxonomy($lc, $field, $product_ref->{$field . "_hierarchy"}); # Remove tags $value =~ s/<(([^>]|\n)*)>//g; @@ -600,12 +661,12 @@ ($product_ref, $field, $language) $value = ""; } - $template_data_ref_field->{language} = $language; - $template_data_ref_field->{field} = $field; - $template_data_ref_field->{class} = $class; - $template_data_ref_field->{value} = $value; - $template_data_ref_field->{display_lc} = $display_lc; - $template_data_ref_field->{autocomplete} = $autocomplete; + $template_data_ref_field->{language} = $language; + $template_data_ref_field->{field} = $field; + $template_data_ref_field->{class} = $class; + $template_data_ref_field->{value} = $value; + $template_data_ref_field->{display_lc} = $display_lc; + $template_data_ref_field->{autocomplete} = $autocomplete; $template_data_ref_field->{fieldtype} = $Lang{$fieldtype}{$lang}; my $html_field = ''; @@ -619,11 +680,14 @@ ($product_ref, $field, $language) } foreach my $note ("_note", "_note_2") { - if (defined $Lang{$fieldtype . $note }{$lang}) { + if (defined $Lang{$fieldtype . $note}{$lang}) { - push (@field_notes, { - note => $Lang{$fieldtype . $note }{$lang}, - }); + push( + @field_notes, + { + note => $Lang{$fieldtype . $note}{$lang}, + } + ); } } @@ -640,12 +704,12 @@ ($product_ref, $field, $language) $template_data_ref_field->{field_type_examples} = $Lang{$fieldtype . "_example"}{$lang}; } - process_template('web/pages/product_edit/display_input_field.tt.html', $template_data_ref_field, \$html_field) or $html_field = "

" . $tt->error() . "

"; + process_template('web/pages/product_edit/display_input_field.tt.html', $template_data_ref_field, \$html_field) + or $html_field = "

" . $tt->error() . "

"; return $html_field; } - if (($action eq 'display') and (($type eq 'add') or ($type eq 'edit'))) { # Populate the energy-kcal or energy-kj field from the energy field if it exists @@ -654,7 +718,7 @@ ($product_ref, $field, $language) my $template_data_ref_display = {}; my $js; - $log->debug("displaying product", { code => $code }) if $log->is_debug(); + $log->debug("displaying product", {code => $code}) if $log->is_debug(); # Lang strings for product.js @@ -668,7 +732,7 @@ ($product_ref, $field, $language) HTML -; + ; $scripts .= < @@ -686,8 +750,7 @@ ($product_ref, $field, $language) HTML -; - + ; my $thumb_selectable_size = $thumb_size + 20; @@ -710,11 +773,11 @@ ($product_ref, $field, $language) height: 180px } CSS -; + ; - - if ((not ((defined $server_options{private_products}) and ($server_options{private_products}))) - and (defined $Org_id)) { + if ( (not((defined $server_options{private_products}) and ($server_options{private_products}))) + and (defined $Org_id)) + { # Display a link to the producers platform @@ -748,7 +811,8 @@ ($product_ref, $field, $language) } $template_data_ref_display->{obsolete_checked} = $checked; - $template_data_ref_display->{display_field_obsolete} = display_input_field($product_ref, "obsolete_since_date", undef); + $template_data_ref_display->{display_field_obsolete} + = display_input_field($product_ref, "obsolete_since_date", undef); } @@ -768,7 +832,7 @@ ($product_ref, $field, $language) defined $product_ref->{lc} or $product_ref->{lc} = $lc; defined $product_ref->{languages_codes} or $product_ref->{languages_codes} = {}; - $product_ref->{sorted_langs} = [ $product_ref->{lc} ]; + $product_ref->{sorted_langs} = [$product_ref->{lc}]; foreach my $olc (sort keys %{$product_ref->{languages_codes}}) { if ($olc ne $product_ref->{lc}) { @@ -778,112 +842,125 @@ ($product_ref, $field, $language) $template_data_ref_display->{product_ref_sorted_langs} = join(',', @{$product_ref->{sorted_langs}}); -sub display_input_tabs($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref) { + sub display_input_tabs ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref) { - my $template_data_ref_tab = {}; - my @display_tabs; + my $template_data_ref_tab = {}; + my @display_tabs; - $template_data_ref_tab->{tabsid} = $tabsid; + $template_data_ref_tab->{tabsid} = $tabsid; - my $active = " active"; + my $active = " active"; - foreach my $tabid (@$tabsids_array_ref, 'new_lc','new') { + foreach my $tabid (@$tabsids_array_ref, 'new_lc', 'new') { - my $new_lc = ''; - if ($tabid eq 'new_lc') { - $new_lc = ' new_lc hide'; - } - elsif ($tabid eq 'new') { - $new_lc = ' new'; - } + my $new_lc = ''; + if ($tabid eq 'new_lc') { + $new_lc = ' new_lc hide'; + } + elsif ($tabid eq 'new') { + $new_lc = ' new'; + } - # We will create an array of fields for each language - my @fields_arr = (); + # We will create an array of fields for each language + my @fields_arr = (); - my $display_tab_ref = { - tabid => $tabid, - active => $active, - new_lc => $new_lc, - }; + my $display_tab_ref = { + tabid => $tabid, + active => $active, + new_lc => $new_lc, + }; - my $language; + my $language; - if ($tabid ne 'new') { - - $language = display_taxonomy_tag($lc,'languages',$language_codes{$tabid}); # instead of $tabsids_hash_ref->{$tabid} - $display_tab_ref->{language} = $language; - - my $display_lc = $tabid; - $template_data_ref_tab->{display_lc} = $display_lc; + if ($tabid ne 'new') { - foreach my $field (@{$fields_array_ref}) { + $language = display_taxonomy_tag($lc, 'languages', $language_codes{$tabid}) + ; # instead of $tabsids_hash_ref->{$tabid} + $display_tab_ref->{language} = $language; - # For the ingredient_text field, we will output a div above to display the image of the ingredients - my $image_full_id; - my $display_div; + my $display_lc = $tabid; + $template_data_ref_tab->{display_lc} = $display_lc; - if ($field =~ /^(.*)_image/) { + foreach my $field (@{$fields_array_ref}) { - my $image_field = $1 . "_" . $display_lc; - $display_div = display_select_crop($product_ref, $image_field, $language); - } - elsif ($field eq 'ingredients_text') { - $image_full_id = "ingredients_" . ${display_lc} . "_image_full"; - $display_div = display_input_field($product_ref, $field . "_" . $display_lc, $language); - } - else { - $log->debug("display_field", { field_name => $field, field_value => $product_ref->{$field} }) if $log->is_debug(); - $display_div = display_input_field($product_ref, $field . "_" . $display_lc, $language); - } + # For the ingredient_text field, we will output a div above to display the image of the ingredients + my $image_full_id; + my $display_div; - push(@fields_arr, { - image_full_id => $image_full_id, - field => $field, - display_div => $display_div, - }); - } + if ($field =~ /^(.*)_image/) { - $display_tab_ref->{fields} = \@fields_arr; - } - - # For moderators, add a checkbox to move all data and photos to the main language - # this needs to be below the "add (language name) in all field labels" above, so that it does not change this label. - if (($User{moderator}) and ($tabsid eq "front_image")) { + my $image_field = $1 . "_" . $display_lc; + $display_div = display_select_crop($product_ref, $image_field, $language); + } + elsif ($field eq 'ingredients_text') { + $image_full_id = "ingredients_" . ${display_lc} . "_image_full"; + $display_div = display_input_field($product_ref, $field . "_" . $display_lc, $language); + } + else { + $log->debug("display_field", {field_name => $field, field_value => $product_ref->{$field}}) + if $log->is_debug(); + $display_div = display_input_field($product_ref, $field . "_" . $display_lc, $language); + } - my $msg = f_lang("f_move_data_and_photos_to_main_language", { - language => '' . $language . '', - main_language => '' . lang("lang_" . $product_ref->{lc}) . '' - }); + push( + @fields_arr, + { + image_full_id => $image_full_id, + field => $field, + display_div => $display_div, + } + ); + } - my $moveid = "move_" . $tabid . "_data_and_images_to_main_language"; + $display_tab_ref->{fields} = \@fields_arr; + } - $display_tab_ref->{moveid} = $moveid; - $display_tab_ref->{msg} = $msg; - } + # For moderators, add a checkbox to move all data and photos to the main language + # this needs to be below the "add (language name) in all field labels" above, so that it does not change this label. + if (($User{moderator}) and ($tabsid eq "front_image")) { + + my $msg = f_lang( + "f_move_data_and_photos_to_main_language", + { + language => '' . $language . '', + main_language => '' + . lang("lang_" . $product_ref->{lc}) + . '' + } + ); - push(@display_tabs, $display_tab_ref); + my $moveid = "move_" . $tabid . "_data_and_images_to_main_language"; - # Only the first tab is active - $active = ""; + $display_tab_ref->{moveid} = $moveid; + $display_tab_ref->{msg} = $msg; + } - } + push(@display_tabs, $display_tab_ref); - $template_data_ref_tab->{display_tabs} = \@display_tabs; + # Only the first tab is active + $active = ""; - my $html_tab = ''; - process_template('web/pages/product_edit/display_input_tabs.tt.html', $template_data_ref_tab, \$html_tab) or $html_tab = "

" . $tt->error() . "

"; + } - return $html_tab; -} + $template_data_ref_tab->{display_tabs} = \@display_tabs; + + my $html_tab = ''; + process_template('web/pages/product_edit/display_input_tabs.tt.html', $template_data_ref_tab, \$html_tab) + or $html_tab = "

" . $tt->error() . "

"; + return $html_tab; + } - $template_data_ref_display->{display_tab_product_picture} = display_input_tabs($product_ref, "front_image", $product_ref->{sorted_langs}, \%Langs, ["front_image"]); - $template_data_ref_display->{display_tab_product_characteristics} = display_input_tabs($product_ref, "product", $product_ref->{sorted_langs}, \%Langs, ["product_name", "generic_name"]); + $template_data_ref_display->{display_tab_product_picture} + = display_input_tabs($product_ref, "front_image", $product_ref->{sorted_langs}, \%Langs, ["front_image"]); + $template_data_ref_display->{display_tab_product_characteristics} + = display_input_tabs($product_ref, "product", $product_ref->{sorted_langs}, + \%Langs, ["product_name", "generic_name"]); my @display_fields_arr; foreach my $field (@fields) { - next if $field eq "origins"; # now displayed below allergens and traces in the ingredients section - $log->debug("display_field", { field_name => $field, field_value => $product_ref->{$field} }) if $log->is_debug(); + next if $field eq "origins"; # now displayed below allergens and traces in the ingredients section + $log->debug("display_field", {field_name => $field, field_value => $product_ref->{$field}}) if $log->is_debug(); my $display_field = display_input_field($product_ref, $field, undef); push(@display_fields_arr, $display_field); } @@ -901,12 +978,15 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref } $template_data_ref_display->{nutrition_checked} = $checked; - $template_data_ref_display->{display_tab_ingredients_image} = display_input_tabs($product_ref, "ingredients_image", $product_ref->{sorted_langs}, \%Langs, \@ingredients_fields); - $template_data_ref_display->{display_field_allergens} = display_input_field($product_ref, "allergens", undef); - $template_data_ref_display->{display_field_traces} = display_input_field($product_ref, "traces", undef); - $template_data_ref_display->{display_field_origins} = display_input_field($product_ref, "origins", undef); - $template_data_ref_display->{display_tab_nutrition_image} = display_input_tabs($product_ref, "nutrition_image", $product_ref->{sorted_langs}, \%Langs, ["nutrition_image"]); - $template_data_ref_display->{display_field_serving_size} = display_input_field($product_ref, "serving_size", undef); + $template_data_ref_display->{display_tab_ingredients_image} + = display_input_tabs($product_ref, "ingredients_image", $product_ref->{sorted_langs}, \%Langs, + \@ingredients_fields); + $template_data_ref_display->{display_field_allergens} = display_input_field($product_ref, "allergens", undef); + $template_data_ref_display->{display_field_traces} = display_input_field($product_ref, "traces", undef); + $template_data_ref_display->{display_field_origins} = display_input_field($product_ref, "origins", undef); + $template_data_ref_display->{display_tab_nutrition_image} + = display_input_tabs($product_ref, "nutrition_image", $product_ref->{sorted_langs}, \%Langs, ["nutrition_image"]); + $template_data_ref_display->{display_field_serving_size} = display_input_field($product_ref, "serving_size", undef); $initjs .= display_select_crop_init($product_ref); @@ -954,9 +1034,12 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref my $nutrition_data_per = "nutrition_data" . $product_type . "_per"; - if (($product_ref->{$nutrition_data_per} eq 'serving') + if ( + ($product_ref->{$nutrition_data_per} eq 'serving') # display by serving by default for the prepared product - or (($product_type eq '_prepared') and (not defined $product_ref->{nutrition_data_prepared_per}))) { + or (($product_type eq '_prepared') and (not defined $product_ref->{nutrition_data_prepared_per})) + ) + { $checked_per_serving = 'checked="checked"'; $checked_per_100g = ''; $nutrition_data_per_display_style{$nutrition_data . "_serving"} = ''; @@ -964,38 +1047,45 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref } my $nutriment_col_class = "nutriment_col" . $product_type; - + my $product_type_as_sold_or_prepared = "as_sold"; if ($product_type eq "_prepared") { $product_type_as_sold_or_prepared = "prepared"; } - push(@nutrition_products, { - checked => $checked, - nutrition_data => $nutrition_data, - nutrition_data_exists => $Lang{$nutrition_data_exists}{$lang}, - nutrition_data_per => $nutrition_data_per, - checked_per_100g => $checked_per_100g, - checked_per_serving => $checked_per_serving, - nutrition_data_instructions => $nutrition_data_instructions, - nutrition_data_instructions_check => $Lang{$nutrition_data_instructions}, - nutrition_data_instructions_lang => $Lang{$nutrition_data_instructions}{$lang}, - hidden => $hidden, - nutriment_col_class => $nutriment_col_class, - product_type_as_sold_or_prepared => $product_type_as_sold_or_prepared, - checkmate => $product_ref->{$nutrition_data_per}, - }); + push( + @nutrition_products, + { + checked => $checked, + nutrition_data => $nutrition_data, + nutrition_data_exists => $Lang{$nutrition_data_exists}{$lang}, + nutrition_data_per => $nutrition_data_per, + checked_per_100g => $checked_per_100g, + checked_per_serving => $checked_per_serving, + nutrition_data_instructions => $nutrition_data_instructions, + nutrition_data_instructions_check => $Lang{$nutrition_data_instructions}, + nutrition_data_instructions_lang => $Lang{$nutrition_data_instructions}{$lang}, + hidden => $hidden, + nutriment_col_class => $nutriment_col_class, + product_type_as_sold_or_prepared => $product_type_as_sold_or_prepared, + checkmate => $product_ref->{$nutrition_data_per}, + } + ); } $template_data_ref_display->{nutrition_products} = \@nutrition_products; - $template_data_ref_display->{column_display_style_nutrition_data} =$column_display_style{"nutrition_data"}; - $template_data_ref_display->{column_display_style_nutrition_data_prepared} =$column_display_style{"nutrition_data_prepared"}; + $template_data_ref_display->{column_display_style_nutrition_data} = $column_display_style{"nutrition_data"}; + $template_data_ref_display->{column_display_style_nutrition_data_prepared} + = $column_display_style{"nutrition_data_prepared"}; $template_data_ref_display->{nutrition_data_100g_style} = $nutrition_data_per_display_style{"nutrition_data_100g"}; - $template_data_ref_display->{nutrition_data_serving_style} = $nutrition_data_per_display_style{"nutrition_data_serving"}; - $template_data_ref_display->{nutrition_data_prepared_100g_style} = $nutrition_data_per_display_style{"nutrition_data_prepared_100g"}; - $template_data_ref_display->{nutrition_data_prepared_serving_style} = $nutrition_data_per_display_style{"nutrition_data_prepared_serving"}; + $template_data_ref_display->{nutrition_data_serving_style} + = $nutrition_data_per_display_style{"nutrition_data_serving"}; + $template_data_ref_display->{nutrition_data_prepared_100g_style} + = $nutrition_data_per_display_style{"nutrition_data_prepared_100g"}; + $template_data_ref_display->{nutrition_data_prepared_serving_style} + = $nutrition_data_per_display_style{"nutrition_data_prepared_serving"}; $template_data_ref_display->{tablestyle} = $tablestyle; @@ -1005,20 +1095,22 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref my %seen_unknown_nutriments = (); foreach my $nid (keys %{$product_ref->{nutriments}}) { - next if (($nid =~ /_/) and ($nid !~ /_prepared$/)) ; + next if (($nid =~ /_/) and ($nid !~ /_prepared$/)); $nid =~ s/_prepared$//; - $log->trace("detect unknown nutriment", { nid => $nid }) if $log->is_trace(); + $log->trace("detect unknown nutriment", {nid => $nid}) if $log->is_trace(); - if ((not exists_taxonomy_tag("nutrients", "zz:$nid")) and (defined $product_ref->{nutriments}{$nid . "_label"}) - and (not defined $seen_unknown_nutriments{$nid})) { + if ( (not exists_taxonomy_tag("nutrients", "zz:$nid")) + and (defined $product_ref->{nutriments}{$nid . "_label"}) + and (not defined $seen_unknown_nutriments{$nid})) + { push @unknown_nutriments, $nid; - $log->debug("unknown nutriment detected", { nid => $nid }) if $log->is_debug(); + $log->debug("unknown nutriment detected", {nid => $nid}) if $log->is_debug(); } } - my @nutriments; + my @nutriments; foreach my $nutriment (@{$nutriments_tables{$nutriment_table}}, @unknown_nutriments, 'new_0', 'new_1') { my $nutriment_ref = {}; @@ -1038,12 +1130,19 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref my $shown = 0; - if (($nutriment !~ /-$/) + if ( + ($nutriment !~ /-$/) or ((defined $product_ref->{nutriments}{$nid}) and ($product_ref->{nutriments}{$nid} ne '')) - or ((defined $product_ref->{nutriments}{$nid . "_prepared"}) and ($product_ref->{nutriments}{$nid . "_prepared"} ne '')) - or ((defined $product_ref->{nutriments}{$nid . "_modifier"}) and ($product_ref->{nutriments}{$nid . "_modifier"} eq '-')) - or ((defined $product_ref->{nutriments}{$nid . "_prepared_modifier"}) and ($product_ref->{nutriments}{$nid . "_prepared_modifier"} eq '-')) - or ($nid eq 'new_0') or ($nid eq 'new_1')) { + or ( (defined $product_ref->{nutriments}{$nid . "_prepared"}) + and ($product_ref->{nutriments}{$nid . "_prepared"} ne '')) + or ( (defined $product_ref->{nutriments}{$nid . "_modifier"}) + and ($product_ref->{nutriments}{$nid . "_modifier"} eq '-')) + or ( (defined $product_ref->{nutriments}{$nid . "_prepared_modifier"}) + and ($product_ref->{nutriments}{$nid . "_prepared_modifier"} eq '-')) + or ($nid eq 'new_0') + or ($nid eq 'new_1') + ) + { $shown = 1; } @@ -1066,8 +1165,8 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref my $nidp = $nid . "_prepared"; my $enidp = encodeURIComponent($nidp); - $nutriment_ref->{label_value} = $product_ref->{nutriments}{$nid . "_label"}; - $nutriment_ref->{product_add_nutrient} = $Lang{product_add_nutrient}{$lang}; + $nutriment_ref->{label_value} = $product_ref->{nutriments}{$nid . "_label"}; + $nutriment_ref->{product_add_nutrient} = $Lang{product_add_nutrient}{$lang}; $nutriment_ref->{prefix} = $prefix; my $unit = "g"; @@ -1075,7 +1174,8 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref if (exists_taxonomy_tag("nutrients", "zz:$nid")) { $nutriment_ref->{name} = display_taxonomy_tag($lc, "nutrients", "zz:$nid"); # We may have a unit specific to the country (e.g. US nutrition facts table using the International Unit for this nutrient, and Europe using mg) - $unit = get_property("nutrients", "zz:$nid", "unit_$cc:en") // get_property("nutrients", "zz:$nid", "unit:en") // 'g'; + $unit = get_property("nutrients", "zz:$nid", "unit_$cc:en") + // get_property("nutrients", "zz:$nid", "unit:en") // 'g'; } else { if (defined $product_ref->{nutriments}{$nid . "_unit"}) { @@ -1083,8 +1183,8 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref } } - my $value; # product as sold - my $valuep; # prepared product + my $value; # product as sold + my $valuep; # prepared product if ($nid eq 'water-hardness') { $value = mmoll_to_unit($product_ref->{nutriments}{$nid}, $unit); @@ -1125,7 +1225,7 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref elsif ($product_ref->{nutriments}{$nid . "_modifier"} eq '-') { # The - minus sign indicates that there is no value specified on the product $value = '-'; - } + } } if (defined $product_ref->{nutriments}{$nidp . "_modifier"}) { @@ -1140,7 +1240,7 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref elsif ($product_ref->{nutriments}{$nidp . "_modifier"} eq '-') { # The - minus sign indicates that there is no value specified on the product $valuep = '-'; - } + } } if (lc($unit) eq "mcg") { @@ -1157,34 +1257,39 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref if (($nid eq 'alcohol') or ($nid eq 'energy-kj') or ($nid eq 'energy-kcal')) { my $unit = ''; - if (($nid eq 'alcohol')) { $unit = '% vol / °'; } # alcohol in % vol / ° - elsif (($nid eq 'energy-kj')) { $unit = 'kJ'; } - elsif (($nid eq 'energy-kcal')) { $unit = 'kcal'; } + if (($nid eq 'alcohol')) {$unit = '% vol / °';} # alcohol in % vol / ° + elsif (($nid eq 'energy-kj')) {$unit = 'kJ';} + elsif (($nid eq 'energy-kcal')) {$unit = 'kcal';} - $nutriment_ref->{nutriment_unit} = $unit; + $nutriment_ref->{nutriment_unit} = $unit; } else { - my @units = ('g','mg','µg'); + my @units = ('g', 'mg', 'µg'); my @units_arr; if ($nid =~ /^energy/) { - @units = ('kJ','kcal'); + @units = ('kJ', 'kcal'); } elsif ($nid eq 'water-hardness') { - @units = ('mol/l', 'mmol/l', 'mval/l', 'ppm', "\N{U+00B0}rH", "\N{U+00B0}fH", "\N{U+00B0}e", "\N{U+00B0}dH", 'gpg'); + @units = ( + 'mol/l', 'mmol/l', 'mval/l', 'ppm', "\N{U+00B0}rH", "\N{U+00B0}fH", + "\N{U+00B0}e", "\N{U+00B0}dH", 'gpg' + ); } - if ((defined get_property("nutrients", "zz:$nid", "dv_value:en")) + if ( (defined get_property("nutrients", "zz:$nid", "dv_value:en")) or ($nid =~ /^new_/) - or (uc($unit) eq '% DV')) { + or (uc($unit) eq '% DV')) + { push @units, '% DV'; } - if ((defined get_property("nutrients", "zz:$nid", "iu_value:en")) + if ( (defined get_property("nutrients", "zz:$nid", "iu_value:en")) or ($nid =~ /^new_/) or (uc($unit) eq 'IU') - or (uc($unit) eq 'UI')) { + or (uc($unit) eq 'UI')) + { push @units, 'IU'; } @@ -1203,9 +1308,9 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref $hide_percent = ' style="display:none"'; } - $nutriment_ref->{hide_select} = $hide_select; - $nutriment_ref->{hide_percent} = $hide_percent; - $nutriment_ref->{nutriment_unit_disabled} = $disabled; + $nutriment_ref->{hide_select} = $hide_select; + $nutriment_ref->{hide_percent} = $hide_percent; + $nutriment_ref->{nutriment_unit_disabled} = $disabled; $disabled = $disabled_backup; @@ -1220,17 +1325,20 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref $label = "mcg/µg"; } - push(@units_arr, { - u => $u, - label => $label, - selected => $selected, - }); + push( + @units_arr, + { + u => $u, + label => $label, + selected => $selected, + } + ); } $nutriment_ref->{units_arr} = \@units_arr; - + } - + $nutriment_ref->{shown} = $shown; $nutriment_ref->{enid} = $enid; $nutriment_ref->{enidp} = $enidp; @@ -1243,7 +1351,7 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref push(@nutriments, $nutriment_ref); } - + $template_data_ref_display->{nutriments} = \@nutriments; # Compute a list of nutrients that will not be displayed in the nutrition facts table in the product edit form @@ -1263,12 +1371,15 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref if (defined get_property("nutrients", "zz:$nid", "iu_value:en")) { $supports_iu = "true"; } - + my $other_nutriment_unit = get_property("nutrients", "zz:$nid", "unit:en"); - $other_nutriments .= '{ "value" : "' . $other_nutriment_value - . '", "unit" : "' . $other_nutriment_unit - . '", "iu": ' . $supports_iu - . ' },'. "\n"; + $other_nutriments + .= '{ "value" : "' + . $other_nutriment_value + . '", "unit" : "' + . $other_nutriment_unit + . '", "iu": ' + . $supports_iu . ' },' . "\n"; } $nutriments .= '"' . $other_nutriment_value . '" : "' . $nid . '",' . "\n"; } @@ -1287,12 +1398,13 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref HTML -; + ; # Packaging photo and data my @packaging_fields = ("packaging_image", "packaging_text"); - $template_data_ref_display->{display_tab_packaging} =display_input_tabs($product_ref, "packaging_image", $product_ref->{sorted_langs}, \%Langs, \@packaging_fields); + $template_data_ref_display->{display_tab_packaging} + = display_input_tabs($product_ref, "packaging_image", $product_ref->{sorted_langs}, \%Langs, \@packaging_fields); # Product check @@ -1317,7 +1429,8 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref $template_data_ref_display->{code} = $code; $template_data_ref_display->{display_product_history} = display_product_history($code, $product_ref); - process_template('web/pages/product_edit/product_edit_form_display.tt.html', $template_data_ref_display, \$html) or $html = "

" . $tt->error() . "

"; + process_template('web/pages/product_edit/product_edit_form_display.tt.html', $template_data_ref_display, \$html) + or $html = "

" . $tt->error() . "

"; process_template('web/pages/product_edit/product_edit_form_display.tt.js', $template_data_ref_display, \$js); $initjs .= $js; @@ -1326,20 +1439,22 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref my $template_data_ref_moderator = {}; - $log->debug("display product", { code => $code }) if $log->is_debug(); + $log->debug("display product", {code => $code}) if $log->is_debug(); $template_data_ref_moderator->{product_name} = $product_ref->{product_name}; $template_data_ref_moderator->{type} = $type; $template_data_ref_moderator->{code} = $code; - process_template('web/pages/product_edit/product_edit_form_display_user-moderator.tt.html', $template_data_ref_moderator, \$html) or $html = "

" . $tt->error() . "

"; + process_template('web/pages/product_edit/product_edit_form_display_user-moderator.tt.html', + $template_data_ref_moderator, \$html) + or $html = "

" . $tt->error() . "

"; } elsif ($action eq 'process') { - my $template_data_ref_process = { type => $type }; + my $template_data_ref_process = {type => $type}; - $log->debug("phase 2", { code => $code }) if $log->is_debug(); + $log->debug("phase 2", {code => $code}) if $log->is_debug(); $product_ref->{interface_version_modified} = $interface_version; @@ -1359,7 +1474,11 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref if (defined $product_ref->{server}) { # product that was moved to OBF from OFF etc. - $edited_product_url = "https://" . $subdomain . "." . $options{other_servers}{$product_ref->{server}}{domain} . product_url($product_ref); + $edited_product_url + = "https://" + . $subdomain . "." + . $options{other_servers}{$product_ref->{server}}{domain} + . product_url($product_ref); } elsif ($type eq 'delete') { @@ -1372,26 +1491,30 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref $html MAIL -; + ; send_email_to_admin(lang("deleting_product"), $email); - } else { + } + else { # Notify robotoff send_notification_for_product_change($product_ref, "updated"); - $template_data_ref_process->{display_random_sample_of_products_after_edits_options} = $options{display_random_sample_of_products_after_edits}; + $template_data_ref_process->{display_random_sample_of_products_after_edits_options} + = $options{display_random_sample_of_products_after_edits}; # warning: this option is very slow - if ((defined $options{display_random_sample_of_products_after_edits}) and ($options{display_random_sample_of_products_after_edits})) { + if ( (defined $options{display_random_sample_of_products_after_edits}) + and ($options{display_random_sample_of_products_after_edits})) + { my %request = ( - 'titleid'=>get_string_id_for_lang($lc,product_name_brand($product_ref)), - 'query_string'=>$ENV{QUERY_STRING}, - 'referer'=>referer(), - 'code'=>$code, - 'product_changes_saved'=>1, - 'sample_size'=>10 + 'titleid' => get_string_id_for_lang($lc, product_name_brand($product_ref)), + 'query_string' => $ENV{QUERY_STRING}, + 'referer' => referer(), + 'code' => $code, + 'product_changes_saved' => 1, + 'sample_size' => 10 ); display_product(\%request); @@ -1400,7 +1523,8 @@ ($product_ref, $tabsid, $tabsids_array_ref, $tabsids_hash_ref, $fields_array_ref } $template_data_ref_process->{edited_product_url} = $edited_product_url; - process_template('web/pages/product_edit/product_edit_form_process.tt.html', $template_data_ref_process, \$html) or $html = "

" . $tt->error() . "

"; + process_template('web/pages/product_edit/product_edit_form_process.tt.html', $template_data_ref_process, \$html) + or $html = "

" . $tt->error() . "

"; }